Previous Section  < Day Day Up >  Next Section

6.2. Windows.Forms Control Classes

The previous examples have demonstrated how a custom form is derived from the Windows.Forms.Form class. Now, let's take a broader look at the class hierarchy from which the form derives and the functionality that each class offers (see Figure 6-2).

Figure 6-2. Windows Forms class hierarchy


The Control Class

It may seem counterintuitive that the Form class is below the Control class on the hierarchical chain, because a form typically contains controls. But the hierarchy represents inheritance, not containment. A Form is a container control, and the generic members of the Control class can be applied to it just as they are to a simple Label or TextBox.

System.Windows.Forms.dll contains more than fifty controls that are available for use on a Windows Form. A few that inherit directly from Control are listed in Figure 6-2. All controls share a core set of inherited properties, methods, and events. These members allow you to size and position the control; adorn it with text, colors, and style features; and respond to keyboard and mouse events. This chapter examines the properties that are common to all inheriting controls; Chapter 7 offers a detailed look at the individual controls and how to use their distinct features in applications.

Control Properties

Table 6-1 defines some of the properties common to most types that inherit from Control.

Table 6-1. Common Control Properties

Category

Property

Description

Size and position

Size

A Size object that exposes the width and height.

Location

Location is a Point object that specifies the x and y coordinates of the top left of the control.

Width, Height, Top, Left, Right

These int values are derived from the size and location of the object. For example, Right is equal to Left + Width; Bottom is equal to Top + Height.

Bounds

Bounds is a rectangle that defines a control's position and size:


Button.Bounds =

   new Rectangle (10,10,50,60)


ClientRectangle

ClientRectangle is a rectangle that represents the client area of the control梩hat is, the area excluding scroll bars, titles, and so on.

Anchor

Specifies which edges of the container the control is anchored to. This is useful when the container is resized.

Dock

Controls which edge of the container the control docks to.

Color and appearance

BackColor, ForeColor

Specifies the background and foreground color for the control. Color is specified as a static property of the Color structure.

BackGroundImage

BackGroundImage specifies an image to be used in the background of the control.

Text

Text

Text associated with the control.

Font

Font describes the characteristics of the text font: typeface, size, bold, and so on.

Focus

TabIndex

int value that indicates the tab order of this control within its container.

TabStop

Boolean value that indicates whether this control can receive focus from the Tab key.

Focused

Boolean value indicating whether a control has input focus.

Visible

Boolean value indicating whether the control is displayed.

Keyboard and mouse

MouseButtons

Returns the current state of the mouse buttons (left, right, and middle).

MousePosition

Returns a Point type that specifies the cursor position.

ModifierKeys

Indicates which of the modifier keys (Shift, Ctrl, Alt) is pressed.

Cursor

Specifies the shape of the mouse pointer when it is over the control. Assigned value is a static property of the Cursors class. These include:


.Hand    .Cross   UpArrow      Default

.Beam    .Arrow   WaitCursor


Runtime status

Handle

int value representing the handle of Windows control.

Focused

bool value indicating whether the control has focus.


Working with Controls

When you drag a control onto a form, position it, and size it, VisualStudio.NET (VS.NET) automatically generates code that translates the visual design to the underlying property values. There are times, however, when a program needs to modify controls at runtime to hide them, move them, and even resize them. In fact, size and position are often based on the user's screen size, which can only be detected at runtime. An IDE cannot do this, so it is necessary that a programmer understand how these properties are used to design an effective control layout.

Size and Position

As we saw in the earlier example, the size of a control is determined by the Size object, which is a member of the System.Drawing namespace:


button1.Size = new Size(80,40);  // (width, height)

button2.Size = button1.Size;     // Assign size to second button


A control can be resized during runtime by assigning a new Size object to it. This code snippet demonstrates how the Click event handler method can be used to change the size of the button when it is clicked:


private void button1_Click(object sender, System.EventArgs e)

{

   MessageBox.Show("Up and Running");

   Button button1 = (Button) sender;  //Cast object to Button

   button1.Size = new Size(90,20);    //Dynamically resize


The System.Drawing.Point object can be used to assign a control's location. Its arguments set the x and y coordinates梚n pixels梠f the upper-left corner of a control. The x coordinate is the number of pixels from the left side of the container. The y coordinate is the number of pixels from the top of the container.


button1.Location = new Point(20,40);  // (x,y) coordinates


It is important to recognize that this location is relative to the control's container. Thus, if a button is inside a GroupBox, the button's location is relative to it and not the form itself. A control's Location also can be changed at runtime by assigning a new Point object.

Another approach is to set the size and location in one statement using the Bounds property:


button1.Bounds = new Rectangle(20,40, 100,80);


A Rectangle object is created with its upper-left corner at the x,y coordinates (20,40) and its lower-right coordinates at (100,80). This corresponds to a width of 80 pixels and a height of 40 pixels.

How to Anchor and Dock a Control

The Dock property is used to attach a control to one of the edges of its container. By default, most controls have docking set to none; some exceptions are the StatusStrip/StatusBar that is set to Bottom and the ToolStrip/ToolBar that is set to Top. The options, which are members of the DockStyle enumeration, are Top, Bottom, Left, Right, and Fill. The Fill option attaches the control to all four corners and resizes it as the container is resized. To attach a TextBox to the top of a form, use


TextBox1.Dock = DockStyle.Top;


Figure 6-3 illustrates how docking affects a control's size and position as the form is resized. This example shows four text boxes docked, as indicated.

Figure 6-3. Control resizing and positioning using the Dock property


Resizing the form does not affect the size of controls docked on the left or right. However, controls docked to the top and bottom are stretched or shrunk horizontally so that they take all the space available to them.

Core Note

The Form class and all other container controls have a DockPadding property that can be set to control the amount of space (in pixels) between the container's edge and the docked control.


The Anchor property allows a control to be placed in a fixed position relative to a combination of the top, left, right, or bottom edge of its container. Figure 6-4 illustrates the effects of anchoring.

Figure 6-4. How anchoring affects the resizing and positioning of controls


The distance between the controls' anchored edges remains unchanged as the form is stretched. The PictureBox (1) is stretched horizontally and vertically so that it remains the same distance from all edges; the Panel (2) control maintains a constant distance from the left and bottom edge; and the Label (3), which is anchored only to the top, retains its distance from the top, left, and right edges of the form.

The code to define a control's anchor position sets the Anchor property to values of the AnchorStyles enumeration (Bottom, Left, None, Right, Top). Multiple values are combined using the OR ( | ) operator:


btnPanel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left);


Tab Order and Focus

Tab order defines the sequence in which the input focus is given to controls when the Tab key is pressed. The default sequence is the order in which the controls are added to the container.

The tab order should anticipate the logical sequence in which users expect to input data and thus guide them through the process. The form in Figure 6-5 represents such a design: The user can tab from the first field down to subsequent fields and finally to the button that invokes the final action.

Figure 6-5. Tab order for controls on a form


Observe two things in the figure: First, even though labels have a tab order, they are ignored and never gain focus; and second, controls in a container have a tab order that is relative to the container梟ot the form.

A control's tab order is determined by the value of its TabIndex property:


TextBox1.TabIndex = 0;  //First item in tab sequence


In VS.NET, you can set this property directly with the Property Manager, or by selecting ViewTabOrder and clicking the boxes over each control to set the value. If you do not want a control to be included in the tab order, set its TabStop value to false. This does not, however, prevent it from receiving focus from a mouse click.

When a form is loaded, the input focus is on the control (that accepts mouse or keyboard input) with the lowest TabIndex value. During execution, the focus can be given to a selected control using the Focus method:


if(textBox1.CanFocus)

{ textBox1.Focus(); }


Iterating Through Controls on a Form

All controls on a form are contained in a Controls collection. By enumerating through this collection, it is possible to examine each control, identify it by name and type, and modify properties as needed. One common use is to clear the value of selected fields on a form in a refresh operation. This short example examines each control in Figure 6-5 and displays its name and type:


int ctrlCt = this.Controls.Count;   //  8

foreach (Control ct in this.Controls)

{

   object ob = ct.GetType();

   MessageBox.Show(ob.ToString());  //Displays type as string

   MessageBox.Show(ct.Name);

}


There are several things to be aware of when enumerating control objects:

  • The type of each control is returned as a fully qualified name. For example, a TextBox is referred to as System.Forms.Form.TextBox.

  • Only a container's top-level objects are listed. In this example, the Controls.Count value is 8 rather than 10 because the GroupBox is counted as one control and its child controls are excluded.

  • You can use a control's HasChildren property to determine if it is a container. Listing 6-2 uses recursion to list all child controls.

Listing 6-2. Enumerate All Controls on a Form Recursively

void IterateThroughControls(Control parent)

{

   foreach (Control c in parent.Controls)

   {

      MessageBox.Show(c.ToString());

      if(c.HasChildren)

      {

         IterateThroughControls(c);

      }

   }

}


Applying this code to Figure 6-5 results in all controls on the main form being listed hierarchically. A control is listed followed by any child it may have.

Control Events

When you push a key on the keyboard or click the mouse, the control that is the target of this action fires an event to indicate the specific action that has occurred. A registered event handling routine then responds to the event and formulates what action to take.

The first step in handling an event is to identify the delegate associated with the event. You must then register the event handling method with it, and make sure the method's signature matches the parameters specified by the delegate. Table 6-2 summarizes the information required to work with mouse and keyboard triggered events.

Table 6-2. Control Events

Event

Built-In Delegate/Parameters

Description


Click,

DoubleClick,

MouseEnter,

MouseLeave,

MouseHover,

MouseWheel



EventHandler (

   object sender, EventArgs e)


Events triggered by clicking, double clicking, or moving the mouse.


MouseDown,

MouseUp,

MouseMove



MouseEventHandler (

   object sender, MouseEventArgs)


Events triggered by mouse and mouse button motions. Note that this event is not triggered if the mouse action occurs within a control in the current container.


KeyUp,

KeyDown



KeyEventHandler (

   object sender, KeyEventArgs e)


Events triggered by key being raised or lowered.

KeyPress


KeyPressEventHandler (

   object sender, 

   KeyPressEventArgs e)


Event triggered by pressing any key.


Handling Mouse Events

In addition to the familiar Click and DoubleClick events, all Windows Forms controls inherit the MouseHover, MouseEnter, and MouseLeave events. The latter two are fired when the mouse enters and leaves the confines of a control. They are useful for creating a MouseOver effect that is so common to Web pages.

To illustrate this, let's consider an example that changes the background color on a text box when a mouse passes over it. The following code sets up delegates to call OnMouseEnter and OnMouseLeave to perform the background coloring:


TextBox userID = new TextBox();

userID.MouseEnter += new EventHandler(OnMouseEnter);

userID.MouseLeave += new EventHandler(OnMouseLeave);


The event handler methods match the signature of the EventHandler delegate and cast the sender parameter to a Control type to access its properties.


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

   Control ctrl = (Control) sender;

   ctrl.BackColor= Color.Bisque;

} 

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

   Control ctrl = (Control) sender;

   ctrl.BackColor= Color.White;

}


Core Note

It is possible to handle events by overriding the default event handlers of the base Control class. These methods are named using the pattern Oneventname梖or example, OnMouseDown, OnMouseMove, and so on. To be consistent with .NET, the delegate approach is recommended over this. It permits a control to specify multiple event handlers for an event, and it also permits a single event handler to process multiple events.


The delegates for the MouseDown, MouseUp, and MouseMove events take a second argument of the MouseEventArgs type rather than the generic EventArgs type. This type reveals additional status information about a mouse via the properties shown in Table 6-3.

Table 6-3. Properties of MouseEventArgs

Property

Description

Button

Indicates which mouse button was pressed. The attribute value is defined by the MouseButtons enumeration: Left, Middle, None, Right

Clicks

Number of clicks since last event.

Delta

Number of detents the mouse wheel rotates. A positive number means it's moving forward; a negative number shows backward motion.

X, Y

Mouse coordinates relative to the container's upper-left corner. This is equivalent to the control's MousePosition property.


The properties in this table are particularly useful for applications that must track mouse movement across a form. Prime examples are graphics programs that rely on mouse location and button selection to control onscreen drawing. To illustrate, Listing 6-3 is a simple drawing program that draws a line on a form's surface, beginning at the point where the mouse key is pressed and ending where it is raised. To make it a bit more interesting, the application draws the line in black if the left button is dragged, and in red if the right button is dragged.

Listing 6-3. Using Mouse Events to Draw on a Form

using System;

using System.Windows.Forms;

using System.Drawing;

class MyWinApp

{

   static void Main() {

   Form mainForm = new DrawingForm();

   Application.Run(mainForm);  

   }

}

// User Form derived from base class Form

class DrawingForm:Form

{

   Point lastPoint = Point.Empty; // Save coordinates

   public Pen myPen;      //Defines color of line

   public DrawingForm()

   {

      this.Text = "Drawing Pad";

      // reate delegates to call MouseUp and MouseDown

      this.MouseDown += new MouseEventHandler(OnMouseDown);

      this.MouseUp   += new MouseEventHandler(OnMouseUp);

   }

   private void OnMouseDown(object sender, MouseEventArgs e)

   {

      myPen = (e.Button==MouseButtons.Right)? Pens.Red:

            Pens.Black;

      lastPoint.X = e.X;

      lastPoint.Y = e.Y;

   }



   private void OnMouseUp(object sender, MouseEventArgs e)

   {

      // Create graphics object

      Graphics g = this.CreateGraphics(); 

      if (lastPoint != Point.Empty) 

         g.DrawLine(myPen, lastPoint.X,lastPoint.Y,e.X,e.Y);

      lastPoint.X = e.X;

      lastPoint.Y = e.Y;

      g.Dispose();

   }

}


Even without an understanding of .NET graphics, the role of the graphics-related classes should be self-evident. A Graphics object is created to do the actual drawing using its DrawLine method. The parameters for this method are the Pen object that defines the color and the coordinates of the line to be drawn. When a button is pressed, the program saves the coordinate and sets myPen based on which button is pressed: a red pen for the right button and black for the left. When the mouse button is raised, the line is drawn from the previous coordinate to the current location. The MouseEventArgs object provides all the information required to do this.

Handling Keyboard Events

Keyboard events are also handled by defining a delegate to call a custom event handling method. Two arguments are passed to the event handler: the sender argument identifies the object that raised the event and the second argument contains fields describing the event. For the KeyPress event, this second argument is of the KeyPressEventArgs type. This type contains a Handled field that is set to TRue by the event handling routine to indicate it has processed the event. Its other property is KeyChar, which identifies the key that is pressed.

KeyChar is useful for restricting the input that a field accepts. This code segment demonstrates how easy it is to limit a field's input to digits. When a non-digit is entered, Handled is set to TRue, which prevents the form engine from displaying the character. Otherwise, the event handling routine does nothing and the character is displayed.


private void OnKeyPress(object sender,  KeyPressEventArgs e)

{

   if (! char.IsDigit(e.KeyChar)) e.Handled = true;

}


The KeyPress event is only fired for printable character keys; it ignores non-character keys such as Alt or Shift. To recognize all keystrokes, it is necessary to turn to the KeyDown and KeyUp events. Their event handlers receive a KeyEventArgs type parameter that identifies a single keystroke or combination of keystrokes. Table 6-4 lists the important properties provided by KeyEventArgs.

Table 6-4. KeyEventArgs Properties

Member

Description

Alt, Control, Shift

Boolean value that indicates whether the Alt, Control, or Shift key was pressed.

Handled

Boolean value that indicates whether an event was handled.

KeyCode

Returns the key code for the event. This code is of the Keys enumeration type.

KeyData

Returns the key data for the event. This is also of the Keys enumeration type, but differs from the KeyCode in that it recognizes multiple keys.

Modifiers

Indicates which combination of modifier keys (Alt, Ctrl, and Shift) was pressed.


A few things to note:

  • The Alt, Control, and Shift properties are simply shortcuts for comparing the KeyCode value with the Alt, Control, or Shift member of the Keys enumeration.

  • KeyCode represents a single key value; KeyData contains a value for a single key or combination of keys pressed.

  • The Keys enumeration is the secret to key recognition because its members represent all keys. If using Visual Studio.NET, Intellisense lists all of its members when the enum is used in a comparison; otherwise, you need to refer to online documentation for the exact member name because the names are not always what you expect. For example, digits are designated by D1, D2, and so on.

The preceding code segment showed how to use KeyPress to ensure a user presses only number keys (0?). However, it does not prevent one from pasting non-numeric data using the Ctrl-V key combination. A solution is to use the KeyDown event to detect this key sequence and set a flag notifying the KeyPress event handler to ignore the attempt to paste.

In this example, two event handlers are registered to be called when a user attempts to enter data into a text box using the keyboard. KeyDown is invoked first, and it sets paste to true if the user is pushing the Ctrl-V key combination. The KeyPress event handler uses this flag to determine whether to accept the key strokes.


private bool paste;



//Register event handlers for TextBox t.

//They should be registered in this order,

//because the last registered is the

//first executed

t.KeyPress += new KeyPressEventHandler(OnKeyPress);

t.KeyDown  += new KeyEventHandler(OnKeyDown);



private void OnKeyDown(object sender, KeyEventArgs e)

{

if (e.Modifiers == Keys.Control  && e.KeyCode == Keys.V)

{

   paste=true; //Flag indicating paste attempt

   string msg = string.Format("Modifier: {0} \nKeyCode: {1}

       \nKeyData: {2}", e.Modifiers.ToString(),

        e.KeyCode.ToString(), e.KeyData.ToString());

   MessageBox.Show(msg);      //Display property values

}



private void OnKeyPress(object sender, KeyPressEventArgs e)

{

   if (paste==true)  e.Handled = true;

}


This program displays the following values for the selected KeyEventArgs properties when Ctrl-V is pressed:


Modifier:  Control

KeyCode:   V

KeyData:   Control, V


    Previous Section  < Day Day Up >  Next Section