Silverlight 4 Data Binding to Dynamic DataContext

If you ever want to build a Silverlight control that supports TwoWay data binding on a dynamic object, you might hit the same issues as I did, so I hope this post is useful for at least some of you.

To put this in context, think about the following request:As an end-user I want to be able to design my own personalized user control so that I can use my company's branding, business vocabulary, modify the available fields on the control, ...E.g. replace control by a Post-It on a Silverlight wall in an application, or replace by any similar situation you can think of.Of course, this control is not read-only, but has to be editable (hence the TwoWay data binding).

The above use case has some pretty neat consequences:

  • the datacontext Type is unknown (yes, I know, you can work-around it with proper OO, although it will take you much more time to come up with a design that fits all scenarios, not to mention maintenance time afterwards): I'm following the principle that all code is bad code: the less code I write, the less defects I create, and the more time I have to do other stuff (in other words, I was lazy ^^)
  • how do you make the controltemplate suitable for such a scenario? preferably, you won't create new controltemplates for every type of datacontext you might ever bind to it, right? (if you do, I will send you over 10K different scenarios your control needs to support)
  • there are more, but they can be tackled without this dynamic stuff

First of all, it is noteworthy that you won't be able to use the built-in .NET 4 ExpandoObject, although it implements INotifyPropertyChanged. The reason is that Silverlight is using reflection to perform binding. In other words, Silverlight looks for the actual property.Hence my approach creating my own dynamic type by inheriting from the new DynamicObject class.

Behold, my newly created DynamicDataContext type, which supports adding and removing properties of any type at runtime, and above all, it will be supporting Silverlight TwoWay data binding when you use the Silverlight 4 indexer syntax for your Binding Path (see example usage at the bottom of this post).Note that the code sample below does not implement all virtual members of the base type for brevity.

using System.Dynamic;
using System.ComponentModel;

public class DynamicDataContext : DynamicObject, INotifyPropertyChanged
{
     private readonly IDictionary<string, object> propertyBag = new Dictionary<string, object>();

     public event PropertyChangedEventHandler PropertyChanged;

     /// <summary>
     /// The indexer is needed to be able to use indexer-syntax in XAML 
     /// to data bind to the properties available in the private property bag.
     /// </summary>
     /// <param name="index">The name of the property.</param>
     /// <returns>The value of the property, or null if the property doesn't exist.</returns>
     public object this [string index]
     {
          get
          {
               object result;
               propertyBag.TryGetValue(index, out result);
               return result;
          }
          set { propertyBag[index] = value; RaisePropertyChanged(index); }
     }

     public override bool TryGetMember(GetMemberBinder binder, out object result)
     {
          return propertyBag.TryGetValue(binder.Name, out result);
     }

     public override bool TrySetMember(SetMemberBinder binder, object value)
     {
          propertyBag[binder.Name] = value;
          RaisePropertyChanged(binder.Name);
          return true;
     }

     private void RaisePropertyChanged(string propertyName)
     {
          if(PropertyChanged != null)
               PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
     }
}

To perform TwoWay data binding in XAML, you simply use the indexer syntax, forcing the Silverlight data binding system to look for the indexer (which points to the property bag).The example below binds a control to some datacontext of type DynamicDataContext.Also note that this controltemplate is not the one that will be able to pick up all dynamically added properties at runtime (I need to do a bit more magic to do that).

<SomeTemplatedControl DataContext={Binding DynamicDataContext}>
     <SomeTemplatedControl.ControlTemplate>
          <ControlTemplate TargetType="SomeTemplatedControl">
               <StackPanel Orientation="Vertical">
                    <ContentPresenter Content="[Title]"/>
                    <ContentPresenter Content="[Description]"/>
               </StackPanel>
          </ControlTemplate>
     </SomeTemplatedControl.ControlTemplate>
</SomeTemplatedControl>

The data context can be created by dynamically by adding any property you want, as shown in the example below:

dynamic datacontext = new DynamicDataContext();
     datacontext.Title = "A dynamic title";
     datacontext.Description = "A dynamic description (duh!)";
     DynamicDataContext = datacontext;

I have to admit, the new dynamic features in .NET 4 are pretty cool stuff.

Sharing is caring

Posted by Xavier Decoster on
Last revised: 12 Nov, 2012 11:22 PM
blog comments powered by Disqus