Xamarin.Forms 模式页面
Xamarin.Forms 支持模式页面。 模式页面鼓励用户完成独立任务,在完成或取消该任务之前,不允许导航离开该任务。 本文演示如何导航到模式页面。
本文讨论以下主题:
概述
模式页可以是 Xamarin.Forms 支持的任何页面类型。 若要显示模式页面,应用程序会将页面推送到模式堆栈中,在堆栈中,该页面会变为活动页面,如下图所示:
若要返到回前一页,应用程序会从模式堆栈弹出当前页面,而使最顶层的新页面成为活动页面,如下图所示:
执行导航
可以由任何 Page
派生类型上的 Navigation
属性公开模式导航方法。 可通过这些方法将模式页面推送到模式堆栈中,还可从模式堆栈中弹出模式页面。
Navigation
属性也可公开 ModalStack
属性,并从中获得模式堆栈中的模式页面。 但是,在模式导航中没有执行模式堆栈操作或弹出到根页的概念。 这是因为基础平台普遍都不支持这些操作。
注意
执行模式页面导航无需具有 NavigationPage
实例。
将页面推送到模式堆栈
若要导航到 ModalPage
,必须调用当前页的 Navigation
属性上的 PushModalAsync
方法,如以下代码示例所示:
async void OnItemSelected (object sender, SelectedItemChangedEventArgs e)
{
if (listView.SelectedItem != null) {
var detailPage = new DetailPage ();
...
await Navigation.PushModalAsync (detailPage);
}
}
这会导致 ModalPage
实例被推送到模式堆栈中,在该堆栈中,该实例将变为活动页面,前提是已在 MainPage
实例上的 ListView
中选择了一个项目。 ModalPage
实例如以下屏幕截图中所示:
调用 PushModalAsync
时,会发生以下事件:
- 如果基础平台不是 Android,则调用
PushModalAsync
的页面会调用其OnDisappearing
替代。 - 要导航到的页面会调用其
OnAppearing
替代。 PushAsync
任务完成。
但是,这些事件发生的确切顺序取决于平台。 有关详细信息,请参阅 Charles Petzold 所著的 Xamarin.Forms 书籍的第 24 章。
注意
不能将对 OnDisappearing
和 OnAppearing
替代的调用视为绝对的页面导航指示。 例如,在 iOS 上,应用程序终止后,将对活动页面调用 OnDisappearing
替代。
从模式堆栈中弹出页面
通过设备上的“后退”按钮(无论是设备上的物理按钮还是屏幕按钮),可以从模式堆栈中弹出活动页面。
若要以编程方式返回原始页,ModalPage
实例必须调用 PopModalAsync
方法,如以下代码示例所示:
async void OnDismissButtonClicked (object sender, EventArgs args)
{
await Navigation.PopModalAsync ();
}
这会从模式堆栈中删除 ModalPage
实例,而使最顶层的新页面成为活动页面。 调用 PopModalAsync
时,会发生以下事件:
- 调用
PopModalAsync
的页面会调用其OnDisappearing
替代。 - 如果基础平台不是 Android,则要返回
OnAppearing
的页面会调用其 替代。 PopModalAsync
任务返回。
但是,这些事件发生的确切顺序取决于平台。 有关详细信息,请参阅 Charles Petzold 所著的 Xamarin.Forms 书籍的第 24 章。
禁用“后退”按钮
在 Android 上,只要按设备上的标准“后退”按钮,用户就可返回到上一页。 如果模式页面要求用户在离开页面之前完成独立任务,则应用程序必须禁用“后退”按钮。 可通过替代模式页面上的 Page.OnBackButtonPressed
方法来完成此操作。 有关详细信息,请参阅 Charles Petzold 所著的 Xamarin.Forms 书籍的第 24 章。
对页面过渡效果进行动画处理
每个页面的 Navigation
属性还提供已替代的 push 和 pop 方法,这些方法包含一个 boolean
参数,用于控制是否在导航期间显示页面动画,如以下代码示例所示:
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PushModalAsync (new DetailPage (), false);
}
async void OnDismissButtonClicked (object sender, EventArgs args)
{
// Page appearance not animated
await Navigation.PopModalAsync (false);
}
将 boolean
参数设置为 false
会禁用页面过渡动画,而将参数设置为 true
则会启用页面过渡动画,前提是基础平台支持该动画。 但是,缺少此参数的 push 和 pop 方法默认启用该动画。
导航时传递数据
有时,页面必须在导航期间将数据传递到另一个页面。 实现此操作的两种方法是:通过页面构造函数传递数据,和通过将新页面的 BindingContext
设置为该数据。 下面将依次讨论每种方法。
通过页面构造函数传递数据
在导航期间,通过页面构造函数参数将数据传递到另一个页面是最简单的方法,如以下代码示例所示:
public App ()
{
MainPage = new MainPage (DateTime.Now.ToString ("u")));
}
此代码创建一个 MainPage
实例,以 ISO8601 格式传递当前日期和时间。
MainPage
实例通过构造函数参数接收数据,如以下代码示例所示:
public MainPage (string date)
{
InitializeComponent ();
dateLabel.Text = date;
}
然后,通过设置 Label.Text
属性,在页面上显示数据。
通过 BindingContext 传递数据
在导航期间,另一种将数据传递到另一个页面的方法是将新页面的 BindingContext
设置为该数据,如以下代码示例所示:
async void OnItemSelected (object sender, SelectedItemChangedEventArgs e)
{
if (listView.SelectedItem != null) {
var detailPage = new DetailPage ();
detailPage.BindingContext = e.SelectedItem as Contact;
listView.SelectedItem = null;
await Navigation.PushModalAsync (detailPage);
}
}
此代码将 DetailPage
实例的 BindingContext
设置为 Contact
实例,然后导航到 DetailPage
。
然后,DetailPage
使用数据绑定来显示 Contact
实例数据,如以下 XAML 代码示例所示:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ModalNavigation.DetailPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,40,0,0" />
</OnPlatform>
</ContentPage.Padding>
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" FontSize="Medium" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
...
<Button x:Name="dismissButton" Text="Dismiss" Clicked="OnDismissButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
以下代码示例显示了如何在 C# 中完成数据绑定:
public class DetailPageCS : ContentPage
{
public DetailPageCS ()
{
var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
nameLabel.SetBinding (Label.TextProperty, "Name");
...
var dismissButton = new Button { Text = "Dismiss" };
dismissButton.Clicked += OnDismissButtonClicked;
Thickness padding;
switch (Device.RuntimePlatform)
{
case Device.iOS:
padding = new Thickness(0, 40, 0, 0);
break;
default:
padding = new Thickness();
break;
}
Padding = padding;
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
}
},
...
dismissButton
}
};
}
async void OnDismissButtonClicked (object sender, EventArgs args)
{
await Navigation.PopModalAsync ();
}
}
然后,数据会通过一系列 Label
控件显示在页面上。
若要深入了解数据绑定,请参阅数据绑定基本知识。
总结
本文演示了如何导航到模式页面。 模式页面鼓励用户完成独立任务,在完成或取消该任务之前,不允许导航离开该任务。