轉自: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。
應用的導航歷史記錄表示爲後進先出結構,稱爲堆棧。此處該結構還稱爲後退堆棧,因爲它在表示應用後退導航的堆棧結構中,包含一組頁面。
可以將該堆棧看成是一疊盤子。添加到該堆棧的最後一個盤子就是可以移除的第一個盤子。最新項被添加到此堆棧的頂部。此操作稱爲推送操作。通過從堆棧頂部一次刪除一個項目,可以檢索堆棧中的某些內容。從堆棧中刪除頂部項的操作稱爲彈出操作。下圖顯示了堆棧的概念。
當應用中的頁面調用 Navigate 時,當前頁面會被放到後退堆棧上,並且系統將創建並顯示目標頁的新實例。當您在應用的頁面之間進行導航時,系統會將多個條目添加到此堆棧。
當頁面調用 GoBack 時,或者當用戶按手機的“返回”按鍵時,將放棄當前頁面,並將堆棧頂部的頁面從後退堆棧中彈出並進行顯示。此後退導航會繼續彈出並顯示,直到堆棧中不再有條目。此時,點按手機的“返回”按鈕將退出應用。
大多數應用都無需處理後退堆棧,並且在默認導航中可以發揮完整功能。其它應用則需要調整導航歷史記錄,以提供最佳用戶體驗。例如,應用中可能有一個登錄頁面。您可能不希望用戶在登錄後能夠導航回登錄頁面 。
本主題演示如何使用 BackStack 屬性和RemoveBackEntry 方法操作導航歷史記錄。
本節介紹如何在應用中使導航歷史記錄或後退堆棧可視化,以便在應用運行時輕鬆地對其進行檢測。示例應用包含多個頁面。從一個頁面導航到下一個頁面時,您可以查看後退堆棧上有哪些條目。您還可以輕鬆移除條目,並查看後退堆棧中反映的這些更新。在此應用中,後退堆棧將顯示爲列表,如下圖所示。
上圖中的灰色區域是示例應用中的後退堆棧可視化。在導航應用時,該列表將使用導航歷史記錄中的條目進行填充。您可以使用“Pop Last”和“Pop To Selected”按鈕來更改導航歷史記錄。
此灰色列表並不是在每個單獨頁面上都存在。而是會將該列表添加到應用的 RootFrame 中的某個單獨位置。RootFrame 對象是與應用關聯的PhoneApplicationFrame。每個應用都有一個RootFrame。當用戶導航到該頁面時,導航框架會將應用的每個頁面或PhoneApplicationPage 的實例設置爲框架的Content。在創建新 Windows Phone 應用時獲取的RootFrame 對象的默認模板會顯示應用頁面和其他元素(例如該應用的系統托盤和應用欄)。在此示例中,您將創建一個模板以顯示每個頁面,但會在屏幕底部留下一些空間用於以列表形式顯示後退堆棧。在逐頁進行導航時,將更新此列表以反映應用的導航歷史記錄或後退堆棧的當前狀態。
有關 Windows Phone 應用剖析的更多信息,請參見 Windows Phone 應用內導航。
使後退堆棧視化
-
在 Visual Studio 中,通過選擇“文件 | 新建 | 項目”菜單命令創建一個新項目。
-
將顯示“新建項目”窗口。展開“Visual C#”模板,然後選擇“Windows Phone”模板。
-
選擇 Windows Phone 應用 模板。在“名稱”中填入您選擇的名稱。
-
單擊“確定”。將顯示 Windows Phone 平臺選擇對話框。選擇面向的版本或接受默認版本。
-
單擊“確定”。將創建一個新的項目,並且“MainPage.xaml”將在 Visual Studio 設計器窗口中打開。
-
下一步是更改應用的 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>
-
在 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
-
在 App.xaml.cs 代碼隱藏文件中,添加以下 using 語法。
C#using System.Windows.Media;
然後在構造函數中的 InitializePhoneApplication 調用後面添加以下代碼行。首先,將RootFrame 的模板設置爲NewFrameTemplate,這是您在步驟 6 中定義的模板名稱。此處還掛鉤模板上按鍵的事件處理程序。最後,爲RootFrame 的Navigated 事件定義委託以更新歷史記錄。
請注意,事件處理程序尚未創建。
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(); }); };
-
向 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; } }
-
將以下方法添加到 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(); }
-
將以下方法添加到 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.xaml、Page1.xaml、Page2.xaml 和Page3.xaml。每個頁面的結構都相同並使用相同的 UI。因此,將對每個頁面重複以下步驟。在此示例中,未嘗試通過幫助器方法或通過包裝UserControl 中的 UI 重新使用代碼。
嚮應用添加頁面
-
通過選擇“項目 | 添加新項”菜單命令向項目添加一個新頁面。將顯示“添加新項”窗口。在項目列表中選擇“Windows Phone 縱向頁面”並在“名稱”字段中鍵入Page1.xaml。單擊“添加”以將新頁面添加到您的項目。現在名爲 Page1.xaml 的新頁面已添加到項目中。
-
首先,定義頁面 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>
-
爲 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>
-
在 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;
-
在類構造函數的 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";
-
添加以下方法以重寫 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; }
-
添加以下方法以處理該頁面上“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)); } }
-
添加以下方法以處理該頁上“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 按鍵可以改善用戶的體驗,因爲這減少了用戶返回應用開始位置所需執行的點按次數。
-
-
上述步驟介紹如何添加頁面和更新頁面以便在此應用中使用。要完成應用,請爲以下所示的主頁、頁面 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。
本節介紹如何運行本主題中生成的應用。
測試應用的步驟
-
通過選擇“調試 | 啓動調試”菜單命令運行應用。
-
將啓動應用,並顯示 MainPage.xaml 頁。該頁如下圖所示。屏幕下半部分,顯示後退堆棧可視化。此時導航歷史記錄中沒有任何內容,因此列表爲空。當前頁顯示爲“[/MainPage.xaml]”
-
點按“Next Page”按鍵。觀察對“page 1”的頁更改,後退堆棧列表現在包含“/MainPage.xaml”。
-
再次點按“Next Page”按鍵。當前頁面爲“page 2”。後退堆棧現在包含兩個條目:/Page1.xaml 和/MainPage.xaml。作爲堆棧,它會將最新條目顯示在頂部並將最舊條目顯示在底部。下圖對此進行了闡釋。
-
點按“Pop Last”按鍵。將從後退堆棧列表中刪除“/Page1.xaml”條目。
-
點按的手機上的硬件“返回”按鍵。將顯示“main page”,並且後退堆棧爲空。發生這種情況是因爲我們在前一個步驟中從後退堆棧刪除了“/Page1.xaml”,因此會將後退導航從“Page 2 -> Page1 -> MainPage”更改爲“Page2 -> MainPage”。
-
點按“main page”上的“Next Page”按鍵。將顯示“page 1”頁。後退堆棧中有一個條目:“/MainPage.xaml”。
-
點按“page 1”上的“Next Page”按鍵。此時將顯示“page 2”頁。後退堆棧包含兩個條目:“/Page1.xaml”和“/MainPage.xaml”。
-
點按“page 2”上的“Pin To Start”。
-
將在手機的“開始”屏幕上創建名爲“page 2”的“圖塊”。
-
點按在上一個步驟中創建的“圖塊”。將啓動該應用,並顯示“page 2”。還請注意後退堆棧爲空。如果在手機上點按“返回”按扭,則應用會終止。下圖顯示了此狀態的一個示例。“page 2”被固定到手機的“開始”屏幕。點按“圖塊”後,應用將啓動,並顯示 Page2。此圖形中顯示的後退堆棧爲空。