Select to view content in your preferred language

C# MVC Add-in pattern

1567
4
01-18-2014 09:39 PM
LeoDonahue
Deactivated User
I took a crack at creating a MVC pattern for C# Add-ins (since I mostly do Java). 
I am not quite sure I have the event handler correct. 

Would love to have some feedback on this.

Program.cs  (the View, Model and Controller would be created in your on_click event for a button Add-in)
You would also want to pass a reference of IMap to the Model and Controller.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (_view == null)
            {
                _view = View.getInstance();
            }
            if (_model == null)
            {
                _model = new Model(_view);
            }
            if (_controller == null)
            {
                _controller = new Controller(_view, _model);
            }

            Application.Run(_view);
        }

        private static View _view;
        private static Model _model;
        private static Controller _controller;
    }
}


Model.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public class Model
    {
        private View m_view;

        public Model(View vw)
        {
            m_view = vw;
        }

        public void respondClick()
        {
            MessageBox.Show("The title of this form is: " + m_view.Text + "\r\n" + "you clicked me");
        }

    }
}


Controller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace WindowsFormsApplication1
{
    public class Controller
    {
        private View c_view;
        private Model c_model;

        public Controller(View vw, Model md)
        {
            c_view = vw;
            c_model = md;

            c_view.getBtnClickMe().Click += handleClick;
        }

        private void handleClick(object sender, EventArgs e)
        {
            c_model.respondClick();
        }

    }
}


View.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class View : Form
    {
        public View()
        {
            InitializeComponent();
        }

    }
}


View.Designer.cs
namespace WindowsFormsApplication1
{
    partial class View
    {

        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private static View view = null;

        public static View getInstance()
        {
            if (view == null)
            {
                view = new View();
            }
            return view;
        }

        private void InitializeComponent()
        {
            this.btnClickMe = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // btnClickMe
            // 
            this.btnClickMe.Location = new System.Drawing.Point(98, 60);
            this.btnClickMe.Name = "btnClickMe";
            this.btnClickMe.Size = new System.Drawing.Size(75, 23);
            this.btnClickMe.TabIndex = 0;
            this.btnClickMe.Text = "Click Me";
            this.btnClickMe.UseVisualStyleBackColor = true;
            // 
            // View
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.btnClickMe);
            this.Name = "View";
            this.Text = "View1";
            this.ResumeLayout(false);

        }

        public System.Windows.Forms.Button getBtnClickMe()
        {
            return this.btnClickMe;
        }

        private System.Windows.Forms.Button btnClickMe;
    }
}
0 Kudos
4 Replies
NeilClemmons
Honored Contributor
I don't profess to be any kind of expert with this particular design pattern and it seems that no two examples I've ever looked at were the same.  From what I can gather, the underlying purpose of MVC is to separate the GUI from the business logic.  The view knows nothing about the model and the model knows nothing about the view.  In your example, you're passing a reference to the view into the model.  To me, this seems to break the pattern.  Your model class is now dependent on the view being a Windows Form class.  What if you wanted to create a view in a web application based on this model?  We implement this pattern in several of our applications, a couple of which have both desktop and web versions.  When I say implement, I mean that we follow the idea of MVC to some degree but some may argue that it's not true MVC.  For example, we don't refer to models as "models".  They are just typical classes that represent entities within the application (a Widget class for example).  A typical scenario would be a form that creates a new widget and saves it into the database.  The form gets a reference to a controller class and, as the user inputs values into the controls on the form, methods are called on the controller to pass the input (we don't use events as they seem to be cumbersome, confusing and unnecessary).  The controller class creates a new instance of a Widget when it is initialized and sets the properties on the Widget as they are updated via the view.  When the user finishes, they click a Save button on the dialog which calls a method on the controller to finish the deal.  The controller in turn calls a method on the Widget class that saves the object into the database.  Using this pattern, both our web and desktop versions can use the same Widget class which keeps the business logic common.  They, of course, need their own view and controller classes but once you create them for one it's a relatively simple process to copy it to the other.

Anyway, that's my two cents.  I'm always interested to see how other programmers do things.
0 Kudos
LeoDonahue
Deactivated User
Neil,

I was wondering if my post fell on deaf ears, thanks for replying.

You mention that you re-create the controller for every application because you make your controller dependent on the type of view, rather than the model so that your model is free to act on form views or web views.  I see your point. 

When I read: http://msdn.microsoft.com/en-us/library/ff649643.aspx

Model. The model manages the behavior and data of the  application domain, responds to requests for information about its state  (usually from the view), and responds to instructions to change state  (usually from the controller).


Are application domains supposed to be isolated from one another?

I read the quote as the model is able to change the state of the view, through the controller, usually.  And what about not usually?

The hardest part is defining what is business logic.  You can leave out the reference of the view in the model, but that requires that you have "some" business logic in your controller that is not just controller code.  Alot of times before you call model methods, you want to do some pre-model logic with the view, enabling or disabling a button, resetting sliders to their default positions, etc.  What if your model needs to act on the value of a component, such as getting the value of the selected row in a table or grid?  You would have to pass all those references to the model, or your controller now handles all of that logic that is really not related to responding to view events.  Now you have two places to check for things that go wrong.  Is the problem in the Controller's pre-model logic, or is the problem in the model logic?  Hard to pin down.

Whether you keep this business logic that is really not controller code in your controller or whether you move this business logic that is really not model code to the model, you have code that doesn't really fit in either area. I find it easier to break the pattern and move that code to the model, for desktop applications.

The article also says:
It is important to note that both the view and the controller depend on  the model. However, the model depends on neither the view nor the  controller.


In my Java web applications, I do not pass a reference of the view to the model.  I think there are some differences in .NET MVC vs Java MVC depending on the web framework you are using.  Doesn't .NET call theirs: Model, View, View Model (MVVM)?
0 Kudos
NeilClemmons
Honored Contributor
I read the quote as the model is able to change the state of the view, through the controller, usually. And what about not usually?


Can the model change the state of the view?  Yes.  Should it?  I don't know.  Probably not, at least not directly.  Having the model directly change the state of the view assumes that the model knows something about the view.  Since multiple views can be created for the same model, allowing the model to change the view would potentially create conflicts where the model was attempting to change something that a particular view doesn't support.  For example, let's say our model is a class named Employee that represents a record from the Employees table in our database.  Our Employee class has a property named DateOfBirth that represents the column of the same name in the database table.  We have a view that allows the user to edit the record for an existing employee.  We have another view that displays a list of current employees.  This list displays various information about the employee including their age.  Since age isn't a value that is stored in the employees table it must be calculated from the DateOfBirth property.  To keep things up to date, we would want the age of an employee displayed in the list to automatically update if the record for that employee is edited and the DateOfBirth is changed.  One way to accomplish this is to have the model update the employee list whenever the DateOfBirth property value changes.  This can lead to problems.  What if the view isn't open?  What if the model is being used in an application that doesn't have that view?  A better way to handle this would be to create a DataOfBirthUpdated event that a controller class could subscribe to.  The model fires the event when the property changes and any listeners can respond in whatever manner is appropriate.  In this case, the controller calculates the new age and updates the list in the view.  There are other solutions as well, but it would seem to me that having the model directly interact with the view is the least preferred.

Alot of times before you call model methods, you want to do some pre-model logic with the view, enabling or disabling a button, resetting sliders to their default positions, etc.


To me, this isn't business logic.  That's GUI logic.  The GUI is responsible for gathering input, validating input, leading the user through the correct workflow, etc.  Keeping with the Employee model example, let's say we have a view for creating a new Employee.  The view will prompt the user for relevant input such as name, date of birth, job title, salary, etc.  Pretty much everything the user is entering should have a corresponding property on the Employee class.  As the user enters it in, the view passes it to the controller which sets the property on it's internal instance of the Employee class.  When the user is done, they click a button which calls the controller and tells it to create the new record, which it does by calling a method on the Employee class.  For the most part, the view can perform any validation checks before passing the data along.  In some instances you may want the Employee class to do that.  Typically this would be when there is some type of business rule in place.  Maybe a particular job title has a minimum salary.  In this case, the model would perform the check and throw an exception if the rule isn't satisfied.  The controller would catch the exception and handle it.  Other types of validation, such as making sure the user enters the date of birth in a valid Date format would typically be handled directly by the view.

As I said in my first post, the examples I've looked at on the web are quite varied in their approach.  I don't think there's any "right" way as long as what you choose to do meets your requirements, is easy to understand and is easy to come back to and change in 6 months!
0 Kudos
LeoDonahue
Deactivated User
I appreciate the dialog.

MVC patterns for non ArcGIS Add-in projects can be expressed in the ways you described, when you are abstracting objects.

The issue with MVC in ArcGIS Add-in projects is that we find ourselves still writing procedural based code (some in the model, some in the controller and some for your view) to handle the workflow.  And some in the event of the Add-in, be it a tool or button click event.

I have no logic in any of the desktop Add-in views, except for certain Java Swing components that need to be created after the form is loaded, such as JFormattedTextFields and JFrames that use AbstractTableModels.  To me the view is just a renderer of view components.

But you are right, I don't think there is a hard and fast MVC pattern for every situation.

I do think that having a pattern similar to MVC will help alot of the users in the forum lately who express issues with forms and needing tricks or global data to accomplish their goals.
0 Kudos