<September 2010>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
MVVM + DLR: Use DynamicObject as the base class for a ViewModel

When you’re creating a ViewModel for a View to consume, your are often writing mapping Code in the ViewModel that forward to properties of the Model, if you’re not subclassing that is, but that brings problems of it’s own.

For example the View.xaml contains a Binding like this:

   1: <Label Content="{Binding Version}" />

and your Model looks like this:

   1: public class SettingsModel
   2: {
   3:     public string Version { get; set; }
   4:  
   5:     // ...
   6: }

so your ViewModel looks something like this:

   1: public class SettingsViewModel
   2: {
   3:     public SettingsModel Model { get; set; }
   4:  
   5:     public string Version { get { return Model.Version; } set { Model.Version = value; } }
   6:  
   7:     // ...additional properties (IsSelected, ...)
   8: }

This is error prone and tedious, but you can get rid of those problems by using the DLR. You can keep the View and the Model but change the ViewModel into a base class that is derived of a DynamicObject:

   1: public class BaseViewModel<T> : DynamicObject
   2: {
   3:     public T Model { get; private set; }
   4:     private Dictionary<string, PropertyInfo> ModelProperties = new Dictionary<string, PropertyInfo>();
   5:  
   6:     public BaseViewModel(T model)
   7:         : base()
   8:     {
   9:         this.Model = model;
  10:  
  11:         PropertyInfo[] modelProperties = model.GetType().GetProperties();
  12:         foreach (PropertyInfo property in modelProperties)
  13:                 ModelProperties.Add(property.Name, property);
  14:     }
  15:  
  16:     public override bool TrySetMember(SetMemberBinder binder, object value)
  17:     {
  18:         bool valueSet = false;
  19:  
  20:         if (ModelProperties.ContainsKey(binder.Name))
  21:         {
  22:             try
  23:             {
  24:                 ModelProperties[binder.Name].SetValue(this.Model, value, null);
  25:                 valueSet = true;
  26:             }
  27:             catch { }
  28:         }
  29:         return valueSet;
  30:     }
  31:  
  32:     public override bool TryGetMember(GetMemberBinder binder, out object result)
  33:     {
  34:         bool valueGet = false;
  35:         result = null;
  36:  
  37:         PropertyInfo property;
  38:  
  39:         if (ModelProperties.TryGetValue(binder.Name, out property))
  40:         {
  41:             try
  42:             {
  43:                 result = property.GetValue(this.Model, null);
  44:                 valueGet = true;
  45:             }
  46:             catch { }
  47:         }
  48:  
  49:         return valueGet;
  50:     }
  51: }

and the actual implementation:

   1: public class SettingsViewModel : BaseViewModel<SettingsModel>
   2: {
   3:     // ...additional properties (IsSelected, ...)
   4:  
   5:     public SettingsViewModel(SettingsModel settings)
   6:         : base(settings)
   7:     {
   8:     }
   9: }

Now every property of the Model is accessible for the View by Accessing the ViewModel and you can simply add additional Properties to the ViewModel, for those that are missing. If you want to “override” the dynamic Property you can also simply implement it in the ViewModel.

You could also implement IDynamicMetaObjectProvider yourself instead of deriving from DynamicObject.

No Comments

Title
Author
Comment
Anti Bot Image   
  
Rss
  
Elevate Source (3.6 KB)
Sourcecode for elevate command line tool
Elevate.exe (20.7 KB)
Elevate commandline tool

Rss
Tag Cloud