Recently, I've been testing with binding of type which implements INotifyPropertyChanged
and updating property from worker thread throwing cross thread issue.
Here is sample code :
public class MyViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler hanlder = PropertyChanged;
if(hanlder != null)
hanlder(this, new PropertyChangedEventArgs(propertyName));
}
}
above viewmodel has been bind with label text in windows form and updating label value from worker thread.
Updating label1 text from worker thread causes cross thread issue :
public partial class MainForm : Form
{
private MyViewModel _myViewModel = new MyViewModel();
public MainForm()
{
InitializeComponent();
Btn1.Click += Btn1_Click;
label1.DataBindings.Add("Text", _myViewModel, "Name");
}
private void Btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
_myViewModel.Name = "Updating from worker thread!"; It causes cross thread issue
});
}
}
So far, I can believe is that it is due to updating UI from worker thread. Is there any work around to make it thread safe without changing in button click method i.e. probably making thread safe in viewmodel.
Grab the UI's SynchronizationContext
(using SynchronizationContext.Current
from the UI thread when the app starts, for example), and store it in some static variable somewhere (i've called it uiSynchronizationContext
).
Then on your OnPropertyChanged
do something like:
protected virtual void OnPropertyChanged(string propertyName)
{
uiSynchronizationContext.Post(
o => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))
,null
);
}
Or you can use Send
instead of Post
if you want that operation synchronous (synchronous to the thread that started the send/post operation, it'll always be "synchronous" on the UI thread)
I particulary don't like doing direct databinding when multithreading (I prefer to poll with a timer on the UI thread from some graph object with the changes), but this should solve the problem.
I admiteddly haven't tested it