階層式導覽

Download Sample 下載範例

NavigationPage 類別提供的階層式導覽體驗讓使用者能夠視需要,向前及向後巡覽頁面。 此類別會實作一堆後進先出 (LIFO) 的 Page 物件導覽。 本文示範如何使用 NavigationPage 類別,在一堆頁面中執行導覽。

若要從一頁移到另一頁,應用程式會將新的頁面推送到導覽堆疊上,該頁面就會變成使用中頁面,如下圖所示:

Pushing a Page to the Navigation Stack

若要返回上一頁,應用程式將會從導覽堆疊中快顯目前的頁面,新的最上層頁面就會變成使用中頁面,如下圖所示:

Popping a Page from the Navigation Stack

Navigation 屬性會在任何 Page 衍生類型上公開導覽方法。 這些方法可讓您將頁面推送到導覽堆疊上、從導覽堆疊中快顯頁面,以及執行堆疊操作。

執行導覽

在階層式導覽中,NavigationPage 類別用來導覽一疊 ContentPage 物件。 下列螢幕擷取畫面顯示每個平台上 NavigationPage 的主要元件:

NavigationPage Components

NavigationPage 的配置取決於平台:

  • 在 iOS 上,頁面頂端有一個導覽列,其中顯示標題,並具有可返回上一頁的 [上一頁] 按鈕。
  • 在 Android 上,頁面頂端有一個導覽列,其中顯示標題、圖示,以及可返回上一頁的 [上一頁] 按鈕。 該圖示是在 [Activity] 屬性中定義,以裝飾 Android 平台特定專案中的 MainActivity 類別。
  • 在通用 Windows 平台上,頁面頂端有一個顯示標題的導覽列。

在所有平台上,Page.Title 屬性的值會顯示為頁面標題。 此外, IconColor 屬性也可以設定為 Color 套用至導覽列中圖示的 。

注意

建議僅以 ContentPage 執行個體填入 NavigationPage

建立根頁面

加入至導覽堆疊中的第一頁指的是應用程式的頁面,下列程式碼範例會示範如何完成此作業:

public App ()
{
  MainPage = new NavigationPage (new Page1Xaml ());
}

這會使 Page1XamlContentPage 執行個體推送至導覽堆疊上,而變成使用中頁面及應用程式的根頁面。 如下列螢幕擷取畫面所示:

Root Page of Navigation Stack

注意

NavigationPage 執行個體的 RootPage 屬性可讓您存取導覽堆疊中的第一頁。

將頁面推送到導覽堆疊

若要巡覽至 Page2Xaml,必須在目前頁面的 Navigation 屬性上叫用 PushAsync 方法,如下列程式碼範例所示:

async void OnNextPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PushAsync (new Page2Xaml ());
}

這會使 Page2Xaml 執行個體推送至導覽堆疊上,而變成使用中的頁面。 如下列螢幕擷取畫面所示:

Page Pushed onto Navigation Stack

叫用 PushAsync 方法時,會發生下列事件:

  • 呼叫 PushAsync 的頁面叫用其 OnDisappearing 覆寫。
  • 要巡覽的頁面叫用其 OnAppearing 覆寫。
  • PushAsync 工作完成。

不過,發生這些事件的確切順序則取決於平台。 如需詳細資訊,請參閱查理斯·佩茨羅德的Xamarin.Forms書第 24 章。

注意

呼叫 OnDisappearingOnAppearing 覆寫無法保證一定是指頁面導覽。 例如,在 iOS 上,當應用程式終止時,會在使用中頁面上呼叫 OnDisappearing 覆寫。

從導覽堆疊中快顯頁面

無論是裝置上的實體按鈕還是螢幕上的按鈕,按下裝置上的 [上一頁] 按鈕都可以從導覽堆疊快顯使用中的頁面。

若要以程式設計的方式返回原始頁面,Page2Xaml 執行個體必須叫用 PopAsync 方法,如下列程式碼範例所示:

async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PopAsync ();
}

這會使 Page2Xaml 執行個體從導覽堆疊中移除,進而使新的最上層頁面變成使用中的頁面。 叫用 PopAsync 方法時,會發生下列事件:

  • 呼叫 PopAsync 的頁面叫用其 OnDisappearing 覆寫。
  • 要返回的頁面叫用其 OnAppearing 覆寫。
  • 傳回 PopAsync 工作。

不過,發生這些事件的確切順序則取決於平台。 如需詳細資訊,請參閱查理斯·佩茨羅德的Xamarin.Forms書第24章。

除了 PushAsyncPopAsync 方法,每頁的 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 屬性在頁面上顯示資料,如下列螢幕擷取畫面所示:

Data Passed Through a Page Constructor

透過 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 控制項在頁面上顯示資料,如下列螢幕擷取畫面所示:

Data Passed Through a BindingContext

如需有關資料繫結的詳細資訊,請參閱資料繫結基本概念

操作導覽堆疊

Navigation 屬性會公開可以從中取得導覽堆疊中頁面的 NavigationStack 屬性。 雖然 Xamarin.Forms 會維護流覽堆疊的存取權, Navigation 但 屬性會藉由插入頁面或移除頁面來提供 InsertPageBeforeRemovePage 方法來操作堆疊。

InsertPageBefore 方法會將指定的頁面插入導覽堆疊中現有指定頁面之前,如下圖所示:

Inserting a Page in the Navigation Stack

RemovePage 方法會從導覽堆疊中移除指定的頁面,如下圖所示:

Removing a Page from the Navigation Stack

這些方法會啟用自訂導覽體驗,例如在成功登入之後,以新頁面取代登入頁面。 下列程式碼範例將示範此情節:

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

Slider TitleView

重要

除非使用 WidthRequestHeightRequest 屬性指定檢視的大小,否則許多檢視不會出現在導覽列中。 或者,您可以將檢視包裝在 StackLayout 中,並將 HorizontalOptionsVerticalOptions 屬性設定為適當的值。

請注意,由於 Layout 類別衍生自 View 類別,因此可設定 TitleView 附加屬性,顯示包含多個檢視的配置類別。 在 iOS 和通用 Windows 平台 (UWP) 上,無法變更導覽列的高度,因此如果導覽列中所顯示檢視大於導覽列的預設大小,則會發生裁剪。 不過,在 Android 上,則可以變更導覽列的高度,方法是將 NavigationPage.BarHeight 可繫結屬性設定為表示新高度的 double。 如需詳細資訊,請參閱設定 NavigationPage 上的導覽列高度

或者,您可以將一些內容放在導覽列中,並將一些內容放在色彩符合導覽列的頁面內容頂端檢視中,來建議擴充導覽列。 此外,在 iOS 上,您可以將 NavigationPage.HideNavigationBarSeparator 可繫結屬性設定為 true,來移除導覽列底部的分隔線和陰影。 如需詳細資訊,請參閱隱藏 NavigationPage 上的導覽列分隔線

注意

BackButtonTitleTitleTitleIconTitleView 屬性全都能定義佔用導覽列上空間的值。 由於導覽列大小會依平台和螢幕大小而有所不同,因此設定上述所有屬性將會由於可用空間有限而導致衝突。 與其嘗試使用這些屬性的組合,您可能會發現單獨設定 TitleView 屬性更容易取得您想要的導覽列設計。

限制

NavigationPage 的導覽列中顯示 View 時有一些要注意的限制:

  • 在 iOS 上,放在 NavigationPage 導覽列中的檢視會根據是否啟用大型標題,出現在不同的位置。 如需啟用大型標題的詳細資訊,請參閱顯示大型標題
  • 在 Android 上,只有在使用 app-compat 的應用程式中,才能成功將檢視放在 NavigationPage 的導覽列中。
  • 不建議將大型且複雜的檢視 (例如 ListViewTableView) 放在 NavigationPage 的導覽列中。