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 }
}
}
}
source to share
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 */ }
}
source to share
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.
source to share