TwoWay binding to Name/Value Pairs

by Administrator 7. September 2009 18:59

It is sometimes useful or necessary to represent complex data types as name/value pairs, or Dictionaries if you like.  While the Data Binding implementation in Silverlight 3 is admirable, there is not a built in way to do this cleanly.  Doing some Internet searches, you may find some approaches like this, from http://silverlight.net/forums/t/51864.aspx :

public class DictionaryItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var dict = value as Dictionary<string, string>;
        if (dict != null)
        {
            return dict[parameter as string];
        }
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Note that in all cases you will find the “ConvertBack” strategy missing.  Since the original source object is not passed to ConvertBack, two-way binding to  name/value pairs is an elusive goal.  The binding mechanisms in Silverlight 3 are not very extensible.  I cannot create my own binding type or provide a new implementation for Property Paths.  The Silverlight 3 Data Binding framework wants real Properties to bind to.  If we must have two way bindings to name/value pairs, we are left with pursuing more extreme measures.

Dynamic Type Generation

I have written in the past about the complete Reflection Emit implementation in Silverlight.  It seemed to me that given a means of representing the names and types of the items we want to pull out of a dictionary as Properties, we could dynamically generate a bindable version of a class represented as a dictionary.  We’ll start with creating a very simple model for Objects with data represented as name/value pairs.

namespace HandWaver.AG.EVModel
{
    public class Attribute
    {
        public Attribute(int id, string name, Type t)
        {
            Id = id;
            Name = name;
            AttributeValueType = t;
        }

        public int Id { get; set; }

        public string Name { get; set; }

        public Type AttributeValueType { get; set; }
    }
}

This attribute, not to be confused with the .NET Attribute, represents a sort of metadata for a single property on an object.  We give it a name (which ultimately must be friendly to becoming a Property name) and a run time type.  A single Attribute might be the "Name” property of some object with a type of string.  We may not ultimately want to bind to every single name/value pair available to us.  A means of representing a certain sub-Model is therefore very helpful.  A List of Attribute will suffice for this demo.

namespace HandWaver.AG.EVModel
{
    public class AttributeViewModel : System.Collections.Generic.List<Attribute>
    {
        public AttributeViewModel() : this( Guid.NewGuid() )
        {

        }

        public AttributeViewModel(Guid g)
        {
            UniqueId = g;
        }

        public Guid UniqueId { get; set; }
    }
}

The AttributeViewModel class exposes a UniqueId property of type Guid, which will be useful later.

Reflection Emit code, while incredibly powerful, can also be tedious, difficult, and error prone.  Once practice that I follow is to have as little dynamic code as possible, calling out to pre built classes and only doing what must be done in dynamically created types.  In order to be friendly to binding and provide a base to extend we’ll use a base class that does most of what we want to do already:

namespace HandWaver.AG.EVModel
{
    public class EntityWithAttributes : INotifyPropertyChanged
    {
        public EntityWithAttributes()
        {
            _values = new Dictionary<string, object>();
        }

        Dictionary<string, object> _values;

        public object this[string key]
        {
            get
            {
                return _values[key];
            }
            set
            {
                _values[key] = value;
                OnPropertyChanged(key);
            }
        }


        protected void OnPropertyChanged(string propName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

Next, we’ll build a class to create the dynamic types.  I’m currently calling this a Bindable Entity-Value Builder.  It’s worth going over the Reflection Emit code in some detail.  The public interface for BindableEVBuilder is a single method:

public static Type GetDecoratorType(AttributeViewModel prototype)
{
    EnsureDynamicAssembly();
    EnsureModelTypes();
    if (!_modelTypes.ContainsKey(prototype.UniqueId))
    {
        BuildDecoratorType(prototype);
    }

    return _modelTypes[prototype.UniqueId];        
}

We can look at the first several methods later.  The bulk of our work is done in BuildDecoratorType.

static void BuildDecoratorType(AttributeViewModel prototype)
{
    var typeBuilder = _moduleBuilder.DefineType("HandWaver.AG.EVModel.TypeDecorator.DictWrapper"+ GetName(prototype.UniqueId),
        TypeAttributes.Public | TypeAttributes.Serializable | TypeAttributes.Class, 
        typeof(EntityWithAttributes));

    foreach (var attribute in prototype)
    {
        BuildProperty(typeBuilder, attribute);
    }
    _modelTypes.Add(prototype.UniqueId, typeBuilder.CreateType());
}

So far there’s nothing clever here, just creating a dynamic class that extends EntityWithAttributes and making sure that we only generate one dynamic type for each AttributeViewModel by coming up with a naming scheme.  Properties are somewhat special in the .NET CLR.  In order to create a property using IL, the property itself must be defined as well as a getter and setter method.

static void BuildProperty(TypeBuilder typeBuilder, Attribute attr)
{
    var baseType = typeof(EntityWithAttributes);
    var propertyBuilder =
        typeBuilder.DefineProperty(attr.Name, PropertyAttributes.None, attr.AttributeValueType, null);
    // The property set and property get methods require a special
    // set of attributes.
    MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

    var getterBuilder = BuildGetter(typeBuilder, attr, baseType, getSetAttr);

    var setterBuilder = BuildSetter(typeBuilder, attr, baseType, getSetAttr);

    propertyBuilder.SetGetMethod(getterBuilder);
    propertyBuilder.SetSetMethod(setterBuilder);
}

The getter is more interesting than the setter in this situation.

private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, Attribute attr, Type baseType, MethodAttributes getSetAttr)
{
    var getterBuilder = typeBuilder.DefineMethod("get_" + attr.Name, getSetAttr, attr.AttributeValueType, Type.EmptyTypes);
    var getterILGen = getterBuilder.GetILGenerator();

    var baseGetterInfo = baseType.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
        null, new Type[] { typeof(string) }, null);

    LocalBuilder localValBuilder = getterILGen.DeclareLocal(attr.AttributeValueType);
    // Preparing labels
    Label label20 = getterILGen.DefineLabel();
    // Writing body
    getterILGen.Emit(OpCodes.Nop);
    getterILGen.Emit(OpCodes.Ldarg_0);
    getterILGen.Emit(OpCodes.Ldstr, attr.Name);
    getterILGen.Emit(OpCodes.Call, baseGetterInfo);
    if (attr.AttributeValueType.IsValueType)
    {
        getterILGen.Emit(OpCodes.Unbox_Any, attr.AttributeValueType);
    }
    else
    {
        getterILGen.Emit(OpCodes.Castclass, attr.AttributeValueType);
    }
    getterILGen.Emit(OpCodes.Stloc_0);
    getterILGen.Emit(OpCodes.Br_S, label20);
    getterILGen.MarkLabel(label20);
    getterILGen.Emit(OpCodes.Ldloc_0);
    getterILGen.Emit(OpCodes.Ret);
    return getterBuilder;
}

It is a convention that property methods are named as get_ and set_ plus the actual public property name.  What’s interesting here is the code we must generate to call the dictionary methods on the base class.  You will note that the method name is “get_Item”, which I only discovered by using Reflector.  I made the decision that I wanted I wanted the generated Properties to be strongly typed rather than return Object.  After emitting the OpCodes for calling the base class’s dictionary get method, we cast the Object to the type specified by the Attribute used to build this Property.  When I was first testing this I had properties of type string and double; the string was working and the double was not.  The Castclass opcode produces odd results when working with value types, so I found the correct Opcode for non-reference types which is naturally OpCodes.Unbox.

If you want to become more familiar with what the CLR is really doing, try writing Reflection Emit versions of some common tasks.

The setter code is straightforward as far as these things go.

private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, Attribute attr, Type baseType, MethodAttributes getSetAttr)
{
    var setterBuilder = typeBuilder.DefineMethod("set_" + attr.Name, getSetAttr, null, new Type[] { attr.AttributeValueType });
    var setterILGen = setterBuilder.GetILGenerator();
    var baseSetterInfo = baseType.GetMethod("set_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(object) }, null);
    setterBuilder.SetReturnType(typeof(void));
    setterBuilder.SetParameters(attr.AttributeValueType);
    // Parameter name=value
    ParameterBuilder value = setterBuilder.DefineParameter(1, ParameterAttributes.None, "value");
    ILGenerator gen = setterBuilder.GetILGenerator();
    // Writing body
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldstr, attr.Name);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Call, baseSetterInfo);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ret);
    return setterBuilder;
}

 

Proving it Out

With a first draft of the reflection code written, I wanted to write a simple UI to smoke test three obvious Data Binding scenarios:

  1. Basic binding to a dynamically generated property
  2. Invoking code manually to update a property to make sure update notifications are firing
  3. Change a value using a Control of some kind to make sure TwoWay binding is updating the instance 

These goals can be met with a simple and unglamorous user interface:

<Grid x:Name="LayoutRoot" Width="450" HorizontalAlignment="Left">
      <StackPanel>
          <TextBlock FontSize="30" FontFamily="Verdana">Things that I have time for:</TextBlock>
          <TextBlock Text="{Binding Code}"></TextBlock>
          <TextBox Text="{Binding Wine, Mode=TwoWay}"></TextBox>
          <Border CornerRadius="5" BorderBrush="Red" BorderThickness="2">
              <Border.Child>
                  <TextBlock Text="{Binding Wine}"></TextBlock>
              </Border.Child>
          </Border>
          <Button x:Name="ChangeBtn" Click="ChangeBtn_Click" Content="Change"></Button>
          <StackPanel Orientation="Horizontal">
              <TextBlock>Money(double)</TextBlock>
              <TextBlock Text="{Binding Money}"></TextBlock>
          </StackPanel>
      </StackPanel>
</Grid>

Yes, I’ve identified Code, Wine, and Money (and little else) as things that I have time for.  You’ve got to write what you know.  In the Loaded event handler for this basic Control, we can see the Entity Value classes in action.  The first step is to create a simple model to generate a bindable Type from.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    var model = new AttributeViewModel();
    model.Add(new HandWaver.AG.EVModel.Attribute(0, "Wine", typeof(string)));
    model.Add(new HandWaver.AG.EVModel.Attribute(1, "Code", typeof(string)));
    model.Add(new HandWaver.AG.EVModel.Attribute(2, "Money", typeof(double)));

First we’ll try one object with three attributes.  Creating an instance of the dynamic type looks like this:

var t = BindableEVBuilder.GetDecoratorType(model); _entity = (EntityWithAttributes)Activator.CreateInstance(t);

var t =  BindableEVBuilder.GetDecoratorType(model);
_entity = (EntityWithAttributes)Activator.CreateInstance(t);
_entity["Wine"] = "Chateauneuf du Pape";
_entity["Code"] = "Silverlight";
_entity["Money"] = 5555785.0;
LayoutRoot.DataContext = _entity;

Time for the moment of truth.  Running the code and interacting with the UI yields the following:

UI0

Everything works.  The one time binding “Silverlight” works, I typed Malbec in the TextBox and it updated the object, the Property changed evens fired, and clicking the Change button changed the bottom text to 1000000.  My dynamic property system works.

Going a Little Further

The basic binding situations work, but what about a slightly more complex scenario?  Let’s go ahead and test multiple dynamic types, multiple instances, and IValueConverters.

void BindGrid()
{
    var carModel = new AttributeViewModel();
    var year = new HandWaver.AG.EVModel.Attribute(0, "Year", typeof(int));
    var make = new HandWaver.AG.EVModel.Attribute(1, "Make", typeof(string));
    var model = new HandWaver.AG.EVModel.Attribute(2, "Model", typeof(string));
    var series = new HandWaver.AG.EVModel.Attribute(3, "Series", typeof(string));
    var msrp = new HandWaver.AG.EVModel.Attribute(4, "MSRP", typeof(double));
    carModel.AddRange( new List<HandWaver.AG.EVModel.Attribute>() {year,make,model,series, msrp} );

    var carType = BindableEVBuilder.GetDecoratorType(carModel);

    var wrx = (EntityWithAttributes)Activator.CreateInstance(carType);
    var s4 = (EntityWithAttributes)Activator.CreateInstance(carType);
    var s4Prestige = (EntityWithAttributes)Activator.CreateInstance(carType);

    year.SetValue(wrx, 2002);
    make.SetValue(wrx, "Subaru");
    model.SetValue(wrx, "Impreza");
    series.SetValue(wrx, "WRX");
    msrp.SetValue(wrx, 26000.00);

    year.SetValue(s4, 2010);
    make.SetValue(s4, "Audi");
    model.SetValue(s4, "S4");
    series.SetValue(s4, "Premium Plus");
    msrp.SetValue(s4, 47900.00);

    year.SetValue(s4Prestige, 2010);
    make.SetValue(s4Prestige, "Audi");
    model.SetValue(s4Prestige, "S4");
    series.SetValue(s4Prestige, "Prestige");
    msrp.SetValue(s4Prestige, 53000.00);

    var carList = new List<EntityWithAttributes>() {wrx, s4, s4Prestige};
    CarGrid.ItemsSource = carList;
}

You can see that there’s a bit of code being written to create the instances and set the values, but we could obviously write more code around that to automate more of the tedious work.  I will also note that AutoGenerateColumns=”True” did not work on the DataGrid: I leave that as an exercise for another day.  The resulting display is exactly what you’d expect:

UI1

 

A little polish

When first testing this code, I could see that it worked but inspecting the dynamically generated EntityWithAttributes descendant was producing odd results in the Visual Studio debugger.  Even trying to add a watch for the base class would show an error like “Unable to evaluate expression”.  Silly rabbit, I had forgotten to include debug information in my Reflection Emit code.  This turned out to be a lot easier than it sounds.  Let’s take a look at the EnsureDynamicAssembly method that I skipped earlier.

        /// <summary>
        /// Debugger info found at http://blogs.msdn.com/jmstall/archive/2005/02/03/366429.aspx
        /// </summary>
        static void EnsureDynamicAssembly()
        {
            if (null == _dynamicName)
            {
                var appDomain = AppDomain.CurrentDomain;
                _dynamicName = new AssemblyName("HandWaver.AG.EVModel.TypeDecorator");

                _aBuilder = appDomain.DefineDynamicAssembly(_dynamicName, AssemblyBuilderAccess.Run);
#if DEBUG
                var debugAttrType = typeof(DebuggableAttribute);
                var debugAttrCtor = debugAttrType.GetConstructor(new Type[] { typeof(DebuggableAttribute.DebuggingModes) });

                var customAttributeBuilder =
                    new CustomAttributeBuilder(debugAttrCtor,
                        new object[] { DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default });
                _aBuilder.SetCustomAttribute(customAttributeBuilder);
#endif
#if DEBUG
                _moduleBuilder = _aBuilder.DefineDynamicModule(_dynamicName + ".dll", true);
#else
                _moduleBuilder = _aBuilder.DefineDynamicModule(_dynamicName + ".dll");
#endif
            }
        }

I don’t usually use preprocessor directives but this is exactly what they’re for.  The stuff in between the #if DEBUG statements includes some symbol information in the dynamic assembly.  There’s more work I could have done to associate the code to a Document for source step-through, but at least going this far gets my Local/Watch windows in VS working:

WatchWindow

You can see that I can evaluate properties on the base class and even Reflect the dynamic type to see that it exposes “real” properties like any other Type.

Conclusion

There is a saying that every problem in computer science can be solved with one more level of indirection.  When using the Full .NET Framework it often seems they’ve thought of everything, even providing an effective layer of indirection for Reflection in the form of TypeDescriptor and friends.  Lacking TypeDescriptor in Silverlight, it’s up to us in the community to come up with solutions while we simultaneously pester Microsoft to add more extensibility to the Data Binding frameworks.

Tags:

Comments (6) -

Ken
Ken
9/8/2009 1:26:08 PM #

Great post. I've been working on exactly this problem the last couple days and this is the cleanest and most thorough approach I've seen.

Reply

samcov
samcov
9/8/2009 9:58:13 PM #

This looks interesting, and has been done before, although done specifically to do binding a Dictionary to a DataGrid.

Reflection Emit was used to create a type, which would then be bound to the grid.  Your solution is a bit more general... GOOD WORK!

My only request would be that you should post the solution.  While the code isn't massive, spending time copy and pasting isn't productive when the work has already been done.

Thanks.

Reply

Robert
Robert
9/9/2009 3:21:59 PM #

You are a rock star.  This type of flexibility is exactly what i've been missing.

Reply

Damon
Damon
9/12/2009 5:38:25 PM #

The source is coming, posting with my next article.

Reply

Thierry Duval
Thierry Duval
10/5/2009 6:21:08 PM #

Very great post!

This is just what i have to do but i never work with Reflection Emit.
Can you post your solution or just the BindableEVBuilder class please??

Thanks.

Reply

cz jewelry wholesale
cz jewelry wholesale
10/6/2010 5:17:54 AM #

We are a leading wholesale trader for wholesale jewelry including imitation jewelry and fashion jewelry.
In www.jewelora.com, we are now supplying over 5000 styles of wholesale imitation jewelry including Cubic Zirconia jewelry, pearl jewelry, gold plated jewelry, stainless steel jewelry and other imitation jewelry.
About fashion jewelry

Reply

Pingbacks and trackbacks (3)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

Damon Payne is a Microsoft MVP specializing in Smart Client solution architecture. 

INETA Community Speakers Program

Month List

Page List

flickr photostream