# Wpf

> Build and modernize WPF applications on .NET with correct XAML, data binding, commands, threading, styling, and Windows desktop migration decisions. USE FOR: working on WPF UI, MVVM, binding, commands, or desktop modernization; migrating WPF from .NET Framework to .NET; integrating newer Windows capabilities into a WPF app. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this spe…

- **Type:** Skill
- **Install:** `agentstack add skill-managedcode-dotnet-skills-wpf`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [managedcode](https://agentstack.voostack.com/s/managedcode)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [managedcode](https://github.com/managedcode)
- **Source:** https://github.com/managedcode/dotnet-skills/tree/main/catalog/Frameworks/WPF/skills/wpf
- **Website:** https://skills.managed-code.com

## Install

```sh
agentstack add skill-managedcode-dotnet-skills-wpf
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# WPF

## Trigger On

- working on WPF UI, MVVM, binding, commands, or desktop modernization
- migrating WPF from .NET Framework to .NET
- integrating newer Windows capabilities into a WPF app
- implementing data binding, styles, templates, or control customization

## Documentation

- [WPF Overview](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview/)
- [Data Binding Overview](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/)
- [MVVM Toolkit Introduction](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/)
- [Styles and Templates](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/styles-templates-overview)
- [Migration Guide](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/migration/)

### References

- [patterns.md](references/patterns.md) - MVVM patterns, binding patterns, command patterns, and reusable architectural approaches
- [anti-patterns.md](references/anti-patterns.md) - Common WPF mistakes and how to avoid them

## Workflow

1. **Confirm Windows-only scope** — WPF is Windows-only even when the wider .NET stack is cross-platform
2. **Apply MVVM pattern** — keep views dumb, logic in ViewModels, use commands
3. **Manage data binding explicitly** — choose correct binding modes, validate at runtime
4. **Use styles and templates deliberately** — keep UI composable, avoid page-specific hacks
5. **Handle threading correctly** — use Dispatcher for UI updates, async/await for long operations
6. **Validate both designer and runtime** — XAML composition failures often surface only at runtime

## Current Upstream Notes

- The refreshed WPF overview page reiterates WPF as a Windows desktop UI stack with XAML, data binding, styling, templates, resources, and vector/rich-media composition. Keep WPF-specific guidance separate from WinUI or MAUI unless the task is explicitly a migration or comparison.
- For modernization work, check both `.NET Framework` compatibility constraints and current .NET desktop migration docs before moving project files or XAML resource dictionaries.

## Project Structure

```
MyWpfApp/
├── MyWpfApp/
│   ├── App.xaml                # Application entry
│   ├── MainWindow.xaml         # Main window
│   ├── Views/                  # XAML views/windows
│   ├── ViewModels/             # MVVM ViewModels
│   ├── Models/                 # Domain models
│   ├── Services/               # Business logic
│   ├── Converters/             # Value converters
│   ├── Resources/              # Styles, templates, dictionaries
│   └── Controls/               # Custom controls
└── MyWpfApp.Tests/
```

## MVVM Pattern

### ViewModel with MVVM Toolkit
```csharp
public partial class CustomersViewModel : ObservableObject
{
    private readonly ICustomerService _customerService;

    [ObservableProperty]
    private ObservableCollection _customers = [];

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
    private Customer? _selectedCustomer;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(RefreshCommand))]
    private bool _isLoading;

    public CustomersViewModel(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    [RelayCommand(CanExecute = nameof(CanRefresh))]
    private async Task RefreshAsync()
    {
        IsLoading = true;
        try
        {
            var items = await _customerService.GetAllAsync();
            Customers = new ObservableCollection(items);
        }
        finally
        {
            IsLoading = false;
        }
    }

    private bool CanRefresh() => !IsLoading;

    [RelayCommand(CanExecute = nameof(CanSave))]
    private async Task SaveAsync()
    {
        if (SelectedCustomer is null) return;
        await _customerService.SaveAsync(SelectedCustomer);
    }

    private bool CanSave() => SelectedCustomer is not null;
}
```

### View Binding
```xml

    
        
            
            
        

        
            
            
        

        
            
                
                
            
        
    

```

## Dependency Injection

```csharp
public partial class App : Application
{
    private readonly IHost _host;

    public App()
    {
        _host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                // Services
                services.AddSingleton();
                services.AddSingleton();

                // ViewModels
                services.AddTransient();
                services.AddTransient();

                // Views
                services.AddTransient();
                services.AddTransient();
            })
            .Build();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        await _host.StartAsync();
        var mainWindow = _host.Services.GetRequiredService();
        mainWindow.Show();
        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        await _host.StopAsync();
        _host.Dispose();
        base.OnExit(e);
    }
}
```

## Data Binding Modes

```xml

```

## Value Converters

```csharp
public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool boolValue)
        {
            return boolValue ? Visibility.Visible : Visibility.Collapsed;
        }
        return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is Visibility.Visible;
    }
}

// Multi-value converter
public class MultiplyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length == 2 && values[0] is double a && values[1] is double b)
        {
            return a * b;
        }
        return 0.0;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
```

## Styles and Templates

### Resource Dictionary
```xml

    
    
        
        
        
        
        
            
                
                    
                        
                    
                
            
        
    

    
    
        
    

```

## Threading and Dispatcher

```csharp
// Update UI from background thread
await Task.Run(async () =>
{
    var data = await LoadDataAsync();

    // Must use Dispatcher to update UI
    Application.Current.Dispatcher.Invoke(() =>
    {
        Items.Clear();
        foreach (var item in data)
        {
            Items.Add(item);
        }
    });
});

// Better: Use async/await properly
private async Task LoadDataAsync()
{
    IsLoading = true;
    try
    {
        // This runs on background thread
        var data = await _service.GetDataAsync();

        // This automatically marshals to UI thread
        Items = new ObservableCollection(data);
    }
    finally
    {
        IsLoading = false;
    }
}
```

## Anti-Patterns to Avoid

| Anti-Pattern | Why It's Bad | Better Approach |
|--------------|--------------|-----------------|
| Logic in code-behind | Hard to test, tight coupling | Use MVVM with ViewModels |
| Synchronous blocking calls | UI freezes | Use async/await |
| Manual INotifyPropertyChanged | Boilerplate, error-prone | Use MVVM Toolkit attributes |
| Hardcoded colors/sizes | Inconsistent, hard to theme | Use resource dictionaries |
| Direct Dispatcher.Invoke everywhere | Complex, error-prone | Prefer async/await marshaling |
| God ViewModel | Unmaintainable | Split into focused ViewModels |
| Skipping binding validation | Runtime errors hidden | Use ValidatesOnDataErrors |
| Event handlers for everything | Memory leaks, coupling | Use commands and bindings |

## Best Practices

1. **Use compiled bindings in .NET 5+:**
   - Enable `x:CompileBindings="True"` for performance

2. **Implement INotifyDataErrorInfo for validation:**
   ```csharp
   [ObservableProperty]
   [NotifyDataErrorInfo]
   [Required(ErrorMessage = "Name is required")]
   [MinLength(2, ErrorMessage = "Name must be at least 2 characters")]
   private string _name = string.Empty;
   ```

3. **Use weak event patterns for long-lived subscriptions:**
   ```csharp
   WeakEventManager.AddHandler(source, "EventName", Handler);
   ```

4. **Virtualize large collections:**
   ```xml
   
   ```

5. **Freeze Freezables when possible:**
   ```csharp
   var brush = new SolidColorBrush(Colors.Blue);
   brush.Freeze(); // Thread-safe, better performance
   ```

6. **Use design-time data:**
   ```xml
   
   ```

## Testing

```csharp
[Fact]
public async Task RefreshCommand_LoadsCustomers()
{
    var mockService = new Mock();
    mockService.Setup(s => s.GetAllAsync())
        .ReturnsAsync(new[] { new Customer { Name = "Test" } });

    var viewModel = new CustomersViewModel(mockService.Object);

    await viewModel.RefreshCommand.ExecuteAsync(null);

    Assert.Single(viewModel.Customers);
    Assert.Equal("Test", viewModel.Customers[0].Name);
}

[Fact]
public void SaveCommand_CannotExecute_WhenNoSelection()
{
    var mockService = new Mock();
    var viewModel = new CustomersViewModel(mockService.Object);

    viewModel.SelectedCustomer = null;

    Assert.False(viewModel.SaveCommand.CanExecute(null));
}
```

## Deliver

- cleaner WPF views and view-model boundaries
- safer binding and threading behavior
- migration guidance grounded in actual Windows constraints
- MVVM pattern with testable ViewModels

## Validate

- binding and command flows are explicit
- code-behind is not carrying hidden business logic
- Windows-only assumptions are acknowledged
- threading and dispatcher usage is correct
- styles and resources are properly organized

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [managedcode](https://github.com/managedcode)
- **Source:** [managedcode/dotnet-skills](https://github.com/managedcode/dotnet-skills)
- **License:** MIT
- **Homepage:** https://skills.managed-code.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-managedcode-dotnet-skills-wpf
- Seller: https://agentstack.voostack.com/s/managedcode
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
