WPF Caliburn.Micro - 在Singe Window应用程序中导航的最佳方式

时间:2015-05-14 12:41:26

标签: c# wpf mvvm caliburn.micro

  1. 我的历史:
  2. 我正在开发一个WPF应用程序,它将在触摸屏上全屏运行。我的应用程序中的导航只能通过单击每个页面上的按钮来完成("返回"或"注销")。

    这不是通用应用程序,但它看起来像。

    1. 项目假设:

      • 应用程序将在触摸屏上的Windows 7中以全屏模式运行。
      • 我正在使用Caliburn.Micro MVVM框架。
    2. 问题和疑问:

    3. 我有1个窗口和3个UserControl(和ViewModels)Concept art

       Window ShellView
           UserControl LoginView
           UserControl OrdersView
           UserControl OrderDetailView
      

      当应用程序启动时,我将LoginView设置为默认值并使用CM Conductor ActivateItem方法加载它,但我不知道如何从UserControl设置另一个View,如LoginView

      我已阅读:this question但这并不能解决我的问题 和this answer,但我很难理解。

      我的想法:

      • 在ShellViewModel中创建静态方法,如:

      ShellViewModel

      public static void setOrdersView() {
          ActivateItem(new OrdersViewModel());
          // Error : An object reference is required for the non-static field, method, or property 'Caliburn.Micro.ConductorBase<object>.ActivateItem(object)
      }
      ShellViewModel.setOrdersView();
      
      • 在ShellViewModel中创建侦听器并从子ViewModel发送事件(但现在我不知道如何实现它)

      问题:在这种情况下处理导航的最佳方式是什么?

      1. 应用程序架构:
      2. ShellView

        <Window>
           <ContentControl x:Name="ActiveItem" />
        </Window>
        

        ShellViewModel

        public class ShellViewModel : Conductor<object>, IShell
        {
        
            public ShellViewModel()
            {
                LoadDefault();
            }    
        
            public void LoadDefault()
            {
                ActivateItem(new LoginViewModel());
            }
        }
        

        LoginView

        <UserControl>
            <Button x:Name="Login" />
        </UserControl>
        

        LoginViewModel

        public class LoginViewModel : PropertyChangedBase
        {
            public void Login() {
                if (LoginManager.Login("User", "Password")) {
                    // How to redirect user to OrdersView?
                }
            }
        }
        

1 个答案:

答案 0 :(得分:5)

我有一个类似的应用程序,一个shell窗口和许多激活的视图内部和一些对话框窗口。 你应该使用 EventAggregator 模式来满足这些需求,Caliburn已经实现了。

如何实现

最低Shell签名

public class ShellViewModel : Conductor<object>,
    IHandle<ChangePageMessage>,
    IHandle<OpenWindowMessage>

你需要两个字段(第二个用于对话框):

public IEventAggregator EventAggregator { get; private set; }
public IWindowManager WindowManager { get; private set; }

我通过IoC设置了这些对象的单个实例。您也可以将它们定义为单身人士。 EventAggregator需要对实现IHandle的对象进行订阅。

EventAggregator.Subscribe(this); //You should Unsubscribe when message handling is no longer needed

处理程序实现:

public void Handle(ChangePageMessage message) {
    var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
    ActivateItem(instance);
}

public void Handle(OpenWindowMessage message) {
    var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
    WindowManager.ShowWindow(instance);
}

事件聚合器的消息只能是标记类,但有时传递更多参数(例如我们的OpenWindowMessageChangePageMessage类很有用 - 它们与内容完全相似,例如:

public class OpenWindowMessage {

    public readonly Type ViewModelType;

    public OpenWindowMessage(Type viewModelType) {
        ViewModelType = viewModelType;
    }
}

您的所有viewModel也可以订阅EventAggregator实例并处理一些消息以进行通信,甚至是初始参数。我几乎为每个viewModel都有类似MyViewModelInitMessage类的东西,并且只使用两个发布方法。

EventAggregator.Publish(new ChangePageMessage(typeof(MyViewModel)));
EventAggregator.Publish(new MyViewModelInitMessage("...all needed parameters"));

因此,当我发布这两个时 - 我的ViewModel将被激活,然后它会订阅EventAggregator不要忘记这样做或第二个消息处理永远不会发生 ),并将在之后处理InitMessage

现在使用 EventAggregator ,您可以在当前订阅它的所有ViewModel之间发送消息。

这似乎是非常常见的解决方案。