From wpf-dev-pack
Explains WPF rendering pipeline including Measure/Arrange/Render passes, invalidation methods, and hardware acceleration tiers. Use for debugging layouts, optimizing performance, or diagnosing software rendering.
npx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packThis skill uses the workspace's default tool permissions.
```
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.
User Input → Property Change → InvalidateXxx()
↓
┌───────────────┐
│ Measure Pass │ → Determines DesiredSize
├───────────────┤
│ Arrange Pass │ → Determines ActualSize/Position
├───────────────┤
│ Render Pass │ → OnRender() / DrawingContext
└───────────────┘
↓
Composition Tree → MILCore → DirectX → GPU
| Method | Triggers | Use When |
|---|---|---|
InvalidateMeasure() | Measure + Arrange + Render | Size might change |
InvalidateArrange() | Arrange + Render | Position might change |
InvalidateVisual() | Render only | Visual appearance change |
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(double), typeof(MyControl),
new FrameworkPropertyMetadata(10.0,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure));
| Flag | Effect |
|---|---|
AffectsMeasure | Triggers Measure pass |
AffectsArrange | Triggers Arrange pass |
AffectsRender | Triggers Render pass |
AffectsParentMeasure | Parent re-measures |
AffectsParentArrange | Parent re-arranges |
protected override Size MeasureOverride(Size availableSize)
{
// Calculate desired size based on content
double width = Math.Min(200, availableSize.Width);
double height = Math.Min(100, availableSize.Height);
return new Size(width, height);
}
protected override Size ArrangeOverride(Size finalSize)
{
// Position children within finalSize
foreach (UIElement child in Children)
{
child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
}
return finalSize;
}
protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(Background, null, new Rect(RenderSize));
}
| Tier | GPU Memory | Features |
|---|---|---|
| 0 | < 30MB | Software rendering |
| 1 | 30-120MB | Partial hardware (no PS 2.0) |
| 2 | ≥ 120MB | Full hardware acceleration |
int tier = RenderCapability.Tier >> 16;
// 0 = Software, 1 = Partial, 2 = Full GPU
WPF falls back to software rendering when:
AllowsTransparency="True" on WindowBitmapEffect used// For images that will be scaled
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.LowQuality);
// For pixel-perfect lines
RenderOptions.SetEdgeMode(element, EdgeMode.Aliased);
// For tiled brushes
RenderOptions.SetCachingHint(brush, CachingHint.Cache);
RenderOptions.SetCacheInvalidationThresholdMinimum(brush, 0.5);
RenderOptions.SetCacheInvalidationThresholdMaximum(brush, 2.0);
// Bad: Multiple layout passes
for (int i = 0; i < 100; i++)
{
items[i].Width = newWidths[i]; // Each triggers layout
}
// Good: Single layout pass
using (Dispatcher.DisableProcessing())
{
for (int i = 0; i < 100; i++)
{
items[i].Width = newWidths[i];
}
} // Layout happens once here
| Problem | Cause | Solution |
|---|---|---|
| Slow scrolling | Deep Visual Tree | Virtualization |
| Layout thrashing | Frequent InvalidateMeasure | Batch updates |
| Slow startup | Complex Grid | Simplify layout |
| Memory growth | BitmapEffect | Use Effect class |
public void ConfigureForTier()
{
int tier = RenderCapability.Tier >> 16;
if (tier < 2)
{
// Disable expensive effects
DisableDropShadows();
DisableAnimations();
UseSimplifiedVisuals();
}
}