开发者

DrawingContext.DrawLine performance problem

I was trying out different strategies for drawing a graph from the left edge of a control to the right edge. Until now we were using a Canvas with a polyline which performs OK, but could still use some improvement.

When I tried out DrawingContext.DrawLine I experienced incredibly bad performance, and I can't figure out why. This is the most condensed code I can come up with that demonstrates the problem:

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;
        }
    }
}

And MainWindow.xaml just contains this:

<StackPanel>
    <l:TestControl Height="16"/>
    <!-- copy+paste the above line a few times -->
</StackPanel>

Now resize the window: depending on the number of TestControls in the StackPanel I experience a noticeable delay (10 controls) or a 30-second-total-standstill (100 controls) where I can't even hit the "Stop Debugger"-Button in VS...

I'm quite confused about this, obviously I am doing something wrong but since the code is so simple I don't see what that could be... I am using .Net4 in case it matters.


You can gain performance by freezing the pen.

static TestControl()
{
    pen.Freeze();
}


The most efficient way to draw a graph in WPF is to use DrawingVisual.

Charles Petzold wrote an excellent article explaining how to do it in MSDN Magazine:

Foundations: Writing More Efficient ItmesControls

The techniques work for displaying thousands of data points.


Ok, playing around with it a bit more, I found that freezing the pen had a huge impact. Now I create the pen in the constructor like this:

public TestControl() {
    if (pen == null) {
        pen = new Pen(Brushes.Gray, 1.0);
        pen.Freeze();
    }
}

The performance is now as I would expect it to be. I knew it had to be something simple...


Drawing in WPF becomes extremely slow if you use a pen with a dash style other than Solid (the default). This affects every draw method of DrawingContext that accepts a pen (DrawLine, DrawGeometry, etc.)


This question is really old but I found a way that improved the execution of my code which used DrawingContext.DrawLine aswell.

This was my code to draw a curve one hour ago:

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();

Here is the code now:

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();

The old code would take ~ 140 ms to plot two curves of 3000 points. The new one takes about 5 ms. Using StreamGeometry seems to be much more efficient than DrawingContext.Drawline.

Edit: I'm using the dotnet framework version 3.5


My guess is that the call to rnd.Next(...) is causing a lot of overhead each render. You can test it by providing a constant and then compare the speeds.

Do you really need to generate new coordinates each render?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜