Some poor chap posted a question on CodeProject asking how to convert a textbox
to be lower or upper case. My initial reaction to this (as with so much else), is to use MVVM and just handle the property change. I nearly posted this, but then I decided not to – instead, in this post I'd like to demonstrate the use of attached behaviours (or behaviors for those who don't like the u).
An attached behaviour allows us to do things without having to depend on other items, such as a trigger or actions. Now, there’s a handy class added to the System.Windows.Interactivity DLL called Behavior
. We derive from this to add the behaviour to a particular control type. In this example, we're going to hook into a TextBox
and add the functionality that we want to automatically handle the upper/lower casing as appropriate.
In the first part, we're going to add a DependencyProperty
that will accept the boolean to indicate whether or not it should be all upper or lower case (yes, I know that it would be better to use an enumeration here, but this is just a sample to demonstrate the functionality).
public static readonly DependencyProperty UpperLowerMaskProperty =
DependencyProperty.Register
("UpperLowerMask", typeof(bool), typeof(UpperOrLowerBehavior), null);
public bool UpperLowerMask
{
get { return (bool)GetValue(UpperLowerMaskProperty); }
set { SetValue(UpperLowerMaskProperty, value); }
}
As you can see, there’s nothing particularly frightening about the code so far. It’s a DP like any other that we've created before. Now, we've derived from Behavior
, so we need to override the OnAttached
and OnDetached
methods to hook/unregister the events that we're interested in from the TextBox
. The code to do this is shown here:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewKeyDown +=
new KeyEventHandler(AssociatedObject_PreviewKeyDown);
AssociatedObject.TextChanged +=
new TextChangedEventHandler(AssociatedObject_TextChanged);
DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewKeyDown -=
new KeyEventHandler(AssociatedObject_PreviewKeyDown);
AssociatedObject.TextChanged -=
new TextChangedEventHandler(AssociatedObject_TextChanged);
DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
}
As you can see, there are three things that we are interested in; the PreviewKeyDown
, TextChanged
events, and the paste handler. PreviewKeyDown
is used to identify whether or not we want to handle the text changing, and retrieving the cursor position at that point in time. TextChanged
is used to handle the text being changed.
The code for these looks a lot like this:
void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e)
{
if (!_shouldBeCaptured) return;
if (UpperLowerMask)
AssociatedObject.Text = AssociatedObject.Text.ToUpperInvariant();
else
AssociatedObject.Text = AssociatedObject.Text.ToLowerInvariant();
AssociatedObject.SelectionStart = _keyPos + 1;
}
void AssociatedObject_PreviewKeyDown(object sender, KeyEventArgs e)
{
char key;
char.TryParse(e.Key.ToString(), out key);
_shouldBeCaptured = Keyboard.Modifiers == ModifierKeys.None && Char.IsLetter(key);
_keyPos = AssociatedObject.SelectionStart;
}
Again, as you can see, there’s nothing particularly scary here. The AssociatedObject
identifies the TextBox
that we're working on. Now that we have the behaviour, we need to use it. This means that we need to hook it into the XAML; here’s the page that we're working with:
<Window x:Class="AllLowerOrUpper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behavior="clr-namespace:AllLowerOrUpper"
xmlns:interact="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
Title="Upper/Lower Behavior" Height="350" Width="525">
<StackPanel>
<TextBox Width="500" >
<interact:Interaction.Behaviors>
<behavior:UpperOrLowerBehavior UpperLowerMask="true" />
</interact:Interaction.Behaviors>
</TextBox>
<TextBox Width="500" >
<interact:Interaction.Behaviors>
<behavior:UpperOrLowerBehavior UpperLowerMask="false" />
</interact:Interaction.Behaviors>
</TextBox>
</StackPanel>
</Window>
And that’s it – a simple attached behavior to cope with the upper/lower functionality. It’s not perfect code, it’s intended to demonstrate the use of attached behaviors, so please add whatever functionality you need. Feel free to download the related sample (and don't forget to rename the .doc file to .zip to get round the WordPress allowed files issue).