TABLE OF CONTENTS
Visual Basic is a unique language in nearly all respects: a different interface, a different style, and a different method of doing things must be learned. A good understanding of procedures and modular programming will be invaluable in learning Visual Basic. A good knowlege of BASIC programming will also help. Unlike other languages, Visual Basic is completely graphically oriented, so you'll be learning a lot about how to control the program to be subject to the whim of the user, and you'll learn how windows processes system messages so things happen. Visual Basic is also a fun and practical language because all the dredgery and tedium of coding is minimized with it's easy to use graphical interface. It's primary purpose is to create custom databases, but it is fully functional for creating games, applications, diagnostics, modem terminals... you name it! Virtually any windows program can be created with Visual Basic, and you'll find it's the easiest language out there for the power it has.
The major disadvantage of Visual Basic is that you have to have add-ons to implement powerful features. These add-ons are called VBX files, and they can be found through third party sources. When you buy Visual Basic, you also get some VBX controls, but to expand the capabilities of te language you'll want to find some additionals. There are two versions of Visual Basic for Windows: Standard and Professional. I use Visual Basic Professional Edition 3.0, but I will be explaining from the Standard 3.0 perspective. I've had standard, and I know how annoying it is to read about a cool feature, and realize you don't have it because you "just" have standard. Standard runs for about $100.00, and Professional is $300.00. This is worth the money though!
The Visual Basic IDE (Integrated Development Environment) looks intimidating,
but it is actually quite simple. Here is a figure labeling the basic parts of
- Toolbox: all your VBX controls are contained on the toolbar. You use the buttons on the toolbar to draw controls on the form. When you add a VBX, it's icon will appear here.
- Form: this is what the user sees. It contains controls, and it's actions are defined by the code. Most applications have arround 5 - 6 forms, but there are obviously exceptions.
- Code Module: the code for the controls is defined in the code module. Notice the two combo boxes at the top of the code module. The left one shows the object who's code is being defined. The right one shows the Procedure, or event of the control which is being defined. We'll get into this later, but remember that this is where the combo boxes are located.
- Properties Window: properties for a control are set in this window, such as the control's color, dimensions, and opperations.
- Project Window: this is a representation of the .mak file which defines which files are to be used in the program. Every form, base code module, and vbx used with the .mak file is shown in this window. We'll get into .mak files shortly.
The Code Module window is where you'll do all the coding. The two combo boxes are
probably the most confusing part of VBasic for the beginner, but once you learn how the
language is set up it's not so hard. Imagine a button, like the windows kind you see
when you have a choice between "OK" and "Cancel." You can do different things to this
button: you can click it, drag the mouse over it, or push a button while it is selected.
These things that can happen are called events, and it is the programmer's job to write
code for these events. Here is a list of all the events for a regular old button control:
_Click _DragDrop _DragOver _GotFocus _KeyDown _KeyPress _KeyUp _LostFocus _MouseDown _MouseMove _MouseUpFor the button control, you'll probably only use a couple of these, like KeyPress and Click. The others aren't that common for a button control. So let's make a short program and test what we know. Open up Visual Basic, select the button control in the toolbox, and draw a button on the form. Double click on the button on the form, and you'll see the following:
Sub Command1_Click () End SubFor this tutorial, I'll type the above and from that you will know which control's event to code. This is Command1's Click event, for example. Now, enter in the following code:
Sub Command1_Click () Const MSG_INFO = 64 Msgbox "You clicked the button", MSG_INFO End SubClick on the run button (the button on the toolbar that looks like the right arrow for the play button on a stereo) and you'll see your program pop up. Click the button and you'll see a windows message box pop up with "You clicked the button" in there. Also notice the "i" icon to the left of the text. Now let's go over the code you just typed in. The first line declares the variable "MSG_INFO" as a constant (can't be changed) and assigns the value of 64 to it. The last line calls a message box to be displayed with the message as the first argument and the box style as the second argument. 64 is the box style setting you use to put that "i" icon on the left. There are other style arguments you can use as well, like putting Yes/No/Cancel buttons in the box instead of just OK, and there are 3 other icons you can use as well.
In addition to events, a control also has properties. You can bring up the properties window by selecting properties from the windows menu. There are a lot of properties for each control, so don't get scared! To display the properties for a control, simply click that control. Each control may have different properties. The property for a control is specified in the left hand column, while the value of that property is set in the right. Most properties can be set at run time (from within the code while the program is running), but some must be set at design time (when you are coding and drawing the controls -- designing the interface). When you create a control, it is good practice to set the name property to something of meaning. "Command1" has little meaning (which is a default name), while btnMsgBox tells you as the programmer what that does, so you don't have a mental lapse and forget what a control does. I like to prefix my control names with a three letter symbol, like btn for button, img for image, mnu for menu, txt for textbox, or pic for picture box. Then you have a name which reflects the purpose of the button. Of course, you don't have to name your controls, but I do and if you want to be like me then you should. :) Plus it's also good practice.
The properties of the controls are pretty self explanatory, but when I introduce a control
I'll define what the properties do just incase they are weird. And instead of saying
"Ok, draw a control button, draw a picture box, draw a frame, draw an image control", I'll
just display one of the following charts. It also says what values to set to the
form .name=frmMain .caption="Useless Program" command button .name=btnExit .caption="E&xit" image control .name=imgViewer2 .filename="picture1.bmp" image control .name=imgViewer2 .filename="picture2.bmp" frame .name=fraControl radio button .name=radComputer radio button .name=radRam radio button .name=radControl textbox .name=txtNameinput .text="" form .name=frmInput .caption="Name" textbox .name=txtNameinput .text="" command button .name=btnOK .caption="OK" .default=true command button .name=btnCancel .caption="Cancel"This would tell you that the program will have two forms. On the first form there would be a command button, two image controls, a frame with three radio buttons in it, and a text box. The second form would have a textbox and two command buttons. The c ontrols would have the properties to the left of them. For the code, I will go one form at a time and use the regular Visual Basic sub definition header syntax. For example, the code for everything on the second form (frmInput) might look like this:
Sub btnOK_Click () usrName = txtNameinput Unload Me frmMain.Show End Sub Sub btnCancel () Unload Me frmMain.Show End SubYou don't need to know what all that does right now, just know that that is how I'll set it up for this tutorial.
If you don't know about procedures then you'll be real lost in Visual Basic. Everything in windows programming is set up in procedures because programs don't start from line 1 and end at line 300. They are controled by the user, not the programmer. The user can click the mouse anywhere on the screen, or exit the program suddenly, or even call up another program while the current one is running! You as the programmer have to take all this into account. Whenever the user does something in windows, l ike click the mouse, move it, or click a button on a form, windows issues what is called a message. This message is interpreted by visual basic as an event. Each control in visual basic has several events, and you add code to an event to make it do some thing when windows issues the message. Events of a control are also called that object's procedure, as the menu boxes in the code module imply. Let's start out by introducing you to the Visual Basic code syntax and a simple program.
There five main types of code in Visual Basic
- Procedure Calls: You can call a procedure that you created, or you can call another control's event. Procedures include functions (return a value) and subs (don't return a value).
- Compiler Functions: If you know another form of BASIC then you are undoubtedly familar with it's simple functions like Chr$(), Asc(), and Ucase$(). These functions are like user-defined functions and always accept arguments and return values.
- Keywords: Like compiler functions, all versions of BASIC have built in keywords, or reserved words, like For, Next, Do, Loop, Select Case, Error, Goto, and Gosub. These appear in dark blue by default in the Visual Basic IDE.
- Control Properties: You can set control properties that can be set at run time from within the code. For example, you could set the text property of a text box by typing
txtInput.Text = "This is in the textbox", assuming that the na
me of the textbox is txtInput. Notice the period. All properties are set after the control name with a seperating period. There are three times you will use periods after a word in Visual Basic: as a property, as a method, and as the member of a user-d
- Methods: Many objects in Visual Basic contain unique functions that can be performed on them. A function unique to an object is called a method. The method function is called after the name of the object and is set in dark blue by default in th e Visual Basic IDE. An example of a method might look like
imgPicturebox.Zorder, which will set the image control to the front of the screen, assuming that the image control's name is imgPicturebox.
Let's write a quick program that performs a mathmatical calculation based on the values in two text boxes and prints the result in a label.
form .name=frmMathCalc .caption="VB Calculator" textbox .name=txtOperand1 .text="" textbox .name=txtOperand2 .text="" label .name=lblResult .caption="" command button .name=btnAdd .caption="+" command button .name=btnSubtract .caption="-" command button .name=btnMultiply .caption="x" command button .name=btnDivide .caption="/"This program will either add, subtract, multiply or divide the two numbers in txtOperand1 and txtOperand2 and print the result in lblResult. Once everything is created on the form, it is time to add the code. Now lets think about this for a minute. The user enters in the text into the two text boxes and then presses one of the buttons. So all we have to do as the programmer is put code into the four command buttons. There is one thing that could go wrong; the user could put a 0 into txtOperand2 and t hen push the divide button. Division by zero is a big mathematical no-no, so we have to make sure the user doesn't do something stupid. So here's the code for the four operator buttons
Sub btnAdd_Click () lblResult = Val(txtOperand1) + Val(txtOperand2) End Sub Sub btnSubtract_Click () lblResult = Val(txtOperand1) - Val(txtOperand2) End Sub Sub btnMultiply_Click () lblResult = Val(txtOperand1) * Val(txtOperand2) End Sub Sub btnDivide_Click () Const ICON_EXCLAMATION = 48 If Val(txtOperand2) = 0 Then lblResult = "" MsgBox "Division By 0", ICON_EXCLAMATION Else lblResult = Val(txtOperand1) / Val(txtOperand2) End If End SubThe first three events are pretty straigtforward: just put the result of the calculation in lblResult. Notice you have to use the Val() function to convert the argument to a number because the type of a text box is a string, and you can't perform mathema tical calculations on strings. In btnDivide_Click () we run accross the MsgBox function, which here accepts two arguments: the code that goes in the message box and the type of message box. In this case, the type is just a regular message box with an OK button (default) and a cute picture of an exclamation point (number 48). So if the user entered 0 for txtOperand2, we yell at him with a message box. Also notice I cleared the result of lblResult because the user might think that the value that was the re before (if this isn't the first calculation) is the result of the division by zero.
That used the basic controls, now lets use some more advanced controls and properties. We'll create a program that loads up a picture that the user types into a text box, and use the RGB() function to set the colors of some things.
form .name=frmPicLoader .caption="Picture Loader" text box .name=txtFilename .text="" command button .name=btnLoadPicture .caption="Load Picture" .default=true image control .name=imgPicture .stretch=trueA couple properties here that you haven't seen yet. The .default property of a command button can be set to true so when the user presses ENTER, that button's _Click event is called. A command button with the .default property set is usually the most co mmon and safe button, so you wouldn't have a button called btnDeleteRandomFile set to default. Also the .stretch property for an image control. This is set so that when we load the picture, it will fit to the size of the image control and we won't just get a microscopic corner of a big picture. The only problem is that if you load a really small picture, it will be enlarged so it might look like spew. Ok, so we have two controls to code. I want to add some code into the Form_Load event to show you th at, and we also have to add some code into the btnLoadPicture control so the button actually does something.
Sub Form_Load () Me.BackColor = RGB(0, 0, 100) End Sub Sub btnLoadPicture_Click () imgPicture = LoadPicture(txtFilename) End SubI love how you can write such little code and do so much! Ok, first of all, the Form_Load event. The Form_Load event is called whenever a form loads (obviously), and is where you can initialize variables and set initial properties, like load a control a rray or create mulitple instances of a form (this will sound more like english in a couple more sections). The Me object is the current form. Me.Caption would be referring to the current form's caption, Me.BackColor refers to the current form's backgroun d color. I could have said frmPicLoader.Backcolor but Me is shorter, more efficient, and it looks cooler. So just remember that Me refers to the current form.
The RGB() function is cool. It an easy way of setting a color with a choice of 16.8 million colors. It has three arguments: the red value, the green value, and the blue value. This is backwards in respect to the BASIC
PALETTE statement an
d propper hexidecimal, but it makes more sense when you think about the abbreviation RGB! The values can be any number between 0 and 255. So to make something black you would say RGB(0, 0, 0), blue would be RGB(0, 0, 255), and magenta would be RGB(255,
0, 255). The above example is a dark blue, which I think looks really nice. "Microsoft Gray," which is the gray color you see in toolbars and backgrounds of Microsoft software, is RGB(178, 178, 178). So all I'm doing in the Form_Load event is setting t
he background color of the form to a dark blue.
The btnLoadPicture_Click event simply loads the picture specified in txtFileName into imgPicture. The .picture property of an image control is the filename of the picture currently displayed in the control. You load a picture into the .picture property of an image control with the LoadPicture() function and the filename as the argument. txtFilename is the name of the file you want to load, so it is passed as the argument.
But what happpens if the user enters in a file that is not in the computer, or is not a valid windows bitmap? Well, the whole program shuts down with a mean message "Bad File Type" or "File Not Found!" That can be devastating to the users mental state, so we have to take care of that and only let the user enter in a valid windows bitmap filename. We'll take care of that when we get into the section on error trapping.
Well now you've written a couple simple programs with basic controls; it's about time you started learning about some more advanced controls and using some more advanced code!
In this section we're going to cover every control in the Standard edition with the exception of the three custom control, and practically everything you can do with that control. If you are already somewhat experienced with VB and know what the contr ols do, then skip this chapter. Once you get the hang of controls, however, it's not too hard to figure out for yourself how to use them. But some of the fundamental windows controls, like file list boxes and combo boxes, aren't self explanatory, so the y need some explanation. Well, here goes!
This control is like a form within a form. It's primary use is to display pictures, as the name implies, but it can also be used to contain other objects within a group like a frame, or to display messages. The print method can be used on the picture bo x control to display text, and other controls can simply be dragged into the box while in design time to make them part of picture box (in a sense). It is a good tool for when you want a colored region of a form or when you would like to group controls o n the form seperately, but there is not much to say about this control -- it's pretty straightforward. No special events are affiliated with the picture box control besides the regular _MouseMove, _MouseDown, etc.
In a Windows program you will often see text above or to the left of a text box identifying what the purpose of that text box is. That is a label control. The primary purpose of the label control is to show the user of your program what something on the form does, but it can also be used to display a message or some result calculation that is not intended to be changed by the user. The user cannot change the contents of a label unless you provide the means nessesary to do so. Text is displayed in a la bel control through it's .caption property. The .caption property can be set at run time or design time. You can also experiment with the other properties like .borderStyle to make a border arround the label, and .backStyle to make the background of the label transparent. Another usefull property is the .WordWrap property. If the length of the text you want to assign to the .Caption property exceeds the width of the label, then the words are placed on the next line. Make sure you make the label high enough to display at least two lines if the .WordWrap property is set to true.
Here's a more advanced control. The primary purpose of the text box is to display a method of the user typing information to the program. A text box an be a one line input box, or it can be several lines long, like a word processor form. The .text prop erty of the text box determines it's text, which can be set at design time or run time (by the program OR the user). It is a useful control, but not very advanced. You cannot place carriage returns through code into a text box using Chr$(13), and the te xt box can only contain text of one font and color at a time. Needless to say, you could not make a good quality word processor using the text box control with Visual Basic. There are four interesting properties that are affiliated with the text box.
- .MaxLength: For text input, you can put a limit on the number of characters the user can type into the text box with this property. For instance, if you created a text box where the user had to enter in his password and the password would always be 10 characters or less, you could set the .MaxLength property of the text box to 10. When the user tries to type the 11th character, he will recieve a beep.
- .Multiline: If the .Multiline property is set to true, then when the user presses enter the cursor will go to the next line, like a word processor. Be sure you make the text box high enough for the user to type several lines if you don't plan to make a Vertical scroll bar.
- .PasswordChar: If you've ever seen a password input box, you'll notice that the password the user types isn't printed in regular characters for the whole world to see; rather, asteriks are displayed for each character the user types. If you are doing a password input box, the .PasswordChar property can be usefull since it sets what character appears when the user types. You would usually want to set this to *, but anything else can be used as well.
- .ScrollBars: There are 4 possible values which can be set for this control:
You have also seen a lot of frames in the Windows opperating system; in dialog boxes inparticular. Frames are used to group a series of controls together. Grouping controls can have a number of effects. First of all, the coordinate system for a control is based on it's bounding control. Don't worry, I'll interpret that into English. Every control has an x,y coordinate, x being the distance from the left and y being the distance from the top. The coordinate specifies the upper left corner of the cont rol, and the .width and .height properties of the control define, well, the width and height. Coordinates are measured in twips, which is a unit used in GUIs. Twips are usually used instead of pixels because the screen resolution determines the size of the pixels. There are always 1440 twips in an inch. If you put a control at 0,0 in a form by itself, it's upper left corner will be at the upper left corner of the form. If you put a control which belongs in a frame or picture box at 0,0, it's u pper left corner will be at the upper left corner of the frame or picture box. See? Another use of grouping controls is for radio buttons. Radio buttons are like surveys that ask you to check one of the following. You see these in windows too. To have more than one radio button checked on a form, you seperate them into logical groups and the user can enable one button from each group. We'll get more into this concept when we get into radio buttons, a little further down. Frames also have a . caption property, which is like a label arround the frame border for the group so the user knows what that group is representing.
We'll be using command buttons a lot in VB programming. Command buttons are clickable options in windows like OK, CANCEL, RETRY, ABORT, FAIL, etc. When you click the button, the _Click event is generated for the control. The .caption property determine s what is displayed on the button. This is kind of a cool trick that applies to any control: when you put a & symbol in front of a letter in the .caption property, that letter is underlined. An underlined character means that the user can press ALT and that letter to click that control (or in most cases, just set the focus to it). I use the btn prefix for naming command buttons instead of cmd because I think of button not command when I'm in the code... so get used to it!
Check boxes are options that can either be checked, unchecked, or grayed. They are like survey things that ask you "what magazine(s) do you read, check all that apply." Then there would be a list of magazines, and you'd put a check infront of any that f it the description. In other words, more that one can be checked at a time. The .caption property defines what you are checking. This would be the name of the magazine in our example. The .alignment property can be set to either right or left, definin g where the box is in relation to the text. Most importantly, the value property defines the checkbox as being either check, unchecked, or grayed.
Radio buttons are like check boxes, only just one of them can be enabled at a time (plus there is no grayed value for the .value property). You will want to seperate your radio buttons in to groups using either frames or picture boxes. Radio buttons are like questions on a survey which ask you "What year of high school are you in," and the options are FRESHMAN, SOPHOMORE, JUNIOR, SENIOR, or NONE. You can't be more that one, so only one can be checked. If you have two "questions," then they should be s eperated into groups.
Here's the first advanced control we're covering. Combo boxes are drop down list boxes with a text entry box at the top. There are three styles of combo boxes:
form .name=frmCombo .caption="Favorite Food Selector" combo box .name=cmbFood .style=1 button .name=btnAdd .caption="&Add" .default=true button .name=btnOk .caption="&OK"Now for the code
Sub Form_Load () cmbFood.AddItem "Pizza" cmbFood.AddItem "Burritos" cmbFood.AddItem "Chicken Divan" cmbFood.AddItem "Nachos" cmbFood.AddItem "Pop" End Sub Sub btnAdd_Click () cmbFood.Additem cmbFood.Text End Sub Sub btnOk_Click () Const ICON_INFORMATION = 64 MsgBox "Your favorite food is " & cmbFood.Text, ICON_INFORMATION End SubWe add the items to the combo box when the program starts from within the Form_Load procedure. There are two buttons on the form: one to add new items to the box and one to select your item as your favorite food. If your favorite food is not on the list , then you can add it by typing it in the text box at the top of the combo box and pressing the Add button. In the btnAdd_Click event the current value of the combo box's .text property is added to the list using the .AddItem method. In the btnOK_Click event, the user's favorite food is displayed with a message box. This example is extremely trivial, but it demonstrates the point. Combo boxes also have a couple properties that are worth discussing. The most common property you will use is .text, whic h defines the text which is currently selected at the top of the box. Secondly is the .Sorted property, which gives you the option of sorting everything in the list part of the combo box alphabetically. When you add a new item, it is automatically place d in order with the rest.
A list box is a simple version of a combo box. Actually, a combo box is a combination of a text box and a list box, but let's not get technical! A list box can do everything a combo box can do, but since it doesn't have a text box part, nothing appears to happen when you select something. However, the .text property is being set just as before. One thing that is the same with combo boxes that I didn't talk about in the previous section is the .list property of the controls. The .list property applies to a list box or combo box and can be used only at run time. You use it to set or read an item on the list. Here's a new concept: every item on the list is assigned a number called an index when it is added. The first item in the list has an index of 0, the second, 1, and so on. So using the .list property, you could set the third item in the list like this (assuming that the listbox's name is lstPrime):
lstPrime.List(2) = "Fruit". The return value of the .List property is the
text in the list box. Another cool property unique to list boxes is the .columns property. This defines the number of columns that can fit on one "screen" of the list box. If the .columns property is set to 0 (default) then when the number of items exce
eds the maximum length of the list box, a vertical scroll bar enables you to scroll down and see the rest. If the .columns property is a non-zero, that many columns can be seen in the list box. There will not be a vertical scroll bar, but rather a horiz
ontal scroll bar. Say .columns is set to 3, and you have a lot of items in the list box. Three columns of items appear when you start it up, and a horizontal scroll bar is at the bottom. When you go right, three more columns appears. That's how that w
orks. You don't use it a lot, but it comes in useful.
Horizontal and Vertical Scroll Bars
A scroll bar is a very common windows element that lets the user give input on a scale and at the same time, gives the user output on a scale. This scale is manipulated by either clicking an arrow on the scroll bar (there is one at each end), clicking th e elevator (the empty space on the scroll bar that is not a handle and is not an arrow), or by moving the handle. As you may already know, clicking the arrows increments the data a little, clicking the elevator usually increments the data a lot, and movi ng the handle increments the data at your own pace. The most common use of these controls in Visual Basic is to input data on a scale, like giving a color value or a temperature. Using different forms of input makes your programs more interesting. There are a couple properties and events worth mentioning for the scroll bars. The most nessesary value of the scroll bar is the .Value property. There is a numerical value for every position the handle has on the scroll bar. The value is changed by definin g the .LargeChange and .SmallChange properties, and bounded by the .Min and .Max properties. The .LargeChange and .SmallChange properties set the ammount the scroll bar's value is itterated at run time by the user. The .SmallChange applies to when the us er clicks an arrow, and the .LargeChange applies to when the user clicks somewhere in the elevator. The standard is .SmallChange=1, .LargeChange=10. The .Min and .Max properties set the minimum and maximum values of the scroll bar. On a horizontal scro ll bar, the minimum value is the leftmost position of the scroll bar. On a vertical scroll bar, the minimum value is the top value of the scroll bar.
As for events, the scroll bars have two main ones: _Change and _Scroll. The _Change event occurs whenever the user 1) Clicks an arrown, 2) Clicks the elevator, or 3) Moves the handle, but not while the user is moving the handle. The _Scroll event occurrs only when the user is moving the handle. When one of these events is called, the value of the scroll bar as obviously been changed. Let's write a quick program that demonstrates the usage of the scroll bars.
form .name=frmScrollDemo .caption="Scroll Bar Demo" vscroll bar .name=vsbRed .Max=0 .Min=255 .LargeChange=10 .SmallChange=1 vscroll bar .name=vsbGreen .Max=0 .Min=255 .LargeChange=10 .SmallChange=1 vscroll bar .name=vsbBlue .Max=0 .Min=255 .LargeChange=10 .SmallChange=1 picture box .name=picColorNotice the .Max and .Min properties of the scroll bars seem like they would be backwards the way I have them. Well, remember that the minimum is at the top! In this case, we want the minimum to be at the bottom, so we just pretend that the maximum is a minimum. No harm done. Here's the code:
Sub vsbRed_Change () picColor.BackColor = RGB(vsbRed.Value, vsbGreen.Value, vsbBlue.Value) End Sub Sub vsbRed_Scroll () vsbRed_Change End Sub Sub vsbGreen_Change () picColor.BackColor = RGB(vsbRed.Value, vsbGreen.Value, vsbBlue.Value) End Sub Sub vsbGreen_Scroll () vsbGreen_Change End Sub Sub vsbBlue_Change () picColor.BackColor = RGB(vsbRed.Value, vsbGreen.Value, vsbBlue.Value) End Sub Sub vsbBlue_Scroll () vsbBlue_Change End SubThe code looks pretty redundant, doesn't it. Well, there is a better way to do it, but we'll get into that when we get into scope! What we've created here is a form with three vertical scroll bars on it and a picture box. The scroll bars corresp ond to the red, green, and blue values that will be the color displayed in the the picture box. When the _Change event is called for a scroll bar, the background color of the picture box is updated to correspond with the values of the scroll bars. When a _Scroll event occurs, we simply call the _Change event instead of typing that long line out all over again. This works very effectively. Even more sneaky would be to put the picColor.BackColor = line in one of the scroll bars, and then call thi s event from the other ones without typing the line over again. However, sacrificing readability for sneakyness is not wise, especially if you happen to be programming something with other people. So that's the program, and that's scroll bars!
Ahh, this is a fun control. It is very simple and straightforward, and it is extreemely useful. When you want code to be executed many times in regular intervals, an option is to use a timer control. All you have to do is draw the control on the form y ou want to use it on, set the .interval property, and add code to the _Timer event. The .interval property defines how often the code in the _Timer event will be executed, and is measured in milliseconds. Therefore, if you wanted the code in the _Timer event to be executed once every second, you would set the .interval property to 1000. Here, let's write a quick program that prints the current time in a label on the form, and updates it twice every second.
form .name=frmClock .caption="VB Timer Clock" timer .name=tmrClock .interval=500 label .name=lblTime .caption=""All the code you need for this program is defined nicely under one procedure
Sub tmrClock_Timer () lblTime.Caption = Now End SubIs that the shortest program we've written? I think so. A couple things about this. The Now function returns the current time. Notice also that it returns the current date. We'll work on formatting that when we get into doing some more advanced code. Also notice I wrote
lblTime.Caption =instead of just
lblTime =as I have been doing before. Well, using just
lblTimeis called using the default property. Every control has a default property, so you can just ap parently assign a value to the control and it is actually setting it's default property. The default property for the label is the .caption property, so I don't need to write the fully qualified name. I'll talk more about this at the end of the section. But anyways, now you see how easy timers are to use. I don't use them a whole lot, but they are virtually manditory for games and time sensitive programs.
File Access Controls: Drive List Box, Directory List Box, and File List Box
Visual Basic provides a very simple way of using files on your computer. We will get more into file i/o later (which is exactly the same as QBasic) but these controls simply provide a method of accessing files on your (or the user's) disk drive. The co ntrols I'm referring to are most commonly seen when you open up a SAVE or OPEN dialog box in the windows environment. A drive box selects the drive you want to view the files on, a directory list box lists the directories on that hard drive, and a file l ist box lists the files in the currently selected directory (usually changed in the directory list box).
For the drive box and the directory list box, there is a _Change event which occurs when ever the drive is changed or the directory is changed. You have to manually code what happens when this event occurs. When you change the drive in the drive box, th e directories in the directory list box don't automatically change to the directories on the drive selected; you have to set the .path property of the directory list box to the drive of changed in the drive box. The drive box has a .drive property which describes the current drive displayed in the box (such as "a:" "c:" or "d:"). The directory list box has a .path property which describes the path of the current directory selected in the box (such as "c:\" "c:\windows" or "d:\windows\system"). Notice t hat when the root directory ("a:\" "c:\" or "d:\") is selected in the directory list box, a backslash comes after the drive letter, but when any other directory is selected, there is no postfixed backslash.
Like the directory list box does not automatically display the directories from the currently selected drive, the file list box does not automatically display the files from the currently selected directory. Both the directory list box and the file list
box have a .path property, and they both work the same way. In fact, you would usually put a line like
flbFiles.Path = dlbDirectories.Path in the directory list box's _Change procedure. The only difference is that the file list box does not
have a _Change event, it has a _Click event. Also, the .path property for the file list box does not set the currently selected file, it sets the files that are displayed in the list box. The .FileName property is describes the currently selected file,
but does not include the path. When you select a file in the file list box, the _Click event is called and the .FileName property gets set to the file you selected.
In addition to the .Path and .FileName properties of the file list box, there is also the .Mask property which can be set at either design or run time, and determines what file types will be displayed in the box. For instance, if you only wanted to displ ay .bmp files, you would set the .Mask property of the file list box to .bmp. It's that simple. So lets write a program that allows the user to go through the drives and directories on their computer and select a file. When that file is selected, it's complete path is printed in a label control.
form .name=frmFileBrowser .caption="VB File Browser" drive list box .name=drvBrowser dir list box .name=dirBrowser file list box .name=flbBrowser label .name=lblFilename .caption=""Now for the code. Don't worry, I'll explain it afterwords.
Sub drvBrowser_Change () dirBrowser.Path = drvBrowser.Drive End Sub Sub dirBrowser_Change () flbBrowser.Path = dirBrowser.Path End Sub Sub flbBrowser_Click () If Mid$(flbBrowser.Path, Len(flbBrowser.Path), 1) = "\" Then lblFilename = flbBrowser.Path & flbBrowser.FileName Else lblFilename = flbBrowser.Path & "\" & flbBrowser.FileName End If End SubWhen the user changes the drive in the drive list box, the drvBrowser_Change event is called, and we set the path of the directory list box to the drive in the drive list box (the root directory on that drive). When the user changes the directory in the directory list box, the dirBrowser_Change event is called and we change the path of the file list box. When the user selects a file from the file list box, the flbBrowser_Click event is called, and we run into some intimidating code. Recall that the Mid $() function returns a character from the specified string (1st argument) starting at the specified location in that string (2nd argument) for the durration specified (as the 3rd argument). The string we want to search in is the path of the file list box . We want to see if the last character is a backslash, and if it is, we know that we are in the root directory and all we have to do is append the filename to the path. But if we aren't in the root directory (the last character in the path isn't a backs lash) then we have to sandwhich one between the path and the filename to make it correct. Why can't we just do this in the first place and skip all this conditional testing? Well, if we were in the root directory we would be assigning two backslashes to the fully qualified file name, and the compiler would grunt an unfriendly error at us. So try this program out, and you'll see that manipulating files on the hard drive is more difficult than QBasic's
MKDIRmethod, but graphical interfaces can require a lot more work!
The Shape tool provides a method of making simple geometrical shapes on a form simply by drawing them out and setting the .Shape property. A shape can either be a rectangle, a square, a circle, oval, rounded rectangle, or a rounded square, depending on t he value of the .Shape property. There are no events for a shape object, but there are a few properties worth noting.
The line control works virtually the same way as the shape control with a couple property differences. You have the .BorderColor, .BorderStyle, and .BorderWidth properties that define the attributes for the color, style, and thickness of the line that is drawn.
The image tool is primarily used for displaying images (duh). Pretty much everything I say here applies to the Picture box control as well with the exception of the .Stretch property. Pictures can be loaded into visual basic, as long as they are either Windows Bitmaps (.bmp - the most common), Run length encoded files (.rle) or Windows Metafiles (.wmf - clipart). You'll probably never use the latter two. You can load a picture into the image control by setting the .Picture property of the image contro l at design time. As mentioned earlier, it is different if you want to load a picture into an image control at runtime; you have to use the LoadPicture() function. The code for loading a picture into an image control called imgPicture looks like this:
imgPicture.Picture = LoadPicture(filename) where filename represents any valid .bmp, .rle, or .wmf file. It's pretty easy.
The .Stretch property can be set which scales the loaded picture to the size of the image control frame. Be warned, however, that if you enable this it can seriously distort your pictures, so small ones have really big pixels when they are loaded. It do esn't do any permenent damage, but it doesn't always accurately display what the picture really looks like. If you don't enable the .Stretch property, the upper left corner of the picture will be cropped to fit the frame.
Data Access Control
I'm not going to go into full detail about this control until the section on databases, but just keep in mind that this is what this control is used for. You can control the data in a database very easily and without even using code with Visual Basic sim ple data access features, although a little code can make all the difference!
Well now you have a very good knowlege of the controls that ship with the standard version of Visual Basic. As I mentioned above, I need to talk a bit about default properties. Every control has a default property, which is usually the most used propert y of that control. For instance, the default property of the label control is the .caption property, for the text box it is the .text property, and for the image control it is the .picture property. This means that you don't have to type out the full co ntrol name and property for each control. The following pair lines of code have identical effects.
textbox.text = "text" textbox = "text" label.caption = "caption" label = "caption" image.picture = LoadPicture(picture.bmp) image = LoadPicture(picture.bmp)To avoid confusion, I will usually use the full qualified name (with the property after the control), unless it's something totally obvious or monotonous like lable.caption or textbox.text.
From what we know now, Visual Basic seems very constrained to small events that don't seem to be able to interract with each other. However, Visual Basic supports multiple forms, code modules, and user defined procedures within a program. From our ex perience with Pascal (I do hope you read at lease some of the Pascal tutorial) we've seen that some variables maintain their value for the whole program and can be accessed from any procedure, some variables are visible in a procedure becaus e they were passed to it from another procedure, and other variables are only visible within the procedure they were defined, and maintain their value until the end of that procedure. Visual Basic takes this a step further. You can have a base (or globa l) code module in which you define global types, variables, and constants which are meant to maintain their values for the durration of the program. These modules contain only code -- they are not related to any control or visual element and do not conta in events. Base modules have a .bas extension. There is also the form declaration module, which every form possesses. In the definitions block you define variables and procedures which are meant to be local to that form with respect to the entire progr am ... things defined in the definitions section of a form are not visible to another form. Lastly, there are the individual procedures and events which are visible only to themselves. Let's discuss each of these elements in detail.
Base Code Modules: To create a base code module, just click the second icon from the left on the toolbar. It looks like a bunch of little cells or something, I don't know. Base code modules do not belong to any form, and have a .bas extension. I n a base code module you define variables, types, and constants which are to be read from all other forms in the program. To declare a variable in a base code module, use the Global keyword instead of Dim. This will make the variable a global variable, which can be access from anywhere in the program. For example, say you wanted a variable called score which could be read from all the forms in your program.
Global score As IntegerThat's all you have to do. So why, you ask, don't you just put all your variables in a base code module so you don't have to worry about scope? Here's three answers:
Aside from just variables, you can also have procedures. Scoped procedures is a new concept to the languages we have studied so far (QBasic and Pascal), and it can be pretty usefull. The procedures defined from a base code module are what you are used t o working with -- global. If a procedure is called from many different forms and outside procedures, it would be a wise option to define them in a base code module. Some programmers also write a main procedure in a base code module where the program exe cution order is determined. You use this method in Pascal and C++ especially. You can load the forms of your program in any order you choose through a main procedure, and it definitely the method you will want to go with if you are using a start to end program, like a quiz or debt ratio worksheet.
In this tutorial, I will seperate modules and forms with a string of === signs so you don't get confused, and write the module name within the symbols. Example:
==== Base Code Module ==== Global a As Integer Global b As Integer Global c As Integer Function AddNumbers () AddNumbers = a + b + c End Function ==== frmInputVariables ==== Dim d As String Dim e As String Sub PrintStrings () lblD.Caption = d lblE.Caption = e End SubThis isn't real code, so don't worry. The first three declarations declare global variables a, b, and c which are integers. They can be accessed anywhere. The function AddNumbers() can also be accessed from anywhere. In frmInputVariables two variables local to the form are defined. The procedure PrintStrings() is also local to the form -- it cannot be accessed by another form. In conclusion, use your global variables wisely. They add unessesary complexities to your program, but they are sometimes n essesary. You'll get a feel for it!
Form Modules: Like base code modules have definition blocks and procedures, forms also contain these. However, unlike in base code modules, Global variables cannot be defined here. You use the Dim statement like in QBasic. Variables declared in
the Definitions block of a form are local to that form with respect to the whole program (and global to that form period, but let's not get picky). This means that any variable defined here can be accessed by any function, procedure, or event from the en
tire form. This is very usefull for cutting down on overhead usage. These variables do not take up as much overhead because they are allocated in memory only when the form is loaded. If a form is unloaded, they loose their value. Furthermore, variable
s defined in one form's Definitions block, those variables are not visible by other forms. To make variables visible to all forms, place them in the Definitions block of the base code module, as mentioned in the previous section. You define procedures t
he same way as in a base code module, only as usual, they are only visible to that form.
You can feel free to use form variables less sparingly than global variables because they are good programming style and do not waste so much overhead. Sometimes it gets confusing to pass conventional values from event to event, so in my opionion form va riables are the best way to make variables visible between multiple controls.
Local Blocks: Local blocks include procedures (Functions and Subs) and events (which is really a procedure). Variables defined within a procedure are local to that procedure, meaning that they cannot be used by any other procedure besides the one they are defined in. This means that one procedure could define a variable, and another procedure could define a variable of the same name, and they would use them sperately. The only way a variable defined in one procedure can be accessed by other proc edures is if you pass it. Concider the following example:
Sub procedure1 () Dim variable As String variable = "SHATTERED PERSPECTIVES GAMES" procedure2(variable) End Sub Sub procedure2 (text As String) Const ICON_INFORMATION = 64 MsgBox text, ICON_INFORMATION End SubI need to lay off the trivial examples! Anyway, a variable aptly named variable is defined in procedure1. A value is assigned to it. Then procedure2 is called with variable as the argument. In procedure2, the variable passed to it (variable) becomes t ext, but it still has the same value (SHATTERED PERSPECTIVES GAMES). Then the icon constant for the information symbol is defined for the message box. Finnally, a message box pops up telling you the company to contact if you want great shareware games. Simple enough?
Some events are regularly called more than once, such as the _Timer event for the timer control. When a procedure is done executing, everything defined in that procedure goes out of scope, looses it's value, and is deallocated from memory (the same thing ). But what if you want to preserve the value of one of these variables in a procedure which is called more than once, and you don't need to define it in a global block because it is only called in one procedure? The answer is the Static keyword, which says that space for this variable is allocated when the program starts, just like a global variable. But a variable declared static is not accessable from other procedures, just itself. To declare a variable static, just put the Static keyword in the pl ace of Dim. This will ensure that the value stored in the variable is not lost for the durration of the program.
Now that you have a good understanding of scope, let's write a quick program which demonstrates the usage of what we know. We'll write a stopwatch like program which has a timer and a readout in one form, and a start and stop button in another. We could
put them in the same form, but then I wouldn't be able to demonstrate static variable allocation!
Before we start though, there are two statements and a method you'll need to learn. When using more than one form in a program, it might be helpful to know how to load and unload these forms. Well, the programmers of Visual Basic thought that it should be obvious, so to unload and load a form into memory, you use the load and unload statements. To load a form called frmLoadMe, just write
load frmLoadMe anywhere in the program. Ok, you do that, and nothing seems to happen. Well, th
e load an unload statements only load the form into memory! frmLoadMe is there, but you just can't see it. To make the form visible, you use the .Show method on it. But the .Show method automatically calls the Load statement, so the Load statement is u
sually pretty useless. So to load a form and show it, you would write
frmLoadMe.Show. To unload a form, you use the Unload statement. The Unload statement unloads the form from memory and takes it off the screen, so that's nice. So
when would you ever want to use the Load method? Well, many large programs load all the memory they need at the beginning so the program runs faster when the user is using it. They would do this with the Load statement in the Form_Load procedure of the
startup form or the main procedure, then use the .Show method to show it. But for now, just use .Show to load a form and Unload to unload it. So let's write our [useless] stopwatch program.
form .name=frmControls .caption="Stopwatch Controls" button .name=btnStart .caption="Start" button .name=btnStop .caption="Stop" form .name=frmReadout .caption="Stopwatch Readout" label .name=lblReadout .caption="0" timer .name=tmrStopwatch .interval=1000We'll have code in both the frmControls form and frmReadout form.
==== frmControls ==== Sub btnStart_Click () frmReadout.Show End Sub Sub btnStop_Click () Unload frmReadout End Sub ==== frmReadout ==== Sub tmrStopwatch_Timer () Static timeElapsed As Integer lblReadout.Caption = Str$(timeElapsed) End SubIn frmControls, we have two buttons, one which starts and resumes the time, and one which stops (or pauses) the time. In frmReadout, we declare timeElapsed as a Static Integer and print that value in lblReadout. The value of timeElapsed starts at 0 from the start of the program, and keeps it's value from until the program ends.
Well that's pretty much it for scope. There's not really much to learn about it. Just remember that there are three places which have different scopes where variables can be declared in Visual Basic: Base Code Modules, Form Declaration Modules, and Loca l Procedures. Variables declared in Base Code Modules are global to the whole program. Variables declared in the Form Declaration Modules can be accessed by any function belonging to that form, but only by that form. Variables declared in local procedu res are local to those procedures unless values are passed to another one. Finnally, remember that when a procedure is done, all the variables defined in that procedure loose their values unless they are initialized as static.
You've come to the point where you can draw a control on a form, define the properties for that control, write some code for the control, and run the program. Ideal as this sounds, all programs cannot be run simply through this process -- there is sti ll more to learn! I help a lot of people with Visual Basic problems, and the majority of them concern control arrays. Control arrays are not difficult once you grasp the basic concept of them.
Say you have a form with 100 command buttons and they all accomplish pretty much the same task -- they print their caption to a label control. Now what would be better; to draw 100 command buttons and name them btnPrint1 through btnPrint100, or should yo u find a better way to do it? In this scenario you would undoubtedly use a control array. A control array is exactly what the name implies -- an array of controls. So instead of 100 different names, you would have btnPrint(1) through btnPrint(100). We 'll talk about how to set up a control array.
In a control array, there is one control which the other controls take after when they are created. Each control in a control array also has an index so we can determine which one is to be used. You assign the index of each control by giving the control a value in it's .index property. If a control has a value in it's .index property, then it is assumed by the program to belong to a control array. Every control in the control array also has the same name, and they are distinguished as different contro ls through their index property. Let's set up a program with 5 command buttons that are part of a control array. When you click on one of the 5 buttons a notice will be displayed in a label indicating which button has been clicked.
button .name=btnControl .index=0 .caption="Button &1" button .name=btnControl .index=1 .caption="Button &2" button .name=btnControl .index=2 .caption="Button &3" button .name=btnControl .index=3 .caption="Button &4" button .name=btnControl .index=4 .caption="Button &5" label .name=lblNotice .caption=""Notice that the indexes start from 0 and go to 4, but the captions indicate the button's number is one greater than it's index. We'll take this into account when we code the controls. All the controls in a control array share the same code, and each eve nt of a control in a control array is passed the control's index. So you can use this index argument in your code, which we'll do here:
Sub btnControl_Click (Index As Integer) lblNotice = "You selected button " & Str$(Index + 1) End SubPretty straightforward. We're just setting the caption of lblNotice to indicate which button we pushed. We add one to Index because the first index is 0, and the caption of the first button indicates that it is number 1. So we take this into account by adding one to the index.
Now, drawing every control from a control array on the form manually, then setting the name of control and it's index can be monotonous, and sometimes impossible if we don't know how many controls will be in the array at design-time. We can load controls from a control array dynamically at run time if we just have the first existing control. Let's load a control array of check boxes dynamically at run time.
There are a couple things you'll need to know before we begin, however. Each control that has not been created at design time must be loaded using the Load statement. A control is not visible by default when it is loaded, so you must set it's .visible p roperty to true after you have loaded it. Also, a control is placed in the same location as the control it was loaded from, so you'll have to set the position from within the code. The caption of each control will also be the same as the original, so yo u'll have to set that too. Let's make a short program that loads 10 check boxes and prints in a label how many check boxes are currently checked.
Check box .name=chkControl .index=0 .caption="Option &1" Label .name=lblStatus .caption=""We'll load the other check boxes from within the code. Notice you have to set the original control (in this case the check box) to .index=0 to identify that it will be in a control array.
Sub Form_Load () For i% = 1 To 9 '0 is already loaded Load chkControl(i%) chkControl(i%).Caption = "Option &" & Str$(i% + 1) chkControl(i%).Top = chkControl(i%-1).Top + chkControl(i%-1).Height + 100 chkControl(i%).Left = chkControl(i%-1).Left chkControl(i%).Visible = True Next i% End Sub Sub chkControl_Click (Index As Integer) Static numChecked% If chkControl(Index).Value = 1 Then numChecked% = numChecked% + 1 Else numChecked% = numChecked% - 1 End If lblStatus = Str$(numChecked%) & " options are selected" End SubIn the Form_Load procedure we have a loop from 1 to 9 to load the remaining 9 controls in the control array. The first line in the loop loads the check box of index i%. Then the caption is set, then the vertical location, the horizontal location, and fi nnally the control is made visible. The line where we set the vertical location looks complex, but if we think about it, it starts to make sense. We are setting the .Top property of the control by setting it equal to the last loaded control's top proper ty, plus it's height, plus 100. There are 100 twips between the two controls this way. i% - 1 referrs to the last loaded control.
In the chkControl_Click event we have a static integer numChecked% which holds the number of check boxes that are currently checked. When the user checks or unchecks a check box, the _Click event is called and the .value property is set to either 1 (chec ked) or 0 (not checked). If the check box has been checked, the value will be 1 and we can increment the number of currently checked boxes. If the value is 0, then we know it must have been checked before and now it is being unchecked, so we decrement n umChecked%. Then lblStatus is set to indicate how many boxes have been checked.
You will be using control arrays a great deal if you plan to continue in Visual Basic. They can save a lot of time and overhead, but since you usually load them from code, it can be difficult to position them if you don't know how many there will be at d esign time. For instance, if there are 100 controls to be loaded, you more than likely won't be able to position them all vertically. You'll have to set the .Left property so you can position them in columns, which can get complex.
More to come!