Visual basic

The Paint Event
This event takes place every time the form must be refreshed, and we use its handler to execute
code for any custom drawing on the form. When you switch to another form that partially or
totally overlaps the current one and then switch back to the ?rst form, the Paint event will be
?red to notify your application that it must redraw the form. The form will refresh its controls
automatically, but any custom drawing on the form won’t be refreshed automatically. We’ll dis-
cuss this event in more detail in Chapter 18, ‘‘Drawing and Painting with Visual Basic 2008,’’ in
the presentation of the Framework’s drawing methods.
Loading and Showing Forms
Most practical applications are made up of multiple forms and dialog boxes, and one of the oper-
ations you’ll have to perform with multiform applications is to load and manipulate forms from
within other forms’ code. For example, you might want to display a second form to prompt the
user for data speci?c to an application. You must explicitly load the second form and read the
information entered by the user when the auxiliary form is closed. Or you might want to maintain
two forms open at once and let the user switch between them. A text editor and its Find & Replace
dialog box is a typical example.
You can access a form from within another form by its name. Let’s say that your application
has two forms, named Form1 and Form2, and that Form1 is the project’s startup form. To show
Form2 when an action takes place on Form1,callthe Show method of the auxiliary form:
Form2.Show
This statement brings up Form2 and usually appears in a button’s or menu item’s Click event
handler. To exchange information between two forms, use the techniques described in the ‘‘Con-
trolling One Form from within Another,’’ section later in this chapter.
The Show method opens a form in a modeless manner: The two forms are equal in stature on the
desktop, and the user can switch between them. You can also display the second form in a modal
manner, which means that users can’t return to the form from which they invoked it without
closing the second form. While a modal form is open, it remains on top of the desktop, and you
can’t move the focus to any other form of the same application (but you can switch to another
application). To open a modal form, use the ShowDialog method:
Form2.ShowDialog
The modal form is, in effect, a dialog box like the Open File dialog box. You must ?rst select a
?le on this form and click the Open button, or click the Cancel button to close the dialog box andPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 237
LOADING AND SHOWING FORMS 237
return to the form from which the dialog box was invoked. This brings up the topic of forms and
dialog boxes.
A dialog box is simply a modal form. When we display forms as dialog boxes, we change the
border of the forms to the setting FixedDialog and invoke them with the ShowDialog method.
Modeless forms are more dif?cult to program because the user may switch among them at any
time. Moreover, the two forms that are open at once must interact with one another. When the
user acts on one of the forms, it might necessitate some changes in the other, and you’ll see shortly
how this is done. If the two active forms don’t need to interact, display one of themas a dialog box.
When you’re ?nished with the second form, you can either close it by calling its Close method
or hide it by calling its Hide method. The Close method closes the form, and its resources are
returned to the system. The Hide method sets the form’s Visible property to False; you can still
access a hidden form’s controls from within your code, but the user can’t interact with it. Forms
that are displayed often, such as the Find & Replace dialog box of a text-processing application,
should be hidden — not closed. To the user, it makes no difference whether you hide or close a
form. If you hide a form, however, the next time you bring it up with the Show method, its controls
are in the state they were the last time.
The Startup Form
A typical application has more than a single form. When an application starts, the main form is
loaded. You can control which form is initially loaded by setting the startup object in the project
Properties window, shown in Figure 7.11. To open this dialog box, right-click the project’s name in
the Solution Explorer and select Properties. In the project’s Properties pages, select the Application
tab and select the appropriate item in the Startup Form combo box.
Figure 7.11
In the Properties win-
dow, you can specify the
form that’s displayed
when the application
starts.
By default, the IDE suggests the name of the ?rst form it created, which is Form1.Ifyouchange
the name of the form, Visual Basic will continue using the same form as the startup form with its
new name.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 238
238 CHAPTER 7 WORKING WITH FORMS
You can also start an application by using a subroutine, without loading a form. This sub-
routine is the MyApplication Startup event handler, which is ?red automatically when the
application starts. To display the AuxiliaryForm object from within the Startup event handler,
use the following statement:
Private Sub MyApplication Startup(...) Handles Me.Startup
System.Windows.Forms.Application.Run(New AuxiliaryForm())
End Sub
To view the MyApplication Startup event handler, click the View Application Events button
at the bottom of the Application pane, as shown in Figure 7.11. This action will take you to the
MyApplication code window, where you can select the MyApplication Events item in the object
list and the Startup item in the events list.
Controlling One Form from within Another
Loading and displaying a formfromwithin another form’s code is fairly trivial. In some situations,
this is all the interaction you need between forms. Each form is designed to operate independently
of the others, but they can communicate via public variables (see the following section). In most
situations, however, you need to control one form from within another’s code. Controlling the
form means accessing its controls, and setting or reading values from within another form’s code.
In Chapter 6, you developed the TextPad application, which is a basic text editor and consists
of the main form and an auxiliary form for the Find & Replace operations. All other operations on
the text are performed with menu commands on the main form.When the user wants to search for
and/or replace a string, the program displays another form on which the user speci?es the text to
?nd, the type of search, and so on. When the user clicks one of the Find & Replace form’s buttons,
the corresponding code must access the text on the main form of the application and search for
a word or replace a string with another. The Find & Replace dialog box not only interacts with
the TextBox control on the main form, it also remains visible at all times while it’s open, even if it
doesn’t have the focus, because its TopMost property was set to True.
Sharing Variables between Forms
The preferred method for two forms to communicate with each other is via public variables.These
variables are declared in the form’s declarations section, outside any procedure, with the key-
word Public. If the following declarations appear in Form1,thevariable NumPoints and the array
DataValues can be accessed by any procedure in Form1,aswellasfromwithinthecodeofany
form belonging to the same project:
Public NumPoints As Integer
Public DataValues(100) As Double
To access a public variable declared in Form1 from within another form’s code, you must pre?x
the variable’s name by the name of the form, as in the following:
Form1.NumPoints = 99
Form1.DataValues(0) = 0.3395022Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 239
LOADING AND SHOWING FORMS 239
You can use the same notation to access the controls on another form. If Form1 contains the
TextBox1 control, you can use the following statement to read its text:
Form1.TextBox1.Text
If a button on Form1 opens the auxiliary form Form2, you can set selected controls to speci?c
values before showing the auxiliary form. The following statements should appear in a button’s
or menu item’s Click event handler:
Form2.TextBox1.Text = ”some text”
Form2.DateTimePicker1.Value = Today
Form2.Show()
You can also create a variable to represent another form and access the auxiliary form through
this variable. Let’s say you want to access the resources of Form2 from within the code of Form1.
Declare the variable auxForm to represent Form2 and then access the resources of Form2 with the
following statements:
Dim auxForm As Form2
auxForm.TextBox1.Text = ”some text”
auxForm.DateTimePicker1.Value = Today
auxForm.Show
Multiple Instances of a Single Form
Note that the variable that represents an auxiliary form is declared without the New keyword. The
auxForm variable represents an existing form. If we used the New keyword, we’d create a new
instance of the corresponding form. This technique is used when we want to display multiple
instances of the same form, as in an application that allows users to open multiple documents of the
same type.
Let’s say you’re designing an image-processing application, or a simple text editor. Each new docu-
ment should be opened in a separate window. Obviously, we can’t design many identical forms and
use them as needed. The solution is to design a single form and create new instances of it every time
the user opens an existing document or creates a new one. These instances are independent of one
another and they may interact with the main form. Usually they don’t, because they aren’t auxiliary
forms; they contain the necessary interface elements, such as menus, for processing the speci?c doc-
ument type, and users can arrange them any way they like on the desktop.
The approach described here is reminiscent of Multiple Document Interface (MDI) applications. The
MDI interface requires that all windows be contained within a parent window and, although once
very popular, it’s going slowly out of style. The new interfaces open multiple independent windows
on the desktop. Each window is an instance of a single form and it’s declared with the New keyword.
I’ve used this style of interface to redesign the TextPad application of Chapter 6, and I’ve included the
revised application in this chapter’s projects for your reference. Open the project in Visual Studio and
examine its code, which contains a lot of comments.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 240
240 CHAPTER 7 WORKING WITH FORMS
Forms versus Dialog Boxes
Dialog boxes are special types of forms with very speci?c functionality, which we use to prompt
the user for data. The Open and Save dialog boxes are two of the most familiar dialog boxes in
Windows. They’re so common that they’re actually known as common dialog boxes. Technically, a
dialog box is a good old form with its FormBorderStyle property set to FixedDialog. Like forms,
dialog boxes might contain a few simple controls, such as Labels, TextBoxes, and Buttons. You
can’t overload a dialog box with controls and functionality, because you’ll end up with a regular
form.
Figure 7.12 shows a few dialog boxes you have certainly seen while working with Windows
applications. The Insert Caption dialog box ofWord is amodal dialog box: Youmust close it before
switching to your document. The Find & Replace dialog box is modeless: It allows you to switch
to your document, yet it remains visible while open even if it doesn’t have the focus.
Figure 7.12
Typical dialog boxes
used by Word
Notice that some dialog boxes, such as Open, Color, and even the humble MessageBox, come
with the Framework, and you can incorporate them in your applications without having to design
them.
A characteristic of dialog boxes is that they provide an OK and a Cancel button. The OK button
tells the application that you’re ?nished using the dialog box, and the application can process the
information in it. The Cancel button tells the application that it should ignore the information in
the dialog box and cancel the current operation. As you will see, dialog boxes allow you to quickly
?nd out which buttons were clicked to close them, so that your application can take a different
action in each case.
In short, the difference between forms and dialog boxes is arti?cial. If it were really important
to distinguish between the two, they’d be implemented as two different objects— but they’re the
same object. So, without any further introduction, let’s look at how to create and use dialog boxes.
To create a dialog box, start with a Windows form, set its FormBorderStyle property to Fixed-
Dialog, and set the ControlBox, MinimizeBox,and MaximizeBox properties to False. Then add the
necessary controls on the form and code the appropriate events, as you would do with a regular
Windows form.
Figure 7.13 shows a simple dialog box that prompts the user for an ID and a password (see the
Password sample project). The dialog box contains two TextBox controls, next to the appropriate
labels, and the usual OK and Cancel buttons. The Cancel button signi?es that the user wants to
cancel the operation, which was initiated in the form that displayed the dialog box.
Start a new project, rename the form to MainForm, and place a button on the form. This is the
application’s main form, and we’ll invoke the dialog box from within the button’s Click eventPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 241
LOADING AND SHOWING FORMS 241
handler. Then add a new form to the project, name it PasswordForm, and place on it the controls
shown in Figure 7.13.
Figure 7.13
A simple dialog box
that prompts users for a
username and password
To display a modal form, you call the ShowDialog method, instead of the Show method. You
already know how to read the values entered on the controls of the dialog box. You also need
to know which button was clicked to close the dialog box. To convey this information from the
dialog box back to the calling application, the Form object provides the DialogResult property.
This property can be set to one of the values shown in Table 7.3 to indicate which button was
clicked. The DialogResult.OK value indicates that the user has clicked the OK button on the form.
There’s no need to place an OK button on the form; just set the form’s DialogResult property to
DialogResult.OK.
Table 7.3: The DialogResult Enumeration
Value Description
Abort The dialog box was closed with the Abort button.
Cancel The dialog box was closed with the Cancel button.
Ignore The dialog box was closed with the Ignore button.
No The dialog box was closed with the No button.
None The dialog box hasn’t been closed yet. Use this option to ?nd out whether a modeless dialog
box is still open.
OK The dialog box was closed with the OK button.
Retry The dialog box was closed with the Retry button.
Yes The dialog box was closed with the Yes button.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 242
242 CHAPTER 7 WORKING WITH FORMS
The dialog box need not contain any of the buttons mentioned here. It’s your responsibility to
set the value of the DialogResult property from within your code to one of the settings shown in
the table. This value can be retrieved by the calling application. The code behind the two buttons
in the dialog box is quite short:
Private Sub bttnOK Click(...) Handles bttnOK.Click
Me.DialogResult = DialogResult.OK
Me.Close
End Sub
Private Sub bttnCancel Click(...) Handles bttnCancel.Click
Me.DialogResult = DialogResult.Cancel
Me.Close
End Sub
The event handler of the button that displays this dialog box should contain an If statement
that examines the value returned by the ShowDialog method:
If PasswordForm.ShowDialog = DialogResult.OK Then
{ process the user selection }
End If
Depending on your application, you might allow the user to close the dialog box by clicking
more than two buttons. Some of them must set the DialogResult property to DialogResult.OK,
others to DialogResult.Cancel.
If the form contains an Accept and a Cancel button, you don’t have to enter a single line of code
in the modal form. The user can enter values on the various controls and then close the dialog
box by pressing the Enter or Cancel key. The dialog box will close and will return the Dialo-
gResult.OK or DialogResult.Cancel value. The Accept button sets the form’s DialogResult
property to DialogResult.OK automatically, and the Cancel button sets the same property to
DialogResult.Cancel. Any other button must set the DialogResult property explicitly. Listing
7.3 shows the code behind the Log In button on the sample project’s main form.
Listing 7.3: Prompting the User for an ID and a Password
Private Sub Button1 Click(...) Handles Button1.Click
If PasswordForm.ShowDialog() = DialogResult.OK Then
If PasswordForm.txtUserID.Text = ”” Or
PasswordForm.txtPassword.Text = ”” Then
MsgBox(”Please specify a user ID and a password to connect”)
Exit Sub
End If
MsgBox(”You were connected as ” &
passwordForm.txtUserID.Text)
ElsePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 243
LOADING AND SHOWING FORMS 243
MsgBox(”Connection failed for user ” &
password.txtPassword.Text)
End If
End Sub
VB 2008 at Work: The MultipleForms Project
It’s time to write an application that puts together the most important topics discussed in this
section. The MultipleForms project consists of a main form, an auxiliary form, and a dialog box.
All three components of the application’s interface are shown in Figure 7.14. The buttons on the
main form display both the auxiliary form and the dialog box.
Figure 7.14
The MultipleForms
project’s interface
Let’s review the various operations we want to perform — they’re typical for many situations,
not for only this application. At ?rst, we must be able to invoke both the auxiliary form and the
dialog box from within the main form; the Show Auxiliary Form and Show Dialog Box buttons
do this. The main form contains a variable declaration: strProperty. This variable is, in effect, a
property of the main form and is declared as public with the following statement:
Public strProperty As String = ”Mastering VB 2008”
The main form calls the auxiliary form’s Show method to display it in a modeless manner. The
auxiliary form button named Read Shared Variable In Main Form reads the strProperty variable
of the main form with the following statement:
Private Sub bttnReadShared Click(...) Handles bttnReadShared.Click
MsgBox(MainForm.strProperty, MsgBoxStyle.OKOnly,
”Public Variable Value”)
End SubPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 244
244 CHAPTER 7 WORKING WITH FORMS
Using the same notation, you can set this variable from within the auxiliary form. The follow-
ing event handler prompts the user for a new value and assigns it to the shared variable of the
main form:
Private Sub bttnSetShared Click(...) Handles bttnSetShared.Click
Dim str As String
str = InputBox(”Enter a new value for strProperty”)
MainForm.strProperty = str
End Sub
The two forms communicate with each other through public variables. Let’s make this commu-
nication a little more elaborate by adding an event. Every time the auxiliary form sets the value of
the strProperty variable, it will raise an event to notify the main form. The main form, in turn,
will use this event to display the new value of the string on the TextBox control as soon as the code
in the auxiliary form changes the value of the variable and before it’s closed.
To raise an event, you must declare the event’s name in the form’s declaration section. Insert
the following statement in the auxiliary form’s declarations section:
Event strPropertyChanged()
Now add a statement that ?res the event. To raise an event, we call the RaiseEvent statement,
passing the name of the event as an argument. This statement must appear in the Click event
handler of the Set Shared Variable In Main Form button, right after setting the value of the shared
variable. Listing 7.4 shows the revised event handler.
Listing 7.4: Raising an Event
Private Sub bttnSetShared Click(...) Handles bttnSetShared.Click
Dim str As String
str = InputBox(”Enter a new value for strProperty”)
MainForm.strProperty = str
RaiseEvent strPropertyChanged
End Sub
The event will be raised, but it will go unnoticed if we don’t handle it from within the main
form’s code. To handle the event, you must create a variable that represents the auxiliary form
with the WithEvents keyword:
Dim WithEvents FRM As New AuxiliaryForm()
The WithEvents keyword tells VB that the variable is capable of raising events. If you
expand the drop-down list with the objects in the code editor, you will see the name of the FRM
variable, along with the other controls you can program. Select FRM in the list and then expand
the list of events for the selected item. In this list, you will see the strPropertyChanged event.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 245
LOADING AND SHOWING FORMS 245
Select it, and the de?nition of an event handler will appear. Enter these statements in this event’s
handler:
Private Sub FRM strPropertyChanged() Handles FRM.strPropertyChanged
TextBox1.Text = strProperty
Beep()
End Sub
It’s a simple handler, but it’s adequate for demonstrating how to raise and handle custom
events on the form level. If you want, you can pass arguments to the event handler by includ-
ing them in the declaration of the event. To pass the original and the new value through the
strPropertyChanged event, use the following declaration:
Event strPropertyChanged(ByVal oldValue As String,
ByVal newValue As String)
If you run the application now, you’ll see that the value of the TextBox control in the main form
changes as soon as you change the property’s value in the auxiliary form.
Of course, you can update the TextBox control on the main form directly from within the
auxiliary form’s code. Use the expression MainForm.TextBox1 to access the control and then
manipulate it as usual. Events are used to perform some actions on a form when an action takes
place in one of the other forms of the application. The bene?t of using events, as opposed to
accessing members of another form from within our code, is that the auxiliary form need not
know anything about the form that called it. The auxiliary form raises the event, and it’s the other
form’s responsibility to handle it.
Let’s see now how the main form interacts with the dialog box. What goes on between a form
and a dialog box is not exactly interaction; it’s a more timid type of behavior. The form displays
the dialog box and waits until the user closes the dialog box. Then it looks at the value of the
DialogResult property to ?nd out whether it should even examine the values passed back by
the dialog box. If the user has closed the dialog box with the Cancel (or an equivalent) button, the
application ignores the dialog box settings. If the user closed the dialog box with the OK button,
the application reads the values and proceeds accordingly.
Before showing the dialog box, the code of the Show Dialog Box button sets the values of certain
controls in it. In the course of the application, it usually makes sense to suggest a few values in the
dialog box, so that the user can accept the default values by pressing the Enter key. The main form
selects a date on the dialog box’s controls and then displays the dialog box with the statements
given in Listing 7.5.
Listing 7.5: Displaying a Dialog Box and Reading Its Values
Protected Sub Button3 Click(...) Handles Button3.Click
’ Preselects the date 4/11/1980
AgeDialog.cmbMonth.Text = ”4”
AgeDialog.cmbDay.Text = ”11”
AgeDialog.CmbYear.Text = ”1980”Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 246
246 CHAPTER 7 WORKING WITH FORMS
AgeDialog.ShowDialog()
If AgeDialog.DialogResult = DialogResult.OK Then
MsgBox(AgeDialog.cmbMonth.Text & ” ” &
AgeDialog.cmbDay.Text & ”, ” &
AgeDialog.cmbYear.Text)
Else
MsgBox(”OK, we’ll protect your vital personal data”)
End If
End Sub
To close the dialog box, you can click the OK or Cancel button. Each button sets the DialogRe-
sult property to indicate the action that closed the dialog box. The code behind the two buttons
is shown in Listing 7.6.
Listing 7.6: Setting the Dialog Box’s DialogResult Property
Protected Sub bttnOK Click(...) Handles bttnOK.Click
Me.DialogResult = DialogResult.OK
End Sub
Protected Sub bttnCancel Click(...) Handles bttnCancel.Click
Me.DialogResult = DialogResult.Cancel
End Sub
Because the dialog box is modal, the code in the Show Dialog Box button is suspended at the
line that shows the dialog box. As soon as the dialog box is closed, the code in the main form
resumes with the statement following the one that called the ShowDialog method of the dialog
box. This is the If statement in Listing 7.5 that examines the value of the DialogResult property
and acts accordingly.
BuildingDynamic Forms at Runtime
Sometimes you won’t know in advance how many instances of a given control might be required
on a form. Let’s say you’re designing a form for displaying the names of all tables in a database.
It’s practically impossible to design a form that will accommodate every database users might
throw at your application. Another typical example is a form for entering family related data,
which includes the number of children in the family and their ages. As soon as the user enters
(or changes) the number of children, you should display as many TextBox controls as there are
children to collect their ages.
For these situations, it is possible to design dynamic forms, which are populated at runtime.
The simplest approach is to create more controls than you’ll ever need and set their Visible
properties to False at design time. At runtime, you can display the controls by switching their
Visible properties to True. As you know already, quick-and-dirty methods are not the most
ef?cient ones. You must still rearrange the controls on the form to make it look nice at all times.
The proper method to create dynamic forms at runtime is to add controls to and remove them
from your form as needed, using the techniques discussed in this section.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 247
BUILDING DYNAMIC FORMS AT RUNTIME 247
Just as you can create new instances of forms, you can also create new instances of any control
and place them on a form. The Form object exposes the Controls collection, which contains all
the controls on the form. This collection is created automatically as you place controls on the form
at design time, and you can access the members of this collection from within your code. It is also
possible to add new members to the collection, or remove existing members, with the Add and
Remove statements accordingly.
The Form’s Controls Collection
All the controls on a form are stored in the Controls collection, which is a property of the Form
object. The Controls collection exposes members for accessing and manipulating the controls at
runtime, and they’re the usual members of a collection:
Add method The Add method adds a new element to the Controls collection. In effect, it
adds a new control on the current form. The Add method accepts a reference to a control as
an argument and adds it to the collection. Its syntax is the following, where controlObj is an
instance of a control:
Controls.Add(controlObj)
To place a new Button control on the form, declare a variable of the Button type, set its proper-
ties, and then add it to the Controls collection:
Dim bttn As New System.WinForms.Button
bttn.Text = ”New Button”
bttn.Left = 100
bttn.Top = 60
bttn.Width = 80
Me.Controls.Add(bttn)
Remove method The Remove method removes an element from the Controls collection. It
accepts as an argument either the index of the control to be removed or a reference to the con-
trol to be removed (a variable of the Control type that represents one of the controls on the
form). The syntax of these two forms of the Remove method is the following:
Me.Controls.Remove(index)
Me.Controls.Remove(controlObj)
Count property This property returns the number of elements in the Controls collection.
Notice that if there are container controls, the controls in the containers are not included in the
count. For example, if your form contains a Panel control, the controls on the panel won’t be
included in the value returned by the Count property. The Panel control, however, has its own
Controls collection.
All method This method returns all the controls on a form (or on a container control) as
an array of the System.WinForms.Control type. You can iterate through the elements of this
array with the usual methods exposed by the Array class.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 248
248 CHAPTER 7 WORKING WITH FORMS
Clear method The Clear method removes all the elements of the Controls array and
effectively clears the form.
The Controls collection is also a property of any control that can host other controls. Many
of the controls that come with VB 2008, such as the Panel control, can host other controls. As you
recall from our discussion of the Anchor and Dock properties, it’s customary to place controls
on a panel and handle them collectively as a section of the form. They are moved along with
the panel at design time, and they’re rearranged as a group at runtime. The panel belongs to the
form’s Controls collection, and it provides its own Controls collection, which lets you access the
controls on the panel.
VB 2008 at Work: The ShowControls Project
The ShowControls project (Figure 7.15) demonstrates the basic methods of the Controls array.
Open the project and add any number of controls on its main form. You can place a panel to act as
a container for other controls as well. Just don’t remove the button at the top of the form (the Scan
Controls On This Form button), which contains the code to list all the controls.
Figure 7.15
Accessing the controls
on a form at runtime
The code behind the Scan Controls On This Form button enumerates the elements of the form’s
Controls collection. The code doesn’t take into consideration containers within containers. This
would require a recursive routine, which would scan for controls at any depth. The code that
iterates through the form’s Controls collection and prints the names of the controls in the Output
window is shown in Listing 7.7.
Listing 7.7: Iterating the Controls Collection
Private Sub Button1 Click(...) Handles Button1.Click
Dim Control As Windows.Forms.Control
For Each Control In Me.Controls
Debug.WriteLine(Control.ToString)
If Control.GetType Is GetType(System.Windows.Forms.Panel) ThenPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 249
BUILDING DYNAMIC FORMS AT RUNTIME 249
Dim nestedControl As Windows.Forms.Control
For Each nestedControl In Control.Controls
Debug.WriteLine(” ” & nestedControl.ToString)
Next
End If
Next
End Sub
The form shown in Figure 7.15 produced the following (partial) output (the controls on the
Panel are indented to stand out in the listing):
Panel1: System.Windows.Forms.Panel,
BorderStyle:
System.Windows.Forms.BorderStyle.FixedSingle
CheckBox4: System.Windows.Forms.CheckBox, CheckState: 0
CheckBox3: System.Windows.Forms.CheckBox, CheckState: 0
HScrollBar1: System.Windows.Forms.HScrollBar,
Minimum: 0, Maximum: 100, Value: 0
CheckedListBox1: System.Windows.Forms.CheckedListBox,
Items.Count: 3, Items[0]: Item 1
TextBox2: System.Windows.Forms.TextBox,
Text: TextBox2
TextBox1: System.Windows.Forms.TextBox,
Text: TextBox1
Button4: System.Windows.Forms.Button,
Text: Button4
To ?nd out the type of individual controls, call the GetType method. The following statement
examines whether the control in the ?rst element of the Controls collection is a TextBox:
If Me.Controls(0).GetType Is GetType(system.WinForms.TextBox) Then
MsgBox(”It’s a TextBox control”)
End If
Notice the use of the Is operator in the preceding statement. The equals operator will cause
an exception because objects can be compared only with the Is operator. (You’re comparing
instances, not values.)
If you know the type’s exact name, you can use a statement like the following:
If Me.Controls(i).GetType.Name = ”TextBox” Then ...
To access other properties of the control represented by an element of the Controls collection,
you must ?rst cast it to the appropriate type. If the ?rst control of the collection is a TextBox con-
trol, use the CType() function to cast it to a TextBox variable and then request its Text property:
If Me.Controls(0).GetType Is GetType(system.WinForms.TextBox) Then
Debug.WriteLine(CType(Me.Controls(0), TextBox).Text)
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 250
250 CHAPTER 7 WORKING WITH FORMS
The If statement is necessary, unless you can be sure that the ?rst control is a TextBox control.
If you omit the If statement and attempt to convert the control to a TextBox, a runtime exception
will be thrown if the object Me.Controls(0) isn’t a TextBox control.
VB 2008 at Work: The DynamicForm Project
To demonstrate how to handle controls at runtime from within your code, I included the
DynamicFormproject (Figure 7.16), a simple data-entrywindow for a small number of data points.
The user can specify at runtime the number of data points she wants to enter, and the number of
TextBoxes on the form is adjusted automatically.
Figure 7.16
The DynamicForm
project
The control you see at the top of the form is the NumericUpDown control. All you really need
to know about this control is that it displays an integer in the range speci?ed by its Minimum
and Maximum properties and allows users to select a value. It also ?res the ValueChanged event
every time the user clicks one of the two arrows or types another value in its edit area. This event
handler’s code adds or removes controls on the form, so that the number of text boxes (as well
as the number of corresponding labels) matches the value on the control. Listing 7.8 shows the
handler for the ValueChanged event of the NumericUpDown1 control. The ValueChanged event is
?red when the user clicks one of the two arrows on the control or types a new value in the control’s
edit area.
Listing 7.8: Adding and Removing Controls at Runtime
Private Sub NumericUpDown1 ValueChanged(...) Handles NumericUpDown1.ValueChanged
Dim TB As New TextBox()
Dim LBL As New Label()
Dim i, TBoxes As Integer
’ Count all TextBox controls on the Form
For i = 0 To Me.Controls.Count - 1
If Me.Controls(i).GetType Is
GetType(System.Windows.Forms.TextBox) Then
TBoxes = TBoxes + 1
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 251
BUILDING DYNAMIC FORMS AT RUNTIME 251
Next
’ Add new controls if number of controls on the Form is less
’ than the number specified with the NumericUpDown control
If TBoxes < NumericUpDown1.Value Then
TB.Left = 100
TB.Width = 120
TB.Text = ””
For i = TBoxes To CInt(NumericUpDown1.Value) - 1
TB = New TextBox()
LBL = New Label()
If NumericUpDown1.Value = 1 Then
TB.Top = 20
TB.TabIndex = 0
Else
TB.Top = Me.Controls(Me.Controls.Count - 2).Top + 25
End If
’ Set the trivial properties of the new controls
LBL.Left = 20
LBL.Width = 80
LBL.Text = ”Data Point ” & i
LBL.Top = TB.Top + 3
TB.Left = 100
TB.Width = 120
TB.Text = ””
’ add controls to the form
Me.Controls.Add(TB)
Me.Controls.Add(LBL)
TB.TabIndex = Convert.ToInt32(NumericUpDown1.Value)
’ and finally connect their GotFocus/LostFocus events
’ to the appropriate handler
AddHandler TB.Enter,
New System.EventHandler(AddressOf TBox Enter)
AddHandler TB.Leave,
New System.EventHandler(AddressOf TBox Leave)
Next
Else
For i = Me.Controls.Count - 1 To Me.Controls.Count -
2 * (TBoxes - CInt(NumericUpDown1.Value)) Step -2
Me.Controls.Remove(Controls(i))
Me.Controls.Remove(Controls(i - 1))
Next
End If
End Sub
Ignore the AddHandler statements for now; they’re discussed in the following section. First,
the code counts the number of TextBoxes on the form; then it ?gures out whether it should add
or remove elements from the Controls collection. To remove controls, the code iterates through
the last n controls on the form and removes them. The number of controls to be removed is thePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 252
252 CHAPTER 7 WORKING WITH FORMS
following, where TBoxes is the total number of controls on the form minus the value speci?ed in
the NumericUpDown control:
2 * (TBoxes - NumericUpDown1.Value)
If the value entered in the NumericUpDown control is less than the number of TextBox controls
on the form, the code removes the excess controls from within a loop. At each step, it removes two
controls, one of them a TextBox and the other a Label control with the matching caption. (That’s
why the loop variable is decreased by two.) The code also assumes that the ?rst two controls on the
form are the Button and the NumericUpDown controls. If the value entered by the user exceeds
the number of TextBox controls on the form, the code adds the necessary pairs of TextBox and
Label controls to the form.
To add controls, the code initializes a TextBox (TB)andaLabel(LBL) variable. Then, it sets
their locations and the label’s caption. The left coordinate of all labels is 20, their width is 80, and
their Text property (the label’s caption) is the order of the data item. The vertical coordinate is
20 pixels for the ?rst control, and all other controls are 3 pixels below the control on the previous
row. After a new control is set up, it’s added to the Controls collection with one of the following
statements:
Me.Controls.Add(TB) ’ adds a TextBox control
Me.Controls.Add(LBL) ’ adds a Label control
The code contains a few long lines, but it isn’t really complicated. It’s based on the assump-
tion that except for the ?rst few controls on the form, all others are pairs of Label and TextBox
controls used for data entry. You can simplify the code a little by placing the Label and Text-
Box controls on a Panel and manipulate the Panel’s Controls collection. This collection contains
only the data-entry controls, and the form may contain any number of additional controls.
To use the values entered by the user on the dynamic form, we must iterate the Controls col-
lection, extract the values in the TextBox controls, and use them. Listing 7.9 shows how the top
Process Values button scans the TextBox controls on the form and performs some basic calcula-
tions with them (counting the number of data points and summing their values).
Listing 7.9: Reading the Controls on the Form
Private Sub Button1 Click(...) Handles Button1.Click
Dim TBox As TextBox
Dim Sum As Double = 0, points As Integer = 0
Dim iCtrl As Integer
For iCtrl = 0 To Me.Controls.Count - 1
If Me.Controls(iCtrl).GetType Is
GetType(System.Windows.Forms.TextBox) Then
TBox = CType(Me.Controls(iCtrl), TextBox)
If IsNumeric(TBox.Text) Then
Sum = Sum + Val(TBox.Text)
points = points + 1
End If
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 253
BUILDING DYNAMIC FORMS AT RUNTIME 253
Next
MsgBox(”The sum of the ” & points.ToString &
” data points is ” & Sum.ToString)
End Sub
You can add more statements to calculate the mean and other vital statistics, or you can process
the values in any other way. You can even dump all the values into an array and then use the array
notation to manipulate them.
The project’s form has its AutoScroll property set to True, so that users can scroll it up and
down if they specify a number of data points that exceeds the vertical dimension of the form. The
two controls on the top-right side of the form, however, must remain at their location at all times.
I placed them on a Panel control and added some code to the form’s Scroll event handler, so that
every time the user scrolls the form, the Panel control maintains its distance from the top and right
edges of the form (otherwise, the two controls would scroll out of view). A single statement is all
it takes to keep the Panel control in view at all times:
Private Sub Form1 Scroll(ByVal sender As Object,
ByVal e As System.Windows.Forms.ScrollEventArgs)
Handles Me.Scroll
Panel1.Top = Panel1.Top + (e.NewValue - e.OldValue)
End Sub
You should try to redesign this application and place the data-entry controls on a Panel with
its AutoSize and AutoScroll properties set to True.
The second button on the form does the exact same thing as the top one, only this one uses a
For Each ... Next loop structure to iterate through the form’s controls.
Creating Event Handlers at Runtime
You saw how to add controls on your forms at runtime and how to access the properties of these
controls from within your code. In many situations, this is all you need: a way to access the prop-
erties of the controls (the text on a TextBox control or the status of a CheckBox or RadioButton
control). What good is a Button control, however, if it can’t react to the Click event? The only
problem with the controls you add to the Controls collection at runtime is that they don’t react
to events. It’s possible, though, to create event handlers at runtime, and this is what you’ll learn in
this section.
To create an event handler at runtime, create a subroutine that accepts two arguments — the
usual sender and e arguments — and enter the code you want to execute when a speci?c control
receives a speci?c event. The type of the e argument must match the de?nition of the second
argument of the event for which you want to create a handler. Let’s say that you want to add one
ormore buttons at runtime on your form, and these buttons should react to the Click event. Create
the ButtonClick() subroutine and enter the appropriate code in it. The name of the subroutine
can be anything; you don’t have to make up a name that includes the control’s or the event’s name.
After the subroutine is in place, you must connect it to an event of a speci?c control. The But-
tonClick() subroutine, for example, must be connected to the Click event of a Button control.
The statement that connects a control’s event to a speci?c event handler is the AddHandler state-
ment, whose syntax is as follows:
AddHandler control.event, New System.EventHandler(AddressOf ButtonClick)Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 254
254 CHAPTER 7 WORKING WITH FORMS
For example, to connect the ProcessNow() subroutine to the Click event of the Calculate
button, use the following statement:
AddHandler Calculate.Click,
New System.EventHandler(AddressOf ProcessNow)
Let’s add a little more complexity to the DynamicForm application. We’ll program the Enter
and Leave events of the TextBox controls added at runtime through the Me.Controls.Add method.
When a TextBox control receives the focus, we’ll change its background color to a light yellow, and
when it loses the focus, we’ll restore the background to white, so the user knows which box has
the focus at any time. We’ll use the same handlers for all TextBox controls. (The code of the two
handlers is shown in Listing 7.10.)
Listing 7.10: Event Handlers Added at Runtime
Private Sub TBox Enter(ByVal sender As Object,
ByVal e As System.EventArgs)
CType(sender, TextBox).BackColor = color.LightCoral
End Sub
Private Sub TBox Leave(ByVal sender As Object,
ByVal e As System.EventArgs)
CType(sender, TextBox).BackColor = color.White
End Sub
The two subroutines use the sender argument to ?nd out which TextBox control received or
lost the focus, and they set the appropriate control’s background color. (These subroutines are not
event handlers yet, because they’re not followed by the Handles keyword— at least, not before
we associate them with an actual control and a speci?c event.) This process is done in the same
segment of code that sets the properties of the controls we create dynamically at runtime. After
adding the control to the Me.Controls collection, call the following statements to connect the new
control’s Enter and Leave events to the appropriate handlers:
AddHandler TB.Enter, New System.EventHandler(AddressOf TBox Enter)
AddHandler TB.Leave, New System.EventHandler(AddressOf TBox Leave)
Run the DynamicForm application and see how the TextBox controls handle the focus-related
events. With a few statements and a couple of subroutines, we were able to create event handlers
at runtime from within our code.
Designing an Application Generator
In the preceding sections of this chapter, you learned how to create new forms from within your code
and how to instantiate them. In effect, you have the basic ingredients for designing applications from
within your code. Designing an application programmatically is not a trivial task, but now you have a
good understanding of how an application generator works. You can even design a wizard that
prompts the user for information about the appearance of the form and then design the form from
within your code.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 255
DESIGNING MENUS 255
DesigningMenus
Menus are among the most common and most characteristic elements of the Windows user inter-
face. Even in the old days of character-based displays, menus were used to display methodically
organized choices and guide the user through an application. Despite the visually rich interfaces
of Windows applications and the many alternatives, menus are still the most popular means of
organizing a large number of options. Many applications duplicate some or all of their menus in
the form of toolbar icons, but the menu is a standard ?xture of a form. You can turn the toolbars
on and off, but not the menus.
The Menu Editor
Menus can be attached only to forms, and they’re implemented through the MenuStrip control.
The items that make up the menu are ToolStripMenuItem objects. As you will see, the MenuStrip
control and ToolStripMenuItem objects give you absolute control over the structure and appear-
ance of the menus of your application. The MenuStrip control is a variation of the Strip control,
which is the base of menus, toolbars, and status bars.
You can design menus visually and then program their Click event handlers. In principle,
that’s all there is to a menu: You specify its items (the menu’s commands) and then you program
each command’s actions. Depending on the needs of your application, you might want to enable
and disable certain commands, add context menus to some of the controls on your form, and so
on. Because each item in a menu is represented by a ToolStripMenuItem object, you can control
the application’s menus from within your code by manipulating the properties of the ToolStrip-
MenuItem objects. Let’s start by designing a simple menu, and I’ll show you how to manipulate
the menu objects from within your code as we go along.
Double-click the MenuStrip icon in the Toolbox. (You’ll ?nd the MenuStrip control in the
Menus & Toolbars tab of the Toolbox.) An instance of the MenuStrip control will be added to
the form, and a single menu command will appear on your form. Its caption will be Type Here.
If you don’t see the ?rst menu item on the form right away, select the MenuStrip control in the
Components tray below the form. Do as the caption says: Click it and enter the ?rst command’s
caption, File, as seen in Figure 7.17. To add items under the File menu, press Enter. To enter
another command in the main menu, press Tab. Depending on your action, another box will be
added, in which you can type the caption of the next command. Press Enter to move to the next
itemvertically, and Tab tomove to the next itemhorizontally. To insert a separator, enter a hyphen
(-) as the item’s caption.
When you hover the pointer over a menu item, a drop-down button appears to the right of
the item. Click this button to select the type of item you’ll place on the menu. This item can be a
MenuItem object, a separator, a ComboBox, or a TextBox. In this chapter, I’ll focus on menu items,
which are by far the most common elements on a menu. The last two options, however, allow you
to build elaborate menus, reminiscent of the Of?ce menus.
Enter the items of the File menu — New, Open, Save, SaveAs,and Exit — and then click
somewhere on the form. All the temporary items (the ones with the Type Here caption) will
disappear, and the menu will be ?nalized on the form.
To add the Edit menu, select the MenuStrip icon to activate the visual menu editor and then
click the File item. In the new item that appears next to the File item on the control, enter the string
Edit. Press Enter and you’ll switch to the ?rst item of the Edit menu. Fill the Edit menu with the
usual editing commands. Table 7.4 shows the captions (property Text) and names (property Name)
for each menu and each command. You can also insert a standard menu with the Insert Standard
Items command of the MenuStrip object’s context menu.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 256
256 CHAPTER 7 WORKING WITH FORMS
Figure 7.17
Designing a menu on
the form
Table 7.4: The Captions and Names of the File and Edit Menus
Caption Name
File FileMenu
New FileNew
Open FileOpen
Save FileSave
Save As FileSaveAs
Exit FileExit
Edit EditMenu
Copy EditCopy
Cut EditCut
Paste EditPaste
The leftmost items in Table 7.4 are the names of the ?rst-level menus (File and Edit); the
captions that are indented in the table are the commands on these two menus. Each menu item
has a name, which allows you to access its properties from within your code. The same name
is also used in naming the Click event handler of the item. The default names of the menu
items you add visually to the application’s menu are based on the item’s caption followed by the
suf?x ToolStripMenuItem (FileToolStripMenuItem, NewToolStripMenuItem, and so on). You’llPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 257
DESIGNING MENUS 257
probably want to change the default names to something less redundant. To do so, change the
Name property in the Properties window. To view the properties of a menu item, right-click it and
select Properties from the context menu.
The most convenient method of editing a menu is to use the Items Collection Editor window,
which is shown in Figure 7.18. This isn’t a visual editor, but you can set all the properties of each
menu item without having to switch to the Properties window.
Figure 7.18
Editing a menu with the
Items Collection Editor
The Add button adds to the menu an item of the type speci?ed in the combo box next to it (a
menu item, combo box, or text box). To insert an item at a different location, add it to the menu
and then use the arrow buttons to move it up or down. As you add new items, you can set their
Text and Name properties on the right pane of the editor. You can also set their font, set the align-
ment and orientation of the text, and specify an image to be displayed along with the text. To
add an image to a menu item, locate the Image property and click the ellipsis button. A dialog
box will appear, in which you can select the appropriate resource. Notice that all the images you
use on your form are stored as resources of the project. You can add all the images and icons you
might need in a project to the same resource ?le and reuse them at will. The TextImageRelation
property allows you to specify the relative positions of the text and the image. You can also select
to display text only, images only, or text and images for each menu item with the DisplayStyle
property.
If the menu item leads to a submenu, you must also specify the submenu’s items. Locate the
DropDownItems property and click the ellipsis button. An identical window will appear, in which
you can enter the drop-down items of the current menu item. Notice that the menu on the form is
continuously updated while you edit it in the Items Collection Editor window, so you can see the
effects of your changes on the form. Personally, I’m more productive with the editor than with the
visual tools, mainly because all the properties are right there, and I don’t have to switch between
the design surface and the Properties window.
The ToolStripMenuItem Properties
The ToolStripMenuItem class represents a menu command, at any level. If a command leads to
a submenu, it’s still represented by a ToolStripMenuItem object, which has its own collection ofPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 258
258 CHAPTER 7 WORKING WITH FORMS
ToolStripMenuItem objects: the DropDownItems collection, which is made up of ToolStripMenu-
Item objects. The ToolStripMenuItem class provides the following properties, which you can set
in the Properties window at design time or manipulate from within your code:
Checked Some menu commands act as toggles, and they are usually selected (checked) to
indicate that they are on, or are deselected (unchecked) to indicate that they are off. To initially
display a check mark next to a menu command, set its Checked property to True. You can also
access this property from within your code to change the checked status of a menu command
at runtime. For example, to toggle the status of a menu command called FntBold,usethis
statement:
FntBold.Checked = Not FntBold.Checked
Enabled Some menu commands aren’t always available. The Paste command, for example,
has no meaning if the Clipboard is empty (or if it contains data that can’t be pasted in the cur-
rent application). To indicate that a command can’t be used at the time, you set its Enabled
property to False. The command then appears grayed out in the menu, and although it can
be highlighted, it can’t be activated. The following statements enable and disable the Undo
command depending on whether the TextBox1 control can undo the most recent operation:
If TextBox1.CanUndo Then
cmdUndo.Enabled = True
Else
cmdUndo.Enabled = False
End If
cmdUndo is the name of the Undo command in the application’s Edit menu. The CanUndo prop-
erty of the TextBox control returns a True/False value that indicates whether the last action can
be undone or not.
IsOnDropDown If the menu command, represented by a ToolStripMenuItem object, belongs
to a submenu, its IsOnDropDown property is True; otherwise, it’s False. The IsOnDropDown
property is read-only and False for the items on the ?rst level of the menu.
Visible To remove a command temporarily from the menu, set the command’s Visible
property to False. The Visible property isn’t used frequently in menu design. In general, you
should prefer to disable a command to indicate that it can’t be used at the time (some other
action is required to enable it). Making a command invisible frustrates users, who might spend
time trying to locate the command in another menu.
Programming Menu Commands
When a menu item is selected by the user, it triggers a Click event. To program a menu item,
insert the appropriate code in the item’s Click event handler. The Exit command’s code would be
something like the following:
Sub menuExit(...) Handles menuExit.Click
End
End SubPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 259
DESIGNING MENUS 259
If you need to execute any cleanup code before the application ends, place it in the CleanUp()
subroutine and call this subroutine from within the Exit item’s Click event handler:
Sub menuExit(...) Handles menuExit.Click
CleanUp()
End
End Sub
The same subroutine must also be called from within the FormClosing event handler of the
application’s main form because some users might terminate the application by clicking the form’s
Close button.
An application’s Open menu command contains the code that prompts the user to select a ?le
and then open it. You will see many examples of programming menu commands in the following
chapters. All you really need to know now is that each menu item is a ToolStripMenuItem object,
and it ?res the Click event every time it’s selected with the mouse or the keyboard. In most cases,
you can treat the Click event handler of a ToolStripMenuItem object just like the Click event
handler of a Button.
Another interesting event of the ToolStripMenuItem is the DropDownOpened event, which is
?red when the user opens a menu or submenu (in effect, when the user clicks a menu item that
leads to a submenu). In this event’s handler, you can insert code to modify the submenu. The
Edit menu of just about any application contains the ubiquitous Cut/Copy/Paste commands.
These commands are not meaningful at all times. If the Clipboard doesn’t contain text, the Paste
command should be disabled. If no text is selected, the Copy and Cut commands should also be
disabled. Here’s how you could change the status of the Paste command from within the Drop-
DownOpened event handler of the Edit menu:
If My.Computer.Clipboard.ContainsText Then
PasteToolStripMenuItem.Enabled = True
Else
PasteToolStripMenuItem.Enabled = True
End If
Likewise, to change the status of the Cut and Copy commands, use the following statements in
the DropDownOpened event of the ToolStripMenuItem that represents the Edit menu:
If txtEditor.SelectedText.Trim.Length > 0 Then
CopyToolStripMenuItem.Enabled = True
CutToolStripMenuItem.Enabled = True
Else
CopyToolStripMenuItem.Enabled = False
CutToolStripMenuItem.Enabled = False
End If
Using Access and Shortcut Keys
Menus provide a convenient way to display a large number of choices to the user. They allow
you to organize commands in groups, according to their functions, and are available at all times.
Opening menus and selecting commands with the mouse, however, can be an inconvenience.
When using a word processor, for example, you don’t want to have to take your hands off the
keyboard and reach for the mouse. To simplify menu access, Windows forms support access keys
and shortcut keys.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 260
260 CHAPTER 7 WORKING WITH FORMS
Access Keys
Access keys allow the user to open a menu by pressing the Alt key and a letter key. To open the Edit
menu in all Windows applications, for example, you can press Alt+E. E is the Edit menu’s access
key. After the menu is open, the user can select a command with the arrow keys or by pressing
another key, which is the command’s access key, without holding down the Alt key.
Access keys are designated by the designer of the application and aremarked with an underline
character. To assign an access key to a menu item, insert the ampersand symbol (&) in front of the
character you want to use as an access key in the ToolStripMenuItem’s Text property.
Default Access Keys Are Based on Item Captions
If you don’t designate access keys, Visual Basic will use the ?rst character in each top-level menu as
its access key. The user won’t see the underline character under the ?rst character, but can open the
menu by pressing the ?rst character of its caption while holding down the Alt key. If two or more
menu captions begin with the same letter, the ?rst (leftmost and topmost) menu will open.
Because the & symbol has a special meaning in menu design, you can’t use it in a menu
item’s caption. To actually display the & symbol in a caption, pre?x it with another & symbol.
For example, the caption &Drag produces a command with the caption Drag (the ?rst character
is underlined because it’s the access key). The caption Drag && Drop will create another com-
mand whose caption will be Drag & Drop. Finally, the string &Drag && Drop will create another
command with the caption Drag & Drop.
Shortcut Keys
Shortcut keys are similar to access keys, but instead of opening a menu, they run a command when
pressed. Assign shortcut keys to frequently used menu commands, so that users can reach them
with a single keystroke. Shortcut keys are combinations of the Ctrl key and a function or character
key. For example, the usual access key for the Close command (after the File menu is opened with
Alt+F) is C, but the usual shortcut key for the Close command is Ctrl+W.
Toassignashortcutkeytoamenucommand,dropdownthe ShortcutKeys list in the Tool-
StripMenuItem’s Properties window and select a keystroke. Specify a modi?er (Shift, Ctrl, or Alt)
and a key. You don’t have to insert any special characters in the command’s caption, nor do you
have to enter the keystroke next to the caption. It will be displayed next to the command auto-
matically. When assigning access and shortcut keys, take into consideration the well-established
Windowsstandards.UsersexpectAlt+F to open the File menu, so don’t use Alt+F for the Format
menu. Likewise, pressing Ctrl+C universally performs the Copy command; don’t use Ctrl+Casa
shortcut for the Cut command.
Manipulating Menus at Runtime
Dynamic menus change at runtime to display more or fewer commands, depending on the current
status of the program. This section explores two techniques for implementing dynamic menus:
? Creating short and long versions of the same menu
? Adding and removing menu commands at runtimePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 261
DESIGNING MENUS 261
Creating Short and Long Menus
A common technique in menu design is to create long and short versions of a menu. If a menu
contains many commands, and most of the time only a few of them are needed, you can create
one menu with all the commands and another with the most common ones. The ?rst menu is
the long one, and the second is the short one. The last command in the long menu should be Short
Menu, and when selected, it should display the short version. The last command in the shortmenu
should be Long Menu, and it should display the long version.
Figure 7.19 shows a long and a short version of the same menu from the LongMenu project.
The short version omits infrequently used commands and is easier to handle.
Figure 7.19
The two versions of the
Format menu of the
LongMenu application
To implement the LongMenu command, start a new project and create a menu with the options
shown in Figure 7.19. Listing 7.11 is the code that shows/hides the long menu in the MenuSize
command’s Click event.
Listing 7.11: TheMenuSizeMenu Item’s Click Event
Private Sub mnuSize Click(...) Handles mnuSize.Click
If mnuSize.Text = ”Short Menu” Then
mnuSize.Text = ”Long Menu”
Else
mnuSize.Text = ”Short Menu”
End If
mnuUnderline.Visible = Not mnuUnderline.Visible
mnuStrike.Visible = Not mnuStrike.Visible
mnuSmallCaps.Visible = Not mnuSmallCaps.Visible
mnuAllCaps.Visible = Not mnuAllCaps.Visible
End Sub
The subroutine in Listing 7.11 doesn’t do much. It simply toggles the Visible property of
certain menu commands and changes the command’s caption to Short Menu or Long Menu,
depending on the menu’s current status. Notice that because the Visible property is a True/False
value, we don’t care about its current status; we simply toggle the current status with the Not
operator.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 262
262 CHAPTER 7 WORKING WITH FORMS
Adding and Removing Commands at Runtime
I conclude the discussion of menu design with a technique for building dynamic menus, which
grow and shrink at runtime. Many applications maintain a list of the most recently opened ?les in
the File menu. When you ?rst start the application, this list is empty, and as you open and close
?les, it starts to grow.
The RunTimeMenu project demonstrates how to add items to and remove items fromamenu at
runtime. The main menu of the application’s form contains the Run Time Menu submenu, which
is initially empty.
The two buttons on the form add commands to and remove commands from the Run Time
Menu. Each new command is appended at the end of the menu, and the commands are removed
from the bottom of the menu ?rst (the most recently added commands are removed ?rst). To
change this order and display the most recent command at the beginning of the menu, use the
Insert method instead of the Add method to insert the new item. Listing 7.12 shows the code
behind the two buttons that add and remove menu items.
Listing 7.12: Adding and RemovingMenu Items at Runtime
Private Sub bttnAddItem Click(...) Handles bttnAddItem.Click
Dim Item As New ToolStripMenuItem
Item.Text = ”Run Time Option” &
RunTimeMenuToolStripMenuItem.
DropDownItems.Count.ToString
RunTimeMenuToolStripMenuItem.DropDownItems.Add(Item)
AddHandler Item.Click,
New System.EventHandler(AddressOf OptionClick)
End Sub
Private Sub bttnRemoveItem Click(...) Handles bttnRemoveItem.Click
If RunTimeMenuToolStripMenuItem.DropDownItems.Count > 0 Then
Dim mItem As ToolStripItem
Dim items As Integer =
RunTimeMenuToolStripMenuItem.DropDownItems.Count
mItem = RunTimeMenuToolStripMenuItem.DropDownItems(items - 1)
RunTimeMenuToolStripMenuItem.DropDownItems.Remove(mItem)
End If
End Sub
TheRemovebutton’scodeusesthe Remove method to remove the last item in the menu by its
index, after making sure the menu contains at least one item. The Add button adds a new item,
sets its caption to Run Time Option n,where n is the item’s order in the menu. In addition, it
assigns an event handler to the new item’s Click event. This event handler is the same for all the
items added at runtime; it’s the OptionClick() subroutine.
All the runtime options invoke the same event handler — it would be quite cumbersome to
come up with a separate event handler for different items. In the single event handler, you can
examine the name of the ToolStripMenuItem object that invoked the event handler and act accord-
ingly. The OptionClick() subroutine used in Listing 7.13 displays the name of the menu item thatPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 263
DESIGNING MENUS 263
invoked it. It doesn’t do anything, but it shows you how to ?gure out which item of the Run Time
Menu was clicked.
Listing 7.13: Programming DynamicMenu Items
Private Sub OptionClick(...)
Dim itemClicked As New ToolStripMenuItem
itemClicked = CType(sender, ToolStripMenuItem)
MsgBox(”You have selected the item ” &
itemClicked.Text)
End Sub
Creating Context Menus
Nearly every Windows application provides a context menu that the user can invoke by
right-clicking a form or a control. (It’s sometimes called a shortcut menu or pop-up menu.) This
is a regular menu, but it’s not anchored on the form. It can be displayed anywhere on the form
or on speci?c controls. Different controls can have different context menus, depending on the
operations you can perform on them at the time.
To create a context menu, place a ContextMenuStrip control on your form. The new context
menu will appear on the form just like a regular menu, but it won’t be displayed there at run-
time. You can create as many context menus as you need by placing multiple instances of the
ContextMenuStrip control on your form and adding the appropriate commands to each one. To
associate a context menu with a control on your form, set the control’s ContextMenuStrip property
to the name of the corresponding context menu.
Designing a context menu is identical to designing a regular menu. The only difference is that
the ?rst command in the menu is always ContextMenuStrip and it’s not displayed along with the
menu. Figure 7.20 shows a context menu at design time and how the same menu is displayed at
runtime.
You can create as many context menus as you want on a form. Each control has a ContextMenu
property, which you can set to any of the existing ContextMenuStrip controls. Select the control for
which you want to specify a context menu and locate the ContextMenu property in the Properties
window. Expand the drop-down list and select the name of the desired context menu.
To edit one of the context menus on a form, select the appropriate ContextMenuStrip control at
the bottom of the Designer. The corresponding context menu will appear on the form’s menu bar,
as if it were a regular form menu. This is temporary, however, and the only menu that appears on
the form’s menu bar at runtime is the one that corresponds to the MenuStrip control (and there
can be only one of them on each form).
Iterating a Menu’s Items
The last menu-related topic in this chapter demonstrates how to iterate through all the items of a
menu structure, including their submenus, at any depth. The main menu of an application can be
accessed by the expression Me.MenuStrip1 (assuming that you’re using the default names). This
is a reference to the top-level commands of the menu, which appear in the form’s menu bar. Each
command, in turn, is represented by a ToolStripMenuItem object. All the items under a menu
command form a ToolStripMenuItems collection, which you can scan to retrieve the individual
commands.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 264
264 CHAPTER 7 WORKING WITH FORMS
Figure 7.20
Acontextmenuat
design time (top) and
at runtime (bottom)
The ?rst command in a menu is accessed with the expression Me.MenuStrip1.Items(0);thisis
the File command in a typical application. The expression Me.MenuStrip1.Items(1) is the second
command on the same level as the File command (typically, the Edit menu).
To access the items under the ?rst menu, use the DropDownItems collection of the top command.
The ?rst command in the File menu can be accessed by this expression:
Me.MenuStrip1.Items(0).DropDownItems(0)
The same items can be accessed by name as well, and this is how you should manipulate the
menu items from within your code. In unusual situations, or if you’re using dynamic menus to
which you add and subtract commands at runtime, you’ll have to access the menu items through
the DropDownItems collection.
VB 2008 at Work: The MapMenu Project
The MapMenu project demonstrates how to access the items of a menu from within your applica-
tion’s code. The project’s main form contains a menu, a TextBox control, and a Button control that
prints the menu’s structure in the TextBox. You can edit the menu before running the program,
and the code behind the button will print the current structure of the menu items.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 265
THE BOTTOM LINE 265
The code behind the Map Menu button iterates through the items of the form’s MenuStrip
items and prints their names, as well as the names of their drop-down items, in the Output win-
dow. It scans all the members of the control’s Items collection and prints their captions. After
printing each command’s caption, it calls the PrintSubMenu() subroutine, passing the current
ToolStripMenuItem as an argument. The PrintSubMenu() subroutine iterates through the Drop-
DownItems of the collection passed as an argument and prints their captions. If one of the items
leads to a nested submenu, it calls itself, passing the current ToolStripMenuItem as an argument.
You can open the MapMenu project with Visual Studio and examine its code.
The BottomLine
Use forms’ properties. Forms expose a lot of trivial properties for setting their appearance.
In addition, they expose a few properties that simplify the task of designing forms that can be
resized at runtime. The Anchor property causes a control to be anchored to one or more edges
of the form to which it belongs. The Dock property allows you to place on the form controls that
are docked to one of its edges. To create forms with multiple panes that the user can resize at
runtime, use the SplitContainer control. If you just can’t ?t all the controls in a reasonably sized
form, use the AutoScroll properties to create a scrollable form.
Master It You’ve been asked to design a form with three distinct sections. You should also
allow users to resize each section. How will you design this form?
Design applications with multiple forms. Typical applications are made up of multiple
forms: the main form and one or more auxiliary forms. To show an auxiliary form from within
the main form’s code, call the auxiliary form’s Show method, or the ShowDialog method if you
want to display the auxiliary form modally (as a dialog box).
Master It How will you set the values of selected controls in a dialog box, display them,
and then read the values selected by the user from the dialog box?
Design dynamic forms. You can create dynamic forms by populating them with controls at
runtime through the form’s Controls collection. First, create instances of the appropriate con-
trols by declaring variables of the corresponding type. Then set the properties of the variable
that represents the control. Finally, place the control on the form by adding it to the form’s Con-
trols collection.
Master It How will you add a TextBox control to your form at runtime and assign a han-
dler to the control’s TextChanged event?
Design menus. Both form menus and context menus are implemented through the Menu-
Strip control. The items that make up the menu are ToolStripMenuItem objects. The ToolStrip-
MenuItem objects give you absolute control over the structure and appearance of the menus of
your application.
Master It What are the two basic events ?red by the ToolStripMenuItem object?Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 266Petroutsos V1 c08.tex Page 267 01/28/2008 1:24pm
Chapter 8
More Windows Controls
In this chapter, we’ll continue our discussion of the basic Windows controls with the controls that
implement the common dialog boxes and the RichTextBox control.
The .NET Framework provides a set of controls for displaying common dialog boxes, such
as the Open or Color dialog boxes. Each of these controls encapsulates a large amount of
functionality that would take a lot of code to duplicate. The common dialog controls are
fundamental components because they enable you to design user interfaces with the look and feel
of a Windows application.
Besides the common dialog boxes, we’ll also explore the RichTextBox control, which is an
advanced version of the TextBox control. The RichTextBox control provides all the functionality
you’ll need to build a word processor — WordPad is actually built around the RichTextBox
control. The RichTextBox control allows you to format text bymixing fonts and attributes, aligning
paragraphs differently, and so on. You can also embed other objects in the document displayed in
a RichTextBox, such as images. Sure, the RichTextBox control is nothing like a full-?edged word
processor, but it’s a great tool for editing formatted text at runtime.
In this chapter you’ll learn how to do the following:
? Use the OpenFileDialog and SaveFileDialog controls to prompt users for ?lenames
? Use the ColorDialog and FontDialog controls to prompt users for colors and typefaces
? Use the RichTextBox control as an advanced text editor to present richly formatted text
The CommonDialog Controls
A rather tedious, but quite common, task in nearly every application is to prompt the user for
?lenames, font names and sizes, or colors to be used by the application. Designing your own
dialog boxes for these purposes would be a hassle, not to mention that your applications wouldn’t
conform to the basic Windows interface design principles. In fact, all Windows applications use
standard dialog boxes for common operations; two of them are shown in Figure 8.1. These dialog
boxes are implemented as standard controls in the Toolbox. To use any of the common dialog
controls in your interface, just place the appropriate control from the Dialog section of the Toolbox
on your form and activate it from within your code by calling the ShowDialog method.
The common dialog controls are invisible at runtime, and they’re not placed on your forms,
because they’re implemented as modal dialog boxes and they’re displayed as needed. You simply
add them to the project by double-clicking their icons in the Toolbox; a new icon appears inPetroutsos V1 c08.tex Page 268 01/28/2008 1:24pm
268 CHAPTER 8 MORE WINDOWS CONTROLS
the components tray of the form, just below the Form Designer. The common dialog controls
in the Toolbox are the following:
OpenFileDialog Lets users select a ?le to open. It also allows the selection of multiple ?les
for applications that must process many ?les at once.
SaveFileDialog Lets users select or specify the path of a ?le in which the current document
will be saved.
FolderBrowserDialog Lets users select a folder (an operation that can’t be performed with
the OpenFileDialog control).
ColorDialog Lets users select a color from a list of prede?ned colors or specify custom colors.
FontDialog Lets users select a typeface and style to be applied to the current text selection.
The Font dialog box has an Apply button, which you can intercept from within your code and
use to apply the currently selected font to the text without closing the dialog box.
Figure 8.1
The Open and Font
common dialog boxes
There are three more common dialog controls: the PrintDialog, PrintPreviewDialog, and
PageSetupDialog controls. These controls are discussed in detail in Chapter 20, ‘‘Printing with
Visual Basic 2008,’’ in the context of VB’s printing capabilities.
Using the Common Dialog Controls
To display any of the common dialog boxes from within your application, you must ?rst add an
instance of the appropriate control to your project. Then you must set some basic properties of
the control through the Properties window. Most applications set the control’s properties from
within the code because common dialogs interact closely with the application. When you call the
Color common dialog, for example, you should preselect a color from within your application
and make it the default selection on the control. When prompting the user for the color of the
text, the default selection should be the current setting of the control’s ForeColor property. Like-
wise, the Save dialog box must suggest a ?lename when it ?rst pops up (or the ?le’s extension,
at least).Petroutsos V1 c08.tex Page 269 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 269
To display a common dialog box from within your code, you simply call the control’s
ShowDialog method, which is common for all controls. Note that all common dialog controls
can be displayed only modally and they don’t expose a Show method. As soon as you call the
ShowDialog method, the corresponding dialog box appears onscreen, and the execution of the
program is suspended until the box is closed. Using the Open, Save, and FolderBrowser dialog
boxes, users can traverse the entire structure of their drives and locate the desired ?lename or
folder. When the user clicks the Open or Save button, the dialog box closes and the program’s
execution resumes. The code should read the name of the ?le selected by the user through the
FileName property and use it to open the ?le or store the current document there. The folder
selected in the FolderBrowserDialog control is returned to the application through the
SelectedPath property.
Here is the sequence of statements used to invoke the Open common dialog and retrieve the
selected ?lename:
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
fileName = OpenFileDialog1.FileName
’ Statements to open the selected file
End If
The ShowDialog method returns a value indicating how the dialog box was closed. You should
read this value from within your code and ignore the settings of the dialog box if the operation
was cancelled.
The variable fileName in the preceding code segment is the full pathname of the ?le selected
by the user. You can also set the FileName property to a ?lename, which will be displayed when
the Open dialog box is ?rst opened:
OpenFileDialog1.FileName =
”C:\WorkFiles\Documents\Document1.doc”
If OpenFileDialog1.ShowDialog =
Windows.Forms.DialogResult.OK Then
fileName = OpenFileDialog1.FileName
’ Statements to open the selected file
End If
Similarly, you can invoke the Color dialog box and read the value of the selected color by using
the following statements:
ColorDialog1.Color = TextBox1.BackColor
If ColorDialog1.ShowDialog = DialogResult.OK Then
TextBox1.BackColor = ColorDialog1.Color
End If
The ShowDialog method is common to all controls. The Title property is also common to all
controls and it’s the string displayed in the title bar of the dialog box. The default title is the name
of the dialog box (for example, Open, Color, and so on), but you can adjust it from within your code
with a statement such as the following:
ColorDialog1.Title = ”Select Drawing Color”Petroutsos V1 c08.tex Page 270 01/28/2008 1:24pm
270 CHAPTER 8 MORE WINDOWS CONTROLS
The ColorDialog Control
The Color dialog box, shown in Figure 8.2, is one of the simplest dialog boxes. Its Color property
returns the color selected by the user or sets the initially selected color when the user opens the
dialog box.
Figure 8.2
The Color dialog box
The following statements set the initial color of the ColorDialog control, display the dialog box,
and then use the color selected in the control to ?ll the form. First, place a ColorDialog control in
the form and then insert the following statements in a button’s Click event handler:
Private Sub Button1 Click(...)
Handles Button1.Click
ColorDialog1.Color = Me.BackColor
If ColorDialog1.ShowDialog =
Windows.Forms.DialogResult.OK Then
Me.BackColor = ColorDialog1.Color
End If
End Sub
The following sections discuss the basic properties of the ColorDialog control.
AllowFullOpen
Set this property to True if you want users to be able to open the dialog box and de?ne their own
custom colors, like the one shown in Figure 8.2. The AllowFullOpen property doesn’t open the
custom section of the dialog box; it simply enables the De?ne Custom Colors button in the dialog
box. Otherwise, this button is disabled.
AnyColor
This property is a Boolean value that determines whether the dialog box displays all available
colors in the set of basic colors.Petroutsos V1 c08.tex Page 271 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 271
Color
This is the color speci?ed on the control. You can set it to a color value before showing the dialog
box to suggest a reasonable selection. On return, read the value of the same property to ?nd out
which color was picked by the user in the control:
ColorDialog1.Color = Me.BackColor
If ColorDialog1.ShowDialog = DialogResult.OK Then
Me.BackColor = ColorDialog1.Color
End If
CustomColors
This property indicates the set of custom colors that will be shown in the dialog box. The Color
dialog box has a section called Custom Colors, in which you can display 16 additional custom
colors. The CustomColors property is an array of integers that represent colors. To display three
custom colors in the lower section of the Color dialog box, use a statement such as the following:
Dim colors() As Integer = {222663, 35453, 7888}
ColorDialog1.CustomColors = colors
You’d expect that the CustomColors property would be an array of Color values, but it’s not.
You can’t create the array CustomColors with a statement such as this one:
Dim colors() As Color =
{Color.Azure, Color.Navy, Color.Teal}
Because it’s awkward to work with numeric values, you should convert color values to integer
values by using a statement such as the following:
Color.Navy.ToArgb
The preceding statement returns an integer value that represents the color navy. This value,
however, is negative because the ?rst byte in the color value represents the transparency of the
color. To get the value of the color, you must take the absolute value of the integer value returned
by the previous expression. To create an array of integers that represent color values, use a
statement such as the following:
Dim colors() As Integer =
{Math.Abs(Color.Gray.ToArgb),
Math.Abs(Color.Navy.ToArgb),
Math.Abs(Color.Teal.ToArgb)}
Now you can assign the colors array to the CustomColors property of the control, and the
colors will appear in the Custom Colors section of the Color dialog box.
SolidColorOnly
This indicates whether the dialog box will restrict users to selecting solid colors only. This
setting should be used with systems that can display only 256 colors. Although today few
systems can’t display more than 256 colors, some interfaces are limited to this number. WhenPetroutsos V1 c08.tex Page 272 01/28/2008 1:24pm
272 CHAPTER 8 MORE WINDOWS CONTROLS
you run an application through Remote Desktop, for example, only the solid colors are displayed
correctly on the remote screen, regardless of the remote computer’s graphics card (and that’s for
ef?ciency reasons).
The FontDialog Control
The Font dialog box, shown in Figure 8.3, lets the user review and select a font and then set its
size and style. Optionally, users can also select the font’s color and even apply the current set-
tings to the selected text on a control of the form without closing the dialog box, by clicking the
Apply button.
Figure 8.3
The Font dialog box
When the dialog is closed by clicking the OK button, you can retrieve the selected font by using
the control’s Font property. In addition to the OK button, the Font dialog box may contain the
Apply button, which reports the current setting to your application. You can intercept the Click
event of the Apply button and adjust the appearance of the text on your form while the common
dialog is still visible.
The main property of this control is the Font property, which sets the initially selected font in
the dialog box and retrieves the font selected by the user. The following statements display the
Font dialog box after setting the initial font to the current font of the TextBox1 control. When
the user closes the dialog box, the code retrieves the selected font and assigns it to the same
TextBox control:
FontDialog1.Font = TextBox1.Font
If FontDialog1.ShowDialog = DialogResult.OK Then
TextBox1.Font = FontDialog1.Font
End If
Use the following properties to customize the Font dialog box before displaying it.Petroutsos V1 c08.tex Page 273 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 273
AllowScriptChange
This property is a Boolean value that indicates whether the Script combo box will be displayed in
the Font dialog box. This combo box allows the user to change the current character set and select
a non-Western language (such as Greek, Hebrew, Cyrillic, and so on).
AllowVerticalFonts
This property is a Boolean value that indicates whether the dialog box allows the display and
selection of both vertical and horizontal fonts. Its default value is False, which displays only
horizontal fonts.
Color, ShowColor
The Color property sets or returns the selected font color. To enable users to select a color for the
font, you must also set the ShowColor property to True.
FixedPitchOnly
This property is a Boolean value that indicates whether the dialog box allows only the selection
of ?xed-pitch fonts. Its default value is False, which means that all fonts (?xed- and variable-pitch
fonts) are displayed in the Font dialog box. Fixed-pitch fonts, or monospaced fonts, consist of
characters of equal widths that are sometimes used to display columns of numeric values so that
the digits are aligned vertically.
Font
This property is a Font object. You can set it to the preselected font before displaying the dialog
box and assign it to a Font property upon return. You’ve already seen how to preselect a font and
how to apply the selected font to a control from within your application.
You can also create a new Font object and assign it to the control’s Font property. Upon return,
the TextBox control’s Font property is set to the selected font:
Dim newFont As Font(”Verdana”, 12, FontStyle.Underline)
FontDialog1.Font = newFont
If FontDialog1.ShowDialog() = DialogResult.OK Then
TextBox1.ForeColor = FontDialog1.Color
End If
FontMustExist
This property is a Boolean value that indicates whether the dialog box forces the selection of
an existing font. If the user enters a font name that doesn’t correspond to a name in the list
of available fonts, a warning is displayed. Its default value is True, and there’s no reason to
change it.
MaxSize, MinSize
These two properties are integers that determine the minimum and maximum point size the user
can specify in the Font dialog box. Use these two properties to prevent the selection of extremely
large or extremely small font sizes, because these fonts might throw off a well-balanced interface
(text will over?ow in labels, for example).Petroutsos V1 c08.tex Page 274 01/28/2008 1:24pm
274 CHAPTER 8 MORE WINDOWS CONTROLS
ShowApply
This property is a Boolean value that indicates whether the dialog box provides an Apply button.
Its default value is False, so the Apply button isn’t normally displayed. If you set this property to
True, youmust also programthe control’s Apply event — the changes aren’t applied automatically
to any of the controls in the current form.
The following statements display the Font dialog box with the Apply button:
Private Sub Button2 Click(...) Handles Button2.Click
FontDialog1.Font = TextBox1.Font
FontDialog1.ShowApply = True
If FontDialog1.ShowDialog = DialogResult.OK Then
TextBox1.Font = FontDialog1.Font
End If
End Sub
The FontDialog control raises the Apply event every time the user clicks the Apply button. In
this event’s handler, you must read the currently selected font and use it in the form, so that users
can preview the effect of their selection:
Private Sub FontDialog1 Apply(...) Handles FontDialog1.Apply
TextBox1.Font = FontDialog1.Font
End Sub
ShowEffects
This property is a Boolean value that indicates whether the dialog box allows the selection of
special text effects, such as strikethrough and underline. The effects are returned to the
application as attributes of the selected Font object, and you don’t have to do anything special
in your application.
The OpenDialog and SaveDialog Controls
Open and Save As, the two most widely used common dialog boxes (see Figure 8.4), are
implemented by the OpenFileDialog and SaveFileDialog controls. Nearly every application
prompts users for ?lenames, and the .NET Framework provides two controls for this purpose.
The two dialog boxes are nearly identical, and most of their properties are common, so we’ll start
with the properties that are common to both controls.
When either of the two controls is displayed, it rarely displays all the ?les in any given folder.
Usually the ?les displayed are limited to the ones that the application recognizes so that users can
easily spot the ?le they want. The Filter property limits the types of ?les that will appear in the
Open or Save As dialog box.
It’s also standard for the Windows interface not to display the extensions of ?les (although
Windows distinguishes ?les by their extensions). The ?le type ComboBox, which appears at the
bottom of the form next to the File Name box, contains the various ?le types recognized by
the application. The various ?le types can be described in plain English with long descriptive
names and without their extensions.Petroutsos V1 c08.tex Page 275 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 275
Figure 8.4
The Open and Save As
common dialog boxes
The extension of the default ?le type for the application is described by the DefaultExtension
property, and the list of the ?le types displayed in the Save As Type box is determined by the
Filter property.
To prompt the user for a ?le to be opened, use the following statements. The Open dialog box
displays the ?les with the extension .bin only.
OpenFileDialog1.DefaultExt = ”.bin”
OpenFileDialog1.AddExtension = True
OpenFileDialog1.Filter = ”Binary Files|*.bin”
If OpenFileDialog1.ShowDialog() =
Windows.Forms.DialogResult.OK Then
Debug.WriteLine(OpenFileDialog1.FileName)
End If
The following sections describe the properties of the OpenFileDialog and SaveFileDialog
controls.
AddExtension
This property is a Boolean value that determines whether the dialog box automatically adds an
extension to a ?lename if the user omits it. The extension added automatically is the one speci?edPetroutsos V1 c08.tex Page 276 01/28/2008 1:24pm
276 CHAPTER 8 MORE WINDOWS CONTROLS
by the DefaultExtension property, which you must set before calling the ShowDialog method.
This is the default extension of the ?les recognized by your application.
CheckFileExists
This property is a Boolean value that indicates whether the dialog box displays a warning if the
user enters the name of a ?le that does not exist in the Open dialog box, or if the user enters
the name of a ?le that exists in the Save dialog box.
CheckPathExists
This property is a Boolean value that indicates whether the dialog box displays a warning if the
user speci?es a path that does not exist, as part of the user-supplied ?lename.
DefaultExt
This property sets the default extension for the ?lenames speci?ed on the control. Use this prop-
erty to specify a default ?lename extension, such as .txt or .doc, so that when a ?le with no
extension is speci?ed by the user, the default extension is automatically appended to the ?lename.
You must also set the AddExtension property to True. The default extension property starts with
the period, and it’s a string — for example, .bin.
DereferenceLinks
This property indicates whether the dialog box returns the location of the ?le referenced by the
shortcut or the location of the shortcut itself. If you attempt to select a shortcut on your desktop
when the DereferenceLinks property is set to False, the dialog box will return to your application
a value such as C:\WINDOWS\SYSTEM32\lnkstub.exe, which is the name of the shortcut, not the
name of the ?le represented by the shortcut. If you set the DereferenceLinks property to True,
the dialog box will return the actual ?lename represented by the shortcut, which you can use in
your code.
FileName
Use this property to retrieve the full path of the ?le selected by the user in the control. If you set
this property to a ?lename before opening the dialog box, this value will be the proposed ?lename.
The user can click OK to select this ?le or select another one in the control. The two controls
provide another related property, the FileNames property, which returns an array of ?lenames.
To ?nd out how to allow the user to select multiple ?les, see the discussion of the MultipleFiles
and FileNames properties in ‘‘VB 2008 atWork: Multiple File Selection’’ at the end of
this section.
Filter
This property is used to specify the type(s) of ?les displayed in the dialog box. To display text ?les
only, set the Filter property to Text files|*.txt. The pipe symbol separates the description of
the ?les (what the user sees) from the actual extension (how the operating system distinguishes
the various ?le types).
If you want to display multiple extensions, such as .BMP, .GIF,and .JPG, use a semicolon to
separate extensions with the Filter property. Set the Filter property to the string Images|*.BMP;Petroutsos V1 c08.tex Page 277 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 277
*.GIF;*.JPG to display all the ?les of these three types when the user selects Images in the Save As
Type combo box, under the box with the ?lename.
Don’t include spaces before or after the pipe symbol because these spaces will be displayed
on the dialog box. In the Open dialog box of an image-processing application, you’ll probably
provide options for each image ?le type, as well as an option for all images:
OpenFileDialog1.Filter =
”Bitmaps|*.BMP|GIF Images|*.GIF|”&
”JPEG Images|*.JPG|All Images|*.BMP;*.GIF;*.JPG”
FilterIndex
When you specify more than one ?le type when using the Filter property of the Open dialog
box, the ?rst ?le type becomes the default. If you want to use a ?le type other than the ?rst one,
use the FilterIndex property to determine which ?le type will be displayed as the default when
the Open dialog box is opened. The index of the ?rst type is 1, and there’s no reason to ever set
this property to 1. If you use the Filter property value of the example in the preceding section
and set the FilterIndex property to 2, the Open dialog box will display GIF ?les by default.
InitialDirectory
This property sets the initial folder whose ?les are displayed the ?rst time that the Open and Save
dialog boxes are opened. Use this property to display the ?les of the application’s folder or to spec-
ify a folder in which the application stores its ?les by default. If you don’t specify an initial folder,
the dialog box will default to the last folder where the most recent ?le was opened or saved. It’s
also customary to set the initial folder to the application’s path by using the following statement:
OpenFileDialog1.InitialDirectory = Application.ExecutablePath
The expression Application.ExecutablePath returns the path in which the application’s
executable ?le resides.
RestoreDirectory
Every time the Open and Save As dialog boxes are displayed, the current folder is the one that was
selected by the user the last time the control was displayed. The RestoreDirectory property is a
Boolean value that indicates whether the dialog box restores the current directory before closing.
Its default value is False, which means that the initial directory is not restored automatically.
The InitialDirectory property overrides the RestoreDirectory property.
The following four properties are properties of the OpenFileDialog control only: FileNames,
MultiSelect, ReadOnlyChecked,and ShowReadOnly.
FileNames
If the Open dialog box allows the selection of multiple ?les (see the later section ‘‘VB 2008 atWork:
Multiple File Selection’’), the FileNames property contains the pathnames of all selected ?les.
FileNames is a collection, and you can iterate through the ?lenames with an enumerator. This
property should be used only with the OpenFileDialog control, even though the SaveFileDialog
control exposes a FileNames property.Petroutsos V1 c08.tex Page 278 01/28/2008 1:24pm
278 CHAPTER 8 MORE WINDOWS CONTROLS
MultiSelect
This property is a Boolean value that indicates whether the user can select multiple ?les in the
dialog box. Its default value is False, and users can select a single ?le. When the MultiSelect
property is True, the user can select multiple ?les, but they must all come from the same folder
(you can’t allow the selection of multiple ?les from different folders). This property is unique to
the OpenFileDialog control.
ReadOnlyChecked, ShowReadOnly
The ReadOnlyChecked property is a Boolean value that indicates whether the Read-Only check
box is selected when the dialog box ?rst pops up (the user can clear this box to open a ?le in
read/write mode). You can set this property to True only if the ShowReadOnly property is also set
to True. The ShowReadOnly property is also a Boolean value that indicates whether the Read-Only
check box is available. If this check box appears on the form, the user can select it so the ?le will
be opened as read-only. Files opened as read-only shouldn’t be saved onto the same ?le — always
prompt the user for a new ?lename.
The OpenFile and SaveFile Methods
The OpenFileDialog control exposes the OpenFile method, which allows you to quickly open the
selected ?le. Likewise, the SaveFileDialog control exposes the SaveFile method, which allows
you to quickly save a document to the selected ?le. Normally, after retrieving the name of the
?le selected by the user, you must open this ?le for reading (in the case of the Open dialog box)
or writing (in the case of the Save dialog box). The topic of reading from or writing to ?les is
discussed in detail in Chapter 15, ‘‘Accessing Folders and Files.’’
When this method is applied to the Open dialog box, the ?le is opened with read-only permis-
sion. The same method can be applied to the SaveFile dialog box, in which case the ?le is opened
with read-write permission. Both methods return a Stream object, and you can call this object’s
Read and Write methods to read from or write to the ?le.
VB 2008 at Work: Multiple File Selection
The Open dialog box allows the selection of multiple ?les. This feature can come in handy when
you want to process ?les en masse. You can let the user select many ?les, usually of the same type,
and then process them one at a time. Or, you might want to prompt the user to select multiple ?les
to be moved or copied.
To allow the user to select multiple ?les in the Open dialog box, set the MultiSelect property
to True. The user can then select multiple ?les with the mouse by holding down the Shift or Ctrl
key. The names of the selected ?les are reported by the property FileNames, which is an array
of strings. The FileNames array contains the pathnames of all selected ?les, and you can iterate
through them and process each ?le individually.
One of this chapter’s sample projects is the MultipleFiles project, which demonstrates how to
use the FileNames property. The application’s form is shown in Figure 8.5. The button at the top
of the form displays the Open dialog box, where you can select multiple ?les. After closing the
dialog box by clicking the Open button, the application displays the pathnames of the selected
?les on a ListBox control.Petroutsos V1 c08.tex Page 279 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 279
Figure 8.5
The MultipleFiles
project lets the user
select multiple ?les in
the Open dialog box.
The code behind the Open Files button is shown in Listing 8.1. In this example, I used the
array’s enumerator to iterate through the elements of the FileNames array. You can use any of the
methods discussed in Chapter 2, ‘‘The Visual Basic 2008 Language,’’ to iterate through the array.
Listing 8.1: ProcessingMultiple Selected Files
Private Sub bttnFile Click(...)
Handles bttnFile.Click
OpenFileDialog1.Multiselect = True
OpenFileDialog1.ShowDialog()
Dim filesEnum As IEnumerator
ListBox1.Items.Clear()
filesEnum = OpenFileDialog1.FileNames.GetEnumerator()
While filesEnum.MoveNext
ListBox1.Items.Add(filesEnum.Current)
End While
End Sub
The FolderBrowserDialog Control
Sometimes we need to prompt users for a folder, rather than a ?lename. An application that
processes ?les in batch mode shouldn’t force users to select the ?les to be processed. Instead, it
should allow users to select a folder and process all ?les of a speci?c type in the folder (it couldPetroutsos V1 c08.tex Page 280 01/28/2008 1:24pm
280 CHAPTER 8 MORE WINDOWS CONTROLS
encrypt all text documents or resize all image ?les, for example). As elaborate as the File Open
dialog box might be, it doesn’t allow the selection of a folder. To prompt users for a folder’s path,
use the FolderBrowser dialog box, which is a very simple one; it’s shown in Figure 8.6 in the
section ‘‘VB 2008 at Work: Folder Browsing Demo Project.’’ The FolderBrowserDialog control
exposes a small number of properties, which are discussed next.
RootFolder
This property indicates the initial folder to be displayed when the dialog box is shown. It is not
necessarily a string; it can also be a member of the SpecialFolder enumeration. To see the
members of the enumeration, enter the following expression:
FolderBrowserDialog1.RootFolder =
As soon as you enter the equals sign, you will see the members of the enumeration. The most
common setting for this property is My Computer, which represents the target computer’s ?le
system. You can set the RootFolder property to a number of special folders (for example, Personal,
Desktop, ApplicationData, LocalApplicationData, and so on). You can also set this property to a
string with the desired folder’s pathname.
SelectedFolder
After the user closes the FolderBrowser dialog box by clicking the OK button, you can retrieve the
name of the selected folder with the SelectedFolder property, which is a string, and you can
use it with themethods of the System.IO namespace to access andmanipulate the selected folder’s
?les and subfolders.
ShowNewFolderButton
This property determines whether the dialog box will contain a New button; its default value
is True. When users click the New button to create a new folder, the dialog box prompts them
for the new folder’s name, and creates a new folder with the speci?ed name under the
selected folder.
VB 2008 at Work: Folder Browsing Demo Project
The FolderBrowser control is a trivial control, but I’m including a sample application to demon-
strate its use. The same application demonstrates how to retrieve the ?les and subfolders of the
selected folder and how to create a directory listing in a RichTextBox control, like the one shown in
Figure 8.6. The members of the System.IO namespace, which allow you to access and manipulate
?les and folders from within your code, are discussed in detail in Chapter 15.
The FolderBrowser dialog box is set to display the entire ?le system of the target computer and
is invoked with the following statements:
FolderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer
FolderBrowserDialog1.ShowNewFolderButton = False
If FolderBrowserDialog1.ShowDialog = DialogResult.OK Then
’ process files in selected folder
End IfPetroutsos V1 c08.tex Page 281 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 281
Figure 8.6
Selecting a folder via the
FolderBrowser
dialog box
As usual, we examine the value returned by the ShowDialog method of the control and we
proceed if the user has closed the dialog box by clicking the OK button. The code that iterates
through the selected folder’s ?les and subfolders, shown in Listing 8.2, is basically a demonstration
of some members of the System.IO namespace, but I’ll review it brie?y here.
Listing 8.2: Scanning a Folder
Private Sub bttnSelectFiles Click(...)
Handles bttnSelectFiles.Click
FolderBrowserDialog1.RootFolder =
Environment.SpecialFolder.MyComputer
FolderBrowserDialog1.ShowNewFolderButton = False
If FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
RichTextBox1.Clear()
’ Retrieve initial folder
Dim initialFolder As String =
FolderBrowserDialog1.SelectedPath
Dim InitialDir As New IO.DirectoryInfo(
FolderBrowserDialog1.SelectedPath)Petroutsos V1 c08.tex Page 282 01/28/2008 1:24pm
282 CHAPTER 8 MORE WINDOWS CONTROLS
’ and print its name w/o any indentation
PrintFolderName(InitialDir, ””)
’ and then print the files in the top folder
If InitialDir.GetFiles(”*.*”).Length = 0 Then
SwitchToItalics()
RichTextBox1.AppendText(
”folder contains no files” & vbCrLf)
SwitchToRegular()
Else
PrintFileNames(InitialDir, ””)
End If
Dim DI As IO.DirectoryInfo
’ Iterate through every subfolder and print it
For Each DI In InitialDir.GetDirectories
PrintDirectory(DI)
Next
End If
End Sub
The selected folder’s name is stored in the initialFolder variable and is passed as an
argument to the constructor of the DirectoryInfo class. The InitialDir variable represents the
speci?ed folder. This object is passed to the PrintFolderName() subroutine, which prints
the folder’s name in bold. Then the code iterates through the same folder’s ?les and prints them
with the PrintFileNames() subroutine, which accepts as an argument the DirectoryInfo object
that represents the current folder and the indentation level. After printing the initial folder’s name
and the names of the ?les in the folder, the code iterates through the subfolders of the initial folder.
The GetDirectories method of the DirectoryInfo class returns a collection of objects, one for each
subfolder under the folder represented by the InitialDir variable. For each subfolder, it calls the
PrintDirectory() subroutine, which prints the folder’s name and the ?les in this folder,
and then iterates through the folder’s subfolders. The code that iterates through the selected
folder’s ?les and subfolders is shown in Listing 8.3.
Listing 8.3: The PrintDirectory() Subroutine
Private Sub PrintDirectory(ByVal CurrentDir As IO.DirectoryInfo)
Static IndentationLevel As Integer = 0
IndentationLevel += 1
Dim indentationString As String = ””
indentationString =
New String(Convert.ToChar(vbTab), IndentationLevel)
PrintFolderName(CurrentDir, indentationString)
If CurrentDir.GetFiles(”*.*”).Length = 0 Then
SwitchToItalics()
RichTextBox1.AppendText(indentationString &
”folder contains no files” & vbCrLf)
SwitchToRegular()Petroutsos V1 c08.tex Page 283 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 283
Else
PrintFileNames(CurrentDir, indentationString)
End If
Dim folder As IO.DirectoryInfo
For Each folder In CurrentDir.GetDirectories
PrintDirectory(folder)
Next
IndentationLevel -= 1
End Sub
The code that iterates through the subfolders of a given folder is discussed in detail in
Chapter 15, so you need notworry if you can’t ?gure out howit works yet. In the following section,
you’ll learn how to display formatted text in the RichTextBox control.
The RichTextBox Control
The RichTextBox control is the core of a full-blown word processor. It provides all the functionality
of a TextBox control; it can handle multiple typefaces, sizes, and attributes, and offers precise
control over the margins of the text (see Figure 8.7). You can even place images in your text on a
RichTextBox control (although you won’t have the kind of control over the embedded images that
you have with Microsoft Word).
Figure 8.7
A word processor based
on the functionality of
the RichTextBox control
The fundamental property of the RichTextBox control is its Rtf property. Similar to the
Text property of the TextBox control, this property is the text displayed on the control. Unlike
the Text property, however, which returns (or sets) the text of the control but doesn’t contain
formatting information, the Rtf property returns the text along with any formatting informa-
tion. Therefore, you can use the RichTextBox control to specify the text’s formatting, including
paragraph indentation, font, and font size or style.Petroutsos V1 c08.tex Page 284 01/28/2008 1:24pm
284 CHAPTER 8 MORE WINDOWS CONTROLS
RTF, which stands for Rich Text Format, is a standard for storing formatting information along
with the text. The beauty of the RichTextBox control for programmers is that they don’t need
to supply the formatting codes. The control provides simple properties to change the font of
the selected text, change the alignment of the current paragraph, and so on. The RTF code is
generated internally by the control and used to save and load formatted ?les. It’s possible to
create elaborately formatted documents without knowing the RTF speci?cation.
The WordPad application that comes with Windows is based on the RichTextBox control.
You can easily duplicate every bit of WordPad’s functionality with the RichTextBox control,
as you will see later, in the section ‘‘VB 2008 at Work: The RTFPad Project.’’
The RTF Language
A basic knowledge of the RTF format, its commands, and how it works will certainly help you
understand the RichTextBox control’s inner workings. RTF is a language that uses simple
commands to specify the formatting of a document. These commands, or tags,areASCIIstrings,
such as \par (the tag that marks the beginning of a new paragraph) and \b (the tag that turns
on the bold style). And this is where the value of the RTF format lies. RTF documents don’t
contain special characters and can be easily exchanged among different operating systems and
computers, as long as there is an RTF-capable application to read the document. Let’s look at an
RTF document in action.
Open the WordPad application (choose Start  Programs  Accessories  WordPad) and
enter a few lines of text (see Figure 8.8). Select a few words or sentences, and format them in
different ways with any of WordPad’s formatting commands. Then save the document in RTF
format: Choose File  Save As, select Rich Text Format, and then save the ?le as Document.rtf.
If you open this ?le with a text editor such as Notepad, you’ll see the actual RTF code that pro-
duced the document. A section of the RTF ?le for the document shown in Figure 8.8 is shown in
Listing 8.4.
Listing 8.4: The RTF Code for the First Paragraph of the Document in Figure 8.8
{\rtf1\ansi\ansicpg1252\deff0\deflang1033
{\fonttbl{\f0\fnil\fcharset0 Verdana;}{\f1\fswiss\fcharset0 Arial;}}
\viewkind4\uc1\pard\nowidctlpar\fi720 \b\f0\fs18 RTF
\b0 stands for \i Rich Text Format\i0 ,
which is a standard for storing formatting
information along with the text. The beauty
of the RichTextBox control for programmers
is that they don\rquote t need to supply the
formatting codes. The control provides simple
properties that turn the selected text into bold,
change the alignment of the current paragraph, and so on.\par
As you can see, all formatting tags are pre?xed with the backslash (\) symbol. The tags
are shown in bold to stand out in the listing. To display the \ symbol itself, insert an additional
slash. Paragraphs are marked with the \par tag, and the entire document is enclosed in a pair
of curly brackets. The \li and \ri tags that are followed by a numeric value specify the amount
of the left and right indentation. If you assign this string to the RTF property of a RichTextBox
control, the result will be the document shown in Figure 8.7, formatted exactly as it appears
in WordPad.Petroutsos V1 c08.tex Page 285 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 285
Figure 8.8
The formatting applied
to the text by using
WordPad’s commands
is stored along with the
text in RTF format.
RTF is similar to Hypertext Markup Language (HTML), and if you’re familiar with HTML,
a few comparisons between the two standards will provide helpful hints and insight into
the RTF language. Like HTML, RTF was designed to create formatted documents that could be
displayed on different systems. The following RTF segment displays a sentence with a few words
in italics:
\bRTF\b0 (which stands for Rich Text Format) is a \i
document formatting language\i0 that uses simple
commands to specify the formatting of the document.
The following is the equivalent HTML code:
<b>RTF</b> (which stands for Rich Text Format) is a
<i>document formatting language</i> that uses simple
commands to specify the formatting of the document.
The <b> and <i> tags of HTML, for example, are equivalent to the \b and \i tags of RTF.
The closing tags in RTF are \b0 and \i0, respectively.
RTF, however, is much more complicated than HTML. It’s not nearly as easy to understand
an RTF document as it is to understand an HTML document, because RTF was meant to be used
internally by applications. As you can see in Listing 8.3, RTF contains information about the font
being used, its size, and so on. Just as you need a browser to view HTML documents, you need an
RTF-capable application to view RTF documents. WordPad, for instance, supports RTF and can
both save a document in RTF format and read RTF ?les.
Although you don’t need to understand the RTF speci?cations to produce formatted text with
the RichTextBox control, if you want to generate RTF documents from within your code, visit the
RTF Cookbook site at http://search.cpan.org/˜sburke/RTF-Writer/lib/RTF/Cookbook.pod.Petroutsos V1 c08.tex Page 286 01/28/2008 1:24pm
286 CHAPTER 8 MORE WINDOWS CONTROLS
There’s also a Microsoft resource on RTF at http://msdn2.microsoft.com/en-us/library/
aa140277(office.10).aspx.
Text Manipulation and Formatting Properties
The RichTextBox control provides properties for manipulating the selected text on the control.
The names of these properties start with the Selection or Selected pre?x, and the most
commonly used ones are shown in Table 8.1. Some of these properties are discussed in further
detail in following sections.
Table 8.1: RichTextBox Properties for Manipulating Selected Text
Property What It Manipulates
SelectedText The selected text
SelectedRtf The RTF code of the selected text
SelectionStart The position of the selected text’s ?rst character
SelectionLength The length of the selected text
SelectionFont The font of the selected text
SelectionColor The color of the selected text
SelectionBackColor The background color of the selected text
SelectionAlignment The alignment of the selected text
SelectionIndent,
SelectionRightIndent,
SelectionHangingIndent
The indentation of the selected text
RightMargin The distance of the text’s right margin from the left edge of the control
SelectionTabs An array of integers that sets the tab stop positions in the control
SelectionBullet Whether the selected text is bulleted
BulletIndent The amount of bullet indent for the selected text
SelectedText
The SelectedText property represents the selected text, whether it was selected by the user via
the mouse or from within your code. To assign the selected text to a variable, use the following
statement:
selText=RichTextbox1.SelectedTextPetroutsos V1 c08.tex Page 287 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 287
You can also modify the selected text by assigning a new value to the SelectedText property.
The following statement converts the selected text to uppercase:
RichTextbox1.SelectedText =
RichTextbox1.SelectedText.ToUpper
You can assign any string to the SelectedText property. If no text is selected at the time, the
statement will insert the string at the location of the pointer.
SelectionStart, SelectionLength
To simplify the manipulation and formatting of the text on the control, two additional properties,
SelectionStart and SelectionLength, report (or set) the position of the ?rst selected character
in the text and the length of the selection, respectively, regardless of the formatting of the selected
text. One obvious use of these properties is to select (and highlight) some text on the control:
RichTextBox1.SelectionStart = 0
RichTextBox1.SelectionLength = 100
You can also use the Select method, which accepts as arguments the starting location and the
length of the text to be selected.
SelectionAlignment
Use this property to read or change the alignment of one or more paragraphs. This property’s
value is one of the members of the HorizontalAlignment enumeration: Left, Right, and Center.
Users don’t have to select an entire paragraph to align it; just placing the pointer anywhere in the
paragraph will do the trick, because you can’t align part of the paragraph.
SelectionIndent, SelectionRightIndent, SelectionHangingIndent
These properties allow you to change the margins of individual paragraphs. The Selection-
Indent property sets (or returns) the amount of the text’s indentation from the left edge of the
control. The SelectionRightIndent property sets (or returns) the amount of the text’s indentation
from the right edge of the control. The SelectionHangingIndent property indicates the inden-
tation of each paragraph’s ?rst line with respect to the following lines of the same paragraph. All
three properties are expressed in pixels.
The SelectionHangingIndent property includes the current setting of the SelectionIndent
property. If all the lines of a paragraph are aligned to the left, the SelectionIndent property
can have any value (this is the distance of all lines from the left edge of the control), but the
SelectionHangingIndent property must be zero. If the ?rst line of the paragraph is shorter
than the following lines, the SelectionHangingIndent has a negative value. Figure 8.9 shows
several differently formatted paragraphs. The settings of the SelectionIndent and Selection-
HangingIndent properties are determined by the two sliders at the top of the form.
SelectionBullet, BulletIndent
You use these properties to create a list of bulleted items. If you set the SelectionBullet property
to True, the selected paragraphs are formatted with a bullet style, similar to the <ul> tag in
HTML. To create a list of bulleted items, select them from within your code and assign the value
True to the SelectionBullet property. To change a list of bulleted items back to normal text,
make the same property False.Petroutsos V1 c08.tex Page 288 01/28/2008 1:24pm
288 CHAPTER 8 MORE WINDOWS CONTROLS
Figure 8.9
Various combinations
of the Selection-
Indent and
SelectionHanging-
Indent properties
produce all possible
paragraph styles.
The paragraphs formatted as bullets are also indented from the left by a small amount. To set
the amount of the indentation, use the BulletIndent property, which is also expressed in pixels.
SelectionTabs
Use this property to set the tab stops in the RichTextBox control. The Selection tab should be set to
an array of integer values, which are the absolute tab positions in pixels. Use this property to set
up a RichTextBox control for displaying tab-delimited data.
Using the RichTextBox Control to Display Delimited Data
As a developer I tend to favor the RichTextBox control over the TextBox control, even though I don’t
mix font styles or use the more-advanced features of the RichTextBox control. I suggest that you treat
the RichTextBox control as an enhanced TextBox control and use it as a substitute for the TextBox
control. One of the features of the RichTextBox control that I ?nd very handy is its ability to set the
tab positions and display tabular data. You can also display tabular data on a ListView control, as you
will see in the following chapter, but it’s simpler to use a RichTextBox control with its ReadOnly
property set to True and its SelectionTabs property to an array of values that will accommodate
your data. Here’s how to set up a RichTextBox control to display a few rows of tab-delimited data:
RichTextBox1.ReadOnly = True
RichTextBox1.SelectionTabs = New Integer() {100, 160, 340}
RichTextBox1.AppendText(”R1C1” & vbTab &
”R1C2” & vbTab &
”R1C3” & vbCrLf)
RichTextBox1.AppendText(”R2C1” & vbTab &
”R2C2” & vbTab &
”R2C3” & vbCrLf)
This technique is a life-saver when I have to read the delimited data from a ?le. I just set up the tab
positions and then load the data with the LoadFile method, which is discussed in the following
section.Petroutsos V1 c08.tex Page 289 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 289
Methods
The ?rst two methods of the RichTextBox control you need to know are SaveFile and LoadFile.
The SaveFile method saves the contents of the control to a disk ?le, and the LoadFile method
loads the control from a disk ?le.
SaveFile
The syntax of the SaveFile method is as follows:
RichTextBox1.SaveFile(path, filetype)
where path is the path of the ?le in which the current document will be saved. By default, the
SaveFile method saves the document in RTF format and uses the .RTF extension. You can specify
a different format by using the second optional argument, which can take on the value of one of
the members of the RichTextBoxStreamType enumeration, described in Table 8.2.
Table 8.2: The RichTextBoxStreamType Enumeration
Format Effect
PlainText Stores the text on the control without any formatting
RichNoOLEObjs Stores the text without any formatting and ignores any embedded OLE objects
RichText Stores the text in RTF format (text with embedded RTF commands)
TextTextOLEObjs Stores the text along with the embedded OLE objects
UnicodePlainText Stores the text in Unicode format
LoadFile
Similarly, the LoadFile method loads a text or RTF ?le to the control. Its syntax is identical to the
syntax of the SaveFile method:
RichTextBox1.LoadFile(path, filetype)
The filetype argument is optional and can have one of the values of the RichTextBoxStream-
Type enumeration. Saving and loading ?les to and from disk ?les is as simple as presenting a Save
or Open common dialog to the user and then calling one of the SaveFile or LoadFile methods
with the ?lename returned by the common dialog box.
Select, SelectAll
The Select method selects a section of the text on the control, similar to setting the Selection-
Start and SelectionLength properties. The Select method accepts two arguments: the location
of the ?rst character to be selected and the length of the selection:
RichTextBox1.Select(start, length)
The SelectAll method accepts no arguments and it selects all the text on the control.Petroutsos V1 c08.tex Page 290 01/28/2008 1:24pm
290 CHAPTER 8 MORE WINDOWS CONTROLS
Advanced Editing Features
The RichTextBox control provides all the text-editing features you’d expect to ?nd in a text-editing
application, similar to the TextBox control. Among its more-advanced features, the RichTextBox
control provides the AutoWordSelection property, which controls how the control selects text. If
it’s True, the control selects a word at a time.
In addition to formatted text, the RichTextBox control can handle object linking and embedding
(OLE) objects. You can insert images in the text by pasting them with the Paste method. The
Paste method doesn’t require any arguments; it simply inserts the contents of the Clipboard at
the current location in the document.
The RichTextBox control encapsulates undo and redo operations at multiple levels. Each
operation has a name (Typing, Deletion, and so on), and you can retrieve the name of the
next operation to be undone or redone and display it on the menu. Instead of a simple Undo
or Redo caption, you can change the captions of the Edit menu to something like Undo Delete or
Redo Typing. To program undo and redo operations from within your code, you must use the
properties and methods discussed in the following sections.
CanUndo, CanRedo
These two properties are Boolean values you can read to ?nd out whether there’s an operation that
can be undone or redone. If they’re False, you must disable the corresponding menu command
from within your code. The following statements disable the Undo command if there’s no action
to be undone at the time (EditUndo is the name of the Undo command on the Edit menu):
If RichTextBox1.CanUndo Then
EditUndo.Enabled = True
Else
EditUndo.Enabled = False
End If
These statements should appear in the menu item’s Select event handler (not in the Click
event handler) because they must be executed before the menu is displayed. The Select event is
triggered when a menu is opened. As a reminder, the Click event is ?red when you click an item,
and not when you open a menu. For more information on programming the events of a menu, see
Chapter 7, ‘‘Working with Forms.’’
UndoActionName, RedoActionName
These two properties return the name of the action that can be undone or redone. The most
common value of both properties is Typing, which indicates that the Undo command will delete
a number of characters. Another common value is Delete, whereas some operations are named
Unknown. If you change the indentation of a paragraph on the control, this action’s name
is Unknown. Even when an action’s name is Unknown, the action can be undone with the Undo
method.
The following statement sets the caption of the Undo command to a string that indicates the
action to be undone (Editor is the name of a RichTextBox control):
If Editor.CanUndo Then
EditUndo.Text = ”Undo ” & Editor.UndoActionName
End IfPetroutsos V1 c08.tex Page 291 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 291
Undo, Redo
These two methods undo or redo an action. The Undo method cancels the effects of the last action
of the user on the control. The Redo method redoes the most recent undo action. The Redo method
does not repeat the last action; it applies to undo operations only.
Cutting and Pasting
To cut, copy, and paste text in the RichTextBox control, you can use the same techniques you use
with the regular TextBox control. For example, you can replace the current selection by assigning
astringtothe SelectedText property. The RichTextBox, however, provides a few useful methods
for performing these operations. The Copy, Cut,and Paste methods perform the corresponding
operations. The Cut and Copy methods are straightforward and require no arguments. The Paste
method accepts a single argument, which is the format of the data to be pasted. Because the data
will come from the Clipboard, you can extract the format of the data in the Clipboard at the time
and then call the CanPaste method to ?nd out whether the control can handle this type of data. If
so, you can then paste them in the control by using the Paste method.
This technique requires a bit of code because the Clipboard class doesn’t return the format of
the data in the Clipboard. You must call the following method of the Clipboard class to ?nd out
whether the data is of a speci?c type and then paste it on the control:
If Clipboard.GetDataObject. –
GetDataPresent(DataFormats.Text) Then
RichTextBox.Paste(DataFormats.Text)
End If
This is a very simple case because we know that the RichTextBox control can accept text. For a
robust application, you must call the GetDataPresent method for each type of data your
application should be able to handle. (You may not want to allow users to paste all types of data
that the control can handle.) By the way, you can simplify the code with the help of the
ContainsText/ContainsImage and GetText/GetImage methods of the My.Application
.Clipboard object.
In the RTFPad project later in this chapter, we’ll use a structured exception handler to allow
users to paste anything in the control. If the control can’t handle it, the data won’t be pasted in
the control.
Searching in a RichTextBox Control
To locate a string in the text of the RichTextBox control, use the Find method. The Find method is
quite ?exible, as it allows you to specify the type of the search, whether it will locate entire words,
and so on. The simplest form of this method accepts the search string as an argument and returns
the location of the ?rst instance of the word in the text. If the search argument isn’t found, the
method returns the value -1.
RichTextBox1.Find(string)
Another equally simple syntax of the Find method allows you to specify how the control will
search for the string:
RichTextBox1.Find(string, searchMode)Petroutsos V1 c08.tex Page 292 01/28/2008 1:24pm
292 CHAPTER 8 MORE WINDOWS CONTROLS
The searchMode argument is a member of the RichTextBoxFinds enumeration, which is
shown in Table 8.3.
Table 8.3: The RichTextBoxFinds Enumeration
Value Effect
MatchCase Performs a case-sensitive search.
NoHighlight The text found will not be highlighted.
None Locates instances of the speci?ed string even if they’re not whole words.
Reverse The search starts at the end of the document.
WholeWord Locates only instances of the speci?ed string that are whole words.
Two more forms of the Find method allow you specify the range of the text in which the search
will take place:
RichTextBox1.Find(string, start, searchMode)
RichTextBox1.Find(string, start, end, searchMode)
The arguments start and end are the starting and ending locations of the search (use them to
search for a string within a speci?ed range only). If you omit the end argument, the search will
start at the location speci?ed by the start argument and will extend to the end of the text.
You can combine multiple values of the searchMode argument with the OR operator. The
default search is case-insensitive, covers the entire document, and highlights the matching text
on the control. The RTFPad application’s Find command demonstrates how to use the Find
method and its arguments to build a Search & Replace dialog box that performs all the types
of text-searching operations you might need in a text-editing application.
Handling URLs in the Document
An interesting feature of the RichTextBox control is the automatic formatting of URLs embedded
in the text. To enable this feature, set the DetectURLs property to True. Then, as soon as the control
determines that you’re entering a URL (usually after you enter the three w’s and the following
period), it will format the text as a hyperlink. When the pointer rests over a hyperlink, its shape
turns into a hand, just as it would in Internet Explorer. Run the RTFDemo project, enter a URL
such as http://www.sybex.com, and see how the RichTextBox control handles it.
In addition to formatting the URL, the RichTextBox control triggers the LinkClicked event
when a hyperlink is clicked. To display the corresponding page from within your code, enter the
following statement in the LinkClicked event handler:
Private Sub RichTextBox1 LinkClicked(
ByVal sender As Object,
ByVal e As System.Windows.Forms.LinkClickedEventArgs)
Handles RichTextBox1.LinkClicked
System.Diagnostics.Process.Start(e.LinkText)
End SubPetroutsos V1 c08.tex Page 293 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 293
The System.Diagnostics.Process class provides the Start method, which starts an application.
You can specify either the name of the executable or the path of a ?le. If you specify, the Start
method will look up the associated application and start it. As you can see, handling embedded
URLs with the RichTextBox control is almost trivial.
Displaying a Formatted Directory Listing
This is a good point to review the subroutines that produced the formatted directory listings
shown in Figure 8.6. Folder names are printed in bold by the PrintFolderName() subroutine,
and ?lenames are printed in regular style by the PrintFileNames() subroutine. Both subroutines
accept as arguments a DirectoryInfo object that represents the folder whose name (or ?les) we
want to print, as well as an indentation string. This string is increased every time the code drills
down to a subfolder and is decreased every time it moves up to a parent folder. Listing 8.5 shows
the implementation of the two subroutines.
Listing 8.5: The PrintFolderName() and PrintFileNames() Subroutines
Private Sub PrintFolderName(
ByVal folder As IO.DirectoryInfo,
ByVal Indentation As String)
SwitchToBold()
RichTextBox1.AppendText(Indentation)
RichTextBox1.AppendText(folder.Name & vbCrLf)
SwitchToRegular()
End Sub
Private Sub PrintFileNames(
ByVal folder As IO.DirectoryInfo,
ByVal indentation As String)
Dim file As IO.FileInfo
For Each file In folder.GetFiles(”*.*”)
RichTextBox1.AppendText(
indentation & file.Name & vbCrLf)
Next
End Sub
The code for printing folder names and ?lenames is trivial. Before calling the AppendText
method to add a new folder name to the control, the code calls the SwitchToBold() subroutine.
After printing the folder name, it calls the SwitchToRegular subroutine to reset the font. The two
subroutines manipulate the SelectionFont property. Because no text is selected at the time, the
subroutines simply change the attributes of the text that will be appended to the control with
the next call to the AppendText method. The implementation of the two subroutines is shown next:
Private Sub SwitchToItalics()
RichTextBox1.SelectionFont =
New Font(RichTextBox1.SelectionFont.Name,
RichTextBox1.SelectionFont.Size, FontStyle.Italic)
End SubPetroutsos V1 c08.tex Page 294 01/28/2008 1:24pm
294 CHAPTER 8 MORE WINDOWS CONTROLS
Private Sub SwitchToRegular()
RichTextBox1.SelectionFont =
New Font(RichTextBox1.SelectionFont.Name,
RichTextBox1.SelectionFont.Size, FontStyle.Regular)
End Sub
VB 2008 at Work: The RTFPad Project
Creating a functional — even fancy — word processor based on the RichTextBox control is
unexpectedly simple. The challenge is to provide a convenient interface that lets the user select
text, apply attributes and styles to it, and then set the control’s properties accordingly. The RTFPad
sample application of this section does just that.
The RTFPad application (refer to Figure 8.7) is based on the TextPad application developed
in Chapter 6, ‘‘Basic Windows Controls.’’ It contains the same text-editing commands and some
additional text-formatting commands that can be implemented only with the RichTextBox control;
for example, it allows you to apply multiple fonts and styles to the text, and, of course, multiple
Undo/Redo operations.
The two TrackBar controls above the RichTextBox control manipulate the indentation of the
text.We already explored this arrangement in the discussion of the TrackBar control in Chapter 6,
but let’s review the operation of the two controls again. Each TrackBar control has a width of 816
pixels, which is equivalent to 8.5 inches on a monitor that has a resolution of 96 dots per inch (dpi).
The height of the TrackBar controls is 42 pixels, but unfortunately they can’t be made smaller. The
Minimum property of both controls is 0, and the Maximum property is 16. The TickFrequency is 1.
With these values, you can adjust the indentation in steps of 1 / 2 inch. Set the Maximum property to
32 and you’ll be able to adjust the indentation in steps of 1 / 4 inch. It’s not the perfect interface, as
it’s built for A4 pages in portrait orientation only. You can experiment with this interface to build
an even more functional word processor.
Each time the user slides the top TrackBar control, the code sets the SelectionIndent
property to the proper percentage of the control’s width. Because the SelectionHangingIndent
includes the value of the SelectionIndent property, it also adjusts the setting of the
SelectionHangingIndent property. Listing 8.6 is the code that’s executed when the upper
TrackBar control is scrolled.
Listing 8.6: Setting the SelectionIndent Property
Private Sub TrackBar1 Scroll(...)
Handles TrackBar1.Scroll
Editor.SelectionIndent = Convert.ToInt32(
Editor.Width *
(TrackBar1.Value / TrackBar1.Maximum))
Editor.SelectionHangingIndent = Convert.ToInt32(
Editor.Width *
(TrackBar2.Value / TrackBar2.Maximum) - Editor.SelectionIndent)
End Sub
Editor is the name of the RichTextBox control on the form. The code sets the control’s
indentation to the same percentage of the control’s width, as indicated by the value of the top
TrackBar control. It also does the same for the SelectionHangingIndent property, which isPetroutsos V1 c08.tex Page 295 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 295
controlled by the lower TrackBar control. If the user has scrolled the lower TrackBar control, the
code sets the RichTextBox control’s SelectionHangingIndent property in the event handler, as
presented in Listing 8.7.
Listing 8.7: Setting the SelectionHangingIndent Property
Private Sub TrackBar2 Scroll(...)
Handles TrackBar2.Scroll
Editor.SelectionHangingIndent =
Convert.ToInt32(Editor.Width *
(TrackBar2.Value / TrackBar2.Maximum) -
Editor.SelectionIndent)
End Sub
Enter a few lines of text in the control, select one or more paragraphs, and check out the
operation of the two sliders.
The Scroll events of the two TrackBar controls adjust the text’s indentation. The opposite
action must take place when the user rests the pointer on another paragraph: The sliders’
positions must be adjusted to re?ect the indentation of the selected paragraph. The selection of a
new paragraph is signaled to the application by the SelectionChanged event. The statements of
Listing 8.8, which are executed from within the SelectionChanged event, adjust the two slider
controls to re?ect the indentation of the text.
Listing 8.8: Setting the Slider Controls
Private Sub Editor SelectionChanged(...)
Handles Editor.SelectionChanged
If Editor.SelectionIndent = Nothing Then
TrackBar1.Value = TrackBar1.Minimum
TrackBar2.Value = TrackBar2.Minimum
Else
TrackBar1.Value = Convert.ToInt32(
Editor.SelectionIndent *
TrackBar1.Maximum / Editor.Width)
TrackBar2.Value = Convert.ToInt32(
(Editor.SelectionHangingIndent /
Editor.Width) *
TrackBar2.Maximum + TrackBar1.Value)
End If
End Sub
If the user selects multiple paragraphs with different indentations, the SelectionIndent
property returns Nothing. The code examines the value of this property and, if it’s Nothing,
it moves both controls to the left edge. This way, the user can slide the controls and set the
indentations for multiple paragraphs. Some applications make the handles gray to indicate
that the selected text doesn’t have uniform indentation, but unfortunately you can’t gray thePetroutsos V1 c08.tex Page 296 01/28/2008 1:24pm
296 CHAPTER 8 MORE WINDOWS CONTROLS
sliders and keep them enabled. Of course, you can always design a custom control. This wouldn’t
be a bad idea, especially if you consider that the TrackBar controls are too tall for this type of
interface and can’t be made very narrow (as a result, the interface of the RTFPad application isn’t
very elegant).
The File Menu
The RTFPad application’s File menu contains the usual Open, Save, and Save As commands,
which are implemented with the control’s LoadFile and SaveFile methods. Listing 8.9 shows the
implementation of the Open command in the File menu.
Listing 8.9: The Open Command
Private Sub OpenToolStripMenuItem Click(...)
Handles OpenToolStripMenuItem.Click
If DiscardChanges() Then
OpenFileDialog1.Filter =
”RTF Files|*.RTF|DOC Files|*.DOC|”&
”Text Files|*.TXT|All Files|*.*”
If OpenFileDialog1.ShowDialog() =
DialogResult.OK Then
fName = OpenFileDialog1.FileName
Editor.LoadFile(fName)
Editor.Modified = False
End If
End If
End Sub
The fName variable is declared on the form’s level and holds the name of the currently open
?le. This variable is set every time a new ?le is successfully opened and it’s used by the Save
command to automatically save the open ?le, without prompting the user for a ?lename.
DiscardChanges() is a function that returns a Boolean value, depending on whether the
control’s contents can be discarded. The function examines the Editor control’s Modified
property. If True, it prompts users as to whether they want to discard the edits. Depending
on the value of the Modified property and the user response, the function returns a Boolean
value. If the DiscardChanges() function returns True, the program goes on and opens a new doc-
ument. If the function returns False, the program aborts the operation to give the user a chance to
save the document. Listing 8.10 shows the DiscardChanges() function.
Listing 8.10: The DiscardChanges() Function
Function DiscardChanges() As Boolean
If Editor.Modified Then
Dim reply As MsgBoxResult
reply = MsgBox(
”Text hasn’t been saved. Discard changes?”,
MsgBoxStyle.YesNo)Petroutsos V1 c08.tex Page 297 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 297
If reply = MsgBoxResult.No Then
Return False
Else
Return True
End If
Else
Return True
End If
End Function
The Modified property becomes True after typing the ?rst character and isn’t reset back to
False. The RichTextBox control doesn’t handle this property very intelligently and doesn’t reset it
to False even after saving the control’s contents to a ?le. The application’s code sets the
Editor.Modified property to False after creating a new document, as well as after saving the
current document.
The Save As command (see Listing 8.11) prompts the user for a ?lename and then stores the
Editor control’s contents to the speci?ed ?le. It also sets the fName variable to the ?le’s path, so
that the Save command can use it.
Listing 8.11: TheSaveAsCommand
Private Sub SaveAsToolStripMenuItem Click(...)
Handles SaveAsToolStripMenuItem.Click
SaveFileDialog1.Filter =
”RTF Files|*.RTF|DOC Files” &
”|*.DOC|Text Files|*.TXT|All Files|*.*”
SaveFileDialog1.DefaultExt = ”RTF”
If SaveFileDialog1.ShowDialog() = DialogResult.OK Then
fName = SaveFileDialog1.FileName
Editor.SaveFile(fName)
Editor.Modified = False
End If
End Sub
The Save command’s code is similar, only it doesn’t prompt the user for a ?lename. It calls the
SaveFile method, passing the fName variable as an argument. If the fName variable has no value
(in other words, if a user attempts to save a new document by using the Save command), the
code activates the event handler of the Save As command automatically and resets the control’s
Modified property to False. Listing 8.12 shows the code behind the Save command.
Listing 8.12: TheSaveCommand
Private Sub SaveToolStripMenuItem Click(...)
Handles SaveToolStripMenuItem.Click
If fName <> ”” Then
Editor.SaveFile(fName)
Editor.Modified = FalsePetroutsos V1 c08.tex Page 298 01/28/2008 1:24pm
298 CHAPTER 8 MORE WINDOWS CONTROLS
Else
SaveAsToolStripMenuItem Click(sender, e)
End If
End Sub
The Edit Menu
The Edit menu contains the usual commands for exchanging data through the Clipboard (Copy,
Cut, Paste), Undo/Redo commands, and a Find command to invoke the Search & Replace dialog
box. All the commands are almost trivial, thanks to the functionality built into the control. The
basic Cut, Copy, and Paste commands call the RichTextBox control’s Copy, Cut,and Paste
methods to exchange data through the Clipboard. Listing 8.13 shows the implementation of the
Paste command.
Listing 8.13: The Paste Command
Private Sub PasteToolStripMenuItem Click(...)
Handles PasteToolStripMenuItem.Click
Try
Editor.Paste()
Catch exc As Exception
MsgBox(
”Can’t paste current clipboard’s contents”)
End Try
End Sub
As you may recall from the discussion of the Paste command, we can’t use the CanPaste
method because it’s not trivial; you have to handle each data type differently. By using an
exception handler, we allow the user to paste all types of data that the RichTextBox control can
accept, and display a message when an error occurs.
The Undo and Redo commands of the Edit menu are coded as follows. First, we display the
name of the action to be undone or redone in the Edit menu. When the Edit menu is selected,
the DropDownOpened event is ?red. This event takes place before the Click event, so I inserted a
few lines of code that read the name of the most recent action that can be undone or redone and
print it next to the Undo or Redo command’s caption. If there’s no such action, the program will
disable the corresponding command. Listing 8.14 is the code that’s executed when the Edit menu
is dropped. 

Make a Free Website with Yola.