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
Anonymous
March 15, 2005
Excellent! I like this classes very much, I cannot wait Whidbey to be released :-)Anonymous
March 16, 2005
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,
-DineshAnonymous
March 24, 2005
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 WeinhardtAnonymous
March 24, 2005
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 ChandnaniAnonymous
March 27, 2005
The comment has been removedAnonymous
May 11, 2005
Found a blog of one of the developers that works in the .NET Client
Team which is responsible for the...Anonymous
August 14, 2005
In case of IEnumerable, you will need to set AllowNew property of BindingSource to true. Please let me know of any questions.
Regards,
-Dinesh ChandnaniAnonymous
February 08, 2006
I&rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past...Anonymous
February 08, 2006
I&rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past...Anonymous
May 22, 2006
I&rsquo;m building a little app at the moment to leanr some winforms stuff. I have done this in the past...Anonymous
June 03, 2007
I’m building a little app at the moment to leanr some winforms stuff. I have done this in the pastAnonymous
November 03, 2007
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.netAnonymous
March 31, 2008
The current project I'm on is using Infragistics controls exclusively.  Infragistics can do someAnonymous
May 01, 2008
PingBack from http://ramsonit.wordpress.com/2008/05/01/databinding-basics/Anonymous
June 09, 2009
PingBack from http://cellulitecreamsite.info/story.php?id=798