Files
wehub-resource-sync a6e8bcde90
Check asset sync / cli/assets must match src/ui-ux-pro-max (push) Failing after 4s
Release / Semantic release (push) Has been skipped
Smoke test data / smoke (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 11:58:17 +08:00

25 KiB

1NoCategoryGuidelineDescriptionDoDon'tCode GoodCode BadSeverityDocs URL
21XAMLUse x:Bind for compiled bindingsCompile-time checked bindings with better performancex:Bind for type-safe bindings{Binding} when x:Bind works<TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/><TextBlock Text="{Binding Title}"/>Highhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
32XAMLUse x:Load for deferred loadingOnly instantiate UI elements when neededx:Load=False for hidden panels and dialogsLoading all UI upfront<StackPanel x:Load="{x:Bind ShowDetails, Mode=OneWay}">Always-loaded collapsed panelsMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-load-attribute
43XAMLUse x:Phase for incremental renderingLoad list items in phases for smooth scrollingx:Phase on secondary content in DataTemplatesLoading all template content in phase 0<TextBlock x:Phase="1" Text="{x:Bind Description}"/>All content in single phase for complex templatesMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
54XAMLUse x:DefaultBindModeSet default binding mode for a scopex:DefaultBindMode=OneWay on containers with many bindingsMode=OneWay on every individual x:Bind<StackPanel x:DefaultBindMode="OneWay">Mode=OneWay repeated on 20 bindingsLowhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
65ControlsUse NavigationView for app navigationWinUI 3 NavigationView with Left Top and LeftCompact display modes plus footer itemsNavigationView with PaneDisplayMode for main app shellCustom hamburger menu implementation<NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home"/></NavigationView.MenuItems></NavigationView>Custom SplitView with manual hamburger buttonHighhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview
76ControlsUse InfoBar for status messagesNon-intrusive informational messagesInfoBar for success warning and error messagesCustom styled StackPanel for status<InfoBar IsOpen="True" Severity="Warning" Title="Update available"/><StackPanel Background="Yellow"><TextBlock Text="Warning"/></StackPanel>Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/infobar
87ControlsUse TeachingTip for onboardingContextual tips attached to UI elementsTeachingTip for feature discoveryCustom popup for teaching<TeachingTip Target="{x:Bind SearchBox}" Title="Try searching"/>Custom Popup positioned near target elementLowhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/teaching-tip
98ControlsUse ContentDialog for modal interactionsStandard modal dialog patternContentDialog for confirmations and inputCustom overlay Panel as dialog<ContentDialog Title="Delete?" PrimaryButtonText="Delete" CloseButtonText="Cancel"/>Grid overlay with manual focus trappingMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs
109ControlsUse BreadcrumbBar for hierarchyShow navigation path in hierarchical appsBreadcrumbBar for folder or category navigationManual TextBlock breadcrumb chain<BreadcrumbBar ItemsSource="{x:Bind Breadcrumbs}"/><StackPanel Orientation="Horizontal"><TextBlock Text="Home > Settings > Display"/></StackPanel>Lowhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/breadcrumbbar
1110StylingUse Lightweight StylingOverride control sub-properties via resourcesLightweight styling resource keys to tweak controlsFull 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 colorHighhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-styles#lightweight-styling
1211StylingUse WinUI theme resourcesConsistent Fluent Design colors and brushesWinUI theme resource keys for colorsHardcoded hex color valuesBackground="{ThemeResource CardBackgroundFillColorDefaultBrush}"Background="#FF2D2D30"Highhttps://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/color
1312StylingSupport light and dark themesRespect user and system theme preferenceThemeResource for theme-adaptive valuesHardcoded colors that break in dark modeForeground="{ThemeResource TextFillColorPrimaryBrush}"Foreground="Black"Highhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-theme-resources
1413StylingUse Fluent Design systemAcrylic Mica Reveal and rounded cornersBuilt-in Fluent materials and effectsCustom blur and shadow implementations<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"/>Custom CompositionBrush recreating acrylicMediumhttps://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
1514NavigationUse Frame for page navigationMicrosoft.UI.Xaml.Controls.Frame for WinUI 3 page navigationFrame.Navigate with page types and parametersSwapping UserControls in a ContentControlrootFrame.Navigate(typeof(SettingsPage), parameter);contentArea.Content = new SettingsControl();Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
1615NavigationPass typed navigation parametersType-safe data passing between pagesTyped parameter in OnNavigatedToDictionary or string parsing for parametersprotected override void OnNavigatedTo(NavigationEventArgs e) { var item = (Item)e.Parameter; }var id = int.Parse(e.Parameter.ToString());Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigate-between-two-pages
1716NavigationHandle back navigationWinUI 3 uses NavigationView.BackRequested instead of UWP SystemNavigationManagerRegister NavigationView.BackRequested handler and manage back stackIgnoring back navigationnavigationView.BackRequested += OnBackRequested;No back button supportMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/navigation/navigation-history-and-backwards-navigation
1817NavigationUse deep linkingHandle protocol activation so URIs route to the right pageRegister protocol then check ExtendedActivationKind.Protocol on activationSingle entry point ignoring activation contextAppInstance.GetCurrent().GetActivatedEventArgs() with ExtendedActivationKind.ProtocolIgnoring activation argumentsMediumhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
1918Data BindingUse ObservableCollection for listsNotifies UI of collection changesObservableCollection<T> for bound ItemsSourcesList<T> for bound collectionsObservableCollection<Item> Items { get; } = new();List<Item> Items { get; set; } = new();Highhttps://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
2019Data BindingUse INotifyPropertyChangedEnable property change notification for UI updatesINotifyPropertyChanged on ViewModelsProperties without notificationpublic string Name { get => _name; set => SetProperty(ref _name, value); }public string Name { get; set; } without notificationHighhttps://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
2120Data BindingUse function binding with x:BindCall methods directly in bindingsx:Bind with method references for transformsIValueConverter for simple logic<TextBlock Visibility="{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}"/>IValueConverter class for bool to visibilityMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/data-binding/function-bindings
2221Data BindingSpecify Mode explicitly on x:Bindx:Bind defaults to OneTime not OneWayMode=OneWay or Mode=TwoWay when updates neededForgetting Mode and getting stale UIText="{x:Bind Title, Mode=OneWay}"Text="{x:Bind Title}" expecting live updatesHighhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
2322PerformanceUse ItemsRepeater for custom listsVirtualizing layout with full controlItemsRepeater for custom list layoutsListView for highly customized item layouts<ItemsRepeater ItemsSource="{x:Bind Items}"><ItemsRepeater.Layout><StackLayout/></ItemsRepeater.Layout></ItemsRepeater>ListView with heavily modified template and removed chromeMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/items-repeater
2423PerformanceUse incremental loadingLoad data on demand as user scrollsISupportIncrementalLoading for large data setsLoading entire dataset upfrontclass IncrementalItemSource : ObservableCollection<Item>, ISupportIncrementalLoadingawait LoadAllItems() on page load for 10K itemsMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/data-binding/data-binding-in-depth
2524PerformanceReduce visual tree complexitySimpler trees render fasterMinimal nesting in DataTemplatesDeeply nested panels in item templates<StackPanel><TextBlock/><TextBlock/></StackPanel><Grid><Border><StackPanel><Grid>... 8 levels deepMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/performance/optimize-xaml-loading
2625PerformanceUse compiled bindings over reflectionx:Bind generates code at compile timex: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>Highhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/x-bind-markup-extension
2726ThreadingUse DispatcherQueue not DispatcherWinUI 3 uses Microsoft.UI.Dispatching.DispatcherQueue instead of UWP CoreDispatcherDispatcherQueue.TryEnqueue for UI thread accessDispatcher.RunAsync (UWP pattern)_dispatcherQueue.TryEnqueue(() => Status = "Done");Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ...);Highhttps://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue
2827ThreadingUse async/await for IO operationsKeep UI responsive during file and network accessasync/await for IO so the UI thread keeps renderingSynchronous IO on UI threadvar data = await httpClient.GetStringAsync(url);var data = httpClient.GetStringAsync(url).Result;Highhttps://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
2928ThreadingUse Task.Run for CPU-bound workOffload compute to thread poolTask.Run for heavy computationLong-running CPU work on UI threadvar result = await Task.Run(() => ProcessLargeDataSet());var result = ProcessLargeDataSet(); blocking UIHighhttps://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming
3029PackagingUse WinAppSDK correctlyWindows App SDK provides the runtimeWinAppSDK NuGet package and WindowsAppSDK bootstrapperMixing UWP and WinUI 3 APIs<PackageReference Include="Microsoft.WindowsAppSDK"/>Using Windows.UI.Xaml instead of Microsoft.UI.XamlHighhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/
3130PackagingUse unpackaged or packaged appropriatelyChoose deployment model for your scenarioPackaged (MSIX) for Store distributionUnpackaged without considering API limitations<WindowsPackageType>None</WindowsPackageType> for unpackagedAssuming all APIs work in unpackaged modeMediumhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-packaged-apps
3231PackagingUse single-project MSIXSimplified packaging for single appSingle-project MSIX packagingSeparate WAP project when not needed<EnableMsixTooling>true</EnableMsixTooling> in csprojSeparate Windows Application Packaging project for simple appsLowhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/single-project-msix
3332AccessibilitySet AutomationPropertiesEnable Narrator and screen reader supportAutomationProperties.Name on all interactive controlsControls without accessible names<Button AutomationProperties.Name="Save document"><FontIcon Glyph="&#xE74E;"/></Button><Button><FontIcon Glyph="&#xE74E;"/></Button> without nameHighhttps://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
3433AccessibilitySupport keyboard navigationFull keyboard accessibilityTab navigation and access keys for all controlsMouse-only interactions<Button AccessKey="S" Content="Save"/>Interactive elements unreachable by keyboardHighhttps://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-interactions
3534AccessibilityUse proper heading levelsScreen readers use headings for navigationAutomationProperties.HeadingLevel on section headersAll text at same heading level<TextBlock AutomationProperties.HeadingLevel="Level1" Text="Settings"/><TextBlock Style="{StaticResource TitleTextBlockStyle}"/> without heading levelMediumhttps://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information
3635AccessibilitySupport high contrastRespect system high contrast settingsThemeResource brushes that adapt to high contrastHardcoded colors ignoring high contrastForeground="{ThemeResource TextFillColorPrimaryBrush}"Foreground="#333333"Highhttps://learn.microsoft.com/en-us/windows/apps/design/accessibility/high-contrast-themes
3736AccessibilityTest with Accessibility InsightsValidate accessibility complianceAccessibility Insights for Windows scanningManual accessibility checking onlyRun Accessibility Insights FastPass on every pageShip without accessibility testingMediumhttps://accessibilityinsights.io/
3837ArchitectureUse MVVM with CommunityToolkitSource generators reduce boilerplate[ObservableProperty] and [RelayCommand] attributesManual INotifyPropertyChanged and ICommand[ObservableProperty] private string _title; [RelayCommand] private void Save() { }Full INotifyPropertyChanged implementation per propertyMediumhttps://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
3938ArchitectureUse dependency injectionRegister services with Microsoft.Extensions.DIIServiceProvider for ViewModel and service resolutionnew ViewModel() and new Service() everywhereservices.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>();new MainViewModel(new DataService()) in code-behindMediumhttps://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview
4039ArchitectureUse Template Studio patternsStart with proven architectural templatesTemplate Studio for WinUI 3 project scaffoldingBlank project with manual setup for complex appsWinUI 3 Template Studio with MVVM and navigationBlank App template for production appLowhttps://github.com/microsoft/TemplateStudio
4140ArchitectureSeparate platform from business logicKeep business logic in .NET Standard or shared librariesBusiness logic in separate class libraryBusiness logic mixed with WinUI typesShared.Core project with no WinUI referencesViewModel importing Microsoft.UI.Xaml typesMediumhttps://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
4241ArchitectureUse WinUI 3 Window managementProper window lifecycle managementAppWindow API for multi-window scenariosSingle Window assumption in complex appsvar appWindow = this.AppWindow;Relying solely on MainWindow for everythingMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
4342TestingUnit test ViewModelsTest logic independent of UI frameworkxUnit or MSTest on ViewModel properties and commandsTesting through UI only[Fact] public async Task LoadItems_PopulatesCollection() { await vm.LoadCommand.ExecuteAsync(null); Assert.NotEmpty(vm.Items); }Manual testing by running the appMediumhttps://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
4443TestingUse WinAppDriver for UI testsAutomated UI testing for WinUI 3WinAppDriver or Appium for end-to-end testsManual regression testingvar element = session.FindElementByAccessibilityId("SaveButton"); element.Click();Click-through manual testingMediumhttps://github.com/microsoft/WinAppDriver
4544TestingMock WinRT APIs in testsIsolate tests from platform dependenciesInterface wrappers around WinRT APIsDirect WinRT API calls in testable codeIFileService wrapping StorageFile APIsStorageFile.GetFileFromPathAsync directly in ViewModelMediumhttps://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
4645ControlsUse NumberBox for numeric inputBuilt-in numeric entry with validation formatting and spin buttonsNumberBox with Minimum Maximum and SpinButtonPlacementModeTextBox with manual numeric parsing and validation<NumberBox Value="{x:Bind Quantity, Mode=TwoWay}" Minimum="0" Maximum="100" SpinButtonPlacementMode="Inline"/>TextBox with regex validation for numbersMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/number-box
4746ControlsUse Expander for collapsible sectionsExpandable content area with header for progressive disclosureExpander for settings groups and optional contentManual visibility toggling with buttons<Expander Header="Advanced Settings"><StackPanel><ToggleSwitch Header="Debug mode"/></StackPanel></Expander>Button toggling StackPanel.Visibility for collapsible contentLowhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/expander
4847ControlsUse ProgressRing and ProgressBar for loadingBuilt-in loading indicators for determinate and indeterminate statesProgressRing for indeterminate and ProgressBar for determinate progressCustom spinning animation or text-based loading indicators<ProgressRing IsActive="{x:Bind IsLoading, Mode=OneWay}"/><TextBlock Text="Loading..." Visibility="{x:Bind IsLoading}"/>Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/progress-controls
4948LayoutUse VisualStateManager for responsive layoutsAdapt UI layout to window size using adaptive triggersAdaptiveTrigger with MinWindowWidth for responsive breakpointsFixed 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 sizesHighhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/layouts-with-xaml
5049LifecycleHandle app activation and launchWinUI 3 apps receive activation events for URI and notification launchesCheck LaunchActivatedEventArgs in OnLaunched for activation contextIgnoring activation arguments losing deep link contextprotected override void OnLaunched(LaunchActivatedEventArgs args) { if (args.Arguments.Contains("settings")) NavigateToSettings(); }Empty OnLaunched ignoring all activation parametersMediumhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-rich-activation
5150LifecycleUse single instancing with AppInstancePrevent multiple app windows competing for resourcesAppInstance.FindOrRegisterForKey for single-instance enforcementMultiple instances with conflicting statevar instance = AppInstance.FindOrRegisterForKey("main"); if (!instance.IsCurrent) { await instance.RedirectActivationToAsync(args); }No instance management allowing duplicate windowsMediumhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/applifecycle/applifecycle-instancing
5251LifecycleSave and restore app statePersist 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 navigationLosing user context on every restartApplicationData.Current.LocalSettings.Values["lastPage"] = currentPage;No state persistence losing navigation position on restartMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/data/store-and-retrieve-app-data
5352StylingChoose Mica vs Acrylic by surface lifetimeMica is for long-lived primary surfaces like main windows; Acrylic is for transient light-dismiss surfaces like flyouts and context menusMica on root window backgrounds and Acrylic on flyouts and overlaysAcrylic 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 costMediumhttps://learn.microsoft.com/en-us/windows/apps/design/signature-experiences/materials
5453StylingSet SystemBackdrop on Window directlyWinUI 3 1.3+ exposes Window.SystemBackdrop with MicaBackdrop and DesktopAcrylicBackdrop classes replacing manual MicaController plumbingWindow.SystemBackdrop in XAML or codeHand-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>());Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/system-backdrops
5554ArchitectureOpen secondary windows with new WindowWinUI 3 supports multiple top-level windows; each Window owns an AppWindow accessible via Window.AppWindow for size and position controlnew Window().Activate() for secondary windows tracking them in App stateFaking multi-window via main-window content swaps or ContentDialogvar settings = new SettingsWindow(); settings.Activate();MainWindow.Content = new SettingsView(); when a separate window is neededMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/manage-app-windows
5655ArchitectureExtend client area into the title barUse Window.ExtendsContentIntoTitleBar with SetTitleBar to host custom XAML in the chrome while preserving caption buttonsExtendsContentIntoTitleBar=true plus SetTitleBar(element) for custom drag regionHardcoded chrome height or custom caption buttons that break with theme and size changesthis.ExtendsContentIntoTitleBar = true; this.SetTitleBar(AppTitleBar);Padding="0,32,0,0" to reserve space without SetTitleBar leaves window non-draggableMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/title-bar
5756AccessibilityUse KeyboardAccelerator for shortcutsMap Ctrl/Alt/Shift combinations to commands using KeyboardAccelerator on UIElementKeyboardAccelerator with Modifiers and Key on relevant controlsManual 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.KeyHighhttps://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators
5857StylingOrganize resources with merged dictionariesShare styles and brushes via App.xaml MergedDictionaries instead of duplicating per pageMergedDictionaries in App.xaml for shared styles brushes and colorsDuplicating 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 pageMediumhttps://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-resource-dictionary
5958ArchitectureUse AsyncRelayCommand for async commandsAsyncRelayCommand exposes IsRunning and supports cancellation for IO bound work[RelayCommand] on async Task method or AsyncRelayCommand for IO workasync 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(); }Mediumhttps://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand
6059ArchitectureUse ILogger for structured loggingMicrosoft.Extensions.Logging ILogger<T> with DI for structured leveled logsILogger<T> injected via constructor for diagnostic loggingDebug.WriteLine or Console.WriteLine for app diagnosticspublic MainViewModel(ILogger<MainViewModel> logger) { _logger = logger; } _logger.LogInformation("Loaded {Count} items", count);Debug.WriteLine($"Loaded {count} items");Mediumhttps://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview