Basic Event Handling

Basic Event Handling

All controls raise events that are used to notify you when something of interest has occurred within the control. Many events are generated when the user interacts with the control. For example, when the user clicks a button or enters characters in a text box, events are generated. By handling these events, Windows Forms applications interact with the user and respond to the user’s actions.

An application indicates its interest in handling an event by supplying an event handler delegate with an appropriate signature and associating the delegate with an event exposed by the control. Any events that aren’t handled by the application are provided with default handling by the class associated with the control. The behaviors that are common to all controls are implemented by base classes. For example, the Control base class provides a basic implementation of everything a Windows Forms control is required to do, including managing a window handle from the operating system and providing a user interface. As mentioned, the ButtonBase class implements the default behavior for all button controls.

Event Handling Architecture

Each control class in the .NET Framework exposes a large number of events that are used to send event notifications to a Windows Forms application. Many of these events are defined by the Control base class, and most of the descendants of Control add events that are specific to each type of control.

Although Windows Forms controls expose dozens of events, you’ll probably handle only a few of these events routinely. Some events, such as the Click event raised by a button control, almost always will be handled each time the control is used. Many of the other events are exposed to enable you to properly subclass controls and provide appropriate behavior. For example, whenever control properties such as the background color, location, and size are changed, an event is raised. Although you’ll rarely need to handle these events in a form that contains only Button or TextBox objects, derived classes can use these events to update their appearance to properly reflect underlying properties.

The button control is one of the most commonly used controls in a Windows Forms application. The most commonly handled events raised by the Button class are as follows:

  • Click  Fires when the user has clicked the control or selected the control using other means

  • Disposed  Fires when the Dispose method of the control has been invoked

  • DoubleClick  Fires when the user has double-clicked the control

  • GotFocus  Fires when the control has acquired keyboard input focus

  • LostFocus  Fires when the control has lost keyboard input focus

  • TextChanged  Fires when the control’s Text property has changed

  • Validated  Fires when the control’s contents have been validated

  • Validating  Fires when the control’s contents are being validated

Keep in mind that this is only a partial list of the events available to Windows Forms controls. Many of the control classes provide additional events specific to the characteristics of each control. These additional events will be discussed with the relevant controls in later sections and in the remainder of Part III. There are also additional events that are used to implement specific functionality. For example, if you implement drag and drop for one or more controls, a number of events must be handled. In a similar way, interaction using the mouse involves the management of various mouse movement events. Although these events aren’t discussed in this chapter, the remaining chapters in Part III will introduce the events required to interact with various Windows Forms features.

Adding New Event Handlers

You subscribe to an event by creating an event handler and associating it with a specific event. You can use several approaches to handle events. One straightforward approach is to define a method that conforms to the event handler signature and then programmatically add the event handler to the control, as shown here:

public ButtonForm()
{
    
    

    closeButton.Click += new EventHandler(OnButtonClicked);
}

private void OnButtonClicked(object sender, EventArgs e)
{
    MessageBox.Show("I've been clicked");
}

The Windows Forms Designer can be used to automatically define an event handler method and wire it to an appropriate event. If you double-click on a form or control in the Forms Designer, an event handler method will be created for you and will automatically be wired to handle the default event for the control. The source code editor will be opened automatically, with the insertion point positioned in the new event handler method. If an event handler is already defined for the default event, the source code editor will be opened with the insertion point positioned in the existing event handler.

Because each control handles a large number of events, the Windows Forms Designer will automatically generate an event handler for the control’s default event, which is the event that you’re most likely to handle. For example, if you double-click any of the button controls, a handler for the Click event will be created. If you double-click a text box control, an event handler for the Text­Changed event will be created. If you double-click the form surface, the Forms Designer will create a handler for the form’s Load event.

When the Forms Designer creates an event handler method for you, it follows the naming convention objectname_eventname for the new method. So a method that handles the Click event for a Button object named closeButton is named closeButton_Click. To wire the event handler, the Windows Forms Designer adds code to your Visual C# source file in two places. First it creates a method that conforms to the event’s delegate signature, as shown here:

private void closeBtn_Click(object sender, System.EventArgs e)
{
    
    

}

Next the Windows Forms Designer creates an event handler delegate and assigns it to the relevant object, as shown here:

this.closeBtn.Click += new System.EventHandler(this.closeBtn_Click);

This code is located in the form’s InitializeComponent method, which is found in the code region that’s collapsed by default when the source file is viewed in the Visual C# Code Editor.

The Windows Forms Designer isn’t limited to creating handlers for default events; it can be used to create event handler methods for any event raised by a control. To view the events that are exposed by the control, click the Events icon in the Properties window, as shown in Figure 12-4.

As you can see in the figure, event names are shown on the left side of the Properties window and the names of any event handlers are shown on the right side. Double-click any event handler listed on the right side of the window, and the source code editor will open with the insertion point positioned at the event handler method.

Figure 12-4.
The Properties window, displaying events as well as ­properties.

To add a new event handler, enter the event handler’s name in the space adjacent to the event with which it should be associated. Using the name entered in the Properties window, the Windows Forms Designer will create an event handler for you and will take care of wiring the method to the control or form’s event through a delegate. Alternatively, you can double-click the event listed in the Properties window, and an event handler will be created using objectname_eventname as the name of the event handler. Using the Properties window to add an event guarantees that the event handler will have the proper signature and will be correctly subscribed to the desired event with just a few keystrokes.

When a form contains multiple controls of the same type, it’s a simple matter to use a single event handler for all of the controls. The event handler’s sender parameter can be used to determine the control that sent the event, as shown here:

private void OnClick(object sender, System.EventArgs e)
{
    Button clickedButton = (Button)sender;
    switch(clickedButton.Name)
    {
        case "standardButton":
            MessageBox.Show("Standard button clicked");
            break;

        case "popupButton":
            MessageBox.Show("Popup button clicked");
            break;

        case "flatButton":
            MessageBox.Show("Flat button clicked");
            break;

        default:
            MessageBox.Show("Unknown button clicked");
            break;
    }
}

In this code, three push buttons—standardButton, popupButton, and flat­Button—are using the OnClick method as the event handler for their Click event. The OnClick method begins by casting the sender parameter to a Button reference; it then displays different messages based on the sending button control’s Name property.

To route additional events to an existing handler, simply create a new delegate and associate it with the desired method, as shown here:

EventHandler clickHandler = new EventHandler(OnClick);
standardButton.Click += clickHandler;
popupButton.Click += clickHandler;
flatButton.Click += clickHandler;

The Properties window can be used to easily assign multiple events to a single handler. When an event is selected in the Properties window, a drop-down button is displayed on the right side of the cell. Click the drop-down button to display a list of all event handler methods that are capable of handling the event, and then select an event handler from the list to route the event to an existing method.

Removing Event Handlers

Removing an event handler at run time follows the steps outlined in Chapter 6 for removing any delegate or event handler. You must use the subtraction with assignment operator (-=), passing an instance of the delegate to be removed, as shown here:

flatButton.Click -= new EventHandler(OnClick);

Although the code to remove a delegate is somewhat counterintuitive at first glance, it follows the delegate and event handler view of equality: two delegates are equivalent if they’re associated with the same method.

Translating Key Presses into Button Clicks

One way that Windows Forms differ from Win32 dialog boxes is that forms don’t automatically route key presses when the user presses the Enter and Esc keys. As part of the default behavior that all Win32 modal dialog boxes share, pressing the Enter key is translated into a click of the OK button, and pressing the Esc key is translated into a click of the Cancel button. Although the default behavior for Windows Forms doesn’t perform this translation, the following properties can be used to achieve the same result:

  • AcceptButton  Specifies a button that will be clicked when the user presses Enter

  • CancelButton  Specifies a button that will be clicked when the user presses Esc

Assuming that your form has an OK button named okButton, you can use code such as the following to enable a user to click the button by pressing the Enter key:

AcceptButton = okButton;

The following code causes a Click event for a button named cancelButton when the user presses Esc:

CancelButton = cancelButton;
Validating the Contents of Controls

All Windows Forms controls expose events that enable you to easily validate their contents. As mentioned, every control exposes two validation events: Validating and Validated.

Because the Form class is a descendant of Control, all forms also expose the validation events. For a validation event to be raised for a control, its Causes­Validation property must be set to true, which is the default value. You can take advantage of control validation in either of two ways: you can manage the validation events for each control that requires validation, or you can manage the validation events at the form level.

The first approach requires you to provide validation event handlers for each control that requires validation. By explicitly providing an event handler for the Validating event, you indicate your interest in validating input for a specific control. A typical event handler for the Validating event is shown here:

private void OnValidating(object sender, CancelEventArgs e)
{
    if(itemTextBox.Text.Length == 0)
    {
        e.Cancel = true;
        MessageBox.Show("You must provide a value for the new item.",
                        "Validation failure",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Hand);
    }
    else
    {
        _newItem = itemTextBox.Text;
    }
}

As shown in this code, the validation handler tests the user’s input and displays an error message if the input is invalid. In addition, the handler interacts with the CancelEventArgs object passed to it as a parameter. By setting the object’s Cancel property to true, the event handler prevents further processing of the event. This has the effect of keeping the keyboard input focus unchanged and will prevent the user from dismissing the form.

One problem with tying a validation event handler to a particular control is that it tends to complicate the user’s interaction with your form. The event handler for Validating will be called whenever focus is removed from the control or when the form is dismissed. If the user simply tabs away from the control, your validation handler will be called; if this behavior is what you’re looking for, tie your validation handler to the specific control you want to validate. Another problem is that you must handle validation for each control separately, which leads to more complex validation code.

The second approach to handling validation events is used to perform validation at the form level instead of at the control level. In the majority of validation scenarios, you’re interested in validating input only when the user is dismissing the form and explicitly accepts the current control values, usually by clicking an OK or Apply button. When the user clicks the Cancel button in a modal dialog box, there’s usually no reason to perform validation.

By attaching a validation handler to a central location, you can validate user input for the entire form rather than a specific control, which allows users to provide input as they see fit, with validation occurring only when the user is ready to submit. If the user dismisses a modal form with an OK button to confirm input, you can attach the validation handler to the form. Alternatively, if the user clicks an Apply or a Confirm button that leaves the form displayed, you should attach the validation handler to the button control clicked by the user.

Any controls that shouldn’t trigger validation events must have their Causes­Validation property set to false, as shown here:

cancelButton.CausesValidation = false;

As with other properties, you can simply set the Validation property for the control to false through the Properties window.

In a similar vein, if the user clicks the Close icon in the form’s caption to cancel the dialog box, you’ll also want to prevent validation. To prevent validation in this case, you must inhibit validation for the form, as shown here:

// Form constructor
public AddListBoxItemForm()
{
    
    

    CausesValidation = false;
}

Alternatively, you can set the Validation property to false for the form using the Properties window, as is done with other controls.



Part III: Programming Windows Forms