zoukankan      html  css  js  c++  java
  • 豆瓣电台WP7客户端 MVVM重构记录之使用MVVM Light的Message实现导航

    这几天使用MVVM重构这个应用,发现一个严重的问题,那就是导航。基于MVVM的思想,View跟ViewModel之间依靠绑定等技术通信,而且是View可以拿到ViewModel,ViewModel不可以拿到View。本来用CodeBehind的时候很容易的导航,到这里就无从下手了。当然也是有办法把View传递到ViewModel的,不过这样就破坏了MVVM的初衷了。

    解决这个问题,首先需要解决怎么在ViewModel中得到NavgationServices来导航。以下是解决办法:

    root = Application.Current.RootVisual as PhoneApplicationFrame;
    

    拿到这个root之后就可以导航了。

    root.Navigate(pageUri);
    不过这样直接在ViewModel里导航总感觉比较唐突,而且有个重要的问题,那就是使用CodeBehind时,可以依靠重写OnNavigatedTo等这种方法来处理的逻辑在ViewModel里如何来处理。当了解了MVVM Light的Message机制之后,我想到了一套解决方案。
    MVVM Light的Message机制可以Send一个消息,它会被广播出去,然后被register的对象接收,然后调用指定的方法。
    思路:
    当一个VM需要导航的时候,Send一个Message把导航的URL传递出去,这个消息被一个NavgationController截获,执行导航操作,导航完成之后NavgationController会Send一个Message,通知导航到的View对应的ViewModel执行Navigated方法。
    image
    NavigationHelper:
     
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using Microsoft.Phone.Controls;
    using GalaSoft.MvvmLight.Messaging;
    namespace MvvmLightNavgation
    {
        public class NavigationHelper 
        {
            private static PhoneApplicationFrame root;
             
            public static PhoneApplicationFrame GetPhoneFrameRoot()
            {
            
             if (root == null)
              {
                root = Application.Current.RootVisual as PhoneApplicationFrame;
               if (root == null)
                {
               throw new Exception("获取 ApplicationRootVisual 失败!");
                }
               }
    		return root;
    } /// <summary> /// 根据url字符串导航 /// </summary> /// <param name="url"></param> public static void NavigationTo(string url) { if (root==null) GetPhoneFrameRoot(); if(root !=null) { var pageUri = new Uri(url, UriKind.Relative); root.Navigate(pageUri); } } /// <summary> /// 根据Uri导航 /// </summary> /// <param name="pageUri"></param> public static void NavigationTo(Uri pageUri) { if (root == null) GetPhoneFrameRoot(); if (root != null) { root.Navigate(pageUri); } } public static Uri CreateUri(string url) { return new Uri(url, UriKind.RelativeOrAbsolute); } /// <summary> /// 发送导航Msg /// </summary> /// <param name="pageUri"></param> public static void NavigationMsgSend(Uri pageUri) { Messenger.Default.Send(pageUri, MsgToken.Navigation); } /// <summary> /// 发送导航Msg /// </summary> /// <param name="pageUrl"></param> public static void NavigationMsgSend(string pageUrl) { Messenger.Default.Send(CreateUri(pageUrl), MsgToken.Navigation); } /// <summary> /// 注册导航完成MSG /// </summary> public static void NavigatedMsgReg(object recipient) { INavigation navigation = recipient as INavigation; if (navigation!=null) { Messenger.Default.Register<Uri>(recipient, navigation.GetViewUrl(), navigation.Navigated); } } } }

    这个类提供了一堆静态方法来实现页面之间的导航。其中最重要的方法是:

    /// <summary>
          /// 发送导航Msg
          /// </summary>
          /// <param name="pageUrl"></param>
          public static void NavigationMsgSend(string pageUrl)
          {
              Messenger.Default.Send(CreateUri(pageUrl), MsgToken.Navigation);
          }
           这个方法会发送一个导航的消息,这个消息会被导航控制器拦截到。
    
          /// <summary>
          /// 注册导航完成MSG
          /// </summary>
          public static  void NavigatedMsgReg(object recipient)
          {
              INavigation navigation = recipient as INavigation;
              if (navigation!=null)
              {
                  Messenger.Default.Register<Uri>(recipient, navigation.GetViewUrl(), navigation.Navigated);
              }
          }
          这个方法注册一个导航完成的接受对象及方法,拦截导航控制器发出的完成消息。
     
     

    NavigationController :

    using GalaSoft.MvvmLight.Messaging;
    namespace MvvmLightNavgation
    {
        public class NavigationController
        {
            public NavigationController()
            {
                Messenger.Default.Register<Uri>(this, MsgToken.Navigation, Navigation);
    
                NavigationHelper.GetPhoneFrameRoot().Navigated += new System.Windows.Navigation.NavigatedEventHandler(RootNavigated);
            }
    
            private void Navigation(Uri uri)
            {
                NavigationHelper.NavigationTo(uri);
            }
    
         private void RootNavigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
          {
             string token = e.Uri.OriginalString;
             if (token.Contains("?"))
            {
               int index = e.Uri.OriginalString.IndexOf('?');
               token = token.Substring(0, index);
            }
            Messenger.Default.Send(e.Uri,token);
         }
         这个方法是导航完成之后的回调方法。它将发送一个以导航到的View对应的Url为Token的Message。
        }
    }

    导航控制器,拦截所有导航的消息,然后导航,导航完成之后发送消息通知导航到的View对应的VM执行Navigated方法。

    导航接口:

    namespace MvvmLightNavgation
    {
        public interface INavigation
        {
            /// <summary>
            /// 获取对应的View的Url
            /// </summary>
            /// <returns></returns>
            string GetViewUrl();
            /// <summary>
            /// 导航完成后发生
            /// </summary>
            /// <param name="uri"></param>
            void Navigated(Uri uri);
        }
    }
    让VM去实现这个接口,保证所有VM都具有这2个方法。
     

    使用:

    在App.xaml里添加一个NavigationController静态资源

      <Application.Resources>
            <vm:MvvmViewModelLocator xmlns:vm="clr-namespace:DBFM7"
                                       x:Key="Locator" />
            <nav:NavigationController  x:Key="NavCtr"/>
        </Application.Resources>
    

    在需要导航的地方发送导航消息:

    string pageUrl = "/View/MainPage.xaml?Channle=" + hubTitle;
    NavigationHelper.NavigationMsgSend(pageUrl);
    在VM的构造函数中注册接受导航完成消息的对象。
    public ChannelTileViewModel()
         {
             NavigationHelper.NavigatedMsgReg(this);

            }

    VM去实现导航接口:

    public class ChannelTileViewModel : ViewModelBase,INavigation
       {
    。。。。。。。。。。。。。。。。。。。
           /// <summary>
           /// 导航完成后发生
           /// </summary>
           /// <param name="uri"></param>
           public void Navigated(Uri uri)
           {
           }
    
           /// <summary>
           /// 获取对应的View的Url
           /// </summary>
           /// <returns></returns>
           public string GetViewUrl()
           {
               return "/View/ChannelTile.xaml";
           }
       }
    
    通过Message跟NavigationController这一层的过渡彻底的解决了因导航而带来的View跟ViewModel的耦合问题。而且可以方便的扩展Navigating,NavigateFailed,NavigateStopped等逻辑。

    QQ群:1022985150 VX:kklldog 一起探讨学习.NET技术
    作者:Agile.Zhou(kklldog)
    出处:http://www.cnblogs.com/kklldog/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    【转】千万别理程序员
    qemu-ifup and qemu-ifdown
    Fedora-23 installation in VM image
    Set up bridge to connect to internet
    fedora25 上设置br0
    助教工作总结
    树1
    线性结构
    链表基本操作
    自定义函数
  • 原文地址:https://www.cnblogs.com/kklldog/p/2374927.html
Copyright © 2011-2022 走看看