During this hour you will learn
This hour's lesson contains some code used in the VBScheduler application. This lesson includes some examples of the VBScheduler code so that you can get an idea of how code ties things together and finalizes the application. This lesson previews the code that you will ultimately learn to write.
Be warned that the code in the VBScheduler takes the previous 34 lessons one step beyond their normal flow. In other words, if you've studied to this point, you're ready for the more advanced topics that this lesson discusses to code VBScheduler. If you jumped ahead to this fast-track part of the book, you might be surprised at how much you'll learn by going back through the earlier lessons. If the code here seems cryptic, it won't after you finish the earlier lessons in this course book.
Hang on to your thinking caps, because this coding survey of the Visual Basic Scheduler is about to begin!
FAST TRACK |
To understand code better, refer to the lessons in Part II, "Programs That Do Work," to learn what the code means that you read about here. Part II begins with Hour 6, "Understanding the VB Program Structure". |
Before you go further, choose Properties from the Project
menu and enter Figure 35.1's Project Properties dialog box values.
You enter a value for the Project Name text box and for
the Project Description.
Prepare the project properties before adding code.
The
Startup Object is Sub Main, meaning that the primary form's form module (named frmMain) doesn't take over when the application begins. Instead, an external standard module's procedure (named Main) that you add takes over. Sub Main tells Visual Basic to read the external module's code for startup instructions.![]()
Sub Main appears in an external module called the standard
module. Before you can use the standard module, you must add
the module to the project. If you want to follow along, you can
add the standard external module by choosing Add Module
from the Project menu, double-clicking the Module icon,
and then saving the module under the file name modSched.bas.
To code or to load?
The complete VBScheduler application is on the accompanying CD-ROM. The code is quite lengthy! This lesson could have listed all the code in the project and you could have typed all that code, but you wouldn't have learned a lot from that exercise. Whether you're a programming guru in Visual Basic or any other language or a programming novice, such lengthy code at this point would do little for you.
Therefore, now that you've prepared all of the project except for the code, you can now read the rest of this lesson to get a fast-track approach to understanding the way Visual Basic code interacts and extends the form. If you want to enter some lines of code, you can do that and become familiar with the editor. You very easily could find that the fast-track approach this part of the book uses is too advanced at this point because there is no way to explain all the ins and outs of the Visual Basic programming language (that's what the rest of the book does). Study the project from the CD-ROM. Now that you've seen a preview of creating a project and adding controls to the form, load and run the VBScheduler application from this book's CD-ROM. When you go back through the lessons, you'll be better prepared to tackle details such as this project's code. When you load the application from the book's CD-ROM, you can use your Code window editor's find tools or select from the Object Browser to locate the code being discussed in this lesson.![]()
As stated, this part of the book is a fast-track section that shows the process of building a complete application. This part of the book can't even begin to explain all the details behind the application's controls or language, or even all the details needed to create the project from scratch. Therefore, this lesson doesn't spend any time describing the fundamentals, such as the need for variables, because you either know those things already or you're here to preview the things you're going to learn when you go study the tutorial's lessons.
![]()
As you load this lesson's application into the Code window and follow the short code segments that this lesson duplicates, notice in the Code window that the programmer uses Option Explicit at the top of both code modules. The option forces you to declare all variables or otherwise get an error message when you run the program. By requesting that Visual Basic require you to declare all variables, you eliminate misnaming errors that can occur and be difficult to trace without the option set. Option Explicit always appears in the General section of a module before any variables (global or local) and before other code begins.
Listing 35.1 shows the code found in the subroutine procedure named Main. Load the VBScheduler application if you haven't done so already. Double-click the Project window's modSchedule object and scroll down a few lines to see Listing 35.1's code. VB code consists of several small routines called procedures. Main() is the first procedure executed when you load and run the application (the parentheses after the name indicates that Main() is a procedure and not one of the other kinds of Visual Basic objects).
FAST TRACK |
To learn more about procedures, turn to Hour 13, "Understanding How Procedures Work". |
Listing 35.1 The Sub Main Startup Instructions
Sub Main() 'Load the form and let it run the code in the ' Form_Load event handler Load frmMain 'Intialize the contact list combo box Call InitComboAsDb(frmMain.cboName, frmMain.dataMain) 'Fill the appointment list with today's appoinments Call GetDailyAppointments(CDbl(CDate(frmMain.FormDateString())), _ frmMain.lstSchedule, _ frmMain.dataMain, _ gAppointmentDelta) 'Show the main form frmMain.Show 'Set the mouse pointer back to an arrow frmMain.MousePointer = 0 End Sub
Right after the VBScheduler application starts, the frmMain form loads into memory (the form doesn't display just yet, but the code must place and initialize the form's controls), initializes the form's combo box from the database, and finally shows the form on-screen.
This Main subroutine is a little misleading. Although the routine looks short and simple, you must keep in mind that virtually everything that happens to objects triggers events. Therefore, the Load statement does load frmMain into memory, but the Load statement also triggers the Form_Load() event procedure for that form after the form loads. Therefore, you must follow the startup progression even further (throughout the next couple of sections) before you've truly mastered the application's startup process.
Throughout the application's code you'll find remarks-messages preceded by an apostrophe that describe the program. The application's author inserted these remarks to help you follow the program and learn from the code. The remarks describe in plain language what the code does.
![]()
FAST TRACK |
To learn more about remarks, look at Hour 6, "Understanding the VB Program Structure". |
VBScheduler contains several global literals and variables that you need to understand before going much further with the executing code. You've seen examples of Visual Basic's named literals throughout this tutorial, but next you see a list of programmer-defined named literals (remember that constant is another name for literal). Listing 35.2 contains the General section. This code goes above the Sub Main procedure in the global section because Main, as well as every other procedure in the project, might use these global values and named literals.
FAST TRACK |
To learn more about literals and globals, read Hour 7, "Handling Data". |
Listing 35.2 The Sub Main's Global Declarations
Option Explicit 'Main Form Width Public Const FORM_WIDTH = 440 'Control sizing literals Public Const COLUMN_GUTTER = 1 Public Const COLUMN_MEMO = 48 Public Const FRAME_RATIO = 0.8 Public Const LIST_RATIO_H = 0.95 Public Const LIST_RATIO_V = 0.9 Public Const GUTTER_HORZ = 6 Public Const GUTTER_VERT = 6 'Move literals for frmMain.PollForDataChanges Public Const MOVE_NEXT = 1 Public Const MOVE_PREVIOUS = 0 'View increment literals Public Const QUARTER_HOUR = 15 Public Const HALF_HOUR = 30 Public Const FULL_HOUR = 60 'Database literals Public Const DATABASE_FILENAME = "vbSched.mdb" Public Const DQ = """" 'Error literals Public Const APPOINTMENT_MADE = 0 Public Const ERR_MATCHING_TIME = 1 Public Const ERR_NULL_APPOINTMENT = 2 Public Const ERR_APPOINTMENT_CONFLICT = 3 'Sizing globals Public gAppointmentDelta As Integer ' Time change granularity Public gColOneSize As Integer 'Global for size of 1st column Public gColTwoSize As Integer 'Global for size of 2nd column Public gColThreeSize As Integer 'Global for size of 3rd column Public gAppointmentField As Integer 'Global appointment width Public gComboBox As ComboBox 'The combo box in edit mode Public gComboText As String 'Global value for gbComboEditOn examination Public gLeftFramesWidth As Integer 'Global left frame's width Public gGutterHorz As Integer Public gGutterVert As Integer 'Appointment String Globals Public gOpenApptLine As String ' String denoting an open appt Public gClosedApptLine As String ' String denoting a closed appt 'Semaphores Public gbAddOn As Boolean 'Indicates a contact ADD in progress Public gbEditOn As Boolean 'Indicates an app wide EDIT in progress Public gbKBEditOn As Boolean 'Indicates that a contact is being edited Public gbComboEditOn As Boolean 'Indicate a combo dropdown Public gbInitOver As Boolean 'Indicates that all the controls are loaded 'and ready for poplulating Public gCurrentApptIndex As Integer 'Index of item being edited Public gcrContactBuffer As ContactRec 'Holds last old record data Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Type POINTAPI X As Long Y As Long End Type Type ContactRec fn As String 'First Name ln As String 'Last Name cmp As String 'Company addr As String 'Address city As String 'City st As String 'State zip As String 'Zip ph As String 'Phone fax As String 'Fax em As String 'Email End Type Type Appt num As Integer sTime As Double eTime As Double Comment As String End Type
Many of the named literals define control and frame placement that other routines will use. Converting numeric literals, such as 15, for the quarter-hour time intervals helps document the rest of the program that uses the named constant over the literal itself. The uppercase names for the literals help remind you that the value is constant and can't change, unlike variables.
The code does declare a strange-looking function procedure as follows:
Declare Function GetCursorPos Lib "user32" [ccc](lpPoint As POINTAPI) As Long
GetCursorPos() is a Windows procedure, not a Visual Basic procedure. Visual Basic can make calls to internal Windows API (Application Programming Interface) routines as long as you know which routines are available. Windows supplies the GetCursorPos() function so that applications such as yours can locate the mouse cursor when needed. VBScheduler needs to track when the mouse cursor resides over open appointment. This strange-looking Windows API declaration warns Visual Basic that GetCursorPos() is a Windows routine and not one that you forgot to add to your own application's set of procedures.
Do the globals (declared with the Public keyword, meaning they're available to the entire project) violate data safety rules that suggest variables should be local? The globals refer to screen-positioning and text-alignment values. Also, the globals define programmer data types so that every routine in the entire project can safely use the defined record data types and declare user-defined variables from those global record types. (For example, the POINTAPI user-defined data type holds the x- and y-coordinate values returned from the Windows API GetCursorPos() function.) If the record type descriptions weren't global, you could declare such variables only from whatever procedures had access to the descriptions.
Many programmers declare object variables (as done in Listing 35.2 for a combo box variable) globally. Those variables are representing objects on the form, and the global objects make the variables available to the entire project just as the form's controls are available to the entire project.
![]()
Listing 35.3 shows the frmMain form's Form_Load() event procedure. This code, after the global initialization, does the first real job of the application. Throughout the application, notice that the programmer chose to use data-type suffix characters rather than data type-prefixes. You can compare this variable-naming method to the ones used in earlier lessons and choose your preference. Many programmers prefer suffixes because they don't have to type as much as the three-letter abbreviation requires.
Listing 35.3 The Form_Load() Event Procedure
Private Sub Form_Load() Dim FirstLine As Integer ' Location to sync first label to MousePointer = 11 frmMain.Height = FORM_WIDTH * Screen.TwipsPerPixelX Call InitGlobalValues Call InitHelpIDs Call SetDateFrame(frDate, frmMain.ScaleTop, frmMain.ScaleLeft) Call InitDateFrame(frDate, cboMonth, cboDate, cboYear, lblDay, _ lblCurrentTime, lblTime) Call SetAppointmentFrame(frAppointments, lstSchedule, frDate) Call SetContactFrame Call InitContactFrame(lstSchedule.Top) Call PopulateDataCtrls(8) Call InitCmdButtons gAppointmentDelta = QUARTER_HOUR Call SetFrames(frDate, frAppointments, frContact, Me) Call InitDataBase Call CenterForm(Me) gbInitOver = True End Sub
You took the trouble of placing controls on the Form window in Hour 34. This Form_Load() event procedure does the rest of the initial formatting of the form and its controls. The programmer used good programming techniques. Because the code that executes due to the Form_Load() event procedure is lengthy, the programmer broke the code into many separate and smaller procedures that the Form_Load() simply calls. The smaller procedures help make the application easier to debug because you can narrow down problems and make bug fixes without adversely affecting surrounding code.
After the Form_Load() event procedure initializes the global variables (see Listing 35.4), the rest of Form_Load() concerns itself with initializing the form's controls.
Listing 35.4 Initializing Global Variables
'************************************************ 'Sub/Function Name: InitGlobalValues ' 'Arguments: None ' 'Return: None ' 'Remarks: This Sub is used to initialized the global variables ' values. ' 'VB concept demonstrated: Screen.TwipsPerPixelX (Y) ' TextWidth() ' Variables with global scope. ' '************************************************' Public Sub InitGlobalValues() Dim tWidth% tWidth% = frmMain.TextWidth("t") gGutterHorz = GUTTER_HORZ * Screen.TwipsPerPixelY gGutterVert = GUTTER_VERT * Screen.TwipsPerPixelX gColOneSize = 9 gColTwoSize = 34 gColThreeSize = 4 gAppointmentField = (gColOneSize + gColTwoSize _ + gColThreeSize) * tWidth% gLeftFramesWidth = (gAppointmentField * (1 + _ (1 - LIST_RATIO_H))) ' Used to indicate an open appointment gOpenApptLine = String(gColTwoSize - 4, "-") End Sub
You can't initialize a global variable at the same location (in the General section) where you declare the global variable. Therefore, you must initialize the global variables with assignment statements inside an executable procedure, as done in Listing 35.4. Much of the InitGlobalValues() procedure initializes the global variables in relation to the Screen object's properties so that the form centers itself properly and adjusts its control sizes and labels appropriately depending on the end user's screen size and resolution parameters.
![]()
More important than the code details (at this point), notice how the program's author added helpful remarks at the top of the procedure. The remarks consume almost as much code space as the code itself. The application's author uses these kinds of remarks throughout the entire application for all but the most trivial procedures. The remarks describe these items:
FAST TRACK |
If you want to learn more about how Visual Basic handles arguments, look at Hour 13,, "Understanding How Procedures Work". That lesson also describes function return values. |
Throughout the entire project in both code modules, the primary procedures contain this same set of remarks so that you can analyze the procedure's job. The remarks encapsulate an overview of the procedure so that you'll have the tools to then analyze the procedure's details.
Many of the application's procedures appear in the form module and others appear in the external standard module. You might need to use the Find dialog box (choose
Find from theEdit menu) to locate procedures within your application's Code window. Select theProject option before you begin your search so that Visual Basic looks throughout both project code modules when searching for your procedure.![]()
The rest of the code handles the runtime execution. Most of the code is taken up by event procedures that respond to users. So now that Main() has set the form and controls properly (according to the screen resolution and size), the program just waits and responds to event procedures.
Listing 35.5 contains some event procedure code. An event procedure occurs only when users click or otherwise use a control to trigger the procedure's execution. For example, when users click the command button named cmdApptDelete, the event procedure named cmdApptDelete_Click() executes.
FAST TRACK |
To learn how event procedures work, turn to Hour 6, "Understanding the VB Program Structure". |
Listing 35.5 Some Event Procedures
Private Sub cboDate_Click() If gbInitOver Then Call SetDayLabel(lblDay, cboMonth, cboDate, cboYear) Call GetDailyAppointments(CDbl(CDate(FormDateString())), _ lstSchedule, dataMain, gAppointmentDelta) End If End Sub Private Sub cmdApptDelete_Click() Dim dblStartTime As Double dblStartTime = CDbl(CDate(FormDateString) + _ CDate(lblStartTime.Caption)) Call DeleteApptRecord(dblStartTime, dataMain) Call GetDailyAppointments(CDbl(CDate(FormDateString())), _ lstSchedule, dataMain, gAppointmentDelta) frEnterAppt.Visible = False cmdApptDelete.Enabled = False End Sub Private Sub cmdNext_MouseUp(Button As Integer, _ Shift As Integer, X As Single, Y As Single) If frEnterAppt.Visible = True Then cboName.DragMode = 1 End If End Sub Private Sub mnuHelpAbout_Click() ' Menu response for Help, About Dim msg As Integer ' MsgBox() return value Dim strMsg As String strMsg = strMsg & "VBScheduler" & String(24, " ") & vbCrLf & vbCrLf strMsg = strMsg & "Copyright 1997" & vbCrLf strMsg = strMsg & "Macmillan Publishing" & vbCrLf strMsg = strMsg & "by Bob Reselman" msg = MsgBox(strMsg, , "About") End Sub
To round out this lesson's coding exercise, Listing 35.6 contains two additional procedures. The InitDataBase() procedure shows some database programming operations; the InitHelpIDs() procedure demonstrates how to add help context ID assignments so that the application can access an external help file.
Listing 35.6 A Few Additional Procedures to Round Out This Lesson
'************************************************ 'Sub/Function Name: InitDataBase ' 'Arguments: None ' 'Return: None ' 'Remarks: InitDataBase intializes a Data control to a ' an Access database file as defined by DATABASE_FILENAME. ' 'VB concept demonstrated: Data control method: Refresh ' Data control property: DatabaseName ' Data control property: Recordsouce ' Assigning an SQL statement as a ' recordsource for a data control '************************************************ Public Sub InitDataBase() Dim db As String ' Database name variable Dim MySQL As String ' string to hold SQL statement Dim msg As String ' string to hold error message ' Define the database name db = CurDir & "\" & DATABASE_FILENAME ' db = DATABASE_FILENAME ' Make sure that it is good If IsValidFile(db) Then dataMain.DatabaseName = db ' Reconnect the new database file dataMain.Refresh Else ' Make and show an error message msg = "Cannot find database files. " msg = msg & vbCrLf & vbCrLf & "Make sure that the database " msg = msg & "file " & DATABASE_FILENAME & " is in the same" msg = msg & " directory as the application file. If you are " msg = msg & "running this in app from the VB IDE, make sure that " msg = msg & "you copy the MDB file to VB's IDE directory." msg = msg & "This also applies to the help file (VBsched.hlp)." MsgBox msg, vbCritical, "File Error" End Exit Sub End If ' Make an SQL statement that retrieves all the records ' from the table, tblContacts and sort them by last name. MySQL = "SELECT * FROM tblContacts " MySQL = MySQL & "ORDER BY tblContacts.LastName" ' Assign the SQL statement to the data control's recordsource ' property dataMain.RecordSource = MySQL ' Reconnect the data control with the new recordsource. dataMain.Refresh End Sub '************************************************ 'Sub/Function Name: InitHelpIDs ' 'Arguments: None ' 'Return: None ' 'Remarks: This sub sets the Help Topic IDs as assigned ' to the constants defined in module, modHelp.bas ' 'VB concept demonstrated: WhatThisHelpID, HelpContextID, ' Controls collection, Controls.Count, ' TypeOf, Is, And '************************************************' Public Sub InitHelpIDs() Dim i% Dim MyControl As Control ' Assign the WhatsThisHelpID for text fields to ' all the text boxes except if it is the appointment ' entry text box For i% = 0 To frmMain.Controls.Count - 1 Set MyControl = frmMain.Controls(i%) ' If it is a text box. but NOT the text box in from the ' appointment entry frame. If TypeOf MyControl Is TextBox And _ MyControl <> txtApptEntry Then ' Then set the Topic ID's MyControl.WhatsThisHelpID = IDH_WH_CNTCT_FIELD MyControl.HelpContextID = IDH_WH_CNTCT_FIELD End If Next i% ' Name combo box from contract frame cboName.WhatsThisHelpID = IDH_WH_CNTCT_LIST cboName.HelpContextID = IDH_CNTCT_NAV ' Appointment List lstSchedule.WhatsThisHelpID = IDH_WH_APPT_ENTRY lstSchedule.HelpContextID = IDH_APPT_ADD ' << button cmdPrevious.WhatsThisHelpID = IDH_WH_CNTCT_PREVIOUS cmdPrevious.HelpContextID = IDH_CNTCT_NAV ' Add button from contact frame cmdAdd.WhatsThisHelpID = IDH_WH_CNTCT_ADD cmdAdd.HelpContextID = IDH_CNTCT_ADD ' Edit button from contact frame cmdEdit.WhatsThisHelpID = IDH_WH_CNTCT_EDIT cmdEdit.HelpContextID = IDH_CNTCT_EDIT ' Delete button from contact frame cmdDelete.WhatsThisHelpID = IDH_WH_CNTCT_DELETE cmdDelete.HelpContextID = IDH_CNTCT_DELETE ' >> button from contact frame cmdNext.WhatsThisHelpID = IDH_WH_CNTCT_NEXT cmdNext.HelpContextID = IDH_CNTCT_NAV ' Enter button from appoinment entry frame cmdOK.WhatsThisHelpID = IDH_WH_APPT_ENTER cmdOK.HelpContextID = IDH_APPT_ADD ' Delete button from appointment entry frame cmdApptDelete.WhatsThisHelpID = IDH_WH_APPT_DELETE cmdApptDelete.HelpContextID = IDH_APPT_DELETE ' Cancel button from appointment frame cmdCancel.WhatsThisHelpID = IDH_WH_APPT_CANCEL ' Time increment button from appointment entry frame cmdPlus.WhatsThisHelpID = IDH_WH_APPT_PLUS cmdPlus.HelpContextID = IDH_APPT_ADD ' Time decrement button from appointment entry frame cmdMinus.WhatsThisHelpID = IDH_WH_APPT_MINUS cmdMinus.HelpContextID = IDH_APPT_ADD ' Start time label from appointment entry frame ' WhatsThisHelp only lblStartTime.WhatsThisHelpID = IDH_WH_APPT_STARTTIME ' End time label from appointment entry frame ' WhatsThisHelp only lblEndTime.WhatsThisHelpID = IDH_WH_APPT_ENDTIME ' Current time label from appoinment entry frame ' WhatsThisHelp only lblCurrentTime.WhatsThisHelpID = IDH_WH_APPT_CURTIME End Sub
FAST TRACK |
To learn more about database programming, turn to Hour 21, "Accessing Files". If you want to know how to add help files to your application, turn to Hour 29, "Building a Help Subsystem". |
Remember that VBScheduler is rather advanced. Once you finish this book you are ready to tackle the entire project. In addition to demonstrating almost every concept taught in this book, the application extends some operations to show you additional ways to use controls. For example, the application applies SQL statements to the Data control to manipulate the appointment database in addition to event procedures that also work to modify the data.
![]()
This lesson reviewed much of the VBScheduler's code components that put the brains behind the application. Because the code is lengthy, this lesson didn't even include a fourth of the VBScheduler's application code. As you can see, a major application requires a lot of code despite Visual Basic's visual development tools. You can prototype an application rather quickly by gathering all its I/O controls and placing them on the form. Then you add code to manage the controls and produce the final application's actions.
In next hour's lesson, you learn how to test and debug the application so that you can quickly move from the trial stages to the final stages of your application. After you test and debug the application, the only job left is to compile and distribute it.
No questions or exercises exist for this lesson due to the general nature of the material.
![]()