#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.