From wpf-dev-pack
Optimizes WPF memory usage via Freezable patterns, fixes common leaks from events, CompositionTarget.Rendering, timers, bindings, and images, with diagnostic checklists. Use for memory growth or leak debugging.
npx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packThis skill uses the workspace's default tool permissions.
| Benefit | Description |
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
| Benefit | Description |
|---|---|
| Thread-safe | Can be used across threads |
| No change tracking | Reduces overhead |
| Renderer optimization | Better GPU utilization |
// Always freeze static resources
var brush = new SolidColorBrush(Colors.Red);
brush.Freeze();
var pen = new Pen(Brushes.Black, 1);
pen.Freeze();
<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>
if (brush.CanFreeze)
brush.Freeze();
else
// Has bindings or animations - cannot freeze
var clone = frozenBrush.Clone(); // Creates unfrozen copy
clone.Color = Colors.Blue;
clone.Freeze(); // Freeze again if needed
// ❌ LEAK: Static event holds reference
SomeStaticClass.StaticEvent += OnEvent;
// ✅ FIX: Unsubscribe in Unloaded
Unloaded += (s, e) => SomeStaticClass.StaticEvent -= OnEvent;
// ❌ LEAK: Never unsubscribed
CompositionTarget.Rendering += OnRendering;
// ✅ FIX: Always unsubscribe
Loaded += (s, e) => CompositionTarget.Rendering += OnRendering;
Unloaded += (s, e) => CompositionTarget.Rendering -= OnRendering;
// ❌ LEAK: PropertyDescriptor retained
public string Name { get; set; } // No INPC
// ✅ FIX: Implement INPC
public string Name
{
get => _name;
set { _name = value; OnPropertyChanged(); }
}
// ❌ LEAK: Timer keeps running
_timer = new DispatcherTimer();
_timer.Tick += OnTick;
_timer.Start();
// ✅ FIX: Stop and cleanup
Unloaded += (s, e) =>
{
_timer.Stop();
_timer.Tick -= OnTick;
};
// ❌ LEAK: Stream kept open
var bitmap = new BitmapImage(new Uri(path));
// ✅ FIX: Load immediately and release stream
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
bitmap.Freeze();
BitmapCacheOption.OnLoad| Tool | Purpose |
|---|---|
| VS Diagnostic Tools | Real-time memory snapshots |
| dotMemory | Detailed retention paths |
| PerfView | GC and allocation analysis |
public static class MemoryMonitor
{
public static void LogMemory(string context)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
var mem = GC.GetTotalMemory(true) / 1024.0 / 1024.0;
Debug.WriteLine($"[{context}] Memory: {mem:F2} MB");
}
}
public static class FrozenResources
{
public static SolidColorBrush CreateBrush(Color color)
{
var brush = new SolidColorBrush(color);
brush.Freeze();
return brush;
}
public static Pen CreatePen(Color color, double thickness)
{
var pen = new Pen(CreateBrush(color), thickness);
pen.Freeze();
return pen;
}
}