Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WinForms multi-threaded databinding scenario, best practice?

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, ...)
  • ...

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.

Sometimes 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?

like image 709
Martin Moser Avatar asked Mar 02 '09 15:03

Martin Moser


3 Answers

Create a new UserControl, add your control and format it (maybe dock = fill) and add a property. now configure the property to invoke the usercontrol and update your element, each time you change the property form any thread you want!

thats my solution:

    private long value;
    public long Value
    {
        get { return this.value; }
        set
        {
            this.value = value;

            UpdateTextBox();
        }
    }

    private delegate void Delegate();
    private void UpdateTextBox()
    {
        if (this.InvokeRequired)
        {
            this.Invoke(new Delegate(UpdateTextBox), new object[] {});
        }
        else
        {
            textBox1.Text = this.value.ToString();
        }
    }

on my form i bind my view

viewTx.DataBindings.Add(new Binding("Value", ptx.CounterTX, "ReturnValue"));
like image 124
Nicki Avatar answered Oct 19 '22 11:10

Nicki


This is a hard problem since most “solutions” lead to lots of custom code and lots of calls to BeginInvoke() or System.ComponentModel.BackgroundWorker (which itself is just a thin wrapper over BeginInvoke).

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

Firstly, each custom WinForms control should read all data it needs to paint itself 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 itself when it gets new data; instead, it should call Control.Invalidate(). Windows will combine the WM_PAINT messages into as 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 changed” 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 pass the UI thread a (virtual) copy of the data objects. Having the data object be persistent/immutable so that any changes to the data object return a new data object rather than changing the current data object can enable this.

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

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

(For WPF, I would have a View-Model that sets in the UI thread that was then updated in ‘batches’ from the multi-threaded model by the background thread. However, WPF is a lot better at combining data binding events then WinForms.)

like image 41
Ian Ringrose Avatar answered Oct 19 '22 09:10

Ian Ringrose


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

like image 31
TFD Avatar answered Oct 19 '22 10:10

TFD