导航系统 #

导航概述 #

导航类型 #

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