How can I update a list box using an asynchronous call?

I have developed a Windows C forms application, I just want to update the items in the Listbox in the main form, spinning another thread without blocking the GUI form. Since threads cannot access forms like listbox, I thought about using delegates. The following code below shows how I used a delegate to accomplish this task, but it blocks the GUI form. so I just want to convert it to an asynchronous delegate that updates the list without blocking the GUI form

Delegate declaration

 delegate void monitoringServiceDel();

      

delegate invocation

new monitoringServiceDel(monitoringService).BeginInvoke(null, null);

      

delegate method implementation

private void monitoringService()
{
        this.listEvents.Invoke(new MethodInvoker(delegate()
        {
            int i = 0 ;
            while (i<50)
            {

                listEvents.Items.Add("count :" + count++);
                Thread.Sleep(1000);
                i ++;
            }

        }));


}

      

+3


source to share


4 answers


For Win Forms, you need to use the Invoke method :

Executes the specified delegate on the thread that owns the window's bottom handle

Main scenario:

Something line by line:



var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork;
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl;
bw.RunWorkerAsync();

      

This should get you moving in the right direction.

Edit: working sample

public List<string> MyList { get; set; }

private void button1_Click( object sender, EventArgs e )
{
    MyList = new List<string>();

    var bw = new BackgroundWorker();
    bw.DoWork += ( o, args ) => MethodToDoWork();
    bw.RunWorkerCompleted += ( o, args ) => MethodToUpdateControl();
    bw.RunWorkerAsync();
}

private void MethodToDoWork()
{
    for( int i = 0; i < 10; i++ )
    {
        MyList.Add( string.Format( "item {0}", i ) );
        System.Threading.Thread.Sleep( 100 );
    }
}

private void MethodToUpdateControl()
{
    // since the BackgroundWorker is designed to use
    // the form UI thread on the RunWorkerCompleted
    // event, you should just be able to add the items
    // to the list box:
    listBox1.Items.AddRange( MyList.ToArray() );

    // the above should not block the UI, if it does
    // due to some other code, then use the ListBox's
    // Invoke method:
    // listBox1.Invoke( new Action( () => listBox1.Items.AddRange( MyList.ToArray() ) ) );
}

      

+7


source


If you are modifying a UI element then you are going to block the UI thread. If items end up in packages or require processing between adding each one, you might consider doing the processing behind the scenes (via backgroundworker or Task ). But if you are just taking data and populating a list, then you need to use the UI thread.



+2


source


The simplest solution would be to use the control BackgroundWorker

in combination with the two Panels

. The idea is to have one panel in the foreground Visible

when the form loads and have a ImageBox

simple loading gif inside it that plays. ListBox

will be inside another pane that will not be visible by default and will be right behind the first pane.

Once the form is loaded, fire up BackgroundWorker

yours and execute any data received or update data that you need to complete and once the task is complete set the data inside your ListBox and just bring the bar ListBox

and make it visible.

This way you will get a semi-sync loading of yours ListBox

, but not updated after each item is added. You can use this technique at any time, not just when loading the form.

Here's some sample code:

namespace AsyncForm
{
    public partial class Form1 : Form
    {

        private List<String> collectionItems = new List<String>();

        public Form1()
        {
            InitializeComponent();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 20; i++)
            {
                ((List<String>)e.Argument).Add("Something " + i);
                System.Threading.Thread.Sleep(200);
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            listBox1.Items.AddRange(collectionItems.ToArray());
            listBox1.Visible = true;
            pictureBox1.Visible = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync(collectionItems);
        }
    }
}

      

+1


source


You should separate the UI refresh function from the time consuming process.

To handle the logic of the user interface.

    private void UpdateUI(string item) 
    {
        if (Thread.CurrentThread.IsBackground) 
        {
            listEvents.Dispatcher.Invoke(new Action(() => //dispatch to UI Thread
            {
                listEvents.Items.Add(item);
            }));
        }
        else
        {
            listEvents.Items.Add(item);
        }
    }

      

Executing an asynchronous process using TaskParallel

    private void Dowork()
    {
        Task task = Task.Factory.StartNew(() =>
        {
            int i = 0;
            while (i < 10)
            {
                Thread.Sleep(1000);
                UpdateUI(i.ToString());
                i++;
            }
        });
    }

      

0


source







All Articles