Avoid end-to-end operations

I want to access a list box in my GUI from my desktop. Without any further modification trying to make a mistake like this, this error

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

      

The option I've seen to avoid this is to use Invoke

with the following syntax, but is this valid .Net 4

(or higher)?

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

      

For a clearer picture, this is how I want to access the list items from my background.

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 }
    }
   }
}

      

+3


source to share


2 answers


Invoke

in an event handler Backgroundworker's

DoWork

is deprecated by Microsoft. Use Backgroundworker's

ProgressChanged

or instead RunWorkerCompleted

.

See MSDN Help here .

See first note for this text:

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



If you have indeed posted more code, a more complete answer may be provided.

Edit 1: More code

Since the OP updated the post to contain more code, I did 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 */ }
    }

      

+6


source


This is "acceptable" but not good practice right now. BackgroundWorker is deprecated and Tasks with async / await should be used instead.

Here's an example of how I could implement a method to do something on a different thread, maintain the UI, and update the UI after execution is complete (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 information.

0


source







All Articles