如何使用 Windows Phone 的後退堆棧導航

轉自:http://msdn.microsoft.com/zh-cn/library/windowsphone/develop/ff402561(v=vs.105).aspx

源碼:Navigate Using the Back Stack for Windows Phone.zip

 

適用於: Windows Phone 8 | Windows Phone OS 7.1

  

本主題介紹如何通過操作應用的導航歷史記錄(稱爲後退堆棧),以便修改其導航。您可以使用 NavigationService API 來檢查及處理導航歷史記錄。本主題將使用NavigationService 類的屬性和方法來檢測後退堆棧、刪除條目,然後觀察這些更改對應用導航所產生的影響。

說明注意:

在本主題中,導航歷史記錄一詞和後退堆棧一詞可換用,它們都是指由NavigationService.BackStack 屬性公開的導航歷史記錄。

演示該功能的應用的完整示例可以下載。有關更多信息,請參閱使用 Windows Phone 的後退堆棧進行導航。這一可下載的示例面向 Windows Phone OS 7.1。此主題中的代碼已調整爲面向 Windows Phone 8。

介紹導航歷史記錄或後退堆棧

應用的導航歷史記錄表示爲後進先出結構,稱爲堆棧。此處該結構還稱爲後退堆棧,因爲它在表示應用後退導航的堆棧結構中,包含一組頁面。

可以將該堆棧看成是一疊盤子。添加到該堆棧的最後一個盤子就是可以移除的第一個盤子。最新項被添加到此堆棧的頂部。此操作稱爲推送操作。通過從堆棧頂部一次刪除一個項目,可以檢索堆棧中的某些內容。從堆棧中刪除頂部項的操作稱爲彈出操作。下圖顯示了堆棧的概念。

BackStack stack representation

當應用中的頁面調用 Navigate 時,當前頁面會被放到後退堆棧上,並且系統將創建並顯示目標頁的新實例。當您在應用的頁面之間進行導航時,系統會將多個條目添加到此堆棧。

當頁面調用 GoBack 時,或者當用戶按手機的“返回”按鍵時,將放棄當前頁面,並將堆棧頂部的頁面從後退堆棧中彈出並進行顯示。此後退導航會繼續彈出並顯示,直到堆棧中不再有條目。此時,點按手機的“返回”按鈕將退出應用。

大多數應用都無需處理後退堆棧,並且在默認導航中可以發揮完整功能。其它應用則需要調整導航歷史記錄,以提供最佳用戶體驗。例如,應用中可能有一個登錄頁面。您可能不希望用戶在登錄後能夠導航回登錄頁面 。

本主題演示如何使用 BackStack 屬性和RemoveBackEntry 方法操作導航歷史記錄。

 

創建示例應用以可視化後退堆棧

本節介紹如何在應用中使導航歷史記錄或後退堆棧可視化,以便在應用運行時輕鬆地對其進行檢測。示例應用包含多個頁面。從一個頁面導航到下一個頁面時,您可以查看後退堆棧上有哪些條目。您還可以輕鬆移除條目,並查看後退堆棧中反映的這些更新。在此應用中,後退堆棧將顯示爲列表,如下圖所示。

BackStack Initial UI

上圖中的灰色區域是示例應用中的後退堆棧可視化。在導航應用時,該列表將使用導航歷史記錄中的條目進行填充。您可以使用“Pop Last”“Pop To Selected”按鈕來更改導航歷史記錄。

此灰色列表並不是在每個單獨頁面上都存在。而是會將該列表添加到應用的 RootFrame 中的某個單獨位置。RootFrame 對象是與應用關聯的PhoneApplicationFrame。每個應用都有一個RootFrame。當用戶導航到該頁面時,導航框架會將應用的每個頁面或PhoneApplicationPage 的實例設置爲框架的Content。在創建新 Windows Phone 應用時獲取的RootFrame 對象的默認模板會顯示應用頁面和其他元素(例如該應用的系統托盤和應用欄)。在此示例中,您將創建一個模板以顯示每個頁面,但會在屏幕底部留下一些空間用於以列表形式顯示後退堆棧。在逐頁進行導航時,將更新此列表以反映應用的導航歷史記錄或後退堆棧的當前狀態。

有關 Windows Phone 應用剖析的更多信息,請參見 Windows Phone 應用內導航

 

創建示例應用

使後退堆棧視化

  1. 在 Visual Studio 中,通過選擇“文件 | 新建 | 項目”菜單命令創建一個新項目。

  2. 將顯示“新建項目”窗口。展開“Visual C#”模板,然後選擇“Windows Phone”模板。

  3. 選擇 Windows Phone 應用 模板。在“名稱”中填入您選擇的名稱。

  4. 單擊“確定”。將顯示 Windows Phone 平臺選擇對話框。選擇面向的版本或接受默認版本。

  5. 單擊“確定”。將創建一個新的項目,並且“MainPage.xaml”將在 Visual Studio 設計器窗口中打開。

  6. 下一步是更改應用的 RootFrame 使用的模板以使後退堆棧可視化。這是通過在 RootFrame 的自定義ControlTemplate 中放置ListBox 來實現的。在App.xaml 文件中,使用以下標記替換Application.Resources 條目。

    此模板由兩行網格組成。ContentPresenter 位於網格第一行。這是顯示每個頁面內容的地方。向第二行網格添加另一個網格以包含後退堆棧的 UI。此 UI 包含ListBox,用於顯示後退堆棧中的每個條目以及操作後退堆棧的一些按鍵。

    XAML
    <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.cs 代碼隱藏文件中,添加以下  using 語法。

    C#
    using System.Windows.Controls;
    

    然後,將以下聲明添加到 App 類的頂部。將使用這些聲明引用從自定義模板創建的 UI 元素。

    C#
    // 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.cs 代碼隱藏文件中,添加以下  using 語法。

    C#
    using System.Windows.Media;
    

    然後在構造函數中的 InitializePhoneApplication 調用後面添加以下代碼行。首先,將RootFrame 的模板設置爲NewFrameTemplate,這是您在步驟 6 中定義的模板名稱。此處還掛鉤模板上按鍵的事件處理程序。最後,爲RootFrameNavigated 事件定義委託以更新歷史記錄。

    請注意,事件處理程序尚未創建。

    C#
    // Set the template for the RootFrame to the new template you 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.cs 代碼隱藏文件添加以下方法。UpdateHistory 刷新導航後退堆棧的 UI。當在RootFrame 上激發Navigated 事件時調用此方法,每當在應用中發生導航時便會激發該事件。此方法循環訪問BackStack 屬性中的所有條目並將其添加到ListBox。它還顯示當前頁面的 URI。如果導航後退堆棧中存在多個條目,則會啓用“Pop Last”按鍵。HistoryList_SelectionChanged 根據是否選擇了導航歷史記錄列表中的項目,啓用或禁用“Pop To Selected”按鍵。

    C#
    /// <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)
       {
          historyListBox.Items.Insert(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.cs 代碼隱藏文件。這會處理“Pop Last”按鍵的點按事件。當用戶點按此按鍵時,將刪除添加到後退堆棧中的最後一個條目。此方法使用RootFrame 上的RemoveBackEntry 方法。在刪除條目後,通過調用UpdateHistory 刷新後退堆棧條目列表。

    C#
    /// <summary>
    /// Remove the last entry from the back stack.
    /// </summary>
    private void PopLastButton_Click(object sender, RoutedEventArgs e)
    {
    
        RootFrame.RemoveBackEntry();
    
        // Refresh the history list since the back stack has been modified.
        UpdateHistory();
    }
    
    
  11. 將以下方法添加到 App.xaml.cs 代碼隱藏文件。如果用戶選擇後退堆棧中的某一項並點按“Pop To Selected”,則從後退堆棧中刪除選定條目前的所有條目。RootFrame 上的RemoveBackEntry 方法用於刪除後退堆棧中的每個條目。在刪除條目之後,通過調用UpdateHistory 刷新後退堆棧 UI。

    C#
    /// <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.xamlPage1.xamlPage2.xamlPage3.xaml。每個頁面的結構都相同並使用相同的 UI。因此,將對每個頁面重複以下步驟。在此示例中,未嘗試通過幫助器方法或通過包裝UserControl 中的 UI 重新使用代碼。

嚮應用添加頁面

  1. 通過選擇“項目 | 添加新項”菜單命令向項目添加一個新頁面。將顯示“添加新項”窗口。在項目列表中選擇“Windows Phone 縱向頁面”並在“名稱”字段中鍵入Page1.xaml。單擊“添加”以將新頁面添加到您的項目。現在名爲 Page1.xaml 的新頁面已添加到項目中。

  2. 首先,定義頁面 UI。在 Page1.xaml 中,使用以下代碼替換名爲 ContentPanel 的網格。這會創建一個用於切換是否將頁面固定到“開始”屏幕的 CheckBox 和一個用於導航到應用中下一頁(如果下一頁存在)的Button。將頁面固定到手機的“開始”屏幕是一件非常有趣的事情,在此演示此過程是爲了顯示點按“開始”屏幕上頁面“圖塊”以啓動應用時後退堆棧的狀態。以下代碼中顯示的事件處理程序將在後續步驟中進行介紹。

    XAML
    <!--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" 
    VerticalAlignment="Center" Click="PinToStartCheckBox_Click"/>
        <Button x:Name="btnNext" Content="Next Page"  Height="80" VerticalAlignment="Bottom" 
    Click="btnNext_Click"/>
    </Grid>
    
    
  3. 爲 2 個 XAML TextBlock 控件指定名稱,以便能夠以編程方式設置它們的值。

    XAML
            <StackPanel Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="AppName" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
    
  4. Page1.xaml.cs 代碼隱藏文件中,將下列變量聲明添加到類的頂部。

    C#
    // 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() 之後添加以下代碼行。

    說明注意:

    爲其他幾個頁面重複此過程的步驟。對於您添加的每個附加頁面,切記在此代碼中更新頁面標題和 nextPage 變量的值。

    C#
    // Set the application title - use the same application title on each page.
    AppName.Text = "SDK BACKSTACK SAMPLE";
    
    // Set a unique page title. In this example, you 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";
    
    
  6. 添加以下方法以重寫 OnNavigatedTo 事件處理程序。如果此頁設置了nextPage 變量,則此處會顯示“Next Page”按鍵。根據此頁面的“圖塊”是否存在設置“Pin To Start”複選框。

    C#
    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
       base.OnNavigatedTo(e);
       
       // Show the Next button, if you 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 方法以導航到該頁面。

    C#
    /// <summary>
    /// Navigate to the next page.
    /// </summary>
    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
         // Make sure to attempt navigation only if you have defined a next page.
         if (!String.IsNullOrWhiteSpace(nextPage))
         {
            this.NavigationService.Navigate(new Uri(nextPage, UriKind.Relative));
         }
    }
    
    
  8. 添加以下方法以處理該頁上“Pin To Start”複選框的 Click 事件。該操作類似於切換操作。如果此頁的“圖塊”已經存在於“開始”屏幕上,則將其刪除。如果“開始”屏幕上不存在此頁的“圖塊”,則添加一個圖塊。有關“圖塊”的更多信息,請參見Windows Phone 的圖塊

    C#
    /// <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. 上述步驟介紹如何添加頁面和更新頁面以便在此應用中使用。要完成應用,請爲以下所示的主頁、頁面 2 和頁面 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,因爲頁面 3 是應用中的最後一頁。不能從頁面 3 繼續向前導航。

完成本節後,解決方案資源管理器中的解決方案如下圖所示。該應用有四個頁面,在這些頁面之間的前進導航如下所示:MainPage.xaml -> Page1.xaml -> Page2.xaml -> Page3.xaml

Solution view for BackStack HowTo

 

測試應用

本節介紹如何運行本主題中生成的應用。

測試應用的步驟

  1. 通過選擇“調試 | 啓動調試”菜單命令運行應用。

  2. 將啓動應用,並顯示 MainPage.xaml 頁。該頁如下圖所示。屏幕下半部分,顯示後退堆棧可視化。此時導航歷史記錄中沒有任何內容,因此列表爲空。當前頁顯示爲“[/MainPage.xaml]”

    BackStack Initial UI
  3. 點按“Next Page”按鍵。觀察對“page 1”的頁更改,後退堆棧列表現在包含“/MainPage.xaml”

  4. 再次點按“Next Page”按鍵。當前頁面爲“page 2”。後退堆棧現在包含兩個條目:/Page1.xaml/MainPage.xaml。作爲堆棧,它會將最新條目顯示在頂部並將最舊條目顯示在底部。下圖對此進行了闡釋。

    Interim UI for BackStack HowTo
  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。此圖形中顯示的後退堆棧爲空。

    State of BackStack when navigation from Tile.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章