[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:
- Create an Attached Property
- In the
PropertyChangedCallback
, attach to the target’s events - 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.