
Xamarin.Forms 导航栏 Navigation (三)
添加导航栏
一个常规的App是由多个Page组成的,出现多个Page就会涉及页面跳转问题。Xamarin.Forms页面之间的跳转通过Navigation Stack管理Page,如页面A跳转到页面B时,会将B压入栈定,此时页面B成为活动页面,执行Back操作时,页面B从栈定推出使页面A重新变为活动页面。每个应用程序都有一个特殊页作为应用程序的入口(main page, or the home page, or the start page),Xamarin.Forms中由App的MainPage属性设置。
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
}
添加新页面
我们希望通过上一篇文章所选的集合子选项,弹出一个新的页面。
首先我们先添加一个新的页面DetailPage.xaml
。
创建一个显示我选中内容的Label
,并且通过设置VerticalOptions
属性值为CenterAndExpand
来进行垂直居中,然后通过设置HorizontalOptions
属性来进行水平居中;还创建一个Dismiss
按钮用于关闭当前的页面,在这里呢我让Label
的Text
属性绑定了提供数据的DetailPageViewModel.NoteText
属性,Dismiss
按钮的点击事件绑定了DetailPageViewModel.DismissPageCommand
命令。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="FirstApp.DetailPage">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height=".2*" />
</Grid.RowDefinitions>
<Label Text="{Binding NoteText}" FontSize="Title" Grid.Row="0" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" />
<Button Grid.Row="1" Text="Dismiss" Command="{Binding DismissPageCommand}" />
</Grid>
</ContentPage.Content>
</ContentPage>
接着我们来查看DetailPageViewModel
数据源的实体定义。DetailPageViewModel
同样实现了INotifyPropertyChanged
接口,并创建了一个NoteText
属性用于显示所选择的内容值,DismissPageCommand
通过await Application.Current.MainPage.Navigation.PopModalAsync();
用于当前用户界面退出页面栈达到一个退出当前页面的功能。
public class DetailPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DetailPageViewModel(string note)
{
DismissPageCommand = new Command(async () => {
await Application.Current.MainPage.Navigation.PopModalAsync();
});
NoteText = note;
}
string noteText;
public string NoteText
{
get => noteText;
set {
noteText = value;
var args = new PropertyChangedEventArgs(nameof(NoteText));
PropertyChanged?.Invoke(this, args);
}
}
public Command DismissPageCommand { get; }
}
在我们首页所绑定的数据源MainPageViewModel
类中,当前集合中所选择文本定义SelectedNote
属性。以及定义用户界面所选择集合子选项所触发的SelectedNoteChangedCommand
命令。在触发命令时,我们将创建一个新的页面DetailPage
,并提供我们所选的子选项内容来创建数据源DetailPageViewModel
,通过detailPage.BindingContext
来绑定DetailPage
所需要的数据。最后通过await Application.Current.MainPage.Navigation.PushModalAsync
来将这个新的页面压入页面栈顶呈现给用户。
public class MainPageViewModel : INotifyPropertyChanged
{
public Command SelectedNoteChangedCommand { get; }
string selectedNote;
public string SelectedNote
{
get => selectedNote;
set
{
selectedNote = value;
var args = new PropertyChangedEventArgs(nameof(SelectedNote));
PropertyChanged?.Invoke(this, args);
}
}
public MainPageViewModel()
{
SelectedNoteChangedCommand = new Command(async ()=> {
//创建一个新的页面
var detailVM = new DetailPageViewModel(SelectedNote);
var detailPage = new DetailPage();
detailPage.BindingContext = detailVM;
//向应用端推送页面
await Application.Current.MainPage.Navigation.PushModalAsync(detailPage);
});
EraseCommand = new Command(() => {
TheNote = string.Empty;
});
SaveCommand = new Command(() => {
AllNotes.Add(TheNote);
TheNote = string.Empty;
});
}
public ObservableCollection<string> AllNotes { get; set; } = new ObservableCollection<string>();
public event PropertyChangedEventHandler PropertyChanged;
string theNote;
public string TheNote
{
get => theNote;
set
{
theNote = value;
var args = new PropertyChangedEventArgs(nameof(TheNote));
PropertyChanged?.Invoke(this, args);
}
}
public Command SaveCommand { get; }
public Command EraseCommand { get; }
}
接着我们需要在前端绑定好我们相关的定义。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FirstApp"
x:Class="FirstApp.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel/>
</ContentPage.BindingContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height=".5*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="logo_xamarin" BackgroundColor="PowderBlue"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" />
<Editor Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Placeholder="Enter Note Here" Text="{Binding TheNote}" />
<Button Grid.Row="2" Grid.Column="0" Text="Save" Command="{Binding SaveCommand}" />
<Button Grid.Row="2" Grid.Column="1" Text="Erase" Command="{Binding EraseCommand}" />
<CollectionView
ItemsSource="{Binding AllNotes}"
SelectionMode="Single"
SelectedItem="{Binding SelectedNote}"
SelectionChangedCommand="{Binding SelectedNoteChangedCommand}"
Grid.Row="3" Grid.ColumnSpan="2" Grid.Column="0">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10,10">
<Frame>
<Label Text="{Binding .}" FontSize="Title"/>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
Xamarin.Forms Navigation
Xamarin.Forms 提供许多不同的页面导航体验,具体取决于所使用的页面类型。
目前我们使用的是NavigationPage
分层导航类型。
分层导航
该类NavigationPage提供分层导航体验,用户可以根据需要在页面中向前和向后导航。该类将导航实现为Page对象的后进先出 (LIFO) 堆栈。通过Push可以推送到页面栈顶端,通过Pop方法可以退出页面栈顶端。
创建根页面
添加到导航堆栈的第一个页面称为应用程序的根页面,我们在添加导航栏时讲到了,以下代码示例显示了这是如何完成的:
public App ()
{
MainPage = new NavigationPage(new MainPage());
}
这会导致MainPage
ContentPage实例被推送到导航堆栈上,在那里它成为应用程序的活动页面和根页面。
将页面推送到导航堆栈
要导航到Page2Xaml
,需要在当前页面PushAsync的属性上调用方法Navigation,如以下代码示例所示:
await Navigation.PushAsync(new Page2Xaml());
# 全局调用
await Application.Current.MainPage.Navigation.PushAsync(new Page2Xaml());
调用该PushAsync方法时,会发生以下事件:
— 页面调用调用PushAsync了它的OnDisappearing
事件。
— 被导航到的页面OnAppearing
调用了它的事件。
事件名 | 描述 |
---|---|
OnDisappearing |
当前页面被覆盖时,页面所触发的事件。 |
OnAppearing |
当前页面被覆盖时,显示下一个页面之前所触发的事件。 |
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
}
从导航堆栈中退出页面
活动页面可以通过按下设备上的后退按钮从导航堆栈中弹出,无论这是设备上的物理按钮还是屏幕按钮。
要以编程方式返回原始页面,Page2Xaml
实例必须调用该PopAsync
方法,如以下代码示例所示:
await Navigation.PopAsync();
# 全局调用
await Application.Current.MainPage.Navigation.PopAsync();
这会从导航堆栈中删除 Page2Xaml 实例,而使最顶层的页成为活动页。 调用 PopAsync 方法后,会发生以下事件:
调用 PopAsync 的页面会调用其 OnDisappearing
事件。
要返回到的页面会调用其 OnAppearing
事件。
PopAsync 任务返回。
当然它们都还有一个构造PopAsync(Boolean)
与PushAsync(Boolean)
,这个可以以动画的方式进行删除。
关于不同的导航进行退出与呈现的区别
方法 | 描述 |
---|---|
PushAsync |
显示某个页面 |
PopAsync |
退出当前页面 |
PushModalAsync |
以模态的方式显示某个页面(相当于以弹出的页面为主) |
PopModalAsync |
退出当前模态页面 |
PopToRootAsync |
退出主页 |
RemovePage |
删除某个页面 |
在导航栏中显示滚动条
任何 Xamarin.Forms View
中可以显示在导航栏 NavigationPage 中。 这是通过将 NavigationPage.TitleView
附加属性设置为 View
来实现的。 此附加属性可以在任何 Page
上设置,当 Page
被推送到 NavigationPage
上后,NavigationPage
会遵守属性的值。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FirstApp"
x:Class="FirstApp.MainPage">
<NavigationPage.TitleView>
<Slider HeightRequest="44" WidthRequest="300" />
</NavigationPage.TitleView>
....
</ContentPage>
下面是等效 C# 代码:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
NavigationPage.SetTitleView(this, titleView);
}
protected override void OnAppearing()
{
base.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
}
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

