
Winui
- 19 installs
- 466 repo stars
- Updated July 25, 2026
- managedcode/dotnet-skills
Helps with ai & agent building tasks.
About
winui is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- winui
- AI & Agent Building
- AI-coding skill
Winui by the numbers
- 19 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #10,571 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/managedcode/dotnet-skills --skill winuiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 19 |
|---|---|
| repo stars | ★ 466 |
| Last updated | July 25, 2026 |
| Repository | managedcode/dotnet-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
WinUI 3 and Windows App SDK
Trigger On
- building native modern Windows desktop UI on WinUI 3
- integrating Windows App SDK features into a .NET app
- deciding between WinUI, WPF, WinForms, and MAUI for Windows work
- implementing MVVM patterns in Windows App SDK applications
Workflow
1. Confirm WinUI is the right choice — use when modern Windows-native UI, Fluent Design, and Windows App SDK capabilities are needed. For cross-platform, consider MAUI instead. 2. Choose packaging model early — packaged (MSIX) vs unpackaged differ materially for deployment, identity, and API access:
<!-- Unpackaged: add to .csproj -->
<WindowsPackageType>None</WindowsPackageType>3. Apply MVVM pattern with the MVVM Toolkit — keep views dumb, logic in ViewModels:
public partial class ProductsViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Product> _products = [];
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
private Product? _selectedProduct;
[RelayCommand(CanExecute = nameof(CanDelete))]
private async Task DeleteAsync()
{
if (SelectedProduct is null) return;
await _productService.DeleteAsync(SelectedProduct.Id);
Products.Remove(SelectedProduct);
}
private bool CanDelete() => SelectedProduct is not null;
}4. Use x:Bind for compiled bindings — better performance and compile-time checking than {Binding}:
<TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/>5. Wire DI through `Host.CreateDefaultBuilder` — register services, ViewModels, and views. Resolve via App.GetService<T>(). 6. Implement navigation service — map ViewModels to Pages by convention. See references/patterns.md for the full pattern. 7. Handle Windows App SDK features — windowing (AppWindow), custom title bar, app lifecycle, notifications. 8. Always set `XamlRoot` when showing ContentDialog — omitting this causes silent failures. 9. Validate on Windows targets — behavior depends on runtime, packaging model, and Windows version.
Current Upstream Notes
- Windows App SDK
2.2.0adds theMicrosoft.Windows.AI.Video.VideoScalerAPI,ApplicationData.GetForUnpackaged(), newXamlBindingHelpervalue setter overloads, andSetter.ValueProperty. - For unpackaged apps, prefer
ApplicationData.GetForUnpackaged()over registry or custom folder conventions when the app needs first-class app data storage. - When upgrading to 2.2.0, retest
RenderTargetBitmap,ScrollView,ThemeSettings, pointer cancellation, sparse-packaged PRI discovery, and Windows ML startup/shutdown paths if the app uses those surfaces.
flowchart LR
A["Choose WinUI"] --> B["Select packaging model"]
B --> C["MVVM + DI setup"]
C --> D["Navigation and views"]
D --> E["Windows App SDK features"]
E --> F["Validate on target runtime"]Key Decisions
| Decision | Guidance |
|---|---|
| Packaged vs unpackaged | Packaged (MSIX) for Store, auto-update, and full API access; unpackaged for simpler deployment |
| x:Bind vs Binding | Always prefer x:Bind — compiled, faster, type-safe |
| MVVM Toolkit attributes | Use [ObservableProperty], [RelayCommand] to eliminate boilerplate |
| Navigation | Convention-based ViewModel→Page mapping via navigation service |
| Theming | Use RequestedTheme on root element; respect system theme by default |
Deliver
- modern Windows UI code with clear platform boundaries
- explicit deployment and packaging assumptions
- MVVM pattern with testable ViewModels
- cleaner interop between shared and Windows-specific layers
Validate
- WinUI is chosen for a real product reason, not defaulted to
- Windows App SDK dependencies are explicit in the project file
- packaging and runtime assumptions are tested on target
- x:Bind is used for compiled bindings throughout
- navigation and ContentDialog both work with correct XamlRoot
- custom title bar renders correctly on Windows 10 and 11
References
- references/patterns.md - WinUI 3 patterns including MVVM, navigation services, DI setup, windowing, theming, dialogs, and lifecycle handling
- references/anti-patterns.md - common WinUI mistakes with explanations and corrections
{
"version": "1.1.0",
"category": "Desktop",
"package_prefix": "Microsoft.WindowsAppSDK"
}
WinUI 3 Anti-Patterns
Common mistakes to avoid when building WinUI 3 applications.
MVVM Violations
Logic in Code-Behind
Problem: Business logic placed directly in XAML code-behind.
// Bad: Logic in code-behind
public sealed partial class OrderPage : Page
{
private async void SubmitButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(CustomerNameTextBox.Text))
{
await ShowError("Customer name is required");
return;
}
var order = new Order
{
CustomerName = CustomerNameTextBox.Text,
Total = decimal.Parse(TotalTextBox.Text)
};
using var client = new HttpClient();
await client.PostAsJsonAsync("https://api.example.com/orders", order);
}
}Solution: Move logic to ViewModel with proper commands.
// Good: Logic in ViewModel
public partial class OrderViewModel : ObservableObject
{
private readonly IOrderService _orderService;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SubmitCommand))]
private string _customerName = string.Empty;
[RelayCommand(CanExecute = nameof(CanSubmit))]
private async Task SubmitAsync()
{
await _orderService.CreateOrderAsync(new Order { CustomerName = CustomerName });
}
private bool CanSubmit() => !string.IsNullOrEmpty(CustomerName);
}Manual Property Change Notifications
Problem: Writing boilerplate INotifyPropertyChanged code.
// Bad: Manual implementation
public class ProductViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName)));
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}Solution: Use MVVM Toolkit source generators.
// Good: MVVM Toolkit
public partial class ProductViewModel : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(DisplayName))]
private string _name = string.Empty;
public string DisplayName => $"Product: {Name}";
}Binding Issues
Using Binding Instead of x:Bind
Problem: Using traditional {Binding} instead of compiled {x:Bind}.
<!-- Bad: Classic binding (runtime, slower) -->
<TextBlock Text="{Binding Path=Title}"/>
<Button Command="{Binding SaveCommand}"/>Solution: Use x:Bind for compile-time binding.
<!-- Good: Compiled binding (faster, type-safe) -->
<TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}"/>
<Button Command="{x:Bind ViewModel.SaveCommand}"/>Missing Mode in x:Bind
Problem: Not specifying binding mode when needed.
<!-- Bad: Default is OneTime, won't update -->
<TextBlock Text="{x:Bind ViewModel.Status}"/>Solution: Specify appropriate mode.
<!-- Good: Updates when property changes -->
<TextBlock Text="{x:Bind ViewModel.Status, Mode=OneWay}"/>
<!-- Good: Two-way for input controls -->
<TextBox Text="{x:Bind ViewModel.Name, Mode=TwoWay}"/>Threading Issues
Blocking the UI Thread
Problem: Performing synchronous I/O on the UI thread.
// Bad: Blocks UI
private void LoadData()
{
var client = new HttpClient();
var response = client.GetAsync("https://api.example.com/data").Result;
var data = response.Content.ReadAsStringAsync().Result;
ProcessData(data);
}Solution: Use async/await properly.
// Good: Non-blocking
private async Task LoadDataAsync()
{
using var client = new HttpClient();
var response = await client.GetAsync("https://api.example.com/data");
var data = await response.Content.ReadAsStringAsync();
ProcessData(data);
}Updating UI from Background Thread
Problem: Modifying UI elements from non-UI thread.
// Bad: Direct UI update from background
Task.Run(() =>
{
var data = LoadExpensiveData();
StatusTextBlock.Text = "Loaded"; // Crashes or undefined behavior
});Solution: Use DispatcherQueue to marshal to UI thread.
// Good: Marshal to UI thread
Task.Run(() =>
{
var data = LoadExpensiveData();
DispatcherQueue.TryEnqueue(() =>
{
StatusTextBlock.Text = "Loaded";
});
});Dialog and Picker Issues
Missing XamlRoot
Problem: Not setting XamlRoot on dialogs and pickers.
// Bad: Missing XamlRoot
var dialog = new ContentDialog
{
Title = "Confirm",
Content = "Are you sure?"
};
await dialog.ShowAsync(); // Throws exceptionSolution: Always set XamlRoot.
// Good: XamlRoot set
var dialog = new ContentDialog
{
Title = "Confirm",
Content = "Are you sure?",
XamlRoot = Content.XamlRoot // Or rootElement.XamlRoot
};
await dialog.ShowAsync();Pickers Without Window Handle
Problem: Using file pickers without initializing with window handle.
// Bad: Missing initialization
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync(); // FailsSolution: Initialize picker with window handle.
// Good: Properly initialized
var picker = new FileOpenPicker();
var hWnd = WindowNative.GetWindowHandle(App.MainWindow);
InitializeWithWindow.Initialize(picker, hWnd);
picker.FileTypeFilter.Add(".txt");
var file = await picker.PickSingleFileAsync();Resource and Styling Issues
Hardcoded Colors and Sizes
Problem: Using hardcoded values instead of resources.
<!-- Bad: Hardcoded values -->
<TextBlock Foreground="#333333" FontSize="14"/>
<Border Background="#0078D4"/>Solution: Use theme resources.
<!-- Good: Theme-aware resources -->
<TextBlock Foreground="{ThemeResource TextFillColorPrimary}"
Style="{StaticResource BodyTextBlockStyle}"/>
<Border Background="{ThemeResource AccentFillColorDefaultBrush}"/>Not Supporting Theme Changes
Problem: App doesn't respond to system theme changes.
// Bad: Fixed theme
rootElement.RequestedTheme = ElementTheme.Light;Solution: Support theme switching and system theme.
// Good: Respect user/system preference
public void ApplyTheme(ElementTheme theme)
{
if (Content is FrameworkElement root)
{
root.RequestedTheme = theme; // Default follows system
}
}List and Collection Issues
Not Virtualizing Large Lists
Problem: Loading all items without virtualization.
<!-- Bad: No virtualization, loads all items -->
<StackPanel>
<ItemsControl ItemsSource="{x:Bind ViewModel.LargeCollection}">
<!-- All items created immediately -->
</ItemsControl>
</StackPanel>Solution: Use virtualizing panels.
<!-- Good: Virtualized list -->
<ListView ItemsSource="{x:Bind ViewModel.LargeCollection, Mode=OneWay}"
VirtualizingStackPanel.VirtualizationMode="Recycling"/>Replacing Entire Collection
Problem: Replacing collection instead of updating items.
// Bad: Causes full UI refresh
Items = new ObservableCollection<Item>(await _service.GetItemsAsync());Solution: Update items incrementally when possible.
// Good: Incremental update for better UX
var newItems = await _service.GetItemsAsync();
foreach (var item in newItems.Except(Items))
{
Items.Add(item);
}
foreach (var item in Items.Except(newItems).ToList())
{
Items.Remove(item);
}Navigation Issues
Tightly Coupled Navigation
Problem: Direct Frame access scattered throughout code.
// Bad: Direct coupling to Frame
public sealed partial class ProductPage : Page
{
private void GoToDetails(Product product)
{
Frame.Navigate(typeof(ProductDetailPage), product.Id);
}
}Solution: Use a navigation service.
// Good: Decoupled via service
public partial class ProductViewModel : ObservableObject
{
private readonly INavigationService _navigation;
[RelayCommand]
private void GoToDetails(Product product)
{
_navigation.NavigateTo<ProductDetailViewModel>(product.Id);
}
}Not Handling Back Navigation
Problem: Ignoring back navigation and history.
// Bad: No back navigation supportSolution: Handle system back button and navigation history.
// Good: Handle back navigation
public MainWindow()
{
InitializeComponent();
var navigationView = FindName("NavigationViewControl") as NavigationView;
navigationView.BackRequested += (s, e) =>
{
if (_navigationService.CanGoBack)
{
_navigationService.GoBack();
}
};
}Packaging and Deployment Issues
Ignoring Packaging Choice Impact
Problem: Assuming packaged and unpackaged apps work identically.
// Bad: Using packaged-only API in unpackaged app
var localFolder = ApplicationData.Current.LocalFolder; // Throws in unpackagedSolution: Check packaging state and use appropriate APIs.
// Good: Handle both scenarios
public string GetStorageFolder()
{
if (IsPackaged())
{
return ApplicationData.Current.LocalFolder.Path;
}
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyApp");
}
private static bool IsPackaged()
{
try
{
return Package.Current.Id != null;
}
catch
{
return false;
}
}Wrong Target Framework
Problem: Using incompatible target framework for Windows App SDK.
<!-- Bad: Missing Windows version -->
<TargetFramework>net8.0</TargetFramework>Solution: Use correct Windows target framework.
<!-- Good: Correct TFM for WinUI 3 -->
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<UseWinUI>true</UseWinUI>Service and Dependency Issues
Creating Services in Views
Problem: Instantiating services directly in views.
// Bad: Tight coupling, hard to test
public sealed partial class OrderPage : Page
{
private readonly HttpClient _client = new();
private readonly JsonSerializerOptions _options = new();
private async void LoadOrders()
{
var response = await _client.GetAsync("...");
// ...
}
}Solution: Inject services via constructor.
// Good: Dependency injection
public sealed partial class OrderPage : Page
{
public OrderViewModel ViewModel { get; }
public OrderPage()
{
ViewModel = App.GetService<OrderViewModel>();
InitializeComponent();
}
}Not Disposing Resources
Problem: Not disposing IDisposable resources.
// Bad: Resource leak
public async Task DownloadFileAsync(string url)
{
var client = new HttpClient();
var stream = await client.GetStreamAsync(url);
// client never disposed
}Solution: Use using statements or patterns.
// Good: Proper disposal
public async Task DownloadFileAsync(string url)
{
using var client = new HttpClient();
await using var stream = await client.GetStreamAsync(url);
// ...
}Windowing Issues
Not Handling DPI Changes
Problem: Fixed pixel sizes that don't scale.
// Bad: Fixed pixel size
_appWindow.Resize(new SizeInt32(800, 600));Solution: Consider DPI-aware sizing when appropriate.
// Good: Consider display scale factor
var displayArea = DisplayArea.GetFromWindowId(_appWindow.Id, DisplayAreaFallback.Primary);
var scaleFactor = GetScaleFactor(); // Get from DisplayInformation
var width = (int)(800 * scaleFactor);
var height = (int)(600 * scaleFactor);
_appWindow.Resize(new SizeInt32(width, height));Multiple Window Confusion
Problem: Not tracking window instances properly.
// Bad: Lost reference to additional windows
private void OpenNewWindow()
{
var window = new SecondaryWindow();
window.Activate();
// Window reference lost, may be GC'd
}Solution: Track window instances.
// Good: Track windows
private readonly List<Window> _windows = [];
private void OpenNewWindow()
{
var window = new SecondaryWindow();
_windows.Add(window);
window.Closed += (s, e) => _windows.Remove((Window)s);
window.Activate();
}Summary Table
| Category | Anti-Pattern | Impact | Solution |
|---|---|---|---|
| MVVM | Code-behind logic | Untestable | Use ViewModels |
| Binding | Using {Binding} | Slower, no type safety | Use {x:Bind} |
| Threading | Blocking UI | Frozen app | Use async/await |
| Dialogs | Missing XamlRoot | Runtime crash | Set XamlRoot |
| Styling | Hardcoded values | Poor theming | Use resources |
| Lists | No virtualization | Poor performance | Use ListView |
| Navigation | Tight coupling | Hard to test | Use service |
| Packaging | Wrong TFM | Build failures | Use correct TFM |
| Services | Direct instantiation | Tight coupling | Use DI |
| Windows | Lost references | GC issues | Track instances |
WinUI 3 Patterns
Reference patterns for building WinUI 3 applications with Windows App SDK.
MVVM Pattern
Core Principles
1. View - XAML UI, minimal code-behind, binds to ViewModel 2. ViewModel - Exposes data and commands, contains presentation logic 3. Model - Domain data and business rules
MVVM Toolkit Integration
Use CommunityToolkit.Mvvm for source-generated MVVM:
public partial class OrderViewModel : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(TotalDisplay))]
private decimal _total;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SubmitCommand))]
private bool _isValid;
public string TotalDisplay => $"Total: {Total:C}";
[RelayCommand(CanExecute = nameof(IsValid))]
private async Task SubmitAsync()
{
// Submit order
}
}ViewModel Initialization
Initialize ViewModels through navigation or page lifecycle:
public sealed partial class OrderPage : Page
{
public OrderViewModel ViewModel { get; }
public OrderPage()
{
ViewModel = App.GetService<OrderViewModel>();
InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is int orderId)
{
await ViewModel.LoadAsync(orderId);
}
}
}Service Pattern
Service Registration
Register services at application startup:
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<INavigationService, NavigationService>();
services.AddTransient<IFileService, FileService>();
services.AddHttpClient<IApiService, ApiService>();Service Abstraction
Abstract Windows-specific APIs behind interfaces for testability:
public interface IFilePickerService
{
Task<StorageFile?> PickFileAsync(IEnumerable<string> extensions);
Task<StorageFolder?> PickFolderAsync();
}
public class FilePickerService : IFilePickerService
{
private readonly Window _window;
public FilePickerService(Window window)
{
_window = window;
}
public async Task<StorageFile?> PickFileAsync(IEnumerable<string> extensions)
{
var picker = new FileOpenPicker();
InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(_window));
foreach (var ext in extensions)
{
picker.FileTypeFilter.Add(ext);
}
return await picker.PickSingleFileAsync();
}
}Navigation Pattern
Frame-Based Navigation
Use a navigation service that wraps Frame navigation:
public class NavigationService : INavigationService
{
private readonly Dictionary<Type, Type> _viewModelToPageMap = new();
private Frame? _frame;
public void RegisterPage<TViewModel, TPage>()
where TViewModel : class
where TPage : Page
{
_viewModelToPageMap[typeof(TViewModel)] = typeof(TPage);
}
public bool NavigateTo<TViewModel>(object? parameter = null)
{
if (_viewModelToPageMap.TryGetValue(typeof(TViewModel), out var pageType))
{
return _frame?.Navigate(pageType, parameter) ?? false;
}
return false;
}
}NavigationView Integration
Integrate with NavigationView for shell navigation:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.IsSettingsSelected)
{
_navigationService.NavigateTo<SettingsViewModel>();
return;
}
if (args.SelectedItemContainer?.Tag is string tag)
{
var viewModelType = Type.GetType($"MyApp.ViewModels.{tag}ViewModel");
if (viewModelType != null)
{
_navigationService.NavigateTo(viewModelType);
}
}
}Window Management Pattern
AppWindow Abstraction
Wrap AppWindow operations for cleaner code:
public class WindowHelper
{
private readonly AppWindow _appWindow;
public WindowHelper(Window window)
{
var hWnd = WindowNative.GetWindowHandle(window);
var windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
_appWindow = AppWindow.GetFromWindowId(windowId);
}
public void SetSize(int width, int height)
{
_appWindow.Resize(new SizeInt32(width, height));
}
public void CenterOnScreen()
{
var display = DisplayArea.GetFromWindowId(_appWindow.Id, DisplayAreaFallback.Primary);
var x = (display.WorkArea.Width - _appWindow.Size.Width) / 2;
var y = (display.WorkArea.Height - _appWindow.Size.Height) / 2;
_appWindow.Move(new PointInt32(x, y));
}
public void SetTitle(string title)
{
_appWindow.Title = title;
}
public void CustomizeTitleBar(Color backgroundColor)
{
if (AppWindowTitleBar.IsCustomizationSupported())
{
var titleBar = _appWindow.TitleBar;
titleBar.ExtendsContentIntoTitleBar = true;
titleBar.ButtonBackgroundColor = backgroundColor;
}
}
}Messaging Pattern
WeakReferenceMessenger
Use the messaging system for loosely coupled communication:
// Define message
public record UserLoggedInMessage(User User);
// Send message
WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(user));
// Receive message in ViewModel
public partial class DashboardViewModel : ObservableRecipient
{
protected override void OnActivated()
{
Messenger.Register<DashboardViewModel, UserLoggedInMessage>(this, (r, m) =>
{
r.CurrentUser = m.User;
});
}
}Request Messages
Use request messages for data retrieval across ViewModels:
public class CurrentThemeRequestMessage : RequestMessage<ElementTheme> { }
// Handler
Messenger.Register<SettingsViewModel, CurrentThemeRequestMessage>(this, (r, m) =>
{
m.Reply(r.CurrentTheme);
});
// Requester
var theme = WeakReferenceMessenger.Default.Send<CurrentThemeRequestMessage>();Settings Pattern
Settings Service
Persist settings using local storage:
public class SettingsService : ISettingsService
{
private readonly ApplicationDataContainer _localSettings;
public SettingsService()
{
_localSettings = ApplicationData.Current.LocalSettings;
}
public T? Get<T>(string key, T? defaultValue = default)
{
if (_localSettings.Values.TryGetValue(key, out var value))
{
return (T)value;
}
return defaultValue;
}
public void Set<T>(string key, T value)
{
_localSettings.Values[key] = value;
}
}Unpackaged Settings Alternative
For unpackaged apps, use file-based settings:
public class FileSettingsService : ISettingsService
{
private readonly string _settingsPath;
private Dictionary<string, object?> _settings = new();
public FileSettingsService()
{
_settingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyApp",
"settings.json");
Load();
}
private void Load()
{
if (File.Exists(_settingsPath))
{
var json = File.ReadAllText(_settingsPath);
_settings = JsonSerializer.Deserialize<Dictionary<string, object?>>(json) ?? new();
}
}
private void Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_settingsPath)!);
var json = JsonSerializer.Serialize(_settings);
File.WriteAllText(_settingsPath, json);
}
}Data Template Selector Pattern
Select templates based on data type:
public class NotificationTemplateSelector : DataTemplateSelector
{
public DataTemplate? InfoTemplate { get; set; }
public DataTemplate? WarningTemplate { get; set; }
public DataTemplate? ErrorTemplate { get; set; }
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject container)
{
return item switch
{
InfoNotification => InfoTemplate,
WarningNotification => WarningTemplate,
ErrorNotification => ErrorTemplate,
_ => base.SelectTemplateCore(item, container)
};
}
}<Page.Resources>
<local:NotificationTemplateSelector x:Key="NotificationSelector">
<local:NotificationTemplateSelector.InfoTemplate>
<DataTemplate x:DataType="models:InfoNotification">
<InfoBar Severity="Informational" Title="{x:Bind Title}"/>
</DataTemplate>
</local:NotificationTemplateSelector.InfoTemplate>
<!-- Other templates -->
</local:NotificationTemplateSelector>
</Page.Resources>
<ListView ItemsSource="{x:Bind ViewModel.Notifications}"
ItemTemplateSelector="{StaticResource NotificationSelector}"/>Async Loading Pattern
Handle async data loading with loading states:
public partial class DataViewModel : ObservableObject
{
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string? _errorMessage;
[ObservableProperty]
private ObservableCollection<Item> _items = [];
[RelayCommand]
private async Task LoadAsync()
{
IsLoading = true;
ErrorMessage = null;
try
{
var data = await _dataService.GetItemsAsync();
Items = new ObservableCollection<Item>(data);
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
finally
{
IsLoading = false;
}
}
}<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Items}"
Visibility="{x:Bind ViewModel.IsLoading, Converter={StaticResource InverseBoolToVisibility}}"/>
<ProgressRing IsActive="{x:Bind ViewModel.IsLoading, Mode=OneWay}"
Visibility="{x:Bind ViewModel.IsLoading, Mode=OneWay}"/>
<InfoBar IsOpen="{x:Bind ViewModel.ErrorMessage, Converter={StaticResource NullToBool}}"
Severity="Error"
Title="Error"
Message="{x:Bind ViewModel.ErrorMessage, Mode=OneWay}"/>
</Grid>Activation Pattern
Handle different activation scenarios:
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
m_window = new MainWindow();
var activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
switch (activatedArgs.Kind)
{
case ExtendedActivationKind.File:
HandleFileActivation(activatedArgs);
break;
case ExtendedActivationKind.Protocol:
HandleProtocolActivation(activatedArgs);
break;
case ExtendedActivationKind.ToastNotification:
HandleToastActivation(activatedArgs);
break;
default:
HandleDefaultActivation();
break;
}
m_window.Activate();
}
private void HandleFileActivation(AppActivationArguments args)
{
if (args.Data is IFileActivatedEventArgs fileArgs)
{
var file = fileArgs.Files.FirstOrDefault() as StorageFile;
if (file != null)
{
_navigationService.NavigateTo<FileViewerViewModel>(file.Path);
}
}
}Background Task Pattern
Register and handle background tasks:
public static class BackgroundTaskHelper
{
public static async Task RegisterTimerTaskAsync(string taskName, uint intervalMinutes)
{
var access = await BackgroundExecutionManager.RequestAccessAsync();
if (access is BackgroundAccessStatus.DeniedBySystemPolicy or
BackgroundAccessStatus.DeniedByUser)
{
return;
}
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == taskName)
{
return; // Already registered
}
}
var builder = new BackgroundTaskBuilder
{
Name = taskName
};
builder.SetTrigger(new TimeTrigger(intervalMinutes, false));
builder.Register();
}
}