Вы находитесь на странице: 1из 110

VB TIPS & TRICKS

To Pause your application for a length of time without a timer control


First put this code either in a module as a private function on a form

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

Then when you want to pause something just do this


Sleep 1000 'Pause for 1 second

Function to Retrieve a Standard Recordset Object.


Required: VB5 / VB6 / ADO
Most of my application require extensive use of recordsets. I found myself writing the same "with"
construct over and over again. To save some time, I built the following function to return a recordset
object I can use in any situation:
'**************************************************************************
'Function: GetRSTemplate
'
'Description: Builds an empty recordset to return to the calling
' procedure. This is used to initialize a default recordset, yet allow
' the user to change default cursor parameters at this time
'
'**************************************************************************
Function GetRSTemplate(Optional eCurLoc As CursorLocationEnum, _
Optional eCurType As CursorTypeEnum, _
Optional eLockType As LockTypeEnum) As ADODB.Recordset

Dim rsholder As New ADODB.Recordset

'If the user does not include a parameter use defaults.


'The default recordset sets up a static, client side cursor with
'minimal locking

With rsholder
If eCurLoc = 0 Then
.CursorLocation = adUseClient
Else
.CursorLocation = eCurLoc
End If
If eCurType = 0 Then
.CursorType = adOpenStatic
Else
.CursorType = eCurType
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If eLockType = 0 Then
.LockType = adLockBatchOptimistic
Else
.LockType = eLockType
End If
End With

Set GetRSTemplate = rsholder


Set rsholder = Nothing
End Function

Using it is very simple. If you supply no paramters, you get a client side, static cursor with minimal
locking:

Private sub command1_click()


Dim rs as new ADODB.Recordset
set rs = GetRSTemplate()
End sub

The function uses the same enumerated values as the recordset properties, so the choices will pop up as
they do when you type in object.property =

Determines the number of workdays within a date range.


I used implicit If Statements (IIF) because they take less lines of code, and this procedure is convoluted
enough without making is bigger! In case you aren't familiar with them, they are a hold-over from VBA.
The first argument is the expression, the second is what to do if it's true, the third is what to do if it's false.
The first part determines the number of days between 2 dates. The second part determines the number of
whole weeks because each whole week has a Saturday & a Sunday in it.
The last part determines if the range spanned a weekend but not a whole week so it can subtract the
weekend out.
Private Sub FindWorkDays()
Dim A1 As Date
Dim A2 As Date
Dim iWorkDays As Integer

A1 = Text1.Text
A2 = Text2.Text

iWorkDays = A2 - A1 + 1 - Int((A2 - A1 + 1) / 7) * 2 - _
IIf(Int((A2 - A1 + 1) / 7) = (A2 - A1 + 1) / 7, 0, _
IIf(WeekDay(A2) < WeekDay(A1), 2, 0)) - _
IIf((WeekDay(A1) = 1 Or WeekDay(A2) = 7), 1, 0)

Label1.Caption = iWorkDays

End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Display a Form without a Titlebar.

Often, an application needs a special window without a title bar, the horizontal
bar at the top of a window that contains the title of the window. For instance, a splash
screen or a custom context sensitive menu are nothing more than VB forms without title
bars. To make a form appear without a title bar set the following design-time and run-time
properties.

Properties that must be set at design-time, they are read-only at


run-time:
ControlBox=False ' Turns off Control-menu: Restore, Move, Size,
Minimize, Maximize, Close
MaxButton=False ' Turns off Maximize button
MinButton=False ' Turns off Minimize button
ShowInTaskbar=False ' Hides window in Taskbar

Properties that can be set at either run-time or design-time:


Caption="" ' Blanks window title

Making a form invisible, but leaving the controls visible.

This involves using the SetWindowLong API function with the following parameters, GWL_EXSTYLE
and WS_EX_TRANSPARENT as constants.

the syntax of the function using the paramters is

dim intresult as long

intresult = setwindowlong(me.hwnd, gwl_exstyle, ws_ex_transparent)

this will make the form referenced by me.hwnd transparent so that the background can be seen but the
controls can still be accessed.

Making a form stay on top.


This one is for VB programmers who enjoy VB but are new to Visual Basic.
Create a Module called ontop.bas in this module put the following constants.

Public Const HWND_NOTOPMOST = -2


Public Const HWND_TOPMOST = -1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOSIZE = &H1

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

In the General declarations of the form (ontop.frm) declare a variable


lngontop as long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Put two command buttons on the form,

command1.caption = &SetOnTop
command2.caption = &Make Normal

Code for command1

Private Sub Command1_Click()

lngontop = SetWindowPos = (me.hwnd,HWND_TOPMOST,0,0,0,0,(SWP_NOMOVE


or SWP_NOSIZE))

End Sub

Code for command2

Private Sub Command1_Click()

lngontop = SetWindowPos =
(me.hwnd,HWND_NOTOPMOST,0,0,0,0,(SWP_NOMOVE or SWP_NOSIZE))

End Sub

The Command1 button set the Always On Top behaviour of the form, while Command2 sets it back to
normal.

Number of Workdays between two dates.


After stumbling around with case statements, tons of if statements I finally found the code to get the
number of Workdays between two dates, & it only takes a few lines of code.
Anyway, here is the code that Excel uses for its NetWorkdays function, and the code i modified to fit VB:

Excel=A2-A1+INT((A2-A1+1)/7*2-IF(INT((A2-A1+1)/7)=(A2-A1+1/7,0,IF
WEEKDAY(A2)<WEEKDAY(A1),2,0))-IF(OR(WEEKDAY(A1)=1, pre <
lblDays.Caption="intDaysBetween" 0) 1, WeekDay(EndDate)="7)," Or IIf((WeekDay(StartDate)="1" - _
0)) 2, WeekDay(StartDate), IIf(WeekDay(EndDate) 0, 7, 1) + StartDate 7)="(EndDate" IIf(Int((EndDate 2
* 7 Int(EndDate 1 intDaysBetween Date As EndDate Dim Integer VB WEEKDAY(A2)="7),1,0)"

As you can see i had to replace all the IF's with IIf's (implicit If's). The first part
determines the number of days between the 2 dates. The next part determines the number of whole weeks
between because of the Saturdays & Sundays. Then the last part determines if the dates spanned a
weekend where the start date day would be smaller that the end date day.

Lostfocus of a text box control.


This tip is w.r.t the problem of going in a loop when performing validation on the lostfocus of a text box
control.

I have seen that many programmers face this problem and to solve this problem VB 6.0 has introduced a
Validate event also.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


But as i have been using VB 4.0 from a long time i have found a simpler trick to handle this problem,
which works on VB 4.0 and VB 6.0

Place two text boxes on the form and see that the initial text property is blank.

Paste the following code on the form:

Private Sub Text1_LostFocus()


If Val(Text1.Text) > 0 Then
Text1.SetFocus
MsgBox "Value cannot be greater than zero."
End If
End Sub

Private Sub Text2_LostFocus()


If Val(Text2.Text) = 0 Then
Text2.SetFocus
MsgBox "Value cannot be equal to zero."
End If
End Sub

And now run the application. Type some value in text1 and do a lostfocus and everything will work fine.
Now change the code and do a setfocus after the message is displayed on the lostfocus of both the text
boxes.
That is change the code to :

Private Sub Text1_LostFocus()


If Val(Text1.Text) > 0 Then
MsgBox "Value cannot be greater than zero."
Text1.SetFocus
End If
End Sub

Private Sub Text2_LostFocus()


If Val(Text2.Text) = 0 Then
MsgBox "Value cannot be equal to zero."
Text2.SetFocus
End If
End Sub

Now run the application.Type some value in the first text box and perform a lostfocus and you will see
that everything goes in a loop and the only solution is to perform an abnormal termination of the
application..

Hence the trick is that if any validation is to be done and you need to place the control back on the text
box then perform a setfocus before the Message is displayed instead of performing it after the message is
displayed.

Map and Disconnect network drive letter.

I really think VBers will like this little application. It answers several questions:
How can I map a networked Drive Letter

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


How can I disconnect a networked Drive Letter
How can I Open a file or application with VB without using the limited "SHELL" command
I have included the compiled app and its source code. You will need VB6 to open this project, but it will work
in any version of VB from 32-bit 4.0 on up!

Invalid use of null error.


A nice way to avoid the 'invalid use of null' error is to use the VB build in function VarType. Use the
following code to check for the vartype:
dim retval
dim retvaltype

'add code to retrieve information from the recordset and assign it to the retval

retvaltype = VarType(retval)

if retvaltype = 2 then
retval = "" ' or you could put a 0(zero) if it is a numeric field
else
reval = retval
end if
This should be done only with small amount of variables declared as variant as it take more overhead to
delcare a variant, however if you need a fast way to solve this problem it works.

How to create dll files using Visual Basic.


It is always said that the only language for creating dll is C or C++, but VB is also very powerful and easy
to use for the creation of dll files. Here is a sample of how u can create a small message dll file and then
call it in your program, the way you call windows dll.
Open a new ActiveXDll project from New Project Menu in VB. change the name of class1 to message and
set the following properties as
Properties of class Message

Name Message
DataBindingBehavior 0-VBNone
DataSourceBehavior 0-VBNone
Instancing 5-Multiuse
MTSTransactionMode 0-NotAnMTSObject
Persistable 0-NonPersistable
There are four public proceudres for message dialogue box. You can write any type of procedure or
function, but for simplicity I wrote procdures that only display message boxes.
Public Sub information(details, title)
MsgBox details, vbInformation, title
End Sub

Public Sub exclaim(details, title)


MsgBox details, vbExclamation, title
End Sub

Public Sub question(details, title)


MsgBox details, vbQuestion, title
End Sub

Public Sub critical(details, title)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


MsgBox details, vbCritical, title
End Sub
From the File menu, choose Make Project1 Dll.. Since we haven't change the name of project, the default
name is project1. save the program.
Now open a new project in VB. Go to the Project Menu and choose References and check the Project1 as
shown below.
Create a form and place two text boxes on that form. Name them txtDescription and txtTitle. Also place
four Option button on the form and name them, optInformation, optQuestion, optCritical, and optexclaim
and a command button name cmdRun as shown

Option Explicit
Private mess As New message

Private Sub cmdRun_Click()

If optInformation = True Then


mess.Information txtDescription, txtTitle
ElseIf optQuestion = True Then
mess.question txtDescription, txtTitle
ElseIf optCritical = True Then
mess.critical txtDescription, txtTitle
Else
mess.exclaim txtDescription, txtTitle
End If

End Sub

Now write some text in description box and the title text in title text box and also choose the message type
and press the command button and u will see the appropriate message coming through the dll file.

Find the number of workdays between two dates.


Like Bill Mosca, I had to find the number of workdays between two dates. This solution doesn’t have
rules, the starting day may be any day of the week. Workdays are Monday through Friday
Put two textboxes and a command button on a form, and call them;
txtDagen
Data2
wd
Here’s the code I came up with:
Please Note: Code in dutch
Private Sub wd_Click()
If Not IsDate(txtDagen.Text) Then
MsgBox "U moet een datum invullen ( Datum 1 )"
txtDagen.Text = ""
Exit Sub

End If

If Not IsDate(Data2.Text) Then


MsgBox "U moet een datum invullen ( Datum 2 )"
Data2.Text = ""

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Data2.SetFocus
Exit Sub

End If
'================
Dagnummer$ = ""
zat$ = ""
bij$ = ""
Dagnummer1$ = ""
Dagnummer2$ = ""
Rekendagen$ = ""
Verschil$ = ""
weken$ = ""
Overdagen$ = ""
aww$ = ""
odb$ = ""
Weekdagen$ = ""
awr$ = ""
aftel$ = ""
Werkdagen = ""

Verschil$ = DateDiff("d", txtDagen.Text, Data2.Text, 2)


Dagnummer$ = DatePart("W", txtDagen.Text, 2)
Dagnummer2$ = DatePart("W", Data2.Text, 2)

Overdagen$ = Format$(7 - Val(Dagnummer$))


Rekendagen$ = Format$(Val(Verschil$) - Val(Overdagen$))
weken$ = Format$(Int(Val(Rekendagen$) / 7))

Weekdagen$ = Format$(Val(Rekendagen$) - (7 * Val(weken$)))

aww$ = Format$(Val(weken$) * 5)
awr$ = Format$(Val(aww$) + Val(Weekdagen$))

'========

If Val(Dagnummer$) = 6 Then
odb$ = Format$(Val(Overdagen$) - 1)
aftel$ = "1"
ElseIf Val(Dagnummer$) = 7 Then
odb$ = Format$(Val(Overdagen$) - 0)
aftel$ = "0"
Else
odb$ = Format$(Val(Overdagen$) - 2)
End If
'==========

If Val(Dagnummer2$) = 6 Then
zat$ = "-1"
Else
zat$ = "0"
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If Val(Dagnummer$) = 6 Or Val(Dagnummer$) = 7 Then
bij$ = "0"
Werkdagen = Format$(Val(awr$) + Val(odb$) + Val(zat$))
Else
bij$ = "1"
Werkdagen = Format$(Val(awr$) + Val(odb$) + Val(bij$) + Val(zat$))
End If

'==================

MsgBox "Aantal werkdagen = " & Werkdagen

End Sub

This function checks the validity of a credit card number.


Returns 'True' If the number is in a valid format.
Returns 'False' If the number is in an invalid format.
Description:
This algorithm should work with all credit cards.
If a card validates with this code it doesn't mean that the card is actually good, it just means that the
numbers are arranged in a valid format. If they don't validate then you've saved some time because you
don't have to process the card to find out that it is defiantely bad. I use this function in CGI forms that
process credit card orders.
Function CheckCard(CCNumber As String) As Boolean
Dim Counter As Integer, TmpInt As Integer
Dim Answer As Integer

Counter = 1
TmpInt = 0

While Counter <= Len(CCNumber)


If (Len(CCNumber) Mod 2) Then
TmpInt = Val(Mid$(CCNumber, Counter, 1))
If Not (Counter Mod 2) Then
TmpInt = TmpInt * 2
If TmpInt > 9 Then TmpInt = TmpInt - 9
End If
Answer = Answer + TmpInt
Counter = Counter + 1
Else
TmpInt = Val(Mid$(CCNumber, Counter, 1))
If (Counter Mod 2) Then
TmpInt = TmpInt * 2
If TmpInt > 9 Then TmpInt = TmpInt - 9
End If
Answer = Answer + TmpInt
Counter = Counter + 1
End If
Wend

Answer = Answer Mod 10

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If Answer = 0 Then CheckCard = True
End Function

Find the number of workdays between two dates


I had to find the number of workdays between two dates (including Saturday as a possible starting day).
The number of days were grouped into these ranges: 1 day, 2 days, 3 days, 5+days, 10+days, and 30+days.
The rules were No input on Sundays and starting dates were only Monday through Friday, but ending
dates might be on Saturdays. Here's the code I came up with:
Select Case DaysBetween
Case 0 To 6
Select Case DaysBetween
Case 0, 1
rs.Edit
rs("Days") = 1
rs.Update
Case 2
rs.Edit
rs("Days") = 2
rs.Update
Case 3
If WeekDay(Date2) = vbMonday Then
rs.Edit
rs("Days") = 1
rs.Update
Else
rs.Edit
rs("Days") = 3
rs.Update
End If
Case 4
If WeekDay(Date2) = vbMonday Then
rs.Edit
rs("Days") = 2
rs.Update
ElseIf WeekDay(Date2) = vbTuesday Then
rs.Edit
rs("Days") = 3
rs.Update
Else
rs.Edit
rs("Days") = 3
rs.Update
End If

Case 5 '5 to 9 -->5+


If WeekDay(Date2) < vbFriday Then
rs.Edit
rs("Days") = 3
rs.Update
ElseIf WeekDay(Date2) >= vbFriday Then
rs.Edit
rs("Days") = 5
rs.Update

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End If
Case 6
If WeekDay(Date2) < vbThursday Then
rs.Edit
rs("Days") = 3
rs.Update
ElseIf WeekDay(Date2) >= vbThursday Then
rs.Edit
rs("Days") = 5
rs.Update
End If
End Select
Case 7 To 11 '5 to 9 -->5+
rs.Edit
rs("Days") = 5
rs.Update
Case Is > 11
WeeksBetween = DateDiff("w", Date1, Date2)
Select Case WeeksBetween
Case 1 To 5 '10 to 29 -->10+
rs.Edit
rs("Days") = 10
rs.Update
Case Is > 5 '30+
rs.Edit
rs("Days") = 30
rs.Update
End Select
End Select
Else 'if bill is a Send Back
rs.Edit
rs("Days") = 0
rs.Update
End If
rs.MoveNext
Loop

Return True if no value is passed


If you use a typed variable as optional argument in a Function or Sub you can't use "ISMISSING" function
to determine whether it is initialized or not.
To solve this you can use "Empty" keyword as follows,
Public Function MyFun (Optional ByVal sOptVar
as String) As Boolean
if sOptVar = Empty then
MyFun = True
Else
MyFun = False
End if
End Function
This function returns True if no value is passed to the optional variable sOptVar, else returns False.
This Works in VB5.0.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Call a value from your enumeration
Everybody talks about "How useful enumeration is". Here goes a little piece of code that really
demonstrates that statement is correct.
With a little help from a property let and property get, you can call a value from your enumeration without
having to remember the description you previouslly coded inside of your enumeration since it will pops up
for you.
* Here goes the code:
'Add this code to a module.bas
Dim mForm As Byte
Enum FromForm
FormNameA = 1
FormNameB = 2
FormNameC = 3
End Enum

Property Get SetForm() As FromForm


SetForm = mForm
End Property

Property Let SetForm(Value As FromForm)


mForm = Value
End Property

'Add this to a command button on your application


'to associate with a form
SetForm = FormNameB

Snds parameters in an INDEX format to a class


We are developing a crystal report print engine dll, that will be called from another dll, and will generate
reports.
On the first tier, we want to pass the report parameters to the second tier. Although we know we can do
that on an array manner, we found out that sending the parameters in an INDEX manner should make the
process much more friendly for the first tier developer. Therefore, the code bellow, sends parameters in an
INDEX format to a class. When the class receives the parameters, it builds an array that can be used in
the class as desired.
On the first tier the code is something like the one bellow.
* After referencing the DLL or making a class named PrintEngine
Dim ObjPrint as PrintEngine.Engine
Set ObjPrint = New Engine
objPrint.StoreProcParam(0) = Variable 1
objPrint.StoreProcParam(1) = Variable 2
objPrint.StoreProcParam(2) = Variable 3
objPrint.StoreProcParam(3) = Variable 3
and so on......
On second tier the code is something like the one bellow.
Private mStoreprocparam() As Variant
Public Property Let StoreProcParam(Index As Integer, value As String)
ReDim Preserve mStoreProcParam(Index)
Let mStoreProcParam(Index) = Value
End property

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Then I use the variant mStoreProcParam on the second tier to work with the received parameters.
The main point for the sample above is the property Let StoreProcParam(Index As Integer, value As
String). This property receives one index and one value at a time and stores it in the array
mStoreProcParam(). Then, that Array can be used in a lot of different ways.

How to use MSFlexGrid to display data via ADO method


We tried this example to show how to use MSFlexGrid to display data via ADO method, and also to show
a simple SQL query. first we set up ADO object, and connected with a back end Sybase database, opened a
table, which called mirtb_patient, we used a MSFlexGrid for 25 columns(fields), and specified two loops,
variable ii for row(record), oo for column. This prepared query can get the data from a Sybase via ADO,
and dispaly them on the MSFlexGrid. Notice that when it finish, all opening objects and methods should
be closed.

Option Explicit
Dim Cn As New ADODB.Connection
Dim rs As ADODB.Recordset

'Use a connecting string or Connection object here

Dim sSQL As String


Dim sOut As String

Private Sub connect_cmd_Click()

' Set ii as the row counter

Dim ii As Long

' Set oo as the column counter

Dim oo As Integer

'ADO Demo, it populates data with MSFlexGrid !

If Cn.State = adStateOpen Then


MsgBox "ADO Connection Successful!"
End If

' Set up SQL calling

sSQL = "Select * from mirtb_patient"


rs.Open sSQL, Cn, adOpenDynamic
sOut = ""

ii = 0

' To see if end of the table?


While rs.EOF <> True
DBGrid1.Rows = ii + 1
iii = 1

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


' Loop for the column, we just used 25 columns for display!

For oo = 0 To 24
DBGrid1.TextMatrix(ii, oo) = rs.Fields(oo)
Next oo
ii = ii + 1

' Move to next record

rs.MoveNext
Wend
End Sub

Private Sub Form_Load()

'Define a Command object for the connection

Set Cn = New ADODB.Connection


Set rs = New ADODB.Recordset
Cn.ConnectionString = "DSN=TESTRCH01;UID=ADOTRY;PWD=ADOTRY;"
Cn.ConnectionTimeout = 100
Cn.Open
End Sub

Private Sub quit_cmd_Click()

' To close objects rs and cn etc., they shoud be closed here!

rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
Unload Me
End Sub

Quick single line toggle for 1/0-type boolean values


This is for toggling boolean values that flip between 1 and zero as opposed to -1 and zero, which are VB's
built-in boolean values.
One common place I use it is toggling checkboxes/checkable menu options. Both of these use as their
"checked" status either vbUnchecked (0) or vbChecked (1).
The standard method for toggling these is to use an If-Then statement:
if check_value = vbChecked Then
check_value = vbUnchecked
Else
check_value = vbChecked
End If
There is nothing inherently wrong with this, except that it takes up 5 lines of code, and the alternative of
putting it on one line, separated by colons is messy. This means extra scrolling space which can be a
hindrance, especially if you have already long procedures.
Unfortunately, you can't use the Not operator as in:
check_value = Not check_value

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


because Not (0) is -1, and worse, Not (1) is -2. This problem arises because the Not() function performs a
bitwise NOT, rather than a true logical NOT. As a result, this only works with "true" boolean values (-
1/0).
The solution which hit me is simply to add 2:
check_value = (Not check_value) + 2
hey... works like a charm!

How to save a brande new WAV file using mciSendString API


Shows you how to save a brand new WAV file using the mciSendString API (Doen't use the MCI Control
that comes with VB5!) It is really easy to do that. I learned it after studying this API for a while. I couldn't
get the MCI Control to save a WAV file though! I don't know why! If any body could, just e-mail it to me
and I will see how it is possible to do. :-)
DownloadWAVESAVE.ZIP <wavsave.zip> (File size 1.4K)
Making the Close button (X) on a form disabled
Ever wonder how those Setup programs have their X button on caption bar disabled, so you can't close
them? Well, now you can do it, when you download this!
DownloadCANTCLOS.ZIP </vb-mag/9808/item/cantclos.zip> (File size 1.7K)
Even or Odd - Update
the tip featured in your current issue to tell if a number is even or odd, works v_e_r_y slow. It does a lot of
type conversion between numbers and strings and unnecessary, slow string manipulation. It will
remarkably slow down the machine if used in a loop and uses a dirty kind of type casting because an
integer value is used as a return value of a boolean type function (!). In addition, it will only work with
Visual Basic.
There are at least two much more efficient and simple ways to tell if a number is odd or even; both
examples will store "TRUE" in the variable "bool_IsEven" if the number is even and will work in nearly
all programming languages:
a) bool_IsEven = int_Number MOD 2 = 0 b) bool_IsEven = int_Number AND 1 = 0
How to disable Ctrl Alt Del.
With Windows 95 it is possible to disable CTRL-ALT-DEL, CTRL-ESC and ALT-TAB by claiming to be
the current screen saver. Sample code can be found at http://www.vbonline.com/vb-
mag/qaweb/win95/cad.htm in the new qaweb section of VB Online Magazine. To start an application you
have a few options. Normally the Startup group is used, but since this sort of application must be
inaccessible to the general user you need to place it in the registry. There are three keys of interest:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices

All entries under these three keys are started during the bootup process and your application should be
listed under one of them. The "Run" key names applications to run every time, as does the "RunServices"
key. The difference is when they start during the boot process. You probably want to use the Run key, but
you can try both and see what the difference is. The RunOnce key will run the next time Win95 starts, but
is then purged. This is a good place to put applications that complete a setup process or a cleanup.
List Box To List Box - VB5 Professional
Here is another way to move data from one list box to another and back. The four buttons ( ADD, ADD
ALL, REMOVE and REMOVE ALL) are a control array and the subroutine FourButtons handles all

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


movements between the two list boxes. I tried to use the least amount of code. This app is an unattended
bulk printing app.

Private Sub FourButtons(ByVal Index As Integer)


Screen.MousePointer = 11
Select Case Index
Case 0 ' Add selected contractId from List1 to List2
If List1.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List1.ListIndex = -1 Then 'nothing is selected
List1.SetFocus
List1.Selected(0) = True 'select the first item in the list.
End If
DoEvents
List2.AddItem List1.Text
List1.RemoveItem List1.ListIndex

Case 1 ' Add All ContractIds from List1 to List2


If List1.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List1.ListIndex = -1 Then
List1.SetFocus
List1.Selected(0) = True
End If
DoEvents
For i = (List1.ListCount - 1) To 0 Step -1
List2.AddItem List1.List(i)
DoEvents
Next i
List1.Clear

Case 2 ' Remove selected contractId from List2 to List1


If List2.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List2.ListCount = 0 Then Exit Sub
If List2.ListIndex = -1 Then
List2.SetFocus
List2.Selected(0) = True
End If

List1.AddItem List2.Text
List2.RemoveItem List2.ListIndex

Case 3 ' Remove All ContractIds From List2 to List1


If List2.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If List2.ListCount = 0 Then Exit Sub
If List2.ListIndex = -1 Then 'nothing is selected.
List2.SetFocus 'set focus.
List2.Selected(0) = True 'select first item in list.
End If
For i = (List2.ListCount - 1) To 0 Step -1
List1.AddItem List2.List(i)
DoEvents
Next i
List2.Clear

End Select
Screen.MousePointer = 0
End Sub

How to write a Screen Saver.


The basic requirements for creating a screen saver are:
In the properties for your application set the title to:

SCRNSAVE: name

The 'name' part will appear on the list of available screen savers in the control panel. Note that
this is not the title of the form, it is the application title and must be set in the project properties.
When you compile the program use .SCR for the extension instead of .EXE as normal. Place the
compiled code in the Windows\System directory (it can also go in Windows, but System is
preferred)
When your application starts check for a previous instance and stop if one is found:

Sub Form_Load()
If App.PrevInstance Then
Unload Me ' NEVER use END!
Exit Sub
End If

End Sub

Check the command line for switches:

/s setup mode - display/update configuration options only


/p nnn preview mode - "nnn" is a handle to the preview window
/a password support check

If the screen saver is to run normally use the following to tell Windows that you are taking over as a
screen saver (this will disable ALT-TAB and CTRL-ALT-DEL and prevent more instances from
being started):

Private Const SPI_SCREENSAVERRUNNING=97


Private Declare Function SystemParametersInfo Lib "user32" _
Alias "SystemParametersInfoA" (ByVal uAction As Long, _
ByVal uParam As Long, lpvParam As Any, _
ByVal fuWinIni As Long) As Long
SystemParametersInfo SPI_SCREENSAVERRUNNING, 1, ByVal 1&, False

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Before your code terminates tell Windows you are leaving with:
SystemParametersInfo SPI_SCREENSAVERRUNNING, 0, ByVal 1&, False

For more information, including sample code that handles password protected screen savers
check http://www.arcatapet.com/vb.html or http://www.tiac.net/users/lvasseur/

Reading data from a database to display on a form is null fields.

To prevent an 'invalid use of null' error occurring when reading data from a database use the following
syntax:

Text fields:
Form1.text1.text = "" & myRecordset("TextName")

Numeric fields:
Form1.Option1.Value = 0 & myRecordset("NumricField")

How to place your application in the System Tray.


Applies to Win95 and WinNT

Both Windows95 and WindowsNT have that neat new feature called the system tray where you can place
your application. This is easily done via the Windows API called "Shell_NotifyIcon" which manages the
Taskbar which the system tray is part of.
There are a couple of tricky things that you need to keep in mind when using this function. First, the
application needs to be visible (but not necessarily seen--you can set its Top & Left to -10000) and you
also need to know how to handle callback functions. If your not sure what a callback function is then stop
here (you should learn about callback functions before proceeding).
First, place the declares into a bas module:
' These tell you what is happening in the system tray (over your icon)
Public Const WM_MOUSEISMOVING = &H200 ' Mouse is moving
Public Const WM_LBUTTONDOWN = &H201 'Button down
Public Const WM_LBUTTONUP = &H202 'Button up
Public Const WM_LBUTTONDBLCLK = &H203 'Double-click
Public Const WM_RBUTTONDOWN = &H204 'Button down
Public Const WM_RBUTTONUP = &H205 'Button up
Public Const WM_RBUTTONDBLCLK = &H206 'Double-click
Public Const WM_SETHOTKEY = &H32

' The API Call


Public Declare Function Shell_NotifyIcon Lib "shell32" _
Alias "Shell_NotifyIconA" _
(ByVal dwMessage As enm_NIM_Shell, pnid As NOTIFYICONDATA) As
Boolean

' User defined type required by Shell_NotifyIcon API call


Public Type NOTIFYICONDATA
cbSize As Long
hwnd As Long
uId As Long
uFlags As Long
uCallbackMessage As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


hIcon As Long
szTip As String * 64
End Type

' This is an Enum that tells the API what to do...


' Constants required by Shell_NotifyIcon API call:
Public Enum enm_NIM_Shell
NIM_ADD = &H0
NIM_MODIFY = &H1
NIM_DELETE = &H2
NIF_MESSAGE = &H1
NIF_ICON = &H2
NIF_TIP = &H4
WM_MOUSEMOVE = &H200
End Enum

Public nidProgramData As NOTIFYICONDATA

Place this code in Form_Load():

Private Sub Form_Load()

' Hide the form


With Me
.Top = -10000
.Left = -10000
.WindowState = vbMinimized
End With

With nidProgramData
.cbSize = Len(nidProgramData)
.hwnd = Me.hwnd
.uId = vbNull
.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
' This is the event that will trigger when stuff happens
.uCallbackMessage = WM_MOUSEMOVE
.hIcon = Me.Icon
.szTip = "Lock Your System" & vbNullChar
End With

' Call Notify...


Shell_NotifyIcon NIM_ADD, nidProgramData

End Sub

This goes into the Form_MouseMove event:

' Name: Form_MouseMove

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


'
' Purpose: Processes the Events from the TaskBar...
'
' Inputs: None
'
' Returns: None
'
' Revision: James E. Bettone 12/02/1997
'
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As
Single, Y As Single)

On Error GoTo Form_MouseMove_err:

' This procedure receives the callbacks from the System Tray icon.
Dim Result As Long
Dim msg As Long

' The value of X will vary depending upon the scalemode setting
If Me.ScaleMode = vbPixels Then
msg = X
Else
msg = X / Screen.TwipsPerPixelX
End If

Select Case msg


Case WM_LBUTTONUP
' process single click on your icon
Call Command1_Click()

Case WM_LBUTTONDBLCLK
' Process double click on your icon

Case WM_RBUTTONUP
' Usually display popup menu

Case WM_MOUSEISMOVING
' Do Somthing...

End Select

Exit Sub

Form_MouseMove_err:
' Your Error handler goes here!

End Sub

When your Application executes, it will head right into the System Tray. It is up to you to program in the
context menus and to process the callback messages.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Attaching and manipulating PictureBox and TextBox on the Command
Button.
When images are needed to be inserted at the Command Button control, there are two ways.

First, the Command Button style property is set 1(Graphic) and the shape of the image can be changed
according that the user presses the mouse button up or down. However, the images should be placed at a
fixed position, and both images and texts can't be displayed. Second, we can put the PictureBox which has
the Enabled property False on the Command Button control. In this case, we can't make use of a function
of the PictureBox. That is, we can't change the BorderStyle according that the mouse pointer is in the
PictureBox or out of the PictureBox. If PostMessage Windows API is used, we can solve this problem.
Suppose there are two bitmap files, button.bmp and camera.bmp.
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long

Private Sub Form_Load()


Picture1.TabStop = False
Picture1.BorderStyle = 0
End Sub

Private Sub Command2_Click()


Unload Me
End Sub

Private Sub Picture1_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Picture1.BorderStyle = 1
End Sub

Private Sub Command1_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Picture1.BorderStyle = 0
End Sub

Private Sub Command1_MouseDown(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Form1.Picture1.Picture = LoadPicture("button.bmp")
End Sub

Private Sub Command1_MouseUp(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Form1.Picture1.Picture = LoadPicture("camera.bmp")
End Sub

Private Sub Picture1_MouseDown(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Ret = PostMessage(Form1.Command1.hwnd, WM_LBUTTONDOWN, _
wParam, lParam)
End Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Private Sub Picture1_MouseUp(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
Ret = PostMessage(Form1.Command1.hwnd, WM_LBUTTONUP, _
wParam, lParam)
End Sub

"ShowInTaskBar" property of a VB Form at runtime.


Option Explicit
'--------------------------------

' This code will set "ShowInTaskBar" property


' of a VB Form at runtime. I haven't been able
' to change this property without unloading the
' form. This code could be useful when used in
' conjunction with a user-chosen preference.
' (Maybe you can think of a good use for it.)
' Comments, improvements? Please forward to:
' Joe LeVasseur
'-----------------------------------------------
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 Const GWL_EXSTYLE = (-&H14)
Private Const WS_EX_TOOLWINDOW = &H80
'----------------------------
Private Sub Form_Initialize()
Call SetStyle
End Sub
Private Sub SetStyle()
Dim lWindowStyle&, lRetVal&
Debug.Print Second(Now)
' 50/50 chance
If (Second(Now) Mod 2) Then
lWindowStyle = GetWindowLong(hwnd, GWL_EXSTYLE)
lWindowStyle = lWindowStyle And WS_EX_TOOLWINDOW
lRetVal = SetWindowLong(hwnd, GWL_EXSTYLE, lWindowStyle)
End If
End Sub

Delaying a VB app for a set number of Minutes, Seconds.


Option Explicit
Dim StopTheTimer As Boolean

Private Sub Command1_Click()


Dim lRetval&

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


lRetval = Delay(1, 5, StopTheTimer)
If lRetval = 0 Then
MsgBox "Time's up!"
Else
MsgBox "You cancelled!"
End If
End Sub
Private Sub Command2_Click()
StopTheTimer = True
End Sub

Public Function Delay(Mins%, Secs%, Optional ByRef StopFlag) As Long


Dim EndOfDelay
EndOfDelay = DateAdd("n", Mins, Now)
EndOfDelay = DateAdd("s", Secs, EndOfDelay)
Delay = 0
Do While (Now < EndOfDelay)
DoEvents
If Not IsMissing(StopFlag) Then
If StopFlag Then
Delay = 1
StopFlag = False
Exit Do
End If
End If
Loop
End Function

Easy Transparent Scrolling text on picture


The idea is from the scrolling text I read in a old "tips&tricks" and part of the listing is from it. Create a
form and load on it a <<.bmp>> picture, by picture property. Create a label and a timer on the form. The
properties of the label you must change are BackStyle=3D0 transparent and borderstyle 0=3DNome. Then
type the statements in the subs:
Sub Form_Load()
Me.Show
Label1.Caption =3D "Hi!! scroll Test.."
Label1.ForeColor =3D &HFF
ScaleMode =3D 3
theleft =3D ScaleWidth / 2

thetop =3D ScaleHeight

Timer1.Enabled =3D True


Timer1.Interval =3D 10
End Sub

Sub Timer1_Timer()
Label1.Top =3D thetop
thetop =3D thetop - 1
If thetop < -p1hgt Then
Timer1.Enabled =3D False

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Txt$ =3D "Finished With Scrolling"
CurrentY =3D ScaleHeight / 2
CurrentX =3D (ScaleWidth - TextWidth(Txt$)) / 2
Print Txt$
End If
End Sub
No Listbox.ScrollPos member?
Whilst working on a current project the requirement to scroll an array of listboxes simultaneously cropped
up. I was surprised to discover that there is no Listbox.ScrollPos member of the ListBox control and so
had to resort to the windows API to overcome this oversight. The results of which are included below.
Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd
As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long

' *** ListBox Scroll Event ***


Private Sub lst_Ranges_Scroll(Index As Integer)
Dim I As Integer, Pos As Integer
Static bScrolling As Boolean

If Not bScrolling Then


bScrolling = True
Pos = GetScrollPos(lst_Ranges(Index).hwnd, SB_VERT)
For I = 0 To 3
If I <> Index Then
SendMessage lst_Ranges(I).hwnd, WM_VSCROLL,
MakeLong(SB_THUMBPOSITION, Pos), SB_THUMBPOSITION
End If
Next
bScrolling = False
End If
End Sub

' *** MakeLong Method ***


Private Function MakeLong(LOWORD As Integer, HIWORD As Integer) As Long
MakeLong = Bitwise_OR(LOWORD, Bitwise_SHL(HIWORD, 16))
End Function

I hope this will be of some use to somebody someday, I know that I was surprised when I discovered the
lack of a ScrollPos Member.
The "AlwaysOnTop" property.
By Joe LeVasseur
lvasseur@tiac.net
Simply paste the code below into the form that you need to have the "AlwaysOnTop" property. Syntax
would be like this-[FormName].AlwaysOnTop= True Note the commented code in the Resize event, the
form loses the "AlwaysOnTop" property if it is minimized. I think that under most circumstances a
"AlwaysOnTop" window would probably be a fixed dialog and therefore not minimizable. One other
caveat- this code may or may not work in the IDE. (I've seen both.)
__________snip____________________________________
Option Explicit
'------------------
' Paste this into a form and you
' will have a new property-AlwaysOnTop.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


' You then can use it like any other
' property- [FormName].AlwaysOnTop= True
' Joe LeVasseur
'------------------
Private Const HWND_TOPMOST = -&H1
Private Const HWND_NOTOPMOST = -&H2
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
'------------------
Private Declare Sub 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)
'------------------
Private bOnTopState As Boolean
Public Property Let AlwaysOnTop(bState As Boolean)
Dim lFlag As Long
On Error Resume Next
If bState = True Then
lFlag = HWND_TOPMOST
Else
lFlag = HWND_NOTOPMOST
End If
bOnTopState = bState
Call SetWindowPos(Me.hWnd, lFlag, 0&, 0&, 0&, 0&, _
(SWP_NOSIZE Or SWP_NOMOVE))
End Property
Public Property Get AlwaysOnTop() As Boolean
AlwaysOnTop = bOnTopState
End Property
'------------------
Private Sub Form_Resize()
' Only need this if form can be
' minimized.(Loses the setting.)
'AlwaysOnTop = bOnTopState
End Sub
_________snip___________________________________________
Here's a another way to speed up looping through a recordset
There was a good tip for speeding up looping through a recordset in this tips section. (I have included it
below for reference) However, if you have the option, there is another way to perform an operation on
every member of a recordset that will execute much faster than any recordset operation, use a SQL
UPDATE statement to modify the records. That way, the modification executes on the database server,
and gets whatever optimizations your database engine provides. For example, to give all your
longstanding customers a discount, you could use the following SQL statement in place of any recordset:

UPDATE Customers SET Discount = 0.10 WHERE CustSince <= 1993


This could take the place of getting a recordset with the SQL:
SELECT Discount FROM Customers WHERE CustSince <= 1993
and then needing to navigate the whole recordset setting each Discount to 10%.
----------------------------------------------------------------------------
----------------------------------------------------------Accessing Databases

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


This tip is on accessing databases. For performing an operation in
every record of a Table or RecordSet (for example you could
want to update some fields, or retrieve some information), generally
this code is used.

Do
...
...
MyDynaset.MoveNext
Loop Until MyDynaset.EOF

Bat if you use this code

Dim k As Long, j As Long

MyDynaset.MoveLast
j = MyDynaset.RecordCount
MyDynaset.MoveFirst

For k = 1 to j
...
...
MyDynaset.MoveNext
Next

you should notice is 30% speed increase from the first code. I tried this code with a MDB file with
17000 records, and with a MDB file with 500 records. They both work well. The reason? Of course
because the second code does not check the EOF condition every iteration (even if it must go to the last
record and then to the first record)! Maybe, if the recordset is small you may not notice any
improvement, but in this case, processing time is short anyway. I found this code does not improve
processing time in Delphi. I think it's because Delphi EXE is compiled and not p-code.

Here's a efficient IsEven function


Function IsEven( n as Integer) As Boolean IsEven = Not -(n And 1) End Function If you want an IsOdd
function, just omit the Not.

Move elements from one list box to another


This is something I feel a few people might find to be useful. Basically it allows elements to be moved
between two list boxes as well as conditionally enabling an Execute button if there are any elements in the
second list box. You should first design a form that looks like this:

Private Sub cmdLeft2Right_Click()


If lstList1.ListCount > 0 Then
List_Refresh lstList1, lstList2
cmdExecute.Enabled = True
End If
If lstList1.ListCount > 0 Then
cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
Else

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


cmdLeft2Right.Enabled = False
cmdAll2Right.Enabled = False
cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
End If
End Sub

Private Sub cmdAll2Right_Click()


Move_All_Items "Right", lstList1, lstList2
cmdLeft2Right.Enabled = False
cmdAll2Right.Enabled = False
cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
cmdExecute.Enabled = True
End Sub

Private Sub cmdAll2Left_Click()


Move_All_Items "Left", lstList1, lstList2
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
cmdAll2Left.Enabled = False
cmdRight2Left.Enabled = False
cmdExecute.Enabled = False
End Sub

Private Sub cmdRight2Left_Click()


If lstList2.ListCount > 0 Then List_Refresh lstList2, lstList1
If lstList2.ListCount > 0 Then
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
Else
cmdExecute.Enabled = False
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
cmdAll2Left.Enabled = False
cmdRight2Left.Enabled = False
End If
End Sub

Public Sub Move_All_Items(Direction As String, List1 As Control, List2 As Control)


Dim Count As Integer
Select Case Direction
Case "Left"
For Count = 0 To List2.ListCount - 1
List1.AddItem List2.List(Count)
List2.Selected(Count) = 0
Next Count
List_Refresh List2, List1
List2.Clear
Case "Right"
For Count = 0 To List1.ListCount - 1
List2.AddItem List1.List(Count)
List1.Selected(Count) = 0

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Next Count
List_Refresh List1, List2
List1.Clear
End Select
End Sub

' Special Subroutine to move selected items from one listbox to another
Public Sub List_Refresh(SourceList As Control, TargetList As Control)
Dim N As Integer
Dim I As Integer
ReDim Remove(0 To SourceList.ListCount - 1) As Boolean 'Items to remove
For N = 0 To (SourceList.ListCount - 1) ' If selected, then , add to Target List
If SourceList.Selected(N) = True Then
TargetList.AddItem SourceList.List(N)
Remove(N) = True ' Sets item for removal from Source list
End If
Next N

Dim C As Integer ' Counts how many have been removed


C=0
For N = 0 To UBound(Remove)
If Remove(N) Then
SourceList.RemoveItem N - C
C=C+1
End If
Next N

For I = 0 To (SourceList.ListCount - 1) ' Reset Selected Flags and reset


SourceList.Selected(I) = False
Next I
SourceList.Refresh
TargetList.Refresh
End Sub

"This method is meant to work best when the multi-select feature of the listboxes is enabled"

Check a DOB against a valid age


Description: This function checks a DOB against a valid age.
Returns 'True' if the DOB is valid for the specified age.
Returns 'False' if the DOB is not valid for the specified age.
I've used this in CGI programs that need DOB validation.
Function CheckDOB(TestDOB As String, ValidAGE As Integer) As Boolean
Dim TmpInt As Long

TmpInt = DateDiff("d", CDate(TestDOB), Now)

If TmpInt < (ValidAGE * 365.25) Then


CheckDOB = False
Else
CheckDOB = True
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End Function
Tip supplied by James Johnston instant@email4life.com

Tells whether a number is even or odd.


Returns 'True' if even.
Returns 'False' if odd.
Function IsEven(TestInt As Integer) As Boolean
Dim TestValues As String, TmpChar As String, Answer As Integer

TestValues = "02468"
TmpChar = Right$(Trim(Str$(TestInt)), 1)
Answer = InStr(1, TestValues, TmpChar)

IsEven = Answer

End Function
Tip supplied by James Johnston instant@email4life.com

Accessing Databases
This tip is on accessing databases. For performing an operation in every record of a Table or RecordSet
(for example you could want to update some fields, or retrieve some information), generally this code is
used.
Do
...
...
MyDynaset.MoveNext
Loop Until MyDynaset.EOF

Bat if you use this code

Dim k As Long, j As Long

MyDynaset.MoveLast
j = MyDynaset.RecordCount
MyDynaset.MoveFirst

For k = 1 to j
...
...
MyDynaset.MoveNext
Next
you should notice is 30% speed increase from the first code. I tried this code with a MDB file with 17000
records, and with a MDB file with 500 records. They both work well. The reason? Of course because the
second code does not check the EOF condition every iteration (even if it must go to the last record and
then to the first record)! Maybe, if the recordset is small you may not notice any improvement, but in this
case, processing time is short anyway. I found this code does not improve processing time in Delphi. I
think it's because Delphi EXE is compiled and not p-code.

1 Why does CommonDialog control show error "No Fonts Installed" when ShowFont method is called ?
The possible reason could be invalid value assignment for Flags property of CommonDialog Control.
Try assigning cdlCFBoth to Flags property. This will inform the Control to use the installed fonts.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


2 What's the alternate way of connecting to database using Recordset without Connection object [ADO]
?
Dear Ravi, It's possible to avoid use of a separate connection object by passing ConnectString to
ActiveConnection property of the Recordset Object. But this may not help you in reducing the resource
usage.

3 How can i add the application to SysTray in windows ? Can you please help me out ?
Dear Hansal, It's possible to add your VB form to System Tray in 32-bit windows. Yu can fidn the
code for this with detailed explanation in the 'Downloads Section' of VB Forum on this site. Thank you for
your query.

4 Is it possible to create our own setup programs (customized) for installing VB Applications ? If so how
can we do it ? Do we need to know VC++ or SDK ?

Hi Anwar and Ramesh, You need to know only VB, Application Package and Deployment Wizard for
preparing a customized installation program. A VB App Installer package will have two major files
firstly, setup.exe and secondly, setup1.exe. The former one installs VB Runtime Environment and the
later installs the actual application. So it's always possible to modify (customize) Setup1.exe file. It's
nothing but a VB Project.

5 Sir as we will be using forms with similar properties like BorderStyle, WindowState, StartUp position,
Font, BackColor, ForeColor and sometimes even startup code, Can't we make templates and use this form
as a VB Form ? [ Srinath, Sripad, Manjunath, Harry and Monica ]
Hello, The query sent by all of you was very similar. It'll be very usefull while developing big
applications. It's possible to create your own templates. What all you ahve to do is, Open a new form set
all the properties add any code required and save it in <VB-HOME>\Template\Forms\ Folder. After this
you can see the new form in the VB Add Form Dialog itself. Thank you.

6 Is it possible to allow the user to Cut or Copy the contents of a password field (Text Box) ?
Hi Again Anwar, Well by default you are not allowed to copy or cut the contents of a password field
but it's possible to provide this feature by writing few lines of code in the KeyDown or KeyUp Events of
the password field. The following code demonstrates the same. [ To run the following code open a
StandardEXE Project and add a TextBoxby name Text1 and set it's PasswordChar field to * or # etc... ]
Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) 'For Cut [ Ctrl + X ] If Shift =
vbCtrlMask And KeyCode = vbKeyX Then Clipboard.SetText Text1.Text Text1.Text = "" End If 'For
Copy [ Ctrl + C ] If Shift = vbCtrlMask And KeyCode = vbKeyC Then Clipboard.SetText Text1.Text
End If End Sub

7 Sir, Actual Problem that i have is Can we Create control like DataGrid which contains
combobox,CheckBox. Means when we call addnew method then user must be able to select apprppriate
type from combobox as well as check for his status with check box. [ Mahesh Shinde
<mailto:mailmaheshshinde@rediffmail.com> ]
Dear Mahesh, What i understood from your query is, you wanna integrate ComboBox and CheckBox
sort of controls with DataGrid so the user can choose a value from a list or check rather than typing. It's
possible. Either you can create a new control or soemtiems you can prepare a floating control. Anyways i
suggest yout o gor for the later as the former one would take more time and you need to be carefull while
designing such a control as it may kill available resources.

8 I am doing a final year project at the mement and wan't to know how to control the Com1(or 2) ports
using VB, to Tx & Rx data from a hand unit. any suggestions welcome. [ Sweeney
<mailto:Eoin.Sweeney@bench.com> ]

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Dear Sweeney, It's very much possible to control the Communication prots from a VB application.
Please download THIS <zipz/Q_8_VID103.zip> zip and go through, it may help you.

9 sir i would like to know the code to disable the default rightclick menu that pops up when we right clik
on a text box (when appl is running) except for this all other aspects for the textbox must be enabled I was
unable to find any such refrence in the site. [ Deepa Iyer <mailto:deep7476@yahoo.com> ]
Hi Deepa, The best possible way of doing it is using SubClassing. You have to use few APIs for this.
Please download THIS <zipz/Q_9_VID104.zip> file. It has both the source code and some docs about
subclassing. Hope this will solve your problem.

10. How do you speed up VB programs?


This is a really big topic but this is going to be, at first at least, just a small section. Four things that can
often be a proven speeder upper of your program are:

Go to the Tools|Custom Controls menu choice. Remove ALL check marks or all that it lets you. Then go
to the Tolls|Reference menu choice. Remove all of these it lets you. You should be left with most of the
controls your programs need. Most of the rest of the controls your program might need are probably in
Windows Common Controls(Go back to Tools|Custom Controls and check this if you need this). This
makes a HUGE difference in many parts of your program. Loading it when run and when edited in VB is
faster. The needed files list in the Setup Wizard is cut in half almost.

Use Labels instead of TextBoxes, unless the user needs to type into the TextBox. If you like the look of the
TextBox you can reproduce it by setting a Labels border to Single.

Use control arrays for any related controls, especially with Label controls that just display text and do
nothing else. Also this works really well with Option Controls contained in a Frame.

Unless you have pictures that are updated often, you can set the Autoredraw to False and as long as you
placed controls correctly, no overlapping, set the Clip Controls option to False.

11. How to play a WAV file in VB ?


This example shows how to play a WAV file in VB. It requires one declaration (API) and one SUB. The
declaration looks like this:
' Declare Function sndPlaySound Lib "MMSYSTEM" (ByVal lpWavName$, ByVal Flags%) As Integer
Next, create a SUB to actually handle the playing of the WAV file. We'll call it PlaySound. It requires one
argument to be passed to it. The argument filename is the name of the WAV file to play.
Sub PlaySound (FileName As String)
Dim x%
x% = sndPlaySound(FileName, 1)
End Sub
Finally, place a command button or some other control on the form and place the following code in it's
click event procedure. We'll use TADA.WAV for the sound to play, but it could just as easily be any WAV
file you like.
Command1_Click
PlaySound ("tada.wav")
End Sub
Finally, just run the program and click the button!

12. Detecting Previous Instance of Form ?


Ans.This routine will prevent two copies of your program from running at the same time. It consists of a
Function that determines if another instance is already running and activates it if it is. The Sub

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


(Form_Load()) calls this function and closes the program if there is another instance of the program
running.
Function AnotherInstance () As Integer
Dim AppTitle$
If App.PrevInstance Then
' Hold the title of the application (title bar caption)
AppTitle$ = App.Title
' Change our application title
App.Title = "No longer want this app running..."
' Activate the previous instance
AppActivate AppTitle$
' Let calling procedure know another instance was detected
AnotherInstance = True
Else
' Let calling procedure know another instance was NOT detected
AnotherInstance = False
End If
End Function

Sub Form_Load ()
' Don't want two copies of the program running at the same time
If AnotherInstance() Then End
' Note: that this routine will not work if the application's title changes
' (showing file names in the title bar for example).
End Sub

13. How to Extract Numerical Values from Text Strings ?


Ans . The purpose of this routine is to take a string of text (such as with a textbox) and extract a
numerical value from it. let's say that you have a textbox in which people enter dollar amounts. Many
users are likely to enter something such as "$ 4,335.49" and expect calculations to be performed on it. The
trouble is, the value of that string is 0 (zero), not 4335.49!
Using the following function, a person would actually be able to enter a string like "$4,335.49" or even
"4335.49 dollars" and still have the value returned as 4335.49. The function shown below called
PurgeNumericInput requires one argument. That argument is a string containing numbers with or without
special characters. Look at the function below.
Function PurgeNumericInput (StringVal As Variant) As Variant
On Local Error Resume Next
Dim x As Integer
Dim WorkString As String

If Len(Trim(StringVal)) = 0 Then Exit Function ' this is an empty string


For x = 1 To Len(StringVal)
Select Case Mid(StringVal, x, 1)
Case "0" To "9", "." 'Is this character a number or decimal?
WorkString = WorkString + Mid(StringVal, x, 1) ' Add it to the string being built
End Select
Next x
PurgeNumericInput = WorkString 'Return the purged string (containing only numbers and decimals
End Function
You then just need to call the function passing a string argument to it. An example is shown below.
Sub Command1_Click
Dim NewString as Variant

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


NewString = PurgeNumericInput("$44Embedded letters and spaces 33 a few more pieces of garbage
.9")
If Val(NewString) <>0 Then
MsgBox "The Value is: " & NewString
Else
MsgBox "The Value is ZERO or non-numeric"
End If
End Sub
Notice how much alphanumeric garbage was placed in the string argument. However, the returned value
should be 4433.9! Two questions might arise when using this type of example.
#1 - What if the string was "0"? This could be determined by checking the length of the string (variant)
returned. If the user entered a "0" then the length of the string would be > 0.
#2 - What if the string contains more than one decimal? You could use INSTR to test for the number of
decimals. However, chances are, if the user entered more than one decimal you might better have them re-
enter that field again anyway. <sly smile>

14. How to Create Multi colored Forms in VB without using API ?


Ans Although using the API is a nice way to create multi-colored forms, there might be a reason why you
would wish to create one without using the API. To use the example shown below, place the SUB in a
module (*.BAS). The routine requires several parameters to be passed to it. They are:
• FormName - Used to indicate which form is to be colored
• Orientation% - Top to bottom or right to left painting effect
• RStart% - (0-255) value for Red
• GStart% - (0-255) value for Green
• BStart% - (0-255) value for Blue
• RInc% - Amount to increment or decrement for Red
• GInc% - Amount to increment or decrement for Green
• BInc% - Amount to increment or decrement for Blue
Sub PaintForm (FormName As Form, Orientation%, RStart%, GStart%, BStart%, RInc%, GInc%,
BInc%)
' This routine does NOT use API calls
On Error Resume Next
Dim x As Integer, y As Integer, z As Integer, Cycles As Integer
Dim R%, G%, B%
R% = RStart%: G% = GStart%: B% = BStart%
' Dividing the form into 100 equal parts
If Orientation% = 0 Then
Cycles = FormName.ScaleHeight \ 100
Else
Cycles = FormName.ScaleWidth \ 100
End If
For z = 1 To 100
x=x+1
Select Case Orientation
Case 0: 'Top to Bottom
If x > FormName.ScaleHeight Then Exit For
FormName.Line (0, x)-(FormName.Width, x + Cycles - 1), RGB(R%, G%, B%), BF
Case 1: 'Left to Right
If x > FormName.ScaleWidth Then Exit For
FormName.Line (x, 0)-(x + Cycles - 1, FormName.Height), RGB(R%, G%, B%), BF
End Select
x = x + Cycles
R% = R% + RInc%: G% = G% + GInc%: B% = B% + BInc%

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If R% > 255 Then R% = 255
If R% < 0 Then R% = 0
If G% > 255 Then G% = 255
If G% < 0 Then G% = 0
If B% > 255 Then B% = 255
If B% < 0 Then B% = 0
Next z
End Sub
To paint a form call the PaintForm procedure as follows:
PaintForm Me, 1, 100, 0, 255, 1, 0, -1
Experiment with the parameters and see what you can come up with. Keep the values for the incrementing
low so as to create a smooth transition, whether they are negative or positive numbers.

15. I would like to create masks for date and time input. I have heard of
the MaskedEdit control but don`t know how to use it.
Ans. Keyword: MaskedBox control, Mask property.
Example: To input a date in a MaskedEditBox, set its mask to "##/##/####". When
you run the app, the box will show "__/__/____" and you will be able to type
numbers in its prompt area.
16. I am writing a VB5 program to interface with a MS Access
database...it will add and delete records and scroll through all the
records currently entered. I need to be able to add a Find function to this
application. I tried the FindFirst and FindNext methods but...it didn`t
work. I need it to display the record that matches the given search
string.
A: You want to Search the Given data in a Table...Let me give you an
example for this. Suppose You want to Search for CustomerName in
Customer Table of CustomerDetails Database. The text you want to
search is in "Text1.Text" box.

Dim VSearch as String

VSearch = "CustomerName="+Text1.Text
CustomerDetails.Recordset.FindFirst VSearch

'This is all you have to do to check that


'your search is succesful use NoMatch Property

ustomerDetails.Recordset.Nomatch = True' Not Found


Else "Record is Found "

Yogesh

A: The following is from an early program of mine.


Today I use SQL and do not use the data control
if possible. But this works:

Dim sBookMk As String, sTmp As String

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


sBookMk = Data1.Recordset.Bookmark
Tmp = "Name >= " & Chr$(34) & _ txtSeek.Text & Chr$(34)'Was one line.
Data1.Recordset.FindFirst sTmp
If Data1.Recordset.NoMatch Then
Data1.Recordset.Bookmark = sBookMk
End If

The number one problem with FindNext, etc. methods and SQL is in
handling the quotes. Note the Chr$(34)`s. They are quotation marks.
Beyond that, note that the routine cleans up after itself by checking
for a match. If no match, the data control`s recordset is set back to
the record that was current prior to the FindNext.

Ans: In an inventory database I have I need to be able to look up items


by number. This must be done so entering a number of 35 will find
item 35, but not item 535. In case it would help here is the code
I use from my access module.

Function Find_Record_By_Item#()
Dim Notice As String
Dim Msg As String
Dim Buttons As Integer
Dim Title As String
Dim Number As String
Dim Number2 As Integer
start:
Msg = "Enter an item number to look up and press enter." _
& Chr(13) & Chr(13) & "To cancel this operation press escape."
Title = "Item Number Look up"
Number = InputBox(Msg, Title)
If Number <> "" Then
DoCmd.FindRecord Number, acEntire, False, acSearchAll, True, acAll, True
Screen.ActiveForm![txtItem_Number].SetFocus
Number2 = Screen.ActiveForm![txtItem_Number].Text
If Number <> Number2 Then
Msg = "The item number you entered was not found." & Chr(13) _
& "Please enter another one."
Title = "Invalid Item Number"
Buttons = 48
Notice = MsgBox(Msg, Buttons, Title)
GoTo start
End If
Else
Screen.ActiveForm![txtItem_Number].SetFocus
End If
If Screen.ActiveForm![txtItem_Number].Locked = True Then
Screen.ActiveForm![txtDate_of_Sale].SetFocus
End If
End Function

The last If allows for immediate data entry after lookup


on the sales form where the item number, description, and

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


asking price fields are looked (They can only be changed
on the items form.). Chr(13) represents a carriage return.

17. Forms with Scrollbars?


The problem I am facing is this : I am trying to create a form with a height greater than the current screen
height, and I am trying to incorporate vertical scrollbars to navigate up and down in that form. I have no
idea how to do it. Any help would be appreciated.

A. Add a picture box, and then the scroll box to the form. Have the scroll bar move the picture box up and
down on the form. Put all of the controls on the "scrollable form" inside the picture box. Make sure the
picture box has no border.

18. How do I make a Combo box drop down when


the control first gets focus?
A. One way that this can be done in VB5 is with the SendKeys command as follows:

'Put this in Combo1_GotFocus:

If Combo1.ListIndex = -1 Then
SendKeys "{F4}{Down}"
Else
SendKeys "{F4}"
End If

This will make it dropdown and select first item if nothing is selected or just drop down if there is an item
already selected.
19. How to activate cancel button in dialog box?
How do I assign the cancel property to the cancel button of a simple dialog box? I`ve written a macro in
Excel using VB and use dialog box inputs to get user data. However, hitting the cancel button during
data entry has no effect.

A. What you have to do is set CancelError = True

This means that if the user presses Cancel it causes an error - so then you set up an error trap to do what
ever you want when the error happens. Look in the help file to get the exact syntax but it is something like
this:
'Pseudo code
on error goto errortrap ... ... ...
'all your stuff here ... ... ...
exit sub
errortrap:
if err=32755 then
'that might not be the right value, you can look up the error
'codes in the help file.
end if
end sub

20. . I need to know how to pass variables between two forms.


A. There are two ways to accomplish this:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


1. Declare your variables needed in both forms as global in a module. This
will make them accessable for all.
2. The other way is to prefix a variable name with its form name:
Form2.Label1.Caption = Form1.List1.List(List1.ListIndex)
This example displays in Form2, Label1, the item selected in a Listbox
on Form1.

21. I`m trying to write some code where clicking a radio button on form
#1 makes
a command button visible on form #2. I know how to do this within one form, but I don`t know how to do
this with two forms.

A. This is an easy one. You just have to include Form2`s name in front of the button`s name. Example:

1.)Create two forms(Form1 and Form2)


2.)On Form1 place two option buttons(Option1 and Option2)
3.)On Form2 place one command button(Command1)

'code for Form1


Private Sub Form_Load()
Form2.Show 'Show Form2 along w/Form1
End Sub

Private Sub Option1_Click


Form2.Command1.visible = True 'Show button. See?
End Sub

Private Sub Option2_Click


Form2.Command1.Visible = False 'Hide button.
End Sub

22. How To: Capture Keyboard Input


Ans : Keywords to see in on-line help: KeyPreview property, KeyDown, KeyUp
and KeyPress events, ActiveControl property.

If you need to capture a user`s keyboard input _before_ the keystroke


reaches any of the controls mounted on a form, set your form's
KeyPreview property to True, and process each keystroke in the
form`s KeyDown, KeyUp or KeyPress events. Use the KeyPress event
for regular alphanumeric (ANSI) keystrokes and the KeyDown or KeyUp
events for all other keys (Escape, Backspace, Enter, Arrow keys,
Function Keys, etc.). A simple example:

Private Sub Form_KeyPress(KeyAscii As Integer)

If ActiveControl.Name = "txtName" Then 'If txtName has the focus...


If KeyAscii < 65 Or KeyAscii > 90 Then 'If keystroke is NOT a capital letter.
If KeyAscii >=97 And KeyAscii <=122 Then'If lower case...
KeyAcsii = (KeyAscii Or 32) 'Convert to upper case.
Else
KeyAscii = 0 'Set keystroke to zero.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End If
End If

The above is a brief example of the control your code can have over
keyboard input. The code fragment simply filters out any characters
that are not upper case. If the character received is lower case, it is
converted to caps, otherwise the character is zeroed out. Setting the
keystroke value to zero will be interpreted by most controls as a null
character and will therefore be ignored. You can also set the
keystroke to some other default value depending on the control
receiving the keystroke.

New Tips
Dollar sign Functions--what are they?
Ever wonder about those functions ending in a dollar sign? If you are running VB5 or VB6, you might not
even see them mentioned in VB Help.

In VB, there are a number of functions that have a dollar sign version---such as Left$, Right$ and Case$

What's the difference between the functions Left$ and Left?


For those of you not familiar with functions, functions return a value to the procedure that calls them.
This return value can be of a particular data type, and dollar sign functions return a value of type String.
The
non-dollar sign versions return a Variant Data Type.

Which one should you choose to use?


Dollar sign functions run faster than non dollar sign functions, so in general, it's a good choice to use
them.

Is there ever a reason to use the non-dollar sign function?

Yes---if there's a possibility that the function will encounter a Null character in its argument string, you
need to use the non-dollar sign version--otherwise the function will bomb. For instance, in this code

Form1.Print Left(string1, 1)
Form1.Print Left$(string1, 1)

both Left and Left$ will return the left-most character of the string. However, if string1 contains a Null
character, Left$ will bomb---however, Left will work fine.

Trying to compile a Project using the VB Learning Edition?


You'll need to purchase either the Visual Basic Standard, Professional or Enterprise edition in order to
compile your program into an executable.

'-------------------------------------------

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Your compiled program won't run on a friend's PC
You probably gave your friend just the compiled executable file.

Visual Basic programs need to have the Visual Basic runtime DLL installed on the PC also---plus some
other support files.

The easiest way to do this is to run the Package and Deployment Wizard in Visual Basic to create a Setup
package.

Then all you need to do is give the diskette(s) to your friend, and have them run the Setup program found
on Disk 1---the same they would do for any other Windows program.

This way both your executable, and the Visual Basic runtime DLL and support files are installed.

'-------------------------------------------
Looking for a free version of VB?
Microsoft made a free version of VB5 called the Control Creation Edition available for download from
their website.
There is no free version of Visual Basic 6 available from Microsoft. However, there are several Visual
Basic books (including my first book, Learn to Program with Visual Basic 6) on the market that come
with
the Visual Basic Working Model Edition--which is a surprisingly full featured version of Visual Basic
(no,
you can't compile programs with it).
These tips were extracted from the June 2000 issue of the Visual Basic Explorer
newsletter and were submitted by John Smiley. Be sure to check out John's other
tutorials in the tutorials section or check out his website here <http://www.johnsmiley.com>.

You can direct any questions, comments or suggestions to the VB Forums


<http://www.vbexplorer.com/q&aform.asp> section
of this site. Many of the more interesting or frequently asked questions will
end up in one of our newsletter issues.

'-------------------------------------------
Where have you gone Crystal Reports?
Crystal Reports, the leading third party Report Writer for Visual Basic, was previously packaged with VB
in versions 4 and 5 and appeared as an Add-in off of the Visual Basic menu.

VB6 users were disappointed to find that Crystal Reports apparently was not shipped with VB6---most
likely, it was believe, because Microsoft decided to include a Report Writer of its own called the Microsoft
Data Report designer.

For those of you still yearning for Crystal Reports, take heart--you can still find it on your Visual Basic
installation CD-ROM, provided, that is, you purchase Visual Basic Pro or Enterprise.

To find it, look in the

\Common\Tools\CryReports

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


folder on your installation CD.
'-------------------------------------------
Access Key for a Textbox
Most Visual Basic programmers are familiar with how to designate an access or hot key for a Command
Button. An Access key allows the user to `click` on the control by pressing the Alternate Key plus a letter.

For instance, if you have a Command Button whose caption is OK, to designate a hot key combination of
Alternate+O for the Command Button, you specify

&OK

as the Caption property for the Command Button.

But what a shortcut to get to a Textbox?

The user can click on the textbox or tab to it, but wouldn't it be great to be able to get to a Textbox by
pressing the Alternate key plus a character?

Well, as it turns out, there is.

The secret is to place a label control next to a textbox, and then designate a hot key combination for the
label control using the ampersand character in the Caption property of the label control.

For instance, if

you have a textbox sitting next to a label control whose Caption Property is &Name, if the user presses
Alternate+N, focus will move to the textbox adjacent to the label control.

Just one word of warning. In order for this to work, the Textbox's TabIndex property must be immediately
after the TabIndex Property of the Label Control. If the Label Control's TabIndex property is 15, for
instance, the Textbox must be 16.

'-------------------------------------------
Textbox selection goes away
Have you noticed that if the user selects text in a textbox, and then moves focus off of the textbox, that the
text is no longer selected.

This is because the HideSelect property of the textbox is by default set to True.

If you would prefer to keep the text selected when the user moves `off` of the textbox, then just set the
HideSelect property of the Textbox to False.

'-------------------------------------------
Password Char
Have a textbox into which you want the user to type an entry, but don't want anyone looking over their
shoulder to see it.

Just set the PasswordChar Property of the textbox to something other than its default blank character.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


For instance, if you set the PasswordChar Property to an asterisk(*), then whatever the user enters into the
Textbox will appear as asterisks.

'-------------------------------------------
The Click event requires both the Mouse Down and Mouse Up over the
control
Just a reminder that in order for the Click event procedure of a control to take place, the user must both
click and then release the mouse button over the control. If, after clicking the mouse over the control they
then move it over the form or over another control before releasing it, the click event procedure never
takes place.

'-------------------------------------------
Entries in a Listbox
Many beginners are confounded with the List Property of a Listbox, which is where the programmer can,
at design time, place items that are to appear in a Listbox.

Why are they confounded?

Because when they type an item into the Listbox, invariably they hit the ENTER key, and the List
Property in the Properties Window closes. They select it again, make a second entry, and then hit ENTER
again. Once again, the List property closes. This continues on and on until they have finished adding all
of the items (experienced programmers use the AddItem Method of the Listbox at runtime.).

If you want to add items to a Listbox at design time using the List property, after making your first entry,
type the

Ctrl+N

combination instead of the hitting the ENTER key. This will advance the carriage return to the next line
in the List property, and enable you to more quickly add the items to the Listbox.

Working with the Tray


The Windows 95 tray (recessed area on the right side of taskbar) provides an easy way for
utility and notification programs to run, with out cluttering up the taskbar. Examples of
such programs include: the volume control (if you have a sound card), the FlexiCD
control, and the Resource Meter. Don't you wish you could create applications down
there, too? Oh, but you can, and it's easier that you think!
Laying Out a Plan
Before designing a tray-based application, you must first decide if putting your application there makes
sense. Obviously, putting a word processor or spreadsheet there would be a bad idea, but a notification
program of some sort would be useful. For example. if you were writing a PIM (personal information
manager), you could make an alarm clock to go into the tray that would notify the user about upcoming
appointments.

Each icon in the tray is 16 by 16 pixels large -- the same as a control menu icon. Any icon can be used,
but if you use a 32 by 32 pixel icon, it will be re-sized to fit the space. If you want to create a separate icon

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


for this, use an icon editor that supports multiple resolution icons. The ImageEdit program on the Visual
Basic 4.0 CD does this.
You will also need to take under consideration a few other details: every icon needs a menu. If all it does
is exit the program, that's fine. When users right click on the icon, they expect a menu -- give them one.
You will also need some sort of ToolTip for your icon. Keep it simple and to the point, but not cryptic. We
will discuss how to implement menus and ToolTips later in this article.
Welcome to the Thunder Filling Station
The Thunder Filling Station (Thunder was the code name for VB, get it?) is a small demo the shows how
to implement icons in the tray. It acts as a gas station, that will allow you to fuel you car from the taskbar
(if it were only real!)
Note: The code used in this article is available in the Toolbox's
<http://www.netins.net/showcase/legend/vb/> CodeVault. The file UI-0596.ZIP is a Visual Basic 4.0 (32-
bit) project that contains a sample application that fully demonstrates the techniques in this article.
The following goes in the General, Declarations section. It sets all of the constants and declarations are
needed by the program.
Option Explicit

' Used to detect clicking on the icon


Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_RBUTTONUP = &H205

' Used to control the icon


Private Const NIM_ADD = &H0
Private Const NIM_MODIFY = &H1
Private Const NIF_MESSAGE = &H1
Private Const NIM_DELETE = &H2
Private Const NIF_ICON = &H2
Private Const NIF_TIP = &H4

' Used as the ID of the call back message


Private Const WM_MOUSEMOVE = &H200

Private Declare Function Shell_NotifyIcon Lib "shell32" Alias


"Shell_NotifyIconA" (ByVal dwMessage As Long, pnid As NOTIFYICONDATA)
As Boolean

' Used by Shell_NotifyIcon


Private Type NOTIFYICONDATA
cbSize As Long
hWnd As Long
uId As Long
uFlags As Long
ucallbackMessage As Long
hIcon As Long
szTip As String * 64
End Type

Dim TrayIcon As NOTIFYICONDATA


The code that follows is needed in the Form_Load event.
Private Sub Form_Load()
TrayIcon.cbSize = Len(TrayIcon)
' Handle of the window used to handle messages
TrayIcon.hWnd = Me.hWnd
' ID code of the icon

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


TrayIcon.uId = vbNull
' Flags
TrayIcon.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
' ID of the call back message
TrayIcon.ucallbackMessage = WM_MOUSEMOVE
' The icon
TrayIcon.hIcon = imgIcon.Picture
' The Tooltip for the icon
TrayIcon.szTip = "Free 16-oz fountain pop with gas purchace!" & Chr$(0)

' Add icon to the tray


Call Shell_NotifyIcon(NIM_ADD, TrayIcon)

' Don't let application appear in the Windows task list


App.TaskVisible = False

' Hide form on start-up


Me.Hide
End Sub
The first seven statements sets the parameters for the Shell_NotifyIcon event. Most of these can be used
as-is in your applications, but you may wish to change TrayIcon.hIcon, which sets the actual icon that is
displayed, and TrayIcon.szTip, which is the ToolTip that appears when the user leaves the cursor over the
icon. By calling Shell_NotifyIcon, we place the icon in the tray. Finally, we hide the application from the
task list using the App object, and we hide the form on start up.
Now that we have the icon in tray, we need to be able to handle it once it's there. We do that using the
next chunk of code. It is needed in the MouseMove event of the controlling form, or other control you
specified in Shell_NotifyIcon.hWnd:
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Static Message As Long
Static RR As Boolean

Message = X / Screen.TwipsPerPixelX

If RR = False Then
RR = True
Select Case Message
' Left double click (This should bring up a dialog box)
Case WM_LBUTTONDBLCLK
Me.Show
' Right button up (This should bring up a menu)
Case WM_RBUTTONUP
Me.PopupMenu mnuPump
End Select
RR = False
End If
End Sub
This looks for one of two things. If the user double clicks on the icon, it displays it's form. If they right
click on the icon, a popup menu is displayed that gives the user a list of options. The menu needs to reside
on the controlling form, but the top-most level must be hidden.
Ready to go? Before ending your program, you should always remove the icon, therefore, place this in
QueryUnload to remove the icon:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
TrayIcon.cbSize = Len(TrayIcon)
TrayIcon.hWnd = Me.hWnd

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


TrayIcon.uId = vbNull
'Remove icon
Call Shell_NotifyIcon(NIM_DELETE, TrayIcon)
End Sub
Like other notification programs, your VB application can also place itself in the Windows 95 tray. All it
takes is a few chunks of code. If your are still having problems, be sure to download the code!
Creating a Toolbar
The toolbar (also called a ribbon or control bar) has become a standard feature in
many Windows-based applications. A toolbar provides quick access to the most frequently
used menu commands in an application. Creating a toolbar is easy and convenient using
the toolbar control, which is available with the Professional and Enterprise editions of
Visual Basic. If you are using the Learning Edition of Visual Basic, you can create
toolbars manually as described in "Negotiating Menu and Toolbar Appearance" later in
this chapter.

The following example demonstrates creating a toolbar for an MDI application; the
procedure for creating a toolbar on a standard form is basically the same.

To manually create a toolbar


1. Place a picture box on the MDI form.

The width of the picture box automatically stretches to fill the width of the MDI
form's workspace. The workspace is the area inside a form's borders, not including
the title bar, menu bar, or any toolbars, status bars, or scroll bars that may be on
the form.

Note You can place only those controls that support the Align property directly
on an MDI form (the picture box is the only standard control that supports this
property).
2. Inside the picture box, place any controls you want to display on the toolbar.

Typically, you create buttons for the toolbar using command buttons or image
controls. To add a control inside a picture box, click the control button in the
toolbox, and then draw it inside the picture box.

Note When an MDI form contains a picture box, the internal area of the MDI
form does not include the area of the picture box. For example, the ScaleHeight
property of the MDI form returns the internal height of the MDI form, which does
not include the height of the picture box.
3. Set design-time properties.

One advantage of using a toolbar is that you can present the user with a graphical
representation of a command. The image control is a good choice as a toolbar
button because you can use it to display a bitmap. Set its Picture property at design
time to display a bitmap; this provides the user with a visual cue of the command
performed when the button is clicked. You can also use ToolTips, which display

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


the name of the toolbar button when a user rests the mouse pointer over a button,
by setting the ToolTipText property for the button.
4. Write code.

Because toolbar buttons are frequently used to provide easy access to other
commands, most of the time you call other procedures, such as a corresponding
menu command, from within each button's Click event.

Tip You can use controls that are invisible at run time (such as the timer control) with an
MDI form without displaying a toolbar. To do this, place a picture box on the MDI form,
place the control in the picture box, and set the picture box's Visible property to False.
Writing Code for Toolbars
Toolbars are used to provide the user with a quick way to access some of the
application's commands. There are now three places in the MDI NotePad sample
application where the user can request a new file:
• On the MDI form (New on the MDI form File menu)

• On the child form (New on the child form File menu)

• On the toolbar (File New button)

Rather than duplicate this code three times, you can take the original code from the child
form's mnuFileNew_Click event and place it in a public procedure in the child form. You
can call this procedure from any of the preceding event procedures. Here's an example:
' This module is in a public procedure.
Public Sub FileNew ()
Dim frmNewPad As New frmNotePad
frmNewPad.Show
End Sub

' The user chooses New on the child form File menu.
Private Sub mnuchildFileNew_Click ()
FileNew
End Sub

' The user chooses New on the MDI form File menu.
Private Sub mnumdiFileNew_Click ()
frmNotePad.FileNew
End Sub

' The user clicks the File New button on the toolbar.
Private Sub btnFileNew_Click ()
frmNotePad.FileNew
End Sub

Visual Basic Concepts

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Adding an Event to the ShapeLabel Control

It’s important to distinguish between the events received by your UserControl object (or
by the controls it contains) and the events your control raises. Events your control
receives are opportunities for you to do something interesting; events your control raises
provide opportunities for the developer who uses your control to do something
interesting.

There are many events that might be of interest to the user of the ShapeLabel control. The
Visual Basic Label control raises a Click event, and ShapeLabel is just a fancy label, so the
following procedure will add a Click event. To make the event more interesting, it will be
raised only if the user clicks on the oval background.

Being compatible with other controls of the same type is an important reason to add a
particular event to your control. Other criteria for choosing what events to raise can be
found in "Raising Events from Controls," in "Building ActiveX Controls."

Note This topic is part of a series that walks you through creating a sample ActiveX
control. It begins with the topic Creating an ActiveX Control.

To add a Click event to the ShapeLabel control


1. In the Project Explorer window, click ShapeLabel to select it, then press F7 or click the Code
button on the Project Explorer window toolbar, to open the Code window.

2. In the Object box, select (General). In the Procedure box, select (Declarations) to position
yourself at the top of the code module. Add the following code:
3. Option Explicit
4. ' Declare a public Click event with no arguments.
5. Public Event Click()
6. In the Object box, select lblCaption. In the Procedure box, select the Click event for the label
control. Add the following code to the lblCaption_Click event procedure:
7. Private Sub lblCaption_Click()
8. ' Raise a Click event whenever the user clicks
9. ' on the label.
10. RaiseEvent Click
11. End Sub
The code above raises a Click event only if the user clicks on the constituent
control lblCaption. It will seem more natural to users to be able to click anywhere
on ShapeLabel’s oval background, so the next step shows how to raise the click
event if the user clicks on the colored oval.
12. In the Object box, select UserControl. In the Procedure box, select the UserControl’s
MouseUp event. Add the following code to the UserControl_MouseUp event procedure:
13. Private Sub UserControl_MouseUp(Button As Integer, _
14. Shift As Integer, X As Single, Y As Single)
15. ' Raise a Click event only if the color of the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


16. ' point that was clicked on matches the color
17. ' of the Shape control. Ignore clicks that are
18. ' outside the oval.
19. If Point(X, Y) = shpBack.FillColor Then
20. RaiseEvent Click
21. End If
22. End Sub
Determining whether an event occurred in a particular location is called hit testing.

You might expect to put the hit test code in the shpBack_Click event procedure,
because shpBack is always resized to cover the entire surface of the ShapeLabel
control. However, Shape controls don’t receive Click events. Instead, the Click
event is received by the object that contains the Shape — in this case, the
UserControl object.

"Drawing Your Control," in "Building ActiveX Controls," discusses the use of


transparent backgrounds to create irregularly shaped controls.
23. In the Project Explorer window, click Form1 to select it, then press F7 or click the Code button
on the Project Explorer window toolbar, to open the Code window.

24. In the Object box, select one of the ShapeLabel controls you added to Form1. In the Procedure
box, select the Click event.

Note If the Click event does not appear, make sure the ShapeLabel designer is
closed.

Add the following code to the ShapeLabel1_Click event procedure:


Private Sub ShapeLabel1_Click()
MsgBox "Thanks for clicking! My caption is: " _
& ShapeLabel1.Caption
End Sub
Note If the ShapeLabel you selected is not named ShapeLabel1, use the
appropriate name when entering the code above.

You can click the arrow on the Procedure box to view all of the events for the
ShapeLabel control. In addition to your Click event, there are four events —
DragDrop, DragOver, GotFocus, and LostFocus — that are automatically
provided for you by the container, Form1.

25. On the toolbar, click the Start button, or press CTRL+F5 to run TestCtlDemo. Try clicking
various places on the form and on the ShapeLabel control, to verify that the Click event is being
raised only when you click inside the oval background.

26. There’s a subtle bug in the hit testing for ShapeLabel’s click event. To see this, press the mouse
button while the mouse pointer is in the lower half of the red oval. Holding the mouse button

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


down, carefully move the mouse pointer until the tip of the arrow is on the white text of
ShapeLabel’s caption, then release the mouse button. The message box doesn’t appear!

The lblCaption_Click event procedure doesn’t get executed, because the


MouseDown event occurred over the UserControl. Therefore, when the MouseUp
event occurs, it is received by the UserControl — even if the mouse has been
moved completely off Form1.

The hit test code in the MouseUp event works if the mouse button is released over
the red background that shows through lblCaption’s transparent background, but
not if the button is released over the white foreground color of the text. (If the
button is released outside ShapeLabel, the Point function returns -1, so releasing
the mouse button over some random red spot will not raise the Click event.)

Fixing this bug is left as an exercise for the reader. (Hint: Moving the hit test to the
Click event of the UserControl won’t help, because the Click event doesn’t occur
when the MouseUp event is over a different object from the MouseDown.)

Visual Basic Concepts

Starting the Package and Deployment Wizard

The Visual Basic Package and Deployment Wizard makes it easy for you to create the
necessary .cab files and setup programs for your application. Like other wizards, the
Package and Deployment Wizard prompts you for information so that it can create the
exact configuration you want.

There are three ways you can start the Package and Deployment Wizard:
• You can run it from within Visual Basic as an add-in. If you run the wizard as an add-in, you
must first set the necessary references in the Add-In Manager to load the wizard. When you use
the wizard as an add-in, Visual Basic assumes that you want to work with the project you
currently have open. If you want to work with another project, you must either open that project
before starting the add-in, or use the wizard as a stand-alone component.

• You can run it as a stand-alone component from outside the development environment. When
you run the wizard as a stand-alone component, you are prompted to choose the project on which
you want to work.

• You can start it in silent mode by launching it from a command prompt. See "Running the
Wizard in Silent Mode" in this topic for more information.

After you start the wizard, a series of screens prompt you for information about your
project and let you choose options for the package. Each screen explains how it is to be
used, including which information is optional, and what information must be entered

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


before you can move to the next screen. If you find that you need more information on any
screen, press F1 or click the Help button.

Note You should save and compile your project before running the Package and
Deployment Wizard.

In most cases, the Package and Deployment Wizard is all you need to create a package
that is ready for deployment. However, if you want to customize your packaging process
further or provide functionality not supported by the Package and Deployment Wizard,
you can modify the Setup Toolkit Project.

To start the Package and Deployment Wizard from within Visual Basic
1. Open the project you want to package or deploy using the wizard.

Note If you are working in a project group or have multiple projects loaded,
make sure that the project you want to package or deploy is the current project
before starting the wizard.
2. Use the Add-In Manager to load the Package and Deployment Wizard, if necessary: Select Add-
In Manager from the Add-Ins menu, select Package and Deployment Wizard from the list,
then click OK.

3. Select Package and Deployment Wizard from the Add-Ins menu to launch the wizard.

4. On the main screen, select one of the following options:

• If you want to create a standard package, Internet package, or dependency file for the
project, click Package.

• If you want to deploy the project, click Deploy.

• If you want to view, edit, or delete scripts, click Manage Scripts.

For an introduction to these options, see "The Package and Deployment Wizard."
5. Proceed through the wizard screens.

To start the Package and Deployment Wizard as a stand-alone component


1. If the project you want to package is open, save it and close Visual Basic.

2. Click the Start button, and then click Package and Deployment Wizard from the Visual Basic
submenu.

3. In the Project list on the initial screen, choose the project you want to package.

Note You can click Browse if your project is not in the list.
4. On the main screen, select one of the following options:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• If you want to create a standard package, Internet package, or dependency file for the
project, click Package.

• If you want to deploy the project, click Deploy.

• If you want to view, edit, or delete scripts, click Manage Scripts.

5. Proceed through the wizard screens.


Running the Wizard in Silent Mode
Using scripts, you may package and deploy your project files in silent mode. In silent
mode, the wizard runs without your having to attend it to make choices and move through
screens. The wizard packages and deploys your project using the settings contained in a
script.

Silent mode is especially useful if you are packaging and deploying as part of a batch
process. For example, early in the development of your project, you may use the Package
and Deployment Wizard to package your project and deploy it to a test location. You can
later create a batch file to perform the same packaging and deployment steps periodically
as you update your project.

To package and deploy in silent mode


1. Open an MS-DOS prompt.

2. Type the name of the wizard executable, pdcmdln.exe, followed by the path and file name of your
Visual Basic project, and the appropriate command line arguments, as shown in the following
example:
3. PDCmdLn.exe C:\Project1\Project1.vbp /p "Internet Package"
4. /d Deployment1 /l "C:\Project1\Silent Mode.log"
Note You can perform packaging and deployment in a single silent session by
specifying both the /p and the /d arguments, as shown in the example above.
Otherwise, use either /p or /d.
Argument Description
/p packagingscript Type /p followed by the name of a previously saved packaging
script to package the project silently according to the specified
script.
/d deploymentscript Type /d followed by the name of a previously saved deployment
script to deploy the project silently according to the specified
script.
/l path Specifies that the wizard should store all output from the wizard,
such as error messages and success reports, to a file rather than
displaying them on the screen.
Type /l followed by the path and file name of a file in
which output should be stored. If the file does not exist,
the wizard creates it.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If you do not choose to log output using this argument,
the wizard will display a dialog box to notify when
packaging or deployment is finished. In order to see this
dialog box, you may minimize or close other windows.
/e path Specifies a path for the project's executable file, if it is different
from the path for the project. This argument allows you to package
in silent mode when you are working in a multi-developer
environment. You might use this option if development and
packaging occur on different computers.

Note Any file or script name that includes spaces should be enclosed in quotation marks,
as shown in the example above.

Visual Basic Concepts

Creating Your Own Data Types

You can combine variables of several different types to create user-defined types (known
as structs in the C programming language). User-defined types are useful when you want
to create a single variable that records several related pieces of information.

You create a user-defined type with the Type statement, which must be placed in the
Declarations section of a module. User-defined types can be declared as Private or Public
with the appropriate keyword. For example:
Private Type MyDataType
-or-
Public Type MyDataType
For example, you could create a user-defined type that records information about a
computer system:
' Declarations (of a standard module).
Private Type SystemInfo
CPU As Variant
Memory As Long
VideoColors As Integer
Cost As Currency
PurchaseDate As Variant
End Type
Declaring Variables of a User-Defined Type
You can declare local, private module-level, or public module-level variables of the same
user-defined type:
Dim MySystem As SystemInfo, YourSystem As SystemInfo

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The following table illustrates where, and with what scope, you can declare user-defined
types and their variables.
You can create a user-defined Variables of a user-defined
Procedure/Module type as... type can be declared...
Procedures Not applicable Local only
Standard modules Private or public Private or public
Form modules Private only Private only
Class modules Private or public Private or public

Note If declared using the Dim keyword, user-defined types in Standard or Class
modules will default to Public. If you intend a user-defined type to be private, make sure
you declare it using the Private keyword.
Assigning and Retrieving Values
Assigning and retrieving values from the elements of this variable is similar to setting and
getting properties:
MySystem.CPU = "486"
If MySystem.PurchaseDate > #1/1/92# Then
You can also assign one variable to another if they are both of the same user-defined type.
This assigns all the elements of one variable to the same elements in the other variable.
YourSystem = MySystem
User-Defined Types that Contain Arrays
A user-defined type can contain an ordinary (fixed-size) array. For example:
Type SystemInfo
CPU As Variant
Memory As Long
DiskDrives(25) As String ' Fixed-size array.
VideoColors As Integer
Cost As Currency
PurchaseDate As Variant
End Type
It can also contain a dynamic array.
Type SystemInfo
CPU As Variant
Memory As Long
DiskDrives() As String ' Dynamic array.
VideoColors As Integer
Cost As Currency
PurchaseDate As Variant
End Type
You can access the values in an array within a user-defined type in the same way that you
access the property of an object.
Dim MySystem As SystemInfo
ReDim MySystem.DiskDrives(3)
MySystem.DiskDrives(0) = "1.44 MB"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


You can also declare an array of user-defined types:
Dim AllSystems(100) As SystemInfo
Follow the same rules to access the components of this data structure.
AllSystems(5).CPU = "386SX"
AllSystems(5).DiskDrives(2) = "100M SCSI"
Passing User-Defined Types to Procedures
You can pass procedure arguments using a user-defined type.
Sub FillSystem (SomeSystem As SystemInfo)
SomeSystem.CPU = lstCPU.Text
SomeSystem.Memory = txtMemory.Text
SomeSystem.Cost = txtCost.Text
SomeSystem.PurchaseDate = Now
End Sub
Note If you want to pass a user-defined type in a form module, the procedure must be
private.

You can return user-defined types from functions, and you can pass a user-defined type
variable to a procedure as one of the arguments. User-defined types are always passed by
reference, so the procedure can modify the argument and return it to the calling
procedure, as illustrated in the previous example.

Note Because user-defined types are always passed by reference, all of the data
contained in the user-defined type will be passed to and returned from the procedure. For
user-defined types that contain large arrays, this could result in poor performance,
especially in client/server applications where a procedure may be running on a remote
machine. In such a situation, it is better to extract and pass only the necessary data from
the user-defined type.

For More Information To read more about passing by reference, see "Passing
Arguments to Procedures" in "Programming Fundamentals."
User-Defined Types that Contain Objects
User-defined types can also contain objects.
Private Type AccountPack
frmInput as Form
dbPayRollAccount as Database
End Type
Because the Variant data type can store many different types of data, a Variant array can
be used in many situations where you might expect to use a user-defined type. A Variant
array is actually more flexible than a user-defined type, because you can change the type of
data you store in each element at any time, and you can make the array dynamic so that
you can change its size as necessary. However, a Variant array always uses more memory
than an equivalent user-defined type.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Nesting Data Structures
Nesting data structures can get as complex as you like. In fact, user-defined types can
contain other user-defined types, as shown in the following example. To make your code
more readable and easier to debug, try to keep all the code that defines user-defined data
types in one module.
Type DriveInfo
Type As String
Size As Long
End Type

Type SystemInfo
CPU As Variant
Memory As Long
DiskDrives(26) As DriveInfo
Cost As Currency
PurchaseDate As Variant
End Type

Dim AllSystems(100) As SystemInfo


AllSystems(1).DiskDrives(0).Type = "Floppy"

The Setup Toolkit

The Setup Toolkit is a project installed with Visual Basic that is used by the Package and
Deployment Wizard when it creates a setup program. The Setup Toolkit project contains
the forms and code that the application's setup program uses to install files onto the user's
computer. When you use the Package and Deployment Wizard, the wizard includes the
setup1.exe file that the Setup Toolkit project creates. This file is used as the application's
main installation file.

Note There are two setup programs involved in the installation process — setup.exe and
setup1.exe. The setup.exe program performs pre-installation processing on the user's
computer, including installing the setup1.exe program and any other files needed for the
main installation program to run. Only setup1.exe is customizable through the Setup
Toolkit.

In addition to playing a supporting role in the process of creating a setup program, the
Setup Toolkit can be used to modify the screens seen in the installation process, or to
create a setup program directly. You might create a custom setup program if you need to
add additional functionality not supported by the wizard to your installation sequence.

The Setup Toolkit project resides in the \Wizards\PDWizard\Setup1 subdirectory of the


main Visual Basic directory.

Caution The files in this project are the same files used by the output of the Package and
Deployment Wizard. Do not modify them without making a backup copy in another

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


directory first. If you modify setup1.exe, subsequent setup programs created by the
Package and Deployment Wizard will use the modified version.

You use the Setup Toolkit by loading the Setup1.vbp file into Visual Basic and making
modifications to the appearance or functionality of the project. In doing so, you may need
to manually go through the steps that the Package and Deployment Wizard would
otherwise do for you. The following sections describe steps in the process and explain
how to determine which files you need to include in your setup, how to create a Setup.lst,
how to create distribution media, and how to test your setup.
Overall Steps to Modify the Package and Deployment Wizard
When you modify the Setup Toolkit with the intention of changing the output created by
the Package and Deployment Wizard, you follow these steps:
1. Modify the Setup Toolkit project to contain any new prompts, screens, functions, code, or other
information you want to include. When you are finished, compile the project to create setup1.exe.

2. Run the Package and Deployment Wizard, following the prompts on each screen, to create your
distribution media.
Overall Steps to Create a Custom Setup Program
When you create a setup program manually using the Setup Toolkit rather than the
Package and Deployment Wizard, you must follow these steps:
1. If necessary, modify the Setup Toolkit project to contain any new prompts, screens, functions,
code, or other information you want to include.

2. Determine the files you want to distribute, including all run-time, setup, and dependency files.

3. Determine where to install the files on the users' computers.

4. Manually create your Setup.lst file to reflect the names and installation locations of all files that
must be included for your project.

5. Determine how you will be distributing files.

6. Create the .cab files for your project using the Makecab utility.

You can use the Package and Deployment Wizard to create your .cab files, then modify
the .cab files manually. When the wizard creates your .cab files, it creates a .ddf file and a
batch file in the \Support subdirectory of your project directory. To modify the .cab files,
edit the .ddf file, then run the batch file provided. The batch file in turn will run
Makecab.exe to recreate your .cab files.
7. Create the setup1.exe for your project by compiling the Setup Toolkit project with your changes.

8. Copy your files to the distribution media, or manually publish your files to the Web site using the
Web Publishing Wizard, available in the ActiveX SDK.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


For More Information For more information on using the Web Publishing Wizard, see
"Internet Tools and Technologies" in the Internet/Intranet/Extranet Services SDK. See
"Modifying the Setup Project" later in this chapter for more information on modifying the
Setup Toolkit project. See "Files you Need to Distribute" and "Where to Install Files on
the User's Machine" for more information on how to place files on the user's computer.
See "Creating Distribution Media" for more information on copying your files to the
appropriate media.
Visual Basic Concepts

Processing Multiple RDO Result Sets

Any SQL statement can include multiple SELECT statements or stored procedures that
invoke one or more SELECT statements. Each SELECT statement generates a result set
that must be processed by your code or discarded before the RDO resources are released
and the next result set is made available.

Action queries also generate row-less result sets that must also be processed — this is
another type of multiple result set query. In many cases, when you execute a stored
procedure, it might return more than one result set. It is often difficult to determine if a
stored procedure will return more than one result set because a stored procedure might
call another procedure.

For example, if you submit a query that includes four SELECT queries to populate four
local ListBox controls and a stored procedure that updates a table, your code must deal
with at least five result sets. Because you might not know how many result sets can be
generated by a stored procedure, your code must be prepared to process n sets of results.

There are two approaches to executing queries with multiple result sets:
• Execute the OpenResultset method directly against a connection.

• Use the OpenResultset method against an rdoQuery.

Both are processed in similar ways, but if you use the rdoQuery, you can examine the
RowsAffected property to determine the number of rows affected by action queries. While
it is possible to execute a multiple result set query using the Execute method, it is not
possible to retrieve the rows affected from individual statements, and a trappable error
results if any of the queries returns rows.
Using Server-Side Cursor Libraries with Multiple Result Sets
Not all cursor drivers support the ability to process queries that contain more than one set
of results — the SQL Server server-side cursor driver is an example. However, if you
request a cursorless result set by using the rdOpenForwardOnly, rdConcurReadOnly
options and by setting the RowsetSize property to 1, you can execute queries with
multiple result sets using server-side cursors. You can also set these options for all result
sets by setting the CursorDriver property to rdUseNone.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Multiple Result Sets: An Example
This section takes you through a step-by-step procedure that demonstrates how to execute
a query with multiple result sets by using the rdoQuery object.

Whenever you use the concatenation operator "&" to build SQL queries, be sure to
include white space (such as spaces or tabs) between the operators on separate lines.
1. Create your SQL statement and place it in a string variable — for instance, MySQL. For SQL
Server, multiple statements must be separated by semi-colons.
2. Dim MySQL As String
3. MySQL = "Select Name from Authors Where ID = 5; " _
4. & " Select City from Publishers; " _
5. & " Update MyTable " _
6. & " Set Age = 18 Where Name = 'Fred'"
7. Next, create a new rdoQuery and set a variable declared as rdoQuery and multiple result sets to
this object — in this case, MyQy. The example assumes an rdoConnection object (Cn) already
exists. There are a number of other ways to instantiate and initialize rdoQuery objects; this
example illustrates only one of these ways.
8. Dim MyQy As rdoQuery
9. Set MyQy = Cn.CreateQuery("MyQy1", "")
10. MyQy.SQL = MySQL
11. Execute the query by using the OpenResultset method against the rdoQuery object. If you do not
need the extra properties and the ability to pass parameters to the query, you can use the
OpenResultset method directly against the rdoConnection object. The arguments you use here
affect all result sets fetched from this query. For example, if you need to use a cursor on the
second result set, you must specify a cursor type when the first result set is opened.
12. Dim MyRs As rdoResultset
13. Set MyRs = MyQy.OpenResultset(rdOpenForwardOnly, _
14. rdConcurReadOnly)
15. You are now ready to process the first result set. Note that the rdAsyncEnable options argument
was not set. Because of this, control is not returned to the application until the first row of the
first result set is ready for processing. If the current rdoResultset contains rows, the RowCount
property is set to a value > 0, and the EOF and BOF properties are both False. If no rows were
returned, the RowCount property returns either –1 to indicate that the number of rows is not
available, or 0, depending on the driver and data source.

The following example fills a ListBox control called NameList1 with the results of
the query.
While Not MyRs.EOF ' Loop through all rows.
' Use the first column.
NameList1.AddItem = MyRs(0)
MyRs.MoveNext ' Position to the next row
' in the result set.
Wend
16. The first result set is now at end-of-file (MyRs.EOF = True). Use the MoreResults method to
activate the next result set. Once you execute MoreResults, the first set of rows is no longer
available — even if you used one of the cursor options to create it.
17. ' Activate the next set of results.
18. If (MyRs.MoreResults) Then ...

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


19. You are now ready to process the second result set. This example uses only the first few names
and discards the remaining rows.
20. ' Loop through some rows.
21. Do While Not MyRs.EOF and MyRs(0) < "B"
22. ' Use the first column.
23. NameList1.AddItem = MyRs(0)
24. MyRs.MoveNext
25. Loop
26. ' Activate the next set of results
27. ' and discard remaining rows.
28. If (MyRs.MoreResults) Then ...
29. You are now ready to process the last set of results. Because this is an UPDATE statement, there
are no rows to be returned, but you can determine the number of rows affected by using the
RowsAffected property. The MoreResults method is used for the last time to release all resources
connected with this query.
30. If MyQy.RowsAffected = 0 Then
31. MsgBox "No rows were updated"
32. End If
33. ' Activate the next set of results.
34. If (MyRs.MoreResults) Then ...
When you use the MoreResults method against the last result set, it should return False
and other resources required to process the query are released. At this point the rdoQuery
object can be reused. If you use the Close method against an rdoQuery object, it is
removed from the rdoQueries collection.

Visual Basic Concepts

Tabstrip Scenario: Create a Tabbed Dialog Box

The TabStrip control is used to create dialog boxes which contain a number of tabs. Each
tab usually has some relationship to a larger theme, and is therefore related to other tabs in
the same dialog box. In this scenario, we create a tabbed dialog box which sets the fonts,
and indents of a RichTextBox.

The following objects are used in the code below:


• Form object named "frmRTF"

• RichTextBox control named "rtfData"

• TabStrip control named "tabRTF"

• Form object named "frmTab"

• Frame control named "fraTab"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• ComboBox named "cmbFonts"

• OptionButton control named "optNormal"

• OptionButton control named "optBullet"

To create a Tabbed Dialog Box


1. Create two forms, one named "frmRTF" to contain the RichTextbox, and a second named
"frmTab" to contain the TabStrip control.

2. At design time, create two Tab objects on the TabStrip control.

3. Create a Frame control array named "fraTab" on frmTab.

4. Draw the ComboBox on fraTab(0) and two OptionButton controls on fraTab(1).

5. Use the Move method in the Load event to position the Frame controls.

6. In the TabStrip control's Click event, use the SelectedItem property to determine the Index of the
clicked Tab.

7. Use the Index with the ZOrder method to bring the right Frame to the front.
Create two Forms, One Named "frmRTF" to Contain the RichTextbox, and a Second
Named "frmTab" to Contain the TabStrip Control
This scenario requires two forms: the first is named "frmRTF," and contains the
RichTextBox control, the second, named "frmTab", contains the TabStrip control.

To create two Form objects


1. On the File menu, click New Project to display the New Project dialog box.

2. Double-click the Standard EXE Project icon, and a new form named Form1 will be created for
you.

3. If the Properties window is not showing, press F4 to display it.

4. Click the Name box and type "frmRTF."

5. Draw a RichTextBox control on the form.

Note You must have the RichTextBox (RichTx32.ocx) loaded into the Toolbox.
See "Loading ActiveX Controls" for more information.
6. On the Properties page window, click the Name box and type "rtfData"

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


7. On the Project Explorer window, click Add Form to display the Add Form dialog box.

8. Double-click the Form icon to insert another form into the project.

9. On the Properties window, click the name box and type "frmTab."

10. Draw a TabStrip control on frmTab, and name it "tabRTF."

You must also have some code that shows the second form. A quick way to do this would
be to place a Show method in the DblClick event of the first Form object (frmRTF), as
shown below:
Private Sub Form_DblClick()
frmTab.Show
End Sub
At Design time, Create Two Tab Objects on the TabStrip Control
You can create Tab objects at design time and at run time. In this scenario, you should
create the two tabs at design time. Right-click on the TabStrip control and click Properties
to display the Property Pages dialog box. Then click the Tabs tab and click Insert Tab
twice. Be sure to give the tabs appropriate captions — "Fonts," and "Indents."
Create a Control Array Named "fraTab" on frmTab
A TabStrip control functions by managing Tab objects. Each Tab object is associated with
a container control that appears in the tab's client area. It's most efficient to use a control
array to create the container controls. In this scenario, draw a Frame control on the same
form as the TabStrip control, and name it "fraTab."

To create a control array


1. Draw a Frame control on frmTab.

2. Click the Name box on the Properties window and type "fraTab."

3. Click the Frame control and copy it to the clipboard by either pressing CTRL+C or clicking
Copy from the Edit menu.

4. Paste the same control back on the form by pressing CTRL+V. A dialog box will ask you if you
want to create a control array. Click Yes.
Draw the ComboBox on fraTab(0) and Two OptionButton controls on fraTab(1)
On the control named fraTab(0), draw a ComboBox control, and name it "cmbFonts." To
populate the ComboBox with all available fonts on your system, use the following code:
Private Sub Form_Load()
Dim i ' Declare variable.
' Determine number of fonts.
For i = 0 To Printer.FontCount - 1
' Put each font into list box.
cmbFonts.AddItem Printer.Fonts(I)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Next i
cmbFonts.ListIndex = 0
End Sub
To set the SelFontName property of the RichTextBox control, use the following code:
Private Sub cmbFonts_Click()
frmRtf.rtfData.SelFontName = cmbFonts.Text
End Sub
Draw two OptionButton controls on the second Frame control named fraTab(0). Name
the first OptionButton control "optNormal," and change its Caption property to "Normal."
Name the second control "optBullet," and set its Caption property to "Bullet." The code
for these controls sets the SelBullet property to True or False. The code for each is shown
below:
Private Sub optBullet_Click()
' The Form object's ScaleMode is set to Twips.
frmRTF.rtfData.BulletIndent = 500
frmRTF.rtfData.SelBullet = True
End Sub

Private Sub optNormal_Click()


frmRTF.rtfData.SelBullet = False
End Sub
Use the Move Method in the Load Event to Position the Frame Controls
To position the Frame controls over the client area, use the Move method in the Form
object's Load event, as shown below:
Private Sub Form_Load()
' The name of the TabStrip is "tabRTF."
' The Frame control is named "fraTab."
For i = 0 To fraTab.Count - 1
With fraTab(i)
.Move tabRTF.ClientLeft, _
tabRTF.ClientTop, _
tabRTF.ClientWidth, _
tabRTF.ClientHeight
End With
Next I

' Bring the first fraTab control to the front.


fraTab(0).ZOrder 0
End Sub
In the TabStrip Control's Click Event, Use the SelectedItem Property to Determine the
Index of the Clicked Tab
To determine which Tab object, use the SelectedItem property. This property returns a
reference to the clicked tab. However, the Tabs collection is a 1-based collection (the
collection index begins with 1), and the fraTab array is a 0-based collection. To make sure
the two are synchronized, subtract 1 from the Index, as shown below.
Private Sub tabRTF_Click()
fraTab(tabRTF.SelectedItem.Index - 1).ZOrder 0

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End Sub
Tip At design time, you can set the Index property of the Frame control array to become
a 1-based array. Thus the code above would read:
fraTab(tabRTF.SelectedItem.Index).ZOrder 0
The Complete Code
The complete code is shown below:
Private Sub Form_Load()
Dim i As Integer' Declare variable.
' Determine number of fonts.
For i = 0 To Printer.FontCount - 1
' Put each font into list box.
cmbFonts.AddItem Printer.Fonts(i)
Next i

cmbFonts.ListIndex = 0

' The name of the TabStrip is "tabRTF."


' The Frame control is named "fraTab."
For i = 0 To fraTab.Count - 1
With fraTab(i)
.Move tabRTF.ClientLeft, _
tabRTF.ClientTop, _
tabRTF.ClientWidth, _
tabRTF.ClientHeight
End With
Next i

' Bring the first fraTab control to the front.


fraTab(0).ZOrder 0
End Sub

Private Sub cmbFonts_Click()


frmRTF.rtfData.SelFontName = cmbFonts.Text
End Sub

Private Sub optBullet_Click()


frmRTF.rtfData.BulletIndent = 500
frmRTF.rtfData.SelBullet = True
End Sub

Private Sub optNormal_Click()


frmRTF.rtfData.SelBullet = False

End Sub

Private Sub tabRTF_Click()


fraTab(tabRTF.SelectedItem.Index - 1).ZOrder 0
End Sub
This following code goes into the form named "frmRTF."
Private Sub Form_DblClick()

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


frmTab.Show
End Sub

Visual Basic Concepts

Life Cycle of Visual Basic Forms

Because they're visible to the user, forms and controls have a different life cycle than other
objects. For example, a form will not close just because you've released all your references
to it. Visual Basic maintains a global collection of all forms in your project, and only
removes a form from that collection when you unload the form.

In similar fashion, Visual Basic maintains a collection of controls on each form. You can
load and unload controls from control arrays, but simply releasing all references to a
control is not sufficient to destroy it.

For More Information The Forms and Controls collections are discussed in
"Collections in Visual Basic" earlier in this chapter.
States a Visual Basic Form Passes Through
A Visual Basic form normally passes through four states in its lifetime:
1. Created, but not loaded.

2. Loaded, but not shown.

3. Shown.

4. Memory and resources completely reclaimed.

There's a fifth state a form can get into under certain circumstances: Unloaded and
unreferenced while a control is still referenced.

This topic describes these states, and the transitions between them.
Created, But Not Loaded
The beginning of this state is marked by the Initialize event. Code you place in the
Form_Initialize event procedure is therefore the first code that gets executed when a form
is created.

In this state, the form exists as an object, but it has no window. None of its controls exist
yet. A form always passes through this state, although its stay there may be brief.

For example, if you execute Form1.Show, the form will be created, and Form_Initialize will
execute; as soon as Form_Initialize is complete, the form will be loaded, which is the next
state.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The same thing happens if you specify a form as your Startup Object, on the General tab
of the Project Properties dialog box (which is available from the Project menu). A form
specified as the Startup Object is created as soon as the project starts, and is then
immediately loaded and shown.

Note You can cause your form to load from within Form_Initialize, by calling its Show
method or by invoking its built-in properties and methods, as described below.
Remaining Created, But Not Loaded
By contrast, the following code creates an instance of Form1 without advancing the form
to the loaded state:
Dim frm As Form1
Set frm = New Form1
Once Form_Initialize has ended, the only procedures you can execute without forcing the
form to load are Sub, Function, and Property procedures you've added to the form's code
window. For example, you might add the following method to Form1:
Public Sub ANewMethod()
Debug.Print "Executing ANewMethod"
End Sub
You could call this method using the variable frm (that is, frm.ANewMethod) without forcing
the form on to the next state. In similar fashion, you could call ANewMethod in order to
create the form:
Dim frm As New Form1
frm.ANewMethod
Because frm is declared As New, the form is not created until the first time the variable is
used in code — in this case, when ANewMethod is invoked. After the code above is
executed, the form remains created, but not loaded.

Note Executing Form1.ANewMethod, without declaring a form variable, has the same effect
as the example above. As explained in "Customizing Form Classes," Visual Basic creates a
hidden global variable for each form class. This variable has the same name as the class;
it's as though Visual Basic had declared Public Form1 As New Form1.

You can execute as many custom properties and methods as you like without forcing the
form to load. However, the moment you access one of the form's built-in properties, or
any control on the form, the form enters the next state.

Note You may find it helpful to think of a form as having two parts, a code part and a
visual part. Before the form is loaded, only the code part is in memory. You can call as
many procedures as you like in the code part without loading the visual part of the form.
The Only State All Forms Pass Through
Created, But Not Loaded is the only state all forms pass through. If the variable frm in the
examples above is set to Nothing, as shown here, the form will be destroyed before
entering the next state:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Dim frm As New Form1
frm.ANewMethod
Set frm = Nothing ' Form is destroyed.
A form used in this fashion is no better than a class module, so the vast majority of forms
pass on to the next state.
Loaded, But Not Shown
The event that marks the beginning of this state is the familiar Load event. Code you place
in the Form_Load event procedure is executed as soon as the form enters the loaded state.

When the Form_Load event procedure begins, the controls on the form have all been
created and loaded, and the form has a window — complete with window handle (hWnd)
and device context (hDC) — although that window has not yet been shown.

Any form that becomes visible must first be loaded.

Many forms pass automatically from the Created, But Not Loaded state into the Loaded,
but Not Shown state. A form will be loaded automatically if:
• The form has been specified as the Startup Object, on the General tab of the Project Properties
dialog box.

• The Show method is the first property or method of the form to be invoked, as for example
Form1.Show.

• The first property or method of the form to be invoked is one of the form's built-in members, as
for example the Move method.

Note This case includes any controls on the form, because each control defines a
property of the form; that is, in order to access the Caption property of
Command1, you must go through the form's Command1 property:
Command1.Caption.

• The Load statement is used to load the form, without first using New or As New to create the
form, as described earlier.

Forms That Are Never Shown


In the first two cases listed above, the form will continue directly on to the visible state, as
soon as Form_Load completes. In the last two cases, the form will remain loaded, but not
shown.

It has long been common coding practice in Visual Basic to load a form but never show it.
This might be done for several reasons:
• To use the Timer control to generate timed events.

• To use controls for their functionality, rather than their user interface — for example, for serial
communications or access to the file system.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


• To execute DDE transactions.

Note With the Professional or Enterprise edition, you can create ActiveX components
(formerly called OLE servers), which are often better at providing code-only functionality
than controls are. See Creating ActiveX Components in the Component Tools Guide.
Always Coming Home
Forms return from the visible state to the loaded state whenever they're hidden. Returning
to the loaded state does not re-execute the Load event, however. Form_Load is executed
only once in a form's life.
Shown
Once a form becomes visible, the user can interact with it. Thereafter, the form may be
hidden and shown as many times as you like before finally being unloaded.
Interlude: Preparing to Unload
A form may be either hidden or visible when it's unloaded. If not explicitly hidden, it
remains visible until unloaded.

The last event the form gets before unloading is the Unload event. Before this event
occurs, however, you get a very important event called QueryUnload. QueryUnload is
your chance to stop the form from unloading. If there's data the user might like to save,
this is the time to prompt the user to save or discard changes.

Important Setting the Cancel argument of the QueryUnload to True will stop the form
from unloading, negating an Unload statement.

One of most powerful features of this event is that it tells you how the impending unload
was caused: By the user clicking the Close button; by your program executing the Unload
statement; by the application closing; or by Windows closing. Thus QueryUnload allows
you to offer the user a chance to cancel closing the form, while still letting you close the
form from code when you need to.

Important Under certain circumstances, a form will not receive a QueryUnload event: If
you use the End statement to terminate your program, or if you click the End button (or
select End from the Run menu) in the development environment.

For More Information See "QueryUnload Event" in the Language Reference.


Returning to the Created, But Not Loaded State
When the form is unloaded, Visual Basic removes it from the Forms collection. Unless
you've kept a variable around with a reference to the form in it, the form will be destroyed,
and its memory and resources will be reclaimed by Visual Basic.

If you kept a reference to the form in a variable somewhere, such as the hidden global
variable described in "Customizing Form Classes," then the form returns to the Created,
But Not Loaded state. The form no longer has a window, and its controls no longer exist.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The object is still holding on to resources and memory. All of the data in the module-level
variables in the form's code part are still there. (Static variables in event procedures,
however, are gone.)

You can use that reference you've been keeping to call the methods and properties that
you added to the form, but if you invoke the form's built-in members, or access its
controls, the form will load again, and Form_Load will execute.
Memory and Resources Completely Reclaimed
The only way to release all memory and resources is to unload the form and then set all
references to Nothing. The reference most commonly overlooked when doing this is the
hidden global variable mentioned earlier. If at any time you have referred to the form by its
class name (as shown in the Properties Window by the Name property), you've used the
hidden global variable. To free the form's memory, you must set this variable to Nothing.
For example:
Set Form1 = Nothing
Your form will receive its Terminate event just before it is destroyed.

Tip Many professional programmers avoid the use of the hidden global variable,
preferring to declare their own form variables (for example, Dim dlgAbout As New
frmAboutBox) to manage form lifetime.

Note Executing the End statement unloads all forms and sets all object variables in your
program to Nothing. However, this is a very abrupt way to terminate your program. None
of your forms will get their QueryUnload, Unload, or Terminate events, and objects
you've created will not get their Terminate events.
Unloaded and Unreferenced, But a Control Is Still Referenced
To get into this odd state, you have to unload and free the form while keeping a reference
to one of its controls. If this sounds like a silly thing to do, rest assured that it is.
Dim frm As New Form1
Dim obj As Object
frm.Show vbModal
' When the modal form is dismissed, save a
' reference to one of its controls.
Set obj = frm.Command1
Unload frm
Set frm = Nothing
The form has been unloaded, and all references to it released. However, you still have a
reference to one of its controls, and this will keep the code part of the form from releasing
the memory it's using. If you invoke any of the properties or methods of this control, the
form will be reloaded:
obj.Caption = "Back to life"
The values in module-level variables will still be preserved, but the property values of all
the controls will be set back to their defaults, as if the form were being loaded for the first
time. Form_Load will execute.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Note In some previous versions of Visual Basic, the form did not completely re-initialize,
and Form_Load did not execute again.

Note Not all forms behave as Visual Basic forms do. For example, the Microsoft Forms
provided in Microsoft Office don't have Load and Unload events; when these forms
receive their Initialize events, all their controls exist and are ready to use.

For More Information Forms are discussed in "Designing a Form" in "Forms, Controls,
and Menus" and in "More About Forms" in "Creating a User Interface."

Visual Basic Concepts

Data Limitations

The following limitations apply to variables in the Visual Basic language.


Form, Standard, and Class Module Data
The data segment (that is, the data defined in the Declarations section) of the VBA
module of any form or module in Visual Basic can be up to 64K. This data segment
contains the following data:
• Local variables declared with Static.

• Module-level variables other than arrays and variable-length strings.

• 4 bytes for each module-level array and variable-length string.


Procedures, Types, and Variables
If a procedure or module exceeds the 64K code limit, Visual Basic generates a compile-
time error.

If you define a procedure that has more than 64K of local variables defined, you get the
error "Too many local nonstatic variables."

If you define a module that has more than 64K of module-level variables defined, or if you
define a User-Defined Type larger than 64K, you get the error "Fixed or static data can't
be larger than 64K."

If you encounter this error, you can avoid it by breaking extremely large procedures into
several smaller procedures, or by moving module-level declarations into another module.

An array declared as a variable doesn't contribute to the entire size of the array; only the
array descriptor counts toward the 64K limit. So it is acceptable, for example, to have a
declaration such as Dim x(1000000) As Byte either in a procedure or at module level. Out of
memory problems occur, however, if you declare a large, fixed-size array in a record, then
declare instances of those records as variables.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


User-Defined Types
No variable of a user-defined type can exceed 64K, although the sum of variable-length
strings in a user-defined type may exceed 64K (variable-length strings occupy only 4 bytes
each in the user-defined type; the actual contents of a string are stored separately). User-
defined types can be defined in terms of other user-defined types, but the total size of the
types cannot exceed 64K.
Stack Space
Arguments and local variables in procedures take up stack space at run time. Module-level
and static variables do not take up stack space because they are allocated in the data
segment for forms or modules. Any DLL procedures you call use this stack while they are
executing.

Visual Basic itself uses some of the stack for its own purposes, such as storing
intermediate values when evaluating expressions.

Total available stack size for Visual Basic is one megabyte (1MB) per thread. A stack may
grow beyond this, however, if there is adjacent free memory.
Visual Basic Concepts

Tips for Debugging

There are several ways to simplify debugging:


• When your application doesn't produce correct results, browse through the code and try to find
statements that may have caused the problem. Set breakpoints at these statements and restart the
application.

• When the program halts, test the values of important variables and properties. Use Quick Watch
or set watch expressions to monitor these values. Use the Immediate window to examine
variables and expressions.

• Use the Break on All Errors option to determine where an error occurred. To temporarily change
this option, select Toggle from the Code window context menu, then toggle the option from the
submenu. Step through your code, using watch expressions and the Locals window to monitor
how values change as the code runs.

• If an error occurs in a loop, define a break expression to determine where the problem occurs.
Use the Immediate window together with Set Next Statement to re-execute the loop after making
corrections.

• If you determine that a variable or property is causing problems in your application, use a
Debug.Assert statement to halt execution when the wrong value is assigned to the variable or
property.

• To set the error trapping state that Visual Basic defaults to at the beginning of any debugging
session, open the Options dialog box (available from the Tools menu), select the General tab, and

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


set the Default Error Trapping State option. Visual Basic will use this setting the next time you
start it, even if the setting was entered for another project.

Occasionally you may encounter a bug that’s especially difficult to track down. Don’t
panic – here are some things that you can do:
• First and foremost, make a backup. This is the point at which even experienced programmers
frequently lose many hours of work. When you experiment, it is far too easy to accidentally
overwrite or delete necessary sections of code.

• Use the debugging facilities built in to Visual Basic. Attempt to identify the line or lines of code
generating the error. Isolate the code. If you can isolate the problem to one block of code, try to
reproduce the same problem with this block of code separated from the rest of your program.
Select the code, copy it, start a new project, paste the code into the new project, run the new
project, and see if the error still occurs.

• Create a log file. If you cannot isolate the code or if the problem is erratic or if the problem only
happens when compiled, then the debugging facility of Visual Basic will be less effective. In
these situations you can create a log file which records the activity of your program. This will
allow you to progressively isolate the location of the suspect code. Call the following procedure
from various points in your program. You should pass in a string of text which indicates the
current location of the code executing in your program.
• Sub LogFile (Message As String)
• Dim LogFile As Integer
• LogFile = FreeFile
• Open "C:\VB\LogFile.Log" For Append As #LogFile
• Print #LogFile, Message
• Close #LogFile
• End Sub

• Sub Sub1 ()
• '...
• Call LogFile("Here I am in Sub1")
• '...
• End Sub
• Simplify the problem. If possible, remove any third party controls and custom controls from your
project. Replace them with Visual Basic standard controls. Eliminate any code that does not seem
to relate to the problem.

• Reduce the search space. If you cannot resolve the problem with any of the above methods, then
it is time to eliminate all other non-Visual Basic causes from the problem search space. Copy
your AUTOEXEC.BAT and CONFIG.SYS files to backup files. Comment out any and all drivers
and programs from these two files that are not absolutely essential to running your program
under Windows. Change your Windows video driver to the standard Windows VGA driver. Shut
down Windows and reboot your machine. This will eliminate the possibility that there is some
other program or driver which is interfering with your program.

• If you cannot locate a solution and are unable to isolate or resolve the problem with any of these
methods, it's time to look for help. See the technical support documentation.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


For More Information Breakpoints are described in "Using a Breakpoint to Selectively
Halt Execution" earlier in this chapter. Read more about Watch expressions in
"Monitoring Data with Watch Expressions." The Immediate window is discussed in
"Testing Data and Procedures with the Immediate Window." See "Verifying Your Code
with Assertions" for more about the Assert method of the Debug object.

Visual Basic Concepts

How to Handle Focus in Your Control

The way you handle focus for your control depends on which model you're using to
develop your control. Models for building ActiveX controls are discussed in "Three Ways
to Build ActiveX Controls," earlier in this chapter.

Important If you're authoring a control that can be a container for other controls, as
described in "Allowing Developers to Put Controls on Your Control," note that the
material in this topic does not apply to controls a developer places on an instance of your
control. These contained controls will receive focus independent of your control and its
constituent controls.
User-Drawn Controls
If you're authoring a user-drawn control, there won't be any constituent controls on your
UserControl. If you don't want your control to be able to receive the focus, set the
CanGetFocus property of the UserControl object to False. CanGetFocus is True by
default.

If your user-drawn control can receive the focus, the UserControl object will receive
GotFocus and LostFocus events when your control receives and loses the focus. A user-
drawn control is responsible for drawing its own focus rectangle when it has the focus, as
described in "User-Drawn Controls," in this chapter.

This is the only function your UserControl's GotFocus and LostFocus events need to fulfill
for a user-drawn control. You don't need to raise GotFocus or LostFocus events for the
user of your control, because the container's extender provides these events if the
CanGetFocus property is True.

Note The UserControl object of a user-drawn control will also receive a EnterFocus
event prior to GotFocus, and an ExitFocus event after LostFocus. You don't need to put
any code in the event procedures of these event, and in fact it is recommended that you
not do so.

User-drawn controls can respond to access keys, as described later in this topic.
Controls That Use Constituent Controls
If you're authoring a control that enhances a single constituent control, or is an assembly
of constituent controls, your UserControl object will be unable to receive the focus,

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


regardless of the setting of the CanGetFocus property, unless none of its constituent
controls can receive the focus.

If no constituent controls can receive the focus, and CanGetFocus is True, then your
UserControl object will receive the same events a user-drawn control receives. The only
thing you need to do with these events is provide a visual indication that your control has
the focus.
How Constituent Controls Are Affected by CanGetFocus
If your control contains at least one constituent control that can receive the focus, the
CanGetFocus property of the UserControl object must be set to True. If you attempt to
set CanGetFocus to False on a UserControl that has constituent controls that can receive
focus, an error will occur.

Visual Basic will not allow a constituent control that can receive focus to be placed on a
UserControl whose CanGetFocus property is False: Icons of controls that can receive
focus are disabled in the Toolbox when the UserControl's design window is active.
EnterFocus and ExitFocus
When the focus moves from outside your control to any of your control's constituent
controls, the UserControl object will receive an EnterFocus event. The GotFocus event
for the constituent control that receives the focus will be raised after the
UserControl_EnterFocus event procedure.

As long as the focus remains within your control, the UserControl object's focus-related
events will not be raised. As the focus moves from one constituent control to another,
however, the appropriate GotFocus and LostFocus events of the constituent controls will
be raised.

When the focus moves back outside your control, the last constituent control that had the
focus will receive its LostFocus event. When the event procedure returns, the UserControl
object will receive its ExitFocus event.

You can use the EnterFocus event to change which constituent control receives the focus.
You may wish to do this in order to restore the focus to the constituent control that last
had it, rather than simply allowing the first constituent control in your UserControl's tab
order to receive the focus, which is the default behavior.

Tip If your control is complex — as for example an Address control with multiple
constituent controls — you may be tempted to validate the data in the ExitFocus event.
Don't. The user of your control can put code in the Validate event of the user control to
handle data validation as they see fit. If it's absolutely necessary to validate data inside the
control, use the Validate events in combination with the CausesValidation properties of
the constituent controls. Be aware that you can't always count on the Validate event for
constituent controls, as is discussed in "Handling the Validate Event" below.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Tip Generally speaking, it's not a good idea to use MsgBox when you're debugging
focus-related events, because the message box immediately grabs the focus. It's a very bad
idea to use MsgBox in EnterFocus and ExitFocus events. Use Debug.Print instead.
Receiving Focus via Access Keys
Avoid hard coding access keys for your control's constituent controls, because access keys
permanently assigned to your control in this fashion will limit a user's freedom to choose
access keys for her form. In addition, two instances of your control on the same form will
have access key conflicts.

"Allowing Developers to Set Access Keys for Your Control," later in this chapter,
discusses how you can give the user of your control the ability to set access keys on
instances of your control.
Forwarding Focus to the Next Control in the Tab Order
If your control cannot receive the focus itself, and has no constituent controls that can
receive the focus, you can give your control the same behavior displayed by Label
controls. That is, when the access key for your control is pressed, the focus is forwarded
to the next control in the tab order.

To enable this behavior, set the ForwardFocus property of the UserControl object to
True.
Handling the Validate Event
The Validate event and CausesValidation property for a user control behave exactly like
they do for any other control, but the behavior of Validate and CausesValidation for
constituent controls may not yield the expected results. Let's review the standard behavior.
When a control loses focus, its Validation event is fired before its LostFocus event — but
only if the control about to receive the focus has its CausesValidation property set to
True. This allows you to handle data validation before the control loses focus.

A user control exposes a Validate event and, via the Extender object, exposes a
CausesValidation property. Code in the Validate event is executed when the focus is
shifted from the user control to any control that had its CausesValidation property set to
True; setting the CausesValidation property of the user control to True will enable the
Validation event for any control passing focus to the user control.

The Validate event and CausesValidation property for constituent controls work as
expected as long as the focus remains inside the user control. When the focus is shifted
outside of the user control, the Validate event for the constituent control isn't fired. For
that reason, it's best to avoid trying to handle validation within a user control.

Visual Basic Concepts

Using the MaskedEdit Control

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


The MaskedEdit control is used to prompt users for data input using a mask pattern. You
can also use it to prompt for dates, currency, and time, or to convert input data to all
upper- or lowercase letters. For example, to prompt the user to enter a phone number, you
can create the following input mask: "(___) - ___ - ____". If you don't use an input mask,
the MaskedEdit control behaves much like a standard text box.

When you define an input mask using the Mask property, each character position in the
MaskedEdit control maps to a placeholder of a specified type, or to a literal character.
Literal characters, or literals, give visual cues about the type of data being used. For
example, the parentheses surrounding the area code of a telephone number are literals:
(206).

The input mask prevents users from entering invalid characters into the control. If the user
attempts to enter a character that conflicts with the input mask, the control generates a
ValidationError event.

The MaskedEdit control is a bound control and can be used with a data control to display
or update field values in a data set.
Possible Uses
• To prompt for date/time, number, or currency information.

• To prompt for custom mask formats such as a telephone number or any other input that follows a
pattern.

• To format the display and printing of mask input data.

• To work with a data control to display and update field values in a data set.
The Mask Property
The Mask property determines the type of information that is input into the MaskedEdit
control. The Mask property uses characters such as the pound sign (#), backslash (\),
comma (,), and ampersand (&) as placeholders that define the type of input. The following
table lists all the characters you can use to set the Mask property:
Mask Description
character
# Digit placeholder.
. Decimal placeholder. The actual character used is the one specified as the decimal
placeholder in your international settings. This character is treated as a literal for
masking purposes.
, Thousands separator. The actual character used is the one specified as the thousands
separator in your international settings. This character is treated as a literal for
masking purposes.
: Time separator. The actual character used is the one specified as the time separator
in your international settings. This character is treated as a literal for masking
purposes.
/ Date separator. The actual character used is the one specified as the date separator in

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


your international settings. This character is treated as a literal for masking
purposes.
\ Treat the next character in the mask string as a literal. This allows you to include the
'#', '&', 'A', and '?' characters in the mask. This character is treated as a literal for
masking purposes.
& Character placeholder. Valid values for this placeholder are ANSI characters in the
following ranges: 32-126 and 128-255.
> Convert all the characters that follow to uppercase.
< Convert all the characters that follow to lowercase.
A Alphanumeric character placeholder (entry required). For example: a z, A Z, or 0
9.
a Alphanumeric character placeholder (entry optional).
9 Digit placeholder (entry optional). For example: 0 9.
C Character or space placeholder (entry optional).
? Letter placeholder. For example: a z or A Z.
Literal All other symbols are displayed as literals; that is, as themselves.

To create an input mask, you combine mask characters with literal characters. Literal
characters are characters which rather than representing some data type or format, are
used as themselves. For example, to create an input mask for a phone number you define
the Mask property as follows:
MaskEdBox1.Mask = (###) - ### - ####
The pound sign (a digit placeholder) is used with the left and right parentheses and the
hyphen (literal characters). At run time, the MaskedEdit control would look like the
following:

A MaskedEdit control with a phone number mask

When you define an input mask, the insertion point automatically skips over literals as you
enter data or move the insertion point.
The Text and ClipText Properties
All data entered in the MaskedEdit control is contained in and can be retrieved from the
Text property. This is a run time only property and includes all the literal and prompt
characters of the input mask. For instance, retrieving data from the Text property of the
example above returns the string "(555) - 555 - 5555" – the phone number that was
entered.

The ClipText property also returns data entered in the MaskedEdit control, but without
the literal and prompt characters. Using the example above, retrieving data from the
ClipText property returns the string "5555555555". The ClipText property is available
only at run time.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Defining the Input Character
By default, all mask characters are underlined. This indicates to the user that the character
is a placeholder for data input. When the user enters a valid character, the underline
disappears. If you want the underline to remain, you can set the FontUnderline property of
the MaskedEdit control to True.

You can also change the underline input character to a different character by using the
PromptChar property. For example, to change the underline (_) character to the asterisk
(*) character, you simply redefine the value of the PromptChar property:
MaskEdBox1.PromptChar = "*"
Using Mask Characters as Literals
If you want to use a mask character as a literal, you can precede the mask character with a
backslash (\). For example, if you want the pound sign (#) to display, you set the mask as
follows:
MaskEdBox1.Mask = "\##"
This would produce a mask that displays a pound sign (#) followed by a blank space for
entering a number.
The Format Property
You can modify how the MaskedEdit control is displayed and printed using the Format
property. The Format property provides you with standard formats for displaying number,
currency, and date/time information.

The following table lists the standard formats you can use with the Format property:
Data type Value Description
Number (Default) Empty string General Numeric format. Displays as entered.
Number $#,##0.00;($#,##0.00) Currency format. Uses thousands separator; displays
negative numbers enclosed in parentheses.
Number 0 Fixed number format. Displays at least one digit.
Number #,##0 Commas format. Uses commas as thousands
separator.
Number 0% Percent format. Multiplies value by 100 and appends
a percent sign.
Number 0.00E+00 Scientific format. Uses standard scientific notation.
Date/Time (Default) c General Date and Time format. Displays date ,
time, or both.
Date/Time Dddddd Long Date format. Same as the Long Date setting in
the International section of the Microsoft Windows
Control Panel. Example: Tuesday, May 26, 1992.
Date/Time dd-mmm-yy Medium Date format. Example: 26-May-92.
Date/Time Ddddd Short Date format. Same as the Short Date setting in
the International section of the Microsoft Windows
Control Panel. Example: 5/26/92.
Date/Time Ttttt Long Time format. Same as the Time setting in the
International section of the Microsoft Windows

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Control Panel. Example: 05:36:17 A.M.
Date/Time hh:mm A.M./P.M. Medium Time format. Example: 05:36 A.M.
Date/Time hh:mm Short Time format. Example: 05:36.

You use the Format property with the Mask property. For example, to create a mask that
prompts for a Short Date input that displays in the Long Date format, you set the Mask
and Format properties as follows:
MaskEdBox1.Mask = "##-##-##"
MaskEdBox1.Format = "dddddd"
When the user enters the date in the short format (06-27-96, for instance), the MaskedEdit
control verifies that the entered data is valid, and then, when the focus passes to the next
control, it is displayed as "Thursday, June 27, 1996".

Note To automatically shift the focus to the next control when the data has been verified
as valid, set the AutoTab property of the MaskedEdit control to True.

The Format property also allows you to specify custom formatting using the same format
expressions defined by the Visual Basic Format function.

For More Information See "Format Function" or "Format Property (MaskedEdit


control)."
Setting Properties at Design Time
You can set the property values at design time using the MaskedEdit control Property
Pages. Click the Custom option in the Properties window of the MaskedEdit control to
bring up the Property Pages dialog box, as shown below:

You enter the mask and format patterns as in the run time examples above. The Format
drop down list allows you to select any of the predefined standard formats shown above.
This dialog box also allows you to easily set such properties as PromptChar, ClipMode,
and MaxLength.

A MaskedEdit field can have a maximum of 64 characters (the valid range is 1 to 64


characters). This includes literal characters as well as mask characters. You can set this
value using the MaxLength property. At design time, this property is set automatically to
the number of characters in the pattern when you enter a mask pattern.

The ClipMode property specifies whether or not literal characters are included when doing
a cut or copy command. By default, when a selection in the MaskedEdit control is copied
to the Clipboard, the entire selection, including the literals, is transferred. To limit the copy
operation to only the data entered by the user, set the ClipMode property to True.
The ValidationError Event
The ValidationError event occurs when the MaskedEdit control receives invalid input, as
determined by the input mask. For example, if you've defined an input mask that prompts
for numbers, a ValidationError event will occur if the user attempts to enter a letter.
Unless you write an event handler to respond to the ValidationError event, the

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


MaskedEdit control will simply remain at the current insertion point — nothing will
happen.

Mask characters are validated as they are entered and the insertion point is shifted to the
right. When a character is entered or deleted out of sequence (when a digit is inserted or
deleted after the phone number has been entered, for example), all nonliteral characters
shift either to the right or left. When the shift occurs, if an invalid character replaces the
position of a valid character, the ValidationError event is triggered.

For example, suppose the Mask property is defined as "?###", and the current value of the
Text property is "A12." If you attempt to insert the letter "B" before the letter "A," the
"A" would shift to the right. Since the second value of the input mask requires a number,
the letter "A" would cause the control to generate a ValidationError event.

The MaskedEdit control also validates the values of the Text property at run time. If the
Text property settings conflict with the input mask, the control generates a run-time error.

You can select text in the same way you would with a standard text box control. When
selected text is deleted, the control attempts to shift the remaining characters to the right
of the selection. However, any remaining character that might cause a validation error
during this shift is deleted, and no ValidationError event is generated.
Using MaskedEdit as a Bound Control
The MaskedEdit control is a bound control. This means that it can be linked to a data
control and display field values for the current record in a data set. The MaskedEdit
control can also write out values to a data set.

Note When the value of the field referenced by the DataField property is read, it is
converted to a Text property string, if possible. If the recordset is updatable, the string is
converted to the data type of the field.

The MaskedEdit control has three bound properties: DataChanged, DataField, and
DataSource.

To Pause your application for a length of time without a timer control


By Danny King
dannyk@ntlworld.com

First put this code either in a module as a private function on a form

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

Then when you want to pause something just do this

Sleep 1000 'Pause for 1 second

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


LAZY BOYS SCREENSAVER
By Jyothish kumar
jyo1@mailcity.com
A neat little utility called VBCAMERA.OCX. You just load the component and place it in
your form. It will take the photograph of the running screen.
VBCAMERA.OCX </vb-mag/0010/item/vbcamera.ocx>
VBTalk.OCX
By Jyothish kumar
jyo1@mailcity.com
Here is an innovative OCX with a beautifully shaded background, in which you can type
anything. The OCX will make the computer say the same thing that is in the textbox.
VBTalk1.OCX </vb-mag/0010/item/vbtalk1.ocx>
Function to Retrieve a Standard Recordset Object.
By Ray Peltz
Ray_Peltz@cibolaes.com
Required: VB5 / VB6 / ADO
Most of my application require extensive use of recordsets. I found myself writing the
same "with" construct over and over again. To save some time, I built the following
function to return a recordset object I can use in any situation:
'**************************************************************************
'Function: GetRSTemplate
'
'Description: Builds an empty recordset to return to the calling
' procedure. This is used to initialize a default recordset, yet allow
' the user to change default cursor parameters at this time
'
'**************************************************************************
Function GetRSTemplate(Optional eCurLoc As CursorLocationEnum, _
Optional eCurType As CursorTypeEnum, _
Optional eLockType As LockTypeEnum) As ADODB.Recordset

Dim rsholder As New ADODB.Recordset

'If the user does not include a parameter use defaults.


'The default recordset sets up a static, client side cursor with
'minimal locking

With rsholder
If eCurLoc = 0 Then
.CursorLocation = adUseClient
Else
.CursorLocation = eCurLoc
End If
If eCurType = 0 Then
.CursorType = adOpenStatic
Else
.CursorType = eCurType
End If
If eLockType = 0 Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


.LockType = adLockBatchOptimistic
Else
.LockType = eLockType
End If
End With

Set GetRSTemplate = rsholder

Set rsholder = Nothing

End Function

Using it is very simple. If you supply no paramters, you get a client side, static cursor with
minimal locking:

Private sub command1_click()

Dim rs as new ADODB.Recordset

set rs = GetRSTemplate()

end sub

The function uses the same enumerated values as the recordset properties, so the choices
will pop up as they do when you type in object.property =
Determines the number of workdays within a date range.
By Bill Mosca
WRMosca@aol.com
I used implicit If Statements (IIF) because they take less lines of code, and this procedure
is convoluted enough without making is bigger! In case you aren't familiar with them, they
are a hold-over from VBA. The first argument is the expression, the second is what to do
if it's true, the third is what to do if it's false.
The first part determines the number of days between 2 dates. The second part determines
the number of whole weeks because each whole week has a Saturday & a Sunday in it.
The last part determines if the range spanned a weekend but not a whole week so it can
subtract the weekend out.
Private Sub FindWorkDays()
Dim A1 As Date
Dim A2 As Date
Dim iWorkDays As Integer

A1 = Text1.Text
A2 = Text2.Text

iWorkDays = A2 - A1 + 1 - Int((A2 - A1 + 1) / 7) * 2 - _
IIf(Int((A2 - A1 + 1) / 7) = (A2 - A1 + 1) / 7, 0, _
IIf(WeekDay(A2) < WeekDay(A1), 2, 0)) - _
IIf((WeekDay(A1) = 1 Or WeekDay(A2) = 7), 1, 0)

Label1.Caption = iWorkDays

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End Sub

An Elliptical Command Button.


By Jyothish kumar
jyo1@mailcity.com
Here is an OCX file (JYOT.ZIP) which is a command button in an elliptical shape. You
can use this control without writing code for getting this elliptical shape.
JYOT.ZIP </vb-mag/0001/item/jyot.zip>
Display a Form without a Titlebar.
By January Smith
january@texas.net
Often, an application needs a special window without a title bar, the horizontal bar at the
top of a window that contains the title of the window. For instance, a splash screen or a
custom context sensitive menu are nothing more than VB forms without title bars. To
make a form appear without a title bar set the following design-time and run-time
properties.
Properties that must be set at design-time, they are read-only at
run-time:
ControlBox=False ' Turns off Control-menu: Restore, Move, Size,
Minimize, Maximize, Close
MaxButton=False ' Turns off Maximize button
MinButton=False ' Turns off Minimize button
ShowInTaskbar=False ' Hides window in Taskbar

Properties that can be set at either run-time or design-time:


Caption="" ' Blanks window title
Making a form invisible, but leaving the controls visible.
By Nigel Pierre
vbdog@vbomail.com
This involves using the SetWindowLong API function with the following parameters,
GWL_EXSTYLE and WS_EX_TRANSPARENT as constants.

the syntax of the function using the paramters is

dim intresult as long

intresult = setwindowlong(me.hwnd, gwl_exstyle, ws_ex_transparent)

this will make the form referenced by me.hwnd transparent so that the background can be
seen but the controls can still be accessed.
Making a form stay on top.
By Nigel Pierre
vbdog@vbomail.com
This one is for VB programmers who enjoy VB but are new to Visual Basic.
Create a Module called ontop.bas in this module put the following constants.

Public Const HWND_NOTOPMOST = -2

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Public Const HWND_TOPMOST = -1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOSIZE = &H1

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

In the General declarations of the form (ontop.frm) declare a variable


lngontop as long

Put two command buttons on the form,

command1.caption = &SetOnTop
command2.caption = &Make Normal

Code for command1

Private Sub Command1_Click()

lngontop = SetWindowPos = (me.hwnd,HWND_TOPMOST,0,0,0,0,(SWP_NOMOVE


or SWP_NOSIZE))

End Sub

Code for command2

Private Sub Command1_Click()

lngontop = SetWindowPos =
(me.hwnd,HWND_NOTOPMOST,0,0,0,0,(SWP_NOMOVE or SWP_NOSIZE))

End Sub

The Command1 button set the Always On Top behaviour of the form, while Command2
sets it back to normal.
Number of Workdays between two dates.
By Bill Mosca.
WRMosca@aol.com
After stumbling around with case statements, tons of if statements I finally found the code
to get the number of Workdays between two dates, & it only takes a few lines of code.
Anyway, here is the code that Excel uses for its NetWorkdays function, and the code i
modified to fit VB:
Excel
=A2-A1+INT((A2-A1+1)/7*2-IF(INT((A2-A1+1)/7)=(A2-A1+1/7,0,IF
WEEKDAY(A2)<WEEKDAY(A1),2,0))-IF(OR(WEEKDAY(A1)=1, pre <
lblDays.Caption="intDaysBetween" 0) 1, WeekDay(EndDate)="7)," Or IIf((WeekDay(StartDate)="1" - _
0)) 2, WeekDay(StartDate), IIf(WeekDay(EndDate) 0, 7, 1) + StartDate 7)="(EndDate" IIf(Int((EndDate 2
* 7 Int(EndDate 1 intDaysBetween Date As EndDate Dim Integer VB WEEKDAY(A2)="7),1,0)"
As you can see i had to replace all the IF's with IIf's (implicit If's). The
first part determines the number of days between the 2 dates. The next part

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


determines the number of whole weeks between because of the Saturdays &
Sundays. Then the last part determines if the dates spanned a weekend where
the start date day would be smaller that the end date day.

Lostfocus of a text box control.


By Mehjabeen.
mehjabeens@123india.com
This tip is w.r.t the problem of going in a loop when performing validation on the lostfocus of a text box
control.

I have seen that many programmers face this problem and to solve this problem VB 6.0 has introduced a
Validate event also.
But as i have been using VB 4.0 from a long time i have found a simpler trick to handle this problem,
which works on VB 4.0 and VB 6.0

Place two text boxes on the form and see that the initial text property is blank.

Paste the following code on the form:

Private Sub Text1_LostFocus()


If Val(Text1.Text) > 0 Then
Text1.SetFocus
MsgBox "Value cannot be greater than zero."
End If
End Sub

Private Sub Text2_LostFocus()


If Val(Text2.Text) = 0 Then
Text2.SetFocus
MsgBox "Value cannot be equal to zero."
End If
End Sub
And now run the application. Type some value in text1 and do a lostfocus and everything will work fine.
Now change the code and do a setfocus after the message is displayed on the lostfocus of both the text
boxes. That is change the code to :
Private Sub Text1_LostFocus()
If Val(Text1.Text) > 0 Then
MsgBox "Value cannot be greater than zero."
Text1.SetFocus
End If
End Sub

Private Sub Text2_LostFocus()


If Val(Text2.Text) = 0 Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


MsgBox "Value cannot be equal to zero."
Text2.SetFocus
End If
End Sub
Now run the application.Type some value in the first text box and perform a lostfocus and you will see
that everything goes in a loop and the only solution is to perform an abnormal termination of the
application..
Hence the trick is that if any validation is to be done and you need to place the control back on the text
box then perform a setfocus before the Message is displayed instead fo performing it after the message is
displayed.
Map and Disconnect network drive letter.
By Keith Keller.
kkeller@solacomm.com
I really think VBers will like this little application. It answers several questions:
How can I map a networked Drive Letter
How can I disconnect a networked Drive Letter
How can I Open a file or application with VB without using the limited "SHELL" command
I have included the compiled app and its source code. You will need VB6 to open this project, but it will work
in any version of VB from 32-bit 4.0 on up!
Invalid use of null error.
By John Wright.
bonito@standard.net
A nice way to avoid the 'invalid use of null' error is to use the VB build in function VarType. Use the
following code to check for the vartype:
dim retval
dim retvaltype

'add code to retrieve information from the recordset and assign it to the retval

retvaltype = VarType(retval)

if retvaltype = 2 then
retval = "" ' or you could put a 0(zero) if it is a numeric field
else
reval = retval
end if
This should be done only with small amount of variables declared as variant as it take more overhead to
delcare a variant, however if you need a fast way to solve this problem it works.
How to create dll files using Visual Basic.
By Ahmed Zulfiqar.
Ahmed.Zulfiqar@MarshallAerospace.com
Intermediate level of VB knowledge required. VB4/5/6
It is always said that the only language for creating dll is C or C++, but VB is also very powerful and easy
to use for the creation of dll files. Here is a sample of how u can create a small message dll file and then
call it in your program, the way you call windows dll.
Open a new ActiveXDll project from New Project Menu in VB. change the name of class1 to message and
set the following properties as
Properties of class Message

Name Message
DataBindingBehavior 0-VBNone
DataSourceBehavior 0-VBNone

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Instancing 5-Multiuse
MTSTransactionMode 0-NotAnMTSObject
Persistable 0-NonPersistable
There are four public proceudres for message dialogue box. You can write any type of procedure or
function, but for simplicity I wrote procdures that only display message boxes.
Public Sub information(details, title)
MsgBox details, vbInformation, title
End Sub

Public Sub exclaim(details, title)


MsgBox details, vbExclamation, title
End Sub

Public Sub question(details, title)


MsgBox details, vbQuestion, title
End Sub

Public Sub critical(details, title)


MsgBox details, vbCritical, title
End Sub
From the File menu, choose Make Project1 Dll.. Since we haven't change the name of project, the default
name is project1. save the program.
Now open a new project in VB. Go to the Project Menu and choose References and check the Project1 as
shown below.
Create a form and place two text boxes on that form. Name them txtDescription and txtTitle. Also place
four Option button on the form and name them, optInformation, optQuestion, optCritical, and optexclaim
and a command button name cmdRun as shown

Option Explicit
Private mess As New message

Private Sub cmdRun_Click()

If optInformation = True Then


mess.Information txtDescription, txtTitle
ElseIf optQuestion = True Then
mess.question txtDescription, txtTitle
ElseIf optCritical = True Then
mess.critical txtDescription, txtTitle
Else
mess.exclaim txtDescription, txtTitle
End If

End Sub

Now write some text in description box and the title text in title text box and also choose the message type
and press the command button and u will see the appropriate message coming through the dll file.
Find the number of workdays between two dates.
By High Tree Soft - Warnsveld, The Nteherlands.
karelTree@freemail.nl
Like Bill Mosca, I had to find the number of workdays between two dates. This solution doesn’t have
rules, the starting day may be any day of the week. Workdays are Monday through Friday
Put two textboxes and a command button on a form, and call them;

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


txtDagen
Data2
wd
Here’s the code I came up with:
Please Note: Code in dutch
Private Sub wd_Click()
If Not IsDate(txtDagen.Text) Then
MsgBox "U moet een datum invullen ( Datum 1 )"
txtDagen.Text = ""
Exit Sub

End If

If Not IsDate(Data2.Text) Then


MsgBox "U moet een datum invullen ( Datum 2 )"
Data2.Text = ""
Data2.SetFocus
Exit Sub

End If
'================
Dagnummer$ = ""
zat$ = ""
bij$ = ""
Dagnummer1$ = ""
Dagnummer2$ = ""
Rekendagen$ = ""
Verschil$ = ""
weken$ = ""
Overdagen$ = ""
aww$ = ""
odb$ = ""
Weekdagen$ = ""
awr$ = ""
aftel$ = ""
Werkdagen = ""

Verschil$ = DateDiff("d", txtDagen.Text, Data2.Text, 2)


Dagnummer$ = DatePart("W", txtDagen.Text, 2)
Dagnummer2$ = DatePart("W", Data2.Text, 2)

Overdagen$ = Format$(7 - Val(Dagnummer$))


Rekendagen$ = Format$(Val(Verschil$) - Val(Overdagen$))
weken$ = Format$(Int(Val(Rekendagen$) / 7))

Weekdagen$ = Format$(Val(Rekendagen$) - (7 * Val(weken$)))

aww$ = Format$(Val(weken$) * 5)
awr$ = Format$(Val(aww$) + Val(Weekdagen$))

'========

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If Val(Dagnummer$) = 6 Then
odb$ = Format$(Val(Overdagen$) - 1)
aftel$ = "1"
ElseIf Val(Dagnummer$) = 7 Then
odb$ = Format$(Val(Overdagen$) - 0)
aftel$ = "0"
Else
odb$ = Format$(Val(Overdagen$) - 2)
End If
'==========

If Val(Dagnummer2$) = 6 Then
zat$ = "-1"
Else
zat$ = "0"
End If

If Val(Dagnummer$) = 6 Or Val(Dagnummer$) = 7 Then


bij$ = "0"
Werkdagen = Format$(Val(awr$) + Val(odb$) + Val(zat$))
Else
bij$ = "1"
Werkdagen = Format$(Val(awr$) + Val(odb$) + Val(bij$) + Val(zat$))
End If

'==================

MsgBox "Aantal werkdagen = " & Werkdagen

End Sub
This function checks the validity of a credit card number.
By James Johnston
instant@email4life.com
Returns 'True' If the number is in a valid format.
Returns 'False' If the number is in an invalid format.
Description:
This algorithm should work with all credit cards.
If a card validates with this code it doesn't mean that the card is actually good, it just means that the
numbers are arranged in a valid format. If they don't validate then you've saved some time because you
don't have to process the card to find out that it is defiantely bad. I use this function in CGI forms that
process credit card orders.
Function CheckCard(CCNumber As String) As Boolean
Dim Counter As Integer, TmpInt As Integer
Dim Answer As Integer

Counter = 1
TmpInt = 0

While Counter <= Len(CCNumber)


If (Len(CCNumber) Mod 2) Then
TmpInt = Val(Mid$(CCNumber, Counter, 1))
If Not (Counter Mod 2) Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


TmpInt = TmpInt * 2
If TmpInt > 9 Then TmpInt = TmpInt - 9
End If
Answer = Answer + TmpInt
Counter = Counter + 1
Else
TmpInt = Val(Mid$(CCNumber, Counter, 1))
If (Counter Mod 2) Then
TmpInt = TmpInt * 2
If TmpInt > 9 Then TmpInt = TmpInt - 9
End If
Answer = Answer + TmpInt
Counter = Counter + 1
End If
Wend

Answer = Answer Mod 10

If Answer = 0 Then CheckCard = True


End Function
Making a Grid Auto-Resize
By Richard Roberts
Richard.Roberts@pgen.com
The problem with the Grid control in VB5.0 is that there is no property to force it to auto-resize the
column width. So I wrote this little function that accepts the form name that the grid is on and the name
of the grid. The function then loops through the grid and resizing each column width to the widest text
entry in that column.
GRID.FRM </vb-mag/9812/item/grid.frm>
Find the number of workdays between two dates
By Bill Mosca
BMosca@DHSI.com
I had to find the number of workdays between two dates (including Saturday as a possible starting day).
The number of days were grouped into these ranges: 1 day, 2 days, 3 days, 5+days, 10+days, and 30+days.
The rules were No input on Sundays and starting dates were only Monday through Friday, but ending
dates might be on Saturdays. Here's the code I came up with:
Select Case DaysBetween
Case 0 To 6
Select Case DaysBetween
Case 0, 1
rs.Edit
rs("Days") = 1
rs.Update
Case 2
rs.Edit
rs("Days") = 2
rs.Update
Case 3
If WeekDay(Date2) = vbMonday Then
rs.Edit
rs("Days") = 1
rs.Update
Else
rs.Edit

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


rs("Days") = 3
rs.Update
End If
Case 4
If WeekDay(Date2) = vbMonday Then
rs.Edit
rs("Days") = 2
rs.Update
ElseIf WeekDay(Date2) = vbTuesday Then
rs.Edit
rs("Days") = 3
rs.Update
Else
rs.Edit
rs("Days") = 3
rs.Update
End If

Case 5 '5 to 9 -->5+


If WeekDay(Date2) < vbFriday Then
rs.Edit
rs("Days") = 3
rs.Update
ElseIf WeekDay(Date2) >= vbFriday Then
rs.Edit
rs("Days") = 5
rs.Update
End If
Case 6
If WeekDay(Date2) < vbThursday Then
rs.Edit
rs("Days") = 3
rs.Update
ElseIf WeekDay(Date2) >= vbThursday Then
rs.Edit
rs("Days") = 5
rs.Update
End If
End Select
Case 7 To 11 '5 to 9 -->5+
rs.Edit
rs("Days") = 5
rs.Update
Case Is > 11
WeeksBetween = DateDiff("w", Date1, Date2)
Select Case WeeksBetween
Case 1 To 5 '10 to 29 -->10+
rs.Edit
rs("Days") = 10
rs.Update
Case Is > 5 '30+
rs.Edit
rs("Days") = 30
rs.Update

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End Select
End Select
Else 'if bill is a Send Back
rs.Edit
rs("Days") = 0
rs.Update
End If
rs.MoveNext
Loop
Return True if no value is passed
By Sentil Kumar Muniyandi
senthilmi@rocketmail.com
If you use a typed variable as optional argument in a Function or Sub you can't use "ISMISSING" function
to determine whether it is initialized or not.
To solve this you can use "Empty" keyword as follows,
Public Function MyFun (Optional ByVal sOptVar
as String) As Boolean
if sOptVar = Empty then
MyFun = True
Else
MyFun = False
End if
End Function
This function returns True if no value is passed to the optional variable sOptVar, else returns False.
This Works in VB5.0.
Call a value from your enumeration
By Marcio Coelho
mcoelho@bellatlantic.net
Everybody talks about "How useful enumeration is". Here goes a little piece of code that really
demonstrates that statement is correct.
With a little help from a property let and property get, you can call a value from your enumeration without
having to remember the description you previouslly coded inside of your enumeration since it will pops up
for you.
* Here goes the code:
'Add this code to a module.bas
Dim mForm As Byte
Enum FromForm
FormNameA = 1
FormNameB = 2
FormNameC = 3
End Enum

Property Get SetForm() As FromForm


SetForm = mForm
End Property

Property Let SetForm(Value As FromForm)


mForm = Value
End Property

'Add this to a command button on your application


'to associate with a form
SetForm = FormNameB

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Snds parameters in an INDEX format to a class
By Marcio Coelho
mcoelho@bellatlantic.net
We are developing a crystal report print engine dll, that will be called from another dll, and will generate
reports.
On the first tier, we want to pass the report parameters to the second tier. Although we know we can do
that on an array manner, we found out that sending the parameters in an INDEX manner should make the
process much more friendly for the first tier developer. Therefore, the code bellow, sends parameters in an
INDEX format to a class. When the class receives the parameters, it builds an array that can be used in
the class as desired.
On the first tier the code is something like the one bellow.
* After referencing the DLL or making a class named PrintEngine
Dim ObjPrint as PrintEngine.Engine
Set ObjPrint = New Engine
objPrint.StoreProcParam(0) = Variable 1
objPrint.StoreProcParam(1) = Variable 2
objPrint.StoreProcParam(2) = Variable 3
objPrint.StoreProcParam(3) = Variable 3
and so on......
On second tier the code is something like the one bellow.
Private mStoreprocparam() As Variant
Public Property Let StoreProcParam(Index As Integer, value As String)
ReDim Preserve mStoreProcParam(Index)
Let mStoreProcParam(Index) = Value
End property
Then I use the variant mStoreProcParam on the second tier to work with the received parameters.
The main point for the sample above is the property Let StoreProcParam(Index As Integer, value As
String). This property receives one index and one value at a time and stores it in the array
mStoreProcParam(). Then, that Array can be used in a lot of different ways.
How to use MSFlexGrid to display data via ADO method
By Liu, Qiang
qiang.liu@mayo.edu
We tried this example to show how to use MSFlexGrid to display data via ADO method, and also to show
a simple SQL query. first we set up ADO object, and connected with a back end Sybase database, opened a
table, which called mirtb_patient, we used a MSFlexGrid for 25 columns(fields), and specified two loops,
variable ii for row(record), oo for column. This prepared query can get the data from a Sybase via ADO,
and dispaly them on the MSFlexGrid. Notice that when it finish, all opening objects and methods should
be closed.

Option Explicit
Dim Cn As New ADODB.Connection
Dim rs As ADODB.Recordset

'Use a connecting string or Connection object here

Dim sSQL As String


Dim sOut As String

Private Sub connect_cmd_Click()

' Set ii as the row counter

Dim ii As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


' Set oo as the column counter

Dim oo As Integer

'ADO Demo, it populates data with MSFlexGrid !

If Cn.State = adStateOpen Then


MsgBox "ADO Connection Successful!"
End If

' Set up SQL calling

sSQL = "Select * from mirtb_patient"


rs.Open sSQL, Cn, adOpenDynamic
sOut = ""

ii = 0

' To see if end of the table?


While rs.EOF <> True
DBGrid1.Rows = ii + 1
iii = 1

' Loop for the column, we just used 25 columns for display!

For oo = 0 To 24
DBGrid1.TextMatrix(ii, oo) = rs.Fields(oo)
Next oo
ii = ii + 1

' Move to next record

rs.MoveNext
Wend
End Sub

Private Sub Form_Load()

'Define a Command object for the connection

Set Cn = New ADODB.Connection


Set rs = New ADODB.Recordset
Cn.ConnectionString = "DSN=TESTRCH01;UID=ADOTRY;PWD=ADOTRY;"
Cn.ConnectionTimeout = 100
Cn.Open
End Sub

Private Sub quit_cmd_Click()

' To close objects rs and cn etc., they shoud be closed here!

rs.Close

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Set rs = Nothing
Cn.Close
Set Cn = Nothing
Unload Me
End Sub
Quick single line toggle for 1/0-type boolean values
By Sean C. Nichols
This is for toggling boolean values that flip between 1 and zero as opposed to -1 and zero, which are VB's
built-in boolean values.
One common place I use it is toggling checkboxes/checkable menu options. Both of these use as their
"checked" status either vbUnchecked (0) or vbChecked (1).
The standard method for toggling these is to use an If-Then statement:
if check_value = vbChecked Then
check_value = vbUnchecked
Else
check_value = vbChecked
End If
There is nothing inherently wrong with this, except that it takes up 5 lines of code, and the alternative of
putting it on one line, separated by colons is messy. This means extra scrolling space which can be a
hindrance, especially if you have already long procedures.
Unfortunately, you can't use the Not operator as in:
check_value = Not check_value
because Not (0) is -1, and worse, Not (1) is -2. This problem arises because the Not() function performs a
bitwise NOT, rather than a true logical NOT. As a result, this only works with "true" boolean values (-
1/0).
The solution which hit me is simply to add 2:
check_value = (Not check_value) + 2
hey... works like a charm!
How to save a brande new WAV file using mciSendString API
By Pranay Uppuluri
PRUppL2aol.com
Shows you how to save a brand new WAV file using the mciSendString API (Doen't use the MCI Control
that comes with VB5!) It is really easy to do that. I learned it after studying this API for a while. I couldn't
get the MCI Control to save a WAV file though! I don't know why! If any body could, just e-mail it to me
and I will see how it is possible to do. :-)
DownloadWAVESAVE.ZIP <wavsave.zip> (File size 1.4K)
Making the Close button (X) on a form disabled
By Pranay Uppuluri
PRUppL2aol.com
Ever wonder how those Setup programs have their X button on caption bar disabled, so you can't close
them? Well, now you can do it, when you download this!
DownloadCANTCLOS.ZIP </vb-mag/9808/item/cantclos.zip> (File size 1.7K)
Even or Odd - Update
By Guido
guido.coenen@d.kamp.net
the tip featured in your current issue to tell if a number is even or odd, works v_e_r_y slow. It does a lot of
type conversion between numbers and strings and unnecessary, slow string manipulation. It will
remarkably slow down the machine if used in a loop and uses a dirty kind of type casting because an
integer value is used as a return value of a boolean type function (!). In addition, it will only work with
Visual Basic.

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


There are at least two much more efficient and simple ways to tell if a number is odd or even; both
examples will store "TRUE" in the variable "bool_IsEven" if the number is even and will work in nearly
all programming languages:
a) bool_IsEven = int_Number MOD 2 = 0 b) bool_IsEven = int_Number AND 1 = 0
How to disable Ctrl Alt Del.
By Bob Butler
b_butler@msn.com
With Windows 95 it is possible to disable CTRL-ALT-DEL, CTRL-ESC and ALT-TAB by claiming to be
the current screen saver. Sample code can be found at http://www.vbonline.com/vb-
mag/qaweb/win95/cad.htm in the new qaweb section of VB Online Magazine. To start an application you
have a few options. Normally the Startup group is used, but since this sort of application must be
inaccessible to the general user you need to place it in the registry. There are three keys of interest:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices

All entries under these three keys are started during the bootup process and your application should be
listed under one of them. The "Run" key names applications to run every time, as does the "RunServices"
key. The difference is when they start during the boot process. You probably want to use the Run key, but
you can try both and see what the difference is. The RunOnce key will run the next time Win95 starts, but
is then purged. This is a good place to put applications that complete a setup process or a cleanup.
List Box To List Box - VB5 Professional
By Peter Nunez
pnunez1@tuelectric.com
Here is another way to move data from one list box to another and back. The four buttons ( ADD, ADD
ALL, REMOVE and REMOVE ALL) are a control array and the subroutine FourButtons handles all
movements between the two list boxes. I tried to use the least amount of code. This app is an unattended
bulk printing app.

Private Sub FourButtons(ByVal Index As Integer)


Screen.MousePointer = 11
Select Case Index
Case 0 ' Add selected contractId from List1 to List2
If List1.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List1.ListIndex = -1 Then 'nothing is selected
List1.SetFocus
List1.Selected(0) = True 'select the first item in the list.
End If
DoEvents
List2.AddItem List1.Text
List1.RemoveItem List1.ListIndex

Case 1 ' Add All ContractIds from List1 to List2


If List1.ListCount = 0 Then

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Screen.MousePointer = 0
Exit Sub
End If
If List1.ListIndex = -1 Then
List1.SetFocus
List1.Selected(0) = True
End If
DoEvents
For i = (List1.ListCount - 1) To 0 Step -1
List2.AddItem List1.List(i)
DoEvents
Next i
List1.Clear

Case 2 ' Remove selected contractId from List2 to List1


If List2.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List2.ListCount = 0 Then Exit Sub
If List2.ListIndex = -1 Then
List2.SetFocus
List2.Selected(0) = True
End If

List1.AddItem List2.Text
List2.RemoveItem List2.ListIndex

Case 3 ' Remove All ContractIds From List2 to List1


If List2.ListCount = 0 Then
Screen.MousePointer = 0
Exit Sub
End If
If List2.ListCount = 0 Then Exit Sub
If List2.ListIndex = -1 Then 'nothing is selected.
List2.SetFocus 'set focus.
List2.Selected(0) = True 'select first item in list.
End If
For i = (List2.ListCount - 1) To 0 Step -1
List1.AddItem List2.List(i)
DoEvents
Next i
List2.Clear

End Select
Screen.MousePointer = 0
End Sub
How to write a Screen Saver.
By Bob Butler
b_butler@msn.com
The basic requirements for creating a screen saver are:
In the properties for your application set the title to:

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


SCRNSAVE: name

The 'name' part will appear on the list of available screen savers in the control panel. Note that
this is not the title of the form, it is the application title and must be set in the project properties.
When you compile the program use .SCR for the extension instead of .EXE as normal. Place the
compiled code in the Windows\System directory (it can also go in Windows, but System is
preferred)
When your application starts check for a previous instance and stop if one is found:

Sub Form_Load()
If App.PrevInstance Then
Unload Me ' NEVER use END!
Exit Sub
End If

End Sub

Check the command line for switches:

/s setup mode - display/update configuration options only


/p nnn preview mode - "nnn" is a handle to the preview window
/a password support check

If the screen saver is to run normally use the following to tell Windows that you are taking over as a
screen saver (this will disable ALT-TAB and CTRL-ALT-DEL and prevent more instances from
being started):

Private Const SPI_SCREENSAVERRUNNING=97


Private Declare Function SystemParametersInfo Lib "user32" _
Alias "SystemParametersInfoA" (ByVal uAction As Long, _
ByVal uParam As Long, lpvParam As Any, _
ByVal fuWinIni As Long) As Long
SystemParametersInfo SPI_SCREENSAVERRUNNING, 1, ByVal 1&, False
Before your code terminates tell Windows you are leaving with:
SystemParametersInfo SPI_SCREENSAVERRUNNING, 0, ByVal 1&, False

For more information, including sample code that handles password protected screen savers
check http://www.arcatapet.com/vb.html or http://www.tiac.net/users/lvasseur/
reading data from a database to display on a form is null fields.
By Martin Woodward
MWoodward@SkyGp.PharMed.org.uk
To prevent an 'invalid use of null' error occurring when reading data from a database use the following
syntax:

Text fields:
Form1.text1.text = "" & myRecordset("TextName")

Numeric fields:
Form1.Option1.Value = 0 & myRecordset("NumricField")

How to place your application in the System Tray.


By James E. Bettone
jbettone@premier1.net

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Applies to Win95 and WinNT
Both Windows95 and WindowsNT have that neat new feature called the system tray where you can place
your application. This is easily done via the Windows API called "Shell_NotifyIcon" which manages the
Taskbar which the system tray is part of.
There are a couple of tricky things that you need to keep in mind when using this function. First, the
application needs to be visible (but not necessarily seen--you can set its Top & Left to -10000) and you
also need to know how to handle callback functions. If your not sure what a callback function is then stop
here (you should learn about callback functions before proceeding).
First, place the declares into a bas module:
' These tell you what is happening in the system tray (over your icon)
Public Const WM_MOUSEISMOVING = &H200 ' Mouse is moving
Public Const WM_LBUTTONDOWN = &H201 'Button down
Public Const WM_LBUTTONUP = &H202 'Button up
Public Const WM_LBUTTONDBLCLK = &H203 'Double-click
Public Const WM_RBUTTONDOWN = &H204 'Button down
Public Const WM_RBUTTONUP = &H205 'Button up
Public Const WM_RBUTTONDBLCLK = &H206 'Double-click
Public Const WM_SETHOTKEY = &H32

' The API Call


Public Declare Function Shell_NotifyIcon Lib "shell32" _
Alias "Shell_NotifyIconA" _
(ByVal dwMessage As enm_NIM_Shell, pnid As NOTIFYICONDATA) As
Boolean

' User defined type required by Shell_NotifyIcon API call


Public Type NOTIFYICONDATA
cbSize As Long
hwnd As Long
uId As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * 64
End Type

' This is an Enum that tells the API what to do...


' Constants required by Shell_NotifyIcon API call:
Public Enum enm_NIM_Shell
NIM_ADD = &H0
NIM_MODIFY = &H1
NIM_DELETE = &H2
NIF_MESSAGE = &H1
NIF_ICON = &H2
NIF_TIP = &H4
WM_MOUSEMOVE = &H200
End Enum

Public nidProgramData As NOTIFYICONDATA

Place this code in Form_Load():

Private Sub Form_Load()

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


' Hide the form
With Me
.Top = -10000
.Left = -10000
.WindowState = vbMinimized
End With

With nidProgramData
.cbSize = Len(nidProgramData)
.hwnd = Me.hwnd
.uId = vbNull
.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
' This is the event that will trigger when stuff happens
.uCallbackMessage = WM_MOUSEMOVE
.hIcon = Me.Icon
.szTip = "Lock Your System" & vbNullChar
End With

' Call Notify...


Shell_NotifyIcon NIM_ADD, nidProgramData

End Sub

This goes into the Form_MouseMove event:

' Name: Form_MouseMove


'
' Purpose: Processes the Events from the TaskBar...
'
' Inputs: None
'
' Returns: None
'
' Revision: James E. Bettone 12/02/1997
'
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As
Single, Y As Single)

On Error GoTo Form_MouseMove_err:

' This procedure receives the callbacks from the System Tray icon.
Dim Result As Long
Dim msg As Long

' The value of X will vary depending upon the scalemode setting
If Me.ScaleMode = vbPixels Then
msg = X
Else

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


msg = X / Screen.TwipsPerPixelX
End If

Select Case msg


Case WM_LBUTTONUP
' process single click on your icon
Call Command1_Click()

Case WM_LBUTTONDBLCLK
' Process double click on your icon

Case WM_RBUTTONUP
' Usually display popup menu

Case WM_MOUSEISMOVING
' Do Somthing...

End Select

Exit Sub

Form_MouseMove_err:
' Your Error handler goes here!

End Sub

When your Application executes, it will head right into the System Tray. It is up to you to program in the
context menus and to process the callback messages.
Attaching and manipulating PictureBox and TextBox on the Command
Button.
By JungHwan Kim
apple@iae2000.iae.re.kr
When images are needed to be inserted at the Command Button control, there are two ways.
First, the Command Button style property is set 1(Graphic) and the hape of the image can be changed
according that the user presses the mouse button up or down. However, the images should be placed at a
fixed position, and both images and texts can't be displayed. Second, we can put the PictureBox which has
the Enabled property False on the Command Button control. In this case, we can't make use of a function
of the PictureBox. That is, we can't change the BorderStyle according that the mouse pointer is in the
PictureBox or out of the PictureBox. If PostMessage Windows API is used, we can solve this problem.
Suppose there are two bitmap files, button.bmp and camera.bmp.
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long

Private Sub Form_Load()


Picture1.TabStop = False
Picture1.BorderStyle = 0
End Sub

Private Sub Command2_Click()

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Unload Me
End Sub

Private Sub Picture1_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Picture1.BorderStyle = 1
End Sub

Private Sub Command1_MouseMove(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Picture1.BorderStyle = 0
End Sub

Private Sub Command1_MouseDown(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Form1.Picture1.Picture = LoadPicture("button.bmp")
End Sub

Private Sub Command1_MouseUp(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Form1.Picture1.Picture = LoadPicture("camera.bmp")
End Sub

Private Sub Picture1_MouseDown(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Ret = PostMessage(Form1.Command1.hwnd, WM_LBUTTONDOWN, _
wParam, lParam)
End Sub

Private Sub Picture1_MouseUp(Button As Integer, _


Shift As Integer, X As Single, Y As Single)
Ret = PostMessage(Form1.Command1.hwnd, WM_LBUTTONUP, _
wParam, lParam)
End Sub

"ShowInTaskBar" property of a VB Form at runtime.


By Joe LeVasseur
lvasseur@tiac.net
Option Explicit
'--------------------------------

' This code will set "ShowInTaskBar" property


' of a VB Form at runtime. I haven't been able
' to change this property without unloading the
' form. This code could be useful when used in
' conjunction with a user-chosen preference.
' (Maybe you can think of a good use for it.)
' Comments, improvements? Please forward to:
' Joe LeVasseur
'-----------------------------------------------
Private Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long) As Long

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
'----------------------------
Private Const GWL_EXSTYLE = (-&H14)
Private Const WS_EX_TOOLWINDOW = &H80
'----------------------------
Private Sub Form_Initialize()
Call SetStyle
End Sub
Private Sub SetStyle()
Dim lWindowStyle&, lRetVal&
Debug.Print Second(Now)
' 50/50 chance
If (Second(Now) Mod 2) Then
lWindowStyle = GetWindowLong(hwnd, GWL_EXSTYLE)
lWindowStyle = lWindowStyle And WS_EX_TOOLWINDOW
lRetVal = SetWindowLong(hwnd, GWL_EXSTYLE, lWindowStyle)
End If
End Sub
Delaying a VB app for a set number of Minutes, Seconds.
By Joe LeVasseur
lvasseur@tiac.net
Option Explicit
Dim StopTheTimer As Boolean

Private Sub Command1_Click()


Dim lRetval&
lRetval = Delay(1, 5, StopTheTimer)
If lRetval = 0 Then
MsgBox "Time's up!"
Else
MsgBox "You cancelled!"
End If
End Sub
Private Sub Command2_Click()
StopTheTimer = True
End Sub

Public Function Delay(Mins%, Secs%, Optional ByRef StopFlag) As Long


Dim EndOfDelay
EndOfDelay = DateAdd("n", Mins, Now)
EndOfDelay = DateAdd("s", Secs, EndOfDelay)
Delay = 0
Do While (Now < EndOfDelay)
DoEvents
If Not IsMissing(StopFlag) Then
If StopFlag Then
Delay = 1
StopFlag = False
Exit Do
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


End If
Loop
End Function
Easy Transparent Scrolling text on picture
By Antonio Mazzella
mazzella@vaxca3.unica.it
The idea is from the scrolling text I read in a old "tips&tricks" and part of the listing is from it. Create a
form and load on it a <<.bmp>> picture, by picture property. Create a label and a timer on the form. The
properties of the label you must change are BackStyle=3D0 transparent and borderstyle 0=3DNome. Then
type the statements in the subs:
Sub Form_Load()
Me.Show
Label1.Caption =3D "Hi!! scroll Test.."
Label1.ForeColor =3D &HFF
ScaleMode =3D 3
theleft =3D ScaleWidth / 2

thetop =3D ScaleHeight

Timer1.Enabled =3D True


Timer1.Interval =3D 10
End Sub

Sub Timer1_Timer()
Label1.Top =3D thetop
thetop =3D thetop - 1
If thetop < -p1hgt Then
Timer1.Enabled =3D False
Txt$ =3D "Finished With Scrolling"
CurrentY =3D ScaleHeight / 2
CurrentX =3D (ScaleWidth - TextWidth(Txt$)) / 2
Print Txt$
End If
End Sub
No Listbox.ScrollPos member?
By Dave Wilson
dwilson@infosys.co.uk
Whilst working on a current project the requirement to scroll an array of listboxes simultaneously cropped
up. I was surprised to discover that there is no Listbox.ScrollPos member of the ListBox control and so
had to resort to the windows API to overcome this oversight. The results of which are included below.
Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd
As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long

' *** ListBox Scroll Event ***


Private Sub lst_Ranges_Scroll(Index As Integer)
Dim I As Integer, Pos As Integer
Static bScrolling As Boolean

If Not bScrolling Then


bScrolling = True
Pos = GetScrollPos(lst_Ranges(Index).hwnd, SB_VERT)
For I = 0 To 3

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If I <> Index Then
SendMessage lst_Ranges(I).hwnd, WM_VSCROLL,
MakeLong(SB_THUMBPOSITION, Pos), SB_THUMBPOSITION
End If
Next
bScrolling = False
End If
End Sub

' *** MakeLong Method ***


Private Function MakeLong(LOWORD As Integer, HIWORD As Integer) As Long
MakeLong = Bitwise_OR(LOWORD, Bitwise_SHL(HIWORD, 16))
End Function

I hope this will be of some use to somebody someday, I know that I was surprised when I discovered the
lack of a ScrollPos Member.
The "AlwaysOnTop" property.
By Joe LeVasseur
lvasseur@tiac.net
Simply paste the code below into the form that you need to have the "AlwaysOnTop" property. Syntax
would be like this-[FormName].AlwaysOnTop= True Note the commented code in the Resize event, the
form loses the "AlwaysOnTop" property if it is minimized. I think that under most circumstances a
"AlwaysOnTop" window would probably be a fixed dialog and therefore not minimizable. One other
caveat- this code may or may not work in the IDE. (I've seen both.)
__________snip____________________________________
Option Explicit
'------------------
' Paste this into a form and you
' will have a new property-AlwaysOnTop.
' You then can use it like any other
' property- [FormName].AlwaysOnTop= True
' Joe LeVasseur
'------------------
Private Const HWND_TOPMOST = -&H1
Private Const HWND_NOTOPMOST = -&H2
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
'------------------
Private Declare Sub 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)
'------------------
Private bOnTopState As Boolean
Public Property Let AlwaysOnTop(bState As Boolean)
Dim lFlag As Long
On Error Resume Next
If bState = True Then
lFlag = HWND_TOPMOST
Else
lFlag = HWND_NOTOPMOST
End If
bOnTopState = bState

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


Call SetWindowPos(Me.hWnd, lFlag, 0&, 0&, 0&, 0&, _
(SWP_NOSIZE Or SWP_NOMOVE))
End Property
Public Property Get AlwaysOnTop() As Boolean
AlwaysOnTop = bOnTopState
End Property
'------------------
Private Sub Form_Resize()
' Only need this if form can be
' minimized.(Loses the setting.)
'AlwaysOnTop = bOnTopState
End Sub
_________snip___________________________________________
Here's a another way to speed up looping through a recordset
By Jeff L
JeffL@pobox.com
There was a good tip for speeding up looping through a recordset in this tips section. (I have included it
below for reference) However, if you have the option, there is another way to perform an operation on
every member of a recordset that will execute much faster than any recordset operation, use a SQL
UPDATE statement to modify the records. That way, the modification executes on the database server,
and gets whatever optimizations your database engine provides. For example, to give all your
longstanding customers a discount, you could use the following SQL statement in place of any recordset:
UPDATE Customers SET Discount = 0.10 WHERE CustSince <= 1993
This could take the place of getting a recordset with the SQL:
SELECT Discount FROM Customers WHERE CustSince <= 1993
and then needing to navigate the whole recordset setting each Discount to 10%.
----------------------------------------------------------------------------
----------------------------------------------------------Accessing Databases

This tip is on accessing databases. For performing an operation in


every record of a Table or RecordSet (for example you could
want to update some fields, or retrieve some information), generally
this code is used.

Do
...
...
MyDynaset.MoveNext
Loop Until MyDynaset.EOF

Bat if you use this code

Dim k As Long, j As Long

MyDynaset.MoveLast
j = MyDynaset.RecordCount
MyDynaset.MoveFirst

For k = 1 to j
...
...
MyDynaset.MoveNext
Next

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


you should notice is 30% speed increase from the first code. I tried
this code with a MDB file with 17000 records, and with a MDB
file with 500 records. They both work well. The reason? Of course
because the second code does not check the EOF condition
every iteration (even if it must go to the last record and then to the
first record)! Maybe, if the recordset is small you may not notice
any improvement, but in this case, processing time is short anyway. I
found this code does not improve processing time in Delphi. I
think it's because Delphi EXE is compiled and not p-code.

Tip supplied by Castelli Stefano


---------------------------------------------------------------------------- ---------------------------------------------------
-------
Here's a efficient IsEven function
By Sam Hills
shills@bbll.com
Function IsEven( n as Integer) As Boolean IsEven = Not -(n And 1) End Function If you want an IsOdd
function, just omit the Not.
Move elements from one list box to another
By Eric Howard
ehoward@hotmail.com. <mailto:ehoward@hotmail.com>
This is something I feel a few people might find to be useful. Basically it allows elements to be moved
between two list boxes as well as conditionally enabling an Execute button if there are any elements in the
second list box. You should first design a form that looks like this:

Private Sub cmdLeft2Right_Click()


If lstList1.ListCount > 0 Then
List_Refresh lstList1, lstList2
cmdExecute.Enabled = True
End If
If lstList1.ListCount > 0 Then
cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
Else
cmdLeft2Right.Enabled = False
cmdAll2Right.Enabled = False
cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
End If
End Sub

Private Sub cmdAll2Right_Click()


Move_All_Items "Right", lstList1, lstList2
cmdLeft2Right.Enabled = False
cmdAll2Right.Enabled = False

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


cmdAll2Left.Enabled = True
cmdRight2Left.Enabled = True
cmdExecute.Enabled = True
End Sub

Private Sub cmdAll2Left_Click()


Move_All_Items "Left", lstList1, lstList2
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
cmdAll2Left.Enabled = False
cmdRight2Left.Enabled = False
cmdExecute.Enabled = False
End Sub

Private Sub cmdRight2Left_Click()


If lstList2.ListCount > 0 Then List_Refresh lstList2, lstList1
If lstList2.ListCount > 0 Then
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
Else
cmdExecute.Enabled = False
cmdLeft2Right.Enabled = True
cmdAll2Right.Enabled = True
cmdAll2Left.Enabled = False
cmdRight2Left.Enabled = False
End If
End Sub

Public Sub Move_All_Items(Direction As String, List1 As Control, List2 As Control)


Dim Count As Integer
Select Case Direction
Case "Left"
For Count = 0 To List2.ListCount - 1
List1.AddItem List2.List(Count)
List2.Selected(Count) = 0
Next Count
List_Refresh List2, List1
List2.Clear
Case "Right"
For Count = 0 To List1.ListCount - 1
List2.AddItem List1.List(Count)
List1.Selected(Count) = 0
Next Count
List_Refresh List1, List2
List1.Clear
End Select
End Sub

' Special Subroutine to move selected items from one listbox to another
Public Sub List_Refresh(SourceList As Control, TargetList As Control)
Dim N As Integer
Dim I As Integer
ReDim Remove(0 To SourceList.ListCount - 1) As Boolean 'Items to remove
For N = 0 To (SourceList.ListCount - 1) ' If selected, then , add to Target List

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


If SourceList.Selected(N) = True Then
TargetList.AddItem SourceList.List(N)
Remove(N) = True ' Sets item for removal from Source list
End If
Next N

Dim C As Integer ' Counts how many have been removed


C=0
For N = 0 To UBound(Remove)
If Remove(N) Then
SourceList.RemoveItem N - C
C=C+1
End If
Next N

For I = 0 To (SourceList.ListCount - 1) ' Reset Selected Flags and reset


SourceList.Selected(I) = False
Next I
SourceList.Refresh
TargetList.Refresh
End Sub

"This method is meant to work best when the multi-select feature of the listboxes is enabled"
Check a DOB against a valid age
Author: James Johnston
Email : instant@email4life.com
Description: This function checks a DOB against a valid age.
Returns 'True' if the DOB is valid for the specified age.
Returns 'False' if the DOB is not valid for the specified age.
I've used this in CGI programs that need DOB validation.
Function CheckDOB(TestDOB As String, ValidAGE As Integer) As Boolean
Dim TmpInt As Long

TmpInt = DateDiff("d", CDate(TestDOB), Now)

If TmpInt < (ValidAGE * 365.25) Then


CheckDOB = False
Else
CheckDOB = True
End If

End Function
Tip supplied by James Johnston instant@email4life.com
Tells whether a number is even or odd.
Returns 'True' if even.
Returns 'False' if odd.
Function IsEven(TestInt As Integer) As Boolean
Dim TestValues As String, TmpChar As String, Answer As Integer

TestValues = "02468"
TmpChar = Right$(Trim(Str$(TestInt)), 1)
Answer = InStr(1, TestValues, TmpChar)

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


IsEven = Answer

End Function
Tip supplied by James Johnston instant@email4life.com
Accessing Databases
This tip is on accessing databases. For performing an operation in every record of a Table or RecordSet
(for example you could want to update some fields, or retrieve some information), generally this code is
used.
Do
...
...
MyDynaset.MoveNext
Loop Until MyDynaset.EOF

Bat if you use this code

Dim k As Long, j As Long

MyDynaset.MoveLast
j = MyDynaset.RecordCount
MyDynaset.MoveFirst

For k = 1 to j
...
...
MyDynaset.MoveNext
Next
you should notice is 30% speed increase from the first code. I tried this code with a MDB file with 17000
records, and with a MDB file with 500 records. They both work well. The reason? Of course because the
second code does not check the EOF condition every iteration (even if it must go to the last record and
then to the first record)! Maybe, if the recordset is small you may not notice any improvement, but in this
case, processing time is short anyway. I found this code does not improve processing time in Delphi. I
think it's because Delphi EXE is compiled and not p-code.

How to implement "What's this?" Help


Under Windows 95 and Windows NT 3.51, it is possible to right-click on items and get the "What's
This?" window to appear. To set this up in Visual Basic, you need to use a pop-up menu.
Example
• Start Visual Basic 4.0 or, if it is already running, click New Project on the File menu.
• Set the WhatsThisHelp and WhatsThisButton properties for Form1 to TRUE.
• Add the following code to reference the Hotspot Editor help file to the Form_Load procedure:
Private Sub Form_Load()
'This should point to the hc directory
'under the VB4 directory
App.HelpFile = "c:vbhcshed.hlp"
End Sub
• Add two menu items to Form1 using the following as a guide:
Menu Property Value
----------------------------------------------------
WhatsThis Caption WhatsThis
WhatsThis Name mnuWhatsThis

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


WhatsThis Visible False

What's This? Caption What's This?


What's This? Name mnuWhatsThisHelp
• Indent "What's This?" to make it a sub-menu of WhatsThis by pressing the right-arrow button on
the Menu Editor.
• Add the following code to the General Declarations section of Form1:
Public ThisControl as control
• Add the following code to the mnuWhatsThisHelp_Click procedure:
Private Sub mnuWhatsThisHelp_Click()
ThisControl.ShowWhatsThis
End Sub
• Add a Command Button control to the form and set the following Properties:
WhatsThisHelpID = 1
HelpContextID = 5
• Add following code to the Command1_MouseUp event:
Private Sub Command1_MouseUp(Button As Integer, Shift As Integer, _
X As Single, Y As Single)
If Button = vbRightButton Then
Set ThisControl = Command1
PopupMenu mnuWhatsThis
End If
Set ThisControl = Nothing
End Sub
• Run the application. Right-click on the Command button and then left-
Click on the "What's This?" popup to bring up
the Help file.
Creating Rainbow Text
This week I will show you a useful tip that is very simple to do and very effective.
You may have seen programs that have multicoloured (Rainbow) text in the background.
Well this is very simple to do using a for loop.
Code
1. Start a new Standard Exe project; form1 is created by default
2. Type in the following code.
Sub Form_Paint()
Dim I As Integer, X As Integer, Y As Integer
Dim C As String
Cls
For I = 0 To 91
X = CurrentX
Y = CurrentY
C = Chr(I)
'Line -(X + TextWidth(C), Y = TextHeight(C)), _
QBColor(Rnd * 16), BF
CurrentX = X
CurrentY = Y
ForeColor = RGB(Rnd * 256, Rnd * 256, Rnd * 256)
Print "Hello World Hello World Hello World Hello"
Next
End Sub
3. Run the program by pressing F5 or choosing start from the run program and watch the form fill with
lots of multi-coloured text

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html


How can I detect if the system has a sound card?
A lot of programs are now playing sounds for certain events. However, there is no point in wasting time
by playing a sound if the system does not support it. This tip demonstrates how to detect if a sound card
exists.
Declarations
Add the following code to the declarations section of the project.
Declare Function waveOutGetNumDevs Lib "winmm.dll" _
Alias "waveOutGetNumDevs" () As Long
Code
Dim i As Integer
i = waveOutGetNumDevs()
If i > 0 Then
MsgBox "Your system can play sound files.", _
vbInformation, "Sound Card Test"
Else
MsgBox "Your system can not play sound Files.", _
vbInformation, "Sound Card Test"
End If

No license: PDF produced by PStill (c) F. Siegert - http://www.this.net/~frank/pstill.html