12

I have to draw a lot of Shape (about 1/2 hundred thousand) as [Canvas][2]'s childrens. I make this in my WPF application dividing work in two parts: first thing I create shapes by setting the properties of each of them (like Margin, Fill, Width, etc...), after I add shapes as Canvas's children.

MyCanvas.Children.Add(MyShape)

Now i want to improve the performance of the second part, because when i draw the shapes my application is blocked for a long period of time. So i tried to use the Dispatcher and its method [BeginInvoke][4] with different [priorities][5]: only if I use the Background priority the main application does not block, otherwise the application remains blocked and the "picture" is not displayed until all shapes are added to my Canvas, but if I use the Background priority obviously everything is slower. I also tried to create a new thread instead of using the Dispatcher, but there was no significant change.

How can I fix this problem, and generally improve the performance of my application when I add my shapes to Canvas?

Thanks.

gliderkite
  • 8,828
  • 6
  • 44
  • 80
  • Have you tried DrawingVisual? – Ritch Melton Apr 03 '12 at 18:51
  • No. Could you give me an example of how to use DrawingVisual instead of a Shape like Ellipse or Path. For example, how can I add to my Canvas [this](http://msdn.microsoft.com/en-us/library/ms745546.aspx) Path using DrawingVisual? – gliderkite Apr 03 '12 at 20:56
  • Yes, there's some great info on google. Here's a link to get you started: http://msdn.microsoft.com/en-us/magazine/dd483292.aspx – Ritch Melton Apr 03 '12 at 20:59
  • This is a simpler example, but its focused around hit-testing and doesn't explain the why as well as the previous link. http://msdn.microsoft.com/en-us/library/ms771684.aspx – Ritch Melton Apr 03 '12 at 20:59
  • I read first article but i saw on MSDN library that DrawingVisual does not provide event handlig! I need to interact whit my shapes, it's very important to catch mouse events for me. – gliderkite Apr 04 '12 at 12:37
  • That's where the host comes into play. I have a geometry 'solver' type of app where I manipulate points, lines, conics, etc... all through DrawingVisual. – Ritch Melton Apr 04 '12 at 14:04

4 Answers4

8

Need to use Visual objects instead of Shape; in particular, as suggested, DrawingVisual: a visual object that can be used to render vector graphics. In fact, as written in the MSDN library:

DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout, input, focus, or event handling, which improves its performance. For this reason, drawings are ideal for backgrounds and clip art.

So, for example, to create a DrawingVisual that contains a rectangle:

private DrawingVisual CreateDrawingVisualRectangle()
{
   DrawingVisual drawingVisual = new DrawingVisual();

   // Retrieve the DrawingContext in order to create new drawing content.
   DrawingContext drawingContext = drawingVisual.RenderOpen();

   // Create a rectangle and draw it in the DrawingContext.
   Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
   drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);

   // Persist the drawing content.
   drawingContext.Close();

   return drawingVisual;
}

In order to use DrawingVisual objects, you need to create a host container for the objects. The host container object must derive from the FrameworkElement class, which provides the layout and event handling support that the DrawingVisual class lacks. When you create a host container object for visual objects, you need to store the visual object references in a VisualCollection.

public class MyVisualHost : FrameworkElement
{
   // Create a collection of child visual objects.
   private VisualCollection _children;

   public MyVisualHost()
   {
       _children = new VisualCollection(this);
       _children.Add(CreateDrawingVisualRectangle());

       // Add the event handler for MouseLeftButtonUp.
       this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
   }
}

The event handling routine can then implement hit testing by invoking the HitTest method. The method's HitTestResultCallback parameter refers to a user-defined procedure that you can use to determine the resulting action of a hit test.

gliderkite
  • 8,828
  • 6
  • 44
  • 80
  • 3
    It'll help, marginally, however WPF isn't geared to handle 50,000 visuals of any type. – Dr. Andrew Burnett-Thompson May 04 '12 at 08:48
  • So what is geared to handle hundred thousand vector graphics elements? – gliderkite May 04 '12 at 08:54
  • See the Q and A I linked to: http://stackoverflow.com/questions/8713864/high-performance-graphics-using-the-wpf-visual-layer/8714107#8714107 – Dr. Andrew Burnett-Thompson May 04 '12 at 08:55
  • 1
    Correct, Vector graphics implementations are inefficient and not suited to high object counts. Far better are immediate mode APIs such as Bitmap or DirectX based renderers. Believe me, I've worked with WPF for years and the best I've gotten out of it is around 5,000 visuals on the screen at any one time. Any more than that and it totally falls over! – Dr. Andrew Burnett-Thompson May 04 '12 at 09:50
  • Thanks, i'll try to consider the WriteableBitmap possibility as much as possible to reach the best solution. – gliderkite May 04 '12 at 11:43
  • I think given your current problem, I would go with looking at the million elements canvas (using virtualization). However if that is still sub-par, try DrawingVisual (I don't believe you can virtualize with drawingvisual). If that is still slow, you may need to consider an alternative renderer and/or change your requirements – Dr. Andrew Burnett-Thompson May 04 '12 at 13:31
3

Agreed that if you want to draw millions of elements, you simply can't do it in WPF. WriteableBitmapEx as mentioned is a good alternative.

See this related question which goes into depth on high performance graphics in WPF and the alternatives available.

If you simply must use Canvas, check out this ZoomableApplication2 - A million items. This is a Canvas based demo which makes heavy use of Virtualization to get reasonable performance with 1,000,000 UIElements on a Canvas.

Community
  • 1
  • 1
Dr. Andrew Burnett-Thompson
  • 20,980
  • 8
  • 88
  • 178
  • it's possible because i have to draw about 1/2 hundred thousand of elements (as i wrote) and i've done using my answer (has been a long time since I asked the question), anyway thanks for your answer, but bitmap is not a vector graphics. – gliderkite May 04 '12 at 08:49
1

This may be somewhat unrelated, and I apologize if you feel this way, but in the hopes that it can shed some light for other users, I'll share this tidbit.

We had some performance issues with a Canvas control used for capturing signatures. The capture was very jagged, and we couldn't draw curved lines as a result. It turned out to be related to a style was was generating drop-shadows on the UI elements. Disabling the drop-shadow effect solved our problem.

Yves Rochon
  • 1,492
  • 16
  • 28
  • Thanks for this, I was having performance issues drawing a large number of lines on a grid, and by removing my drop shadow effect there is no lag at all! – Cyral Jan 23 '15 at 22:14
1

That's a lot of UIElements and probably isn't going to give the kind of performance you're looking for. Do you need to be able to interact with each of the elements you're rendering? If not, I would highly recommend looking into using WriteableBitmap instead. If you need to draw shapes and don't want to create all that logic yourself (who would want to?), check out the WriteableBitmapEx project over on CodePlex.

Drew Marsh
  • 33,111
  • 3
  • 82
  • 100
  • I need to interact with all shapes, also I use Shape because the "picture" is a vector graphics image (image can be scaled - with zoom - by any amount without degrading quality). With WritableBitmapEx I could do the same things? – gliderkite Apr 03 '12 at 17:40
  • 1
    I wouldn't use Shape, I'd use DrawingVisual. Can you add these details to your question please? I'll think about it and revise my answer. – Drew Marsh Apr 03 '12 at 17:42