【问题标题】:Where does the navigation logic belong, View, ViewModel, or elsewhere?导航逻辑属于哪里,View、ViewModel 还是其他地方?
【发布时间】:2011-04-20 22:13:06
【问题描述】:

我是视图中的一个按钮,绑定到 ViewModel 的 ICommand 属性(实际上是来自 mvvv-light 的 RelayCommand)

如果用户点击我想要导航到新视图的按钮。当然 NavigationService 是 View 的一部分,而不是 ViewModel。这意味着导航是视图的责任?但在我的情况下,单击按钮时我将要查看的视图取决于很多因素,包括登录用户是谁、数据库所处的状态等......当然视图不应该需要所有这些信息。

执行 NavigationService.Navigate 调用的首选选项是什么?

【问题讨论】:

    标签: mvvm silverlight-4.0 mvvm-light wcf-ria-services


    【解决方案1】:

    如果您已经在使用 MVVM Light,则一种选择是利用它包含的消息总线。因此,您将按钮绑定到视图模型上的 RelayCommand,正如您已经说过的那样。在 RelayCommand 的处理程序中,您可以决定导航到哪个视图。这会将所有逻辑保留在视图模型中。

    一旦您的命令处理程序决定导航到哪个视图,它就可以在消息总线上发布消息。您的视图将监听该消息,然后使用 NavigationService 实际执行导航。所以除了等待被告知导航到某个地方,然后导航到它被告知的地方之外,它没有做任何事情。

    我一直在通过定义我的视图模型可以发布的 NavigationMessage 类和我的视图继承的包含侦听器的视图基类来做到这一点。 NavigationMessage 如下所示:

    public class NavigationMessage : NotificationMessage
    {
        public string PageName
        {
            get { return base.Notification; }
        }
    
        public Dictionary<string, string> QueryStringParams { get; private set; }
    
        public NavigationMessage(string pageName) : base(pageName) { }
    
        public NavigationMessage(string pageName, Dictionary<string, string> queryStringParams) : this(pageName)
        {
            QueryStringParams = queryStringParams;
        }
    }
    

    这允许简单地传递页面名称,或者还可以选择包含任何必要的查询字符串参数。 RelayCommand 处理程序会像这样发布此消息:

    private void RelayCommandHandler()
    {
        //Logic for determining next view, then ...
        Messenger.Default.Send(new NavigationMessage("ViewToNavigate"));
    }
    

    最后,视图基类如下所示:

    public class BasePage : PhoneApplicationPage
    {
        public BasePage()
        {
            Messenger.Default.Register<NavigationMessage>(this, NavigateToPage);
        }
    
        protected void NavigateToPage(NavigationMessage message)
        {
            //GetQueryString isn't shown, but is simply a helper method for formatting the query string from the dictionary
            string queryStringParams = message.QueryStringParams == null ? "" : GetQueryString(message);
    
            string uri = string.Format("/Views/{0}.xaml{1}", message.PageName, queryStringParams);
            NavigationService.Navigate(new Uri(uri, UriKind.Relative));
        }
    }
    

    这是假设所有视图都位于应用程序根目录中的“视图”文件夹中的约定。这适用于我们的应用程序,但当然可以扩展它以支持您如何组织视图的不同场景。

    【讨论】:

    • 我承认我对 MVVM-Light 提供的 Messenger 系统不太熟悉。进一步思考,这不意味着所有的视图都会注册并听到这个 NavigationMessage 吗?跨度>
    • 我想这可能是个问题。我在 Windows Phone 7 应用程序的上下文中使用此技术,我一次只有一个视图处于活动状态,因此它运行良好。如果您正在使用 Silverlight 或 WPF 的桌面版本,并且您同时有多个处于活动状态的视图,我可以看到这可能是一个问题。将不得不考虑更多。
    • 您可能还想查看 Caliburn Micro 框架:caliburnmicro.codeplex.com。它包括一个 INavigationService 实现,您可以将其注入到您的视图模型中,类似于 Jon 在他的回答中描述的内容。
    【解决方案2】:

    警告:自以为是的 MVVM 新手警报 :)(我对 MVVM 很陌生,但到目前为止非常享受。)

    好问题。我发现模拟NavigationService 并将INavigationService 传递给ViewModel 是完全可行的(如果有些地方有点丑)。事实上,您甚至可以使用泛型使接口稍微好一点,以传入 type(作为类型参数)而不是字符串 URI。

    但是,我发现在将导航中涉及的额外数据放置在何处时,我有些不知所措...我还没有找到一个好的单一位置来执行所有编码/取消编码以传播状态整齐。我怀疑 ViewModelFactory 很可能是该等式的一部分...

    所以,目前还不是一个完美的解决方案——但至少 ViewModel 可以负责“现在导航”(或“返回”)的操作。

    【讨论】:

    • 我也很喜欢 MVVM,但是似乎有一些 Silverlight 模板对 MVVM 不是特别友好;导航是一个。感谢您将类型而不是字符串作为“uri”传递的想法。这在这种情况下非常有意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-16
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多