Jeff Webb, Mike McKelvy, Ronald Martinsen, Taylor Maxwell, Michael Regelski September 1995 Special Edition Using Visual Basic 4 - Chapter 30 1-56529-998-1 Computer Programming computer programming Visual Basic OLE database applications ODBC VB VBA API This book is an all-in-one reference that provides extensive coverage of every topic and technique for creating optimized and customized applications with Visual Basic.

Chapter 30

Advanced Control Techniques


The Visual Basic development environment provides a set of tools for creating Windows applications. Unfortunately, simply having all these controls does not help you to learn to use them effectively and efficiently. Fortunately, sources exist that can help you learn these skills.

Learning how to use a control comes with experience, but you do not have to always wait for that to come. This kind of experience is the usual trial-and-error process of finding out what works for particular situations. There is no substitute for this kind of learning experience. But you don't always have the time and must often depend on the past experience of other programmers.

You cannot expect to memorize every solution that you have ever written. If you tried to do so, you would inevitably forget techniques and constantly find yourself reinventing the wheel. The obvious solution to this problem is to create a central repository of code that you can access as needed. Included with this book are the elements that can serve as the basis of a central repository of code�the basic code for creating new Visual Basic applications. Subsequent chapters explain the different elements of this code.

In this chapter, you learn how to do the following:

Be careful how you reference DOS files in Visual Basic. The path in which you place your files might not be the same path that the application's users have on their own machines. Hard-coded paths to graphics files, data controls, and Help files will change. The solution to this problem is to reference a global variable that contains the path in all your code.

Accessing Crystal Reports

Designing reports in Visual Basic used to be a pain. Providing reports used to involve dealing with the small number of methods and properties belonging to the limited Printer object. The results were neither professional looking nor easy to work with. Now you can design applications that generate sophisticated, professional-looking reports that you can integrate into your Visual Basic applications.

The process of adding a Crystal Report to an application is easy. Simply place the Crystal control in your project and draw it any place on one of your screens. For the sake of simplicity, you get better results when you put the control on the form where the user will invoke the report. Then you need not reference the control's form as well the control and the properties or methods.

To print an existing report from an application, follow these steps:

  1. Create a new project.
  2. Change the name property of Form1 to Print.
  3. Add the Form_Load event of Print.
  4. Save the project under the name 30PROJ01.PRJ.
  5. Add the CRYSTAL32.OCX to the project.
  6. Add a command button to the form.
  7. Add the Command1_Click event to the command button.
  8. Add to the form the code in listing 30.1.
  9. Add the Dim statement for DatabasePath$ and DatabaseName$ to the form's General Declarations section.
  10. Get the SHELL.MDB and SHELL.RPT files from the shell code and copy them to the project directory.

Listing 30.1 The 30Proj01 Code Showing Crystal Report

General Declarations
Dim DatabasePath$
Dim DatabaseName$
Form_Load ()
DatabasePath$ = app.Path
DatabaseName$ = AddPath$(DatabasePath$, "SHELL.MDB" )
End Sub
Function AddPath (Path$, DatabaseName$) As String
If Mid$(Path$, Len(Path$), 1) <> "\" Then
Path$ = Path$ & "\"
End If
AddPath$ = Path$ & DatabaseName$
End Function
Sub Command1_CLICK ()
Screen.MousePointer = HOUGLASS
rptPrint.Destination = 0
rptPrint.DataFiles(0) = DatabaseName$
rptPrint.ReportFileName = AddPath(DatabasePath$, "SHELL.RPT")
rptPrint.Action = 1
Screen.MousePointer = DEFAULT
End Sub

Notice that the Crystal Report displays on top of the calling form. The resulting crystal window, which you can minimize on the screen, displays like a normal window (see fig. 30.1). Also, notice how the AddPath function provides a path to use to call the Crystal form. This is a very useful function to add to your projects.

Fig. 30.1

An example of a Crystal Report displayed as a normal window.

Referencing Forms in Visual Basic

Visual Basic provides many ways to reference forms. In Visual Basic 1.0, you can make references to a text box on form1 by using the following syntax:

Form1.Text1.Text

Unfortunately, this syntax can confuse those reading the code if the names are unclear, as in Change.ChangeName.Text. Visual Basic 2.0 introduces the solution of using an exclamation point to indicate that you are referencing a form rather than a control, as in Form1!Text1.Text. To make your code more readable, use the exclamation point.

Creating MDI Children

By definition, MDI applications display everything but dialog boxes within the confines of the contain MDI form. Making a form into an MDI child is a simple process of changing the MDIChild property to True. The companion CD's 30Proj02 project provides a simple demonstration of how to create multiple child windows.

Make sure to format your code properly to make it readable. Don't forget to indent your code by pressing the Tab key before each new line of code. Code set within If-Then, Select Case, Do-While, and similar contructions should be further indented one tab stop. Nonindented code is difficult to read, even for the programmer who originally wrote it.

Understanding Form Arrays

To begin understanding how the following code listing works, start by studying the code in listing 30.2.

Listing 30.2 The mnuFileOpen_Click Event

Sub mnuFileOpen_Click()
Screen.MousePointer = vbHourglass
ReDim Preview(gNbrPreview% + 1)
Load Preview(gNbrPreview%)
Preview(gNbrPreview%).Caption = "Preview"
Screen.MousePointer = vbDefault
End Sub

This event increments the size of the Preview form array by one and then loads the new member of this form array. Notice that the gNbrPreview variable stores the size of the form array. Each time that the user chooses the Open menu choice, this code generates a new member of the form array.

ReDim Preview(gNbrPreview% + 1)
Load Preview(gNbrPreview%)
Preview(gNbrPreview%).Caption = "SHELL REPORT"

To find out where the form arrays Preview and gNbrPreview are referenced, look for them in the GLOBAL.BAS under the comment 'Print Preview Flag":

'Print Preview Flag
Public Preview() As New frmPrintPreview
Public gNbrPreview%

Keep four things in mind about this code:

Visual Basic ships with the default tab size set to 4. Code that uses many nested statements is very difficult to read. You can change this default size to 2 and make this code easier to read by choosing Tools, Options. Under the Editor tab, change the Tab Width value to 2. This setting displays more of the code on the screen.

Optimizing Control Arrays

As the user continues to learn more, your programs must become increasingly more configurable at run time. To accommodate user sophistication and to address security concerns, your screens' appearance should change based on different conditions that occur at run time. Controls must be enabled and disabled based on specific criteria. Changing the Enabled property of affected controls has this effect. Unfortunately, however, this solution is not always acceptable, because frustrated users want to know why they cannot access something on the screen. Another possibility is to use the Visible property to make a control appear and disappear at run time. If your application has very few controls, this solution is quite acceptable.

Follow these steps to make a control array:

  1. Create a control and draw it on the form.
  2. Make the control's Visible property False.
  3. Change the Index property to False.
  4. At run time, load the control by using the load method and then change the Visible property to True.

Creating Killer Toolbars

Toolbars are an integral part of the current Windows standards (see fig. 30.2). Applications that don't have toolbars are becoming the exception rather than the rule. The reason for this is obvious: Toolbars provide a convenient place on the screen for users to look for frequently used functions. Because the toolbar is normally on the top of the screen, it provides a familiar place for the user to look on almost any application.

Fig. 30.2

The 30Proj03 application showing the standard colored toolbar.

The earlier versions of applications used monochrome toolbars. Almost all of the current applications have since switched to color toolbars. The bitmaps that ship with Visual Basic are all monochrome. Don't worry, there is an easy method that you can use to create color bitmaps from a monochrome bitmap.

Sheridan Software ships in its Designer Widgets package a Visual Basic .OCX that automates the toolbar-creation process. This product provides another method for creating Visual Basic applications with toolbars.

Color Bitmaps

Standard Visual Basic toolbars consist of bitmaps rather than icons. The standard size of a toolbar icon is 24 pixels wide by 22 pixels high. Around all toolbar icons is a bevel edge that is exactly the same for each icon. Only the interior differs from icon to icon. This interior is 19 pixels wide by 17 pixels high. This is the part that you have to create to make icons in the Visual Basic toolbar custom control.

You can find bitmaps for your toolbars in applications that you already own. The most common application in which to find your icons is Word for Windows. To create your icons from Word for Windows, follow these steps:

  1. Display Word for Windows on the screen.
  2. Press Print Screen.
  3. Open Paint and paste in the resulting screen as shown in figure 30.3.

Fig. 30.3

A Word for Windows screen pasted into Paint.

  1. Choose View, Zoom, Large Size, as shown in figure 30.4, to display size the display to Large Size.

Fig. 30.4

The View, Zoom, Large Size menu choice in Paint.

  1. Select the interior of your chosen icon, as shown in figure 30.5.

Fig. 30.5

The interior of the Save icon selected in Paint.

  1. Choose Edit, Copy To to save the interior of the bitmap to a bitmap file.

You can join the Microsoft Developer's Network by calling (800) 759-5474 department 1016 (24 hours a day). The subscription price is $195 for Level 1 membership, which includes four CD-ROMs (one per quarter). Level 2 membership, which costs $495, also includes at least four quarterly releases of the Development Platform (which includes the latest operating systems, and SDKs). Either membership is worth the money depending on your budget and individual needs.

The Image List

For each icon that you want to add your toolbar, you must repeat the icon-creation process. After you have all your icons, you can begin assembling your toolbar in a new project. The steps for this process are as follows:

  1. Create a new project and rename it as 30Proj03.
  2. Add an MDI form and save it as frmMain with a DOS name of mainMDI.
  3. Add a toolbar to frmMain and rename it as ToolBar.
  4. Add a status bar to frmMain and rename it as StatusBar.
  5. Add a picture box to frmMain and rename it as picToolBar. Change its Visible property to False.
  6. Add an image list to picToolBar and rename it as ImageList.
  7. Add the icons to ImageList using the Custom dialog box as shown in figure 30.6. You can access this by double-clicking on the Custom property on the Properties dialog box.

Fig. 30.6

The Custom dialog box for ImageList.

The Toolbar Setup

The next step in the process is to create the code that displays your toolbar buttons. The gSetupToolBar procedure (listing 30.3) is an example of toolbar-creation code for some of the most often used icons on a toolbar. Each button appears with the creation of a btn object which you then can use to create each button with the add method. The tbrDefault and tbrSeparator constants are built in, so you need not define them. The lines that use tbrSeparator provide spaces between each icon. Those icons with tbrDefault are the icons with the last number argument identifying the icon to display.

Listing 30.3 The gSetupToolBar Procedure

Sub gSetupToolBar(ToolBar As ToolBar, ImageList As ImageList)
Dim btn As Button
Set ToolBar.ImageList = ImageList
Set btn = ToolBar.Buttons.Add(, "Sep1", , tbrSeparator, 0)
Set btn = ToolBar.Buttons.Add(, "New", , tbrDefault, 1)
btn.ToolTipText = "New"
btn.Description = "New Object"
Set btn = ToolBar.Buttons.Add(, "Open", , tbrDefault, 2)
btn.ToolTipText = "Open"
btn.Description = "Open Object"
Set btn = ToolBar.Buttons.Add(, "Sep2", , tbrSeparator, 0)
Set btn = ToolBar.Buttons.Add(, "Save", , tbrDefault, 3)
btn.ToolTipText = "Save"
btn.Description = "Open Object"
Set btn = ToolBar.Buttons.Add(, "Sep3", , tbrSeparator, 0)
Set btn = ToolBar.Buttons.Add(, "Print", , tbrDefault, 4)
btn.ToolTipText = "Print"
btn.Description = "Print Object"
Set btn = ToolBar.Buttons.Add(, "Preview", , tbrDefault, 5)
btn.ToolTipText = "Print Preview"
btn.Description = "Print Preview"
Set btn = ToolBar.Buttons.Add(, "Sep4", , tbrSeparator, 0)
Set btn = ToolBar.Buttons.Add(, "Cut", , tbrDefault, 6)
btn.ToolTipText = "Cut"
btn.Description = "Cut Text"
Set btn = ToolBar.Buttons.Add(, "Copy", , tbrDefault, 7)
btn.ToolTipText = "Copy"
btn.Description = "Copy Text"
Set btn = ToolBar.Buttons.Add(, "Paste", , tbrDefault, 8)
btn.ToolTipText = "Paste"
btn.Description = "Paste Text"
Set btn = ToolBar.Buttons.Add(, "Sep5", , tbrSeparator, 0)
Set btn = ToolBar.Buttons.Add(, "Help", , tbrDefault, 9)
btn.ToolTipText = "Help"
btn.Description = "Context Help"
End Sub

Tooltips and Status Bar Text

Help text for tooltips and status bars is becoming a standard feature in Windows applications. The toolbar control supports tooltips and status bar text. To make the tooltips feature work properly, you need only set the ToolTiptext property of each button to the appropriate text. To make the status bar text work, you must add the ToolBar_MouseMove code to your project. This subroutine displays each button's Description property in the status bar when the user moves the mouse over a button.

Private Sub ToolBar_MouseMove(Button As Integer, _
Shift As Integer, x As Single, y As Single)
StatusBar.Panels(1).TEXT = Button.Description
End Sub

The ButtonClick event (listing 30.4) enables the program to find which button the user clicked and which action to take. In the example program, you use the buttons' Key property to identify which button the user clicked. Any unique property would work just as well.

Listing 30.4 The ButtonClick Event

Private Sub ToolBar_ButtonClick(ByVal Button As Button)
Select Case Button.KEY
Case "New"
frmChild.Show
frmChild.Caption = "New"
Case "Open"
frmChild.Show
frmChild.Caption = "Open"
Case "Save"
StatusBar.Panels(1).TEXT = "Object Saved!"
Case "Print"
StatusBar.Panels(1).TEXT = "Object Printed!"
Case "Preview"
Case "Cut"
Case "Copy"
Case "Paste"
Case "Help"
End Select
End Sub

Every program has bugs or sometimes just methods of doing things that you do not like. Some programmers prefer to refer to most bugs as features (except for the obvious kinds of bugs, such as those that cause catastrophic General Protection Faults and so on). By thinking of these features in a more positive light, you can begin to take advantage of opportunities that you might not have ever previously considered. As you encounter these features, try to keep this in mind and learn to take advantage of them.

Using the Status Bar

Status bars are another popular element of today's Windows applications. A status bar's main function is to tell the user what is going on. When the user places the mouse over a control for a few seconds, the status bar should give some information about that control. During a very long process, the status bar gives messages about the progress that the program is making. Such messages might use text, graphics, a percentage indicator, or a combination of the three. One of the greatest benefits of a status bar is to give information that indicates to the user that the application has not locked up. Nothing is more frustrating than an application that simply displays an hourglass while you sit and wait. A status bar fixes this problem by letting the user know that something is happening.

You can shorten If-Then constructions that check whether a variable or property is True. If you omit the = TRUE part of such a construction, Visual Basic assumes it. This technique is very useful for simplifying code on the screen for better readability and faster execution.

Styles

There are as many different types of status bars as there are applications. Status bars are by definition tied to the type of functions performed by the application to which they belong. Microsoft Word for Windows 6.0 displays such information as the page number, the position on the screen, and the time (see fig. 30.7). Microsoft Excel 5.0 shows a "Ready" message to indicate that no processing is taking place (see fig. 30.8). There are also indicators for the Caps Lock, Insert, Scroll Lock, and Number Lock keys. When you design your applications, think carefully and include status bar elements that make sense for that particular program.

Fig. 30.7

Microsoft Word for Windows 6.0 with its status bar.

Fig 30.8

Microsoft Excel 5.0 with its status bar.

Status Bar Controls

Visual Basic 4.0 provides a status bar control. In earlier versions of Visual Basic, you had to create status bar with code and controls. Now the process of creating a very useful status bar is a simple one:

  1. Open the existing main MDI form.
  2. Add a status bar control and rename it as StatusBar.
  3. Use the Custom property to display the Custom dialog box.
  4. Add a panel to the status bar with the style set to Text.
  5. Add a panel to the status bar with the style set to Date.
  6. Add a panel to the status bar with the style set to Time.
  7. Change the AutoSize property of the first panel to Spring.

Using MaskEdBox Text Boxes

Databases are only as useful as the data that they contain. One of the most important aspects of a useful stable database is consistent data-entry practices. A phone number entered inconsistently as either (555) 555-9821 or 555-555-9821 creates problems. If the user doesn't enter a customer's phone numbers using a consistent format, making meaningful searches against this information becomes difficult.

There are two ways to filter the type of information that the user enters. The first method involves the use of the Mask property. Set the Mask property to (###) ###-####, and the user can enter the telephone number only with this format. This method is terrific for quick data-entry that does not redisplay the information after the user enters it. One problem with the method, however, occurs when the contents of the maskEdBox control are blank and the control is bound to a database table. Under these conditions, a blank entry triggers a Visual Basic error.

The second, more useful method of filtering the user's entered data involves the use of the Format property and the KeyPress event. Set the Format property to (###) ###-#### and add the code in listing 30.5 to the KeyPress event of the maskEdBox control. This method prevents the user from entering anything but numbers and then displays the phone number as a valid number afterward.

Listing 30.5 The 30Proj04 Code Showing MaskEdBox Enhancements

Private Sub MaskEdBox1_KeyPress(KeyAscii As Integer)
If KeyAscii = 8 Or KeyAscii > 47 And KeyAscii < 58 Then
' Show number
Else
KeyAscii = 0
End If
End Sub

To implement the second filtering method, follow these steps:

  1. Create a new project and save it as 30Proj02.
  2. Add MSMASK32.OCX to the project.
  3. Place a MaskEdBox on Form1.
  4. Change the Mask property to (###) ###-####.
  5. Add the listing 30.5 code to the KeyPress event of MaskEdBox1.
  6. Add a text box to Form1 and change its Text property to blank.
  7. Adjust form to look like the one shown in figure 30.9.

Fig. 30.9

An example of what your 30Proj04 should look like showing MaskEdBox enhancements.

Outline Enhancements

Many kinds of information take the form of a one-to-many relationship with a single header and many lines of detail. For sales invoices, this relationship is represented with the customer's information as the header and the individual purchases as the detail lines. Visual Basic provides an excellent custom control for displaying this relationship graphically, MSOUTL32.OCX. By displaying information in this fashion, you give the user a high-level view of the information.

The outline control works quite well with some minor code enhancements. Listing 30.6 shows these codes enhancements. Unless you use one of the Outline style properties that incorporate the + and � signs, the outline control does not expand and collapse automatically. Many users prefer the Explorer's look and feel (see fig. 30.10), which the outline control supports. The code in listing 30.6 shows how simply you can add this capability.

Fig. 30.10

The Microsoft Explorer.

Listing 30.6 The 30Proj05 Code Showing Outline Control Enhancements

Private Sub Form_Load()
Outline1.AddItem "Invoice 308912"
Outline1.PictureType(0) = 0
Outline1.Indent(0) = 1
Outline1.AddItem "Alaskan Crab"
Outline1.PictureType(1) = 2
Outline1.Indent(1) = 2
Outline1.AddItem "Ripple"
Outline1.PictureType(2) = 2
Outline1.Indent(2) = 2
Outline1.AddItem "Tossed Salad"
Outline1.PictureType(3) = 2
Outline1.Indent(3) = 2
Outline1.AddItem "Baked Potato"
Outline1.PictureType(4) = 2
Outline1.Indent(4) = 2
End Sub
Private Sub Outline1_Click()
Dim Index%
If Outline1.HasSubItems(Index%) Then
Index% = Outline1.ListIndex
Outline1.Expand(Index%) = Not Outline1.Expand(Index%)
End If
End Sub
Private Sub Outline1_PictureClick(ListIndex As Integer)
Outline1.ListIndex = ListIndex%
Call Outline1_Click
End Sub

To create the 30Proj05 application, which demonstrates how to add outline control enhancements, follow these steps:

  1. Create a new project and save it as 30Proj05.
  2. Add MSOUTL32.OCX to the project.
  3. Place an outline on Form1.
  4. Change the Style property to 5.
  5. Add the listing 30.5 code to Form1.

The key to making the outline control expand and collapse when the user clicks on it lies in the Outline1_Click event. This code uses the HasSubItems property of the outline control to determine whether the current item has subitems. If the currently selected item has subitems, the routine toggles the Expand property by using Not. Figure 30.11 shows what the outline should look like expanded, and figure 30.12 displays the outline collapsed.

Fig. 30.11

An example of 30Proj03 when expanded.

Fig. 30.12

An example of 30Proj03 when collapsed.

There is one last piece to finish for 30proj03. If the user clicks on the picture of the folder, the outline neither collapses nor expands. There is a separate event for this. To ensure that the same thing happens for clicking on the picture as well as the text, add the code found in Outline1_PictureClick. This code first changes the ListIndex property to the index value of the picture that the user clicked. Then the routine triggers the Outline1_Click event by using the Call method.

Notice the use of the Call method in the Outline1_Click found in listing 30.5. You do not need this method to make the code function properly, but it serves to clarify that the routine triggers an external subroutine. This makes the Outline1_PictureClick event easier to read.

From Here...

Controls are the elements of the screens that your applications' users see. A control's behavior affects how useful your applications are to users. Adding outlines to your applications can give them a graphical view of complex information that users can more easily understand. By preventing the user from entering inappropriate values into a screen, you make your applications less error-prone. Toolbars give users a quick way to access your application's often-used functions. All these control techniques help produce better and more useable applications.

For more information about related topics, see the following chapters:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.