17

WPF ListBox を使用して Graph コントロールを作成しようとしています。VirtualizingPanel から派生する独自の Canvas を作成し、アイテムの実現と仮想化を自分で処理します。

次に、リストボックスのアイテム パネルをカスタムの仮想化キャンバスに設定します。

私が遭遇している問題は、次のシナリオで発生します。

  • ListBox アイテム A が最初に作成されます。
  • ListBox アイテム B は、キャンバス上のアイテム A の右側に作成されます。
  • ListBox アイテム A が最初に仮想化されます (パンしてビューから外します)。
  • ListBox アイテム B は 2 番目に仮想化されます (これもパンしてビューから外します)。
  • ListBox アイテム A と B を表示します (つまり、それらを認識します)。
  • Snoop を使用して、ListBox に 3 つの項目があり、そのうちの 1 つは 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));
    }
4

2 に答える 2

8

これは、対応する項目が削除されたか、コレクションが更新されたか、またはコンテナーが画面からスクロールされて再仮想化されたために、ビジュアル ツリーからコンテナーが削除されるたびに使用されます。

これは WPF 4 の既知のバグです。

既知のバグについては、このリンクを参照してください。適用できる回避策もあります。

「センチネル オブジェクト {DisconnectedItem} への参照を最初に表示したときに保存し、その後保存された値と比較することで、ソリューションをもう少し堅牢にすることができます。

{DisconnectedItem} をテストする公開の方法を作成する必要がありましたが、見過ごされてしまいました。将来のリリースで修正する予定ですが、現時点では、一意の {DisconnectedItem} オブジェクトがあるという事実を信頼できます。」

于 2013-01-16T12:40:02.813 に答える