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

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

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

首先创建

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

    是什么原因导致创建此“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 4中的一个已知错误

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

    编辑:

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

    我们应该以一种公开的方式来测试{DisconnectedItem},但是它漏了一些缝隙。我们将在将来的版本中修复该问题,但现在您可以依靠一个独特的{DisconnectedItem}对象这一事实。”

    关于c# - WPF列表框虚拟化创建DisconnectedItems,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14282894/

  • 10-15 22:25