Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF C# - Editing a listbox from another thread

I realize what I'm doing is probably pretty silly, but I'm in the middle of learning WPF and would like to know how to do this.

I have a window with a listbox on it. The listbox is being used to deliver status messages about the program while it's running. For example "Server started" "New connection at IP #" etc. I wanted this to be constantly updating in the background, so I spawned a new thread for handling updating this, but when I made the call to add an item I get the error message "The calling thread cannot access this object because a different thread owns it."

Any idea how I can update the listbox from another thread? Or in the background, etc.

like image 928
cost Avatar asked Nov 16 '10 04:11

cost


1 Answers


UPDATE

If you are using C# 5 and .NET 4.5 or above you can avoid getting on another thread in the first place using async and await, e.g.:

private async Task<string> SimLongRunningProcessAsync()
{
    await Task.Delay(2000);
    return "Success";
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    button.Content = "Running...";
    var result = await SimLongRunningProcessAsync();
    button.Content = result;
}

Easy:

Dispatcher.BeginInvoke(new Action(delegate() 
  {
     myListBox.Items.Add("new item"));
  }));

If you are in code-behind. Otherwise you can access the Dispatcher (which is on every UIElement) using:

Application.Current.MainWindow.Dispatcher.BeginInvoke(...

Ok thats a lot in one line let me go over it:

When you want to update a UI control you, as the message says, have to do it from the UI thread. There is built in way to pass a delegate (a method) to the UI thread: the Dispatcher. Once you have the Dispatcher you can either Invoke() of BeginInvoke() passing a delegate to be run on the UI thread. The only difference is Invoke() will only return once the delegate has been run (i.e. in your case the ListBox's new item has been added) whereas BeginInvoke() will return immediately so your other thread you are calling from can continue (the Dispatcher will run your delegate soon as it can which will probably be straight away anyway).

I passed an anonymous delegate above:

delegate() {myListBox.Items.Add("new item");}

The bit between the {} is the method block. This is called anonymous because only one is created and it doesnt have a name (usually you can do this using a lambda expression but in this case C# cannot resolve the BeginInvoke() method to call). Or I could have instantiated a delegate:

Action myDelegate = new Action(UpdateListMethod);

void UpdateListMethod() 
{
  myListBox.Items.Add("new item");
}

Then passed that:

Dispatcher.Invoke(myDelegate);

I also used the Action class which is a built in delegate but you could have created your own - you can read up more about delegates on MSDN as this is going a bit off topic..

like image 183
markmnl Avatar answered Nov 02 '22 15:11

markmnl