信息
您所需的主题如下所示。但此主题未包含在此库中。
3(共 3)对本文的评价是有帮助 - 评价此主题

如何使用 Windows Phone 的后退堆栈进行导航

2012/2/9

本主题介绍如何通过操作应用程序的后退导航历史记录(称为后退堆栈),修改其导航。在 Windows Phone OS 7.1 中,向 NavigationService API 添加了处理导航历史记录和启用导航历史记录检测的功能。本主题将使用这些属性和方法检测后退堆栈、删除条目,然后观察这些更改在应用程序导航上产生的效果。演示该功能的应用程序的完整示例可以下载。有关更多信息,请参阅导航后退堆栈示例。

注意注意:

以下过程中的步骤用于 Visual Studio 2010 Express for Windows Phone。 当您使用用于 Visual Studio 2010 Professional 或 Visual Studio 2010 Ultimate 的插件时,您可能会看到菜单命令或窗口布局中的一些微小改变。

本主题包含以下各节:

注意注意:

在本主题中,导航历史记录一词和后退堆栈一词可换用,它们都是指由 NavigationService.BackStack 属性公开的后退导航历史记录。

应用程序的导航历史记录表示为后进先出结构,称为堆栈。此处该结构还称为后退堆栈,因为它在表示应用程序后退导航的堆栈结构中,包含一组页面。可以将该堆栈看成是一叠盘子。添加到该堆栈的最后一个盘子将是可以移除的第一个盘子。最新项被添加到此堆栈的顶部。此操作称为推送操作。删除堆栈顶部项的操作称为弹出操作。通过从堆栈顶部一次删除一个项目,可以检索堆栈中的某些内容。下图显示了堆栈的概念。

BackStack 堆栈表示

当应用程序中的页面调用 Navigate 时,当前页面会被放到后退堆栈上,并且系统将创建并显示目标页的新实例。当您在应用程序的页面之间进行导航时,系统会将多个条目添加到此堆栈。当页面调用 GoBack 时,或者当用户按手机的“返回”按键时,将放弃当前页面,并将堆栈顶部的页面从后退堆栈中弹出并进行显示。此后退导航会继续弹出并显示,直到堆栈中不再有条目。此时,点按手机的“返回”按键将终止应用程序。

虽然大多数应用程序从来不必操作后退堆栈即可使用默认导航完美地运行,但是一些开发人员需要调整导航历史记录,以便为其应用程序提供最佳的用户体验。要实现此目的的原因很多。例如,应用程序中可能有一个登录页面。您可能不希望用户在登录后能够导航回该页面 。本主题演示如何使用 BackStack 属性和 RemoveBackEntry 方法操作导航历史记录。

本节介绍如何在应用程序中使导航历史记录或后退堆栈可视化,以便它可以在应用程序运行时轻松进行检测。我们的应用程序包含多个页面。当从一个页面导航到下一个页面时,我们希望查看后退堆栈上的条目。我们还想要一种简单的方法来删除条目,并查看后退堆栈的所有更新。在此应用程序中,我们将后退堆栈可视化为屏幕上的列表。下图对此进行了阐释。这样做是为了理解后退堆栈的概念。您不需要在应用程序中显示后退堆栈条目,但这完全取决于您。

BackStack 初始 UI

上图中的灰色区域是应用程序中后退堆栈的可视化。当我们导航应用程序时,该列表将使用导航历史记录中的条目进行填充。我们将使用“Pop Last”“Pop To Selected”按键更改导航历史记录。此灰色区域并不是在每个单独页面上都存在。而是会将该列表集中添加到应用程序的 RootFrame 中。RootFrame 对象是与应用程序关联的 PhoneApplicationFrame。每个应用程序都有一个 RootFrame。当用户导航到该页面时,导航框架会将应用程序的每个页面或 PhoneApplicationPage 的实例设置为框架的 Content。在创建新 Windows Phone 应用程序时获取的 RootFrame 对象的默认模板会显示应用程序页面和其他元素(例如该应用程序的系统托盘和应用程序栏)。在此示例中,我们将创建一个模板,显示应用程序页面,但会在屏幕底部留下一些空间用于以列表形式显示后退堆栈。在逐页进行导航时,将更新该集中列表以反映应用程序的导航历史记录或后退堆栈的当前状态。有关 Windows Phone 应用程序剖析的更多信息,请参阅 Windows Phone 框架和页面导航概述

使后退堆栈视化

  1. 在 Visual Studio 2010 Express for Windows Phone 中,通过选择“文件 | 新建项目”菜单命令创建一个新项目。

  2. 将显示“新建项目”窗口。展开“Visual C#”模板,然后选择“Silverlight for Windows Phone”模板。

  3. 选择“Windows Phone 应用程序”模板。在“名称”中填入您选择的名称。

  4. 单击“确定”。将显示 Windows Phone 平台选择窗口。为“Windows Phone OS 目标版本”选择“Windows Phone OS 7.1”

  5. 单击“确定”。将创建一个新的项目,并且“MainPage.xaml”将在 Visual Studio 设计器窗口中打开。

  6. 下一步是更改应用程序的 RootFrame 使用的模板以使后退堆栈可视化。这是通过在 RootFrame 的自定义 ControlTemplate 中放置 ListBox 来实现的。此新模板将在 App.xaml 页的 Application.Resources 部分中进行定义。在 App.xaml 文件中,使用以下标记替换 Application.Resources 条目。此模板由两行网格组成。ContentPresenter 位于第一行网格中。这是显示每个页面内容的地方。向第二行网格添加另一个网格以包含后退堆栈的 UI。此 UI 包含一个 ListBox,用于显示后退堆栈中的每个条目以及操作后退堆栈的一些按键。

    
    <Application.Resources>
            <ControlTemplate x:Name="NewFrameTemplate">
                <Grid x:Name="ClientArea">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <ContentPresenter Grid.Row="0"/>
                    <Border Grid.Row="1" BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="{StaticResource PhoneBorderThickness}" Height="300">
                        <Grid x:Name="ContentPanel" Background="{StaticResource PhoneSemitransparentBrush}">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition />
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <TextBlock  Grid.Row="0" x:Name="CurrentPage" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Center"/>
                            <ListBox Grid.Row="1" ItemsSource="{Binding}" x:Name="HistoryList" 
                                    HorizontalAlignment="Center" Height="300">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Border BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="{StaticResource PhoneBorderThickness}" Width="300" Margin="5" 
                                                Background="DarkGray" HorizontalAlignment="Center">
                                            <TextBlock Text="{Binding}"/>
                                        </Border>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>
                            <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
                                <Button Content="Pop Last" x:Name="btnPopLast" IsEnabled="false"/>
                                <Button Content="Pop To Selected" x:Name="btnPopToSelected" IsEnabled="false"/>
                            </StackPanel>
                        </Grid>
                    </Border>
    
                </Grid>
            </ControlTemplate>
        </Application.Resources>
    
    
  7. App.xaml 代码隐藏文件中,将下列声明添加到 App 类的顶部。将使用这些声明引用从自定义模板创建的 UI 元素。

    
    // UI controls on the RootFrame template.
    ListBox historyListBox;            // ListBox for listing the navigation history
    Button popLastButton;              // Button to pop the newest entry from the back stack
    Button popToSelectedButton;        // Button to pop all entries in the back stack up to the selected entry
    TextBlock currentPageTextBlock;    // TextBlock to display the current page the user is on
    
    
  8. App.xaml 页的代码隐藏文件中,将以下代码行直接添加在构造函数中对 InitializePhoneApplication 的调用之后。首先,将 RootFrame 的模板设置为 NewFrameTemplate,这是我们在步骤 6 中定义的模板名称。此处还挂钩模板上按键的事件处理程序。最后,为 RootFrameNavigated 事件定义委托以更新历史记录。

    
    // Set the template for the RootFrame to the new template we created in the Application.Resources in App.xaml
    RootFrame.Template = Resources["NewFrameTemplate"] as ControlTemplate;
    RootFrame.ApplyTemplate();
    
    popToSelectedButton = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("btnPopToSelected") as Button;
    popToSelectedButton.Click += new RoutedEventHandler(PopToSelectedButton_Click);
    
    popLastButton = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("btnPopLast") as Button;
    popLastButton.Click += new RoutedEventHandler(PopLastButton_Click);
    
    currentPageTextBlock = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("CurrentPage") as TextBlock;
    
    historyListBox = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("HistoryList") as ListBox;
    historyListBox.SelectionChanged += new SelectionChangedEventHandler(HistoryList_SelectionChanged);
    
    // Update the navigation history listbox whenever a navigation happens in the application
    RootFrame.Navigated += delegate { RootFrame.Dispatcher.BeginInvoke(delegate { UpdateHistory(); }); };
    
    
  9. App.xaml 页面代码隐藏文件添加以下方法。UpdateHistory 刷新导航后退堆栈的 UI。当在 RootFrame 上激发 Navigated 事件时调用此方法,每当在应用程序中发生导航时便会激发该事件。此方法循环访问 BackStack 属性中的所有条目并将其添加到 ListBox。它还显示当前页面的 URI。如果导航后退堆栈中存在多个条目,则会启用“Pop Last”按键。HistoryList_SelectionChanged 根据是否选择了导航历史记录列表中的项目,启用或禁用“Pop To Selected”按键。

    
    /// <summary>
    /// Use the BackStack property to refresh the navigation history list box with the latest history.
    /// </summary>
    void UpdateHistory()
    {
       historyListBox.Items.Clear();
       int i = 0;
    
       foreach (JournalEntry journalEntry in RootFrame.BackStack.Reverse())
       {
          historyListBox.Items.Insert(0, i + ": " + journalEntry.Source);
          i++;
       }
       currentPageTextBlock.Text = "[" + RootFrame.Source + "]";
       if (popLastButton != null)
       {
           popLastButton.IsEnabled = (historyListBox.Items.Count() > 0);
       }
    }
    
    /// <summary>
    /// Handle the SelectionChanged event for navigation history list.
    /// </summary>
    private void HistoryList_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
       if (historyListBox != null && popToSelectedButton != null)
       {
           popToSelectedButton.IsEnabled = (historyListBox.SelectedItems.Count > 0) ? true : false;
       }
    }
    
    
  10. 将以下方法添加到 App.xaml 页面的代码隐藏文件中。这会处理“Pop Last”按键的点按事件。当用户点按此按键时,将删除添加到后退堆栈中的最后一个条目。此方法使用 RootFrame 上的 RemoveBackEntry 方法。在删除条目后,通过调用 UpdateHistory 刷新后退堆栈条目列表。

    
    /// <summary>
    /// Remove the last entry from the back stack.
    /// </summary>
    private void PopLastButton_Click(object sender, RoutedEventArgs e)
    {
        // If RemoveBackEntry is called on an empty back stack, an InvalidOperationException is thrown.
        // Check to make sure the back stack has entries before calling RemoveBackEntry.
        if (RootFrame.BackStack.Count() > 0)
            RootFrame.RemoveBackEntry();
    
        // Refresh the history list since the back stack has been modified.
        UpdateHistory();
    }
    
    
  11. 将以下方法添加到 App.xaml 页面的代码隐藏文件中。如果用户选择后退堆栈中的某一项并点按“Pop To Selected”,则从后退堆栈中删除选定条目前的所有条目。RootFrame 上的 RemoveBackEntry 方法用于删除后退堆栈中的每个条目。在删除条目之后,通过调用 UpdateHistory 刷新后退堆栈 UI。

    
    /// <summary>
    /// Remove all entries from the back stack up to the selected item, but not including it.
    /// </summary>
    private void PopToSelectedButton_Click(object sender, RoutedEventArgs e)
    {
        // Make sure something has been selected.
        if (historyListBox != null && historyListBox.SelectedIndex >= 0)
        {
            for (int i = 0; i < historyListBox.SelectedIndex; i++)
            {
                RootFrame.RemoveBackEntry();
            }
            // Refresh the history list since the back stack has been modified.
            UpdateHistory();
        }
     }
    
    

本节介绍如何为应用程序的 RootFrame 添加自定义模板,以便我们在导航应用程序时可以检测并修改后退堆栈。为了演示此功能,我们需要向应用程序添加多个页面。这将在下一节中介绍。

为了演示导航历史记录检测和操作,我们需要向应用程序添加多个页面。本节介绍如何将这些页面添加到应用程序。我们的示例将包含四个页面:“MainPage.xaml”“Page1.xaml”“Page2.xaml”“Page3.xaml”。每个页面的结构都相同并使用相同的 UI。因此,将对每个页面重复以下步骤。在此示例中,未尝试通过帮助器方法或通过包装 UserControl 中的 UI 重新使用代码。

向应用程序添加页面

  1. 通过选择“项目 | 添加新项”菜单命令向项目添加一个新页面。将显示“添加新项”窗口。在项目列表中选择“Windows Phone 纵向页面”并在“名称”字段中键入 Page1.xaml。单击“添加”以将新页面添加到您的项目。现在名为 Page1.xaml 的新页面已添加到项目中。

  2. 首先,定义页面 UI。在 Page1.xaml 中,使用以下代码替换名为“ContentPanel”的网格。这会创建一个用于切换是否将页面固定到“开始”屏幕的 CheckBox 和一个用于导航到应用程序中下一页(如果下一页存在)的 Button。将页面固定到手机的“开始”屏幕是一件非常有趣的事情,在此演示此过程是为了显示点按“开始”屏幕上页面“磁贴”以启动应用程序时后退堆栈的状态。以下代码中显示的事件处理程序将在后续步骤中进行介绍。

    
    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <CheckBox x:Name="PinToStartCheckBox" Content="Pin To Start" IsChecked="False" HorizontalAlignment="Center" 
                  Click="PinToStartCheckBox_Click"/>
        <Button x:Name="btnNext" Content="Next Page"  Height="80" VerticalAlignment="Bottom" 
                Click="btnNext_Click"/>
    </Grid>
    
    
  3. 在此页的代码隐藏文件中,添加以下 using 指令。

    using Microsoft.Phone.Shell;
    
    
  4. 在类顶部添加以下变量声明。

    
    // The URI string of the next page to navigate to from this page.
    // String.Empty here means that there is no next page.
    private string nextPage;
    
    
  5. 在类构造函数的 InitializeComponent() 之后添加以下代码行。

    
    // Set the application title - use the same application title on each page.
    ApplicationTitle.Text = "SDK BACKSTACK SAMPLE";
    
    // Set a unique page title. In this example, we will use "page 1", "page 2", and so on.
    PageTitle.Text = "page 1";
    
    // Set the URI string of the next page, or String.Empty if there is no next page.
    nextPage = "/Page2.xaml";
    
    
    注意注意:

    由于将对您添加的每个页面重复此过程中的步骤,因此记住为创建的每个页面更新 PageTitle.TextnextPage

  6. 添加以下方法以重写 OnNavigatedTo 事件处理程序。如果此页设置了 nextPage 变量,则此处会显示“Next Page”按键。根据此页面的“磁贴”是否存在设置“Pin To Start”复选框。

    
    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
       base.OnNavigatedTo(e);
       
       // Show the Next button, if we have defined a next page.
       btnNext.Visibility = (String.IsNullOrWhiteSpace(nextPage)) ? Visibility.Collapsed : Visibility.Visible;
    
       if (ShellTile.ActiveTiles.FirstOrDefault(o => o.NavigationUri.ToString().Contains(NavigationService.Source.ToString())) == null)
          PinToStartCheckBox.IsChecked = false;
       else
          PinToStartCheckBox.IsChecked = true;
    }
    
    
  7. 添加以下方法以处理该页面上“Next Page”按键的 Click 事件。如果已经为此页定义了 nextpage 变量,则调用 Navigate 方法以导航到该页面。

    
    /// <summary>
    /// Navigate to the next page.
    /// </summary>
    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
         // Make sure to attempt navigation only if we have defined a next page.
         if (!String.IsNullOrWhiteSpace(nextPage))
         {
            this.NavigationService.Navigate(new Uri(nextPage, UriKind.Relative));
         }
    }
    
    
  8. 添加以下方法以处理该页上“Pin To Start”复选框的 Click 事件。该操作类似于切换操作。如果此页的“磁贴”已经存在于“开始”屏幕上,则将其删除。如果“开始”屏幕上不存在此页的“磁贴”,则添加一个磁贴。有关“磁贴”的更多信息,请参阅 Windows Phone 的图块

    
    /// <summary>
    /// Toggle pinning a Tile for this page on the Start screen.
    /// </summary>
    private void PinToStartCheckBox_Click(object sender, RoutedEventArgs e)
    {
       // Try to find a Tile that has this page's URI.
       ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(o => o.NavigationUri.ToString().Contains(NavigationService.Source.ToString()));
    
       if (tile == null)
       {
          // No Tile was found, so add one for this page.
          StandardTileData tileData = new StandardTileData { Title = PageTitle.Text };
          ShellTile.Create(new Uri(NavigationService.Source.ToString(), UriKind.Relative), tileData);
       }
       else
       {
          // A Tile was found, so remove it.
          tile.Delete();
       }
    }
    
    
    注意注意:

    如果您的应用程序允许用户固定页面,则应该考虑是否需要使用“Home”按键来允许用户快速返回应用程序的根目录。Home 按键的实现将导航到应用程序的主页,然后清除整个导航后退堆栈。检查每种导航方案并确定在每种情况下是否需要此功能。如果固定的页面是自包含页面(例如联系人信息),则用户可能需要点按该页面、查看信息,然后退出应用程序。在这种情况下退出应用程序可以使用硬件“返回”按键实现。如果固定的页面是用户从中执行深入导航应用程序的入口点,则可能需要返回应用程序根目录的快捷方法。例如,如果固定的页面是购物车,则用户可能要在购物车中完成购买,然后再次开始购物。在此情况下,为用户提供一个 Home 按键可以改善用户的体验,因为这减少了用户返回应用程序开始位置所需执行的点按次数。

  9. 上述步骤介绍如何添加页面和更新页面以便在此应用程序中使用。若要完成应用程序,应按以下方式对 MainPage、Page2 和 Page 3 重复这些步骤。

    页名称

    新建页面?

    步骤 5 的更改

    MainPage.xaml

    否。此页在创建应用程序时创建。对此页重复步骤 2 至 8。

    PageTitle.Text = “main”;

    nextPage = “/Page1.xaml”;

    Page2.xaml

    是。对此页重复步骤 1 至 8。

    PageTitle.Text = “page 2”;

    nextPage = “/Page3.xaml”;

    Page3.xaml

    是。对此页重复步骤 1 至 8。

    PageTitle.Text = “page 3”;

    nextPage = String.Empty

    为下一页指定 String.Empty,因为 page 3 是应用程序中的最后一页,我们不希望从 page 3 向前导航。

完成本节后,解决方案资源管理器中的解决方案如下图所示。该应用程序有四个页面,在这些页面之间的前进导航如下所示:MainPage.xaml -> Page1.xaml -> Page2.xaml -> Page3.xaml

BackStack HowTo 的解决方案视图

本节介绍如何运行本主题中生成的应用程序。

测试应用程序的步骤

  1. 通过选择“调试 | 启动调试”菜单命令运行应用程序。

  2. 将启动应用程序,并显示 MainPage.xaml 页。该页如下图所示。屏幕下半部分,显示后退堆栈可视化。此时导航历史记录中没有任何内容,因此列表为空。当前页显示为“[/MainPage.xaml]”

    BackStack 初始 UI
  3. 点按“Next Page”按键。观察对“page 1”的页更改,后退堆栈列表现在包含“/MainPage.xaml”

  4. 再次点按“Next Page”按键。当前页面为“page 2”。后退堆栈现在包含两个条目:“/Page1.xaml”“/MainPage.xaml”。作为堆栈,它会将最新条目显示在顶部并将最旧条目显示在底部。下图对此进行了阐释。

    BackStack HowTo 的中间 UI
  5. 点按“Pop Last”按键。将从后退堆栈列表中删除“/Page1.xaml”条目。

  6. 点按的手机上的硬件“返回”按键。将显示“main page”,并且后退堆栈为空。发生这种情况是因为我们在前一个步骤中从后退堆栈删除了“/Page1.xaml”,因此会将后退导航从“Page 2 -> Page1 -> MainPage”更改为“Page2 -> MainPage”

  7. 点按“main page”上的“Next Page”按键。将显示“page 1”页。后退堆栈中有一个条目:“/MainPage.xaml”

  8. 点按“page 1”上的“Next Page”按键。此时将显示“page 2”页。后退堆栈包含两个条目:“/Page1.xaml”“/MainPage.xaml”

  9. 点按“page 2”上的“Pin To Start”

  10. 将在手机的“开始”屏幕上创建名为“page 2”的“磁贴”。

  11. 点按在上一个步骤中创建的“磁贴”。将启动该应用程序,并显示“page 2”。还请注意后退堆栈为空。如果在手机上点按“返回”按键,则应用程序会终止。下图显示了此状态的一个示例。“page 2”被固定到手机的“开始”屏幕。点按“磁贴”后,应用程序将启动,并显示 Page2。此图形中显示的后退堆栈为空。

    从磁贴导航时 BackStack 的状态

本文是否对您有所帮助?
(1500 个剩余字符)
感谢您的反馈
显示:
© 2014 Microsoft. 版权所有。