WPF performance when updating elements - c#

I am currently developing a GUI for an Ising model (german wikipedia because only the picture on the right really matters) which should consist of approx 200x200 spin elements. I implemented this in the following way:
<UniformGrid Name="grid" .... />
and added a rectangle for every spin in the code behind which I update if the value of the spin changes. This somehow was very slow and I changed it so it uses Binding
<ItemsControl Name="IsingLattice" ItemsSource="{Binding Spins}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Name="grid" ...
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill={Binding Color} ...
but this is again - very slow. I tried to debug and improve it for 3 days now but no success so far.
Now the question is: Is my approach wrong? What should I use instead if so? If not - how could I improve the performance?
If it's relevant I will update this post with some details of the implementation of my model.
Edit: It should be possible to change single spins by interacting with the elements. This could be done with a transparent layer on top of the actual graphics though so maybe not that hard anyway.

You could write a single custom element (derived from FrameworkElement) that stores the spin data internally then renders the data in one pass by overriding the OnRender method:
public sealed class IsingModel : FrameworkElement
{
readonly bool[] _spinData = new bool[200 * 200];
protected override void OnRender(DrawingContext dc)
{
// use methods of DrawingContext to draw appropriate
// filled squares based on stored spin data
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
// work out which "cell" was clicked on and change
// appropriate spin state value in Boolean array
InvalidateVisual(); // force OnRender() call
}
}
This approach should be faster than having several thousand individual elements. How much faster I don't know.

ItemsControl is meant to repeat UI controls based on datasource. UI control must be customizable, responsive when layout changes, and interactive. Neither of this is your case.
You actualy want to render just a picture.
This - seems to be a Bitmap, so you should threat it as a Bitmap. Instead of ItemsControl use Image and instead of ItemsSource use WritableBitmap as a Source of Image.
When it comes to your original code, it could have couple of performance bottlenecks:
Generation of your classes you used as a ItemsSource can take some time
UniformGrid needs to measure size and calculate position of each element. This can take a while. With Canvas you could achieve better results
It can take some time to ItemsControl to generate 40 000 items from DataTemplate. You could create those rectangles manualy and add it to canvas in code behind
Binding have performance cost. If each of your item is databound, then your binding needs to be evaluated 40 000 times. Instead of binding you could set the properties manualy in code behind.
Using canvas and no binding I was able to render the grid in just 500miliseconds. However, using WritableBitmap or other "pixel based" approach you could display much larger grids.

A GUI, any kind of GUI technology, being it WPF or Windows Forms or anything else, is not meant to handle heavy graphics. It's meant to be easy to develop simple graphics.
If you need graphic power (like dynamically updating 40.000 cells) then you need a framework for graphics. Most likely, a gaming framework will do, pick one of your choice.
Alternatively, you could try to emulate it yourself by only binding to a single picture and drawing that picture yourself when a cell changes. Maybe that's enough, you will have to test that.

Have you considered using the BitmapCache to improve rendering speed?
My understanding is that this can significantly improve rendering speed when drawing complex controls, or when having many instances of the control on screen at the same time. You would want to enable the cache at the grid level, not on each individual spinner.

Related

Why do I have have to use UIElement.UpdateLayout?

We have a rather large WPF business application and I am working on a retool of an existing WPF FixedPage/FixedDocument report.
It's a somewhat busy ecosystem. We have a built-in forms generator, with lots of different controls you can put on (think like a mini built-in visual studio). All that works fine. You fill in the form on the screen, and then you can print out (to XPS) the identical copy to standard 8.5x11 paper.
In the code, we break out this report into vertical chunks. Say each chunk would be an inch or two tall on a printed piece of paper. This is how we handle pagination. If the next chunk is too tall for the page, we do a NewPage() and repeat. As I mentioned, this was working fine.
WPF has an enormous learning curve and I've been going back over old code and refactoring things and happily working with DataTemplates, strongly typed ViewModels, and generic ContentControls in order to reduce the size of our code. The on-screen forms generator still works, but the FixedDocument report has gotten weird.
Going back to those vertical slices, we print the user's forms to paper as individual Grid controls. Nothing fancy. Each grid (as I mentioned above) may be an inch or two high, containing any random mixture of checkboxes, radiobuttons, textblocks, and so on.
When the grids contained these stock (standard) MS WPF controls, I could do this all day long:
System.Windows.Controls.Grid g = .....
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
And get back proper sizes, i.e. 100 x 67.
Now, sometimes the grids have just one control - a header if you will (i.e. "This Month's Schedule). The only child control added to that grid is a ContentControl.
The ContentControl is simply bound to a ViewModel:
<ContentControl Content="{Binding}" />
There's then two DataTemplates in the resource dictionary that picks up this binding. Here, I'll show that:
<UserControl.Resources>
<w:MarginConverter x:Key="boilerMargin" />
<DataTemplate DataType="{x:Type render:BoilerViewModel}">
<render:RtfViewer
Width="{Binding Path=Width}"
TextRTF="{Binding Path=Rtf}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type render:Qst2NodeViewModel}">
<ContentControl Content="{Binding Path=BoilerVm}">
<ContentControl.Margin>
<MultiBinding Converter="{StaticResource boilerMargin}">
<Binding Path="NodeCaptionVm.Height" />
<Binding Path="NodeLeft" />
</MultiBinding>
</ContentControl.Margin>
</ContentControl>
</DataTemplate>
</UserControl.Resources>
The ContentControl will pick up that bottom-most datatemplate. That template will then in turn use the smaller one above.
The fancy converter just sets a margin. It may be fugly to read, but this all displays correctly on the screen within the parent usercontrol. It's all the right size and justification and all that.
On the printed report side (XPS), I have to create these controls in code and measure them to see if they'll fit on the current FixedPage. When I go to do this step: (on the grid containing this ContentControl)
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
I get back 0,0 size. Even though it should be like 730x27 for instance. Again, on the screen, hosted in a UserControl, this all works fine. Just trying to instantiate it and measure it purely in code fails. I've confirmed that the control is added to the grid, has its row and col set, has been added to the Children collection, etc...
If I prepend those two statements with an UpdateLayout call, like this, then it works:
g.UpdateLayout(); //this fixes it
g.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
g.Arrange(new Rect(g.DesiredSize));
I've been reading that UpdateLayout is expensive and to be avoided, and I'd rather not be calling this on each grid section before I add it to my FixedPage of the FixedDocument report. There could be dozens or even hundreds of iterations. And, again, if the Grid has regular WPF controls in it, without any ContentControls and fancy finding and looking up datatemplates, the measuring works fine without the UpdateLayout call.
Any advice? Thank you!
I just don't understand why it became necessary to start calling it once I started utilizing the Xaml engine. It almost feels like I'm being punished for using the advanced features.
Its complicated to explain that but let me try using plain words... In wpf everything works with dispatcher. Futhermore like you may already know dispatcher deals with tasks ordered by priority.
For example first a control is being initalized, then binding is being triggered, then values are updated, in the end all that is being measured.. etc etc
What you managed somehow is by setting all those contentcontrol inside contentcontrol stuff, you screwed up that order
Calling UpdateLayout basically forces dispatcher to finish its pending work in layout so you can work with clean layout afterwards
Screwing with dispatcher is quite common in wpf since some controls or values may be added later which ends in remeasuring things.
In your case you seem to be creating all at once in one method call without letting dispatcher take a breath. Therefore you need UpdateLayout method to normalize dispatchers queue.
I hope this helps you. You can also solve your issue by using Dispatcher.BeginInvoke.
UpdateLayout does not work in my case. I had to wait until dispatcher finishes processing of the layout tasks.
toPrint.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
toPrint.Arrange(new Rect(new Point(0, 0), toPrint.DesiredSize));
I found another article about this approach.

C#/WPF Improving performance of fill update of 1000s of controls simultaneously via datatriggers

So on the model side I have a 100x100 array of Cell objects. The objects have a single property, State, which is a bool. Cell implements INotifyPropertyChanged. I also have a CellCollection object, which holds the 100x100 array and has methods for modifying that array.
On the view side, I have a canvas containing 10,000 Rectangle controls, arranged in a 100x100 grid. Each of the elements of the 100x100 array is set as the DataContext of their corresponding Rectangle in the Canvas. A DataTrigger is defined for Rectangle controls in the XAML that binds on the State property of the Rectangle's DataContext. If the value of State is True, the background of the Rectangle is set to Black (via a Setter), and if it's False, the background is set to White.
Now I have a DispatcherTimer that runs at 100ms intervals that invokes a method on the CellCollection object. The method evaluates the state of the array, and changes the value of some of its elements' State property. As the value of the State property changes, the changes are reflected in the GUI as expected.
The only problem with this, is that it is terribly slow.
So the CellCollection method called by the dispatcher works in 2 parts: first it calculates what the new state of the cell array should be and stores the results in a 100x100 bool array. And second it iterates through the Cell array and updates the State property of its elements based on the values in the bool array. I printed some timestamps to the console, and it seem that calculating the bool array takes around 10 milliseconds, so I do not believe this is a problem with my algorithm being inefficient, but rather I'm speculating that the slowness is due to the cells all updating almost simultaneously (triggering property change events, and in turn triggering data triggers which change the Fill value of the corresponding Rectangle).
I'm new to C# and WPF, so I'm not entirely sure how to make this faster. I've been told that one can use the Dispatcher to greatly improve performance, but I'm unsure of how to do so. I'm also aware of a BackgroundWorker class, but am not entirely sure how to use it in this case.
Originally I was binding the State property of the Cell to the Fill property of the Rectangle and using a ValueConverter to convert from a bool to a color. Somebody suggested that DataTriggers might be faster, which is what I'm currently using, but I haven't really noticed any performance improvements. Someone else also suggested that I should use RectangleGeometry and Path objects instead of a Rectangle object as it might improve graphic performance, but am not sure if this is accurate, and haven't had much luck implementing this successfully.
Anyway, what can I do to improve my application's performance?
First, make sure that you only change cells that actually change value. If something stays the same, don't set the properties background field and don't signal NotifyChanged.
This will most likely not help. So you will need another approach. You didn't mention any input through this wpf elements. So I assume they are for display only. Updating 10K elements because it's easier to handle is well... easier to handle, but incredibly slow. What you really want is a single element showing a picture of 100 x 100 cells. Try to draw that picture into a bitmap in your code first and then make the bitmap appear on screen by using an Image control and binding to a Bitmap. While you draw, the Bitmap is the old one, once you are done producing the bitmpa in the background, you can set this new Bitmap.
You could handle the drawing of the bitmap in a background thread, but my guess is that it will be way to fast to make sense.

Plotting Pins on Map in WinRT

In my metro app using XAML and C# I need to plot some Pins on the Map control. I found several samples which all of them having plot the pins in the code behind file(add pin as children to the map control). Is it possible in another way? (similar to Windows Phone)
You can create a custom control that derives from ItemsControl. It will monitor changes to a backing ObservableCollection in your ViewModel. Then, you can just add/subtract pins from that.
Note: This is not necessarily a simple task. There is a reason there are companies creating complex controls for profit, and this is certainly a more complex one. It's not nearly impossible, but it will take some work to accomplish, much less successfully.
To get you started, it will most likely need:
Some kind of backing Map image
The ability to convert coordinates in the Pin to (x,y) pixel coordinates on the map image
You'll most likely need to override:
MeasureOverride : Find out how much space the map needs
ArrangeOverride : Find out where all of the current children go
OnDisconnectVisualChildren : What to do when you remove a pin
PrepareContainerForItemOverride : Creating an item container for items added/removed
You can then go on to add things like childtransitions (so they 'pop in').
Then, all you need to do is set up your VM, load your pins, and you're off to the races.
Good coding!

Invalidate own WPF control

I have a custom WPF control that I made a couple of days ago:
public class MapContext : FrameworkElement{
//....
protected override void OnRender(DrawingContext dc) {
// Draw the map
if (mapDrawing != null) dc.DrawDrawing(mapDrawing);
}
The mapDrawing drawing is updated in another thread where all the geometries to display are computed, the thread then updates the UI by calling InvalidateVisual():
Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, new Action(delegate { InvalidateVisual(); }));
On InvalidateVisual, MSDN documentation says:
Invalidates the rendering of the element, and forces a complete new layout pass. OnRender is called after the layout cycle is completed.
This is not the behaviour I want as the MapContext control layout did not change. Only the drawing inside has changed.
Question
Is there a proper way of forcing OnRender method to be called without doing a complete layout pass?
Thanks in advance
No, there is no way to force a re-render without a layout pass.
Since WPF uses a retained mode system, OnRender() does not work like the old WinAPI days. OnRender() simply stores a collection of drawing instructions, and WPF determines how and when to do the actual rendering.
If you need to change the look of your control independantly of sizing, I'd suggest you use something like a DrawingVisual, and using RenderOpen() to add your mapDrawing when you want it to change.
Maybe instead of using your own control, you can draw image with your map and set this image as source to standard control? Such operations like drawing are usually taking some time so its better to prepare image in background and then switch it with currently displayed.

High performance plot control in WPF

I am doing some work for which I need to develop a control, it should be a simple graph that shows several points and two edges.
My problem is that I need to show up to 16k points, with an update rate of 30 Hz. Has anyone done something similar?, and has any advice?.
For example whether to inherit from FrameworkElement or Control (ItemsControl in this case). If the control inherits from FrameworkElememt it may have a better performance drawing the points in the OnRender method but I would miss the Templating feature that comes from inheriting from Control.
Or does there exist another control that can do this out there?
Thanks in advance for your time.
I ended up using InteropBitmap, it is the fatest bitmap rendering class from WPF.
It allows you to map the image that you want to paint (in memory) and then reder it as a Image. This was perfect as i needed to plot points on the screen.
I got great performance (almost 50Hz for 20k points), i also use PLINQ to update the points in memory.
check this article for more details...
Try and read about ZoomableCanvas. I believe it can solve your problem. You can render all the points as small rectangles/ellipses inside the ZoomableCanvas.

Categories