[2011.12.03] MVVM 模式开发Windows Phone程序处理页面导航

我们知道MVVM模式是SL和WP开发一种很重要的模式。

其宗旨是将View和Model分离。可是,这之间还是有少量的问题。比如说我们有时候还是需要一些界面的反馈等等。

 

一,页面导航事件

 

首先我们来谈谈页面导航事件。

WP里面页面导航事件的触发用得比较多的是OnNavigatedTo和OnNavigatedFrom。

这两个属于Page类的一个可重载函数。PhoneApplicationPage是继承的Page所以可以重写这两个函数。

如果不是采用MVVM模式的话,我们可以很容易的在这两个方法里面写一些例如处理QueryString的代码。

但是采用MVVM之后,VM里面没发获取导航事件的触发,也没法获取QueryString了。

 

很多朋友愿意用Messenger或Event之类的机制来处理这个问题,可是我个人还是比较喜欢直观的函数调用的形式。

那么我们怎么在ViewModel里面接收到页面导航的事件呢。

首先,我们建立几个辅助的类别。

一个标志接口:INavigationBase,只是用来继承的。

两个派生接口:

public interface INavigatedViewModel : INavigationBase 
{
void OnNavigatedTo(NavigationEventArgs e);
void OnNavigatedFrom(NavigationEventArgs e);
}

public interface INavigatingViewModel : INavigationBase
{
void OnNavigatingFrom(NavigatingCancelEventArgs e);
}

一个用来Hook两个OnNavigated,另一个用来Hook OnNavigatingFrom。

建立一个NavigationController类。在RootFrame新建时将其初始化即可。

初始化时候最好做成Singleton形式。我自己是做成静态属性放在ViewModelLocator里面初始化的。

public class ViewModelLocator
{
private static NavigationController _navController;

public static void Init(PhoneApplicationFrame frame)
{
_navController = new NavigationController(frame);
}
}
public App()
{
// Global handler for uncaught exceptions.
// Note that exceptions thrown by ApplicationBarItem.Click will not get caught here.
UnhandledException += Application_UnhandledException;

// Standard Silverlight initialization
InitializeComponent();

// Phone-specific initialization
InitializePhoneApplication();

ViewModelLocator.Init(RootFrame);
}

NavigationController类部分代码如下:

private INavigationBase _currentVM; 
public NavigationController(PhoneApplicationFrame frame)
{
frame.Navigated += onRootFrameNavigated;
frame.Navigating += onRootFrameNavigating;
frame.NavigationFailed += onRootFrameNavigationFailed;
frame.NavigationStopped += onRootFrameNavigationStopped;
}

private void onRootFrameNavigated(object sender, NavigationEventArgs e)
{
if (_currentVM != null)
{
if (_currentVM is INavigatedViewModel)
{
(_currentVM as INavigatedViewModel).OnNavigatedFrom(e);
}
_currentVM = null;
}

var page = e.Content as PhoneApplicationPage;
if (page != null && page.DataContext is INavigationBase)
{
_currentVM = page.DataContext as INavigationBase;

if (_currentVM is INavigatedViewModel)
{
(_currentVM as INavigatedViewModel).OnNavigatedTo(e);
}
}
}

private void onRootFrameNavigating(object sender, NavigatingCancelEventArgs e)
{
if (_currentVM is INavigatingViewModel)
{
(_currentVM as INavigatingViewModel).OnNavigatingFrom(e);
}
}

这样如果你需要你某个ViewModel接收到页面导航信息,只需将其实现相应的接口即可。例如:

public class PictureVM : ViewModelBase, INavigatedViewModel 
{

public void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{

}

public void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{

}
}

当页面导航发生时,NavigationController会判断当前页面和被导航页面是否需要触发相应的函数。 

问题就这样丑陋的解决了。另外,这样VM中相应事件的触发会先于View里面哦,各位看官注意了~

posted @ 2011-12-03 19:42  akita  阅读(805)  评论(0编辑  收藏  举报