다음을 통해 공유


Input Overview

The Windows Presentation Foundation (WPF) subsystem provides a powerful new API for input.

This topic describes the services provided by WPF and explains the architecture of the input systems.

This topic contains the following sections.

  • Input API
  • Event Routing
  • Handling Input Events
  • Text Input
  • Focus
  • Mouse Position
  • Commands
  • What's Next
  • Related Topics

Input API

The primary input API are found on the base element classes: UIElement, ContentElement, FrameworkElement, and FrameworkContentElement. For more information about the base elements, see the Base Elements Overview. These classes provide functionality for input events related to key presses, mouse buttons, mouse wheel, mouse movement, focus management, and mouse capture, to name a few. Many input events have a pair of events associated with them. For example, the key down event is associated with the KeyDown and PreviewKeyDown events. The difference in these events is in how they are routed to the target element. Preview events tunneling down the element tree from the root element to the target element. Bubbling events bubbling up from the target element to the root element. Event routing in WPF is discussed in more detail later in this overview and in the Routed Events Overview.

Keyboard and Mouse Classes

In addition to the input API on the base element classes, the Keyboard class and Mouse classes provide additional API for working with keyboard and mouse input.

Examples of input API on the Keyboard class are the Modifiers property, which returns the ModifierKeys currently pressed, and the IsKeyDown method, which determines whether a specified key is pressed.

The following example uses the GetKeyStates method to determine if a Key is in the down state.

// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison. 
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}

Examples of input API on the Mouse class are MiddleButton which obtains the state of the middle mouse button and DirectlyOver which gets the element the mouse pointer is currently over.

The following example determines whether the LeftButton on the mouse is in the Pressed state.

if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}

These classes are covered in more details throughout this overview.

Stylus Input

WPF has integrated support for the Stylus. The Stylus is a pen input made popular by the Tablet PC. WPF applications can treat the stylus as a mouse by using the mouse API, but WPF also exposes a stylus that use a model similar to the keyboard and mouse. All stylus-related members contain the word Stylus.

Since the stylus can act as a mouse, applications that support only mouse input can still obtain some level of stylus support automatically. When the stylus is used in such a manner, the application is given the opportunity to handle the appropriate stylus event and then handles the corresponding mouse event. In addition, higher-level services such as inking are also available. For more information about ink, see Getting Started with Ink.

Event Routing

A FrameworkElement can contain other elements, forming a tree of elements. In WPF, the parent element can participate in input directed to its child elements or other descendants. This is especially useful for building controls out of smaller controls, a process known as "control composition" or "compositing." For more information about element trees, see the Element Tree overview.

Routing is the process of delivering events to multiple elements until one of them marks the event as handled. Events use one of three routing mechanisms: direct-only (also known as "no routing"), bubbling, and tunneling. In direct-only routing, the target element is the only element notified. Bubbling works up the element tree by first notifying the target element, then the target's parent, and so on. Tunneling starts at the root of the element tree and works down, ending with the target element. For more information about routed events, see the Routed Events Overview.

WPF input events generally come in pairs that consists of a tunneling event and a bubbling event. Tunneling events are distinguished from bubbling events with the "Preview" prefix. For instance, PreviewMouseMove is the tunneling version of a mouse move event and MouseMove is the bubbling version of this event.

Handling Input Events

To receive input on an element an event handler must be associated with that particular event. In XAML this is straightforward. Simply include the name of the event as an attribute of the element that will be listening for this event. Then, set the value of the attribute to the name of the event handler. The event handler must be written in code such as C# and can be included in a code-behind file.

Keyboard Input Event Example

The follow example listens for a left arrow key press. A StackPanel is created that has a Button. An event handler to listen for the left arrow key press is attached to the Button.

The first section of the example creates the StackPanel and the Button and attaches the event handler for the KeyDown.

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);

The second section of code is written in code and defines the event handler. When the left arrow key is pressed and the Button has keyboard focus, the Background color of the Button is changed. If the key is pressed, but it is not the left arrow key, the Background color of the Button is changed back to its starting color.

private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}

Mouse Input Event Example

In the following example, the Background color of a Button is changed when the mouse pointer enters the Button. The Background color is restored when the mouse leaves the Button.

The first section of the example creates the StackPanel and the Button control and attaches the event handlers for the MouseEnter and MouseLeave events to the Button.

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button
          
  </Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);

The second section of code is written in code and defines the event handlers. When the mouse enters the Button, the Background color of the Button is changed to SlateGray. When the mouse leaves the Button, the Background color of the Button is changed back to AliceBlue.

private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.SlateGray;
    }
}
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}

Text Input

The TextInput event enables you to listen for text input in a device-independent manner. The keyboard is the primary means of text input, but speech, handwriting, and other input devices can generate text input.

For keyboard input, WPF first sends the appropriate KeyDown / KeyUp events and if those are not handled and the key is textual then a TextInput event is raised. There is not always a simple one-to-one mapping between KeyDown/KeyUp and TextInput events because multiple keystrokes can generate a single character of text input and single keystrokes can generate multi-character strings. This is especially true for languages such as Chinese, Japanese, and Korean which use Input Method Editors (IMEs) to generate the thousands of different characters in their corresponding alphabets.

When WPF sends a KeyUp/KeyDown event, if the keystrokes could become part of a TextInput event, Key is set to Key.TextInput so applications do not accidentally process keystrokes that are part of larger text input. In these cases, Key reveals the real keystroke. Similarly, if an IME is active, Key has the value of Key.ImeProcessed, and ImeProcessedKey gives the actual keystroke.

Consider a simple example where pressing CTRL+O opens a file (regardless of what control has focus), and pressing the Open button performs the same action.

The following example defines a handler for the Click event and a handler for the KeyDown event,

The first segment of code creates the user interface.

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);

The second segment of code contains the event handlers

private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

public void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
} 

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}

Because input bubbles up the tree, the StackPanel receives the input regardless of which element has keyboard focus. The TextBox control is notified first and the OnTextInputKeyDown handler is only called if the TextBox did not handle the input. If the PreviewKeyDown event is used instead of the KeyDown event, the OnTextInputKeyDown handler is called first.

In this example, the handling logic is written two times—one time for CTRL+O, and again for button's click event. This can be simplified by using commands. Commands are discussed in this overview and in the Commanding Overview.

Focus

There are two main concepts that pertain to focus in WPF: keyboard focus and logical focus.

Keyboard Focus

Keyboard focus refers to the element that is receiving keyboard input. There can be only one element on the whole desktop that has keyboard focus. In WPF, the element that has keyboard focus will have IsKeyboardFocused set to true. The static Keyboard method FocusedElement returns the element that currently has keyboard focus.

Keyboard focus can be obtained by tabbing to an element or by clicking the mouse on certain elements, such as a TextBox. Keyboard focus can also be obtained programmatically by using the Focus method on the Keyboard class. Focus attempts to give the specified element keyboard focus. The element returned by Focus is the element that currently has keyboard focus.

In order for an element to obtain keyboard focus the Focusable property and the IsVisible properties must be set to true. Some classes, such as Panel, have Focusable set to false by default; therefore, you may have to set this property to true if you want that element to be able to obtain focus.

The following example uses Focus to set keyboard focus on a Button. The recommended place to set initial focus in an application is in the Loaded event handler.

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

For more information about keyboard focus, see the Focus Overview.

Logical Focus

Logical focus refers to the System.Windows.Input.FocusManager.FocusedElement in a focus scope. There can be multiple elements that have logical focus in an application, but there may only be one element that has logical focus in a particular focus scope.

A focus scope is a container element that keeps track of the FocusedElement within its scope. When focus leaves a focus scope, the focused element will lose keyboard focus but will retain logical focus. When focus returns to the focus scope, the focused element will obtain keyboard focus. This allows for keyboard focus to be changed between multiple focus scopes but insures that the focused element within the focus scope remains the focused element when focus returns.

An element can be turned into a focus scope in Extensible Application Markup Language (XAML) by setting the FocusManager attached property IsFocusScope to true, or in code by setting the attached property by using the SetIsFocusScope method.

The following example makes a StackPanel into a focus scope by setting the IsFocusScope attached property.

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

Classes in WPF which are focus scopes by default are Window, Menu, ToolBar, and ContextMenu.

An element that has keyboard focus will also have logical focus for the focus scope it belongs to; therefore, setting focus on an element with the Focus method on the Keyboard class or the base element classes will attempt to give the element keyboard focus and logical focus.

To determine the focused element in a focus scope, use GetFocusedElement.

To change the focused element for a focus scope, use SetFocusedElement.

For more information about logical focus, see the Focus Overview.

Mouse Position

The WPF input API provides helpful information with regard to coordinate spaces. For example, coordinate (0,0) is the upper-left coordinate, but the upper-left of which element in the tree? The element that is the input target? The element you attached your event handler to? Or something else? To avoid confusion, the WPF input API requires that you specify your frame of reference when you work with coordinates. The GetPosition method returns the coordinate of the mouse pointer relative to the specified element.

Commands

Commands enable input handling at a more semantic level than device input. Commands are simple directives, such as Cut, Copy, Paste, or Open. Commands are useful for centralizing your command logic. The same command might be accessed from a Menu, on a ToolBar, or through a keyboard shortcut. Commands also provide a mechanism for disabling controls when the command becomes unavailable.

RoutedCommand is the WPF implementation of ICommand. When a RoutedCommand is executed, a PreviewExecuted and an Executed event are raised on the command target, which tunnel and bubble through the element tree like other input. If a command target is not set, the element with keyboard focus will be the command target. The logic that performs the command is attached to a CommandBinding. When an Executed event reaches a CommandBinding for that specific command, the ExecutedRoutedEventHandler on the CommandBinding is called. This handler performs the action of the command.

For more information on commanding, see the Commanding Overview.

WPF provides a library of common commands which consists of ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommands, and EditingCommands, or you can define your own.

The following example shows how to set up a MenuItem so that when it is clicked it will invoke the Paste command on the TextBox, assuming the TextBox has keyboard focus.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
           
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

For more information about commands in WPF, see the Commanding Overview.

What's Next

You now have several techniques to handle input in WPF. You should also have an improved understanding of the various types of input events and the routed event mechanisms used by WPF.

Additional resources are available that explain WPF framework elements and event routing in more detail. See the following overviews for more information, Commanding Overview, Focus Overview, Base Elements Overview, Element Tree overview, and the Routed Events Overview.

See Also

Concepts

Focus Overview
Commanding Overview
Routed Events Overview
Base Elements Overview

Other Resources

Properties