WinForms中的MVP导航

3

我一直在学习MVP模式,并尝试使用WinForms编写测试应用程序。我很难找到一个解释得很好的示例来说明如何在我的表单/视图之间导航。例如,程序启动后,如果登录成功,我想显示一个登录对话框,然后进入主界面。目前,我的Main方法大致如下:

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
}

注入器对象只是一个IoC工具(目前使用的是StructureMap)的包装器。问题在于,我读到过不应该通过注入器手动创建实例,而应该通过构造函数注入来完成。
我已经做到了一定程度,但当涉及到导航时就无法想出一个优雅的方法。我想知道这里是否有人能够给我一些启示?我已经阅读了一些关于应用程序控制器的文章,但没有找到清晰的示例。

mainView 可能有一些方法来显示视图。 - techBeginner
你可以在mainView中插入一个返回实际表单的方法。 - Rockstart
这对我来说看起来不像MVP,你应该把逻辑放在Presenter中。 - Alex Burtsev
3个回答

4
关于导航问题: 我已经在某种程度上实现了这一点,但是当涉及到导航时却做不到优雅。我想知道是否有人能够为此提供一些帮助?我已经阅读了一些关于应用程序控制器的内容,但没有找到一个清晰的示例来说明它。 下面是我使用过的一个结构的简化版本。请注意,当调用NavigateTo方法时,设置和拆卸挂钩会自动调用。同时,向@AlexBurtsev表示+1,因为他的答案暗示了这个非常类似的方法。
// 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
   }

}

3
我已经很久没有使用WinForms了,但我会尝试回答这个问题。我会使用与WPF Prism相同的策略。
关于MainView和Application.Run: 创建一个主区域(根表单),内部有一个空容器,可以容纳UserControl(我忘记了确切的类名)。然后当你需要切换根视图时,你可以使用RootView.SetView(UserControl view)方法,该方法会执行类似Form.Clear(),Form.AddChild(view)的操作。
关于导航和使用容器: 您可以创建一个导航服务:INavigationService,将其注入构造函数中,使用方法如INavigationService.NavigateView(String(或Type)viewName,params object[] additionalData)。

谢谢。看了tcarvin的回答后,我明白你说的是什么了。 - woodstock

1

您可以在mainView中插入一个返回实际表单的方法。然后您就可以调用该方法。

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

在主函数中,你可以调用:
Application.Run(mainView.GetView())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接