WinRT / UWP框架和页面caching:如何在Navigate()上创build新的页面实例,并保持页面实例在GoBack()

我正在尝试用C#创build一个UWP(通用Windows应用程序)应用程序。 我的问题是Frame控制:如果我使用它没有NavigationCacheMode = Required ,每次用户返回,页面不会保留在内存中,将被重新创build。 如果我将NavigationCacheMode设置为RequiredEnabled ,则返回正常工作(无新页面对象), 如果我导航到来自同一types的另一个页面,则上一页面对象将被回收并重用(无新页面实例)。

期望的行为:

有没有办法与原来的Frame控制有如下行为(如在Windows Phone中):

  1. Navigate()上创build新页面实例
  2. 将页面实例保留在GoBack()

我知道的唯一的解决scheme是创build一个自己的Frame控制,但这会导致其他问题(例如:缺lessSetNavigationState()方法等)

示例场景:

简单的应用程序示例有三个页面: TvShowListPageTvShowDetailsPageSeasonDetailsPage

  1. TvShowListPage是入口页面。 点击一个TvShow导航到TvShowDetailsPage
  2. 现在在TvShowDetailsPageselect列表中的一个季节并导航到TvShowDetailsPage
  3. 如果导航回来,页面应该留在内存中,以避免重新加载页面。
  4. 但是,如果用户返回到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。 我去了La​​youtAwarePage.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会影响前进/后退导航。 如果您还想使用向前/向后导航,则需要处理一些其他情况。