我试图创建一个使用一个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 =新的矩形(
-HorizontalOffset / 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请参阅这个链接,已知的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."
相关推荐
最新文章