Silverlight DataGrid – Populate Dynamic Columns from a Child Collection
Lets face it, any one building LOB (line of business) applications know that their users love Microsoft Excel. If the users had their way, they would do everything in Excel. Due to this known fact, when building Silverlight LOB applications, there is often a need to flatten out an object for editing in a grid. For example; you may have an object that has an n-level number of properties or attributes that aren’t known until runtime, but you want to edit the object in a single row in a grid. You don’t want to add a bunch of properties on your object like Prop1, Prop2, Prop3, etc.., just so you can bind it to your grid. You want to dynamically add columns to your grid and bind those columns to the correct object in the child collection at run time.
Well, this is much easier than you may think and I will show you how to accomplish this with just a few simple helper methods, and you can use any grid of your choice. For this example, I will be using the DataGrid that comes with the Silverlight Toolkit. Make sure you download and install it, because I am not including the System.Windows.Controls.Data assembly required for the DataGrid.
It will be located at c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.Data.dll
In my scenario I am building a staffing application and I have a “StaffMember” object that has a collection of “Period” objects as a child property. My objects look something like this:
Pretty simple! now, lets create our DataGrid that will show our data for editing.
Okay, now I don’t know how many Periods my StaffMember will have until I get the data back from the database at run time. For demonstration purposes, I just created a method on my StaffMember class that would create my objects by iterating through a loop.
Now, we need to set the datasource on the DataGrid. Since we are creating the columns at runtime make sure you set AutoGenerateColumns to false;
Next, lets take care of creating the easy columns first. I created a method that its’ sole purpose is to give me new DataGridTextColumns.
Using this method we can create our first two columns.
Your DataGrid should now look something like this.
Now we need to create our columns based off the Periods collection. To do this we will utilize a DataGridTemplateColumn. The first thing we need to do is create a method that will dynamically create a DataTemplate that the DataGridTemplateColumn will use as the CellTemplate.
What I am doing here is using a StringBuilder to create a DataTemplate, represented by XAML. Pay special attention to the TextBlock’s binding. I am using String.Format to create my binding string base off the index the of the object in the collection and the name of the property on the child object I want to bind to. Now, lets create our template that will be used for editing.
This method is very similar to the previous one we wrote, but notice the subtle difference; I am using a TextBox instead of a TextBlock, and the Mode is set to TwoWay. This will allow us to edit the values in the DataGrid. Now we need a method that will actually create the TemplateColumns.
Notice that we are setting the CellTemplate and CellEditTemplate by using the XamlReader to load our StringBuilder result and cast it as a legitimate DataTemplate the column can use. Now that we have the method that will create our TemplateColumns, lets go ahead and build our dynamic columns to the n-level. We do this by looping though the number of columns that need to be created and using our CreateTemplateColumn method to add the new columns to the DataGrid.
Now of course, in the real world you would not want to use the first index of the child collection to figure out how many columns to build. I would recommend some kind of definition object that will define what columns and how many columns to build.
That is it. You have now successfully satisfied your customer’s addiction to Excel. Well, at least a little bit.