Intrinsic Ctrls
ActiveX Ctrls
Cmd Reference
File Handling

Drag and Drop
Error Handling
Database Controls

Internet File Transfers
Perl Commands
Web Page Forms

GBIC >> VB >> Tutorials >> Code Librarian
Code Librarian
In response to requests for sample VB code, I've come up with the VB Information Center Code Librarian (CodeLib), Version 1.6. You can download the source code and then read the following discussion of the code to help you understand how the application works. CodeLib has dozens of code snippets which you can use in your own applications. Watch the revision history for new versions that become available. Plus, here are a few other sample projects that you might find interesting:

Countdown Timer CaseMaker


I hate dry, useless example projects. It makes the project so hard to walk through because you have to work to get to the good parts. CodeLib is written with the "instant gratification" in mind. The code is kept short, but useful. The list below gives the features that CodeLib demonstrates. You can jump to each section to see the source code and to look at my comments on how it works. To begin the discussions, here's an image of CodeLib's main screen.

The left side of the screen is a list of code snippet titles. On the right side is the code itself. In the lower left is a list of keywords that have been associated with each snippet of code. Finally, there is a text box where you can type in a search string. CodeLib can search through the titles, keywords, and code lines for the search string.

Before I get into the code, though, let's talk about the CodeLib program. When you're in the VB IDE it's nice to be able to reach into a library somewhere and pull out a snippet of code that adds a particular features to your own application. That's exactly what CodeLib does. While you're in the VB IDE, start CodeLib also. It's window is kept reasonably small so that it can be placed at the bottom of your screen, pretty much out of the way (or you can simply minimize it).

When you need a code snippet, go to CodeLib and scroll through the available routines by moving through the list on the left side of the screen. Once you find what you want, simply click on the "Copy" menu selection and the entire multi-line code snippet will be placed on the clipboard.

Then go back into the VB IDE, put the cursor where you want the code to go and use the Windows paste command (Ctrl-V or Edit/Paste) to put the code snippet into IDE code window.

You can also search through the code for any keyword/text string that you want, making it easier to find the code you need.

I'll upgrade CodeLib over the next few months and provide additional features - but always with an eye towards using CodeLib to demonstrate how to implement VB feature options.

Of course, CodeLib also allows you to enter new snippet or to change the ones that are already there. I use a second window for the edit screen to help demonstrate how to talk between windows within an application.

Here are some of the features which CodeLib demonstrates:

Splash Screen Read/Writing Binary Files
Clipboard Automatic selection of text when control gets focus
Registry Go to next control when ENTER is pressed
Inter-form variable referencing Save/Restore window positions for next session
Menus Search an array
Auto-sizing forms Sort an array
About form Deleting an element from an array
Calling HELP files Adding an element to an array
Confirming an action Printing a simple report
Confirming exit from a program To Database or Not?
PopUp menus

Splash Screen
More often than not it takes several seconds for a VB application to load up all of the files (EXE, DLLs, OCXs, and others) needed by the program. To entertain the user while all that loading is going on, many VB programs first display a simple (fast-loading) screen which draws the users attention. After a moment, once all the application's files have been loaded, the VB application unloads the Splash screen and displays the main screen of the application. CodeLib does this as well.

All it takes is a simple form with a graphics to entertain the user and a timer control placed on the form. In CodeLib I set the timer interval for 1.5 seconds, at which time the splash form unloads itself and then loads CodeLib's main form. The 1.5 seconds is adequate for the user to actually look at the splash form, and long enough for the rest of CodeLib to load, but not too long to bore the user.

You may have to experiment with the timer's interval setting on your own application to determine how long the splash screen should be kept visible. Here's the code for the Timer control event:

Private Sub Timer1_Timer()
Unload Me        'unloads the splash form
frmMain.Show     'loads the main form
End Sub

Menus, Sub-Menus, and Control Arrays
With VB it is exceptionally easy to create a menu. In the IDE, just highlight the form for which you want to make a menu and then call up the Menu Editor. The tools within the editor are pretty simple to understand and they allow you to create exactly the same kind of menu as most other Windows application.

In several places in my menu I create sub-menus. However, I've show a little trick which can often help you to greatly simply your code. In the CodeLib toplevel menu "Options", there are two normal sub-menus: "Minimize on Copy" and "Search". They work just like you would expect. However, at the "Search" menu there is another level of sub-menus and there is where is use the concept of a control array to save me from writing more code than is necessary.

The concept of a control array is pretty sinple. Create two controls with example the same name and VB will treat them as an array (VB forces you to give each one a unique number, called an Index). When you click on any one control that is within a control array VB will execute an event which is common to all of the controls within the control array. VB also passes the Index value to the event procedure so that you know which control was clicked.

The magic of this is that you don't have to repeat code for each array! Make sure you caught this. You don't have to repeat the code!. In CodeLib, under the "Search" menu, are the sub-menus "All", "Key", "Title", and "Code". Each is part of the same control array and when you click on any one of them you see the exact same event procedure. Then, within that event procedure, you only have to write code once which uses the Index value to help determine what action you want to take.

In the case of CodeLib, I use code to check the selected item and un-select all others. I also save the Index value in the Windows registry so that the next time I start CodeLib I can restore the last setting.

Auto-sizing the contents of a form
Windows allows users to manually change the size of its windows but it is up to the VB programmer to create the code to change the size of the controls inside the window appropriately. In frmMain and frmEdit I use code to keep the controls sized proportionate to the window size. Here is the code for sizing the contents of frmMain - note that the code is placed within the resize event procedure of the form.

Private Sub Form_Resize()
'set width/height of controls (check for illegal values)
If frmEdit.ScaleWidth - txtCode.Left - 50 > 200 Then
    txtCode.Width = frmEdit.ScaleWidth - txtCode.Left - 50
    txtKey.Width = txtCode.Width
    txtTitle.Width = txtCode.Width
End If
If frmEdit.ScaleHeight - txtCode.Top - 50 > 200 Then
    txtCode.Height = frmEdit.ScaleHeight - txtCode.Top - 50
End If
End Sub

The clipboard is a really great feature! It's the poor man's way of communicating between programs and of making data available to other programs. And the really cool thing about it is that it can handle graphics or text equally well! Believe me, that's a big benefit - eliminating code you would otherwise have to write.

One of the most often used features of the clipboard is to simply copy text into the clipboard's memory so that it can then be pasted into another program. With CodeLib, that's exactly what happens. In many programs it is normal for you to have to select some text and then use the Edit/Copy menu commands (or Ctrl-C) to copy the selected text. With CodeLib I've opted to just have a single copy command in the menu which will copy the entire code snippet, without the need for you to first select the code.

Microsoft has done a good job of keeping access to the clipboard simple. The code from frmMain which puts the code snippets onto the clipboard is:

Private Sub mnuEditCopy_Click()
'clear clipboard then set to code
Clipboard.SetText lblCode.Caption

Once you have it in the clipboard, you can paste it into any other Windows program by using Ctrl-V or by using the menu selections Edit/Paste. It's no more complicated than that.

The ability to store data in the registry is analogous to storing project information into a file. Previously, applications used a .INI file, which was a simple text file that stored information the program would need the next time it ran.

For reasons that I have trouble appreciating, Microsoft has decided to get away from the .INI file strategy and now goes with a "Master INI" file that they call the Registry. In lieu of using individual .INI files, Microsoft encourages all application designers to put their program-specifc data into the Registry. VB provides functions which can read/write information into either the Registry or to a .INI file, so you still have freedom of choice. Personally, I've been using the Registry simply because that means my users have fewer files to worry about.

For your purposes, it's really not that critical whether a .INI file or the Registry is used to store data. They both are transparent to the user of your application and both are supported by VB. In general, I'd suggest going to the Registry approach because Microsoft may yet decide to discontinue the .INI file features.

In case you don't appreciate what you could do with the Registry, take a look at this list:

Of course, for every "store" feature there is a corresponding "retrieve" option for reading and using the data in an application.

In CodeLib I use the two simple functions that are all most folks need to save and retrieve Registry information - "GetSetting" and "SaveSetting":

Status = GetSetting("CodeLib", "Options", "Minimize", "TRUE")
SaveSetting "CodeLib", "Options", "Minimize", "TRUE"
The first line reaches into the Registry into a section called CodeLib/Options and reads a value of the property "Minimize", using a default value of "TRUE". The second line write a value into the Registry, again in a section called CodeLib/Options, setting the "Minimize" property to a value of "TRUE". If it looks simple that's because it is. As a beginner I remember having the impression that using the Registry was going to be difficult but it really is as simple as I just described.

Inter-form variable referencing
What do you do if you're executing code in one window and you want to use values from another form? The following code from CodeLib shows what to do:

txtTitle = frmMain.lstTitles.List(frmMain.lstTitles.ListIndex)
In this example (found on the frmEdit) I set the value of a textbox called txtTitle. I fill it with the content of a listbox on frmMain. All I have to do to access the listbox is to precede the control name by the name of the form! So, the "frmMain." at the front distinguishes between a control on the current form or on another form entirely!

The About form
Almost all applications use an "About" form, which is typically used to identify the author of the program and any information he chooses to provide to help you locate him. The next line of code calls the About form. Note the use of the vbmodal constant. This forces the user to close the About form before continuing with the application. vbmodal

The next code listing is used in the load event of the About form to display. I keep the credits in an external text file that I can easily change, and simply read the entire text file into a textbox for viewing.

Private Sub Form_Load()
Dim temp As String, message As String
'load credits file
ChDir App.Path
Open "credits.txt" For Input As #1
While Not EOF(1)
    Line Input #1, temp
    message = message & vbCrLf & temp
Close #1
txtCredits.Text = message
End Sub

Calling HELP files
In the VB IDE, use the Project/Project Properties menu to get to the dialog box where you can type in the name of a HELP file that will be called up when you press F1 while your program is running.

You can also set the HELP filename in code using:

app.helpfile = filename

Once you set the HELP filename for an application pressing F1 will call up the HELP file. You can also call up a HELP file by using the Windows API, by using the CommonDialog Box, or by simply running the WinHelp EXE program that displays the HELP files. In frmMain I use the following code to display the CodeLib HELP file:

Private Sub mnuHelpContent_Click()
Shell "winhelp " & App.HelpFile, vbNormalFocus
End Sub

Confirming an action
In the CodeLib menu is a Delete option. To prevent accidental deletions of a code snippet I include the following statement in the click event of the menu control:

If MsgBox("Are you sure?", vbOKCancel, "Delete Code") = vbCancel Then Exit Sub
Since MsgBox is a function I can simply call it and check the return value to determine which button in the MsgBox was pressed by the user. By using the "vbcancel" VB constant I don't have to remember the actual numerical value.

PopUp Menus
Creating a popup menu that appears when you right-mouse click an object is made easy by the VB PopUpMenu function. In CodeLib, the main menu provides the following code which provide popup menus with most of the controls on the form. The following code is placed in the MouseDown event procedure:

If Button = 2 Then PopupMenu mnuListPopup
One thing to notice is that the popup menus do not replicate the code of the other menus. I just use the popup menu to call out the name of an event procedure for one of the other menus. Also, notice that the menu which is called by the right mouse is made invisible but that only it's visible sub-menus will be show in the popup menu.

Confirming exit from a program
In almost all programs you will want to ask the user to confirm that he wishes to exit from the program - primarily to give the user a chance to save any changes that were made. In CodeLib I created a Global variable called FileChanged and whenever a change to the data was made I set FileChanged to TRUE. Then, in the Unload event of the form I can check to see if changes were made and ask the user whether to save the changes. Note that if I Save changes to the data before exiting from the program I set FileChanged to FALSE.

To ask the user if changes should be saved, I again use the MsgBox function, using the returned value as in a Select Case decision loop. The code is put in the Form_Unload event and the Cancel argument is changed to match the users selection:

Private Sub Form_Unload(Cancel As Integer)
If FileChanged = True Then
    Select Case MsgBox("Save changes?", vbYesNoCancel, "Exit")
        Case vbYes
            'save the data, leave cancel as is
        Case vbNo
            'take no action, leave cancel as is
        Case vbCancel
            'stop the unload action
            Cancel = 1
    End Select
End If

Read/Writing Binary Files
In another part of my tutorial, I noted that VB supports the creation and editing of both ASCII text files as well as the direct manipulation of binary files. In CodeLib, I made use of the fact that in binary file writing you do not have to specify the format of the stored material. You'll see in my GET and PUT statements that I simply list the variable names which I want to write to the file and VB handles the formating for me.

Even more useful is that VB allows you to write variables of a User-Defined Type by referencing only the variable name. You do not have to specifically list all of the sub-elements to the user-defined type variable! This is very powerful so you'll want to review my Save code.

Private Sub mnuFileSave_Click()
'save the data
ChDir App.Path
Kill "codelib.dat"
Open "codelib.dat" For Binary As #1
LibDate = "-04/25/99-"
Put #1, , LibDate   'single variable
Put #1, , MaxCode   'single variable
Put #1, , Code      'the array
Close #1
FileChanged = False
End Sub

Automatic selection of text when control gets focus
When a user clicks on a textbox it is useful to have the entire content of the textbox become highlighted. This code does that for you:

Private Sub txtTitle_GotFocus()
txtTitle.SelStart = 0
txtTitle.SelLength = Len(txtTitle.Text)
End Sub

Go to next control when ENTER is pressed
Sometimes, when the user is entering data you may want to take an action only when the ENTER key is pressed. In frmEdit you will find the following code in the KeyPress event procedure. The KeyAsccii value for ENTER is 13, so in this example pressing the ENTER key causes the focus to move to the control txtKey.

Private Sub txtTitle_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then txtKey.SetFocus
End Sub

Save/Restore window positions for next session
After a user takes the time to move the windows to the location that meets his work habits, you might as well go to the effort to store the positions of those windows. The following two procedures are used in the frmMain to save the positions on exit and to restore the positions on return to the program.

Private Sub Form_Unload(Cancel As Integer)
'save position of window
SaveSetting "CodeLib", "Options", "EditPositionTop", frmEdit.Top
SaveSetting "CodeLib", "Options", "EditPositionLeft", frmEdit.Left
SaveSetting "CodeLib", "Options", "EditPositionHeight", frmEdit.Height
SaveSetting "CodeLib", "Options", "EditPositionWidth", frmEdit.Width
End Sub

This next code was taken from the Form_Load event of frmMain, and sets
the starting window position and size:

frmMain.Top = Val(GetSetting("CodeLib", "Options", "PositionTop", frmMain.Top))
frmMain.Left = Val(GetSetting("CodeLib", "Options", "PositionLeft", frmMain.Left))
frmMain.Height = Val(GetSetting("CodeLib", "Options", "PositionHeight", frmMain.Height))
frmMain.Width = Val(GetSetting("CodeLib", "Options", "PositionWidth", frmMain.Width))

Search an array
There are many sophisticated ways to search through data. However, in CodeLib, the library size is not very large and so I chose to simply walk through each and every entry to see if I could find a match for the search string. There's nothing all that special about my search routine. I do allow the user to chose whether to search the title, keywords, or codelines for a search string. I make use of the INSTR function to do my string searching.

Private Sub cmdSearch_Click()
Dim i As Integer, Location As Integer, StartPoint As Integer
Dim SearchString As String, Status As Boolean

'set the starting point
If mnuOptionsStartTop.Checked = True Then
    StartPoint = 0
    If lstTitles.ListIndex < lstTitles.ListCount - 1 Then
        StartPoint = lstTitles.ListIndex + 1
    End If
End If

'search the database
For i = StartPoint To lstTitles.ListCount - 1
    'set the search string
    If mnuOptionsSearchChoice(0).Checked = True Then   'all
        SearchString = lblKey & lblCode & lstTitles.List(i)
    ElseIf mnuOptionsSearchChoice(1).Checked = True Then     'key
        SearchString = lblKey
    ElseIf mnuOptionsSearchChoice(2).Checked = True Then     'title
        SearchString = lstTitles.List(i)
    ElseIf mnuOptionsSearchChoice(3).Checked = True Then     'code
        SearchString = lblCode
    End If
    'search the string
    Location = InStr(LCase(SearchString), Trim$(LCase(txtSearch.Text)))
    If Location > 0 Then
        Status = True
        Location = i
        Exit For
    End If
Next i

'go to the match if found
If Status = True Then
    lstTitles.ListIndex = Location
    MsgBox "Search string not found!", vbOKOnly, "Search"
End If

End Sub

Sort an array
There are many ways to sort a list or an array of data. I've used what is known as a bubble sort. It's not very efficient but it is easy to code and for small lists it works quickly enough not to be a problem. Here's the sort routine from frmMain:

Private Sub mnuEditSort_Click()
Dim i As Integer, j As Integer
Dim temptitle As String, tempkey As String, tempcode As String
'sort the Code() array
For i = 0 To MaxCode - 1
    For j = i + 1 To MaxCode
        If Code(i).title > Code(j).title Then
            temptitle = Code(i).title
            tempkey = Code(i).keywords
            tempcode = Code(i).CodeLines
            Code(i).title = Code(j).title
            Code(i).keywords = Code(j).keywords
            Code(i).CodeLines = Code(j).CodeLines
            Code(j).title = temptitle
            Code(j).keywords = tempkey
            Code(j).CodeLines = tempcode
        End If
    Next j
Next i

Deleting an element from an array
To remove an element from an array you basically move all of the data in the array down by one position and then eliminate the top-most position by resizing the array to an upper bound of 1 less than it was before the delete action. I use the technique in frmMain:

Private Sub mnuEditDelete_Click()
'verify actions (only if confirm on delete is checked)
If mnuOptionsConfirm.Checked = True Then
End If

'delete the code snippet
Dim i As Integer, Start As Integer
FileChanged = True
Start = lstTitles.ListIndex

'move all data down 1 position, unless the line to delete is the last line
If lstTitles.ListIndex <> lstTitles.ListCount - 1 Then
    For i = lstTitles.ListIndex To lstTitles.ListCount - 1
        Code(i).title = Code(i + 1).title
        Code(i).keywords = Code(i + 1).keywords
        Code(i).CodeLines = Code(i + 1).CodeLines
    Next i
End If

'lower the array size by one
MaxCode = MaxCode - 1
ReDim Preserve Code(MaxCode)   'note the PRESERVE keyword!

Adding an element to an array
To add an element to an array you simply resize the array by 1, using the keword PRESERVE so as not to lose the data. Then write the new data into the new position at the end of the array. In frmEdit, the following code is executed by the Save command when a new snippet is being added.

'if new, then increment MaxCode, array size, and lstTitles
If EditStatus = "new" Then
    'resize the array
    MaxCode = MaxCode + 1
    ReDim Preserve Code(MaxCode)
    'add new entry to lstTitles
    frmMain.lstTitles.AddItem txtTitle
    frmMain.lstTitles.ListIndex = MaxCode - 1
End If

Printing a simple report
The printing routine I use in CodeLib is pretty plain, but it still can be used to show you how to print out data. In printing you may often want to precisely determine where a print action is to take place, and that means you would be setting the .CurrentX and .CurrentY properties. If you simply follow one print statement after another then VB will simply set the CurrentX and CurrentY values to correspond to normal lines of print one below the other starting at the left margine of the page. That's what I do in CodeLib.

Printer.Print "VB Information Center Code Librarian"
For i = StartSnippet To EndSnippet
    Printer.Print "Title:"; Tab(15); Code(i).title
    Printer.Print "Keywords:"; Tab(15); Code(i).keywords
    Printer.Print Code(i).CodeLines
    Printer.Print "=================================================="
Next i

Reading sequential text files
I often need to read a sequential text file, either to grab the whole thing or to take action on a line by line basis. The CodeLib library is a binary file, but the credits listed in the About form are kept in a sequential file called "credits.txt". The following code is from the frmAbout load event and shows two different ways to read in the sequential text file.

Private Sub Form_Load()
Dim tempString As String
ChDir App.Path
Open "credits.txt" For Binary As #1
tempString = Input(LOF(1), #1)   'reads the whole file with a single line of code
Close #1
txtCredits.Text = tempString

'The following code is an alternate way of reading a text file
'reading one line at a time
'Dim temp As String, message As String
'load credits file
'ChDir App.Path
'Open "credits.txt" For Input As #1
'While Not EOF(1)
'    Line Input #1, temp
'    message = message & vbCrLf & temp
'Close #1
'txtCredits.Text = message

To Database or Not?
I plan to release a second version of CodeLib in a few weeks. The second version will not be a replacement of this one. It will use VB database capabilities (versus the binary file handling used in this version of CodeLib), and will be useful in showing how the application of database features can both be helpful as well as cause problems in writing and releasing an application.

CodeLib Revision History