Reputation: 340
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.
Upvotes: 4
Views: 2855
Reputation: 27338
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:
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.
Upvotes: 4
Reputation: 16991
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.
Upvotes: 0
Reputation: 5421
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.
Upvotes: 6
Reputation: 77304
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.
Upvotes: 0