MVC Winforms FTW

by losthack on Sunday, November 18, 2012

If you are like me, you are on this page because you were searching for MVC Winforms Tutorial on google or something. I bet you work at a place that still uses winforms to develop thick apps for windows. And let me guess, you are probably dealing with a lot of legacy UI code that has to be maintained, right? Yeah it sucks, tell me about it. You probably want to be a good little code monkey and place the legacy UI code into a testable state—I mean your 1 or 2 QA dudes can't test everything, right?


Since you are reading this page, you probably started reading something on UI design patterns and came across MVC, MVP, MVVM, MVPVM, etc. You have probably seen dozens of blogs with images showing how MVC works—I bet you have thought to yourself:


Yeah, OK, but how do I actually implement a dialog using one of these patterns?


Even worse, I bet you have downloaded dozens of so called example projects only to find the most convoluted examples humanly possible. Don't worry, I am not going to give you an example project; however, I will discuss how to implement the most basic UI design pattern, MVC, using winforms. Later on, if I feel like it, I will show you MVP and the rest.

What is MVC?

For those who don't know what MVC is: shame on you. MVC stands for Model-View-Controller and is a design pattern commonly used to separate concerns between the presentation and domain layers of applications.

If implemented correctly, you can use MVC to enable your application to be testable and mock the UI in a unit test and simulate user interactions thus providing some level of test coverage. Of course, you still need QA; however, their job becomes focused on usability rather than functionality (assuming you actually wrote the unit tests before shipping to QA).

How does MVC work?

There are 3 main abstractions in MVC: Model, View, and Controller. They interact with each other in very specific ways and have very specific dependencies on each other. Understanding these specifics will enable you to successfully implement a presentation layer that uses MVC.

Model

The Model represents a hook into your domain layer. Its responsibility is to consume dependencies on your application's domain layer and notify the presentation layer appropriately.

View

The View represents the user interface in your presentation layer. Its responsibility is to consume the Model and subscribe to any necessary notifications on the Model which can be used to update the user. By the way, the View is an abstraction that is usually is represented by a dialog, or website or something; however, it can also be a command prompt.

The View has another responsibility: consume the Controller and invoke actions on the Controller based on user generated events. For example, when a user clicks on something, that might require a controller's action be invoked.

The View has yet another responsibility: spawn additional views. You might ask yourself:

Where do I place modal dialogs?

Or...

What if I am knee-deep in my Model's work and I need user input?

The answer is simple: Views spawn Views. This will make threads happy and make you happy.

Controller

The Controller represents the actions the View can take. The implementation of those actions delegates work to the model (which will update the view).

Depedency Rules

  • The Model is unaware of the View and the Controller
  • The Controller is unaware of the View, but is aware of the Model
  • The View is aware of both the Model and the Controller

Dependency Injection Rules

  • Use Constructor Injection to inject domain object dependencies in your Model
  • Use Constructor Injection to inject Model dependencies in your Controller
  • Use Constructor Injection to inject Model dependencies in your View
  • Use Property Injection to inject Controller dependencies in your View

Defining the Abstractions for MVC

Like any good little code monkey, you should be programming against interfaces instead of concrete classes. So let us do just that and start with the simplest one.

Model Abstraction

public interface IModel
{
}

Notice that the interface is empty. That's because there's no guarantee that two models will behave the same. Remember, concrete models will inject dependencies on your domain objects. However, nothing is stopping you from doing something like this:

public interface ISpecificModel : IModel
{
    event Action SomethingChangedEvent;
}

The IModel interface will be used when defining the other abstractions.

Controller Abstraction

I hate type casting because I like the compiler doing as much work for me as possible. So I break up my Controller's abstraction into a base type and a generic type.

public interface IController
{
}

public interface IController<​TModel​> : IController
    where TModel : IModel
{
    TModel Model { get; }
}

You want to expose the Model consumed by the Controller so the View can use it to register for notifications. I also like it here because it enforces the dependency rules quite nicely.

View Abstraction

Thank goodness we have those IModel and IController base interfaces. It makes writing the where clauses on the generic types much easier.

public interface IView
{
}

public interface IView<​TController, TModel​> : IView
    where TController : IController
    where TModel : IModel
{
    TController Controller { get; set; }
    void Register(TModel model);
    void Release(TModel model);
}

We expose the public setter for the Controller because we want to enable property injection in the concrete classes. The Register and Release methods subscribe and unsubscribe to/from notifications in the Model.

Concrete Example

OK, OK, enough setup; let's get the ball rolling. We will be creating a very simple calculator application using MVC.

Clearly there are two text boxes, one for A and one for B. There is also a big fat 0 in the center. When the user clicks on the + button, the zero changes to whatever A + B evaluates to. I'm going to leave you to figure out how to make the dialog, but lets go over how to implement it using MVC.

Calculator Model Interface

Like all good code little monkeys, lets start with the interfaces for our example and build on top the interfaces above.

public interface ICalculatorModel : IModel
{
    event Action<​int​> AddResultEvent;
    int Add(int a, int b);
}

Our ICalculatorModel looks pretty simple: it adds two numbers and notifies the world that it added two numbers. That event is critical for the View.

Calculator Controller Interface

OK, now let's design that controller. Remember a controller represents the actions that users can take in the View.

public interface ICalculatorController : IController<​ICalculatorModel​>
{
    void PlusButtonClicked(int a, int b);
}

Geez, I wonder what the ICalculatorController does.

Calculator View Interface

The View interface is really bare because all the details lie in the implementation.

public interface ICalculatorView : IView<​ICalculatorController, ICalculatorModel​>
{
}

Concrete Calculator Model

public class CalculatorModel : ICalculatorModel
{
    public event Action<​int​> AddResultEvent;

    public int Add(int a, int b)
    {
        int result = a + b;

        if (AddResultEvent != null)
            AddResultEvent(result);

        return result;
    }
}

The important thing here is that the Add method will raise the AddResultEvent. The AddResultEvent will notify the view to update. Notice that there is no constructor injection and that's because there are no dependencies; however, constructor injection is ideal here.

Concrete Calculator Controller

public class CalculatorController : ICalculatorController
{
    private readonly ICalculatorModel model;

    public CalculatorController(ICalculatorModel model)
    {
        this.model = model;
    }

    public void PlusButtonClicked(int a, int b)
    {
        this.model.Add(a, b);
    }

    public ICalculatorModel Model
    {
        get
        {
            return this.model;
        }
    }
}

The concrete CalculatorController uses constructor injection to depend on the CalculatorModel.

Concrete Calculator View

public partial class CalculatorView : Form, ICalculatorView
{
    private readonly ICalculatorModel model;

    private ICalculatorController controller;

    public CalculatorView(ICalculatorModel model)
    {
        this.model = model;
        InitializeComponent();
    }

    public ICalculatorController Controller
    {
        get
        {
            if (this.controller == null)
                this.controller = new CalculatorController(model);

            return this.controller;
        }
        set
        {
            if (value == null)
                throw new InvalidOperationException();
            else if (this.controller != null)
                throw new InvalidOperationException();

            this.controller = value;
        }
    }

    public void Register(ICalculatorModel model)
    {
        model.AddResultEvent += model_AddResultEvent;
    }

    public void Release(ICalculatorModel model)
    {
        model.AddResultEvent -= model_AddResultEvent;
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Register(model);
    }

    protected override void OnClosed(EventArgs e)
    {
        Release(model);
        base.OnClosed(e);
    }

    private void model_AddResultEvent(int result)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new Action<​int​>(model_AddResultEvent), result);
            return;
        }

        this.labelResult.Text = result.ToString();
    }

    private void buttonAdd_Click(object sender, EventArgs args)
    {
        try
        {
            int a = int.Parse(this.textBoxA.Text);
            int b = int.Parse(this.textBoxB.Text);
            this.Controller.PlusButtonClicked(a, b);
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }
}

So this is where it gets interesting. We use constructor injection to depend on the model and use property injection to depend on the controller. Notice that Register and Release are invoked when the dialog loads and closes. When Register is invoked it subscribes to events from the model dependency. When Release is invoked it unsubscribes to events from the model dependency. Finally, when the user clicks on the + button, the controller's action is invoked.

Can you see the flow of events clearly? When the user clicks on a button, the controller is invoked, which then invokes the model to perform the work and then the model notifies the view.

Um... why use Property Injection to inject the Controller dependency in the View?

I guess you don't really have to use property injection; however, property injection gives you so much flexibility. You could use constructor injection instead; however, that would force you to construct the model first, then the controller next, and then the view. That can get old quick. Property injection allows you to have a default value for the property—thus allowing you to bind a default concrete controller to your concrete view and also allow you to override the default controller. This might come in handy when developing your view since you can define your default controller to be what ships to QA and also define a controller to help find UI bugs.

So... why is this now Testable?

Listen up, little code monkey. What we did here is create a nice little stack of dependencies:

View

Controller

Model


This means we can mock the dependencies and swap out the real thing with the mocked dependency. Ultimately, this means you can mock the View and simulate user interaction.

Simulation Example

I'm going to be really nice to you here and show one possible way to simulate user interactions.

[TestClass]
public class CalculatorViewTests : ICalculatorView
{
    public ICalculatorController Controller
    {
        get;
        set;
    }

    public void Register(ICalculatorModel model)
    {
        model.AddResultEvent += model_AddResultEvent;
    }

    public void Release(ICalculatorModel model)
    {
        model.AddResultEvent -= model_AddResultEvent;
    }

    public int Result
    {
        get;
        set;
    }

    private void model_AddResultEvent(int result)
    {
        Result = result;
    }

    [TestInitialize]
    public void Setup()
    {
        var model = new CalculatorModel();
        Controller = new CalculatorController(model);
        Register(Controller.Model);
    }

    [TestCleanup]
    public void Teardown()
    {
        Release(Controller.Model);
        Controller = null;
    }

    [TestMethod]
    public void Add50To50()
    {
        Controller.PlusButtonClicked(50, 50);
        Assert.AreEqual(100, Result);
    }
}

Using MSTest to write our unit tests was pretty easy, and best of all the unit test implemented the ICalculatorView. The setup and teardown will reset the state of the unit test for each test method. The test method Add50To50 will simulate the user pushing the + button (assuming the user entered "50" into textBoxA and "50" into textBoxB). You can imagine how this extended to more complicated simulations that just pressing a button.

I Guess That's It

So there you have it. I am tired of writing this crap. I hope you got something out of it. Also, for people with a higher degree of skill than me: I haven't written a unit test to simulate user interactions before and I guess I am not surprised if there are much better ways to do it. The point of this blog post was just to show the less fortunate how to remove some of their headaches.

UI For the win!