tags:

views:

326

answers:

3

I have been playing around with the MVP pattern using winforms for the last couple of days, there is only really one thing that I'm not sure how to do. How do you create a subform from another view. Would this be a valid option.

 public class MyForm : IMainFormView
    {
        private MainFormPresenter pres;
        public MyForm() { pres = new MainFormPresenter(this); }

        //Event from interface.
        public event EventHandler<EventArgs> LoadSecondForm;

        public void SomeButtonClick()
        {
            LoadSecondForm(this, EventArgs.Empty);
        }
    }

    public class MainFormPresenter
    {
        private IMainFormView mView;

        public MainFormPresenter(IMainFormView view) {
            this.mView = view;
            this.Initialize();
        }

        private void Initialize() {
            this.mView.LoadSecondForm += new EventHandler<EventArgs>(mView_LoadSecondForm);
        }


        private void mView_LoadSecondForm(object sender, EventArgs e) {
            SecondForm newform = new SecondForm(); //Second form has its own Presenter.
            newform.Load(); // Load the form and raise the events on its presenter.
        }
    }

I'm mainly concerned with how you would load a subform using this pattern, and how you would pass say an ID from the first page to the subform.

Thanks.

+4  A: 

http://www.rickardnilsson.net/post/The-Humble-dialog-v2.aspx

The link above is the closest I've seen to answering this. Most attempts at solving this leave something to be desired.

codeelegance
A: 

Take a look at this other SO question. While it relates to WPF and not WinForms, the problem seems to be the same.

Essentially, I consider the need to show a subsidiary window a Service (you might call it the WindowsService or DialogService or something like that). That helps you to put things into perspective, because as soon as you realize that, Dependency Injection becomes the answer.

In your answer, you model this with events, but I prefer a more explicit model where you invoke a DialogService's ShowDialog method. However, the mechanics of each approach aren't that different.

Mark Seemann
A: 

A couple of comments on this

First This

    private void mView_LoadSecondForm(object sender, EventArgs e) {
        SecondForm newform = new SecondForm(); //Second form has its own Presenter.
        newform.Load(); // Load the form and raise the events on its presenter.
    }

What happens if you decide to substitute ThirdForm for SecondForm? You need to find every call of newform = new SecondForm and make the change.

Instead you should wrap the creation of the SecondForm in a Command Object

   public class CreateSecondForm : ICommand
    {

        public void Execute() {
            SecondForm newform = new SecondForm(); //Second form has its own Presenter.
            newform.Load(); // Load the form and raise the events on its presenter.
        }

    }

Then here and any other place brings up second form uses this syntax

private void mView_LoadSecondForm(object sender, EventArgs e) {
    CreateSecondForm createCmd = new CreateSecondForm(); 
    createCmd.Execute(); // Load the form and raise the events on its presenter.
}

If you want to subsitute a completely new form for SecondForm then you have only one place you need to go. If you want to pass in status or setup values then use the constructor of the command. You can even pass in another Presenter or View and have the command pull the information from it's interface.

Another thing I recommend is that you register the forms that implements your views instead of using the new command. This is done during initialization and the registry hangs off of your main application class.

For example.

public class MySecondForm : ISecondFormView, IViewForm
    {
        //Stuff ....
       Public ViewFormEnum ViewFormType {
          return ViewFormEnum.SecondForm;
       }
        //Stuff ....
    }

elsewhere in the software

public void InitializeApp() {
        //Stuff ....
        MyApp.ViewForm.Add(new MySecondForm);

        //Stuff ....

}

Then the command is setup like this.

   public class CreateSecondForm : ICommand
    {
        myApp theApp;
        public CreateSecondForm(myApp thisApp) {
           theApp = thisApp;
        }

        public void Execute() {
            SecondForm newform = theApp.Find(ViewFormEnum.SecondForm);
            if (newForm != null) 
               newform.Load(); // Load the form and raise the events on its presenter.
        }

    }

Pardon my C# it is not my primary language. The advantage of this approach is that the assembly containing the Forms can swapped out for a different assembly with a different set of Forms. This is especially useful for automating testing where mock classes can be made instead of the form. You can also set it up to handle null Views which is useful for releasing a subset of your full application.

While I strongly recommend you use the command to wrap the creation of your Views. The second suggestion of registering View may be overkill depending on the application. In my CAD/CAM application I have dozens of dialogs and several different main forms used for different aspects of setting up and controlling a 2D metal cutting table. However in some of my company's other application I use a simple approach as they are mostly simple utilities.

RS Conley