Covers WPF memory optimization including Freezable patterns, common memory leak causes, and diagnostic techniques. Use when experiencing memory growth, implementing resource caching, or debugging memory issues.
View on GitHubchristian289/dotnet-with-claudecode
wpf-dev-pack
January 23, 2026
Select agents to install to:
npx add-skill https://github.com/christian289/dotnet-with-claudecode/blob/main/wpf-dev-pack/skills/optimizing-wpf-memory/SKILL.md -a claude-code --skill optimizing-wpf-memoryInstallation paths:
.claude/skills/optimizing-wpf-memory/# WPF Memory Optimization
## 1. Freezable Pattern
### Why Freeze?
| Benefit | Description |
|---------|-------------|
| Thread-safe | Can be used across threads |
| No change tracking | Reduces overhead |
| Renderer optimization | Better GPU utilization |
### Basic Usage
```csharp
// Always freeze static resources
var brush = new SolidColorBrush(Colors.Red);
brush.Freeze();
var pen = new Pen(Brushes.Black, 1);
pen.Freeze();
```
### XAML Freeze
```xml
<Window xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
mc:Ignorable="po">
<Window.Resources>
<SolidColorBrush x:Key="FrozenBrush" Color="Red" po:Freeze="True"/>
</Window.Resources>
</Window>
```
### When Freeze Fails
```csharp
if (brush.CanFreeze)
brush.Freeze();
else
// Has bindings or animations - cannot freeze
```
### Modifying Frozen Objects
```csharp
var clone = frozenBrush.Clone(); // Creates unfrozen copy
clone.Color = Colors.Blue;
clone.Freeze(); // Freeze again if needed
```
## 2. Common Memory Leaks
### Event Handler Leaks
```csharp
// ❌ LEAK: Static event holds reference
SomeStaticClass.StaticEvent += OnEvent;
// ✅ FIX: Unsubscribe in Unloaded
Unloaded += (s, e) => SomeStaticClass.StaticEvent -= OnEvent;
```
### CompositionTarget.Rendering Leak
```csharp
// ❌ LEAK: Never unsubscribed
CompositionTarget.Rendering += OnRendering;
// ✅ FIX: Always unsubscribe
Loaded += (s, e) => CompositionTarget.Rendering += OnRendering;
Unloaded += (s, e) => CompositionTarget.Rendering -= OnRendering;
```
### Binding Without INotifyPropertyChanged
```csharp
// ❌ LEAK: PropertyDescriptor retained
public string Name { get; set; } // No INPC
// ✅ FIX: Implement INPC
public string Name
{
get => _name;
set { _name = value; OnPropertyChanged(); }
}
```
### DispatcherTimer Leak
```csharp
// ❌ LEAK: Timer keeps running
_timer = new DispatcherTimer();
_timer.Tick += OnTick;
_timer.Start();
// ✅ FIX: Stop and cleanup
Unloaded += (s, e) =>
{
_timer.