Really stumped with binding & thread

Really stumped with binding & thread

Old forum URL: forums.lhotka.net/forums/t/4218.aspx


jh72i posted on Wednesday, January 23, 2008

Folks, completely stumped with a databinding issue since I moved to .net20.....

Story is that I bind UI controls to a properties of business objects all over my UI.  When the property is changed on the object it raises the PropertyChanged event, etc. but because when the object is changed on another thread I get a "Cross-thread operation not valid".
Now, the thing is that I do no processing of the control myself - its all down to databinding.  however, if I do hook a format event I can sucessfully change the forecolor of the control based on its value but the value itself never makes it to the control.  The other thing is that this all worked fine in 1.1.  The stack trace is:

System.InvalidOperationException was unhandled by user code
  Message="Cross-thread operation not valid: Control 'lblNetTotal' accessed from a thread other than the thread it was created on."
  Source="System"
  StackTrace:
       at System.ComponentModel.ReflectPropertyDescriptor.SetValue(Object component, Object value)
       at System.Windows.Forms.Binding.SetPropValue(Object value)
       at System.Windows.Forms.Binding.PushData(Boolean force)
       at System.Windows.Forms.BindingManagerBase.PushData(Boolean& success)
       at System.Windows.Forms.BindingManagerBase.PushData()
       at System.Windows.Forms.PropertyManager.OnCurrentChanged(EventArgs ea)
       at System.Windows.Forms.BindToObject.PropValueChanged(Object sender, EventArgs e)
       at System.EventHandler.Invoke(Object sender, EventArgs e)
       at System.ComponentModel.PropertyDescriptor.OnValueChanged(Object component, EventArgs e)
       at System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(Object component, EventArgs e)
       at System.ComponentModel.ReflectPropertyDescriptor.OnINotifyPropertyChanged(Object component, PropertyChangedEventArgs e)
       at System.ComponentModel.PropertyChangedEventHandler.Invoke(Object sender, PropertyChangedEventArgs e)
       at Csla.Core.BindableBase.OnPropertyChanged(String propertyName) in C:\_LRT\src\main\myCompany.Framework\CSLA\Csla\Core\BindableBase.cs:line 101
       at Csla.Core.BindableBase.OnUnknownPropertyChanged() in C:\_LRT\src\main\myCompany.Framework\CSLA\Csla\Core\BindableBase.cs:line 84
       at myCompany.Business.xxx.FundingPosition.ReCalculateTotals() in C:\_LRT\src\main\myCompany.Business.xxx\Funding\Position\FundingPosition.cs:line 424
       at myCompany.Business.xxx.FundingPosition._fundingPositionItems_ListChanged(Object sender, ListChangedEventArgs e) in C:\_LRT\src\main\myCompany.Business.xxx\Funding\Position\FundingPosition.cs:line 449
       at System.ComponentModel.ListChangedEventHandler.Invoke(Object sender, ListChangedEventArgs e)
       at CSLA.Core.BindableCollectionBase.OnListChanged(ListChangedEventArgs e) in C:\_LRT\src\main\myCompany.Framework\CSLA.Core.BindableBase\BindableCollectionBase.cs:line 102
       at myCompany.Business.BusinessCollectionBaseEx.OnListChanged(ListChangedEventArgs e) in C:\_LRT\src\main\myCompany.Business\BusinessCollectionBaseEx.cs:line 2118
       at myCompany.Business.xxx.FundingPositionItems.OnListChanged(ListChangedEventArgs e) in C:\_LRT\src\main\myCompany.Business.xxx\Funding\Position\FundingPositionItems.cs:line 75
       at CSLA.Core.BindableCollectionBase.OnSetComplete(Int32 index, Object oldValue, Object newValue) in C:\_LRT\src\main\myCompany.Framework\CSLA.Core.BindableBase\BindableCollectionBase.cs:line 144
       at myCompany.Business.BusinessCollectionBaseEx.OnSetComplete(Int32 index, Object oldValue, Object newValue) in C:\_LRT\src\main\myCompany.Business\BusinessCollectionBaseEx.cs:line 2450
       at System.Collections.CollectionBase.System.Collections.IList.set_Item(Int32 index, Object value)

From call: if (_nonSerializableHandlers != null) _nonSerializableHandlers.Invoke(this, new PropertyChangedEventArgs(propertyName));

After a few days of looking I am really struggling at this, pretty early hurdle in our translation to .Net20.

Any ideas at all would be welcome - i literally cannot find anyone experiencing the same problem.

ajj3085 replied on Wednesday, January 23, 2008

The problem is that this other thread is changing the BO, which then fires the property changed event.   It looks like databinding isn't checking if InvokeRequired is true.

BO's aren't thread safe by design anyway, so I'd not update the properties of the BO on a different thread, unless you've unbound the object first.

RockfordLhotka replied on Wednesday, January 23, 2008

So many people were doing things wrong with threading in .NET 1.x that Microsoft added this exception in .NET 2.0 to help you identify when you are doing something wrong. (rather than silently corrupting Windows Forms until your app crashed...) This is why you are seeing the change from 1.1 to 2.0 - they are helping you identify a problem you've always had.

Windows Forms is not threadsafe. Interacting with UI elements from any thread but the UI thread (typically your primary thread) is not supported.

Andy is right, if you are going to interact with your objects on a background thread, you must first unbind them so their change events aren't handled by data binding, because that'll cause the issue you are seeing.

jh72i replied on Wednesday, January 23, 2008

Thanks for those replies.  Now I truly am stumped - stunned even.  So you are telling me that it is completely wrong to have 1 thread bind to a datasource and have another thread update it...and expect databinding to reflect the changes via listchanged/etc. events in the first thread.  I'd have to somehow communicate to the 1st thread that it should unbind and later rebind!?!?!

Why does this work in 1.1 and why does it still work with grids, etc.?  But more importantly HOW do you suggest I safely have a number of threads interact with a single datasource while having changes reflected on screen.

Surely many, many people there are in this situation?

ajj3085 replied on Wednesday, January 23, 2008

jh72i:
So you are telling me that it is completely wrong to have 1 thread bind to a datasource and have another thread update it...and expect databinding to reflect the changes via listchanged/etc. events in the first thread.  I'd have to somehow communicate to the 1st thread that it should unbind and later rebind!?!?!


Yes, exactly.  Your code is wrong because it interacts with Windows Forms components on different threads, and Windows Forms is NOT thread safe.  As Rocky said, this was such a common mistake that was bringing applications down that Windows Forms components now have a thread affinity set, and any attempts to do anything other than InvokeRequired or Invoke will cause this exception.

jh72i:
Why does this work in 1.1 and why does it still work with grids, etc.?  But more importantly HOW do you suggest I safely have a number of threads interact with a single datasource while having changes reflected on screen.


It worked in 1.1 because Windows Forms didn't enforce thread affinity.  It seems like you'll need to pass messages between threads to the UI thread, which will actually update the BO. 

jh72i:
Surely many, many people there are in this situation?


Well.. yes and no.  Many people, including myself, hit the exception you did.  I corrected it by ensuring that I checked InvokeRequired before interacting with any Windows Forms controls.  In your case, this isn't possible, so you'll need to figure a way to syncronize things so that only the UI thread is updating the BO, or you'll need to unbind your BO before letting the threads loose.

I'm curious; have you tried your application on framework 1.1 on a dual core machine?  It wasn't until I was on a dual core machine that I started having my application crash.   I would try it on both a single and dual (or more) core workstation... just setup a Csla BO, data bind it, and let your threads loose (keep them updating the BO randomly).  I'm willing to bet that given enough time / threads, your application will crash in 1.1

jh72i replied on Wednesday, January 23, 2008

Thanks again for that. 
 
I think everything I am doing is fine - it is just that the events need to be raised on the UI thread rather that the thread that is making the changes.  Or delegate to the UI thread the work of applying the changes the other thread want to make.
 
Got to figure out how to do that is all :)  I could walk the multicastdelegates via the invocation list or something...i have to give this some serious thought now.  My apps aren't that complex really - they hook up to communication channels and listen out for changes to other instances of themselves - when a change occurs they get a notification (on the troublesome background thread/s) and upto now I actually update the contents of the object on that thread leaving the listchanged/etc. events to work their magic.  If i hook to a listchanged event from the UI i always check for invocation required.  As you point out sometimes I simply don't have any control over this. 
 
Again, thanks for the input - as i say i have to give this much thought.
 
 
 
 

RockfordLhotka replied on Wednesday, January 23, 2008

jh72i:

Why does this work in 1.1 and why does it still work with grids, etc.?  But more importantly HOW do you suggest I safely have a number of threads interact with a single datasource while having changes reflected on screen.

It didn't really work. Threading bugs are hard sometimes hard to see, and even harder to fix. There are numerous times when I've had threading bugs in WinForms and didn't know about them.

Users would sometimes see erratic results (data appearing/disappearing without reason), but users often don't complain - they figure it was something they did, or gremlins in the machine or whatever.

It is less common for WinForms to actually crash, though that can happen too. But more likely is just that the UI doesn't quite show the right data, and often a refresh will work because it is all about thread timing after all.

Get on a dual or quad core machine though, and the issues get worse really fast!

ajj3085 replied on Wednesday, January 23, 2008

RockfordLhotka:
Users would sometimes see erratic results (data appearing/disappearing without reason), but users often don't complain - they figure it was something they did, or gremlins in the machine or whatever.


Indeed.  I have users that usually tell me about problems, but there were a few issues that were causing them major headaches for weeks, and they never said a word to me.   The fix for the problem took me only a few minutes too..

rlarno replied on Wednesday, January 23, 2008

Not that this currently helps your problem, but in .NET 1.1; it did not work either, it might have appeared to work, but there might have been mysterious behaviours because of calling control properties from a different thread than the one the control was created on.

In .NET 2.0 they have added some checks in all of the controls to detect these incorrect cross-thread calls. (note: you can switch this off using Form.CheckForIllegalCrossThreadCalls but I do not recommend this)

I'm not sure if anyone has already solved something like this, however there would be 2 options (in theory):

1) Before updating the properties of the BO, use a reference to the form to query the InvokeRequired property, and if required use Invoke or BeginInvoke to call the method that will update the properties on the BO. Be aware that you will be switching threads and that the method responsible for updating the BO properties is thread-aware. (i.e. during this call, you should not allow any other thread to modify the BO or read state from the BO)

2) Find a way to intercept the databinding events and have these first check the Control.InvokeRequired property, then use Invoke or BeginInvoke to call the eventhandler.

Rudi

 

 

jh72i replied on Wednesday, January 23, 2008

Thanks Rudi.

1. surely this is an absolute no-no - having a business object hold a reference to a UI

2. it looks to me that the BindToObject class does not check this property.  Everywhere in our own UI code we check to before updating the UI but this this automatic binding it is out of our hands.

 

lawrencek replied on Thursday, January 24, 2008

Would something like this be workable it was from the following tread http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=65024&SiteID=1

You can use the AsyncOperationManager to marshall the call back to the UI thread.  The following short sample shows how to do this for a ListChanged handler, but the same applies for a PropertyChanged handler:

private             AsyncOperation      _oper = null;

Initialize it in your constructor:
 
_oper = AsyncOperationManager.CreateOperation(null);
 
Implement your PropertyChanged handler (sample below shows ListChanged - but this should give you the idea):
 
private void PostCallback(object state)
{
    /* Make sure we fire ListChanged on the UI thread */
    ListChangedEventArgs args = (state as ListChangedEventArgs);
    base.OnListChanged(args);
}

protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs e)
{
    if (!_suspend)
    {
        /* We need to marshall changes back to the UI  */
        _oper.Post(new SendOrPostCallback(PostCallback), e);
    }
}
 

I have also run into this issue and ended up just not threading b/c I really did not need it.  But I saved this for a back burner idea if I ever had the time.  I was thinking it could be added to the framework but I have not really tried it.


 

RockfordLhotka replied on Thursday, January 24, 2008

Conceptually yes.

 

The challenge is that you aren’t the one who handles the events – the bindingsource control handles the events.

 

It is also important to consider that the background thread could still be changing the object when the UI is trying to refresh its display due to the event (even if you somehow make the event switch to the UI thread). The result would be two threads mucking around in the same object at the same time – and that would lead to some “interesting” results (read: bad).

 

What you need to do is arrive at a model where only one thread is talking to a given object graph (not just one object – but the whole graph – a root and all its child objects) at a time.

 

The WPF dataprovider does this nicely. The dataprovider can be told to do an async data load. In this case it loads the object on a background thread, and notifies the UI once the entire load is complete. At that point “ownership” of the object transfers from that background thread to the UI thread so WPF can safely do its data binding.

 

You can do the same thing in Windows Forms using the backgroundworker component. Call the BW to load the object, then in the work completed event handler (which is on the UI thread) bind it to the UI. For updates, unbind the object from the UI, then call the BW to save the object, and rebind it in the work completed event handler.

 

But I sense that what people are trying to do are incremental load/save operations, where the UI does partial refreshes as the load/save occurs. That’s really hard! Especially in an n-tier scenario. The only real solution is to do a series of data portal calls, one for each chunk of data. Then merge the data from the resulting business objects into another set of objects that are owned by the UI.

 

In other words, the background threads never interact with the “real” objects. They have their own objects. The UI thread is then responsible for two things: merging data from the background threads into the “real” objects, and data binding to the real objects.

 

Rocky

 

 

From: lawrencek [mailto:cslanet@lhotka.net]
Sent: Thursday, January 24, 2008 8:35 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Really stumped with binding & thread

 

Would something like this be workable it was from the following tread http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=65024&SiteID=1

You can use the AsyncOperationManager to marshall the call back to the UI thread.  The following short sample shows how to do this for a ListChanged handler, but the same applies for a PropertyChanged handler:

private             AsyncOperation      _oper = null;

Initialize it in your constructor:
 
_oper = AsyncOperationManager.CreateOperation(null);
 
Implement your PropertyChanged handler (sample below shows ListChanged - but this should give you the idea):
 
private void PostCallback(object state)
{
    /* Make sure we fire ListChanged on the UI thread */
    ListChangedEventArgs args = (state as ListChangedEventArgs);
    base.OnListChanged(args);
}

protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs e)
{
    if (!_suspend)
    {
        /* We need to marshall changes back to the UI  */
        _oper.Post(new SendOrPostCallback(PostCallback), e);
    }
}
 

I have also run into this issue and ended up just not threading b/c I really did not need it.  But I saved this for a back burner idea if I ever had the time.  I was thinking it could be added to the framework but I have not really tried it.


 



jh72i replied on Friday, January 25, 2008

Again, thanks a million for those thoughts.  I still have to get around to dealing with this - i have other problems right now including one new post I'm about to make here!

jh72i replied on Friday, February 08, 2008

Folks (lawrencek in particular), I had a chance to try out that AsyncOperation technique last night and it worked a treat when called from the right location.  I only did preliminary tests and now I'm dragged away to something else but just wanted to say thanks a million for the steer on that.  My approach is to keep the same model but alway have the creating thread actually update the data source on behalf of any background threads.

Sorry I had asked the question and not tried out the suggestions but I am being pulled in so many directions these days I'm going crazy.

Copyright (c) Marimer LLC