WinRT / UWP框架和页面caching:如何在Navigate()上创build新的页面实例,并保持页面实例在GoBack()
我正在尝试用C#创build一个UWP(通用Windows应用程序)应用程序。 我的问题是Frame
控制:如果我使用它没有NavigationCacheMode = Required
,每次用户返回,页面不会保留在内存中,将被重新创build。 如果我将NavigationCacheMode
设置为Required
或Enabled
,则返回正常工作(无新页面对象), 但如果我导航到来自同一types的另一个页面,则上一页面对象将被回收并重用(无新页面实例)。
期望的行为:
有没有办法与原来的Frame
控制有如下行为(如在Windows Phone中):
- 在
Navigate()
上创build新页面实例 - 将页面实例保留在
GoBack()
我知道的唯一的解决scheme是创build一个自己的Frame
控制,但这会导致其他问题(例如:缺lessSetNavigationState()
方法等)
示例场景:
简单的应用程序示例有三个页面: TvShowListPage
, TvShowDetailsPage
, SeasonDetailsPage
。
-
TvShowListPage
是入口页面。 点击一个TvShow
导航到TvShowDetailsPage
。 - 现在在
TvShowDetailsPage
select列表中的一个季节并导航到TvShowDetailsPage
。 - 如果导航回来,页面应该留在内存中,以避免重新加载页面。
- 但是,如果用户返回到
TvShowListPage
并select另一个TvShow
TvShowDetailsPage
被回收,并且可能处于错误的状态(例如,显示投射枢轴而不是第一个季节枢轴)
我正在寻找默认的Windows Phone 7行为:导航在页面堆栈上创build一个新页面,然后从堆栈中删除顶层页面,并显示堆栈中的前一页(存储在内存中)。
解:
由于没有解决这个问题,我不得不重新实现所有寻呼相关的类:页面,框架,SuspensionManager等…
提供所有这些类的库MyToolkit可以在这里下载: https : //github.com/MyToolkit/MyToolkit/wiki/Paging-Overview
参考文献:
- http://www.jayway.com/2012/05/25/clearing-the-windows-8-page-cache/ :没有好的解决scheme
- http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/88e6d1b3-1fa6-4ab4-a816-e77c86ef236f/ :实现自己的框架类是没有解决scheme,因为它不适用于
SuspensionManager
由于没有解决这个问题,我不得不重新实现所有寻呼相关的类:页面,框架,SuspensionManager等…
解决scheme可以在这里下载: https : //github.com/MyToolkit/MyToolkit/wiki/Paging-Overview
更新:
页面类现在还提供了OnNavigatingFromAsync方法来显示例如一个asynchronouspopup和取消导航如果需要…
我有很多相同的问题。 我想这样做,当我在Metro(Windows Store是正确的)前进时,它会创build一个新的实例。 但是,返回时,它会保留我想要保存的数据。
所以,我也喜欢使用NavigationCacheMode = NavigationCacheMode.Enabled。 我发现无论我前进还是后退,都保存着。 所以,我会前进几页,然后退一步。 我希望所有的事情都能在我前进的时候重置,我总是发现事情并非如此。 它保留了这些数据。
我尝试了一切,包括编写我自己的后退button代码,以包括NavigationCacheMode = NavigationCacheMode.Disabled,但无济于事。 正如其他人所指出的,一旦启用它,NavigationCacheMode将不会被禁用。
我find了一个解决scheme。 我去了LayoutAwarePage.cs,只是做了一个小小的改变。 根据“OnNavigatedTo”,我find了这一行:
// Returning to a cached page through navigation shouldn't trigger state loading if (this._pageKey != null) return;
然而,这个评论与我想要的相反。 我正在寻找一个单向模式的状态加载。 如果前进,我想要加载状态; 如果向后移动,我想要的行为评论表明 – 没有国家加载。
所以我只是修改了这一行。
// Returning to a cached page through navigation shouldn't trigger state loading if (this._pageKey != null && e.NavigationMode == NavigationMode.Back) return;
我已经testing过,它完美的工作。 现在,向后导航时会记住状态并保持页面一致。 前进,它加载新鲜。
也许不是最好的做法,但是我不会在我的代码背后调用“OnNavigatedTo”。 我通过“LoadState”执行所有操作。 如果您在代码隐藏中重写“OnNavigatedTo”,则可能会看到不同的行为。
谢谢,
约瑟夫·欧文
在向前导航时 ,是否可以在调用Frame.Navigate之前将NavigationCacheMode设置为Disabled ? 然后,在OnNavigatedTo()将NavigationCacheMode重新设置为Enabled 。
这应该使得当你向前导航时,caching被禁用。 但是,当您到达新的页面实例时, OnNavigatedTo将再次启用它。 当您想要导航时,在调用Frame.GoBack之前,您不会触摸NavigationCacheMode 。 这应该给你caching的实例,我认为。
我相信这会起作用,但我还没有testing过。 我很好奇,知道它是否。 那里有趣的场景。 我很想看到这个应用程序的行动,并更好地理解这种行为的使用。
您可以使用NavigationCacheMode属性来指定是否为每次访问页面创build页面的新实例,或者是否为每次访问使用了caching中保存的页面的以前构造的实例。
NavigationCacheMode属性的默认值是Disabled。 当页面的新实例对于每次访问不是必需的时,将NavigationCacheMode属性设置为Enabled或Required。 通过使用页面的caching实例,可以提高应用程序的性能并减less服务器的负载。
将NavigationCacheMode设置为Required意味着不pipeCacheSize属性中指定的caching页面的数量如何,页面都被caching。 标记为必需的页面不计入CacheSize总数。 将NavigationCacheMode设置为Enabled意味着页面被caching,但如果caching的页面数量超过CacheSize的值,则可以将其丢弃。
如果必须为每次访问创build一个新实例,请将NavigationCacheMode属性设置为“已禁用”。 例如,您不应该caching显示每个客户所特有信息的页面。
即使从caching中检索页面,也会为每个请求调用OnNavigatedTo方法。 您应该在此方法中包含必须为每个请求执行的代码,而不是将该代码放置在Page构造函数中。
我必须从我的page
类派生page2
类,然后当我想导航到同一页的第二个版本时,我检测到this
对象是page
还是page2
。 然后,如果我在page
,则导航到page2
,如果在page2
导航到page
。
唯一的缺点是巨大的缺点是无法从另一个XAML文件中派生出一个XAML文件。 因此,如预期的那样,所有C#代码都在page
代码后面,但是有两个几乎相同的XAML文件,每个版本的页面都有一个。
可以添加一个小脚本作为预生成步骤,从第一个生成第二个页面类,复制XAML数据并调整类名称。
这是丑陋的,但它几乎完美,我从来不必担心C#代码重复或怪异的导航caching问题。 我只是最终有重复的XMAL代码,在我的情况下真的永远不会改变。 我还最终得到两个关于在page2.InitializeComponent()
和page2.Connect()
的自动生成的代码中不使用new
关键字的警告。
有趣的是,导航到page
然后到page
然后到page
不会导致问题, page
类的第二个实例是与page
一个无关的实际的第二个实例。
请注意,这个解决scheme可能是由MS推荐的。
我通过以下方式实现了
- 将NavigationCacheMode设置为必需/启用所需页面。
-
点击Page2button/链接:
遍历Frame BackStack并找出Page2是否在BackStack中。 如果findPage2,则调用Frame.GoBack()所需的次数。 如果找不到,只需导航到新的页面。 这将适用于任何页面。
代码示例:
public void Page2Clicked(object sender, RoutedEventArgs e) { int isPresent = 0; int frameCount = 0; //traverse BackStack in reverse order as the last element is latest page for(int index= Frame.BackStack.Count-1; index>=0;index--) { frameCount += 1; //lets say the first page name is page1 which is cached if ("Page2".Equals(Frame.BackStack[index].SourcePageType.Name)) { isPresent = 1; //Go back required no of times while (frameCount >0) { Frame.GoBack(); frameCount -= 1; } break; } } if (isPresent == 0) { Frame.Content = null; Frame.Navigate(typeof(Page2)); } }
如果向前/向后button不会被使用的话,这将是有帮助的。 因为这个解决scheme会影响前进/后退导航。 如果您还想使用向前/向后导航,则需要处理一些其他情况。