jay_t55
jay_t55

Reputation: 11652

UI freezes while items are added to ListView Control

I need to update the UI while I am loading things, and I've read and been told that the BackgroundWorker is the right "tool" for the job in a Windows Forms desktop app.

So now I'm wondering why my UI freezes while the ListView Control updates as each items are added to it.

private void Form1_Shown(object sender, EventArgs e)
{
    if (library.Songs != null)
    {
        loader.Enabled = true;
        worker.RunWorkerAsync();
    }
}

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    int songCount = 0;
    foreach (Song song in library.Songs)
    {
        songs.Add(song);
        worker.ReportProgress(songCount++);
    }
}

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    list.Items.Add(songs[e.ProgressPercentage].Artist.Name);
    list.Update(); // This line is not the culprit. Removing it has no affect on the freezing.
    loader.Enabled = false;
}

private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Hooray.
}

Why is my UI freezing up and how do I stop it? I thought the whole point of using the BackgroundWorker was so that the bloody UI doesn't freeze-up while we're doing stuff?!

Update:

I have a theory that this is happening because there are many files being loaded and thus there are too many calls to ReportProgress(song) happening and so the UI updates are being left behind in the queue waiting for the Progress calls to finish.

So, if that's the case, how can I split it all up so that it processes maybe a certain percentage of songs at a time?

Update:

Form1.cs:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using Microsoft.Xna.Framework.Media;

    namespace Play
    {
        public partial class Form1 : Form
        {
            public MediaLibrary library = new MediaLibrary();
            public static List<Song> songs = new List<Song>();

            public Form1()
            {
                InitializeComponent();
            }

            public void TestPlay()
            {
                list.BeginUpdate();

                var itemsToAdd = library.Songs
                            .Select(x => new ListViewItem(x.Artist.Name))
                            .ToArray();

                list.Items.AddRange(itemsToAdd);

                list.EndUpdate();
            }

            private void Form1_Shown(object sender, EventArgs e)
            {
                TestPlay();
            }
        }
}

Upvotes: 1

Views: 2579

Answers (1)

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73452

Your BackgroundWorker usage doesn't gains you much(in this context). It is just adds Song to the collection and asks UI thread to add the song to ListView.

I suggest you to use ListView.BeginUpdate and EndUpdate methods and populate the list in UI thread itself. You can completely throw away that BackgroundWorker which adds no value.

Otherwise create and array of items you need to add, then call ListViewItems.AddRange which is optimized for adding multiple elements at once.

var itemsToAdd = library.Songs
                        .Select(x => new ListViewItem(x.Artist.Name))
                        .ToArray();

listView.Items.AddRange(itemsToAdd);

Upvotes: 4

Related Questions