导航系统 #
导航概述 #
导航类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ Xamarin.Forms 导航类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 堆栈导航 (NavigationPage) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ← 返回 页面标题 │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 当前页面内容 │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 2. 标签导航 (TabbedPage) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 当前标签内容 │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ 🏠 首页 │ 📋 列表 │ ⚙️ 设置 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 3. 侧滑导航 (FlyoutPage) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ☰ 标题 │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ 详情页面内容 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 4. Shell 导航 (推荐) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🏠 首页 │ 📋 列表 │ ⚙️ 设置 │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ Shell 内容区域 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
堆栈导航 (NavigationPage) #
基本概念 #
text
┌─────────────────────────────────────────────────────────────┐
│ 导航堆栈 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 导航堆栈是一个后进先出 (LIFO) 的页面集合: │
│ │
│ Push 操作: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 页面1 │ ──► │ 页面2 │ ──► │ 页面3 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └──────────────┴──────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ 导航堆栈 │ │
│ │ [页面1, │ │
│ │ 页面2, │ │
│ │ 页面3] │ │
│ └───────────────┘ │
│ │
│ Pop 操作: │
│ 从堆栈顶部移除当前页面,返回上一个页面 │
│ │
└─────────────────────────────────────────────────────────────┘
创建 NavigationPage #
csharp
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new HomePage())
{
BarBackgroundColor = Color.FromHex("#2196F3"),
BarTextColor = Color.White,
TitleIconImageSource = "logo.png"
};
}
导航方法 #
text
┌─────────────────────────────────────────────────────────────┐
│ 导航方法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ PushAsync - 推入新页面 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ await Navigation.PushAsync(new DetailPage()); │ │
│ │ │ │
│ │ 堆栈:[首页] → [首页, 详情页] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ PopAsync - 弹出当前页面 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ await Navigation.PopAsync(); │ │
│ │ │ │
│ │ 堆栈:[首页, 详情页] → [首页] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ PopToRootAsync - 返回根页面 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ await Navigation.PopToRootAsync(); │ │
│ │ │ │
│ │ 堆栈:[首页, 页面2, 页面3] → [首页] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ PushModalAsync - 模态推入 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ await Navigation.PushModalAsync(new LoginPage()); │ │
│ │ │ │
│ │ 模态堆栈:独立于主导航堆栈 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ PopModalAsync - 弹出模态页面 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ await Navigation.PopModalAsync(); │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
导航示例 #
csharp
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
private async void OnNavigateToDetail(object sender, EventArgs e)
{
await Navigation.PushAsync(new DetailPage());
}
private async void OnNavigateToSettings(object sender, EventArgs e)
{
await Navigation.PushAsync(new SettingsPage());
}
private async void OnShowModal(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new LoginPage());
}
}
public partial class DetailPage : ContentPage
{
public DetailPage()
{
InitializeComponent();
}
private async void OnGoBack(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
private async void OnGoHome(object sender, EventArgs e)
{
await Navigation.PopToRootAsync();
}
}
导航栏定制 #
xml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.DetailPage"
Title="详情页"
NavigationPage.HasNavigationBar="True"
NavigationPage.HasBackButton="True">
<ContentPage.ToolbarItems>
<ToolbarItem Text="编辑"
IconImageSource="edit.png"
Command="{Binding EditCommand}" />
<ToolbarItem Text="分享"
IconImageSource="share.png"
Command="{Binding ShareCommand}" />
</ContentPage.ToolbarItems>
<StackLayout>
<Label Text="详情页内容" />
</StackLayout>
</ContentPage>
csharp
NavigationPage.SetHasNavigationBar(this, true);
NavigationPage.SetHasBackButton(this, true);
NavigationPage.SetBackButtonTitle(this, "返回");
参数传递 #
构造函数传参 #
csharp
public partial class DetailPage : ContentPage
{
private readonly Item _item;
public DetailPage(Item item)
{
InitializeComponent();
_item = item;
BindingContext = new DetailViewModel(item);
}
}
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem is Item item)
{
await Navigation.PushAsync(new DetailPage(item));
}
}
导航参数 #
csharp
public partial class DetailPage : ContentPage
{
public static readonly BindableProperty ItemIdProperty =
BindableProperty.Create(nameof(ItemId), typeof(int), typeof(DetailPage), -1);
public int ItemId
{
get => (int)GetValue(ItemIdProperty);
set => SetValue(ItemIdProperty, value);
}
public DetailPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
if (ItemId > 0)
{
LoadItem(ItemId);
}
}
}
private async void OnNavigate(object sender, EventArgs e)
{
await Navigation.PushAsync(new DetailPage { ItemId = 123 });
}
消息中心传参 #
csharp
public class Messages
{
public const string ItemSelected = "ItemSelected";
}
public partial class ListPage : ContentPage
{
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem is Item item)
{
MessagingCenter.Send(this, Messages.ItemSelected, item);
await Navigation.PushAsync(new DetailPage());
}
}
}
public partial class DetailPage : ContentPage
{
private Item _item;
public DetailPage()
{
InitializeComponent();
MessagingCenter.Subscribe<ListPage, Item>(this, Messages.ItemSelected,
(sender, item) =>
{
_item = item;
BindingContext = new DetailViewModel(item);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<ListPage, Item>(this, Messages.ItemSelected);
}
}
标签导航 (TabbedPage) #
创建 TabbedPage #
xml
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp.Views"
x:Class="MyApp.MainTabbedPage"
BarBackgroundColor="White"
SelectedTabColor="#2196F3"
UnselectedTabColor="Gray">
<local:HomePage Title="首页"
IconImageSource="home.png" />
<local:ListPage Title="列表"
IconImageSource="list.png" />
<local:SettingsPage Title="设置"
IconImageSource="settings.png" />
</TabbedPage>
代码创建 TabbedPage #
csharp
public class MainTabbedPage : TabbedPage
{
public MainTabbedPage()
{
Children.Add(new NavigationPage(new HomePage())
{
Title = "首页",
IconImageSource = "home.png"
});
Children.Add(new NavigationPage(new ListPage())
{
Title = "列表",
IconImageSource = "list.png"
});
Children.Add(new NavigationPage(new SettingsPage())
{
Title = "设置",
IconImageSource = "settings.png"
});
BarBackgroundColor = Color.White;
SelectedTabColor = Color.FromHex("#2196F3");
}
}
标签页徽章 #
csharp
public partial class MainTabbedPage : TabbedPage
{
public void SetBadge(int index, int count)
{
var page = Children[index];
if (count > 0)
{
TabbedPage.SetBadgeText(page, count.ToString());
}
else
{
TabbedPage.SetBadgeText(page, null);
}
}
}
侧滑导航 (FlyoutPage) #
创建 FlyoutPage #
xml
<FlyoutPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainFlyoutPage">
<FlyoutPage.Flyout>
<ContentPage Title="菜单" IconImageSource="menu.png">
<StackLayout Padding="20">
<Label Text="菜单" FontSize="24" FontAttributes="Bold" />
<Button Text="首页" Command="{Binding NavigateCommand}"
CommandParameter="Home" />
<Button Text="列表" Command="{Binding NavigateCommand}"
CommandParameter="List" />
<Button Text="设置" Command="{Binding NavigateCommand}"
CommandParameter="Settings" />
<Button Text="关于" Command="{Binding NavigateCommand}"
CommandParameter="About" />
</StackLayout>
</ContentPage>
</FlyoutPage.Flyout>
<FlyoutPage.Detail>
<NavigationPage>
<x:Arguments>
<ContentPage Title="首页">
<Label Text="首页内容" />
</ContentPage>
</x:Arguments>
</NavigationPage>
</FlyoutPage.Detail>
</FlyoutPage>
FlyoutPage 导航逻辑 #
csharp
public partial class MainFlyoutPage : FlyoutPage
{
public MainFlyoutPage()
{
InitializeComponent();
FlyoutPage.SetFlyoutBehavior(this, FlyoutBehavior.Flyout);
}
public async Task NavigateTo(string page)
{
Page newPage = page switch
{
"Home" => new HomePage(),
"List" => new ListPage(),
"Settings" => new SettingsPage(),
"About" => new AboutPage(),
_ => new HomePage()
};
Detail = new NavigationPage(newPage);
IsPresented = false;
}
}
Shell 导航 #
Shell 概述 #
text
┌─────────────────────────────────────────────────────────────┐
│ Shell 导航 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Shell 是 Xamarin.Forms 推荐的导航方式: │
│ │
│ 优势: │
│ ✅ 统一的导航体验 │
│ ✅ 内置 URI 路由 │
│ ✅ 搜索功能集成 │
│ ✅ 灵活的布局选项 │
│ ✅ 简化的导航语法 │
│ │
│ 结构: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Shell │ │
│ │ ├── FlyoutItem (侧滑菜单项) │ │
│ │ │ ├── Tab (标签页) │ │
│ │ │ │ └── ShellContent (内容页) │ │
│ │ │ └── Tab │ │
│ │ │ └── ShellContent │ │
│ │ └── TabBar (底部标签栏) │ │
│ │ └── ShellContent │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
创建 Shell #
xml
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp.Views"
x:Class="MyApp.AppShell"
FlyoutBehavior="Flyout">
<Shell.FlyoutHeader>
<Grid HeightRequest="100" BackgroundColor="#2196F3">
<Label Text="我的应用"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="White"
FontSize="20"
FontAttributes="Bold" />
</Grid>
</Shell.FlyoutHeader>
<FlyoutItem Title="首页" Icon="home.png">
<ShellContent ContentTemplate="{DataTemplate local:HomePage}" />
</FlyoutItem>
<FlyoutItem Title="列表" Icon="list.png">
<ShellContent ContentTemplate="{DataTemplate local:ListPage}" />
</FlyoutItem>
<FlyoutItem Title="设置" Icon="settings.png">
<ShellContent ContentTemplate="{DataTemplate local:SettingsPage}" />
</FlyoutItem>
</Shell>
csharp
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute("detail", typeof(DetailPage));
Routing.RegisterRoute("settings/profile", typeof(ProfilePage));
}
}
Shell 导航 #
csharp
public class HomeViewModel
{
public Command NavigateCommand { get; }
public HomeViewModel()
{
NavigateCommand = new Command<string>(async (route) =>
{
await Shell.Current.GoToAsync(route);
});
}
}
private async void OnNavigateToDetail()
{
await Shell.Current.GoToAsync("detail");
}
private async void OnNavigateToDetailWithId()
{
await Shell.Current.GoToAsync($"detail?id=123");
}
private async void OnGoBack()
{
await Shell.Current.GoToAsync("..");
}
private async void OnGoToRoot()
{
await Shell.Current.GoToAsync("//");
}
Shell 路由 #
xml
<Shell ...>
<FlyoutItem Route="animals">
<Tab Route="domestic">
<ShellContent Route="dogs" ContentTemplate="{DataTemplate local:DogsPage}" />
<ShellContent Route="cats" ContentTemplate="{DataTemplate local:CatsPage}" />
</Tab>
<Tab Route="wild">
<ShellContent Route="monkeys" ContentTemplate="{DataTemplate local:MonkeysPage}" />
</Tab>
</FlyoutItem>
</Shell>
text
┌─────────────────────────────────────────────────────────────┐
│ 路由示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 绝对路由: │
│ //animals/domestic/dogs → 导航到狗页面 │
│ //animals/wild/monkeys → 导航到猴子页面 │
│ │
│ 相对路由: │
│ .. → 返回上一页 │
│ ../cats → 返回后导航到猫页面 │
│ detail → 导航到详情页 │
│ detail?id=123 → 带参数导航 │
│ │
└─────────────────────────────────────────────────────────────┘
Shell 参数传递 #
csharp
[QueryProperty(nameof(ItemId), "id")]
public partial class DetailPage : ContentPage
{
private string _itemId;
public string ItemId
{
get => _itemId;
set
{
_itemId = value;
LoadItem(int.Parse(value));
}
}
private void LoadItem(int id)
{
var item = DataService.GetItem(id);
BindingContext = new DetailViewModel(item);
}
}
private async void OnNavigate()
{
await Shell.Current.GoToAsync($"detail?id={item.Id}");
}
模态导航 #
模态页面 #
text
┌─────────────────────────────────────────────────────────────┐
│ 模态导航 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 模态页面特点: │
│ - 独立于导航堆栈 │
│ - 无导航栏(默认) │
│ - 需要用户交互才能关闭 │
│ - 适合登录、确认对话框等 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 模态页面 │ │
│ │ │ │
│ │ (覆盖在主页面之上) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 关闭按钮 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
csharp
private async void OnShowLogin(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new LoginPage());
}
private async void OnCloseModal(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
模态导航页 #
csharp
private async void OnShowLogin(object sender, EventArgs e)
{
var loginPage = new LoginPage();
var navPage = new NavigationPage(loginPage)
{
BarBackgroundColor = Color.FromHex("#2196F3"),
BarTextColor = Color.White
};
await Navigation.PushModalAsync(navPage);
}
导航事件 #
页面生命周期 #
text
┌─────────────────────────────────────────────────────────────┐
│ 页面生命周期 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 页面创建 │
│ │ │
│ ▼ │
│ 构造函数 │
│ │ │
│ ▼ │
│ InitializeComponent() │
│ │ │
│ ▼ │
│ OnAppearing() ←─────────┐ │
│ │ │ │
│ ▼ │ │
│ 页面可见 ───────────────┘ │
│ │ │
│ ▼ │
│ OnDisappearing() │
│ │ │
│ ▼ │
│ 页面隐藏 │
│ │
└─────────────────────────────────────────────────────────────┘
csharp
public partial class DetailPage : ContentPage
{
public DetailPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
protected override bool OnBackButtonPressed()
{
return base.OnBackButtonPressed();
}
}
导航最佳实践 #
1. 使用依赖注入 #
csharp
public interface INavigationService
{
Task NavigateToAsync<T>() where T : Page;
Task NavigateToAsync<T>(object parameter) where T : Page;
Task GoBackAsync();
Task GoToRootAsync();
}
public class NavigationService : INavigationService
{
public async Task NavigateToAsync<T>() where T : Page
{
var page = App.Current.ServiceProvider.GetRequiredService<T>();
await Application.Current.MainPage.Navigation.PushAsync(page);
}
public async Task GoBackAsync()
{
await Application.Current.MainPage.Navigation.PopAsync();
}
}
2. 避免内存泄漏 #
csharp
public partial class DetailPage : ContentPage
{
public DetailPage()
{
InitializeComponent();
MessagingCenter.Subscribe<SourcePage, Message>(this, "Event", OnMessage);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<SourcePage, Message>(this, "Event");
}
}
3. 使用异步导航 #
csharp
public ICommand NavigateCommand { get; }
public HomeViewModel()
{
NavigateCommand = new Command(async () =>
{
await Shell.Current.GoToAsync("detail");
});
}
下一步 #
现在你已经掌握了 Xamarin.Forms 的导航系统,接下来学习 数据绑定,了解如何将 UI 与数据连接起来!
最后更新:2026-03-29