Files
Jan Ivar Z. Carlsen 090a405cc0 feat(stacks): add 5 .NET desktop stacks — WPF, WinUI 3, UWP, Avalonia, Uno Platform
Adds WPF, WinUI 3, UWP, Avalonia, and Uno Platform stacks (17 -> 22 total),
each with its own guidelines CSV, registered in core.py and synced to cli/assets.

- New stack CSVs in src/ and cli/assets/data/stacks/
- search.py / core.py registry updated
- smoke-stacks.sh: EXPECTED_STACK_COUNT 21 -> 22 with a smoke-test workflow
- platform template descriptions and README bumped to 22 technology stacks
2026-06-25 21:51:59 +02:00

22 KiB

1NoCategoryGuidelineDescriptionDoDon'tCode GoodCode BadSeverityDocs URL
21XAMLUse x:Bind for compiled bindingsCompile-time validated bindings with better performancex:Bind for type-safe performant bindings{Binding} when x:Bind is available<TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}"/><TextBlock Text="{Binding Name}"/>Highhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
32XAMLUse x:Load for deferred elementsDelay creation of elements until neededx:Load=False for hidden or conditional panelsLoading all UI elements at page load<StackPanel x:Name="AdvancedPanel" x:Load="{x:Bind ShowAdvanced, Mode=OneWay}">Collapsed StackPanel that is always instantiatedMediumhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-load-attribute
43XAMLUse x:Phase for incremental item renderingRender list items in priority phasesx:Phase on secondary content in item DataTemplatesAll template content loaded in one pass<TextBlock x:Phase="1" Text="{x:Bind Description}"/>Complex DataTemplate with no phasingMediumhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-phase-attribute
54XAMLUse x:DefaultBindModeReduce repetitive Mode= declarationsx:DefaultBindMode=OneWay on containersMode=OneWay on every individual x:Bind<StackPanel x:DefaultBindMode="OneWay">Mode=OneWay repeated on every binding in a panelLowhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
65XAMLUse x:DeferLoadStrategy for legacy supportDeferred loading before x:Load was availablex:Load (preferred) or x:DeferLoadStrategy=LazyEagerly loading rarely shown UIx:Load="False" (or x:DeferLoadStrategy="Lazy" on older targets)Always-loaded panels toggled with VisibilityLowhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-deferloadstrategy-attribute
76ControlsUse NavigationView for app shellStandard UWP navigation pattern with hamburger menuNavigationView for top-level navigationCustom SplitView hamburger menu<NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home" Icon="Home"/></NavigationView.MenuItems></NavigationView>Custom SplitView with manual toggle buttonHighhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/navigationview
87ControlsUse CommandBar for app actionsStandard app bar for primary commandsCommandBar with AppBarButtonsCustom StackPanel toolbar<CommandBar><AppBarButton Icon="Save" Label="Save"/></CommandBar><StackPanel Orientation="Horizontal"><Button>Save</Button></StackPanel>Mediumhttps://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/command-bar
98ControlsUse ContentDialog for modalsSystem-styled modal dialogsContentDialog for confirmations and inputCustom popup overlays<ContentDialog Title="Confirm" PrimaryButtonText="Yes" CloseButtonText="No"/>Grid overlay with manual focus trappingMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/dialogs-and-flyouts/dialogs
109ControlsUse AutoSuggestBox for searchBuilt-in search box with suggestionsAutoSuggestBox with QuerySubmitted and SuggestionChosenTextBox with manual suggestion popup<AutoSuggestBox QueryIcon="Find" TextChanged="OnTextChanged" SuggestionChosen="OnChosen" QuerySubmitted="OnQuerySubmitted"/>TextBox with custom Popup and ListBox for suggestionsMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/auto-suggest-box
1110ControlsUse CalendarDatePicker and TimePickerPlatform-consistent date and time selectionBuilt-in date and time pickersCustom date selection controls<CalendarDatePicker Header="Start date"/><TimePicker Header="Time"/>TextBox with date parsing and validationLowhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/date-and-time
1211ControlsUse PersonPicture for user avatarsConsistent avatar display with fallback initialsPersonPicture with DisplayName and ProfilePictureCustom Ellipse with ImageBrush for avatars<PersonPicture DisplayName="Jane Doe" ProfilePicture="{x:Bind AvatarUri}"/><Ellipse><Ellipse.Fill><ImageBrush ImageSource="avatar.png"/></Ellipse.Fill></Ellipse>Lowhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/person-picture
1312StylingUse ThemeResource for adaptive colorsColors that switch with light and dark themeThemeResource for all color referencesHardcoded hex values that break in dark modeForeground="{ThemeResource SystemControlForegroundBaseHighBrush}"Foreground="#000000"Highhttps://learn.microsoft.com/en-us/windows/uwp/design/style/color
1413StylingUse Fluent Design materialsAcrylic translucent material for depthBuilt-in Fluent materials for depth and motionCustom shader effects for blur and reveal<Grid Background="{ThemeResource SystemControlAcrylicWindowBrush}"/>Custom CompositionEffectBrush recreating acrylicMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/style/acrylic
1514StylingUse Lightweight StylingOverride control resource keys for subtle changesLightweight styling resource overridesFull ControlTemplate copy for small tweaks<Button><Button.Resources><SolidColorBrush x:Key="ButtonBackground" Color="Blue"/></Button.Resources></Button>Entire ControlTemplate copied to change backgroundMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles#lightweight-styling
1615StylingUse implicit styles for consistencyTargetType without x:Key applies to all instancesImplicit Style for default control appearanceRepeating Setters on every control instance<Style TargetType="Button"><Setter Property="CornerRadius" Value="4"/></Style>CornerRadius="4" on every Button in the pageMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-styles
1716StylingUse VisualStateManager for visual statesDefine visual states with Setters that change properties when triggeredVisualStateGroup containing VisualStates with Setter targetsToggling Visibility from code-behind on SizeChanged<VisualStateGroup><VisualState x:Name="Wide"><VisualState.Setters><Setter Target="root.Orientation" Value="Horizontal"/></VisualState.Setters></VisualState></VisualStateGroup>SizeChanged handler that flips Orientation in code-behindHighhttps://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
1817NavigationUse Frame for page navigationWindows.UI.Xaml.Controls.Frame for UWP page navigationFrame.Navigate with typed parametersSwapping UserControls manuallyrootFrame.Navigate(typeof(DetailPage), itemId);contentArea.Content = new DetailPage();Mediumhttps://learn.microsoft.com/en-us/windows/uwp/design/basics/navigate-between-two-pages
1918NavigationHandle back button correctlyProvide an in-app Back button styled with NavigationBackButtonNormalStyle and handle SystemNavigationManager.BackRequested for hardware back gamepad B and Tablet-Mode back; also handle CoreDispatcher.AcceleratorKeyActivated for Alt+LeftIn-app NavigationBackButtonNormalStyle button plus SystemNavigationManager.BackRequested handlerRelying on the deprecated title-bar back button (AppViewBackButtonVisibility) or ignoring system back signalsSystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;No back button support on phone or tabletHighhttps://learn.microsoft.com/en-us/windows/uwp/ui-input/back-navigation
2019NavigationSupport deep linking with protocol activationRespond to URI activation and toast tapsOnActivated handler with proper page routingIgnoring activation argumentsprotected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { ... } }Empty OnActivated ignoring URI parametersMediumhttps://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-uri-activation
2120NavigationUse ConnectedAnimations for continuitySmooth transitions between pagesConnectedAnimationService for shared element transitionsAbrupt page transitions with no visual continuityConnectedAnimationService.GetForCurrentView().PrepareToAnimate("image", sourceImage);No transition animation between list and detailLowhttps://learn.microsoft.com/en-us/windows/uwp/design/motion/connected-animation
2221Data BindingImplement INotifyPropertyChangedEnable UI updates on property changesINotifyPropertyChanged on all ViewModelsAuto-properties without notificationpublic string Title { get => _title; set { _title = value; OnPropertyChanged(); } }public string Title { get; set; } expecting UI updatesHighhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
2322Data BindingUse ObservableCollection for listsCollection change notifications for ItemsSourcesObservableCollection<T> for bound listsList<T> for data-bound collectionsObservableCollection<Item> Items { get; } = new();List<Item> Items { get; set; } = new();Highhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
2423Data BindingUse function bindings with x:BindCall static methods directly in markupx:Bind to static converter methodsIValueConverter for trivial transforms<TextBlock Visibility="{x:Bind local:Converters.BoolToVisibility(IsActive), Mode=OneWay}"/>Full IValueConverter class for bool to VisibilityMediumhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/function-bindings
2524Data BindingSpecify Mode on x:Bindx:Bind defaults to OneTime not OneWayMode=OneWay or TwoWay when live updates neededOmitting Mode and getting stale UIText="{x:Bind Title, Mode=OneWay}"Text="{x:Bind Title}" expecting live updatesHighhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
2625Data BindingUse CollectionViewSource for groupingGroup and sort collections declarativelyCollectionViewSource for grouped ListView and GridViewManual grouping logic in code-behind<CollectionViewSource x:Key="GroupedItems" IsSourceGrouped="True" Source="{x:Bind GroupedData}"/>Manual loop building grouped StackPanelsMediumhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
2726PerformanceUse ListView and GridView virtualizationOnly creates containers for visible itemsDefault virtualization in ListView and GridViewSetting ItemsPanel to non-virtualizing panel<ListView ItemsSource="{x:Bind Items}"/> (virtualizes by default)<ListView><ListView.ItemsPanel><ItemsPanelTemplate><StackPanel/></ItemsPanelTemplate></ListView.ItemsPanel></ListView>Highhttps://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview
2827PerformanceUse ISupportIncrementalLoadingLoad data on demand as user scrollsISupportIncrementalLoading for large datasetsLoading entire collection upfrontclass IncrementalSource : ObservableCollection<Item>, ISupportIncrementalLoadingawait LoadAll() loading 50K items at startupMediumhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
2928PerformanceReduce XAML visual tree depthSimpler trees layout and render fasterFlat templates with minimal nestingDeeply nested panels in DataTemplates<StackPanel><TextBlock/><TextBlock/></StackPanel> in item template<Grid><Border><StackPanel><Grid>... 8 levels in item templateMediumhttps://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-xaml-loading
3029PerformanceUse compiled bindings in DataTemplatesx:Bind in templates requires x:DataTypex:DataType on DataTemplate for compiled bindings{Binding} in item templates for large lists<DataTemplate x:DataType="local:Item"><TextBlock Text="{x:Bind Name}"/></DataTemplate><DataTemplate><TextBlock Text="{Binding Name}"/></DataTemplate>Highhttps://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
3130PerformanceProfile with Visual Studio diagnosticsMeasure before optimizingApplication Timeline and Memory Usage toolsGuessing at performance problemsVS Diagnostic Tools > Application TimelineOptimizing without profiling dataMediumhttps://learn.microsoft.com/en-us/visualstudio/profiling/application-timeline
3231ThreadingUse async/await for all IOKeep UI thread responsiveasync/await for file network and database operationsSynchronous IO blocking the UI threadvar file = await StorageFile.GetFileFromPathAsync(path);StorageFile.GetFileFromPathAsync(path).AsTask().Result;Highhttps://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
3332ThreadingUse CoreDispatcher for UI thread accessPost work back to the UI thread from backgroundDispatcher.RunAsync from background threadsTouching UI elements from background threadsawait Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Status = "Done");textBlock.Text = "Done" from Task.RunHighhttps://learn.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher
3433ThreadingOffload CPU work with Task.RunKeep compute-heavy work off UI threadTask.Run for CPU-bound operationsHeavy computation blocking UIvar result = await Task.Run(() => ProcessData(items));var result = ProcessData(items); freezing UIHighhttps://learn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
3534ThreadingUse IProgress for status updatesReport progress from background operationsIProgress<T> for progress reporting to UIPolling shared variables for progressvar progress = new Progress<int>(p => ProgressBar.Value = p); await Task.Run(() => Process(progress));while (!done) { await Task.Delay(100); check shared field; }Mediumhttps://learn.microsoft.com/en-us/dotnet/api/system.progress-1
3635AdaptiveUse AdaptiveTrigger for responsive layoutsMinWindowWidth and MinWindowHeight triggers fire at standard breakpoints (640 small / 1008 medium)AdaptiveTrigger inside VisualState.StateTriggers with the 640 and 1008 breakpointsFixed layouts for a single screen size<VisualState.StateTriggers><AdaptiveTrigger MinWindowWidth="640"/></VisualState.StateTriggers>Single-column layout at all widthsHighhttps://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml
3736AdaptiveDesign for multiple device familiesPhone tablet desktop Xbox and HoloLensDeviceFamily-specific views and resourcesDesktop-only design ignoring other form factorsDeviceFamily-Mobile/MainPage.xaml for phone-specific layoutFixed 1920x1080 layoutMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/layout/screen-sizes-and-breakpoints-for-responsive-design
3837AdaptiveUse RelativePanel for adaptive positioningControls position relative to each otherRelativePanel for layouts that reflow at breakpointsAbsolute positioning or fixed margins<Button RelativePanel.Below="title" RelativePanel.AlignLeftWithPanel="True"/><Button Margin="0,60,0,0"/> calculated from title heightMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/layout/layouts-with-xaml#relativepanel
3938AdaptiveSupport multi-window with secondary viewsOpen detached views with CoreApplication.CreateNewView and ApplicationViewSwitcherCreateNewView and TryShowAsStandaloneAsync for multi-document scenariosSingle-window assumptions when scenarios benefit from secondary viewsvar view = CoreApplication.CreateNewView(); await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { /* set content */ });Modal overlay used for content that should be a separate windowMediumhttps://learn.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views
4039AccessibilitySet AutomationPropertiesEnable Narrator and screen reader supportAutomationProperties.Name on all interactive controlsControls without accessible names<AppBarButton AutomationProperties.Name="Save document" Icon="Save"/><AppBarButton Icon="Save"/> without nameHighhttps://learn.microsoft.com/en-us/windows/uwp/design/accessibility/basic-accessibility-information
4140AccessibilitySupport keyboard and gamepadAll functions reachable without touchTab navigation XYFocus and access keysTouch-only interactions<Button AccessKey="S" XYFocusDown="{x:Bind OtherButton}"/>No keyboard or gamepad supportHighhttps://learn.microsoft.com/en-us/windows/uwp/design/input/keyboard-interactions
4241AccessibilitySupport contrast themesRespect system contrast themes (renamed from high contrast in Windows 11)ThemeResource brushes that adapt to contrast themesHardcoded colors that vanish under contrast themesForeground="{ThemeResource SystemControlForegroundBaseHighBrush}"Foreground="#444444"Highhttps://learn.microsoft.com/en-us/windows/uwp/design/accessibility/high-contrast-themes
4342AccessibilityTest with Narrator and Accessibility InsightsValidate screen reader and automation complianceRegular Narrator walkthrough and Accessibility Insights scanShipping without accessibility testingAccessibility Insights FastPass on every pageNo accessibility testing before releaseMediumhttps://accessibilityinsights.io/
4443ArchitectureUse MVVM patternSeparate View ViewModel and ModelViewModel with INotifyPropertyChanged and ICommandBusiness logic in code-behindViewModel bound via DataContext with commandsMainPage.xaml.cs with database calls and UI logicMediumhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
4544ArchitectureUse Template Studio for scaffoldingProven project templates with navigation and servicesWindows Template Studio for new UWP projectsBlank project with manual boilerplateTemplate Studio with MVVM Toolkit and navigation serviceBlank App template building everything from scratchLowhttps://github.com/microsoft/TemplateStudio
4645ArchitectureUse dependency injectionRegister services for testabilityMicrosoft.Extensions.DI for service resolutionStatic singletons and manual constructionservices.AddTransient<MainViewModel>(); services.AddSingleton<IDataService, DataService>();DataService.Instance or new DataService() everywhereMediumhttps://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
4746ArchitectureKeep platform APIs behind abstractionsIsolate WinRT APIs from business logicInterfaces wrapping StorageFile FilePicker etcDirect WinRT calls in ViewModelsIFileService wrapping FileOpenPicker and StorageFileFileOpenPicker usage directly in ViewModelMediumhttps://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-and-mvvm
4847LifecycleHandle suspend and resumeUWP apps are suspended when not in foregroundSave state in OnSuspending and restore in OnLaunchedIgnoring app lifecycle losing user stateApplication.Current.Suspending += (s, e) => SaveState();No suspend handler losing in-progress form dataHighhttps://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
4948LifecycleUse ExtendedExecutionSession for background workRequest extended time for unfinished operationsExtendedExecutionSession for saving or uploadsAssuming background work completes after suspendvar session = new ExtendedExecutionSession { Reason = ExtendedExecutionReason.SavingData };Long upload with no extended execution that gets killed on suspendMediumhttps://learn.microsoft.com/en-us/windows/uwp/launch-resume/run-minimized-with-extended-execution
5049LifecycleHandle prelaunchApps must opt in to prelaunch via CoreApplication.EnablePrelaunch(true) starting in Windows 10 1607; check LaunchActivatedEventArgs.PrelaunchActivated to skip user-visible workOpt in with EnablePrelaunch and skip heavy init when PrelaunchActivated is truePerforming full initialization or navigating during prelaunchCoreApplication.EnablePrelaunch(true); if (e.PrelaunchActivated) return; // skip heavy initLoading all data and navigating on prelaunchMediumhttps://learn.microsoft.com/en-us/windows/uwp/launch-resume/handle-app-prelaunch
5150TestingUnit test ViewModelsTest logic without UI framework dependenciesxUnit or MSTest on ViewModel methodsTesting only through the running app[Fact] public async Task Load_PopulatesItems() { await vm.LoadAsync(); Assert.NotEmpty(vm.Items); }Manual testing by tapping through the appMediumhttps://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices
5251TestingUse WinAppDriver with Appium for UI testsAutomated UI testing for UWP (Coded UI Test was deprecated in Visual Studio 2019); WinAppDriver v1 is in low-maintenance mode and Appium 2 is the modern directionWinAppDriver with Appium for end-to-end testsManual regression testingsession.FindElementByAccessibilityId("SaveButton").Click();Manual click-through testing before each releaseMediumhttps://github.com/microsoft/WinAppDriver
5352TestingTest on multiple device familiesBehavior varies across phone desktop and XboxTest on device emulators and real hardwareDesktop-only testingTest on Mobile emulator and Xbox dev modeOnly running on local desktopMediumhttps://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/device-portal
5453ArchitecturePrefer WinUI 3 for new projectsUWP is in maintenance mode and Microsoft recommends WinUI 3 and Windows App SDK for new developmentWinUI 3 with Windows App SDK for new desktop appsStarting new projects on UWP when WinUI 3 is availableNew project with Microsoft.WindowsAppSDK and WinUI 3New UWP project for a desktop-only app in 2024+Mediumhttps://learn.microsoft.com/en-us/windows/apps/get-started/
5554ArchitecturePlan migration to Windows App SDKMicrosoft provides migration guides from UWP to WinUI 3Incremental migration using XAML Islands or full port to WinUI 3Ignoring migration path and accumulating UWP technical debtFollow UWP to WinUI 3 migration guide for existing appsContinuing major feature development on UWP without migration planMediumhttps://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/migrate-to-windows-app-sdk/overall-migration-strategy
5655LifecycleUse a deferral when saving async state on suspendSuspending grants only ~5 seconds before the OS may terminate; await work needs SuspendingOperation.GetDeferral and Complete or save returns before it finishesGetDeferral around async save calls and Complete in finallyAsync work that returns the suspending handler before completionasync void OnSuspending(object s, SuspendingEventArgs e) { var d = e.SuspendingOperation.GetDeferral(); try { await SaveAsync(); } finally { d.Complete(); } }async void OnSuspending(object s, SuspendingEventArgs e) { await SaveAsync(); } // handler returns before save completesMediumhttps://learn.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle