我正在尝试不同的策略,用于从控件的左边缘到右边缘绘制图形。到目前为止,我们使用的是带有折线的Canvas,它可以正常运行,但仍然可以使用一些改进。
当我尝试使用DrawingContext.DrawLine时,我遇到了令人难以置信的糟糕表现,我无法弄清楚原因。这是我能提出的最简洁的代码,用于演示问题:
public class TestControl : Control {
static Pen pen = new Pen(Brushes.Gray, 1.0);
static Random rnd = new Random();
protected override void OnRender(DrawingContext drawingContext) {
var previousPoint = new Point(0, 0);
for (int x = 4; x < this.ActualWidth; x += 4) {
var newPoint = new Point(x, rnd.Next((int)this.ActualHeight));
drawingContext.DrawLine(pen, previousPoint, newPoint);
previousPoint = newPoint;
}
}
}
MainWindow.xaml只包含这个:
<StackPanel>
<l:TestControl Height="16"/>
<!-- copy+paste the above line a few times -->
</StackPanel>
现在调整窗口大小:根据StackPanel中的TestControl数量,我会遇到明显的延迟(10个控件)或30秒的完全静止(100个控件),我甚至无法点击“停止调试器” “-Button in VS ...
我对此很困惑,显然我做错了但是由于代码很简单,我看不出那可能是什么...... 我正在使用.Net4以防万一。
答案 0 :(得分:8)
你可以通过冻结笔来获得表现。
static TestControl()
{
pen.Freeze();
}
答案 1 :(得分:1)
在WPF中绘制图形的最有效方法是使用DrawingVisual。
Charles Petzold在MSDN杂志上写了一篇很好的文章,解释了如何做到这一点:
Foundations: Writing More Efficient ItmesControls
这些技术可用于显示数千个数据点。
答案 2 :(得分:1)
好的,再多玩一遍,我发现冻结笔产生了巨大的影响。现在我在构造函数中创建笔,如下所示:
public TestControl() {
if (pen == null) {
pen = new Pen(Brushes.Gray, 1.0);
pen.Freeze();
}
}
现在的表现正如我所料。我知道它必须是简单的......
答案 3 :(得分:0)
我的猜测是,对rnd.Next(...)
的调用会导致每次渲染产生大量开销。您可以通过提供常量来测试它,然后比较速度。
你真的需要为每个渲染生成新坐标吗?
答案 4 :(得分:0)
如果使用除Solid
(默认值)以外的短划线样式的笔,则在WPF中绘图会变得非常慢。这会影响接受笔(DrawingContext
,DrawLine
等的DrawGeometry
的每种绘制方法。
答案 5 :(得分:0)
这个问题真的很陈旧,但我找到了一种方法,可以改善我使用DrawingContext.DrawLine的代码的执行情况。
这是我一小时前绘制曲线的代码:
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
foreach (SerieVM serieVm in _curve.Series) {
Pen seriePen = new Pen(serieVm.Stroke, 1.0);
Point lastDrawnPoint = new Point();
bool firstPoint = true;
foreach (CurveValuePointVM pointVm in serieVm.Points.Cast<CurveValuePointVM>()) {
if (pointVm.XValue < xMin || pointVm.XValue > xMax) continue;
double x = basePoint.X + (pointVm.XValue - xMin) * xSizePerValue;
double y = basePoint.Y - (pointVm.Value - yMin) * ySizePerValue;
Point coord = new Point(x, y);
if (firstPoint) {
firstPoint = false;
} else {
dc.DrawLine(seriePen, lastDrawnPoint, coord);
}
lastDrawnPoint = coord;
}
}
dc.Close();
现在是代码:
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
foreach (SerieVM serieVm in _curve.Series) {
StreamGeometry g = new StreamGeometry();
StreamGeometryContext sgc = g.Open();
Pen seriePen = new Pen(serieVm.Stroke, 1.0);
bool firstPoint = true;
foreach (CurveValuePointVM pointVm in serieVm.Points.Cast<CurveValuePointVM>()) {
if (pointVm.XValue < xMin || pointVm.XValue > xMax) continue;
double x = basePoint.X + (pointVm.XValue - xMin) * xSizePerValue;
double y = basePoint.Y - (pointVm.Value - yMin) * ySizePerValue;
Point coord = new Point(x, y);
if (firstPoint) {
firstPoint = false;
sgc.BeginFigure(coord, false, false);
} else {
sgc.LineTo(coord, true, false);
}
}
sgc.Close();
dc.DrawGeometry(null, seriePen, g);
}
dc.Close();
旧代码需要约140毫秒来绘制两条3000点的曲线。新的大约需要5毫秒。使用StreamGeometry似乎比DrawingContext.Drawline更有效。
编辑:我正在使用dotnet framework version 3.5