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?
精彩评论