Winforms data-binding to business objects in a multi-threaded scenario without InvokeRequired?

我只是一个虾纸丫 提交于 2019-12-04 05:23:46

问题


For example, I've got a business object Person:

class Person : INotifyPropertyChanged
{
    string Name { get; set; }
    DateTime DateOfBirth { get; set; }
}
// ^ abbreviated for better legibility; implementation would be trivial

And I've got some Winforms UI controls data-bound to an object of this class:

Person somePerson = ...;
nameTextBox.DataBindings.Add("Text", somePerson, "Name");
dobDatePicker.DataBindings.Add("Value", somePerson, "DateOfBirth");

Now I am making changes to somePerson and thanks to having INotifyPropertyChanged implemented, those changes are reflected in the UI. So far so good.

Now to my problem: If I make changes to somePerson in a worker thread (ie. not in the UI thread), e.g. because I'm loading data from a DB as a background operation, this might cause exceptions because the data binding attempts to update the controls, which is only allowed to happen on the UI thread.

This means that I need to call InvokeRequired on a UI element to see if I'm allowed to update a business object — which seems like a violation of the application's logical layering.

Ideally, I want to be able to modify my business objects without having to care whether it is data-bound to the UI or not. Is this somehow possible with Winforms data binding, or not?


回答1:


This doesn't answer your question, but I try as far as possible to avoid this problem.

Because of the existence of data binding, I make sure the only code that can update business objects is code that's running on the GUI thread.

For async operations, I adopt a pattern of:

  1. Send work to the background: Trigger an async operation -- say, a thread pool item -- from the GUI thread. Pass only plain data types into the thread pool, and receive only plain data types back. If business objects are used by the thread pool, make sure that these are either brand new ones (that haven't been data bound yet), or are clones of the originals (to avoid concurrent access)
  2. Do the work and obtain a result: Execute the async operation on a background thread. The background thread code will own the 'safe' objects given to it by the GUI; it won't have any interaction with the rest of the application.
  3. Unpack the result in the GUI: When the async operation is finished, it triggers an 'I am complete' event on the GUI thread. In response, the GUI thread can unpackage any results from the background operation and merge them back into the main business objects, safe in the knowledge that it won't be dealing with concurrent access.

I can recommend the System.Threading.Tasks.Task class as an abstraction around most of the above steps. It's new in .NET 4.0, but it's also available as a separate download for .NET 3.5 apps.

Steps (1) and (2) are what the Task class does without any customisation. You can achieve (3) by spawning a separate Task from inside the background thread and specifying TaskScheduler.FromCurrentSynchronizationContext as a scheduler. (You'll need to call FromCurrentSynchronizationContext from the GUI thread, in step (1), not from the background thread.)



来源:https://stackoverflow.com/questions/3381536/winforms-data-binding-to-business-objects-in-a-multi-threaded-scenario-without-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!