使用WPF绘制数千个数据点的最佳方式?

24 .net c# wpf performance 2d

我写了一张显示财务数据的图表.性能很好,而我使用和s PathGeometry一起绘制的连接线显示不到10.000点.但是现在我需要同时显示多达100.000点(没有滚动),并且已经非常慢,只有50.000点.我在考虑,但我不确定,因为它与字节流的信息基本相同.有没有人有想法让这更高效,或者有人甚至做过类似的事情?PathFigureLineSegmentStreamGeometryPathGeometry

编辑:这些数据点一旦绘制就不会改变,所以如果有潜在的优化,请告诉我(线段现在被冻结).

编辑:我试过StreamGeometry.由于某种原因,创建图形的时间更长,但这不是问题.绘制所有点后在图表上绘制仍然与前一个方法一样慢.我认为WPF处理的数据点太多了.

编辑:我已经进行了一些实验,我注意到通过将先前为double的坐标转换为int来改善性能,以防止WPF抗锯齿子像素线.

编辑:感谢所有建议减少线段数的回复.对于阶梯线,我将它们降低到最多两倍的水平分辨率,对于简单的线条,最多是水平分辨率,现在性能相当不错.

Cra*_*rks 18

我会考虑对您尝试渲染的点数进行下采样.您可能拥有50,​​000点数据,但您不太可能将它们全部放在屏幕上; 即使您在一个显示器中绘制每个点,您也需要100,000像素的水平分辨率来绘制它们!即使在D3D中也是如此.

由于您更有可能拥有2,048像素,因此您可以减少绘图点并绘制适合屏幕的近似曲线,并且只有几千个顶点.例如,如果用户绘制包括10000个点的时间帧,则在绘图之前将那些10000点下采样到1000.您可以尝试许多技术,从简单平均到中间邻居到高斯卷积再到(我的建议)双三次插值.绘制大于1/2屏幕分辨率的任意数量的点将是一种浪费.

当用户放大图形的一部分时,您可以重新采样以获得更高的分辨率和更准确的曲线拟合.

  • 谢谢,我正在考虑那两个.我遇到的问题是数据点在x轴上没有相等的间隔(它们基于时间).点x可能有几个点,但y可能不同.我也不知道过滤会有多干净,因为我不知道点将被渲染的分辨率,因为它是WPF中的所有矢量图形. (2认同)

Dat*_*han 6

当您开始处理几何中数十万个不同的顶点和向量时,您应该考虑将图形代码迁移到使用图形框架而不是依赖于WPF(它建立在Direct3D之上,因此能够非常高效矢量图形渲染,有很多额外的开销正在阻碍其效率).可以在WPF中托管Direct3D和OpenGL图形渲染窗口 - 我建议移动该方向,而不是继续仅在WPF中工作.

(编辑:将"DirectX"更改为"Direct3D"的原始答案)


cpl*_*tts 5

刚刚遇到这个问题,但正如我在线程中提到的,最高效的方法可能是针对 WPF 的 Visual layer 进行编程

WPF 中的所有 Visual 最终都与这一层背道而驰……因此它是所有这些方法中最轻量级的方法。

有关更多信息,请参阅。Matthew MacDonald's Pro WPF in C# 2008 book 的第 14 章也有一个很好的部分。

作为另一个参考...请参阅 Pavan Podila 的书WPF Control Development Unleashed 的第 2 章。在第 13 页,他讨论了 DrawingVisuals 如何成为图表组件的绝佳选择。

最后,我刚刚注意到Charles Petzold写了一篇 MSDN 杂志文章,其中最好的整体(无论如何都是高性能的)解决方案(对于散点图)是 DrawingVisual 方法。


小智 5

另一个想法是使用Image控件,并将Source属性设置为您动态创建的DrawingImage.

根据WPF Control Development Unleashed的Pavan Podila所说,当你有成千上万的不需要任何交互性的视觉效果时,这种方法会非常有用.查看他的书的第25页了解更多信息.

这是一个旧线程,但我认为值得一提的是,您可以通过使用MouseUp()事件与上述方法实现交互.您知道图像视口的大小,图像的分辨率和鼠标的位置.例如,您可以通过附加到UserControl_SizeChanged事件的计时器来维护集合actualScreenPoints:

    double xworth = viewport.ActualWidth / (XEnd - XStart);
    double xworth = viewport.ActualHeight / (YEnd - YStart);
    List<Point> actualScreenPoints = new List<Point>(); 
    for (var i = 0; i < points.Count; i++)
    {
        double posX = points[i].X * xworth;
        double posY = points[i].Y * yworth;
        actualScreenPoints.Add(posX, posY);
    }
Run Code Online (Sandbox Code Playgroud)

然后当您的MouseUp()事件触发时,检查集合中的任何点是否在+ -2px范围内.在给定的点上有你的MouseUp.