tags:

views:

260

answers:

3

I'm learning to apply MVP to a simple WinForms app (only one form) in C# and encountered an issue while creating the main presenter in static void Main(). Is it a good idea to expose a View from the Presenter in order to supply it as a parameter to Application.Run()?

Currently, I've implemented an approach which allows me to not expose the View as a property of Presenter:

    static void Main()
    {
        IView view = new View();
        Model model = new Model();
        Presenter presenter = new Presenter(view, model);
        presenter.Start();
        Application.Run();
    }

The Start and Stop methods in Presenter:

    public void Start()
    {
        view.Start();
    }

    public void Stop()
    {
        view.Stop();
    }

The Start and Stop methods in View (a Windows Form):

    public void Start()
    {
        this.Show();
    }

    public void Stop()
    {
        // only way to close a message loop called 
        // via Application.Run(); without a Form parameter
        Application.Exit();
    }

The Application.Exit() call seems like an inelegant way to close the Form (and the application). The other alternative would be to expose the View as a public property of the Presenter in order to call Application.Run() with a Form parameter.

    static void Main()
    {
        IView view = new View();
        Model model = new Model();
        Presenter presenter = new Presenter(view, model);
        Application.Run(presenter.View);
    }

The Start and Stop methods in Presenter remain the same. An additional property is added to return the View as a Form:

    public void Start()
    {
        view.Start();
    }

    public void Stop()
    {
        view.Stop();
    }

    // New property to return view as a Form for Application.Run(Form form);
    public System.Windows.Form View
    {
        get { return view as Form(); }
    }

The Start and Stop methods in View (a Windows Form) would then be written as below:

    public void Start()
    {
        this.Show();
    }

    public void Stop()
    {
        this.Close();
    }

Could anyone suggest which is the better approach and why? Or there even better ways to resolve this issue?

+3  A: 

I would go for the second approach. You could also get rid of the extra property by simply casting view to form in the void Main, since you know it is a form anyway at that point (I see no reason to make it more generic than that since it just starts the winform app)

Application.Run(view as Form);
Roger Alsing
+3  A: 

What about the following:

// view
public void StartApplication() // implements IView.StartApplication
{ 
    Application.Run((Form)this);
}

// presenter
public void StartApplication()
{
    view.StartApplication();
}

// main
static void Main()     
{     
    IView view = new View();     
    Model model = new Model();     
    Presenter presenter = new Presenter(view, model);     
    presenter.StartApplication();     
}     

That way, you don't need to expose the view to the outside. In addition, the view and the presenter know that this view has been started as a "main form", which might be a useful piece of information.

Heinzi
IMO, Neither of the presenter or the view interfaces should know how to kickstart the windows message pump.You would only bloat the IView interface with a method that doesn't have to exist, that you would have to implement if you are writing tests against fake views.
Roger Alsing
Would creating another interface for the view and presenter e.g IMainView, IMainPresenter to specifically handle only StartApplication() and StopApplication(). So View will implement Iview and IMainView, Presenter will implement IMainPresenter.
Mr Roys
@Mr Roys: That's a nice idea. You could even make IMainView and IMainPresenter subinterfaces of IView and IPresenter, since an IMainView will always be an IView (same for Presenter).
Heinzi
+1  A: 

Things get a bit more complex if you allow more than one way to exit the application (e.g.: a menu item for exiting), or if you prevent closing of the application under certain conditions. In either case, the actual invocation of application closing should usually be invoked from presenter code rather than by simply closing the concrete view. This can be accomplished by using either the Application.Run() or Application.Run(ApplicationContext) overloads and exposing the application exit action via inversion of control.

The exact approach to registering and using the application exit action would depend on the IoC mechanism (e.g.: service locator and/or dependency injection) that you are using. Since you haven't mentioned what your current IoC approach might be, here's a sample that's independent of any particular IoC frameworks:

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        ApplicationActions.ExitApplication = Application.Exit;

        MainPresenter mainPresenter = new MainPresenter(new MainView(), new Model());
        mainPresenter.Start();

        Application.Run(); 
    }
}

public static class ApplicationActions
{
    public static Action ExitApplication { get; internal set; }
}

public class MainPresenter : Presenter
{
    //...

    public override void Stop()
    {
        base.Stop();

        ApplicationActions.ExitApplication();
    }
}

This basic approach could be adapted quite easily to your preferred IoC approach. For example, if you're using a service locator, you would probably want to consider removing at least the setter on the ApplicationActions.ExitApplication property, and storing the delegate in the service locator instead. If the ExitApplication getter were to remain, it would provide a simple façade to the service locator instance retriever. e.g.:

public static Action ExitApplication
{
    get
    {
        return ServiceLocator.GetInstance<Action>("ExitApplication");
    }
}
Nicole Calinoiu
Originally, I intended to place the Application.Exit() in presenter.Stop() but then decided against it because that would create a dependency on the technology used to implement the view e.g WinForms within the presenter. I'll look into IoC in the meantime - any way to illustrate it with some pseudocode?
Mr Roys
For which portions of the implementation would you like an example?
Nicole Calinoiu
The IoC part for the Application.Exit() section would be nice. Thanks!
Mr Roys
I've added a code sample to the answer above. Given that you mentioned above that you chose to go with a MainPresenter, IMainView approach, I've incorporated that choice to allow the presenter to know that it ought to exit the application as part of its stopping.
Nicole Calinoiu
Hi Nicole, sorry that I did not wait for your sample code before deciding on the accepted answer as I had to go onsite that day. Thank you for the sample code, much appreciated :)
Mr Roys