#1,118 – An Example of Input that Obeys CurrentCulture

One step in internationalizing an application is to respect the current regional settings when reading numeric or date/time values from a user.

If you are parsing user-entered text and converting to numeric or date/time data, the Parse methods associated with individual data types respect the current regional settings.

Below, we read text from two TextBox controls, interpreting the first value as a double and the second as a DateTime using the corresponding Parse method.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                double num = double.Parse(txt1.Text);
                DateTime dt = DateTime.Parse(txt2.Text);
            }
            catch (Exception xx)
            {
                MessageBox.Show(xx.ToString());
            }
        }

On an English/US system, we can enter the data as “1.1” and “5/2/12”.  The date is interpreted as May 2nd.

1118-001

For French/France, we must enter “1,1” for the double.  “5/2/12” is interpreted as Feb 5th.

1118-002

Advertisement

#1,117 – Internationalization II – Obey CurrentCulture for Input

The first step in internationalizing an application is to ensure that your application honors the user’s current regional settings, as reflected by the CurrentCulture property of the application’s main thread.

Current culture impacts not only how you display the following types of data, but also how the user inputs this data:

  • Numeric data
  • Date/time values

These values are typically stored internally as numeric (e.g. double, int) or DateTime values.  The values exist in memory in a culture-agnostic form.  You need to worry about regional settings only when you display a value to the user or receive input from the user.

In .NET, if you use the Parse method of a numeric or date/time type to convert a user-supplied string to the internal type, the parse operation will expect the string to be in a format that is valid for the current region.

 

#1,084 – A KeyBinding Binds a Command to a Key

A user interface element has a CommandBindings collection containing command binding objects that indicate which commands are supported for the element and the code that the command is bound to.

User interface elements also have an InputBindings collection that contains KeyBinding and MouseBinding instances, each of which maps keyboard or mouse input to a command that is also present in the CommandBindings collection.

In the code below, we wire up the Open command for both key (Ctrl+O) and mouse (Ctrl+Left Click) input.

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open,
               (sender, e) => { MessageBox.Show("Executing the Open command"); },
               (sender, e) => { e.CanExecute = CanOpenIsChecked; }));

            // Ctrl+O = Open
            this.InputBindings.Add(new KeyBinding(ApplicationCommands.Open,
                new KeyGesture(Key.O, ModifierKeys.Control)));

            // Ctrl+Left Mouse Click = Open
            this.InputBindings.Add(new MouseBinding(ApplicationCommands.Open,
                new MouseGesture(MouseAction.LeftClick, ModifierKeys.Control)));
        }

We can now use either form of input to execute the Open command.

1084-001

#1,077 – Checking for Single Modifer vs. Multiple Modifier Keys

In keypress events, you can check to see if the user is also holding down one of the modifier keys (Ctrl, Alt, Shift, or Windows key).  You do this by checking the KeyEventArgs.KeyboardDevice.Modifiers property.

You sometimes want to check to see if one and only one modifier key is being held down (e.g. Ctrl key without Alt, Shift, or Windows).  You do this by checking to see if the Modifiers property is equal to one of the ModifierKeys enumerated values.

            if ((e.Key == Key.G) &&
                (e.KeyboardDevice.Modifiers == ModifierKeys.Control))
                MessageBox.Show("Ctrl+G detected, NO Alt/Shift/Windows");

You may also want to check to see if the control key is being pressed, either alone or in conjunction with one of the other modifier keys.  You do this by using a mask.

            if ((e.Key == Key.G) &&
                ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control))
                MessageBox.Show("Ctrl+G or Ctrl+Alt+G, Ctrl+Alt+Windows+G, etc.");

#1,076 – Two Ways to Check for Use of Modifier Keys in Keypress Handlers

You can check for the presence of modifier keys (e.g. Alt, Ctrl, Shift, or Windows key) in keypress handlers using the KeyboardDevice.Modifier property.  For example:

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.Key == Key.G) &&
                (e.KeyboardDevice.Modifiers == ModifierKeys.Control))
                MessageBox.Show("Ctrl+G detected");
        }

Note that this method doesn’t distinguish between whether you pressed the left vs. right Ctrl keys.  This is normally what you want.  If you do want to distinguish between the two, you can explicitly check for left vs. right.

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.Key == Key.G) &&
                (Keyboard.IsKeyDown(Key.LeftCtrl)))
                MessageBox.Show("Left Ctrl+G detected");
            else if ((e.Key == Key.G) &&
                (Keyboard.IsKeyDown(Key.RightCtrl)))
                MessageBox.Show("Right Ctrl+G detected");
        }

#1,075 – Triggering on IsKeyboardFocusWithin Property

You can set up a trigger that fires whenever a control’s IsKeyboardFocused property becomes truechanging the value of some other property when the control gains keyboard focus.

You can also trigger on the IsKeyboardFocusWithin property.  This property will get set to true for an element when any child of that element has keyboard focus.

In the example below, we set the background color of either StackPanel when any element within the StackPanel has focus.  This technique may be useful when you want to keep track of what section of a window the user is working in and do something based on that knowledge.

    <Window.Resources>
        <Style x:Key="HoneydewFocus" TargetType="StackPanel">
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="true">
                    <Setter Property="Background" Value="Honeydew"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    
    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10"
                    Style="{StaticResource HoneydewFocus}">
            <Button Content="Click Me" VerticalAlignment="Center"/>
            <TextBox Width="200" Height="25" Margin="10"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="10"
                    Style="{StaticResource HoneydewFocus}">
            <Button Content="Or Me" VerticalAlignment="Center"/>
            <TextBox Width="200" Height="25" Margin="10"/>
        </StackPanel>
    </StackPanel>

1075-001

1075-002

#1,072 – Adding Custom Triggers Related to Keyboard Focus

The default control template for the TextBox control changes the color of the Border around the control when the control gets focus.  It does this by using a trigger hooked to the IsKeyboardFocused property.

You can add your own triggers related to keyboard focus by defining a custom property trigger.  The XAML fragment below defines a new Style element that changes the Background of the control when IsKeyboardFocused is true.

    <Window.Resources>
        <Style x:Key="HoneydewFocus" TargetType="TextBox">
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocused" Value="true">
                    <Setter Property="Background" Value="Honeydew"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <StackPanel>
        <TextBox Margin="5" Width="80"
                 Style="{StaticResource HoneydewFocus}"/>

        <TextBox Margin="5" Width="80"
                 Style="{StaticResource HoneydewFocus}"/>

        <Button Content="Click Me" HorizontalAlignment="Center"
                Padding="12,5" Margin="5" />
    </StackPanel>

As we tab between the two TextBox controls, the one with keyboard focus will have a Honeydew-colored background, as well as the default blue border.

1072-001

#1,071 – How TextBox Reacts to Gaining Keyboard Focus

If you have an application with several TextBox controls, you’ll notice that the TextBox that currently has focus “lights up” by drawing a light blue border around the edge of the TextBox.

1071-001

The TextBox draws the blue border by setting up a trigger in its control template.  You can see the body of the control template by right-clicking the TextBox in Visual Studio from the design surface and selecting Edit Template, followed by Edit a Copy.

1071-002

When you do this, you’ll be asked to give the new copy a name (e.g. TextBoxStyle1).  You’ll then get the full body of the template in the XAML document.

Looking at this template, you’ll see a Trigger on the IsKeyboardFocused property that sets the value of the BorderBrush on a Border element.  It sets it to a static resource, which is defined earlier in the template (to a light blue color).

1071-002 1071-003

#1,070 – Event Sequence for Keyboard Focus Events

Events raised by user interface elements that are related to keyboard focus are:

  • PreviewLostKeyboardFocus  (tunneling)
  • PreviewGotKeyboardFocus  (tunneling)
  • LostKeyboardFocus  (bubbling)
  • GotKeyboardFocus  (bubbling)

When keyboard focus changes from one control to another, the events fired by the controls are in the order listed above.

Suppose that we have two TextBox controls in a StackPanel, which is contained within a Window.  If the first TextBox has keyboard focus and the user causes the second TextBox to receive focus, the sequence of events is as follows:

  • Window fires PreviewLostKeyboardFocus
  • StackPanel fires PreviewLostKeyboardFocus
  • TextBox #1 fires PreviewLostKeyboardFocus
  • Window fires PreviewGotKeyboardFocus
  • StackPanel fires PreviewGotKeyboardFocus
  • TextBox #2 fires PreviewGotKeyboardFocus
  • TextBox #1 fires LostKeyboardFocus
  • StackPanel fires LostKeyboardFocus
  • Window fires LostKeyboardFocus
  • TextBox #2 fires GotKeyboardFocus
  • StackPanel fires GotKeyboardFocus
  • Window fires GotKeyboardFocus

1070-001

#1,069 – Main Window Initially Has Keyboard Focus

When you first start a WPF application which contains elements that can get keyboard focus (e.g. TextBox), it’s the main Window that has the keyboard focus when the application starts.

We can see this by using code that reports the current keyboard focus within a Label at the bottom of the window.

1069-001

If we attach an event handler to the main Window for the TextInput event and use the handler to log information about the event, we can start typing after the window comes up and see that the Window is getting TextInput events based on what we type.  Nothing is rendered to the screen, but our logging indicates that TextInput events are being fired, with the main window as their source.

        private void Window_TextInput(object sender, TextCompositionEventArgs e)
        {
            Trace.WriteLine(string.Format("Window_TextInput: [{0}], source={1}", e.Text, e.Source.ToString()));
        }

1069-002