Udostępnij za pośrednictwem


Input Overview

The Windows Presentation Foundation (WPF) subsystem provides a powerful API for obtaining input from a variety of devices, including the mouse, keyboard, and stylus.

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
  • Mouse Capture
  • Commands
  • The Input System and Base Elements
  • What's Next
  • Related Topics

Input API

The primary input API exposure is found on the base element classes: UIElement, ContentElement, FrameworkElement, and FrameworkContentElement. For more information about the base elements, see 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. By placing the input API on the base elements, rather than treating all input events as a service, the input architecture enables the input events to be sourced by a particular object in the UI, and to support an event routing scheme whereby more than one element has an opportunity to handle an input event. 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 tunnel down the element tree from the root element to the target element. Bubbling events bubble 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");
}

The Mouse and Keyboard classes are covered in more detail 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 device abstraction that use a model similar to the keyboard and mouse. All stylus-related APIs contain the word "Stylus".

Because 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 ink input are also available through the stylus device abstraction. For more information about ink as input, see Getting Started with Ink.

Event Routing

A FrameworkElement can contain other elements as child elements in its content model, forming a tree of elements. In WPF, the parent element can participate in input directed to its child elements or other descendants by handing events. 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 and how element trees relate to event routes, see Trees in WPF.

Event routing is the process of forwarding events to multiple elements, so that a particular object or element along the route can choose to offer a significant response (through handling) to an event that might have been sourced by a different element. Routed events use one of three routing mechanisms: direct, bubbling, and tunneling. In direct routing, the source element is the only element notified, and the event is not routed to any other elements. However, the direct routed event still offers some additional capabilities that are only present for routed events as opposed to standard CLR events. Bubbling works up the element tree by first notifying the element that sourced the event, then the parent element, and so on. Tunneling starts at the root of the element tree and works down, ending with the original source element. For more information about routed events, see 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. This event pairing is a convention that is implemented at the element level and is not an inherent capability of the WPF event system. For details, see the WPF Input Events section in Routed Events Overview.

Handling Input Events

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

Keyboard events occur when the operating system reports key actions that occur while keyboard focus is on an element. Mouse and stylus events each fall into two categories: events that report changes in pointer position relative to the element, and events that report changes in the state of device buttons.

Keyboard Input Event Example

The following 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 instance.

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 is written in code and defines the event handler. When the left arrow key is pressed and the Button has keyboard focus, the handler runs and 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 the example 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 also.

For keyboard input, WPF first sends the appropriate KeyDown/KeyUp events. If those events are not handled and the key is textual (rather than a control key such as directional arrows or function keys), 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 possible characters in their corresponding alphabets.

When WPF sends a KeyUp/KeyDown event, Key is set to Key.System if the keystrokes could become part of a TextInput event (if ALT+S is pressed, for example). This allows code in a KeyDown event handler to check for Key.System and, if found, leave processing for the handler of the subsequently raised TextInput event. In these cases, the various properties of the TextCompositionEventArgs argument can be used to determine the original keystrokes. Similarly, if an IME is active, Key has the value of Key.ImeProcessed, and ImeProcessedKey gives the original keystroke or keystrokes.

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

The first segment of code or markup 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;
    }
}

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

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

Because input events bubble up the event route, the StackPanel receives the input regardless of which element has keyboard focus. The TextBox control is notified first and the OnTextInputKeyDown handler is called only 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, instead of handling the input events directly. Commands are discussed in this overview and in 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 Focus Overview.

Logical Focus

Logical focus refers to the 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 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 obtained through the mouse. The GetPosition method returns the coordinate of the mouse pointer relative to the specified element.

Mouse Capture

Mouse devices specifically hold a modal characteristic known as mouse capture. Mouse capture is used to maintain a transitional input state when a drag-and-drop operation is started, so that other operations involving the nominal on-screen position of the mouse pointer do not necessarily occur. During the drag, the user cannot click without aborting the drag-and-drop, which makes most mouseover cues inappropriate while the mouse capture is held by the drag origin. The input system exposes APIs that can determine mouse capture state, as well as APIs that can force mouse capture to a specific element, or clear mouse capture state. For more information on drag-and-drop operations, see Drag and Drop Overview.

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 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;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

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

The Input System and Base Elements

Input events such as the attached events defined by the Mouse, Keyboard, and Stylus classes are raised by the input system and injected into a particular position in the object model based on hit testing the visual tree at run time.

Each of the events that Mouse, Keyboard, and Stylus define as an attached event is also re-exposed by the base element classes UIElement and ContentElement as a new routed event. The base element routed events are generated by classes handling the original attached event and reusing the event data.

When the input event becomes associated with a particular source element through its base element input event implementation, it can be routed through the remainder of an event route that is based on a combination of logical and visual tree objects, and be handled by application code. Generally, it is more convenient to handle these device-related input events using the routed events on UIElement and ContentElement, because you can use more intuitive event handler syntax both in XAML and in code. You could choose to handle the attached event that initiated the process instead, but you would face several issues: the attached event may be marked handled by the base element class handling, and you need to use accessor methods rather than true event syntax in order to attach handlers for attached events.

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 OverviewBase Elements OverviewTrees in WPF, and Routed Events Overview.

See Also

Concepts

Focus Overview

Commanding Overview

Routed Events Overview

Base Elements Overview

Other Resources

Properties