豆瓣电台WP7客户端 MVVM重构记录之使用MVVM Light的Message实现导航
2012-03-01 02:09
429 查看
这几天使用MVVM重构这个应用,发现一个严重的问题,那就是导航。基于MVVM的思想,View跟ViewModel之间依靠绑定等技术通信,而且是View可以拿到ViewModel,ViewModel不可以拿到View。本来用CodeBehind的时候很容易的导航,到这里就无从下手了。当然也是有办法把View传递到ViewModel的,不过这样就破坏了MVVM的初衷了。
解决这个问题,首先需要解决怎么在ViewModel中得到NavgationServices来导航。以下是解决办法:
拿到这个root之后就可以导航了。
///<summary>
///根据url字符串导航
///</summary>
///<paramname="url"></param>
publicstaticvoidNavigationTo(stringurl)
{
if(root==null)
GetPhoneFrameRoot();
if(root!=null)
{
varpageUri=newUri(url,UriKind.Relative);
root.Navigate(pageUri);
}
}
///<summary>
///根据Uri导航
///</summary>
///<paramname="pageUri"></param>
publicstaticvoidNavigationTo(UripageUri)
{
if(root==null)
GetPhoneFrameRoot();
if(root!=null)
{
root.Navigate(pageUri);
}
}
publicstaticUriCreateUri(stringurl)
{
returnnewUri(url,UriKind.RelativeOrAbsolute);
}
///<summary>
///发送导航Msg
///</summary>
///<paramname="pageUri"></param>
publicstaticvoidNavigationMsgSend(UripageUri)
{
Messenger.Default.Send(pageUri,MsgToken.Navigation);
}
///<summary>
///发送导航Msg
///</summary>
///<paramname="pageUrl"></param>
publicstaticvoidNavigationMsgSend(stringpageUrl)
{
Messenger.Default.Send(CreateUri(pageUrl),MsgToken.Navigation);
}
///<summary>
///注册导航完成MSG
///</summary>
publicstaticvoidNavigatedMsgReg(objectrecipient)
{
INavigationnavigation=recipientasINavigation;
if(navigation!=null)
{
Messenger.Default.Register<Uri>(recipient,navigation.GetViewUrl(),navigation.Navigated);
}
}
}
}[/code]
这个类提供了一堆静态方法来实现页面之间的导航。其中最重要的方法是:
NavigationController:
{
stringtoken=e.Uri.OriginalString;
if(token.Contains("?"))
{
intindex=e.Uri.OriginalString.IndexOf('?');
token=token.Substring(0,index);
}
Messenger.Default.Send(e.Uri,token);
}
导航控制器,拦截所有导航的消息,然后导航,导航完成之后发送消息通知导航到的View对应的VM执行Navigated方法。
导航接口:
使用:
在App.xaml里添加一个NavigationController静态资源
在需要导航的地方发送导航消息:
}
VM去实现导航接口:
解决这个问题,首先需要解决怎么在ViewModel中得到NavgationServices来导航。以下是解决办法:
root=Application.Current.RootVisualasPhoneApplicationFrame;
拿到这个root之后就可以导航了。
root.Navigate(pageUri); 不过这样直接在ViewModel里导航总感觉比较唐突,而且有个重要的问题,那就是使用CodeBehind时,可以依靠重写OnNavigatedTo等这种方法来处理的逻辑在ViewModel里如何来处理。当了解了MVVMLight的Message机制之后,我想到了一套解决方案。
MVVMLight的Message机制可以Send一个消息,它会被广播出去,然后被register的对象接收,然后调用指定的方法。
思路:
当一个VM需要导航的时候,Send一个Message把导航的URL传递出去,这个消息被一个NavgationController截获,执行导航操作,导航完成之后NavgationController会Send一个Message,通知导航到的View对应的ViewModel执行Navigated方法。
NavigationHelper:
usingSystem;[code] returnroot;}
usingSystem.Collections.Generic;
usingSystem.Windows;
usingMicrosoft.Phone.Controls;
usingGalaSoft.MvvmLight.Messaging;
namespaceMvvmLightNavgation
{
publicclassNavigationHelper
{
privatestaticPhoneApplicationFrameroot;
publicstaticPhoneApplicationFrameGetPhoneFrameRoot()
{if(root==null)
{
root=Application.Current.RootVisualasPhoneApplicationFrame;if(root==null)
{
thrownewException("获取ApplicationRootVisual失败!");
}
}
///<summary>
///根据url字符串导航
///</summary>
///<paramname="url"></param>
publicstaticvoidNavigationTo(stringurl)
{
if(root==null)
GetPhoneFrameRoot();
if(root!=null)
{
varpageUri=newUri(url,UriKind.Relative);
root.Navigate(pageUri);
}
}
///<summary>
///根据Uri导航
///</summary>
///<paramname="pageUri"></param>
publicstaticvoidNavigationTo(UripageUri)
{
if(root==null)
GetPhoneFrameRoot();
if(root!=null)
{
root.Navigate(pageUri);
}
}
publicstaticUriCreateUri(stringurl)
{
returnnewUri(url,UriKind.RelativeOrAbsolute);
}
///<summary>
///发送导航Msg
///</summary>
///<paramname="pageUri"></param>
publicstaticvoidNavigationMsgSend(UripageUri)
{
Messenger.Default.Send(pageUri,MsgToken.Navigation);
}
///<summary>
///发送导航Msg
///</summary>
///<paramname="pageUrl"></param>
publicstaticvoidNavigationMsgSend(stringpageUrl)
{
Messenger.Default.Send(CreateUri(pageUrl),MsgToken.Navigation);
}
///<summary>
///注册导航完成MSG
///</summary>
publicstaticvoidNavigatedMsgReg(objectrecipient)
{
INavigationnavigation=recipientasINavigation;
if(navigation!=null)
{
Messenger.Default.Register<Uri>(recipient,navigation.GetViewUrl(),navigation.Navigated);
}
}
}
}[/code]
这个类提供了一堆静态方法来实现页面之间的导航。其中最重要的方法是:
///<summary>
///发送导航Msg
///</summary>
///<paramname="pageUrl"></param>
publicstaticvoidNavigationMsgSend(stringpageUrl)
{
Messenger.Default.Send(CreateUri(pageUrl),MsgToken.Navigation);
}
这个方法会发送一个导航的消息,这个消息会被导航控制器拦截到。
///<summary>
///注册导航完成MSG
///</summary>
publicstaticvoidNavigatedMsgReg(objectrecipient)
{
INavigationnavigation=recipientasINavigation;
if(navigation!=null)
{
Messenger.Default.Register<Uri>(recipient,navigation.GetViewUrl(),navigation.Navigated);
}
}
这个方法注册一个导航完成的接受对象及方法,拦截导航控制器发出的完成消息。
NavigationController:
usingGalaSoft.MvvmLight.Messaging;[code]privatevoidRootNavigated(objectsender,System.Windows.Navigation.NavigationEventArgse)
namespaceMvvmLightNavgation
{
publicclassNavigationController
{
publicNavigationController()
{
Messenger.Default.Register<Uri>(this,MsgToken.Navigation,Navigation);NavigationHelper.GetPhoneFrameRoot().Navigated+=newSystem.Windows.Navigation.NavigatedEventHandler(RootNavigated);
}
privatevoidNavigation(Uriuri)
{
NavigationHelper.NavigationTo(uri);
}
{
stringtoken=e.Uri.OriginalString;
if(token.Contains("?"))
{
intindex=e.Uri.OriginalString.IndexOf('?');
token=token.Substring(0,index);
}
Messenger.Default.Send(e.Uri,token);
}
这个方法是导航完成之后的回调方法。它将发送一个以导航到的View对应的Url为Token的Message。
}
}
导航控制器,拦截所有导航的消息,然后导航,导航完成之后发送消息通知导航到的View对应的VM执行Navigated方法。
导航接口:
namespaceMvvmLightNavgation
{
publicinterfaceINavigation
{
///<summary>
///获取对应的View的Url
///</summary>
///<returns></returns>
stringGetViewUrl();
///<summary>
///导航完成后发生
///</summary>
///<paramname="uri"></param>
voidNavigated(Uriuri);
}
}
让VM去实现这个接口,保证所有VM都具有这2个方法。
使用:
在App.xaml里添加一个NavigationController静态资源
<Application.Resources>
<vm:MvvmViewModelLocatorxmlns:vm="clr-namespace:DBFM7"
x:Key="Locator"/>
<nav:NavigationControllerx:Key="NavCtr"/>
</Application.Resources>
在需要导航的地方发送导航消息:
stringpageUrl="/View/MainPage.xaml?Channle="+hubTitle;
NavigationHelper.NavigationMsgSend(pageUrl);
在VM的构造函数中注册接受导航完成消息的对象。
publicChannelTileViewModel()
{
NavigationHelper.NavigatedMsgReg(this);
}
VM去实现导航接口:
publicclassChannelTileViewModel:ViewModelBase,INavigation
{
。。。。。。。。。。。。。。。。。。。
///<summary>
///导航完成后发生
///</summary>
///<paramname="uri"></param>
publicvoidNavigated(Uriuri)
{
}
///<summary>
///获取对应的View的Url
///</summary>
///<returns></returns>
publicstringGetViewUrl()
{
return"/View/ChannelTile.xaml";
}
}
通过Message跟NavigationController这一层的过渡彻底的解决了因导航而带来的View跟ViewModel的耦合问题。而且可以方便的扩展Navigating,NavigateFailed,NavigateStopped等逻辑。
相关文章推荐
- 豆瓣电台WP7客户端 MVVM重构记录之使用MVVM Light实现Event绑定
- 豆瓣电台WP7客户端 MVVM重构记录之使用MVVM Light实现数据绑定
- 豆瓣电台WP7客户端 MVVM重构记录之使用AppBarUtils使ApplicationBarIconButton支持绑定(包括IconUri)
- 豆瓣电台WP7客户端 MVVM重构记录之-总结
- 使用ViewPager和Fragment实现底部导航滑动重构版
- 使用 ASP.NET Atlas PageNavigator控件实现客户端分页导航
- 豆瓣电台WP7客户端 开发记录2
- 豆瓣电台WP7客户端 开发记录6
- 豆瓣电台WP7客户端 开发记录 8
- 使用ASP.NETAtlasPageNavigator控件实现客户端分页导航
- 豆瓣电台WP7客户端 开发记录3
- 豆瓣电台WP7客户端 开发记录4
- 使用 ASP.NET Atlas PageNavigator控件实现客户端分页导航
- 豆瓣电台WP7客户端 开发记录7
- 使用 ASP.NET Atlas PageNavigator控件实现客户端分页导航
- 使用 ASP.NET Atlas PageNavigator控件实现客户端分页导航
- 豆瓣电台WP7客户端 开发记录1
- MVVMLight Toolkit在Windows Phone中的使用扩展之一:在ViewModel中实现导航,并传递参数
- java在线聊天项目1.1版 ——开启多个客户端,分别实现注册和登录功能,使用客户端与服务端信息request机制,重构线程,将单独的登录和注册线程合并
- 豆瓣电台WP7客户端 开发记录5