2012年9月20日星期四

WinRT/Metro: NavigationCacheMode.Disabled和LayoutAwarePage

WinRT/Metro: NavigationCacheMode.Disabled和LayoutAwarePage

WinRT的导航变化很大,当然从WPF,到Silverlight到现在的WinRT,XAML中的导航一直在变!(可以参考之前的一篇文章:从WPF到Silverlight到WinRT:导航的变化)。WinRT使用了一种特有的导航方式,以Type为标识的导航!微软仿佛在强制命令我们写入UI上数据的读取和保存逻辑。为什么这样说?因为在WinRT中,即便是你使用NavigationCacheMode.Enabled或者NavigationCacheMode.Required,页面也确实被缓存了,但是问题是当你再次创建一个相同类型的页面,上一个缓存的页面会被返回!这就是WinRT中以类型为导航的方式和之前Silverlight或者WPF以对象或者Uri导航的不同。除非你的程序可以确保不会使用到重复的类型的页面。否则,使用页面缓存不会解决什么问题,你仍然要面对多个相同页面间数据的保存问题,而这正是NavigationCacheMode.Disabled时需要考虑的。

 

缓存的方式当然多种多样,其中Visual Studio 2012中提供了一个LayoutAwarePage,当然LayoutAwarePage的功能不仅仅有缓存,从名字上还可以看出来有界面上自动调整Layout的功能。这里只讲缓存,事实上LayoutAwarePage的缓存依靠另一个类型SuspensionManager类型,两者都在Visual Studio工程模板中的Common文件夹中。

image

 

SuspensionManager通过附加依赖属性来提供数据的存储以及Frame的注册,Frame可以注册SessionState,所谓的SessionState就是Frame的一套数据的命名。接着LayoutAwarePage继承自Page并改写Page.OnNavigatedTo和OnNavigatedFrom来进行页面数据的加载和写入。这里画龙点睛一笔在于LayoutAwarePage使用Frame.BackStackDepth来命名内部Page并根据实际情况来删除不需要的Page数据,比如当前Frame历史是A->B->C->D,但是用户在B的时候创建了E,那么此时Frame历史应该是A->B->E,而C和D的数据就可以清理掉了。所以SessionState存储的是Frame的SessionState名称,和数据。这个数据有存储着Frame中Page的数据,类型也是Dictionary<string, object>。

 

在LayoutAwarePage中的LoadState或者SaveState中导航中的参数总是被传的,导航模式没有参数但是可以从Page的数据存储参数pageState是否为null判断出来,当然OnNavigatedTo和OnNavigatedFrom方法总是可以被改写的。

 

写了个示例小程序演示这个问题,这个程序只有一个页面!这个页面可以创建或者切换到其他页面,当然页面类型都是同一个,最终程序实现每个页面都有自己的数据同时可以设置创建页面的数据。

 

比如程序启动,第一个页面,设置页面数据为“Mgen”,下一个页面数据“Tony”。

image

 

然后点“创建新页面”,下一个页面被导航至:

image

 

数据被正确设置,同时从上面可以看到此时这种类型的页面被创建了2次。接着再点“后退”:

image

 

第一个页面被正确还原,同时可以看到此时这种页面类型被创建了3次,而且所有页面的导航次数都是1。也就是说每当页面被导航后,一个新的实例都会被创建!因为Page.NavigationCacheMode默认就是Disabled。

 

那么程序的实习就是通过改写LayoutAwarePage的LoadState和SaveState来完成数据的缓存和读取的。

逻辑代码是这样的:

//改写LayoutAwarePage的方法

//变量tbxCurr和tbxNext是界面上TextBox的名称

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)

{

    if (pageState == null)

    {

        //新创建的页面,读取参数。

        if (navigationParameter != null)

            tbxCurr.Text = (string)navigationParameter;

    }

    else

    {

        //不是新创建的页面,读取缓存值。

        tbxCurr.Text = (string)pageState["tbxCurr"];

        tbxNext.Text = (string)pageState["tbxNext"];

    }

}

 

protected override void SaveState(Dictionary<String, Object> pageState)

{

    //离开页面后实例会被销毁,因此保存界面数据至缓存。

    pageState["tbxCurr"] = tbxCurr.Text;

    pageState["tbxNext"] = tbxNext.Text;

}

 

对于导航新页面的创建,仍然使用Frame.Navigate方法并传入参数就OK了:

Frame.Navigate(typeof(MainPage), tbxNext.Text);

 

 

 

源代码下载
下载地址
注意:此为微软SkyDrive存档,请用浏览器直接下载,用某些下载工具可能无法下载
源代码环境:Microsoft Visual Studio Express 2012 for Windows 8


TAG: