Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Guide for implementing Shell-based navigation in .NET MAUI apps. Covers AppShell setup, visual hierarchy (FlyoutItem, TabBar, Tab, ShellContent), URI-based navigation with GoToAsync, route registration, query parameters, back navigation, flyout and tab configuration, navigation events, and navigation guards. Use when: setting up Shell navigation, adding tabs or flyout menus, navigating between pages with GoToAsync, passing parameters between pages, registering routes, customizing back button behavior, or guarding navigation with confirmation dialogs. Do not use for: deep linking from external URLs (see .NET MAUI deep linking documentation), data binding on pages (use maui-data-binding), dependency injection setup (use maui-dependency-injection), or NavigationPage-only apps that don't use Shell.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Implement page navigation in .NET MAUI apps using Shell. Shell provides URI-based navigation, a flyout menu, tab bars, and a four-level visual hierarchy — all configured declaratively in XAML.
GoToAsyncmaui-data-bindingmaui-dependency-injectionNavigationPage without Shell (different navigation API)AppShell.xaml as the root shellContentPage) to navigate betweenThese are the Shell-specific decisions that are easy to get wrong. Apply them whenever they are relevant to what the user asked.
| Situation | Do this | Not this |
|---|---|---|
Declaring pages in AppShell.xaml | With xmlns:views="clr-namespace:MyApp.Views" declared: <ShellContent ContentTemplate="{DataTemplate views:MyPage}" /> — the page is created on first navigation | <ShellContent><views:MyPage /></ShellContent>, which constructs every page at startup |
| Navigating to a page not in the visual hierarchy | Routing.RegisterRoute("details", typeof(DetailsPage)) first | Calling GoToAsync("details") unregistered — it throws at runtime |
| Receiving navigation parameters | Implement IQueryAttributable on the ViewModel | Implementing it on the Page, which splits state from the BindingContext |
| Passing a whole object | ShellNavigationQueryParameters | Serialising the object into the query string |
Any GoToAsync call | await it | Fire-and-forget — exceptions are swallowed and navigation races |
| Confirming before back navigation | ShellNavigatingEventArgs.GetDeferral() … deferral.Complete() | Blocking synchronously on the dialog task |
| Detecting back navigation | Check e.Source == ShellNavigationSource.Pop | Assuming every navigation is a back action |
Do not propose NavigationPage / PushAsync solutions for a Shell app, and do
not restructure a working AppShell hierarchy unless the user asked.
Answer narrowly, but completely. Staying on topic does not mean being terse. When
you show a navigation change, include the pieces needed to run it: the AppShell.xaml
markup and the Routing.RegisterRoute call, or the GoToAsync call and the
receiving IQueryAttributable / [QueryProperty] code. Where two approaches are both
valid (query string vs ShellNavigationQueryParameters), show both and say when each
fits — a single snippet the user still has to complete is a worse answer.
Shell uses a four-level hierarchy. Each level wraps the one below it:
Shell
├── FlyoutItem / TabBar (top-level grouping)
│ ├── Tab (bottom-tab grouping)
│ │ ├── ShellContent (page slot → ContentPage)
│ │ └── ShellContent (multiple = top tabs)
│ └── Tab
└── FlyoutItem / TabBar
Tab childrenShellContent; multiple children produce top tabsContentPageYou can omit intermediate wrappers. Shell auto-wraps:
| You write | Shell creates |
|---|---|
ShellContent only | FlyoutItem > Tab > ShellContent |
Tab only | FlyoutItem > Tab |
ShellContent in TabBar | TabBar > Tab > ShellContent |
AppShell.xaml inheriting from ShellFlyoutItem or TabBar elements for top-level navigationTab elements for bottom tabs; nest multiple ShellContent for top tabsContentTemplate with DataTemplate so pages load on demandShellContent an explicit Route (see below)AppShell constructorSet
Route=on everyShellContent. If you omit it, MAUI auto-generates a name from a shared counter —Routing.csproducesD_FAULT_{TypeName}{n}. A real shell with three unnamedShellContentelements yields routes likeD_FAULT_ShellContent2andD_FAULT_ShellContent5: the numbers are not sequential, they depend on how many Shell elements were constructed first, and they shift when you reorder or add pages. You cannot write a stable absolute route (//dashboard) or deep link against that. An explicitRoute="dashboard"is stable forever.
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:MyApp.Views"
x:Class="MyApp.AppShell"
FlyoutBehavior="Flyout">
<FlyoutItem Title="Animals" Icon="animals.png">
<Tab Title="Cats">
<ShellContent Title="Domestic" Route="domesticcats"
ContentTemplate="{DataTemplate views:DomesticCatsPage}" />
<ShellContent Title="Wild" Route="wildcats"
ContentTemplate="{DataTemplate views:WildCatsPage}" />
</Tab>
<Tab Title="Dogs" Icon="dogs.png">
<ShellContent Route="dogs" ContentTemplate="{DataTemplate views:DogsPage}" />
</Tab>
</FlyoutItem>
<TabBar>
<ShellContent Title="Home" Icon="home.png" Route="home"
ContentTemplate="{DataTemplate views:HomePage}" />
<ShellContent Title="Settings" Icon="settings.png" Route="settings"
ContentTemplate="{DataTemplate views:SettingsPage}" />
</TabBar>
</Shell>
// AppShell.xaml.cs
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute("animaldetails", typeof(AnimalDetailsPage));
Routing.RegisterRoute("editanimal", typeof(EditAnimalPage));
}
}
All programmatic navigation uses Shell.Current.GoToAsync. Always await the call.
| Prefix | Meaning |
|---|---|
// | Absolute route from Shell root |
| (none) | Relative; pushes onto the current nav stack |
.. | Go back one level |
../ | Go back then navigate forward |
// 1. Absolute — switch to a specific hierarchy location
await Shell.Current.GoToAsync("//animals/cats/domestic");
// 2. Relative — push a registered detail page
await Shell.Current.GoToAsync("animaldetails");
// 3. With query string parameters
await Shell.Current.GoToAsync($"animaldetails?id={animal.Id}");
// 4. Go back one page
await Shell.Current.GoToAsync("..");
// 5. Go back two pages
await Shell.Current.GoToAsync("../..");
// 6. Go back one page, then push a different page
await Shell.Current.GoToAsync("../editanimal");
Implement on ViewModels to receive all parameters in one call:
public class AnimalDetailsViewModel : ObservableObject, IQueryAttributable
{
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.TryGetValue("id", out var id))
AnimalId = id.ToString();
}
}
Apply on the ViewModel class (or the page, if it genuinely owns the state).
Prefer IQueryAttributable on the ViewModel — it keeps navigation state with the
BindingContext and handles multiple parameters in one call:
[QueryProperty(nameof(AnimalId), "id")]
public partial class AnimalDetailsViewModel : ObservableObject
{
[ObservableProperty]
private string _animalId = string.Empty;
}
Shell applies query attributes after the page constructor sets BindingContext,
so the property must raise change notification — a plain auto-property leaves the
binding stuck on its initial value.
Pass objects without serializing to strings:
var parameters = new ShellNavigationQueryParameters
{
{ "animal", selectedAnimal }
};
await Shell.Current.GoToAsync("animaldetails", parameters);
Receive via IQueryAttributable:
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
Animal = query["animal"] as Animal;
}
Use GetDeferral() in OnNavigating for async checks (e.g., "save unsaved changes?"):
// In AppShell.xaml.cs
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();
}
}
Multiple ShellContent (or Tab) children inside a TabBar or FlyoutItem produce bottom tabs.
Multiple ShellContent children inside a single Tab produce top tabs:
<Tab Title="Photos">
<ShellContent Title="Recent" ContentTemplate="{DataTemplate views:RecentPage}" />
<ShellContent Title="Favorites" ContentTemplate="{DataTemplate views:FavoritesPage}" />
</Tab>
| Attached Property | Type | Purpose |
|---|---|---|
Shell.TabBarBackgroundColor | Color | Tab bar background |
Shell.TabBarForegroundColor | Color | Selected icon color |
Shell.TabBarTitleColor | Color | Selected tab title color |
Shell.TabBarUnselectedColor | Color | Unselected tab icon/title |
Shell.TabBarIsVisible | bool | Show/hide the tab bar |
<!-- Hide the tab bar on a specific page -->
<ContentPage Shell.TabBarIsVisible="False" ... />
Set on Shell: Disabled, Flyout, or Locked.
<Shell FlyoutBehavior="Flyout"> ... </Shell>
Controls how children appear in the flyout:
AsSingleItem (default) — one flyout entry for the groupAsMultipleItems — each child Tab gets its own entry<FlyoutItem Title="Animals" FlyoutDisplayOptions="AsMultipleItems">
<Tab Title="Cats" ... />
<Tab Title="Dogs" ... />
</FlyoutItem>
<MenuItem Text="Log Out"
Command="{Binding LogOutCommand}"
IconImageSource="logout.png" />
Customize the back button per page:
<Shell.BackButtonBehavior>
<BackButtonBehavior Command="{Binding BackCommand}"
IconOverride="back_arrow.png"
TextOverride="Cancel"
IsVisible="True" />
</Shell.BackButtonBehavior>
Properties: Command, CommandParameter, IconOverride, TextOverride, IsVisible, IsEnabled.
// Current URI location
string location = Shell.Current.CurrentState.Location.ToString();
// Current page
Page page = Shell.Current.CurrentPage;
// Navigation stack of the current tab
IReadOnlyList<Page> stack = Shell.Current.Navigation.NavigationStack;
Override in AppShell:
protected override void OnNavigated(ShellNavigatedEventArgs args)
{
base.OnNavigated(args);
// args.Current, args.Previous, args.Source
}
ShellNavigationSource values: Push, Pop, PopToRoot, Insert, Remove, ShellItemChanged, ShellSectionChanged, ShellContentChanged, Unknown.
Content directly instead of ContentTemplate with DataTemplate creates all pages at Shell init, hurting startup time. Always use ContentTemplate.Routing.RegisterRoute throws ArgumentException if a route name matches an existing route or a visual hierarchy route. Every route must be unique across the app.GoToAsync("somepage") unless somepage was registered with Routing.RegisterRoute. Visual hierarchy pages use absolute // routes.GoToAsync causes race conditions and silent failures. Always await the call.//FlyoutItem/Tab/ShellContent). Wrong paths produce silent no-ops, not exceptions.GoToAsync for all navigation changes.GetDeferral() for async guards: Synchronous cancellation in OnNavigating works, but async checks require GetDeferral() / deferral.Complete() to avoid race conditions.references/shell-navigation-api.md — Full API reference for Shell hierarchy, routes, tabs, flyout, and navigationまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Use a repo-root `.editorconfig` to configure free .NET analyzer and style rules. Use when a .NET repo needs rule severity, code-style options, section layout, or analyzer ownership made explicit. USE FOR: the repo needs a root .editorconfig; analyzer severity and style ownership are unclear; the team wants one source of truth for rule configuration. DO NOT USE FOR: choosing analyzers with no config change; formatting-only execution with no config ownership question. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
日本語の概要は準備中です。原文の説明を表示しています。
Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java/Kotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift/Objective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.
日本語の概要は準備中です。原文の説明を表示しています。
Design or review .NET solution architecture across modular monoliths, clean architecture, vertical slices, microservices, DDD, CQRS, and cloud-native boundaries without over-engineering. USE FOR: .NET architecture choices; layer and domain boundary review; service decomposition; clean architecture, vertical slice, DDD, CQRS, and modular monolith decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
日本語の概要は準備中です。原文の説明を表示しています。