To explain this problem, I put everything I need in a small sample application, which I hope explains the problem. I really tried to push as few lines as possible, but in my actual application these different actors do not know each other, and they should not either. So, a simple answer like βtake a variable a few lines above and invoke Invoke on itβ will not work.
So, let's start with the code, and then a little more explanation. First, there is a simple class that implements INotifyPropertyChanged:
public class MyData : INotifyPropertyChanged { private string _MyText; public MyData() { _MyText = "Initial"; } public string MyText { get { return _MyText; } set { _MyText = value; PropertyChanged(this, new PropertyChangedEventArgs("MyText")); } } public event PropertyChangedEventHandler PropertyChanged; }
So nothing special. And here is an example of code that can simply be placed in any empty console application project:
static void Main(string[] args) { // Initialize the data and bindingSource var myData = new MyData(); var bindingSource = new BindingSource(); bindingSource.DataSource = myData; // Initialize the form and the controls of it ... var form = new Form(); // ... the TextBox including data bind to it var textBox = new TextBox(); textBox.DataBindings.Add("Text", bindingSource, "MyText"); textBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged; textBox.Dock = DockStyle.Top; form.Controls.Add(textBox); // ... the button and what happens on a click var button = new Button(); button.Text = "Click me"; button.Dock = DockStyle.Top; form.Controls.Add(button); button.Click += (_, __) => { // Create another thread that does something with the data object var worker = new BackgroundWorker(); worker.RunWorkerCompleted += (___, ____) => button.Enabled = true; worker.DoWork += (___, _____) => { for (int i = 0; i < 10; i++) { // This leads to a cross-thread exception // but all i'm doing is simply act on a property in // my data and i can't see here that any gui is involved. myData.MyText = "Try " + i; } }; button.Enabled = false; worker.RunWorkerAsync(); }; form.ShowDialog(); }
If you run this code, you will get a cross-thread exception by trying to change the MyText property. This happens by calling MyData on a PropertyChanged object that will be caught by BindindSource . Then, according to Binding , try updating the Text TextBox property. This clearly leads to an exception.
My biggest problem here is that the MyData object MyData not know anything about gui (because it is a simple data object). Also, the workflow knows nothing about gui. It just acts on a bunch of data objects and manipulates them.
IMHO, I think that BindingSource should check in which thread the receiving object lives, and make an appropriate Invoke() to get the value of them. Unfortunately, this is not built into it (or am I mistaken?), So my question is:
How to resolve this cross-threading exception if the data object or workflow does not know anything about the binding source that listens for their events to insert data into gui.