Responding to the Mouse and Keyboard
Controls also provide some built-in intelligence for dealing with the keyboard and mouse. These include low-level events that react to key presses and mouse movement, and methods that return key and mouse button state information (Table 3-4).
Table 3-4: Events for Reacting to the Keyboard
EventDescription
KeyDown
Occurs when a key is pressed while the current control has focus. The event provides additional information (through KeyEventArgs) about the state of the Alt and Ctrl keys and the key code.
KeyPress
This is a higher-level event that occurs once the key press is complete (but before the character appears, if the control is an input control). The event provides a KeyPressEventArgs object with information about the key character. The KeyPressEventArgs object also provides a Handled property, which you can set to true to cancel further processing, effectively canceling the character and suppressing its display in an input control.
KeyUp
This occurs when a key is released, just after the KeyPress event. It provides information through a KeyEventArgs object.
Generally you will react to the KeyDown and KeyUp events when you need to react to special characters like the arrow keys, which do not trigger KeyPress events. The KeyPress event is used when you need to restrict input and perform character validation.
If you want to update the display or react to a changed text value in an input control, you would probably not use any of these events. Instead, you should react to the higher-level Changed event, which fires when any modifications are made. The Changed event will fire if you modify the text programmatically or the user deletes the text with the right-click menu.
Forms provide a Boolean KeyPreview property. If you set this to true, your form receives key press events when any of its controls have focus, and it receives these events before the control does (Table 3-5). This technique is useful if you are programming a customized interface or a game where you need to take complete control of the keyboard. You'll also see in Chapter 14 that this technique is useful when you are handling the F1 key to create your own context-sensitive help system.
Table 3-5: Events for Reacting to the Mouse
EventDescription
MouseEnter
Occurs when the mouse moves into a control's region.
MouseMove[*]
Occurs when the mouse is moved over a control by a single pixel. Event handlers are provided with additional information about the current coordinates of the mouse pointer. Be warned that a typical mouse movement can generate dozens of MouseMove events. Event handlers that react to this event can be used to update the display, but not for more time-consuming tasks.
MouseHover
Occurs when the mouse lingers, without moving, over the control for a system-specified amount of time (typically a couple of seconds). Usually, you react to this event to highlight the control that is being hovered over, or update the display with some dynamic information.
MouseDown[*]
Occurs when a mouse button is clicked.
MouseUp[*]
Occurs when a mouse button is released. For many controls, this is where the logic for right-button mouse clicks is coded, although MouseDown is also sometimes used.
Click
Occurs when a control is clicked. Generally, this event occurs after the MouseDown event but before the MouseUp event. For basic controls, a Click event is triggered for left-button and right-button mouse clicks. Some controls have a special meaning for this event. One example is the button control. You can raise a Button Click event by tabbing to the button and pressing the Enter key, or clicking with the left mouse button. Right-button clicks button trigger MouseDown and MouseUp events, but not Click events.
DoubleClick
Occurs when a control is clicked twice in succession. A Click event is still generated for the first click, while the second click generates the DoubleClick event.
MouseWheel
Occurs when the mouse wheel moves while the control has focus. The mouse pointer is not necessarily positioned over the control.
MouseLeave
Occurs when the mouse leaves a control's region.
[*]Indicates that the event handler uses the MouseEvent delegate, and provides additional information about the location of the mouse pointer (and X and Y properties), the mouse wheel movement (Delta), and the state of the mouse buttons (Button).
The MouseMove, MouseDown, and MouseUp events provide additional information about the state of the mouse buttons. Separate MouseDown and MouseUp events are triggered for every mouse button. In this case, the MouseEventArgs.Button property indicates the button the caused the event.
private void lbl_MouseUp(Object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
// This event was caused by a right-click.
// Here is a good place to show a context menu.
}
}
In the MouseMove event, however, the Button property indicates all the buttons that are currently depressed. That means that this property could take on more than one value from the MouseButtons enumeration. To test for a button, you need to use bitwise arithmetic.
private void lbl_MouseMove(Object sender, System.Windows.Forms.MouseEventArgs e)
{
if ((e.Button & MouseButtons.Right) == MouseButtons.Right)
{
// The right mouse button is currently being held down.
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// You can only get here if both the left and the right mouse buttons
// are currently held down.
}
}
}
Every control also provides a MousePosition, MouseButtons, and ModifierKeys property for information about the mouse and keyboard. These properties are less useful than the event data. For example, if you use them in an event handler they retrieve information about the current location of the mouse pointer, not the position where it was when the event was triggered. Additionally, the MousePosition property uses screen coordinates, not control coordinates. However, this information can still be useful when you are reacting to an event that doesn't provide mouse information. In Chapter 11 you'll see how it can be used with a dynamic drawing application.
A Mouse/Keyboard Example
The mouse and keyboard events have some subtleties, and it's always best to get a solid and intuitive understanding by watching the events in action. The sample code for this chapter provides an ideal example that creates a list of common mouse and keyboard events as they take place. Each entry also includes some event information, giving you an accurate idea of the order these events occur, and the information they provide.
MouseMove events are not included in the list (because they would quickly swamp it with entries), but a separate label control reports on the current position of the mouse (see Figure 3-8).
Figure 3-8: An event tracker
For example, here's the code that adds an entry in response to the pic.MouseLeave event:
private void pic_MouseLeave(object sender, System.EventArgs e)
{
Log("Mouse Leave");
}
The private Log() method adds the string of information, and scrolls the list control to the bottom to ensure that it is visible.
private void Log(String data)
{
lstLog.Items.Add(data);
int itemsPerPage = (int)(lstLog.Height / lstLog.ItemHeight);
lstLog.TopIndex = lstLog.Items.Count - itemsPerPage;
}
Mouse Cursors
One other useful mouse-related property is Cursor. It sets the type of mouse cursor that is displayed when the mouse is moved over a control, and it applies to all child controls. If your application is about to perform a potentially time-consuming operation, you might want to set the form's Cursor property to an hourglass. You can access standard system-defined cursors using the static properties of the Cursors class.
myForm.Cursor = Cursors.WaitCursor;
// (Perform long task.)
myForm.Cursor = Cursors.Default;
You can also create a custom cursor using the Cursor class, load a custom cursor graphic, and assign it to a control.
Cursor myCursor = new Cursor(Application.StartupPath + "\\mycursor.cur");
myCustomControl.Cursor = myCursor;
Cursor files are similar to icons, but they are stored in a special .cur file format. Currently, animated cursors (.ani files) are not supported.