BindingSource – A Closer Look…


In my previous posts I have spoken about how databinding works in Whidbey and some simple scenarios of binding in Whidbey using BindingSource and BindingList<T>. Now, let’s take a closer look at the BindingSource.

 

BindingSource can be bound to any of the following:

          Object

          System.Type

          IEnumerable

          ICollection

          IList

          IListSource

          IBindingList

          IBindingListView

 

Please note that the ListChanged event on the BindingSource is the main event and will be referenced below. Binding to each of these objects mentioned above has a subtle difference. Let’s go through each of these:

 

Binding to an Object:

You can arbitrarily set the DataSource of BindingSource to any System.Object. When this is done, BindingSource internally creates a BindingList<T> of that Object type and adds the item to this list. You can then add objects to this list by calling BindingSource.Add(…).

 

e.g. Say you have a Customer object. You can do the following:

 

            BindingSource bs = new BindingSource();

            bs.DataSource = new Customer();

 

This will create a BindingList<Customer> internally with one object.

 

Binding to a System.Type:

BindingSource can be bound to a System.Type. When you do this, the BindingSource creates an empty BinidngList<T> of that Type. When you add new items to the BindingSource, it makes sure it is of the correct Type, and if so the item is added, else it will throw. The cool thing is that you can very easily bind any business object this way.

 

e.g. Say you want to bind to a list of Controls (TextBox, CheckBox, Button, etc). If you set the DataSource of the BindingSource to an instance of a control then it will not work correctly. Consider you do this:

 

            BindingSource bs = new BindingSource();

            bs.DataSource = new TextBox();  // BindingSource will create a BinidngList<TextBox> internally

            bs.Add(new Button()); // This will throw since the type is not TextBox

 

The correct way to do this would be to set the DataSource to type of control and then add items:

 

            BindingSource bs = new BindingSource();

            bs.DataSource = typeof(Control);            // BindingSource creates a BindingList<Control> internally

            bs.Add(new TextBox());

            bs.Add(new Button());

 

This will add the TextBox and Button to the list correctly.

 

Binding to IEnumerable and ICollection:

When DataSource of BidningSource is set to IEnumerable or ICollection the BindingSource creates a BindingList internally. What this means is that the BindingSource creates a copy of the list and uses it; so it is not aware of any changes you make to the original list.

 

Consider this:

 

            BindingSource bs = new BindingSource();

            bs.DataSource = MyCollection; // BindingSource create BindingList internally

            MyCollection.Add(newObject);

 

BindingSource does not fire ListChanged events in this case since it is unaware of any changes to the underlying list. Also, any changes to the items in the list itself (e.g. if this is a collection of Customers, and if you change say the Name property of a Customer object in the collection), BindingSource will be unaware of these changes since it only references it’s copy. The way to make the BindingSource “aware” of these changes is to set the DataSource of BindingSource again.

So, doing:

            bs.DataSource = MyCollection;

 

Will refresh the BindingList that the BindingSource uses internally.

 

Binding to IList and IListSource

When DataSource of BindingSource is set to IList or IListSource, BindingSource does not create its internal copy. It will always reference the underlying list for Data. However since IList and IListSource does not have ListChanged events, BindingSource will not be aware when items are added or removed from the list.

 

Typical case is Binding the BindingSource to ArrayList or List<T> (IList) and DataTable (IListSource). One thing to note though, is that DataTable’s IList.GetList() method returns a DataView which is an IBindingListView and not a pure IList. That’s why DataTable would behave differently from an IListSource that returns a pure IList instead. More information on IBindingListView below.

 

e.g.

            List<Customer> list = new List<Customer>();

            // Fill the list

 

            BindingSource bs = new BindingSource();

            bs.DataSource = list;

           

            DataGridView dgv = new DataGridView();

            dgv.DataSource = bs;

 

This will bind DGV to BindingSource which is bound to List<T>.

 

            list.Add(new Customer()); // Add a new Customer to the list

 

BindingSource is not aware of this change and so does not fire ListChanged event. Thus, DGV does not show a new row. If you refresh the DGV then it will get the data from the BindingSource which will get it from the underlying list and then the new row will appear.

 

To show the changes of items added and removed to the list, you can use BindingList<T> instead. Details below.

 

Binding to IBindingList and IBindingListView

When DataSource of BindingSource is set to IBindingList or IBindingListView, the BindingSource does not create internal copy of the list (just as it does in the case of IList and IListSource). It always references the underlying list for the Data. Also, since IBindingList and IBindingListView have the ListChanged event, the BindingSource hooks on to those events and so it is aware of any items added or removed from the list.

 

Consider this:

 

e.g.

 

            BindingList<Customer> bList = new BindingList<Customer>();

            // Fill the list

 

            BindingSource bs = new BindingSource();

            bs.DataSource = bList;

           

            DataGridView dgv = new DataGridView();

            dgv.DataSource = bs;

 

This will bind DGV to BindingSource which is bound to List<T>.

 

            bList.Add(new Customer()); // Add a new Customer to the list

 

BindingList<T> fires ListChanged event when a new item is added to the list. Since BindingSource hooks on to the ListChanged event of BindingList<T> it also is “aware” of these changes and so the BindingSource fires the ListChanged event. This will cause the new row to appear instantly in the DataGridView or make any controls listening to the BindingSource “aware” about this change.

 

Next I will talk about making changes to properties of an item in the list (e.g. changing the Name property of a Customer), rather than changing the list by adding and removing items, and we can take a closer look at how the BindingSource deals with these changes.


Comments (15)

  1. mihailik says:

    Excellent! I like this classes very much, I cannot wait Whidbey to be released 🙂

  2. Dinesh Chandnani says:

    Yes – these classes are really cool!

    One thing I should mention – though I mention DataTable in the section ‘Binding to IList and IListSource’, in reality, binding to a DataTable really behaves like Binding to IBindingListView. This is because a DataTable is an IListSource and it’s getList method returns a DataView which is a IBindingListViewView. DataView is aware of all the changes to the DataTable i.e. row added/deleted/value changed/etc. So if you bind to a DataTable through a DataView you will see all the changes made to the DataTable since ListChanged events will be fired.

    Thanks,

    -Dinesh

  3. Hi Dinesh,

    I’m wondering if this entry describes the features that will be available from the BindingSource, or that are available from the BindingSource.

    For example, I’m using the Feb CTP and, if I bind to IEnumerable from a DGV, via a BindingSource, I get a read-only DGV.

    Also, if I build a custom BindingList<T> that implements sorting, say, then set it as the BindingSource’s data source using the designer eg

    public class MyDataSource : CustomBindingList<Person> {}

    public class SortableBindingList<T> : BindingList<T> {

    //IBindingList sorting imp.

    }

    this.bobBindingSource.DataSource = typeof(WindowsApplication1.bob)

    When it comes to running the app, the sorting disappears, suggesting a new BindingList is created in place of the one I thought I’d specified.

    Is the end goal to basically take any type and turn it into a BindingList (unless alreaedy one) as you suggest?

    Cheers,

    Michael Weinhardt

  4. Hello Michael,

    These features are currently available in BindingSource.

    When you bind IEnumerable to a BindingSource whioch is bound to a DGV, BindingSource basically checks to see if the object in the list has a default public constructor. If it does, then it will allow new objects to be added in the list and the DGV will thus be read/write. However, if the object does not have a public default constructor then the BindingSource cannot add new objects to the list and so the DGV will end up being read-only. So, you will have to add a public default constructor to the object to have a read-write DGV.

    When you bind to a BindingSource by specifying a type, yes, the BindingSource creates a BindingList<T> internally of the type specified. So in the case above, BindingSource created BindingList<typeof(WindowsApplication1.bob)>. If you want to allow sorting you can set the datasource of BindingSource to be the SortableBindingList<T>.

    SortableBindingList<Customer> sbList = new SortableBindingList<Customer>();

    this.bindingSource.DataSource = sbList; // Now sorting will work fine

    So, if the DataSource is an IList, IListSource, IBindingList or IBindingListView, the BindingSource will not create an internal BindingList<T>.

    So end goal is to turn it into a BindingList if it is anything "lesser" than an IList, or to keep it as IList or IListSource if it is one of these already.

    Please let me know if you have any more questions.

    Thanks,

    -Dinesh Chandnani

  5. Hi Dinesh,
    <br>
    <br>I don’t see the IEnumerable behavior you are describing ie it remains read-only when bound to a DGV via a BindingSource eg:
    <br>
    <br> public class Person {
    <br> public string Name { … }
    <br> public int Age { … }
    <br> }
    <br> public class Persons : IEnumerable { … }
    <br> public partial class Form1 : Form {
    <br> public Form1() {
    <br> InitializeComponent();
    <br> Persons persons = new Persons();
    <br> BindingSource bs = new BindingSource(persons, null);
    <br> this.dataGridView1.DataSource = bs;
    <br> }
    <br> }
    <br>
    <br>Also, why is there are difference between me setting a BindingSource’s data source using the designer (BindingSource.DataSource = typeof(mySortableType); vs run-time (BindingSource.DataSource = new mySortabletype();. What I mean by that is, if I can set the BindingSource with a DataSource that provides sorting, from the designer, I expect the sorting to be retained when run. I don’t expect to have to write additional code to, essentially, reseed the BindingSource with the desired data source.
    <br>
    <br>BTW I actually love the BindingSource and the Data Binding improvements, but the design-time/run-time differences concern me. What am I missing?
    <br>
    <br>Thanks Dinesh!
    <br>Michael

  6. Bat's Blog says:

    Found a blog of one of the developers that works in the .NET Client

    Team which is responsible for the…

  7. dchandnani says:

    In case of IEnumerable, you will need to set AllowNew property of BindingSource to true. Please let me know of any questions.

    Regards,

    -Dinesh Chandnani

  8. SimonS says:

    I&amp;rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past…

  9. SimonS says:

    I&amp;rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past…

  10. I&amp;rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past…

  11. I&rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past

  12. rabidhamster1971 says:

    You might be interested in this alternative to data binding. It automatically responds to changes in your own data stuctures without requiring a bindingsource. You handle events on the controls that call your own business logic. When the underlying data changes, the controls fire the events again and update themselves automatically.

    http://updatecontrols.net

  13. Dan's Blog says:

    The current project I’m on is using Infragistics controls exclusively.&#160; Infragistics can do some