Search code examples
c#wpfperformancewpf-controlsuielementcollection

Rapidly removing (and adding) many items to a Canvas in WPF


At a certain point in my WPF application, the UI thread will lock up for ~0 till ~500 ms (depending on the amount) once I remove and/or add items to a Canvas. Several performance tests pointed to Canvas.Children.Remove for the main cause, and .Add as well (.Remove is much more severe). I remove about 500 items at the same time, and add about 500 items at the same time as well. This about 10 times per second causes issues.

In the end, I've written a simple benchmarking application, code below.

    public MainWindow() {
        InitializeComponent();
        this.Loaded += (object sender, RoutedEventArgs e) => {
            List<Image> a = new List<Image>();
            Stopwatch s = new Stopwatch();
            s.Start();
            for (int i = 0; i < 7500; i++) {
                Image img = new Image();
                Canvas.SetLeft(img, i * 10);
                C.Children.Add(img);
                if (i % 10 == 1)
                    a.Add(img);
            }
            s.Stop();
            long add = s.ElapsedMilliseconds;
            s.Reset();
            s.Start();
            foreach (Image img in a) {
                C.Children.Remove(img);
            }
            s.Stop();
            MessageBox.Show(String.Format("Add: {0}, Remove: {1}.", add, s.ElapsedMilliseconds));
        };
    }

This gives the following result (around this, every time I run it):

Add: 174, Remove: 156.

Keep in mind only 750 items get removed, while 7500 get added.

The process is a bit heavy, but I don't want the UI (ScrollViewer mainly) to lock up while it's doing this. Another problem I face is that I can't "just move" this to another Thread, since I can't control the UI from that specific Thread.

How to improve this process? Or is there a way to dynamically add/remove items "over time" so it does not hang up?

Thanks,

~Tgys


Solution

  • What if you split this into new thread and dispatch the work into UI thread with every 20ms or so? This way you wont completely lock the UI up. So the user could do other things in meanwhile.

    Other things I know: Is there a AddRange method? Perhaps you could add all items at once. This way only one layout update would theoretically happen cutting the time into zero.

    Same for Remove. See Canvas.Children.RemoveRange(a);