Implements WPF Adorner decoration layers with AdornerLayer, AdornerDecorator, and custom Adorner patterns. Use when building drag handles, validation indicators, watermarks, selection visuals, or resize grips.
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/implementing-wpf-adorners/SKILL.md -a claude-code --skill implementing-wpf-adornersInstallation paths:
.claude/skills/implementing-wpf-adorners/# WPF Adorner Patterns
Adorner is a mechanism for overlaying decorative visual elements on top of UIElements.
## 1. Adorner Concept
### 1.1 Characteristics
- **AdornerLayer**: Separate rendering layer that holds Adorners
- **Z-Order**: Always renders above the adorned element
- **Layout Independent**: No effect on target element's layout
- **Event Support**: Can receive mouse/keyboard events
### 1.2 Usage Scenarios
| Scenario | Description |
|----------|-------------|
| **Validation Display** | Input field error display |
| **Drag Handles** | Element move/resize handles |
| **Watermark** | Hint text for empty TextBox |
| **Selection Display** | Highlight selected elements |
| **Tooltip/Badge** | Additional info display on elements |
| **Drag and Drop** | Preview during drag |
---
## 2. Basic Adorner Implementation
### 2.1 Simple Adorner
```csharp
namespace MyApp.Adorners;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
/// <summary>
/// Adorner that draws border around element
/// </summary>
public sealed class BorderAdorner : Adorner
{
private readonly Pen _borderPen;
public BorderAdorner(UIElement adornedElement) : base(adornedElement)
{
_borderPen = new Pen(Brushes.Red, 2)
{
DashStyle = DashStyles.Dash
};
_borderPen.Freeze();
// Disable mouse events (decoration only)
IsHitTestVisible = false;
}
protected override void OnRender(DrawingContext drawingContext)
{
var rect = new Rect(AdornedElement.RenderSize);
// Draw border
drawingContext.DrawRectangle(null, _borderPen, rect);
}
}
```
### 2.2 Applying Adorner
```csharp
// Get AdornerLayer
var adornerLayer = AdornerLayer.GetAdornerLayer(targetElement);
if (adornerLayer is not null)
{
// Add Adorner
var adorner = new BorderAdorner(targetElement);
adornerLayer.Add(adorner);
}
```
### 2.3 Removing Adorner
```csharp
// Remove all Adorners from s