WPF列表框虚拟化创建DisconnectedItems

本文关键字:创建 DisconnectedItems 虚拟化 列表 WPF | 更新日期: 2023-09-27 17:51:18

我正在尝试使用WPF列表框创建一个图形控件。我创建了自己的Canvas,它来源于VirtualizingPanel,我自己处理项目的实现和虚拟化。

列表框的项目面板,然后设置为我的自定义虚拟画布。

我遇到的问题发生在以下场景:

  • ListBox Item A首先创建。
  • 列表框项目B创建在画布上项目A的右边。
  • 列表框项A首先被虚拟化(通过将其移出视图)。
  • 列表框项B被虚拟第二(再次通过平移到视图之外)。
  • 将列表框项A和B置于视图中(即:实现它们)
  • 使用Snoop,我检测到列表框现在有3个项目,其中一个是"DisconnectedItem"位于列表框项目b的正下方。

是什么原因导致这个"DisconnectedItem"的创建?如果我先虚拟化B,然后虚拟化A,则不会创建这个项目。我的理论是,在ListBox中虚拟化在其他项目之前的项目会导致子项目断开连接。

使用具有数百个节点的图时,问题更加明显,因为当我四处平移时,我最终会得到数百个不相连的项目。

下面是canvas的一部分代码:

/// <summary>
/// Arranges and virtualizes child element positionned explicitly.
/// </summary>
public class VirtualizingCanvas : VirtualizingPanel
{
   (...)
    protected override Size MeasureOverride(Size constraint)
    {
        ItemsControl itemsOwner = ItemsControl.GetItemsOwner(this);
        // For some reason you have to "touch" the children collection in 
        // order for the ItemContainerGenerator to initialize properly.
        var necessaryChidrenTouch = Children;
        IItemContainerGenerator generator = ItemContainerGenerator;
        IDisposable generationAction = null;
        int index = 0;
        Rect visibilityRect = new Rect(
            -HorizontalOffset / ZoomFactor,
            -VerticalOffset / ZoomFactor,
            ActualWidth / ZoomFactor,
            ActualHeight / ZoomFactor);
        // Loop thru the list of items and generate their container
        // if they are included in the current visible view.
        foreach (object item in itemsOwner.Items)
        {
            var virtualizedItem = item as IVirtualizingCanvasItem;
            if (virtualizedItem == null || 
                visibilityRect.IntersectsWith(GetBounds(virtualizedItem)))
            {
                if (generationAction == null)
                {
                    GeneratorPosition startPosition = 
                                 generator.GeneratorPositionFromIndex(index);
                    generationAction = generator.StartAt(startPosition, 
                                           GeneratorDirection.Forward, true);
                }
                GenerateItem(index);
            }
            else
            {
                GeneratorPosition itemPosition = 
                               generator.GeneratorPositionFromIndex(index);
                if (itemPosition.Index != -1 && itemPosition.Offset == 0)
                {
                    RemoveInternalChildRange(index, 1);
                    generator.Remove(itemPosition, 1);
                }
                // The generator needs to be "reseted" when we skip some items
                // in the sequence...
                if (generationAction != null)
                {
                    generationAction.Dispose();
                    generationAction = null;
                }
            }
            ++index;
        }
        if (generationAction != null)
        {
            generationAction.Dispose();
        }
        return default(Size);
    }
   (...)
    private void GenerateItem(int index)
    {
        bool newlyRealized;
        var element = 
          ItemContainerGenerator.GenerateNext(out newlyRealized) as UIElement;
        if (newlyRealized)
        {
            if (index >= InternalChildren.Count)
            {
                AddInternalChild(element);
            }
            else
            {
                InsertInternalChild(index, element);
            }
            ItemContainerGenerator.PrepareItemContainer(element);
            element.RenderTransform = _scaleTransform;
        }
        element.Measure(new Size(double.PositiveInfinity,
                                 double.PositiveInfinity));
    }

WPF列表框虚拟化创建DisconnectedItems

我迟到了6年,但这个问题在WPF中仍然没有解决。以下是解决方案(变通方法)。

对DataContext进行自绑定,例如:

<Image DataContext="{Binding}" />

这对我来说很有效,即使是非常复杂的xaml.

当容器从可视化树中删除时,或者因为相应的项被删除,或者集合被刷新,或者容器被滚动出屏幕并重新虚拟化时,就会使用它。

这是WPF 4中一个已知的错误

请参阅此链接了解已知的错误,它也有一个解决方案,您可能可以应用

"您可以通过保存引用使您的解决方案更加健壮第一次看到哨兵对象{DisconnectedItem}时,然后与之后保存的值进行比较。

我们应该创建一个公共的方法来测试{DisconnectedItem},但是它从裂缝中溜走了。我们将在以后的版本中修复这个问题,但是现在你可以相信有一个唯一的

{DisconnectedItem}对象!"