47

我正在尝试使用 C# 创建一个 UWP(通用 Windows 应用程序)应用程序。我的问题是Frame控件:如果我在没有 的情况下使用它NavigationCacheMode = Required,则每次用户返回时,页面都不会保存在内存中,而是会重新创建。如果我设置NavigationCacheModeRequiredor Enabled,返回可以正常工作(没有新的页面对象),但是如果我从相同类型导航到另一个页面,则前一个页面对象将被回收和重用(没有新的页面实例)。

期望的行为:

有没有办法使原始控件具有以下行为Frame(例如在 Windows Phone 中):

  1. 创建新的页面实例Navigate()
  2. 保持页面实例开启GoBack()

我知道的唯一解决方案是创建一个自己的Frame控件,但这会导致其他问题(例如:缺少SetNavigationState()方法等......)

示例场景:

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

  1. TvShowListPage是入口页面。单击TvShow导航到后TvShowDetailsPage
  2. 现在在TvShowDetailsPage列表中选择一个季节并导航到TvShowDetailsPage.
  3. 如果向后导航,页面应保留在内存中以避免重新加载页面。
  4. 但是,如果用户返回TvShowListPage并选择另一个TvShowTvShowDetailsPage则会被回收并且可能处于错误状态(例如,显示演员枢轴而不是第一个,季节枢轴)

我正在寻找默认的 Windows Phone 7 行为:导航在页面堆栈上创建一个新页面,返回从堆栈中删除顶部页面并显示堆栈中的前一页(存储在内存中)。

解决方案:

因为没有解决这个问题,我不得不重新实现所有与分页相关的类:Page、Frame、SuspensionManager 等...

提供所有这些类的库 MyToolkit可以在这里下载:https ://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

参考:

4

7 回答 7

17

因为没有解决这个问题,我不得不重新实现所有与分页相关的类:Page、Frame、SuspensionManager 等...

该解决方案可以在这里下载: https ://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

更新:

页面类现在还提供OnNavigatingFromAsync方法来显示例如异步弹出窗口并在需要时取消导航......

于 2012-10-04T15:46:59.997 回答
8

我有很多同样的问题。我希望这样当我在 Metro(正确的是 Windows 商店)中前进时,它会创建一个新实例。但是,当返回时,它会保留我想要保存的数据。

所以,我同样使用了 NavigationCacheMode = NavigationCacheMode.Enabled。我发现,无论我走哪条路,前进或后退,一切都被保存了下来。所以,我会前进几页,然后退后几页。希望在我前进的过程中一切都被重置,我总是发现它不是。它保留了数据。

我尝试了一切,包括编写自己的后退按钮代码以包含 NavigationCacheMode = NavigationCacheMode.Disabled,但无济于事。正如其他人指出的那样,一旦启用它, NavigationCacheMode 就不会禁用。

我确实找到了解决方案。我去了 LayoutAwarePage.cs 并简单地做了一个小改动。在“OnNavigatedTo”下,我找到了这一行:

// 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;

我已经对其进行了测试,并且效果很好。现在,当向后导航时,它会记住状态并保持页面不变。向前导航,它加载新鲜。

也许不是最佳实践,但我不会从我的代码隐藏中调用“OnNavigatedTo”。我通过“LoadState”做所有事情。如果您在代码隐藏中覆盖“OnNavigatedTo”,您可能会看到不同的行为。

谢谢,

约瑟夫·欧文

于 2012-08-30T16:54:40.303 回答
6

向前导航时,可以在调用Frame.Navigate之前将NavigationCacheMode设置为Disabled吗?然后,在OnNavigatedTo()中再次将NavigationCacheMode设置回Enabled

这应该使得当您向前导航时,缓存被禁用。但是当您到达新页面实例时,OnNavigatedTo将再次启用它。当您想向后导航时,您不会在调用Frame.GoBack之前触摸NavigationCacheMode。我认为这应该给你缓存的实例。

我相信这会起作用,但我还没有测试过。我很想知道是否确实如此。有趣的场景。我很想看到该应用程序在运行,并更好地了解这种行为的使用。

于 2012-07-18T21:04:13.243 回答
2

您使用 NavigationCacheMode 属性来指定是否为每次访问页面创建一个新的页面实例,或者是否为每次访问使用保存在缓存中的先前构造的页面实例。

NavigationCacheMode 属性的默认值为已禁用。当页面的新实例对于每次访问都不是必需的时,将 NavigationCacheMode 属性设置为 Enabled 或 Required。通过使用页面的缓存实例,您可以提高应用程序的性能并减少服务器上的负载。

将 NavigationCacheMode 设置为 Required 意味着无论 CacheSize 属性中指定的缓存页数如何,都会缓存该页。标记为必需的页面不计入 CacheSize 总数。将 NavigationCacheMode 设置为 Enabled 意味着页面被缓存,但如果缓存页面的数量超过 CacheSize 的值,则可以处理。

如果必须为每次访问创建一个新实例,请将 NavigationCacheMode 属性设置为 Disabled。例如,您不应缓存显示每个客户独有信息的页面。

每次请求都会调用 OnNavigatedTo 方法,即使从缓存中检索页面也是如此。您应该在此方法中包含必须为每个请求执行的代码,而不是将该代码放在 Page 构造函数中。

于 2012-07-18T11:13:29.530 回答
0

我必须page2从我的类派生一个类page,然后当我想导航到同一页面的第二个版本时,我检测this对象是page还是page2. 然后我导航到page2if I was inpage并导航到pageif in page2

唯一的缺点是一个巨大的缺点,即无法从另一个 XAML 文件派生一个 XAML 文件。因此,所有 C# 代码都在page类代码隐藏中,如预期的那样,但是有两个几乎相同的 XAML 文件,一个用于页面的每个版本。

可以添加一个小脚本作为预构建步骤,从第一个页面类生成第二个页面类,复制 XAML 数据并调整类名。

它很丑,但几乎可以完美运行,我永远不必担心 C# 代码重复或奇怪的导航缓存问题。我最终得到了重复的 XMAL 代码,在我的情况下,它真的永远不会改变。new我还收到了两个关于在自动生成的代码中不使用关键字的警告page2.InitializeComponent()page2.Connect()

有趣的是,导航到pagethen 到page2thenpage不会导致问题,并且page该类的第二个实例是与第一个无关的实际第二个实例。

请注意,MS 可能建议不要使用此解决方案。

于 2015-10-19T23:24:50.943 回答
0

我通过以下方式实现了这一目标:

  • 将所需页面的 NavigationCacheMode 设置为必需/启用。
  • 单击 Page2 按钮/链接时:

    遍历 Frame BackStack 并找出 Page2 是否在 BackStack 中。如果找到 Page2,则调用 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));
    }
}

如果没有太多使用前进/后退按钮,这将很有帮助。因为此解决方案将影响向前/向后导航。如果您还希望使用前进/后退导航,则必须处理一些额外的情况。

于 2016-10-21T19:42:59.387 回答
0

启用NavigationCacheMode后,您可以在 OnNavigatedFrom() 中(有条件地)调用Dispose ()确保在应用程序下次导航到该页面时创建一个新的页面实例。

于 2019-11-27T11:08:59.833 回答