25 KiB
25 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | XAML | Use Avalonia XAML namespace | Avalonia has its own XAML namespace not WPF | xmlns= for Avalonia-specific namespace | WPF xmlns or UWP xmlns | <Window xmlns="https://github.com/avaloniaui"> | <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | High | https://docs.avaloniaui.net/docs/fundamentals/avalonia-xaml |
| 3 | 2 | XAML | Use compiled bindings with x:DataType | Enable compile-time binding validation | x:DataType on root or DataTemplate for compiled bindings | Reflection-based bindings in production | <Window x:DataType="vm:MainViewModel"><TextBlock Text="{Binding Name}"/></Window> | <Window><TextBlock Text="{Binding Name}"/> without x:DataType | High | https://docs.avaloniaui.net/docs/data-binding/compiled-bindings |
| 4 | 3 | XAML | Enable compiled bindings globally | AvaloniaUseCompiledBindingsByDefault in csproj makes every binding require x:DataType and is required for trim-safe Native AOT | AvaloniaUseCompiledBindingsByDefault MSBuild property in csproj | Relying on runtime binding resolution | <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> | No compiled binding enforcement | High | https://docs.avaloniaui.net/docs/data-binding/compiled-bindings |
| 5 | 4 | XAML | Use #name shorthand for element-to-element bindings | Compiled bindings cannot resolve {Binding ElementName=...} - use the #name shorthand which relies on NameScope lookup | #name shorthand referencing x:Name controls in the same NameScope | ElementName binding inside a compiled-binding scope | <TextBlock Text="{Binding #SearchBox.Text}"/> | <TextBlock Text="{Binding ElementName=SearchBox, Path=Text}"/> under compiled bindings | Medium | https://docs.avaloniaui.net/docs/data-binding/compiled-bindings |
| 6 | 5 | Styling | Use CSS-like selectors | Avalonia uses selectors not implicit styles | Selectors targeting control types classes and pseudoclasses | WPF-style implicit Style with TargetType | <Style Selector="Button.primary"><Setter Property="Background" Value="Blue"/></Style> | <Style TargetType="Button"> without Selector | High | https://docs.avaloniaui.net/docs/styling/selectors |
| 7 | 6 | Styling | Use pseudoclass selectors for states | Target control states with colon syntax | :pointerover :pressed :focus for interactive states | VisualStateManager or Triggers | <Style Selector="Button:pointerover"><Setter Property="Opacity" Value="0.8"/></Style> | <VisualStateManager> for hover effects | Medium | https://docs.avaloniaui.net/docs/styling/pseudoclasses |
| 8 | 7 | Styling | Use nesting selectors | Child and descendant combinators for scoped styles | > for direct child and space for descendant | Flat selectors that match too broadly | <Style Selector="StackPanel > Button"><Setter Property="Margin" Value="4"/></Style> | <Style Selector="Button"> that affects all buttons unintentionally | Medium | https://docs.avaloniaui.net/docs/styling/style-selector-syntax |
| 9 | 8 | Styling | Use StyleInclude for modularity | Split styles into separate AXAML files | StyleInclude to import themed resource files | All styles in a single monolithic App.axaml | <StyleInclude Source="/Styles/ButtonStyles.axaml"/> | 1000+ line App.axaml with all styles | Medium | https://docs.avaloniaui.net/docs/styling/styles |
| 10 | 9 | Styling | Use Fluent or Simple theme | Built-in Avalonia themes | FluentTheme or SimpleTheme as base | Custom theme from scratch | <FluentTheme/> | Building all control templates manually | High | https://docs.avaloniaui.net/docs/styling/themes |
| 11 | 10 | Styling | Use theme variants for dark mode | Switch between light and dark | RequestedThemeVariant for theme switching | Hardcoded colors ignoring theme variants | Application.Current.RequestedThemeVariant = ThemeVariant.Dark; | Manually changing every brush for dark mode | Medium | https://docs.avaloniaui.net/docs/styling/themes |
| 12 | 11 | Controls | Use DataGrid for tabular data | DataGrid is a separate Avalonia.Controls.DataGrid NuGet package and requires its theme StyleInclude in App.axaml | DataGrid after adding package and StyleInclude for the matching theme | Custom 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) | Medium | https://docs.avaloniaui.net/docs/reference/controls/datagrid/ |
| 13 | 12 | Controls | Use TreeView with TreeDataTemplate | Avalonia uses TreeDataTemplate for hierarchical data - HierarchicalDataTemplate is WPF only | TreeDataTemplate inside TreeView.ItemTemplate with ItemsSource pointing at child collection | HierarchicalDataTemplate 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 Avalonia | High | https://docs.avaloniaui.net/docs/reference/controls/treeview-1 |
| 14 | 13 | Controls | Use NativeMenu for platform menus | Native menu bar on macOS and desktop | NativeMenu for cross-platform menu bar | Custom menu implementation per platform | <NativeMenu.Menu><NativeMenu><NativeMenuItem Header="File"/></NativeMenu></NativeMenu.Menu> | Custom menu bar control for each platform | Medium | https://docs.avaloniaui.net/docs/reference/controls/nativemenu |
| 15 | 14 | Data Binding | Implement INotifyPropertyChanged | Standard .NET property notification | INotifyPropertyChanged or CommunityToolkit.Mvvm | Properties without change notification | [ObservableProperty] private string _name; | public string Name { get; set; } without notification | High | https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged |
| 16 | 15 | Data Binding | Use ObservableCollection for lists | UI updates on collection changes | ObservableCollection<T> for bound collections | List<T> for ItemsSources | ObservableCollection<Item> Items { get; } = new(); | List<Item> Items { get; set; } | High | https://docs.avaloniaui.net/docs/data-binding/inotifypropertychanged |
| 17 | 16 | Data Binding | Use binding to named controls | Element-to-element binding with # syntax | #ElementName.Property for cross-element binding | Code-behind for element references | <TextBlock Text="{Binding #slider.Value, StringFormat='{}{0:F0}'}"/> | Code-behind ValueChanged handler to update TextBlock | Medium | https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding |
| 18 | 17 | Data Binding | Use converters or FuncValueConverter | Transform data for display | FuncValueConverter for simple inline conversions | Complex IValueConverter classes for trivial transforms | public static FuncValueConverter<bool, IBrush> BoolToColor = new(b => b ? Brushes.Green : Brushes.Red); | Full IValueConverter class for bool to color | Medium | https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter |
| 19 | 18 | Cross-Platform | Use platform-specific code carefully | Isolate platform code behind abstractions | Interface + platform implementation pattern | #if directives scattered through ViewModels | IPlatformService with platform-specific implementations | #if WINDOWS ... #elif LINUX ... in ViewModel | Medium | https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/ |
| 20 | 19 | Cross-Platform | Test on all target platforms | Rendering and behavior varies across platforms | CI testing on Windows macOS and Linux | Testing only on development platform | GitHub Actions matrix with windows-latest ubuntu-latest macos-latest | Testing only on Windows assuming cross-platform works | High | https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/ |
| 21 | 20 | Cross-Platform | Handle platform file paths | Path separators differ across OS | Path.Combine and Environment.SpecialFolder | Hardcoded backslashes or forward slashes | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyApp") | @"C:\Users\data\config.json" | Medium | https://docs.avaloniaui.net/docs/guides/building-cross-platform-applications/dealing-with-platforms |
| 22 | 21 | Cross-Platform | Use Avalonia asset system | Platform-agnostic resource loading | avares:// URI scheme for embedded resources | File system paths for assets | <Image Source="avares://MyApp/Assets/logo.png"/> | <Image Source="C:/images/logo.png"/> | High | https://docs.avaloniaui.net/docs/fundamentals/including-assets |
| 23 | 22 | Performance | Use virtualization for large lists | Only render visible items | ListBox and ItemsRepeater with virtualization | Non-virtualizing ItemsControl for large lists | <ListBox ItemsSource="{Binding LargeList}"/> | <ItemsControl><StackPanel> for 10K items | High | https://docs.avaloniaui.net/docs/reference/controls/listbox |
| 24 | 23 | Performance | Avoid unnecessary bindings | Each binding has overhead | Bind only properties that change | Binding static labels and headers | <TextBlock Text="{Binding DynamicTitle}"/> but static: <TextBlock Text="Settings"/> | <TextBlock Text="{Binding SettingsLabel}"/> for constant string | Low | https://docs.avaloniaui.net/docs/data-binding/introduction-to-data-binding |
| 25 | 24 | Performance | Set bitmap interpolation mode on scaled images | RenderOptions.BitmapInterpolationMode controls image scaling quality vs cost; default may look aliased on upscaled or downscaled bitmaps | RenderOptions.SetBitmapInterpolationMode tuned to the use case | Default interpolation on scaled images that look blurry or aliased | RenderOptions.SetBitmapInterpolationMode(image, BitmapInterpolationMode.HighQuality); | Image scaled with Stretch and no interpolation hint set | Low | https://docs.avaloniaui.net/docs/concepts/image-interpolation |
| 26 | 25 | Performance | Profile with Avalonia DevTools | Built-in diagnostic tools | DevTools for visual tree and binding inspection | Console.WriteLine debugging | Attach DevTools in debug mode with F12 | Print statements to debug layout issues | Medium | https://docs.avaloniaui.net/docs/guides/implementation-guides/developer-tools |
| 27 | 26 | Architecture | Use MVVM with ReactiveUI or CommunityToolkit | Proven MVVM frameworks for Avalonia | ReactiveUI or CommunityToolkit.Mvvm for ViewModels | Code-behind for all logic | public class MainViewModel : ReactiveObject { } | MainWindow.axaml.cs with all business logic | High | https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/ |
| 28 | 27 | Architecture | Use ViewLocator pattern | Convention-based View-ViewModel resolution | ViewLocator for automatic view resolution | Manual view instantiation and DataContext wiring | class ViewLocator : IDataTemplate { Build(object data) => new MainView(); } | new MainView { DataContext = new MainViewModel() } everywhere | Medium | https://docs.avaloniaui.net/docs/data-templates/view-locator |
| 29 | 28 | Architecture | Use dependency injection | Register services in a Microsoft.Extensions.DependencyInjection container during startup before any view is constructed - resolve ViewModels through the provider not via a static ServiceLocator | Build the ServiceProvider in BuildAvaloniaApp or OnFrameworkInitializationCompleted then resolve ViewModels from it | Static ServiceLocator or new-ing ViewModels inline in code-behind | services.AddSingleton<IDataService, DataService>(); services.AddTransient<MainViewModel>(); var provider = services.BuildServiceProvider(); // wired before windows are created | ServiceLocator.Current.GetInstance<IDataService>() called from random ViewModels with no registration ordering | Medium | https://docs.avaloniaui.net/docs/app-development/dependency-injection |
| 30 | 29 | Architecture | Separate Views from ViewModels | Keep UI and logic in separate projects | ViewModels in a separate class library | ViewModels in the same project referencing Avalonia types | MyApp.Core (no Avalonia refs) + MyApp.Desktop (Avalonia views) | ViewModel importing Avalonia.Controls | Medium | https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/ |
| 31 | 30 | Accessibility | Set AutomationProperties | Enable screen reader support | AutomationProperties.Name on interactive controls | Controls without accessible names | <Button AutomationProperties.Name="Close dialog"><PathIcon Data="..."/></Button> | <Button><PathIcon/></Button> without accessible name | High | https://docs.avaloniaui.net/api/avalonia/automation/automationproperties |
| 32 | 31 | Accessibility | Support keyboard navigation | Full keyboard operability | TabIndex and KeyboardNavigation properties | Mouse-only interactions | <Button TabIndex="1" Content="Save"/> | Clickable controls without keyboard support | High | https://docs.avaloniaui.net/docs/input-interaction/keyboard-and-hotkeys |
| 33 | 32 | Accessibility | Use semantic control types | Controls convey meaning to assistive tech | Button for actions ListBox for selection | TextBlock with PointerPressed as fake button | <Button Content="Submit"/> | <TextBlock PointerPressed="OnSubmitClick" Text="Submit"/> | High | https://docs.avaloniaui.net/docs/reference/controls/ |
| 34 | 33 | Testing | Use Avalonia.Headless for UI tests | Run UI tests without display server | Avalonia.Headless for CI-compatible UI testing | Skipping UI tests in CI | [AvaloniaTest] public void Button_Click_Updates_Label() { ... } | UI tests that require a display server | Medium | https://docs.avaloniaui.net/docs/concepts/headless/ |
| 35 | 34 | Testing | Unit test ViewModels | Test business logic independently | xUnit or NUnit on ViewModel methods | Testing through UI only | [Fact] public void AddItem_IncreasesCount() { vm.AddItem(); Assert.Equal(1, vm.Items.Count); } | Manual testing by running the app | Medium | https://docs.avaloniaui.net/docs/concepts/headless/ |
| 36 | 35 | Testing | Test converters independently | Value converters contain testable logic | Unit tests on Convert and ConvertBack | Assuming converters work without tests | [Fact] public void BoolToColor_True_ReturnsGreen() { Assert.Equal(Brushes.Green, converter.Convert(true)); } | No converter tests | Low | https://docs.avaloniaui.net/docs/data-binding/how-to-create-a-custom-data-binding-converter |
| 37 | 36 | Navigation | Use ReactiveUI routing for navigation | IScreen and RoutingState for page navigation | ReactiveUI RoutingState with IScreen on main ViewModel | Manual content swapping in code-behind | public RoutingState Router { get; } = new(); Router.Navigate.Execute(new DetailViewModel()); | contentControl.Content = new DetailView(); in code-behind | Medium | https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing |
| 38 | 37 | Navigation | Use UserControl for views | Pages and screens should be UserControls hosted in a ContentControl | UserControl for each view with RoutedViewHost or ContentControl | Window per page or nested Windows | <UserControl x:Class="MyApp.Views.DetailView"> | new Window() for each page in the app | Medium | https://docs.avaloniaui.net/docs/custom-controls/ |
| 39 | 38 | Navigation | Use page transitions for view switching | Built-in transitions for smooth navigation | CrossFade PageSlide or CompositePageTransition declared as a property element | Abrupt 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 views | Low | https://docs.avaloniaui.net/docs/reference/controls/transitioningcontentcontrol |
| 40 | 39 | Navigation | Support back navigation | Maintain navigation history for complex apps | Router.NavigateBack or custom back stack | No way to return to previous views | <Button Command="{Binding Router.NavigateBack}" Content="Back"/> | Single-direction navigation with no back support | Medium | https://v11.docs.avaloniaui.net/docs/concepts/reactiveui/routing |
| 41 | 40 | Controls | Use AutoCompleteBox for search | Built-in autocomplete and suggestion control | AutoCompleteBox with FilterMode and ItemsSource | TextBox with manual Popup and ListBox for suggestions | <AutoCompleteBox ItemsSource="{Binding Suggestions}" FilterMode="Contains"/> | TextBox with custom Popup for autocomplete | Medium | https://docs.avaloniaui.net/docs/reference/controls/autocompletebox |
| 42 | 41 | Controls | Use TabControl for tabbed interfaces | Standard tabbed navigation and content switching | TabControl with TabItem for tabbed layouts | Manual toggle buttons swapping content | <TabControl><TabItem Header="General"><GeneralView/></TabItem><TabItem Header="Advanced"><AdvancedView/></TabItem></TabControl> | ToggleButtons with manual content switching logic | Medium | https://docs.avaloniaui.net/docs/reference/controls/tabcontrol |
| 43 | 42 | Controls | Use SplitView for master-detail | Collapsible pane layout for navigation or panels | SplitView with Pane and Content areas | Manual 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 sidebar | Medium | https://docs.avaloniaui.net/docs/reference/controls/splitview |
| 44 | 43 | Controls | Use Flyout for contextual actions | Attach popup menus and actions to controls | Flyout and MenuFlyout on Button or other controls | Custom 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 positioning | Medium | https://docs.avaloniaui.net/docs/reference/controls/flyouts |
| 45 | 44 | Lifecycle | Use AppBuilder for app configuration | Configure platform features and services at startup | AppBuilder with UsePlatformDetect and fluent API | Manual platform initialization | AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().StartWithClassicDesktopLifetime(args); | Manual platform-specific startup code per OS | High | https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes |
| 46 | 45 | Lifecycle | Initialize MainWindow in OnFrameworkInitializationCompleted | Override 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 ready | Override OnFrameworkInitializationCompleted and pattern-match on IClassicDesktopStyleApplicationLifetime for desktop-only setup | Creating windows in the App constructor or assuming the same lifetime type on every platform | public 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 type | High | https://docs.avaloniaui.net/docs/fundamentals/application-lifetimes |
| 47 | 46 | Animation | Use CSS-like keyframe animations | Avalonia supports declarative animations in XAML and code | Animation with KeyFrame and Setter for property animations | Manual timer-based property updates | <Border.Transitions><DoubleTransition Property="Opacity" Duration="0:0:0.3"/></Border.Transitions> | DispatcherTimer ticking to update Opacity manually | Medium | https://docs.avaloniaui.net/docs/graphics-animation/animations |
| 48 | 47 | Animation | Use Transitions for implicit animations | Automatic animation when property values change | Transitions collection on controls for smooth changes | Instant property changes with no visual feedback | <Button.Transitions><TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2"/></Button.Transitions> | Direct property set with no transition | Low | https://docs.avaloniaui.net/docs/graphics-animation/control-transitions |
| 49 | 48 | Performance | Use compiled bindings and TrimmerRoots.xml for PublishAot | Avalonia 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 them | x:CompileBindings=True on every view plus TrimmerRoots.xml for runtime-resolved types | PublishAot 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 configuration | Medium | https://docs.avaloniaui.net/docs/deployment/native-aot |
| 50 | 49 | Threading | Marshal cross-thread work to the UI thread | Avalonia controls and bound properties are not thread-safe and touching them off the UI thread throws InvalidOperationException | Dispatcher.UIThread.Post or InvokeAsync to bounce work back to the UI thread | Direct property writes from Task.Run or background threads | await Dispatcher.UIThread.InvokeAsync(() => Status = "Done"); | Task.Run(() => { Status = "Done"; }); // throws Call from invalid thread | High | https://docs.avaloniaui.net/docs/app-development/threading |
| 51 | 50 | Commands | Use AsyncRelayCommand or ReactiveCommand for async work | Async-aware commands disable themselves while running and surface CancellationToken so users cannot double-invoke a long operation | [RelayCommand] async Task method or ReactiveCommand.CreateFromTask | async 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(); } | High | https://docs.avaloniaui.net/docs/input-interaction/commanding |
| 52 | 51 | Styling | Use DynamicResource for theme-aware brushes | ResourceDictionary.ThemeDictionaries entries must be looked up via DynamicResource - StaticResource resolves once at load and won't update when the active theme variant changes | DynamicResource for brushes and colors that follow the active theme variant | Hardcoded 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 variants | Medium | https://docs.avaloniaui.net/docs/app-development/resource-dictionary |
| 53 | 52 | Input | Use HotKey or KeyBinding for keyboard shortcuts | Built-in HotKey on ICommandSource and Window.KeyBindings handle modifier keys focus scoping and cross-platform Ctrl/Cmd mapping | HotKey on a command-bound control or KeyBinding on the Window | Manual KeyDown handlers checking Key and KeyModifiers | <Button Command="{Binding SaveCommand}" HotKey="Ctrl+S" Content="Save"/> | OnKeyDown checking e.Key == Key.S && e.KeyModifiers == KeyModifiers.Control | Medium | https://docs.avaloniaui.net/docs/input-interaction/mouse-and-keyboard-shortcuts |
| 54 | 53 | Windowing | Customize window chrome with ExtendClientAreaToDecorationsHint | Set ExtendClientAreaToDecorationsHint to extend content into the title bar area and tag a region with WindowDecorationProperties.ElementRole=TitleBar to keep native drag and maximize behavior | ExtendClientAreaToDecorationsHint plus a region tagged ElementRole=TitleBar | SystemDecorations=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-behind | Medium | https://docs.avaloniaui.net/docs/app-development/window-management |
| 55 | 54 | Storage | Use TopLevel.StorageProvider for file pickers | The 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 browser | TopLevel.StorageProvider with OpenFilePickerAsync and FilePickerOpenOptions | OpenFileDialog or SaveFileDialog from older Avalonia samples or copied from WPF | var 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 API | High | https://docs.avaloniaui.net/docs/concepts/services/storage-provider/ |
| 56 | 55 | Windowing | Use TrayIcon for system tray icon | TrayIcon 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 desktops | TrayIcon with NativeMenu inside TrayIcon.Icons on the Application | Custom 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/Invoke | Medium | https://docs.avaloniaui.net/docs/controls/tray-icon |
| 57 | 56 | Cross-Platform | Use OnPlatform and OnFormFactor markup for per-OS values | OnPlatform 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 sizes | OnPlatform with Default Windows macOS Linux entries directly in the property setter | RuntimeInformation.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()) ... | Medium | https://docs.avaloniaui.net/docs/platform-specific-guides/xaml |