Click here to Skip to main content
Click here to Skip to main content

Simple Model-View-ViewModel in Windows Forms

By , 31 Jan 2013
 

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);

 // or even transform the bound property value using
 myButon.Bind(b => b.Visible, 
  viewModel, m => m.SomePropertyThatWouldLikelyDetermineVisiblityOfAControl,
  value => !value); // negates the value of the binding
}

Hope you will find this interesting! Smile | <img src=

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?   

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

César de Souza
Software Developer
Brazil Brazil
Member
Just a Computer Science student attending the Federal University of Sao Carlos, Brazil.
 
A technology enthusiast, interested in computer networks, operational systems, free software, computer and devices programming, electronics and, more recently, in artificial intelligence, self-organizing systems and neural networks.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questiondoes it support collection source binding?membernull exception7 Jan '13 - 23:30 
for example,how to bind the collection source to datagirdview when using your bind method?
QuestionMobileDialermemberrita kapoor2 Jan '13 - 7:27 
http://www.adoresoftphone.com/mobiledialer.html

 
The Mobile Dialer or Mobile VoIP Dialer can be easily installed in a compatible handset. The Dialer runs on an Operating System based mobile phone. Most popular Mobile Dialer Operating Systems are Symbian, Windows and BlackBerry. Few Mobile Clients or Dialers are compatible with Android Operating System as well. Once installed in a Compatible Mobile Handset, the dialer requires data connectivity to work. General data connectivity options are Wi-Fi, 2G, 3G, 4G, GPRS or Hot Spot. The Mobile Dialer, which is pre-mapped to a Softswitch, then can be used for making VoIP calls directly from Mobile handset.
QuestionMobileDialermemberrita kapoor2 Jan '13 - 7:27 
http://www.adoresoftphone.com/mobiledialer.html
 

The Mobile Dialer or Mobile VoIP Dialer can be easily installed in a compatible handset. The Dialer runs on an Operating System based mobile phone. Most popular Mobile Dialer Operating Systems are Symbian, Windows and BlackBerry. Few Mobile Clients or Dialers are compatible with Android Operating System as well. Once installed in a Compatible Mobile Handset, the dialer requires data connectivity to work. General data connectivity options are Wi-Fi, 2G, 3G, 4G, GPRS or Hot Spot. The Mobile Dialer, which is pre-mapped to a Softswitch, then can be used for making VoIP calls directly from Mobile handset.

GeneralMy vote of 5memberHoangitk1 Jan '13 - 15:06 
Simple and useful
GeneralRe: My vote of 5memberCésar de Souza1 Jan '13 - 15:40 
Thanks, I am glad you liked!
 
I am going to update the tip and include a full BindingExtensions class soon.
 
Happy new year! Smile | :)
Interested in Machine Learning in .NET? Check the Accord.NET Framework.
See also Handwriting Recognition Revisited: Kernel Support Vector Machines

GeneralRe: My vote of 5memberHoangitk1 Jan '13 - 17:54 
Smile | :) , Happy New Year!
 
I had got a problem with specific property as text when code was obfuscated.
So, you can consider support property expression in Binding as
 
public static void Bind<C, V>(this IBindableComponent component,
            Expression<Func<C, object>> propertyNameExp, V dataSource,
            Expression<Func<V, object>> dataMemberExp)
        {
            var exp1 = GetMemberInfo(propertyNameExp);
 
            if (exp1 == null)
                throw new ArgumentException("Lambda expression for PropertyName is not correct");
 
            var propertyName = exp1.Member.Name;
 
            var exp2 = GetMemberInfo(dataMemberExp);
 
            if(exp2 == null)
                throw new ArgumentException("Lambda expression for DataMember is not correct");
 
            var dataMember = exp2.Member.Name;
 
            Bind(component, propertyName, dataSource, dataMember);
        }
 
Usage:
this.textBox1.Bind<TextBox, SampleViewModel>(c => c.Text, _vm, v => v.FirstName);
this.textBox2.Bind<TextBox, SampleViewModel>(c => c.Text, _vm, v => v.LastName);
this.textBox3.Bind<TextBox, SampleViewModel>(c => c.Text, _vm, v => v.DateOfBirth);
 
GetMemberInfo is refered as
http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression
        public static MemberExpression GetMemberInfo(Expression exp)
        {
            var lambdaExp = exp as LambdaExpression;
 
            if (lambdaExp == null)
                throw new ArgumentNullException("Lambda expression syntax is not correct");
 
            MemberExpression memberExp = null;
 
            if (lambdaExp.Body.NodeType == ExpressionType.MemberAccess)
            {
                memberExp = lambdaExp.Body as MemberExpression;
            }
            else if (lambdaExp.Body.NodeType == ExpressionType.Convert)
            {
                memberExp = ((UnaryExpression)lambdaExp.Body).Operand as MemberExpression;
            }
            
            return memberExp;
        }

GeneralRe: My vote of 5memberCésar de Souza2 Jan '13 - 5:18 
Nice idea! I will be updating the tip. Thanks!
Interested in Machine Learning in .NET? Check the Accord.NET Framework.
See also Haar-feature Object Detection (With The Viola-Jones Framework) in C#

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 31 Jan 2013
Article Copyright 2012 by César de Souza
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid