mcpField
M
maui-shell-navigation

maui-shell-navigation

github.com/davidortinau开发17664 次安装17 周前更新
代码仓库

关于 maui-shell-navigation

面向 .NET MAUI Shell 导航的技能,介绍按需创建页面、通过查询传递数据、异步导航拦截,以及常见路由和导航问题。

主要功能

  1. 01使用 ContentTemplate 按需创建页面
  2. 02通过 IQueryAttributable 传递数据
  3. 03使用 ShellNavigationQueryParameters 传递复杂对象
  4. 04以异步检查延迟导航
  5. 05注册唯一路由并等待导航完成

适用场景

  1. 01减少 Shell 启动时创建的页面
  2. 02向目标页面传递标识符或对象
  3. 03离开页面前确认未保存的更改
  4. 04排查路由不匹配或导航竞态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-shell-navigation

安装后在 Claude Code 中输入/maui-shell-navigation确认已加载。

附带文件references/shell-navigation-api.md

详细介绍

.NET MAUI Shell 导航

关键决策

始终使用 ContentTemplate

始终将 ContentTemplate 与 DataTemplate 搭配使用,以便按需创建页面。 直接使用 Content 会在 Shell 初始化时创建所有页面,拖慢启动速度。


<ShellContent ContentTemplate="{DataTemplate views:HomePage}" />


<ShellContent>
    <views:HomePage />
</ShellContent>
传递数据 — 优先使用 IQueryAttributable,而不是 QueryProperty

IQueryAttributable 可一次性获取所有参数,并且适用于视图模型:

public class AnimalDetailsViewModel : ObservableObject, IQueryAttributable
{
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.TryGetValue("id", out var id))
            AnimalId = id.ToString();
    }
}

对于复杂对象,请使用 ShellNavigationQueryParameters,以避免序列化:

var parameters = new ShellNavigationQueryParameters
{
    { "animal", selectedAnimal }
};
await Shell.Current.GoToAsync("animaldetails", parameters);
守卫导航 — 异步延期模式

使用 GetDeferral() 进行异步检查(例如,“是否保存未保存的更改?”):

protected override async void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);
    if (hasUnsavedChanges && args.Source == ShellNavigationSource.Pop)
    {
        var deferral = args.GetDeferral();
        bool discard = await ShowConfirmationDialog();
        if (!discard)
            args.Cancel();
        deferral.Complete();
    }
}

常见问题

  1. 路由名称重复 — 如果路由名称已注册或与可视层次结构中的路由匹配,Routing.RegisterRoute 会抛出 ArgumentException。 整个应用中的每个路由都必须唯一。

  2. 相对路由需要注册 — 除非通过 Routing.RegisterRoute 注册了 somepage,否则不能调用 GoToAsync("somepage")。 可视层次结构中的页面则使用绝对 // 路由。

  3. 页面按需创建 — 使用 ContentTemplate 时,页面构造函数仅在首次导航时运行。不要假设页面在启动时就已存在。

  4. Tab.Stack 只读 — 不能直接操作导航堆栈;所有导航更改都应使用 GoToAsync。

  5. GoToAsync 是异步方法 — 始终等待它完成 — 即发即弃式导航会导致竞态条件,也可能静默失败:

    // ❌ Fire-and-forget — race conditions
    Shell.Current.GoToAsync("details");
    
    // ✅ Always await
    await Shell.Current.GoToAsync("details");
    
  6. 路由层次结构很重要 — 绝对路由必须匹配可视层次结构中的完整路径(//FlyoutItem/Tab/ShellContent)。路径错误会导致静默无操作,而不会抛出异常。

评论

支持 Markdown