The ASP.NET GridView is able to bind to several data sources. These include the new DataSource controls, but also any IListSource, IEnumerable or IDataSource implementing objects. This also includes the generic collections such as List. The List makes binding to objects really easy, as long as your objects have properties, not just fields. Yes, all pretty convenient.
Whenever you want to support more complex binding scenarios (also with the Windows Forms DataGridView), you’ll find that you’ll need a special interface IBindingList. This interface is used in a lot of situations, such as for supplying design-time databinding support. Implementing this interface is not trivial. Most of the time you would like your List to have this interface. Fortunately, there is the BindingList class that does this all for you, so you can bind to a list of objects again, without having to implement IBindingList yourself.
Let’s take a look at an example.Take the following BindingList of Person objects and bind that to a GridView.BindingList<Person> persons = newBindingList<Person>();
When you bind to non-DataSource controls you need to handle the sorting event that the GridView fires yourself. If you don’t an Exception is thrown.
protected void personsGridView_Sorting(object sender, GridViewSortEventArgs e)
protected void Page_PreRender(object sender, EventArgs e)
personsGridView.DataSource = persons;
And voila, databinding on a GridView with a collection of objects. Switch on that sorting and click away at the headers. Shock… No sorting. How come?
The BindingList defers the sorting functionality from IBindingList.ApplySort to a protected virtual method called ApplySortCore. This method is implemented like so:
protected virtual void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
throw new NotSupportedException();
That explains a lot. The key is to derive from BindingList and override the ApplySortCore method and fill it with your own sorting logic. When I Googled the web I found only a few implementations of this and they were using .NET 1.1 logic. The tricky part is getting the PropertyDescriptor for the property to sort on. I have included my own version of a SortableBindingList that is able to sort on a given string that corresponds to a property name.
It works pretty well, but you’ll notice from the sample that the GridView is now refusing to coorporate fully. It does do the sorting, but will not remember the SortDirection and SortExpression properties when binding to anything other than a DataSource control. Both of these properties are stored as part of ControlState. Diving in with Reflector, you can see that GridView checks the IsBoundUsingDataSourceID property and does a lot more if this evaluates to true.privatevoid HandleSort(string sortExpression, SortDirection sortDirection)
bool flag1 = base.IsBoundUsingDataSourceID;
GridViewSortEventArgs args1 =
this.SortExpressionInternal = args1.SortExpression;
this.SortDirectionInternal = args1.SortDirection;
So, the implementation of HandleSort fires the OnSorting event, but then only stores the SortExpression and SortDirection when it is bound to a DataSource. How stupid.
Hold on for a little, while I figure out the most elegant way to solve this. Right now I would probably top my dirtiest hack ever, and go in with reflection and set the private properties SortExpressionInternal and SortDirectionInternal anyway.