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

gre*_*ort 3 c# wpf mvvm caliburn.micro

  1. 我的历史:

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

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

  1. 项目设想:

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

我有1个窗口和3个UserControl(和ViewModels)概念图

 Window ShellView
     UserControl LoginView
     UserControl OrdersView
     UserControl OrderDetailView
Run Code Online (Sandbox Code Playgroud)

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

我已经读过:这个问题,但这并不涵盖我的案子和答案,但是对我来说很难理解。

我的想法:

  • 在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();
Run Code Online (Sandbox Code Playgroud)
  • 在ShellViewModel中创建侦听器并从子ViewModel发送事件(但现在我不知道如何实现)

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

  1. 应用架构:

壳牌

<Window>
   <ContentControl x:Name="ActiveItem" />
</Window>
Run Code Online (Sandbox Code Playgroud)

ShellViewModel

public class ShellViewModel : Conductor<object>, IShell
{

    public ShellViewModel()
    {
        LoadDefault();
    }    

    public void LoadDefault()
    {
        ActivateItem(new LoginViewModel());
    }
}
Run Code Online (Sandbox Code Playgroud)

登录查看

<UserControl>
    <Button x:Name="Login" />
</UserControl>
Run Code Online (Sandbox Code Playgroud)

LoginViewModel

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

Vla*_*sev 6

我有一个外壳窗口,多个激活的视图以及一些对话框窗口,它们具有类似的应用程序。你应该用 EventAggregator模式满足这些需求,并且Caliburn已经实现。

如何实现:

最小Shell签名

public class ShellViewModel : Conductor<object>,
    IHandle<ChangePageMessage>,
    IHandle<OpenWindowMessage>
Run Code Online (Sandbox Code Playgroud)

您需要在其中两个字段(第二个字段用于对话框):

public IEventAggregator EventAggregator { get; private set; }
public IWindowManager WindowManager { get; private set; }
Run Code Online (Sandbox Code Playgroud)

我已经通过IoC设置了该对象的单个实例。您也可以将它们定义为Singletons。EventAggregator需要在实现的对象上进行预订IHandle。

EventAggregator.Subscribe(this); //You should Unsubscribe when message handling is no longer needed
Run Code Online (Sandbox Code Playgroud)

处理程序实现:

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);
}
Run Code Online (Sandbox Code Playgroud)

用于事件聚合器的消息只能是标记类,但是有时传递更多的参数(例如我们的OpenWindowMessage和ChangePageMessage类)很有用-它们在内容上绝对相似,因此:

public class OpenWindowMessage {

    public readonly Type ViewModelType;

    public OpenWindowMessage(Type viewModelType) {
        ViewModelType = viewModelType;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

EventAggregator.Publish(new ChangePageMessage(typeof(MyViewModel)));
EventAggregator.Publish(new MyViewModelInitMessage("...all needed parameters"));
Run Code Online (Sandbox Code Playgroud)

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

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

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