WPF列表框虚拟化创建DisconnectedItems列表、WPF、DisconnectedItems

2023-09-03 03:19:56 作者:坠红颜君故

我试图创建一个使用一个WPF列表框一个图表控件。我创造了我自己的画布这源于一个VirtualizingPanel和我处理项目的实现和虚拟化自己。

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

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

在列表框项A的第一次创建。 在列表框B项是创建项目A的画布上的权利。 在列表框项A的虚拟化第一(通过淘选出来的观点)。 在列表框B项是虚拟化的第二个(再由淘洗出来的观点)。 把列表框项目A和B的观点(即:实现它们) 使用史努比,我发现该列表框现在有3个项目,其中一个是DisconnectedItem直接位于列表框B项下。

是什么原因导致创建这个DisconnectedItem的?如果我是虚拟化B首先,随之而来的是,该项目将不会被创建。我的理论是,虚拟化是precedes其他项目在ListBox的项目导致儿童被断开。

用图,数百个节点,因为我结束了数百断开的项目,因为我平移的问题更加明显。

这里是code的帆布部分:

  ///<总结>
///安排和虚拟化明确positionned子元素。
///< /总结>
公共类VirtualizingCanvas:VirtualizingPanel
{
   (...)

    保护覆盖面积的MeasureOverride(大小限制)
    {
        ItemsControl的itemsOwner = ItemsControl.GetItemsOwner(本);

        //出于某种原因,你必须在触动孩子们集合
        //为使ItemContainerGenerator正确初始化。
        VAR necessaryChidrenTouch =儿童;

        IItemContainerGenerator发电机= ItemContainerGenerator;

        IDisposable的generationAction = NULL;

        INT索引= 0;
        矩形visibilityRect =新的矩形(
            -Horizo​​ntalOffset / ZoomFactor,
            -VerticalOffset / ZoomFactor,
            ActualWidth的/ ZoomFactor,
            的ActualHeight / ZoomFactor);

        //循环通项目清单,并产生它们的容器
        //如果它们包括在当前可见视图。
        的foreach(在itemsOwner.Items对象项)
        {
            VAR virtualizedItem =项目作为IVirtualizingCanvasItem;

            如果(virtualizedItem == NULL ||
                visibilityRect.IntersectsWith(的getBounds(virtualizedItem)))
            {
                如果(generationAction == NULL)
                {
                    GeneratorPosition指定startPosition =
                                 generator.GeneratorPositionFromIndex(指数);
                    generationAction = generator.StartAt(指定startPosition,
                                           GeneratorDirection.Forward,真正的);
                }

                GenerateItem(指数);
            }
            其他
            {
                GeneratorPosition itemPosition =
                               generator.GeneratorPositionFromIndex(指数);

                如果(!itemPosition.Index = -1&功放;&安培; itemPosition.Offset == 0)
                {
                    RemoveInternalChildRange(指数,1);
                    generator.Remove(itemPosition,1);
                }

                //发电机需要reseted当我们跳过一些项目
                //序列中...
                如果(generationAction!= NULL)
                {
                    generationAction.Dispose();
                    generationAction = NULL;
                }
            }

            ++指数;
        }

        如果(generationAction!= NULL)
        {
            generationAction.Dispose();
        }

        返回默认值(大小);
    }

   (...)

    私人无效GenerateItem(INT指数)
    {
        布尔newlyRealized;
        VAR元=
          ItemContainerGenerator.GenerateNext(出newlyRealized)为UIElement的;

        如果(newlyRealized)
        {
            如果(索引> = InternalChildren.Count)
            {
                AddInternalChild(元);
            }
            其他
            {
                InsertInternalChild(索引,元件);
            }

            ItemContainerGenerator prepareItemContainer(元)。

            element.RenderTransform = _scaleTransform;
        }

        element.Measure(新尺寸(double.PositiveInfinity,
                                 double.PositiveInfinity));
    }
 

解决方案

它被用来当一个容器从视觉树中删除,要么是因为相应的项目已被删除,或收集是刷新,或容器滚出屏幕并重新虚拟化。

这是在WPF 4

一个已知的bug wpf小项目 CSDN

请参阅这个链接,已知的bug ,它也有一个解决方法,你或许可以适用。

编辑:

你可以让你的解决方案有点更强大的通过引用保存到定点对象{DisconnectedItem}在你第一次看到它,然后比对后所保存的值。

我们应该做一个公开的方式来测试{DisconnectedItem},但它通过裂缝下滑。我们会解决这个问题在将来的版本,但现在你可以在一个事实,即有一个独特的{DisconnectedItem}对象计数。

I'm attempting to create a Graph control using a WPF ListBox. I created my own Canvas which derives from a VirtualizingPanel and I handle the realization and virtualization of items myself.

The listbox' item panel is then set to be my custom virtualized canvas.

The problem I am encountering occurs in the following scenario:

ListBox Item A is created first. ListBox Item B is created to the right of Item A on the canvas. ListBox Item A is virtualized first (by panning it out of view). ListBox Item B is virtualized second (again by panning it out of view). Bring ListBox Item A and B in view (i.e: realize them) Using Snoop, I detect that the ListBox has now 3 items, one of them being a "DisconnectedItem" located directly underneath ListBox Item B.

What causes the creation of this "DisconnectedItem" ? If I were to virtualize B first, followed by A, this item would not be created. My theory is that virtualizing items that precedes other items in a ListBox causes children to be disconnected.

The problem is even more apparent using a graph with hundreds of nodes, as I end up with hundreds of disconnected items as I pan around.

Here is a portion of the code for the 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));
    }

解决方案

It's used whenever a container is removed from the visual tree, either because the corresponding item was deleted, or the collection was refreshed, or the container was scrolled off the screen and re-virtualized.

This is a known bug in WPF 4

See this link for known bug, it also has a workaround you may be able to apply.

EDIT:

"You can make your solution a little more robust by saving a reference to the sentinel object {DisconnectedItem} the first time you see it, then comparing against the saved value after that.

We should have made a public way to test for {DisconnectedItem}, but it slipped through the cracks. We'll fix that in a future release, but for now you can count on the fact that there's a unique {DisconnectedItem} object."