17

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

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

我遇到的问题发生在以下情况:

  • 首先创建 ListBox 项 A。
  • ListBox Item B 创建在画布上 Item A 的右侧。
  • ListBox Item A 首先被虚拟化(通过将其移出视图)。
  • ListBox Item B 是第二个虚拟化的(再次通过将其移出视图)。
  • 将 ListBox 项 A 和 B 显示在视图中(即:实现它们)
  • 使用 Snoop,我检测到 ListBox 现在有 3 个项目,其中一个是位于 ListBox Item 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));
    }
4

2 回答 2

9

我迟到了 6 年,但 WPF 中的问题仍未解决。是解决方案(解决方法)。

对 DataContext 进行自绑定,例如:

<Image DataContext="{Binding}" />

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

于 2019-08-27T10:48:28.900 回答
8

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

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

有关已知错误,请参阅此链接,它还有一个您可以应用的解决方法。

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

我们应该公开测试 {DisconnectedItem} 的方法,但它漏掉了。我们将在未来的版本中解决这个问题,但现在你可以相信有一个独特的 {DisconnectedItem} 对象这一事实。”

于 2013-01-16T12:40:02.813 回答