階層ナビゲーション

NavigationPage クラスは、ユーザーが前後を希望どおりにページを移動することができる階層ナビゲーション エクスペリエンスを提供します。 このクラスは、Page オブジェクトの後入れ先出し (LIFO) スタックとしてナビゲーションを提供します。 この記事では、NavigationPage クラスを使用してページのスタックでナビゲーションを実行する方法について説明します。

1 つのページから別のページに移動するには、次の図に示すように、アプリケーションは新しいページを、そこでアクティブなページとなるナビゲーション スタックにプッシュします。

ナビゲーション スタックにページをプッシュする

次の図に示すように、前のページに戻るには、アプリケーションは現在のページをナビゲーション スタックからポップします。そして新しい最上位のページがアクティブ ページになります。

ナビゲーション スタックからページをポップする

ナビゲーション メソッドは、任意の Page 派生型の Navigation プロパティによって公開されます。 これらのメソッドには、ページをナビゲーション スタックにプッシュし、ナビゲーション スタックからページをポップし、スタック操作を実行する機能があります。

ナビゲーションを実行する

階層ナビゲーションでは、NavigationPage クラスは ContentPage オブジェクトのスタック間をナビゲートするために使用されます。 次のスクリーンショットは、各プラットフォームでの NavigationPage のメイン コンポーネントを示します。

NavigationPage コンポーネント

NavigationPage のレイアウトは、プラットフォームによって異なります。

  • iOS では、ナビゲーション バーがページの上部にあり、タイトルが表示され、前のページに戻る [戻る] ボタンがあります。
  • Android では、ナビゲーション バーがページの上部にあり、タイトル、アイコンと、前のページに戻る [戻る] ボタンが表示されています。 Android プラットフォーム固有プロジェクトでは、アイコンは MainActivity クラスを修飾する [Activity] 属性で定義されています。
  • ユニバーサル Windows プラットフォームでは、ナビゲーション バーはページの上部にあり、タイトルが表示されています。

いずれのプラットフォームでも、Page.Title プロパティの値がページ タイトルとして表示されます。 さらに、IconColor プロパティを、ナビゲーション バーのアイコンに適用される Color に設定できます。

Note

NavigationPageContentPage インスタンスのみで作成することをお勧めします。

ルート ページを作成する

ナビゲーション スタックに追加された最初のページは、アプリケーションのルート ページとなります。次のコード例に、これを実現する方法を示しています。

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

これにより、Page1Xaml ContentPage インスタンスがナビゲーション スタックにプッシュされるようになります。そこがアクティブ ページであり、アプリケーションのルート ページとなります。 これを以下のスクリーンショットに示します。

ナビゲーション スタックのルート ページ

Note

NavigationPage インスタンスの RootPage プロパティを使用すると、ナビゲーション スタック内の最初のページにアクセスできます。

ナビゲーション スタックにページをプッシュする

Page2Xaml にナビゲートするには、次のコード例で示すように、現在のページの Navigation プロパティで PushAsync メソッドを起動する必要があります。

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

これにより、Page2Xaml インスタンスがナビゲーション スタックにプッシュされるようになり、そこがアクティブ ページとなります。 これを以下のスクリーンショットに示します。

ナビゲーション スタックにプッシュされるページ

PushAsync メソッドが呼び出されると、次のイベントが発生します。

  • PushAsync を呼び出すページでは、OnDisappearing のオーバーライドが呼び出されます。
  • ナビゲート先のページでは、OnAppearing のオーバーライドが呼び出されます。
  • PushAsync タスクが完了します。

ただし、これらのイベントが発生する正確な順序はプラットフォームによって異なります。 詳細については、Charles Petzold 氏著作の Xamarin.Forms ブックの第 24 章を参照してください。

Note

OnDisappearing および OnAppearing のオーバーライドの呼び出しは、ページ ナビゲーションを示す保証として扱うことはできません。 たとえば、iOS では、アプリケーションの終了時にアクティブ ページで OnDisappearing のオーバーライドが呼び出されます。

ナビゲーション スタックからページをポップする

アクティブ ページは、これが物理的なボタンであるか画面上のボタンであるかどうかにかかわらず、デバイスの [戻る] ボタンを押すことによってナビゲーション スタックからポップすることができます。

元のページにプログラムを使用して戻るには、Page2Xaml インスタンスが、次のコード例のとおり PopAsync メソッドを起動する必要があります。

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

これにより、ナビゲーション スタックから Page2Xaml インスタンスが削除され、新しい最上位のページがアクティブ ページとなります。 PopAsync メソッドが呼び出されると、次のイベントが発生します。

  • PopAsync を呼び出すページでは、OnDisappearing のオーバーライドが呼び出されます。
  • 戻り先のページでは、OnAppearing のオーバーライドが呼び出されます。
  • PopAsync タスクが復帰します。

ただし、これらのイベントが発生する正確な順序はプラットフォームによって異なります。 詳細については、Charles Petzold 氏著作の Xamarin.Forms ブックの第 24 章を参照してください。

各ページの Navigation プロパティには、PushAsync および PopAsync メソッドだけでなく、次のコード例に示すように、PopToRootAsync メソッドも用意されています。

async void OnRootPageButtonClicked (object sender, EventArgs e)
{
  await Navigation.PopToRootAsync ();
}

このメソッドは、ルート Page 以外のすべてをナビゲーション スタックからポップします。そのため、アプリケーションのルート ページがアクティブ ページになります。

ページ遷移をアニメーション化する

各ページの Navigation プロパティには、次のコード例に示すように、ナビゲーション中にページ アニメーションを表示するかどうかを制御する 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 に設定すると、基となるプラットフォームでサポートされている場合はページ遷移アニメーションが有効になります。 ただし、プッシュとポップのメソッドでこのパラメーターが指定されていない場合は、既定でアニメーションが有効になります。

ナビゲーション時にデータを渡す

場合によっては、ナビゲーション中に、あるページから別のページにデータを渡す必要があります。 これを実現する 2 つの手法では、ページ コンストラクターを介してデータを渡し、新しいページの BindingContext をデータに設定しています。 これから、それぞれについて順番に説明します。

ページ コンストラクターを介してデータを渡す

ナビゲーション中に別のページにデータを渡す最も簡単な手法は、次のコード例で示すように、ページ コンストラクター パラメーターを使用することです。

public App ()
{
  MainPage = new NavigationPage (new MainPage (DateTime.Now.ToString ("u")));
}

このコードでは、現在の日付と時刻を ISO8601 形式で渡す MainPage インスタンスを作成します。これは NavigationPage インスタンスでラップされています。

MainPage インスタンスは、次のコード例に示すように、コンストラクター パラメーターを使用してデータを受け取ります。

public MainPage (string date)
{
  InitializeComponent ();
  dateLabel.Text = date;
}

次のスクリーンショットに示すように、Label.Text プロパティを設定することでデータがページに表示されます。

ページ コンストラクターを介して渡されるデータ

BindingContext を介してデータを渡す

ナビゲーション中に別のページにデータを渡すもう 1 つの方法は、次のコード例に示すように、新しいページの 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 インスタンスの BindingContextContact インスタンスに設定し、SecondPage にナビゲートします。

次の XAML コード例に示すように、SecondPage ではデータ バインディングを使用して Contact インスタンス データを表示します。

<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 コントロールによってデータがページに表示されます。

BindingContext を介して渡されるデータ

データ バインディングの詳細については、「Data Binding Basics」 (データ バインディングの基礎) を参照してください。

ナビゲーション スタックの操作

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 に設定できます。また、PageNavigationPage にプッシュされると、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 が表示されます:

スライダーの TitleView

重要

ビューのサイズが WidthRequest およびHeightRequest のプロパティで指定されていない場合、多くのビューはナビゲーション バーに表示されません。 または、HorizontalOptions および VerticalOptions プロパティを適切な値に設定してビューを StackLayout にラップすることができます。

Layout クラスは View クラスから派生しているため、複数のビューを含むレイアウト クラスを表示するように TitleView 添付プロパティを設定することができます。 iOS およびユニバーサル Windows プラットフォーム (UWP) では、ナビゲーション バーの高さを変更できないため、ナビゲーション バーに表示されるビューがナビゲーション バーの既定サイズより大きい場合、クリップが発生します。 一方 Android では、NavigationPage.BarHeight バインディング可能プロパティを新しい高さを表す double に設定することで、ナビゲーション バーの高さを変更できます。 詳細については、「Setting the Navigation Bar Height on a NavigationPage」(NavigationPage でナビゲーション バーの高さを設定する) を参照してください。

また、ナビゲーション バーにコンテンツの一部を配置し、ナビゲーション バーと色を合わせたビューの一部をページ コンテンツの上部に配置して、拡張ナビゲーション バーを提案することもできます。 さらに、iOS では、NavigationPage.HideNavigationBarSeparator バインド可能プロパティを true に設定することで、ナビゲーション バーの下部にあるセパレーターと影を削除できます。 詳細については、「Hiding the Navigation Bar Separator on a NavigationPage」(NavigationPage でナビゲーション バーのセパレーターを非表示にする) を参照してください。

Note

BackButtonTitleTitleTitleIconTitleView のプロパティのいずれでも、ナビゲーション バー上の領域を占める値を定義できます。 ナビゲーション バーのサイズはプラットフォームや画面サイズによって変わりますが、これらのプロパティをすべて設定すると、領域が限られているために競合が発生します。 これらのプロパティの組み合わせを使用するのではなく、TitleView プロパティのみ設定して目的のナビゲーション バーのデザインを改善することをお勧めします。

制限事項

NavigationPage のナビゲーション バーに View を表示するときに注意が必要な制限事項がいくつかあります。

  • iOS では、NavigationPage のナビゲーション バーに配置されるビューは、大きなタイトルが有効かどうかによって表示される位置が変わります。 大きなタイトルを有効にする方法については、「Displaying Large Titles」(大きなタイトルの表示) を参照してください。
  • Android では、NavigationPage のナビゲーション バーにビューを配置することは、app-compat を使用するアプリでのみ実現できます。
  • ListViewTableView などの大きくて複雑なビューを NavigationPage のナビゲーション バーに配置することはお勧めしません。