階層式導覽
NavigationPage 類別提供的階層式導覽體驗讓使用者能夠視需要,向前及向後巡覽頁面。 此類別會實作一堆後進先出 (LIFO) 的 Page 物件導覽。 本文示範如何使用 NavigationPage 類別,在一堆頁面中執行導覽。
若要從一頁移到另一頁,應用程式會將新的頁面推送到導覽堆疊上,該頁面就會變成使用中頁面,如下圖所示:
若要返回上一頁,應用程式將會從導覽堆疊中快顯目前的頁面,新的最上層頁面就會變成使用中頁面,如下圖所示:
Navigation
屬性會在任何 Page
衍生類型上公開導覽方法。 這些方法可讓您將頁面推送到導覽堆疊上、從導覽堆疊中快顯頁面,以及執行堆疊操作。
執行導覽
在階層式導覽中,NavigationPage
類別用來導覽一疊 ContentPage
物件。 下列螢幕擷取畫面顯示每個平台上 NavigationPage
的主要元件:
NavigationPage
的配置取決於平台:
- 在 iOS 上,頁面頂端有一個導覽列,其中顯示標題,並具有可返回上一頁的 [上一頁] 按鈕。
- 在 Android 上,頁面頂端有一個導覽列,其中顯示標題、圖示,以及可返回上一頁的 [上一頁] 按鈕。 該圖示是在
[Activity]
屬性中定義,以裝飾 Android 平台特定專案中的MainActivity
類別。 - 在通用 Windows 平台上,頁面頂端有一個顯示標題的導覽列。
在所有平台上,Page.Title
屬性的值會顯示為頁面標題。 此外, IconColor
屬性也可以設定為 Color
套用至導覽列中圖示的 。
注意
建議僅以 ContentPage
執行個體填入 NavigationPage
。
建立根頁面
加入至導覽堆疊中的第一頁指的是應用程式的根頁面,下列程式碼範例會示範如何完成此作業:
public App ()
{
MainPage = new NavigationPage (new Page1Xaml ());
}
這會導致Page1Xaml
ContentPage
實例推送至流覽堆疊,而該堆疊會變成使用中的頁面和應用程式的根頁面。 如下列螢幕擷取畫面所示:
注意
NavigationPage
執行個體的 RootPage
屬性可讓您存取導覽堆疊中的第一頁。
將頁面推送到導覽堆疊
若要巡覽至 Page2Xaml
,必須在目前頁面的 Navigation
屬性上叫用 PushAsync
方法,如下列程式碼範例所示:
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PushAsync (new Page2Xaml ());
}
這會使 Page2Xaml
執行個體推送至導覽堆疊上,而變成使用中的頁面。 如下列螢幕擷取畫面所示:
叫用 PushAsync
方法時,會發生下列事件:
- 呼叫
PushAsync
的頁面叫用其OnDisappearing
覆寫。 - 要巡覽的頁面叫用其
OnAppearing
覆寫。 PushAsync
工作完成。
不過,發生這些事件的確切順序則取決於平台。 如需詳細資訊,請參閱查理斯·佩茨羅德的Xamarin.Forms書第 24 章。
注意
呼叫 OnDisappearing
和 OnAppearing
覆寫無法保證一定是指頁面導覽。 例如,在 iOS 上,當應用程式終止時,會在使用中頁面上呼叫 OnDisappearing
覆寫。
從導覽堆疊中快顯頁面
無論是裝置上的實體按鈕還是螢幕上的按鈕,按下裝置上的 [上一頁] 按鈕都可以從導覽堆疊快顯使用中的頁面。
若要以程式設計的方式返回原始頁面,Page2Xaml
執行個體必須叫用 PopAsync
方法,如下列程式碼範例所示:
async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PopAsync ();
}
這會使 Page2Xaml
執行個體從導覽堆疊中移除,進而使新的最上層頁面變成使用中的頁面。 叫用 PopAsync
方法時,會發生下列事件:
- 呼叫
PopAsync
的頁面叫用其OnDisappearing
覆寫。 - 要返回的頁面叫用其
OnAppearing
覆寫。 - 傳回
PopAsync
工作。
不過,發生這些事件的確切順序則取決於平台。 如需詳細資訊,請參閱查理斯·佩茨羅德的Xamarin.Forms書第24章。
除了 PushAsync
和 PopAsync
方法,每頁的 Navigation
屬性還會提供 PopToRootAsync
方法,如下列程式碼範例所示:
async void OnRootPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PopToRootAsync ();
}
此方法會從導覽堆疊中快顯根 Page
以外的所有頁面,因此讓應用程式的根頁面成為使用中頁面。
以動畫顯示頁面轉換
每頁的 Navigation
屬性還會提供覆寫的 push 和 pop 方法,其中包含控制是否在巡覽期間顯示頁面動畫的 boolean
參數,如下列程式碼範例所示:
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PushAsync (new Page2Xaml (), false);
}
async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PopAsync (false);
}
async void OnRootPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PopToRootAsync (false);
}
只要基礎平台支援,將 boolean
參數設定為 false
會停用頁面轉換動畫,將此參數設定為 true
會啟用頁面轉換動畫。 不過,缺少此參數的 push 和 pop 方法預設會啟用動畫。
巡覽時傳遞資料
有時必須在巡覽期間將某頁資料傳遞到另一頁。 有兩種方法可以完成此作業:透過頁面建構函式來傳遞資料,以及透過設定新頁面與資料的 BindingContext
。 現在將依序討論各種方法。
透過頁面建構函式傳遞資料
在巡覽期間將資料傳遞到另一頁的最簡單方法,就是透過頁面建構函式參數,如下列程式碼範例所示:
public App ()
{
MainPage = new NavigationPage (new MainPage (DateTime.Now.ToString ("u")));
}
此程式碼會建立 MainPage
執行個體,並以 ISO8601 格式傳入目前的日期和時間,這會包裝在 NavigationPage
執行個體中。
MainPage
執行個體會透過建構函式參數接收資料,如下列程式碼範例所示:
public MainPage (string date)
{
InitializeComponent ();
dateLabel.Text = date;
}
接著可設定 Label.Text
屬性在頁面上顯示資料,如下列螢幕擷取畫面所示:
透過 BindingContext 傳遞資料
在巡覽期間將資料傳遞到另一頁的另一個方法,就是設定新頁面與資料的 BindingContext
,如下列程式碼範例所示:
async void OnNavigateButtonClicked (object sender, EventArgs e)
{
var contact = new Contact {
Name = "Jane Doe",
Age = 30,
Occupation = "Developer",
Country = "USA"
};
var secondPage = new SecondPage ();
secondPage.BindingContext = contact;
await Navigation.PushAsync (secondPage);
}
此程式碼會將 SecondPage
執行個體的 BindingContext
設定為 Contact
執行個體,然後巡覽至 SecondPage
。
SecondPage
會接著使用資料繫結來顯示 Contact
執行個體資料,如下列 XAML 程式碼範例所示:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PassingData.SecondPage"
Title="Second Page">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
...
<Button x:Name="navigateButton" Text="Previous Page" Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
下列程式碼範例示範如何在 C# 中完成資料繫結:
public class SecondPageCS : ContentPage
{
public SecondPageCS ()
{
var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
nameLabel.SetBinding (Label.TextProperty, "Name");
...
var navigateButton = new Button { Text = "Previous Page" };
navigateButton.Clicked += OnNavigateButtonClicked;
Content = new StackLayout {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Children = {
new StackLayout {
Orientation = StackOrientation.Horizontal,
Children = {
new Label{ Text = "Name:", FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)), HorizontalOptions = LayoutOptions.FillAndExpand },
nameLabel
}
},
...
navigateButton
}
};
}
async void OnNavigateButtonClicked (object sender, EventArgs e)
{
await Navigation.PopAsync ();
}
}
接著可透過一連串的 Label
控制項在頁面上顯示資料,如下列螢幕擷取畫面所示:
如需有關資料繫結的詳細資訊,請參閱資料繫結基本概念。
操作導覽堆疊
Navigation
屬性會公開可以從中取得導覽堆疊中頁面的 NavigationStack
屬性。 雖然 Xamarin.Forms 會維護流覽堆疊的存取權, Navigation
但 屬性會藉由插入頁面或移除頁面來提供 InsertPageBefore
和 RemovePage
方法來操作堆疊。
InsertPageBefore
方法會將指定的頁面插入導覽堆疊中現有指定頁面之前,如下圖所示:
RemovePage
方法會從導覽堆疊中移除指定的頁面,如下圖所示:
這些方法會啟用自訂導覽體驗,例如在成功登入之後,以新頁面取代登入頁面。 下列程式碼範例將示範此情節:
async void OnLoginButtonClicked (object sender, EventArgs e)
{
...
var isValid = AreCredentialsCorrect (user);
if (isValid) {
App.IsUserLoggedIn = true;
Navigation.InsertPageBefore (new MainPage (), this);
await Navigation.PopAsync ();
} else {
// Login failed
}
}
若使用者的認證正確,MainPage
執行個體會插入導覽堆疊中目前頁面之前。 PopAsync
方法會接著從導覽堆疊中移除目前頁面,進而使 MainPage
執行個體變成使用中頁面。
在導覽列中顯示檢視
任何 Xamarin.FormsView
都可以顯示在的 NavigationPage
導覽列中。 這是透過將 NavigationPage.TitleView
附加屬性設定為 View
來完成。 此附加屬性可以在任何 Page
上設定,而且當 Page
推送到 NavigationPage
時,NavigationPage
會採用該屬性的值。
下列範例示範如何從 XAML 設定 NavigationPage.TitleView
附加屬性:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NavigationPageTitleView.TitleViewPage">
<NavigationPage.TitleView>
<Slider HeightRequest="44" WidthRequest="300" />
</NavigationPage.TitleView>
...
</ContentPage>
以下是對等的 C# 程式碼:
public class TitleViewPage : ContentPage
{
public TitleViewPage()
{
var titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
NavigationPage.SetTitleView(this, titleView);
...
}
}
這會導致在 NavigationPage
的導覽列中顯示 Slider
:
重要
除非使用 WidthRequest
和 HeightRequest
屬性指定檢視的大小,否則許多檢視不會出現在導覽列中。 或者,您可以將檢視包裝在 StackLayout
中,並將 HorizontalOptions
和 VerticalOptions
屬性設定為適當的值。
請注意,由於 Layout
類別衍生自 View
類別,因此可設定 TitleView
附加屬性,顯示包含多個檢視的配置類別。 在 iOS 和通用 Windows 平台 (UWP) 上,無法變更導覽列的高度,因此如果導覽列中所顯示檢視大於導覽列的預設大小,則會發生裁剪。 不過,在 Android 上,則可以變更導覽列的高度,方法是將 NavigationPage.BarHeight
可繫結屬性設定為表示新高度的 double
。 如需詳細資訊,請參閱設定 NavigationPage 上的導覽列高度。
或者,您可以將一些內容放在導覽列中,並將一些內容放在色彩符合導覽列的頁面內容頂端檢視中,來建議擴充導覽列。 此外,在 iOS 上,您可以將 NavigationPage.HideNavigationBarSeparator
可繫結屬性設定為 true
,來移除導覽列底部的分隔線和陰影。 如需詳細資訊,請參閱隱藏 NavigationPage 上的導覽列分隔線。
注意
BackButtonTitle
、Title
、TitleIcon
和 TitleView
屬性全都能定義佔用導覽列上空間的值。 由於導覽列大小會依平台和螢幕大小而有所不同,因此設定上述所有屬性將會由於可用空間有限而導致衝突。 與其嘗試使用這些屬性的組合,您可能會發現單獨設定 TitleView
屬性更容易取得您想要的導覽列設計。
限制
在 NavigationPage
的導覽列中顯示 View
時有一些要注意的限制:
- 在 iOS 上,放在
NavigationPage
導覽列中的檢視會根據是否啟用大型標題,出現在不同的位置。 如需啟用大型標題的詳細資訊,請參閱顯示大型標題。 - 在 Android 上,只有在使用 app-compat 的應用程式中,才能成功將檢視放在
NavigationPage
的導覽列中。 - 不建議將大型且複雜的檢視 (例如
ListView
和TableView
) 放在NavigationPage
的導覽列中。