mirror of
https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git
synced 2026-07-07 22:54:06 +08:00
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
22 KiB
22 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2 | XAML | Set x:Class on root element | Connects XAML to its code-behind partial class | x:Class on Window UserControl and Page | Missing x:Class or mismatched namespace | <Window x:Class="MyApp.MainWindow"> | <Window> without x:Class | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/ |
| 3 | 3 | XAML | Use x:Name sparingly | Only name elements accessed from code-behind | x:Name when code-behind reference is needed | Naming every element | <TextBox x:Name="SearchBox"/> | x:Name on every control | Low | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/ |
| 4 | 4 | XAML | Prefer attached properties for layout | Grid.Row Grid.Column DockPanel.Dock etc | Attached properties for panel positioning | Margin hacks for alignment | <Button Grid.Row="1" Grid.Column="2"/> | <Button Margin="200,100,0,0"/> | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/attached-properties-overview |
| 5 | 5 | XAML | Use routed events for tree-wide handling | Events bubble up or tunnel down the element tree letting parents handle child events with one handler | Handler at parent using TypeName.EventName syntax with e.Handled=true when consumed | Wiring identical handlers on every child when one parent handler suffices | <StackPanel Button.Click="OnAnyButtonClick"> | Click="OnClick" repeated on every Button under a common parent | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/events/routed-events-overview |
| 6 | 6 | Data Binding | Implement INotifyPropertyChanged | Enable UI updates when properties change | INotifyPropertyChanged on ViewModels | Public properties without notification | public string Name { get => _name; set { if (_name != value) { _name = value; OnPropertyChanged(); } } } | public string Name { get; set; } without notification | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification |
| 7 | 7 | Data Binding | Use ObservableCollection for lists | Notifies UI of add remove and reset | ObservableCollection<T> for bound collections | List<T> or Array for bound ItemsSources | ObservableCollection<Item> Items { get; } = new(); | List<Item> Items { get; set; } = new(); | High | https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1 |
| 8 | 8 | Data Binding | Set DataContext at the right level | Enables binding for the visual subtree | DataContext on Window or root container | DataContext on every child control | <Window DataContext="{Binding Source={StaticResource VM}}"> | Setting DataContext on each TextBlock individually | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/ |
| 9 | 9 | Data Binding | Prefer Binding over code-behind assignments | Declarative binding keeps UI and logic separate | {Binding Path=Name} in XAML | textBlock.Text = viewModel.Name in code-behind | <TextBlock Text="{Binding Name}"/> | Loaded event handler that sets every property | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-binding-overview |
| 10 | 10 | Data Binding | Use UpdateSourceTrigger appropriately | Controls when source updates | PropertyChanged for instant feedback | Default LostFocus when search-as-you-type is needed | Text="{Binding Query, UpdateSourceTrigger=PropertyChanged}" | Text="{Binding Query}" when search-as-you-type needed | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-control-when-the-textbox-text-updates-the-source |
| 11 | 11 | Data Binding | Use IValueConverter for display transforms | Convert data for presentation without changing the model | IValueConverter for bool-to-visibility etc | Visibility properties on ViewModel | <TextBlock Visibility="{Binding IsActive, Converter={StaticResource BoolToVis}}"/> | public Visibility IsActiveVisibility => IsActive ? Visibility.Visible : Visibility.Collapsed; | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-convert-bound-data |
| 12 | 12 | Data Binding | Use INotifyDataErrorInfo for validation | Surface validation errors to the binding system instead of ad-hoc error UI | ObservableValidator with DataAnnotations attributes | Throwing in setters or maintaining separate error properties | public partial class FormVm : ObservableValidator { [ObservableProperty][NotifyDataErrorInfo][Required] private string _email; } | if (string.IsNullOrEmpty(Email)) ErrorMessage = "Required"; | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/observablevalidator |
| 13 | 13 | Layout | Use Grid for complex layouts | Rows and columns with proportional or fixed sizing | Grid with RowDefinitions and ColumnDefinitions | Canvas with absolute positions for forms | <Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions></Grid> | <Canvas><TextBox Canvas.Left="50" Canvas.Top="80"/></Canvas> | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/grid |
| 14 | 14 | Layout | Use StackPanel for linear content | Simple vertical or horizontal stacking | StackPanel for toolbars and simple lists | Grid with single column for linear content | <StackPanel Orientation="Horizontal"><Button/><Button/></StackPanel> | <Grid><Grid.RowDefinitions>..12 Auto rows..</Grid.RowDefinitions></Grid> | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/stackpanel |
| 15 | 15 | Layout | Use DockPanel for docked regions | Dock children to edges with last child filling | DockPanel for shell layouts (menu top sidebar left) | Nested StackPanels to simulate docking | <DockPanel><Menu DockPanel.Dock="Top"/><TreeView DockPanel.Dock="Left"/><Frame/></DockPanel> | Nested StackPanels with fixed widths for shell | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/dockpanel |
| 16 | 16 | Layout | Avoid hardcoded sizes | Use Auto Star and MinWidth/MaxWidth | Proportional sizing with * and Auto | Fixed pixel widths on resizable content | <ColumnDefinition Width="2*"/> | <ColumnDefinition Width="350"/> on main content | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/grid |
| 17 | 17 | Layout | Use ScrollViewer for overflow | Wrap content that may exceed available space | ScrollViewer around long forms or lists | Clipping content without scroll | <ScrollViewer><StackPanel>...long content...</StackPanel></ScrollViewer> | <StackPanel> that clips off-screen items | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/scrollviewer |
| 18 | 18 | Styling | Use Resource Dictionaries | Centralize colors brushes and styles | ResourceDictionary in App.xaml for theme values | Inline colors and font sizes on every element | <SolidColorBrush x:Key="PrimaryBrush" Color="#0078D4"/> | <Button Background="#0078D4"/> repeated everywhere | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/systems/xaml-resources-overview |
| 19 | 19 | Styling | Use pack URIs for embedded resources | Reference embedded images fonts and resource dictionaries via the pack scheme | pack://application:,,, syntax for cross-assembly assets | File-system paths for resources compiled into the assembly | <Image Source="pack://application:,,,/MyApp;component/Resources/icon.png"/> | <Image Source="C:\Resources\icon.png"/> | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/pack-uris-in-wpf |
| 20 | 20 | Styling | Use implicit styles | Apply a Style to all instances of a TargetType | Style with TargetType and no x:Key for defaults | Manually styling every Button instance | <Style TargetType="Button"><Setter Property="Padding" Value="12,6"/></Style> | Padding="12,6" on every Button | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/styles-templates-overview |
| 21 | 21 | Styling | Use explicit styles with x:Key and BasedOn | Named variant styles that inherit from a base via BasedOn | x:Key styles that BasedOn an implicit or named style | Duplicating setters across variants | <Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> | Copy-pasting 10 Setters into a second Style | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/styles-templates-overview |
| 22 | 22 | Styling | Prefer StaticResource over DynamicResource | StaticResource is resolved once and faster | StaticResource for values that do not change at runtime | DynamicResource for static theme values | Background="{StaticResource PrimaryBrush}" | Background="{DynamicResource PrimaryBrush}" when theme never changes | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/systems/xaml-resources-overview |
| 23 | 23 | Styling | Use ControlTemplate for full control | Override default rendering of a control | ControlTemplate when built-in styles are insufficient | Nesting extra panels to hide the default template | <ControlTemplate TargetType="Button"><Border><ContentPresenter/></Border></ControlTemplate> | Wrapping Button in Border to fake a custom look | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/how-to-create-apply-template |
| 24 | 24 | Styling | Use DataTemplate for data presentation | Define how data objects render in ItemsControls | DataTemplate for ListBox ComboBox and ItemsControl items | ToString overrides for display | <DataTemplate DataType="{x:Type local:Person}"><TextBlock Text="{Binding FullName}"/></DataTemplate> | Relying on ToString() in ListBox | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-templating-overview |
| 25 | 25 | Styling | Use Fluent theme on .NET 9+ | ThemeMode applies the Windows 11 Fluent style; values are Light Dark System and None (default Aero2) | ThemeMode on Application or Window | Legacy Aero2 styling for new Windows 11 apps | <Application ThemeMode="System"> | <Application> with default Aero2 look | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/whats-new/net90 |
| 26 | 26 | Commands | Use ICommand for user actions | Decouple UI actions from logic | ICommand implementations (RelayCommand DelegateCommand) | Click event handlers in code-behind | <Button Command="{Binding SaveCommand}"/> | <Button Click="OnSaveClick"/> with logic in code-behind | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview |
| 27 | 27 | Commands | Use CanExecute for enable/disable | Automatically disable controls when action unavailable; call NotifyCanExecuteChanged when state changes | CanExecute returning false to disable buttons | IsEnabled binding to a separate bool | new RelayCommand(Save, () => !IsBusy) | <Button IsEnabled="{Binding IsNotBusy}"/> | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview |
| 28 | 28 | Commands | Use RelayCommand or DelegateCommand | Avoid implementing ICommand from scratch every time | RelayCommand (CommunityToolkit.Mvvm) or DelegateCommand (Prism) | New ICommand class per command | [RelayCommand] private void Save() { } | class SaveCommand : ICommand { ... } for each action | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/relaycommand |
| 29 | 29 | Commands | Use AsyncRelayCommand for async operations | Tracks IsRunning and disables the command while it executes preventing re-entry | AsyncRelayCommand or [RelayCommand] on async Task method | async void event handlers in code-behind | [RelayCommand] private async Task SaveAsync() { await _service.SaveAsync(); } | private async void OnSaveClick(object s, RoutedEventArgs e) { await _service.SaveAsync(); } | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/asyncrelaycommand |
| 30 | 30 | Commands | Use CommandParameter for context | Pass data from the UI element to the command handler | CommandParameter for item-specific actions | Relying on SelectedItem in every command | <Button Command="{Binding DeleteCommand}" CommandParameter="{Binding}"/> | Command handler accessing SelectedItem directly | Low | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview |
| 31 | 31 | Commands | Use InputBindings for keyboard shortcuts | Bind keyboard gestures to commands without manual key handling | KeyBinding inside Window.InputBindings | Custom key handling in PreviewKeyDown | <Window.InputBindings><KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding SaveCommand}"/></Window.InputBindings> | PreviewKeyDown handler checking for Ctrl+S | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/commanding-overview |
| 32 | 32 | Performance | Use VirtualizingStackPanel for large lists | Only creates UI elements for visible items. ListBox/ListView virtualize by default; TreeView requires opt-in | VirtualizingStackPanel.IsVirtualizing=True (set on TreeView; default for ListBox) | Disabling virtualization on long lists | <TreeView VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"/> | <ListBox ScrollViewer.CanContentScroll="False"/> | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/optimizing-performance-controls |
| 33 | 33 | Performance | Freeze Freezable objects | Frozen brushes and geometries skip change tracking | Freeze brushes and pens that do not change | Mutable brushes used as static resources | var brush = new SolidColorBrush(Colors.Blue); brush.Freeze(); | new SolidColorBrush() without Freeze in resources | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/freezable-objects-overview |
| 34 | 34 | Performance | Use DependencyProperty for custom-control binding targets | Binding target properties on custom controls must be DependencyProperties (sources can be plain CLR properties with INPC) | Define DependencyProperty for properties bound TO on a custom control | Plain CLR properties as binding targets on custom controls | public static readonly DependencyProperty NameProperty = ... | public string Name { get; set; } as binding target on custom control | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-properties-overview |
| 35 | 35 | Performance | Use async for long operations | Keep UI thread responsive | async/await with Task.Run for CPU work | Synchronous operations that freeze the UI | await Task.Run(() => HeavyComputation()); | HeavyComputation() on UI thread | High | https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/ |
| 36 | 36 | Performance | Profile with PerfView and Visual Studio | Measure before optimizing | Visual Studio diagnostic tools and PerfView | Guessing at performance bottlenecks | Performance Profiler in Visual Studio (Alt+F2) | Optimize without profiling | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/optimizing-wpf-application-performance |
| 37 | 37 | Threading | Use Dispatcher for UI updates | UI elements can only be accessed from the UI thread | Dispatcher.Invoke or BeginInvoke from background threads | Accessing UI elements from background threads | Application.Current.Dispatcher.Invoke(() => Status = "Done"); | textBlock.Text = "Done" from Task.Run | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/threading-model |
| 38 | 38 | Threading | Prefer async/await over Dispatcher | Modern async code returns to UI context automatically | async/await which resumes on captured SynchronizationContext | Manual Dispatcher.BeginInvoke for every callback | var data = await LoadDataAsync(); Items = data; | Dispatcher.BeginInvoke(() => Items = result) in callback | Medium | https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/ |
| 39 | 39 | Threading | Use Task.Run for CPU-bound work | Offload intensive work from UI thread | Task.Run for compute-bound work | Long-running computations on UI thread | var result = await Task.Run(() => Compute()); | var result = Compute(); on UI thread | High | https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming |
| 40 | 40 | Threading | Report progress from background tasks | Update UI with progress during long operations | IProgress<T> with Task.Run | Polling a shared variable for progress | var progress = new Progress<int>(p => ProgressBar.Value = p); await Task.Run(() => Work(progress)); | while (!done) { Thread.Sleep(100); check shared int; } | Medium | https://learn.microsoft.com/en-us/dotnet/api/system.progress-1 |
| 41 | 41 | Threading | Handle DispatcherUnhandledException | Catch unhandled UI-thread exceptions to log them and prevent the default WPF crash dialog | Subscribe in App.xaml or App.OnStartup and set e.Handled=true after logging | Letting WPF show its default crash dialog and silently shut down | <Application DispatcherUnhandledException="App_OnUnhandledException"> | No global handler so any unhandled exception crashes the app | High | https://learn.microsoft.com/en-us/dotnet/api/system.windows.application.dispatcherunhandledexception |
| 42 | 42 | Accessibility | Set AutomationProperties | Enable screen reader support | AutomationProperties.Name on interactive controls | Controls without automation names | <Button AutomationProperties.Name="Save document"/> | <Button><Image Source="save.png"/></Button> without name | High | https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automationproperties |
| 43 | 43 | Accessibility | Support keyboard navigation | All functionality reachable via keyboard | Tab order and KeyboardNavigation properties | Mouse-only interactions | <StackPanel KeyboardNavigation.TabNavigation="Cycle"> | Click handlers with no keyboard equivalent | High | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/focus-overview |
| 44 | 44 | Accessibility | Support high contrast themes | Respect Windows high contrast settings | SystemColors and SystemFonts resources | Hardcoded colors that disappear in high contrast | Foreground="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" | Foreground="#333333" everywhere | Medium | https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/accessibility-best-practices |
| 45 | 45 | Accessibility | Use appropriate control types | Semantic controls convey role to assistive tech | Button for actions CheckBox for toggles | Styled TextBlock with click handler as fake button | <Button Content="Submit"/> | <TextBlock MouseDown="OnSubmitClick" Text="Submit"/> | High | https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/accessibility-best-practices |
| 46 | 46 | Accessibility | Support DPI scaling | Ensure UI is crisp at all display scale factors | Device-independent units and vector graphics | Pixel-based bitmaps that blur at high DPI | <Path Data="M 10,10 L 20,20"/> or DrawingImage | <Image Source="icon_32x32.png"/> at 200% scaling | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/graphics |
| 47 | 47 | Accessibility | Declare PerMonitorV2 DPI awareness | WPF defaults to System-DPI-aware unless you opt into PerMonitorV2 via app.manifest | app.manifest with dpiAwareness PerMonitorV2 | Default System DPI awareness for Windows 10/11 apps | <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> | No app.manifest leaving app at System DPI | Medium | https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows |
| 48 | 48 | Architecture | Use MVVM pattern | Separate View ViewModel and Model concerns | MVVM with data binding and commands | Logic in code-behind | ViewModel with INotifyPropertyChanged and ICommand | MainWindow.xaml.cs with all business logic | High | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 49 | 49 | Architecture | Override App.OnStartup for app initialization | Wire DI build the host resolve MainWindow and parse command-line args in OnStartup | Override OnStartup when DI or argument parsing is needed | Relying on StartupUri when MainWindow needs constructor injection | protected override void OnStartup(StartupEventArgs e) { _host.Start(); _host.Services.GetRequiredService<MainWindow>().Show(); } | <Application StartupUri="MainWindow.xaml"/> when MainWindow has constructor dependencies | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/application-management-overview |
| 50 | 50 | Architecture | Use dependency injection | Wire Microsoft.Extensions.Hosting Generic Host in App.OnStartup and resolve ViewModels from the container | Generic Host with Microsoft.Extensions.DependencyInjection | new Service() in ViewModel constructors | services.AddTransient<MainViewModel>(); | new MainViewModel(new DataService()) in App.xaml.cs | Medium | https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview |
| 51 | 51 | Architecture | Use CommunityToolkit.Mvvm | Source generators reduce MVVM boilerplate | [ObservableProperty] and [RelayCommand] attributes | Hand-written INotifyPropertyChanged for every property | [ObservableProperty] private string _name; | private string _name; public string Name { get ... set ... OnPropertyChanged ... } | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 52 | 52 | Architecture | Keep code-behind minimal | Code-behind should only contain view-specific logic | View logic like focus management and animations in code-behind | Business logic and data access in code-behind | Loaded handler that sets initial focus | Loaded handler that calls database and populates grid | Medium | https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/ |
| 53 | 53 | Architecture | Use messaging for loose coupling | Communicate between ViewModels without references | WeakReferenceMessenger from CommunityToolkit.Mvvm | Direct ViewModel-to-ViewModel references | WeakReferenceMessenger.Default.Send(new ItemSavedMessage(item)); | MainViewModel.Instance.RefreshItems() from DetailViewModel | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/messenger |
| 54 | 54 | Testing | Unit test ViewModels | Test business logic independent of UI | xUnit or NUnit tests on ViewModel methods and properties | Manual testing through the UI only | [Fact] public void Save_WhenValid_SetsIsBusy() { vm.Save(); Assert.True(vm.IsBusy); } | Clicking buttons in the running app to verify | Medium | https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ |
| 55 | 55 | Testing | Mock services in tests | Isolate ViewModel from external dependencies | Moq or NSubstitute for service interfaces | Real database calls in unit tests | var mock = new Mock<IDataService>(); var vm = new MainViewModel(mock.Object); | new MainViewModel(new SqlDataService()) in tests | Medium | https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices |
| 56 | 56 | Testing | Use UI Automation for integration tests | Automated UI testing with Microsoft UI Automation | FlaUI or Appium 2 (appium-windows-driver) for end-to-end tests | Manual regression testing only | AutomationElement.FindFirst(TreeScope.Children, condition) | Manual click-through testing | Medium | https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview |