Binding to Dynamic Properties with ICustomTypeProvider (Silverlight 5 Beta)
Silverlight 5 Beta introduces a new ICustomTypeProvider interface that enables data binding to objects the structure of which cannot be known until runtime. This is a common problem when you work with data in any format from databases to metadata or XML files to JSON objects. New attributes or new columns can be added over time and prior to Silverlight 5 you needed to update and recompile your source code if you wanted to show this new data through data binding in a Silverlight application.
With ICustomTypeProvider you can add properties to objects on the fly and then databind to these newly created objects. The interface is pretty simple:
public interface ICustomTypeProvider
{
public Type GetCustomType();
}
When you implement the ICustomTypeProvider interface you need to return your own Type. Silverlight’s data binding engine now checks whether an object implements this interface or not. If the object does implement ICustomTypeProvider, the data binding engine uses your custom type instead of the System.Type.
Implementing ICustomTypeProvider
However, implementing this interface requires some effort. Here is the list of things to consider:
- Create your own Type by deriving from System.Type.
- Your new type needs to store its properties somewhere, so you need to create your own PropertyInfo by deriving from System.Reflection.PropertyInfo.
- And don’t forget about INotifyPropertyChanged interface if you want your objects to work properly with the data binding.
In Silverlight 4 and earlier, you could not derive from System.Type or System.Reflection.PropertyInfo, but these APIs were changed in Silverlight 5 Beta. Here is the list of types that are now inheritable:
- Type
- Assembly
- ConstructorInfo
- EventInfo
- FieldInfo
- MemberInfo
- MethodBase
- MethodInfo
- Module
- PropertyInfo
- LocalVariableInfo
Creating a Helper Class
OK, implementing your own type and using reflection APIs might not be easy, but on the other hand it’s very flexible. What’s good is that you can write all this code just once and create a helper class that can be reused over and over.
I created such a helper class myself and you are free to use it in your code. This helper allows you to combine statically defined properties with dynamic ones. The Silverlight team is considering releasing this helper later as a part of the toolkit, so your feedback is really appreciated.
Just to be clear, the helper class is not a requirement for using this feature and you can handle things differently. Also, my implementation of the helper class is not the only possible implementation – you are welcome to change it according to your needs or write your own.
The structure of my helper class looks as follows:
public class CustomTypeHelper<T> : ICustomTypeProvider,
INotifyPropertyChanged
{
private static List<CustomPropertyInfoHelper> _properties =
new List<CustomPropertyInfoHelper>();
private Dictionary<string, object> _customPropertyValues;
private CustomType _ctype;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info) {}
public CustomTypeHelper() {}
public Type GetCustomType()
public static void AddProperty(String name) {}
public static void AddProperty(String name, Type propertyType) {}
public static void AddProperty(
String name, Type propertyType, List<Attribute> attributes) {}
public void SetPropertyValue(string propertyName, object value) {}
public object GetPropertyValue(string propertyName) {}
public PropertyInfo[] GetProperties() {}
private class CustomType : Type {}
private class CustomPropertyInfoHelper : PropertyInfo {}
}
You can download the source code for CustomTypeHelper<T> class and example of its usage. In this class, the list of properties is a static list, so that if you add a new property it will be added to all instances of this class. The values of properties for each instance are stored in the _customPropertyValues dictionary. The class is generic, so you can use it for different types (for example, create Customer and Product classes with their own sets of properties).
You might notice that this helper class doesn’t allow for removing or changing properties. The feature itself doesn’t prohibit this, but it is hard to handle changes in the structure of the existing objects in a generalized helper class. You’d better figure it out for each particular application. But once again, I am looking for your feedback and if you can think of common scenarios or envision some kind of a general solution, let me know.
Using a Helper Class
Let’s see how you can use such a class. Imagine that you have a DataGrid named dataGrid1 and you want to bind it to a collection of objects that can add new properties at runtime. There are two options for using the CustomTypeHelper<T> class. First, you can simply inherit it.
public class Customer : CustomTypeHelper<Customer>
{
public String FirstName { get; set; }
public String LastName { get; set; }
}
If you need to preserve your own class hierarchy, you can use the classic delegation pattern. In this case you need to implement the ICustomTypeProvider on your class and also create a property or a field of the CustomTypeHelper<T> class to which you can delegate all the calls.
public class Customer : ICustomTypeProvider
{
public String FirstName { get; set; }
public String LastName { get; set; }
private CustomTypeHelper<Customer> helper =
new CustomTypeHelper<Customer>();
// Redirect all method calls to the helper like shown below
public Type GetCustomType()
{
return helper.GetCustomType();
}
public static void AddProperty(String name)
{
CustomTypeHelper<Customer>.AddProperty(name);
}
// ... Do the same for all other methods from CustomTypeHelper<T>
}
No matter whether you create your class through inheritance or by using the delegation pattern, you can use it for adding properties and data binding as shown below.
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
public MainPage()
{
InitializeComponent();
dataGrid1.AutoGenerateColumns = true;
dataGrid1.ItemsSource = customers;
Customer.AddProperty("Age", typeof(int));
Customer.AddProperty("Married", typeof(bool));
Customer customer1 = new Customer
{ FirstName = "Mary", LastName = "Smith" };
customer1.SetPropertyValue("Age", 40);
customer1.SetPropertyValue("Married", true);
Customer customer2 = new Customer
{ FirstName = "John", LastName = "Smith" };
customer2.SetPropertyValue("Age", 45);
customer2.SetPropertyValue("Married", true);
customers.Add(customer1);
customers.Add(customer2);
}
As you can see, most of the reflection aspects can be hidden in the helper class and the public interface can be made pretty simple.
What about WPF and DLR?
Some of you might be familiar with the ICustomTypeDescriptor interface from WPF, which is solving the same problem as ICustomTypeProvider in Silverlight 5 (even the names are quite similar). So, why didn’t we simply add ICustomTypeDescriptor? The reason is that WPF’s interface requires its own hierarchy of classes (TypeDescriptor, EventDescriptor, etc.) which basically duplicates the reflection hierarchy and also increases the size of the installation package. It is more convenient to use the reflection hierarchy directly. In fact, this new reflection-based model is considered for both WPF and Silverlight. However, the next Silverlight release ships earlier than WPF, so for a while there will be some inconsistency in these APIs.
Another question that many of you might have is whether this feature has anything to do with the Dynamic Language Runtime (DLR), which is a part of the .NET 4 Framework. The answer is no, it’s an independent feature.
Since it’s already a popular question, let me explain why. The DLR objects such as ExpandoObject or DynamicObject (or any other implementation of the IDynamicMetaObjectProvider interface) do not carry any type information for their properties. The data binding engine, on the other hand, needs to get this information in order to perform type conversion for anything other than String.
For example, imagine that you have a text box bound to a property of the System.DateTime type. When you enter a value to this text box you of course enter just a string of characters. But the data binding engine checks the type of the bound property and converts the text box value from string to the necessary date format and vice versa. However, if you bind to a property of let’s say an ExpandoObject, the type of the property itself will simply change to string in this case and no data conversion will be possible. This is exactly what’s happening in WPF: you can bind DLR objects to UI elements, but no type conversion is happening, all you get from UI is strings.
So, while DLR is very convenient for interacting with other platforms and languages, it’s not really suitable for data binding and interacting with the UI of your own application.
Downloads, Links, and Feedback
The feature is available in Silverlight 5 Beta. The helper class source code and usage example are also available. Here are the links:
- Download Silverlight 5 Beta and Tools
- Download the CustomTypeHelper<T> source code (it is also attached to this blog post, see at the bottom of the page).
And of course, your feedback is appreciated here in comments, on Silverlight forums, and at UserVoice.
Comments
Anonymous
April 26, 2011
it's a greate feature, but how to derive from the "Product" class at runtime? in silverlight 4 I have to use TypeBuilder to achieve this goal.Anonymous
April 26, 2011
Hello Everett, Can you please explain why would you want to derive from the class at run time? What is your actual scenario?Anonymous
April 26, 2011
Hi alexandra, my silverlight application get data from server by calling web service, the web service return data in XML format. in client I build one class and add their instance to a collection, then bind datagird to the collection, that's my scenario. The returned XML data maybe changed at run time.Anonymous
April 26, 2011
This is very interesting. It can be used everywhere. The only limitation is that you cannot use linq on the dynamic properties, except of course if you grok and use Dynamic.Linq.Anonymous
April 26, 2011
If a type implements ICustomTypeProvider, then i have to instantiate it and call GetCustomType just to discover the new Type. Why not also have an attribute (something like CustomTypeProviderAttribute) to be able to get the type without any object instantiation? This would allow, for example, a grid to show available columns even though its bound collection is empty.Anonymous
April 27, 2011
@Everett In fact ICustomTypeProvider should be much easier for you than using Reflection.Emit. It is added exactly for your scenario. You can create one class and then add properties to it at run-time depending on your XML schema. But why do you want to derive from a type at run time? @Malisa I guess you could write your own LINQ provider :-) Not sure it's worth the effort, though. @Fadi Do you mean run time or design time? It's kind of doable at run-time, but not at design time (nobody knows what columns you will add at run time).Anonymous
April 27, 2011
Alexandra, using Emit is torturous, so I try to use ICustomTypeProvider: AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("TestAssemblyName"), AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DataModule"); TypeBuilder tb = moduleBuilder.DefineType("ProductA", TypeAttributes.Public | TypeAttributes.Class, typeof(Product)); System.Type rowType = tb.CreateType(); ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); var rowData = Activator.CreateInstance(rowType) as Product; MethodInfo mi = rowType.GetMethod("AddProperty", BindingFlags.Static); mi.Invoke(null, new object[] { "FOO", typeof(string) }); //a Error occured here =========== I don't know what's wrong with the code, Maybe you can give me some Suggestions, thanksAnonymous
April 27, 2011
In Silverlight 4, I have been using a Dictionary with indexed properties in order to bind to dynamic types, but might try this out.Anonymous
April 28, 2011
Hi, i mean't at runtime of course. Consider the following MVVM example to see my point. public class MyCustomModel:ICustomTypeProvider { internal MyCustomModel(object someParameter) { //Some construction code } public Type GetCustomType() { //... //Some custom type implementation where properties are added //... } //In this example, MyCustomType doesn't contain any public properties } public class ViewModel { IEnumerable<MyCustomModel> myCustomModelCollection = new List<MyCustomModel>(); public IEnumerable<MyCustomModel> MyCustomModelCollection { get { return myCustomModelCollection; } } } public class View: UserControl { ViewModel vm = new ViewModel(); public View() { var grid = new DataGrid(); this.Content = grid; grid.ItemSource = vm; Assert.Istrue(typeof(MyCustomModel).GetProperties().Length == 0);//Can't get the custom properties using reflection which is normal and expected } } /Collection is empty => there is no way for the grid to generate columns at runtime based on the custom type because ICustomTypeProvider is defined per instance. So, in general, given a type which may not have a public empty constructor, i need to determine the custom avaible properties for it. => the silverlight team should consider providing a way to define ICustomTypeProvider using an attribute then of course the DataGrid needs to be updated to be aware of this attribute/ //Attribute example [CustomTypeProvider(typeof(MyCustomTypeProvider))]//where MyCustomTypeProvider implements ICustomTypeProvider public class MyDiscoverableCustomType { }Anonymous
May 01, 2011
I totally can understand this being an independent feature and not related to the DLR specifically. Both the example of TextField to DateTime through binding and DataColumn generation via ICustomTypeProvider would work just as well with a IDynamicMetaObjectProvider as runtime data types like XML, JSON, Database. But in fairness the DLR should be more convenient for interacting with UI than not. Both automatically coercing IConvertables and generating AutoColumns are edge cases and thus its very appropriate to require implementing this ICustomTypeProvider for any type of dynamic object. Most things in the UI care only about the binding path and the path alone which matches up perfectly with the DLR's duck typing and thus it feels nature to have property binding path syntax be automatic with DLR objects just like WPF, if you exclude those two edge cases you'd don't really need type info for anything else with binding you really just need the name. UI view code is typically dynamic, while typically not dynamic typed, and that makes it more of a pain to deal with. IDynamicMetaObjectProvider should just work with binding path syntax specifically, and you shouldn't need to implement an ICustomTypeProvider. For my project I did go ahead and write a custom type that works with any IDynamicMetaObjectProvider, and boy did it feel bad to have to copy paste one method and interface into every DynamicObject subclass. For those interested in the custom type is below. The GetProperties code obviously only works for DynamicObjects that implemented GetDynamicMemberNames, however that's only in there for AutoColumns which is an edge case. code.google.com/.../ImpromptuRuntimeType.cs and the complete code is here code.google.com/.../checkout Info on the orignal code is here code.google.com/.../UsageMVVMAnonymous
May 04, 2011
Nice article, I took much the same approach when I wrote about this while I was still at MIX www.damonpayne.com/.../Using-ICustomTypeProvider-in-Silverlight-5.aspx, in this article I also link to the Reflection Emit method I wrote a couple of years ago, and I agree this is far easier.Anonymous
May 17, 2011
Hi Alexandra. I'm developing a silverlight application that shows a list of all the tables of a database. The user can select one to edit its data. Of course, every table has its own fields. How could I use ICustomTypeProvider? Your helper adds properties to a type, not to an instance of a type. An instance of MyTable class could have the fields of Customers table, but another instance of MyTable could have the properties of Products table. Do I need create dynamically a diferent type for each different table? I should much appreciate any help.Anonymous
September 25, 2011
Hi there. Nice feature and it works good with DataGrid . Can you confirm that I can use this for any type of control for example PivotViewer ?Anonymous
December 21, 2011
@Randenko: I have gotten this to work with PivotViewer.Anonymous
February 15, 2012
Hi and thank you for a good article! I'm having som problems when adding properties that themselves implement ICustomTypeProvider, for example Customer.OtherCustomer. The problem occurs when trying to sort a datagridcolumn, the sortmemberpath doesn't seem to find the property. Is this not supported, and if so, do you know any way around it? /OlaAnonymous
June 01, 2012
Visual Studio 11 RC designer is not supporting ICustomTypeProvider (or ICustomTypeDescriptor) (VS2010 supported ICustomTypeDescriptor with WPF) Please vote for the bug I submitted to Connect: connect.microsoft.com/.../create-data-binding-tool-does-not-respect-icustomtypeprovider-or-icustomtypedescriptorAnonymous
June 17, 2012
The comment has been removedAnonymous
July 16, 2012
I launched ICustomTypeProviderSample and everything is OK, except the fact that when I was trying to edit data in DataGrid there were NotImplementedException. Is this by design or I doing something wrong?Anonymous
October 11, 2012
I need to use CustomTypeHelper<T> in my project and it works fine but now i need to send this object over WCF service. Any idea how to do it? Regards, manuAnonymous
November 05, 2012
I've made a CustomTypeHelper class based on your article. I've modified GetIndexParameters() to make it work for editable DataGrid: public override ParameterInfo[] GetIndexParameters(){ return new ParameterInfo[0];} When I make a new Type with custom properties everything works perfect. It allows to modify properties, remove and add a new one. DataGrid Binding works just perfect. Problem starting after I'm trying to modify a cell. First edit works perfect. Second time if I modify Type (remove and/or add a new property) and try to edit cell DataAnotations Validation fire an error "Property not found". Apparently it's looking for an old property. Seems like DataAnotations cache Types and looking for an old property in already modified Type. Is there a way to work around? Disable or force to refresh DataAnotations cache? Thanks in advance!Anonymous
June 25, 2014
Thanks for this - used it as basis for WPF library to automatically bind events, command and create notification properties (among other things): github.com/.../wikiAnonymous
September 29, 2014
Hi, Thanks for this excellent article. I was looking for exactly like this but in WPF and not in Silverlight. WPF adaption of your article is available here www.develop.com/dynamic-type-binding-in-wpf-45utm_campaigndevelopments-may-2012utm_sourcedevelopments I tried to run their sample but when I run their sample I get the Exception as it is trying to call "GetIndexParameter". I would really appreciate if you can provide help on how the same solution can be implemented for WPF. Looking forward to your reply and help. Regards, KrunalAnonymous
May 21, 2015
Thanks a lot. Clear explanation. Thanks for your helper class, it helps to understand and start very quickly.