WPF Dynamic View Model with Enum Support





5.00/5 (5 votes)
A technique to create a Dynamic View Model that handles enums.
Introduction
If you’ve used WPF then you’ve used binding to connect the values in your business objects/models with the properties of WPF controls. You’d typically assign the
DataContext
of the window to a view model which will manipulate the properties of the model. In a lot of scenarios the view model will repeat the properties of the model that you want to expose to the UI. Being kind of lazy efficient, the idea of writing all this glue code and maintaining it doesn’t appeal to me. Luckily it’s possible to create a dynamic view model in c# that can be used as a general wrapper for other classes.
The dynamic view model works well for basic types like strings and numbers but not so well for enums. The purpose of this article is to explain one way of dealing with enums that doesn’t require much effort. Once the generic view model is defined all that’s required to make it work is to add description attributes to the enum and do some easy binding in the XAML code.
Background
The original article that I base this idea on is found at: http://blogs.msdn.com/b/csharpfaq/archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx. Also I needed a way of converting the enum to a string and vice versa, read about that here: http://blog.spontaneouspublicity.com/associating-strings-with-enums-in-c.
Typically you would provide the user with a list of options to select the value of an enum field. That could be a list box, combo box or a set of radio buttons. The example I will be using is a real estate sales application and I’ll be using a list box. The
RealProperty
class has properties such as an address,
sale price, asking price, and a property type (e.g. detached house, semi-detached house, etc.) which is an enum of type
EPropertyType
. We’ll bind the selected value of a list box or combo box to the value of the
PropertyType
property and bind the SourceItems
property to a list of possible values.
The properties used in the XAML code won’t be defined in DynamicViewModel
so what happens when the properties aren’t found is that either
TryGetMember
or TrySetMember
is called. We can determine if the property is an enum in the
TryGet
/SetMember
functions and either return the description of the value or convert a description to a value. Another thing we can do is have our view model
object handle requests for a property that will return all the descriptions for the values an enum property can have. In the example code the list box’s
SelectedItem
property is bound to “PropertyType
” and the
ItemsSource
property is bound to “PropertyType_All
” which is a special name that
DynamicViewModel
recognizes. The “_All” property will return a list of descriptions for all the
possible values it can be assigned to using reflection.
Using the code
The first step is to have descriptions of the options in your enum. Simply add some
Description
attributes as shown in the code below.
public enum EPropertyType
{
[Description("Detached")]
Detached,
[Description("Semi-detached")]
SemiDetached,
[Description("Townhouse")]
Townhouse,
[Description("Highrise Condominium")]
HighriseCondo,
[Description("Townhouse Condominium")]
CondoTownhouse
}
Next we need to initialize the DataContext
with a DynamicViewModel
object:
_model = new RealProperty()
{
Address = "1 Yonge Street",
AskingPrice = 900000.0,
SellingPrice = 869000.0,
PropertyType = EPropertyType.HighriseCondo
};
DataContext = new DynamicViewModel(_model);
Inside the XAML code, we can now refer to properties within the Model. For basic controls all we do is refer to the property name and we're done.
<textbox text="{Binding Path=Address}" grid.column="1" name="textBox1" margin="0,9,12,3" height="23" />
For list boxes that use enums all we need to do is bind the property to SelectedItem
and the property name with “_All” appended to it for the
SourceItems
property.
<listbox grid.column="1" name="listBox1" margin="0,12,12,8"
itemssource="{Binding Path=PropertyType_All}" selectedvalue="{Binding Path=PropertyType}" grid.row="1" />
The result is that the control will be populated with the descriptions from the enum and
DynamicViewModel
will handle all the conversions for us.
Points of Interest
Because we’re using reflection a lot this method will be significantly slower than using model specific view models. However in a typical application the impact will be too small to notice. The other consequence is that using the binding dialog to bind to a path in Visual Studio will not work since we’re not using a class with specific properties. What this means is that all binding will need to be done in the XAML manually. For me it hasn’t been a big deal because I find the interface more difficult to use anyways. You might want to try using the model itself for the design time datacontext which will allow the binding dialog to work and not affect the app at run time, see http://karlshifflett.wordpress.com/2009/10/28/ddesigninstance-ddesigndata-in-visual-studio-2010-beta2/.
The solution as presented does not yet deal with localization. For that you’ll need to modify the code in
DynamicViewModel
to look for the descriptions in a resource file. One way to do it would be to have a standard
format for the names in the resource like Desc_EPropertyType_Detached
and then we look that up using the type/name of the value in the enum inside
TryGetMember
/TrySetMember
.