DrawingContext.DrawLine性能问题

时间:2011-05-09 13:12:43

标签: wpf

我正在尝试不同的策略,用于从控件的左边缘到右边缘绘制图形。到目前为止,我们使用的是带有折线的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以防万一。

6 个答案:

答案 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中绘图会变得非常慢。这会影响接受笔(DrawingContextDrawLine等的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