问题 如何提高Canvas渲染性能?


我必须画很多 形状 (约半十万)作为[Canvas] [2]的孩子们。我在我的WPF应用程序中将这项工作分为两部分:首先我通过设置每个属性(如边距,填充,宽度等等)来创建形状,然后我将形状添加为Canvas的子项。

MyCanvas.Children.Add(MyShape)

现在我想提高第二部分的性能,因为当我绘制形状时,我的应用程序被封锁了很长一段时间。所以我试着用 调度员 和它的方法[BeginInvoke] [4]具有不同的[优先级] [5]:只有当我使用背景优先级时,主应用程序才会阻止,否则应用程序将被阻止,并且在添加所有形状之前不会显示“图片”到我的画布,但如果我使用背景优先级,显然一切都比较慢。我也试图创建一个新的线程,而不是使用Dispatcher,但没有重大的变化。

我如何解决这个问题,并在将形状添加到Canvas时通常会提高应用程序的性能?

谢谢。


7807
2018-04-03 16:56


起源

你试过DrawingVisual吗? - Ritch Melton
你能不能给我一个如何使用DrawingVisual而不是像Ellipse或Path这样的Shape的例子。例如,如何添加到我的Canvas 这个  使用DrawingVisual的路径? - gliderkite
是的,谷歌上有一些很棒的信息。这是一个帮助您入门的链接: msdn.microsoft.com/en-us/magazine/dd483292.aspx - Ritch Melton
这是一个更简单的例子,但它专注于命中测试,并没有解释为什么以及之前的链接。 msdn.microsoft.com/en-us/library/ms771684.aspx - Ritch Melton
我读了第一篇文章,但我在MSDN库上看到DrawingVisual没有提供事件handlig!我需要与我的形状进行交互,为我捕捉鼠标事件非常重要。 - gliderkite


答案:


需要使用 视觉 对象而不是 形状;特别是,如所建议的, DrawingVisual:可用于渲染矢量图形的可视对象。实际上,正如MSDN库中所写:

DrawingVisual是一个轻量级绘图类,用于渲染形状,图像或文本。此类被视为轻量级,因为它不提供布局,输入,焦点或事件处理,从而提高了性能。出于这个原因,图纸是背景和剪贴画的理想选择。

因此,例如,要创建包含矩形的DrawingVisual:

private DrawingVisual CreateDrawingVisualRectangle()
{
   DrawingVisual drawingVisual = new DrawingVisual();

   // Retrieve the DrawingContext in order to create new drawing content.
   DrawingContext drawingContext = drawingVisual.RenderOpen();

   // Create a rectangle and draw it in the DrawingContext.
   Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
   drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);

   // Persist the drawing content.
   drawingContext.Close();

   return drawingVisual;
}

要使用DrawingVisual对象,您需要为对象创建主机容器。主机容器对象必须派生自FrameworkElement类,该类提供DrawingVisual类缺少的布局和事件处理支持。为可视对象创建宿主容器对象时,需要将可视对象引用存储在a中 VisualCollection

public class MyVisualHost : FrameworkElement
{
   // Create a collection of child visual objects.
   private VisualCollection _children;

   public MyVisualHost()
   {
       _children = new VisualCollection(this);
       _children.Add(CreateDrawingVisualRectangle());

       // Add the event handler for MouseLeftButtonUp.
       this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
   }
}

然后,事件处理例程可以通过调用来实现命中测试 的HitTest 方法。方法的HitTestResultCallback参数引用用户定义的过程,您可以使用该过程来确定命中测试的结果操作。


7
2018-05-04 08:45



它会有所帮助,但WPF并不适合处理任何类型的50,000个视觉效果。 - Dr. ABT
那么什么是处理十万个矢量图形元素? - gliderkite
查看我链接到的Q和A: stackoverflow.com/questions/8713864/... - Dr. ABT
位图是 光栅图形 - gliderkite
正确,矢量图形实现效率低,不适合高对象计数。更好的是立即模式API,例如基于Bitmap或DirectX的渲染器。相信我,我已经与WPF合作多年了,而且我已经完成的最好的是屏幕上的大约5,000个视觉效果。除此之外,它完全失败了! - Dr. ABT


同意如果你想绘制数百万个元素,你根本无法在WPF中完成。如上所述,WriteableBitmapEx是一个不错的选择。

看到 这个相关的问题 它深入介绍了WPF中的高性能图形和可用的替代方案。

如果您只是必须使用Canvas,请查看此内容 ZoomableApplication2 - 一百万件商品。这是一个基于Canvas的演示,它大量使用虚拟化来在Canvas上获得1,000,000个UIElements的合理性能。


3
2018-05-04 08:37



它是可能的,因为我必须绘制大约半数元素(正如我写的)并且我已经完成了使用我的答案(自从我提出问题已经很长时间了),无论如何,谢谢你的回答,但位图是不是矢量图形。 - gliderkite


那是 很多UIElements可能不会给你想要的那种表现。您是否需要能够与正在渲染的每个元素进行交互?如果没有,我强烈建议考虑使用 WriteableBitmap 代替。如果你需要绘制形状而不想自己创建所有逻辑(谁想要?), 看看 WriteableBitmapEx 在CodePlex上进行投影


1
2018-04-03 17:23



我需要与所有形状进行交互,我也使用Shape,因为“图片”是矢量图形图像(图像可以缩放 - 使用缩放 - 任意数量而不会降低质量)。使用WritableBitmapEx我可以做同样的事情吗? - gliderkite
我不会使用Shape,我会使用DrawingVisual。您能否将这些详细信息添加到您的问题中?我会考虑并修改我的答案。 - Drew Marsh


这可能有点不相关,如果你有这种感觉我会道歉,但希望它可以为其他用户提供一些亮点,我将分享这个小窍门。

我们在使用Canvas控件捕获签名时遇到了一些性能问题。捕获是非常锯齿状的,因此我们无法绘制曲线。事实证明,风格与UI元素产生阴影有关。禁用阴影效果解决了我们的问题。


1
2018-05-26 13:47



多亏了这一点,我遇到了在网格上绘制大量线条的性能问题,并且通过删除我的投影效果,根本没有延迟! - Cyral