|
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.
|