Introduction
With the introduction of XAML, WPF, and Silverlight, Microsoft also introduced us to the MVVM pattern for developing graphical applications. MVVM provides an excellent separation
of concerns, but relies heavily on the declarative data binding abilities of XAML, and is not directly applicable to a WinForms environment.
WinForms also provides data binding support, but is much less advanced than that in WPF/Silverlight. This article demonstrates some techniques whereby you can achieve a similar
separation of concerns as MVVM, through the use of intermediate classes analogous to View-Models.
Data Binding in WinForms and WPF
There are several good articles here on CodeProject explaining data binding in detail, so I will give only a quick summary
here (here is a good one for WPF). In WPF and Silverlight, controls have a binding context (which is usually
set to the control's View Model), and data binding is set declaratively in the XAML:
<Button Text="{Binding ButtonText}" ... />
Crucially, WPF/Silverlight also allows you to modify the binding process, by adding a binding converter. Converters are called during the binding process
to translate the value in the bound property (usually in the View Model) to the value for the UI control in the View (and, if the binding is two way, for the reverse
translation). This means that in WPF/Silverlight, the View Model does not have to expose exactly the properties the View needs, only properties that can be translated
into them. For example, consider a radio button group:
class RadioViewModel {
public enum ButtonName { First, Second, Third };
public ButtonName SelectedButton { get; set; }
public class Converter : IValueConverter {
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture) {
return value == parameter;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture) {
return value ? parameter : null;
}
}
}
... and declarative binding in the XAML:
<StackPanel>
<StackPanel.Resources>
<RadioViewModel.Converter x:Key="radioConverter"/>
</StackPanel.Resources>
<RadioButton Content="One" IsChecked=
"{Binding SelectedButton, Converter={StaticResource radioConverter},
ConverterParameter=RadioViewModel.ButtonName.First}"/>
<RadioButton Content="Two" IsChecked=
"{Binding SelectedButton, Converter={StaticResource radioConverter},
ConverterParameter=RadioViewModel.ButtonName.Second}"/>
<RadioButton Content="Three" IsChecked=
"{Binding SelectedButton, Converter={StaticResource radioConverter},
ConverterParameter=RadioViewModel.ButtonName.Third}"/>
</StackPanel>
This simple example skips over the reverse binding and sets the View Model up to notify changes, because this is not an article about WPF binding,
but it shows the type of thing that converters can do, and how it can move much of the binding logic into converters.
WinForms data binding is much more basic. It is essentially as simple as binding a single UI property to a single property in one of your classes.
This means that the class you bind to must expose a property for every UI property you want to bind; in the radio button example, you'd need three boolean properties.
To bind a property, you need to manually add to the DataBindings
collection, usually in a form or user control constructor (after InitializeComponent
is called
if it's an auto-designed control):
myLabel.DataBindings.Add("Text", modelInstance, "LabelText");
The View Model and a WinForms Analogue
The role of the View Model, at least as I see it, is to translate between the data format that the model naturally wants to use, and the one which maps
best to what a View will need to refer to. As we've just seen, large parts of this role can be specified directly in the binding, through converters,
and this means that View Models are often simple to the point of triviality. In fact, a simple WPF or Silverlight application can often be written binding
UI controls directly to model classes with appropriate converters.
Because WinForms data binding doesn't provide converters, the View Model role is much more important – the View Model needs to do all the conversion.
To avoid confusion with the View Models in MVVM, I call this role a binding helper. It sits between the model object and UI controls, providing directly bindable properties.
For example, let's consider the one-of-three selection again, but this time let's make that a model object referring to business data:
class FavouriteColour {
public enum ColourName { Red, Green, Blue };
public ColourName Value;
}
To bind this to radio buttons in WinForms, we need to translate the enum values into boolean properties that can be data bound:
class FavouriteColourBindingHelper {
public FavouriteColour Model {get; set;}
public bool IsRed {
get { return Model.Value == FavouriteColour.ColourName.Red; }
set { if(value) Model.Value = FavouriteColour.ColourName.Red; }
}
public bool IsGreen {
get { return Model.Value == FavouriteColour.ColourName.Green; }
set { if(value) Model.Value = FavouriteColour.ColourName.Green; }
}
public bool IsBlue {
get { return Model.Value == FavouriteColour.ColourName.Blue; }
set { if(value) Model.Value = FavouriteColour.ColourName.Blue; }
}
}
... and controls can now be bound to an instance of the helper class:
var helper = new FavouriteColourBindingHelper();
helper.Model = ourModelColourObject;
radioButtonRed.DataBindings.Add("Checked", helper, "IsRed");
radioButtonGreen.DataBindings.Add("Checked", helper, "IsGreen");
radioButtonBlue.DataBindings.Add("Checked", helper, "IsBlue");
Because you can only bind properties to properties, there is no way to avoid copy-and-paste property declarations for this type of translation, unfortunately (without
using Reflection and emitting temporary types with the properties, or classes declared dynamic, but that's even less clear). But by using a binding helper, that is in one
clear and obvious place, and a binding helper property for each UI control property you wish to bind is no worse than, for example, the code to set UI properties
in an InitializeComponent
method.
Notification
The simple example above skipped over an important point: notification of changes. One of the main benefits of data binding is that, if two UI controls
are representations of the same data, you don't need to include spaghetti code in the UI layer to manage those dependencies – instead, changes made in
one control are passed through the data, and then to the other controls that depend on it.
Let's upgrade the favourite colour example to also provide a binding property for a list or combo box:
class FavouriteColourBindingHelper {
...
public int SelectedIndex {
get { return (int)Model.Value; }
set { Model.Value = (FavouriteColour.ColourName)value; }
}
}
If you bind SelectedIndex
and also bind the boolean properties, you'll see that (unless you manually refresh the bindings) changing the favourite colour
in one place won't correctly update it in the other. That is because there is no notification process that allows a control to know when it should be re-bound.
INotifyPropertyChanged
The Framework provides a solution to this in the form of the INotifyPropertyChanged
interface. If you data bind to a property
of an INotifyPropertyChanged
, the data binding implementation listens for the PropertyChanged
event and will refresh bindings when it is fired.
The interface only defines the event, so I like to define a base class that binding helpers can inherit from to make life easier:
public abstract class BaseNotifier : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propName) {
PropertyChangedEventHandler h = PropertyChanged;
if(h != null) h(this, new PropertyChangedEventArgs(propName));
}
protected void NotifyAll() { Notify(null); }
protected void Notify(params string[] properties) {
foreach(string propName in properties) Notify(propName);
}
}
Then binding helpers can inherit from this class, and the property setters can notify any relevant properties. Once again, unfortunately,
there is no way to automatically declare this, and the resulting addition of a method call to every property setter produces untidy code – you may have heard
of the 'INotifyPropertyChanged
code smell' and this is what it refers to. However, at least by keeping it in the binding helper it is clear, obvious, and well isolated.
class FavouriteColourBindingHelper : BaseNotifier {
public bool IsRed {
get { return Model.Value == FavouriteColour.ColourName.Red; }
set {
if(value) {
Model.Value = FavouriteColour.ColourName.Red;
NotifyAll();
}
}
}
... }
In this simple example, we want to notify a change to all properties whenever any change happens, but the call could more accurately be written as:
Notify("IsRed", "IsGreen", "IsBlue", "SelectedIndex");
... which, since this group of properties will always be modified together, should probably either use a constant array or call a one line method for the group in a real application.
Notification on the Model?
Any form of model, view, and third component structure (MVC, MVP, MVVM, or this one) will always come up against the question of whether the model classes should support notification,
or whether that should be done through a single instance of the binding layer through which all data manipulation is done.
The answer to that question varies depending on the situation. Putting notification only into the binding layer (binding helper or View Model) is cleaner in terms of separation
of concerns. However, it means that your model can't modify its own state, or modify its state in response to external events, which are fairly common requirements.
So often you need to implement notification on the model classes too. In this case, the binding helper should hook to the PropertyChanged
event of its model counterpart
and notify the correct group of helper properties in each case.
Lists
In many cases, for example data grids or list views, it is useful to bind a control to a list of bindable objects. This means that you need a binding interface
which maps a IList<ModelObject>
to an IList<ModelObjectBindingHelper>
. The best way to do that depends on whether the list changes,
whether the binding is read-write, and whether all changes can be sent through the binding-helper list.
The simplest way, for a list which doesn't change, is to simply create and populate a list at binding time:
var bindingList = new List<ModelObjectBindingHelper>();
foreach(ModelObject obj in modelList) bindingList.Add(new ModelObjectBindingHelper(obj));
var bindingList = modelList.Select(o => new ModelObjectBindingHelper(o)).ToList();
Note that this still allows individual elements of the list to be read-write bound. If items are added or removed from the source list, you can manually
do the same thing to the bound list.
If the list is to be editable through user interaction, it is probably better to create a pass-through IList
implementation which passes add and remove requests
to the model list and creates binding helpers for objects that are in it.
These approaches are significantly less clean than the intermediate helper class for single bound objects, and if you need to bind a list to a control,
it's worth considering writing your model classes in such a way that you don't need binding helpers. Grids and list views typically expect simple properties
which shouldn't need translating in most cases.