#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

#1,068 – Most Controls Can Get Keyboard Focus

In WPF, controls that a user interacts with are typically able to get keyboard focus.  (Focusable property is true).  This includes controls that supported text-based input, like the TextBox.  But it also includes controls that the user typically interacts with using the mouse, rather than the keyboard.

Controls that cannot get keyboard focus are ones like the Label that the user does not interact with.

For example, a user will typically use only the mouse when interacting with a Button or a CheckBox, but both of these controls can get keyboard focus.  This is because the user can also interact with these controls using the keyboard.  (E.g. Enter key to “click” a Button, or Spacebar to toggle a CheckBox).

The example below uses code to detect which control has focus.  Note that as we tab through the controls, the TextBox controls get focus, as well as the Button and the CheckBox.

1068-001

1068-002

1068-003

#1,067 – Experimenting with Keyboard Focus

A control has keyboard focus if it can accept input from the keyboard.  The sample code below is a working app that lets us experiment with keyboard focus by setting a Label to indicate which control has focus.

XAML includes some controls and defines a GotKeyboardFocus for the top-level Window.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Keyboard Focus" Width="320" Height="190"
        GotKeyboardFocus="Window_GotKeyboardFocus">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">
            <StackPanel Background="AliceBlue" Margin="5">
                <TextBox Name="txtLeft" Margin="5" Width="80"/>
                <Button Name="btnClickMe" Content="Click Me" Margin="5"/>
            </StackPanel>

            <StackPanel Background="PaleGoldenrod" Margin="5">
                <Label Content="I'm a Label"/>
                <TextBox Name="txtRight" Margin="5" Width="80"/>
                <CheckBox Name="chkCheckMe" Margin="5" Content="Check Me"/>
            </StackPanel>
        </StackPanel>

        <Label Grid.Row="1" Margin="5" Content="{Binding WhoHasFocus}"/>
    </Grid>
</Window>

Code-behind uses this code to set property indicating who has focus.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
//using System.Windows.Media;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }

        private string whoHasFocus;
        public string WhoHasFocus
        {
            get { return whoHasFocus; }
            set {
                if (value != whoHasFocus)
                {
                    whoHasFocus = value;
                    RaisePropertyChanged("WhoHasFocus");
                }
            }
        }

        private void Window_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            CheckKeyboardFocus();
        }

        private void CheckKeyboardFocus()
        {
            IInputElement elem = Keyboard.FocusedElement;

            if (elem == null)
                WhoHasFocus = "NO FOCUS";
            else
            {
                FrameworkElement felem = elem as FrameworkElement;
                if (felem != null)
                {
                    string identifier = ((felem.Name != null) && (felem.Name.Length > 0)) ?
                        felem.Name :
                        felem.GetType().ToString();
                    WhoHasFocus = string.Format("FrameworkElement [{0}]", identifier);
                }
                else
                {
                    // Maybe a FrameworkContentElement has focus
                    FrameworkContentElement fcelem = elem as FrameworkContentElement;
                    if (fcelem != null)
                    {
                        string identifier = ((fcelem.Name != null) && (fcelem.Name.Length > 0)) ?
                            fcelem.Name :
                            fcelem.GetType().ToString();
                        WhoHasFocus = string.Format("FrameworkContentElement [{0}]", identifier);
                    }
                    else
                    {
                        WhoHasFocus = string.Format("Element of type [{0}] has focus", elem.GetType().ToString());
                    }
                }
            }
        }
    }
}

1067-001

#311 – Giving Focus to a Control, Part II

You can give focus to a specific control at run-time using the Keyboard.Focus static method.  You can also give focus to any control that inherits from UIElement using the control’s Focus method.

		public MainWindow()
		{
			this.InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);

		}

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // On startup, set focus to first TextBox in window
            txtFirst.Focus();
        }

The difference between these two methods is:

  • Keyboard.Focus just sets keyboard focus
  • UIElement.Focus tries to set keyboard focus.  If the control fails to get keyboard focus, the method sets logical focus to the control