The repo carried two parallel UIs (WPF + WinUI) sharing Models/Services via
cross-directory Link includes. Now that WinUI is the only frontend, collapse
the structure so the WinUI project IS the repo root instead of a nested
sibling folder:
- Delete the WPF project entirely (App.xaml, MainWindow.xaml, csproj) and its
bin/obj output
- Move AmiReel.WinUI/* up to the repo root (App, MainWindow, MainPage,
DialogHelper, Assets, Package.appxmanifest, app.manifest, Properties,
.github/instructions, AGENTS.md) via git mv, preserving history
- Rename AmiReel.WinUI.csproj -> AmiReel.csproj; regenerate the solution as
AmiReel.slnx (the newer XML solution format) with a single project
- Rename namespace AmigaDB.VideoRenderer.{Models,Services} -> AmiReel.{...}
and AmiReel_WinUI -> AmiReel across all files, including the embedded
ffmpeg/ffprobe resource logical names in the csproj and ToolExtractor
- Models/ and Services/ no longer need the Link-based cross-directory
<Compile Include>; they're picked up by the SDK's default globbing now
that they live under the project directory
- Rename assets/ -> branding/ (source icon art) to avoid a case-insensitive
collision with Assets/ (packaged tile art) once both sit at repo root
- Merge the two .gitignore files into one; track the PublishProfiles pubxml
files instead of ignoring them (no secrets, and they keep publish
reproducible across machines) as branding, gitignore, etc.
- Simplify publish-win-x64.ps1 (drop the -Target Wpf/WinUI switch, there's
only one target now) and rewrite README.md to describe the single-project
layout, build/run/publish commands, and file structure
Verified: dotnet build succeeds for both AmiReel.csproj and AmiReel.slnx, and
the built exe launches and renders identically to before the move.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
13 KiB
description, applyTo
| description | applyTo |
|---|---|
| WinUI 3 / WinAppSDK architecture, MVVM, XAML patterns, DI, theming, and controls guidance | **/*.cs, **/*.xaml, **/*.csproj |
WinUI 3 / WinAppSDK -- Best Practices & Patterns
This file covers WinUI 3-specific patterns, conventions, and architecture guidance for this project.
1. Architecture -- MVVM Pattern
Overview
Use Model-View-ViewModel (MVVM) for all UI features:
| Layer | Responsibility | Example |
|---|---|---|
| Model | Data structures & business entities | Item.cs, UserProfile.cs |
| View | XAML UI -- layout, styles, animations | MainPage.xaml |
| ViewModel | UI state, commands, data transformation | MainViewModel.cs |
| Service | Business logic, data access, navigation | IDataService.cs, NavigationService.cs |
Project Folder Structure
<ProjectName>/
Models/ -> Data classes
ViewModels/ -> ViewModels (one per page/dialog)
Views/ -> XAML pages and windows
Services/ -> Business logic & platform services
Converters/ -> IValueConverter implementations
Helpers/ -> Static utility methods
Controls/ -> Custom/reusable controls
Strings/
en-us/
Resources.resw
Assets/ -> Images, icons, splash screens
ViewModel Base
Use CommunityToolkit.Mvvm (recommended) for boilerplate-free ViewModels:
<!-- Use latest stable version; do not hard-code version numbers in instructions -->
<PackageReference Include="CommunityToolkit.Mvvm" Version="*" />
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string _title = string.Empty;
[ObservableProperty]
private bool _isLoading;
[RelayCommand]
private async Task LoadDataAsync()
{
IsLoading = true;
try
{
// Load data
}
finally
{
IsLoading = false;
}
}
}
View-ViewModel Binding
<Page
x:Class="<RootNamespace>.Views.MainPage"
xmlns:vm="using:<RootNamespace>.ViewModels">
<Page.DataContext>
<vm:MainViewModel />
</Page.DataContext>
<Grid>
<TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}" />
<Button Command="{x:Bind ViewModel.LoadDataCommand}" Content="Load" />
</Grid>
</Page>
2. XAML Best Practices
Use x:Bind Over {Binding}
| Feature | x:Bind |
{Binding} |
|---|---|---|
| Compile-time check | Yes | No |
| Performance | Faster (compiled) | Slower (reflection) |
| Default mode | OneTime | OneWay |
| IntelliSense | Yes | No |
<!-- GOOD -->
<TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}" />
<!-- AVOID -->
<TextBlock Text="{Binding Name}" />
Use x:Load for Deferred Loading
<StackPanel x:Load="{x:Bind ViewModel.ShowAdvancedOptions, Mode=OneWay}">
<!-- Heavy content loaded only when needed -->
</StackPanel>
Use WinUI Controls from the SDK
Prefer the WinUI 3 controls from Microsoft.UI.Xaml.Controls, not the older UWP Windows.UI.Xaml.Controls:
// GOOD -- WinUI 3
using Microsoft.UI.Xaml.Controls;
// AVOID -- UWP (won't work in WinUI 3 desktop)
// using Windows.UI.Xaml.Controls;
XAML Formatting Convention
<Button
x:Name="SaveButton"
x:Uid="SaveButton"
AutomationProperties.Name="Save"
Command="{x:Bind ViewModel.SaveCommand}"
Style="{StaticResource AccentButtonStyle}" />
- One attribute per line for controls with 3+ attributes.
- Order:
x:Name->x:Uid->AutomationProperties-> layout -> data -> style.
3. Dependency Injection
Setup with Microsoft.Extensions.DependencyInjection
<!-- Use latest stable version; do not hard-code version numbers in instructions -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="*" />
// App.xaml.cs
public partial class App : Application
{
public static IServiceProvider Services { get; private set; } = null!;
public App()
{
InitializeComponent();
Services = ConfigureServices();
}
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Services
services.AddSingleton<INavigationService, NavigationService>();
services.AddTransient<IDataService, DataService>();
// ViewModels
services.AddTransient<MainViewModel>();
return services.BuildServiceProvider();
}
}
4. Navigation
Frame-based Navigation
public interface INavigationService
{
bool CanGoBack { get; }
void NavigateTo<TPage>() where TPage : Page;
void GoBack();
}
Use a NavigationView with a Frame:
<NavigationView SelectionChanged="OnNavigationChanged">
<NavigationView.MenuItems>
<NavigationViewItem Content="Home" Tag="Home" />
<NavigationViewItem Content="Settings" Tag="Settings" />
</NavigationView.MenuItems>
<Frame x:Name="ContentFrame" />
</NavigationView>
5. Windowing & Title Bar
Custom Title Bar
// In Window constructor or Activated handler
ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar); // AppTitleBar is a UIElement in your XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" /> <!-- Title bar -->
<RowDefinition Height="*" /> <!-- Content -->
</Grid.RowDefinitions>
<Grid x:Name="AppTitleBar" Grid.Row="0">
<TextBlock Text="My App" VerticalAlignment="Center" Margin="16,0" />
</Grid>
<Frame Grid.Row="1" x:Name="ContentFrame" />
</Grid>
Window Sizing
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
var appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.Resize(new SizeInt32(1280, 720));
6. Theming
Support Light/Dark/High Contrast
<!-- In App.xaml -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Detect Theme Changes
if (Content is FrameworkElement rootElement)
{
rootElement.ActualThemeChanged += (s, e) =>
{
// React to theme change
};
}
Use Theme Resources, Not Hard-coded Colors
<!-- GOOD -->
<TextBlock Foreground="{ThemeResource TextFillColorPrimaryBrush}" />
<!-- AVOID -->
<TextBlock Foreground="#000000" />
7. System Backdrop (Mica / Acrylic)
This project uses Mica backdrop (already configured in MainWindow.xaml):
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
Alternatives:
<DesktopAcrylicBackdrop /> <!-- Acrylic -->
<MicaBackdrop Kind="BaseAlt" /> <!-- Mica Alt -->
8. Community Toolkit
Recommended Packages
<!-- Use latest stable versions; do not hard-code version numbers in instructions -->
<PackageReference Include="CommunityToolkit.Mvvm" Version="*" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="*" />
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="*" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" Version="*" />
Useful Toolkit Features
| Feature | Package | Use Case |
|---|---|---|
ObservableObject |
CommunityToolkit.Mvvm | ViewModel base class |
RelayCommand |
CommunityToolkit.Mvvm | Command implementation |
ObservableProperty |
CommunityToolkit.Mvvm | Auto INotifyPropertyChanged |
SettingsCard |
CommunityToolkit.WinUI.Controls | Settings pages |
IncrementalLoadingCollection |
CommunityToolkit.WinUI | Lazy-loading lists |
9. Common Pitfalls
| Pitfall | Solution |
|---|---|
Using Windows.UI.Xaml namespace |
Use Microsoft.UI.Xaml for WinUI 3 |
Calling Window.Current |
Not available in WinUI 3 -- pass window reference explicitly |
Using CoreDispatcher |
Use DispatcherQueue instead |
REGDB_E_CLASSNOTREG error |
Ensure Developer Mode is enabled, then re-register: run winapp unregister followed by dotnet run (or winapp run <build-output>) to refresh the loose-layout registration |
| Stale package state after manifest changes | Run winapp unregister, then dotnet run -- using winapp run --clean additionally wipes LocalState/settings to test first-run behavior |
| XAML Designer crashes | Clean & rebuild; ensure platform matches (x64 vs AnyCPU) |
{Binding} not updating |
Switch to x:Bind with Mode=OneWay or Mode=TwoWay |
10. Validation
Build & register the MSIX package -- see Build, Run & Deploy in .github/agents/Agents.md.
Verify
- Run the app and verify the changed UI renders correctly on x64.
- Search XAML for
{Binding-- replace withx:Bind. - Search XAML for
Foreground="#orBackground="#-- replace with{ThemeResource}. - Search C# for
Windows.UI.Xaml-- replace withMicrosoft.UI.Xaml. - Search C# for
Window.Current-- replace with explicit window reference. - Search C# for
CoreDispatcher-- replace withDispatcherQueue. - Test Light, Dark, and High Contrast themes.
11. Must Read & Research
Agent Rule: Before making any WinUI/WinAppSDK-related change, you must fetch and review the relevant references below using
fetch_webpage. Consult the appropriate section based on the type of change. Apply what you learn -- do not skip this step.
Official Documentation
| # | Reference | When to consult |
|---|---|---|
| 1 | WinUI 3 Overview | Starting new features, onboarding to the project |
| 2 | Windows App SDK | Using SDK-specific APIs (windowing, lifecycle, activation) |
| 3 | WinUI 3 API Reference | Looking up specific class/method signatures |
| 4 | WinUI 3 Gallery App | Choosing controls, reviewing control patterns |
| 5 | AI Dev Gallery | On-device AI/ML integration patterns, model usage examples in WinUI |
| 6 | Windows App SDK Release Notes | Checking for breaking changes, new APIs, known issues |
Architecture & Patterns
| # | Reference | When to consult |
|---|---|---|
| 7 | CommunityToolkit.Mvvm Docs | Implementing ViewModels, commands, ObservableProperty |
| 8 | Dependency Injection in .NET | Registering services, constructor injection, lifetime management |
| 9 | Template Studio for WinUI | Scaffolding pages, navigation, project structure patterns |
Controls & Design
| # | Reference | When to consult |
|---|---|---|
| 10 | WinUI 3 Gallery (GitHub) | Example implementations of any WinUI control |
| 11 | Windows Community Toolkit (GitHub) | Before building custom controls -- check if the toolkit already has one |
| 12 | Fluent Design System | Spacing, typography, colour, motion, layout decisions |
| 13 | XAML Controls Gallery | Interactive demo of all controls and their properties |
Windows APIs & AI
| # | Reference | When to consult |
|---|---|---|
| 14 | Windows APIs instruction file | First stop -- check if a built-in API already exists for the capability you need (AI, windowing, notifications, widgets, lifecycle, etc.) |
| 15 | Windows AI APIs | On-device AI: Phi Silica (text gen), OCR, imaging (super-res, description, object extract, erase) |
| 16 | Windows ML | Custom ONNX model inference on CPU/GPU/NPU |
| 17 | Foundry Local | Run OSS LLMs (Llama, Mistral, Phi) locally via REST API |
| 18 | Windows AI on Windows | AI landing page -- all AI options for Windows apps |
Samples
Agent Rule -- MANDATORY: Before implementing any WinAppSDK or Platform SDK API you have not used before, search the samples repo first and study the working example. Do not guess API usage from docs alone -- see the Sample-First Rule for details and known pitfalls.
| # | Reference | When to consult |
|---|---|---|
| 19 | Windows App SDK Samples | Always search here first before implementing any SDK API for the first time |
| 20 | WinUI 3 Demos | Reference implementations and patterns |
| 21 | Windows AI API Samples | AI API usage with WinUI (ImageDescription, TextRecognizer, LanguageModel, etc.) |