[WPF] Attached Properties as an alternative to code behind

It's crazy the things you can do by writing a few lines of code behind. WPF controls expose so many events that you can really change the behavior of a control without deriving it.

The problem with code behind is that it's OK to write a small event handler in one place, but you can't easily reuse code from one project to another.

If only we could reuse the code behind just by adding a few things in the XAML... or better, just by applying a <Style>...

The principle

Well, we can almost do that, here is the strategy:

  1. Create an Attached Property
  2. In the PropertyChangedCallback, attach to the target's events
  3. Create a <Style> with a <Setter> that set the property

Sounds simple, right ?

A concrete example

Let see a simple example.

Let's say I want a TextBox that only accepts hexadecimal characters. This can be done very easily in code behind by adding an event handler on TextBox.PreviewTextInput.

Now, you can do exactly the same with the following attached property:

static class TextBoxHelpers
{
    const string ALLOWED_CHARS = "0123456789ABCDEFabcdef";

    public static bool GetOnlyHexadecimal(TextBox textBox)
    {
        return (bool)textBox.GetValue(OnlyHexadecimalProperty);
    }

    public static void SetOnlyHexadecimal(TextBox textBox, bool value)
    {
        textBox.SetValue(OnlyHexadecimalProperty, value);
    }

    public static readonly DependencyProperty OnlyHexadecimalProperty =
        DependencyProperty.RegisterAttached(
            "OnlyHexadecimal",typeof(bool), typeof(TextBoxHelpers), 
            new PropertyMetadata(false, OnOnlyHexadecimalChanged));

    static void OnOnlyHexadecimalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBox = d as TextBox;

        if( textBox == null)
            throw new InvalidOperationException("This property can only be applied to TextBox");

        var oldValue = (bool)e.OldValue;
        if (oldValue)
        {
            textBox.PreviewTextInput -= OnPreviewTextInput;
        }

        var newValue = (bool)e.NewValue;
        if (newValue)
        {
            textBox.PreviewTextInput += OnPreviewTextInput;
        }
    }

    static void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        e.Handled = !IsTextAllowed(e.Text);
    }

    static bool IsTextAllowed(string text)
    {
        return text.All(ALLOWED_CHARS.Contains);
    }
}

Thanks to that simple attached property, you can transform any TextBox into a TextBox that only accepts hexadecimal characters:

<TextBox my:TextBoxHelpers.OnlyHexadecimal="True" />

And you're done.

Then if you need to apply this behavior from a style, you just write:

<Style TargetType="TextBox">
    <Setter Property="my:TextBoxHelpers.OnlyHexadecimal" Value="True" />
</Style>  

See the sample project on GitHub

Thanks you to my talented coworker Franck for showing me this.