Monday, April 11, 2011

WinForms multi-threaded databinding scenario, best practice?

Hi,

I'm currently designing/reworking the databinding part of an application that makes heavy use of winforms databinding and updates coming from a background thread (once a second on > 100 records). Let's assume the application is a stock trading application, where a background thread monitors for data changes and putting them onto the data objects. These objects are stored in a bindinglist<> and implement INotifyPropertyChanged to propagate the changes via databinding to the winforms controls. Additionally the data objects are currently marshalling the changes via WinformsSynchronizationContext.Send to the UI thread. The user is able to enter some of the values in the UI, which means that some values can be changed from both sides. And the user values shouldn't be overritten by updates.

So there are several question coming to my mind:

  • Is there a general design-guildline how to do that (background updates in databinding)?
  • When and how to marshal on the UI thread?
  • What is the best way of the background thread to interact with binding/data objects?
  • Which classes/Interfaces should be used? (BindingSource, ...)
  • ...

I'm search for general proposals/design guidelines for such scenarios...

I'm thankful for all ideas, links, ...

tia Martin

thx for the ideas so far, but I'm know Control.Invoke/BeginInvoke,... I'm more interessted in best practice. As an additional info, the UI doesn't really know that there is a background thread, that updates the control, and as of my understanding in databinding scenarios the UI shouldn't know where the data is coming from... You can think of the background thread as something that pushes data to the UI, so I'm not sure if the backgroundworker is the option I'm searching for.

Edit: Another question came to my mind: Something you want to get some UI response during an operation in the data-/business object (e.g. setting the background during recalculations). Raising a propertychanged on a status property which is bound to the background isn't enough, as the control get's repainted after the calculation has finished? My idea would be to hook on the propertychanged event and call .update() on the control... Any other ideas about that?

From stackoverflow
  • There is an MSDN article specific on that topic. But be prepared to look at VB.NET. ;)

    Additionally maybe you could use System.ComponentModel.BackgroundWorker, instead of a generic second thread, since it nicely formalize the kind of interaction with the spawned background thread you are describing. The example given in the MSDN library is pretty decent, so go look at it for a hint on how to use it.

    Edit: Please note: No marshalling is required if you use the ProgressChanged event to communicate back to the UI thread. The background thread calls ReportProgress whenever it has the need to communicate with the UI. Since it is possible to attach any object to that event there is no reason to do manual marshalling. The progress is communicated via another async operation - so there is no need to worry about neither how fast the UI can handle the progress events nor if the background thread gets interruped by waiting for the event to finish.

    If you prove that the background thread is raising the progress changed event way too fast then you might want to look at Pull vs. Push models for UI updates an excellent article by Ayende.

    Ian Ringrose : System.ComponentModel.BackgroundWorker,may be part of the solution, however the hard problem is how to keep the data updates fast without looking out the UI thread. Doing the above without having every other line of code having to think about locking or cross thread calls.
    Martin Moser : If you look at the Backgroundworker there is not much difference to spawning a thread and marshal with WindowsFormsSynchronizationContext the BW does the same.
    Dun3 : See edit for response.
  • This is a problem that I solved in Update Controls. I bring this up not to suggest you rewrite your code, but to give you some source to look at for ideas.

    The technique that I used in WPF was to use Dispatcher.BeginInvoke to notify the foreground thread of a change. You can do the same thing in Winforms with Control.BeginInvoke. Unfortunately, you have to pass a reference to a Form object into your data object.

    Once you do, you can pass an Action into BeginInvoke that fires PropertyChanged. For example:

    _form.BeginInvoke(new Action(() => NotifyPropertyChanged(propertyName))) );
    

    You will need to lock the properties in your data object to make them thread-safe.

  • Yes all the books show threaded structures and invokes etc. Which is perfectly correct etc, but it can be a pain to code, and often hard to organise so you can make decent tests for it

    A UI only needs to be refreshed so many times a second, so performance is never an issue, and polling will work fine

    I like to use a object graph that is being continuously updated by a pool of background threads. They check for actual changes in data values and when they notice an actual change they update a version counter on the root of the object graph (or on each main item whatever makes more sense) and updates the values

    Then your foreground process can have a timer (same as UI thread by default) to fire once a second or so and check the version counter, and if it changes, locks it (to stop partial updates) and then refreshes the display

    This simple technique totally isolates the UI thread from the background threads

    Ian Ringrose : I have found that timers work well in the past.
  • This is a hard problem, as most “solutions” lead to lots of custom code, and lots of calls to BeginInvoke() or System.ComponentModel.BackgroundWorker (that is just a thin wrapper over BeginInvoke)

    Also I have found in the past that you soon wish to delay sending your INotifyPropertyChanged events until the data is stable. The code that handles one propriety-changed event, often need to read other proprieties. You also often has a control that needs to redraw it’s self whenever the state of one of many properties changes, and you don’t wish the control the redraw it’s self too often.

    Firstly each custom winforms control should read all data it needs to paint it’s self in the PropertyChanged event handler, so it does not need to lock any data objects when it was a WM_PAINT (OnPaint) message. The control should not immediately repaint it self when it gets new data, instead it should call Control.Invalidate(). Windows will combine the WM_PAINT messages into a few requests as possible and only send them when the UI thread has nothing else to do. This minimizes the number of redraws and the time the data objects are locked. (Standard controls mostly do this with data binding anyway)

    The data objects need to record what has changed as the changes are made, then once a set of changes has been completed, “kick” the UI thread into calling the “SendChangeEvents” method that then calls the PropertyChanged event handler (On the UI thread) for all properties that have changed. While the SendChangeEvents() method is running, the data objects must be locked to stop the background thread(s) from updating them.

    The UI thread can be “kicked” with a call to “BeginInvoke” whenever a set of update have bean read from the database. Often it is better to have the UI thread poll using a timer, as windows only sends the WM_TIMER message when the UI message queue is empty, hence leading to the UI feeling more responsive.

    Also consider not using data binding at all, and having the UI ask each data object “what has change” each time the timer fires. Databinding always looks nice, but can quickly become “part of the problem, rather then part of the solution”.

    As locking/unlock of the data-objects is a pain and may not allow the updates to be read from the database fast enough, you may wish to passed the UI thread a copy (virtual) of the data objects. Having the data object be Persistent/ Immutable that any changes to the data object returns a new data object rather then changing the current data object can enable this.

    Persistent objects sound very slow, but need not be, see this and that for some pointer. Also look at this and that on Stack Overflow.

    Also have a look at retlang - Message based concurrency in .NET it's message batching may be useful.

0 comments:

Post a Comment