Files
phaichayon bca8d3f7e0 commit
2026-07-02 10:20:45 +07:00

25 KiB

1NoCategoryGuidelineDescriptionDoDon'tCode GoodCode BadSeverityDocs URL
21XAMLUse Avalonia XAML namespaceAvalonia has its own XAML namespace not WPFxmlns= for Avalonia-specific namespaceWPF xmlns or UWP xmlns<Window xmlns="https://github.com/avaloniaui"><Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">Highhttps://docs.avaloniaui.net/docs/fundamentals/avalonia-xaml
32XAMLUse compiled bindings with x:DataTypeEnable compile-time binding validationx:DataType on root or DataTemplate for compiled bindingsReflection-based bindings in production<Window x:DataType="vm:MainViewModel"><TextBlock Text="{Binding Name}"/></Window><Window><TextBlock Text="{Binding Name}"/> without x:DataTypeHighhttps://docs.avaloniaui.net/docs/data-binding/compiled-bindings
43XAMLEnable compiled bindings globallyAvaloniaUseCompiledBindingsByDefault in csproj makes every binding require x:DataType and is required for trim-safe Native AOTAvaloniaUseCompiledBindingsByDefault MSBuild property in csprojRelying on runtime binding resolution<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>No compiled binding enforcementHighhttps://docs.avaloniaui.net/docs/data-binding/compiled-bindings
54XAMLUse #name shorthand for element-to-element bindingsCompiled bindings cannot resolve {Binding ElementName=...} - use the #name shorthand which relies on NameScope lookup#name shorthand referencing x:Name controls in the same NameScopeElementName binding inside a compiled-binding scope<TextBlock Text="{Binding #SearchBox.Text}"/><TextBlock Text="{Binding ElementName=SearchBox, Path=Text}"/> under compiled bindingsMediumhttps://docs.avaloniaui.net/docs/data-binding/compiled-bindings
65StylingUse CSS-like selectorsAvalonia uses selectors not implicit stylesSelectors targeting control types classes and pseudoclassesWPF-style implicit Style with TargetType<Style Selector="Button.primary"><Setter Property="Background" Value="Blue"/></Style><Style TargetType="Button"> without SelectorHighhttps://docs.avaloniaui.net/docs/styling/selectors
76StylingUse pseudoclass selectors for statesTarget control states with colon syntax:pointerover :pressed :focus for interactive statesVisualStateManager or Triggers<Style Selector="Button:pointerover"><Setter Property="Opacity" Value="0.8"/></Style><VisualStateManager> for hover effectsMediumhttps://docs.avaloniaui.net/docs/styling/pseudoclasses
87StylingUse nesting selectorsChild and descendant combinators for scoped styles> for direct child and space for descendantFlat selectors that match too broadly<Style Selector="StackPanel > Button"><Setter Property="Margin" Value="4"/></Style><Style Selector="Button"> that affects all buttons unintentionallyMediumhttps://docs.avaloniaui.net/docs/styling/style-selector-syntax
98StylingUse StyleInclude for modularitySplit styles into separate AXAML filesStyleInclude to import themed resource filesAll styles in a single monolithic App.axaml<StyleInclude Source="/Styles/ButtonStyles.axaml"/>1000+ line App.axaml with all stylesMediumhttps://docs.avaloniaui.net/docs/styling/styles
109StylingUse Fluent or Simple themeBuilt-in Avalonia themesFluentTheme or SimpleTheme as baseCustom theme from scratch<FluentTheme/>Building all control templates manuallyHighhttps://docs.avaloniaui.net/docs/styling/themes
1110StylingUse theme variants for dark modeSwitch between light and darkRequestedThemeVariant for theme switchingHardcoded colors ignoring theme variantsApplication.Current.RequestedThemeVariant = ThemeVariant.Dark;Manually changing every brush for dark modeMediumhttps://docs.avaloniaui.net/docs/styling/themes
1211ControlsUse DataGrid for tabular dataDataGrid is a separate Avalonia.Controls.DataGrid NuGet package and requires its theme StyleInclude in App.axamlDataGrid after adding package and StyleInclude for the matching themeCustom Grid layouts for tabular data or DataGrid without the theme StyleInclude<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False"/> with <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/> in App.axaml<DataGrid/> with no package reference or no StyleInclude (renders unstyled)Mediumhttps://docs.avaloniaui.net/docs/reference/controls/datagrid/
1312ControlsUse TreeView with TreeDataTemplateAvalonia uses TreeDataTemplate for hierarchical data - HierarchicalDataTemplate is WPF onlyTreeDataTemplate inside TreeView.ItemTemplate with ItemsSource pointing at child collectionHierarchicalDataTemplate copied from WPF or nested ItemsControls<TreeView ItemsSource="{Binding Nodes}"><TreeView.ItemTemplate><TreeDataTemplate ItemsSource="{Binding Children}"><TextBlock Text="{Binding Name}"/></TreeDataTemplate></TreeView.ItemTemplate></TreeView><TreeView><TreeView.ItemTemplate><HierarchicalDataTemplate/></TreeView.ItemTemplate></TreeView> // HierarchicalDataTemplate does not exist in AvaloniaHighhttps://docs.avaloniaui.net/docs/reference/controls/treeview-1
1413ControlsUse NativeMenu for platform menusNative menu bar on macOS and desktopNativeMenu for cross-platform menu barCustom menu implementation per platform<NativeMenu.Menu><NativeMenu><NativeMenuItem Header="File"/></NativeMenu></NativeMenu.Menu>Custom menu bar control for each platformMediumhttps://docs.avaloniaui.net/docs/reference/controls/nativemenu
1514Data BindingImplement INotifyPropertyChangedStandard .NET property notificationINotifyPropertyChanged or CommunityToolkit.MvvmProperties without change notification[ObservableProperty] private string _name;public string Name { get; set; } without notificationHighhttps://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
1615Data BindingUse ObservableCollection for listsUI updates on collection changesObservableCollection<T> for bound collectionsList<T> for ItemsSourcesObservableCollection<Item> Items { get; } = new();List<Item> Items { get; set; }Highhttps://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged
1716Data BindingUse binding to named controlsElement-to-element binding with # syntax#ElementName.Property for cross-element bindingCode-behind for element references<TextBlock Text="{Binding #slider.Value, StringFormat='{}{0:F0}'}"/>Code-behind ValueChanged handler to update TextBlockMediumhttps://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
1817Data BindingUse converters or FuncValueConverterTransform data for displayFuncValueConverter for simple inline conversionsComplex IValueConverter classes for trivial transformspublic static FuncValueConverter<bool, IBrush> BoolToColor = new(b => b ? Brushes.Green : Brushes.Red);Full IValueConverter class for bool to colorMediumhttps://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
1918Cross-PlatformUse platform-specific code carefullyIsolate platform code behind abstractionsInterface + platform implementation pattern#if directives scattered through ViewModelsIPlatformService with platform-specific implementations#if WINDOWS ... #elif LINUX ... in ViewModelMediumhttps://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
2019Cross-PlatformTest on all target platformsRendering and behavior varies across platformsCI testing on Windows macOS and LinuxTesting only on development platformGitHub Actions matrix with windows-latest ubuntu-latest macos-latestTesting only on Windows assuming cross-platform worksHighhttps://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/
2120Cross-PlatformHandle platform file pathsPath separators differ across OSPath.Combine and Environment.SpecialFolderHardcoded backslashes or forward slashesPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyApp")@"C:\Users\data\config.json"Mediumhttps://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/dealing-with-platforms
2221Cross-PlatformUse Avalonia asset systemPlatform-agnostic resource loadingavares:// URI scheme for embedded resourcesFile system paths for assets<Image Source="avares://MyApp/Assets/logo.png"/><Image Source="C:/images/logo.png"/>Highhttps://docs.avaloniaui.net/docs/fundamentals/including-assets
2322PerformanceUse virtualization for large listsOnly render visible itemsListBox and ItemsRepeater with virtualizationNon-virtualizing ItemsControl for large lists<ListBox ItemsSource="{Binding LargeList}"/><ItemsControl><StackPanel> for 10K itemsHighhttps://docs.avaloniaui.net/docs/reference/controls/listbox
2423PerformanceAvoid unnecessary bindingsEach binding has overheadBind only properties that changeBinding static labels and headers<TextBlock Text="{Binding DynamicTitle}"/> but static: <TextBlock Text="Settings"/><TextBlock Text="{Binding SettingsLabel}"/> for constant stringLowhttps://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding
2524PerformanceSet bitmap interpolation mode on scaled imagesRenderOptions.BitmapInterpolationMode controls image scaling quality vs cost; default may look aliased on upscaled or downscaled bitmapsRenderOptions.SetBitmapInterpolationMode tuned to the use caseDefault interpolation on scaled images that look blurry or aliasedRenderOptions.SetBitmapInterpolationMode(image, BitmapInterpolationMode.HighQuality);Image scaled with Stretch and no interpolation hint setLowhttps://docs.avaloniaui.net/docs/concepts/image-interpolation
2625PerformanceProfile with Avalonia DevToolsBuilt-in diagnostic toolsDevTools for visual tree and binding inspectionConsole.WriteLine debuggingAttach DevTools in debug mode with F12Print statements to debug layout issuesMediumhttps://docs.avaloniaui.net/docs/guides/implementation-guides/developer-tools
2726ArchitectureUse MVVM with ReactiveUI or CommunityToolkitProven MVVM frameworks for AvaloniaReactiveUI or CommunityToolkit.Mvvm for ViewModelsCode-behind for all logicpublic class MainViewModel : ReactiveObject { }MainWindow.axaml.cs with all business logicHighhttps://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
2827ArchitectureUse ViewLocator patternConvention-based View-ViewModel resolutionViewLocator for automatic view resolutionManual view instantiation and DataContext wiringclass ViewLocator : IDataTemplate { Build(object data) => new MainView(); }new MainView { DataContext = new MainViewModel() } everywhereMediumhttps://docs.avaloniaui.net/docs/data-templates/view-locator
2928ArchitectureUse dependency injectionRegister services in a Microsoft.Extensions.DependencyInjection container during startup before any view is constructed - resolve ViewModels through the provider not via a static ServiceLocatorBuild the ServiceProvider in BuildAvaloniaApp or OnFrameworkInitializationCompleted then resolve ViewModels from itStatic ServiceLocator or new-ing ViewModels inline in code-behindservices.AddSingleton<IDataService, DataService>(); services.AddTransient<MainViewModel>(); var provider = services.BuildServiceProvider(); // wired before windows are createdServiceLocator.Current.GetInstance<IDataService>() called from random ViewModels with no registration orderingMediumhttps://docs.avaloniaui.net/docs/app-development/dependency-injection
3029ArchitectureSeparate Views from ViewModelsKeep UI and logic in separate projectsViewModels in a separate class libraryViewModels in the same project referencing Avalonia typesMyApp.Core (no Avalonia refs) + MyApp.Desktop (Avalonia views)ViewModel importing Avalonia.ControlsMediumhttps://v11.docs.avaloniaui.net/docs/concepts/reactiveui/
3130AccessibilitySet AutomationPropertiesEnable screen reader supportAutomationProperties.Name on interactive controlsControls without accessible names<Button AutomationProperties.Name="Close dialog"><PathIcon Data="..."/></Button><Button><PathIcon/></Button> without accessible nameHighhttps://docs.avaloniaui.net/api/avalonia/automation/automationproperties
3231AccessibilitySupport keyboard navigationFull keyboard operabilityTabIndex and KeyboardNavigation propertiesMouse-only interactions<Button TabIndex="1" Content="Save"/>Clickable controls without keyboard supportHighhttps://docs.avaloniaui.net/docs/input-interaction/keyboard-and-hotkeys
3332AccessibilityUse semantic control typesControls convey meaning to assistive techButton for actions ListBox for selectionTextBlock with PointerPressed as fake button<Button Content="Submit"/><TextBlock PointerPressed="OnSubmitClick" Text="Submit"/>Highhttps://docs.avaloniaui.net/docs/reference/controls/
3433TestingUse Avalonia.Headless for UI testsRun UI tests without display serverAvalonia.Headless for CI-compatible UI testingSkipping UI tests in CI[AvaloniaTest] public void Button_Click_Updates_Label() { ... }UI tests that require a display serverMediumhttps://docs.avaloniaui.net/docs/concepts/headless/
3534TestingUnit test ViewModelsTest business logic independentlyxUnit or NUnit on ViewModel methodsTesting through UI only[Fact] public void AddItem_IncreasesCount() { vm.AddItem(); Assert.Equal(1, vm.Items.Count); }Manual testing by running the appMediumhttps://docs.avaloniaui.net/docs/concepts/headless/
3635TestingTest converters independentlyValue converters contain testable logicUnit tests on Convert and ConvertBackAssuming converters work without tests[Fact] public void BoolToColor_True_ReturnsGreen() { Assert.Equal(Brushes.Green, converter.Convert(true)); }No converter testsLowhttps://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter
3736NavigationUse ReactiveUI routing for navigationIScreen and RoutingState for page navigationReactiveUI RoutingState with IScreen on main ViewModelManual content swapping in code-behindpublic RoutingState Router { get; } = new(); Router.Navigate.Execute(new DetailViewModel());contentControl.Content = new DetailView(); in code-behindMediumhttps://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
3837NavigationUse UserControl for viewsPages and screens should be UserControls hosted in a ContentControlUserControl for each view with RoutedViewHost or ContentControlWindow per page or nested Windows<UserControl x:Class="MyApp.Views.DetailView">new Window() for each page in the appMediumhttps://docs.avaloniaui.net/docs/custom-controls/
3938NavigationUse page transitions for view switchingBuilt-in transitions for smooth navigationCrossFade PageSlide or CompositePageTransition declared as a property elementAbrupt content swaps with no visual continuity<RoutedViewHost><RoutedViewHost.PageTransition><PageSlide Orientation="Horizontal" Duration="0:0:0.3"/></RoutedViewHost.PageTransition></RoutedViewHost>ContentControl with no transition between viewsLowhttps://docs.avaloniaui.net/docs/reference/controls/transitioningcontentcontrol
4039NavigationSupport back navigationMaintain navigation history for complex appsRouter.NavigateBack or custom back stackNo way to return to previous views<Button Command="{Binding Router.NavigateBack}" Content="Back"/>Single-direction navigation with no back supportMediumhttps://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing
4140ControlsUse AutoCompleteBox for searchBuilt-in autocomplete and suggestion controlAutoCompleteBox with FilterMode and ItemsSourceTextBox with manual Popup and ListBox for suggestions<AutoCompleteBox ItemsSource="{Binding Suggestions}" FilterMode="Contains"/>TextBox with custom Popup for autocompleteMediumhttps://docs.avaloniaui.net/docs/reference/controls/autocompletebox
4241ControlsUse TabControl for tabbed interfacesStandard tabbed navigation and content switchingTabControl with TabItem for tabbed layoutsManual toggle buttons swapping content<TabControl><TabItem Header="General"><GeneralView/></TabItem><TabItem Header="Advanced"><AdvancedView/></TabItem></TabControl>ToggleButtons with manual content switching logicMediumhttps://docs.avaloniaui.net/docs/reference/controls/tabcontrol
4342ControlsUse SplitView for master-detailCollapsible pane layout for navigation or panelsSplitView with Pane and Content areasManual Grid with column toggling for sidebar<SplitView IsPaneOpen="{Binding IsPaneOpen}" DisplayMode="Inline"><SplitView.Pane><ListBox/></SplitView.Pane><ContentControl/></SplitView>Grid with manual column width animation for sidebarMediumhttps://docs.avaloniaui.net/docs/reference/controls/splitview
4443ControlsUse Flyout for contextual actionsAttach popup menus and actions to controlsFlyout and MenuFlyout on Button or other controlsCustom Popup positioning and management<Button Content="Options"><Button.Flyout><MenuFlyout><MenuItem Header="Edit"/><MenuItem Header="Delete"/></MenuFlyout></Button.Flyout></Button>Custom Popup with manual open/close and positioningMediumhttps://docs.avaloniaui.net/docs/reference/controls/flyouts
4544LifecycleUse AppBuilder for app configurationConfigure platform features and services at startupAppBuilder with UsePlatformDetect and fluent APIManual platform initializationAppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().StartWithClassicDesktopLifetime(args);Manual platform-specific startup code per OSHighhttps://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
4645LifecycleInitialize MainWindow in OnFrameworkInitializationCompletedOverride OnFrameworkInitializationCompleted on App and check ApplicationLifetime - on desktop cast to IClassicDesktopStyleApplicationLifetime to set MainWindow and ShutdownMode; never create windows in the App constructor before the framework is readyOverride OnFrameworkInitializationCompleted and pattern-match on IClassicDesktopStyleApplicationLifetime for desktop-only setupCreating windows in the App constructor or assuming the same lifetime type on every platformpublic override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow(); desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; } base.OnFrameworkInitializationCompleted(); }public App() { new MainWindow().Show(); } // window created before framework init and ignores lifetime typeHighhttps://docs.avaloniaui.net/docs/fundamentals/application-lifetimes
4746AnimationUse CSS-like keyframe animationsAvalonia supports declarative animations in XAML and codeAnimation with KeyFrame and Setter for property animationsManual timer-based property updates<Border.Transitions><DoubleTransition Property="Opacity" Duration="0:0:0.3"/></Border.Transitions>DispatcherTimer ticking to update Opacity manuallyMediumhttps://docs.avaloniaui.net/docs/graphics-animation/animations
4847AnimationUse Transitions for implicit animationsAutomatic animation when property values changeTransitions collection on controls for smooth changesInstant property changes with no visual feedback<Button.Transitions><TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2"/></Button.Transitions>Direct property set with no transitionLowhttps://docs.avaloniaui.net/docs/graphics-animation/control-transitions
4948PerformanceUse compiled bindings and TrimmerRoots.xml for PublishAotAvalonia 11+ supports Native AOT for self-contained desktop deployments; XAML reflection paths must use compiled bindings or be preserved via TrimmerRoots so trimming does not strip themx:CompileBindings=True on every view plus TrimmerRoots.xml for runtime-resolved typesPublishAot with reflection-based {Binding} markup or trimming without checking warnings<UserControl x:CompileBindings="True" x:DataType="vm:MainViewModel"/> with <PublishAot>true</PublishAot> and TrimmerRoots.xml listing reflected types<PublishAot>true</PublishAot> with default <Binding> markup and no TrimmerRoots configurationMediumhttps://docs.avaloniaui.net/docs/deployment/native-aot
5049ThreadingMarshal cross-thread work to the UI threadAvalonia controls and bound properties are not thread-safe and touching them off the UI thread throws InvalidOperationExceptionDispatcher.UIThread.Post or InvokeAsync to bounce work back to the UI threadDirect property writes from Task.Run or background threadsawait Dispatcher.UIThread.InvokeAsync(() => Status = "Done");Task.Run(() => { Status = "Done"; }); // throws Call from invalid threadHighhttps://docs.avaloniaui.net/docs/app-development/threading
5150CommandsUse AsyncRelayCommand or ReactiveCommand for async workAsync-aware commands disable themselves while running and surface CancellationToken so users cannot double-invoke a long operation[RelayCommand] async Task method or ReactiveCommand.CreateFromTaskasync void event handlers or fire-and-forget Task.Run from a click handler[RelayCommand] private async Task LoadAsync(CancellationToken ct) { await _api.GetAsync(ct); }private async void OnClick(object s, RoutedEventArgs e) { await LongOperation(); }Highhttps://docs.avaloniaui.net/docs/input-interaction/commanding
5251StylingUse DynamicResource for theme-aware brushesResourceDictionary.ThemeDictionaries entries must be looked up via DynamicResource - StaticResource resolves once at load and won't update when the active theme variant changesDynamicResource for brushes and colors that follow the active theme variantHardcoded hex colors or StaticResource for values that should follow theme<Border Background="{DynamicResource SystemControlBackgroundAccentBrush}"/><Border Background="#FF0078D4"/> while the rest of the app honors light/dark variantsMediumhttps://docs.avaloniaui.net/docs/app-development/resource-dictionary
5352InputUse HotKey or KeyBinding for keyboard shortcutsBuilt-in HotKey on ICommandSource and Window.KeyBindings handle modifier keys focus scoping and cross-platform Ctrl/Cmd mappingHotKey on a command-bound control or KeyBinding on the WindowManual KeyDown handlers checking Key and KeyModifiers<Button Command="{Binding SaveCommand}" HotKey="Ctrl+S" Content="Save"/>OnKeyDown checking e.Key == Key.S && e.KeyModifiers == KeyModifiers.ControlMediumhttps://docs.avaloniaui.net/docs/input-interaction/mouse-and-keyboard-shortcuts
5453WindowingCustomize window chrome with ExtendClientAreaToDecorationsHintSet ExtendClientAreaToDecorationsHint to extend content into the title bar area and tag a region with WindowDecorationProperties.ElementRole=TitleBar to keep native drag and maximize behaviorExtendClientAreaToDecorationsHint plus a region tagged ElementRole=TitleBarSystemDecorations=None with hand-rolled PointerPressed dragging in code-behind<Window ExtendClientAreaToDecorationsHint="True"><Border WindowDecorationProperties.ElementRole="TitleBar"/></Window><Window SystemDecorations="None"> with manual BeginMoveDrag from code-behindMediumhttps://docs.avaloniaui.net/docs/app-development/window-management
5554StorageUse TopLevel.StorageProvider for file pickersThe legacy OpenFileDialog/SaveFileDialog APIs are obsolete in Avalonia 11 - use TopLevel.GetTopLevel(this).StorageProvider with OpenFilePickerAsync/SaveFilePickerAsync/OpenFolderPickerAsync which returns IStorageFile/IStorageFolder and works on desktop mobile and browserTopLevel.StorageProvider with OpenFilePickerAsync and FilePickerOpenOptionsOpenFileDialog or SaveFileDialog from older Avalonia samples or copied from WPFvar top = TopLevel.GetTopLevel(this); var files = await top.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = false });var dlg = new OpenFileDialog(); var paths = await dlg.ShowAsync(window); // obsolete APIHighhttps://docs.avaloniaui.net/docs/concepts/services/storage-provider/
5655WindowingUse TrayIcon for system tray iconTrayIcon shows a native system tray/notification-area icon with a NativeMenu - declare it via the Application.TrayIcon.Icons attached property in App.axaml; works on Windows macOS and most Linux desktopsTrayIcon with NativeMenu inside TrayIcon.Icons on the ApplicationCustom borderless window pretending to be a tray icon or per-platform native interop<TrayIcon.Icons><TrayIcons><TrayIcon Icon="/Assets/tray.ico" ToolTipText="MyApp"><TrayIcon.Menu><NativeMenu><NativeMenuItem Header="Show" Command="{Binding ShowCommand}"/></NativeMenu></TrayIcon.Menu></TrayIcon></TrayIcons></TrayIcon.Icons>Hidden Window with custom shell-notification-area P/InvokeMediumhttps://docs.avaloniaui.net/docs/controls/tray-icon
5756Cross-PlatformUse OnPlatform and OnFormFactor markup for per-OS valuesOnPlatform and OnFormFactor markup extensions resolve to a different value per OS or form factor at XAML load time and replace if-statements in code-behind for tweaks like fonts spacing or icon sizesOnPlatform with Default Windows macOS Linux entries directly in the property setterRuntimeInformation.IsOSPlatform branches in code-behind to set XAML properties<TextBlock FontFamily="{OnPlatform Default='Inter', Windows='Segoe UI', macOS='SF Pro Text', Linux='Ubuntu'}"/>if (OperatingSystem.IsWindows()) textBlock.FontFamily = new("Segoe UI"); else if (OperatingSystem.IsMacOS()) ...Mediumhttps://docs.avaloniaui.net/docs/platform-specific-guides/xaml