Product Information
  Technical Information
  Samples & Downloads
  News & Reviews
  Registration



Past Tips of the Week

elementk logo The Visual Basic Tip of the Week is provided by Element K Journals, publishers of Inside Visual Basic, a monthly publication for Visual Basic users. Sign up for your free preview Issue today. Find out how to receive the Tip of the Week in e-mail!

You can quickly navigate to past tips using the alphabetical topic list below. The tips are also organized chronologically further down on this page.

A better way to gather multi-selected Visual Basic ListBox items
A caveat for the FileSystemObject Delete methods in Visual Basic
A new Format function
Accept only numbers in a text field
Add a new line to existing textbox text
Add controls to a Visual Basic control array at run-time
Add Dithered Backgrounds to your Visual Basic Forms
Add pop-up menu support to the Rich Textbox control
Add Web mouseover effects to Visual Basic
An OO way to pass Visual Basic variable values to a form
Avoid API bit-masking comparison errors in Visual Basic

Avoid cascading UserControl Change() events
Avoid variable type comparison glitches in textboxes
Confirm Screen Resolution
Convert NULL Values to Empty Strings to Avoid Errors
Compact and repair Jet databases with DAO
Compact long Visual Basic filepaths with SHLWAPI library
Correctly pass variant array parameters in Visual Basic
Create templates from existing forms
Creating a incrementing number box

Creating a new context menu in editable controls
Create a splash page with real Flash
Creating Professional Documents with Code
Creating Win32 region windows
Deciphering MP3 Tag information with Visual Basic
Display animated GIFs in Visual Basic
Display UserControl property settings in the Property Window
Don't lose focus in UserControls
Drag & Drop the real selected Visual Basic treeview node
Dragging items from a list to another one

Enabling the horizontal scrollbar in a RichTextbox control
Enhancing Browser Page Load Performance
Entering parameters into a DAO parameter query
Evaluate string formulas in Visual Basic
Finding the last day of the month
Force Visual Basic 6.0 to open maximized code windows
Generate temporary Visual Basic files with API

GETting the contents of a file
Globally replace text using just 5 lines of Visual Basic code
Hide the mouse cursor in a Visual Basic application
How to build a data-shaped SQL clause for Visual Basic
Implementing a "wait" function in Visual Basic
Importing Registry settings
Increase Your RAD-ness by Creating Your Own Visual Basic 5.0 Templates

Increment and decrement dates with the [+] and [-] keys
Keep Your Background Processes Running
Keeping track of Visual Basic source code builds
Labeling your forms
Launch a PC's default browser with ShellExecute API

Let DateDiff() determine if two dates are in the same month
Let users add tab spaces in VB's Rich Textbox control
Let Visual Basic determine if the CD Rom drive contains media
Link Lotus Notes to Visual Basic 6.0

Maintain a Visual Basic DBGrid's runtime column widths
Make LBound() and UBound() even more efficient
Manipulate your controls from the keyboard
Measuring a text extent
Monitor a notebook computer's battery information with SysInfo
Move files to the Recycle Bin with Visual Basic and Windows API
Nothing to declare?
Opening a browser to your homepage
Prevent drag and drop operations on Visual Basic treeview root nodes

Parsing Using SPLIT Function
Prevent phantom apps when using VB 6.0's DataReport
Put your Visual Basic application into deep sleep
Quick Custom Dialogs for DBGrid Cells
Quick Text Select On GotFocus
Read Registry values in Visual Basic without API
Referring to a DE Command's recordset in Visual Basic 6.0
Replace lengthy path strings with Visual Basic object variables

Retrieve a file's short name in Visual Basic without API
Retrieve subitem values from a Visual Basic ListView control
Scroll to a Visual Basic listview control's selected item
Showing long ListBox entries as a ToolTip
Simple file checking from anywhere
Spell check RTF documents in Visual Basic
Switching to Design View after Modifying ASP Script in Visual InterDev

Take Advantage of Built-in OLE Drag and Drop
Uncover internal DLL functions with Dependency Walker
Update the Visual Basic 6.0 DataGrid control with recordset changes
Use ADO's native OLEDB drivers instead of ODBC
Use FileDSNs to ease ODBC Installs
Use FreeFile to Prevent File Open Conflicts
Use LBound() and UBound() when iterating Visual Basic arrays
Use ParamArray to Accept an Arbitrary Number of Parameters

Use Visual Basic's ADOX to determine if internal database objects exist
Use the Timer control for longer than 1 minute
Use the Visual Basic 6.0 Split function to count substrings
Using Response.IsClientConnected Property to Determine Browser Connection
Using Server.HTMLEncode() Function
Using the Alias Option to Prevent API Crashes
Visible Cues from a Minimized Form
With statement precedence in Visual Basic
Writing to the Windows NT event log


For the week of December 4th
Launch a PC's default browser with ShellExecute API

Often, you may want a user to access a specific URI on the Web by launching his default browser and navigating to the Web site of your choice. Fortunately, a simple Windows API function ShellExecute() lets you do just that. When you pass this function a filename, it uses the Windows file associations to start the appropriate application. As a result, all you need do is pass this function a URI, and it automatically launches the default browser and navigates to the requested location.

The ShellExecute() function conforms to the following syntax:

 Private Declare Function ShellExecute Lib _
     "shell32.dll" Alias "ShellExecuteA" _
     (ByVal hWnd As Long, ByVal lpOperation _
     As String, ByVal lpFile As String, ByVal _
     lpParameters As String, ByVal lpDirectory _
     As String, ByVal nShowCmd As Long) As Long

As you can see, it takes quite a few parameters, but don't worry, for our purposes only two concern us: lpFile and nShowCmd. The lpFile parameter holds the name of the file or application you want to launch, while the nShowCmd parameter contains directions indicating how you want the application to appear when it opens. Typically, you'll use the SW_SHOWNORMAL constant (1).

So for instance, to use this function to navigate to a Web page with the URL www.google.com, you'd write code along the lines of:

ShellExecute 0&, vbNullString, "www.google.com", vbNullString, _
      vbNullString, SW_SHOWNORMAL

If for any reason the ShellExecute() doesn't execute the application properly, the function returns a value less than or equal to 32. Otherwise, it returns a value that points to the launched application.


For the week of November 27th
Entering parameters into a DAO parameter query

As you probably know, in Access a parameter query displays one or more predefined dialog boxes that prompt you for a parameter value. In most cases, you use a parameter query to determine criterion. For example, if you limited a query's result by the parameter, <[Please Enter A Date], then when you ran the query Access would display a prompt and ask you to Please Enter A Date and filter the records accordingly.

When you manipulate these queries in DAO through VBA, however, you'll need to supply the parameter value before you open the recordset object. If you don't, DAO generates an error.

To do so, access the Parameters collection of the DAO querydef object. A querydef is a query's definition, or blueprint. It tells DAO what the query's layout should be, while a recordset contains the results generated by the querydef. To illustrate, take a look at the following code:

 
Private Sub cmbRunParam_Click()
Dim wrk As DAO.Workspace
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim qdf As QueryDef

Set wrk = CreateWorkspace("", "Admin", "", dbUseJet)
'Change database path
Set db = wrk.OpenDatabase("D:\Examples\db1.mdb")
Set qdf = db.QueryDefs("qryParamQuery")

qdf.Parameters("[Please Enter a Date]") = #8/15/2000#
Set rst = qdf.OpenRecordset()
rst.MoveLast

MsgBox "There are " & rst.RecordCount & " projects to" _
	& vbCr & "complete before this date."

Set rst = Nothing
Set qdf = Nothing
Set db = Nothing
Set wrk = Nothing
End Sub

For the week of November 13th
Monitor a notebook computer's battery information with SysInfo

One of the most underused controls shipped with Visual Basic is the SysInfo control, seen as Microsoft SysInfo Control Version 6.0 (or 5.0 in VB 5.0) in the Components dialog box. The SysInfo control lets you retrieve information about operating system properties, plug and play events, system events and the status of a battery. As you know, batteries are the cornerstone of most notebook computers, although some desktop systems have batteries, as well. With the help of three properties from SysInfo, you can monitor a PC's battery in Visual Basic.

The control's ACStatus property lets you determine if the computer system is currently using an AC power source or battery power. This property returns one of three values: 0, 1 and 255. The following code snippet illustrates each value:

Select Case SysInfo1.ACStatus
Case 0
    MsgBox "Not using AC power"
Case 1
    MsgBox "Using AC power"
Case 255
    MsgBox "Unknown AC power status"
End Select

The BatteryLifePercent property lets you monitor the battery's charge status. This property returns a percentage value indicating the amount of power remaining in the battery. You can use

Dim PerCentLeft As String
If SysInfo1.BatteryLifePercent <> 255 Then
    PerCentLeft = SysInfo1.BatteryLifePercent
    MsgBox PerCentLeft & "%"
Else
    MsgBox "Battery charge status not known"
End If

For the week of November 6th
With statement precedence in Visual Basic

Given the choice of creating a With statement for use with an object or an user-defined type (UDT), choose the object over the UDT. Because UDTs are simple variables and not full-blown objects, they don't take the same resource hit as objects do when you repeatedly access their properties. For instance, consider the following code, which alters the command button's settings when you click it:

Private Type TBtnSettings
    BgColor As Long
    FontSize As Integer
    Top As Integer
    Left As Integer
    Width As Integer
    Height As Integer
End Type
Private mOrigSettings As TBtnSettings

Private Sub Command1_Click()
With Command1
    .BackColor = vbGreen
    .Top = mOrigSettings.Top - 100
    .Left = mOrigSettings.Left - 100
    .Width = mOrigSettings.Width + 200
    .Height = mOrigSettings.Height + 200
    .FontSize = mOrigSettings.FontSize + 4
End With
End Sub

Here, we could have used the With statement to reduce the number of times we needed to manually type 'mOrigSettings'. However, as we stated, by using the With statement for Command1 instead, our code becomes much more efficient.


For the week of October 30th
Deciphering MP3 Tag information with Visual Basic

The Windows Media Player provides an easy, quick way to drop MP3 capability into a Visual Basic application. However, once you have an MP3, you may have wondered how to read information about the song, such as the song title and artist's name. If the MP3 file uses the most popular tag encryption, ID3, then you're in luck. This standard stores the Tag information in the last 128 bytes of the file (Tag:3, Title:30, Artist:30, Album:30, Year:4, Comment:30, Genre:1)

To read this information, first open the MP3 file and grab the last 128 bytes. With ID3, the first three slots hold the string TAG if the file actually contains information. If the file does contain Tag information, store the last 128 bytes in a custom variable. After that, cycle through the MP3 file, extracting information as you go. The following procedure shows the code that extracts this information as well as creates several important variables to use later on:

Option Explicit
Private Type TagInfo
    Tag As String * 3
    Songname As String * 30
    artist As String * 30
    album As String * 30
    year As String * 4
    comment As String * 30
    genre As String * 1
End Type

Dim FileName As String
Dim CurrentTag As TagInfo

Private Sub Form1_Load()
Dim temp As String
On Error Resume Next

FileName = App.Path & "\myMP3.mp3"    
Open FileName For Binary As #1
With CurrentTag
    Get #1, FileLen(FileName) - 127, .Tag
    If Not .Tag = "TAG" Then
        label8.Caption = "No tag"
        Close #1
        Exit Sub
    End If
    Get #1, , .Songname
    Get #1, , .artist
    Get #1, , .album
    Get #1, , .year
    Get #1, , .comment
    Get #1, , .genre
    Close #1 

    txtTitle = RTrim(.Songname)
    txtArtist = RTrim(.artist)
    txtAlbum = RTrim(.album)
    txtYear = RTrim(.year)
    txtComment = RTrim(.comment)
	
    Temp = RTrim(.genre)
    txtGenreCode = Asc(Temp)
    Combo1.ListIndex = CInt(txtGenreCode) - 1
End With
End Sub

Notice that the code has to handle the genre character a little differently. That's because ID3 stores this data as a single ASCII character. To match up the actual number with its corresponding description--say contained in a combobox--the procedure converts the ASCII to a number, and then looks up that number in the combobox.


For the week of October 23rd
Let users add tab spaces in VB's Rich Textbox control

When you use the Rich Textbox control on a form, you'll often want to let users add tabs to their input. If the Rich Textbox is the only control on the form, then pressing the [Tab] key inserts the requested tab space into the text. Most likely, however, you'll have more than one control on the form, in which case pressing the [Tab] key moves the focus from the Rich Textbox to another control.

To prevent this behavior, you could simply set each control's TabStop property to False. This prevents users from tabbing between ANY controls--but does let them enter tabs into the Rich Textbox.

Chances are, though, you want to give users the ability to tab between controls AND enter tabs in the Rich Textbox. Fortunately, VB provides the tools for you to do so in the Rich Textbox control's KeyDown() event.

As you probably know, the Rich Textbox fires this event when a user presses a key while the control has the focus. Like all KeyDown() events, you can use code to determine which key the user pressed and react accordingly. To capture the Tab key and insert a tab space into the Rich Textbox control's edit area, we could use the following:

Private Sub RichTextBox1_KeyDown(KeyCode _
     As Integer, Shift As Integer)
Dim mblnTabPressed As Boolean

mblnTabPressed = (KeyCode = vbKeyTab)
If mblnTabPressed Then
    RichTextBox1.SelText = vbTab
    KeyCode = 0
End If
End Sub

For the week of October 16th
Make LBound() and UBound() even more efficient

In a previous tip, we showed you how to loop through an array with the LBound() and UBound() function. The code we provided used these two functions in the For...Next statement itself, like this:

For x = LBound(MusicGenres) To UBound(MusicGenres)

where MusicGenres was the array we wanted to traverse.

As many astute tipsters pointed out, howevever, when you loop through large arrays it's much more efficient to set variables to the LBound() and UBound() function results and use these variables inside the loop instead. This way, Visual Basic only wastes time performing the functions once rather than many times.

So the code would then become

lctr = LBound(MusicGenres)
uctr = UBound(MusicGenres)
For x = lctr To uctr
    Debug.Print MusicGenres(x)
Next x

For the week of October 9th
An OO way to pass Visual Basic variable values to a form

Object oriented programming guidelines stipulate that objects--including forms--should maintain their own internal values. With this in mind, you'll need a way to pass values to loaded forms without using universally global variables. Fortunately, Visual Basic provides just such a way with its property Let() and Get() procedures.

For example, suppose you have a button Click() event on Form1 that loads Form2. Form2 displays a value collected in Form1. In the past, you may have created a public variable with an application scope, and then read this variable in Form2 to obtain the proper value. Using this technique, however, soon results in hard-to-manage, spaghetti code.

As a more elegant alternative, you can add a property to Form2 that Form1 can then set.

For instance, in Form2 you would add the following property procedures along with the variable declaration:

Private myVar as Variant

Public Property Get PassVar() As Variant
PassVar = myVar
End Property

Public Property Let PassVar(ByVal vNewValue As Variant)
If IsNumeric(vNewValue) Then myVar = vNewValue
End Property

For these procedures, you can change the property type, but it must match in both the Get and Let procedures. Also notice that we included some validation code to ensure that the passed value is a number.

Now, to read or assign a value to this property, use the following code on Form1

Private Sub Command1_Click()
With Form2
    .PassVar = 25
    .Show
    Debug.Print .PassVar
End With
End Sub

For the week of October 2nd
Prevent phantom apps when using VB 6.0's DataReport

VB 6.0's new DataReport designer provides a great way to display simple datasets. When you use this new tool, however, you may have found phantom instances of your application cropping up on machines using the final compiled version. This behavior occurs if you print the DataReport via code, but don't unload it when you're through with it.

For instance, suppose we added a button to a form with the following code:

Private Sub Command1_Click()
DataReport1.PrintReport
End Sub

and then compiled the project. When you launch the new application, click the button, and close the application, a phantom instance of the application shows up in the Windows Task Manager. This phantom instance results because Visual Basic instantiates the DataReport object but never unloads it when it's through printing.

To fix this glitch, simply unload the DataReport in the form's Unload() event, like so

Private Sub Form_Unload(Cancel As Integer)
Unload DataReport1
End Sub

For the week of September 25th
Create a splash page with real Flash

You've probably seen this plug-in in Web pages, some good and some bad. But did you know that you could also use the Flash plug-in in Visual Basic? While it may seem to have limited use, many developers have begun to use the plug-in to display graphical GUI buttons, as well as animated splash screens. To take advantage of the Shockwave Flash OCX (also known as Flash Player) in Visual Basic, you must have it installed on your machine. If you don't already have it, visit www.macromedia.com/software/flash and select the Flash Player | Download Flash Player menu option, which leads you to a simple, three-step download process.

The Shockwave Flash component uses Flash Player Movies, which use the extension .smf. The Flash software also creates FLA-formatted files, but you can't use these with the Flash component. Once you create a movie, drop a Flash component onto a form, and enter the movie's path and filename into the Movie property on the property sheet. When you launch the program and form, Flash plays the animation.


For the week of September 18th
Use LBound() and UBound() when iterating Visual Basic arrays

When you use a For...Next loop to iterate through an array, you may be inclined to hard code the starting and ending counter values. For instance, suppose you created the following array

MusicGenres = Array("Blues", "Classic Rock", "Country", _
	"Dance", "Disco", "Funk", "Grunge", "Hip -Hop", _
	"Jazz", "Metal", "New Age", "Oldies", "Other")

you might think to loop through the array like so

For x = 0 To 12
    Debug.Print MusicGenres(x)
Next x

However, because you may want to add more items to the array at a later time, it's best to use the LBound() and UBound() functions to delimit the counter's boundaries, as in

For x = LBound(MusicGenres) To UBound(MusicGenres)
	Debug.Print MusicGenres(x)
Next x

This way, no matter how many times you add items to the array, you won't need to modify the For...Next loop at all. Also, keep in mind that you don't need to subtract 1 from the UBound() value because the function returns the array's largest available subscript NOT the number of items in the array.


For the week of September 11th
How to build a data-shaped SQL clause for Visual Basic

Since it's introduction in ADO 2.0, data shaping has remained largely on the fringes of Visual Basic arcanum. Relegated to the back pages of musty manuals, you may have overlooked this useful aspect of ADO. If you're not familiar with data-shaping, in essence, it lets you create recordsets within recordsets--or parent/child relationships--all with a single ADO object. This means no messy joins, no complicated filtering, and no need for spaghetti-code in presentation logic. Data shaping reduces the amount of traffic crossing a network, provides more flexibility when using aggregate functions, and reduces overhead when interfacing with leading-edge tools like XML.

To create a shaped recordset, you use a standard SQL statement, along with three major keywords: SHAPE, APPEND, and RELATE, like so
SHAPE {SELECT EmployeeID,FirstName, LastName 
FROM Employees} 
	APPEND ({SELECT OrderID, shipname, EmployeeID 
	FROM orders} AS SomeAlias 
	RELATE EmployeeID TO EmployeeID);

The SHAPE keyword specifies and defines the parent recordset. Once you create a parent object, you next APPEND the child recordset. This clause uses a similar syntax as SHAPE and contains the necessary SQL statement to create the child recordset within the parent. In addition to the child recordset's SQL statement, you must also indicate how you want the two recordsets to RELATE.


For the week of August 28th
Add pop-up menu support to the Rich Textbox control

While Visual Basic provides an easy way to create shortcut menus, some controls such as rich text boxes don't inherently support pop-up menus. To overcome this obstacle, you can take advantage of the MouseDown() event. Visual Basic triggers this event when you press the mouse button. To provide a pop-up menu, create a shortcut menu, then simply call it in the control's MouseDown() event.

To see how this works, add a Rich Textbox control to a sample form. Next, select Tools | Menu Editor from Visual Basic's IDE menu bar, and create a simple menu with one or two items. Then, switch to the form's code window, and enter

Private Sub RichTextBox1_MouseDown(Button As Integer, _
	Shift As Integer, X As Single, Y As Single)
If Button = vbRightButton Then
	Me.PopupMenu Menu
End IF
End Sub

For the week of August 21st
Add Web mouseover effects to Visual Basic

One common effect found in sites all across the Web is what's termed the mouseover effect. This effect usually provides some form of limited animation in a Web page element. For instance, when you move the mouse over a button, the button changes color, giving it a glowing appearance. Given an understanding of Visual Basic's mouse events, you can easily supply these same effects in your own applications.

To imitate the same graphical effect in a Visual Basic project, use the MouseMove() event. As its name implies, this event lets you track the mouse's movement over controls, forms, and MDIForms. For forms, this event follows the syntax:

Private Sub Form_MouseMove(button As Integer, _
	shift As Integer, x As Single, y As Single)

Where button stores the current state of the mouse buttons; shift indicates the state of [Shift], [Ctrl], and [Alt] keys; and x and y the current mouse coordinates.

To illustrate, launch a new Visual Basic project and place a command button on the default form. Next, set the control's background color to yellow (&H0000FFFF&) and make the font 8 point. Finally, set the Style property to 1-Graphical so Visual Basic will display the color. Now add the following code

Private Sub Command1_MouseMove(Button As Integer, _
	Shift As Integer, X As Single, Y As Single)
With Command1
	.BackColor = vbGreen
	.FontSize = 12
End With
End Sub

Now, when you run the project, the button turns green and the font size increases when you move the mouse pointer over it. To turn the button back to its original state when you move the mouse pointer elsewhere, add the following event:

Private Sub Form_MouseMove(Button As Integer, Shift As _
	Integer, X As Single, Y As Single)
With Command1
	.BackColor = vbYellow
	.FontSize = 8
End With
End Sub

For the week of August 14th
Move files to the Recycle Bin with Visual Basic and Windows API

Want to delete a file and have it go into the recycle bin? The Kill statement permanently deletes the file. Instead, try the following:

Private Declare Function SHFileOperation Lib _
"shell32.dll" (ByRef lpFileOp As _
SHFILEOPSTRUCT) As Long

Private Const ERROR_SUCCESS = 0&
Private Const FO_COPY = &H2
Private Const FO_DELETE = &H3
Private Const FO_MOVE = &H1
Private Const FO_RENAME = &H4
Private Const FOF_ALLOWUNDO = &H40
Private Const FOF_CONFIRMMOUSE = &H2
Private Const FOF_FILESONLY = &H80
Private Const FOF_MULTIDESTFILES = &H1
Private Const FOF_NOCONFIRMATION = &H10
Private Const FOF_NOCONFIRMMKDIR = &H200
Private Const FOF_RENAMEONCOLLISION = &H8
Private Const FOF_SILENT = &H4
Private Const FOF_SIMPLEPROGRESS = &H100
Private Const FOF_WANTMAPPINGHANDLE = &H20

Private Type SHFILEOPSTRUCT
        hwnd As Long
        wFunc As Long
        pFrom As String
        pTo As String
        fFlags As Integer
        fAnyOperationsAborted As Long
        hNameMappings As Long
        lpszProgressTitle As String '  only used if FOF_SIMPLEPROGRESS
End Type

Next create a function called Recycle, like so

Public Sub Recycle(ByVal FileName As String)
    Dim CFileStruct As SHFILEOPSTRUCT

    With CFileStruct
        .hwnd = Me.hwnd
        .fFlags = FOF_ALLOWUNDO
        .pFrom = FileName
        .wFunc = FO_DELETE
    End With

    If SHFileOperation(CFileStruct) <> ERROR_SUCCESS Then
        'An error occurred.
    End If

End Sub

To test the procedure, create a dummy text file, drop a command button onto a Visual Basic form, and then attach the following code

Private Sub Command1_Click()
Recycle "c:\test.txt"
End Sub

When you click the button, Windows asks if you want to move the file to the Recycle Bin.


For the week of August 7th
Avoid variable type comparison glitches in textboxes

When you retrieve a numeric value from a textbox, you may want to compare it to some other preset value. For example, consider the following code:

Select Case Text1.Text
   Case 1 to 12
      MsgBox "Acceptable Value"
   Case Else
      MsgBox "Unacceptable Value"
End Select

You might think that this code would compare a numeric value in Text1 and return Acceptable Value for values 1 through 12, and Unacceptable Value for all other numbers. Instead, this code snippet displays the Acceptable Value message for only 1, 10, 11, and 12, but not 2, 3, 4, etc. That's because a textbox's Text property returns the value as a string, and so compares them as such in the Select Case statement.

To avoid this unexpected glitch, convert the numbers with the Val() function. This function automatically converts a string into the appropriate type: integer, long, single, or double. The modified code would look as follows:

Select Case Val(Text1.Text)
   Case 1 to 12
      MsgBox "Acceptable Value"
   Case Else
      MsgBox "Unacceptable Value"
End Select

For the week of July 31st
Put your Visual Basic application into deep sleep

There may be occasions when you want an application to wait for a specified period of time without performing activity or wait loops. The Timer control can help, but only up to 60 seconds. The Sleep Windows API function lets you put the application to sleep. Enter the following declaration into a standard module.

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Then, simply call the function from your code, as in:

Sleep 5000

In this case, the 5000 milliseconds puts the application to bed for 5 seconds. You can specify your own period of time, though.


For the week of July 24th
Compact and repair Jet databases with DAO

Access's databases are typically reliable, but occasionally they can get damaged. There are numerous reasons why, such as powering down the computer without first closing the database file or the system simply crashing. Another source of problems for databases is excessive fragmentation-when records are deleted from a database file, the file becomes fragmented. Whatever the reason, once the database is damaged, end-users are often left stranded until technical support finds the time to help them. Fortunately, you can take measures to help users quickly recover from such problems, by providing built-in utilities that perform routine database maintenance.

The DAO library provides two methods to perform the necessary maintenance that keeps Access databases running smoothly--RepairDatabase and CompactDatabase. The RepairDatabase method uses the syntax

RepairDatabase dbname

where dbname is a string representing the full pathname of the Access database (.MDB) that you need to repair. This CompactDatabase method uses the following syntax:

CompactDatabase olddb, newdb, _
	locale, options, password

You specify the full path and filename of your source database as a string in the method's olddb property. Likewise, you specify the destination of the compacted database with the newdb property.

So, to use these statements, you could add code similar to:

RepairDatabase "C:\myData1.mdb"
CompactDatabase "C:\myData1.mdb", App.Path & "\myDataNewName.mdb"

If you use DA0 3.6, then you can remove the RepairDatabase statement. DAO 3.0 incorporates this functionality into the CompactDatabase method-you can no longer compact and repair databases as separate actions.


For the week of July 17th
Link Lotus Notes to Visual Basic 6.0

With the introduction of Lotus Notes and Domino Release 5.0.2b, you now have the ability to manipulate the Domino object model via COM. As a result, you can use VB 6.0 to take advantage of Lotus/Domino services and databases.

To do so, however, you'll need Lotus Notes client version 5.02.b (or higher), Domino Designer client, or Domino Server. As with most Visual Basic object libraries, the programs need not be running to use them. Lotus has plans to make this runtime package distributable independent of its full software installation.

Next, set a reference to the Notes back-end DLL, Lotus Domino Objects. This object model conforms to a hierarchy similar to CDONTS, as seen in the sample code, which displays a message box with the first name in a Lotus Notes' address book. Notice that before you can access any of the objects within the NotesSession, you must initialize a session first.

Dim domSession As New Domino.NotesSession    
Dim domDatabase As New Domino.NotesDatabase
Dim domDocument As NotesDocument
Dim domViewEntry As NotesViewEntry
Dim domView As NotesView
Dim domViewNav As NotesViewNavigator 
Dim strName As String

domSession.Initialize
Set domDatabase = domSession.GetDatabase("", _
	"names.nsf")   
Set domView = domDatabase.GetView("Contacts")
' This view contains the list of names
Set domViewNav = domView.CreateViewNav
Set domViewEntry = domViewNav.GetFirstDocument()
Set domDocument = domViewEntry.Document

strName = domDocument.GetItemValue("FullName")(0)
MsgBox strName

Set domViewEntry = Nothing
Set domViewNav = Nothing
Set domView = Nothing
Set domDocument = Nothing
Set domDatabase = Nothing
Set domSession = Nothing

For the week of July 10th
Avoid cascading UserControl Change() events

When you expand one of Visual Basic's built in controls, avoid building in a cascading Change event. This occurs when internal UserControl code changes a constituent control's value. Doing so triggers the constituent control's Change() event. Most likely, you'll have also exposed this event to the developer. If the developer has placed code in the UserControl's Change() event, then this will be triggered as well. For instance, suppose you create an ActiveX Control project and add a standard textbox as a constituent control. Next, you add the basic code:

Private mstrText As String
Public Event Change()

Private Sub Text1_Change()
MsgBox "Text1 Change"
RaiseEvent Change
End Sub

Public Property Get Text() As String
Text = mstrText
End Property

Public Property Let Text(Val As String)
mstrText = Val
End Property

Private Sub UserControl_ExitFocus()
Text1.Text = "Hello, " & Text1.Text
End Sub

Under this scenario, the UserControl will modify the textbox's text whenever it loses the focus. However, suppose a developer places the UserControl on a standard form and adds the following code:

Dim X as Integer
Private Sub UserControl11_Change()
X = X + 1
MsgBox X
End Sub

Now, every time the UserControl loses focus, Visual Basic increments X by one-probably not what the developer had in mind.

To avoid the unintentional consequences cascading Change events can cause, consider adding an internal public Boolean variable to the UserControl that tracks internal changes vs. external ones, like this:

Private blnInternal as Boolean

Private Sub Text1_Change()
If Not blnInternal Then
   MsgBox "Text1 Change"
   RaiseEvent Change
End If
End Sub

Private Sub UserControl_ExitFocus()
blnInternal = True
Text1.Text = "Hello, " & Text1.Text
blnInternal = False
End Sub

For the week of July 3rd
Accept only numbers in a text field

Often, to ensure that users enter only numbers in a text field, you'll want to validate the text as they enter it. The textbox's Change() event provides the best place to do so. However, simply using the IsNumeric() function alone won't do the trick. For instance, suppose you created the following procedure

Private Sub Text1_Change()
If Not IsNumeric(Text1.Text) Then
    Text1.Text = ""
End If

Under these circumstances, if the user entered the number -333, the control wouldn't accept it because the beginning dash isn't a number. Instead, consider using the following function

Private Sub Text1_Change()
If Not ValidateNumeric(Text1.Text) Then
    Text1.Text = ""
End If
End Sub

Private Function ValidateNumeric(strText As String) _
    As Boolean
ValidateNumeric = CBool(strText = "" _
    Or strText = "-" _
    Or strText = "-." _
    Or strText = "." _
    Or IsNumeric(strText))
End Function

For the week of June 26th
Display UserControl property settings in the Property Window

At design-time, when you set a control's property value, Visual Basic often provides a list of valid settings in a dropdown list. To display such a list for your own custom UserControl property settings, you'll need to add an enumerated property to your ActiveX Control project. To do so, declare the property's potential values with the Enum statement, like so

Public Enum UCBorderStyles
    UCBorderNone = 0
    UCBorderFlat = 1
    UCBorderThin = 2
    UCBorderFrame = 3
    UCBorderThick = 4
End Enum

Once you've established the potential values, you'll need to use the enumerated type in the Property Let and Get statements, as in

Private mintBorderStyle As UCBorderStyles

Public Property Get BorderStyle() As UCBorderStyles
   BorderStyle = mintBorderStyle
End Property

Public Property Let BorderStyle(val As UCBorderStyles)
   mintBorderStyle = val
End Property

Now, when Visual Basic instantiates the control at design time, the valid UCBorderStyles will appear in the Property Window's dropdown list for the BorderStyle property.


For the week of June 19th
Don't lose focus in UserControls

Typically, to determine when a UserControl gains and loses focus, you might think to use the standard GotFocus and LostFocus events. However, in UserControls these two events aren't always reliable. There are situations where a control will lose the focus without firing the LostFocus event (for example, when Alt+Tabbing to another application), and also get the focus without firing the GotFocus event. As a result, your best bet for monitoring the focus status is to insert code in the UserControl's EnterFocus and ExitFocus events.


For the week of June 12th
Uncover internal DLL functions with Dependency Walker

While the Windows API functions are fairly well documented, you may come across other DLL files that remain a mystery. For example, suppose you want to incorporate the MIDAS digital audio player into your application. The help files that come with this free DLL list all the important external function names. However, because the DLL doesn't support Visual Basic, you must hook into the DLL with API declarations. The internal names supported in this library aren't in the help files.

To unravel this mystery, you can use the Dependency Walker, a handy utility that comes with Visual Studio (appears as Depends on the Windows program menu). Simply point the utility to the DLL in question, and it will list all the internal file names that you'll need to refer to in your API declarations. For example, in the MIDAS DLL, you use a function called MIDASplayModule to play a sound module. However, the internal name for the function is _MIDASplayModule@8. As a result, you must declare the function in Visual Basic like so

Declare Function MIDASplayModule Lib "midas11.dll" Alias _
     "_MIDASplayModule@8" (ByVal module As Long, ByVal loopSong _
     As Boolean) As Long

Using the Dependency Walker on MIDAS, you could quickly and easily determine the library's internal function names.


For the week of June 5th
A better way to gather multi-selected Visual Basic ListBox items

Conventional Visual Basic wisdom states that in order to gather the selected items from a multi-select ListBox, you should loop through all the items and test the Selected property. As with all loops, however, this can potentially bog down slower CPU's. As a much faster and more elegant alternative, you can use the SendMessage() API function instead.

As you probably know, this function lets you send a message to one or more windows. The declaration statement conforms to the following syntax:

Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg _
   As Long, ByVal wParam As Long, lParam As Any) As Long

Since we want to gather the listbox's selected items, we'll send the LB_GETSELITEMS constant in the wMsg argument, which you declare like so:

Private Const LB_GETSELITEMS = &H191

In essence, the LB_GETSELITEMS message fills an array with the index numbers of all the selected items. As a result, you must pass two additional arguments with SendMessage(). The first argument should contain the maximum number of selected items. To retrieve this value, you can simply use the listbox's SelCount property. The second argument should hold the array variable you want to fill with index values. The following example shows how you might use this function:

Dim ItemIndexes() As Long, x As Integer, iNumItems As Integer
iNumItems = ThisBox.SelCount
If iNumItems Then
   ReDim ItemIndexes(iNumItems - 1)
   SendMessage ListBox1.hwnd, LB_GETSELITEMS, iNumItems, _
     ItemIndexes(0)
End If
For x = 0 To iNumItems - 1
   MsgBox ListBox1.List(ItemIndexes(x))
Next x

After being passed to the SendMessage function, iNumItems holds the total number of selected items, and the ItemIndexes array holds the selected item index values. Notice, that you must pass a pointer to the ItemIndexes array, and not the array itself. Thus, we passed ItemIndexes(0) into the SendMessage function, not ItemIndexes().


For the week of May 22nd
Spell check RTF documents in Visual Basic

To spell check RTF documents, you could resort to embedding Word into your application. However, if you plan to use the WebBrowser control, there's no need to program Word for spell checking. That's because the WebBrowser control provides this functionality for you. To access it, you use the WebBrowser control's ExecWB() method with the OLECMDID_SPELL flag. To illustrate, add the WebBrowser control to a form, then add the following code to the form's Load() event

WebBrowser1.Navigate "D:\Internet\spell.rtf"

Replace the path with any string that points to an RTF file. Next, add a command button, and enter this code in its Click() event:

WebBrowser1.ExecWB OLECMDID_SPELL, OLECMDEXECOPT_DODEFAULT

Run the project. The WebBrowser displays the RTF file, and when you click the command button, VB runs through the normal spell check operation.


For the week of May 15th
Compact long Visual Basic filepaths with SHLWAPI library

The PathCompactPath function in SHLWAPI provides one way to compact long filenames. It does so by replacing a portion of the pathname with an ellipsis (...). This function uses the following API declaration statement:

Private Declare Function _
PathCompactPath Lib "shlwapi"_
	Alias "PathCompactPathA" _
	(ByVal hDC As Long, ByVal _
	lpszPath As String, _
	ByVal dx As Long) As Long

As you can see, the PathCompactPath function requires three arguments. The first argument contains a device context handle. The second argument holds the address of the pathname you want to use. The third argument contains the width in pixels of the spot in which you want the pathname to fit. So, to place a compacted filename in a label named lblEllipsis, place the following in a command button's Click() event:

Private Sub Command1_Click()
Dim lhDC As Long, lCtlWidth As Long
Dim FileSpec As String

FileSpec =  "C:\MyFolder\VisualBasic\MyReallyWayTooLongFolderName\" _
& "ButWhoCares\IhaveTheAPI.doc"
Me.ScaleMode = vbPixels
lCtlWidth = lblEllipsis.Width - Me.DrawWidth
lhDC = Me.hDC
PathCompactPath lhDC, FileSpec, lCtlWidth
lblEllipsis.Caption = FileSpec
End Sub

For the week of May 8th
Referring to a DE Command's recordset in Visual Basic 6.0

Each Command object in Visual Basic 6.0's new Data Environment also has an associated recordset. In code, you refer to this object by preceding the Command object's name with rs. So, for example, if your Data Environment contains a Command named cmdSomeQuery, then to refer to the resulting recordset, you'd use something like this:

Set rst = DataEnvironment1.rscmdSomeQuery

For the week of May 1st
Read Registry values in Visual Basic without API

Often you'll want to manipulate the Windows registry in Visual Basic without resorting to lengthy API calls. Fortunately you can with the Registry Access Functions (RegObj.dll) library. This DLL lets you create a Registry object with which you can manipulate specific keys. For example, in a previous tip we showed you how to use the WebBrowser control to display an animated GIF. Unfortunately, the WebBrowser is only available if the target machine also has Internet Explorer installed. As a result, you may want to display an alternative image if Internet Explorer isn't available. To determine if IE is installed on the target Windows 95 machine, first set a reference in your project to Registry Access Functions. Then use code similar to that below:

Dim myReg As New Registry, KeyFound As Boolean
Dim HasIE As Boolean, sValue As String
sValue = ""
KeyFound = myReg.GetKeyValue(HKEY_LOCAL_MACHINE, _
    "Software\Microsoft\Windows\CurrentVersion\" & _
    "App Paths\IEXPLORE.EXE", "Path", sValue)
If KeyFound Then HasIE = (sValue <> "")
If HasIE Then MsgBox sValue 'contains the path to IE

For the week of April 10th
Display animated GIFs in Visual Basic

While the Picture ActiveX control offers a great way to display graphics, it only shows the first image in an animated GIF. To display a fully animated GIF, without rebuilding the entire graphic frame by frame, you can use the WebBrowser control (just keep in mind that this control isn't available to machines without IE 3.0 or greater). To do so, select the Microsoft Internet Controls component. When you do, the WebBrowser control appears on Visual Basic's toolbar. Drop the control onto a form, then in the form's Load() event place the following code:

WebBrowser1.Navigate "C:\Internet\anim.gif"

Where the filespec points to a valid animated GIF path or URL. When you run the program, Visual Basic displays the GIF.

Unfortunately, the WebBrowser also displays a right-hand scroll bar--probably not what you want for a decorative image. Believe or not, you can turn this scrollbar off just like you would normally via HTML, as in:

WebBrowser1.Navigate "about:<html><body scroll='no'>
<img src='D:\Internet\anim.gif'></img></body></html>"

Now when you run the form, Visual Basic displays the image sans scrollbar.


For the week of April 3rd
Use Visual Basic's ADOX to determine if internal database objects exist

Soon after ADO's release, Microsoft created an extension to the object library called ActiveX Data Objects Extensions, or ADOX. This library includes most of the DAO features missing from standard ADO, including objects related to a database's schema. With ADOX you can easily determine if a database contains a specific table, view, or query.

To do so, set a reference to Microsoft ADO Ext. for DDL and Security. For schema purposes, the ADOX consists of a Catalog object, which contains the object collections that describe the database, tables and views among them. To test for an existing table in the Tables collection, you can either iterate through the collection and check each item's name against the test string; or you can use our favorite method, as seen in the following code:

Dim cat As ADOX.Catalog
Dim tbl As ADOX.Table
Dim con As New ADODB.Connection

con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.3.51;" _
& "Data Source=C:\Program Files\Microsoft Visual Studio\VB98\Biblio.mdb"
con.Open

Set cat = New ADOX.Catalog
Set cat.ActiveConnection = con

On Error Resume Next
Set tbl = cat.Tables("MyTable")

If tbl Is Nothing Then
    MsgBox "MyTable doesn't exist"
Else
    MsgBox "MyTable exists"
    Set tbl = Nothing
End If
bar, or click on screen objects.

For the week of March 27th
Hide the mouse cursor in a Visual Basic application

The ShowCursor API function provides a quick way to hide the mouse cursor. It takes the following declaration statement

Private Declare Function ShowCursor Lib "user32" _
    (ByVal bShow As Long) As Long

When you enter 0 for the bShow argument, the mouse cursor disappears. Enter -1 to make the mouse reappear. Just be aware when you use this function, however, that just because you hide the mouse pointer, doesn't mean it's disabled. To see what we mean, add the above declaration and the following code to a form.

Dim blnShow As Boolean

Private Sub Form_Click()
blnShow = Not bln
ShowShowCursor blnShow
End Sub

Private Sub Form_Load()
blnShow = True
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If Not blnShow Then ShowCursor TrueEnd 
SubNow, run the project.  

When you click the form, Visual Basic toggles off the cursor's visibility. Click the form once more, and the mouse cursor reappears. If the mouse cursor had been truly disabled, you wouldn't have been able to make the mouse cursor reappear because the click wouldn't have registered. Now, click the form so the mouse cursor disappears, and move the mouse towards the IDE's menu bar. As the invisible mouse cursor passes over each enabled button, it raises, ready to be clicked. In fact, you can still use the mouse to choose options from the menu bar, or click on screen objects.


For the week of March 20th
Evaluate string formulas in Visual Basic

If you've come to Visual Basic from Microsoft Access, you probably miss the handy Eval() method. This method evaluates a supplied string as though it were code. As a result, you could easily evaluate mathematical expressions passed as a string, like so:

iResult = Eval("(2 * 3) + 5)")

which fills iResult with the value 11.

To achieve this same functionality in Visual Basic, you've no doubt resorted to complicated string parsing functions. Or perhaps you added Access' DLL to your project, which may have seemed like an awful lot of DLL for such a simple method.Now, though, you'll be happy to know that you can use the Eval() method without a lot of overhead. Microsoft provides this functionality in its Script Control. This very lightweight ActiveX component is available so you can add end-user scripting ability to your applications. However, as a result, it also comes with an Eval method. Adding this component to your project provides very little overhead and gives you the ability to evaluate mathematical strings.To download this OCX, visit http://msdn.microsoft.com/scripting/. Select the Script Control link, then follow the Download instructions. The following code shows the simple Visual Basic we added to a command button's click event. The SC1 script control evaluates a mathematical formula in the txtFormula textbox.

Private Sub Command1_Click()
MsgBox txtFormula & " = " & SC1.Eval(txtFormula)
End Sub

For the week of March 13th
Scroll to a Visual Basic listview control's selected item

The listview control is a great way to associate items with icons, pictures, or in report rows. This control allows you to programmatically select an item in the list. However, just because Visual Basic selects an item, doesn't always mean that it will be visible to a user. For example, if you have many items displayed in the control, you might need to use the scroll bar to display an item at the bottom of the list. Fortunately, the listview control also makes it easy to programmatically scroll to a selected item. Each item in the list has an EnsureVisible method. Just as you'd expect, when you call this method, it forces the control to display the item in the visible portion of the listview.To illustrate, drop a listview control onto a default form, then right-click on it and select Properties from the shortcut menu. In the Properties Pages dialog box, change the control's View property to 3 - lvwReport. Next, click on the Column Headers tab, click Insert Column, and enter "Which Foo?" in the Text field. Click OK to complete the process.

Finally, add the following code to the form:

Private Sub Form_Load()
   Dim x As Integer

   With ListView1
      For x = 1 To 20
         .ListItems.Add Key:="Item " & x, Text:="Item" & x
       Next x
       .SelectedItem = .ListItems("foo20")
       .SelectedItem.EnsureVisible
   End With
End Sub

When you run the project, Visual Basic opens the form and displays the 20th item in this list.


For the week of March 6th
Avoid API bit-masking comparison errors in Visual Basic

Often when you use API functions, you'll want to determine if the results contain a specific flag. To do so, you use the And operator and mask the specific flag for which you're testing. For example, suppose you want to determine if the current window is maximized. First, get the current window's settings with the GetWindowLong() API function, which you declare in a standard module like so

View Source

When you run the form and click the button, the code determines if the style value contains the flag's bit value. If it does, then the boolean comparison returns a number, which Visual Basic interprets as True. If not, the comparison returns 0, or false.

Errors can occur, however, when you try to test for the exclusion of a specific flag. For example, suppose you want to perform actions when the window isn't maximized. You might think to simply use

If Not (lWinStyle And WS_MAXIMIZE) Then

(Of course, in this particular case, you could simply test for the WS_MINIMIZE flag, but there are times when API functions don't have a flag representing an opposing setting) Unfortunately, this condition statement always returns True. That's because when you use Not on a number, Visual Basic returns the opposite value of that number minus one. For instance, the expression Not 15 returns -16, which as you know Visual Basic interprets as True. With regard to the bit masking example, if the window is maximized, Not (lWinStyle And WS_MAXIMIZE) evaluates to a non-zero number, which again Visual Basic evaluates as True.

To get around this minor glitch, you can use one of the following two statments:

If Not Cbool(lWinStyle And WS_MAXIMIZE) Then
or
If Not ((lWinStyle And WS_MAXIMIZE)<>0) Then

For the week of February 28th
Correctly pass variant array parameters in Visual Basic

If you create a procedure that will accept both standard arrays as well as those created from the Spit() and Array() functions, then you'll definitely want to type the argument as a variant, not an array, as in

Sub FooList ( MyArrayList as Variant)

instead of

Sub FooList (MyArrayList() as Variant)

Why? Well, both the Split() and Array() functions create variant arrays-that is, a variant variable filled with an array subtype. And even though you manipulate them the same way, Visual Basic doesn't consider an array of variants and a variant array the same thing. So, if your procedure expects a regular array and you pass it a variant array created from the Split() or Array() function, you'll get a type mismatch error. On the other hand, Visual Basic does let you pass a regular array into a variant parameter-which makes sense when you consider a variant's universal nature.


For the week of February 21st
Generate temporary Visual Basic files with API

If you've ever used Word, or any other Office application, you probably noticed that each time you open a file, Office creates a temporary file to store changes. You may have wondered how to generate random temporary file names in your own Visual Basic application. To do so, use the GetTempFileName API function, which you declare in a standard module, like so

Public Declare Function GetTempFileName Lib "kernel32" _
     Alias "GetTempFileNameA" (ByVal lpszPath As String, _
     ByVal lpPrefixString As String, ByVal wUnique As Long, _
     ByVal lpTempFileName As String) As Long

Pass the full path name in the lpszPath argument. The lpPrefixString lets you add a three letter prefix to the beginning of the filename, and wUnique tells Windows to either create a random file name (0 setting) or use the number you supply. The lpTempFileName, of course, contains the new temporary filename. As an example, place the API declaration above in a standard module, then add the following function

Private Function GenTempName(sPath As String)
Dim sPrefix As String
Dim lUnique As Long
Dim sTempFileName As String

If IsEmpty(sPath) Then sPath = "D:\Articles\IVB"
sPrefix = "fVB"
lUnique = 0      

sTempFileName = Space$(100)
GetTempFileName sPath, sPrefix, lUnique, sTempFileName
sTempFileName = Mid$(sTempFileName, 1, InStr(sTempFileName, Chr$(0)) - 1)
GenTempName = sTempFileName
End Function

Now, open a new form and add the following code to its Click() event. (Replace D:\Articles\IVB with any valid path)

MsgBox GenTempName("D:\Articles\IVB")

For more information on generating temporary Visual Basic files with API, please see the following article in the Microsoft Knowlege Base:

Q195763 - HOWTO: Use GetTempFileName API to Create a Unique Temporary File


For the week of February 14th
Globally replace text using just 5 lines of Visual Basic code

If you've ever tried to implement a text search and replace function in Visual Basic, you probably resorted to lengthy parsing loops that used Instr() and an entire host of string functions. If so, you'll be glad to know that there's a much easier way.

With the advent of VBScript 5.0, Microsoft introduced the Regular Expression engine, which you can also use in Visual Basic. If you've used Perl or JavaScript, you may be familiar with these pattern-matching powerhouses. In a nutshell, regular expressions let you define a pattern, literal or representative, which you can then match against a second string. For example, the literal pattern 'abc' as a regular expression would find a match in 'dabcef', 'abcdef', or 'defabc'. To further refine a regular expression, the Regular Expression engine offers a host of special metacharacters, similar to wildcard characters. For example, using these metacharacters, you could search for any word that began with a letter and ended in a digit. To view these metacharacters and their uses, visit Microsoft Windows Script Technologies.

then search in the VBScript documentation for the RegExp object's Pattern property.

To create a simple find and replace subroutine, first download the VBScript DLL from http://www.microsoft.com/msdownload/vbscript/scripting.asp.

Once you've registered the dll on your system, in your Visual Basic project set a reference to Microsoft VBScript Regular Expressions. Now, via code, create a RegExp object, like so

Set MyReg = new RegExp

Next, set the object's properties.

MyReg.IgnoreCase = True
MyReg.Global = True
MyReg.Pattern = "abc"

Here, we've used a literal string to create a global, case-insensitive pattern. Finally, you execute the object's Replace method on the target string, as in

txtSearchText = MyReg.Replace(txtSearchText, "def")

That's *all* there is to it! This example would replace "abc" with "def" wherever it occurred within txtSeartText. To execute the replace only on the first matching string, set the Global property to False.


For the week of February 7th
Retrieve a file's short name in Visual Basic without API

Many times, you'll need to reference a file by it's 8.3 file naming convention. Chances are, you've seen these file names in MSDOS. For instance, under this convention, the Program Files folder becomes Progra~1. You'll be happy to know that you can retrieve this short path name without resorting to the GetShortPathName API function. As an alternative, the new Scripting Runtime library offers the ShortPath property, which it provides for both File and Folder objects. To obtain a file's short path name, simply add a project Reference to the Microsoft Scripting Runtime, then use code similar to:

Private Sub Form_Load()
Dim fsoFile As File, fso As FileSystemObject
Set fso = New FileSystemObject
Set fsoFile = fso.GetFile("C:\MyReallyLongName.txt")
MsgBox fsoFile.ShortPath
Set fsoFile = Nothing
Set fso = Nothing
End Sub

For the week of January 31st
A caveat for the FileSystemObject Delete methods in Visual Basic

As you know, the Microsoft Scripting Runtime object library makes it easy to manipulate files and folders. With the DeleteFolder and DeleteFile methods, you can eliminate unwanted items. However, these methods will abort if they encounter an error--and they don't rollback the changes they made up to that point. For instance, if a method deletes two folders out of ten and an error occurs, Visual Basic aborts the operation and only the first two items will have been deleted. Several FileSystemObject methods operate this way.


For the week of January 24th
Maintain a Visual Basic DBGrid's runtime column widths

The DBGrid is a great way to display data as a familiar grid-style output. However, if you change the column widths on the grid at runtime, Visual Basic doesn't use these new widths the next time you run the application. Fortunately, the following procedure will maintain the column widths for you:

Sub DBGridLayout(Operation As String)
'save width of columnsDim lWidth As Long
Dim clm As Column
Dim lDefWidth As Long  l

DefWidth = DBGrid1.DefColWidth
For Each clm In DBGrid1.Columns
  With clm
    Select Case LCase(Operation)
      Case "save"
        lWidth = .Width
        SaveSetting App.Title, "Cols", CStr(.ColIndex), _
          lWidth
      Case "load"
        lWidth = GetSetting(App.Title, "Cols", _
          CStr(.ColIndex), lDefWidth)
        .Width = lWidth
    End Select
  End With
Next clm

End Sub

As you can see, this procedure uses the SaveSetting and GetSetting functions to store the current width values in Visual Basic's portion of the registry. To use the procedure, call it from the parent form's Load and Unload events. Then, indicate which operation you want the procedure to perform, as in:

Private Sub Form_Load()
DBGridLayout "Load"
End Sub

Private Sub Form_Unload(Cancel As Integer)
DBGridLayout "Save"
End Sub

For the week of January 17th
Prevent drag and drop operations on Visual Basic treeview root nodes

Often when you allow drag and drop operations in a treeview control, you won't want a root node dragged to another level. For instance, if you filled the control with departments and their employees, you wouldn't want a department node placed under an employee. To prevent this from happening, you can use the OLEStartDrag event to provide validation. For example, testing for the Parent property provides a quick way determine a root node. As you know, the Parent property returns an item's Parent node. So, if the property returns nothing, then the target node to be dragged is really a root node. Using our previous tip for assigning a treeview's current node, you can use code like the following:

Option Explicit
Public dragNode As Node, hilitNode As Node

Private Sub TreeView1_MouseDown(Button As Integer, Shift As Integer, _
     x As Single, y As Single)
Set dragNode = TreeView1.HitTest(x, y)
End Sub

Private Sub TreeView1_OLEStartDrag(Data As MSComctlLib.DataObject, _
     AllowedEffects As Long)
If dragNode.Parent Is Nothing Then Set dragNode = Nothing
End Sub

Then in the OLEDragOver and OLEDragDrop events, create conditional statements to test if the dragNode is nothing, then perform the drag and drop operations accordingly. For example, the OLEDragOver event might look like this:

Private Sub TreeView1_OLEDragOver(Data As MSComctlLib.DataObject, _
     Effect As Long, Button As Integer, Shift As Integer, _
     x As Single, y As Single, State As Integer)
If Not dragNode Is Nothing Then
     TreeView1.DropHighlight = TreeView1.HitTest(x, y)
End If

End Sub

For the week of January 10th
Update the Visual Basic 6.0 DataGrid control with recordset changes

The DataGrid control is a great way to display multiple data rows in a table-like format. Unfortunately, the control is also plagued with bugs. Some have been fixed by Service Patch 3, but some haven't. For instance, if you connect the DataGrid to a DataEnvironment, then make changes to the underlying recordset and refresh the DataGrid with the Refresh method, the control still doesn't reflect the changes. Unfortunately, the Refresh method doesn't work when the control's DataSource is a DataEnvironment. Instead, to show the updated recordset changes, first update the DataEnvironment's recordset, then rebind the DataGrid to the DataEnvironment. So, if you have a Refresh button, it's click event might look like this:

DataEnvironment1.rsCommand1.Requery
Set DataGrid1.DataSource = DataEnvironment1

Now, when you click the Refresh button, the code rebinds the DataEnvironment to the DataGrid and refills the control with the refreshed data.


For the week of January 3rd
Drag & Drop the real selected Visual Basic treeview node

Enabling drag and drop in the treeview control can seem deceptively easy. The property sheet displays two properties for altering this capability. However, as you probably know, it takes a little code behind the scenes to actually make the drag and drop work. No doubt, you've seen the control's OLEStartDrag, OLEDragOver, and OLEDragDrop events, which you use to implement this technique. The basic idea behind a drag and drop code procedure is to set a public node variable equal to the currently selected node, highlight each node the mouse passes over during the OLEDragOver event, then add the dragged item to the node under the mouse pointer to complete the procedure. With this in mind, after first setting the control's OLEDragMode to 1-ccOLEDragAutomatic, (and filling it with items, of course) you might think to initiate the OLEStartDrag event like so:

Option Explicit
Public dragNode As Node, hilitNode As Node

Private Sub TreeView1_OLEStartDrag(Data As MSComctlLib.DataObject, _
      AllowedEffects As Long)
Set dragNode = Treeview1.SelectedNode
End Sub

Here, the code sets the node to be dragged equal to the currently selected node. Unfortunately, this doesn't work. That's because, a node only becomes a selected node after the MouseUp event. And as you know, you initiate a drag by holding the mouse button down. As is, the above code actually selects the previously selected node instead. So, to indicate the correct node, use the HitTest method in the treeview's MouseDown event, like so:

Private Sub TreeView1_MouseDown(Button As Integer, Shift As Integer, _
     x As Single, y As Single)
Set dragNode = TreeView1.HitTest(x, y)
End Sub

For the week of December 6th
Replace lengthy path strings with Visual Basic object variables

Many methods that manipulate a drive, folder, or file in the MS Scripting Runtime library require a filespec argument--the item's path, represented as a string. However, because the Path property is the default property for these three items (drive, folder, and file), you can also use object variables in place of the path strings. So for instance, to copy one folder to another using object variables, you could use

fldr1.Copy fldr2, False

or

MyFSO.CopyFile file1, file2, False

For the Week of November 22nd
Use ADO's native OLEDB drivers instead of ODBC

When you create a connection string, ADO gives you a choice between indicating a data source driver as either a Driver, as in

Driver={SQL Server};DBQ=database_file

or a Provider, such as

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=database_name

However, when you use the first option, ADO uses older ODBC drivers to connect to the data source; whereas the second form uses OLEDB, which is ADO's native data access interface. For this reason, you should use the Provider option whenever possible. Such native OLEDB drivers exist for SQL Server, Index Server, Site Server Search, and Oracle, among others.

When using the provider method and distributing the application, the correct version of MDAC must be distributed as well, as they are not compatible. The MDAC 2.0 series carries the OLEDB 3.51 provider, the MDAC 2.1 series carries the OLEDB 4.0 provider.


For the Week of November 15th
Let Visual Basic determine if the CD Rom drive contains media

To quickly determine if the CD Rom drive contains media, use the Scripting Runtime library's IsReady property for the Drive object. For CD Rom drives, this property returns True only if the drive contains the appropriate media. To take advantage of this handy property, add a Reference to Microsoft Scripting Runtime library (scrrun.dll). Next, create a Drive variable based on the CD Rom drive, and test the IsReady property, as shown below:

Dim FSO As FileSystemObject
Dim aDrive As Drive
Set FSO = New FileSystemObject
For Each aDrive In FSO.Drives
    If aDrive.DriveType = CDRom And aDrive.IsReady = False Then
        MsgBox "Please enter a CD."
        Exit For
    ElseIf aDrive.DriveType = CDRom Then
        MsgBox aDrive.VolumeName
        Exit For
    End If
Next
Set FSO = Nothing

For the Week of November 8th
Retrieve subitem values from a Visual Basic ListView control

When you use the ListView control in Report view, the control shows a table-like list. It's the same view that appears in the file dialog boxes (Open, Save, etc.) when you click the Details button. Each row displays additional subitem information about the main item. For example, in the file dialog boxes, the main item is the file itself, while the Size and Type columns are the subitems. When you use the ListView control in your own applications, Visual Basic makes it easy to determine which item you've selected. When you select an item in Report view, Visual Basic triggers the ItemClick event, which passes a ListItem object (the item you just clicked). From this object, you can determine its value. So, for example, the entire event procedure might look like this

Private Sub lstvwFiles_ItemClick _
  ByVal Item As MSComctlLib.ListItem)

strFileSelected = Item

End Sub

This statement would pass the selected item's value (Value being the object's default property) into the strFileSelected variable. However, you may have wondered how to retrieve the Item's associated subitem values. Fortunately, Visual Basic provides the ListSubItems collection, which contains all the subitems of an individual item. So, continuing with the example above, the Size and Type items are both subitems of the main File item. To ascertain the values of these subitems, you simply loop through the collection, like so

Private Sub lstvwFiles_ItemClick _
  (ByVal Item As MSComctlLib.ListItem)
Dim itm As ListSubItem
Dim strSubs As String

MsgBox "ItemClicked: " & Item
For Each itm In Item.ListSubItems
  strSubs = strSubs & itm & ": "
Next itm
MsgBox "SubItems: " & strSubs

End Sub

For the Week of October 11th
Add controls to a Visual Basic control array at run-time

As you probably know, a control array lets you create controls that share the same name and events. They also use fewer resources than do the same number of controls not part of a control array. Often, you may want to add a control, such as a button, to a control array at runtime. To do so, you use the Load statement, which takes the following syntax.

Load object(index)

where object is the name of the control array, and index is the index number of the new control you want to add. In order to add controls to a control array at runtime, however, you must have at least one control already in the array, (with it's index property set-most likely to 0). Visual Basic only allows 32,767 controls in an array. For example, suppose you have a form with a button control array named cmdBtn. On the button's Click event, you want to add another button to the form. To illustrate, open a new project and add a command button to the default form. In the Properties Window, enter 0 for the control's Index. When you do, Visual Basic transforms the button into a control array. Now, add the following code to the form:

Private Sub cmdBtn_Click(Index As Integer)
Dim btn As CommandButton
Dim iIndex As Integer
iIndex = cmdBtn.Count
If iIndex <= 32767 Then
    Load cmdBtn(iIndex)
    Set btn = cmdBtn(iIndex)
    With btn
        .Top = cmdBtn(iIndex - 1).Top + 620
        .Caption = "Command" & iIndex + 1
        .Visible = True
    End With
    Set btn = Nothing
End If
End Sub

For the Week of October 4th
Use the Visual Basic 6.0 Split function to count substrings

As we mentioned in a previous tip, Visual Basic 6.0 introduced the Split function, which provides a new way to parse strings. With it, you indicate a delimiter within a string, and Visual Basic fills a one-dimensional array with the substrings.

However, in addition to the functionality described above, you can also use the Split function as a quick way to determine the number of substrings within a larger string. For example, suppose you want to determine the number of times 'sea' appears in the string "She sells seashells by the seashore." To do so, simply perform the split, and use the UBound function to count the number of elements in the resultant array, as in

strTungTied = "She sells seashells by the seashore."
arySea = Split(strTungTied, "sea")
MsgBox "'Sea' appears in '" & strTungTied _
& "' " & UBound(arySea) & " times."

When you run this code snippet, Visual Basic informs you that 'sea' appears in the phrase twice.


For the Week of September 20th
Let DateDiff() determine if two dates are in the same month

To determine if two dates are in the same month, your first instinct may be to simply use the Month() function on each date, then compare the two resulting numbers. However, under these circumstances, the comparison would equate 1/1/2000 with 1/1/1999. Instead, the DateDiff() function provides one quick way to make this determination, like so:

DateDiff("m", Date1, Date2)

In this expression, the DateDiff() function finds the difference in calendar months between the two dates. If the expression returns a zero, then the two dates are in the same calendar month. To include this feature in an conditional expression, you could use the following in a query:

If DateDiff("m", Date1, Date2) Then
	'Month's are different
Else
	'Month's are same
End If

For the Week of September 13th
Force Visual Basic 6.0 to open maximized code windows

While Visual Basic 5.0 was very good at remembering how you preferred the IDE windows-maximized or normal. Visual Basic 6.0 isn't. It always opens the Code and Object windows in normal view. Fortunately, you can modify this behavior with a minor tweak to the Windows Registry so that the IDE always opens these two windows maximized. Unfortunately, when you make these changes, Visual Basic 6.0 will ALWAYS open them maximized-it still won't remember your preferences between sessions.

Before we begin, take note that altering the Registry is risky business and you should always make a back-up copy of the settings so that you can restore them if something untoward happens. That said, to force Visual Basic 6.0 to open a maximized Code or Object window, you add a new value called MDIMaximized to the following Registry key:

HKEY_CURRENT_USER/Software/Microsoft/Visual Basic/6.0/MDIMaximized = "1"

To do so, in Windows click the Start button and select Run. Enter RegEdit in the Run dialog box, then click OK. When Windows displays the system registry, navigate through the keys until you've found the Visual Basic 6.0 folder. Next, right-click anywhere in the right-hand pane and select New/ String Value from the shortcut menu. Enter MDIMaximized as the name and press [Enter]. Now, right-click on MDIMaximized and select Modify from the shortcut menu. Finally, in the Edit String dialog box, enter 1 for the value and click OK. When you do, Windows assigns the value to MDIMaximized. That's all there is to it! Close the registry and open a Code or Object window in a Visual Basic 6.0 project. The IDE displays them maximized.


For the Week of September 6th
Add a new line to existing textbox text

Often, you may want to append additional information to existing text within a multiline textbox. For instance, suppose you want to add the string "Updated: " followed by the current date. To do so, you can take advantage of the SelStart and SelText properties. As you probably know, the SelStart property returns or sets the beginning of a selection. The SelText returns or sets the actual selected text. If there isn't a selection, then both properties return the insertion point. So, to insert a new line of text in a multiline textbox, use code similar to:

Dim strNewText As String
With Text1
strNewText = "Updated: " & Date
.SelStart = Len(.Text)
.SelText = vbNewLine & strNewText
End With

This code snippet moves the insertion point to the end of any existing text in Text1, then inserts a new line followed by the additional information.


For the Week of August 30th
Enabling the horizontal scrollbar in a RichTextbox control

By default, when you add a RichTextbox control to a form, Visual Basic sets the RightMargin property to 0. This means that the text you enter wraps in the control. To display the horizontal scroll bar, you must set the RightMargin property to a value greater than the control's width. Otherwise, even if you set the ScrollBars property to 1-rtfHorizontal, the RTB won't display the scrollbar.

As an example, add a 3200 wide RTB to a form, then set the RightMargin to 3300 and the ScrollBars to 1-rtfHorizontal. Run the project and type text into the control until it extends beyond the RTB's boundaries. When you do, Visual Basic displays the horizontal scroll bar.


For the Week of August 23rd
Visible Cues from a Minimized Form

Suppose you want a form to perform a task while minimized, then notify the user without a message box and while remaining minimized. You can send a message via a changing icon on the minimized form in the taskbar.

Create a form containing a timer and an image list. Set the timer's Interval property to 2000, then use the ImageList control's Custom property to add three images. Finally, add this code to the Timer event:

Private Sub Timer1_Timer()
Static iImage As Integer
iImage = iImage + 1
If iImage > 3 Then iImage = 1
Me.Icon = ImageList1.ListImages(iImage).Picture
End Sub

For the Week of August 9th
Use the Timer control for longer than 1 minute

As you know, the Timer control provides a great way to schedule events in a Visual Basic project. When you enable the control, it fires off its Timer event every n milliseconds, as determined by the TimeInterval property. However, the TimeInterval property only accepts numbers up to 65,535, or just over one minute. As a result, you may have wondered how to use this control for periods longer than that. To do so, use a form, or project level, variable to keep track of how many times the Timer event fires. Then, in the Timer event, re-enable the control if enough time hasn't passed. For example, consider the code below that we attached to a standard form.

Option Explicit
Dim iElapsedMin As Integer
Const cMax_Min As Integer = 2

Private Sub Form_Load()
Timer1.Enabled = True
iElapsedMin = 1
End Sub

Private Sub Timer1_Timer()
lblText.Visible = (iElapsedMin = cMax_Min)
Timer1.Enabled = (iElapsedMin < cMax_Min)
iElapsedMin = iElapsedMin + 1
End Sub

For a Week of August 2nd
A new Format function

Visual Basic 5 has the Format command that almost works the same as Print. The difference is that Format shortens the output string length if all the format characters are not used. To work around this I wrote a Public Function called FormatNum.

Public Function FormatNum(MyNumber As Double, FormatStr As String) As String

   ' This Function returns number formatted as a string
   '    with the desired minimum number of characters
   ' MyNumber - Use CDbl(MyNumber) in the function
   '    call to prevent type mismatch error.
   '
   FormatNum = Format$(MyNumber, FormatStr)
   If Len(FormatNum) < Len(FormatStr) Then
      FormatNum = Space$(Len(FormatStr) - Len(FormatNum)) & FormatNum
   End If
End Function

Use this function like this:

Print #FileNumber, FormatNum(CDbl(MyVariable), " #### ")

For the Week of June 7th
Using Server.HTMLEncode() Function

Although it is not recommended (and almost always a bad idea) many ASP applications contain and pass connection string information. If your ASP application was written with ActiveX Data Objects (ADO) 1.x and it passed the ADO ConnectionString property using HTTP POST or GET, it may fail when switched to ADO 2.0. ADO 2.0 returns the ConnectionString with the "Extended Properties" argument in double-quotes. If this value is passed to an HTML property, it will result in a truncated ConnectionString value at the first double-quotes encountered. An example of the ConnectionString property returned using ADO 2.0 is displayed below:

Provider="MSDASQL.1;Data Source=Mytest;Connect Timeout=15;
Extended Properties="DSN=MyTest;DBQ=C:\MyTest.mdb;
DriverId=25;FIL=MS Access;MaxBufferSize=512;PageTimeout=5;";
Locale Identifier=1033

The problem may be remedied by displaying the Server.HTMLEncode() function as:

<%@ LANGUAGE="VBScript"%>
<%
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open "TheConnectString.."
%%>

<form method=post action=test.asp>

<input type=hidden name=txtConnString
value="<%=Server.HTMLEncode(objConn.ConnectionString)%>">

<input type=submit value=" Submit! ">
</form>

For the Week of May 31st
Using Response.IsClientConnected Property to Determine Browser Connection

When a browser requests an ASP page from the Web Server, but does not wait for the entire page to be downloaded, the server continues to process the request, wasting valuable CPU cycles. If running Internet Information Server (IIS) 4.0, you can use the Response.IsClientConnected property to determine whether or not the browser is still connected to the server.

If it is not connected to the server, processing can be stopped to conserve CPU cycles. To do this, request an ASP page that contains the script below and use PerfMon to monitor the CPU cycles on the Web server. Note if you click "Stop" in the browser, the number of CPU cycles will decrease sooner than if the loop had continued.

<%@ LANGUAGE="VBSCRIPT" %>
<%
Function IsConnectedAfter(Seconds)
Dim StartTime
Dim PauseTime

IsConnectedAfter = True
StartTime = Now

Do While DateDiff("s", StartTime, Now) < Seconds
PauseTime = Now
Do While DateDiff("s", PauseTime, Now) < 1
'Do Nothing
Loop
Response.Write "."
If Response.IsClientConnected = False then
IsConnectedAfter = False
Exit Function
End If

Loop
End Function
%>


For the Week of May 24th
Enhancing Browser Page Load Performance

If your ASP application is experiencing browser page load performance problems consider implementing one or both of the following techniques.

One easy way to improve the speed performance of an ASP page is to set the @ENABLESESSIONSTATE to False if an ASP page does not use any session variables. To accomplish this, insert the following line of code at the top of the ASP page.

<%@ENABLESESSIONSTATE = False%>

Page performance also may be improved by copying the values of collection variables into local variables (as opposed to referencing the object each time the variable value is needed). For example, if you want to reference the Request.ServerVariable collection to determine the users logon id, you would use the following line of script:

Request.ServerVariables("LOGON_USER")

If you need to use this value in several places on your page, it is much more efficient to store this value to a local variable and then reference the local variable.

<%
Dim User
User = Request.ServerVariables("LOGON_USER")
Request.Write User
%>


For the Week of May 17th
Switching to Design View after Modifying ASP Script in Visual InterDev

When developing an Active Server site using Visual InterDev6 you may have encountered the annoying problem of not being able to switch the editor into Design view after you have modified the ASP script. The Visual InterDev editor can't switch into Design view because there is unquoted ASP script inside of an attribute value or inside of a <select> tag. The problem is that the Design Editor doesn't know how to handle ASP script within certain HTML tags. Inserting the code below into an ASP page from the Visual InterDev source Editor and then switching to Design view will demonstrate this problem:

<SELECT>
<OPTION value="<%=Value1%>">First Option</OPTION>
</SELECT>

To get around this problem, you need to use the Response.Write method to write out the HTML that contains the embedded ASP. You will then be able to switch to Design view. The HTML control however will not appear on the page. The code below demonstrates how to accomplish this.

<SELECT>
<%
Response.Write "<OPTION value=" & Value1 & "> First Option</OPTION>"
%>
</SELECT>


For the Week of May 10th
Parsing Using SPLIT Function

Parsing functions are one of the most commonly, over-written string manipulation functions. Visual Basic 6.0 answered this problem by adding a SPLIT function. The function is very easy to use and, with only one line of code, you can parse any string using a specific delimiter. The code looks like this:


Dim strAnimals As String
Dim iCounter As Integer
Dim arrAnimals() As String

strAnimals = "Cats,Dogs,Horses,Birds"

'-- Parse String
arrAnimals = Split(strAnimals, ",")

'-- Loop through array
For iCounter = LBound(arrAnimals) To UBound(arrAnimals)
MsgBox arrAnimals(iCounter)
Next


For the Week of May 3rd
Keep Your Background Processes Running

In Visual Basic, if you make a call to the MSGBOX function all other background processes that you may have running (counters, timer events, etc) are stopped until the user acknowledges the Msgbox dialog box. This can be potentially devastating if you write an application that runs unattended.

To overcome this problem, you must use the Windows API call for the MessageBox function. It looks and acts the same as the Visual Basic "msgbox" function, but does not stop the background processes from running.

In a module, paste the following API declaration:

Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As
Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As
Long) As Long

Next, on the default form add a timer control, 2 command buttons, and a label. Then type the following code into the form, which demonstrates the Visual Basic msgbox and API MessageBox functions. That's all there is to it.

Private Sub Command1_Click()
MsgBox "The Timer STOPS!"
End Sub

Private Sub Command2_Click()
MessageBox Me.hwnd, "Notice the timer does not stop!", "API Call", _ vbOKOnly + vbExclamation
End Sub

Private Sub Timer1_Timer()
Label1.Caption = Time
End Sub


For the Week of April 26th
Creating Professional Documents with Code

Have you ever wanted to create polished, professional documents, like those created in Microsoft Word, through the use of code? Follow these easy steps to make it happen:

First add a reference to your project for "Microsoft Word 8.0 Object Library" (MSWORD8.OLB).

Then add the following code to create an instance of Word and add text to a new document:

Dim objWord As New Word.Application

'-- Show Microsoft Word
objWord.Visible = True

'-- Add new Document
objWord.Documents.Add

'-- Add text to Document
objWord.Selection.TypeText "Visual Basic!"

'-- Select all Text
objWord.Selection.WholeStory

'-- Change Font Size
objWord.Selection.Font.Size = 50

Set objWord = Nothing

Check out the Object Browser for more properties and methods exposed by the Word Object.


For the Week of April 19th
Convert NULL Values to Empty Strings to Avoid Errors

One way to avoid errors from occurring when retrieving NULL values from a recordset object is to inspect the field's value. If it is NULL, then convert it to an empty string or zero. For example:

If isnull(rs("Field")) then tmp="" else tmp=rs("Field")
form.textfield=tmp

An even simpler way is to use the format function, which will convert a NULL value to an empty string automatically, avoiding any error messages. It will look like this:

form.textfield=format(rs("Field"))

Top of Page


Friday, Feb. 26th:
Writing to the Windows NT event log

Windows applications typically write to the NT event log to provide the user with useful information. In VB5/6, the App object now provides methods to make writing to the event log in Windows NT a snap:

'-- Start Event Logging
Call App.StartLogging("", vbLogToNT)

'-- Log Events to NT
Call App.LogEvent("Info", vbLogEventTypeInformation)
Call App.LogEvent("Error", vbLogEventTypeError)
Call App.LogEvent("Warning", vbLogEventTypeWarning)

Check out the Knowledge Base article App.LogEvent Only Logs in Compiled Applications for more information.

Top of Page


Friday, Feb. 12th:
Keeping track of Visual Basic source code builds

Keeping track of source code builds in Visual Basic can be easy, if you use the Version Numbering feature provided in EXE creations. Click on the options button when creating an EXE file. Then turn on the "Auto Increment" checkbox.

Versioning supports a three-segment version number: Major, Minor and Revision. The Auto Increment feature, if selected, will automatically increase the Revision number by one each time you run the Make Project command for this project.

Typically, build information will go on an About form. Just add a label called, "lblVersion" and add the following code to your form:

lblVersion.Caption = "Version: " & App.Major & "." & App.Minor & "."
& App.Revision

If my Major version number is 2, the Minor number is 1 and the Revision is 12, the label will display: "Version: 2.1.12"

Top of Page


February 5, 1999
Implementing a "wait" function in Visual Basic

Here's a simple way of implementing an accurate "wait" function in Visual Basic.

  1. Add a timer called timer1 to a form. Set its Interval property to 0 and it's enabled property to FALSE.
  2. Add two labels (label1 and label2) and a command button (command1) to the form.
  3. Add the following subroutine and Timer1 event code:
    Public Sub Wait(seconds)
    
        '-- Turn timer on
        Timer1.Enabled = True
    
        '-- Set Timer Interval
        Me.Timer1.Interval = 1000 * seconds
        While Me.Timer1.Interval > 0
            DoEvents
        Wend
    
        '-- Turn timer off
        Timer1.Enabled = False
    End Sub
    
    Private Sub Timer1_Timer()
        Timer1.Interval = 0
    End Sub
    

That's it! Use the Wait function anywhere a delay is required, for example:
Private Sub Command1_Click()
    Label1.Caption = Now
    Wait (5)
    Label2.Caption = Now
End Sub

Top of Page


January 29, 1999
Create templates from existing forms

Here's a way for you to create a template from an existing form so you can use it to design future forms. Once you've created the 'perfect' form, place it in the ..\Template\Forms\ subdirectory. Now, the next time you want to add a new form to your project, you'll see that the newly created form is in the list of available form templates! Just select it and you're on your way. This tip also applies to modules, classes, etc.

Top of Page


January 4, 1999
Nothing to declare?

When you use the Windows API, you should never need to write your own Declare statement. The API Text Viewer utility loads in a text file (WIN31API.TXT for 16-bit, and WIN32API.TXT for 32-bit) and lets you copy declares, global constants, and user-defined types. You can also convert the text file to an Access database for faster searching. Handy, eh?

Top of Page


November 16, 1998
Figuring out the current screen resolution

You can use the following small piece of code to detect the current screen resolution and then act on the information - for instance, by resizing form objects to suit the user's resolution.

Dim x,y As Integer
    x = Screen.Width / 15
    y = Screen.Height / 15
    If x = 640 And y = 480 Then MsgBox ("640 * 480")
    If x = 800 And y = 600 Then MsgBox ("800 * 600")
    If x = 1024 And y = 768 Then MsgBox ("1024 * 768")

Top of Page


November 09, 1998
Finding the last day of the month
In many industries (particularly in the insurance industry), it's important to know the last day of the month. To find the last day of a given month, add a text box and a command button to a form. Enter the following code in the command button:

Dim TEMP2 As Date
Dim nLastDay As Integer
TEMP2 = InputBox$("Please Enter A Date", "LastDay")
nLastDay = DatePart("d", DateAdd("M", 1, TEMP2 - DatePart("d", TEMP2)))
Text1.Text = nLastDay

When you run the application and click the button, you'll be prompted for a date. Then, the program will display the last day of that month of any year.

Top of Page


August 31, 1998
GETting the contents of a file
submitted by Pritesh
TransCapacity LP; spritesh@hotmail.com

To read a complete file in Visual Basic, the normal procedure is to read the contents of the file line by line and accumulate it into a string. Instead, you can use the GET function to read the file with a single call. Doing so simplifies and speeds up the process of reading a file.

You can use the following function:

    Dim Handle As Integer
    Dim FileString As String
    Handle = FreeFile
    Open "C:\TEMP\TST.TXT" For Binary As #Handle
    FileString = Space(FileLen("C:\TEMP\TST.TXT"))
    Get #Handle, ,FileString
    Close #Handle

This code involves a single call to return the contents of the file.

Top of Page


August 24, 1998
Help with Shell
submitted by Brad Gile
bgile@amfam.com

Suppose you have a DOS program, Dosapp.exe. This program produces an output file that will subsequently be processed, and you want to do this N times. The code might look like this:

    for Trial = 1 to N
    x=shell("Dosapp.exe",vbHide)
    Process the output
    Next Trial

The problem is, the code after the Shell may be executed before the Shelled Dosapp.exe has created the file. There are complicated ways of solving this, including API calls, but here's a simple solution: the FileLen function. If the file to be processed is x$, you can just insert a few lines of code:

    for Trial = 1 to N
    Open x$ for output as 1
    Close 1
    ' This sets file length equal to zero
    x=shell("Dosapp.exe",vbHide)
    Do While FileLen(x$) = 0
    DoEvents
    ' Halt further execution until x$ is created and closed
    Loop

    ' Process the output to add to a new file
    Next Trial

You may want to do other things such as using Timer to prevent an infinite Do Loop, but this is the main idea. FileLen() works because even if x$ is open and has data in it, FileLen(x$) = 0. Thus you're assured that the process code won't execute until x$ is fully created.

Top of Page


August 17, 1998
Watch your window state
submitted by Rick Michelhaugh
3m8@ornl.gov

My application saves the window location and size, so it will open the next time at the same screen location. This technique is great and pleases customers. But after several months, the program wouldn't start right -- it would die with an 'overflow error'. We tried every possible test, and could only determined that something in the INI file had become corrupted. Finally, we found that the window's Top, Left, Width, and Height values were incorrect. When the program started and read these wrong numbers, the 'overflow error' was the result.

As it turned out, the numbers were being thrown off when the user minimized the window and exited Windows with it minimized. As the program closes, it saves the position values of the minimized state -- which messes up the values. To fix the problem, we added some code to check the windowstate. If it's vbMinimized, the program skips the code that saves the window position.

Top of Page


August 10, 1998
Writing to and reading from INI files
submitted by David Dommisse
Dommisse@hotmail.com

Here's an example that will write to and read from a INI file. You can use a file named TEST.INI that looks like this:

  [MyApp]
  Key1= TestString

Put the following code in the declaration section of a module:

View Source

Now, use the following code to read from the INI file:

View Source

The key's value will be in retStr = TestString. The following code will write to the INI file:

View Source

After you write to the INI file, its contents will look like this:

  [MyApp]
  Key1=TestString
  Key2=My Test

Top of Page


August 3, 1998
Tracking down Resume Next
submitted by Bill Shadish
Fundamental Objects, Inc; bills@fo.com

This tip is extremely simple. But try it, and see if it helps you some time in the future.

Maybe you've inherited a large chunk of code from that developer who's smiling on the way out the door. Or, perhaps you're getting ready to ship that version 1.0 application to 100 users, try this. In any case, take a minute to search all of the code for the string

     On Error Resume Next

In fact, try this even if you don't want to ship your code to 100 users -- or especially if you have code that seems to fail "for no reason." You might be surprised at what that ol' Resume Next is hiding.

Top of Page


July 27, 1998
Labeling your forms
submitted by Bill Shadish
Fundamental Objects, Inc; bills@fo.com

Do you have a ton of screens in your application? Do you also have plenty of users who want to "help you" by pointing out buttons that are one twip out of place? Sometimes it's hard to know what screen users are talking about when they're trying to communicate a problem -- particularly if they're in a different location than you.

To reduce the pain of this process, I add a label (called lblHeader) to the top of each GUI window, nominally to hold start-up information for users when they first open the window. You can also use this label to hold the name of the window the user is looking at, by using the following code:

Private Sub Form_Load()
SetupScreen me
End Sub


Public SetupScreen (frm as Form)
' Do other set-up stuff here (fonts, colors).
HookInFormName frm
End Sub

Public Sub HookInFormName(frm As Form)
' The Resume Next on Error allows forms that do not use a standard
' header label to get past this.
On Error Resume Next
frm.lblHeader.Caption = "(" & frm.Name & ") " & frm.lblHeader.Caption
End Sub
Note that if you don't want to use a label, that you can also use code like

    frm.print frm.name 
to print to the back of the window itself.

Top of Page


For the Week of July 6, 1998
Opening a browser to your homepage
submitted by Dan Newsome
D&D Information Professionals, newsomed@earthlink.net

You can use code like the following to open a browser to your homepage. Modify filenames, paths, and URLs as necessary to match the values on your system.

Dim FileName As String, Dummy As String
Dim BrowserExec As String * 255
Dim RetVal As Long
Dim FileNumber As Integer
Const SW_SHOWNORMAL = 1 ' Restores Window if Minimized or

Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

Declare Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" _
(ByVal lpFile As String, ByVal lpDirectory As String, ByVal lpResult As _
String) As Long
'<Code> ---------

BrowserExec = Space(255)
FileName = "C:\temphtm.HTM"

FileNumber = FreeFile() ' Get unused file number

Open FileName For Output As #FileNumber ' Create temp HTML file
Write #FileNumber, " <\HTML>" ' Output text
Close #FileNumber ' Close file

' Then find the application associated with it.
RetVal = FindExecutable(FileName, Dummy, BrowserExec)
BrowserExec = Trim$(BrowserExec)
' If an application is found, launch it!
If RetVal <= 32 Or IsEmpty(BrowserExec) Then ' Error

Msgbox "Could not find a browser"

Else
RetVal = ShellExecute(frmMain.hwnd, "open", BrowserExec, _
"www.myurl.com", Dummy, SW_SHOWNORMAL)
If RetVal <= 32 Then ' Error
Msgbox "Web Page not Opened"
End If
End If

Kill FileName ' delete temp HTML file

Top of Page


For the Week of June 29, 1998
Creating a incrementing number box
submitted by Bryan Shoemaker
www.shadow.net/~fubar

You can't increment a vertical scroll bar's value -- a fact that can become annoying. For example, start a new project and place a text box and a vertical scroll bar on the form. Place the vertical scroll bar to the right of the text box and assign their Height and Top properties the same values. Assign the vertical scroll bar a Min property value of 1 and a Max value of 10. Place the following code in the vertical scroll bar's Change event:

Text1.Text = VScroll1.Value

Now press [F5] to run the project. Notice that if you click on the bottom arrow of the vertical scroll bar, the value increases; if you click on the top arrow, the value decreases. From my perspective, it should be the other way around.

To correct this, change the values of the Max and Min properties to negative values. For example, end the program and return to the design environment. Change the vertical scroll bar's Max value to -1 and its Min value to -10. In its Change event, replace the line you entered earlier with the following:

Text1.Text = Abs(Vscroll1.Value)

Now press [F5] to run the project. When you click on the top arrow of the vertical scroll bar, the value now increases. Adjust the Height properties of the text box and the scroll bar so you can't see the position indicator, and your number box is ready to go.

Top of Page


For the Week of June 22, 1998
Measuring a text extent
submitted by Nenad Cus Babic
Nenad@computer.org

It's very simple to determine the extent of a string in Visual Basic. You can do so with WinAPI functions, but there's an easier way: Use the AutoSize property of a Label component. First, insert a label on a form (labMeasure) and set its AutoSize property to True and Visible property to False. Then write this simple routine:

Private Function TextExtent(txt as String) as Integer
labMeasure.Caption = txt
TextExtent = labMeasure.Width
End Function

When you want to find out the extent of some text, simply call this function with the string as a parameter.

In my case it turned out that the measure was too short. I just added some blanks to the string. For example:

Private Function TextExtent(txt As String) As Integer
labMeasure.Caption = " " & txt
TextExtent = labMeasure.Width
End Function

Top of Page


For the Week of June 15, 1998
Importing Registry settings
submitted by Dan Newsome
D&D Information Professionals, newsomed@earthlink.net

You can use just a few lines of code to import Registry settings. If you have an application called myapp.exe and a Registry file called myapp.reg, the following code will put those settings into the Registry without bothering the user.

Dim strFile As String
strFile = App.Path & "\" & opts.AppExeName & ".reg"
If Len(Dir$(strFile)) > 1 Then
lngRet = Shell("Regedit.exe /s " & strFile, vbNormalFocus)
End If

Top of Page


For the Week of June 01, 1998
Use FreeFile to Prevent File Open Conflicts
Submitted by markus@bactive.com

Both Access and Visual Basic let you hard code the file numbers when using the File Open statement. For example:

     Open "myfile.txt" for Append as #1
Print #1,"a line of text"
Close #1

The problem with this method of coding is that you never know which file numbers may be in use somewhere else in your program. If you attempt to use a file number already occupied, you'll get a file error. To prevent this problem, you should always use the FreeFile function. This function will return the next available file number for your use. For example:

     IntFile=FreeFile()
Open "myfile.txt" for Append as #intFile
Print #intFile,"a line of text"
Close #intFile

Top of Page


For the Week of May 25, 1998
Confirm Screen Resolution
Submitted by Nicholas L. Otley, nicholaso@kalamzoo.co.uk; www.kalamazoo.co.uk

Here's a great way to stop the user from running your application in the wrong screen resolution. First, create a function called CheckRez:

Public Function CheckRez(pixelWidth As Long, pixelHeight As Long) As Boolean
'
Dim lngTwipsX As Long
Dim lngTwipsY As Long
'
' convert pixels to twips
lngTwipsX = pixelWidth * 15
lngTwipsY = pixelHeight * 15
'
' check against current settings
If lngTwipsX <> Screen.Width Then
CheckRez = False
Else
If lngTwipsY <> Screen.Height Then
CheckRez = False
Else
CheckRez = True
End If
End If
'
End Function

Next, run the following code at the start of the program:

    If CheckRez(640, 480) = False Then
MsgBox "Incorrect screen size!"
Else
MsgBox "Screen Resolution Matches!"
End If

Top of Page


For the Week of May 18, 1998
Quick Text Select On GotFocus
Submitted by scott@beacon-inc.com

When working with data entry controls, the current value in the control often needs to be selected when the control received focus. This allows the user to immediately begin typing over any previous value. Here's a quick subroutine to do just that:

Public Sub FocusMe(ctlName As Control)
'
With ctlName
.SelStart = 0
.SelLength = Len(ctlName)
End With
'
End Sub

Now add a call to this subroutine in the GotFocus event of the input controls:

Private Sub txtFocusMe_GotFocus()
Call FocusMe(txtFocusMe)
End Sub

Top of Page


For the Week of May 11, 1998
Take Advantage of Built-in OLE Drag and Drop
Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com

To add fully functional drag and drop to your forms, all you need to do is set the OLEDragMode property and OLEDropMode property of the input controls to "Automatic." This works across forms and even across applications on the same workstation. To test this, add two text boxes to a form. Use the Property Window to set the OLEDragMode and OLEDropMode properties to "Automatic." Now run the app and enter text in one text box and drag and drop it into the other text box.

Hint: Some controls only support OLEDrag. A few others do not support OLEDrag or OLEDrop. To determine what a control supports, check for the OLEDragMode and OLEDropMode properties. If their missing the control does not support OLEDrag and/or OLEDrop.

Top of Page


For the Week of May 04, 1998
Use ParamArray to Accept an Arbitrary Number of Parameters
Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com

You can use the ParamArray keyword in the declaration line of a method to create a subroutine or function that accepts an arbitrary number of parameters at runtime. For example, you can create a method that will fill a list box with some number of items even if you do not know the number of items you will be sent. Add the method below to a form:

Public Sub FillList(ListControl As ListBox, ParamArray Items())
'
Dim i As Variant
'
With ListControl
.Clear
For Each i In Items
.AddItem i
Next
End With
'
End Sub

Note that the ParamArray keyword comes BEFORE the parameter in the declaration line. Now add a list box to your form and a command button. Add the code below in the "Click" event of the command button.

Private Sub Command1_Click()
'
FillList List1, "TiffanyT", "MikeS", "RochesterNY"
'
End Sub

Top of Page


For the Week of April 27, 1998
Use FileDSNs to ease ODBC Installs
Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com

If you're using an ODBC connection to your database, you can ease the process of installing the application on workstations by using the FileDSN (data source name) instead of the more-common UserDSN. You define your ODBC connection as you normally would with UserDSNs. However, the resulting definition is not stored in the workstation registry. Instead it gets stored in a text file with the name of the DSN followed by ".dsn" (i.e. "MyFileDSN.dsn"). The default folder for all FileDSNs is "c:\program files\common files\Odbc\data sources". Now, when you want to install the Visual Basic application that uses the FileDSN, all you need to do is add the FileDSN to the Install package and run the install as usual. No more setting up DSNs manually!

NOTE: FileDSNs are available with ODBC 3.0 and higher.

Top of Page


For the Week of April 20, 1998
Increase Your RAD-ness by Creating Your Own VB5 Templates
Submitted by Michael C. Amundsen, mike_tips@amundsen.com, http://www.amundsen.com

You can create your own Visual Basic Templates quickly and easily. If you find that you are adding the same routines to your forms, classes, BAS modules, etc. you can build generic versions and place them in the Templates folder tree of VB5. By placing the coding module (frm, bas.cls, etc.) in the proper subfolder of the Templates folder, you'll see the new item appear whenever you select the Add... dialog box. You can add as many controls, library references, and lines of code as you wish to the templates.

CAUTION: If you uninstall VB5, you may loose your Templates folder and all its contents. Be sure to keep a secured copy of all your template files in a safe location.

Top of Page


For the Week of April 06, 1998
Add Dithered Backgrounds to your Visual Basic Forms
By: Barron Anderson, Micron Electronics, Inc.

Ever wonder how the SETUP.EXE screen gets its cool shaded background coloring? This color shading is called dithering, and you can easily incorporate it into your forms. Add the following routine to a form:

View Source

Now, add to the Form_Activate event the line

   Dither ME

This version creates a fading blue background by adjusting the blue value in the RGB function. (RGB stands for Red-Green-Blue.) You can create a fading red background by changing the RGB call to

   RGB(255 - intLoop, 0, 0).

Top of Page


For the Week of March 30, 1998
Dragging items from a list to another one
By Bassam Alkharashi, bkhrashi@kacst.edu.sa

Here's a way that you can let users drag items from one list and drop them in another one. Create two lists (lstDraggedItems, lstDroppedItems) and a text box (txtItem) in a form (frmTip).

Put the following code in the load event of your form.

Private Sub Form_Load()
' Set the visible property of txtItem to false
txtItem.Visible = False
'Add items to list1 (lstDraggedItems)
lstDraggedItems.AddItem "Apple"
lstDraggedItems.AddItem "Orange"
lstDraggedItems.AddItem "Grape"
lstDraggedItems.AddItem "Banana"
lstDraggedItems.AddItem "Lemon"
'
End Sub

In the mouseDown event of the list lstDraggedItems put the following code:

View Source

In the dragDrop event of the list lstDroppedItems put the following code:

View Source

Now you can drag items from lstDraggedItems and drop them in LstDroppedItems.

Note that you cannot drag from the second list to the first. Also, the dragged item remains in the first list. You'll have to address those limitations yourself.

Top of Page


For the Week of March 23, 1998
Creating a new context menu in editable controls
By Antonio Almeida, future.systems@mail.telepac.pt

This routine will permit you to replace the original context menu with your private context menu in an editable control.

Add the following code to your form or to a BAS module:

View Source

Next, use the Visual Basic Menu Editor and the table below to create a simple menu.

Caption

Name

Visible

Context Menu

mnuContext

NO

...First Item

mnuContext1

...Second Item

mnuContext2

Note that the last two items in the menu are indented (...) one level and that only the first item in the list ("Context Menu") has the Visible property set to NO.

Now add a text box to your form and enter the code below in the MouseDown event of the text box.

View Source

Note: If you just want to kill the system context menu, just comment out the line:

 FormName.PopupMenu MenuName

in the OpenContextMenu routine.

Top of Page


For the Week of March 16, 1998
Quick Custom Dialogs for DBGrid Cells
By Mike Amundsen, mike@amundsen.com, http://www.amundsen.com

It's easy to add custom input dialogs to all the cells in the Microsoft Data Bound Grid control.

First, add a DBGrid control and Data control to your form. Next, set the DatabaseName and RecordSource properties of the data control to a valid database and table ("biblio.mdb" and "Publishers" for example). Then set the DataSource property of the DBGrid control to Data1 (the data control).

Now add the following code to your form.

View Source

Now whenever you attempt to edit any cell in the DBGrid, you'll see the InputBox prompt you for input. You can replace the InputBox with any other custom dialog you wish to build.

Top of Page


For the Week of March 9, 1998
Using the Alias Option to Prevent API Crashes
By Mike Amundsen mike@amundsen.com, http://www.amundsen.com

A number of Windows APIs have parameters that can be more than one data type. For example, the WinHelp API call can accept the last parameter as a Long or String data type depending on the service requested.

Visual Basic allows you to declare this data type as "Any" in the API call, but this can lead to type mismatch errors or even system crashes if the value is not the proper form.

You can prevent the errors and improve the run-time type checking by declaring multiple versions of the same API function in your program. By adding a function declaration for each possible parameter type, you can continue to use strong data type checking.

To illustrate this technique, add the following APIs and constants to a Visual Basic form. Notice that the two API declarations differ only in their initial name ("WinHelp" and "WinHelpSearch") and the type declaration of the last parameter ("dwData as Long" and "dwData as String").

View Source

Now add two command buttons to your form (cmdHelpAbout and cmdHelpSearch) and place the following code behind the buttons. Be sure to edit the location of the help file to match your installation of Visual Basic.

Private Sub cmdHelpAbout_Click()
'
WinHelp Me.hwnd, HelpFile, HELP_HELPONHELP, &H0
'
End Sub


Private Sub cmdHelpSearch_Click()
'
WinHelpSearch Me.hwnd, HelpFile, HELP_PARTIALKEY, "option"
'
End Sub

When you press on the HelpAbout button, you'll see help about using the help system. When you press on the HelpSearch button, you'll see a list of help entries on the "option" topic.

Top of Page


For the Week of March 2, 1998
Increment and decrement dates with the [+] and [-] keys
By Mike Coleman, Mike.Coleman@anixter.com

If you've ever used Quicken, you've probably noticed a handy little feature in that program's date fields. You can press the [+] key to increment one day, [-] to decrement one day, [PgUp] to increment one month, and [PgDn] to decrement one month. In this tip, we'll show you how to emulate this behavior with Visual Basic.

First, insert a text box on a form (txtDate). Set its text property to "" and its Locked property to TRUE.

Now place the following code in the KeyDown event:

View Source

The one nasty thing about this is that if you have characters that are not the characters usually in a date (i.e., 1-9, Monday, Tuesday, or /) you get errors in the format command. To overcome this, I set the Locked property to True. This way, the user can't actually type a character in the field, but the KeyDown event still fires.

Top of Page


For the Week of Febuary 23, 1998
Creating Win32 region windows
By AlMoataz B. Ahmed, AlMoataz_m@hotmail.com

The Win32 API includes a really amazing feature called region windows. A window under Win32 no longer has to be rectangular! In fact, it can be any shape that may be constructed using Win32 region functions. Using the SetWindowRgn Win32 function from within Visual Basic is so simple, but the results are unbelievable. The following example shows a Visual Basic form that is NOT rectangular. Enjoy!

' This goes into the General Declarations section:

Private Declare Function CreateEllipticRgn Lib "gdi32" _
(ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, _
ByVal Y2 As Long) As Long

Private Declare Function SetWindowRgn Lib "user32" _
(ByVal hWnd As Long, ByVal hRgn As Long, _
ByVal bRedraw As Boolean) As Long


Private Sub Form_Load()
Show 'The form!
SetWindowRgn hWnd, _
CreateEllipticRgn(0, 0, 300, 200), _
True
End Sub

Top of Page


For the Week of Febuary 16, 1998
Manipulate your controls from the keyboard
By Narayana Vyas Kondreddi, vyas@aditi.com,
https://members.tripod.com/~vyaskn/index.html

If you're not comfortable using your mouse--or can't achieve the precise results you'd like--these tips will come in handy.

First, you can resize controls at design time by using the [Shift] and arrow keys, as follows:

   SHIFT + RIGHT ARROW increases the width of the control
SHIFT + LEFT ARROW decreases the width of the control
SHIFT + DOWN ARROW increases the height of the control
SHIFT + UP ARROW decreases the height of the control

Note: The target control must have focus, so click on the control before manipulating it from the keyboard.

Second, by using the [Control] key and the arrow keys, you can move your controls at design time, as follows:

   CONTROL + RIGHT ARROW to move the control to the right
CONTROL + LEFT ARROW to move the control to the left
CONTROL + DOWN ARROW to move the control downwards
CONTROL + UP ARROW to move the control upwards

If you select more than one control (by clicking on the first and shift-clicking on the others), the above procedures will affect all the selected controls.

Top of Page


For the Week of Febuary 9, 1998
Simple file checking from anywhere
By Matthew Kent, mace@pacificcoast.net

To keep my applications running smoothly, I often need to check that certain files exist. So, I've written a simple routine to make sure they do. Here it is:

Public Sub VerifyFile(FileName As String)
'
On Error Resume Next
'Open a specified existing file
Open FileName For Input As #1
'Error handler generates error message with file and exits the routine
If Err Then
MsgBox ("The file " & FileName & " cannot be found.")
Exit Sub
End If
Close #1
'
End Sub

Now add a button to your form and place the code below behind the "Click" event.

Private Sub cmdVerify_Click()
'
Call VerifyFile("MyFile.txt")
'
End Sub

Top of Page


For the Week of Febuary 2, 1998
Showing long ListBox entries as a ToolTip
By Matt Vandenbush, matt_vandenbush@whbrady.com

Sometimes the data you want to display in a list is too long for the size of ListBox you can use. When this happens, you can use some simple code to display the ListBox entries as ToolTips when the mouse passes over the ListBox.

First, start a new Visual Basic project and add a ListBox to the default form. Then declare the SendMessage API call and the constant (LB_ITEMFROMPOINT) needed for the operation:

Option Explicit

'Declare the API function call.
Private Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
' Add API constant
Private Const LB_ITEMFROMPOINT = &H1A9

Next, add some code to the form load event to fill the ListBox with data:

Private Sub Form_Load()
'
' load some items in the list box
With List1
.AddItem "Michael Clifford Amundsen"
.AddItem "Walter P.K. Smithworthy, III"
.AddItem "Alicia May Sue McPherson-Pennington"
End With
'
End Sub

Finally, in the MouseMove event of the ListBox, put the following code:

Private Sub List1_MouseMove(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
'
' present related tip message
'
Dim lXPoint As Long
Dim lYPoint As Long
Dim lIndex As Long
'
If Button = 0 Then ' if no button was pressed
lXPoint = CLng(X / Screen.TwipsPerPixelX)
lYPoint = CLng(Y / Screen.TwipsPerPixelY)
'
With List1
' get selected item from list
lIndex = SendMessage(.hwnd, _
LB_ITEMFROMPOINT, _
0, _
ByVal ((lYPoint * 65536) + lXPoint))
' show tip or clear last one
If (lIndex >= 0) And (lIndex <= .ListCount) Then
.ToolTipText = .List(lIndex)
Else
.ToolTipText = ""
End If
End With '(List1)
End If '(button=0)
'
End Sub

Top of Page


Last Updated: 12/11/00  

© 2000 Microsoft Corporation. All rights reserved. Terms of Use.