#637 – Block Repeating Keys by Setting Handled to true

In a keypress event handler, the IsRepeat property indicates whether the current keypress is a result of a character that is repeating because the user is holding the key down.  If you want to prohibit the use of repeating keys in a control, you can check this property and set the Handled property to true if IsRepeat is true.

Let’s say that we want to prohibit repeating characters in a TextBox.  We can do this by adding logic to a handler for the PreviewKeyDown event.  We don’t do this in the KeyDown event, since the TextBox will swallow this event for certain keys.

We define the handler in XAML.

<TextBox HorizontalAlignment="Center" Width="150" Margin="10"
         PreviewKeyDown="TextBox_PreviewKeyDown" />

We then set Handled based on the value of IsRepeat.

        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = e.IsRepeat;
        }

#636 – Keyboard Events that Fire When A Key Is Held Down

When you hold a key down in Windows, the key begins to repeat after a short delay, as if you you were repeatedly pressing it.

While a key is held down, the PreviewKeyDown, KeyDown and PreviewTextInput events will fire repeatedly.

You can detect whether a keypress is the original/first keypress, or one of the repeats, by checking the KeyEventArgs.IsRepeat property.

Let’s say we press the ‘a’ key while a TextBox has focus and hold it down long enough for three ‘a’ characters to be inserted.  We’ll get the following events:

  • PreviewKeyDown, Key = A, IsRepeat = False
  • KeyDown, Key = A, IsRepeat = False
  • PreviewTextInput, Text = a
  • (TextBox contains “a”)
  • PreviewKeyDown, Key = A, IsRepeat = True
  • KeyDown, Key = A, IsRepeat = True
  • PreviewTextInput, Text = a
  • (TextBox contains “aa”)
  • PreviewKeyDown, Key = A, IsRepeat = True
  • KeyDown, Key = A, IsRepeat = True
  • PreviewTextInput, Text = a
  • (TextBox  contains “aaa”)
  • PreviewKeyUp, Key = A, IsRepeat = False
  • KeyUp, Key = A, IsRepeat = False

#635 – Using a Value Converter to Change User Input

Instead of modifying user input by handling the TextChanged event, you can use a value converter to change the text input.  Below, we bind the Text property of a TextBox to a string property and specify a converter, which will convert vowels to uppercase.

<Window x:Class="WpfApplication9.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication9"
        Title="TextChanged"
        Height="150" Width="400">

    <Window.Resources>
        <ResourceDictionary>
            <local:CapVowelsConverter x:Key="capVowelsConverter"/>
        </ResourceDictionary>
    </Window.Resources>

    <StackPanel Orientation="Vertical">
        <TextBox HorizontalAlignment="Center" Width="150" Margin="10"
                 Text="{Binding Path=MyText, Converter={StaticResource capVowelsConverter}, UpdateSourceTrigger=PropertyChanged}"/>
        <Button Content="What Is My Text?" HorizontalAlignment="Center" Margin="10" Click="Button_Click"/>
    </StackPanel>
</Window>

Here’s the code for the value converter:

    public class CapVowelsConverter : IValueConverter
    {
        // From bound property TO the control -- no conversion
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        // To bound property FROM the control -- capitalize vowels
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string input = (string)value;

            const string vowels = "aeiou";

            StringBuilder sbInput = new StringBuilder(input);
            for (int i = 0; i < sbInput.Length; i++)
            {
                if (vowels.Contains(char.ToLowerInvariant(sbInput[i])))
                    sbInput[i] = char.ToUpper(sbInput[i]);
            }

            return sbInput.ToString();
        }
    }


#634 – Modifying Text in the TextChanged Event

Text-based controls like TextBox fire the TextChanged to indicate that their text has changed.  You can use this event to make changes to text being entered by the user, for example to convert all vowels in the TextBox to their uppercase equivalents.  This is possible because you can just set the Text property of a TextBox control from within the event.

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            Trace.WriteLine(string.Format("=>TextBox_TextChanged, now {0}", ((TextBox)sender).Text));
            TextBox tb = (TextBox)sender;

            // Preserve caret position so that we can later restore it
            int pos = tb.CaretIndex;

            tb.Text = CapVowels(tb.Text);

            tb.CaretIndex = pos;
        }

        private string CapVowels(string input)
        {
            const string vowels = "aeiou";

            StringBuilder sbInput = new StringBuilder(input);
            for (int i = 0; i < sbInput.Length; i++)
            {
                if (vowels.Contains(char.ToLowerInvariant(sbInput[i])))
                    sbInput[i] = char.ToUpper(sbInput[i]);
            }

            return sbInput.ToString();
        }

#633 – TextChanged Event Fires After TextBox Text Has Changed

You can intercept the PreviewKeyDown event for a TextBox to look at each key pressed while the TextBox has focus.  You can also intercept the PreviewTextInput event to look at text that is being entered into the TextBox.  However, these events still won’t handle all possible changes to the text in the TextBox.  For example, you might right-click in the TextBox and select Paste to paste some text.

To see all changes to the text in a TextBox, you can handle the TextChanged event.  This event fires after the text has changed.

In the TextChanged event, the easiest way to inspect the text being entered is to just look at the value of the Text property.  This will contain the new contents of the TextBox.

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            Trace.WriteLine(string.Format("TextBox_TextChanged, Text now = [{0}]", ((TextBox)sender).Text));
        }

For example, if I just enter the text “Harpo”:

 

#632 – Block Input Using PreviewTextInput

You can use the PreviewTextInput event for a control that accepts text input to block certain characters from being entered into the control.

To prevent a particular character from being entered into the control, simply set the Handled property of the TextCompositionEventArgs parameter to true.  This will intercept the event routing and will prevent the control from receiving the text.

<TextBox Text="" HorizontalAlignment="Center" Width="150"
     PreviewTextInput="TextBox_PreviewTextInput" />
    private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            // No e's allowed
            if ((e.Text == "e") || (e.Text == "E"))
                e.Handled = true;
        }

#631 – Event Sequence for KeyPressUp, KeyPressDown and TextInput

The full event sequence for keyboard related events is shown below, as they propagate down (tunneling) or up (bubbling) the logical tree.

If we have a Window that contains a StackPanel, which in turn contains a TextBox, the sequence of events would be:

  • Window_PreviewKeyDown
  • StackPanel_PreviewKeyDown
  • TextBox_PreviewKeyDown
  • TextBox_KeyDown
  • StackPanel_KeyDown
  • Window_KeyDown
  • Window_PreviewTextInput
  • StackPanel_PreviewTextInput
  • TextBox_PreviewTextInput
  • TextBox_TextInput – suppressed/handled
  • StackPanel_TextInput – suppressed/handled
  • Window_TextInput – suppressed/handled
  • Window_PreviewKeyUp
  • StackPanel_PreviewKeyUp
  • TextBox_PreviewKeyUp
  • TextBox_KeyUp
  • StackPanel_KeyUp
  • Window_KeyUp

Note that the TextInput event is listed, since TextInput events would normally fire for the originating control and then propagate up the logical tree.  But in the case of TextBox, the TextInput event is marked as handled and therefore does not fire.

#630 – PreviewTextInput and TextInput Events

In addition to the four main keypress events–PreviewKeyDown, KeyDownPreviewKeyUp and KeyUp–an UIElement can fire two other events related to keyboard input.  Both fire when the user presses a key, or combination of keys, that results in the control receiving some text.  They do not fire when keys are pressed that don’t result in the keyboard sending text (e.g. the Backspace key).

Here’s the updated sequence of events:

  • PreviewKeyDown – tunneling
  • KeyDown – bubbling
  • PreviewTextInput  – tunneling
  • TextInput – bubbling
  • PreviewKeyUp – tunneling
  • KeyUp – bubbling

Note that controls that normally accept text and do something with it will suppress the TextInput event, marking it as handled.  For example, the TextBox control takes the text input and adds it to to the TextBox, so it marks TextInput as already handled.  The TextBox is saying that it already “handled” the text, so it doesn’t need to pass the event on to anybody else.

#629 – Some Controls May Swallow Keypress Events

If you are handling KeyUp and/or KeyDown events for a control, you’ll notice that certain keys may be “swallowed” by a particular control.  In other words, you might not get all of the keypress events.

A control may intercept a particular event and mark it as handled if it interprets the keypress and uses it within the control.

For example, a TextBox control will “swallow” the KeyDown event for the Backspace key, since this key is used to delete characters in the control.

In the example below, I press the ‘a’ key and see all four (PreviewKeyDown, KeyDown, PreviewKeyUp, KeyUp) keypress events.  However, when I press Backspace, I see only three events–the TextBox intercepts the KeyDown event, erases a character, and marks the event as handled.

 

#628 – Key Up/Down Sequence When Using ALT Key

When you use the Alt-key in combination with another key, the control that has focus will receive KeyUp and KeyDown events for both the Alt key and the combination key.

When using the Alt key, the Key property of the KeyEventArgs object will be Key.System and the actual key being pressed will be available in the SystemKey property.  This is true for both the Alt key and the combination key.

For example, pressing Alt-Q while a TextBox control has focus will result in the following events for the TextBox, in the sequence listed.

  • PreviewKeyDown, Key = System, SystemKey = LeftAlt
  • KeyDown, Key = System, SystemKey = LeftAlt
  • PreviewKeyDown, Key = System, SystemKey = Q
  • KeyDown, Key = System, SystemKey = Q
  • PreviewKeyUp, Key = System, SystemKey = Q
  • KeyUp, Key = System, SystemKey = Q
  • PreviewKeyUp, Key = System, SystemKey = LeftAlt
  • KeyUp, Key = System, SystemKey = LeftAlt