階層ナビゲーション
NavigationPage クラスは、ユーザーが前後を希望どおりにページを移動することができる階層ナビゲーション エクスペリエンスを提供します。 このクラスは、Page オブジェクトの後入れ先出し (LIFO) スタックとしてナビゲーションを提供します。 この記事では、NavigationPage クラスを使用してページのスタックでナビゲーションを実行する方法について説明します。
1 つのページから別のページに移動するには、次の図に示すように、アプリケーションは新しいページを、そこでアクティブなページとなるナビゲーション スタックにプッシュします。
次の図に示すように、前のページに戻るには、アプリケーションは現在のページをナビゲーション スタックからポップします。そして新しい最上位のページがアクティブ ページになります。
ナビゲーション メソッドは、任意の Page
派生型の Navigation
プロパティによって公開されます。 これらのメソッドには、ページをナビゲーション スタックにプッシュし、ナビゲーション スタックからページをポップし、スタック操作を実行する機能があります。
ナビゲーションを実行する
階層ナビゲーションでは、NavigationPage
クラスは ContentPage
オブジェクトのスタック間をナビゲートするために使用されます。 次のスクリーンショットは、各プラットフォームでの NavigationPage
のメイン コンポーネントを示します。
NavigationPage
のレイアウトは、プラットフォームによって異なります。
- iOS では、ナビゲーション バーがページの上部にあり、タイトルが表示され、前のページに戻る [戻る] ボタンがあります。
- Android では、ナビゲーション バーがページの上部にあり、タイトル、アイコンと、前のページに戻る [戻る] ボタンが表示されています。 Android プラットフォーム固有プロジェクトでは、アイコンは
MainActivity
クラスを修飾する[Activity]
属性で定義されています。 - ユニバーサル Windows プラットフォームでは、ナビゲーション バーはページの上部にあり、タイトルが表示されています。
いずれのプラットフォームでも、Page.Title
プロパティの値がページ タイトルとして表示されます。 さらに、IconColor
プロパティを、ナビゲーション バーのアイコンに適用される Color
に設定できます。
Note
NavigationPage
を ContentPage
インスタンスのみで作成することをお勧めします。
ルート ページを作成する
ナビゲーション スタックに追加された最初のページは、アプリケーションのルート ページとなります。次のコード例に、これを実現する方法を示しています。
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
インスタンスの BindingContext
を Contact
インスタンスに設定し、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
コントロールによってデータがページに表示されます。
データ バインディングの詳細については、「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
に設定できます。また、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
のプロパティで指定されていない場合、多くのビューはナビゲーション バーに表示されません。 または、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
BackButtonTitle
、Title
、TitleIcon
、TitleView
のプロパティのいずれでも、ナビゲーション バー上の領域を占める値を定義できます。 ナビゲーション バーのサイズはプラットフォームや画面サイズによって変わりますが、これらのプロパティをすべて設定すると、領域が限られているために競合が発生します。 これらのプロパティの組み合わせを使用するのではなく、TitleView
プロパティのみ設定して目的のナビゲーション バーのデザインを改善することをお勧めします。
制限事項
NavigationPage
のナビゲーション バーに View
を表示するときに注意が必要な制限事項がいくつかあります。
- iOS では、
NavigationPage
のナビゲーション バーに配置されるビューは、大きなタイトルが有効かどうかによって表示される位置が変わります。 大きなタイトルを有効にする方法については、「Displaying Large Titles」(大きなタイトルの表示) を参照してください。 - Android では、
NavigationPage
のナビゲーション バーにビューを配置することは、app-compat を使用するアプリでのみ実現できます。 ListView
やTableView
などの大きくて複雑なビューをNavigationPage
のナビゲーション バーに配置することはお勧めしません。