Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF performance when updating elements

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.

like image 906
oerpli Avatar asked Dec 24 '22 23:12

oerpli


2 Answers

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.

like image 158
Steven Rands Avatar answered Dec 27 '22 20:12

Steven Rands


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:

  1. Generation of your classes you used as a ItemsSource can take some time
  2. UniformGrid needs to measure size and calculate position of each element. This can take a while. With Canvas you could achieve better results
  3. 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
  4. 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.

like image 29
Liero Avatar answered Dec 27 '22 19:12

Liero