我有多达70000个Shape派生对象要添加到Canvas中。显然这会导致UI无响应,在我的情况下大约20秒。我尝试使用Dispatcher方法来尝试克服这个问题,但我仍然遇到问题。
我实施它的第一种方式是这样的:
foreach (var shape in ShapeList)
{
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
drawingCanvas.Children.Add(shape);
}));
}
这确实使得UI响应,但在不利方面,它需要永远(如10分钟?)才能完全添加所有形状。它也会在添加时立即显示Shape。
我实施的第二种方式是:
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
foreach (var shape in ShapeList)
{
drawingCanvas.Children.Add(shape);
}
}));
而且这似乎与不使用调度程序具有相同的效果。
我已经在一个单独的线程上尝试了两种实现,并且与主线程没有区别。此外,我必须将它们全部添加为单独的Shape对象,因为它们中的每一个都必须支持单个MouseClickEvents,因此我没有使用drawingVisual。
那么有什么方法可以让主窗口保持响应,同时仍然保持添加过程相对较快?
感谢。
*编辑 我知道CanvasVirtualization,但目前对我来说似乎有点复杂。有没有其他方法可以使用?
答案 0 :(得分:2)
使用Dispatcher提到的两个问题相当容易解决或减轻。
对于1),您可以通过批量添加项目而不是一次一个地添加项目来加快流程,同时仍保持响应性。对于2),一个简单的解决方案就是隐藏画布,并在完成绘图时显示它。
这样的事情:
// group shapes into batches
const int batchSize = 100;
var batches = ShapeList.Select((item, index) => new { index = index, item = item })
.GroupBy(item => item.index / batchSize)
.ToArray();
// function to create a local closure for each batch
Action<int> addBatch = batchNumber => {
var batch = batches[batchNumber];
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
foreach (var shape in batch)
{
drawingCanvas.Children.Add(shape.item);
};
if (batchNumber >= batches.Length - 1)
drawingCanvas.Visibility = Visibility.Visible;
}));
};
drawingCanvas.Visibility = Visibility.Collapsed;
for (int i=0 ; i < batches.Length ; i++)
{
addBatch(i);
}
答案 1 :(得分:0)
理想情况下,您应该使用虚拟化技术,只绘制实际可见的内容,而不是绘制所有70,000个项目。 MSDN上有一个博客,其中包含VirtualCanvas
类的源代码,如果您已经使用Canvas
,它几乎可以用于任何用途。
请注意 - 虚拟化具有一些特定条件,可以虚拟化项目。
http://blogs.msdn.com/b/jgoldb/archive/2008/03/08/performant-virtualized-wpf-canvas.aspx