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;
        }
    }
}
Vahid
  • 5,144
  • 13
  • 70
  • 146
  • 7
    Why not just try them both and answer your own question? – Reed Copsey Apr 11 '14 at 20:10
  • Actually I'm doing it right now but I couldn't decide to be honest. They both looked the same to me. – Vahid Apr 11 '14 at 20:12
  • 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. – Reed Copsey Apr 11 '14 at 20:13
  • I'll do that but I would like to know your opinion too. Thanks. – Vahid Apr 11 '14 at 20:15
  • @ReedCopsey StreamGeometry should be the faster one theoretically right? – Vahid Apr 11 '14 at 20:16
  • 1
    What about setting the content of the control to a PathFigure sourced from the StreamGeometry? – Brannon Apr 11 '14 at 20:28
  • 1
    @Vahid Yes, in theory, it's ligtherweight than the path information, since it doesn't support some of the same feature sets. – Reed Copsey Apr 11 '14 at 20:29
  • @Brannon This is the first time I'm hearing about this. Is there any examples? – Vahid Apr 11 '14 at 20:30
  • @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? – Vahid Apr 11 '14 at 20:33
  • 1
    @Vahid 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. – Reed Copsey Apr 11 '14 at 20:35
  • 1
    First approach is faster. – dev hedgehog Apr 11 '14 at 20:36
  • Thanks Reed, by hundreds I mean around 2000-3000 :p – Vahid Apr 11 '14 at 20:38
  • See the examples here: http://msdn.microsoft.com/en-us/library/system.windows.media.streamgeometry.aspx – Brannon Apr 11 '14 at 20:38
  • @Brannon Thanks Brannon, you mean Path.Data, I have tried this before I guess it was a bit slow. – Vahid Apr 11 '14 at 20:41
  • 1
    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. – dev hedgehog Apr 11 '14 at 20:42
  • @devhedgehog You seem right too, I guess there is only one way left to find out. – Vahid Apr 11 '14 at 20:45
  • 2
    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. – dev hedgehog Apr 11 '14 at 20:46
  • @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. – Vahid Apr 11 '14 at 20:51
  • @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? – Vahid Apr 11 '14 at 21:03
  • @devhedgehog Should I use UIElement? – Vahid Apr 11 '14 at 21:06
  • I ll post you an example as answer. Give me 2 minutes – dev hedgehog Apr 11 '14 at 21:11

1 Answers1

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.

Vahid
  • 5,144
  • 13
  • 70
  • 146
dev hedgehog
  • 8,698
  • 3
  • 28
  • 55
  • 1
    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. – Vahid Apr 11 '14 at 21:21
  • By the way can you please append your comment on `StreamGeometry` vs `DrawingContext.DrawLine`. It is lost in the comments. – Vahid Apr 11 '14 at 21:23
  • 1
    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. :) – dev hedgehog Apr 11 '14 at 21:28
  • 2
    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. – dev hedgehog Apr 11 '14 at 21:33
  • Can you append your comment regarding `StreamGeometry` to your answer too? I accepted the answer I want it to be relevant to the question. – Vahid Apr 11 '14 at 21:35
  • Thanks I was wondering from where those points came! I'm kinda newbie here :p – Vahid Apr 11 '14 at 21:44
  • 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. – dev hedgehog Apr 11 '14 at 21:46
  • I've been here for nearly a year now. But I consider myself a newbie in respect to you guys. – Vahid Apr 11 '14 at 21:48
  • 5
    Always consider yourself a pro. Never claim its a bug. Sell it as feature. – dev hedgehog Apr 11 '14 at 22:19