2008-11-01

Mvc for Winforms - Mapping the View event to the Controller action

In my current (or rather one of my current projects) I'm writing a small MVC framework for a WinForms application (I mentioned this in my last post). The latest issue that I stumbled upon and found worth mentioning is about connecting the Views events to the Controllers actions.

If we look at the MVC for ASP.NET the events is the requests by the web client and the URL contains the information that the Routing module uses to find an appropriate action to call. It should be nice to have such a feature in the WinForms solution as well so after some thought I summarized a small example.

In my view I can have several objects (not necessary controls) that can/will fire some events (like a button firering the click event when the user clicks it).
In my controller I have several action methods that should be called by the view when the appropriate event occurs. (Like a Save action that should be called when the user clicks the save button, thus triggering the save button click event) (nothing out of the ordinary here;)

Whats the fuzz about this then, you may think? This is normal stuff, no strange things!? (who is this dumb guy anyway...)

Yes I see your point. I could easily use the Forms designer in Visual studio, double click the Save button and enter the code below to call the Save button.

private void saveButton_Click(object sender, System.EventArgs e) {
Controller.Save();
}
But thats not what I want. I don't want to litter the View with a lot of event methods just calling one line of code. The purpose of the MVC pattern is to separate the View from the logic and if we start putting those event handler methods in the view, soon some line of controller logic will be entered in there (just to test) and we'll never find the way to clean them up again.

My vision is to register these events one by one in the Views constructor, on one line that enables me to
  • Listen to an event from an object in the view
  • Capture the event and pass it to an action in the controller
  • Allow the mapping to pass additional parameters to the action (compare the routing in MVC for ASP.NET)
If I would make the same example like the one above but using the one liner instead, it would look like below.
Controller.RegisterAction(saveButton,"Save");
If I would pass some parameters to the action I could use the same syntax as when defining default routes in MVC for ASP.NET.
Controller.RegisterAction(createBoldTextButton,"CreateText", new {name="Bold",type=4});
This would then be mapped to the action CreateText which declaration looks like below.
public void CreateText(string name, int type) {...}
If I want to listen to an event that isn't the DefaultEvent (see the DefaultEventAttribute) I could state this as well in the call
Controller.RegisterAction(myTextBox,"ValidateText","Validating", null);
And if I declare the action to include the event argument and/or source they should be passed along as well
public void ValidateText(CancelEventArgs e, object source) {...}
How do I code this to be like I want it to then???

In my next post I will try to give you a solution to the requirements above and hopefully prove that I'm not a dumb guy after all ;)
See ya!

4 comments:

  1. Hi,
    What do you think about these implementations from codeplex?

    http://koosserymvcwin.codeplex.com/

    http://wrails.codeplex.com/

    http://winformsmvc.codeplex.com/

    ReplyDelete
  2. I haven't really looked at them so I cannot have an valid opinion. If you should work with WPF there is always the MVVM pattern from Microsoft that you could use. See this article for a readup.

    ReplyDelete
  3. Hi Dan,
    Do you have the code as part of a project file?

    I looked at koosserymvcwin.codeplex.com, it uses IoC and reflection both which are not a good solution for a production system. Reflection code is slow, just say no.

    ReplyDelete
    Replies
    1. Hi CodeWarrior,
      The code is part of a bigger project but hasn't been updated for several years, and reflection, yes, it's slow....
      For performance, there are better alternatives (lambda expressions or emit for instance), but when it comes to simplicity, it can't be much more straight forward...

      Delete