3

I want to access selections from a listbox on my GUI from my backgroundworker. Without any additional changes trying to do such throws this error

Cross-thread Operation Not Valid: Control '_ListBox1' accessed from a thread other than the thread it was created on

The option I saw to avoid this is to use Invoke by the following syntax, but is this .Net 4 (or higher) acceptable?

var selectedItems = (IList)this.Invoke(new Func<IList>(() => Listbox1.SelectedItems.Cast<object>().ToList()));

For a clearer picture this is how I want to access listbox items from my backgroundworker

namespace clown
{
  public partial class Form1 : Form1
  {
    public Form1()
    {
      ListBox1.Items.Add("Firefly");
      ListBox1.Items.Add("Hellfire");
    }

    private void btn1234_Click()
    {
      backgroundworker1.RunWorkerAsync();
    }

    private void backgroundworker1_DoWork(object sender, DoWorkEventArgs e)
    {
      //Long Running Process taking place here 
      //then we hit this
      if (ListBox1.SelectedItems.Contains("Firefly")) { //take this course }
      if (ListBox1.SelectedItems.Contains("Hellfire)) { //take this course }
    }
   }
}
Bob Goblin
  • 1,251
  • 3
  • 16
  • 33
  • What do you mean by .Net 4 acceptable? Do mean like standards wise or compile wise? – Josh L. Apr 27 '15 at 18:44
  • Have you tried it? What happened? – Alex Apr 27 '15 at 18:45
  • What does “to access selections” mean? And what API are you using? ASP.NET? WinForms? WPF? Metro (or whatever Microsoft is calling it this week)? – Dour High Arch Apr 27 '15 at 18:46
  • @Alex & Josh L.- it works, issue free. Just wasn't sure if it was "Acceptable" to do such. – Bob Goblin Apr 27 '15 at 18:57
  • possible duplicate of [Accessing UI Control from BackgroundWorker Thread - C#](http://stackoverflow.com/questions/4428817/accessing-ui-control-from-backgroundworker-thread-c-sharp) – John Apr 27 '15 at 19:00
  • @John Very likely a duplicate question but all the answers in your link are all based on the incorrect premise that `Invoke` should be used. – Rick Davin Apr 27 '15 at 21:05

2 Answers2

6

Invoke in the Backgroundworker's DoWork event handler is frowned upon by Microsoft. You should consider using the Backgroundworker's ProgressChanged or RunWorkerCompleted events instead.

See MSDN Help Here.

See the first Note for this text:

You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.

If you actually posted more code, a fuller answer may be given.

Edit 1: More Code

Since the OP updated his post to contain more code, I have done the same.

   private void btn1234_Click()
   {
        var items = ListBox1.SelectedItems.Cast<string>();
        backgroundWorker1.RunWorkerAsync(items);
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = (BackgroundWorker)sender;
        var items = (IEnumerable<string>)e.Argument;
        //Long Running Process taking place here  then we hit this
        if (items.Contains("Firefly")) { /* take this course */ }
        if (items.Contains("Hellfire")) { /* take this course */ }
    }
Rick Davin
  • 1,010
  • 8
  • 10
0

It is "acceptable", but it's not a good practice nowadays. BackgroundWorker is considered obsolete and one should use Tasks with async/await instead.

Here is an example of how I would implement a method to execute something on another thread, keep the UI responsive and update the UI after execution is finished (with WPF and the MVVM pattern):

public class ViewModel : INotifyPropertyChanged
{
    ...

    public string PathInfo { ... } // raises INotifyPropertyChanged.PropertyChanged event from the setter
    public RelayCommand ProcessPathsCommand { get; set; }

    public ViewModel()
    {
        ProcessPathsCommand = new RelayCommand(ProcessPaths);
    }

    public async void ProcessPaths()
    {
        // disable the command, which will lead to disabling a button bound to the command
        ProcessPathsCommand.IsEnabled = false;

        try
        {
            string result = null;
            // run processing on another thread
            await Task.Run(() =>
            {
                // emulate hard-work
                Thread.Sleep(5000);
                result = "Here are the results: bla bla bla";
            });

            // update the property on the view model, which will lead to updating a textblock bound to this property                
            // thanks to the "await" keyword, this line of code will be executed only when the task finishes
            // and it will be executed on UI thread
            PathInfo = result;
        }
        finally
        {
            ProcessPathsCommand.IsEnabled = true;
        }
    }

Feel free to let me know if you need more details.

nightcoder
  • 13,149
  • 16
  • 64
  • 72
  • I am using a winforms project would the same principle be applicable as with WPF? – Bob Goblin Apr 27 '15 at 21:02
  • Well, I think MVVM is not very popular among WinForms developers, because when MVVM became popular WinForms was already out for several years and so there are a lot of WinForms developers who are used to their old approach. But yes, the same principle can be applicable to WinForms. – nightcoder Apr 27 '15 at 22:00