C# WPF Listbox虚拟化创建DisconnectedItems

C# WPF Listbox虚拟化创建DisconnectedItems,c#,.net,wpf,virtualization,C#,.net,Wpf,Virtualization,我正在尝试使用WPF列表框创建一个图形控件。我创建了我自己的画布,它源自虚拟化面板,我自己处理项目的实现和虚拟化 然后将listbox的“项目”面板设置为我的自定义虚拟画布 我遇到的问题发生在以下场景中: 首先创建列表框项A ListBox项B在画布上的项A右侧创建 ListBox项A首先被虚拟化(通过在视图外平移它) ListBox项B第二次被虚拟化(再次通过将其平移到视图之外) 查看列表框项目A和B(即:实现它们) 使用Snoop,我检测到ListBox现在有3个项目,其中一个是位于Lis

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

然后将listbox的“项目”面板设置为我的自定义虚拟画布

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

  • 首先创建列表框项A
  • ListBox项B在画布上的项A右侧创建
  • ListBox项A首先被虚拟化(通过在视图外平移它)
  • ListBox项B第二次被虚拟化(再次通过将其平移到视图之外)
  • 查看列表框项目A和B(即:实现它们)
  • 使用Snoop,我检测到ListBox现在有3个项目,其中一个是位于ListBox项目B正下方的“DisconnectedItem”
是什么导致创建此“DisconnectedItem”?如果我先虚拟化B,然后虚拟化A,则不会创建此项。我的理论是,虚拟化列表框中位于其他项之前的项会导致子项断开连接

使用具有数百个节点的图形时,问题更为明显,因为我在四处平移时最终会看到数百个断开连接的项目

下面是画布代码的一部分:

/// <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));
    }
//
///安排并虚拟化显式定位的子元素。
/// 
公共类VirtualzingCanvas:VirtualzingPanel
{
(...)
受保护的覆盖尺寸测量覆盖(尺寸约束)
{
ItemsControl itemsOwner=ItemsControl.GetItemsOwner(此);
//出于某种原因,您必须“触摸”中的儿童收藏
//ItemContainerGenerator正确初始化的顺序。
var NecessaryChildrenTouch=儿童;
IIItemContainerGenerator生成器=ItemContainerGenerator;
IDisposable generationAction=null;
int指数=0;
Rect visibilityRect=新Rect(
-水平偏移/缩放因子,
-垂直偏移/缩放因子,
实际宽度/缩放因子,
实际高度/缩放因子);
//循环浏览项目列表并生成其容器
//如果它们包含在当前可见视图中。
foreach(itemsOwner.Items中的对象项)
{
var virtualizedItem=作为IVirtualizationCanvasItem的项目;
如果(virtualizedItem==null | |
visibilityRect.IntersectsWith(GetBounds(virtualizedItem)))
{
if(generationAction==null)
{
发电机位置起始位置=
generator.GeneratorPositionFromIndex(索引);
generationAction=generator.StartAt(startPosition,
生成方向。向前,正确);
}
生成项(索引);
}
其他的
{
GeneratorPosition itemPosition=
generator.GeneratorPositionFromIndex(索引);
if(itemPosition.Index!=-1&&itemPosition.Offset==0)
{
RemoveInternalChildRange(索引,1);
发电机。拆除(项目位置,1);
}
//当我们跳过某些项目时,需要“重置”生成器
//在序列中。。。
if(generationAction!=null)
{
generationAction.Dispose();
generationAction=null;
}
}
++指数;
}
if(generationAction!=null)
{
generationAction.Dispose();
}
返回默认值(大小);
}
(...)
私有void GenerateItem(整数索引)
{
布尔新词化;
变量元素=
ItemContainerGenerator.GenerateNext(out newlyRealized)作为UIElement;
如果(新实现)
{
如果(索引>=InternalChildren.Count)
{
AddInternalChild(元素);
}
其他的
{
InsertInternalChild(索引、元素);
}
ItemContainerGenerator.PrepareItemContainer(元素);
element.RenderTransform=\u scaleTransform;
}
元素。测量(新尺寸(双正不确定度,
双重(正不确定性);
}

每当从可视化树中删除容器时,都会使用它,原因可能是删除了相应的项,或刷新了集合,或将容器从屏幕上滚动并重新虚拟化

这是WPF4中的一个已知错误

请看,它还提供了一个您可以应用的变通方法

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

我们应该公开测试{DisconnectedItem},但是 它从裂缝中滑了出来。我们将在将来的版本中修复它,但是 现在你可以相信一个事实,那就是 {DisconnectedItem}对象。“


我已经晚了6年,但问题仍然没有在WPF中得到解决。这是解决方案(变通方法)

对DataContext进行自绑定,例如:

<Image DataContext="{Binding}" />


这对我来说很有效,即使是对于一个非常复杂的xaml。

你是在回收容器吗?@Blam:我不认为我是,你说回收容器是什么意思?只要搜索msdn中的回收容器就可以了,只是一个触手可及的地方,也只是一个很好的例子comment@Blam:谢谢,我试过使用虚拟化StackPanel并打开容器回收。不幸的是,我仍然我遇到了同样的问题,即:当项目被虚拟化时,会生成DisconnectedItems。“Microsoft Connect已在stackoverflow中退役”。干得好,Microsoft(你真漂亮!修复了我与项目相关的问题)