authoring-wpf-controls
1
总安装量
1
周安装量
#76690
全站排名
安装命令
npx skills add https://github.com/christian289/dotnet-with-claudecode --skill authoring-wpf-controls
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
continue
1
kimi-cli
1
Skill 文档
WPF Control Authoring Guide
A guide for decision-making when authoring WPF controls.
1. Do You Need a New Control?
Review alternatives first. Thanks to WPF’s extensibility, most requirements can be solved without creating a new control.
| Requirement | Alternative | Example |
|---|---|---|
| Change appearance only | Style | Unify TextBlock to red Arial 14pt |
| Change control structure | ControlTemplate | Make RadioButton look like traffic light |
| Change data display method | DataTemplate | Add checkbox to ListBox items |
| Change state-based behavior | Trigger | Make selected item bold red |
| Display composite content | Rich Content | Show image+text together in Button |
When a new control is needed:
- New functionality/behavior not available in existing controls
- Reusable composite components
- Special input/interaction patterns
2. Base Class Selection
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Control Type Decision â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â â
â âââââââââââââââ âââââââââââââââ âââââââââââââââââââ â
â â UserControl â â Control â â FrameworkElementâ â
â ââââââââ¬âââââââ ââââââââ¬âââââââ ââââââââââ¬âââââââââ â
â â â â â
â Combine existing ControlTemplate Direct rendering â
â Quick development Customization Full control â
â No template Theme support Performance â
â optimization â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
UserControl Selection Criteria
- â Combining existing controls is sufficient
- â Prefer application-like development approach
- â ControlTemplate customization not needed
- â Theme support not needed
Control Selection Criteria (Recommended)
- â Need appearance customization via ControlTemplate
- â Need various theme support
- â Need WPF built-in control level extensibility
- â Complete separation of UI and logic
FrameworkElement Selection Criteria
- â Appearance not achievable by simple element composition
- â Need direct rendering via OnRender
- â Custom composition based on DrawingVisual
- â Extreme performance optimization needed
3. Principles for Designing Stylable Controls
3.1 Don’t Strictly Enforce Template Contract
// â Wrong: Throws exception if Part is missing
public override void OnApplyTemplate()
{
var button = GetTemplateChild("PART_Button") as Button;
if (button == null)
throw new InvalidOperationException("PART_Button required!");
}
// â
Correct: Works even if Part is missing
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ButtonElement = GetTemplateChild("PART_Button") as Button;
// If null, only that feature is disabled, control continues to work
}
Core Principles:
- ControlTemplate may be incomplete at design time
- Panel doesn’t throw exceptions for too many or too few children
- If required elements are missing, only disable that feature
3.2 Helper Element Patterns
| Type | Description | Example |
|---|---|---|
| Standalone | Independent, reusable | Popup, ScrollViewer, TabPanel |
| Type-based | Recognizes TemplatedParent, auto-binding | ContentPresenter, ItemsPresenter |
| Named | Referenced in code via x:Name | PART_TextBox, PART_Button |
// Type-based: ContentPresenter automatically binds to TemplatedParent.Content
<ContentPresenter />
// Named: Direct reference needed in code
<TextBox x:Name="PART_EditableTextBox" />
3.3 State/Behavior Expression Priority
Prefer higher items:
- Property Binding –
ComboBox.IsDropDownOpenâToggleButton.IsChecked - Trigger/Animation – Background color change on Hover state
- Command –
ScrollBar.LineUpCommand - Standalone Helper –
TabPanelinTabControl - Type-based Helper –
ContentPresenterinButton - Named Helper –
TextBoxinComboBox - Bubbled Event – Event bubbling from Named element
- Custom OnRender –
ButtonChromeinButton
4. DependencyProperty Implementation
DependencyProperty is required to support styles, bindings, animations, and dynamic resources.
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
nameof(Value),
typeof(int),
typeof(NumericUpDown),
new FrameworkPropertyMetadata(
defaultValue: 0,
propertyChangedCallback: OnValueChanged,
coerceValueCallback: CoerceValue));
public int Value
{
get => (int)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
// â ï¸ Don't add logic to CLR wrapper! It's bypassed during binding
// Use callbacks instead:
private static void OnValueChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) { }
private static object CoerceValue(DependencyObject d, object value)
=> Math.Clamp((int)value, 0, 100);
5. RoutedEvent Implementation
Use RoutedEvent to support bubbling, EventSetter, and EventTrigger.
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent(
nameof(ValueChanged),
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<int>),
typeof(NumericUpDown));
public event RoutedPropertyChangedEventHandler<int> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<int> e)
=> RaiseEvent(e);
6. Customization Support Strategy
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Exposure Strategy by Customization Frequency â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â â
â Very Frequent â Expose as DependencyProperty â
â (Background, Foreground, etc.) â
â â
â Sometimes â Expose as Attached Property â
â (Grid.Row, Canvas.Left, etc.) â
â â
â Rarely â Guide to redefine ControlTemplate â
â (Documentation required) â
â â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
7. Theme Resource Organization
ð Themes/
âââ Generic.xaml â Default (required)
âââ Aero.NormalColor.xaml â Windows Vista/7
âââ Luna.NormalColor.xaml â Windows XP Blue
âââ Luna.Homestead.xaml â Windows XP Olive
âââ Luna.Metallic.xaml â Windows XP Silver
Add ThemeInfo to AssemblyInfo.cs:
[assembly: ThemeInfo(
ResourceDictionaryLocation.SourceAssembly, // Theme-specific resources
ResourceDictionaryLocation.SourceAssembly)] // Generic resources
Set DefaultStyleKey in static constructor:
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Decision Checklist
Before Creating a New Control
- Can it be solved with Style?
- Can it be solved with ControlTemplate?
- Can it be solved with DataTemplate?
- Can it be solved with Trigger?
- Can it be solved with Rich Content?
Base Class Selection
- Need ControlTemplate customization? â Control
- Need theme support? â Control
- Combining existing controls is sufficient? â UserControl
- Need direct rendering? â FrameworkElement
Control Design
- Did you minimize Template Contract?
- Does it work even if Part is missing?
- Handling with feature disable instead of exception?
- Did you follow state expression priority?
Properties/Events
- Are style/binding supporting properties DependencyProperty?
- Is there no logic in CLR wrapper?
- Are events implemented as RoutedEvent?
Theme/Resources
- Is there a default style in Generic.xaml?
- Did you set ThemeInfo attribute?
- Did you set DefaultStyleKey?