Visual Basic Expert Solutions

book coverQUE

Chapter 16

Graphics: Data Analysis

By Michael McKelvy


What do you think of when someone mentions graphics? Do you think of the artistic creations of a graphic designer? Or are you more business-minded and conjure up images of the last sales presentation that you attended? Maybe if you're a very technically-oriented person, you think of computer aided design (CAD) or intricate data charts. Graphics encompass a very wide range of images and applications.

Put in simplest terms, graphics is the placement of lines, circles, points, and text on a screen in a specific pattern. These objects can be different sizes, shapes, and colors. The purpose of this chapter is to illustrate how to control the placement and characteristics of these objects to suit the purposes of your applications.

The design and use of graphics is a large and complex subject. There are a number of books that are devoted specifically to various types of graphics. Obviously, this single chapter cannot cover all the bases. Instead, this chapter concentrates on three specific areas of using graphics. In this chapter, you will learn how to

From the material presented in this chapter, you will be able to create graphics to suit many of your needs. Then, combined with some imagination and experimentation, you can create even more advanced graphics applications.

Enhancing the User Interface

A typical user interface for an application consists of labels, text boxes, command buttons, and perhaps a few other controls for specific pieces of data. The interface may also contain a menu. This will provide the user access to the functions of the application and present her with specific data, but it can be quite boring. Graphics can be used to enhance the user interface by:

These enhancements can be accomplished through the use of the Line and Shape controls, color, pictures, and drawing methods.

Using the Line and Shape Controls

The Line and Shape controls provide the easiest means to add a graphic element to a form. The controls are drawn on the form at designtime and placed where you need them. During the execution of a program, these controls can be hidden or moved, or their colors can be changed by setting the appropriate property values in your code.

As you would guess by its name, the Line control places a line on the form. You can control the width of the line, the line style, the color, and the position of the terminal points of the line through the control's properties. Figure 16.1 shows several Line controls drawn on a form using the various styles.

Fig. 16.1 The Line control lets you place lines of different styles on a form.

Note: If you set the BorderWidth property of the Line control greater than one, the BorderStyle property will have no effect.

The Line control can be used on a form to separate areas of the form from one another. For instance, you might want to separate the data display portion of a form from the command buttons, as shown in figure 16.2. Or you may want to separate the information on the form into distinct groups. If you are presenting a lot of information on a form, this is a good way of enabling the user to focus on one group of information at a time.

Fig. 16.2 The Line control can be used to separate information on a form into distinct groupings.

The Shape control provides another simple means of placing graphics elements on a form. The Shape control can be used to create the following six shapes:

These six shapes are shown in figure 16.3.

Fig. 16.3 The Shape control can be used to create any of these six shapes on a form.

For any of these shapes, you can set the BorderStyle to any of the six line styles shown in figure 16.1 for the Line control. You can also set a fill pattern and fill color for the shapes. The Shape control can be used to enclose various areas of a form and other controls as shown in figure 16.4.

Fig. 16.4 The Shape control can be used to indicate groups of controls or information.

Caution: A Shape control cannot be used as a container for other controls like the Frame and Picture Box controls can. To explain further, if a Frame is placed on a form, and then other controls are placed within the frame's borders, the frame and the controls then act as one unit. When the frame is moved, the other controls are moved with it, maintaining their relative position within the frame. If a frame is hidden, all controls in the frame are also hidden. These characteristics are also true of a Picture Box. Not so with a Shape control. Any controls placed within the border of a Shape control are independent of the shape. If the shape is moved or hidden, the other controls remain unaffected.

The Shape control can also be used to highlight information in a different manner. Suppose you want to draw your user's attention to the fact that they entered an incorrect value in a field. One way to do this would be to draw a shape around the text box where the data would be entered, then set its Visible property to False. Then in the Change event of the text box, place code to either show or hide the shape depending on the value of the text. The following code shows a shape when the text's value is less than zero, and hides the shape otherwise.

If Text1.Text < 0 Then
Shape1.Visible = True
Else
Shape1.Visible = False
End If

The change event is triggered whenever the user starts typing in the text box. Therefore, if they start to enter an incorrect value, the shape immediately appears. Then when an acceptable value is entered, the shape disappears. A good shape for this is an oval with a BorderWidth of three and a red BorderColor.

Pictures on the Form

Another way to enhance your screens with graphics is to place pictures on the screen. A picture is a bitmap file that can contain art, flow diagrams, or photos. The picture can be purely decorative, or it can be used to communicate specific information. You have probably seen pictures used for information in the setup screens of many programs. These pictures tell you about the features and benefits of the program while the installation is running.

There are several ways to add a picture to a form. The picture can be placed on the form itself, or it can be placed in a Picture Box or Image control on the form. There are advantages and drawbacks to each of these methods, as will be addressed in the following paragraphs.

Loading a Picture on the Form

The simplest way to add a picture to your screen is to add it to the form itself. You can do this at designtime by setting the Picture property from the properties dialog box. To load a picture, select the Picture property, and then press the ellipsis button at the far right of the line. This will call up the Load Picture dialog box shown in figure 16.5. From this box, you can select the file containing the desired picture.

Fig. 16.5 You can select a picture to load on your form from the Load Picture dialog box.

When the picture is loaded on the form, it places the entire picture on the form starting in the upper left corner of the form. If the picture is smaller than the form, space will be left below and to the right of the picture. If the picture is larger than the form, the entire picture will still be loaded, but only part of it will be visible. As the form is resized, the amount of the picture shown will change.

Note: The picture is always placed starting in the upper left corner. It cannot be placed anywhere else on the form. If you want a small picture in another area of the form, you must use either a picture or Image control.

When a picture is loaded on the form, it provides a background for the form. Any other controls added to the form appear on top of the picture. With the exception of the label and shape controls, the picture will not show through the background of the control. The label and shape controls will allow the picture to show through if the BackStyle property of the control is set to Transparent. Figure 16.6 shows a simple cross-hatch design picture on a form with other controls placed on it. Notice the background of the controls without the design.

Fig. 16.6 Other controls placed on a form do not allow the picture to show through.

You can also add a picture to a form at runtime. This is done by setting the Picture property of the form with the LoadPicture function as shown in the following code:

Form1.Picture = LoadPicture("C:\MYPICT.BMP")

You can also remove the picture from the form by specifying a null argument for the LoadPicture function:

Form1.Picture = LoadPicture("")

The key advantage to placing your picture directly on the form is that this method uses fewer system resources than placing the picture in a picture or Image control. Another benefit of placing the picture on the form is that you can use drawing methods to annotate the picture if necessary. This capability is available with the Picture Box control as well, but is not available with the Image control.

Placing the picture directly on the form does, however, have several drawbacks, such as the following:

These drawbacks can be overcome with the use of the picture or Image control. However, the added flexibility comes at the expense of system resources.

Using the Image Control

A second way of getting pictures on a form is to use an Image control. The Image control provides a frame for a bitmap or other picture on a form. The control can be placed anywhere on the form and can be drawn to any desired size. You assign a picture to the Image control by setting the Picture property of the control. This can be done either at designtime using the properties dialog or at runtime using an assignment statement. Assigning the picture is done the same way as placing a picture directly on the form.

One other property of the Image control is very important to the appearance of any pictures you may use. This is the Stretch property. The Stretch property determines whether the Image control will be sized to fit the picture, or the picture will be sized to fit the control as drawn. If the Stretch property is set to False (the default), the Image control will automatically be resized to fit the picture you assign to it. If the Stretch property is set to True, the picture will automatically be resized so that the entire picture will fit within the current boundaries of the Image control. This will cause the overall size of the picture to change and can potentially change the aspect ratio of the picture (the ratio of vertical to horizontal size). Figure 16.7 shows the same picture in two Image controls, one with the Stretch property set to True and the other with the property set to False.

Fig. 16.7 The Stretch property affects the appearance of the picture in an Image control.

Note: If you are showing pictures of different sizes in your application, you might want to set the Stretch property to True. Otherwise, the size of the Image control will change with each picture, and may cause the Image control to overlap other controls on the form. The Image control is anchored at its top left corner, so only objects to the right and below the control would be affected. If the appearance of the pictures is unsuitable due to stretching, you may want to use the Picture Box control instead of the Image control.

The advantages of using the Image control over placing a picture directly on the form are as follows:

Using the Image control does have a few drawbacks:

Using the Picture Box Control

The third and most flexible way to place a picture on a form is with the Picture Box control. This method uses the most resources of the three. Loading a picture in a Picture Box control is accomplished the same way as loading a picture on a form or into an Image control. The picture can either be loaded at designtime or at runtime.

Using the Picture Box control enables you to place a picture anywhere on a form and place multiple pictures on the form. It also enables you to control the size of the picture. The default behavior of the Picture Box control is to show only as much of a picture as will fit in the current boundaries of the control. If the picture is larger than the control, the upper left corner of the picture will be shown (though the entire picture is available if the control is resized). If the picture is smaller than the control, there will be space shown around the edges of the picture. This default behavior occurs with the AutoSize property set to False. Setting the AutoSize property to True will cause the Picture Box control to be resized to fit the current picture. As with the Image control, the top left corner of the control is anchored in place, and resizing occurs to the right and down. The Picture Box control always preserves the aspect ratio of the picture being shown. Figure 16.8 shows the same picture in each of two Picture Box controls, one with the AutoSize property set to False, the other with the AutoSize property set to True.

Fig. 16.8 The AutoSize property determines whether or not the Picture Box control will change size to fit the picture being displayed.

The Picture Box control also provides you with other capabilities in handling pictures. Like the picture on a form, you can use the drawing methods to make changes to the picture. Like the Image control, you can hide the picture or move it on the screen. But the Picture Box control also has some added benefits. The Picture Box control can be used as a container for other controls. This means that any controls placed on the Picture Box control are treated as a unit with the Picture Box control. If the Picture Box control is hidden or moved, the other controls on it are also hidden or moved. This allows the Picture Box control to be used to display multiple views or portions of data on a single form. This was discussed in detail in Chapter 4, "Advanced Database Front Ends."

Creating Invisible Buttons on your Form

As useful as it is to be able to display a picture on a form, you probably would like to be able to do more with it. One way to take advantage of the visual information displayed in a picture is to use it to control part of your application's interface.

As an example, consider a flowchart displayed on a form that shows the various input files and calculations in a complex application. A flowchart of this type is shown in figure 16.9.

Fig. 16.9 A flowchart can be used to display information about an application.

The flowchart helps the user understand how the information in the application is related. But suppose you could set up your application so that the user could access an input file merely by clicking on the file name in the flowchart. This can be done by superimposing Image controls over the flowchart to create invisible buttons.

To accomplish this, load a picture onto the form or into a Picture Box control. Then, anywhere that you want an invisible button, place an Image control over the region that you want to activate. Finally, place code in the Click event of each Image control to accomplish the task you want performed. The Image control is invisible because it has no border, and with no picture assigned to it, the background of the control is transparent. Figure 16.10 shows the flowchart from figure 16.9 with the invisible buttons on it. The BorderStyle of the Image controls has been set to a single line to show where the controls are located. Normally, these controls would not be seen. In an actual application, the BorderStyle would be set to none.

Fig. 16.10 Invisible buttons can make a picture part of your user interface.

This flowchart and invisible button application is contained in the file INVISBTN.MAK. When the buttons are clicked, a message box appears to tell you which button or area you pressed.

Okay, since the buttons are invisible, how do you let the user know where they are? You can use a characteristic of the mouse pointer and the MouseMove event of the Image controls to change the mouse pointer to a different picture when it is over one of the invisible buttons. This is done by setting the mouse pointer to an icon as shown in the following code.

Image1.MousePointer = 13
Image1.MouseIcon = Image2.Picture

This code changes the mouse pointer to the icon for as long as the mouse is over the Image control. When the mouse is moved off the control, the mouse pointer reverts to its original style. Figure 16.11 shows the invisible button with the changed mouse pointer over it.

Fig. 16.11 Changing the mouse pointer shows the user where the invisible buttons are located.

This invisible button concept could be used with all types of applications. For example, you could create a demonstration application for a new car. By placing invisible buttons on a picture of the car, you could allow the user to click on the parts of the car to get information about its features (for example, describe the engine capabilities if the user clicks on the hood). The invisible buttons can be made any size you want them.

Creating Toolbars with the Image Control

Another use of the Image control is the creation of Toolbars. Toolbars are a series of buttons that provide access to the functions of your application. Toolbars are used in most commercial Windows applications to provide access to the most commonly used functions.

The buttons on a Toolbar usually contain a graphics image which tells the user the function of the button. In addition, these graphics are usually set up to show several states of the button, such as disabled, enabled, or selected.

You can create a Toolbar for your application using a series of Image controls, each containing a bitmap of a button. You can use the bitmaps provided with Visual Basic (located in the \bitmaps\toolbar subdirectory under the Visual Basic main directory) or create your own with Paintbrush or another graphics package.

Creating the Toolbar

The first step in creating the Toolbar is to place a series of Image controls on the form. To do this, select the Image control from the Visual Basic Toolbar and position it on the form. Add an Image control to the form for each Toolbar button you need to make.

Note: To create a number of Image controls on the form, hold down the Ctrl key while you click on the Toolbar. This will enable you to place multiple controls on the form without having to re-select the Image control from the Toolbar each time.

Once you have placed the Image controls on the form, you will need to assign the picture of a button to each of the Image controls. The pictures can be assigned at designtime using the Load Picture dialog box described previously. As the pictures are loaded, the Image controls will automatically resized to fit the button pictures (remember the default setting of the Stretch property). You can then rearrange the buttons into their final configuration. Figure 16.12 shows a Toolbar derived from the one used in Microsoft Word.

Fig. 16.12 A Toolbar can be created using the Image controls and pictures of buttons.

To activate the buttons, you will need to place code for the desired function in the click event of each Image control. When your application is run, the user will access these functions simply by clicking on the button for the function.

Handling Multiple States of the Button

If you have worked with an application containing a Toolbar, you have probably noticed that many buttons change appearance when they are clicked. Typically, they show one image when the button is "up," then show a different image when the button is pressed. The button is pressed when the mouse button is pushed down. This activity often happens quickly, but if you press and hold the mouse button, you can see the other image.

You can enable this same functionality in your Toolbar by using multiple pictures for each Image control and placing the appropriate code in the MouseDown and MouseUp events of the control. The following discussion will demonstrate this technique for a single button of the Toolbar shown in figure 16.12.

To create this two-state button, you will need to place three Image controls on the form for each button you wish to present to the user. One Image control will be the actual button on the Toolbar. The other two Image controls will contain the pictures for the two button states, one for the "up" state and one for the "down" state. You will need to set the Visible property of these latter two Image controls to False, so that they do not show up in the actual application. Figure 16.13 shows the form as it looks in the design mode and as it looks in the run mode for a single button on a Toolbar.

Fig. 16.13a The controls containing the button images need to be hidden when the application is run.

Fig. 16.13b The controls containing the button images need to be hidden when the application is run.

Changing the state of the button requires changing the picture in the Image control presented to the user. This is done by setting the Picture property of the Image control shown to the user with the picture in one of the other Image controls. The "down" button is loaded in the MouseDown event, while the "up" button is loaded in the MouseUp event. This code is shown in Listing 16.1. Notice that the initial setting of the button is loaded in the Form_Load event.

Listing 16.1 Tool2.Frm The Button's Appearance is Changed by Setting the Picture Property of the Image Control to Different Pictures

‘***************************************************
‘The button is initially loaded with the up picture.
‘***************************************************
Private Sub Form_Load()
Image1.Picture = Image2.Picture
End Sub
‘**********************************************************
‘When the mouse button is pressed, the down button is shown
‘**********************************************************
Private Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Image1.Picture = Image3.Picture
End Sub
‘***************************************************************
‘When the mouse button is released, the up button is shown again
‘***************************************************************
Private Sub Image1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Image1.Picture = Image2.Picture
End Sub

This code and the different pictures provide the appearance that the button is actually being pressed. The up and down appearance of the button is shown in figure 16.14.

Fig. 16.14 Using a two-state button gives the user the impression of actually pressing a button on the screen.

Animating the Button

You can also use multiple pictures to animate a button. The animation can either take the form of showing multiple images after the button is pressed, or the button can be animated while it is just sitting on the form. For the animation, you will need a picture for each frame you want to display, and you will need to use the Timer control. The Timer control provides the events which cause the picture to change at a given interval. For simplicity, this discussion will only deal with the animation of a button while it sits on the form.

To set up the animation, you will need to do the following:

Figure 16.15 shows the Image controls containing the various pictures for the animation.

Fig. 16.15 Animation requires the use of several images and the Timer control.

To make the animation work, you will need to set the interval property of the timer to the desired number of milliseconds that you want to elapse between picture changes. A setting of 500 will produce a half-second delay. Each time the interval elapses, the Timer event of the Timer control will be triggered. By placing the code to change the picture in your Image control in the Timer event, the picture will change. This code is shown below.

If btnidx = 3 Then
btnidx = 0
Else
btnidx = btnidx + 1
End If
Image1.Picture = Image4(btnidx).Picture

In the preceding code, you will notice that the index of the control array is incremented each time the event is triggered. When the last index is reached, the index value is reset to zero, which is the first element of the array. The display Image control and the array index were set to their initial values using the following code placed in the Form_Load event.

Image1.Picture = Image4(0).Picture
btnidx = 0

Managing Pictures with the PicClip Control

As you might guess, loading the images for a large Toolbar can require a lot of Image controls. For instance, the control Toolbar for Visual Basic contains 24 buttons. Using one Image control to display the current button state and two controls to store the button state images would require 72 total Image controls to create the one Toolbar. This would not only clutter your design screen, but more importantly, would use a lot of system resources handling all those pictures.

One possible solution would be to load each picture from a file as needed at runtime. While this would work, it would significantly slow your operations and would require you to distribute a large number of bitmap files with your application. Also, if a user ever deleted one of the bitmap files accidentally, your application would generate an error when it tried to load the file.

Fortunately, there is a control that can help you manage the images needed for a Toolbar or for animation. This is the PicClip control. The PicClip control stores a single bitmap, and enables you to retrieve pieces of the bitmap to use in the picture property of other controls. The PicClip control is one of the custom controls and must be added to your Visual Basic Toolbar from the Tools menu. The PicClip control provides you with two methods to retrieve portions of the bitmap it contains—the random access method and the enumerated method. Both of these will be discussed in this section.

Setting up the PicClip Control

Before you can use either of the clipping methods to get pieces of a picture, you must set up the PicClip control. This is done by first drawing the control on your form. Then you assign a bitmap to the Picture property of the control using the Load Picture dialog. Figure 16.16 shows a PicClip control with a button bitmap loaded.

Fig. 16.16 The PicClip control contains a bitmap from which you can retrieve pieces of the image.

In the design mode, you can also assign the number of rows and columns to use for the enumerated access method. Most of the other properties of the PicClip control are only available at runtime. You can also load the picture and assign the number of rows and columns at runtime if you desire.

Using the Random Access Method

The random access method of retrieving pieces of the bitmap enables you to specify any size piece located anywhere on the bitmap. You retrieve the desired image by setting the ClipX, ClipY, ClipHeight, and ClipWidth properties of the PicClip control. The ClipX and ClipY properties specify the coordinates of the upper left corner of the area to be retrieved. The ClipHeight and ClipWidth properties specify the vertical and horizontal size of the area to be retrieved. After you set the position and size of the region to be retrieved, you place the image in the target control by setting the Picture property of the target control (a form, Picture Box control, or Image control) to the Clip property of the PicClip control. This process is shown in the following code, which loads a portion of a bitmap into an Image control.

PictureClip1.ClipX = 10
PictureClip1.ClipY = 20
PictureClip1.ClipWidth = 40
PictureClip1.ClipHeight = 80
Image2.Picture = PictureClip1.Clip

Caution: The size of the bitmap in the Picture Clip control is measured in pixels instead of twips as is the default for Visual Basic. Therefore, you must use caution in specifying the starting point and size of the region of the Picture Clip you want to retrieve. If you specify a measurement that exceeds a dimension of the control, an error message will be generated.

Using the Enumerated Method

For storing pictures for a Toolbar, the enumerated method is easier to use than the random access method. The enumerated method divides the bitmap in the Picture Clip control into a number of equally sized cells. The number of cells is determined by the number of rows and columns that you set. For example, a setting of three rows and five columns would yield 16 cells. Figure 16.17 shows a bitmap in a Picture Clip control with grid lines superimposed to show the cells of the control.

Fig. 16.17 A bitmap in a Picture Clip control can be divided into a number of equally sized cells.

The cells in the Picture Clip control are addressed through the GraphicCell property of the Picture Clip control. To retrieve a particular cell, you specify the index number of the cell you want, as shown in the following code:

Image1.Picture = PictureClip1.GraphicCell(0)

The index number for the cells starts with zero for the cell in the upper left corner of the control. The numbering then proceeds across the row. For example, in a three row by five column bitmap, the last cell on the first row would be cell number four, while the first cell on the second row would be cell number five. The general formula for the index number of any given cell is:

(Row - 1) * [Total Columns] + Column - 1

This number scheme is illustrated in figure 16.17.

To see how the Picture Clip control works in developing a Toolbar, look again at the code in Listing 16.1. Using the bitmap of the Picture Clip shown in figure 16.17, the code in Listing 16.1 would be updated as shown in Listing 16.2.

Listing 16.2 Using a Picture Clip Control to Manage the Graphics for a Toolbar
‘***************************************************
‘The button is initially loaded with the up picture.
‘***************************************************
Private Sub Form_Load()
Image1.Picture = PictureClip1.GraphicCell(0)
End Sub
‘**********************************************************
‘When the mouse button is pressed, the down button is shown
‘**********************************************************
Private Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Image1.Picture = PictureClip1.GraphicCell(5)
End Sub
‘***************************************************************
‘When the mouse button is released, the up button is shown again
‘***************************************************************
Private Sub Image1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Image1.Picture = PictureClip1.GraphicCell(0)
End Sub

Note: To avoid having to place code in the events of each Image control, you can use a control array for the Image controls used to display the Toolbar buttons. Then by carefully developing a bitmap with two rows and a number of columns equal to the number of Image controls, you can use the index number of the Image control to determine the necessary cell number of the Picture Clip control. Referring back to the Toolbar shown in figure 16.12, the bitmap needed for the Toolbar is shown in figure 16.18, and the code to implement the entire Toolbar is shown in Listing 16.3.

Fig. 16.18 A two-row bitmap can provide all the button images for a Toolbar and simplify the programming.

Listing 16.3 Using an Image Control Array and a Properly Designed Bitmap, You Can Simplify the Code Required to Implement the Entire Toolbar

‘*************************************************
‘Load all buttons with the appropriate up picture.
‘*************************************************
Private Sub Form_Load()
For I = 0 To 14
Toolbar(I).Picture = ToolClip.GraphicCell(I)
Next I
End Sub
‘**********************************************************
‘When the mouse button is pressed, the down button is shown
‘**********************************************************
Private Sub Image1_MouseDown(Index As Integer, Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Toolbar(Index).Picture = ToolClip.GraphicCell(Index + 16)
End Sub
‘***************************************************************
‘When the mouse button is released, the up button is shown again
‘***************************************************************
Private Sub Image1_MouseUp(Index As Integer, Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Toolbar(Index).Picture = ToolClip.GraphicCell(Index)
End Sub

Using the Animated Button Control

In a previous section, animation of a button in an Image control was discussed. The animation consisted of placing different button pictures in the Image control at specified intervals. For some types of animation, the animated button control can be used. This control combines a picture array with a Timer control and handles all the necessary event code for you. While the control cannot be used to animate a button that is sitting idle on the screen, it does provide you with several types of animation for buttons that are clicked. The three types of animated buttons that you can create are as follows:

The animated button is one of the custom controls that comes with Visual Basic. To add it to the Visual Basic Toolbar, you need to select it from the Custom Controls dialog box on the Tools menu. To set up an animated button, you need to draw the control on your form, and then assign a series of pictures to the button. The Picture property in the animated button differs from the Picture property of other controls in that it is actually a picture array. The index of the array is determined by the value of the Frame property. You can load all the necessary pictures for the animated button at designtime using the Frame Settings dialog box as shown in figure 16.19.

Fig. 16.19 The Frame Settings dialog box enables you to set the pictures for all the frames of the animated button.

Using the Frame Settings dialog box, you specify the frame either in the frame number text box or with the slide bar below the text box. When you have set the desired frame index, press the Load button to bring up the open dialog where you select the picture file for that frame. You repeat this process until all the frames have been loaded.

To complete the setup of the animated button, you will also need to choose the button type by setting the Cycle property and the Speed property. The Cycle property tells the animated button which of the three types to use. The values of the property for each button type are summarized in Table 16.1. The Speed property determines the number of milliseconds between frames in the animation sequence. A setting between 100 and 500 provides a good animation speed.

Table 16.1

Table 16.1 The Cycle Property Determines Which Type of Animated Button is Created

Cycle Value                Button Type

0                                  Animated

1                                  Multi-state

2                                  Two-state animated

Because it is impossible to show the effects of animation on the printed page, you will need to run the program in the ANIMATED.MAK project file to see the different types of buttons in action. This project file contains three animated buttons (one for each type) and an Image control that shows the animation of an idle button.

One other property of the animated button that may interest you is the ClickFilter property. This property determines what part of the animated button will accept mouse clicks. This can provide you with finer control over the use of the button. The four setting values of the ClickFilter property and their effects on the performance of the button are summarized in Table 16.2.

Table 16.2

Table 16.2 The ClickFilter Property Determines Which Part of the Animated Button Responds to Mouse Clicks

ClickFilter Value        Responsive Area

0                                  A mouse click anywhere on the control will trigger the click event.

1                                  A mouse click on the image frame or the caption text will trigger the click                                     event. This differs from the "0" setting only if the animated button is larger                                     than the image on the button.

2                                  The click event will only be triggered if the mouse is clicked on the image in                                     the control, not on the caption text.

3                                  The click event will only be triggered if the mouse is clicked on the caption                                    text of the control, not on the image in the control.

Creating and Managing Graphics

Earlier sections of this chapter have shown how graphics can be used to enhance the user interface through displayed pictures, Toolbars, invisible buttons, and other devices. To obtain the graphics used in these display elements, you can either use a graphics image from a library or create your own with a package such as Paintbrush.

But what if your application needs to be able to create graphics on its own? Applications that need to be able to create graphics include data analysis programs that create charts, and programs where the user might need a sketch pad for note-taking. This section will discuss how you can create graphics images in your application, and how to store and manage the created graphics.

Creating Graphics

Visual Basic provides several tools that can be used to create graphics. The drawing methods will work on a form or in a Picture Box control. They can also be used with the printer object to send the output to the printer. This discussion will focus on creating graphics to be stored in files, so the drawing methods will be used with a Picture Box control. If no object is specified with the method, the form that currently has focus will receive the output of the methods.

Visual Basic provides seven basic methods for creating graphics. These methods can be used in combination to create many types of graphics images. The seven methods are as follows:

™B7 Print method, which places text on the target object.

Using the Line method

The Line method is used to draw lines and boxes on the form. To draw a line, you need to provide the Line method with the starting and ending points of the line. You can omit the starting point, and the method will draw a line from the current position to the ending point. The code below draws a triangle on a form.

Line (1600, 750)-(2000, 750)
Line -(2000, 1250)
Line -(1600, 750)

To draw a box on the form, you again use the Line method with the starting and ending points, but you also include the optional B argument. This will draw an open box with the top left and bottom right (or bottom left and top right) corners specified by the starting and ending points. (Drawing an open box assumes that the FillStyle property of the form is set to transparent.) If you want the box to be filled, you can also specify the optional F argument. This will fill the box with the same color used to draw the border of the box. As with drawing a line, if the starting point is omitted, the box is drawn from the current position to the ending point. The commands to draw boxes are shown in the following code. Figure 16.20 displays the results of the line and box drawing commands, as well as the results of several drawing properties that will be discussed below.

Line (2000,2000)-(2500,2500),,B
Line -(3000,3000),,BF


Fig. 16.20 The Line method is used to draw lines and boxes on an object.

As you can see, the commands to draw lines and boxes are quite simple. The real trick to controlling the appearance of lines and boxes is setting the drawing properties of the form (or other object that is receiving the graphics). These properties are set for the form, and the graphics methods will use the setting of the properties that is in effect when the method is used. The properties that affect the graphics methods are summarized in Table 16.3. The effects of some of these properties are shown in figure 16.20.

Table 16.3 Table 16.3 The Drawing Properties Affect the Appearance of Graphics Drawn with the Graphics Methods

Property Name  Purpose

  • DrawMode This determines how the color used to draw the border of the object interacts with                       objects already on the screen.
  • DrawStyle This determines the pattern used to draw the border of the object.
  • DrawWidth This determines the width of the line used to draw the border of the object.
  • FillColor This determines the color used to fill an object.
  • FillStyle This determines the fill pattern used to fill an object.
  • ForeColor This determines the primary color used in drawing the border of an object.
  • The values of these properties are well explained in the help files for Visual Basic. All these properties can be set for a form or Picture Box control at designtime. However, they are most useful when they are set at runtime, where they can be set for a single operation then returned to their original values. The following code draws a series of boxes then returns the form's properties to their original settings.

    Listing 16.4 Draw.Frm Drawing a Series of Lines and Boxes on a Form

    frmset1 = Form1.DrawStyle
    frmset2 = Form1.DrawWidth
    frmset3 = Form1.FillColor
    frmset4 = Form1.FillStyle
    frmset5 = Form1.ForeColor
    Line (1000, 1000)-(1600, 1600), , B
    Form1.DrawStyle = 2
    Form1.FillStyle = 2
    Line -(2000, 2000), , B
    Form1.FillColor = &hff
    Line -(2500, 2500), , B
    Form1.DrawWidth = 3
    Form1.ForeColor = &hff0000
    Line -(3000, 3000)
    Form1.DrawStyle = frmset1
    Form1.DrawWidth = frmset2
    Form1.FillColor = frmset3
    Form1.FillStyle = frmset4
    Form1.ForeColor = frmset5
    Line (100, 100)-(500, 500), , B

    Using the Circle Method

    The Circle method enables you to draw circles, ellipses, arcs, and pies on the form. The simplest form of the method is:

    Circle (X,Y),R

    This command draws a circle of radius R with a center at the position specified by X and Y. As with the Line method, the pattern and color of the circle's border and fill are determined by the settings of the drawing properties.

    There are several optional arguments that can be used with the Circle method. The start and end arguments of the method are used to draw an arc on the form. The values of start and end are the angles from horizontal expressed in radians. (The values of an angle in radians is determined by multiplying the angle in degrees by pi/180.) The values of start and end can range from 0 to 2*pi or 0 to -2*pi. If the value of both the start and end arguments is negative, then the method will draw a pie (an arc with lines extending to the center of the circle). If both values are positive, a simple arc is drawn. If one of the values is negative, a line will be drawn from that end of the arc to the center.

    The other argument that can be specified is the aspect argument. This argument is used to draw an ellipse or oval. The aspect is the ratio of the vertical size of the ellipse to the horizontal size. An aspect of one draws a circle. If the aspect is greater than one, the ellipse is taller than it is wide. If the aspect is less than one, the ellipse is wider than it is tall. In all cases, the radius specified in the method sets the size of the longer dimension. The code for drawing several shapes with the Circle method is shown below. The results of this code are shown in figure 16.21.

    Circle (500, 500), 500
    Circle (1600, 1600), 500, , 1.57, 0
    Circle (2500, 2500), 500, , -4.7, -6.2
    Circle (3500, 3500), 500, , , , 1.5
    Circle (4500, 4500), 500, , -0.01, -1.57, 1.5

    Fig. 16.21 The Circle method can be used to draw circles, ellipses, arcs, and pies.

    Using the PSet method

    The PSet method is used to draw a single point on the form using the color specified by the ForeColor property. The size of the point drawn is dependent on the setting of the DrawWidth property. A larger DrawWidth setting will produce a larger point. The PSet method will draw the point at the coordinates specified in the argument of the method. The following code will draw a point at position 100, 100.

    PSet (100,100)

    One use of the PSet method is to provide a freehand drawing capability for the users of your application. This use will be covered in the "Sketch Pad Application" section later in this chapter.

    Using the PaintPicture Method

    The PaintPicture method enables you to place all or part of a picture from one object into a specific location in another object. Also, by carefully setting the height and width arguments for the source and target objects, you can enlarge or reduce the size of the source picture. The following code paints part of a picture from a Picture Box control onto the base form.

    PaintPicture Picture1.Picture, 50, 50, 750, 750, 0, 0, 500, 500

    The PaintPicture method contains several arguments as shown in this code. The first argument after the name of the method specifies the source of the picture from which a piece is being taken. The first two numerical arguments specify the coordinates of the upper left corner of the region where the picture is to be placed. These three arguments are the only ones that are required for the PaintPicture method. All other arguments are optional. If only the three required arguments are specified, the entire source picture will be copied to the target at full size.

    The second pair of numbers in the command specifies the horizontal and vertical size of the target region. If this size is different from the size of the source picture or region, the picture will be stretched or compressed to fit the specified space. The third pair of numbers in the command specifies the upper left corner of the source region, that is, the part of the picture being copied. The final pair of numbers specifies the height and width of the source region. The code shown in the previous listing will take a piece of a picture from the Picture Box control, enlarge it by 50%, and place it on the form. The results of this command are shown in figure 16.22.

    Fig. 16.22 The PaintPicture method can copy all or part of a picture from one object to another, and enlarge or reduce the picture.

    Some uses of the PaintPicture method in creating graphics would be:

    Using the Print Method

    While the Print method is not technically a graphics method, it is used to place text on a form, Picture Box control, or the printer object. The Print method can be used in conjunction with the graphics methods to create charts or drawings, or to annotate existing bitmaps. The Print method itself is quite simple. The following code displays a single line of text at the current position on the form.

    Print "This is a one-line test."

    The output of the Print method is controlled by the settings of five properties of the object being printed on. These properties are as follows:

    Point and Cls Methods

    The other two graphics methods mentioned at the beginning of this section are the Point method and the Cls method. These two methods each perform a single function. The Point method returns the RGB color setting of a single specified point. The Cls method clears all graphics drawn with the graphics methods from a form, or picture or Image control. The Cls method has no effect on any controls that are on the object; it only clears graphics that were drawn at runtime.

    Sketch Pad Application

    To further illustrate how the different graphics methods work, this section will discuss how to create a sketch pad application. This application will be similar to the Paintbrush application in Windows with which most people are familiar. The starting form for the application is shown in figure 16.23.

    Fig. 16.23 You can create a Paintbrush-style application using the drawing methods.

    This sketch pad application only creates drawings that are bitmaps. Any object placed on the screen is handled as simply a series of points after it is created. The objects cannot be selected again later to be resized or moved. In contrast to this application, programs like PowerPoint save information about the size, type, location, and other characteristics of an object. This allows the object to be re-selected and those characteristics to be modified to change the object's appearance at a later time.

    Setting up the Toolbar

    For the sketch pad application, a Toolbar is needed to allow the user to select which type of object to draw. The Toolbar is set up by placing an Image control on the form for each of the objects to be drawn. A pair of Image controls is then assigned for each tool to hold the image of the "up" button and the "down" button. When the button for a tool is pressed, two things occur. First, the "down" image of the button is displayed to provide the user with an indication of which tool was selected. Second, a variable is set to tell the program which drawing tool to use. The sketch pad application provides the user the ability to draw one of six objects:

    To make the programming simpler, an Image control array will be used, and the index of the control array will define the tool used to draw on the Picture Box control. The code for setting the Image control pictures and the tool type is shown below. The variable ToolTp is declared in the declarations section of the form and is initially set to zero, the number for the Freehand tool.

    ‘****************************************************************
    ‘Reset the button for the previously used tool to the Up position
    ‘****************************************************************
    Image1(ToolTp).Picture = Image2(ToolTp).Picture
    ‘***************************************************************
    ‘Set the button for the newly selected tool to the Down position
    ‘***************************************************************
    Image1(Index).Picture = Image3(Index).Picture
    ‘***************************************
    ‘Set the ToolTp variable to the new tool
    ‘***************************************
    ToolTp = Index

    Using the Various Drawing Tools

    The functionality of the sketch pad enables the user to press the mouse button, and then drag the mouse to generate the object. As the mouse is being dragged, the object changes size and shape to give the user an indication of the appearance of the drawn object. When the mouse button is released, the object is drawn in its final form. The sketch pad application provides a Picture Box control on which the user may draw.

    To implement the various drawing tools, you will need to work with three events: the MouseDown, MouseMove, and MouseUp events. These events correspond to the actions of the user as he is drawing an object.

    The MouseDown event is responsible for telling the program that the user is drawing on the Picture Box control and to set the initial position of the object. The purpose of this initial point depends on the type of object being drawn. Table 16.4 shows the purpose of the initial point for each of the objects in the sketch pad application.

    Table 16.4

    Table 16.4 The Initial Point of a Drawing Operation has Different Meanings for Each Object

    Object                              Use of Initial Point

    Line                                  One of two points defining the line

    Box (open or filled)           One of the corners of the box

    Circle (open or filled)       One corner of a box which would bound the circle

    Freehand                         The first point drawn

    To tell the program whether or not to actually draw objects while the mouse is in motion, a variable has been defined. This variable, DrawNow, will have a value of either True or False. The MouseDown event sets this variable to True. The following code is placed in the MouseDown event to enable the drawing methods. In the code, the variables curX and curY are the coordinates of the initial point. The variables oldX and oldY are the coordinates of the last mouse position. These are important in the MouseMove event as you will see.

    Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, _
    X As Single, Y As Single)
    DrawNow = -1
    curX = X
    curY = Y
    oldX = X
    oldY = Y
    End Sub

    The MouseMove event is the main workhorse of the sketch pad application. If the mouse button is down (DrawNow is set to True), the code in the MouseMove event draws the selected object between the initial point of the drawing and the current mouse position. The event contains a set of cases to handle the various types of objects that might be drawn. You will note that the open and filled boxes and the open and filled circles use the same code. This is because in the Move event, it is only necessary to show the outline of a filled object while the drawing is in progress. You will also note that, for the Freehand drawing, the Line method is used instead of the PSet method. The reason for this is that the move event is triggered at certain intervals, not as a continuous event. Rapid movements of the mouse will then leave gaps in the lines drawn with the PSet method. By using the Line method, a line is drawn between the last position of the mouse and the current position, thereby providing a continuous line. The code for the MouseMove event is shown in Listing 16.5.

    Listing 16.5 Sketch.Frm The MouseMove Event Draws the Outline of the Object as the Mouse is Dragged

    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, _
    X As Single, Y As Single)
    If DrawNow Then
    Select Case ToolTp
    Case 0
    Picture1.Line (oldX, oldY)-(X, Y)
    oldX = X
    oldY = Y
    Case 1
    Picture1.Line (curX, curY)-(oldX, oldY), Picture1.BackColor
    Picture1.Line (curX, curY)-(X, Y)
    oldX = X
    oldY = Y
    Case 2,3
    Picture1.Line (curX, curY)-(oldX, oldY), Picture1.BackColor, B
    Picture1.Line (curX, curY)-(X, Y), , B
    oldX = X
    oldY = Y
    Case 4,5
    cntX = curX + Int((X - curX) / 2)
    cntY = curY + Int((Y - curY) / 2)
    radX = Abs(curX - X)
    radY = Abs(curY - Y)
    radcir = IIf(radX > radY, radX, radY) / 2
    If radX = 0 Then
    aspcir = 1
    Else
    aspcir = radY / radX
    End If
    Picture1.Circle (oldX, oldY), oldrad, Picture1.BackColor, , , _
    oldasp
    Picture1.Circle (cntX, cntY), radcir, , , , aspcir
    oldX = cntX
    oldY = cntY
    oldasp = aspcir
    oldrad = radcir
    End Select
    End If
    End Sub

    When the user is finished drawing and releases the mouse button, the MouseUp event creates the final drawing for the object and turns off the drawing mode. Much of the same code is used in the MouseUp event as in the MouseMove event, but separate cases have been added for the filled boxes and circles. The code for the MouseUp event is shown in Listing 16.6.

    Listing 16.6 Sketch.Frm The MouseUp Event Renders the Final Drawing and Turns Off the Drawing Mode

    Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, _
    X As Single, Y As Single)
    If DrawNow Then
    Select Case ToolTp
    Case 1
    Picture1.Line (curX, curY)-(oldX, oldY), Picture1.BackColor
    Picture1.Line (curX, curY)-(X, Y)
    Case 2
    Picture1.FillStyle = 1
    Picture1.Line (curX, curY)-(oldX, oldY), Picture1.BackColor, B
    Picture1.Line (curX, curY)-(X, Y), , B
    Case 3
    Picture1.FillStyle = 0
    Picture1.Line (curX, curY)-(oldX, oldY), Picture1.BackColor, B
    Picture1.Line (curX, curY)-(X, Y), , B
    Picture1.FillStyle = 1
    Case 4
    cntX = curX + Int((X - curX) / 2)
    cntY = curY + Int((Y - curY) / 2)
    radX = Abs(curX - X)
    radY = Abs(curY - Y)
    radcir = IIf(radX > radY, radX, radY) / 2
    If radX = 0 Then
    aspcir = 1
    Else
    aspcir = radY / radX
    End If
    Picture1.FillStyle = 1
    Picture1.Circle (oldX, oldY), oldrad, Picture1.BackColor, , , _
    oldasp
    Picture1.Circle (cntX, cntY), radcir, , , , aspcir
    Case 5
    cntX = curX + Int((X - curX) / 2)
    cntY = curY + Int((Y - curY) / 2)
    radX = Abs(curX - X)
    radY = Abs(curY - Y)
    radcir = IIf(radX > radY, radX, radY) / 2
    If radX = 0 Then
    aspcir = 1
    Else
    aspcir = radY / radX
    End If
    Picture1.FillStyle = 0
    Picture1.Circle (oldX, oldY), oldrad, Picture1.BackColor, , , _
    oldasp
    Picture1.Circle (cntX, cntY), radcir, , , , aspcir
    Picture1.FillStyle = 1
    End Select
    End If
    DrawNow = 0
    End Sub

    Creating an Undo Feature

    Most applications that perform drawing functions have an Undo feature that enables the user to restore the picture to its state prior to the last drawing operation. An Undo function can be implemented in the sketch pad application by adding a second Picture Box control to the form, placing some additional code in the MouseDown event, and adding an Undo button. The second Picture Box control will contain a copy of the image in the primary drawing Picture Box control. This Picture Box control will have its Visible property set to False so it is not visible when the application is running. The second picture is updated each time a new drawing operation is started. This update is performed in the MouseDown event as shown in the following code.

    Picture2.Picture = Picture1.Image

    To undo an operation, the code simply copies the picture in the second Picture Box control back to the main Picture Box control. This returns the drawing area back to the way it was before the last drawing operation. This code is shown in the line below.

    Picture1.Picture = Picture2.Picture

    Saving the Picture

    Finally, you will want to give your users the capability of saving the pictures they create. The drawings on a Picture Box control can be saved as a bitmap file using the SavePicture function. This function requires the name of the source picture and the name of the output file. You will probably want to use the CommonDialog control to allow the user to specify a name for the output file. For a drawing, the source of the picture is the Image property of the Picture Box control or form on which the drawing was made. The following code gets a file name using the CommonDialog control (named GetFile) and stores the drawing created by the sketch pad application.

    GetFile.Filter = "Bitmap Files (*.BMP)|*.bmp|Icon Files | *.ico |All Files|*.*"
    GetFile.DefaultExt = "BMP"
    GetFile.ShowSave
    DataName = GetFile.FileName
    SavePicture Picture1.Image, DataName

    Bitmap Annotation

    The sketch pad application showed how you can create graphics with the graphics methods then store the graphics in files. Since the Picture Box control is also capable of displaying an existing graphics file, you can use the sketch pad function to modify graphics from other sources. You might want to use this to annotate fax images before sending them on to another person. You can also use it to make changes to bitmaps created by other people.

    To annotate bitmaps, you will need to add a LoadPicture function to the sketch pad application to import the picture into the editing area. The code for this operation is shown below. This code again uses the CommonDialog control to obtain the name of the file to be edited.

    GetFile.Filter = "Bitmap Files (*.BMP)|*.bmp|Icon Files | *.ico |All Files|*.*"
    GetFile.DefaultExt = "BMP"
    GetFile.ShowOpen
    DataName = GetFile.FileName
    Picture1.Picture = LoadPicture(DataName)

    Using a Database to Store Pictures

    You should be aware of one last thing regarding the storage of pictures. An Access database can store pictures in a long binary field. This database can be bound to a data control, and the field containing the pictures can be bound to either a Picture Box control or an Image control. If a Picture Box control is used, the drawing methods discussed in this section can be used to create or edit the pictures in the database. The only thing to be aware of is how to save the changes to the pictures back into the database. The problem arises from the fact that the data field containing the picture is bound to the Picture property of the Picture Box control. The drawings that are made with the graphics methods are not part of the Picture property, but rather part of the Image property of the control. It is therefore necessary to copy the Image property to the Picture property prior to the update of the database. This is done with the following line of code:

    Picture1.Picture = Picture1.Image

    Once the drawing or annotations have been copied to the Picture property, the drawing will be stored in the database. An application that requires a number of sketches can benefit from using this technique to store the drawings. By using the database, all the drawings are stored in one file rather than a series of bitmap files. It is also possible to add other fields to the database that would contain a description of the drawing and possibly supporting information or notes.

    Analyzing Data with Graphics

    The final topic in this chapter deals with analyzing data or information with the use of graphics or charts. Often charts are very useful in giving users a better feel for the information than is possible by looking at just numbers. If you were to look at a pie chart of your household budget, the percentage of your budget devoted to debt reduction is presented far more dramatically than if you only looked at dollar amounts. Charts can also help a user spot trends in the data that would not be possible from viewing only the numeric data.

    This section will look at two ways to create data charts—with the Graph control and with the graphics methods. Using the Graph control is far easier than creating you own charts, but there are situations where "rolling your own" is the best solution. Also, if you understand how to create your own charts, you will have a better understanding of the workings of the Graph control and will be able to make better use of it.

    Using the Graph Control to Analyze Data

    The Graph control is one of the custom controls provided with Visual Basic. This control provides you with the means to create any of the following graph types:

    To create a chart, you first need to select the Graph control from the Toolbar, and draw the chart area on your form. You will then need to set the type of graph desired by using the GraphType property. You will also need to use the NumSets and NumPoints properties to tell the control how many sets of data to include in the chart and how many points are in each set. Finally, you will have to input the data that you want displayed on the chart. You can input all this information at designtime through the properties dialog. After you have drawn the Graph control on your form, click the right mouse to bring up a pop-up menu, then select Properties from the menu. This will bring up the Graph Control Properties dialog box as shown in figure 16.24.

    Fig. 16.24 The Graph Control Properties dialog box provides an easy way of entering information for the graphics control.

    The first tab in the properties enables you to specify the type of graph, and the number of sets and points. You can also set the graph style, which provides additional formatting information, and information about the colors to be used on the graph. For illustration purposes, select a 2D pie chart with one data set and five data points. After entering this information for your graph, you can select the Data tab at the top of the properties page to go to the data-entry dialog box shown in figure 16.25.

    Fig. 16.25 The Data page enables you to enter the data to be displayed on the graph.

    For the pie chart, you can enter the data value and the color and pattern of each pie slice from the Data page. Each data point is accessed using the scroll buttons to the right of the data-entry area. When you have finished entering the data, you click the OK button to return to the form design window, and then run your program to show the results of the graph. Figure 16.26 shows the pie chart with values of 1, 2, 3, 4, and 5 for the five pie slices.

    Fig. 16.26 This pie chart is the result of the information entered for the Graph control.

    If you could only enter data for the Graph control at designtime, the Graph control's usefulness would be pretty limited. You can, however, enter data for the control at runtime. Listing 16.7 will set up the data to produce a pie chart similar to the one in figure 16.26, but with six data points and different values for the data points.

    Listing 16.7 Piechart.Frm Using the Graphic Control to Create a Pie Chart

    Graph1.GraphType = 1
    Graph1.NumPoints = 6
    Graph1.ThisSet = 1
    For I = 1 To 6
    Graph1.ThisPoint = I
    Graph1.GraphData = 100 * Rnd + I
    Graph1.ThisPoint = I
    Graph1.ColorData = I
    Next I

    Note: Once the graph is displayed on a form, it cannot be changed. Therefore, the way to pass data to the graph at runtime is to place the Graph control on a second form, then place the code to set up the graph in the Activate event of that form. When the form is accessed through the Load method, the new information will be displayed on the graph. When the user has finished viewing the graph and is ready to move on, the form containing the Graph control can be unloaded. The same thing can be accomplished through the use of an MDI form.

    Creating Your own Data Analysis Graphics

    Another way to create charts for data analysis is with the graphics methods. Generating charts in this manner is much more difficult than using the graphics control, but there are some advantages to the effort. Some of these advantages are as follows:

    This section will discuss the general programming aspects of creating your own charts, and then look in detail at some of the advantages mentioned in the list.

    Creating a Simple Chart

    To begin the discussion, consider the pie chart created in figure 16.26. To create the same chart in a program, follow these steps:

    1. Calculate the total value of all the points.

    2. Convert the value of each point to a fraction of the total.

    3. Convert the fractional value to the radian values of a circle.

    4. Set the FillColor property for each point.

    5. Draw the pie shape for each of the points.

    Listing 16.8 performs these steps for the graph in figure 16.26.

    Listing 16.8 Piechrt2.Frm Creating a Pie Chart Using the Graphics Methods

    Dim piedat(6) As Integer
    ‘***************
    'Set data values
    ‘***************
    piedat(1) = 5
    piedat(2) = 3
    piedat(3) = 8
    piedat(4) = 2
    piedat(5) = 9
    piedat(6) = 6
    ‘***********************
    'Get total of all points
    ‘***********************
    totpnt = 0
    For I = 1 To 6
    totpnt = totpnt + piedat(I)
    Next I
    ‘********************************************
    'Calculate percent, pie coordinates then plot
    ‘********************************************
    strtpt = -0.001
    Form1.FillStyle = 0
    For I = 1 To 6
    pctpnt = piedat(I) / totpnt
    piesiz = pctpnt * 2 * 3.1416
    endpnt = strtpt - piesiz
    endpnt = IIf(endpnt < -6.2831, -6.2831, endpnt)
    Form1.FillColor = QBColor(I)
    Circle (2000, 2000), 1600, , strtpt, endpnt
    strtpt = endpnt
    Next I

    This is a somewhat generic routine for creating a pie chart. If you needed to add more data points, you would only need to expand the array and the terminal value of the FOR ... NEXT loop. You would also need to change the way colors are handled if you had more than 16 data points.

    Using Methods for Different Chart Types

    As you saw above, a pie chart can be created using the Circle method. Most of the chart types described above for the graphic control can be easily ("easily" being a relative term) created with the graphics methods. Each graphic type will use one or more of the graphics methods to create the charts. Table 16.5 shows several of the most common chart types and the methods used to create them.

    Table 16.5

    Table 16.5 Different Graphics Methods are Used to Create Different Types of Charts

    Chart Type                                      Graphics Method

    Pie                                                     Circle method

    Bar                                                    Line method (drawing boxes)

    Gantt                                                  Line method (drawing lines or boxes)

    Line                                                    Line method

    Scatter                                               PSet method, PaintPicture method, Print method

    High/Low/Close                                 Line method

    Symbols can be drawn on any of the chart types using the PaintPicture method, or if the symbols are simple characters, the Print method can be used to place the character on the chart. You will also note that the Print method was mentioned as one that could produce Scatter charts. This works the same way as placing symbols on the chart. You would establish the necessary position of the symbol using the CurrentX and CurrentY properties, then print the symbol.

    Determining Where to Place Points on the Chart

    The section on "Creating a Simple Chart" showed how to use the Circle method to create a pie chart. The code used to produce the chart assumed that the size of the form and the position of the chart were predetermined. This may not always be the case. If you are drawing a chart on a form whose size can change, you will need to calculate the position of the chart on the form. In addition, for many chart types such as line or bar, you need to establish the range of the horizontal and vertical coordinates that are available for showing data.

    This section will walk through this process using the example of a line chart containing 50 data points with a random value of 0 to 1000. The actual chart is shown in figure 16.27.

    Fig. 16.27 To create a chart, you need to determine the placement of any labels and the area available for drawing the data.

    The first step in determining where the data for the chart will be placed is to find the size of the form or Picture Box control that will contain the chart. Horizontal and vertical size are determined using the ScaleWidth and ScaleHeight methods respectively. These properties determine the maximum space available for the output of the chart.

    Next, determine the amount of space needed for labels on the two axes. This is handled by the TextHeight and TextWidth properties. You will want the maximum value of these properties for all the labels that will be placed on the chart. For the sample case, the Y-axis will range from 0 to 1000 in increments of 200. Therefore, the maximum TextWidth would be for 1000. Along the X-axis, the values will range from 1 to 50 in increments of 10, but all the values should have the same text height. The TextWidth will determine the margin between the edge of the output object (form or picture box) and the Y-axis. Similarly, the TextHeight gives the margin between the bottom of the output object and the X-axis. If you also want a margin at the top and right of the chart, you will have to establish values for these as well. The code for the sample chart uses half the TextWidth and TextHeight margins for the right and top margins respectively.

    Subtracting the margin sizes from the total size of the object gives you the size of the actual drawing area. This size will determine the scaling factor that you need to use to determine the placement of data points. For the Y-axis values, this is the height of the drawing area divided by the maximum value. For the X-axis value, the scaling factor is the width of the drawing area divided by the maximum X value. To obtain the drawing position of any point you will need to do the following:

    1. Multiply the X value by the horizontal scaling factor to determine the distance from the Y-axis.

    2. Add the distance obtained in Step 1 to the width of the left margin to obtain the actual X position on the output object.

    3. Multiply the Y value by the vertical scaling factor to determine the distance from the X-axis.

    4. Subtract the distance obtained in Step 3 from the position of the X-axis to obtain the actual Y position on the output object.

    You will notice that the last step instructs you to subtract the value obtained in step 3 from the position of the X-axis. This is because the vertical position coordinates of an object increase from top to bottom. Therefore, a point above the X-axis will have a smaller number for the vertical position than the axis itself. The program code for the chart is shown in Listing 16.9.

    Listing 16.9 LineChrt.Frm Creating a Chart Involves Setting the Position of the Axis and Labels, and Determining the Scaling Factor to be Used to Position Points

    ‘********************************
    'Determine maximum size of object
    ‘********************************
    maxX = Chart.ScaleWidth
    maxY = Chart.ScaleHeight
    ‘**********************
    'Determine margin sizes
    ‘**********************
    lmarg = Chart.TextWidth("1000")
    bmarg = Chart.TextHeight("50")
    rmarg = maxX - 0.5 * lmarg
    tmarg = 0.5 * bmarg
    bmarg = maxY - bmarg
    ‘*************************************
    'Determine scale factors for each axis
    ‘*************************************
    SclX = (rmarg - lmarg) / 50
    SclY = (bmarg - tmarg) / 1000
    ‘*********
    'Draw axes
    ‘*********
    Chart.Line (lmarg, tmarg)-(lmarg, bmarg)
    Chart.Line -(rmarg, bmarg)
    ‘*************************
    'Draw labels and tic marks
    ‘*************************
    For I = 1 To 6
    Chart.CurrentX = 5
    Ypsn = bmarg - ((I - 1) * 200 * SclY)
    Chart.CurrentY = Ypsn
    Chart.Print Right(Str((I - 1) * 200), 4)
    Chart.Line (lmarg, Ypsn)-(lmarg + 40, Ypsn)
    Next I
    For I = 1 To 6
    Xpsn = lmarg + ((I - 1) * 10 * SclX)
    Chart.CurrentX = Xpsn
    Chart.CurrentY = bmarg + 5
    Chart.Print Right(Str((I - 1) * 10), 2)
    Chart.Line (Xpsn, bmarg)-(Xpsn, bmarg - 40)
    Next I
    ‘************************
    'Draw Points on the chart
    ‘************************
    Chart.ForeColor = &hff
    For I = 1 To 50
    Xpsn = lmarg + (I * SclX)
    Ypsn = bmarg - (1000 * Rnd * SclY)
    If I = 1 Then
    Chart.CurrentX = Xpsn
    Chart.CurrentY = Ypsn
    Else
    Chart.Line -(Xpsn, Ypsn)
    End If
    Next I

    Dynamic (or Time-Dependent) Charts

    One of the advantages mentioned for creating your own data-analysis charts is that you can create a chart that changes with time. To create this dynamic (or time-dependent) chart, you will need a way to add points to the chart at specified intervals. A chart such as this would be used in a manufacturing environment to track continuous processes. There are two ways to handle plotting time dependent information. You can track all the information, adding new points but never removing old points, or you can track some number of points representing the most recent measurements (for example, the last 100 points).

    Tracking a number of recent points is usually the preferable method of developing a dynamic chart. The advantages of this method are that you have a limited number of points, which keep system resource requirements down, and you do not have to constantly recalculate the scale factors to account for additional points. The code below shows the use of the Timer event to generate points at half second intervals and place them on a chart. Listing 16.10 produces the chart shown in figure 16.28.

    Listing 16.10 Dynchart.Frm Code Placed in the Timer Event to Create a Dynamic Chart

    If drwchrt = 1 Then
    Xpsn = lmarg + (pntidx * SclX)
    Ypsn = Int(bmarg - (1000 * Rnd * SclY))
    chrtpnt(pntidx) = Ypsn
    If pntidx = 1 Then
    Chart.CurrentX = Xpsn
    Chart.CurrentY = Ypsn
    Else
    Chart.Line -(Xpsn, Ypsn)
    End If
    pntidx = pntidx + 1
    End If

    Fig. 16.28 A dynamic chart can be created that changes as new points are added.

    This code is set up to track 100 data points. (The array chrtpnt was previously dimensioned to that size.) Now the question is, what do you do after the 100th point is reached? You will need to discard the first value in the array, move the rest of the array elements up, and then add the last value to the end of the array. You would then need to re-plot the entire array. If this were done for each point, your code would be running a lot of operations, and redrawing the chart for every new point. A better way to handle this is to eliminate about one-fourth of the points when the end of the array is reached. This way the redraw operation is performed much less often, improving the efficiency of your code. For the sample case with 100 point, a redraw would occur only every 25 points. Listing 16.11 shows the subroutine that resets the array and redraws the chart.

    Listing 16.11 Dynchart.Frm Redrawing the Chart After a Certain Number of Points Have Been Added

    Sub RdrwChrt()
    drwchrt = 0
    Chart.ForeColor = &h0
    ‘****************
    'Clear chart area
    ‘****************
    Chart.Cls
    ‘*********
    'Draw axes
    ‘*********
    Chart.Line (lmarg, tmarg)-(lmarg, bmarg)
    Chart.Line -(rmarg, bmarg)
    ‘***********
    'Draw labels
    ‘***********
    For I = 1 To 6
    Chart.CurrentX = 5
    Ypsn = bmarg - ((I - 1) * 200 * SclY)
    Chart.CurrentY = Ypsn
    Chart.Print Right(Str((I - 1) * 200), 4)
    Chart.Line (lmarg, Ypsn)-(lmarg + 40, Ypsn)
    Next I
    For I = 1 To 11
    Xpsn = lmarg + ((I - 1) * 10 * SclX)
    Chart.CurrentX = Xpsn
    Chart.CurrentY = bmarg + 5
    Chart.Print Right(Str((I - 1) * 10), 3)
    Chart.Line (Xpsn, bmarg)-(Xpsn, bmarg - 40)
    Next I
    Chart.ForeColor = &hff
    ‘*****************************
    'Reset array and redraw points
    ‘*****************************
    For I = 1 To 75
    chrtpnt(I) = chrtpnt(I + 25)
    Xpsn = lmarg + (I * SclX)
    Ypsn = chrtpnt(I)
    If I = 1 Then
    Chart.CurrentX = Xpsn
    Chart.CurrentY = Ypsn
    Else
    Chart.Line -(Xpsn, Ypsn)
    End If
    Next I
    ‘***********************************
    'Reset point index and restart timer
    ‘***********************************
    pntidx = 76
    drwchrt = 1
    End Sub

    The reset and redraw operations are fairly quick, so if you want to reset a smaller number of points, it should be no problem. The total number of points you use for the chart, and the number of points you reset will depend on your application and the time interval between points. You want to set these so that the chart is not being continuously reset.

    From Here...

    This chapter has discussed any number of ways that graphics can be used to enhance your applications. Each of the sections discussed an individual area of graphics usage. You can, of course, combine many of these techniques to add even greater sophistication to your applications.


    | Previous Chapter | Next Chapter | Search | Table of Contents | Book Home Page |

    | Buy This Book | Que Home Page | Digital Bookshelf | Disclaimer |


    To order books from QUE, call us at 800-716-0044 or 317-361-5400.

    For comments or technical support for our books and software, select Talk to Us.

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