简体   繁体   中英

MVP Navigation in WinForms

Have been learning about MVP and have tried writing a test app using it in WinForms. I'm struggling to find a well explained example on how to navigate between my forms/views. As an example, the program starts and I want to show a login dialog then go into my main view if the login was successful. At the moment, my Main method looks something like this:

static void Main()
{
   var loginView = Injector.Resolve<ILoginView>();
   if (loginView.DoLogin() != LoginResult.OK) return;
   var mainView = Injector.Resolve<IMainView>();
   Application.Run(mainView); // won't work as mainView isn't a form
}

The Injector object is just a wrapper around an IoC tool (currently StructureMap). The thing is, I've read that I shouldn't really be manually creating instances via the Injector as they should really be done via constructor injection.

I've managed to do this up to a point but not when it comes to navigation. I can't think of an elegant way of moving through my views and was wondering if anyone here might shed some light on this? I've read a little on application controllers but have not found an example to show it clearly.

In regards to the navigation question:

I've managed to do this up to a point but not when it comes to navigation. I can't think of an elegant way of moving through my views and was wondering if anyone here might shed some light on this? I've read a little on application controllers but have not found an example to show it clearly.

Below is a simplified version of a construct I've used. Note that the setup and tear down hooks are called automatically when the NavigateTo method is called. Also, +1 to @AlexBurtsev, as his answer hints at this very same approach.

// Presenter can and should offer common services for the
// subclasses 
abstract class Presenter
{

   public void Display()
   {
      OnDisplay();
   }

   public void Dismiss()   
   {
      OnDismiss();
   }


   protected virtual OnDisplay() // hook for subclass
   {   
   }

   protected virtual OnDismiss() // hook for subclass
   {   
   }

   private NavigationManager _navMgr;

   internal NavigationMgr NavigationManager
   {   
      get
      {
         return _navMgr;
      }
      set
      {
         _navMgr = value;
      }

   }

}

// NavigationManager is used to transition (or navigate) 
// between views
class NavigationManager
{

   Presenter _current;

   // use this override if your Presenter are non-persistent (transient)
   public void NavigateTo(Type nextPresenterType, object args)
   {   
      Presenter nextPresenter = Activator.CreateInstance(nextPresenterType);   
      NavigateTo(nextPresenter);   
   }

   // use this override if your Presenter are persistent (long-lived)
   public void NavigateTo(Presenter nextPresenter, object args)
   {
      if (_current != null)
      {
         _current.Dismiss()
         _current.NavigationMgr = null;
         _current = null;
      }

      if (nextPresenter != null)
      {
         _current = nextPresenter;
         _current.NavigationMgr = this;
         _current.Display(args);
      }         
   }

}


class MainMenuPresenter : Presenter
{

   private IMainMenuView _mainMenuView = null;

   // OnDisplay is your startup hook
   protected override void OnDisplay()
   {
      // get your view from where ever (injection, etc)
      _mainMenuView = GetView();      

      // configure your view
      _mainMenuView.Title = GetMainTitleInCurrentLanguage();
      // etc      
      // etc

      // listen for relevent events from the view
      _mainMenuView.NewWorkOrderSelected += new EventHandler(MainMenuView_NewWorkOrderSelected);

      // display to the user
      _mainMenuView.Show();
   }

   protected override void OnDismiss()
   {
      // cleanup
      _mainMenuView.NewWorkOrderSelected -= new EventHandler(MainMenuView_NewWorkOrderSelected);
      _mainMenuView.Close();
      _mainMenuView = null;
   }

   // respond to the various view events
   private void MainMenuView_NewWorkOrderSelected(object src, EventArgs e)
   {
      // example of transitioning to a new view here...
      NavigationMgr.NavigateTo(NewWorkOrderPresenter, null);            
   }

}


class NewWorkOrderPresenter : Presenter
{

   protected override void OnDisplay()
   {
      // get the view, configure it, listen for its events, and show it
   }

   protected override void OnDismiss()
   {
      // unlisten for events and release the view
   }

}

I haven't used WinForms for a long time, but I'll try to answer this. I would use the same strategy as WPF Prism do.

About MainView and Application.Run: Create a main Region (root Form), with empty container inside which can hold UserControl (I forgot exact class names), then when you need to switch root view, you do RootView.SetView(UserControl view) which will do something like Form.Clear(), Form.AddChild(view).

About the navigation and using container: You could create a service for navigation: INavigationService which you inject in constructors with method like INavigationService.NavigateView(String(or Type) viewName, params object[] additionalData)

You can insert a method in mainView that returns the actual form.Then you can call

Mainview:IMainView 
{
        Form GetView()
        {
              //return new Form();
        }
 }

In Main you can call ,

Application.Run(mainView.GetView())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM