From wpf-dev-pack
Generates WPF IValueConverter or IMultiValueConverter classes with MarkupExtension pattern for direct XAML usage. Invoke via /wpf-dev-pack:make-wpf-converter for new converters or MultiValueConverter scaffolding.
How this skill is triggered — by the user, by Claude, or both
Slash command
/wpf-dev-pack:make-wpf-converterhaikuThe 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 Converter name (e.g., BoolToVisibility, NullToVisibility)". Do NOT proceed until a valid name is provided. Use the response as the ConverterName for all subsequent steps.**
If $0 is empty, use the AskUserQuestion tool to ask: "Enter the Converter name (e.g., BoolToVisibility, NullToVisibility)". Do NOT proceed until a valid name is provided. Use the response as the ConverterName for all subsequent steps.
Generate a $0Converter class with MarkupExtension pattern for direct XAML usage.
If multi is appended to the arguments, generate IMultiValueConverter instead of IValueConverter.
{Namespace} with the project's root namespace detected from csproj or existing code.{SourceType} and {TargetType} with the appropriate types based on the converter name (e.g., BoolToVisibility → bool, Visibility).{Project} with the target project path.# IValueConverter
/wpf-dev-pack:make-wpf-converter BoolToVisibility
# IMultiValueConverter
/wpf-dev-pack:make-wpf-converter AllTrue multi
Create this base class first in your Converters folder:
namespace {Namespace}.Converters;
/// <summary>
/// Base class for converters that can be used directly in XAML without resource declaration.
/// </summary>
public abstract class ConverterMarkupExtension<T> : MarkupExtension, IValueConverter
where T : class, new()
{
private static readonly Lazy<T> _converter = new(() => new T());
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter.Value;
}
public abstract object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture);
public virtual object? ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
throw new NotSupportedException("ConvertBack is not supported.");
}
}
namespace {Namespace}.Converters;
/// <summary>
/// Base class for multi-value converters with MarkupExtension support.
/// </summary>
public abstract class MultiConverterMarkupExtension<T> : MarkupExtension, IMultiValueConverter
where T : class, new()
{
private static readonly Lazy<T> _converter = new(() => new T());
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter.Value;
}
public abstract object? Convert(
object?[] values,
Type targetType,
object? parameter,
CultureInfo culture);
public virtual object?[] ConvertBack(
object? value,
Type[] targetTypes,
object? parameter,
CultureInfo culture)
{
throw new NotSupportedException("ConvertBack is not supported.");
}
}
namespace {Namespace}.Converters;
/// <summary>
/// Converts {SourceType} to {TargetType}.
/// </summary>
public sealed class $0Converter : ConverterMarkupExtension<$0Converter>
{
public override object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
// TODO: Implement conversion logic
if (value is not {SourceType} source)
{
return DependencyProperty.UnsetValue;
}
return source; // Replace with actual conversion
}
}
namespace {Namespace}.Converters;
/// <summary>
/// Combines multiple values into a single result.
/// </summary>
public sealed class $0Converter : MultiConverterMarkupExtension<$0Converter>
{
public override object? Convert(
object?[] values,
Type targetType,
object? parameter,
CultureInfo culture)
{
// Validate input values
if (values is null || values.Length < 2)
{
return DependencyProperty.UnsetValue;
}
// Check for unset values
if (values.Any(v => v == DependencyProperty.UnsetValue))
{
return DependencyProperty.UnsetValue;
}
// TODO: Implement multi-value conversion logic
return values;
}
}
<Window xmlns:conv="clr-namespace:MyApp.Converters">
<!-- No resource declaration needed! -->
<TextBlock Visibility="{Binding IsVisible, Converter={conv:BoolToVisibilityConverter}}"/>
<!-- With parameter -->
<TextBlock Visibility="{Binding IsHidden, Converter={conv:BoolToVisibilityConverter}, ConverterParameter=Invert}"/>
</Window>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{conv:FullNameConverter}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public sealed class BoolToVisibilityConverter : ConverterMarkupExtension<BoolToVisibilityConverter>
{
public override object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (value is not bool boolValue)
return Visibility.Collapsed;
var invert = parameter is "Invert" or "invert";
return (boolValue ^ invert) ? Visibility.Visible : Visibility.Collapsed;
}
}
public sealed class NullToVisibilityConverter : ConverterMarkupExtension<NullToVisibilityConverter>
{
public override object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
var isNull = value is null;
var invert = parameter is "Invert";
return (isNull ^ invert) ? Visibility.Collapsed : Visibility.Visible;
}
}
public sealed class InverseBoolConverter : ConverterMarkupExtension<InverseBoolConverter>
{
public override object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (value is not bool boolValue)
return false;
return !boolValue;
}
public override object? ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (value is not bool boolValue)
return false;
return !boolValue;
}
}
{Project}/
└── Converters/
├── ConverterMarkupExtension.cs # Base class
├── MultiConverterMarkupExtension.cs # Multi base class
├── BoolToVisibilityConverter.cs
├── NullToVisibilityConverter.cs
└── $0Converter.cs
global using System;
global using System.Globalization;
global using System.Linq;
global using System.Windows;
global using System.Windows.Data;
global using System.Windows.Markup;
| Aspect | StaticResource | MarkupExtension |
|---|---|---|
| Resource declaration | Required | Not required |
| XAML usage | {StaticResource Key} | {local:Converter} |
| Singleton | Manual | Built-in (Lazy) |
| Boilerplate | More | Less |
using-converter-markup-extension - Detailed MarkupExtension patternadvanced-data-binding - MultiBinding patternsnpx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packProvides C# base classes for IValueConverter and IMultiValueConverter as MarkupExtensions to enable direct XAML usage without StaticResource or resource dictionaries. Use when creating converters for WPF/MAUI data bindings.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.