Attached behaviours in WPF

WPF is all about composition. Good WPF code is made up of lots of small, specific, reusable components — things like value converters, commands, styles and selectors — brought together with XAML. However, one area where there’s no obvious built-in means of composition is behaviour. You can’t style event handling the way you can style appearance. So traditionally, customising the behaviour of a control means writing a new control (though hopefully reusing the existing one!). This is a bit heavyweight, and leads to a combinatoric explosion if you have a bunch of optional behaviours which you want to be able to turn on and off on a case-by-case basis.

Attached behaviours are a solution to this problem. Using attached behaviours, you can bundle up a piece of event-handling logic in a small, specific, reusable package which you can then apply to controls using XAML — even using styling!

Let’s consider a simple example. Suppose we want to create a text box that only allows the user to enter digits. We could of course hook the PreviewTextInput event and block all non-digit input:

<TextBox PreviewTextInput="TextBox_PreviewTextInput" />
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
  foreach (char ch in e.Text)
    if (!Char.IsDigit(ch))
      e.Handled = true;
}

This approach works fine as a specification of behaviour for that one specific text box, but it’s not reusable or composable. First, the event handler has to be in the code-behind for the window, so if we want digit-only text boxes in another window, we end up duplicating the code. Second, we can’t compose this with other behaviour that triggers on the PreviewTextInput event: a control can’t have two handlers for the same event. Third, if there’s logic that depends on multiple events (say, a key-down/key-up pair), the behaviour is no longer atomic: we might remember to hook up the key-down event but forget the key-up event. Fourth, it can’t be hooked up to the control using the normal WPF customisation system of styles and templates.

The solution is to rework the event handling code as an attached behaviour. An attached behaviour is simply an attached property that, when it is set on an object, does something to that object. (As distinct from typical attached properties, which just associate some data with the object for something else to consume. In fact, attached behaviours may be totally uninterested in the value of the property they’re masquerading as — they may care only about who they’re being attached to.) Attached behaviours work their magic in the property changed callback, which is called by WPF when the value of the property on an object changes — of greatest interest, when the value of the property on an object is first set. Let’s take a look, starting with the attached property boilerplate.

public static class InputBehaviour
{
  public static bool GetIsDigitOnly(DependencyObject obj)
  {
    return (bool)obj.GetValue(IsDigitOnlyProperty);
  }
 
  public static void SetIsDigitOnly(DependencyObject obj, bool value)
  {
    obj.SetValue(IsDigitOnlyProperty, value);
  }
 
  public static readonly DependencyProperty IsDigitOnlyProperty =
    DependencyProperty.RegisterAttached("IsDigitOnly", 
    typeof(bool), typeof(InputBehaviour), 
    new UIPropertyMetadata(false, OnIsDigitOnlyChanged));
}

There’s nothing unusual going on here — this is a completely boilerplate attached property definition, straight out of the Visual Studio snippet except for the addition of a property changed callback named OnIsDigitOnlyChanged. What does that callback look like?

private static void OnIsDigitOnlyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
  // ignoring error checking
  TextBox textBox = (TextBox)sender;
  bool isDigitOnly = (bool)(e.NewValue);
 
  if (isDigitOnly)
    textBox.PreviewTextInput += BlockNonDigitCharacters;
  else
    textBox.PreviewTextInput -= BlockNonDigitCharacters;
}
 
private static void BlockNonDigitCharacters(object sender, TextCompositionEventArgs e)
{
  foreach (char ch in e.Text)
    if (!Char.IsDigit(ch))
      e.Handled = true;
}

What’s going on here? When InputBehaviour.IsDigitOnly is set on a TextBox, if it’s being set to true, we hook up the TextBox’s PreviewTextInput event (giving it the same handler as in the original example). If it’s being set to false, we remove that event handler. So what happens when WPF loads the following XAML?

<TextBox local:InputBehaviour.IsDigitOnly="True" />

As WPF loads the XAML, it sets the InputBehaviour.IsDigitOnly property to true on the TextBox. That in turn triggers the property changed callback. And that in turn hooks up the TextBox’s PreviewTextInput event so that it will reject all non-digit input. We’ve got the same behaviour we had in the original one-off event handling example, but now it’s in a nicely packaged, reusable form. We can set IsDigitOnly on text boxes from different windows. We can compile the InputBehaviour class into a library and reuse it from other assemblies, in pure XAML. We can compose it with other behaviours and we can even bundle it up in a style:

<Window.Resources>
  <Style x:Key="BigHonkingDigitOnlyTextBox" TargetType="TextBox">
    <Setter Property="FontSize" Value="36" />  <!-- big -->
    <Setter Property="local:InputBehaviour.HonkOnKeyPress" Value="True" />  <!-- honking -->
    <Setter Property="local:InputBehaviour.IsDigitOnly" Value="True" />  <!-- digit-only -->
  </Style>
</Window.Resources>
 
<StackPanel>
  <TextBox Margin="10" Style="{StaticResource BigHonkingDigitOnlyTextBox}" />
</StackPanel>

If you want to learn more, there’s a reasonable amount of material out there about attached behaviours, describing implementation techniques and going much further into possible applications than I’ve discussed here. Josh Smith’s blog is a good place to start, and John Gossman has an article about how it’s applied in Expression Blend. At any rate, it’s a handy trick and a good way to refactor repetitive event handling code. And the same technique can also be used for some other neat little tricks. We’ll talk about those in a future article.

Tagged as WPF

15 Responses to “Attached behaviours in WPF”

  • Classic Example
    Good Work

  • Great article!

  • Very nice! Kudos on a really great pattern. Definitely adding to my bag of tricks!

  • This is the best introductory article on attached behaviours that I’ve come across so far. Nice work!

  • I like your approach about using the property behaviour to accept digits only; however, I tried to use your code and I get the following error: Error 1 Cannot resolve the Style Property ‘IsDigitOnly’. Verify that the owning type is the Style’s TargetType, or use Class.Property syntax to specify the Property. Line 8
    Note: I am new at WPF and I was wonderting if you had short sample that is complete.

  • Hi ace,

    It sounds like you may not be qualifying the property name in the style declaration. Your setter should read:

    Do you have the local:InputBehaviour qualifier?

    I don’t have a sample to hand but if you still get errors let me know and I’ll dig one out!

  • I am having the same problem, I am new to WPF and I want to keep separate XAML file for styles.

    Any help will be appreciable…….

  • Hi Vishal,

    Check the property name in the Setter. It should be “local:InputBehaviour.IsDigitOnly”. You may be missing the “local:InputBehaviour.” prefix.

  • Nice article… very useful…

  • I am getting the exact same error. It works when I use the attached property in a project using a style in the window resources or application resources. But, when I create a UserControl in the same project and try to use it that way I get the exact same error:

    What is going on here

  • Hi Bob,

    There’s no reason you shouldn’t be able to use an attached behaviour in a user control. If you could throw up some repro code on pastebin or somewhere I’d be glad to take a look.

  • A bit late to the party here but I was also having issues with the Style not being able to resolve the attached property. If I set the attached property on the control then everything worked fine but putting it in a Style (whether that was in the Resources section of the Window/UserControl/App or in a separate ResourceDictionary file) just wasn’t working. Then I noticed in the declaration of my attached property I had the owner type set to – typeof(OnScreenKeyboard) – instead of – typeof(OnScreenKeyboardBehaviours). Changing this fixed it all up, although I’m not exactly sure why since directly setting the attached property on the control was working fine. Any ideas Ivan?

  • Hi Duncan,

    Afraid not. I’ve never been terribly clear on the purpose of the ‘owner type’ thing on dependency properties, so I’m not sure why it would impact the workings of the DP in styling. Maybe the styling engine looks up DPs by their owner type rather than as properties of the qualifying (declaring) type; but that would mean directly setting the attached property worked in a different way, which seems weird. Would be fascinated if you manage to find out anything more though!

  • Why attached properties[behavior] are static? Can they be non-static?

  • The dependency properties are static because they are global objects. (Remember, a DependencyProperty represents a property declaration, and is therefore at a class level, even though the values of that DP are per-instance.) The change callbacks are therefore static because they have to be referenced from the static DP — the DP is not associated with a specific DependencyObject *instance* on which it could perform the callback. The callback only knows the instance when it gets a sender.

    You can have the static callback delegate to an instance method by casting sender to the expected type, and calling an instance method on that. However since attached behaviours are usually used to extend an object with functionality that isn’t built in, such an instance method doesn’t usually exist. It is similar to why C# extension methods are static.

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top