How to bind DataGrid to a list of Hashtable objects
(or any IEnumerable of IDictionary)
Contents
Introduction
Are the traditional .NET languages, C# and Visual Basic, truly dynamic languages? Well, we all know the answer to that question: They are not. Ok, but if they can be used in the same way that we normally use dynamic languages then, perhaps, in a sense they are dynamic. So can we use those languages in some situations as if they were dynamic? My hope is that, after reading this article, you will agree that the answer to that question is a qualified “Yes”.
I believe that the best way to illustrate this is to demonstrate how it can solve a common programming problem. The DataGrid and many of the controls, developed by Microsoft are designed in a way so to expect a list of typed objects as a data source. There are many good reasons for that; but, if we assume for a moment that there might be also a benefit of binding a DataGrid to a list of Hashtables without having to change the controls, wouldn’t that be an example of the dynamic capabilities of .NET languages? If we can turn a list of key value pairs into a class with corresponding properties, then that would certainly be an example of crossing the barrier between dynamic data and static types: We have to be able to do that at run time, of course.
Why would you need to perform such a task? Well, you may need to do it if the number and type of the columns in your DataGrid is something that you can’t predict at design time. This would be the case if, for example, you require that the columns in the grid will display items previously chosen by the user. There are several alternative programming strategies you could use to handle this, and the approach I’m about to describe is among them. I will discuss the other ways later in this article.
How to use it
You would expect that the way to pass dynamic data to the control should be very similar to a traditional dynamic language. Adding a key to Hashtable or IDictionary of (string, object) is just like adding a dynamic property to a dynamic object. In a dynamic language you would use something like this:
1 2 3 |
var myObject = new Object(); myObject["ID"] = 1; myObject["Name"] = "Name"; |
In a .NET language you can do likewise, by using IDictionary. So our code, could look like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public IEnumerable<IDictionary> GenerateData() { for(var i = 0; i < 10; i++) { // or you could use new Hashtable(); var dict = new Dictionary<string, object>(); dict["ID"] = Guid.NewGuid(); dict["Name"] = "Name_" + i; dict["Index"] = i; dict["IsEven"] = (i % 2 == 0); yield return dict; } } |
or in Visual Basic (without the yield return statement)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Public Function GenerateData() As IEnumerable(Of IDictionary) Dim list As New List(Of IDictionary)() For i As Integer = 0 To 9 ' or you could use new Hashtable() Dim dict As IDictionary = New Dictionary(Of String, Object)() dict("ID") = Guid.NewGuid() dict("Name") = "Name_" & i.ToString() dict("Index") = i dict("IsEven") = (i Mod 2 = 0) list.Add(dict) Next Return list End Function |
If we were allowed to bind IDictionary to a DataGrid we could just use this code:If we were allowed to bind IDictionary to a DataGrid we could just use this code:
1 2 3 4 |
public void BindGid(){ MyGrid.DataSource = GenerateData(); MyGrid.DataBind(); } |
But you cannot bind a list of Hashtables or Dictionaries to DataGrid. If you attempt this, you will actually bind the properties of the collection object and not the key because value pairs are treated as object properties.
There is just one more thing we have to do in order to make that work. We have to define an extension method ToDataSource() of the IEnumerable of IDictionary. The method will transform the list of dictionaries into a list of typed objects with each key turned into object property of the corresponding type. So our binding method will have to be modified just as:
1 2 3 4 |
public void BindGid(){ MyGrid.DataSource = GenerateData().ToDataSource(); MyGrid.DataBind(); } |
There is no such method defined by the .NET framework for IEnumerable of IDictionary so we have to define it, and much of this article describes how that works. You can also see the attached source of that extension method for both C# and Visual Basic.
Here we see one of the amazing aspects of .NET 3.5: we have the means to define a method for an interface as an extension method. This is also one dynamic feature in the framework because we don’t have to modify the definition of IEnumerable of T – something we would not be able to do anyway because this is as interface and yet we can make that method a part of the IEnumerable of IDictionary, by a simple inclusion of the extension method class into the current code. One more using statement and your interface is enriched with a new method. I have to stress here that overusing this feature may result in an abundance of unneeded methods but I think we can safely assume that anytime you have IEnumerable of IDictionary you can expect that it may be designed for transformation into a collection of typed objects.
The extension method will use the first IDictionary entry as a template for a newly created typed object. If the next IDictionary contains more properties then they will be ignored, if it contains fewer then the properties will be assigned with the default values. Each key of IDictionary must be alphanumeric and start with character – because it will be transformed into an object property. If it is not alphanumeric then an exception will be thrown. If the keys of IDictionary are not strings they will be transformed to strings by calling ToString() method. If you end up with a key collision because of different dictionary key objects resulting in the same string then an exception will be thrown.
It all may look like a lot of rules, but if you always use strings as keys for the IDictionary as you would in a dynamic language, and make sure that all IDictionary entries have the same keys with the same types, it will just work.
If you want column headers in the grid even if your collection contains no data, you would have to define columns of the grid and set the AutoGenerateColumns property to false.
Despite all the reflection, the code works fine in a middle trust environment of a typical shared hosting. You could also use the same code not only for traditional .NET applications but for Silverlight projects as well. In fact I came to the idea of using this extension method while working on a Silverlight application. In Silverlight 2.0 you have no Hashtables, so there you would use Dictionary of string and object. I find it quite exciting that, within the limited range of Silverlight framework, you can use so powerful data transformation techniques based on reflection. You don’t need to change anything to make it work in Silverlight. Just use the same code.
Static vs. Dynamic – system architecture concerns
I am sure some developers would be convinced that we shouldn’t use the technique I’ve described, because there is a reason for a DataGrid not to accept dynamic data. The argument, in a nutshell, is that the dynamic data does not enforce the data constraints and therefore opens a potential risk of errors.
This isn’t the place for the endless discussion of the merits of Static vs. Dynamic languages, but I cannot pass this topic without even mentioning it. I agree that in general we have to try to restrict the data to the constraints it should follow. This is especially true when we talk about data operations within the business layer, where the application business logic resides. However in the user interface layer, we can be a little bit more liberal if that will make us more agile and if it will help to implement UI changes easier, quicker and with less code. I think that the area of user interface data binding is a perfect place for a more dynamic approach.
It has to be said as well, that if you want to add a data verification code just between the method generating IEnumerable of IDictionary and the call to ToDataSource extension method you are free to do so. There is nothing that prevents you from doing that.
I personally believe that both dynamic and static code have their place in the architecture of one enterprise system. The business layer should be more static and the user interface can be more dynamic. But whatever is your opinion on this topic I believe you will agree with me that having one additional tool in your arsenal will not be a bad idea. And the approach I describe here is just another tool to pass dynamic data into a UI control that expects a collection of statically typed objects.
Alternative ways to pass dynamic data to a DataGrid
Internally the grid will check for the first item in the collection if it is ICustomTypeDescriptor. If it is not, then the properties of the object will be used for column binding. If it is, then the TypeDescriptor.GetProperties static method will transform that object into a collection of PropertyDescriptors. So defining your collection, as ICustomTypeDescriptor would be the classical way of passing dynamic data into a .NET control. This is because many of the .NET controls will check for the type to see if it is ICustomTypeDescriptor.
Here is a reference to Windows Forms code send to me by Lionel WindowsApplicationDataBinding.cs
The code here achieves the same goal as the code I propose; the transformation of IDictionary into a collection that can be bound, but it is based on PropertyDescriptors and ICustomTypeDescriptor interface. I find it to be a very impressive code and would certainly advise you to check it. It has some significant advantages – it does not use reflection and it does not have the restriction that the property has to start with letter.
But despite all the advantages this approach also has some drawbacks. Even though the Windows Forms DataGridView works fine with it, web forms DataGrid could not auto generate columns based on that code, so there you’d have to define the columns by yourself. But the biggest problem is that the code would not work in Silverlight because there is no PropertyDescriptor object defined by the Silverlight framework.
Another way to apply dynamic data to a DataGrid would be to generate DataSet out of Hashtable or XML or anything dynamic. If you choose this way you would have to do some more work to generate the DataSet.
You could also populate DataGrid with many other techniques that are not direct data binding but I will not discuss them, as they are not pure techniques of data binding – passing a collection of data and attaching the data elements to the UI.
All those methods should be considered as valid choices with their advantages and disadvantages.
The reason that I prefer the dynamic solution I’ve described is that it will work without any changes for Windows Forms, Web Forms and Silverlight. It can also be used for a wider range of goals beyond data binding because the objects generated are valid .NET types. The method I propose offers very clean and simple interface – a single extension method transforms “dynamic” object into typed one.
The disadvantage of this idea is in the fact that using reflection will have some performance implications – each binding will result in a separate new dynamic assembly. For a web project where you have thousands of users viewing the grid simultaneously you would rather choose the solution proposed by Lionel, but for a single Windows Forms user you should not expect to notice any performance drawbacks and the same is true for a Silverlight project. As a matter of fact Silverlight is probably the best candidate for that solution because the more traditional ways are not available there and yet this is a single client executing on the client machine and not on the server.
If solving this task is all you are interested in, then you can get the Visual Basic DataSourceCreator.vb or C# DataSourceCreator.cs source, where the ToDataSource extension method is defined and just use it in your code the way it was described above, but if you want to understand how it actually works, please read the next part of this article.
What if you use .NET 2.0 and not 3.5?
I chose to develop this idea using C# 3.0 feature of extension methods as I find this to be a very elegant way to extend the functionality of common interfaces very similar to the idea of extending a dynamic object by simply adding a new method.
However if you use .NET 2.0 you could still use my code. You would only have to change method definition
1 |
public static IEnumerable ToDataSource(this IEnumerable<IDictionary> list) |
by removing this keyword to be as
1 |
public static IEnumerable ToDataSource(IEnumerable<IDictionary> list) |
or in Visual Basic, change
1 2 |
<Extension()> _ Public Function ToDataSource(ByVal list As IEnumerable(Of IDictionary)) As IEnumerable |
To remove the attribute Extension and have the method just as
1 |
Public Function ToDataSource(ByVal list As IEnumerable(Of IDictionary)) As IEnumerable |
Then in your code simply use it as a static method:
1 2 3 4 |
public void BindGid(){ MyGrid.DataSource = DataSourceCreator.ToDataSource(GenerateData()); MyGrid.DataBind(); } |
How does it work?
Here is what happens inside the extension method. First of all I generate dynamic assembly and I call it “TempAssembly” plus the hash code of the IEnumerable collection. I then define the type of the object will be public class.
After that, I get the first IDictionary in the IEnumerable to be used as a template for the newly generated statically typed object. First I check with a regular expression that each key is alphanumeric and starts with letter so it can be transformed into property name. This is the regular expression I have used:
1 2 3 |
Regex PropertNameRegex = new Regex(@"^[A-Za-z]+[A-Za-z0-9_]*$", RegexOptions.Singleline); |
If the key does not follow this constraint, then I throw an application exception. Then for each key I generate a property. In .NET on a lower level getter property is transformed to get_<PropertyName>() method and setter property is transformed to set_<PropertyName>(value) method. So I generate both setter and getter and the private field lying underneath. The private field starts as usual with an underscore character. I get the type for the properties and private field from the current value in the IDictionary. If the value is NULL then the property is of type object.
Here is how the code generating properties looks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); PropertyBuilder propertyBuilder = typeBuilder.DefineProperty( propertyName, PropertyAttributes.HasDefault, propertyType, null); MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); ILGenerator getIL = getPropMthdBldr.GetILGenerator(); getIL.Emit(OpCodes.Ldarg_0); getIL.Emit(OpCodes.Ldfld, fieldBuilder); getIL.Emit(OpCodes.Ret); MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { propertyType }); ILGenerator setIL = setPropMthdBldr.GetILGenerator(); setIL.Emit(OpCodes.Ldarg_0); setIL.Emit(OpCodes.Ldarg_1); setIL.Emit(OpCodes.Stfld, fieldBuilder); setIL.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getPropMthdBldr); propertyBuilder.SetSetMethod(setPropMthdBldr); |
After that, the process of object creation is completed, and you may think that we are done here – let’s just put it into an array of objects and this is all. Unfortunately this is not a good idea because UI controls may use the type of the collection for their internal functionality. Thus the Silverlight DataGrid has sorting functionality that is implemented based on a reflection of the bound collection. I assume that they do this because they want to be able to know what columns they have even if the collection is empty. But that means that we have to pass a collection not of general object type but of the dynamic type we have just generated. This is the way we do that:
1 2 |
var listType = typeof (List<>).MakeGenericType(new[] {objectType}); var listOfCustom = Activator.CreateInstance(listType); |
As you can see, I generate a generic type List of our object type, then I instantiate it with the activator. After that I traverse each IDictionary DictionaryEntry, matching it with the corresponding property in order to set the value. Here we will get an exception if the type of the first IDictionary key is not the same as the type of the corresponding current IDictionary key.
You may ask here if all this reflection work will have significant performance implications, and I need to clarify that the length of the list of IDictionary objects will not have such performance implications because all the reflection work that I’ve described is being applied to the first IDictionary only. After that, the type is already created and everything is just as if you were binding a regular collection of predefined typed objects. In other words a slow down due to the reflection operations can be expected if you generate a huge number of columns / properties, but should not be expected because of a huge number of records. This is because all records except the first one are processed with a type defined in assembly that has already been loaded into the framework.
But still each binding will result in a creation of a dynamic assembly. This is not a problem for a single Windows or Silverlight application but might be a problem for multi-user server.
Summary
The solution that I’ve described here will be among your first choices for a Silverlight project where you have much more limited set of options. It is one of the many options in Windows Forms and can be applied in Web Forms, though it may not be the best choice in this case because each page visit results in a new assembly being generated on the server.
The ability to dynamically generate typed objects makes .NET a really powerful environment. We can retain the constraints of the statically typed objects and at the same time benefit from the agility of the dynamic data.
Data binding of the user interface is a critical point where the dynamic data has to be presented in a way that isn’t known at design time. Once we know how to transform IDictionary into a typed object we can just use Hashtables as if they were dynamic objects – and this is what has been demonstrated in this article. I hope that now you will agree with me in my conclusion that .NET languages C# and Visual Basic can be used just as regular dynamic language for data binding of user interface controls.
As always, the C# and VB code is downloadable from the bottom of the article.
Vladimir’s blog on http://blog.bodurov.com is always well-worth reading.
Load comments