VB5
Level: Beginning
Duplicate the Join Function for VB4 and VB5
The native VB6 Split and Join functions have highlighted a number
of useful techniques, and now VB5 and VB4 programmers can use
this extended facility as well. This code emulates the Join function
of VB6 for use in earlier versions. This function takes in an array of
information and gives a String as output with delimiters per the
user request:
Public Function Join(arr As Variant, Optional _
ByVal delimiter) As String
Dim sRet As String
Dim i As Integer
If IsArray(arr) Then
If IsMissing(delimiter) Then
delimiter = " "
ElseIf Len(CStr(delimiter)) = 0 Then
delimiter = ""
Else
delimiter = CStr(delimiter)
End If
For i = LBound(arr) To UBound(arr)
sRet = sRet & arr(i) & delimiter
Next i
End If
Join = Left(sRet, Len(sRet) - Len(delimiter))
End Function
VB3 and up
Level: Beginning
Store Primary Key in ItemData
Loading a combo/listbox is pretty easy, and determining what the
combo/listbox Text property selects is even easier. But if you load
a table that might contain duplicate values, you might run into a
problem—for example, many people might share the same last
name.
Here’s the solution. First, load your combo box with a table
from your database. A sub such as this works fine, by loading the
list with names and storing a lookup key in each item’s ItemData
property:
Public Sub FillComboBox(ctrControl As Control)
Set rs = db.OpenDatabse("Contact", _
dbReadOnly)
If Not rs.EOF Then
With ctrControl
Do Until rs.EOF
.AddItem rs("LastName")
.ItemData(.NewIndex) = rsTemp("ContactID")
rs.MoveNext
Loop
End With
End If
rs.Close
Set rs = Nothing
End Sub
You can now easily determine exactly which name is selected:
strSQL = "SELECT * FROM Contact Where " & _
"ContactID = " & cboMyComboBox.ItemData( _
cboMyComboBox.ListIndex)
VB5, VB6
Level: Intermediate
Show Non-Modal Forms From ActiveX DLLs
If your VB ActiveX DLL includes a non-modal form, you can’t
summon it from a VC++ client if you call the native VB Show
method of the form from within the DLL:
Public Sub Show()
' Exposed method uses .Show
Dim frm As New frmMyForm
' Show can generate Error 406
frm.Show
End Sub
Instead, use this technique in your exposed interface:
Private Declare Function ShowWindow Lib _
"user32" Alias "ShowWindow" (ByVal hWnd As _
Long, ByVal nCmdShow As Long) As Long
Private Const SW_SHOW = 5
Public Sub Show()
' Exposed method uses API call
Dim frm As New frmMyForm
Call ShowWindow(frm.hWnd, SW_SHOW)
End Sub
VB5, VB6
Level: Beginning
Retrieve Additional File Properties
Several new properties associated with files were not available
when the original VB file I/O statements and functions were
designed. To easily access these new properties—DateLast-Accessed,
Type, DateCreated, DateLastModified, Path, ShortPath,
ShortName, and ParentFolder—you need to set a project refer-ence
to the Microsoft Scripting Runtime. Set the reference by
selecting Project from the VB main menu, then select References
and check the Microsoft Scripting Runtime item. With the refer-ence
established, you can add this code to access and display the
new file properties:
Dim objFSO As New FileSystemObject
Dim objFileDetails as File
' Identify the file for which you want
' to display properties
Set objFileDetails = _
objFSO.GetFile("C:\config.sys")
' Move file properties associated with
' the above selected file into labels
' on your property form
lblFileType=objFileDetails.Type
lblDateCreated=objFileDetails.DateCreated
lblDateModified=objFileDetails.DateLastModified
lblDateAccessed=objFileDetails.DateLastAccessed
VB5, VB6
Level: Intermediate
Determine Control’s Membership in a Control Array
To determine whether a control is a member of a control array, you
can reference its Index property and handle the generated error
when the control is not in an array. Alternatively, you can use the
TypeName function, which returns “Object” for members of a
control array. The trick to using it is to reference the control array,
not just one of its members. You can do this using the Controls
collection, keying on the control’s name:
Public Function IsCntlArray(cntl As Control) _
As Boolean
IsCntlArray = _
(TypeName(cntl.Parent.Controls(cntl. _
Name)) = "Object")
End Function
VB5, VB6
Level: Beginning
Ascertain OK or Cancel From InputBox
When the user presses Cancel on a VB InputBox, the string
returned is a vbNullString. If the user inputs a zero-length string
and presses OK, the return string is empty (""). Unfortunately, in
VB, you can’t compare an empty string to vbNullString because VB
equates "" to be equal to vbNullString even though the two are
quite different.
However, you can use the undocumented StrPtr function to
determine whether the return string is indeed a vbNullString. A
vbNullString’s pointer is, by definition, zero:
Dim strReturn as String
strReturn = InputBox("Enter in a value")
If StrPtr(strReturn) = 0 Then
' User pressed Cancel
End If
VB6
Level: Advanced
Avoid Copying Data
You can use the name of a Function or Property Get procedure as
a local variable anywhere in the procedure. If your procedure
returns a String or UDT type, writing directly to the function name
instead of a temporary variable saves you from making a full copy
of your data at the end of a function. Unfortunately, you can’t
leverage this technique if your function returns an array, because
VB interprets any parentheses after the function name as a call to
the function, not as an index into the array. The overloaded
parentheses force you to use a local variable and make an expen-sive
array copy at the end of the function. However, if the assign-ment
to the function name happens on the statement before an
[End|Exit] [Function|Property], then VB simply transfers owner-ship
of the local variable to the function name instead of copying
it. Any intervening statements (including End If) preclude the
compiler from making this optimization.
VB 6
Level: Beginning
Force Tri-State Checkbox Cycling
The CheckBox control in VB supports three positions: Checked,
Unchecked, and Grayed. Unfortunately, the default behavior for
the control is to cycle between Checked and Unchecked. To set it
to Grayed, you must do it programatically.
This code shows you how to cycle between the three positions
(the order is Checked->Unchecked->Grayed->Checked ...):
Private Sub Check1_Click()
Static iState As CheckBoxConstants
Static bUserClick As Boolean
' Trap if the user clicked on the control
' or if the event was fired because you
' changed the value below
bUserClick = (iState <> Check1.Value)
' Prevents you from entering an infinite
' loop and getting an Out of Stack Space error
If bUserClick Then
Select Case iState
Case vbChecked
iState = vbUnchecked
Case vbUnchecked
iState = vbGrayed
Case vbGrayed
iState = vbChecked
End Select
' This will raise another click event but
' your boolean check prevents you from looping
Check1.Value = iState
End If
End Sub
VB 6
Level: Intermediate
Use the Immediate Window to Write Repetitive Code
You can stop a program’s execution and use the debug window to
generate code you can paste into your program. For example, you
have a recordset called rs and you wish to manually move the
contents into controls on your form or into declared variables.
Place a breakpoint after you open the recordset, press Ctrl-G to
open the Immediate window, and type this:
for each x in rs.Fields : ?"= rs.Fields(""" & _
x.name & """)" : next
When you press Enter, you get one line per field. The output should
resemble this:
= rs.Fields("Edition")
= rs.Fields("Num")
= rs.Fields("Title")
= rs.Fields("ReaderName")
= rs.Fields("ReaderFrom")
= rs.Fields("Bits16")
= rs.Fields("Bits32")
= rs.Fields("Level")
= rs.Fields("Tip")
Copy and paste this output into your code. Now you only need to
enter the destination control or variable’s name on the left side of
the equal signs. If you have a recordset with a large number of
fields, this tip is worth its weight in gold. It prevents typing errors
and saves time because the field names are pulled right from the
recordset.
VB5, VB6
Level: Beginning
Format Your Version Info
Many professional applications are required to display a version
number on all screens to indicate to users which version of the app
is currently running. This also helps with configuration manage-ment.
Here’s a function that appends the VB project’s version
number to a text description passed to the function as input. The
version information is embedded in a project by assigning major,
minor, and revision values on the Make tab of the Project Proper-ties
dialog. Then when you right-click on the resulting EXE file in
Windows, go to Properties, and click on the Version tab, the
version number matches those on your screens, providing a nice
consistency. Putting the function in a standard module—particu-larly
one made of generic reusable functions and subprocedures—
allows other developers to plug the module into their projects and
use the routine:
Public Function GetVersion(strApp As String) _
As String
' Pass in the application name you want
' displayed as part of the form's caption. A
' blank character and the version number are
' appended to the application name
' completing the caption.
GetVersion = strApp & " " & _
Format(App.Major, "#0") & "." & _
Format(App.Minor, "#00") & "." & _
Format(App.Revision, "0000")
End Function
Here’s a sample call to this function:
Dim strVersion As String
strVersion = "Application XYZ Version"
frmMain.Caption = GetVersion(strVersion)
' Set form's caption
VB5, VB6
Level: Intermediate
Bind Option Buttons to Data Controls
The Option button is a convenient way to display multiple options
from which only one can be selected. One problem is that the
Option button cannot be bound to a data control. Here’s an easy
workaround: Create an array of Option buttons and also create a
hidden text field and bind it to your data control. Place this code
in your form:
Private Sub Option1_Click(Index As Integer)
Text1.Text = Index
End Sub
Private Sub Text1_Change
Option1(Val(Text1.Text)).Value = True
End Sub
Whenever the value in Text1 is changed by the data control, it
sets the Option button of the corresponding index value to True.
Whenever the Option button is changed, it stores the correspond-ing
Index in the textbox. Because the textbox is bound to the data
control, the value is saved in the database.
VB5, VB6
Level: Beginning
Change Appearance Property at Run Time
Here’s a way of changing the “read-only at run time” Appearance
property for numerous types of controls. It works especially well
with the ListBox, TextBox, and PictureBox controls, without re-creating
the control.
Paste this code in a module and pass references to controls you
want to alter. By default, this routine changes the style from 3-D to
Flat, but you can change it back to 3-D by passing True for the
optional second parameter:
Option Explicit
Private Declare Function GetWindowLong Lib _
"user32" Alias "GetWindowLongA" (ByVal _
hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib _
"user32" Alias "SetWindowLongA" (ByVal _
hWnd As Long, ByVal nIndex As Long, ByVal _
dwNewLong As Long) As Long
Private Declare Function SetWindowPos Lib _
"user32" (ByVal hWnd As Long, ByVal _
hWndInsertAfter As Long, ByVal x As Long, _
ByVal y As Long, ByVal cx As Long, ByVal _
cy As Long, ByVal wFlags As Long) As Long
Private Const WS_BORDER = &H800000
Private Const WS_EX_CLIENTEDGE = &H200
Private Const GWL_STYLE = (-16)
Private Const GWL_EXSTYLE = (-20)
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOZORDER = &H4
Private Const SWP_NOACTIVATE = &H10
Private Const SWP_FRAMECHANGED = &H20
Public Sub ChangeStyle(ctrl As Control, _
Optional ByVal ThreeD As Boolean = False)
Dim nStyle As Long
Dim nStyleEx As Long
Const swpFlags = SWP_NOSIZE Or _
SWP_NOMOVE Or SWP_NOZORDER Or _
SWP_NOACTIVATE Or SWP_FRAMECHANGED
' Get current styles
nStyle = GetWindowLong(ctrl.hWnd, GWL_STYLE)
nStyleEx = GetWindowLong(ctrl.hWnd, _
GWL_EXSTYLE)
If ThreeD Then
' Turn Border off, ClientEdge on
nStyle = nStyle Or WS_BORDER
nStyleEx = nStyleEx And Not _
WS_EX_CLIENTEDGE
Else
' Turn Border on, ClientEdge off
nStyle = nStyle And Not WS_BORDER
nStyleEx = nStyleEx Or WS_EX_CLIENTEDGE
End If
' Set new styles and force redraw
Call SetWindowLong(ctrl.hWnd, GWL_STYLE, _
nStyle)
Call SetWindowLong(ctrl.hWnd, GWL_EXSTYLE, _
nStyleEx)
Call SetWindowPos(ctrl.hWnd, 0, 0, 0, 0, _
0, swpFlags)
End Sub
You might observe the control shrinking when you click on the
button several times. You can reproduce this behavior by repeat-edly
changing the Appearance property of the control from the
design-time Properties window. You can inhibit this resizing with
ListBox controls by setting IntegralHeight to False. With other
controls, you can consider a sizing correction after changing the
border style.