MVP навигация в WinForms


Изучали MVP и пытались написать тестовое приложение, используя его в WinForms. Я изо всех сил пытаюсь найти хорошо объясненный пример того, как перемещаться между моими формами/представлениями. В качестве примера, программа запускается, и я хочу показать диалоговое окно входа, а затем перейти в мой основной вид, если вход был успешным. На данный момент мой основной метод выглядит примерно так:

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). Дело в том, что я читал, что я ... на самом деле не следует вручную создавать экземпляры с помощью инжектора, поскольку они действительно должны быть сделаны с помощью инъекции конструктора.

Мне удалось сделать это до определенного момента, но не тогда, когда дело доходит до навигации. Я не могу придумать элегантный способ перемещения по своим взглядам, и мне интересно, может ли кто-нибудь здесь пролить свет на это? Я немного читал о контроллерах приложений, но не нашел примера, чтобы показать это ясно.

3 3

3 ответа:

Что касается навигационного вопроса:

Мне удалось сделать это до определенного момента, но не тогда, когда дело доходит до навигация. Я не могу придумать элегантный способ продвижения через мои взгляды и задавался вопросом, Может ли кто-нибудь здесь пролить свет на это? Я почитал немного о контроллерах приложений но не нашел примера чтобы показать это ясно.

Ниже приведена упрощенная версия конструкции, которую я использовал. Обратите внимание, что установку и демонтаж крючки называются автоматически при вызове метода NavigateTo. Кроме того, +1 к @AlexBurtsev, поскольку его ответ намекает на этот самый подход.

// 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
   }

}

Я уже давно не использую WinForms, но постараюсь ответить на этот вопрос. Я бы использовал ту же стратегию, что и WPF Prism.

О MainView и приложении.Бежать: Создайте основную область (корневую форму), с пустым контейнером внутри которого может храниться UserControl (я забыл точные имена классов), а затем, когда вам нужно переключить корневой вид, вы делаете RootView.SetView (UserControl view), который будет делать что-то вроде формы.четкая форма.AddChild (вид).

О навигации и использовании контейнера: Ты мог бы создайте сервис для навигации: INavigationService, который вы вводите в конструкторы с помощью метода, подобного INavigationService.NavigateView (String (or Type) viewName, params object [] additionalData)

В mainView можно вставить метод, который возвращает фактическую форму.Тогда вы можете позвонить

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

В Main вы можете позвонить,

Application.Run(mainView.GetView())