Raising OnPropertyChanged when changing the property of an object from another thread

So, I have this object:

public class SomeObject: INotifyPropertyChanged { public decimal AlertLevel { get { return alertLevel; } set { if(alertLevel == value) return; alertLevel = value; OnPropertyChanged("AlertLevel"); } private void OnPropertyChanged(string propertyName) { if(PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 

Suppose I change this object to a thread that is NOT a GUI thread. How can I get this PropertyChanged object object in the same thread as the GUI if I don't have a link to any GUI component in this class?

+1
multithreading c # winforms
source share
5 answers

An even better idea without using WPF links:

 public class GUIThreadDispatcher { private static volatile GUIThreadDispatcher itsSingleton; private WeakReference itsDispatcher; private GUIThreadDispatcher() { } public static GUIThreadDispatcher Instance { get { if (itsSingleton == null) itsSingleton = new GUIThreadDispatcher(); return itsSingleton; } } public void Init(Control ctrl) { itsDispatcher = new WeakReference(ctrl); } public void Invoke(Action method) { ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: false)); } public void BeginInvoke(Action method) { ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: true)); } private void ExecuteAction(Action<Control> action) { if (itsDispatcher.IsAlive) { var ctrl = itsDispatcher.Target as Control; if (ctrl != null) { action(ctrl); } } } public static void DoInGuiThread(Control ctrl, Action action, bool forceBeginInvoke = false) { if (ctrl.InvokeRequired) { if (forceBeginInvoke) ctrl.BeginInvoke(action); else ctrl.Invoke(action); } else { action(); } } } } 

And do the initialization as follows:

  private void MainForm_Load(object sender, EventArgs e) { //setup the ability to use the GUI Thread when needed via a static reference GUIThreadDispatcher.Instance.Init(this); ... } 

And use like this:

 public class SomeObject: INotifyPropertyChanged { public decimal AlertLevel { get { return alertLevel; } set { if(alertLevel == value) return; alertLevel = value; OnPropertyChanged("AlertLevel"); } private void OnPropertyChanged(string propertyName) { GUIThreadDispatcher.Instance.BeginInvoke(() => { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }); }} 
+1
source share

Typically, the event subscriber should be responsible for marching calls to the user interface stream, if necessary.

But if the class in question is user-specific (aka view model), as soon as it is created in the user interface thread, you can capture the SynchronizationContext and use it to create an event, for example this:

 public class SomeObject : INotifyPropertyChanged { private SynchronizationContext syncContext; public SomeObject() { syncContext = SynchronizationContext.Current; } private decimal alertLevel; public decimal AlertLevel { get { return alertLevel; } set { if (alertLevel == value) return; alertLevel = value; OnPropertyChanged("AlertLevel"); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { if (syncContext != null) syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null); else handler(this, new PropertyChangedEventArgs(propertyName)); } } } 

Alternatively, you can pass the SynchronizationContext through the constructor.

Another way is to keep the object intact, but the data is bound to it through an intermediate synchronized binding source, as described here. Update elements in the BindingSource through a separate task .

+4
source share

for WPF - add the following links:

 PresentationFramework.dll WindowsBase.dll 

In the background thread - wrap code that needs access to the user interface in the dispatcher. Invoke ()

 using System.Windows; using System.Windows.Threading; ... //this is needed because Application.Current will be NULL for a WinForms application, since this is a WPF construct so you need this ugly hack if (System.Windows.Application.Current == null) new System.Windows.Application(); Application.Current.Dispatcher.BeginInvoke(new Action(() => { //Do Your magic here }), DispatcherPriority.Render); 

for winforms use

  Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(() => { //Do Your magic here })); 
+2
source share

This turned out to be a pure implementation (relatively). It was just necessary to include a link to WindowsBase.dll , which, it turns out, will be the WPF library, so ... I did not really like it, but this solution ...:

  public class GUIThreadDispatcher { private static volatile GUIThreadDispatcher itsSingleton; private Dispatcher itsDispatcher; private GUIThreadDispatcher() { } public static GUIThreadDispatcher Instance { get { if (itsSingleton == null) itsSingleton = new GUIThreadDispatcher(); return itsSingleton; } } public void Init() { itsDispatcher = Dispatcher.CurrentDispatcher; } public object Invoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) { return itsDispatcher.Invoke(method, priority, args); } public DispatcherOperation BeginInvoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) { return itsDispatcher.BeginInvoke(method, priority, args); } 

Then initialize it as follows:

 static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { GUIThreadDispatcher.Instance.Init(); //setup the ability to use the GUI Thread when needed via a static reference Application.Run(new MainForm()); } } 

And then use it like this:

 public class SomeObject: INotifyPropertyChanged { public decimal AlertLevel { get { return alertLevel; } set { if(alertLevel == value) return; alertLevel = value; OnPropertyChanged("AlertLevel"); } private void OnPropertyChanged(string propertyName) { GUIThreadDispatcher.Instance.BeginInvoke(() => { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }); }} 
0
source share

An even better answer was found without the need to use WeakReference to control WPF form and NO links based on https://lostechies.com/gabrielschenker/2009/01/23/synchronizing-calls-to-the-ui-in-a-multi-threaded -application / and Ivan answer above:

  public class GUIThreadDispatcher { private static volatile GUIThreadDispatcher itsSingleton; private SynchronizationContext itsSyncContext; private GUIThreadDispatcher() {} /// <summary> /// This needs to be called on the GUI Thread somewhere /// </summary> public void Init() { itsSyncContext = AsyncOperationManager.SynchronizationContext; } public static GUIThreadDispatcher Instance { get { if (itsSingleton == null) itsSingleton = new GUIThreadDispatcher(); return itsSingleton; } } public void Invoke(Action method) { itsSyncContext.Send((state) => { method(); }, null); } public void BeginInvoke(Action method) { itsSyncContext.Post((state) => { method(); }, null); } } 

}

And do the initialization as follows:

  private void MainForm_Load(object sender, EventArgs e) { //setup the ability to use the GUI Thread when needed via a static reference GUIThreadDispatcher.Instance.Init(); ... } 

And use like this:

 public class SomeObject: INotifyPropertyChanged { public decimal AlertLevel { get { return alertLevel; } set { if(alertLevel == value) return; alertLevel = value; OnPropertyChanged("AlertLevel"); } private void OnPropertyChanged(string propertyName) { GUIThreadDispatcher.Instance.BeginInvoke(() => { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }); }} 
0
source share

All Articles