How to properly implement MVC using C#.NET in Windows Forms Applications

Viewed 10401

I've been looking all over the web for example implementations of an MVC setup in .NET. I have found many examples but they all seem to differ in certain aspects. I have a book on Design Patterns that described that MVC was originated in Smalltalk so I read over several people discussing its implementation in that language. What follows is an example project I wrote utilizing what I gathered was a proper implementation but I've been confused by a few details.

One problem I run into is the proper order of the construction of the objects. Here is the impl in my Program.cs

Model mdl = new Model();
Controller ctrl = new Controller(mdl);
Application.Run(new Form1(ctrl, mdl));

The View: Immediately I have a couple of issues I'm unsure of. First, if the view is supposed to only read data from the Model for updating, but contains a reference to it, what is stopping me from making the calls the controller does to the model from the view? Should a programmer just ignore the fact that they are exposed to the models member functions? Another thought I had, was perhaps the event that informs the view the model is updated, would send some sort of state object for the view to update itself with.

public interface IView
{
    double TopSpeed { get; }
    double ZeroTo60 { get; }

    int VehicleID { get; }
    string VehicleName { get; }
}

/// <summary>
/// Assume the form has the following controls
/// A button with a click event OnSaveClicked
/// A combobox with a selected index changed event OnSelectedIndexChanged
/// A textbox that displays the vehicles top speed named mTextTopSpeed
/// A textbox that displays the vehicles zero to 60 time named mTextZeroTo60
/// </summary>

public partial class Form1 : Form, IView
{
    private IController mController;
    private IModel mModel;

    public Form1(IController controller, IModel model)
    {
        InitializeComponent();

        mController = controller;
        mController.SetListener(this);
        mModel = model;
        mModel.ModelChanged += new ModelUpdated(mModel_ModelChanged);
    }

    void mModel_ModelChanged(object sender, EventArgs e)
    {
        mTextTopSpeed.Text = mModel.TopSpeed.ToString();
        mTextZeroTo60.Text = mModel.ZeroTo60.ToString();
    }

    public double TopSpeed { get { return Double.Parse(mTextTopSpeed.Text); } }

    public double ZeroTo60 { get { return Double.Parse(mTextZeroTo60.Text); } }

    public int VehicleID { get { return (int)mComboVehicles.SelectedValue; } }

    public string VehicleName { get { return mComboVehicles.SelectedText; } }

    #region Form Events

    private void OnFormLoad(object sender, EventArgs e)
    {
        mComboVehicles.ValueMember = "Key";
        mComboVehicles.DisplayMember = "Value";
        mComboVehicles.DataSource = new BindingSource(mModel.VehicleList, null);
    }

    private void OnSelectedIndexChanged(object sender, EventArgs e)
    {
        mController.OnSelectedVehicleChanged();
    }

    private void OnSaveClicked(object sender, EventArgs e)
    {
        mController.OnUpdateVehicle();
    }

    #endregion
}

The Controller: My only real issue with the way I have implemented the controller is that it seems a bit odd to me that is possible to construct the controller without definitely having a view assigned to it. I could ignore the view entirely but that would mean I would pass parameters to the controller's functions for updating the model which seems to miss the point entirely.

public interface IController
{
    void OnUpdateVehicle();
    void OnSelectedVehicleChanged();
    void SetListener(IView view);
}

class Controller : IController
{
    private IModel mModel;
    private IView mView = null;

    public Controller(IModel model)
    {
        mModel = model;
    }

    public void OnUpdateVehicle()
    {
        if(mView == null)
            return;

        mModel.UpdateVehicle(mView.VehicleID, mView.TopSpeed, mView.ZeroTo60);
    }

    public void SetListener(IView view)
    {
        mView = view;
    }

    public void OnSelectedVehicleChanged()
    {
        if (mView == null)
            return;
        mModel.SelectVehicle(mView.VehicleID);
    }
}

The Model: In my form, I have a combobox that is a list of the vehicles given in my pseudo database. I feel as though my form should actually implement multiple Views / Models because of this. A view specific to listing the possible vehicles with a corresponding controller / model, and a view for displaying information about the selected vehicle with its own controller / model.

public delegate void ModelUpdated(object sender, EventArgs e);

public interface IModel
{
    event ModelUpdated ModelChanged;

    void UpdateVehicle(int id, double topSpeed, double zeroTo60);
    void SelectVehicle(int id);

    double TopSpeed { get; }
    double ZeroTo60 { get; }
    IDictionary<int, string> VehicleList { get; }
}

// class for the sake of a pseudo database object
class Vehicle
{
    public int ID { get; set; }
    public string Name { get; set; }
    public double TopSpeed { get; set; }
    public double ZeroTo60 { get; set; }

    public Vehicle(int id, string name, double topSpeed, double zeroTo60)
    {
        ID = id;
        Name = name;
        TopSpeed = topSpeed;
        ZeroTo60 = zeroTo60;
    }
}


class Model : IModel
{
    private List<Vehicle> mVehicles = new List<Vehicle>()
    {
        new Vehicle(1, "Civic", 120.0, 5.0),
        new Vehicle(2, "Batmobile", 9000.0, 1.0),
        new Vehicle(3, "Tricycle", 5.0, 0.0)
    };

    private Vehicle mCurrentVehicle;

    public Model()
    {
        mCurrentVehicle = mVehicles[0];
    }

    public event ModelUpdated ModelChanged;

    public void OnModelChanged()
    {
        if (ModelChanged != null)
        {
            ModelChanged(this, new EventArgs());
        }
    }

    public double TopSpeed { get { return mCurrentVehicle.TopSpeed; } }

    public double ZeroTo60 { get { return mCurrentVehicle.ZeroTo60; } }

    public IDictionary<int, string> VehicleList
    {
        get 
        {
            Dictionary<int, string> vDict = new Dictionary<int, string>();
            foreach (Vehicle v in mVehicles)
            {
                vDict.Add(v.ID, v.Name);
            }

            return vDict as IDictionary<int, string>;
        }
    }

    #region Pseudo Database Calls

    public void SelectVehicle(int id)
    {
        foreach (Vehicle v in mVehicles)
        {
            if (v.ID == id)
            {
                mCurrentVehicle = v;
                OnModelChanged(); // send notification to registered views
                break;
            }
        }
    }

    public void UpdateVehicle(int id, double topSpeed, double zeroTo60)
    {
        foreach (Vehicle v in mVehicles)
        {
            if (v.ID == id)
            {
                mCurrentVehicle.TopSpeed = topSpeed;
                mCurrentVehicle.ZeroTo60 = zeroTo60;
                OnModelChanged(); // send notification to registered views
                break;
            }
        }
    }

    #endregion
}

In Conclusion of this tl;dr, I guess what I'm looking for, is some guidance on whether or not what I'm doing here represents a true MVC implementation and maybe for someone to shed some light on the aforementioned concerns. Any advice would be greatly appreciated.

1 Answers
Related