An event is something that a control raises to tell the program that something significant has happened. Events are extremely important because they are the main way the user controls the program. When the user clicks buttons, drags sliders, and selects menu items, events tell the program that something has happened so it can take action.
An event handler is a piece of code that catches the event and executes when an event occurs. The event handler might display a message, perform a calculation, or download the latest Dilbert comic from the web.
Lesson 2 briefly explained how you can catch a Button
's Click
event, but that event is only one of hundreds (if not thousands) of events that your programs can catch.
This lesson explains how you can catch events other than Click
. It describes some of the most useful events provided by common controls and, as a bonus, explains how you can display messages to the user when events occur.
The easiest way to build an event handler is to double-click a control in the Form Designer. This creates an empty event handler for the control's default event and opens the event handler in the Code Editor. You can then type C# code to take whatever action is appropriate.
The following code shows the empty Click
event handler created for a Button
:
private void crashSystemButton_Click(object sender, EventArgs e)
{
}
Probably the most commonly used events are the Click
events raised by Button
s, ToolStripMenuItem
s (which represent menu items), and ToolStripButton
s (which represent toolbar buttons). For these controls and many others, you almost always want to use the default event handler, so double-clicking them is the easiest way to go.
Most controls provide dozens of other events that you can catch. To create an event handler for one of these non-default events, select the control in the Form Designer. Then click the lightning bolt icon near the top of the Properties window to make the window list the control's events. Figure 4.1 shows the Properties window displaying some of the events that a Button
can raise.
To create an empty event handler for an event, simply double-click the event's name in the Properties window's event list.
You can also type the name that you want to give the event handler. When you press Enter, Visual Studio creates the event handler and opens it in the Code Editor.
If your code already contains event handlers that could handle the event, you can click the event and then click the dropdown arrow to the right to select one of those event handlers.
All event handlers include parameters that give additional information about the event. Later lessons say more about parameters and how you can use them, but for now you should know that sometimes they can tell you more about the event.
For example, the following code shows a Button
's Click
event handler. The parameters sender
and e
give extra information about the event.
private void crashSystemButton_Click(object sender, EventArgs e)
{
}
In all event handlers, the sender
parameter tells you what control raised the event. In this example, that's the Button
control that the user clicked.
The e
parameter has the EventArgs
data type, which doesn't give you a lot of additional information. Fortunately, you usually don't need any additional information for a Button
. Just knowing it was clicked is enough.
Some event handlers, however, provide really useful information in their e
parameter. For example, the e
parameter provided by the mouse events MouseClick
, MouseMove
, MouseDown
, and MouseUp
include the X and Y coordinates of the mouse over the control raising the event. Those values are crucial if you're trying to build a drawing application or need to track the mouse's position for some other reason.
The FollowMouse example program shown in Figure 4.2 uses a MouseMove
event handler to make two scrollbars follow the mouse's position. When you click the area in the center of the form, the program moves the picture of the mouse to that position.
The program's form contains a green Panel
control that holds a PictureBox
holding the mouse image. The form also contains VScrollBar
and HScrollBar
controls. The program uses event handlers to do three things: set scrollbar properties, track mouse movement, and move the mouse picture.
When you create a new scrollbar, it has Minimum = 0
and Maximum = 100
so it can take values between 0 and 100. However, this program sets its scrollbars to locations on the Panel
control. For example, if the Panel
is 200 pixels wide, the program might need to give the horizontal scrollbar a value between 0 and 199. Unfortunately if the scroll's Maximum
property is 100 and the program sets its value to 199, the program will crash.
To prevent that, the program sets the scrollbars' Maximum
properties to the width and height of the Panel
. The Panel
might resize at two different times, so the program needs to set the Maximum
properties in two places.
First, when the program's form is initially displayed, the program needs to set the scrollbar Maximum
properties. To detect when the form is displayed, you can catch the form's Load
event.
The following code shows the program's Load
event handler:
// Set the scrollbar maximums to fit the Panel.
private void Form1_Load(object sender, EventArgs e)
{
mouseHScrollBar.Maximum = fieldPanel.Width;
mouseVScrollBar.Maximum = fieldPanel.Height;
}
This code sets the mouseHScrollBar
control's Maximum
property to the width of the Panel
. It then sets the mouseVScrollBar
control's Maximum
property to the height of the Panel
.
Now the program won't crash when you move the mouse around over the Panel
control, unless you resize the form. The Panel
control's Anchor
property makes it resize with the form, so if you make the form bigger, the Panel
gets bigger, too. In that case, the program needs to reset the scrollbars' Maximum
properties to match.
You can do that in the form's Resize
event. This isn't the default event for a form (Load
is), so you can't just double-click the form to create a Resize
event handler. Instead you need to select the form, go to the Properties window, click the Events button (the little lightning bolt), and double-click the Resize
event.
The following code shows this program's Resize
event handler:
private void Form1_Resize(object sender, EventArgs e)
{
mouseHScrollBar.Maximum = fieldPanel.Width;
mouseVScrollBar.Maximum = fieldPanel.Height;
}
This code does the same thing as the form's Load
event handler.
Now even if you resize the form, the scrollbars can hold the coordinates of any point inside the Panel
.
When you move the mouse over the Panel
, the Panel
control raises a MouseMoved
event. For this program, I used the Properties window to create the following MouseMove
event handler:
// Move the scrollbars to track the mouse.
private void fieldPanel_MouseMove(object sender, MouseEventArgs e)
{
mouseHScrollBar.Value = e.X;
mouseVScrollBar.Value = e.Y;
}
This code sets the horizontal scrollbar's value equal to the mouse's X position as reported by the event handler's e.X
parameter. It then sets the vertical scrollbar's value equal to the mouse's Y position as reported by the event handler's e.Y
parameter.
The form's Load
and Resize
event handlers guarantee that the scrollbars' Maximum
properties are big enough to hold any coordinates on the Panel
.
When you click the Panel
, the Panel
raises a MouseClick
event. For this program, I used the Properties window to create the following MouseClick
event handler:
// Move the mouse PictureBox to the point clicked.
private void fieldPanel_MouseClick(object sender, MouseEventArgs e)
{
mousePictureBox.Left = e.X;
mousePictureBox.Top = e.Y;
}
This code simply sets the mouse PictureBox
's Left
and Top
properties to the coordinates of the point that was clicked.
That's all there is to the program. If you like, you can download it and experiment with it.
Getting rid of an event handler isn't as simple as you might like. If you just delete the event handler's code, the program still includes automatically generated code that attaches the event handler to the control that raises it. When you try to move to the Form Designer, you'll get an error similar to:
All this really means is that C# is confused.
The Properties window gives you an easy way to safely remove event handlers. Before you delete the event handler's code, find the event handler in the Properties window. Right-click the event handler's name and select Reset to break the link between the event handler and the control. Now you can safely remove the event handler's code.
Alternatively, you can double-click the error in the Error window to see the automatically generated code that's making C# throw its temper tantrum. The line should look something like this:
this.fieldPanel.MouseClick +=
new System.Windows.Forms.MouseEventHandler(this.fieldPanel_MouseClick);
Delete that line and you should be ready to run again.
WPF programs attach event handlers a bit differently. In a WPF application, the XAML code includes a property that defines the name of the event handler in C# code. For example, the following XAML code defines a button with a Click
event handler:
<Button x:Name="clickMeButton" Content="Click Me"
Width="75" Height="20" Click="clickMeButton_Click"/>
If you remove the event handler's C# code, you should also remove Click="clickMeButton_Click"
from the XAML code.
At design time, you can use the Properties window to attach and detach event handlers. Occasionally you may want to add or remove an event handler by using code at run time.
The following code shows a simple Button Click
event handler. When this event handler executes, it displays a message to the user:
// Display a message box.
private void clickMeButton_Click(object sender, EventArgs e)
{
MessageBox.Show("You clicked me!");
}
Suppose you have written this event handler but have not attached it to any control at design time. The following code attaches the event handler to the clickMeButton
control's Click
event:
clickMeButton.Click += clickMeButton_Click;
The +=
operator means “add to,” so this code adds the event handler to the clickMeButton.Click
event.
After running this code, if the user clicks the clickMeButton
, the event handler executes.
The following code removes the event handler from the button's Click
event:
clickMeButton.Click -= clickMeButton_Click;
The -=
operator means “subtract from,” so this code removes the event handler from the clickMeButton.Click
event.
The DynamicEvents example program shown in Figure 4.3 lets you add and remove event handlers at run time. Initially the Click Me button does nothing. Click the Attach button to attach an event handler to the Click Me button. Click the Detach button to remove the event handler.
Table 4.1 lists some of the more useful events raised by various controls.
Event | Meaning |
CheckedChanged |
A CheckBox 's or RadioButton 's checked state has changed. |
Click |
The user has clicked the control. |
FormClosing |
The form is about to close. Set the e.Cancel parameter to true to cancel the closing and force the form to remain open. |
KeyDown |
The user pressed a key down while this control had focus. |
KeyPress |
The user pressed and released a key while this control had focus. |
KeyUp |
The user released a key while this control had focus. |
Load |
The form is loaded but not yet visible. This is the last place you can change the form's appearance before the user sees it. |
MouseClick |
The user pressed and released a mouse button over the control. Unlike the Click event, this event has parameters that give the click's location. |
MouseDown |
The user pressed a mouse button down over the control. |
MouseEnter |
The mouse entered the control. |
MouseHover |
The mouse hovered over the control. |
MouseLeave |
The mouse left the control. |
MouseMove |
The mouse moved while over the control. |
MouseUp |
The user released a mouse button over the control. |
Move |
The control has moved. |
Paint |
The control needs to be redrawn. (This is useful for drawing graphics.) |
Resize |
The control has resized. |
Scroll |
The slider on a TrackBar or scrollbar was moved by the user. |
SelectedIndexChanged |
A ComboBox 's or ListBox 's selection has changed. |
TextChanged |
The control's Text property has changed. (This is particularly useful for TextBox es.) |
Tick |
A Timer control's Interval has elapsed. |
ValueChanged |
The value of a TrackBar or scrollbar has changed (whether by the user or by code). |
In this Try It, you use event handlers to display color samples as the user adjusts red, green, and blue scrollbars.
Figure 4.4 shows the finished program in action. When you change a scrollbar's value, the label to the right shows the color component's new numeric value, and the large label on the far right shows a sample of the color with the selected red, green, and blue color components.
In this lesson, you:
Anchor
properties.This Try It requires a few techniques that haven't been covered yet, but it's not too hard to build with a couple of hints.
Value
property is an integer. To convert it into a string so you can display it in a label, call its ToString
method. For example, the following code makes the redLabel
control display the redHScrollBar
's Value
property:
redLabel.Text = redHScrollBar.Value.ToString();
Color
class's FromArgb
method returns a color with given red, green, and blue color components between 0 and 255. For example, Color.FromArgb(255, 128, 0)
returns the color orange (red = 255, green = 128, and blue = 0). Pass this method the values selected by the scrollbars (returned by their Value
properties) and assign the result to the sample label's BackColor
property.Anchor
properties.
Minimum = 0
, Maximum
= 264
, SmallChange
= 1
, LargeChange
= 10
, and Anchor
= Top, Left, Right
.
Make an event handler for the red scrollbar that displays all three color values and the color sample.
Scroll
event. Type the bold lines in the following code so the event handler looks like this:// Display a color sample.
private void redHScrollBar_Scroll(object sender, ScrollEventArgs e)
{
redLabel.Text = redHScrollBar.Value.ToString();
greenLabel.Text = greenHScrollBar.Value.ToString();
blueLabel.Text = blueHScrollBar.Value.ToString();
sampleLabel.BackColor = Color.FromArgb(
redHScrollBar.Value,
greenHScrollBar.Value,
blueHScrollBar.Value);
}
The first three lines of code make the Label
s display the corresponding scrollbar values. The final statement, which is split across four lines of code, sets the sample Label
's BackColor
property to a color defined by the scrollbars' values.
Attach the event handler to the green and blue scrollbars, as well as the red one.
Scroll
event, click the dropdown arrow to the right, and select the event handler you already created.Run the program and experiment with it. Note how the largest value you can select in the scrollbars is 255.
ScrollBar
controls, one with Orientation = Horizontal
.Canvas
instead of a Panel
and an Image
instead of a PictureBox
.Load
and Resize
events, catch the Canvas
control's SizeChanged
event and give it the following event handler:
// Set the scrollbar maximums to fit the Canvas.
private void fieldCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
mouseHScrollBar.Maximum = e.NewSize.Width;
mouseVScrollBar.Maximum = e.NewSize.Height;
}
Canvas
's MouseMove
event handler:
// Move the scrollbars to track the mouse.
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
Point location = Mouse.GetPosition(fieldCanvas);
mouseHScrollBar.Value = location.X;
mouseVScrollBar.Value = location.Y;
}
Image
control in the Canvas
control's upper-left corner. Then use the following MouseDown
event handler:
// Move the mouse Image to the point clicked.
private void fieldCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
Point location = Mouse.GetPosition(fieldCanvas);
mouseImage.Margin = new Thickness(location.X, location.Y, 0, 0);
}
Button
labeled “Stop” and two Timer
s named leftTimer
and rightTimer
. Set the Timer
s' Interval
properties to 1000. At design time, set leftTimer
's Enabled
property to True
.
Timer
's Tick
event handler, disable that Timer
and enable the other one.Timer
's Tick
event handler also move the Button
to (10, 10)
by setting its Left
and Top
properties.Timer
's Tick
event handler move the Button
to (200, 200)
.Button
's Click
event handler, set Enabled
= false
for both Timer
s.Run the program. Experiment with different values for the Timer
s' Interval
properties. What happens if Interval
= 10
?
PictureBox
.Image
. (Hints: To save code, use the same event handler for both scrollbars. To prevent the mouse image from appearing on top of the scrollbars and in the small area on the window's lower-right corner, set the Canvas
control's ClipToBounds
property to True
.)GroupBox
.
Hints:
GroupBox
es' Text
properties. Then place the CheckBox
es over the GroupBox
es where their text would go. (Be sure not to place the CheckBox
es inside the GroupBox
es. Try it to see why it won't work. You may need to position the CheckBox
es first and then move the GroupBox
es into position.)GroupBox
, set its Enabled
property equal to the corresponding CheckBox
's Enabled
property as in the following code:
// Enable or disable the corresponding GroupBox.
private void breakfastCheckBox_CheckedChanged(object sender, EventArgs e)
{
breakfastGroupBox.Enabled = breakfastCheckBox.Checked;
}
CheckBox
doesn't display a background so the checkboxes in this program won't cover the GroupBox
borders below them. To work around that problem, place each CheckBox
inside a Canvas
and make the Canvas
use a white background.GroupBox
can have only a single child. Give each GroupBox
a StackPanel
holding RadioButton
s. Use the RadioButton
s' Margin
properties to add some spacing between the choices.CheckBox
control doesn't have a CheckChanged
event. Use the Click
event instead.GroupBox
, set its IsEnabled
property equal to the corresponding CheckBox
's IsChecked.Value
property as in the following code:
// Enable or disable the appropriate GroupBox.
private void breakfastCheckBox_Click(object sender, RoutedEventArgs e)
{
breakfastGroupBox.IsEnabled = breakfastCheckBox.IsChecked.Value;
}
Button
's IsDefault
property to True
.Button
's IsCancel
property to True
.PictureBox
and load an image into it. Set its size to match the picture's size and set its ScaleMode
property to StretchImage
. (Also see what happens if you set this to Normal
.)PictureBox
. Set their Maximum
properties so the user can select values between 0 and the picture's width/height.Value
properties equal to the image's width/height.Scroll
event handlers set the PictureBox
's width/height equal to the scrollbars' values.Image
control's Stretch
property to Fill
. (Also see what happens if you set this to None
.)ScrollBar
to its Maximum
value.Label
in the lower right.
Hints:
Image
control. Name the label control degreesLabel
.Image
by 360 degrees.rotateTransform
a property called x:Name
with value rotateTransform
. The Image
control's code should look something like this:
<Image x:Name="image" HorizontalAlignment="Left"
VerticalAlignment="Top" Source="ScienceGirl.png"
Stretch="None" Grid.ColumnSpan="2"
Margin="10,10,0,0" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform x:Name="rotateTransform"
Angle="360"/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
ScrollBar
's ValueChanged
event handler look like this:
// Rotate the Image.
private void degreesScrollBar_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
rotateTransform.Angle = degreesScrollBar.Value;
degreesLabel.Content = degreesScrollBar.Value.ToString("0");
}
Grid
that contains an Image
. Give the Grid
a MouseMove
event handler that moves the Image
to the mouse's location. (Hint: By default, the window's main Grid
has a transparent background so it won't receive mouse events correctly. To fix that, set its Background
brush. You can use any brush as long as it's not transparent.)TrackBar
and an HScrollBar
. Set their Maximum
properties to 10, and set the scrollbar's LargeChange
property to 1. When the controls' values change at run time, display the new values in Label
s.Slider
s and a ScrollBar
all with Maximum
set to 10. Set IsSnapToTickEnabled
to True
for one of the Slider
s. When the controls' values change at run time, display the new values in Label
s.Hints:
Label
s inside a multi-column TableLayoutPanel
.Label
s appear at the bottom of the TableLayoutPanel
, set their Anchor
properties to Bottom, Left, Right
.Label
s touch, set their Margin
properties to 0, 0, 0, 0.Label
s. Note that the statements in bold are outside of the event handler.
// Make a random number generator.
private Random Rand = new Random();
// Pick random values for the Labels.
private void pickValuesButton_Click(object sender, EventArgs e)
{
label1.Height = Rand.Next(10, 150);
label2.Height = Rand.Next(10, 150);
label3.Height = Rand.Next(10, 150);
label4.Height = Rand.Next(10, 150);
label5.Height = Rand.Next(10, 150);
label6.Height = Rand.Next(10, 150);
}
Timer
to pick random Label
heights instead of using a Button
. Set the Timer
's properties Enabled = True
and Interval = 500
.