WPF Listbox Virtualization创建DisconnectedItems

时间:2013-01-11 16:53:48

标签: c# .net wpf virtualization

我正在尝试使用WPF ListBox创建Graph控件。我创建了自己的Canvas,它来自VirtualizingPanel,我自己处理项目的实现和虚拟化。

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

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

  • 首先创建ListBox项目A.
  • 列表框项目B在画布上的项目A的右侧创建。
  • ListBox项目A首先进行虚拟化(通过将其平移到视图之外)。
  • ListBox项目B被虚拟化为第二个(再次通过将其平移到视图之外)。
  • 将ListBox项目A和B置于视图中(即:实现它们)
  • 使用Snoop,我检测到ListBox现在有3个项目,其中一个是直接位于ListBox项目B下面的“DisconnectedItem”。

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

使用包含数百个节点的图表时问题更加明显,因为当我平移时,我最终会收到数百个断开连接的项目。

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

/// <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));
    }

2 个答案:

答案 0 :(得分:7)

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

这是WPF 4中的已知错误

请参阅this link for known bug,它也有一个可以应用的解决方法。

编辑:

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

我们应该以公开的方式测试{DisconnectedItem},但它已经滑过了裂缝。我们将在未来的版本中解决这个问题,但是现在你可以指望有一个独特的{DisconnectedItem}对象。

答案 1 :(得分:3)

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

对DataContext进行自绑定,例如:

<Image DataContext="{Binding}" />

即使对于非常复杂的xaml,这也对我有用。

相关问题