A Simple SelectAll Behavior for TextBoxes

Not exactly a brand new topic in the area of WPF development are attached behaviors. However, in my opinion, they should be used more often - that's why I decided to write about it. Let us start from the beginning.

What is an attached behavior?

From a high level perspective attached behaviors allow you to write a snippet of code that can add additional features to a WPF control without doing something that drastic as inheriting from an existing control class. As we all know inheritance should not be the default reaction to adding behavior to a class. Most often better results can be achieved in adding additional functionality via composition. And this is the approach of attached behaviors.

How do I build an attached behavior?

Two options: Build it from scratch or use a nice and simple SDK from the Blend team. This SDK allows you to build an attached behavior in a very straight forward manner. If you have VS2012 with Update 2 installed, you should find the System.Windows.Interactivity.dll assemby per default in "C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.5". In earlier versions it is located in the Expression Blend installation folder. Many WPF developers seem to be unaware of this little gem so I would like to advertise it a bit by showing you a brief example.

The out-of-the-box WPF TextBox control has no property that can be changed to trigger the complete selection of the TextBox's text when a user navigates, e.g. with the TAB key, into that TextBox. When a user is required to enter a couple of input fields on a form this feature is often requested. An initial thought might be: OK, let us create a SelectAllTextBox that derives from the TextBox class and introduces a new property to enable that behavior. The alternative is as follows:

  • Declare a normal TextBox in XAML that should be decorated with additional behavior
  • Declare an external class (i.e. our attached behavior class) that extends the features of the TextBox control by using the TextBox instance's events and properties.
  • Finally, hook the TextBox that needs additional functionality with the attached behavior.

The first step is straight forward - we just declare a TextBox control in XAML:

 <StackPanel>
    <TextBlock>TextBox without behavior:</TextBlock>
    <TextBox>this is text</TextBox>
</StackPanel>

The second step requires us to define a new class - our attached behavior. To get a basic infrastructure that eases our development and wires things up seamlessly we need to implement a class that derives from the abstract base class Behavior<T> which is defined in the Blend SDK:

 using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
 
public class SelectAllTextBoxBehavior : Behavior<TextBox>
{
    /// <summary>
    /// Called after the behavior is attached to an AssociatedObject.
    /// </summary>
    /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
    protected override void OnAttached()
    {
        base.OnAttached();
    }
        
    /// <summary>
    /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
    /// </summary>
    /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
    protected override void OnDetaching()
    {
        // Recommended best practice: 
        // Detach the registered event handler to avoid memory leaks.
        base.OnDetaching();
    }
}

Blend's base class provides us two hooks and - most important - a property called AssociatedObject. This property will contain a reference to the control that is decorated with our behavior. The OnAttached and OnDetached hooks define the life cycle where our behavior is brought into play and is removed. In our small example we are interested in getting informed about focus changes of the TextBox. Let us extend the OnAttached method accordingly:

 /// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
protected override void OnAttached()
{
    base.OnAttached();
    this.AssociatedObject.GotFocus += this.OnTextBoxGotFocus;
}

We wanted to be able to select all Text as soon as the TextBox receives focus. Luckily there is a SelectAll method defined that we can use to select the complete text in a TextBox  In consequence, the code inside the event handler could look like this:

 /// <summary>
/// Handles the GotFocus event of the TextBox control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
private void OnTextBoxGotFocus(object sender, RoutedEventArgs e)
{
 this.AssociatedObject.SelectAll();
}

 (Important: The code so far would actually not behave completely as expected - edge cases like mouse selection would not work. The full sample code attached to this post handles that.) 

To be a nice citizen we should clean up after our selves in case the behavior is dynamically removed from the targeted TextBox at runtime. Leaving a dangling event handler could introduce a memory leak. So finally we should also take care in putting some clean-up code into the OnDetaching method:

 protected override void OnDetaching()
{
    // Recommended best practice: 
    // Detach the registered event handler to avoid memory leaks.
    this.AssociatedObject.GotFocus -= this.OnTextBoxGotFocus;
    base.OnDetaching();
}

 The final step is the registration of the Behavior. We could do it in code or (most often) declaratively in XAML:

 [...] xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" [...]
 <StackPanel>
    <TextBlock>TextBox with behavior:</TextBlock>
    <TextBox>this is text
        <i:Interaction.Behaviors>             <local:SelectAllTextBoxBehavior></local:SelectAllTextBoxBehavior>
        </i:Interaction.Behaviors>
    </TextBox>
</StackPanel>

 That's it - all is hooked up now!

Download Source

The complete source code can be downloaded from SkyDrive.

Want to know more about Attached Behaviors?

Josh Smith has an exhaustive article about Attached Behaviors on Codeplex.