25 KiB
25 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | XAML | Use x:Bind for compiled bindings | Compile-time checked bindings with better performance | x:Bind for type-safe bindings | {Binding} when x:Bind works | <TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/> | <TextBlock Text="{Binding Title}"/> | High | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension |
| 3 | 2 | XAML | Use x:Load for deferred loading | Only instantiate UI elements when needed | x:Load=False for hidden panels and dialogs | Loading all UI upfront | <StackPanel x:Load="{x:Bind ShowDetails, Mode=OneWay}"> | Always-loaded collapsed panels | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-load-attribute |
| 4 | 3 | XAML | Use x:Phase for incremental rendering | Load list items in phases for smooth scrolling | x:Phase on secondary content in DataTemplates | Loading all template content in phase 0 | <TextBlock x:Phase="1" Text="{x:Bind Description}"/> | All content in single phase for complex templates | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension |
| 5 | 4 | XAML | Use x:DefaultBindMode | Set default binding mode for a scope | x:DefaultBindMode=OneWay on containers with many bindings | Mode=OneWay on every individual x:Bind | <StackPanel x:DefaultBindMode="OneWay"> | Mode=OneWay repeated on 20 bindings | Low | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension |
| 6 | 5 | Controls | Use NavigationView for app navigation | WinUI 3 NavigationView with Left Top and LeftCompact display modes plus footer items | NavigationView with PaneDisplayMode for main app shell | Custom hamburger menu implementation | <NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home"/></NavigationView.MenuItems></NavigationView> | Custom SplitView with manual hamburger button | High | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview |
| 7 | 6 | Controls | Use InfoBar for status messages | Non-intrusive informational messages | InfoBar for success warning and error messages | Custom styled StackPanel for status | <InfoBar IsOpen="True" Severity="Warning" Title="Update available"/> | <StackPanel Background="Yellow"><TextBlock Text="Warning"/></StackPanel> | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/infobar |
| 8 | 7 | Controls | Use TeachingTip for onboarding | Contextual tips attached to UI elements | TeachingTip for feature discovery | Custom popup for teaching | <TeachingTip Target="{x:Bind SearchBox}" Title="Try searching"/> | Custom Popup positioned near target element | Low | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/teaching-tip |
| 9 | 8 | Controls | Use ContentDialog for modal interactions | Standard modal dialog pattern | ContentDialog for confirmations and input | Custom overlay Panel as dialog | <ContentDialog Title="Delete?" PrimaryButtonText="Delete" CloseButtonText="Cancel"/> | Grid overlay with manual focus trapping | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs |
| 10 | 9 | Controls | Use BreadcrumbBar for hierarchy | Show navigation path in hierarchical apps | BreadcrumbBar for folder or category navigation | Manual TextBlock breadcrumb chain | <BreadcrumbBar ItemsSource="{x:Bind Breadcrumbs}"/> | <StackPanel Orientation="Horizontal"><TextBlock Text="Home > Settings > Display"/></StackPanel> | Low | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/breadcrumbbar |
| 11 | 10 | Styling | Use Lightweight Styling | Override control sub-properties via resources | Lightweight styling resource keys to tweak controls | Full ControlTemplate override for small changes | <Button><Button.Resources><ResourceDictionary><ResourceDictionary.ThemeDictionaries><ResourceDictionary x:Key="Light"><SolidColorBrush x:Key="ButtonBackground" Color="MediumSlateBlue"/></ResourceDictionary></ResourceDictionary.ThemeDictionaries></ResourceDictionary></Button.Resources></Button> | Full ControlTemplate copy to change background color | High | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-styles#lightweight-styling |
| 12 | 11 | Styling | Use WinUI theme resources | Consistent Fluent Design colors and brushes | WinUI theme resource keys for colors | Hardcoded hex color values | Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | Background="#FF2D2D30" | High | https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/color |
| 13 | 12 | Styling | Support light and dark themes | Respect user and system theme preference | ThemeResource for theme-adaptive values | Hardcoded colors that break in dark mode | Foreground="{ThemeResource TextFillColorPrimaryBrush}" | Foreground="Black" | High | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-theme-resources |
| 14 | 13 | Styling | Use Fluent Design system | Acrylic Mica Reveal and rounded corners | Built-in Fluent materials and effects | Custom blur and shadow implementations | <Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"/> | Custom CompositionBrush recreating acrylic | Medium | https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials |
| 15 | 14 | Navigation | Use Frame for page navigation | Microsoft.UI.Xaml.Controls.Frame for WinUI 3 page navigation | Frame.Navigate with page types and parameters | Swapping UserControls in a ContentControl | rootFrame.Navigate(typeof(SettingsPage), parameter); | contentArea.Content = new SettingsControl(); | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages |
| 16 | 15 | Navigation | Pass typed navigation parameters | Type-safe data passing between pages | Typed parameter in OnNavigatedTo | Dictionary or string parsing for parameters | protected override void OnNavigatedTo(NavigationEventArgs e) { var item = (Item)e.Parameter; } | var id = int.Parse(e.Parameter.ToString()); | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages |
| 17 | 16 | Navigation | Handle back navigation | WinUI 3 uses NavigationView.BackRequested instead of UWP SystemNavigationManager | Register NavigationView.BackRequested handler and manage back stack | Ignoring back navigation | navigationView.BackRequested += OnBackRequested; | No back button support | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigation-history-and-backwards-navigation |
| 18 | 17 | Navigation | Use deep linking | Handle protocol activation so URIs route to the right page | Register protocol then check ExtendedActivationKind.Protocol on activation | Single entry point ignoring activation context | AppInstance.GetCurrent().GetActivatedEventArgs() with ExtendedActivationKind.Protocol | Ignoring activation arguments | Medium | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation |
| 19 | 18 | Data Binding | Use ObservableCollection for lists | Notifies UI of collection changes | ObservableCollection<T> for bound ItemsSources | List<T> for bound collections | ObservableCollection<Item> Items { get; } = new(); | List<Item> Items { get; set; } = new(); | High | https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth |
| 20 | 19 | Data Binding | Use INotifyPropertyChanged | Enable property change notification for UI updates | INotifyPropertyChanged on ViewModels | Properties without notification | public string Name { get => _name; set => SetProperty(ref _name, value); } | public string Name { get; set; } without notification | High | https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth |
| 21 | 20 | Data Binding | Use function binding with x:Bind | Call methods directly in bindings | x:Bind with method references for transforms | IValueConverter for simple logic | <TextBlock Visibility="{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}"/> | IValueConverter class for bool to visibility | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/function-bindings |
| 22 | 21 | Data Binding | Specify Mode explicitly on x:Bind | x:Bind defaults to OneTime not OneWay | Mode=OneWay or Mode=TwoWay when updates needed | Forgetting Mode and getting stale UI | Text="{x:Bind Title, Mode=OneWay}" | Text="{x:Bind Title}" expecting live updates | High | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension |
| 23 | 22 | Performance | Use ItemsRepeater for custom lists | Virtualizing layout with full control | ItemsRepeater for custom list layouts | ListView for highly customized item layouts | <ItemsRepeater ItemsSource="{x:Bind Items}"><ItemsRepeater.Layout><StackLayout/></ItemsRepeater.Layout></ItemsRepeater> | ListView with heavily modified template and removed chrome | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/items-repeater |
| 24 | 23 | Performance | Use incremental loading | Load data on demand as user scrolls | ISupportIncrementalLoading for large data sets | Loading entire dataset upfront | class IncrementalItemSource : ObservableCollection<Item>, ISupportIncrementalLoading | await LoadAllItems() on page load for 10K items | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth |
| 25 | 24 | Performance | Reduce visual tree complexity | Simpler trees render faster | Minimal nesting in DataTemplates | Deeply nested panels in item templates | <StackPanel><TextBlock/><TextBlock/></StackPanel> | <Grid><Border><StackPanel><Grid>... 8 levels deep | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/performance/optimize-xaml-loading |
| 26 | 25 | Performance | Use compiled bindings over reflection | x:Bind generates code at compile time | x:Bind for hot paths and list items | {Binding} in DataTemplates and frequently updated UI | <DataTemplate x:DataType="local:Item"><TextBlock Text="{x:Bind Name}"/></DataTemplate> | <DataTemplate><TextBlock Text="{Binding Name}"/></DataTemplate> | High | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension |
| 27 | 26 | Threading | Use DispatcherQueue not Dispatcher | WinUI 3 uses Microsoft.UI.Dispatching.DispatcherQueue instead of UWP CoreDispatcher | DispatcherQueue.TryEnqueue for UI thread access | Dispatcher.RunAsync (UWP pattern) | _dispatcherQueue.TryEnqueue(() => Status = "Done"); | Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ...); | High | https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue |
| 28 | 27 | Threading | Use async/await for IO operations | Keep UI responsive during file and network access | async/await for IO so the UI thread keeps rendering | Synchronous IO on UI thread | var data = await httpClient.GetStringAsync(url); | var data = httpClient.GetStringAsync(url).Result; | High | https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/ |
| 29 | 28 | Threading | Use Task.Run for CPU-bound work | Offload compute to thread pool | Task.Run for heavy computation | Long-running CPU work on UI thread | var result = await Task.Run(() => ProcessLargeDataSet()); | var result = ProcessLargeDataSet(); blocking UI | High | https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming |
| 30 | 29 | Packaging | Use WinAppSDK correctly | Windows App SDK provides the runtime | WinAppSDK NuGet package and WindowsAppSDK bootstrapper | Mixing UWP and WinUI 3 APIs | <PackageReference Include="Microsoft.WindowsAppSDK"/> | Using Windows.UI.Xaml instead of Microsoft.UI.Xaml | High | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/ |
| 31 | 30 | Packaging | Use unpackaged or packaged appropriately | Choose deployment model for your scenario | Packaged (MSIX) for Store distribution | Unpackaged without considering API limitations | <WindowsPackageType>None</WindowsPackageType> for unpackaged | Assuming all APIs work in unpackaged mode | Medium | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-packaged-apps |
| 32 | 31 | Packaging | Use single-project MSIX | Simplified packaging for single app | Single-project MSIX packaging | Separate WAP project when not needed | <EnableMsixTooling>true</EnableMsixTooling> in csproj | Separate Windows Application Packaging project for simple apps | Low | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/single-project-msix |
| 33 | 32 | Accessibility | Set AutomationProperties | Enable Narrator and screen reader support | AutomationProperties.Name on all interactive controls | Controls without accessible names | <Button AutomationProperties.Name="Save document"><FontIcon Glyph=""/></Button> | <Button><FontIcon Glyph=""/></Button> without name | High | https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information |
| 34 | 33 | Accessibility | Support keyboard navigation | Full keyboard accessibility | Tab navigation and access keys for all controls | Mouse-only interactions | <Button AccessKey="S" Content="Save"/> | Interactive elements unreachable by keyboard | High | https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-interactions |
| 35 | 34 | Accessibility | Use proper heading levels | Screen readers use headings for navigation | AutomationProperties.HeadingLevel on section headers | All text at same heading level | <TextBlock AutomationProperties.HeadingLevel="Level1" Text="Settings"/> | <TextBlock Style="{StaticResource TitleTextBlockStyle}"/> without heading level | Medium | https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information |
| 36 | 35 | Accessibility | Support high contrast | Respect system high contrast settings | ThemeResource brushes that adapt to high contrast | Hardcoded colors ignoring high contrast | Foreground="{ThemeResource TextFillColorPrimaryBrush}" | Foreground="#333333" | High | https://learn.microsoft.com/en-us/windows/apps/design/accessibility/high-contrast-themes |
| 37 | 36 | Accessibility | Test with Accessibility Insights | Validate accessibility compliance | Accessibility Insights for Windows scanning | Manual accessibility checking only | Run Accessibility Insights FastPass on every page | Ship without accessibility testing | Medium | https://accessibilityinsights.io/ |
| 38 | 37 | Architecture | Use MVVM with CommunityToolkit | Source generators reduce boilerplate | [ObservableProperty] and [RelayCommand] attributes | Manual INotifyPropertyChanged and ICommand | [ObservableProperty] private string _title; [RelayCommand] private void Save() { } | Full INotifyPropertyChanged implementation per property | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 39 | 38 | Architecture | Use dependency injection | Register services with Microsoft.Extensions.DI | IServiceProvider for ViewModel and service resolution | new ViewModel() and new Service() everywhere | services.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>(); | new MainViewModel(new DataService()) in code-behind | Medium | https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview |
| 40 | 39 | Architecture | Use Template Studio patterns | Start with proven architectural templates | Template Studio for WinUI 3 project scaffolding | Blank project with manual setup for complex apps | WinUI 3 Template Studio with MVVM and navigation | Blank App template for production app | Low | https://github.com/microsoft/TemplateStudio |
| 41 | 40 | Architecture | Separate platform from business logic | Keep business logic in .NET Standard or shared libraries | Business logic in separate class library | Business logic mixed with WinUI types | Shared.Core project with no WinUI references | ViewModel importing Microsoft.UI.Xaml types | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 42 | 41 | Architecture | Use WinUI 3 Window management | Proper window lifecycle management | AppWindow API for multi-window scenarios | Single Window assumption in complex apps | var appWindow = this.AppWindow; | Relying solely on MainWindow for everything | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows |
| 43 | 42 | Testing | Unit test ViewModels | Test logic independent of UI framework | xUnit or MSTest on ViewModel properties and commands | Testing through UI only | [Fact] public async Task LoadItems_PopulatesCollection() { await vm.LoadCommand.ExecuteAsync(null); Assert.NotEmpty(vm.Items); } | Manual testing by running the app | Medium | https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices |
| 44 | 43 | Testing | Use WinAppDriver for UI tests | Automated UI testing for WinUI 3 | WinAppDriver or Appium for end-to-end tests | Manual regression testing | var element = session.FindElementByAccessibilityId("SaveButton"); element.Click(); | Click-through manual testing | Medium | https://github.com/microsoft/WinAppDriver |
| 45 | 44 | Testing | Mock WinRT APIs in tests | Isolate tests from platform dependencies | Interface wrappers around WinRT APIs | Direct WinRT API calls in testable code | IFileService wrapping StorageFile APIs | StorageFile.GetFileFromPathAsync directly in ViewModel | Medium | https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices |
| 46 | 45 | Controls | Use NumberBox for numeric input | Built-in numeric entry with validation formatting and spin buttons | NumberBox with Minimum Maximum and SpinButtonPlacementMode | TextBox with manual numeric parsing and validation | <NumberBox Value="{x:Bind Quantity, Mode=TwoWay}" Minimum="0" Maximum="100" SpinButtonPlacementMode="Inline"/> | TextBox with regex validation for numbers | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/number-box |
| 47 | 46 | Controls | Use Expander for collapsible sections | Expandable content area with header for progressive disclosure | Expander for settings groups and optional content | Manual visibility toggling with buttons | <Expander Header="Advanced Settings"><StackPanel><ToggleSwitch Header="Debug mode"/></StackPanel></Expander> | Button toggling StackPanel.Visibility for collapsible content | Low | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/expander |
| 48 | 47 | Controls | Use ProgressRing and ProgressBar for loading | Built-in loading indicators for determinate and indeterminate states | ProgressRing for indeterminate and ProgressBar for determinate progress | Custom spinning animation or text-based loading indicators | <ProgressRing IsActive="{x:Bind IsLoading, Mode=OneWay}"/> | <TextBlock Text="Loading..." Visibility="{x:Bind IsLoading}"/> | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/progress-controls |
| 49 | 48 | Layout | Use VisualStateManager for responsive layouts | Adapt UI layout to window size using adaptive triggers | AdaptiveTrigger with MinWindowWidth for responsive breakpoints | Fixed layouts that break at different window sizes | <VisualState><VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth="720"/></VisualState.StateTriggers><VisualState.Setters><Setter Target="sidebar.Visibility" Value="Visible"/></VisualState.Setters></VisualState> | Fixed two-column layout at all window sizes | High | https://learn.microsoft.com/en-us/windows/apps/develop/ui/layouts-with-xaml |
| 50 | 49 | Lifecycle | Handle app activation and launch | WinUI 3 apps receive activation events for URI and notification launches | Check LaunchActivatedEventArgs in OnLaunched for activation context | Ignoring activation arguments losing deep link context | protected override void OnLaunched(LaunchActivatedEventArgs args) { if (args.Arguments.Contains("settings")) NavigateToSettings(); } | Empty OnLaunched ignoring all activation parameters | Medium | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation |
| 51 | 50 | Lifecycle | Use single instancing with AppInstance | Prevent multiple app windows competing for resources | AppInstance.FindOrRegisterForKey for single-instance enforcement | Multiple instances with conflicting state | var instance = AppInstance.FindOrRegisterForKey("main"); if (!instance.IsCurrent) { await instance.RedirectActivationToAsync(args); } | No instance management allowing duplicate windows | Medium | https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-instancing |
| 52 | 51 | Lifecycle | Save and restore app state | Persist UI state across app restarts for continuity (ApplicationData APIs require packaged apps; unpackaged apps must use file IO or registry) | Save state to local settings on window close or navigation | Losing user context on every restart | ApplicationData.Current.LocalSettings.Values["lastPage"] = currentPage; | No state persistence losing navigation position on restart | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/data/store-and-retrieve-app-data |
| 53 | 52 | Styling | Choose Mica vs Acrylic by surface lifetime | Mica is for long-lived primary surfaces like main windows; Acrylic is for transient light-dismiss surfaces like flyouts and context menus | Mica on root window backgrounds and Acrylic on flyouts and overlays | Acrylic on the main window or Mica on transient flyouts | <Window.SystemBackdrop><MicaBackdrop/></Window.SystemBackdrop> ... <FlyoutPresenter Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"/> | Acrylic on every Window background causing battery and perf cost | Medium | https://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials |
| 54 | 53 | Styling | Set SystemBackdrop on Window directly | WinUI 3 1.3+ exposes Window.SystemBackdrop with MicaBackdrop and DesktopAcrylicBackdrop classes replacing manual MicaController plumbing | Window.SystemBackdrop in XAML or code | Hand-rolled MicaController wiring when SystemBackdrop API is available | <Window.SystemBackdrop><MicaBackdrop Kind="Base"/></Window.SystemBackdrop> | var ctrl = new Microsoft.UI.Composition.SystemBackdrops.MicaController(); ctrl.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>()); | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/system-backdrops |
| 55 | 54 | Architecture | Open secondary windows with new Window | WinUI 3 supports multiple top-level windows; each Window owns an AppWindow accessible via Window.AppWindow for size and position control | new Window().Activate() for secondary windows tracking them in App state | Faking multi-window via main-window content swaps or ContentDialog | var settings = new SettingsWindow(); settings.Activate(); | MainWindow.Content = new SettingsView(); when a separate window is needed | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows |
| 56 | 55 | Architecture | Extend client area into the title bar | Use Window.ExtendsContentIntoTitleBar with SetTitleBar to host custom XAML in the chrome while preserving caption buttons | ExtendsContentIntoTitleBar=true plus SetTitleBar(element) for custom drag region | Hardcoded chrome height or custom caption buttons that break with theme and size changes | this.ExtendsContentIntoTitleBar = true; this.SetTitleBar(AppTitleBar); | Padding="0,32,0,0" to reserve space without SetTitleBar leaves window non-draggable | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/title-bar |
| 57 | 56 | Accessibility | Use KeyboardAccelerator for shortcuts | Map Ctrl/Alt/Shift combinations to commands using KeyboardAccelerator on UIElement | KeyboardAccelerator with Modifiers and Key on relevant controls | Manual KeyDown handlers swallowing shortcuts | <Button Command="{x:Bind SaveCommand}"><Button.KeyboardAccelerators><KeyboardAccelerator Modifiers="Control" Key="S"/></Button.KeyboardAccelerators></Button> | Window.PreviewKeyDown="OnKeyDown" with switch over args.Key | High | https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators |
| 58 | 57 | Styling | Organize resources with merged dictionaries | Share styles and brushes via App.xaml MergedDictionaries instead of duplicating per page | MergedDictionaries in App.xaml for shared styles brushes and colors | Duplicating SolidColorBrush definitions on every page | <Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="Themes/Brushes.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources> | SolidColorBrush Color hardcoded inline on every page | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-resource-dictionary |
| 59 | 58 | Architecture | Use AsyncRelayCommand for async commands | AsyncRelayCommand exposes IsRunning and supports cancellation for IO bound work | [RelayCommand] on async Task method or AsyncRelayCommand for IO work | async void event handlers or fire-and-forget Task.Run from button click | [RelayCommand] private async Task LoadAsync(CancellationToken ct) { Items = await _service.FetchAsync(ct); } | private async void Button_Click(object s, RoutedEventArgs e) { await LoadAsync(); } | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand |
| 60 | 59 | Architecture | Use ILogger for structured logging | Microsoft.Extensions.Logging ILogger<T> with DI for structured leveled logs | ILogger<T> injected via constructor for diagnostic logging | Debug.WriteLine or Console.WriteLine for app diagnostics | public MainViewModel(ILogger<MainViewModel> logger) { _logger = logger; } _logger.LogInformation("Loaded {Count} items", count); | Debug.WriteLine($"Loaded {count} items"); | Medium | https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview |