MVVM implementation for Windows forms



I. Introduction

In a previous article I demonstrated how to implement an MVVM Model View View-Model patterns in case of WPF, Of Corse, there are a lot of articles, tutorials and books which talk about this, but the extraordinary thing is in fact that one can implement this pattern with other platforms, I mean Windows Forms, ASP.NET or so.

Let's remember that the MVVM pattern is largely adopted by professionals who essentially develop Silverlight and WPF Windows Presentation Foundation applications. The main purpose behind the adoption of this pattern is the application maintenance and the unit testing as the business logic is entirely located within a separate unity from the view which is in this case the user interface that is, the user interface code behind remains cleans with 0 line of code.

But the question for whom already applied the MVVM with the Avalon (Silverlight or/and WPF) how to implement such pattern in Windows forms case? Bechir has an answer for that; all required is to follow this cook recipe.

II.1 First step

Let's create a Windows form project, and design the just created Form1 as follow:

1.gif 

Figure 1

The controls are defined as follow, tree labels are added to the scene, the first one will have as text First operand, and the second will have as a text Second operand, the third one will serve as result displayer, therefore, we will leave its text property empty, it could be found just at the right of the button as the figure 1 displays. Then we add two text boxes and a button. Recall that we won't name any of those controls at all. We just add them to the scene and dispose them as mentioned in the figure 1 above. As you can guess, this interface will serve to leverage some arithmetic operations let's say an addition operation.

II.2 Second step

The second step consists on developing the view model

2.gif

As we can see here, the View model class contains a private field _form1 which is used to get the controls and do all the business at the View Model class. This class also contains a constructor.

public
ViewModel(Form1 form1)
{
    _form1 = form1;
    /*The loop is leveraged to search the button among controls
    And once it is found, the click event of that related button
    * will be raised */
    foreach (Control item in _form1.Controls)
    {
        if (item is Button)
       
            (item as Button).Click+=new EventHandler(ViewModel_Click);
        }
    }
    //This will lunch the form1 instance
    Application.Run(_form1);
}

As we can observe, a Form1 instance could be injected through the constructor, this one will play the role of the binder between the View Model and the user interface. Then a loop will be leveraged to search a button within the interface. Once the button is found then the click event will be registered. At the other hand, once the button is clicked the following event handler will be invoked:

protected
void ViewModel_Click(object sender, EventArgs args)
{
    int result = 0;
    int cumul;
    foreach (Control item in _form1.Controls)
    {
        if (item is TextBox)
       
            if(int.TryParse((item as TextBox).Text,out cumul))
            {
                result += cumul;
            }
        }
    }
    foreach (Control item in _form1.Controls)
    {
        /* As we remark here, we use the TabIndex property
        * to precise which label will be used to dislpay the
        result as we have three labels in the scene */
        if (item is Label && item.TabIndex ==5)
        {
            (item as Label).Text = result.ToString();
        }
    }
}

The main purpose of that event handler is to search for text boxes, then retrieve all texts' values and convert them to numeric values, the cumul variable will be used to add each text box text converted value to the result variable, once all text boxes are enumerated through that loop. Another loop will search for the label on which the result will be displayed, but the problem will be how to distinguish that label form the others as we have three labels in the scene, the solution is to use the TabIndex property to indicate which label to use for this purpose.

foreach
(Control item in _form1.Controls)
{
    /* As we remark here, we use the TabIndex property
    * to precise which label will be used to dislpay the
    result as we have three labels in the scene */
    if (item is Label && item.TabIndex ==5)
    {
        (item as Label).Text = result.ToString();
    }
}

This is now the complete code of the View Model class

///
<summary>
/// This is the view model class
/// </summary>
public class ViewModel
{
    //The form1 instance will be set once the view model is instanciated
    Form1 _form1;
    /// <summary>
    /// Cosntructor
    /// </summary>
    /// <param name="form1">This form1 will be injected in the view model</param>
    public ViewModel(Form1 form1)
    {
        _form1 = form1;
        /*The loop is leveraged to search the button among controls
        And once it is found, the click event of that related button
        * will be raised */
        foreach (Control item in _form1.Controls)
        {
            if (item is Button)
           
                (item as Button).Click+=new EventHandler(ViewModel_Click);
            }
        }
        //This will lunch the form1 instance
        Application.Run(_form1);
    }
    /// <summary>
    /// This event handler will be triggered when the button is
    /// clicked, the addtion operation will be leveraged and then
    /// the result will be displayed in the targeted label
    /// </summary>
    protected void ViewModel_Click(object sender, EventArgs args)
    {
        int result = 0;
        int cumul;
        foreach (Control item in _form1.Controls)
        {
            if (item is TextBox)
           
                if(int.TryParse((item as TextBox).Text,out cumul))
                {
                    result += cumul;
                }
            }
        }
        foreach (Control item in _form1.Controls)
        {
            /* As we remark here, we use the TabIndex property
            * to precise which label will be used to dislpay the
            result as we have three labels in the scene */
            if (item is Label && item.TabIndex ==5)
            {
                (item as Label).Text = result.ToString();
            }
        }
    }
}

II.3 Third step

The third step is a little bit optional but it could be very helpful, in cases those are more complex than our case. In this step we will use some reflection to indicate whether the View Model is activated, if yes, then the business logic is coupled with user interface. For this purpose we have developed a class attribute which is ViewModelAttribute and which has one unique Boolean property and we call it Activated property that will be used as  flag to indicate if the View Model is activated or not.

3.gif
 
This is an implementation of such class:

using
System;
namespace MVVMImplementation.Attribute
{
    [global::System.AttributeUsage(AttributeTargets.Class, 
                            Inherited = false, AllowMultiple = false)]
    sealed class ViewModelAttribute : System.Attribute 
    {
        public ViewModelAttribute() { }
        public ViewModelAttribute(bool Activated)
        {
            this.Activated = Activated;
        }
        public bool Activated { get; set; }
    }   
}

II.4 Fourth step

The final step consists of changing the way that the application uses to start:

We open the Progam.cs class and we change the code so that it appears like this:

using
System;
using System.Reflection;
using System.Windows.Forms;
using MVVMImplementation.Attribute;
namespace MVVMImplementation
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Initialize();
        }
        private static void Initialize()
        {
            Type t = typeof(Form1);
            object[] attributes = t.GetCustomAttributes(typeof(ViewModelAttribute), false);
            if (attributes.Length > 0 && (attributes[0] as ViewModelAttribute).Activated == true)
            {
                 Form1 form1 = new Form1();
                 ViewModel viewModel = new ViewModel(form1);
            }
            else
            {
                 Application.Run(new Form1());
            }
       }
    }
}

In that above code we used reflection to get information whether the Lunched Form1 is decorated with our custom attribute or not, and then we check if the Activated property is set to true

[ViewModel(true)]
public partial class Form1 : Form

If yes, then the View Model will be coupled within the current lunched instance 

   
Form1 form1 = new Form1();
    ViewModel viewModel = new ViewModel(form1);

If no then the View Model won't be coupled with the view and nothing happens when someone clicks on the result button.

II.5 Fifth step

Let's test the project and see what really happens, first let's decorate our Form1 as follow

using
System;
using System.Windows.Forms;
using MVVMImplementation.Attribute;
namespace MVVMImplementation
{
    [ViewModel(true)]
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

Now, we lunch the application and add some numbers to the both text boxes and click the result button

4.gif

In the other hand, let's change the value of the View Model constructor to false

using System;
using System.Windows.Forms;
using MVVMImplementation.Attribute;
namespace MVVMImplementation
{
    [ViewModel(false)]
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

5.gif

Nothing will happen in this case, because the View Model is not activated.

Now, let's try another tricky thing, first we turn back the value of that View Model to true then we add a third operand to the scene and we run the application, the surprise is that the application runs as normally, and the addition is done regardless on the recent user interface modification. Recall that we don't modified a single line of the View Model in one hand and we don't added any business logic to the view code behind.

6.gif
 
Conclusion: 

The Model View View-Model could be applied for other platform other than the Avalon (WPF and Silverligth) platform, as we see through this article, the MVVM is implemented for the case of the Windows Forms. The next time, I will demonstrate how to implement this pattern with ASP.NET 3.5.

Good Dotneting !!!