DrawingVisual performance with Opacity=0 - wpf

If I have a DrawingVisual in WPF with Opacity=0, is that enough for it not to be drawn? We have hundreds of DrawingVisuals on a Canvas, and are currently setting Opacity=0 on the visuals that are not to be displayed, and I wanted to make sure there is no rendering performance hit for rendering a DrawingVisual with Opacity=0.
UPDATE: I have discovered through testing that there IS overhead when Opacity=0, but since DrawingVisual doesn't have a Visibility property, I don't know how else you would tell it to not be displayed unless you actualy remove it from the visual tree, so any suggestions are welcome.

I solved a very similar problem by using a DrawingGroup and adding or removing Drawing objects from the DrawingGroup as they either needed to be displayed or hidden. The key is to organize your Drawing objects in such a way that they are easy to manage and to understand how to add and remove them from the DrawingGroup.
Remember that you want to add and remove the Drawing objects from the DrawingCollection exposed by the DrawingGroup.Children property. So use DrawingGroup.Children.Add() or the other DrawingCollection methods: Insert, Remove, RemoveAt, Clear. You will need to keep an external list of the Drawing objects you add/remove to the DrawingGroup to do this successfully.
I used this technique to great effect by drawing an Image (bitmap) into the first child in my instance of DrawingGroup and then adding and removing Drawing objects to this instance of DrawingGroup in order to layer polygons, paths, text, etc on top of the drawing.
I "draw" or "erase" on the image by adding or removing Drawing objects to the instance of the DrawingGroup. The DrawingGroup is treated as a single Drawing and so any scaling, panning, or other manipulations will affect all Drawing objects within the DrawingGroup.

The best way to check would be to instead set the Visibility to Visibility.Colapsed, and see if there's any drawing performance differences.
Visibility.Colapsed ensures that the element is not visible but also that it will not participate in the Arrange, Measure and Render passes of the UI, while an element with Opacity=0 might participate in all passes.

The most efficient seems to be setting the opacity in my tests. Another simple approach is to redraw the visuals that are affected.
using (DrawingContext dc = RenderOpen()) {} //Hide this visual
And then redraw when they become visible again.
Rendering a blank drawingcontext seems to be very quick. But if you have complicated visuals it could take time to rerender them when they become visible.

Why not simply remove the visual from the visual children list? When it needs to be visible you add it back.

Related

Speed of Thousands of Rectangles (with a Stroke!!!) in a WPF Canvas

this is my fist question here,
but I didn't find a real answer to my question.
I work on a visualization program and use C# and WPF.
I need to draw a treemap. I actually create Rectangle objects and then add them to the Canvas which works really nice and is very useful, since I also add event handlers to those rectangles (mouse click).
I have a problem with the performance though.
When I add the rectangles and set the Stroke property (SolidColorBrush) the whole process is slowing down extremely. Without those strokes set the speed is ok.
I already improved the performance a little by adding Rectangle objects to a newly created Canvas object and then adding this new Canvas object to the original Canvas object (so not all rectangles are direkt children of the original Canvas, which should help speeding things up).
So my question is how it is possible to add a Stroke to all those Rectangles without breaking the speed.
You can find a comparison of the 'strokeless' and 'stroked' treemap versions in the links.
Treemap without Strokes
Treemap with Strokes
Thank you very much for your help!
edit:
Ok, I have found a solution!
Sorry for writing here first, but I already searched for hours to find a solution but now I found out tht the problem was my used SolidColorBrush!
I have something like this:
public static SolidColorBrush TreeMapBorderBrush;
And I use this Brush to set the Stroke property of every Rectangle that should be added to the Canvas. But exactly that was the problem.
The code looked like this before:
rect.Stroke = Visualization_Helper.TreeMapBorderBrush;
I changed it to this now:
rect.Stroke = Visualization_Helper.TreeMapBorderBrush.Clone();
So I only work with a copy of that Brush on each Rectangle now, which speeds up the processing like there was no Stroke (like in my question)!
Just for your information! Weird!

How to improve Canvas rendering performance?

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.
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.
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.
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.
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.

Canvas - dynamic drawing and managing shapes

I'm trying to make an application which allows the user to draw shapes to the canvas. Once drawn, I would like for the user to be able to select, move, resize, basically manipulate the shapes in any which way.
I have done something similar in XNA and that was quite easy due to the fact that there was a draw loop. In Silverlight there is no such thing as far as I understand and I am having trouble figuring out how to manage the objects on the canvas. As in what is the best way to manage the canvas' children collection to ensure appropriate response of the UI to what the user does.
Most examples out there are pretty basic and do not go anywhere near this kind of thing. I would be grateful if somebody who has done this before could tell me how they approached the problem.
Thinking about it for a while I think I figured out how it works.
There is a draw loop for the canvas, which is the draw loop of the top level parent container which it lives on.
The difference with XNA I guess is that the collection of items to draw on the canvas doesn't need to be explicitly drawn, since the canvas takes care of drawing its children automatically.
So, what I need is some way to hold on to any object I add to the canvas' children... I can then update the objects drawing properties and the changes will be reflected in the canvas next time it refreshes.
I guess a dictionary of some sort in which to store the items I put in the list might be best...?
Not a finished answer yet, but I guess I understand half of it now.

Issues with rendering a large tiled map in WPF

What is the best way to manage a very large amount of images (10,000+) in WPF? This is for a 2d tile map editor similar to this : http://www.mapeditor.org/ .
At the moment i have a canvas with all tiles as an image and a list box which contains the different images to choose from. Each tile is added to the canvas as children and then stored in a list for later access. You paint into the canvas by setting the Source property of a tile to the one selected in the listbox. It works well with around 50x50 tile maps but anything above that causes loading delays, in general slow application.
Any suggestions on this? Would QT maybe be more suited instead of wpf?
Thanks in advance
Check out Implementing virtualized panel series of articles.
Virtualized panels are efficient, because:
Only the displayed elements (and a few extra around the borders to enable smooth scrolling) are in the memory (and rendered).
Elements are reused, instead of being repeatedly created and discarded - an old cell is simply filled with new content (supplied with new DataContext) and used in new location.
You might also try to use WPF's DataGrid for this, it supports virtualization out of the box and is essentially what are you trying to do.
WPF is certainly able to do this, if implemented properly (if you can do that in JavaScript, you can certainly do it in WPF as well).

WPF: Creating snappable grid lines with variable spacing

I'm currently creating a MSPaint-like WPF-application and struggling with the implementation of a snappable grid.
The painting of the grid is no problem with a VisualBrush and a Rectangle but the problem is that these lines are then purely for looks and can't be easily changed (for example highlighted when the snapping to a specific line triggered).
My other idea was to have a 2 Canvas solution where 1 Canvas is used for the elements and one Canvas (who is positioned above the other) contains all the grid lines. However I have the feeling that this would mean quite a performance hit.
Are there any other possible ways to implement this kind of functionality?
Efficiency considerations of a two-panel approach vs DrawingContext
I have good news for you: You are wrong about the significant performance hit. Your two-canvas idea is nearly optimal, even if you use individual objects for the grid lines. This is because WPF uses retained-mode rendering: When you create the canvas, everything on it is serialized into a compact structure at native level. This only changes when you change the grid lines in some way, such as changing the grid spacing. At all other times the performance will be indistinguishable from the very fastest possible managed-code methods.
A slight performance increase could be had by using DrawingContext as Nicholas describes.
A simpler and more efficient solution
Perhaps a better way then drawing individual lines on the grid canvas is to use two tiled visual brushes (one horizontal, one vertical) to draw all unhilighted lines, then use Rectangle(s) added in code-behind to hilight the line(s) you are snapping to.
The main advantage of this technique is that your grid can be effectively infinite, so there is no need to calculate the right number of grid lines to draw and then update this every time the window resizes or the zoom changes. You also only have three UIElements involved, plus one more for each grid line that is currently hilighted. It also seems cleaner to me than tracking collections of grid lines.
The reason you want to use two visual brushes is that drawing is more efficient: The brush drawing the vertical lines is stretched to a huge distance (eg double.MaxValue/2) in the vertical direction so the GPU gets only one drawing call per vertical line, the same for the horizontal. Doing a two-way tiling is much less efficient.
Adorner layer
Since you asked about alternatives, another possibility is to use Adorner and AdornerLayer with any of the solutions above rather than stacking your canvas using eg a Grid or containing Canvas. For a Paint-like application this is nice because the adorner layer can be above your graphic layer(s) yet the adorners can still attach to individual items that are being displayed.
You might consider drawing your grid using the DrawingContext inside of OnRender. Drawing this way does not introduce new UIElements into the visual tree, which helps to keep performance up. In some ways, it is similar to what you are currently doing with the VisualBrush, which also does not create new UI elements per copy.
However, since you will actually be individually drawing each line instead of copying the look of a single line, you'll be able to highlight the grid line(s) that participate in snapping without changing the colors of those that do not.
If you are going to go down this route, make sure to have a look into GuidelineSets for positioning your guide lines (more details here), since you'll probably want to have your guide lines snap to the device's pixels so that they draw sharply.

Resources