Dynamic properties in Silverlight 5 - Eternal Coding - HTML5 / Windows / Kinect / 3D development - Site Home - MSDN Blogs

Dynamic properties in Silverlight 5


 

Dynamic properties in Silverlight 5

  • Comments 2

(This post is a translation of a part of my original article about the new features of Sivlerlight 5 beta 1).

This is an important feature in my point of view for Silverlight because it gives access to the opportunity to add properties at runtime to an object and above all it allows  data bindings on these properties.

Consider, for instance that you must deserialize data from a stream (XML or other). It will be possible to make a class whose properties can be added at runtime. And moreover nothing will prevent you from also have standard properties (design-time) on your class.

The only problem I see with this new technology (actually not new since it was present in.NET via ICustomTypeDescriptor and even via IDynamicMetaObjectProvider for DLR) is the complexity of implementation.

We will detail this for your convenience if you want to use it (because you worth it!).

Generally speaking, the operation is as follows: our class must implement ICustomTypeProvider which involves the creation of the GetCustomType method. This method is called by Silverlight to retrieve the actual type of the class (in place of GetType). This type when asked for the list of the properties he managed will return the actual list AND our dynamic list. This dynamic list will be stored in a dictionary in our class.

What, in terms of code will give us:

The DynamicObject class:

public class DynamicObject : ICustomTypeProvider, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        readonly Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();
        MyTypeDelegator<DynamicObject> myTypeDelegator;
        public int TrueProperty { get; set; }
        public object GetPropertyValue(string key)
        {
            object value;
            return !dynamicProperties.TryGetValue(key, out value) ? null : value;
        }
        public void SetPropertyValue(string key, object value)
        {
            dynamicProperties[key] = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(key));
        }
        public Type GetCustomType()
        {
            return myTypeDelegator ?? (myTypeDelegator = new MyTypeDelegator<DynamicObject>());
        }
    }

You can see the access methods to the properties (and a real property for our tests) and especially  the implementation of GetCustomType.
This method is used to return our own implementation of the DynamicObject type (in order to add our dynamic properties):

public class MyTypeDelegator<T> : Type
    {
        public static List<MyPropertyInfo> _classProperties = new List<MyPropertyInfo>();
        public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
        {
            var properties = typeof(T).GetProperties(bindingAttr);
            return properties.Concat(_classProperties).ToArray();
        }
        protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            PropertyInfo propertyInfo = typeof(T).GetProperty(name, bindingAttr);
            if (propertyInfo != null)
                return propertyInfo;
            return new MyPropertyInfo(name);
        }
        // NotImplemented
        public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
        {
            throw new NotImplementedException();
        }
...

This class is responsible for redefining GetProperties and GetPropertyImpl. For the rest, in our sample, we can throw a simple NotImplementedException. It would have been more elegant to use the TypeDelegator class that allows precisely to do a delegation but for some darks reasons I can't make it work with the beta.

Finally, we only have to provide the class MyPropertyInfo to wrap access to a dynamic property:

public class MyPropertyInfo : PropertyInfo
    {
        private readonly string name;
        public MyPropertyInfo(string name)
        {
            this.name = name;
        }
        public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
        {
            DynamicObject myObject = (DynamicObject)obj;
            return myObject.GetPropertyValue(name);
        }
        public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
        {
            DynamicObject myObject = (DynamicObject)obj;
            myObject.SetPropertyValue(name, value);
        }
        // NotImplemented :)
        public override PropertyAttributes Attributes
        {
            get { throw new NotImplementedException(); }
        }
...

This class is just a bridge to the good methods in our DynamicObject.

As you can see the implementation is a little complex but on the final version of Silverlight Microsoft should provide helpers to simplify it a little bit.

In what concerns the use in XAML (and this is where the magic operates), just use our dynamic properties as standard properties:

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <TextBlock Text="Incredible...there is a dynamic property here?"/>
        <TextBlock Text="{Binding Path=MyProperty}" />
        <TextBlock Text="{Binding Path=TrueProperty}" />
    </StackPanel>
</Grid>

The code behind:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    DynamicObject myDynamicObject = new DynamicObject {TrueProperty = 10};
    myDynamicObject.SetPropertyValue("MyProperty", "Here I am!");
    DataContext = myDynamicObject;
}

Our DynamicObject object is therefore constructed both in the design-time (on its property TrueProperty) and in the runtime (on his property MyProperty). The binding in XAML retrieves the two without any problems!

Leave a Comment
  • Please add 5 and 2 and type the answer here:
  • Post
  • My question is, if all you need is to implement how to GetValue by a property name and SetValue by a property name for binding path to work, then why can't micrsoft use a a DLR GetBinder/SetBinder and CallSites to just do it automatically without having to manually implement ICustomTypeProvider? I mean, I can see ICustomTypeProvider on a IDynamicMetaobjectProvider being useful for interacting with things that NEED reflection, but this seems like it shouldn't need reflection.

  • Hi Jay,

    in a near future, as I wrote in the article, Microsoft will provide helpers to simplify the process so user will only have to give a property name and a value.

    In addition, there are scenarios where reflection is needed if you want for istance to get your dynamic properties from others included types.

    I mean, the property value can be complex to get and using GetBinder/SetBinder will not cover all scenarios.

    Regards,

    David.

Page 1 of 1 (2 items)