27 KiB
27 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | XAML | Use WinUI XAML API surface | Uno implements the WinUI API across platforms | Microsoft.UI.Xaml namespace for all UI code | WPF or Xamarin.Forms namespaces | using Microsoft.UI.Xaml.Controls; | using System.Windows.Controls; or using Xamarin.Forms; | High | https://platform.uno/docs/articles/implemented-views.html |
| 3 | 2 | XAML | Check API implementation status | Not all WinUI APIs are implemented on every platform | Uno API compatibility docs before using new APIs | Assuming all WinUI APIs work everywhere | Check platform.uno/docs for API status | Using unimplemented API and discovering at runtime | High | https://platform.uno/docs/articles/implemented-views.html |
| 4 | 3 | XAML | Use Uno.WinUI not Uno.UI for new projects | Uno.WinUI uses WinUI 3 APIs | Uno.WinUI NuGet packages for new projects | Uno.UI (UWP API surface) for new projects | <Project Sdk="Uno.Sdk"> (Uno.WinUI / WinUI 3 surface implicit) | <PackageReference Include="Uno.UI"/> (legacy UWP API surface) | Medium | https://platform.uno/docs/articles/updating-to-winui3.html |
| 5 | 4 | XAML | Use XAML Hot Reload | Speed up development with live XAML editing | Hot Reload for iterating on layouts | Restarting app for every XAML change | Click Hot Reload button in VS toolbar or save in VS Code/Rider to apply XAML changes | Full rebuild for margin tweak | Medium | https://platform.uno/docs/articles/features/working-with-xaml-hot-reload.html |
| 6 | 5 | Conditional | Use platform-specific XAML | Conditional namespaces for platform-specific UI | xmlns:android xmlns:ios xmlns:wasm for platform XAML | Shared XAML when platforms need different controls | <TextBlock android:Text="Android" ios:Text="iOS" Text="Default"/> | #if in code-behind to set text per platform | Medium | https://platform.uno/docs/articles/platform-specific-xaml.html |
| 7 | 6 | Conditional | Use partial classes for platform code | Separate platform implementations in partial files | Partial class files with platform-specific logic | #if directives in shared code for large blocks | MainPage.iOS.cs MainPage.Android.cs partial class files | #if __IOS__ ... #elif __ANDROID__ ... 100-line blocks in shared file | Medium | https://platform.uno/docs/articles/platform-specific-csharp.html |
| 8 | 7 | Conditional | Use preprocessor symbols correctly | Target correct platforms with defines | __IOS__ __ANDROID__ __WASM__ __DESKTOP__ for platform checks | Inventing custom symbols or checking OS at runtime | #if __ANDROID__ Android-specific code #endif | if (RuntimeInformation.IsOSPlatform(OSPlatform.Android)) for compile-time choice | Medium | https://platform.uno/docs/articles/platform-specific-csharp.html |
| 9 | 8 | Conditional | Minimize platform-specific code | Keep shared code maximized | Abstract platform differences behind interfaces | Duplicating logic across platform files | IDeviceService with per-platform implementation | Same 50 lines copy-pasted into iOS and Android partial classes | High | https://platform.uno/docs/articles/platform-specific-csharp.html |
| 10 | 9 | Navigation | Use Frame-based navigation | Standard WinUI navigation pattern | Frame.Navigate with page types | Manual content swapping | rootFrame.Navigate(typeof(DetailPage), parameter); | contentPresenter.Content = new DetailPage(); | Medium | https://platform.uno/docs/articles/guides/native-frame-nav-tutorial.html |
| 11 | 10 | Navigation | Use Uno.Extensions.Navigation | Type-safe navigation with DI integration | Uno.Extensions navigation for complex apps | Manual Frame management in large apps | navigator.NavigateViewModelAsync<DetailViewModel>(this, data: item); | Frame.Navigate with string parsing everywhere | Medium | https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Navigation/NavigationOverview.html |
| 12 | 11 | Navigation | Handle platform back navigation | SystemNavigationManager.BackRequested works on Android iOS and WASM but is unimplemented on WinAppSDK desktop where calling GetForCurrentView() throws at runtime | Subscribe to BackRequested only on platforms that support it or use Uno.Toolkit NavigationBar for cross-platform back UX | Calling SystemNavigationManager.GetForCurrentView() on WinUI 3 desktop without a guard | #if __ANDROID__ || __IOS__ || __WASM__ SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; #endif | SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; with no platform guard — crashes on Windows desktop | High | https://platform.uno/docs/articles/guides/native-frame-nav-tutorial.html |
| 13 | 12 | Navigation | Use deep linking | Support URI activation across platforms | Handle protocol activation and URI routing | Single entry point ignoring activation | Route URIs to specific pages on activation | Ignoring OnLaunched activation args | Medium | https://platform.uno/docs/articles/features/protocol-activation.html |
| 14 | 13 | Renderers | Understand Skia vs native rendering | Uno offers both rendering approaches | Skia for pixel-perfect cross-platform consistency | Assuming native rendering on all platforms | <TargetFrameworks>net10.0-desktop;net10.0-browserwasm</TargetFrameworks> uses unified Skia Desktop shell | Expecting platform-native controls on Skia targets | High | https://platform.uno/docs/articles/features/using-skia-desktop.html |
| 15 | 14 | Renderers | Use unified net10.0-desktop target | Uno 5.2+ ships a single Skia Desktop shell that auto-selects X11 Win32 or AppKit per OS — Skia.Gtk Skia.Linux.Framebuffer and Skia.WPF heads are deprecated | net10.0-desktop TFM with UnoPlatformHostBuilder for cross-platform desktop | Targeting the legacy Skia.Gtk or Skia.Linux.Framebuffer heads in new projects | <TargetFrameworks>net10.0-desktop</TargetFrameworks> in the Uno.Sdk single project | Per-OS Skia.Gtk Skia.MacOS Skia.Linux.Framebuffer head projects | Medium | https://platform.uno/docs/articles/features/using-skia-desktop.html |
| 16 | 15 | Renderers | Test rendering on each target | Visual differences exist between renderers | Visual testing on each active target platform | Testing only on Windows assuming others match | Screenshot tests on iOS Android WASM and Desktop | Testing only on Windows Desktop | High | https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html |
| 17 | 16 | Renderers | Use platform-native features when needed | Access native APIs through Uno abstractions | Native platform APIs via platform-specific code | Avoiding native features for purity | #if __IOS__ UIKit API call #endif for camera access | Pure shared code that avoids using the camera | Medium | https://platform.uno/docs/articles/platform-specific-csharp.html |
| 18 | 17 | Performance | Optimize WASM bundle size | WebAssembly downloads can be large | IL linker and AOT for smaller WASM bundles | Default settings for production WASM | <WasmShellILLinkerEnabled>true</WasmShellILLinkerEnabled> | Publishing WASM without linker | High | https://platform.uno/docs/articles/features/using-il-linker-webassembly.html |
| 19 | 18 | Performance | Use x:Load for deferred XAML | Defer element creation until needed | x:Load=False for hidden panels and tabs | Loading all UI elements upfront | <StackPanel x:Load="{x:Bind ShowAdvanced}"> | Always-loaded Collapsed panels | Medium | https://platform.uno/docs/articles/features/windows-ui-xaml-xbind.html |
| 20 | 19 | Data Binding | Use x:Bind for compiled bindings | Compiled bindings eliminate runtime reflection — Uno supports x:Bind across iOS Android WASM Skia and Windows targets that compile XAML so prefer it over {Binding} for static well-typed bindings | x:Bind for property and event bindings; reserve {Binding} for runtime-typed DataContext scenarios | {Binding} everywhere when x:Bind would compile | <TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/> | <TextBlock Text="{Binding Title}"/> for a statically known property | High | https://platform.uno/docs/articles/features/windows-ui-xaml-xbind.html |
| 21 | 20 | Performance | Profile per platform | Performance characteristics vary by target | Platform-specific profiling tools | Assuming desktop perf equals mobile | Instruments on iOS and Android Profiler on Android | Profiling only on Windows | Medium | https://platform.uno/docs/articles/guides/profiling-applications.html |
| 22 | 21 | Styling | Use WinUI theme resources | Consistent theming across platforms | ThemeResource for adaptive colors | Hardcoded colors per platform | Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" | Background="#FFFFFF" | High | https://platform.uno/docs/articles/features/working-with-themes.html |
| 23 | 23 | Styling | Use Lightweight Styling | Override control sub-properties via resources | Lightweight styling keys for minor tweaks | Full ControlTemplate for small changes | <Button><Button.Resources><StaticResource x:Key="ButtonBackground" ResourceKey="AccentBrush"/></Button.Resources></Button> | Copying entire ControlTemplate to change one color | Medium | https://platform.uno/docs/articles/external/uno.themes/doc/lightweight-styling.html |
| 24 | 24 | Styling | Test themes on each platform | Theme rendering differs across platforms | Visual theme testing on all targets | Assuming themes look identical everywhere | Screenshot comparison across platforms for themed controls | Theming only tested on Windows | Low | https://platform.uno/docs/articles/features/working-with-themes.html |
| 25 | 25 | Architecture | Use MVVM pattern | Separate view and logic | CommunityToolkit.Mvvm or Prism for MVVM | Code-behind for business logic | [ObservableProperty] public partial string Title { get; set; } [RelayCommand] private void Save() { } | MainPage.xaml.cs with all logic | High | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 26 | 26 | Architecture | Use Uno.Extensions | Official extension libraries for common patterns | Uno.Extensions for DI navigation configuration | Building infrastructure from scratch | Host.CreateDefaultBuilder().UseNavigation().UseConfiguration() | Manual DI and navigation setup | Medium | https://platform.uno/docs/articles/external/uno.extensions/doc/ExtensionsOverview.html |
| 27 | 27 | Architecture | Use dependency injection | Register services for testability | Microsoft.Extensions.DI through Uno.Extensions | Static service locators and singletons | services.AddSingleton<IApiService, ApiService>(); | ApiService.Instance or new ApiService() in ViewModels | Medium | https://platform.uno/docs/articles/external/uno.extensions/doc/Learn/DependencyInjection/DependencyInjectionOverview.html |
| 28 | 28 | Architecture | Share code via class libraries | Maximize code reuse across targets | Business logic in .NET Standard or shared library | Business logic in platform head projects | MyApp.Core class library referenced by all heads | Business logic in MyApp.Wasm.csproj | Medium | https://platform.uno/docs/articles/cross-targeted-libraries.html |
| 29 | 29 | Architecture | Use Uno.Resizetizer for assets | Single source SVG to multi-platform assets | UnoImage for automatic asset generation from SVG | Manual asset export per resolution and platform | <UnoImage Include="Assets/icon.svg" BaseSize="24,24"/> | Manually exporting icon_1x.png icon_2x.png icon_3x.png per platform | Medium | https://platform.uno/docs/articles/external/uno.resizetizer/doc/using-uno-resizetizer.html |
| 30 | 30 | Accessibility | Set AutomationProperties | Enable screen readers across platforms | AutomationProperties.Name on interactive controls | Controls without accessible names | <Button AutomationProperties.Name="Submit form"><SymbolIcon Symbol="Accept"/></Button> | <Button><SymbolIcon Symbol="Accept"/></Button> without name | High | https://platform.uno/docs/articles/features/working-with-accessibility.html |
| 31 | 31 | Accessibility | Test accessibility per platform | Each platform has different assistive tech | Test with VoiceOver TalkBack and Narrator | Testing accessibility on one platform only | VoiceOver on iOS + TalkBack on Android + Narrator on Windows | Only testing with Narrator on Windows | High | https://platform.uno/docs/articles/features/working-with-accessibility.html |
| 32 | 32 | Accessibility | Support platform text scaling | Respect user font size preferences | Dynamic font scaling for all text | Fixed font sizes ignoring accessibility | FontSize="{ThemeResource BodyTextBlockFontSize}" | FontSize="14" everywhere | Medium | https://platform.uno/docs/articles/features/working-with-accessibility.html |
| 33 | 33 | Testing | Unit test ViewModels | Test business logic independently | xUnit or MSTest on shared ViewModel code | UI testing only | [Fact] public void LoadData_SetsItems() { vm.Load(); Assert.NotEmpty(vm.Items); } | Manual testing on each platform | Medium | https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html |
| 34 | 34 | Testing | Use Uno.UITest for integration | Cross-platform UI testing framework | Uno.UITest for automated UI tests across platforms | Manual regression testing | app.WaitForElement("SaveButton"); app.Tap("SaveButton"); | Manual click-through on each platform | Medium | https://platform.uno/docs/articles/external/uno.uitest/doc/using-uno-uitest.html |
| 35 | 35 | WASM | Show an extended splash screen on WASM | WASM bundle download and runtime startup take several seconds on first load — render branded UI immediately so users do not see a blank page (AOT and trimming are covered separately) | Render a splash overlay in wwwroot/index.html that hides on first XAML navigation | Letting the user wait on a blank white page while the runtime boots | index.html: <div id="uno-loading">Loading…</div> hidden via JS interop after first Frame.Navigate | No splash markup in index.html — 5-second blank page on first visit | Medium | https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/runtime-execution-modes.html |
| 36 | 36 | WASM | Use AOT compilation for performance | Ahead-of-time compilation improves runtime speed | AOT for production WASM builds | Interpreter mode in production | <WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode> | Default interpreter mode in production deployment | Medium | https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/runtime-execution-modes.html |
| 37 | 37 | WASM | Handle browser limitations | WASM runs in browser sandbox | Feature detection for browser APIs | Assuming desktop capabilities in browser | [JSImport("globalThis.hasApi")] static partial bool HasApi(); #if __WASM__ if (HasApi()) { ... } #endif | #if __WASM__ StorageFile.GetFileFromPathAsync("C:/data") #endif | Medium | https://platform.uno/docs/articles/platform-specific-csharp.html |
| 38 | 38 | Controls | Use NavigationView for app shell | WinUI NavigationView for consistent navigation across platforms | NavigationView with MenuItems for app navigation | Custom hamburger menu implementation | <NavigationView><NavigationView.MenuItems><NavigationViewItem Content="Home" Icon="Home"/></NavigationView.MenuItems></NavigationView> | Custom SplitView with manual toggle button | High | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/navigationview |
| 39 | 39 | Controls | Use ContentDialog for modal interactions | Cross-platform modal dialogs using WinUI API | ContentDialog for confirmations and input | Custom overlay Panel as dialog | <ContentDialog Title="Confirm" PrimaryButtonText="OK" CloseButtonText="Cancel"/> | Grid overlay with manual focus trapping | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/dialogs-and-flyouts/dialogs |
| 40 | 40 | Controls | Use CommandBar for app actions | Standard command bar with primary and secondary commands | CommandBar with AppBarButtons for toolbar actions | Custom StackPanel toolbar | <CommandBar><AppBarButton Icon="Save" Label="Save"/><AppBarButton Icon="Delete" Label="Delete"/></CommandBar> | <StackPanel Orientation="Horizontal"><Button>Save</Button></StackPanel> | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/command-bar |
| 41 | 41 | Controls | Use ToggleSwitch for boolean settings | Platform-native toggle control for on/off preferences | ToggleSwitch for settings and feature flags | CheckBox for toggle settings | <ToggleSwitch Header="Dark Mode" IsOn="{x:Bind ViewModel.IsDarkMode, Mode=TwoWay}"/> | <CheckBox Content="Enable dark mode"/> | Low | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/toggles |
| 42 | 42 | Data Binding | Implement INotifyPropertyChanged | Enable UI updates when ViewModel properties change | CommunityToolkit.Mvvm [ObservableProperty] for auto-notification | Properties without change notification | [ObservableProperty] public partial string Title { get; set; } | public string Title { get; set; } without notification | High | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 43 | 43 | Data Binding | Use ObservableCollection for bound lists | Collection change notifications for ItemsSources across platforms | ObservableCollection<T> for data-bound lists | List<T> for bound ItemsSources | ObservableCollection<Item> Items { get; } = new(); | List<Item> Items { get; set; } = new(); | High | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 44 | 44 | Lifecycle | Handle app suspension on mobile | iOS and Android may suspend or terminate the app — WinAppSDK desktop does not raise Suspending so use window Closed for desktop save-state | Save state in OnSuspending and restore on activation | Ignoring lifecycle losing user state on mobile | Application.Current.Suspending += (s, e) => { var d = e.SuspendingOperation.GetDeferral(); SaveState(); d.Complete(); }; | No suspend handler losing form data on mobile | High | https://platform.uno/docs/articles/features/windows-ui-xaml-application.html |
| 45 | 45 | Lifecycle | Use Uno.Extensions.Hosting for startup | Structured app initialization with DI and configuration | IHost builder pattern for app startup and service registration | Manual initialization in App constructor | Host.CreateDefaultBuilder().ConfigureServices(s => s.AddSingleton<MainViewModel>()).Build(); | new MainViewModel() in App.xaml.cs constructor | Medium | https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Hosting/HostingOverview.html |
| 46 | 46 | Performance | Use ListView virtualization for large lists | Only renders visible items to reduce memory and layout cost | ListView with default ItemsStackPanel virtualization | ItemsControl or StackPanel for large data sets | <ListView ItemsSource="{x:Bind Items}"/> (virtualizes by default) | <ItemsControl><StackPanel> rendering 5000 items at once | High | https://learn.microsoft.com/en-us/windows/apps/develop/ui/controls/listview-and-gridview |
| 47 | 47 | Accessibility | Support keyboard navigation on desktop | Skia and WinAppSDK targets need full keyboard operability — note TabIndex routing is not fully implemented on every Uno target | AccessKey and KeyboardAccelerator on Skia and WinAppSDK targets | Mouse-only interactions on desktop | <Button AccessKey="S" Content="Save"><Button.KeyboardAccelerators><KeyboardAccelerator Modifiers="Control" Key="S"/></Button.KeyboardAccelerators></Button> | Clickable controls without keyboard support on desktop | High | https://learn.microsoft.com/en-us/windows/apps/develop/input/keyboard-accelerators |
| 48 | 48 | WASM | Use service workers for offline support | Enable PWA capabilities for WASM deployments | Service worker registration for caching and offline mode | Online-only WASM app with no offline fallback | <WasmPWAManifestFile>manifest.webmanifest</WasmPWAManifestFile> | No service worker leaving WASM app unusable offline | Low | https://platform.uno/docs/articles/external/uno.wasm.bootstrap/doc/features-pwa.html |
| 49 | 49 | Performance | Marshal to UI thread with DispatcherQueue | Cross-thread access to UI elements throws — capture the UI DispatcherQueue once and use TryEnqueue to update from background work | DispatcherQueue.GetForCurrentThread().TryEnqueue from background work | Touching UI controls directly from a Task | _dispatcher.TryEnqueue(() => StatusText.Text = "Done"); | await Task.Run(() => StatusText.Text = "Done"); throws on non-UI thread | High | https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.dispatching.dispatcherqueue |
| 50 | 50 | Styling | Merge XamlControlsResources in App.xaml | Required for Fluent control styles to load — without it controls render with no template | Add XamlControlsResources at the top of Application.Resources MergedDictionaries | Skipping the merged dictionary and wondering why Buttons look unstyled | <Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources> | <Application.Resources><SolidColorBrush x:Key="MyBrush" Color="Red"/></Application.Resources> with no XamlControlsResources merged | High | https://platform.uno/docs/articles/features/fluent-styles.html |
| 51 | 51 | Architecture | Use async [RelayCommand] for I/O | AsyncRelayCommand reports CanExecute=false (raising CanExecuteChanged) and exposes IsRunning while the Task is in flight — the bound control is disabled and re-entrancy is prevented by default (AllowConcurrentExecutions=false) | [RelayCommand] on a Task-returning method for awaitable work | async void event handlers calling .Wait() or .Result | [RelayCommand] private async Task LoadAsync() { Items = await _api.GetAsync(); } | public void OnLoadClick(object s, EventArgs e) { LoadAsync().Wait(); } deadlock risk | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand |
| 52 | 52 | Architecture | Use x:Uid for localized strings | WinUI x:Uid resolves UI text from .resw resources at runtime — use it instead of hardcoded strings to support localization across iOS Android WASM and desktop from a single project | x:Uid on every user-facing string with matching .resw entries per language under Strings/{lang}/Resources.resw | Hardcoding language-specific strings into XAML or code-behind | <Button x:Uid="SubmitButton"/> with Strings/en/Resources.resw entry SubmitButton.Content=Submit | <Button Content="Submit"/> hardcoded in XAML | High | https://platform.uno/docs/articles/features/working-with-strings.html |
| 53 | 53 | Architecture | Wire up ILogger via Uno.Extensions.Logging | Cross-platform logging routes to platform-native sinks (OSLog on iOS Console on WASM Debug elsewhere) when configured through the IHost builder | Inject ILogger<T> into ViewModels and services and call UseLogging() on the host builder | Console.WriteLine or platform-specific log APIs scattered across shared code | Host.CreateDefaultBuilder().UseLogging(c => c.SetMinimumLevel(LogLevel.Information)) and ILogger<MainViewModel> via constructor injection | Console.WriteLine("error") in shared code with no platform-aware routing | Medium | https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Logging/LoggingOverview.html |
| 54 | 54 | Performance | Enable PublishAot on net10.0-desktop | Skia Desktop on .NET 10 supports Native AOT for faster cold start and smaller deployments — opt in per-target so debug builds remain fast | <PublishAot>true</PublishAot> in a TFM-conditional PropertyGroup for net10.0-desktop release builds | Enabling PublishAot globally and breaking debug iteration on every TFM | <PropertyGroup Condition="'$(TargetFramework)'=='net10.0-desktop' AND '$(Configuration)'=='Release'"><PublishAot>true</PublishAot></PropertyGroup> | <PublishAot>true</PublishAot> at root with no TFM/Configuration condition | Medium | https://platform.uno/docs/articles/features/using-skia-desktop.html |
| 55 | 55 | Performance | Never block on async with .Result or .Wait() | Blocking on a Task from the UI thread deadlocks because the awaiter cannot resume on the captured SynchronizationContext — always await async APIs through to the event handler | Await async methods all the way up; in libraries call ConfigureAwait(false) to avoid context capture | Calling .Result .Wait() or GetAwaiter().GetResult() on a Task from the UI thread | private async void OnLoadClick(object s, RoutedEventArgs e) { var data = await _api.GetAsync(); Items = data; } | private void OnLoadClick(object s, RoutedEventArgs e) { var data = _api.GetAsync().Result; } // deadlocks on UI thread | High | https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/ |
| 56 | 56 | Styling | Define ThemeDictionaries for Light Dark and HighContrast | Resources placed inside ResourceDictionary.ThemeDictionaries entries are automatically swapped when the system theme changes — required for theme-aware brushes | Wrap brushes in a ThemeDictionaries dictionary keyed by Light Dark and HighContrast in App.xaml or page resources | Defining a single brush at the root and missing dark/high-contrast variants | <ResourceDictionary.ThemeDictionaries><ResourceDictionary x:Key="Light"><SolidColorBrush x:Key="Brand" Color="#005A9E"/></ResourceDictionary><ResourceDictionary x:Key="Dark"><SolidColorBrush x:Key="Brand" Color="#3A96DD"/></ResourceDictionary></ResourceDictionary.ThemeDictionaries> | <SolidColorBrush x:Key="Brand" Color="#005A9E"/> at root with no theme variants | Medium | https://platform.uno/docs/articles/features/working-with-themes.html |
| 57 | 57 | Architecture | Use Uno.Sdk with UnoFeatures | Uno.Sdk is the modern single-project SDK that auto-resolves Uno.WinUI Uno.Toolkit Material and other packages from a UnoFeatures property — declare features by name instead of hand-managing dozens of PackageReferences | Declare features in the csproj via <UnoFeatures>...</UnoFeatures> and let the SDK resolve transitive packages | Hand-adding every Uno.* PackageReference and matching version numbers across packages | <Project Sdk="Uno.Sdk"><PropertyGroup><UnoFeatures>Material;Hosting;Toolkit;Logging;MVVM</UnoFeatures></PropertyGroup></Project> | <PackageReference Include="Uno.WinUI"/><PackageReference Include="Uno.Material.WinUI"/> ... duplicated per feature with mismatched versions | Medium | https://platform.uno/docs/articles/features/using-the-uno-sdk.html |
| 58 | 58 | Lifecycle | Persist desktop window state via Window.Closed | WinAppSDK and Skia desktop heads do not raise Application.Suspending — handle the Window.Closed event (and AppWindow size/position changes) to save user state when desktop apps shut down | Subscribe to MainWindow.Closed and persist any unsaved state before the window is destroyed | Relying on Application.Suspending to fire on desktop targets | m_window.Closed += (s, e) => SaveState(); | Application.Current.Suspending += SaveState; // never fires on WinAppSDK or Skia desktop | Medium | https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.window |
| 59 | 59 | Architecture | Use WinRT.Interop for native window handle on Windows | Calling Win32 APIs from a WinUI Window (file pickers icon embedding etc.) requires the HWND — retrieve it via WinRT.Interop.WindowNative.GetWindowHandle and guard the call so non-Windows targets stay unaffected | GetWindowHandle inside a #if WINDOWS block when you need the HWND | Calling WinRT.Interop in shared code without a platform guard | #if WINDOWS\nvar hWnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow);\n#endif | var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow); // breaks build on iOS Android WASM | Medium | https://learn.microsoft.com/en-us/windows/apps/develop/ui/retrieve-hwnd |