Introduction
As soon as you are into MVVM and WPF, it is almost impossible to go back. The problem is, sometimes we have to go back. Perhaps you were escalated to work on a legacy project,
or your current project requirements dictate you have to work with WinForms. However, at least for me, as soon as I got my hands back into WinForms I couldn't avoid to keep thinking
in terms of Views and ViewModels. So this tip shows a quick and interesting trick to get at least some of the conveniences of developing in MVVM inside WinForms.
Note: there are frameworks specially designed for this, such as MVVM FX. However, documentation seems lacking, at best.
Besides, WinForms doesn't have all the required support for a true MVVM pattern, specially for composing the View.
But it doesn't do that bad for data binding.
Binding to ViewModel from a Form
The first thing is to realize that WinForms has useful data binding capabilities. I failed to notice how useful data binding was until I got into WPF.
But let's not make this sin ever again. Suppose, for example, we have a ViewModel like this:
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SomePropertyThatWouldLikelyDetermineVisiblityOfAControl { get; private set; }
...
}
Now, in our view, suppose we would decide to bind the visibility of a control to the suggestively named property of the ViewModel above.
In WPF this would be quite trivial. Now what about WinForms? Well, it is too, if the component implements the IBindableComponent
interface.
If we add the following code to the Load
event of the form, we get a very pleasantly similar result:
private void MainForm_Load(object sender, EventArgs e)
{
myControl.DataBindings.Add(new Binding("Visible", viewModel,
"SomePropertyThatWouldLikelyDetermineVisiblityOfAControl"));
}
However, what if the property type has nothing to do with the property we are binding? We could write some converters, such as in WPF and so on.
But consider the following extension class, for a minute:
public static class BindingExtensions
{
public static void Bind<T, U>(this IBindableComponent component,
Binding binding, Func<T, U> transform)
{
binding.Format += (sender, e) => e.Value = transform((T)e.Value);
component.DataBindings.Add(binding);
}
public static void Bind(this IBindableComponent component, Binding binding)
{
component.DataBindings.Add(binding);
}
public static void Bind<T, U>(this IBindableComponent component,
string propertyName, object dataSource, string dataMember, Func<T, U> transform)
{
Bind(component, new Binding(propertyName, dataSource, dataMember), transform);
}
public static void Bind(this IBindableComponent component,
string propertyName, object dataSource, string dataMember)
{
Bind(component, new Binding(propertyName, dataSource, dataMember));
}
}
Let's see how we can leverage this class to obtain arbitrary data binding fairly easy in our WinForms application.
Using the code
Suppose we would like to bind the Text
property of a TextBox
to this boolean property. Using the aforementioned code, all we would need to do is:
private void MainForm_Load(object sender, EventArgs e)
{
textBox.Bind("Text", viewModel, "SomePropertyThatWouldLikelyDetermineVisiblityOfAControl",
(bool value) => value ? "Property says true" : "Property says false");
}
And so we have converted the boolean value of the ViewModel into our view's text. This is also useful if, for example, instead of enabling the visibility
of a control like in the first example, we would be interested in disabling it whenever the property is true. Let's see:
private void MainForm_Load(object sender, EventArgs e)
{
myButtom.Bind("Visible", viewModel,
"SomePropertyThatWouldLikelyDetermineVisiblityOfAControl", (bool value) => !value);
}
and now we have the negated behavior of the first example.
The zip file attached to this article contains the entire BindingExtensions
class, offering support for both
the format and parse methods of the WinForms data
binding framework using lambda functions. If you feel interested, feel free to take a look. You can also browse the code using CodeProject's article code browser
by clicking the "Browse Code" button on the left.
Using the full class available on the zip, we can also use lambda expressions to achieve both type-safety and, as noted
in Hoangitk's comment,
be used together with code obfuscators. Thus we can create a bindings using:
private void MainForm_Load(object sender, EventArgs e)
{
myButton.Bind(b => b.Visible,
viewModel, m => m.SomePropertyThatWouldLikelyDetermineVisiblityOfAControl);
myButon.Bind(b => b.Visible,
viewModel, m => m.SomePropertyThatWouldLikelyDetermineVisiblityOfAControl,
value => !value);
}
Hope you will find this interesting!
Drawbacks
Obviously this only offers very limited support for basic data binding. Perhaps a better title for this tip would be "a simple way to approximate
MovelView-View-Model behavior inside Windows Forms". For instance, I am not sure if this will handle two-way binding.
But the easiness to simply declare which control binds to which property in a ViewModel, with the ability to control how the binding is done without the need for
writing hideous converter classes was already very handy for me. What do you think?