StreamGeometry vs DrawingContext.DrawLine in WPF C#
Asked Answered
O

1

6

I'm going to draw hundreds of lines in real-time. I have chosen the Visual Layer to do this. But I see that there are two different ways to draw a line here. Which one you suggest to get a better performance and speed?

1. DrawingContext.DrawLine

public class DrawingTypeOne : FrameworkElement
{
    private readonly VisualCollection _visuals;
    public DrawingTypeOne(double thickness)
    {
        var myPen = new Pen
        {
            Thickness = 1,
            Brush = Brushes.White,
        };
        myPen.Freeze();

        _visuals = new VisualCollection(this);
        var drawingVisual = new DrawingVisual();
        using (var dc = drawingVisual.RenderOpen())
        {
            dc.DrawLine(myPen, new Point(0,0) , new Point(100,100));
            _visuals.Add(drawingVisual);
        }
    }

    protected override Visual GetVisualChild(int index)
    {
        return _visuals[index];
    }

    protected override int VisualChildrenCount
    {
        get
        {
            return _visuals.Count;
        }
    }
}

2. StreamGeometry

public class DrawingTypeTwo : FrameworkElement
{
    private readonly VisualCollection _visuals;
    public DrawingTypeTwo()
    {
        _visuals = new VisualCollection(this);

        var geometry = new StreamGeometry();
        using (var gc = geometry.Open())
        {
            gc.BeginFigure(new Point(0, 0), true, true);
            gc.LineTo(new Point(100,100), true, false);
        }
        geometry.Freeze();

        var drawingVisual = new DrawingVisual();
        using (var dc = drawingVisual.RenderOpen())
        {
            dc.DrawGeometry(Brushes.Red, null, geometry);
        }

        _visuals.Add(drawingVisual);
    }

    protected override Visual GetVisualChild(int index)
    {
        return _visuals[index];
    }

    protected override int VisualChildrenCount
    {
        get
        {
            return _visuals.Count;
        }
    }
}
Ora answered 11/4, 2014 at 20:7 Comment(21)
Why not just try them both and answer your own question?Protostele
Actually I'm doing it right now but I couldn't decide to be honest. They both looked the same to me.Ora
If you make the number of lines much larger, you'd see any differences. If its still the same, pick the one you like better.Protostele
I'll do that but I would like to know your opinion too. Thanks.Ora
@ReedCopsey StreamGeometry should be the faster one theoretically right?Ora
What about setting the content of the control to a PathFigure sourced from the StreamGeometry?Crissycrist
@Ora Yes, in theory, it's ligtherweight than the path information, since it doesn't support some of the same feature sets.Protostele
@Crissycrist This is the first time I'm hearing about this. Is there any examples?Ora
@ReedCopsey Thanks Reed, I was thinking since I'm going to draw hundreds of lines, I can define all of these lines in a geometry and then draw them at once using DrawingContext.DrawGeometry instead of drawing one by one using the DrawingContext.DrawLine method. Is this rational?Ora
@Ora Yes, batch drawing should help. In general a few hundred lines is pretty easy in WPF to draw, though, adn likely not going to be a problem no matter how you do it. I wouldn't focus too much on optimizing it until/unless its a problem.Protostele
First approach is faster.Funch
Thanks Reed, by hundreds I mean around 2000-3000 :pOra
See the examples here: msdn.microsoft.com/en-us/library/…Crissycrist
@Crissycrist Thanks Brannon, you mean Path.Data, I have tried this before I guess it was a bit slow.Ora
Because the second approach is in the end doing the same the first does just its wrapped nicely. DrawLine is the lowest end. You cant go any deeper than DrawLine. DrawGeometry is calling DrawLine and some other internal stuff.Funch
@devhedgehog You seem right too, I guess there is only one way left to find out.Ora
If you want performance do not create 100 visuals for 100 lines. Just create one visual and draw those 100 lines inside. WPF will cache your drawing therefore you do not need to inherit from FrameworkElement. Futhermore thats how you will have a visual without measure and arrange support that will also save you alot time.Funch
@devhedgehog I don't need to inherit FrameworkElement? How can I build my class then? Without inheriting it doesn't display anything. Should I inherit something else? Can you explain this more. Thanks.Ora
@devhedgehog I'm searching but couldn't find how should I implement this as you suggested. If I don't inherit FrameworkElement what should I do?Ora
@devhedgehog Should I use UIElement?Ora
I ll post you an example as answer. Give me 2 minutesFunch
F
4

Like I said you only need one visual and inside you can have all your lines.

Take a look at this:

First we define multiple drawings inside our drawing context:

class EllipseAndRectangle : DrawingVisual
{
    public EllipseAndRectangle()
    {
        using (DrawingContext dc = RenderOpen())
        {
            // Black ellipse with blue border
            dc.DrawEllipse(Brushes.Black,
                new Pen(Brushes.Blue, 3),        
                new Point(120, 120), 20, 40); 

            // Red rectangle with green border
            dc.DrawRectangle(Brushes.Red,
                new Pen(Brushes.Green, 4),    
                new Rect(new Point(10, 10), new Point(80, 80))); 
        }
    }
}

This is that one speical visual or element hosting all the drawings:

public class EllAndRectHost : FrameworkElement
{
    private EllipseAndRectangle _ellAndRect = new EllipseAndRectangle();

    // EllipseAndRectangle instance is our only visual child
    protected override Visual GetVisualChild(int index)
    {
        return _ellAndRect;
    }

    protected override int VisualChildrenCount
    {
        get
        {
            return 1;
        }
    }
}

And this is how you can use all those things in XAML:

<local:EllAndRectHost Margin="30" ... />

I was talking about the DrawingVisual class you could inherit from instead of creating 100 visuals for 100 lines.

Regarding your question, first approach is faster. Because the second approach is in the end doing the same the first does just its wrapped nicely. DrawLine is the lowest end. You can't go any deeper than DrawLine. DrawGeometry is calling DrawLine and some other internal stuff.

Funch answered 11/4, 2014 at 21:15 Comment(9)
Actually I'm doing as above. My loop runs inside the Using. What about not inheriting FrameworkElement ? I read that I can inherit UIElement too.Ora
By the way can you please append your comment on StreamGeometry vs DrawingContext.DrawLine. It is lost in the comments.Ora
In your code you have this line: _visuals.Add(drawingVisual); it seemed to me you add each line to VisualCollection. And then you access them with _visuals[index]; No need to have 100 DrawingVisuals, you can just have one. :)Funch
Yes you can inherit from UIElement or even Visual. All you need is one host and one visual with all the drawings. Grid, StackPanel, Canvas can be all your hosts. Everything is a Visual in WPF.Funch
Can you append your comment regarding StreamGeometry to your answer too? I accepted the answer I want it to be relevant to the question.Ora
Thanks I was wondering from where those points came! I'm kinda newbie here :pOra
Welcome to stackoverflow. everytime you edit something or post a helpful answer you will get points. You will get points even when somebody (just like me right now) upvotes your question.Funch
I've been here for nearly a year now. But I consider myself a newbie in respect to you guys.Ora
Always consider yourself a pro. Never claim its a bug. Sell it as feature.Funch

© 2022 - 2024 — McMap. All rights reserved.