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