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)
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.
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.
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?
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!?!?!
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.
jh72i:Surely many, many people there are in this situation?
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!
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.
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
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.
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.
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.
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