Microsoft Communities

Welcome to WindowsClient.net | Sign in | Join

Updating DataBound Controls asynchronously: AsyncBindingList<T>

Daniel Herling

Microsoft Corporation

April 2006

Summary:

This sample shows how to extend BindingList<T> so that it can be updated on a background thread.

Applies To:

Complex bound controls ( DataGridView, ListBox and ComboBox ) and simple bound controls.

Download the sample

Introduction:

A common task Client applications are very good at is presenting business data. The said business data can be stored in a configuration file, a SQL Server, or it can be made available on a web service. For instance, the applications title may be a string stored in SQL Server data table.

When the data changes, the UI changes as well so it reflects the new data. Re-using the previous example, if the string stored in the SQL Server data table changes then the applications title needs to change as well.

However, Windows Forms controls can only be updated on the UI thread. So, if SQL Server changes the string on a different thread than the UI thread updating the applications title on the same thread would be a problem. Luckily, Windows Forms controls have two methods which can be called from any thread in order to update the controls: BeginInvoke and Invoke. I wont get into the details for BeginInvoke and Invoke since they are well documented and understood. I will only give an example on how to use BeginInvoke:

// Compute the new title on a background thread...
string newTitle = "new title";
this.BeginInvoke((MethodInvoker) delegate {
    // ... and set the application's text on the UI thread
    this.Text = newTitle;
});

Note that after computing the new title the background thread calls Control.BeginInvoke to set the applications text and after that the background thread is freed to do other processing. The example above was a pretty straightforward example. Life gets more interesting when we add data binding to the application. Data binding is a mechanism that displays data from a data source into the client application. Also, data binding is used to update the data source from changes done through the applications UI. If data changes in a background thread that poses a problem to application developers because they need to essentially call BeginInvoke for each of this data change: see the following links.

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=ea5add7d-76e2-4e88-bbc9-2f348187be23

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=a75683d8-746a-4b70-9bdd-94b7c7b647d5

The solution is to data bind the UI controls via an AsyncBindingList<T>. This will solve the problem of making changes to the data source on threads other than the UI thread.

AsyncBindingList<T>: implementation details

The philosophy behind the AsyncBindingList<T> is to get the updates from the background thread and forward them to the UI thread using WindowsFormsSynchronizationContext. An update is one of the following operations: Add/AddNew/Insert/Delete/Clear/SetItem. For each such operation the AsyncBindingList<T> constructs a DataTransaction which it then sends to the WindowsFormsSynchronizationContext. The WindowsFormsSynchronizationContext then posts the DataTransaction to the UI thread. All the work is done on the UI thread and the background thread is free to immediately do other work.

With two exceptions, the background thread is free to do other work right away after updating the AsyncBindingList<T>; the background thread does not need to wait for the UI to update. The two methods that need to be executed synchronously are:

  1. AsyncBindingList<T>::get_Count.
    We need this to execute synchronously because otherwise we cant reliably use the Count property. In other words, if get_Count property does not execute synchronously then your app cant write the following logic
// remove the last item in the list
list.RemoveAt(list.Count - 1);
  1. AsyncBindignList<T>::AddNew().
    Since AddNew() has a return value, if your app needs the value of AddNew() then AddNew() needs to be executed synchronously. If your app does not need the return value from AddNew(), then AddNew() does not need to execute synchronously.

AsyncBindingList<T>: how to use

There are only two rules to using AsyncBindingList<T>:

  1. instantiate the AsyncBindingList<T> on the UI thread
  2. update the AsyncBindingList<T> ( via one of its Add/AddNew/Insert/Delete/Clear methods ) on the background thread.

This posting contains a sample app that does exactly that. The app updates the background thread when a System.Timers.Timer fires the elapsed event. Since the AsyncBindingList<T> is not multi threaded ( see Further improvements section) , the updates to AsyncBindingList are serialized in the sample.

Further improvements:

A limitation with the current implementation is that the AsyncBindingList<T> is not thread safe. The sample application attached updates the AsyncBindingList<T> only from one thread. The current implementation could be made thread safe by using a lock on its overridden methods.



Page view counter