Help us improve
Share bugs, ideas, or general feedback.
From wpf-dev-pack
Generates WPF CustomControl C# class and XAML ControlTemplate style from a control name. Use when creating a new custom control, scaffolding a templated control, or generating CustomControl boilerplate code.
npx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packHow this skill is triggered — by the user, by Claude, or both
Slash command
/wpf-dev-pack:make-wpf-custom-controlsonnetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**If `$0` is empty, use the AskUserQuestion tool to ask: "Enter the CustomControl name (e.g., CircularProgress, RangeSlider)". Do NOT proceed until a valid name is provided. Use the response as the ControlName for all subsequent steps.**
Designs stand-alone WPF CustomControl styles via ResourceDictionary files merged in Generic.xaml. Use for reusable control libraries and theme organization.
Designs WinUI 3 UIs and reviews XAML for correctness, covering layout planning, control selection, Fluent Design, theming (Light/Dark/HighContrast), typography, spacing, brushes, accessibility, and data binding.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Share bugs, ideas, or general feedback.
If $0 is empty, use the AskUserQuestion tool to ask: "Enter the CustomControl name (e.g., CircularProgress, RangeSlider)". Do NOT proceed until a valid name is provided. Use the response as the ControlName for all subsequent steps.
Generate a $0 CustomControl.
{BaseClass} with the appropriate WPF base class (e.g., Control, Button, ContentControl, ItemsControl) based on the control name and context.{Namespace} with the project's root namespace detected from csproj or existing code.$0 must be PascalCase$0 name and intended usageCreate $0.cs:
using System.Windows;
using System.Windows.Controls;
namespace {Namespace}.Controls;
/// <summary>
/// $0 - Custom WPF control based on {BaseClass}
/// </summary>
[TemplatePart(Name = "PART_Root", Type = typeof(Border))]
[TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
[TemplateVisualState(GroupName = "CommonStates", Name = "MouseOver")]
[TemplateVisualState(GroupName = "CommonStates", Name = "Pressed")]
[TemplateVisualState(GroupName = "CommonStates", Name = "Disabled")]
public class $0 : {BaseClass}
{
#region Static Constructor
static $0()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof($0),
new FrameworkPropertyMetadata(typeof($0)));
}
#endregion
#region Dependency Properties
/// <summary>
/// Example dependency property
/// </summary>
public static readonly DependencyProperty ExampleProperty =
DependencyProperty.Register(
nameof(Example),
typeof(string),
typeof($0),
new FrameworkPropertyMetadata(
defaultValue: string.Empty,
flags: FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback: OnExampleChanged));
public string Example
{
get => (string)GetValue(ExampleProperty);
set => SetValue(ExampleProperty, value);
}
private static void OnExampleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is $0 control)
{
control.OnExampleChanged((string)e.OldValue, (string)e.NewValue);
}
}
protected virtual void OnExampleChanged(string oldValue, string newValue)
{
// Handle property change
}
#endregion
#region Template Parts
private Border? _partRoot;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_partRoot = GetTemplateChild("PART_Root") as Border;
UpdateVisualState(false);
}
#endregion
#region Visual States
private void UpdateVisualState(bool useTransitions)
{
if (!IsEnabled)
{
VisualStateManager.GoToState(this, "Disabled", useTransitions);
}
else if (IsMouseOver)
{
VisualStateManager.GoToState(this, "MouseOver", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Normal", useTransitions);
}
}
protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
{
base.OnMouseEnter(e);
UpdateVisualState(true);
}
protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
{
base.OnMouseLeave(e);
UpdateVisualState(true);
}
#endregion
}
Create Themes/$0.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:{Namespace}.Controls">
<!-- Resources -->
<SolidColorBrush x:Key="$0Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="$0Foreground" Color="#000000"/>
<SolidColorBrush x:Key="$0BorderBrush" Color="#CCCCCC"/>
<SolidColorBrush x:Key="$0MouseOverBackground" Color="#E5F3FF"/>
<!-- Style -->
<Style TargetType="{x:Type local:$0}">
<Setter Property="Background" Value="{StaticResource $0Background}"/>
<Setter Property="Foreground" Value="{StaticResource $0Foreground}"/>
<Setter Property="BorderBrush" Value="{StaticResource $0BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:$0}">
<Border x:Name="PART_Root"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="4">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="PART_Root"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#E5F3FF"
Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="PART_Root"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#CCE4FF"
Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="PART_Root"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Add to Themes/Generic.xaml MergedDictionaries:
<ResourceDictionary.MergedDictionaries>
<!-- Existing entries -->
<ResourceDictionary Source="/Themes/$0.xaml"/>
</ResourceDictionary.MergedDictionaries>
After generation, provide:
<!-- Usage Example -->
<local:$0 Example="Hello World"/>