豆瓣電台WP7用戶端 MVVM重構記錄之使用MVVM Light的Message實現導航

來源:互聯網
上載者:User

這幾天使用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方法。

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等邏輯。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.