The target for mouse input is always the element currently under the mouse, or the element that has currently captured the mouse. This doesn’t work so well for keyboard input—the user cannot move the keyboard, and it would be inconvenient to need to keep the mouse directly over a text field while typing. Windows therefore uses a different mechanism for directing keyboard input. At any given moment, a particular element is designated as having the focus, meaning that it acts as the target for keyboard input. The user sets the focus by clicking the control in question with the mouse or stylus, or by using navigation keys such as the Tab and arrow keys.
TheUIElementbase class defines anIsFocusedproperty, so in principle, any user interface element can receive the focus. However, theFocusableproperty determines whether this feature is enabled on any particular element. By default, this is true for controls, and false for other elements.
Table 4-2 shows the keyboard input events offered by user interface elements. Most of these items use tunnel and bubble routing for the preview and main events, respectively.
Table 4-2. Keyboard input events
Event
Routing
Meaning
PreviewGotKeyboardFocus, GotKeyboardFocus
Tunnel, Bubble
Element received the keyboard focus.
PreviewLostKeyboardFocus, LostKeyboardFocus
Tunnel, Bubble
Element lost the keyboard focus.
GotFocus
Bubble
Element received the logical focus.
LostFocus
Bubble
Element lost the logical focus.
PreviewKeyDown, KeyDown
Tunnel, Bubble
Key pressed.
PreviewKeyUp, KeyUp
Tunnel, Bubble
Key released.
PreviewTextInput, TextInput
Tunnel, Bubble
Element received text input.
Strictly speaking, theTextInputevent is not caused exclusively by keyboard input. It represents textual input in a device-independent way, so this event can also be raised as a result of ink input from a stylus.
As Table 4-2 shows, WPF makes a distinction between logical focus and keyboard focus. Only one element can have the keyboard focus at any given instant. Often, the focus will not even be in your application—the user may switch to another application. However, applications typically remember where the focus was so that if the user switches back, the focus returns to the same place as before. WPF defines the logical focus concept to keep track of this: when an application loses the keyboard focus, the last element that had the keyboard focus retains the logical focus. When the application regains the keyboard focus, WPF ensures that the focus is put back into the element with the logical focus.
The Keyboard class provides a static property called Modifiers. You can read this at any time to find out which modifier keys, such as the Alt, Shift, and Ctrl keys, are pressed. Example 4-12 shows how you might use this in code that needs to decide whether to copy or move an item according to whether the Ctrl key is pressed.
You can also discover which element has the keyboard focus, using the staticFocusedElementproperty, or set the focus into a particular element by calling theFocusmethod.
The state information returned byKeyboarddoes not represent the current state. It represents a snapshot of the state for the event currently being processed. This means that if for some reason, your application gets bogged down and gets slightly behind in processing messages, the keyboard state will remain consistent.
As an example of why this is important, consider a drag operation where the Ctrl key determines whether the operation is a move or a copy. To behave correctly, your mouse up handler needs to know the state the Ctrl key had when the mouse button was released, rather than the state that it’s in now. If the user releases the Ctrl key after letting go of the mouse button, but before your application has processed the mouse up event, the user will expect a copy operation to be performed, and he will be unhappy if the application performs a move simply because your code couldn’t keep up. By returning a snapshot of the keyboard state rather than its immediate state, theKeyboardclass saves you from this problem.
The stylus used on Tablet PCs and other ink-enabled systems has its own set of events. Table 4-3 shows the ink input events offered by user interface elements.
Table 4-3. Stylus and ink events
Event
Routing
Meaning
GotStylusCapture
Bubble
Element captured stylus.
LostStylusCapture
Bubble
Element lost stylus capture.
PreviewStylusButtonDown, StylusButtonDown
Tunnel, Bubble
Stylus button pressed while over element.
PreviewStylusButtonUp, StylusButtonUp
Tunnel, Bubble
Stylus button released while over element.
PreviewStylusDown, StylusDown
Tunnel, Bubble
Stylus touched screen while over element.
PreviewStylusUp, StylusUp
Tunnel, Bubble
Stylus left screen while over element.
StylusEnter
Direct
Stylus moved into element.
StylusLeave
Direct
Stylus left element.
PreviewStylusInRange, StylusInRange
Tunnel, Bubble
Stylus moved close enough to screen to be detected.
PreviewStylusOutOfRange, StylusOutOfRange
Tunnel, Bubble
Stylus moved out of detection range.
PreviewStylusMove, StylusMove
Tunnel, Bubble
Stylus moved while over element.
PreviewStylusInAirMove, StylusInAirMove
Tunnel, Bubble
Stylus moved while over element but not in contact with screen.
PreviewStylusSystemGesture, StylusSystemGesture
Tunnel, Bubble
Stylus performed a gesture.
PreviewTextInput, TextInput
Tunnel, Bubble
Element received text input.
TheStylusclass provides a staticCapturemethod that works exactly the same as theMouse.Capturemethod described earlier. It also offersCaptured andDirectlyOverproperties that do the same for the stylus as the matching properties of theMouseclass do for the mouse.
TheInkCanvas accepts free-form ink input. Figure 4-3 shows the InkCanvasin action. (It also demonstrates that I should probably stick to using the keyboard.)InkCanvasmakes all of the ink input available to your program through itsStrokes property. It is possible to connect this data to the handwriting recognition APIs in Windows, but that is beyond the scope of this book.
The input events we’ve examined give us a detailed view of user input directed at individual elements. However, it is often helpful to focus on what the user wants our application to do, rather than how she asked us to do it. WPF supports this through the command abstraction—a command is an action the application performs at the user’s request.
The way in which a command is invoked isn’t usually important. Whether the user presses Ctrl-C, selects the Edit -> Copy menu item, or clicks the Copy button on the toolbar, the application’s response should be the same in each case: it should copy the current selection to the clipboard. The event system we examined earlier in this chapter regards these three types of input as being unrelated, but WPF’s command system lets you treat them as different expressions of the same command.
The command system lets a UI element provide a single handler for a command, reducing clutter and improving the clarity of your code. It enables a more declarative style for UI elements; by associating aMenuItemorButtonwith a particular command, you are making a clearer statement of the intended behavior than you would by wiring upClick event handlers. Example 4-15 illustrates how commands can simplify things.
Each menu item is associated with a command. This is all that’s required to invoke these clipboard operations on the text box; we don’t need any code or event handlers because theTextBoxclass has built-in handling for these commands. More subtly, keyboard shortcuts also work in this example: the built-in cut, copy, and paste commands are automatically associated with their standard keyboard shortcuts, so these work wherever you use a text box. WPF’s command system ensures that when commands are invoked, they are delivered to the appropriate target, which in this case is the text box.
You are not obliged to use commands. You may already have classes to represent this idea in your own frameworks, and if WPF’s command abstraction does not suit your needs, you can just handle the routed events offered by menu items, buttons, and toolbars instead. But for most applications, commands simplify the way your application deals with user input.
There are five concepts at the heart of the command system:
Command object An object identifying a particular command, such as copy or paste
Input binding An association between a particular input (e.g., Ctrl- C) and a command (e.g., Copy)
Command source The object that invoked the command, such as a Button, or an input binding
Command target The UI element that will be asked to execute the command—typically the control that had the keyboard focus when the command was invoked
Command binding A declaration that a particular UI element knows how to handle a particular command
Not all of these features are explicitly visible in Example 4-15—the command bindings are buried inside the text box’s implementation, and although input bindings are in use (Ctrl-C will work just fine, for example), they’ve been set up implicitly by WPF. To make it a bit easier to see all of the pieces, let’s look at a slightly more complex example that uses all five concepts explicitly (see Example 4-16).
Example 4-16. Basic command handling
<!-- XAML --> <Window ...> <Grid> <Button Command="ApplicationCommands.Properties" Content="_Properties"/> </Grid> </Window> // Codebehind public partial class Window1 : Window {
public Window1(){ InitializeComponent();
InputBinding ib = new InputBinding( ApplicationCommands.Properties, new KeyGesture(Key.Enter, ModifierKeys.Alt)); this.InputBindings.Add(ib);
CommandBinding cb = new CommandBinding(ApplicationCommands.Properties); cb.Executed += new ExecutedRoutedEventHandler(cb_Executed); this.CommandBindings.Add(cb); }
This example uses the standardApplicationCommands.Propertiescommand object. Applications that support this command would typically open a property panel or window for the selected item. The XAML in this example associates a button with this command object; clicking the button will invoke the command. The code behind establishes an input binding so that the Alt-Enter shortcut may also be used to invoke the command. Our example, therefore, has two potential command sources: the button and the input binding. The command target in this particular example will be the button; this is true even if the command is invoked with a keyboard shortcut, because the button is the only element in the window capable of having the keyboard focus. However, the button doesn’t know how to handle this command, so it will bubble up to the window, much like an input event. The window does know how to handle the command; it has declared this by creating a command binding with a handler attached to the binding’sExecuted event. This handler will be called when the user invokes the command.
Now that we’ve seen all five features in use, we’ll examine each one in more detail.
Please check back next week for the continuation of this series.