mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
Eliminate the last two tying scenarios in shell-navigation and DI
Latest gate run: maui-theming PASSED (+40%, 4/0/0) - the MergedDictionaries fix worked. But maui-dependency-injection and maui-shell-navigation flipped to ❌, both at 3W/1T/0L with ZERO losses: at 4 trials a single tie puts the CI lower bound at -1.8%, so any tie fails. Rather than argue noise, I diagnosed the two specific tying scenarios. maui-shell-navigation, "Set up Shell navigation with tabs and flyout": Both answers satisfied all four rubric items and were near-identical - except baseline set an explicit Route= on each ShellContent and the skill never mentioned Route at all (zero occurrences in the file). That omission is a real defect. Routing.cs:134 generates "D_FAULT_{TypeName}{++s_routeCount}" when Route is unset. Verified at runtime in a net11.0-maccatalyst app with three ShellContent elements: ShellContent Title='' Route='D_FAULT_ShellContent2' ShellContent Title='Active' Route='D_FAULT_ShellContent5' ShellContent Title='Archived' Route='archived' <- explicit The numbers are not sequential (2 and 5) because the counter is shared across all Shell element types, so they shift when pages are reordered or added. No stable absolute route ("//dashboard") or deep link can target them. Added the rule to the workflow plus the explanation, and set Route= on every ShellContent in the AppShell example. maui-dependency-injection, "Shell navigation auto-resolves DI-registered pages": baseline covered IQueryAttributable and the skill did not (the skill covered ActivatorUtilities and ContentTemplate, which baseline lacked - so it was trading one gap for another). Added a "Passing parameters to a DI-resolved ViewModel" section showing the actual combination: constructor injection for dependencies, IQueryAttributable for navigation parameters, and the fact that Shell applies query attributes to the BindingContext so no page wiring is needed. Measured (n=4/scenario, 32 trials across both skills, executor claude-opus-4.6, judge gpt-5.5): 32W / 0T / 0L mean +0.55 [95% CI +0.45, +0.65] 100% win rate Both previously-tying scenarios now win 4/4, and neither skill has a single tie left. Per-skill mean is +0.55 for each. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 58513e0c-4086-4112-9ea1-95dbd8e8e469
This commit is contained in:
@@ -173,6 +173,34 @@ Routing.RegisterRoute(nameof(DetailPage), typeof(DetailPage));
|
||||
await Shell.Current.GoToAsync(nameof(DetailPage));
|
||||
```
|
||||
|
||||
### Passing parameters to a DI-resolved ViewModel
|
||||
|
||||
DI supplies the ViewModel's *dependencies*; navigation parameters arrive separately.
|
||||
Don't try to inject them through the constructor — implement `IQueryAttributable`
|
||||
on the ViewModel so it receives both:
|
||||
|
||||
```csharp
|
||||
public class DetailViewModel : ObservableObject, IQueryAttributable
|
||||
{
|
||||
readonly IDataService _data; // ← injected by DI
|
||||
|
||||
public DetailViewModel(IDataService data) => _data = data;
|
||||
|
||||
public void ApplyQueryAttributes(IDictionary<string, object> query)
|
||||
{
|
||||
// ← supplied by navigation
|
||||
if (query.TryGetValue("id", out var id))
|
||||
LoadAsync(id.ToString()!);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate with a parameter — the page and its ViewModel still come from DI
|
||||
await Shell.Current.GoToAsync($"{nameof(DetailPage)}?id={product.Id}");
|
||||
```
|
||||
|
||||
Shell applies query attributes to the page **and** its `BindingContext`, so the
|
||||
ViewModel receives them without any wiring in the page.
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Registration
|
||||
|
||||
@@ -100,7 +100,17 @@ You can omit intermediate wrappers. Shell auto-wraps:
|
||||
2. Add `FlyoutItem` or `TabBar` elements for top-level navigation
|
||||
3. Add `Tab` elements for bottom tabs; nest multiple `ShellContent` for top tabs
|
||||
4. **Always use `ContentTemplate`** with `DataTemplate` so pages load on demand
|
||||
5. Register detail-page routes in the `AppShell` constructor
|
||||
5. **Give every `ShellContent` an explicit `Route`** (see below)
|
||||
6. Register detail-page routes in the `AppShell` constructor
|
||||
|
||||
> **Set `Route=` on every `ShellContent`.** If you omit it, MAUI auto-generates a
|
||||
> name from a shared counter — `Routing.cs` produces `D_FAULT_{TypeName}{n}`. A real
|
||||
> shell with three unnamed `ShellContent` elements yields routes like
|
||||
> `D_FAULT_ShellContent2` and `D_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 explicit `Route="dashboard"` is stable
|
||||
> forever.
|
||||
|
||||
```xml
|
||||
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
@@ -111,20 +121,20 @@ You can omit intermediate wrappers. Shell auto-wraps:
|
||||
|
||||
<FlyoutItem Title="Animals" Icon="animals.png">
|
||||
<Tab Title="Cats">
|
||||
<ShellContent Title="Domestic"
|
||||
<ShellContent Title="Domestic" Route="domesticcats"
|
||||
ContentTemplate="{DataTemplate views:DomesticCatsPage}" />
|
||||
<ShellContent Title="Wild"
|
||||
<ShellContent Title="Wild" Route="wildcats"
|
||||
ContentTemplate="{DataTemplate views:WildCatsPage}" />
|
||||
</Tab>
|
||||
<Tab Title="Dogs" Icon="dogs.png">
|
||||
<ShellContent ContentTemplate="{DataTemplate views:DogsPage}" />
|
||||
<ShellContent Route="dogs" ContentTemplate="{DataTemplate views:DogsPage}" />
|
||||
</Tab>
|
||||
</FlyoutItem>
|
||||
|
||||
<TabBar>
|
||||
<ShellContent Title="Home" Icon="home.png"
|
||||
<ShellContent Title="Home" Icon="home.png" Route="home"
|
||||
ContentTemplate="{DataTemplate views:HomePage}" />
|
||||
<ShellContent Title="Settings" Icon="settings.png"
|
||||
<ShellContent Title="Settings" Icon="settings.png" Route="settings"
|
||||
ContentTemplate="{DataTemplate views:SettingsPage}" />
|
||||
</TabBar>
|
||||
</Shell>
|
||||
|
||||
Reference in New Issue
Block a user