This post demonstrates a technique for binding a Silverlight DataGrid to dynamic data, the structure of which is not known at compile-time …

Update: I have extended this technique to add include change notification so that the DataGrid can be made editable. Read all about it in part two.

With Silverlight, binding data to a DataGrid is a very straightforward process. With an XML datasource, you can simply parse your data via Linq to SQL into an anonymous type then bind the resulting collection to the grid. Just a few simple lines of code. But, what if the data you want to bind to your grid is dynamic? That is, at compile time you do not know how many columns are required, or what their contents are. This is a common problem that Silverlight users have faced again, and again, and again and again!

The most obvious solution to this problem is to create a dictionary for each row of your DataGrid, and bind your columns to your values via the dictionary’s string indexer:

Binding="{Binding Path=[Name]}”

However, unfortunately the PropertyPath syntax used for binding does not understand indexers, making it impossible to bind to a dictionary.

Vladimir Bodurov presents an ingenious solution to this problem by dynamically generating a new Type based on the values present within the dictionary, i.e. If the dictionary contains the keys “Name” and “Age”, a Type will be generated that has properties of Name and Age. Crazy stuff! Here I would like to show an alternative method that does not use intermediate language or other black magic!

We will start with a simple example of a class which can be used to store ‘dynamic’ data for rendering in our DataGrid:

However, as mentioned earlier, we cannot create a binding path that accesses our Rows indexer, so just how do we bind the columns to our data?

The classic .NET solution to this problem would be to create a custom property descriptor. When databinding, the properties of an object are not accessed directly, rather they are accessed via their associated property descriptor. A custom property descriptor can be supplied for our Row class, via the ICustomTypeDescriptor interface for example, that exposes properties which when accessed invoke our indexer. This is how the .NET DataRowView exposes its properties. Unfortunately there is one small snag here … Silverlight does not include the required interfaces to create custom properties.

A simple workaround to this problem is to use a binding that binds each row directly to each Row item rather than a specific property of the item, then use a value converter to access the indexer and extract the required value. Here is an example:

In the above XAML, the binding expression does not have a Path specified, therefore, the binding source is our Row instance rather than a property of the Row. The RowIndexConverter value converter simply uses the supplied ConverterParameter for each column to access the Row indexer and extract the correct value. This works quite nicely, and we are able to see our data within the grid:

However, there is one problem here, if you click on the column headers the grid does not sort the data. The columns of a DataGrid have CanUserSort and SortMemberPath properties, however setting these will not help because the DataGrid will expect the bound object to have a property with the given name which of course it does not! To solve this problem (without resorting to dynamically generated types), we need to delve a little deeper into the way in which the DataGrid binds to the data.

The DataGrid control uses this interface to access the indicated functionality in the data source assigned to its ItemsSource property. If the ItemsSource implements IList, but does not implement ICollectionView, the DataGrid wraps the ItemsSource in an internal ICollectionView implementation.

The ICollectionView interface is responsible for filtering, sorting and grouping of the data bound to the DataGrid. WPF also has the ICollectionView interface, you can read a good overview of its features on Marlon Grech’s blog. However, whereas WPF wraps the DataContext itself in a view which is shared across multiple controls, it would appear that Silverlight restricts its usage to the DataGrid. This causes problems if, for example, you want to synchronize the current item between controls (However, Laurent Bugnion has a novel solution for emulating this behaviour).

So, it is the ICollectionView which is responsible for sorting our data. The internal implementation of this interface which the DataGrid creates will expect our object to expose the bound properties, which explains why it does not work. However, if we supply our own ICollectionView interface, we can take control of sorting and implement it ourselves, accessing our ‘property’ values via the Row’s string indexer.

The ICollectionView interface has a lot of methods, events and properties, fortunately I was able to find a suitable implementation of this interface on Manish Dalal’s blog. He had implemented this interface, on a class which extends ObservableCollection, in order to bind a DataGrid to a collection where the data from the server is being paged, hence sorting must be done server side. This gives us pretty much everything we need here, a collection class which is able to manage sorting itself. The only change required is to the ICollectiomView.Refresh() method which is responsible for refreshing the view after the SortDescriptions have changed.

When the user clicks on a grid view header, it modifies the bounds ICollectionView.SortDescription to reflect this change in state. The implementation of ICollectiomView from Manish’s blog invokes the Refresh method whenever the ICollectionView.SortDescription collection changes. In the above implementation, we use the OrderBy and ThenBy LINQ extension methods to order the data. Interestingly, OrderBy is a method on IEnumerable, whereas ThenBy is a method on the IOrderedEnumerable, hence the funny looking logic involving ‘firstSort’. Once the sorting has been performed, the underlying collection is re-ordered to match. The only subtle part is that we use a boolean field, _suppressCollectionChanged to suppress the numerous CollectionChanged events that would be fired by ObservableCollection during this process. Finally we raise NotifyCollectionChanged, resulting in the DataGrid updating to reflect the sort order.

Putting it all together, we simply populate our SortableCollectionView and bind this to the DataGrid, modifying the XAML to explicitly inform the grid that it can sort and which property each column sorts on (usually this is inferred from the Binding Path):

I am also a Technical Evangelist at Scott Logic, a provider of bespoke financial software and consultancy for the retail and investment banking, stockbroking, asset management and hedge fund communities.

Is it possible to display data values say from a database query or table without having to hard code any column names? The ideal solution for me would allow the developer to display data based on various database tables or views dynamically at runtime say based on what treeview item a user clicks and not having to hard code any database schema at design time.

One aspect of silverlight I am wrestling with right now is that if you want to access data from a SQL Server database on the web server side you have to use web services, which of course involves creating a design time EDMX file or generating LINQ to SQL classes. The problem is that our database schema changes quite frequently, so that means I would have to keep manually re-updating the web services along with the EDMX and/or LINQ to SQL as well. What I would really like to do would be to just connect to the SQL Server database using ADO.NET to populate the various datagrids without having to deal with web services. Please note that I am pretty new to Silverlight, so perhaps I am missing something obvious. TIA.