Claude
Skills
Sign in
โ† Back

wpf-best-practices

Included with Lifetime
$97 forever

Best practices for building WPF desktop applications with C# and .NET. Use when working on WPF projects, .NET desktop apps, XAML UI, MVVM architecture, system tray apps, or any C# project using Windows Presentation Foundation.

Design

What this skill does


# WPF Best Practices

You are an expert in C#, .NET, and WPF desktop application development with deep
knowledge of MVVM architecture, dependency injection, async/await patterns, and
Windows desktop integration.

## C# Code Style

### Basic Principles

- Use English for all code and documentation
- Always declare types for variables and functions (parameters and return values)
- Enable nullable reference types project-wide
- Use file-scoped namespaces โ€” no braces, no nesting
- Write concise, maintainable code โ€” avoid over-engineering

### File-Scoped Namespaces

Always use file-scoped namespaces:

```csharp
namespace MyApp.Services;

public class SettingsService
{
    // No extra indentation level
}
```

### Nullable Reference Types

Always enabled in csproj (`<Nullable>enable</Nullable>`):

```csharp
public class AppNotification
{
    public string Title { get; set; } = "";       // Non-nullable with default
    public string Message { get; set; } = "";
    public string? Channel { get; set; }           // Explicitly nullable
    public string[]? Tags { get; set; }
}
```

### Naming Conventions

- **PascalCase** for types, interfaces, properties, methods, events, constants
- **camelCase** for local variables and parameters
- **_camelCase** for private fields
- **IPascalCase** for interfaces (prefix with `I`)
- **UPPERCASE** for environment variables only
- Prefix boolean properties/fields with `Is`, `Has`, `Can`, `Should`

```csharp
public class ConnectionService
{
    private readonly ILogger _logger;
    private readonly string _serverUrl;
    private bool _isConnected;

    public bool IsConnected => _isConnected;
    public bool CanReconnect { get; private set; }
}
```

### Modern C# Features

Use switch expressions:

```csharp
public string StatusIcon => Status switch
{
    ConnectionStatus.Connected    => "๐ŸŸข",
    ConnectionStatus.Connecting   => "๐ŸŸก",
    ConnectionStatus.Disconnected => "๐Ÿ”ด",
    _                             => "โšช"
};
```

Use range/slice operators:

```csharp
private static string Capitalize(string s) =>
    string.IsNullOrEmpty(s) ? s : char.ToUpper(s[0]) + s[1..];
```

Avoid redundant default initializers:

```csharp
// Good
private bool _disposed;
private int _retryCount;

// Bad โ€” redundant
private bool _disposed = false;
private int _retryCount = 0;
```

---

## Project Structure

```
MyApp/
โ”œโ”€โ”€ MyApp.sln                    # Solution file
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ MyApp/                   # Main WPF application
โ”‚   โ”‚   โ”œโ”€โ”€ App.xaml             # Application entry, resource dictionaries
โ”‚   โ”‚   โ”œโ”€โ”€ App.xaml.cs          # DI setup, app lifecycle, crash handlers
โ”‚   โ”‚   โ”œโ”€โ”€ Views/               # XAML windows and user controls
โ”‚   โ”‚   โ”œโ”€โ”€ ViewModels/          # MVVM presentation logic
โ”‚   โ”‚   โ”œโ”€โ”€ Models/              # Data classes and enums
โ”‚   โ”‚   โ”œโ”€โ”€ Services/            # Business logic (DI singletons)
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Interfaces/      # Service contracts
โ”‚   โ”‚   โ”œโ”€โ”€ Utilities/           # Helpers (encryption, screen, JSON)
โ”‚   โ”‚   โ””โ”€โ”€ Constants/           # App-wide constants
โ”‚   โ””โ”€โ”€ MyApp.Shared/            # Cross-platform shared library
โ”‚       โ””โ”€โ”€ MyApp.Shared.csproj
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ MyApp.Tests/             # xUnit test project
โ”‚       โ””โ”€โ”€ MyApp.Tests.csproj
โ””โ”€โ”€ .github/
    โ””โ”€โ”€ workflows/               # CI/CD
```

### Project File Configuration

WPF app csproj:

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net10.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
```

Shared library csproj (cross-platform):

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <InternalsVisibleTo Include="MyApp.Tests" />
  </ItemGroup>
</Project>
```

---

## MVVM Pattern

### ViewModelBase

Every ViewModel should inherit from a shared base:

```csharp
namespace MyApp.ViewModels;

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string? name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    protected bool SetProperty<T>(ref T field, T value,
        [CallerMemberName] string? name = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(name);
        return true;
    }
}
```

Usage:

```csharp
public class MainViewModel : ViewModelBase
{
    private string _title = "";
    public string Title
    {
        get => _title;
        set => SetProperty(ref _title, value);
    }

    private bool _isLoading;
    public bool IsLoading
    {
        get => _isLoading;
        set => SetProperty(ref _isLoading, value);
    }
}
```

### RelayCommand

Use a simple `RelayCommand` for XAML command bindings:

```csharp
namespace MyApp.ViewModels;

public class RelayCommand : ICommand
{
    private readonly Action<object?> _execute;
    private readonly Func<object?, bool>? _canExecute;

    public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;
    public void Execute(object? parameter) => _execute(parameter);
}

public class RelayCommand<T> : ICommand
{
    private readonly Action<T?> _execute;
    private readonly Func<T?, bool>? _canExecute;

    public RelayCommand(Action<T?> execute, Func<T?, bool>? canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public bool CanExecute(object? parameter) =>
        _canExecute?.Invoke(parameter is T t ? t : default) ?? true;
    public void Execute(object? parameter) =>
        _execute(parameter is T t ? t : default);
}
```

### AsyncRelayCommand

For async operations bound to UI:

```csharp
namespace MyApp.ViewModels;

public class AsyncRelayCommand : ICommand
{
    private readonly Func<object?, Task> _execute;
    private readonly Func<object?, bool>? _canExecute;
    private bool _isExecuting;

    public AsyncRelayCommand(Func<object?, Task> execute,
        Func<object?, bool>? canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public bool CanExecute(object? parameter) =>
        !_isExecuting && (_canExecute?.Invoke(parameter) ?? true);

    public async void Execute(object? parameter)
    {
        if (_isExecuting) return;
        _isExecuting = true;
        CommandManager.InvalidateRequerySuggested();

        try
        {
            await _execute(parameter);
        }
        finally
        {
            _isExecuting = false;
            CommandManager.InvalidateRequerySuggested();
        }
    }
}
```

### Data Binding in XAML

```xml
<Window x:Class="MyApp.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{Binding Title}">
    <Grid>
        <TextBlock Text="{Binding StatusText}"
                   Visibility="{Binding IsLoading,
                     Converter={S
Files: 1
Size: 35.6 KB
Complexity: 34/100
Category: Design

Related in Design