Chapter 15. Windows Forms

Windows Forms is a part of the .NET Framework that is used to create user interfaces for local applications, often called Win32 clients. Windows Forms does not change in moving from Visual Basic 2005 to Visual Basic 2008. Accordingly, the version number used for Windows Forms in Visual Studio 2008 is still 2.0.

The pace of change in Windows Forms is slowing because of the advent of Windows Presentation Foundation (WPF). Visual Studio 2008 is the first version of Visual Studio with a capable visual designer for WPF. Going forward, you can expect continued innovation in WPF, but not much in Windows Forms. However, that does not imply that you should abandon Windows Forms or be reluctant to write programs in it. Windows Forms still has many advantages over WPF.

Those advantages include a more complete set of controls and a mature, easy-to-use designer. The result is faster development in Windows Forms compared to WPF. WPF has advantages of its own, of course. These are discussed in Chapter 17, which provides an introduction to WPF.

This chapter summarizes the changes in Windows Forms 2.0 compared to the earlier 1.0 and 1.1 versions that were present in Visual Studio 2002 and 2003. This enables those with some experience in previous versions of Windows Forms to quickly identify key changes. Then the chapter looks at the behavior of forms and controls, with emphasis on those elements that are most important for routine application development.

Chapter 16 includes more advanced treatment of certain aspects of Windows Forms. After gaining a basic understanding of the key capabilities in this chapter, you'll be ready to go on to the more advanced concepts in that chapter.

Changes in Windows Forms Version 2.0

If you have already used Windows Forms 1.0 or 1.1, much of the material in this chapter will be familiar to you. To help you quickly focus on the new capabilities in Windows Forms 2.0, here is a summary of changes and additions.

Changes to Existing Controls

Changes to existing controls in Windows Forms 2.0 are minor, but are helpful additions of functionality. Some changes apply to all controls because they are part of the base Control class in Windows Forms 2.0. Other changes apply only to specific controls.

Smart Tags

Many controls in Windows Forms 2.0 display a small triangle, or glyph, toward the top, right side of the control when highlighted in the visual designer. This is a new feature called a smart tag. If you click this glyph, you get a pop-up dialog with common operations for the control. Many of the features that you access through the property box can also be accessed through the smart tag. In general, this chapter uses the Properties window for manipulating properties, but you should be aware of the smart tag shortcut for commonly used features.

AutoCompletion in Text Boxes and Combo Boxes

Text boxes and combo boxes have new properties for autocompletion of text entries. This capability could be added manually or with third-party controls in previous versions, but is now built in. The AutoCompleteMode property controls how autocompletion works in the control, while the AutoCompleteSource and AutoCompleteCustomSource properties tell the control where to get entries for autocompletion.

An example of autocompletion in action is shown later, in the section entitled "Advanced Capabilities for Data Entry."

New Properties for All Controls

The base Control class, which is a base class for all Windows Forms controls, has several new properties in Windows Forms 2.0. Because all controls inherit from this class, all Windows Forms controls gain these new properties and the new functionality that goes along with them.

Two of the new properties, Padding and Margin, are most useful when used in conjunction with some new controls, TableLayoutPanel and FlowLayoutPanel. Those two properties are discussed later in the chapter. The other new properties are discussed here. They include MaximumSize, MinimumSize, and UseWaitCursor.

MaximumSize and MinimumSize Properties

The MaximumSize and MinimumSize properties specify the control's maximum and minimum height and width, respectively. Forms had these properties in Windows Forms 1.0 and 1.1, but now all controls have them.

If the maximum height and width are both set to the default value of 0, then there is no maximum. Similarly, if the minimum height and width are set to zero, then there is no minimum. The form or control can be any size.

If these properties are set to anything else, then the settings become limits on the size of the control. For example, if the MaximumSize height and width are both set to 100, then the control cannot be bigger than 100 × 100 pixels. The visual designer will not make the control any larger on the form design surface. Attempting to set the height or width of the control in code at runtime to a value greater than 100 will cause it to be set to 100 instead.

The MaximumSize and MinimumSize properties can be reset at runtime to enable sizing of the controls outside the limits imposed at design time. However, the properties have a return type of Size, so resetting either property requires creating a Size structure. For example, you can reset the MinimumSize property for a button named Button1 with the following line of code:

Button1.MinimumSize = New Size(20, 20)

This sets the new minimum width and height to 20 pixels.

The Size structure has members for Height and Width, which can be used to fetch the current minimum or maximum sizes for either height or width. For example, to find the current minimum height for Button1, use the following line of code:

Dim n As Integer = Button1.MinimumSize.Height

UseWaitCursor Property

Windows Forms interfaces can use threading or asynchronous requests to allow tasks to execute in the background. When a control is waiting for an asynchronous request to finish, it is helpful to indicate that to the user by changing the mouse cursor when the mouse is inside the control. Normally, the cursor used is the familiar hourglass, which is called the WaitCursor in Windows Forms.

For any control, setting the UseWaitCursor property to True causes the cursor to change to the hourglass (or whatever is being used for the WaitCursor) while the mouse is positioned inside the control. This allows a control to visually indicate that it is waiting for something. The typical usage is to set UseWaitCursor to True when an asynchronous process is begun and then set it back to False when the process is finished and the control is ready for normal operation again.

New Controls

Windows Forms 2.0 includes a number of new controls. Some are brand-new controls that offer completely new functionality. Others are replacements for existing controls, offering additional functionality.

WebBrowser Control

Even smart client applications often need to display HTML or browse websites. Windows Forms 1.0 or 1.1 did not include a true Windows Forms control for browsing. The legacy ActiveX browsing control built into Windows could be used via interoperability, but this had drawbacks for deployment and versioning.

The legacy ActiveX control is still the ultimate foundation for browsing capability, but Windows Forms 2.0 includes an intelligent Windows Forms wrapper that makes it much easier to use and deploy the control.

MaskedTextbox Control

Windows Forms 1.0 offered replacements for almost all of the controls available in Visual Basic 6, but one notable exception was the MaskedEdit control. In Windows Forms 1.0 and 1.1, masked edit capabilities were available only through third-party controls or by doing your own custom development.

That omission has now been rectified. The MaskedTextbox control resembles the old MaskedEdit control in functionality. It allows a mask for input and a variety of useful properties to control user interaction with the control. More information on this control is available in the section "Advanced Capabilities for Data Entry."

TableLayoutPanel and FlowLayoutPanel Controls

Browser-based user interfaces are good at dynamically arranging controls at runtime, because browser windows can be different sizes for different users. Forms-based interfaces have traditionally lacked such capabilities. Dynamic positioning can be done in forms, but it requires writing a lot of sizing and positioning logic.

Two new controls in Windows Forms 2.0 mimic layout capabilities in a browser, offering better options for dynamic positioning of controls: FlowLayoutPanel and TableLayoutPanel. Both are containers than can automatically reposition controls that are placed in them, based on the current space available in the container.

An example illustrating usage of both controls is included in the section "Dynamic Sizing and Positioning of Controls."

Replacements for Older Windows Forms Controls

The Toolbar, MainMenu, ContextMenu, and StatusBar controls in Windows Forms 1.0 and 1.1 offered basic functionality. These controls are still available in Windows Forms 2.0, but in most cases you won't want to use these controls because new replacements offer significantly enhanced capabilities. Because the old versions are still available, the new versions have different names. The table that follows summarizes these replacements:

Old Control

New Control

Most Important New Capabilities

Toolbar

ToolStrip

Enables many new types of controls on the toolbar. Supports rafting, which enables the toolbar to be detached by the user and float over the application. It also enables users to add or remove buttons or other toolbar elements, and includes new cosmetics, enabling toolbars to look like those in Office 2003.

MainMenu

MenuStrip

Both new menu controls inherit from ToolStrip, which enables new cosmetics and more flexible placement.

ContextMenu

ContextMenuStrip

 

StatusBar

StatusStrip

Inherits from ToolStrip, which allows new cosmetics and makes it easier to embed other controls in a status bar

Splitter

SplitContainer

Less difficult to set up

The old versions no longer show up by default in the Toolbox. If you want to use them in new projects, then you must add them to the Toolbox by right-clicking on the Windows Forms Toolbox tab, selecting Choose Items, and placing a check mark on the older control that you want added to the Toolbox. However, you'll probably only want to use the older controls for compatibility with older projects, using the improved versions for new development.

These controls are covered in more detail, including examples, in the sections "Toolbars and the New ToolStrip Control," "Menus," and "Dynamic Sizing and Positioning of Controls."

Default Instances of Forms

In VB6 and earlier, a form named Form1 could be shown by merely including the following line:

Form1.Show

This capability was not available in Visual Basic 2002 and 2003. Instead, a form was treated like any other class, and had to be instantiated before use. Typical code to show a form in Windows Forms 1.0 and 1.1 looked like this:

Dim f As New Form1()
f.Show

This technique is still recommended because it fits object-oriented conventions, but the first form returned to Visual Basic starting with the 2005 version, with the minor change of parentheses at the end of the call:

Form1.Show()

Showing a form without instancing it, as in the first form shown, is referred to as using the default instance of the form. That default instance is available from anywhere in a project containing a form. There is only one default instance, and any reference to it will bring up the same underlying instance of the form.

Another way to get to the default instance of a form is through the new My namespace. The following line has exactly the same effect, showing the default instance of a form:

My.Forms.Form1.Show()

The System.Windows.Forms Namespace

You've already seen how namespaces are used to organize related classes in the .NET Framework. The main namespace used for Windows Forms classes is System.Windows.Forms. The classes in this namespace are contained in the System.Windows.Forms.dll assembly.

If you choose a Windows application project or Windows Control Library project in VS.NET, a reference to System.Windows.Forms.dll is added by default. In some other cases, such as creating a library that will work with controls, you need to add that reference manually. (You can learn more about creating controls in Windows Forms in Chapter 16.)

Using Forms

A form is just a special kind of class in Windows Forms. A class becomes a form based on inheritance. A form must have the System.Windows.Forms class in its inheritance tree, which causes the form to have the behavior and object interface a form requires.

The previous section on changes to Windows Forms 2.0 mentioned that forms can be used by referring to a default instance. However, the preferred technique is to treat a form like any other class, which means creating an instance of the form and using the instance. Typical code would look like this:

Dim f As New Form1
f.Show()

There is one circumstance in which loading a form the same way as a class instance yields undesirable results. Let's cover that next.

Showing Forms via Sub Main

When a form is instanced via the technique just described, it is referenced by an object variable, which establishes an object reference to the instance. References are covered in detail in Chapter 2.

References can disappear as object variables go out of scope or are set to other references or to Nothing. When all object references to a form are gone, the form is disposed of and therefore vanishes. This is particularly apparent if you want to start your application with a Sub Main, and then show your first form inside Sub Main. You might think the following would work:

' This code will not work in any .NET version of VB!!
Sub Main()

    ' Do start up work here
    ' After start up work finished, show the main form...
    Dim f As New Form1
    f.Show()

End Sub

If you try this, however, Form1 briefly appears and then immediately vanishes, and the application quits. That's because the object variable $f$ went out of scope, and it was the only reference to the form that was shown. Therefore, the form was destroyed because it had no references pointing to it.

To get around this behavior, you could use the default instance as the startup form, but there's a better way that stays within good object-oriented conventions. Replace the line that shows the form, as shown here:

' This code will work fine in VB 2008
Sub Main()

    ' Do start up work here
    Dim f As New Form1
    Application.Run(f)

End Sub

Now Sub Main will transfer control to the form, and the form won't vanish when Sub Main ends.

Setting the Startup Form

Instead of using Sub Main as your application entry point, you can also define a startup form, which is the form that is loaded first when your application begins. To define the startup form, open the Properties dialog box for the project and set the Startup form setting. Do this using the Project

Setting the Startup Form
Figure 15-1

Figure 15.1. Figure 15-1

If the Properties menu item doesn't appear under your Project menu, open the Solution Explorer (Ctrl+Alt+L), highlight the project name (it will be in bold font), and then try again.

Startup Location

Often, you'll want a form to be centered on the screen when it first appears. VB.NET does this automatically for you when you set the StartPosition property. The following table shows the settings and their meanings:

StartPosition Value

Effect

Manual

Shows the form positioned at the values defined by the form's Location property

CenterScreen

Shows the form centered on the screen

WindowsDefaultLocation

Shows the form at the window's default location

WindowsDefaultBounds

Shows the form at the window's default location, with the window's default bounding size

CenterParent

Shows the form centered in its owner

Form Borders

Forms have a number of border options in Windows Forms. The FormBorderStyle property is used to set the border option, and the options can affect the way a form can be manipulated by the user. The options available for FormBorderStyle include the following:

  • None — No border, and the user cannot resize the form

  • FixedSingle — Single 3-D border, and the user cannot resize the form

  • Fixed3D — 3-D border, and the user cannot resize the form

  • FixedDialog — Dialog-box-style border, and the user cannot resize the form

  • Sizeable — Same as FixedSingle, except that the user can resize the form

  • FixedToolWindow — Single border, and the user cannot resize the form

  • SizeableToolWindow — Single border, and the user can resize the form

Each of these has a different effect on the buttons that appear in the title bar of the form. For details, check the help topic for the FormBorderStyle property.

Always on Top — The TopMost Property

Some forms need to remain visible at all times, even when they don't have the focus, e.g., floating toolbars and tutorial windows. In Windows Forms, forms have a property called TopMost. Set it to True to have a form overlay other forms even when it does not have the focus.

Note that a form with TopMost set to True is on top of all applications, not just the hosting application. If you need a form to only be on top of other forms in the application, then this capability is provided by an owned form.

Owned Forms

As with the TopMost property, an owned form floats above the application but it does not interfere with using the application. An example is a search-and-replace box. However, an owned form is not on top of all forms, just the form that is its owner.

When a form is owned by another form, it is minimized and closed with the owner form. Owned forms are never displayed behind their owner form, but they do not prevent their owner form from gaining the focus and being used. However, if you want to click on the area covered by an owned form, the owned form has to be moved out of the way first.

A form can only have one "owner" at a time. If a form that is already owned by Form1 is added to the owned forms collection for Form2, then the form is no longer owned by Form1.

There are two ways to make a form owned by another form. It can be done in the owner form or in the owned form.

AddOwnedForm Method

In the owner form, another form can be made owned with the AddOwnedForm method. The following code makes an instance of Form2 become owned by Form1. This code would reside somewhere in Form1 and would typically be placed just before the line that shows the instance of Form2 to the screen:

Dim frm As New Form2
Me.AddOwnedForm(frm)

Owner Property

The relationship can also be set up in the owned form. This is done with the Owner property of the form. Here is a method that would work inside Form2 to make it owned by a form that is passed in as an argument to the function:

Public Sub MakeMeOwned(frmOwner As Form)
    Me.Owner = frmOwner
End Sub

Because this technique requires a reference to the owner inside the owned form, it is not used as often as using the AddOwnedForm method in the Owner form.

OwnedForms Collection

The owner form can access its collection of owned forms with the OwnedForms property. Here is code to loop through the forms owned by a form:

Dim frmOwnedForm As Form
For Each frmOwnedForm In Me.OwnedForms
  Console.WriteLine(frmOwnedForm.Text)
Next

The owner form can remove an owned form with the RemoveOwnedForm property. This could be done in a loop like the previous example, with code like the following:

Dim frmOwnedForm As Form
For Each frmOwnedForm In Me.OwnedForms
  Console.WriteLine(frmOwnedForm.Text)
  Me.RemoveOwnedForm(frmOwnedForm)
Next

This loop would cause an owner form to stop owning all of its slaved forms. Note that those "deslaved" forms would not be unloaded, they would simply no longer be owned.

Making Forms Transparent and Translucent

Windows Forms offers advanced capabilities to make forms translucent, or parts of a form transparent. You can even change the entire shape of a form.

The Opacity Property

The Opacity property measures how opaque or transparent a form is. A value of 0 percent makes the form fully transparent. A value of 100 percent makes the form fully visible. Any value greater than 0 and less than 100 makes the form partially visible, as if it were a ghost. Note that an opacity value of 0 percent disables the capability to click the form.

Very low levels of opacity, in the range of 1 or 2 percent, make the form effectively invisible, but still allow the form to be clickable. This means that the Opacity property has the potential to create mischievous applications that sit in front of other applications and "steal" their mouse clicks and other events.

Percentage values are used to set opacity in the Properties window, but if you want to set the Opacity property in code, you must use values between 0 and 1 instead, with 0 equivalent to 0 percent and 1 equivalent to 100 percent.

Tool and dialog windows that should not completely obscure their background are one example of a usage for Opacity. Setting expiration for a "free trial" by gradually fading out the application's user interface is another.

The following block of code shows how to fade a form out and back in when the user clicks a button named Button1. You may have to adjust the Step value of the array, depending on your computer's performance:

Private Sub Button1_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
                          Handles Button1.Click
   Dim i As Double
   For i = −1 To 1 Step 0.005
     ' Note - opacity is a value from 0.0 to 1.0 in code
     ' Absolute value is used to keep us in that range
     Me.Opacity = System.Math.Abs(i)
     Me.Refresh
   Next i
End Sub

The TransparencyKey Property

Instead of making an entire form translucent or transparent, the TransparencyKey property enables you to specify a color that will become transparent on the form. This enables you to make some sections of a form transparent, while other sections are unchanged.

For example, if TransparencyKey is set to a red color and some areas of the form are that exact shade of red, then they will be transparent. Whatever is behind the form shows through in those areas; and if you click in one of those areas, you are actually clicking the object behind the form.

TransparencyKey can be used to create irregularly shaped "skin" forms. A form can have its BackgroundImage property set with an image, and by just painting a part of the image with the TransparencyKey color, you can make parts of the form disappear.

The Region Property

Another way to gain the capability of "skins" is by using the Region property of a form. The Region property allows a shape for a form to be encoded as a "graphics path," thereby changing the shape from the default rectangle to another shape. A path can contain line segments between points, curves, and arcs, and outlines of letters, in any combination.

The following example changes the shape of a form to an arrow. Create a new Windows application. Set the FormBorderStyle property of Form1 to None. Then place the following code in the Load event for Form1:

Dim PointArray(6) As Point
PointArray(0) = New Point(0, 40)
PointArray(1) = New Point(200, 40)
PointArray(2) = New Point(200, 0)
PointArray(3) = New Point(250, 100)
PointArray(4) = New Point(200, 200)
PointArray(5) = New Point(200, 160)
PointArray(6) = New Point(0, 160)
Dim myGraphicsPath As _
System.Drawing.Drawing2D.GraphicsPath = _
        New System.Drawing.Drawing2D.GraphicsPath

myGraphicsPath.AddPolygon(PointArray)
Me.Region = New Region(myGraphicsPath)

When the program is run, Form1 will appear in the shape of a right-pointing arrow. If you lay out the points in the array, you will see that they have become the vertices of the arrow.

Visual Inheritance

By inheriting from System.Windows.Forms.Form, any class automatically gets all the properties, methods, and events that a form based on Windows Forms is supposed to have. However, a class does not have to inherit directly from the System.Windows.Forms.Form class to become a Windows form. It can become a form by inheriting from another form, which itself inherits from System.Windows.Forms.Form. In this way, controls originally placed on one form can be directly inherited by a second form. Not only is the design of the original form inherited, but also any code associated with these controls (the processing logic behind an Add New button, for example). This means you can create a base form with processing logic required in a number of forms, and then create other forms that inherit the base controls and functionality.

VB 2008 provides an Inheritance Picker tool to aid in this process. Note, however, that a form must be compiled into either an .exe or .dll file before it can be used by the Inheritance Picker. Once that is done, adding a form that inherits from another form in the project can be achieved by selecting Project

Visual Inheritance

Scrollable Forms

Some applications need fields that will fit on a single screen. While you could split the data entry into multiple screens, an alternative is a scrollable form.

You can set your forms to automatically have scrollbars when they are sized smaller than the child controls they contain. To do so, set the AutoScroll property of your form to True. When you run your program, resize the form to make it smaller than the controls require and presto — instant scrolling.

Note

You cannot have both Autoscroll and IsMdiContainer set to True at the same time. MDI containers have their own scrolling functionality. If you set Autoscroll to True for an MDI container, then the IsMdiContainer property will be set to False, and the form will cease to be an MDI container.

MDI Forms

MDI (Multiple Document Interface) forms are forms that are created to hold other forms. The MDI form is often referred to as the parent, and the forms displayed within the MDI parent are often called children. Figure 15-2 shows a typical MDI parent with several children displayed within it.

Figure 15-2

Figure 15.2. Figure 15-2

Creating an MDI Parent Form

In Windows Forms, a regular form is converted to an MDI parent form by setting the IsMDIContainer property of the form to True. This is normally done in the Properties window at design time.

A form can also be made into an MDI parent at runtime by setting the IsMDIContainer property to True in code, but the design of an MDI form is usually different from that of a normal form, so this approach is not often needed.

Differences in MDI Parent Forms between VB6 and VB 2008

In VB6, an MDI parent form can only contain controls that have a property called Align, which is similar to the Dock property in Windows Forms. These controls, such as a PictureBox, can then contain other controls.

In Windows Forms, an MDI parent can contain any control that a regular form can contain. Buttons, labels, and the like can be placed directly on the MDI surface. Such controls appear in front of any MDI child forms that are displayed in the MDI client area.

It is still possible to use controls such as PictureBoxes to hold other controls on a Windows Forms MDI parent. These controls can be docked to the side of the MDI form with the Dock property, which is discussed in the section "Dynamic Sizing and Positioning of Controls."

MDI Child Forms

In Windows Forms, a form becomes an MDI child at runtime by setting the form's MDIParent property to point to an MDI parent form. This makes it possible to use a form as either a standalone form or an MDI child in different circumstances. In fact, the MDIParent property cannot be set at design time — it must be set at runtime to make a form an MDI child. (Note that this is completely different from VB6, where it was necessary to make a form an MDI child at design time.)

Any number of MDI child forms can be displayed in the MDI parent-client area. The currently active child form can be determined with the ActiveForm property of the MDI parent form.

An MDI Example in VB 2008

To see these changes to MDI forms in action, try the following exercise. It shows the basics of creating an MDI parent and making it display an MDI child form:

  1. Create a new Windows application. It will have an empty form named Form1. Change both the name of the form and the form's Text property to MDIParentForm.

  2. In the Properties window, set the IsMDIContainer property for MDIParentForm to True. This designates the form as an MDI container for child windows. (Setting this property also causes the form to have a different default background color.)

  3. From the Toolbox, drag a MenuStrip control to the form. Create a top-level menu item called File with submenu items called New MDI Child and Quit. Also create a top-level menu item called Window. The File

    An MDI Example in VB 2008
  4. In the Component Tray at the bottom of the form, click the MenuStrip item and select Properties. In the Properties window, set the MDIWindowListItem property to WindowToolStripMenuItem. This enables the Window menu to maintain a list of open MDI child windows, with a check mark next to the active child window.

  5. Create an MDI child form to use as a template for multiple instances. Select Project

    An MDI Example in VB 2008
  6. Return to MDIParentForm. In the menu editing bar, double-click the New MDI Child option under File. The Code Editor will appear, with the cursor in the event routine for that menu option. Place the following code in the event:

    Protected Sub NewMdiChildToolStripMenuItem _Click(ByVal sender As Object,
                                   ByVal e As System.EventArgs)
    
        ' This line may change if you are using a form with a different name.
        Dim NewMDIChild As New Form2()
        'Set the Parent Form of the Child window.
        NewMDIChild.MDIParent = Me
        'Display the new form.
        NewMDIChild.Show()
    
    End Sub
  7. In the menu editing bar for MDIParentForm, double-click the Quit option under File. The Code Editor will appear, with the cursor in the event routine for that menu option. Place the following code in the event:

    Protected Sub QuitToolStripMenuItem_Click(ByVal sender As Object, _
                                  ByVal e As System.EventArgs)
        End
    End Sub
  8. Run and test the program. Use the File

    An MDI Example in VB 2008

Arranging Child Windows

MDI parent forms have a method called LayoutMDI that automatically arranges child forms in the familiar cascade or tile layout. For the preceding example, add a menu item to your Windows menu called Tile Vertical and insert the following code into the menu item's Click event to handle it:

Me.LayoutMdi(MDILayout.TileVertical)

To see an example of the rearrangement, suppose that the MDI form in Figure 15-2 is rearranged with the MDILayout.TileVertical option. It would then look similar to the image in Figure 15-3.

Dialog Forms

In VB6 and earlier, forms were shown with the Show method, and this technique is still used in Windows Forms. In both VB6 and VB 2008, the Show method by default displays modeless forms, which are forms that enable the user to click off them onto another form in the application.

Applications also sometimes need forms that retain control until their operation is finished. That is, you can't click off such a form onto another form. Such a form is called a modal form.

Figure 15-3

Figure 15.3. Figure 15-3

In VB6, showing a modal form required using a special parameter on the Show method. Showing a form modally is done differently in Windows Forms, with the ShowDialog method. The following code shows a modal dialog in Windows Forms, assuming the project contains a form with a type of DialogForm:

Dim frmDialogForm As New DialogForm
frmDialogForm.ShowDialog()

DialogResult

When showing a dialog form, you'll often need to get information about what action the user selected. This was often done with a custom property in VB6, but Windows Forms has a built-in property for that purpose. When a form is shown with the ShowDialog method, the form has a property called DialogResult to indicate its state.

The DialogResult property can take the following enumerated results:

  • DialogResult.Abort

  • DialogResult.Cancel

  • DialogResult.Ignore

  • DialogResult.No

  • DialogResult.None

  • DialogResult.OK

  • DialogResult.Retry

  • DialogResult.Yes

When the DialogResult property is set, the dialog is hidden as a byproduct. That is, setting the DialogResult property causes an implicit call to the Hide method of the dialog form, so that control is released back to the form that called the dialog.

The DialogResult property of a dialog box can be set in two ways. The most common way is to associate a DialogResult value with a button. Then, when the button is pressed, the associated value is automatically placed in the DialogResult property of the form.

To set the DialogResult value associated with a button, the DialogResult property of the button is used. If this property is set for the button, then it is unnecessary to set the DialogResult in code when the button is pressed.

Here is an example that uses this technique. In Visual Studio 2008, start a new VB Windows application. On the automatic blank form that comes up (named Form1), place a single button and set its Text property to Dialog.

Now add a new Windows form by selecting Project

DialogResult

Property

Value for First Button

Value for Second Button

Name

OKButton

CancelButton

Text

OK

Cancel

DialogResult

OK

Cancel

Do not put any code in DialogForm at all. The form should look like the one shown in Figure 15-4.

Figure 15-4

Figure 15.4. Figure 15-4

On the first form, Form1, place the following code in the Click event for Button1:

Private Sub Button1_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles Button1.Click
   Dim frmDialogForm As New DialogForm()
   frmDialogForm.ShowDialog()

   ' You're back from the dialog - check user action.
   Select Case frmDialogForm.DialogResult
     Case DialogResult.OK
       MsgBox("The user pressed OK")
     Case DialogResult.Cancel
       MsgBox("The user pressed cancel")
   End Select
   frmDialogForm = Nothing
End Sub

Run and test the code. When a button is pressed on the dialog form, a message box should be displayed (by the calling form) indicating the button that was pressed.

The second way to set the DialogResult property of the form is in code. In a Button_Click event, or anywhere else in the dialog form, a line like the following can be used to set the DialogResult property for the form and simultaneously hide the dialog form, returning control to the calling form:

Me.DialogResult = DialogResult.Ignore

This particular line sets the dialog result to DialogResult.Ignore, but setting the dialog result to any of the permitted values also hides the dialog form.

Forms at Runtime

The life cycle of a form is like that of all objects. It is created and later destroyed. Forms have a visual component, so they use system resources, such as handles. These are created and destroyed at interim stages within the lifetime of the form. Forms can be created and will hold state as a class, but will not appear until they are activated. Likewise, closing a form doesn't destroy its state.

The following table summarizes the states of a form's existence, how you get the form to that state, the events that occur when the form enters a state, and a brief description of each:

Code

Events Fired

Notes

MyForm = New Form1

Load

The form's New method will be called (as will InitializeComponent).

MyForm.Show or

HandleCreated

Use Show for modeless display.

MyForm.ShowDialog

Load

Use ShowDialog for modal display.

 

VisibleChanged

The HandleCreated event only fires the first time the form is shown or after it has previously been closed.

 

Activated

 

MyForm.Activate

Activated

A form can be activated when it is visible but does not have the focus.

MyForm.Hide

Deactivate

Hides the form (sets the Visible property to False).

 

VisibleChanged

 

MyForm.Close

Deactivate

Closes the form and calls Dispose to release the window's resources

 

Closing

During the Closing event, you can set the CancelEventArgs.Cancel property to True to abort the close.

 

Closed

 
 

VisibleChanged

 
 

HandleDestroyed

Also called when the user closes the form using the control box or X button

 

Disposed

The Deactivate event will only fire if the form is currently active.

  

Note: There is no longer an Unload event. Use the Closing or Closed event instead.

MyForm.Dispose

None

Use the Close method to finish using your form.

MyForm = Nothing

None

Releasing the reference to the form flags it for garbage collection. The garbage collector calls the form's Finalize method.

Controls

The controls included in Windows Forms provide basic functionality for a wide range of applications. This section covers the features that all controls use (such as docking) and summarizes the standard controls available to you. Important changes from pre-.NET versions of Visual Basic (VB6 and earlier) are briefly mentioned.

Control Tab Order

The VS 2008 design environment enables you to set the tab order of the controls on a form simply by clicking them in sequence. To activate the feature, open a form in the designer and select View

Control Tab Order

To set the values, simply click on each control in the sequence you want the tab flow to operate. The screen shot in Figure 15-5 shows a simple form with the tab order feature enabled.

In Windows Forms 2.0, it is possible to have two or more controls with the same tab index value. At runtime, Visual Basic will break the tie by using the z-order of the controls. The control that is highest in the z-order receives the focus first. The z-order is a ranking number that determines which controls are in front of or behind other controls. (The term comes from the z-axis, which is an axis perpendicular to the traditional x-axis and y-axis.) The z-order can be changed by right-clicking the control and selecting Bring to Front.

Figure 15-5

Figure 15.5. Figure 15-5

Dynamic Sizing and Positioning of Controls

Windows Forms 2.0 includes a variety of ways to enable dynamic user interfaces. Not only can controls be set to automatically stretch and reposition themselves as a form is resized, they can also be dynamically arranged inside some special container controls intended for that purpose. This section covers all these ways of enabling dynamic sizing and positioning of controls.

Docking

Docking refers to gluing a control to the edge of a parent control. Good examples of docked controls are menu bars and status bars, which are typically docked to the top and bottom of a form, respectively. All visual controls have a Dock property.

To work through an example, create a new Windows application and place a Textbox on a form. Set the Text property of the TextBox to I'm Getting Docked. The result when you show the form should look something like Figure 15-6.

Figure 15-6

Figure 15.6. Figure 15-6

Suppose that you need to glue this TextBox to the top of the form. To do this, view the Dock property of the label. If you pull it down, you'll see a small graphic like the one shown in Figure 15-7.

Figure 15-7

Figure 15.7. Figure 15-7

Simply click the top section of the graphic to stick the label at the top of the form. The other sections give you other effects. (A status bar would use the bottom section, for example. Clicking the box in the middle causes the control to fill the form.) The TextBox control will immediately "stick" to the top of your form. When you run your program and stretch the window sideways, you'll see the effect shown in Figure 15-8.

Note

If you try to dock multiple controls to the same edge, Windows Forms must decide how to break the tie. Precedence is given to controls in reverse z-order. That is, the control that is furthest back in the z-order will be the first control next to the edge. If you dock two controls to the same edge and want to switch them, then right-click the control you want docked first and select Send to Back.

Figure 15-8

Figure 15.8. Figure 15-8

If you want a gap between the edge of your form and the docked controls, set the DockPadding property of the parent control. You can set a different value for each of the four directions (Left, Right, Top, Bottom). You can also set all four properties to the same value using the All setting.

Anchoring

Anchoring is similar to docking except that you can specifically define the distance that each edge of your control will maintain from the edges of a parent. To see it in action, add a button to the form in the docking example. The result should look like what is shown in Figure 15-9.

Figure 15-9

Figure 15.9. Figure 15-9

Dropping down the Anchor property of the button gives you the graphic shown in Figure 15-10.

Figure 15-10

Figure 15.10. Figure 15-10

The four rectangles surrounding the center box enable you to toggle the anchor settings of the control. Figure 15-10 shows the default anchor setting of Top, Left for all controls.

When the setting is on (dark gray), the edge of the control maintains its original distance from the edge of the parent as the parent is resized. If you set the anchor to two opposing edges (such as the left and right edges), the control stretches to accommodate this, as shown in Figure 15-11.

Figure 15-11

Figure 15.11. Figure 15-11

One of the most common uses of anchoring is to set the Anchor property for buttons in the lower-right portion of a form. Setting the Anchor property of a button to Bottom, Right causes the button to maintain a constant distance from the bottom-right corner of the form.

You can also set the Anchor property in code. The most common scenario for this would be for a control created on-the-fly. To set the Anchor property in code, you must add the anchor styles for all the sides to which you need to anchor. For example, setting the Anchor property to Bottom, Left would require a line of code like this:

MyControl.Anchor = Ctype(AnchorStyles.Bottom + AnchorStyles.Right, AnchorStyles)

Sizable Containers

Early versions of Windows Forms used the Splitter control to allow resizing of containers. This control is still available in Windows Forms 2.0, but it doesn't appear by default in the Toolbox. In its place is a replacement control, SplitContainer, that provides the same functionality with less work on your part.

A single SplitContainer acts much like two panels with an appropriately inserted Splitter. You can think of it as a panel with two sections separated by a movable divider so that the relative sizes of the sections can be changed by a user.

To use a SplitContainer, simply drop it on a form, resize it, and position the draggable divider to the appropriate point. If you want the divider to be horizontal instead of vertical, you change the Orientation property. Then you can place controls in each subpanel in any way you like. It is common to insert a control such as a TreeView or ListBox, and then dock it to its respective subpanel. This enables users to resize such contained controls. A typical example of a SplitContainer in action is shown in Figure 15-12.

Figure 15-12

Figure 15.12. Figure 15-12

The cursor in Figure 15-12 shows that the mouse is hovering over the divider, allowing repositioning of the divider by dragging the mouse. A SplitContainer may be nested inside another SplitContainer. This enables you to build forms in which several parts are resizable relative to each other.

FlowLayoutPanel Control

The FlowLayoutPanel enables the dynamic layout of controls contained within it, based on the size of the FlowLayoutPanel. This is quite a departure from traditional Windows Forms layout, in which controls in a container are positioned solely according to their Top and Left properties.

FlowLayoutPanel works conceptually much like a simple HTML page shown in a browser. The controls placed in the FlowLayoutPanel are positioned in sequence horizontally until there's not enough space for the next control, which then wraps further down for another row of controls. The following walk-through demonstrates this capability.

Start a new Windows application project. On the blank Form1 included in the new project, place a FlowLayoutPanel control toward the top of the form, making it a bit less wide than the width of the form. Set the Anchor property for the FlowLayoutPanel to Top, Left, and Right. Set the BorderStyle property for the FlowLayoutPanel to FixedSingle so it's easy to see.

Place three Button controls in the FlowLayoutPanel, keeping their default sizes. The form you create should look like the one shown in Figure 15-13.

Figure 15-13

Figure 15.13. Figure 15-13

Run the application. The initial layout will be similar to the design-time layout. However, if you resize the form to about two thirds of its original width, the layout of the buttons changes. Because there is no longer enough room for them to be arranged side by side, the arrangement automatically switches. Figure 15-14 shows the form in three configurations: first with its original width, then narrower so that only two buttons fit in the FlowLayoutPanel, and finally so narrow that the buttons are all stacked in the FlowLayoutPanel.

Figure 15-14

Figure 15.14. Figure 15-14

Note that no logic of any kind was added to the form — the FlowLayoutPanel handles the repositioning of the buttons automatically. In fact, any position information you set for the button controls is ignored if they are placed in a FlowLayoutPanel.

Padding and Margin Properties

To assist in positioning controls in the FlowLayoutPanel, all controls have a new property called Margin. There are settings for Margin.Left, Margin.Right, Margin.Top, and Margin.Bottom. These settings determine how much space is reserved around a control when calculating its automatic position in a FlowLayoutPanel.

You can see the Margin property in action by changing the Margin property for one or more of the buttons in the previous example. If you change all the Margin settings for the first Button to 10 pixels, for example, and run the application, the form will look like the one shown in Figure 15-15.

Figure 15-15

Figure 15.15. Figure 15-15

The first button now has a 10-pixel separation from all the other controls in the FlowLayoutPanel, as well as a 10-pixel separation from the edges of the FlowLayoutPanel itself.

The Padding property is for the FlowLayoutPanel or other container control. When a control is embedded into a FlowLayoutPanel, the Padding.Left, Padding.Right, Padding.Top, and Padding.Bottom properties of the FlowLayoutPanel determine how far the control should be positioned from the inside edge of the container.

You can see the Padding property in action by changing the Padding property for the FlowLayoutPanel in the previous example. If you set all Padding settings to 20 pixels, and reset the Margin property for the first Button back to the default, then the form will look like what is shown in Figure 15-16 in the visual designer.

Figure 15-16

Figure 15.16. Figure 15-16

Notice that all the controls in the FlowLayoutPanel are now at least 20 pixels from the edges.

The Padding property is also applicable to other container controls, if the contained controls have their Dock property set. If the settings for Padding are not zero, then a docked control will be offset from the edge of the container by the amount specified by the Padding property.

TableLayoutPanel Control

Another control that uses dynamic layout of child controls is the TableLayoutPanel. This control consists of a table of rows and columns, resulting in a rectangular array of cells. You can place one control in each cell. However, that control can itself be a container, such as a Panel or FlowLayoutPanel.

The dimensions of the columns and rows can be controlled by setting some key properties. For columns, set the number of columns with the ColumnCount property, and then control each individual column with the ColumnStyles collection. When you click the button for the ColumnStyles collection, you get a designer window that enables you to set two key properties for each column: SizeType and Width.

SizeType can be set to one of the following enumerations:

  • Absolute — Sets the column width to a fixed size in pixels

  • AutoSize — Indicates that the size of the column should be managed by the TableLayoutPanel, which allocates width to the column depending on the widest control contained in the column

  • Percent — Sets what percentage of the TableLayoutPanel to use for the width of the column

The Width property is only applicable if you do not choose a SizeType of AutoSize. It sets either the number of pixels for the width of the column (if the SizeType is Absolute) or the percentage width for the column (if the SizeType is Percent).

Similarly, for rows, there is a RowCount property to set the number of rows, and a RowStyles collection to manage the size of the rows. Each row in RowStyles has a SizeType, which works the same way as SizeType does for Columns except that it manages the height of the row instead of the width of a column. The Height property is used for rows instead of a Width property, but it works in a corresponding way. Height is either the number of pixels (if SizeType is Absolute) or a percentage of the height of the TableLayoutPanel (if SizeType is Percent). If SizeType is AutoSize, then a row is sized to the height of the tallest control in the row.

An advanced UI layout technique is to first create a TableLayoutPanel, and then embed a FlowLayoutPanel in some of the cells of the TableLayoutPanel. This allows several controls to be contained in a cell and repositioned as the size of the cell changes.

A step-by-step example of using a TableLayoutPanel, with an embedded FlowLayoutPanel, is included in the next chapter in the section "Creating a Composite UserControl."

Panel and GroupBox Container Controls

Of course, not all applications need the dynamic layout of the containers just discussed. Windows Forms includes two controls that are static containers, in which the positions and layout of the contained controls are not adjusted at all.

In VB6, the Frame control was used for this purpose. However, Windows Forms has two such containers, with minor differences between them: the GroupBox control and the Panel control.

These two are similar in the following ways:

  • They can serve as a container for other controls.

  • If they are hidden or moved, then the action affects all the controls in the container.

The GroupBox control is the one that most closely resembles an old VB6 Frame control visually. It acts much like a VB6 Frame control too, with one significant exception: There is no way to remove its border. It always has a border, and it can have a title, if needed. The border is always set the same way. Figure 15-17 shows a form with a GroupBox control containing three RadioButtons.

Figure 15-17

Figure 15.17. Figure 15-17

The Panel control has three major differences from GroupBox:

  • It has options for displaying its border in the BorderStyle property, with a default of no border.

  • It has the capability to scroll if its AutoScroll property is set to True.

  • It cannot set a title or caption.

Figure 15-18 shows a form containing a Panel control with its BorderStyle property set to FixedSingle, with scrolling turned on by setting AutoScroll to True, and with a CheckedListBox that is too big to display all at once (which forces the Panel to show a scrollbar).

Figure 15-18

Figure 15.18. Figure 15-18

Extender Providers

Windows Forms has a family of components that can only be used in association with visual controls. These components are known as extender providers. They work with the Visual Studio IDE to cause new properties to appear in the Properties window for controls on the form.

Extender providers have no visible manifestation except in conjunction with other controls, so they appear in the component tray. The three extender providers available with Windows Forms 2.0 are the HelpProvider, the ToolTip, and the ErrorProvider. All three work in basically the same way. Each extender provider implements the properties that are "attached" to other controls. The best way to see how this works is to go through an example, so let's do that with a ToolTip component.

ToolTip

The ToolTip is the simplest of the built-in extender providers. It adds just one property to each control: ToolTip on ToolTip1 (assuming the ToolTip control has the default name of ToolTip1). This property works in very much the same way the ToolTipText property works in VB6, and in fact replaces it.

To see this in action, create a Windows Forms application. On the blank Form1 that is created for the project, place a couple of buttons. Take a look at the Properties window for Button1. Notice that it does not have a ToolTip property of any kind.

Drag over the ToolTip control, which will be placed in the component tray. Go back to the Properties window for Button1. A property named ToolTip on ToolTip1 is now present. Set any string value you like for this property.

Run the project and hover the mouse pointer over Button1. You will see a tooltip containing the string value you entered for the ToolTip on ToolTip1 property.

Other properties of the ToolTip component enable you to control other characteristics of the tooltip, such as the initial delay before the tooltip appears.

New in Windows Forms 2.0 is the capability to change the shape of tooltips to a "balloon." This is done by setting the IsBalloon property of the Tooltip component to True. Instead of a hovering rectangular tooltip, the tooltip has a rounded rectangular outline with a pointer to the control it is associated with, not unlike the dialog balloons in a comic strip. Figure 15-19 shows an example.

Figure 15-19

Figure 15.19. Figure 15-19

HelpProvider

The HelpProvider enables controls to have associated context-sensitive help available by pressing F1. When a HelpProvider is added to a form, all controls on the form get the following new properties, which show up in the controls' Properties window:

Property

Usage

HelpString on HelpProvider1

Provides a pop-up tooltip for the control when F1 is pressed while the control has the focus. If the HelpKeyword and HelpNavigator properties (described later) are set to provide a valid reference to a help file, then the HelpString value is ignored in favor of the help file information.

HelpKeyword onHelpProvider1

Provides a keyword or other index to use in a help file for context-sensitive help for this control. The HelpProvider1 control has a property that indicates which help file to use. This replaces the HelpContextID property in VB6.

HelpNavigator onHelpProvider1

Contains an enumerated value that determines how the value in HelpKeyword is used to refer to the help file. There are several possible values for displaying such elements as a topic, an index, or a table of contents in the help file.

ShowHelp onHelpProvider1

Determines whether the HelpProvider control is active for this control

Filling in the HelpString property immediately causes the control to provide tooltip help when F1 is pressed while the control has the focus. The HelpProvider control has a property to point to a help file (either an HTML help file or a Win32 help file), and the help topic in the HelpTopic property points to a topic in this file.

ErrorProvider

The ErrorProvider component presents a simple, visual way to indicate to a user that a control on a form has an error associated with it. The added property for controls on the form when an ErrorProvider is used is called Error on ErrorProvider1 (assuming the ErrorProvider has the default name of ErrorProvider1). Setting this property to a string value causes the error icon to appear next to a control. In addition, the text appears in a tooltip if the mouse hovers over the error icon.

Figure 15-20 shows a screen with several text boxes, and an error icon next to one (with a tooltip). The error icon and tooltip are displayed and managed by an ErrorProvider.

Figure 15-20

Figure 15.20. Figure 15-20

The ErrorProvider component's default icon is a red circle with an exclamation point. When the Error property for the text box is set, the icon blinks for a few moments, and hovering over the icon causes the tooltip to appear. Writing your own code to set the Error property is explained in the section "Working with Extender Providers in Code."

Properties of Extender Providers

In addition to providing other controls with properties, extender providers also have properties of their own. For example, the ErrorProvider has a property named BlinkStyle. When it is set to NeverBlink, the blinking of the icon is stopped for all controls affected by the ErrorProvider.

Other properties of the ErrorProvider enable you to change things such as the icon used and where the icon appears in relation to the field containing the error. For instance, you might want the icon to appear on the left side of a field instead of the default right side. You can also have multiple error providers on your form. For example, you might wish to give users a warning, rather than an error. A second error provider with a yellow icon could be used for this feature.

Working with Extender Providers in Code

Setting the Error property in the previous example can be done with the Properties window, but this is not very useful for on-the-fly error management. However, setting the Error property in code is not done with typical property syntax. By convention, extender providers have a method for each extended property they need to set, and the arguments for the method include the associated control and the property setting. To set the Error property in the previous example, the following code was used:

ErrorProvider1.SetError(txtName, "You must provide a location!")

The name of the method to set a property is the word Set prefixed to the name of the property. The preceding line of code shows that the Error property is set with the SetError method of the ErrorProvider.

There is a corresponding method to get the value of the property, and it is named with Get prefixed to the name of the property. To determine the current Error property setting for txtName, you would use the following line:

sError = ErrorProvider1.GetError(txtName)

Similar syntax is used to manipulate any of the properties managed by an extender provider. The discussion of the tooltip provider earlier mentioned setting the tooltip property in the Properties window. To set that same property in code, the syntax would be as follows:

ToolTip1.SetToolTip(Button1, "New tooltip for Button1")

Advanced Capabilities for Data Entry

Windows Forms 2.0 includes some advanced capabilities for data entry that were not available in earlier versions. Textbox and Combobox controls in 2.0 have autocompletion capabilities, and a MaskedTextbox control allows entry of formatted input such as phone numbers.

Autocompletion

Responsive user interfaces help users accomplish their purposes, thereby making them more productive. One classic way to do this is with autocompletion.

An example of autocompletion is IntelliSense in Visual Studio. Using IntelliSense, the user only has to type in a few letters, and Visual Studio presents a list of probable entries matching those letters. If the desired entry is found, the user only needs to select it, rather than type the entire entry.

Autocompletion is available in Windows Forms 2.0 with text boxes and combo boxes. Both use a set of properties to control how autocompletion works and from where the list of entries available to the user comes.

To see autocompletion in action, create a Windows application project. Drag a Textbox from the toolbox onto the blank Form1 created for the project. Set the AutoCompleteMode for the text box to Suggest in the Properties window. Then set the AutoCompleteSource to CustomSource. Finally, click the button in the setting window for AutoCompleteCustomSource. You'll see a window for adding entries that is very similar to the window for entering items for a list box or combo box.

Enter the following items into the dialog:

Holder
Holland
Hollis
Holloway
Holly
Holstein
Holt

Start the project and type Hol into the text box. As soon as you start typing, a drop-down will appear that contains entries matching what you've typed, including all seven elements in the list. If you then type another 1, the list will decrease to four elements that begin with Holl. If you then type an o, the list will contain only the entry Holloway.

The AutoCompleteMode has two other modes. The Append mode does not automatically present a drop-down, but instead appends the rest of the closest matching entry to the text in the Textbox or ComboBox, and highlights the untyped characters. This allows the closest matching entry to be placed in the text area without the user explicitly selecting an entry.

The SuggestAppend mode combines Suggest and Append. The current best match is displayed in the text area, and the drop-down with other possibilities is automatically displayed. This mode is the one most like IntelliSense.

You can also set the list of items to be included in the autocompletion list at runtime, which is the most common usage scenario. A list of items from a database table would typically be loaded for autocompletion. Here is typical code to create a list of items and attach the list to a combo box:

Dim autoCompleteStringCollection1 As New AutoCompleteStringCollection
Dim nReturn As Integer
nReturn = autoCompleteStringCollection1.Add("Holder")
nReturn = autoCompleteStringCollection1.Add("Holland")
nReturn = autoCompleteStringCollection1.Add("Hollis")
nReturn = autoCompleteStringCollection1.Add("Holloway")
ComboBox1.AutoCompleteCustomSource = autoCompleteStringCollection1

For this sample to work properly, the Combobox control's AutoCompleteSource property must be set to CustomSource.

Several built-in lists are available for use with autocompletion. Instead of setting AutoCompleteSource to CustomSource, you can set it to sources such as files in the file system, or URLs recently used in Internet Explorer. See the documentation for AutoCompleteSource for additional options; or, if you are using AutoCompleteSource in code, IntelliSense will show the options available.

MaskedTextbox Control

The MaskedTextbox control fulfills the same function as the old VB6 MaskedEdit control. If you have used MaskedEdit in VB6, the MaskedTextbox will feel quite familiar.

After dragging a MaskedTextbox control to a form, you typically want to first set the mask associated with the control. You can do this in the Properties window by selecting the Mask property, but you can also click the smart tag (right-pointing arrow) on the right side of the MaskedTextbox. In either case, you can either construct a mask manually or select one of the commonly used masks from a list.

If you need to create your own mask, you need to design it based on the following set of formatting characters:

Mask Character

Description

#

Digit placeholder

.

Decimal placeholder. The actual character used is the one specified as the decimal placeholder in your international settings. This character is treated as a literal for masking purposes.

,

Thousands separator. The actual character used is the one specified as the thousands separator in your international settings. This character is treated as a literal for masking purposes.

:

Time separator. The actual character used is the one specified as the time separator in your international settings. This character is treated as a literal for masking purposes.

/

Date separator. The actual character used is the one specified as the date separator in your international settings. This character is treated as a literal for masking purposes.

Treat the next character in the mask string as a literal. This enables you to include the #, &, A, and ? characters in the mask. This character is treated as a literal for masking purposes.

&

Character placeholder. Valid values for this placeholder are ANSI characters in the following ranges: 32–126 and 128–255.

>

Converts all the characters that follow to uppercase

<

Converts all the characters that follow to lowercase

A

Alphanumeric character placeholder (entry required), e.g., a–z, A–Z, or 0–9

a

Alphanumeric character placeholder (entry optional)

9

Digit placeholder (entry optional), e.g., 0–9

C

Character or space placeholder (entry optional). This operates exactly like the & placeholder and ensures compatibility with Microsoft Access.

?

Letter placeholder, e.g., a–z or A–Z

Literal

All other symbols are displayed as literals — that is, as themselves.

Literal characters are simply inserted automatically by the MaskedTextbox control. If you have literal characters for the parentheses in a phone number, for example, the user need not type these in order for them to show up in the text area of the control.

As an example of a mask, suppose that you have an account number that must consist of exactly two uppercase letters and five digits. You could construct a mask of >??00000. The first character forces all letters to uppercase. The two question marks specify two required alphabetic characters, and the five zeros specify five required digits.

Once you have set the Mask for the MaskedTextbox, all entries in the control will be coerced to the Mask pattern. Keystrokes that don't conform will be thrown away.

Validating Data Entry

Most controls that you place on a form require that its content be validated in some way. A text box might require a numeric value only or simply require that the user provide any value and not leave it blank.

The ErrorProvider component discussed earlier makes this task significantly easier than it was in previous versions. To illustrate the use of an ErrorProvider in data validation, create a new Windows application project and change the Text property for the blank Form1 to Data Validation Demo. Then place two text boxes on the form that will hold a user ID and password, as shown in Figure 15-21.

Figure 15-21

Figure 15.21. Figure 15-21

Name the first text box UserNameTextBox and name the second text box PasswordTextBox. Drag an ErrorProvider onto the form, which will cause it to appear in the component tray. In the next section, you'll add the code that simply verifies that the user has filled in both text boxes and gives a visual indication, via the ErrorProvider, if either of the fields has been left blank.

The Validating Event

The Validating event fires when your control begins its validation. It is here that you need to place the code that validates your control, and set a visual indication for an error. Insert the following code to see this in action:

Private Sub UserNameTextBox_Validating(ByVal sender As Object, _
                            ByVal e As System.ComponentModel.CancelEventArgs) _
                            Handles UserNameTextBox.Validating
    If userNameTextbox.Text = "" Then
        ErrorProvider1.SetError(UserNameTextBox, "User Name cannot be blank")
    Else
        ErrorProvider1.SetError(UserNameTextBox, "")
    End If
End Sub
Private Sub PasswordTextBox_Validating(ByVal sender As Object, _
                           ByVal e As System.ComponentModel.CancelEventArgs) _
                           Handles PasswordTextBox.Validating
    If passwordTextbox.Text = "" Then
        ErrorProvider1.SetError(PasswordTextBox, "Password cannot be blank")
    Else
        ErrorProvider1.SetError(PasswordTextBox, "")
    End If
End Sub

Run the program and then tab between the controls without entering any text to get the error message. You'll see an icon blink next to each of the text box controls; and if you hover over an error icon, you'll see the appropriate error message.

There is also a Validated event that fires after a control's Validating event. It can be used, for example, to do a final check after other events have manipulated the contents of the control.

The CausesValidation Property

The CausesValidation property determines whether the control will participate in the validation events on the form. A control with a CausesValidation setting of True (it is True by default) has two effects:

  • The control's Validating/Validated events fire when appropriate.

  • The control triggers the Validating/Validated events for other controls.

It is important to understand that the validation events fire for a control not when the focus is lost but when the focus shifts to a control that has a CausesValidation value of True.

To see this effect, set the CausesValidation property of the password text box in your application to False (be sure to leave it True for the user ID and OK button). When you run the program, tab off the user ID text box and again to the OK button. Notice that it isn't until the focus reaches the OK button that the validating event of the user ID text box fires. Also notice that the validating event of the Password field never fires.

Ultimately, if you determine that the control is not valid, you need to specify what happens. That may include setting the focus to the control that needs attention (as well as indicating the error with an ErrorProvider).

Toolbars and the New ToolStrip Control

As mentioned in the summary of new features in Windows Forms 2.0, the ToolStrip control replaces the Toolbar control from Windows Forms 1.0 and 1.1. ToolStrip has many improvements. It supports movement to sides of a form other than the place where it was laid out, and you have much more flexibility in placing items on the toolbar. It also integrates better with the IDE to assist in creating toolbars and manipulating the many settings available.

The ToolStrip does not sit alone on a form. When a ToolStrip is dragged onto a form, the container that actually sits on the form is called a RaftingContainer. This container handles the positioning so that the toolbar created by a ToolStrip can be dragged to other parts of the form.

The ToolStrip sits inside the RaftingContainer and is the container for toolbar elements. It handles the sizing of the toolbar, movement of toolbar elements, and other general toolbar functions.

The items on the toolbar must be from a set of controls specially designed to serve as toolbar items. All of these items inherit from the ToolStripItem base class. The controls available for toolbar items are as follows:

Control

Description

ToolStripButton

Replicates the functionality of a regular Button for a toolbar

ToolStripLabel

Replicates the functionality of a regular Label for a toolbar

ToolStripSeparator

A visual toolbar element that displays a vertical bar to separate other groups of elements (no user interaction)

ToolStripComboBox

Replicates the functionality of a regular ComboBox for a toolbar. This item must be contained within a ToolStripControlHost (see below).

ToolStripTextBox

Replicates the functionality of a regular TextBox for a toolbar. This item must be contained within a ToolStripControlHost (see below).

ToolStripControlHost

A hosting container for other controls that reside on a ToolStrip. It can host any of the following controls: ToolStripComboBox, ToolStripTextBox, other Windows Forms controls, or user controls.

ToolStripDropDownItem

A hosting container for toolbar elements that feature drop-down functionality. It can host a ToolStripMenuItem, a ToolStripSplitButton, or a ToolStripDropDownButton.

ToolStripDropDownButton

A button that supports drop-down functionality. Clicking the button shows a list of options from which the user must select the one desired. This item is used when the user needs to select from a group of options, none of which is used a large majority of the time.

ToolStripSplitButton

A combination of a regular button and a drop-down button. This item is often used when there is a frequently used option to click, but you also need to offer users other options that are less frequently used.

ToolStripMenuItem

A selectable option displayed on a menu or context menu. This item is typically used with the menu controls that inherit from the ToolStrip, discussed later in this chapter in the section "Menus."

Note that almost any control can be hosted on a toolbar using the ToolStripControlHost. However, for buttons, text boxes, labels, and combo boxes, it is much easier to use the ToolStrip version instead of the standard version.

Creating a ToolStrip and Adding Toolbar Elements

Try an example to see how to build a toolbar using the ToolStrip control. Create a new Windows application. Add a ToolStrip control to the blank Form1 that is included with the new project. Make the form about twice its default width so that you have plenty of room to see the ToolStrip as you work on it.

The ToolStrip is positioned at the top of the form by default. It does not contain any elements, although if you highlight the ToolStrip control in the component tray, a "menu designer" will appear in the ToolStrip.

The easiest way to add multiple elements to the ToolStrip is to use the designer dialog for the ToolStrip. Highlight the ToolStrip in the component tray and click the button in the Properties window for the Items property. You'll see a designer dialog like the one shown in Figure 15-22.

Figure 15-22

Figure 15.22. Figure 15-22

The drop-down in the upper-left corner contains the different types of items that can be placed on the toolbar. The names in the drop-down are the same as the names in the table of controls except that the "Toolstrip" prefix is not present. Add one each of the following types, with the setting specified:

  • Button — Set the Text property to Go. Set the DisplayStyle property to Text.

  • ComboBox — Leave the Text property blank. Set DropDownStyle to DropDownList. Open the Items dialog and add the names of some colors.

  • SplitButton — Set the Text property to Options. Set the Display property to Text.

  • TextBox — Leave the Text property blank.

Click OK. The ToolStrip will look like the one shown in Figure 15-23.

You can now handle events on any of these toolbar elements the same way you would any other controls. You can double-click to get a Click event routine or access the event routines through the drop-downs in the Code Editor.

To make the Toolstrip more dynamic, it must be embedded in a ToolStripContainer. You can do that manually by dragging one over and putting the Toolstrip in it, but the easy way to do it is to click the smart tag on the Toolstrip and then select Embed in ToolStripContainer. This causes a ToolStripContainer to appear on your form. Set the Dock property for the ToolStripContainer to Fill and it will provide a surface for the Toolstrip that includes all four edges of the form.

Run your program. Using the mouse, grab the dotted handle on the far left edge of the toolbar. If you drag this to the right, then the toolbar will be repositioned. If you drag it to other positions on the form, then the entire toolbar will dock to different edges of the form.

Figure 15-23

Figure 15.23. Figure 15-23

Allowing the User to Move Toolbar Elements

By default, the AllowItemReorder property of the ToolStrip is set to False. If you change that to True, then the elements on the toolbar can be moved around in relation to one another (reordered) at runtime.

Change the AllowItemReorder property to True for the ToolStrip and run your program again. Hold down the Alt key and drag elements on the toolbar around. They will assume new positions on the toolbar when you drop them.

Creating a Standard Set of Toolbar Elements

If you need a toolbar that has the typical visual elements for cut, copy, paste, and so on, it is not necessary to create the elements. The designer will do it for you.

Create a new form in your project and drag a ToolStrip onto it. As before, it will be positioned at the top and will not contain any elements. With the ToolStrip highlighted in the component tray, click the Item property. Below the properties in the Properties window, a link named Insert Standard Items will appear. Click that link; elements will be inserted into the ToolStrip, making it look like the one shown in Figure 15-24.

Figure 15-24

Figure 15.24. Figure 15-24

Menus

Menus are added to a form in Windows Forms 2.0 by dragging controls called MenuStrip or ContextMenuStrip onto your form. MenuStrip implements a standard Windows-style menu at the top of the form. ContextMenuStrip allows a pop-up menu with a right mouse button click.

These controls are actually subclasses of the ToolStrip, so much of the information you learned earlier in this chapter about working with the ToolStrip also applies to the MenuStrip and ContextMenuStrip. When dragged onto the form, these controls appear in the component tray just as the ToolStrip does, and you access the designer for these controls the same way you do for the ToolStrip. However, because these are menus, the most common way to add items is to type them directly into the menu designer that appears when the control is highlighted.

The menu designer is extremely intuitive — the menu appears on your form just as it would at runtime, and you simply fill in the menu items you need. Each item can be renamed, and each can have a Click event associated with it.

Adding Standard Items to a Menu

If your form's menu needs to have the standard top-level options (File, Edit, and so on) and the typical options under these items, then you can have all these usual options inserted for you automatically.

To see this capability in action, drag a MenuStrip to a form and then click the smart tag (the right arrow at the right edge) for the MenuStrip to bring up the designer dialog. Click the Insert Standard Items link at the bottom of the dialog.

Icons and Checkmarks for Menu Items

Each menu item has an Image property. Setting this property to an image causes the image to appear on the left side of the text for the menu option. You can see this property in use by looking at the standard items inserted in the preceding example. The File

Icons and Checkmarks for Menu Items

Items can also have check marks beside them. This is done by changing the Checked property of the item to True. You can do this at design time or runtime, enabling you to manipulate the check marks on menus as necessary.

Context Menus

To implement a context menu for a form or any control on a form, drag a ContextMenuStrip to the form and add the menu items. Items are added and changed the same way as they are with the MenuStrip.

To hook a context menu to a control, set the control's ContextMenuStrip property to the ContextMenuStrip menu control you want to use. Then, when your program runs and you right-click in the control, the context menu will pop up.

Dynamically Manipulating Menus at Runtime

Menus can be adjusted at runtime using code. Context menus, for instance, may need to vary depending on the state of your form. The following walk-through shows how to add a new menu item to a context menu and how to clear the menu items.

Create a new Windows application. On the blank Form1 for the project, drag over a MenuStrip control. Using the menu designer, type in a top-level menu option of File. Under that option, type in options for Open and Save.

Now place a button on the form. Double-click the button to get its Click event, and place the following code into the event:

Dim NewItem As New ToolStripMenuItem
NewItem.Text = "Save As"
' Set any other properties of the menu item you like.
FileToolStripMenuItem.DropDownItems.Add(NewItem)
AddHandler NewItem.Click, _
     AddressOf Me.NewMenuItem_Click

Add the event handler referenced in this code at the bottom of the form's code:

Private Sub NewMenuItem_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs)
   MessageBox.Show("New menu item clicked!")
End Sub

If you now run the program and look at the menu, it will only have File and Save options. Clicking the button will cause a new Save As item to be added to the menu, and it will be hooked to the event routine called NewMenuItem_Click.

Common Dialogs

Windows Forms provides you with seven common dialog controls. Each control opens a predefined form that is identical to the one used by the operating system.

These dialogs cannot be shown modeless. They have a ShowDialog method to show them modally. That method returns one of the standard DialogResult values, as discussed earlier in this chapter.

OpenFileDialog and SaveFileDialog

These two controls open the standard dialog control that enables users to select files on the system. They are quite similar except for the buttons and labels that appear on the actual dialog box when it is shown to the user. Each prompts the user for a file on the system by allowing the user to browse the files and folders available.

Use the following properties to set up the dialog boxes:

Property

Comments

InitialDirectory

Defines the initial location that is displayed when the dialog opens, e.g., OpenFileDialog1.InitialDirectory = "C:Program Files"

Filter

String that defines the Files of Type list. Separate items using the pipe character. Items are entered in pairs; the first of each pair is the description of the file type, and the second half is the file wildcard, e.g., OpenFileDialog1.Filter = "All Files$|*.* |Text Files|*.txt|Rich Text Files|*.rtf"

FilterIndex

Integer that specifies the default filter item to use when the dialog box opens. For example, with the preceding filter used, defaults to text files as follows: OpenFileDialog1.FilterIndex = 2

RestoreDirectory

Boolean value that, when True, forces the system's default directory to be restored to the location it was in when the dialog box was first opened. This is False by default.

Filename

Holds the full name of the file that the user selected, including the path

ShowDialog

Displays the dialog

The following code opens the standard dialog box, asking the user to select a file that currently exists on the system, and simply displays the choice in a message box upon return:

OpenFileDialog1.InitialDirectory = "C:"
OpenFileDialog1.Filter = "Text files|*.txt|All files|*.*"
OpenFileDialog1.FilterIndex = 1
OpenFileDialog1.RestoreDirectory = True
If OpenFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
    MessageBox.Show("You selected """ & OpenFileDialog1.FileName & """")
End If

ColorDialog Control

As the name implies, this control gives the user a dialog box from which to select a color. Use the following properties to set up the dialog boxes as follows:

ColorDialog1.Color = TextBox1.BackColor
ColorDialog1.AllowFullOpen = True
If ColorDialog1.ShowDialog()= Windows.Forms.DialogResult.OK Then
    TextBox1.BackColor = ColorDialog1.Color
End If

Property

Comments

Color

The System.Drawing.Color that the user selected. You can also use this to set the initial color selected when the user opens the dialog.

AllowFullOpen

Boolean value that, when True, allows the user to select any color. If False, then the user is restricted to the set of default colors.

ShowDialog

Displays the dialog

FontDialog Control

This control displays the standard dialog box, allowing a user to select a font. Use the following properties to set up the dialog boxes:

Property

Comments

Font

The System.Drawing.Font that the user selected. Also used to set the initial font.

ShowEffects

Boolean value that, when True, makes the dialog box display the text effects options of underline and strikeout.

ShowColor

Boolean value that, when True, makes the dialog box display the combo box of the font colors. The ShowEffects property must be True for this to have an effect.

FixedPitchOnly

Boolean value that, when True, limits the list of font options to only those that have a fixed pitch (such as Courier or Lucida console).

ShowDialog

Displays the dialog

Using these properties looks like this:

FontDialog1.Font = TextBox1.Font
FontDialog1.ShowColor = True
FontDialog1.ShowEffects = True
FontDialog1.FixedPitchOnly = False
If FontDialog1.ShowDialog()= Windows.Forms.DialogResult.OK Then
    TextBox1.Font = FontDialog1.Font
End If

Printer Dialog Controls

There are three more common dialog controls: PrintDialog, PrintPreviewDialog, and PageSetup-Dialog. They can all be used to control the output of a file to the printer, and you can use these in conjunction with the PrintDocument component to run and control print jobs.

Drag and Drop

Implementing a drag-and-drop operation in the .NET Framework is accomplished by a short sequence of events. Typically, it begins in a MouseDown event of one control, and always ends with the DragDrop event of another.

To demonstrate the process, begin with a new Windows application. Add two list boxes to your form, and add three items to the first using the Items Property Designer. This application enables you to drag the items from one list box into the other.

The first step in making drag and drop work is specifying whether or not a control will accept a drop. By default, all controls reject such an act and do not respond to any attempt by the user to drop something onto them. In this case, set the AllowDrop property of the second list box (the one without the items added) to True.

The next item of business is to invoke the drag-and-drop operation. This is typically done in the MouseDown event of the control containing the data you want to drag (although you're not restricted to it). The DoDragDrop method is used to start the operation. This method defines the data that will be dragged and the type of dragging that is allowed. Here, you'll drag the text of the selected list box item, and permit both a move and a copy of the data to occur.

Switch over to the code window of your form and add the following code to the MouseDown event of ListBox1:

Private Sub ListBox1_MouseDown(ByVal sender As Object, _
                              ByVal e As System.Windows.Forms.MouseEventArgs) _
                               Handles ListBox1.MouseDown
   Dim DragDropResult As DragDropEffects
   If e.Button = MouseButtons.Left Then
      DragDropResult = ListBox1.DoDragDrop( _
                    ListBox1.Items(ListBox1.SelectedIndex), _
                    DragDropEffects.Move Or DragDropEffects.Copy)
      ' Leave some room here to check the result of the operation
      ' (You'll fill it in next).
   End If
End Sub

Notice the comment about leaving room to check the result of the operation. You'll fill that in shortly. For now, calling the DoDragDrop method has gotten you started.

The next step involves the recipient of the data — in this case, ListBox2. Two events here are important to monitor: DragEnter and DragDrop.

As you can guess by the name, the DragEnter event occurs when the user first moves over the recipient control. The DragEnter event has a parameter of type DragEventArgs that contains an Effect property and a KeyState property.

The Effect property enables you to set the display of the drop icon for the user to indicate whether a move or a copy occurs when the mouse button is released. The KeyState property enables you to determine the state of the Ctrl, Alt, and Shift keys. It is a Windows standard that when both a move or a copy can occur, a user is to indicate the copy action by holding down the Ctrl key. Therefore, in this event, you check the KeyState property and use it to determine how to set the Effect property.

Add the following code to the DragEnter event of ListBox2:

Private Sub ListBox2_DragEnter(ByVal sender As Object, _
                               ByVal e As DragEventArgs) _
                               Handles ListBox2.DragOver
   If e.KeyState = 9 Then ' Control key
      e.Effect = DragDropEffects.Copy
   Else
      e.Effect = DragDropEffects.Move
   End If
End Sub

Note that you can also use the DragOver event if you want, but it will fire continuously as the mouse moves over the target control. In this situation, you only need to trap the initial entry of the mouse into the control.

The final step in the operation occurs when the user lets go of the mouse button to drop the data at its destination. This is captured by the DragDrop event. The parameter contains a property holding the data that is being dragged. It's now a simple process of placing it into the recipient control as follows:

Private Sub ListBox2_DragDrop(ByVal sender As Object, _
                              ByVal e As System.Windows.Forms.DragEventArgs) _
                              Handles ListBox2.DragDrop
   ListBox2.Items.Add(e.Data.GetData(DataFormats.Text))
End Sub

One last step: You can't forget to manipulate ListBox1 if the drag and drop was a move. Here's where you'll fill in the hole you left in the MouseDown event of ListBox1. Once the DragDrop has occurred, the initial call that invoked the procedure returns a result indicating what ultimately happened. Go back to the ListBox1_MouseDown event and enhance it to remove the item from Listbox1 if it was moved (and not simply copied):

Private Sub ListBox1_MouseDown(ByVal sender As Object, _
              ByVal e As System.Windows.Forms.MouseEventArgs) _
              Handles ListBox1.MouseDown
   Dim DragDropResult As DragDropEffects

   If e.Button = MouseButtons.Left Then
      DragDropResult = ListBox1.DoDragDrop( _
                    ListBox1.Items(ListBox1.SelectedIndex), _
                    DragDropEffects.Move Or DragDropEffects.Copy)
       ' If operation is a move (and not a copy), then remove then
       ' remove the item from the first list box.
       If DragDropResult = DragDropEffects.Move Then
         ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
       End If
    End If
End Sub

When you're done, run your application and drag the items from Listbox1 into Listbox2. Try a copy by holding down the Ctrl key when you do it. The screen shot in Figure 15-25 shows the result after Item1 has been moved and Item3 has been copied a few times.

Figure 15-25

Figure 15.25. Figure 15-25

Summary of Standard Windows.Forms Controls

Windows Forms, of course, contains most of the controls that you are accustomed to using in pre-.NET versions of Visual Basic. This section lists the basic controls that are generally quite intuitive and don't warrant a full example to explain. Where appropriate, the important differences from pre-.NET versions of Visual Basic are noted.

  • Button

    • Known as CommandButton in VB6 and earlier

    • Now uses the Text property instead of Caption

    • Can now display both an icon and text simultaneously. The image is set using the Image property (instead of Picture). The image position can be set using the ImageAlign property (left, right, center, and so on).

    • Text on the button can be aligned using the TextAlign property.

    • Can now have different appearances using the FlatStyle property

    • No longer has the Default and Cancel properties. These are now managed by the form itself using the AcceptButton and CancelButton properties.

  • CheckBox

    • Now uses the Text property instead of Caption

    • Can now appear as a toggle button using the Appearance property

    • Check box and text can now be positioned within the defined area using the CheckAlign and TextAlign properties

    • Uses the CheckState property instead of Value

    • Has a FlatStyle property controlling the appearance of the check box

  • CheckedListBox

    • A list box that has check boxes beside each item (see Listbox)

  • ComboBox

    • Like the new ListBox control, it can now hold a collection of objects instead of an array of strings (see ListBox).

    • Now has a MaxDropDownItems property that specifies how many items to display when the list opens

  • DateTimePicker

    • Formerly known as a DTPicker in VB6 and earlier

  • DomainUpDown

    • A simple one-line version of a list box

    • Can hold a collection of objects and will display the ToString result of an item in the collection

    • Can wrap around the list to give a continuous scrolling effect using the Wrap property

  • HScrollBar

    • Unchanged

  • ImageList

    • Same as previous versions, but with an improved window for managing the images within the list. The MaskColor property is now TransparentColor.

  • Label

    • Essentially the same as previous versions

    • Caption is now Text

    • Can now display an image and text

    • Has automatic sizing capability. Set the AutoSize property to True for automatic horizontal sizing (this is the default value of the property).

    • The TextAlign property is especially useful. The text of a label beside a text box in VB6 would always be a few pixels higher than the text in the text box. Now, by setting the label's TextAlign property so that the vertical alignment is Middle, this problem is solved.

    • Can now specify whether a mnemonic should be interpreted (if UseMnemonic is True, then the first ampersand (&) in the Text property specifies underlining the following character and having it react to the Alt key shortcut, placing the focus on the next control in the tab order that can hold focus, such as a text box).

  • LinkLabel

    • Identical to a label, but behaves like a hyperlink with extra properties, such as LinkBehavior (for example, HoverUnderline), LinkColor, and ActiveLinkColor

  • ListBox

    • A list box can now hold a collection of objects, instead of an array of strings. Use the DisplayMember property to specify what property of the objects to display in the list, and the ValueMember property to specify what property of the objects to use as the values of the list items. (This is similar to the ItemData array from previous versions.) For example, a combo box could store a collection of employee objects, and display to the user the Name property of each, as well as retrieve the EmployeeId as the value of the item currently selected.

    • Can no longer be set to display check boxes using a Style property. Use the CheckedListBox control instead.

  • ListView

    • Same functionality as the VB6 version but with an improved Property Editor that enables you to define the list view item collection and its sub-items at design time

    • Sub-items can have their own font display properties.

    • New HeaderStyle property instead of HideColumnHeaders

  • MonthCalendar

    • Formerly known as MonthView

  • NotifyIcon

    • Gives a form an icon in the system tray

    • Tooltip of the icon is set by the Text property of the control

    • Pop-up menus are set using a ContextMenu control (see the "Menus" section earlier in chapter).

  • NumericUpDown

    • A single-line text box that displays a number and up/down buttons that increment/decrement the number when clicked

  • PictureBox

    • Image property defines the graphic to display instead of Picture

    • Use the SizeMode property to autostretch or center the picture.

  • ProgressBar

    • Now has a Step method that automatically increments the value of the progress bar by the amount defined in the Step property

  • RadioButton

    • Formerly known as OptionButton

    • Use the Checked property to specify value (formerly Value).

    • Use CheckAlign and TextAlign to specify where the radio button and text appear in relation to the area of the control.

  • RichTextBox

    • Has properties not available in VB6, such as ZoomFactor, WordWrap, DetectURLs, and AutoWordSelection

    • Use the Lines array to get or set specific individual lines of the control's text.

  • TabControl

    • Formerly known as the TabStrip control

    • Now has a TabPages collection of TabPage objects. A TabPage object is a subclass of the Panel control specialized for use in the TabControl.

    • Uses the Appearance property to display the tabs as buttons, if desired (formerly the Style property of the TabStrip control)

  • TextBox

    • Now has a CharacterCasing property that can automatically adjust the text entered into uppercase or lowercase

    • ReadOnly property now used to prevent the text from being edited. This used to be the Locked property. (The Locked property now determines whether the control can be moved or resized.)

    • Now has Cut, Copy, Paste, Undo, and ClearUndo methods

  • Timer

    • This is essentially unchanged from previous versions.

    • The timer is now disabled by default.

    • You cannot set the interval to zero to disable it.

  • TrackBar

    • Formerly known as the Slider control in VB6, it is essentially unchanged.

  • TreeView

    • Same functionality as in VB6 but with a new Node Tree Editor that enables you to visually design the tree

  • VScrollBar

    • Unchanged

Retired Controls

The following list outlines the controls from VB6 that you won't find in Windows Forms and how to reproduce their functionality:

  • Spinner

    • Use the DomainUpDown or NumericUpDown control.

  • Line and Shape

    • Windows Forms has no Line or Shape control, nor any immediate equivalent. A "cheap" way of reproducing a horizontal or vertical line is to use a label control. Set its background color to that of the line you want, and then set either the Size.Height or Size.Width value to 1.

    • Diagonal lines and shapes must be drawn using GDI+ graphics methods.

  • DirListBox, FileListBox, DriveListBox

    • You would typically use these controls to create a file system browser similar to Windows Explorer. Windows Forms has no equivalent controls. You can use the OpenFileDialog and SaveFileDialog (see the previous section) to meet your needs in most circumstances.

  • Image

    • Use the PictureBox control.

Handling Groups of Related Controls

Occasionally it is necessary for a set of controls to be treated as a group. For example, a set of RadioButton controls might be related, and you might want to channel the Click event for all the controls in the group to the same event handler.

In VB6 and earlier, that functionality required a control array, but Windows Forms 2.0 does not support control arrays. However, .NET offers greatly enhanced control over event handling, and it's easy to route events from multiple controls to a single handler.

To have a single method handle multiple events from controls, you must attach those controls' events to the handler. You can do that with multiple controls specified in a Handles clause or by using AddHandler for each control. Unless controls are being added to your form on-the-fly, using additional controls in the Handles clause is usually preferable. Here is an example of a declaration for a Click event that handles three RadioButton controls:

Private Sub RadioButton3_Click(ByVal sender As Object, _
          ByVal e As EventArgs) _
          Handles RadioButton1.Click, _
          RadioButton2.Click, RadioButton3.Click

There is no Index property as in old-style control arrays in VB6. Instead, simply use the Sender parameter of the event handler to determine which control originated the event.

A simple example is helpful to see how to set this up. Create a new Windows application and set the Text property of the blank Form1 to Add Dynamic Control Demo. Then add two buttons to the form, as shown in Figure 15-26.

Figure 15-26

Figure 15.26. Figure 15-26

Double-click Button1 to switch over to the code that handles the Button1.Click event. To make this method respond to the Button2.Click event as well, simply add the Button2.Click event handler to the end of the Handles list, and then add some simple code to display a message box indicating what button triggered the event:

' Note the change in the method name from Button1_Click. Since
' two objects are hooked up, it's a good idea to avoid having the
' method specifically named to a single object.
Private Sub Button_Click(ByVal sender As System.Object, _
                 ByVal e As System.EventArgs) _
         Handles Button1.Click, Button2.Click
   Dim buttonClicked As Button
   buttonClicked = CType(sender, Button)
   ' Tell the world what button was clicked
   MessageBox.Show("You clicked " & buttonClicked.Text)
End Sub

Run the program and click the two buttons. Each one will trigger the event and display a message box with the appropriate text from the button that was clicked.

Adding Controls at Runtime

Another capability that control arrays provided in VB6 and earlier was making it easy to add controls to a form at runtime. That's also easy to do in Windows Forms, even without control arrays. Here is an example that enhances the preceding program to add a third button dynamically at runtime. Add another button to your form that will trigger the addition of Button3, as shown in Figure 15-27.

Figure 15-27

Figure 15.27. Figure 15-27

Name the new button AddNewButton and add the following code to handle its Click event:

Private Sub AddNewButton_Click(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) _
                    Handles addNewButton.Click

    Dim newButton As Button

    ' Create the new control
    newButton = New Button()

    ' Set it up on the form
    newButton.Location = New System.Drawing.Point(184, 12)
    newButton.Size = New System.Drawing.Size(75, 23)
    newButton.Text = "Button3"

    ' Add it to the form's controls collection
    Me.Controls.Add(newButton)

    ' Hook up the event handler.
    AddHandler newButton.Click, AddressOf Me.Button_Click
End  Sub

When the AddNewButton button is clicked, the code creates a new button, sets its size and position, and then does two essential things. First, it adds the button to the form's controls collection; second, it connects the Click event of the button to the method that handles it.

With this done, run the program and click the AddNewButton button. Button3 will appear. Then, simply click Button3 to prove that the Click event is being handled. You should get the result shown in Figure 15-28.

Figure 15-28

Figure 15.28. Figure 15-28

Other Handy Programming Tips

Here are some other handy programming tips for using Windows Forms:

  • Switch the focus to a control — Use the .Focus method. To set the focus to TextBox1, for example, use the following code:

    TextBox1.Focus()
  • Quickly determine the container control or parent form — With the use of group boxes and panels, controls are often contained many times. You can now use the FindForm method to immediately get a reference to the form. Use the GetContainerControl method to access the immediate parent of a control.

  • Traverse the tab order — Use the GetNextControl method of any control to get a reference to the next control on the form in the tab order.

  • Convert client coordinates to screen coordinates (and back) — Want to know where a control is in screen coordinates? Use the PointToScreen method. Convert back using the PointToClient method.

  • Change the z-order of controls at runtime — Controls now have both BringToFront and SendToBack methods.

  • Locate the mouse pointer — The control class now exposes a MousePosition property that returns the location of the mouse in screen coordinates.

  • Manage child controls — Container controls, such as a group box or panel, can use the HasChildren property and Controls collection to determine the existence of, and direct references to, child controls, respectively.

  • Maximize, minimize, or restore a form — Use the form's WindowState property.

Summary

Windows Forms is still an excellent technology for the development of rich client and smart client interfaces. While Windows Presentation Foundation will experience more innovation in coming generations of the .NET platform, at present it's significantly easier to develop on Windows Forms. The maturity of the designer and control set in Windows Forms makes it a good choice for many client-based applications, and Windows Forms will be supported indefinitely on the .NET platform.

Becoming a capable Windows Forms developer requires becoming familiar with the controls that are available, including their properties, events, and methods. This takes time. If you are inexperienced with form-based interfaces, you can expend a fair amount of time using the reference documentation to find the control capabilities you need. However, that investment is worthwhile, both because it allows you to be a proficient Windows Forms developer and also because many of the concepts will carry over into WPF.

Many professional Windows Forms developers need to go beyond just creating forms and laying out controls. Complex applications often also require creating new controls or enhancing built-in controls. Accordingly, the next chapter discusses how to create and modify Windows Forms controls, along with some additional advanced Windows Forms topics.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset