Sign in
  • Blog
  • Archive
  • About
  • Contact

Welcome to rickardnilsson.net

 

rickardnilsson.net is a weblog and the online home of web developer and father of three, Rickard Nilsson... More

Rickard blogs about creating software solutions using ASP.NET and agile practices.

Sites I've visited recently

Rtur.net / ASP.NET Forums / Karstad .NET User Group / JetBrains ReSharper / Scott Hanselman / dnrTV! / MSDN Radio / MIX'08 | Sessions / BlogEngine.Net / YUI Theater

Categories

  • RSS feed for .NET.NET
  • RSS feed for ASP.NET 2.0ASP.NET 2.0
  • RSS feed for BlogEngine.NETBlogEngine.NET
  • RSS feed for C# 2.0C# 2.0
  • RSS feed for C# 3.0C# 3.0
  • RSS feed for CSSCSS
  • RSS feed for Design by ContractDesign by Contract
  • RSS feed for Design PatternsDesign Patterns
  • RSS feed for JavaScriptJavaScript
  • RSS feed for TDDTDD
  • RSS feed for User tipUser tip

Five most recent posts

  • ReSharper User tip #2: Refactor rename namespace
  • Building a Photo Album widget for BlogEngine.NET
  • Applying stylesheets dynamically with jQuery
  • ReSharper User tip: Refactor magical strings to variable
  • Blogging with MS Word 2007

Tag cloud

  • ajax
  • blog
  • blogengine.net
  • c#
  • css
  • dbc
  • design by contract
  • dom
  • douglas crockford
  • foto
  • getweekofyear
  • gregoriancalendar
  • highlight
  • html
  • humble dialog box
  • iso 8601
  • javascript
  • jquery
  • jscript
  • julian bucknall
  • metaweblog api
  • model-view-presenter
  • mvp
  • photo album
  • picasa
  • recent posts
  • refactor
  • refactoring
  • release
  • resharper
  • rhino mocks
  • syntax
  • syntax highlighter
  • tdd
  • test coverage
  • types
  • web service
  • week
  • widget
  • word 2007
  • yahoo
  • yui

Recent comments

  • Syntax highlighting in BlogEngine.NET (9)
    darren wrote: thanks for this - got it up and running no problem… [More]
  • Syntax Highlighter Release 0.2 Beta (7)
    Rickard wrote: Line numbers are not supported in the current rele… [More]
  • Syntax Highlighter Release 0.2 Beta (7)
    Jack wrote: How can i show line numbers? [More]
  • Syntax Highlighter Release 0.2 Beta (7)
    Tahir Khalid wrote: Hi Rickard, thank you for replying to my humble bl… [More]
  • Building a Photo Album widget for BlogEngine.NET (5)
    Bryan Avery wrote: Great code, but you need to include the google lib… [More]
<< Syntax Highlighter Release 0.2 Beta | Problem with Related Posts in BlogEngine.NET 1.4 >>

The Humble dialog v.2

Tuesday, 15 July 2008 20:41 by Rickard
Update! Download source: TheHumbleDialog2.zip (29,96 kb)
I've been working to get my head around the Model-View-Presenter pattern and I've focused primarily on a Winforms scenario. As it turns out there are numerous sources out there on MVP, some of which are focused on Winforms, e.g. [2, 8] and some which are not focused on Winforms but highly valuable just the same, e.g. [1, 4].


Figure A. Screen mock up 

However, none of the referenced sources discusses the issue of communication between MVP triads. All of them thoroughly discusses the ins and outs when you have a single presenter talking to a single view and a single model, but nothing about communicating with other MVP sets. I've also found that other people are also struggling with this problem and how to best solve it but no really good solution has presented itself.

The problem

Well, we start with an example to formulate the problem. If we look at the example of the humble dialog [8] this only covers a single triad so we make it slightly more complicated. We have two views, a parent view and a dialog view, and each view has an associated presenter, i.e. parent presenter and dialog presenter. Now, the requirement is that when a user clicks a button on the main view the dialog should pop up modally giving the user the possibility to change a property value. The value change should then be reflected in the parent view. Sounds simple enough, right?

The problem is to implement this leveraging the MVP pattern to achieve a loosely coupled, highly cohesive, and test friendly solution. Some of the questions are; who has the responsibility to create the dialog view, who has the responsibility to show the view, what is the result from the dialog view and who should act on it.

MVP Solution

There are many different ways we can go about doing this but since I'm into Test-Driven Development I'm gonna do it test first. I also like to do UI up front so I create a Winforms project in Visual Studio and start dragging and dropping. The simplest possible screens are shown in figure A.

OK, now let's start with our first test. We want the dialog to appear when the "Edit user name" button is clicked and following MVP the view should simply pass on control to its presenter. The parent presenter should then have the dialog view popping up. This is the first test using Rhino Mocks [3]:

[Test]
public void ParentPresenter_EditUsername_should_open_a_dialog() {
    var view = mockery.DynamicMock<IParentView>();
    var dialogPresenter = mockery.CreateMock<IDialogPresenter>();
   
    using (mockery.Record()) {
        dialogPresenter.Load();
    }

    using (mockery.Playback()) {
        var presenter = new ParentPresenter(view)
                            {
                                DialogPresenter = dialogPresenter
                            };
        presenter.EditValue();
    }
}

The parent presenter should simply pass the control to the presenter of the dialog. Thus, the parent presenter has a dependency on the presenter of the dialog and the dependency is injected through a property:

var presenter = new ParentPresenter(view)
                    {
                        DialogPresenter = dialogPresenter
                    };

Now, these interfaces and classes doesn't exists so first we have to create them. But before we do I see some code that has no test, namely dialogPresenter.Load(), so let's write another test:

[Test]
public void DialogPresenter_Load_should_call_show_on_view() {
    var view = mockery.DynamicMock<IDialogView>();

    using (mockery.Record()) {
        view.Show();
    }

    using (mockery.Playback()) {
        var presenter = new DialogPresenter(view);
        presenter.Load();
    }
}

So, the presenter simply tells the view to show itself. Remember that we're talking to an interface and that the view in the test is not the real implementation. We're only driving out the behavior of the presenter at this point but we're doing it in such a way that the view implementation should be as thin a possible.

Code generation

I'm using ReSharper [7] to generate the interfaces and classes that I dreamed up in the tests.

More testing

When the initial tests are passing let's move on. The next thing that should happen is the user providing a new user name in the dialog and clicks on OK. So lets write a test for the OK event. I realize that some refactoring is also in place:

[Test]
public void DialogPresenter_ChangeName_should_change_value_and_close_dialog() {
    var model = mockery.CreateMock<User>();
    var callBack = mockery.CreateMock<Action>();
    var expectedName = "foo";

    using (mockery.Record()) {
        Expect.Call(dialogView.Username).Return(expectedName);
        dialogView.Close();
        callBack();
    }

    using (mockery.Playback()) {
        IDialogPresenter presenter = new DialogPresenter(dialogView)
                                         {
                                             Model = model
                                         };
        presenter.NameChanged += callBack;
        presenter.EditName();

        Assert.That(model.Name, Is.EqualTo(expectedName));
    }
}

Passive view and Observer synchronization

The last requirement states that the user name change should be reflected in the parent view. There are a couple of ways to do this but in this example I'm gonna go with the Observer synchronization [6] strategy because it provides a nice separation of concerns and is easy enough to mock. Note, however, that it is the presenter which acts as the Observable, not the domain object itself (User in this case). This is because I do not want to polute the domain model with event code. The previous test verified that a callback method is called whenever the name in the dialog is changed. Now, the parent presenter only has to hook up an event handler to the dialog presenter's event. Our next test shoes what should happen in the parent presenter when the event is triggered:

[Test]
public void ParentPresenter_Update_should_update_view() {
    var model = new User {Name = "foo"};

    using (mockery.Record()) {
        Expect.Call(parentView.Username = "foo");
    }

    using (mockery.Playback()) {
        var presenter = new ParentPresenter(parentView)
                            {
                                Model = model
                            };
        presenter.Update();
    }
}

So, the presenter explicitly tells the view what to do. The parent view is an example of a Passive view [5]. The presenter's code is included below for completeness:

// Parent Presenter
public class ParentPresenter {
    private readonly IParentView view;

    public ParentPresenter(IParentView view) {
        this.view = view;
        this.view.Presenter = this;
        Model = new User();
        DialogPresenter = new DialogPresenter(new DialogView())
                              {
                                  Model = Model
                              };
        DialogPresenter.NameChanged += Update;
    }

    public IDialogPresenter DialogPresenter { get; set; }
    public User Model { get; set; }

    public void Load() {
        view.Show();
    }

    public void EditValue() {
        DialogPresenter.Load();
    }

    public void Update() {
        view.Username = Model.Name;
    }
}

// Dialog Presenter
public class DialogPresenter : IDialogPresenter {
    public event Action NameChanged;
    private readonly IDialogView view;

    public DialogPresenter(IDialogView view) {
        this.view = view;
        this.view.Presenter = this;
    }

    public User Model { get; set; }

    public virtual void Load() {
        view.Show();
    }

    public void EditName() {
        Model.Name = view.Username;
        view.Close();

        if (NameChanged != null)
            NameChanged();
    }
}

And then the view implementation which is as minimalistic as possible:

// Parent View
public partial class ParentView : Form , IParentView {
    public ParentView() {
        InitializeComponent();
    }

    public ParentPresenter Presenter { private get; set; }

    public string Username {
        set { username.Text = value; }
    }

    void IParentView.Show() {
        Application.Run(this);
    }

    private void editValueButton_Click(object sender, EventArgs e) {
        Presenter.EditValue();
    }
}

// Dialog View
public partial class DialogView : Form , IDialogView {
    public DialogView() {
        InitializeComponent();
    }

    public IDialogPresenter Presenter { private get; set; }

    public string Username {
        get { return nameTextBox.Text; }
    }

    void IDialogView.Show() {
        ShowDialog();
    }

    void IDialogView.Close() {
        Close();
    }

    private void acceptButton_Click(object sender, EventArgs e) {
        Presenter.EditName();
    }
}

Conclusion
The way the MVP parts are separated makes a UI with allmost 100% test coverage but above all the UI logic is in a separate class which is not tied to a frame, thus enabling us to discover duplicated code and oportunities to refactor and keep the UI maintainable. I think that this is a great way of driving out a design since I start out with the client hat on and not the other way around were I first list a bunch of methods and properties on a class. IMO this kind of interaction based testing really lends itself to driving out interaction design, and tools like ReSharper really makes the developer experience pleasant and productive.
References

[1] Jean-Paul Boodhoo. Design Patterns: Model View Presenter
[2] Dan Bunea. Model View Presenter - is testing the presenter enough
[3] Oren Eini. Rhino Mocks - dynamic mocking framework
[4] Michael Feathers. The Humble Dialog Box
[5] Martin Fowler. Passive View
[6] Martin Fowler. Observer Synchronization
[7] JetBrains. ReSharper
[8] Jeremy Miller. A Simple Example of the "Humble Dialog Box"

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   model-view-presenter, mvp, humble dialog box, tdd, rhino mocks, resharper
Categories:   C# 3.0 | Design Patterns | TDD
Actions:   E-mail | Permalink | Kick it! | DZone it! | del.icio.us | Comments (3) | Comment RSSRSS comment feed

Related posts

Week and strong contracts in Design by Contact Answer to Fredrik Normén on Defensive programming and Design by Contract on a routine level...Driving out a correct implementation of ISO week numbers using TDD #4 Abstract  Refactoring Test coverage  Proceeding with a solution there is no wa...Abstract: Driving out a correct implementation of week numbers using TDD The implementation of weeks according to ISO 8601 (used e.g. in Sweden) is faulty in .NET Fra...

Comments

September 15. 2008 13:52

Marais van Zyl

Do you have the code sample for this article?

Thanks,

Marais

Marais van Zyl

September 16. 2008 00:31

Rickard

The source code is now appended at the top of the post. Thanks for showing interest and please share your thoughts on the article.

Rickard

November 13. 2008 11:47

pingback

Pingback from jlinx.de

JLinX - Blog » Handling Communication Between MVP Triads

jlinx.de

Saving the comment

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading



 
Copyright © 2008 rickardnilsson.net