optimizing-wpf-memory
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/christian289/dotnet-with-claudecode --skill optimizing-wpf-memory
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
continue
1
kimi-cli
1
Skill 文档
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
// Always freeze static resources
var brush = new SolidColorBrush(Colors.Red);
brush.Freeze();
var pen = new Pen(Brushes.Black, 1);
pen.Freeze();
XAML 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>
When Freeze Fails
if (brush.CanFreeze)
brush.Freeze();
else
// Has bindings or animations - cannot freeze
Modifying Frozen Objects
var clone = frozenBrush.Clone(); // Creates unfrozen copy
clone.Color = Colors.Blue;
clone.Freeze(); // Freeze again if needed
2. Common Memory Leaks
Event Handler Leaks
// â LEAK: Static event holds reference
SomeStaticClass.StaticEvent += OnEvent;
// â
FIX: Unsubscribe in Unloaded
Unloaded += (s, e) => SomeStaticClass.StaticEvent -= OnEvent;
CompositionTarget.Rendering Leak
// â LEAK: Never unsubscribed
CompositionTarget.Rendering += OnRendering;
// â
FIX: Always unsubscribe
Loaded += (s, e) => CompositionTarget.Rendering += OnRendering;
Unloaded += (s, e) => CompositionTarget.Rendering -= OnRendering;
Binding Without INotifyPropertyChanged
// â LEAK: PropertyDescriptor retained
public string Name { get; set; } // No INPC
// â
FIX: Implement INPC
public string Name
{
get => _name;
set { _name = value; OnPropertyChanged(); }
}
DispatcherTimer Leak
// â LEAK: Timer keeps running
_timer = new DispatcherTimer();
_timer.Tick += OnTick;
_timer.Start();
// â
FIX: Stop and cleanup
Unloaded += (s, e) =>
{
_timer.Stop();
_timer.Tick -= OnTick;
};
Image Resource Leak
// â 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();
3. Diagnostic Checklist
Code Review Points
- All static event subscriptions have unsubscribe logic
- CompositionTarget.Rendering unsubscribed in Unloaded
- DispatcherTimer stopped in Unloaded
- ViewModels implement INotifyPropertyChanged
- Images use
BitmapCacheOption.OnLoad - Static resources are Frozen
Diagnostic Tools
| Tool | Purpose |
|---|---|
| VS Diagnostic Tools | Real-time memory snapshots |
| dotMemory | Detailed retention paths |
| PerfView | GC and allocation analysis |
Memory Monitor
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");
}
}
4. Resource Factory Pattern
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;
}
}