create pdf thumbnail image c# : Add signature field to pdf Library software class winforms web page ajax AccessTutorial9-part968

Fig 5.
Edit or create
connected records through a
Bind to one
old Stay
Bind to old Guest
+ new Stay
Bind to new Guest
+ new Stay
Private Sub Form_Load()
' OpenArgs: -1: existing stay, filter set; 0: new guest; >0: new stay for existing guest, OpenArgs = guestID.
' Assumed CRUD settings: AllowEdits = True, DataEntry = False.
If OpenArgs >= 0 Then 
’ NewStay or NewGuest
Me.AllowAdditions = True
Me.DataEntry = True 
’ Don't see existing records
If OpenArgs = 0 Then 
’ NewGuest
Me!name = " ”
’ Set blank name to create a dummy guest. Access makes guestID
Me.[tblStay.guestID] = Me.[tblGuest.guestID] 
' Create a stay record. Access makes stayID
Me!name = Null
‘ Remove the blank name
‘ New stay for existing guest
Me.[tblStay.guestID] = OpenArgs  ‘Create a stay record. Access makes stayID
End If
End If
Me.AllowAdditions = False 
' All cases. No more additions
End Sub
Private Sub cmdNewGuest_Click()
DoCmd.OpenForm "frmStay", , , , , , 0
End Sub
Private Sub cmdNewStay_Click()
If Me.subStayList.Form.CurrentRecord = 0 Then  'No guest selected
DoCmd.OpenForm "frmStay", , , , , , 0
DoCmd.OpenForm "frmStay", , , , , , Me.subStayList.Form!guestID
End If
End Sub
Private Sub cmdShowStay_Click()
If Me.subStayList.Form.stayID > 0 Then 'Stay selected
DoCmd.OpenForm "frmStay", , , "stayID=" & Me.subStayList.Form!stayID, , , -1
End If
End Sub
Becomes the Filter
Finally we set AllowAdditions to False so that Page-
Down will not bring the user to a new empty record. 
OpenArgs is > 0: The guest exists already and Open-
Args is the guestID. First we allow additions and data 
entry. Data entry makes the current record a new 
"blank line". Next we set the foreign key of the stay 
record. This creates the stay record and links it to the 
guest. Access automatically joins it to the guest, so the 
guest data is now in the "blank line". 
Finally we set AllowAdditions to False to prevent fur-
ther record creation. 
The strange [  ] 
The program has a strange use of square parentheses. 
The reason is that the query behind the form has two 
guestID fields, one from each table. None of these are 
visible on the form, but they are available anyway as 
fields. The SQL-engine has given them the names  
5. Access through Visual Basic 
Add signature field to pdf - C# PDF Field Edit Library: insert, delete, update pdf form field in, ASP.NET, MVC, Ajax, WPF
Online C# Tutorial to Insert, Delete and Update Fields in PDF Document
add fields to pdf form; adding image to pdf form
Add signature field to pdf - VB.NET PDF Field Edit library: insert, delete, update pdf form field in, ASP.NET, MVC, Ajax, WPF
How to Insert, Delete and Update Fields in PDF Document with VB.NET Demo Code
pdf fillable form creator; build pdf forms
In SQL-view of the query they sometimes appear as 
However, when we address them in Visual Basic, we 
have to write 
Confusing? Yes, very, but Visual Basic needs these 
parentheses to indicate that tblStay.guestID  is one sin-
gle name.  
Note that we use the dot-operator after Me to address 
these fields. This allows us to use Ctrl+J to get a list of 
all properties and fields. You will find tblStay.guestID  
on the list, but you have to add the parenthesis your-
See the mechanisms live 
The Load event is the more interesting part of the solu-
tion. You may try it out in this way: 
1. Open frmStay and use the property sheet to give it a 
Load-event procedure. Type in the procedure from 
Figure 5.5C. Set a breakpoint at the first line. Close 
the form. 
2.  Open the Immediate window with Ctrl+G. Now 
simulate the NewStay button with this command in 
the Immediate window: 
docmd.OpenForm "frmstaymono",acFormDS , , , , ,2
3. The command starts opening the form and will 
show it in Datasheet mode. The Load procedure 
stops at the breakpoint, but the form is not yet visi-
ble. Make it visible with this command in the Im-
mediate window: 
me.Visible = True
4. The form should now appear in Datasheet mode. 
Step through the Load procedure with F8 to see 
what happens to the datasheet. When the procedure 
sets DataEntry, the datasheet should reduce to a 
blank line. When the procedure stores OpenArgs in 
the record, the guest data should appear in the blank 
You may try out the other cases in the same manner. 
5.5.5 Dialog boxes (modal dialog) 
Many of the boxes you see in Windows applications 
are dialog boxes. When a dialog box pops up, you have 
to fill in what is asked for and then close the box. You 
cannot look around at other windows until you have 
closed that dialog box. This is a modal dialog. One 
example is a message box. Another example is the Op-
tions box that many applications have. 
A dialog box is a special case of a Form. Apart from 
being modal, it has narrow borders, and the user cannot 
drag the borders to change the size of the box.  
There are several ways to open a dialog box: 
Message box 
Use the MsgBox function, for instance: 
MsgBox "the message", vbYesNo+vbQuestion
This opens a box with the message and shows a num-
ber of buttons (Yes and No in the example), plus an 
icon (a question bubble in the example). You can check 
the return value to see which button the user chose, for 
If MsgBox("the message", vbYesNo) = vbYes Then . . . 
Notice that we write MsgBox with parameters in a pa-
renthesis when we need to look at the return value. Use 
F1 to see the help information about MsgBox. There is 
an excellent explanation of all the details. 
Multi-line message 
When the message is more than one line, you have to 
compose it in this way:  
MsgBox "line 1" & Chr(10) & "line 2", vbYesNo 
Chr(10) is the new-line character, or more precisely a 
text consisting of the new-line character. In Visual 
Basic you cannot write a new-line character inside the 
text string itself. 
Use OpenForm with acDialog 
Make a Form in the usual way with data fields, 
buttons, etc. Open it with DoCmd and the acDia-
log parameter, for instance: 
DoCmd.OpenForm "form name", , , , ,acDialog
OpenForm sets the right border on the form and makes 
it modal. There is no return value. The form has to 
store results somewhere, for instance in global vari-
ables or in the database. 
Set the dialog properties of the Form 
The last way to make a dialog box is to set the Form 
properties yourself: 
On the Format tab for the form, set Border Style to 
Dialog. The effect of this is that the border of the 
Form is narrow and the user cannot drag the bor-
ders to change the size. 
On the Other tab set PopUp to Yes. When you 
open the form, it stays on top of other forms. It is 
not modal however. You can work with other 
forms while the dialog form is open. 
On the Other tab set Modal to Yes. In Access 97 
this makes the form modal. When open, the user 
cannot work with other forms. In Access 2000 and 
2003 the Modal property has no effect at all (al-
though the help text says it has). 
You may then open the form with DoCmd.OpenForm 
without specifying the acDialog parameter. 
5. Access through Visual Basic 
C# PDF Digital Signature Library: add, remove, update PDF digital
things. Add a signature or an empty signature field in any PDF file page. Search unsigned signature field in PDF document. Prepare
change font pdf fillable form; change font in pdf fillable form
VB.NET PDF Digital Signature Library: add, remove, update PDF
things. Add a signature or an empty signature field in any PDF file page. Search unsigned signature field in PDF document. Prepare
create a pdf form to fill out; adding form fields to pdf
5.5.6 Controlling record selection 
A bound form is connected to a record set (a database 
table or a query). At any given time one of the records 
is the current record. When you see the recordset in 
datasheet mode, the current record is marked with an 
arrow. When you see it in Form mode (normal mode) 
only the current record is visible. 
In many cases we want the program to move to another 
current record. Figure 5.5D illustrates some ways to do 
this. The Form is bound to tblGuest and shows one 
guest at a time. At the top of the form we have added 
some navigation controls. One is a combo box where 
the user can select a guestID from a drop down list. 
The list shows guestID as well as guest name. There 
are also two buttons that navigate to the previous and 
the next record. 
Move and see one record 
First we will look at the situation where we want to 
navigate to the guest selected by the guestID. The user 
should not see other guests than this one. 
The solution is to set the filter properties of the Form 
when the user selects something with the combo box. 
The AfterUpdate procedure for the combo box could 
look like this (top left of the figure): 
Private Sub cboGuestID_AfterUpdate() 
Me.FilterOn = True 
Me.Filter = "guestID=" & Me.cboGuestID 
End Sub 
The filter is a condition to be used by Access in a hid-
den WHERE clause when retrieving the Form's record 
set. It is a computed text. For instance, when the user 
selects guest 2, the text becomes 
Scroll to a record 
Next we will look at the situation where we want to 
navigate to the guest, but the user should be allowed to 
move back and forth from there. In this case we cannot 
use the filter. The solution is to work directly on the 
Form's recordset. The AfterUpdate procedure for the 
combo box could look like this (top right of the figure): 
Private Sub cboGuestID_AfterUpdate() 
Me.Recordset.FindFirst ("guestID=" & Me.cboGuestID) 
' Might also be written without a parenthesis 
End Sub 
The recordset has a FindFirst function with a condition 
as a parameter. It finds the first record in the set that 
matches this condition. Then it makes this record the 
current record. 
The two arrow buttons also work directly on the re-
cordset. They use the MovePrevious and MoveNext 
functions for moving current to the previous or next 
record. Actually, these buttons do the same as Page-
Down and PageUp. Section 5.6 explains more about 
Fig 5.
Controlling record selection
Scroll to
See only
Scroll to
Scroll to
5. Access through Visual Basic 
C# PDF insert image Library: insert images into PDF in, ASP
into PDF form field. Access to freeware download and online C#.NET class source code. How to insert and add image, picture, digital photo, scanned signature or
pdf editable fields; can reader edit pdf forms
VB.NET PDF insert image library: insert images into PDF in
Import graphic picture, digital photo, signature and logo into PDF Add images to any selected PDF page in VB.NET. Insert images into PDF form field in VB.NET.
add image to pdf form; add form fields to pdf without acrobat
5.5.7 Column order, column hidden, etc. 
SelTop (attribute, read and write). The number of the 
top row in the selected area. The first record in the 
table behind the form is row 1, the next 2, etc. 
Notice that the first records may be scrolled out of 
When a form (or subform) is shown in table view, the 
user can rearrange and hide columns. To rearrange 
columns, the user selects a column and drags it to a 
new position. To hide it, he selects the column and uses 
Format -> Hide columns. 
SelHeight (attribute, read and write). The number of 
rows selected. If SelHeight is zero, no area is 
selected, but the record is still selected. 
The program may need to know these settings or it may 
need to change them. 
SelWidth (attribute, read and write). The number of 
columns selected. Hidden columns are included. If 
SelWidth is zero, no area is selected, but the record 
is still selected. 
Remember that each column corresponds to a control 
on the form (section 3.2.1). As an example, column 2 
on Figure 5.5E corresponds to a text box control bound 
to the description field of the table. The column 
heading (Class) is the label associated with the text box 
(or the name of the text box if there is no label). 
SelLeft (attribute, read and write). The number of the 
left column in the selected area. In Access 97 the 
columns are numbered from 1 and up. In Access 
2000 they are numbered from 2 and up when an 
area is selected. 
The program can detect and set column order, etc. 
through these three attributes of the controls: 
ColumnHidden (attribute, read and write). True or 
Beware: The strange change from 1-based numbering 
to 2-based numbering must have puzzled quite a 
number of developers. Obviously it is an error. The 
situation is even stranger when only a record is 
selected, but not an area. In this case you set SelLeft 
with 1-based numbers, but retrieve it with 2-based 
ColumnOrder (attribute, read and write). The number 
of the column. Columns are counted from 1 and up 
in the sequence that the user sees. However, hidden 
columns are included in the counts. 
ColumnWidth (attribute, read and write). The width of 
the column measured in twips (see section 5.5.13). 
If the program sets ColumnWidth to -2, the width is 
adjusted to fit the data in the column. Width = -1 
means a default width. Width = 0 makes the column 
hidden (and you have to explicitly unhide it - 
setting the width is not enough). 
If columns have been reordered, their numbers follow 
what the user sees. However, hidden columns are 
included in the counts. 
Keeping the area selected 
In the hotel system, the user selects rooms through a 
form like Figure 5.5E. In principle this is easy. 
However, as soon as focus moves from the subform to 
the main form, Access removes the area selection. This 
happens for instance when the user clicks a button in 
the main form. The event procedure behind the button 
wouldn't even know which area was selected. 
Example: Suppose the program needs to change the 
column width of Class (the description control) in 
Figure 5.5E. An event procedure on the main form 
could do it in this way: 
Me.subRoomGrid.Form ! description.ColumnWidth = 1500 
The program has to compensate for Access's strange 
behavior. When focus leaves the subform, the Exit 
event procedure saves the area size attributes before 
Access removes the area selection. When some control 
on the main form gets the focus, its event procedure 
sets them back. There is no common event on the main 
form that can do it, so each control has to act. 
5.5.8 Area selection, SelTop, etc. 
When a subform is shown in table view, the user can 
select a rectangular area of the subform. Figure 5.5E 
shows an example where the user has selected column 
3 and 4 from the fourth and fifth row (see more about 
the room grid in sections 7.4 and 7.5).  
Main form 
The program can detect which area the user has 
selected. The figure shows how we can try it out 
through the Immediate window (Ctrl+G). We address 
the first open form, its subform control, and the 
subform it is bound to. When asking for the value of 
SelTop, we get 4 because the top row in the selected 
area is record number 4. 
In the main form, declare these variables: 
Public aWidth As Long, aHeight As Long, aLeft As Long 
' These variables keep track of the current area size. aleft is 
the correct column number, not the Access2000 distortion. 
SelTop is not saved since current record always is the top.
The following procedures on the main form update and 
use these variables (shown in the Access 2000 version).
As the figure shows, we can also change the area size 
and location. We have changed the area width to 2. The 
following four attributes of the form control the area. 
5. Access through Visual Basic 
How to C#: Basic SDK Concept of XDoc.PDF for .NET
You may add PDF document protection functionality into your C# program. To be specific, you can edit PDF password and digital signature, and set PDF file
pdf form save in reader; create a form in pdf
VB.NET PDF: Basic SDK Concept of XDoc.PDF
You may add PDF document protection functionality into your VB.NET program. To be specific, you can edit PDF password and digital signature, and set PDF file
create a fillable pdf form from a pdf; pdf form creation
Fig 5.5E  Selected area, SelLeft, SelTop, SelWidth, SelHeight
SelTop = 4
SelLeft = 4 (3 in Access 97)
SelHeight = 2
SelWidth = 2
ColumnHidden = False
ColumnOrder = 2
ColumnWidth = 920 (twips)
Private Sub subRoomGrid_Exit(Cancel As Integer) 
' This event happens when focus moves from the subform to 
the main form. Save the current area size before Access 
removes it. 
aWidth = subRoomGrid.Form.SelWidth 
aHeight = subRoomGrid.Form.SelHeight 
aLeft = subRoomGrid.Form.SelLeft - 1  
' -1 to correct for the Access2000 error 
End Sub 
Private Sub resetSelection() ' Shared procedure 
subRoomGrid.Form.SelWidth = aWidth 
subRoomGrid.Form.SelHeight = aHeight 
' Make sure to set Width and Height before setting Left 
subRoomGrid.Form.SelLeft = aLeft + 1 
' +1 to compensate for the Access 2000 error. 
End Sub 
Private Sub txt . . ._GotFocus() 
' Reset the area size when a main-form control gets the focus. 
Call resetSelection() 
End Sub 
. . . (GotFocus for other controls) 
Private Sub subRoomGrid_Enter() 
' When focus moves from the main form to the subform. 
Access removes the area selection just before this event 
(Access 2000 only). So reset the area size 
Call resetSelection() 
End Sub 
User actions in the subform 
The solution above works fine when an area is selected 
in the subform. For the hotel system, the program 
selects a suitable area corresponding to a free room. If 
the user clicks Check in, for instance, everything is 
If the user expands the area by means of Shift + arrow 
key, everything is fine too. 
But what happens if the user clicks a cell to move the 
area to another room? Access will remove the area 
selection and the user will just see the cursor flashing 
in the cell he clicked. The user may drag the cursor to 
select an area, or use Shift + click, but it is not 
intuitive. The program should make sure that a suitable 
area is selected at any time.  
One problem is that as soon as the user clicks a cell, 
Access removes the area selection before calling any 
event procedure. The program cannot catch the old 
selection at this point. 
The solution is to let the program act at MouseUp and 
KeyUp. There are two situations: 
An area is selected: This happens if the user expanded 
the area with Shift + arrow key, or if the user 
dragged an area with the mouse. The program 
should save the area size. 
No area is selected: This happens if the user has 
clicked a cell or moved to the cell with an arrow 
key. The program should reset the area size so that 
the selected cell becomes the top left of the area. 
Each single control on the subform has to react this 
way. Although there is a Form_MouseUp and a 
Form_KeyUp, they don't respond to clicks inside the 
grid. They respond only to clicks on the grid border, 
i.e. the headings and the record selectors.  
In the hotel system, fsubRoomGrid has one control for 
each column in the grid. The names of these controls 
are cbo1, cbo2, etc. (see section 7.5 for details). So we 
need these procedures on the subform:
5. Access through Visual Basic 
VB.NET PDF Library SDK to view, edit, convert, process PDF file
NET program. Password, digital signature and PDF text, image and page redaction will be used and customized. PDF Annotation Edit.
pdf add signature field; create a fillable pdf form in word
C# Create PDF Library SDK to convert PDF from other file formats
can add some additional information to generated PDF file. What's more, you can also protect created PDF file by adding digital signature (watermark) on PDF
add image field to pdf form; pdf create fillable form
Public Sub saveSelection() ' Save the current area selection 
after key up or mouse up, or reset it after clicks, etc. 
If SelHeight = 0 Then ' No area selected. Reset area. 
' Check first that the new area is within the grid. 
SelHeight = Parent.aHeight 
SelWidth = Parent.aWidth 
End If 
Parent.aWidth = SelWidth 
Parent.aHeight = SelHeight 
Parent.aLeft = SelLeft - 1 ' Access 2000 compensation 
End Sub  
Private Sub cbo1_KeyUp(KeyCode As Integer, Shift As 
Call saveSelection() 
End Sub 
Private Sub cbo1_MouseUp(Button As Integer, Shift As 
Integer, X As Single, Y As Single) 
Call saveSelection() 
End Sub 
. . . (KeyUp and MouseUp for the other controls) 
Notice that when saveSelection resets the area size, it 
should first check that the area is within the grid. The 
user may for instance have clicked at the far right with 
a wide area selected. 
Also notice how the program uses Parent to address 
the saved area size. 
This solution works okay. The user can use mouse or 
keyboard to move an area around in the grid. However, 
the screen flickers. It is clearly visible that Access 
removes the area selection before the program resets it. 
Below we explain how to deal with it. 
By the way, since the subform saves the area size on its 
own, there is no reason that the subform control on the 
main form saves it too. So the event procedure 
may be removed. 
Clicking in the grid border 
If the user clicks in the grid header, Access selects the 
entire column. If he clicks in the record selector in the 
left border, Access removes the area selection and 
selects the record the user clicked. In both cases we 
want the old area selection to be reset. 
The place to deal with this is the MouseUp procedure 
for the subform, Form_MouseUp. It should set the area 
size in the same way as saveSelection(). However, here 
is another Access error. Access refuses to change 
SelHeight etc. The work-around is to start out with 
SelHeight = 0 
This is necessary even if SelHeight is 0 already (at 
least in Access 2000). 
Avoiding flicker 
It is possible to avoid the flicker by letting the program 
take action before Access removes the area selection. 
Two event procedures on the subform are involved: 
Form_KeyDown(KeyCode As Integer, Shift As Integer) 
This is the Key preview procedure (see section 
5.5.9). It must handle arrow keys and tabs to the left 
or right by setting SelLeft and aLeft - after 
checking that the area is inside the grid, of course. 
Setting SelLeft causes Access to not remove the 
Is called when the user uses arrow up or down, or 
clicks in a new row. It is called before MouseUp or 
KeyUp occurs. Sets SelWidth and SelHeight early 
on to avoid flicker. If Current doesn't do anything, 
the area will be set at MouseUp or KeyUp, but 
Access will remove the area in between, causing 
Getting the selected data 
When a function acts on the selected area, it needs to 
know which data the area contains. If the area is only 
one row high, it can directly address the controls in the 
current subform record. In general, however, it needs to 
retrieve data for several rows. The data has to be 
retrieved from the recordset bound to the form. 
One solution is to retrieve a number of rows from the 
recordset by means of the GetRows property. Here is 
an outline of the solution for the hotel case. 
In the main form, declare a variant array: 
Dim A() 
' The A-array will receive a number of records
The basic idea is to use this piece of code inside the 
button event procedure: 
A = Me.subRoomGrid.Form. Recordset. GetRows(n) 
We address the subform (fsubRoomGrid) in the usual 
way. The property Recordset is the recordset bound to 
the subform. The property GetRows copies n records 
from the current record and on. It stores the copies in 
the array A. 
While the area size and position is 1-based (and 2-
based), the A array is zero-based. As a result, A will 
hold this: 
A(0, 0):   The first field in the first record. 
A(0, 1):   The first field in the second record. 
A(1, 0):   The second field in the first record. 
. . . 
and so on. 
As a side effect, GetRows moves the current record 
forward so that it points to the row after the selection. 
To avoid this, use the built-in clone of the recordset, 
make it point to the current record, and then copy the 
rows through it: 
Me.subRoomGrid.Form.RecordsetClone.Bookmark = _ 
A = Me.subRoomGrid.Form.RecordsetClone.GetRows(n) 
Section 5.6 explains more on this. 
5. Access through Visual Basic 
C# PDF Convert to Images SDK: Convert PDF to png, gif images in C#
image files including all PDF contents, like watermark and signature in .NET. C#.NET DLLs Solution for Converting Images to PDF in C# Add necessary references:
add editable fields to pdf; convert word document to editable pdf form
C# PDF remove image library: remove, delete images from PDF in C#.
Auto Fill-in Field Data. Field: Insert, Delete, Update Field. vector image, graphic picture, digital photo, scanned signature, logo, etc. Add necessary references
add print button to pdf form; change font in pdf form
Dealing with re-ordered columns 
A small problem remains. If the user has re-ordered the 
columns in the datasheet, the order of the record fields 
and the datasheet columns don't match.  
To overcome this, use the ColumnOrder property of the 
subform controls. This property gives you the column 
number (1-based) for the control. The ControlSource 
property of the control gives you the field name that 
the control is bound to. See section 5.5.7 for more on 
5.5.9 Key preview 
When the user clicks something on the keyboard, the 
control in focus will receive several key events. How-
ever, the Form can have a look at the key events before 
the control gets the events. This is useful when no spe-
cific control has to take action.  
One example is function keys. For instance we may 
want F2 to mean Reset Criteria no matter where in the 
form the cursor is. For the Find Guest window it can be 
done in this way: 
On frmFindStay, open the Form-property box, se-
lect the Event tab and set Key Preview to Yes. 
Define an event procedure for the Form's KeyDown 
event. It should look like this: 
Private Sub Form_KeyDown (KeyCode As Integer, _ 
Shift As Integer) 
If KeyCode = vbKeyF2 Then 
Me.subStayList. Form. RecordSource = _ 
"select * from qryStayList; " 
Me.txtName = "" 
End If 
End Sub 
The KeyDown event has two parameters. KeyCode (an 
integer) shows which key is pressed. There are prede-
fined constants for the various keys. For instance 
vbKeyF2 means the F2 key, vbKeyA the A-key and 
vbKeyEscape the Esc key. 
If the procedure returns with KeyCode = 0, Access 
stops further processing of the KeyDown event. 
Shift (an integer) shows whether some of the shift keys 
were down at the same time (Shift, Alt, Ctrl). There are 
predefined constants for the shift keys: acShiftMask 
(=1), acCtrlMask (=2), acAltMask (=4). The program 
can for instance test whether Shift and Ctrl are down at 
the same time with this statement 
If Shift = acShiftMask + acCtrlMask Then . . . 
In the example above, the procedure tests whether the 
F2 key was used. In this case it sets record source to 
the full list of stays, and it sets the search criterion to an 
empty text. 
Parent form 
If you try it, you may notice that F2 works okay if the 
focus is in the main form, but not when it is in the sub-
form. You have to define key preview actions also for 
the subform if F2 is to work here too. However, from 
the subform you have to address the items in the main 
form slightly differently: 
' In the subform: 
Me. Parent. subStayList. Form. RecordSource = _ 
"select * from qryStayList; " 
Me. Parent. txtName = "" 
Me refers to the subform, and Me.Parent refers to the 
parent form, i.e. the master form. 
Return value 
At return from the preview procedure, Access contin-
ues processing the event. It sends it to the control in 
focus, or makes its own response to the event. In case 
of F2 the control ignores F2, but Access has a standard 
response: it toggles between selecting the entire field 
and just showing the simple cursor. 
The preview procedure can skip further processing by 
setting KeyCode=0 at return. In case of F2, the result 
would be that Access didn't toggle between field selec-
tion and the simple cursor. 
5.5.10 Error preview 
Access detects various kinds of user errors, for instance 
that the user enters a non-existing date, or the user 
forgets to select a related record where referential 
integrity is required. Access will show an error 
message that often is entirely gibberish to the user. 
When for instance the referential integrity is violated, 
Access will say: 
You cannot add or change a record because a related 
record is required in table 'tblGuest'
How can the program show a meaningful message 
instead? The BeforeUpdate procedure is intended to let 
the program check the data, but when Access detects 
the error it doesn't even call BeforeUpdate.  
However, the Form has an Error event procedure that 
can interfere before Access shows the error message to 
the user. To use it, define the event procedure for the 
Error event: 
Private Sub Form_Error(DataErr As Integer, _ 
Response As Integer) 
If DataErr = 3201 Then 
MsgBox "Select a guest", vbOKOnly + vbExclamation 
Response = acDataErrContinue 
End If 
End Sub 
The Error event has two parameters. DataErr is an ID 
for the error. Response tells Access what to do at re-
turn. When Response = acDataErrContinue, Access 
will not show its own error message. When Response = 
5. Access through Visual Basic 
acDataErrDisplay (the default), Access will show its 
own error message.  
In the example, the event handler tests whether the 
error is "You cannot add or change . . ." (ID 3201). In 
this case it shows a message box with a more user-
oriented text. Then it tells Access to keep its mouth 
shut about the error. 
At return, Access keeps the focus on the Form where 
the error occurred, so that the user can correct the data - 
or use Esc to undo changes. This happens no matter 
whether Response is acDataErrContinue or not. 
The main problem when using the Error event is to find 
out what the error ID means. I have not been able to 
find a list of the codes.  
Don't confuse the Error event with VBA's handling of 
program errors. The Error event handles errors caused 
by the user's actions. VBA handles cases where the 
program tries to divide by zero, access a non-existing 
object, etc. VBA has statements such as On Error 
GoTo x to let the programmer deal with the error. See 
section 6.1 for a detailed explanation of this kind of 
error handling. 
5.5.11 Timer and loop breaking 
A Form responds to user actions such as editing a field 
or clicking a button. Sometimes we want the Form to 
act on its own. It might for instance update a list of data 
every minute. This would be useful in a multi-user 
application where several people update the database. 
Timer handling 
Start the timer by setting the Form's TimerInterval, for 
instance in the Form's Load event procedure: 
Me.TimerInterval = 60 000 ' One minute is 60 000 ms. 
Next define an event procedure for the Timer event: 
Private Sub Form_Timer() 
End Sub 
When the Form opens, it sets the timer interval to one 
minute and waits for some event. The event may be the 
user doing something or one minute having passed. 
When the minute has passed, Access calls the Timer 
event procedure, which recalculates all bound data in 
the form. 
The next timer event will occur one minute after the 
first timer event, even if the recalculation has taken 
several seconds. How would the program stop the timer 
events? Set the timer interval to 0: 
Me.TimerInterval = 0 ' Stop the timer. 
Let user break a loop - SendKeys 
Assume the user should be allowed to stop the periodic 
updating. It is easy. Provide a button labeled Stop. Let 
its click procedure set the timer interval to 0. 
This works because when the Form waits for the timer, 
it also responds to user events. It will handle the events 
in the sequence they occur. 
Now imagine that the Form runs a long calculation or 
scans the Internet for hours. It runs in a loop and it 
doesn't wait for events at any point. How can we allow 
the user to break the loop? If we provide a Stop button 
as above, the user may click it, but the Form doesn't 
listen to events. However, Access stores the event in a 
queue for later processing. 
The user could use Ctrl+Break. It will in a very rough 
way stop the program, probably in the middle of 
something, so that data is left in an inconsistent state. 
Actually, Ctrl+Break is the only way you can stop a 
very long-lasting database query. In this case the 
database engine takes care that the database ends up in 
a consistent state. For a long calculation in our own 
program, we need a structured way to stop. 
The trick is to let the program use SendKeys every now 
and then to generate an event and wait for the event to 
be processed. This allows Access to process all events 
in the queue. It also allows Access to update the screen, 
for instance to show controls that the program has 
changed during the calculation.  
Here is an outline of such a program. We declare a 
module-level Boolean in the Form, which becomes true 
when the user clicks the Stop button: 
Dim bStop As Boolean
When Access responds to the click, this event 
procedure sets bStop: 
Private Sub cmdStop_Click() 
bStop = True 
End Sub 
A long computation runs in a loop until the computa-
tion is finished. Once in the loop it displays the 
progress of the computation by storing a result in a text 
box control. It could look like this: 
Private Sub cmdCompute_Click() 
' Plain loop
While . . . ' Until calculation is finished 
. . . ' One step in a long calculation 
txt . . . = . . . ' Display progress 
End Sub
In order to respond to the stop, we modify the loop so 
that it runs until bStop is True or the computation is 
finished. In addition we use the SendKey operation to 
send a character to Access and wait for Access to 
5. Access through Visual Basic 
process it - and process other events too. Here is the 
new program: 
Private Sub cmdCompute_Click() 
' Test once in loop
bStop = False 
While Not bStop And . . . ' Until stop or calculation finished 
. . . ' One step in a long calculation 
txt . . . = . . . ' Display progress 
SendKeys "{home}", True ' Type a character and wait. 
' Allows Access to handle all events and  
' update the screen. 
End Sub
SendKeys has two parameters. The first is a string of 
characters. They are sent to Access as if the user typed 
them. (Or rather, to the program that has the focus on 
the screen. You might for instance send characters to 
MS Word in this way.) The second is a Boolean. If it is 
True, SendKeys waits for Access to process all pending 
events and update the screen. The string can hold 
several characters, e.g. "abc{home}+-". Special 
characters are shown as a mnemonic in { }. We have 
chosen {home} since it is a rather harmless character in 
the user dialog. 
SendKeys takes around 1 ms on a plain computer. In 
the IT world this is quite slow. If the calculation step 
for instance takes 0.01 ms, the program suddenly 
becomes 100 times slower. 
One solution is to let the program listen and update the 
screen only once a second. In the procedure below, we 
use the Time() function to find out when a second has 
passed. It returns the number of seconds since 
midnight, with fractional seconds too. The Time 
function itself takes around 0.0005 ms, so it will rarely 
slow down the loop significantly. 
Private Sub cmdCompute_Click() 
' Test once a second
Dim startTime As Double 
bStop = False 
startTime = Timer() +1 
While Not bStop And . . . ' Until stop or calculation finished 
. . . ' One step in a long calculation 
If Timer() > startTime Then ' A second has passed  
txt . . .  = . . . ' Display progress 
startTime = startTime + 1 ' Next in one second 
SendKeys "{home}", True ' "Type" a character and wait. 
' Allows Access to handle all events and  
' update the screen. 
End If 
End Sub
5.5.12 Multiple form instances 
When we open a form with DoCmd.OpenForm, we get 
only one instance of the form. If we try to open it 
again, nothing happens. 
In order to open multiple instances, we need to use the 
basic VBA mechanism of creating an object. Here is a 
program piece that opens two instances of frmStay: 
Dim stay1 As Form, stay2 As Form 
' References to open forms 
. . . 
Set stay1 = New Form_frmStay 
Call stay1.Init(740) 
stay1.Visible = True 
Set stay2 = New Form_frmStay 
Call stay2.Init(753) 
stay2.Visible = True 
The first line declares two references to an open form, 
stay1 and stay2. The declarations may be in a global 
module that remains open as long as Access is open. 
In an event procedure somewhere else, the statement 
Set stay1 = . . .
creates a new object of the class 
. The reference stay1 will now refer to this 
object. During the creation, the form object receives the 
usual open-events: Open, Load, Activate. The form is 
still invisible. 
When we open a form in this way, we cannot give it 
OpenArgs like those we use with DoCmd.OpenForm. 
The form has a property called OpenArgs, but it is 
ReadOnly so we cannot store anything in it. For this 
reason we have written a procedure for initializing the 
form, the procedure Init. We call it with a parameter 
that tells it to open stay 740. The procedure will set the 
filter of the form to show this stay. 
Finally, we set the Visible attribute of the form to True. 
This generates the Current event and makes the form 
Then we repeat the whole thing using stay2 instead of 
stay1. The result is that one more form instance opens, 
this time showing stay 753. 
Closing the forms 
Usually the form closes itself, for instance when the 
user clicks the close button. Access automatically sets 
references to it to Nothing. We may also close the form 
by setting references to it to Nothing, like this: 
Set stay2 = Nothing 
The reason we have stay1 and stay2 in a global module 
is that they have to be there all the time. If we made 
them local variables in a procedure, the opened forms 
would close as soon as the procedure returned. See 
more about variables and their life-time in section 6.2. 
Handling many open forms 
When we need many open instances, we have to make 
an array of references. A click event could then open a 
new instance in this way: 
Dim stay(1 To 10) 
. . . 
Private Sub cmdOpenStay_Click( ) 
' Find an unused reference, let it be j 
Set stay(j) = New Form_frmStay 
Call stay(j).Init( . . . ) 
stay(j).Visible = True 
End Sub
5. Access through Visual Basic 
5.5.13 Resize 
When the user drags the border of a form, Access calls 
the Resize event procedure of the Form. The procedure 
is called for every few pixels the user drags, depending 
on how fast he drags. Access also calls the Resize pro-
cedure once during open. 
If the program doesn't do anything at these events, the 
user will just see a larger or smaller gray area. Usually 
the user expects that the controls somehow adjust to the 
new size, for instance that a subform area expands or 
The way to handle this is to let the Resize procedure 
recompute sizes and positions for the controls. Figure 
5.5F shows a simple example. The form has a couple 
of controls, one of them being a text box (txtTest) and 
another a subform (subTest). As the user drags the 
right border, these controls adjust their width so that 
they keep the same distance from the right border of 
the form. Care must be taken when a control becomes 
so narrow that it cannot keep its distance from the bor-
der. Trying to set its width to a negative number will 
cause a run-time error - and the mouse gets trapped 
inside Access! On the lower version of the form, the 
text box has width zero, and as a result the controls 
don't adjust their width anymore. 
At the time Resize is called, the situation is as follows: 
me.WindowWidth: The new width of the form, in-
cluding the border. 
me.WindowHeight: The new height of the form, in-
cluding the border. 
me.WindowTop: The distance between the form and 
the top of the surrounding Access window. 
(Access 2003 only). 
me.WindowLeft: The distance between the form and 
the left side of the surrounding Access window. 
(Access 2003 only). 
The figure shows the Resize procedure. It declares two 
important variables: 
formWidth: The previous width of the form. When the 
form opens, the variable has its default value, 
zero. It is declared as Static, meaning that it sur-
vives from one call of Resize to the next. 
dif:  The difference to add to the control widths. Ini-
tially, when the form opens, dif becomes zero. 
Later, it is the difference between the new form 
width and the previous form width. 
The procedure computes the new width of txtTest, the 
smallest of the controls. When the new width is larger 
than zero, it adjusts the widths of txtTest and subTest. 
It also saves the new form width in order to calculate 
future changes relative to this. 
For subforms it may be necessary to change the width 
not only of the subform control, but also of the subform 
itself. See below. 
Size unit 
All positions and sizes are computed in twips.  
One twip is 1/20 typographical point = 
1/567 cm = 
1/1440 inch 
Resizing a form 
It is easy to change the width of a control, so it is 
tempting to change the width of a form in a similar 
me.WindowWidth = me.WindowWidth + dif 
However, this works in none of the Access versions. 
You have to use this statement to change position or 
size of a form: 
DoCmd.MoveSize right, down, width, height 
right:  Distance between form and the left side 
of the surrounding Access window. (The 
same as the property WindowLeft.) 
down:  Distance between form and the top of the 
surrounding Access window. (The same 
as the property WindowTop.) 
width:  The width of the form including the 
frame. (The same as the property Win-
height:  The height of the form including the 
frame. (The same as the property Win-
In order to increase the width of the form by dif, you 
have to set the form in focus and use 
DoCmd.MoveSize  , , oldWidth + dif 
When you do this, the form receives a Resize event. 
Resizing a subform 
Resizing with DoCmd works fine for a main form, but 
not for a subform. You cannot bring the subform in 
focus. However, it doesn't matter because an open sub-
form automatically has a width that matches the sub-
form control. 
When resizing the subform control, you just have to 
make the program resize and/or reposition the controls 
on the open subform too. You can do this from the 
Resize procedure in the main form. It soon becomes 
messy to let the main form know about the controls 
inside the subform. So a better approach is to call a 
procedure in the subform that resizes its own controls, 
in much the same way as its resize procedure would do 
When the subform is shown as a datasheet, it has no 
effect to adjust widths and heights of the controls. In-
5. Access through Visual Basic 
Documents you may be interested
Documents you may be interested