如何使用 MVVM Light for WPF 在窗口中导航?

Posted

技术标签:

【中文标题】如何使用 MVVM Light for WPF 在窗口中导航?【英文标题】:How to navigate through windows with MVVM Light for WPF? 【发布时间】:2012-03-06 14:39:40 【问题描述】:

我刚刚开始了一个新项目,其中表示层将由 WPF 完成,MVVM Light 由 GalaSoft 完成。

我需要很多视图,但我不清楚如何通过窗口管理导航。

首先,MVVM Light 中提供的用于创建新“WPF MVVM 视图”的模板会创建一个新的 Window,它不能用于逐帧导航(我的意思是,通过在 mainView 中放置一个框架)并更改要导航的源路径)。

对于我使用模板创建的所有视图,我是否只需将Window 更改为Page

或者是否有其他方法可以使用 MVVM Light 工具包在 WPF 中执行导航?

【问题讨论】:

【参考方案1】:

我通常使用ContentControl 来显示动态内容。它的Content 属性通常绑定到父ViewModel 中的CurrentViewModel 属性,而DataTemplates 用于告诉WPF 如何绘制子ViewModels

要更改视图,只需更改父 ViewModel 中的 CurrentViewModel 属性

你可以在this article of mine找到一个例子

【讨论】:

嗨,Rachel,如果您可以在链接之外发布一个如何实现此功能的最小示例,那就太好了。它只是帮助像我这样的人在 5 年后得到这个答案,以防链接由于任何特定原因不再有效 @Ortund this SO answer 有帮助吗?我写这个答案已经有一段时间了,但我认为其他答案的代码应该是一个合适的例子。【参考方案2】:

最终我是这样做的。

按照 o_q 的思路,我将 NavigationWindow 创建为 MainWindow,并将所有视图更改为 page。

然后,我创建了一个使用 Navigation 的接口和一个类:

    public interface INavigationService
    
        event NavigatingCancelEventHandler Navigating;
        void NavigateTo(Uri pageUri);
        void GoBack();
    

    public class NavigationService : INavigationService
    
        private NavigationWindow _mainFrame;

        #region Implementation of INavigationService

        public event NavigatingCancelEventHandler Navigating;
        public void NavigateTo(Uri pageUri)
        

            if (EnsureMainFrame())
            
                _mainFrame.Navigate(pageUri);
            

        

        public void GoBack()
        
            if (EnsureMainFrame()
                && _mainFrame.CanGoBack)
            
                _mainFrame.GoBack();
            

        

        #endregion

        private bool EnsureMainFrame()
        
            if (_mainFrame != null)
            
                return true;
            

            _mainFrame = System.Windows.Application.Current.MainWindow as NavigationWindow;

            if (_mainFrame != null)
            
                // Could be null if the app runs inside a design tool
                _mainFrame.Navigating += (s, e) =>
                
                    if (Navigating != null)
                    
                        Navigating(s, e);
                    
                ;

                return true;
            

            return false;
        

    

然后,在 viewModelLocator 中,我创建了所有用于存储视图路径的 const 字符串:

    public class ViewModelLocator
    

        #region Views Paths

        public const string FrontendViewPath = "../Views/FrontendView.xaml";
        public const string BackendViewPath = "../Views/BackendView.xaml";
        public const string StartUpViewPath = "../Views/StartUpView.xaml";
        public const string LoginViewPath = "../Views/LoginView.xaml";
        public const string OutOfOrderViewPath = "../Views/OutOfOrderView.xaml";
        public const string OperativeViewPath = "../Views/SubViews/OperativeView.xaml";
        public const string ConfigurationViewPath = "../Views/SubViews/ConfigurationView.xaml";
        #endregion
     

在 App.cs 的 Application_Startup 事件处理程序中,在 Unity IoC 的帮助下,我注册了一个 NavigationService 的单例:

    public partial class App : System.Windows.Application
    

        private static IUnityContainer _ambientContainer;
        public static IServiceLocator AmbientLocator  get; private set; 

        ...

       private void Application_Startup(object sender, System.Windows.StartupEventArgs e)
                  

           _ambientContainer =
               new UnityContainer();

           _ambientContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());

           AmbientLocator = new UnityServiceLocator(_ambientContainer);
           ServiceLocator.SetLocatorProvider(() => AmbientLocator);

现在,在我的 ViewModelLocator 中,我可以注册一个“Galasoft”消息来捕获所有事件并导航到一个页面;在我的构造函数中:

        public ViewModelLocator()
        
            CreateMain();
            CreateFrontend();
            CreateBackend();
            CreateStartUp();
            CreateOperative();
            CreateLogin();
            CreateConfiguration();
            CreateOutOfOrder();


            // Set Startup Page...
            ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));

            Messenger.Default.Register<MoveToViewMessage>(this, message =>
            
                switch (message.StateInfo.StateType)
                
                    case StateType.StartUpState:

                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath,UriKind.Relative));
                        break;
                    case StateType.LoginState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(LoginViewPath, UriKind.Relative));
                        break;
                    case StateType.OperativeState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OperativeViewPath, UriKind.Relative));
                        break;
                    case StateType.ConfigurationState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(ConfigurationViewPath, UriKind.Relative));
                        break;
                    case StateType.ClosedState:
                    case StateType.OutOfOrderState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OutOfOrderViewPath, UriKind.Relative));
                        break;
                    default:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));
                        break;
                
            );

        

通过这种方式,我让所有的 viewModel 保持“无知”......他们对导航一无所知,而且我没有代码。

如果我需要使用视图中的按钮进行导航,我可以从连接的 viewModel 解析 NavigationService 并导航到我需要的页面。

而且,最重要的是,它有效!

【讨论】:

我注意到 ViewModelLocator 构造函数在您的示例中不是静态的,但在默认实现中它是静态的...【参考方案3】:

对于可导航的应用程序,您会希望您的启动视图是 NavigationWindow 而不是 Window

<NavigationWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    x:Class="MainWindow"
    Title="My Application Title"
    Height="300"
    Width="400" />

后面的代码:

using System.Windows.Navigation;

public partial class MainWindow : NavigationWindow

    public MainWindow()
    
        InitializeComponent();
    

MVVM Light 视图模板使用Window,但正如您所猜测的,您可以更改它。如果您希望能够在此视图之间导航,请将其设为Page。 这就是您导航的方式:

<Page
    x:Class="Page1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Page1">
    <Grid>
        <!-- this button will navigate to another page -->
        <Button
            Content="Go to Page 2"
            Click="Button_Click" />
    </Grid>
</Page>

代码背后:

using System.Windows;
using System.Windows.Controls;

public partial class Page1 : Page

    public Page1()
    
        InitializeComponent();
    

    private void Button_Click(object sender, RoutedEventArgs e)
    
        // the Page class has a property "NavigationService" which allows you to navigate.
        // you can supply the "Navigate" method with a Uri or an object instance of the page 
        base.NavigationService.Navigate(new Page2());
    

【讨论】:

这可能是一种可能性,即使我不喜欢将代码放在代码后面。此外,我忘了告诉你,我的应用程序将成为自动售货机的人机界面,因此能够以更简单的方式浏览页面对我来说至关重要,例如通过从较低层发送事件(用户无法与表示层交互,因为没有鼠标、没有触摸屏、没有键盘等等。)。是否可以通过这种方式从 ViewModel 导航页面? @zero51 关于在视图模型中导航的问题,我在这里发布了解决方案:How to navigate from one view to another view from viewmodel in silverlight? 我在 Galasoft 网站 link 找到了这篇文章。它解释了如何在 Windows phone 7 中实现导航...我将尝试在 WPF 中使用导航实现它。 @Zero51:你有没有解决这个问题。我也在寻找一个例子,但一直无法这样做? @user101010101:我实现了导航,正如我在回答中解释的那样。它就像一个魅力!

以上是关于如何使用 MVVM Light for WPF 在窗口中导航?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MVVM Light Toolkit 打开一个新窗口

MVVM Light和MVVM有什么区别?

MVVM Light Toolkit 示例 [关闭]

用户控件中的Mvvm light SimpleIoC

MVVM Light:在 XAML 中添加 EventToCommand 而不使用 Blend、更简单的方法或片段?

我在使用 MVVM for WPF 实现命令处理器模式时遇到问题