INotifyPropertyChanged with threads

I have

BindingList<T> 

which is associated with datagridview. One property in my class takes a long time to compute, so I performed the action. After the calculation, I raise the OnPropertyChanged () event to notify the grid that the value is ready.

At least this theory. But since the OnPropertyChanged method is called from the differend stream, I get some crossed out exceptions in the OnRowPrePaint method of the grid.

Can someone tell me how should I throw out the OnPropertyChanged event in the main thread? I cannot use Form.Invoke, since the MyClass class does not know that it works in a Winforms application.

 public class MyClass : INotifyPropertyChanged { public int FastMember {get;set;} private int? slowMember; public SlowMember { get { if (slowMember.HasValue) return slowMember.Value; else { Thread t = new Thread(getSlowMember); t.Start(); return -1; } } } private void getSlowMember() { Thread.Sleep(1000); slowMember = 5; OnPropertyChanged("SlowMember"); } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChangingEventHandler eh = PropertyChanging; if (eh != null) { eh(this, e); } } } 
+6
c # thread-safety winforms datagridview
source share
4 answers

By design, a control can only be updated by the thread in which it was created. That is why you get exceptions.

Consider using BackgroundWorker and only upgrade it after a long operation has completed by signing up the RunWorkerCompleted event handler.

+8
source share

People sometimes forget that the event handler is MultiCastDelegate and therefore has all the information about each subscriber that we need to handle this situation gracefully, without the need for an excessive penalty for Invoke + Synchronization performance. I use code like this:

 using System.ComponentModel; // ... public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { var e = new PropertyChangedEventArgs(propertyName); foreach (EventHandler h in handler.GetInvocationList()) { var synch = h.Target as ISynchronizeInvoke; if (synch != null && synch.InvokeRequired) synch.Invoke(h, new object[] { this, e }); else h(this, e); } } } 

What he does is simple, but I remember that I almost tore my brain, and then tried to find the best way to do it.

First, it "captures" the event handler in the local property to avoid any race conditions.

If the handler is not null (one subscriber exists in leasing), it prepares the event arguments and then iterates through the call list of this multicast delegate.

The call list has a target property that is an event subscriber. If this subscriber implements ISynchronizeInvoke (all user interface controls implement it), we then check its InvokeRequired property, and it is true, we just call it by passing the delegate and parameters. A call to this method synchronizes the call in the user interface thread.

Otherwise, we simply invoke the event handler delegate directly.

+6
source share

Here is something I wrote a while ago; it should work quite well, but pay attention to the cost of many updates ...

 using System.ComponentModel; using System.Threading; public class ThreadedBindingList<T> : BindingList<T> { SynchronizationContext ctx = SynchronizationContext.Current; protected override void OnAddingNew(AddingNewEventArgs e) { if (ctx == null) { BaseAddingNew(e); } else { ctx.Send(delegate { BaseAddingNew(e); }, null); } } protected override void OnListChanged(ListChangedEventArgs e) { if (ctx == null) { BaseListChanged(e); } else { ctx.Send(delegate { BaseListChanged(e); }, null); } } void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); } } 
+2
source share

Consideration 1:
Take a look at the UIThreadMarshal class and its use in this article:
UI flow routing in model layer
You can change the class from static to instance and inject it into your object. Thus, your object will not know about the Form class. He will only know about the UIThreadMarshal class.

Consideration 2:
I don't think returning -1 from your property is a good idea. It looks bad to me.

Consideration 3:
Perhaps your class should not use antoher thread. Perhaps these are consumer classes that must decide how to name their property: directly or in a separate stream. In this case, you may need to provide an additional property, such as IsSlowMemberInitialized.

+1
source share

All Articles