Access Tutorial
Access Tutorial
Access Tutorial
Soren Lauesen
E-mail: slauesen@itu.dk
Contents
1. The hotel system................................................... 4 5.2.2 Computed SQL and live search...............74
2. Creating a database ............................................. 6 5.2.3 Composite search criteria........................76
2.1 Create a database in Access ............................. 6 5.2.4 Event sequence for text box ....................78
2.2 Create more tables ......................................... 10 5.3 Visual Basic tools...........................................80
2.3 Create relationships ....................................... 12 5.4 Command buttons ..........................................84
2.4 Look-up fields, enumeration type .................. 14 5.5 Forms .............................................................86
2.5 Dealing with trees and networks.................... 16 5.5.1 Open, close, and events...........................86
3. Access-based user interfaces ............................. 18 5.5.2 CRUD control in Forms ..........................87
3.1 Forms and simple controls............................. 18 5.5.3 The OpenForm parameters......................89
3.1.1 Text box, label and command button...... 18 5.5.4 Multi-purpose forms (hotel system)........90
3.1.2 Adjusting the controls............................. 20 5.5.5 Dialog boxes (modal dialog)...................92
3.1.3 Cleaning up the form .............................. 20 5.5.6 Controlling record selection....................93
3.1.4 Shortcut keys for the user ....................... 22 5.5.7 Column order, column hidden, etc. .........94
3.1.5 Lines, checkbox, calendar....................... 22 5.5.8 Area selection, SelTop, etc......................94
3.1.6 Combo box - enumeration type .............. 24 5.5.9 Key preview ............................................97
3.1.7 Combo box - table look up ..................... 26 5.5.10 Error preview ........................................97
3.1.8 Control properties - text box................... 28 5.5.11 Timer and loop breaking .......................98
3.2 Subforms........................................................ 30 5.5.12 Multiple form instances.........................99
3.2.1 Subform in Datasheet view..................... 31 5.5.13 Resize..................................................100
3.2.2 Adjust the subform ................................. 34 5.6 Record sets (DAO).......................................102
3.2.3 Mockup subform..................................... 36 5.6.1 Programmed record updates..................102
3.2.4 Subform in Form view............................ 36 5.6.2 How the record set works......................104
3.2.5 Summary of subforms............................. 38 5.6.3 The bound record set in a Form ............106
3.2.6 Prefixes ................................................... 38 5.6.4 Record set properties, survey ................108
3.3 Bound, unbound and computed controls........ 40 5.7 Modules and menu functions .......................110
3.3.1 Showing subform fields in the main form42 5.7.1 Create a menu function .........................110
3.3.2 Variable colors - conditional formatting. 42 5.7.2 Define the menu item............................112
3.4 Tab controls and option groups...................... 44 5.7.3 Managing modules and class modules ..112
3.5 Menus ............................................................ 46 5.7.4 Global variables ....................................114
3.5.1 Create a new menu bar............................ 46 6. Visual Basic reference......................................116
3.5.2 Add commands to the menu list ............. 48 6.1 Statements ....................................................116
3.5.3 Attach the toolbar to a form.................... 48 6.2 Declarations .................................................120
3.5.4 Startup settings - hiding developer stuff . 48 6.3 Constants and addresses...............................122
3.6 Control tips, messages, mockup prints .......... 50 6.4 Operators and conversion functions .............124
4. Queries - computed tables................................. 52 6.5 Other functions.............................................128
4.1 Query: join two tables.................................... 52 6.6 Display formats and regional settings ..........132
4.2 SQL and how it works ................................... 54 7. Access and SQL................................................134
4.3 Outer join ....................................................... 56 7.1 Action queries - CRUD with SQL ...............134
4.4 Aggregate query - Group By.......................... 58 7.1.1 Temporary table for editing ..................134
4.5 Query a query, handling null values .............. 62 7.2 UNION query...............................................136
4.6 Query with user criteria ................................. 64 7.3 Subqueries (EXISTS, IN, ANY, ALL . . .) ..138
4.7 Bound main form and subform ...................... 66 7.4 Multiple join and matrix presentation ..........140
4.7.1 Editing a GROUP BY query................... 67 7.5 Dynamic matrix presentation .......................142
5. Access through Visual Basic ............................. 68 7.6 Crosstab and matrix presentation .................144
5.1 The objects in Access .................................... 68 8. References.........................................................148
5.2 Event procedures (for text box) ..................... 72 Index......................................................................149
5.2.1 More text box properties......................... 72
Printing instructions
Print on A4 paper with 2-sided printing so that text and associated figures are on
opposing pages.
2 Preface
Preface
This booklet shows how to construct a complex appli- 6. Visual Basic reference. A reference guide to the
cation in Microsoft Access (MS-Access). We assume Visual Basic language for Applications (VBA).
that the user interface has been designed already as a
paper-based mockup (a prototype). How to design a 7. Access and SQL. An overview of the remaining
good user interface is a separate story explained in parts of SQL, for instance how to update the
User Interface Design - a Software Engineering Per- database through SQL. We also explain how to
spective, by Soren Lauesen. generate matrices of data with dynamically chan-
ging headings.
After design, development continues with constructing
the database, constructing the user interface, binding Using the booklet for teaching
the user interface to the database, and finally develop We have experimented with using the booklet for
the program. This is what this booklet is about. teaching. First we tried to present part of the material
with a projector, then let the students try it out on their
The reason we illustrate the construction process with own, next present some more, etc. Although the
MS-Access is that it is a widely available tool. Any- students listened carefully, it turned out to be a waste
body who has Microsoft Office with MS-Word, also of time, partly because the students worked with vastly
has Access and the programming language Visual Ba- different pace.
sic behind Access.
Now we give a 15 minute introduction to the main
MS-Access is also a good illustration of many princi- parts of Access: the database window, the tables, the
ples that exist on other platforms too, for instance a re- forms - and how they relate to what they have learned
lational database, a Graphical User Interface (GUI), in user interface design. Then the students work on
event handling, and an object-oriented programming their own. We have instructors to help them out when
language. MS-Access contains all of these parts - co- they get stuck.
operating reasonably smoothly.
The hotel system
Organization of the booklet We have chosen to illustrate the construction process
The chapters in the booklet are organized like this: with a hotel example, because most people have an
idea what it is about, yet it is sufficiently complex to
1. An introduction to the hotel system that is used as show typical solutions in larger systems. Some of the
an example throughout the booklet. complexities are that a hotel has many types of rooms
at different prices; a guest can book several rooms,
2. Creating a database. Construct a database that cor- maybe in overlapping periods; a room may need reno-
responds to the data model behind the design. The vation or repair, making it unavailable for a period; the
user will only see the database indirectly - through hotel keeps track of regular guests and their visits over
the screens we construct. time.
3. Access-based user interfaces. Construct the screens Simplifications
and menus that the user will see. We follow the pa- However, we have simplified the system in many other
per-based mockup designed in User Interface De- ways to shorten the discussion. For instance we ignore
sign. You can use the result as a tool-based that in most hotels, rooms are not booked by room
mockup. number, but by room type; hotels usually overbook, i.e.
book more rooms than they have, expecting that some
4. Queries - computed tables. Connect the screens to customers will not turn up. We also ignore all the other
the database, usually by means of queries - com- aspects of operating a hotel, for instance keeping track
puted data tables. The result will be a partially of when rooms are cleaned and ready for the next
functional prototype. guest, purchasing goods, planning who is to be on duty
for the next weeks, payroll and general accounting. In
5. Access through Visual Basic. Program what the
spite of these simplifications, the example still shows
buttons and menus will do when the user activates
the structure of larger systems.
them. The result will be a fully functional prototype
and later the final system to be delivered to the On-line resources
customer. The first part of the chapter is tutorial - A demo-version of the hotel system, a VBA reference
mandatory reading if you want to work with Visual card, etc. are available from the authors's web site:
Basic and Access. The rest of the chapter is for www.itu.dk/people/slauesen. Comments are welcome.
looking up various subjects. We assume you know
a bit of programming already. Soren Lauesen, slauesen@itu.dk
Preface 3
1. The hotel system
In this booklet we illustrate MS-Access by means of a record services that the guest has received. The system
system for supporting a hotel reception. The system is uses the term Stay to mean a booking or a guest who
used as the main example in User Interface Design - a has checked in.
Software Engineering Perspective, by Soren Lauesen.
If you know the book, skip this section and go straight Breakfast list. The Breakfast screen shows the break-
to Chapter 2. fast servings for a specific date. It handles just two
kinds of breakfast: self-service breakfast in the restau-
Screens rant (buffet) and breakfast served in the room. The
The hotel system consists of the screens shown in Fig- waiter in the restaurant has a paper copy of the list and
ure 1A. records the servings here. Later the receptionist enters
the data through the Breakfast screen.
Find guest. The Find guest screen allows the recep-
tionist to find a guest or a booking in the database. The Service list. The Service list shows the price for each
receptionist may enter part of the guest name and click kind of service. Hotel management uses this list to
the Find guest button. The system then updates the change service prices or add new kinds of service.
lower part of the screen to show the guests or bookings
that match. The receptionist may also find the guest by Database
his phone number, room number, or stay number (also The system uses a database with several tables. They
called booking number). are shown as an E/R data model on Figure 1B.
The receptionist can select the guest from the list and tblGuest has a record for each guest with his address
click the buttons to see details of the booking or create and phone number.
a new booking for the guest.
tblStay has a record for each stay (booking or checked
Room Selection. The Room Selection screen gives an in) with a booking number (stay number) and the pay
overview of available rooms in a certain period. Avail- method.
ability is shown as IN when the room is occupied,
BOO when it is booked, etc. The receptionist may tblRoom has a record for each room in the hotel.
specify the period of interest and the type of room, then
click the Find room button. The system updates the ta- tblRoomState has a record for each date where a room
ble at the bottom of the screen to show the rooms of is occupied. It connects to the room occupied and the
interest. The receptionist can then choose a room and stay that occupies it. If the room is occupied for repair,
book it for the guest – or check a guest into the room. it doesn’t connect to a stay.
Stay. The Stay screen shows all the details of a book- tblRoomType has a record for each type of room (room
ing, for instance the guest and his address, the rooms class) with a short description of the room type, the
he has booked and the prices. When the guest is number of beds, and the prices.
checked in, the Stay screen also shows breakfast and
tblService has a record for each type of service with its
other services he has received. The system shows these
name and price per unit.
details on the Services tab. Here the receptionist can
tblServiceReceived has a record for each delivery of
service to a guest. It connects to the type of service and
to the stay where the service is charged (there is an in-
voice for each stay).
Fig 1B. Tables as E/R model
tblGuest
tblRoomState
tblRoom tblRoomType
As a systems developer you will design tables and user Create the database
windows. As a user you will enter data into the tables 1. Locate the Access program. Depending on the way
(usually through user windows) and get data out of the the system is set up, you may find it under Pro-
tables, for instance through the same windows or grams -> Microsoft Access or Programs -> Micro-
through printed reports. soft Office -> Microsoft Access.
2. In Access 97 and 2000: Open Access and ask for a
In Access it is very easy to switch between the devel- "blank" database.
oper role and the user role. As a developer you will In Access 2003: Open Access and click the New
typically design some tables, then switch to the user icon (under the File menu). Then click Blank da-
role to enter data into them, then switch back to the de- tabase in the help area to the far right.
veloper role to change the design, design more tables, 3. Access now asks where to store the new database.
etc. Access can to a large extent restructure the data Select the folder you want and give the database
that already is in the database so that it matches the the name hotel (or hotel.mdb).
new table design.
The screen now shows the database window. It should
look like Figure 2.1A. (In Access 97 it looks slightly
Create a
table
Design the table.
Shortcut: Ctrl+Enter
6 2. Creating a database
Fig 2.1B Define a table (design view)
Primary key.
Right click
Field properties.
Also use F1 - Help.
different). We have selected the Tables tab, but there of type Text, except the guestID which is of type
are no tables or other things in the database as yet. AutoNumber.
However, you see three icons that can create tables for
you. When you have created a table, it will appear in Note that although we say phone number and passport
the table window and you can then Open it and enter number, these fields are texts because the "numbers"
data into it, or you can Design it, i.e. change the defi- contain parentheses, dashes and maybe letters.
nition of it. (In Access 97 the database window looks
like a traditional tab form. There are no create-icons, When you have chosen a data type, you can choose a
but function buttons for the same purpose.) number of other field properties. They are in the lower
part of the window. On the figure you can see that the
Define a table name field is a text field with space for 50 characters.
4. Double click on Create table in Design view. You can also see that the user doesn't have to enter
anything in the name field (Required=No). You should
Now you get a window as shown on Figure 2.1B. Here change this to Yes since it doesn't make sense to have a
you define the fields (attributes) of the table. The list of guest without a name.
fields runs downwards with one line per field. Initially
there are only empty lines. The table hasn't got a name Try to use Access's help to find more information about
yet. Access asks for the name when you close the win- the data types and their properties. For instance, put the
dow. cursor in the Data Type of a field and click F1. Or
point at one of the properties and click F1.
The figure shows the finished guest table. You see the
field names to the left. In the middle column is the type Lookup Wizard is not a field type. If you select Lookup
of the field - Data Type. The figure shows all the pos- Wizard, it makes the field into a combo box where the
sible types as a combo box. The most important data user can select a value instead of typing it into the
types are Text, Number, Date/Time, and AutoNumber. field. We will look closer at Lookup in section 2.4.
An AutoNumber is a counter that Access increases for
each new record, so that it serves as a unique key. The Key fields
value is a Long Integer (32-bit integer). We explain Often you have to define a key field so that other tables
more about data types in the next section. can refer to this one. In our case, guestID must be the
key field:
5. Fill in all the field lines according to the attributes
in the guest table (see the figure). All the fields are 6. Right-click somewhere in the guestID line. Then
select Primary Key. Access now shows that the
field is the key.
2. Creating a database 7
You can remove the key property again by once more 12. The file may not be safe. Do you want to open it?
selecting Primary Key. If the key consists of more than Your database is safe, so answer Open.
one field, you first select all the fields by clicking on 13. Unsafe expressions are not blocked. Do you want
their left-hand marker with Ctrl down. Then select to block them? You want full freedom, so answer
Primary Key by right-clicking inside one of the field No.
lines. 14. Access warns you one more time whether you
want to open. Say Open or Yes. (In some versions
7. Close the window. Access asks you for the name the question is a very long text box, and you
of the table. Call it tblGuest. (The prefix tbl will cannot understand it. Say yes anyway.)
help you remember that it is a table. As the system
grows, there will be guest windows, guest buttons As an alternative, you may say yes to blocking the
and many other things. Without discipline on your unsafe expressions. This will save you some questions
part, it becomes a mess.) when you open the file in the future. However, some
installations don't allow you to block expressions.
If you have not defined a primary key, Access will
warn you and suggest that it makes one for you. Don't Note that Access 2003 shows that your database is in
let it - do it yourself. Or at least check what Access Access 2000 format. This is all right. It allows you to
makes in its excessive helpfulness. use it also from Access 2000. You can convert it to
other formats with Tools -> Database Utilities -> Con-
Enter data vert Database.
After these efforts, it is time to record some guests.
Fortunately it is easy: Undo. Use Esc to undo the changes you have made to
the current record.
8. Select the guest table in the database window.
Click Open or just use Enter. • The first Esc undoes changes to the field where the
cursor is.
Now the system shows the table in user mode (Da-
• The second Esc undoes all changes to the record
tasheet view) so that you can enter guest data.
where the cursor is.
9. Enter the guests shown on Figure 2.1C. You add a
new guest in the empty line of the table - the one As soon as you move the cursor to the next line,
marked with a star. Notice that as soon you start Access stores the record in the database and you cannot
entering something, the record indicator changes to make an automatic undo anymore. However, you can
a pencil and a new star line appears. The pencil manually edit the stored record. Notice that the pencil
shows that you are editing the record, and the disappears when the record is stored in the database.
record you see is not yet in the database.
Shortcut keys for data entry
On Figure 2.1C we originally entered a guest that got F2: Toggles between selecting the entire field and se-
guestID 4, later deleted this guest. Access will never lecting a data entry point.
reuse number 4 for a guest. Shift+F2: Opens a small window with space for the
entire field. Useful for entering long texts into a
Close and reopen the database field that is shown only partly in the table. How-
To feel confident with Access, it is a good idea to close ever, the text cannot be longer than you specified in
and open the database now. the table definition.
Alt+ArrowDown: Opens a combo box. Choose with
10. Close the large Access window. (Not the small the arrows and Enter.
database window inside the Access window.)
Shortcut keys for navigation
Notice that Access doesn't ask whether you want to Tab and Shift+Tab: Moves from field to field.
save changes. Access saves them all along, for instance Ctrl+Tab: Moves from one tab form to the next, for in-
when you define a table or when you enter a record in stance in the lower part of the table definition win-
the table. dow.
F6: Moves between upper and lower section of a win-
11. Find your database file (hotel.mdb) in the file fold- dow, for instance in the table definition window.
ers. Use Enter or double click to open it. Ctrl+Enter: Opens the table in design mode (in the da-
tabase window).
Access 2003 is very security concerned and asks you
several questions when you open the file. The dialog
See also shortcuts on the reference card
may vary from one installation to another, but is
something like this:
8 2. Creating a database
Fig 2.1C Enter data in user mode (datasheet view)
In database window:
Select table -> Open (or Enter)
Record
selector
Add
record F2 to select entire field Shift+F2 to see field in a separate window
2. Creating a database 9
2.2 Create more tables
You should now create the remaining tables for the tblRoomState:
hotel. The data model on Figure 2.2 shows the tables stayID and roomID are foreign keys. Ensure their types
we will use. To simplify your job, we have shown all match what they refer to. Notice that roomID refers
the keys, including the foreign keys and the artificial to a natural key, not to an AutoNumber.
keys. date should be a Date/Time field with Format = Short
Date.
1. Close the guest table. personCount is the number of persons staying in the
2. Create all the remaining tables in the same way as room. An integer should suffice.
you created the guest table (from the Tables tab state is similar to state for tblStay, although the values
use Create table in Design view - or click New). are slightly different.
The key consists of two fields: roomID and date. It is a
Make sure you define all the fields. Otherwise you will bit tricky to specify this: select both fields by
get stuck when later constructing the user interface. clicking on the left-hand marker (hold down Ctrl
Here are a few notes about the various tables: while selecting the second field). Then right-click
somewhere on the text inside the line.
tblStay:
stayID is the primary key of tblStay. Make it an Auto- Optional tables
Number.
The following two tables are needed for the full sys-
guestID is a foreign key that refers to the AutoNumber
tem. However, you don't need to create them in order
in tblGuest. The foreign key must have a matching
to follow the tutorial.
data type - a long integer. Choose Data Type =
Number and Field Size = Long Integer. Warning: tblServiceType:
Don't make the foreign key an AutoNumber. This serviceID is an artificial key. Should be an Auto-
would cause Access to fill in the foreign key fields Number.
automatically, and you cannot change the numbers name and price should be obvious. The price should be
so that they point to the primary keys in the guest a decimal number. Choose Data Type=Number,
table. Field Size= Single, Decimal Places =2.
paymethod is an enumeration type. Make it an integer
(a 16-bit integer, not a long integer). Choose Data tblServiceReceived:
Type = Number and Field Size= Integer. We will stayID and serviceID are foreign keys that refer to
use the value 1 to denote Cash, the value 2 to de- AutoNumbers. The foreign keys must thus be long
note Visa, etc. We will look closer at this in section integers.
2.4. roomID is an optional reference to a room. An integer
state must also be an enumeration type. Make it an in- should suffice. (This reference is needed when a
teger. Here the value 1 will denote booked, 2 in, etc. waiter records a service for a specific room and the
guest has more than one room.)
tblRoomType: date should be a Date/Time field. Choose Format =
Contains one record for each type of room, for instance Short Date.
one for double rooms, one for single rooms, etc. (In the quantity is the number of items the guest has got - an
book User Interface Design, we added this table late in integer should suffice.
the design process to illustrate the normalization con-
cept.) Data types
Data is stored in the computer according to its type.
roomType is an artificial key. An AutoNumber is okay.
Here is a description of the most important types in the
description is a short text, for instance "double room,
data base. Visual Basic deals with almost the same
bath".
types (see section 6.2 and the reference card under
bedCount is the number of beds in the room, including
Declarations).
temporary beds.
price1 and price2 are the standard price and a possible Text. The field can contain any characters. The Field
discount price. The price should be a decimal num- Size property defines the maximum number of charac-
ber. Choose Data Type=Number, Field Size= Sin- ters. The maximum cannot be above 255 characters.
gle, Decimal Places =2.
Memo. Like a text field, but the maximum number of
tblRoom: characters is 65,535. Access takes more time to process
roomID is a natural key - the number on the door. So a memo field, so use text fields if adequate.
don't use an AutoNumber. Use an integer.
roomType is a foreign key that refers to tblRoomType. Number. The field can contain a number. The Field
(You should by know how to deal with it.) Size property defines what kind of number:
10 2. Creating a database
• Integer. A small integer. It must be in the range - Also here you can choose a format that adapts to the
32,768 to +32,767 (a 16-bit integer). regional setting.
• Long Integer. It must be in the range from around
-2,140 million to +2,140 million (a 32-bit integer). Yes/No. The field contains a Boolean value shown
• Single. A decimal number in the range from either as Yes/No, True/False, or On/Off. The format
-3.4*1038 to +3.4*1038 with an accuracy of 6 or 7 property specifies this.
significant digits (a 32-bit floating point number).
AutoNumber. The field is a long integer (32 bits) that
• Double. A decimal number in the range from
Access generates itself as a unique number in the table.
-1.8*10308 to +1.8*10308 with 14 significant digits
Access numbers the records 1, 2, . . . as you enter the
(a 64-bit floating point number).
records. However, you cannot trust that the sequence is
• Decimal. A very long integer with a decimal point unbroken. For instance when you add a record and
placed somewhere. Intended for monetary calcula- undo the addition before having completed it, Access
tions where rounding must be strictly controlled. uses the next number in the sequence anyway.
In the book we use Single or Double instead.
A foreign key is a field (or several fields) that refer to
Numbers can be shown in many ways depending on the something unique in another table - usually the primary
format property of the field. You may for instance key. Be careful here. The foreign key and the primary
show them with a fixed number of decimals, with a key must have the same type. However, when the
currency symbol, etc. primary key is an AutoNumber, the foreign key must
be a long integer.
Some formats show data in a way that depends on the
regional settings of the computer. If you for instance Changing a data type. Access is quite liberal with
specify the format of a number as Currency, the changing a data type to something else - even if there
number will show with a $ on a US computer and with are data in the records. It can also change an Auto-
a £ on a British computer. Number field to a number field, but not the other way
around. If you need to change field B to an Auto-
Date/Time. The field gives a point in time. In the
Number, create a new field C and make it an Auto-
computer it is stored as the number of days since
Number. Then delete field B and rename field C to B.
30/12-1899 at 0:00. It is really a Double number, so the
number of days may include a fraction of a day. In this If you for some reason want to store a record with an
way the field specifies the date as well as the time with AutoNumber of your own choice (for instance create a
high precision. As an example, the number 1 corre- stay with stayID=728), you need to append the record
sponds to 31/12-1899 at 0:00, the number 1.75 to with an INSERT query (see section 7.1). You cannot
31/12-1899 at 18:00 (6 PM). just type in the stayID.
Usually we don't show a date field as a number, but as
a date and/or a time. The format property specifies this.
stayID, guestID,
tblStay tblServiceReceived tblServiceType
paymethod (cash | visa ...),
state (booked | in |out | canceled)
stayID, roomID,
date, personCount, tblRoomState
state (booked | occupied | repair)
roomType, description
roomID, roomType tblRoom tblRoomType bedCount, price1, price2
2. Creating a database 11
2.3 Create relationships
When we have several tables, we can make relation- In the edit-relationship window, you can specify
ships (crow's feet). Then we get an E/R model instead foreign keys that consist of several fields. You can also
of a simple collection of tables. The relationships allow specify that the relationship has referential integrity, so
Access to help us retrieve data across tables, check ref- that all records on the m-side point to a record on the 1-
erential integrity, etc. side.
Figure 2.3 shows the hotel relationships in Access. It 7. In our case, all stays must point to a guest, so mark
resembles the crow's feet model quite well. You define the connector enforce referential integrity. (If
the relationships in this way: Access refuses this, it is most likely because you
have not defined the foreign key as a long integer.)
1. Start in the database window and right-click 8. Close the relationship window. The relationship
somewhere. connector now appears in the window between the
2. Choose Relationships. foreign key and its target.
Now you see an empty Relationship Window. You The referential integrity makes Access show the con-
have to tell Access which tables to show here. Some- nector as 1-∞ (1:m). Based on referential integrity and
times a Show Table window pops up by itself. Other- whether the connected fields are primary keys, Access
wise you have to invoke it with a right-click in the re- may also decide that it is a 1:1 relationship. It is not
lationship window. important what Access decides in these matters. You
can later tell it otherwise when you want to use the
3. In the Show Table window, select the tables you connector.
want to include. In the hotel system it is all the ta-
bles. 9. Create the remaining relationships too. Note that
4. Click Add and close the window. Now the tables there is no referential integrity between tblStay and
should be in the relationship window. tblRoomState. It is on purpose - if the room is in
5. Create the relationship between tblGuest and repair state there is no connected stay.
tblStay by dragging guestID from one table to
guestID in the other. Partial integrity. Access provides a more relaxed
6. An edit-relationship window pops up. If not, right- version of referential integrity. It allows the foreign key
click on the relationship connector and choose the to be either empty (Null) or point to a record on the 1-
edit window. side. This is the case for the relationship between
tblStay and tblRoomState. Give it partial integrity in
Access may complain: this way:
Relationships must be on the same number of fields 10. Open tblRoomState in design view. For stayID
with the same data types. (the foreign key) set the Default Value to empty
(delete all characters in the field). Also set
The cause is often that one end of the connector is an Required to No.
AutoNumber and the other end a simple integer. It 11. In the relationship window, right-click on the
must be a long integer to match the AutoNumber. connector and choose the edit window. Select
enforce referential integrity.
12 2. Creating a database
Fig 2.3 Create relationships
Foreign key
Primary key
Create a relationship:
Drag 1-side field to m-side field (or opposite).
Edit the relationship - Referential integrity!
2. Creating a database 13
2.4 Look-up fields, enumeration type
Your next task will be to fill in some data in all the ta- How the look-up field works
bles. However, some of the fields are cumbersome to Open tblStay in design mode and study the Lookup tab
fill in correctly. As an example, the pay method field is for paymethod (bottom of Figure 2.4). The display
a code where 1 means Cash, 2 Visa, etc. The user control property is Combo Box. It means that when the
should not have to remember these codes, so we will user is to fill in the paymethod, he sees a combo box.
let the user choose the value from a list. It is an
enumeration-type field: • For ordinary fields Display Control is Text Box. A
text box shows texts, numbers, etc. as a string of
paymethod(Cash | Visa | . . . ) characters. If you want to change the field back to
an ordinary field, just set Display Control to Text
Figure 2.4 shows what we want when the user fills in Box.
the paymethod field. We want the field to be a combo
box where the user can select the mnemonic text while The values the user can choose between are listed in
Access stores the number code. Here is how to do it: Row Source. You may edit the values here. Column
Count shows that these values are to be displayed as
1. Open tblStay in design view. (Select it and click two columns. Notice that Limit to List is No. It means
Design or use Ctrl+Enter). that the user can enter other values than those in the
2. Select the paymethod field and the data type list. In our case, it is not desirable, so set the property
Lookup Wizard. to Yes. Sections 3.1.6 and 3.1.7 explain more about
3. Access asks whether you (as a user) want to select combo boxes.
the values from a table or from a list of values that
you (as a designer) type in. Choose to type them
in. Then click Next.
4. Access asks how many columns your combo box Undo the Lookup Wizard?
should have. Choose two and fill in the columns as How do you make the field an ordinary field rather
shown on the figure. Then click Next. than a lookup field? It doesn't help to make it an integer
5. Access asks which column holds the value to store or a text. Choose the Lookup tab at the bottom of the
in the table. In our case it is column 1. table design window. Change Display Control to Text
6. Finally, Access asks for the column name that the Box. (See bottom of Figure 2.4.)
user will see. In our case, paymethod is okay.
Click Finish.
Panic? Undo data entry
Fill in some stay records When you enter data into the tables, Access checks
You are now going to create some stay records and against the rules you have defined for the tables and the
connect them to a guest. relationships. For instance, when you enter the guestID
in tblStay, this ID must correspond to a guest in the
7. Close the table design window and open it in user
guest table. Access doesn't allow you to leave the
mode.
record before this is fixed. The reason is that Access
8. Also open tblGuest in user mode. Keep the two
stores the record in the database as soon as you move
tables side by side so you can see both. Make sure
the cursor away from the record. And the database
you have created some guests. Otherwise do it
must meet all the rules you have stated.
now.
9. Fill in a stay record using the combo box for Sometimes you may not know what to type to satisfy
paymethod. Notice that what you see as a designer, Access, and on the other hand you cannot leave the re-
is the number stored in the database. The user cord to look at what to type. Many users panic here and
should not see the number, but the text. We can even switch off the power to close down the system.
arrange for this when the field becomes a text box The solution is to use Esc twice:
in the user window (see section 3.2.2).
10. Also fill in the foreign key guestID so that it refers • First Esc: Undoes the correction you made in the
to one of the guests. Since there is referential field where the cursor is.
integrity, Access won't let you store the stay record • Second Esc: Undoes all the changes you made to
without a proper guestID. If you get into real the record where the cursor is. This means that the
trouble, use Esc twice (see the Panic box for the database returns to a consistent state where all the
explanation). rules are met.
11. Fill in a few more stay records in the same way.
14 2. Creating a database
Fig 2.4 Look-up fields, enumeration type
Desired result
1, 2, 3, 4. Don't worry.
Possible values
Populate the database You may soon find that a simple little database uses
12. Define the other enumeration fields as lookup several megabytes. Fortunately, Access can compact
fields in the same way (the state fields in tblStay the database. Do that every now and then in this way:
and tblRoomState).
13. Fill in some realistic data in all the tables. You 14. Select Tools->Database Utilities->Compact and
may for instance use data corresponding to the Repair Database. That is all. You may check that
situation in Figure 1A. Now you have test data for the file length actually became much smaller. (In
the rest of the booklet. Access 97, the Compact and the Repair utilities are
separate.)
Important: Compact the database
Access is very liberate with disk space and when you
change things, it consumes new blocks on the disk.
2. Creating a database 15
2.5 Dealing with trees and networks
E/R models can neatly describe complex relationships, 1. Create a new database, FlightRoutes. Create the
for instance as we saw it for the flight routes in User tables City, Leg and Route in the usual way.
Interface Design. Figure 2.5 shows the E/R model, but
Access cannot show such a model directly. 2. Open the relationship window and add all three ta-
bles to the relationship window. Then add City and
The problem is that Access identifies a relationship by Leg once more. The relationship window should
means of the two tables it connects. This means that now contain also a City_1 and a Leg_1 as shown
Access cannot have two connectors between the same on the figure.
two tables. Also you cannot have a self-referential con-
nector. In the flight route model we need both of these. 3. Drag the connectors as shown. You now have two
connectors between City and Leg. One is deter-
As a compensation, Access offers shadow copies of a mined by City and the foreign key from. The other
table. The table and its shadow copies are the same ta- is determined by City_1 and the foreign key to.
ble, but they have different names. You can now create You also have a self-referential connector from
connectors to the shadow copies and thus indirectly Leg to itself. It is determined by Leg_1 and the
create multiple connectors between the same two ta- foreign key next.
bles.
4. Try to fill in data for AA331 according to the fig-
Figure 2.5 shows how to handle the flight routes in ure. Note that there are only one City table and one
Access by means of shadow copies. Leg table to fill in. The shadow tables are not real
tables.
16 2. Creating a database
Fig 2.5 Flight routes - shadow tables
2. Creating a database 17
3. Access-based user interfaces
Highlights (control tips). These are the things the user sees on the
• Construct user windows (Forms). screen. Access provides a lot of built-in functionality
• Add fields, sub-windows, etc. (Controls). that makes the user interface respond to user actions.
• Construct menus and other details. However, for a real system the built-in functionality is
rarely sufficient, and you will have to add your own
program pieces written in Visual Basic.
An Access-based user interface consists of user win-
In this chapter we look only at what the user sees on
dows (called Forms in Access) , menus, and all the
the screen. We hardly put real data into the fields. What
little things such as error messages (message boxes)
we are after is a tool-based mockup. Later we will add
and pop up help when the cursor rests on a field
real data and functionality to the screens.
The Form
Controls
Wanted:
Tool-based
mockup
The Forms
(user windows)
of the database Create a Form
List of forms
(empty initially)
Click for
Form
properties
Toolbox:
Click Text Box control.
Draw a box on the Form.
Form name. Access asks for it Handle for moving Handle for moving Drag here to
when you first close the form. Label part . Text Box part . move both.
MinMax buttons
Control Box
Caption
Record
Selector Form View:
User mode
12. Set these properties on the Format tab: Caption Look and feel - Autoformat
(the form name the user sees), Scroll bars (not You can give the form another look by means of Auto-
needed), Record selector (not needed), Naviga- format. This changes the style, that is the background
tion buttons (not needed). of the form and the appearance of all fields and
buttons. You may try it if you like:
There are other interesting properties on the Format tab
that you may need for other windows: • Open the form in design mode. From the Format
menu at the top of the Access window, select
• Border Style specifies whether the form looks like AutoFormat.
a resizable window, a dialog box or a message • You can choose various auto-formats. Through the
box. Options button you can determine whether you
• Control Box is the buttons on the left and right of want to change also field colors, fonts and borders.
the title bar. You may hide them. When you close the AutoFormat box, the form has
• MinMax buttons and Close button are shown changed its look.
when the control box is shown, but you may dis- • You may also create a new auto-format style based
able them. on one of your forms. Open the form in design
• Picture is none in our case, but you may specify a view. In the AutoFormat box, select Customize ->
picture file to be used as background. Create a new, and give the new AutoFormat a
name. You can then use this auto-format for other
You can get a good explanation of most of the proper- forms.
ties by selecting the property and clicking F1.
Shortcuts. You can change to design mode with DateTime picker. The bottom part of Figure 3.1C
Alt+V+Enter, and to user mode with F5. shows two DateTime pickers. They look like combo
boxes, but when the user clicks the down-arrow, a
calendar appears.
3.1.5 Lines, checkbox, calendar
Above we have tried some of the controls: text box, 8. Extend the grid area further, or remove the
label, and command button. Figure 3.1C shows some calendar control.
other controls you may try now: 9. Select the hammer tool again and look for
Microsoft Date and Time Picker Control. Select it.
1. Line. Select the Line tool from the toolbox and (You may have to include it from Tools ->
draw a line somewhere on the form. The line is Reference, as above.)
just a visual effect. It has no functionality. 10. Draw the combo-box part of the control. You now
2. Rectangle. Select Rectangle from the toolbox and have a control that holds a date-time variable. Try
draw a rectangle around some of the existing con- it out in user mode. Notice how the user can
trols, for instance the left fields. The rectangle is increase or decrease dates and months with arrow
just a visual effect without functionality. up and down.
3. Colors. Double click on the rectangle to open its 11. The control doesn't have label. Give it one: Select
property box. On the format tab, give the rectangle one of the other labels. Copy it (Ctrl+C). Select the
a back color and set back style to normal. Now it date-time control and paste it (Ctrl-V).
hides the controls it surrounds. Use the main 12. Create the other date-time control in the same way
Access menu, Format -> SendToBack, to move it - or copy and paste the first one.
behind the other controls. Experiment with differ- 13. Experiment with the properties of the DateTime
ent back colors, border styles, and border colors. It picker: In design mode, double-click the control. A
Line
(visual effect only)
Shortcut keys
Rectangle
(visual effect only)
Checkbox
control
Calendar
control
DateTimePicker
control
DateTimePicker
drop-down
special DTPicker properties window should Try other changes too, for instance the UpDown
appear. checkbox and the colors.
(You can also get to this special window from Access's 15. We are not going to use this fancy version of the
standard property window: Select the Other-tab and form in the following. If you want to keep it, save
then Custom.) a copy of it: Select the form in the database win-
dow and use copy and paste.
14. Try changing the date format: Select format 3, 16. Delete the line, rectangle, checkbox and calendar
dtpCustom. In the CustomFormat box, define the controls. We don't need them in the following.
format as dd-MM-yyyy. Note that MM means
month, while mm means minute. (See also Figure
6.4B, Format function).
Technically speaking, this combo box holds a value of Look a the result in user mode. It doesn't look quite
enumeration type: right. The drop-down list has two columns and the box
itself shows the number - not the user text. We have to
include(booked | canceled | . . . ) repair this:
In the same way as in the database, the user should see 9. Look at the property box of the combo box. The
the values booked, canceled, etc., but they should be Format tab has a field called Column Widths. It
stored as the values 1, 2, etc. shows the widths of the two columns. Set the
width of the first column to 0 (see the bottom of
1. Switch to design mode. Figure 3.1D).
2. Set the Wizard button on at the top of the toolbox. 10. Try it out in user mode. Everything should look
3. Select the Combo box tool and draw the Include right by now.
box as shown at the top right of the figure. • The Format tab has other interesting fields. You
may for instance adjust the List Width for the drop
The Wizard appears. It works much the same way as down list.
when you defined an enumeration-type field in the
database (section 2.4): 11. Select the Data tab. Row Source holds the values
in the list. You may edit them here.
4. The Wizard asks you whether you want to look up 12. Limit to List defines whether the user is allowed to
the values from a table or type them in yourself. enter other values than those in the list. In this
Select the latter and click Next. case, it should be set to Yes.
5. The Wizard asks you how many columns you
• Bound Column defines which column to use for
want. Choose two: one for the stored value and
the stored value.
one for the value the user should see.
6. Fill in the columns as shown, and click Next.
Wanted:
Combo box
Width of first
column = 0
Wanted:
Combo box
Select a text box and look at its property box (Figure When Enabled is No, the user cannot enter any-
3.1F). If the property box isn't open, bring it up by thing in the box because the cursor doesn't stop
double clicking on some control. There are more than there. In this case, Locked has an interesting influ-
60 properties for a text box. Here we will look at some ence on the box color. If Locked is No, the field is
of them. gray. If it is Yes, the color follows the normal pat-
tern determined by Back color and Fore color.
Text box - properties on the Format tab
• Format and decimal places specify the data type Text box - properties on the Other tab
of the control, much the same way as you can • Name is the programmer's name for the text box.
specify the data type for a database field. You can Visual Basic programs refer to the text box with
select among a number of predefined formats, or this name. The designer can change the name. No-
type your own format into the format field. The tice that the name is shown in the title bar too.
formats are similar to the Visual Basic formats
(see section 6.4 and the reference card). • Tab Index determines how the cursor moves
through the controls when the user tabs through
• Scroll Bars. A text box may be large and show a the form. Tab indexes run from 0 and up. When
text consisting of many lines. This property speci- the form opens, the cursor is in the control with tab
fies whether it should have scrollbars. index 0. The tab key moves the cursor though tab
index 0, 1, 2, etc.
• Left, Top, Width, Height specify the position and
size of the control. You may set these properties • ControlTip is the pop-up text the user sees when
instead of dragging with the mouse. the mouse rests on the text box.
• Back color, fore color, font name, etc. specify Label control - properties on the Format tab
colors, borders and other visible properties of the The text box has an associated label. The label has a
control. programmer's name. In the example, Access has given
the label the name Label7.
• Text Align. You can align the text, e.g. left for a
name, right for a number. • Caption is the label text the user sees. In this case
Access generated the caption Text6: The designer
Text box - properties on the Data tab can change it, of course.
• Control Source specifies how the value is com-
puted and where it is stored. For unbound controls Many of these properties exist also for other control
as those on the FindGuest form, control source is types, for instance for the combo box. For the com-
blank. We look at the other possibilities in section mand button, many of them work too. For some strange
3.3. reason, however, you cannot align the text on a com-
mand button. It is always centered. In the hotel system
• Input Mask specifies the text box format when we have made a fake left align by entering spaces after
the box has the focus and the user types into it. the name. You have to enter the spaces directly on the
The mask may for instance be used to enter dates button - you cannot do it in the Caption property.
with predefined slashes and hyphens. The input
mask follows different rules than the format To learn more about a property, click the property line
property. (Not described in this booklet.) and click F1 for help.
The textbox
Text6
Name property -
the programmer’s name
Caption property -
the user’s name
Final version
Subform
Experimental version
Main form
Subform control
In database window:
Select Forms ->
Create form by using wizard
Base form on
guest table
All fields
exceptguestID
Columnar format:
Fields with labels
Continuous
form in Form view
In Datasheet
view
Draw a subform
control
Name property:
Connect to fsubStayList Other -> Name = subStayList
Result in
Datasheet view
Remove navigation
buttons through
property box
Delete unneccesary
fields
Change labels to
user-oriented names
Drag to
form
2. Open fsubStayRooms in design mode. Remove all At this point, the user can only see the form in Da-
fields relating to the guest and insert text boxes as tasheet view. Change this:
shown. Give the labels the user-oriented names.
4. On the property sheet for the form, set Default
3. Change the caption of the form, for instance to Views to Continuous Forms and Views Allowed
RoomList. to Form.
4. Switch to Datasheet mode, and the mockup should In user mode you will now see the continuous form as
be ready. a list of small forms. You may connect it to the main
form to get the wanted result: Change the SourceObject
How does this work? The subform is bound to tblGuest property of the subform control.
and shows a line for each guest. However, the line
doesn't show any guest fields, only the text boxes we Form Wizard - tabular layout
have added. These text boxes are only dialog data. It is a bit cumbersome to construct the continuous form
They don't store anything in the database. If you for in this way. You may let the Form Wizard do some of
instance enter a number in Nights, you will see the the job:
same number in all the lines. There is only one instance
of the dialog data. 5. From the database window, click the Forms tab
and select Create Form by using Wizard.
If you want more lines in the mockup, add new guests 6. Select the proper table, next select Tabular layout.
to tblGuest. The mockup shows one line for each guest. 7. Finally select a style (e.g. Standard) and give the
form a name.
Insert a table directly as a subform?
Access 2000 and 2003 allow you to connect a subform You will now have a form that looks very much like
control to a table - without making a continuous form. the one you constructed above.
Don't use this shortcut.
Wanted:
Tool-based
mockup list
Delete all
guest fields
Wanted: Subform
in Form view
Header
Details
Make a copy of
fsubStayList
Call it fsubStayList2
Detail
Delete labels from detail area.
Adjust fields. Add labels to
the header
Datasheet view allows cursor movements in all direc- Properties. The following properties (attributes) are
tions as in a spreadsheet. It also allows the user to se- important to understand when you work with subforms.
lect a rectangle of cells. The Visual Basic program can Figure 3.2H shows examples of these properties.
find out which cells the user has selected.
Main form: The Name property is the designer's form
The weakness is that the display format is very re- name. It is this form name you see in the data-
stricted. Each record is shown as a single line with text base window. You can only change the name
boxes, combo boxes, etc. Also the column headings are there. The Caption property is the name the
very restricted. They are just simple texts and they user sees in the title bar of the form. You can set
cannot even be empty. the caption through the Format tab in the prop-
erty box.
Form view allows all the available display formats,
including pictures retrieved from a database. (There Subform control: The Name is the designer's name of
cannot, however, be continuous forms inside another the control. A program would use this name to
continuous form. Continuous forms can only be used in address the control. You can set the name prop-
one level.) erty through the Other tab in the property box.
The SourceObject indicates the continuous
The weakness of forms is that the cursor moves less form connected to the subform control.
intuitively. The user can tab through the fields of each
record, but not easily move up and down the list of re- Continuous form: The Name is the form name that you
cords. Furthermore, the user can only select a full see in the database window. Record Source in-
record, not part of a record. dicates the table bound to the form. Default
View indicates whether the form is shown as a
Current record. A subform can show many records at datasheet or as detail forms with a header. An-
the same time, but only one of them is the current other property, Views Allowed, indicates
record. It is marked with the little arrow to the left, the whether the user can change from one view to
record selector, as shown on Figure 3.2H. (You may another.
change the form settings so that the record selector area
is invisible.) 3.2.6 Prefixes
A prefix is a few letters before a name. The prefix
When the user types something into the form, it will helps the reader understand what this name is about. Is
always be into the current record. When the cursor it the name of a table, a form, etc? In just slightly
moves into another record, it becomes current. complex systems, prefixes are crucial to help the
developers. The bottom of Figure 3.2H shows the full
Until now we have only seen a main form that is not list of prefixes used in this booklet. For subforms the
bound to the database. But main forms may be bound following are important:
too. Then they have a current record and they need the
record selector (see examples in section 4.7). tbl Table names.
frm Names of main forms.
The subform concepts sub Names of subform controls.
Figure 3.2H gives a summary of the many subform fsub Names of continuous forms connected to a sub-
concepts we have used above. A main form is a user form control.
window with title bar, etc. It may contain one or more
subform controls.
Main form:
Name = frmFindStay
Caption = Find Guest
Continuous form:
Name = fsubStayList
Subform control Record Source = tblGuest
Default View = Continuous Forms
Views Allowed = Both
Current
record
Continuous form
in Form view
Continuous form
in Datasheet view
Other prefixes:
bas Module (used by VBA)
frm Main form.
fsub Continuous form (connected to a subform control).
qry Query
qxtb Crosstab query
tbl Table
In user mode, the checkboxes still look gray but that is Notice that the programmer name for the address text
because they try to show the passport fields. A box is address1 - exactly the same as the name of the
checkbox can show a Yes/No value or a number (with database field. Access assumes that we want to con-
zero shown as No). The passport fields are either blank catenate the address1 text box and the address2 field. In
or contain a text, and the checkbox doesn't know what order to do this, Access has to compute the address1
to show. text box, but this means concatenating address1 and
address2 once again. The computation would never
6. Try to check and uncheck some of the boxes. stop and Access shows it with #Error.
Notice that they are independent of each other. The
Yes/No value is stored in the passport field of 9. Repair self-reference. In order to repair the
tblGuest. Look at the table contents. Notice that problem, give the address1 text box another pro-
Yes is stored as -1, No as 0. (Sorry if some of the grammer name. On the Other tab, replace the
real passport numbers disappeared.) name address1 with Address. In user mode, the
form should now look as the last form on Figure
3.3A.
Add a checkbox
to the detail form.
As an experiment,
bind it to the passport field
Let us look at another troublesome problem: To avoid the problem, precede the name with a bang
operator (!):
10. Referring to a built-in property. Let us try to = !name & ", " & address2
concatenate the guest name with address2. Change
the text box control source to: The bang operator tells Access that you want a control
= name & ", " & address2 or a database field - not a built-in property. We will
discuss this a lot more in section 5.1.
In user mode, you will see that you don't get the name
of the guest, but the name of the form itself! The rea- 11. Name mistake. Try to use a non-existing name in
son is that a form has a Name property, and this is what the control source:
you referred to. Notice that Access changed the name = zz & address2
you typed to [Name]. This is a sign that Access recog-
nized the name as something spelled with a capital N - Access doesn't give you an error message, but in user
in this case a property name. mode you will see the text #Name? instead of the guest
data.
Textbox
Textbox
automatically
automatically
reflects
reflects the
the current
current selection.
selection.
Access 97 and 2000 only
should work correctly when you close the formatting more elaborate address to tell Access that it is a sub-
box. (In some cases Access shows an error message form of frmFindStay we talk about. The address to be
saying that it cannot find the Forms field. Ignore it, the used is
formula works anyway.)
Forms ! frmFindStay ! subStayList !name > "h"
Unfortunately, this full address works only when we
see fsubStayList separately, but not when we see it as a We will explain a lot more about these addresses in
subform of frmFindStay. We will have to use an even section 5.1.
Wanted
Menu bar
Tool bar
Menu:
Menu heading in the menu bar. Right-click.
Menu list that drops down Command Select Customize
(menu item)
Create a new
toolbar/menubar.
Name it Hotel
Make it a
menubar
DragCommand
it to the
menubar/toolbar area.
Checked toolbars
shown as default
DragCommand
new menu headings
to the toolbar.
Right-click and
Command
give it a name.
The right-click allows you to do many other things to 3.5.4 Startup settings - hiding developer
this command. You can assign an icon to it and edit the stuff
icon. (This is more useful when you design toolbars When the system is finished, the user should not see all
and toolboxes rather than menu bars.) If you click the Access menus, the database window for selecting
Properties at the bottom of the list, you can determine and creating forms, etc. It may be necessary to hide all
which action the command shall perform when se- of this already in the mockup. Here is how to do it:
lected. In our case, we use the built-in action Open a
form. 9. Select Tools->Startup. You now see the startup
settings (Figure 3.5D).
Add the CancelStay command 10. Change these settings: Application Title = Hotel
4. Select the category File and the command Custom. system (the user sees this name in the title bar in-
(This is the only command that doesn't build on an stead of the name Access). Menu bar = Hotel (the
existing command.) Drag this command to the user sees this menu at the top of the screen). Dis-
Stays menu and position it properly relative to the play Form/page = frmFindStay (the user sees this
other command on the menu. form on the screen initially).
5. Right-click the command and set its name to 11. Hide the standard things: Full menus and Database
Cancel Stay. window.
The Custom command has no built-in action. In its This will give the correct view for a mockup. When
property box, we will later add a call to a Visual Basic you later have a functional system, you should disable
function (section 5.7) that cancels the stay. most other things too, for instance built-in shortcut
menus (right-click menus) and special keys, for in-
6. Close the Customize window and try out the com-
stance F11 to open the database window.
mands. The ShowFindGuest command should ac-
tually work, while the CancelStay does nothing at 12. Close the database and open it again. You should
present. now see the naked application window with only
the FindStay window and the hotel menu.
You may add all the other menus and commands at this
stage to complete the mockup, but better spend your Help! How do you get it back to normal so that you
time doing it for your own design project. can work as a developer? You cannot even change the
startup settings anymore. Of course there is a solution:
3.5.3 Attach the toolbar to a form
You can attach a toolbar to a form so that it is shown 13. Close the hotel system window again. Now hold
only when this form is in focus. down Shift while you open it. Keep Shift down
until it is completely open. (In Access 2003 this
7. First hide the Hotel menu: Right click any toolbar includes answering about unsafe files.)
to open the customize window. On the Toolbars
tab, find the Hotel menu at the end of the list. Re- The window looks again the developer way, and you
move the check mark and close Customize. can change the startup settings. You might leave them
in the final user version and remember to use Shift
8. The Hotel menu is not visible anymore. Now open every time you are working as a developer. My experi-
frmFindStay in Design view. In the form's prop- ence is that this is too cumbersome and you forget
DragCommand
new command
to the menu (wait a moment
for the menu list to drop down).
Right-click and
Command
give it a name.
Define style.
about the Shift too often. I always set them back when
I have tried it out.
In the document, you now see a copy of the entire win- You can add as many screen parts to the Word docu-
dow (or the message box). For mockup purposes, you ment as you like. Then print it and your mockup is al-
will only need a small part of the entire window. Crop most ready. What remains is to copy pages, trim mes-
away the unnecessary parts as follows: sage boxes and control tips with scissors, and write
data in the various windows. Usually it is best to en-
10. In the Word document, select the screen picture. If large the screens when you copy them.
necessary, open the Picture toolbox (View ->
Toolbars -> Picture). This works but is very cumbersome, and it is almost
11. Select the Crop tool from the picture toolbox. impossible to crop away exactly the right amount.
12. Crop the picture so that you only have the neces- Further, the document becomes a huge file due to all
sary part left. the large screen dumps (they are there in full size even
if you have cropped most of them away).
Copy a menu. With this approach you cannot copy a
drop-down menu, because it rolls up when you press For professional use, get a screen grabber (a screen
Alt. To copy a drop-down menu, you have to use Print capture tool). There are freeware screen grabbers avail-
Screen without Alt. This copies the entire screen to the able that work very well. These tools help you copy
clipboard. You then have to crop away most of the just the right part of the screen, and many of them can
screen, but it can be done. also capture a menu when it is rolled down.
In the lower part of the window, you see the query 6. Open qryStayList and tblGuest at the same time.
grid where we will make a column for each field in the 7. In the query table, change the name of one of the
computed table. guests. As soon as you move the cursor to the next
line, you will see the change in the query table as
3. Drag stayID from tblStay to the grid. Then drag well as the guest table.
name, address1 and phone from tblGuest. Finally, 8. In tblGuest, try to change the guest name again.
drag state from tblStay. (You may also double- The query table will be updated immediately.
click the fields.)
The query table we look at is a dynaset because it is
You may rearrange the columns by selecting a column updated automatically. We can enter data into it be-
and dragging it to another place. cause it is a simple query where Access can find out
how to store the data into the source tables. For more
4. Switch to datasheet view. The query table should complex queries, this is not possible.
look like the bottom of the figure. It contains all
Computed Tables
tables to join
Drag
Drag the
wanted
wanted
fields to grid.
(Or double click)
Datasheet
view
Query grid
Wanted:
List of stays
with guest data
The query has a property called Recordset Type. It is the guest and cannot preserve the referential integ-
Dynaset as a standard, but you can change it to Snap- rity. If you had included the foreign key in the
shot. Then Access computes the record list when you query, you could set it now and succeed. Never
open the query, and doesn't update it dynamically. In mind. (Remember to use Esc to get out of the in-
this case you cannot enter data through the query. consistent data update.)
(How to find the Recordset Property? From the query 11. In the query table, try to delete a line. What you
design window, use View -> Properties. But don't delete is the stay record, not the guest record.
change anything right now.)
Conclusion. The dynaset is suitable for editing data
Adding/deleting records in a dynaset produced by a simple query. As soon as you fill in a
9. In the query table, enter a guest name in the last field in the new record line (star-marked), Access will
line (with star-indication). When you move the try to create a new record. If you fill in a field from the
cursor up, you have created a new guest record guest table, Access will make a guest record. If you fill
(but not a new stay record). You cannot see it in in a field from the stay table, Access will (also) make a
tblGuest, but if you close and open tblGuest, you stay record. When you move the cursor to another
will see it. (Using the sort button A/Z on the tool- record, Access will check that referential integrity and
bar will also show the new guest.) other rules are met.
10. In the query table, enter a state in the last line and
move the cursor up. Access refuses to do it. It tries
to create a stay record, but lacks the foreign key to
a) Cartesian product:
5*7 combinations of guest and stay
tblStay: tblGuest: guestID b) Join:
stayID, guestID Include only those where
1 2 5 6 8 tblGuest.guestID = tblStay.guestID
c) Where:
728 1 Include only those where . . .
736 1 d) Group By:
Compress bundles of records to one
740 1
e) Having:
727 5 Include only bundles where . . .
729 5 f) Order By:
Sort the remaining records according to . . .
737 6
g) Select:
739 8 Select and compute new fields,
discard the rest
g) Select. Finally, SQL discards all the fields that we much too long time in large databases. Assume that we
have not mentioned in the SELECT-part. In the joined two tables each with 10,000 records. The SQL-
grid there is a Show indication in each column. It engine would have to create 100 million combination
indicates that this column must be in the SELECT- records, and then discard most of them. In practice, the
part and be shown in the final table. We will later SQL-engine uses index tables and other tricks to create
see that we can compute new fields in the only the "crosses".
SELECT part. This computation also takes place at
this stage. Wonder why we talk about Access and the SQL-en-
gine? You don't see the SQL-engine directly because
Notice that these rules tell us that we can have join Access's user interface hides it. But Access and the
criteria (and other criteria) that refer to fields that are SQL-engine are two different software components.
not shown in the result. We have utilized this rule in We may connect our Access application to another
qryStayList because the join criterion is given by SQL-engine than the one delivered with Access. As an
guestID, which isn't in the result at all. example, we might connect it to an Oracle database.
Most of our user interface would still work the same
Although the query result is as described above, the way.
SQL-engine doesn't arrive at it that way. It would take
Wanted:
Include all guests
Outer join:
Right-click and choose
Null stay
We will first compute an extended version of the stay Alias - renaming a column. At this stage the arrival
table with these data added to each stay. The small da- date is computed correctly, but we would like a differ-
tasheet to the right in Figure 4.4A outlines the desired ent name (an alias) for the column:
result. In order to do this, we have to bundle all room
states of the stay into one, leaving only the arrival date 7. Open the query in design view. In the grid, change
and the room indication. This is an example of an ag- the heading of the date column to arrival: date.
gregate query. Here is how to do it: This causes Access to compute the first date as be-
fore, but give the result the name arrival.
1. From the database window, create a new query in
design mode. Include tblRoomState and tblStay. Computed field. The last thing missing is that the first
(See top of Figure 4.4A.) roomID may be confusing to the user if there is more
2. Drag these fields to the grid: stayID, guestID, state than one room in the stay. It is particularly confusing if
(from tblStay) , date and roomID. You now have John Simpson first stays in room 12, then moves to
an ordinary inner join based on the criterion room 11. The stay list would then indicate room 11 and
tblRoomState.stayID=tblStay.stayID the receptionist might by mistake give him the key for
3. Change to datasheet view. You should now see a room 11 when he arrives. This is why we want to show
table like the one at the lower left of Figure 4.4A. "more" if the stay involves more than one room. The
It contains a record for each of the room states - receptionist will then have to open the stay window to
with some of the stay fields added. see which rooms and when.
Find the smallest date and roomID. We want to In order to find out whether there are more than one
combine all records with the same stayID into one, as room in the stay, we compute the Min and Max of
shown on the figure. For instance we have four records roomID and compare them:
with stayID = 727. They make up a bundle of records.
All the records in the bundle have the same stayID, but 8. Add another roomID column in the grid and let it
they have different dates and room numbers. We want compute Max rather than Min (Figure 4.4A). Re-
to compress this bundle into one record - the one name the two roomID columns to A and B as
shown to the right. shown.
9. Use a blank column - no dragging of fields. In the
4. Click the sum symbol on the tool bar (Σ) or right- total line, indicate that it is an Expression (a com-
click in the grid to find the Σ. You will now see a puted value). In the top line specify this expres-
new row in the grid, Total. sion:
room: IIf(A=B, A, "more")
Initially, Access shows Group By everywhere in the
total-line. We want to group records by stayID. For Warning: Depending on the regional settings of
date and roomID we want to find the lowest value in your computer, you may have to use semicolons
each group: instead of commas (see more in section 6.6):
room: IIf(A=B; A; "more")
5. Change the total-setting for the date column to
This expression says that we want a new field called
Min. Do the same for the roomID column. If you
room. If column A (the smallest roomID) is equal to
now change to datasheet view, you should see a
columnB (the largest roomID), then the result shown
shorter table, somewhat like the one to the right.
must be this roomID. Otherwise the result must be the
The heading for the date column now says MinOfDate text "more". The operator IIf is called an immediate if.
and it correctly shows the first date for this stay. The See the result in datasheet mode. It should be as shown
roomID column is similar. at the lower right of Figure 4.4A. The room column is
what we need for the stay list.
You may wonder why we have left guestID and state
as Group By. Actually, when two records have the Also have a look at the SQL-version:
same stayID, they also have the same guestID and
Alias
date)
dle -> Min(
Bun
Also have a look at the SQL-version: First and Last are not parts of SQL because
the result of a true SQL query is a set of
SELECT Min(tblRoomState.date) AS arrival . . . records, not an ordered list of records. It
FROM . . .
GROUP BY tblStay.stayID
makes no sense to talk about the first or
HAVING Min(tblRoomState.date) = #10/21/02# ; last in a set. Ordering of the records into a
list only makes sense when the records are
Notice that in the query grid, we could just set the used outside SQL. As an example, First
criterion on the arrival column. It is tempting to make and Last may be used in Visual Basic to
a similar thing in SQL: find the first or last value in a list shown to
the user.
SELECT Min(tblRoomState.date) AS arrival . . .
FROM . . . Var(x) The variance of the values in column x. A
GROUP BY tblStay.stayID
HAVING arrival = #10/21/02# ;
variance is used in statistical analysis. It
shows how much the x values in the
However, this fails because SQL doesn't compute new bundle deviate from the average x value.
fields such as arrival until all the criteria have been
handled. Thus arrival isn't available at the time when First the Var-function computes the
HAVING is dealt with. So in SQL you have to repeat differences between each x and the
the Min-expression in the HAVING clause. average in the bundle. Then it squares the
differences and finds the total of the
Also notice the date formats. In the query grid we use squares. Finally it finds the average
the regional date formats, but in SQL we must use US squared difference by dividing with
date formats. Count(x)-1.
Count(x) The number of records in the bundle. In statistics, Var(x) is suited if the group is
Records with a null value in column x are a sample of a larger population, while
not counted. VarP(x) is suited for the entire population.
Sum(x) The total of the non-null values in column StDev(x) The standard deviation of the values in
x of the bundle. Null if all values are null. column x. It is the square root of Var(x).
While Var(x) exaggerates large deviations,
Avg(x) The average of the non-null values, i.e. StDev(x) is more like an average
Sum/Count. Null if all values are null. deviation.
Min(x), Max(x) The lowest (highest) non-null value. StDevP(x) The square root of VarP(x).
Null if all values are null.
Stays HAVING
arrival this date.
Local date format
Min(tblA.x + tblB.y )
7. In the grid, use the Sort line for arrival. Set it to 8. In the grid for qryStayArrival, change the room
Decreasing. When you see the list in datasheet expression to this:
mode, the latest arrival is first and the stay-less room: IIf(A <> B, "more", A)
guests last. SQL considers the null value smaller 9. Save qryStayArrival and reopen qryStayList. The
than any other value, so the stay-less guests are result should now be right.
last. (Below we will look at a way to order the
stays according to increasing arrival times, yet When A and B are null, A<>B will be null and SQL
have the stay-less guests last.) will select A. True is required to select "more". So the
result will be A, which is null. Just what we want.
If you look at the SQL version, you see that SQL
expresses the sorting as ORDER BY arrival. Sorting null last. As a finishing touch, we will fix the
ordering problem. The trick is to compute a field where
Null=null? null values become higher than anything else, and then
The erroneous "more" indications are more puzzling. sort by this field:
Let us look closer at the way a query is computed. In
10. In the grid for qryStayList, use an empty column.
section 4.2 we explained that after discarding the
Enter this expression in the top line:
useless Cartesian combinations, the SQL-engine s: IIf( IsNull(arrival), 365000, arrival)
computes the final fields in each record. The room field
Order By arrival
Here we compute a field s by means of another IIf-ex- Show indication for the s-column. Check that the
pression. We ask explicitly whether arrival is null, and list is sorted correctly and doesn't have the s-col-
if so use the large value 365000. Otherwise we use umn.
arrival as it is. Look at the result in datasheet view. 13. Close qryStayList and save the changes. Now open
Notice that 365000 is shown as a large date around it again and look at the query grid. The s-column is
1000 years ahead. Why? Remember that a date is still there, but it is not called "s" anymore. This is
stored as the number of days since December 30, 1899. an example of Access changing the grid behind
Since a year is around 365 days, we have thus specified our back. No harm is done, because it still works
a date around 1000 years after this date. correctly. Access translated the grid into SQL and
when we opened the query again, it constructed the
(Remember the year-2000 problem? We have now cre- grid from the SQL version. Try to look at the SQL
ated a year 2899 problem. We don't care - we will all version to see what the Sort indication and the
be dead at that time. Or should we care anyway?) Show indication becomes in SQL.
11. Now remove the Sort indication from the arrival At this stage we finally have a stay list that is worth
column, and set an Increasing indication for the s- showing in the user's Find Guest window. In the next
column. Check in datasheet view that it looks section we will use it for this purpose and also do a bit
right. about the search criteria.
12. Tiny problem remaining. We don't want the s-col-
umn to appear in the result. In the grid, remove the
User-oriented headings
Mnemonic states
This strange-looking expression means: Look in the 17. Switch to Form mode. The list looks as usual.
collection of forms to find frmFindStay. Then look in Enter for instance an s in the text box. Then click
the collection of controls on this form to find txtName. F9 for requery. You should now see only guests
This is Visual Basic's way of addressing objects, and in with an s in their name.
the next chapter we will look more closely at how it 18. Try other criteria. When you have a blank text box,
works. the system shows the entire list. Why? In this case
it looks for "**", meaning anything followed by
Ready to combine the pieces? anything. All names will match!
14. Open qryFindStay in design mode. Set this crite- In the final system, the user should not use F9 to
rion expression in the name column: search, but our carefully planned and tested buttons and
Like ("*" & forms!frmfindstay!txtname & "*") their shortcuts. In a live search, the system will respond
Don't worry if Access adds brackets as usual. character by character as the user enters a criterion. All
15. Close qryFindStay and open frmFindStay in de- of this needs some Visual Basic programming, and we
sign mode. will show it in section 5.2.2.
16. Select the Last-name text box and set its name to
txtName (on the Other tab). The prefix txt is the
standard prefix for text boxes.
Wanted result
RecordSource = qryStay
Query for
the subform
In this way you specify that you only want to see those user will then see a dialog box where he can enter
child records (in qryStayRooms) where stayID matches the number of persons and possible other editable
stayID in the master form. data. When he closes the button, the VBA program
updates the corresponding room state records.
Now the stay window should look right. Try browsing
through the stays and notice how the room lists vary b) Store query result in a temporary table. The
from one stay to another. alternative is to store the query result as a new,
temporary table. (SQL can do this by means of an
4.7.1 Editing a GROUP BY query INSERT INTO query. See section 7.1) Then you
What kind of actions can the user make on a complex show this table instead of qryRoomState. Since
query table such as qryStayRooms? Actually very this is a table and not a query, the user can edit it.
little. It would make sense to edit the number of Next, the VBA program will have to transfer the
persons in the room, but since the room list is made by changes to tblRoomState.
a Group By, this is impossible. There are two ways out:
Programming issues such as these are the reason many
a) Dialog box. Provide an edit-button that the user software applications don't allow the user to edit di-
can click when he has selected a rooms line. The rectly in what he sees, but offer him a dialog box in-
stead.
Access Database
Open forms
ControlSource, Forms(0)
Properties Textbox
Value, Locked . . . Forms(“frmFindStay”)
Forms ! frmFindStay . Caption = . . .
Combo
Property ControlSource,
Named item Property
Value, Locked . . .
Button in collection
To shorten these long expressions, VBA works with 16. Try printing the number of items in the Forms
default collections. The Controls collection is the de- collection:
?Forms. Count
fault collection on a form. This means that if you omit
Is it correct?
the word Controls, VBA will look in the controls-col-
17. The controls on a form are also a collection, called
lection anyway. You could thus write this instead:
Controls. Try in the same way to print the number
Forms(0) (0) The first control on the first open form. of controls on frmFindStay and fsubStayList.
Forms(0) ("txtName")
The txtName control on the first open form. Even the properties of a control or a form make up a
Forms!frmFindStay!txtName collection, called Properties (see Figure 5.1A). You
The txtName control on frmFindStay. may also look into them with the general addressing
mechanism.
Controls come in many variants (sub-classes), for in-
stance Subform, Text Box, Combo Box, Command Keep frmFindStay open for further exercises in the
button. They all have a name, a position, a height and a next section.
width, but otherwise they have very different proper-
ties. We will look at some of the controls in more detail Subform
later. SourceObject property. A subform control has many
properties, but a particularly interesting one is Sour-
Try it out ceObject. It is the name of the form to show. When we
9. If you have followed the earlier exercises closely, constructed frmFindStay, we set SourceObject to fsub-
the name of the Last-name field should be StayList. We can see it and change it from VBA:
txtName. Try setting the value of this control:
Forms!frmFindStay!txtName. Value = "abc" 18. First display the SourceObject property:
You should see the text box changing on the form. ?Forms!frmFindStay!subStayList. SourceObject
10. The default property of a field is the Value, so this
statement should work the same way: The first part of this statement is the reference to sub-
Forms!frmFindStay!txtName = "def" StayList on frmFindStay. Now comes a dot saying that
frmFindStay
txtName
subStayList
+ fsubStayList
current record
we look for the SourceObject property of subStayList. Was this what you got? Try selecting another record on
The result is the name of the fsub. (Notice that our pre- the list and repeat the command.
fix rules help us distinguish between the subform con-
trol and the fsub connected to this control). Bang versus dot
21. Try using a dot instead of a bang before name:
19. Try setting SourceObject to nothing (an empty text ?Forms!frmFindStay!subStayList. Form. name
in quotes):
Forms!frmFindStay!subStayList. SourceObject = "" It doesn't print the name of the guest, but the name of
The subform in the user window becomes blank. the subform. Because of the dot, the statement asks for
Try setting it back again (it is a text, so remember the name property of the form.
the quotes.)
When you use the bang, you ask for a control on the
As soon as we set SourceObject, Access closes the fsub - or a field in the bound record behind the fsub.
previous subform and opens the new one. The Source-
Object property is a dialogue variable, so a change will Abbreviations. Above we have used the full address
not survive close and open. expressions, but Access allows various abbreviations.
For instance you may refer to the guest control of the
Form property. Above we looked at the SourceObject subform in any of these ways:
property, which is a text - the name of the fsub. But a
subform has another interesting property, Form. It is a Dot instead of bang
Forms!frmFindStay!subStayList. Form. address1
reference to the open form that is shown in the subform
area. You cannot see this property in the property box This works only because there is no address1 property
because it is not a text, but a pointer that only exists in in a form.
user mode. However, you can use the Form property in
VBA. Form omitted
Forms!frmFindStay!subStayList!guest
20. Try this command in the Immediate window:
?Forms!frmFindStay!subStayList. Form!name However, you cannot omit Form and use a dot at the
same time. Access will believe that you ask for a prop-
It should print the name of the guest selected in the erty of the sub-control.
subform area. The first part is the reference to sub-
StayList on frmFindStay. Now comes a dot saying that Warning. The bang mechanism works only in Visual
we look for the Form property of subStayList. The re- Basic. It doesn't work in SQL. For instance you have to
sult is a reference to the open form that is shown in the write
subform area.
... JOIN ON tblGuest.guestID = tblStay.guestID
Next comes a bang and a reference to the name field,
i.e. the address of a guest. Now which guest? The sub- If you write tblGuest!guestID, the system gives an error
form shows many records - which of the addresses will message.
we get? The address in the currently selected record.
AfterUpdate event 5. Enter a criterion in the name field, for instance "a".
We will now write an event procedure that makes Click Enter. The stay list should now shrink ac-
txtName respond when the user has entered a search cording to the new criterion. Enter another crite-
criterion. rion and use Tab. The system should respond.
1. Open frmFindStay, select the txtName search cri- 6. Change the criterion and then - without using En-
terion, and view the property box. (In Access 97 ter, etc. - click in another window, for instance the
you can only do this in Design mode.) property box. The stay list should not change be-
2. Look at the Event tab. It has a list of the events cause txtName still has the focus and Access as-
that the control can respond to (Figure 5.2A). The sumes that the user hasn't finished the field. Now
event we are interested in is AfterUpdate. It hap- click another field on FindStay. The stay list
pens when the user has finished entering the text in should respond.
the control.
3. Create event procedure. Choose AfterUpdate, the 5.2.1 More text box properties
three dots, and Code Builder. This creates the We will now make txtName respond each time the user
event procedure for AfterUpdate. hits a key. To do this, we need to act on another event,
OnChange. This event happens whenever the user
You are now in the Visual Basic window. You may types something in the text box or deletes a character.
make the window smaller so that you can see frmFind- When Access calls this event procedure, the situation is
Stay too. Also make the Access window smaller so that a bit complex because several properties of the text box
you can see the two windows side by side. are involved:
Inside the Visual Basic window you see the event pro- Value: This property is the value before the user
cedure for AfterUpdate (Figure 5.2A, right). The first started editing the text box. Note that Value is the
line reads default property for a text box, so if you don't spec-
Private Sub txtName_AfterUpdate ify another property in your address expression,
It shows that it is the AfterUpdate subroutine (proce- VBA will use Value. When the Value property is
dure) for the txtName control. empty, it has the value Null, similar to a database
field
4. The body of the procedure is initially empty. Now
enter this statement: Text: The value the user enters, but hasn't finished yet.
Me!subStayList . Requery This is the text the user sees on the screen, but it is
not yet stored in Value. You can only access this
Me. The word Me means the open form where this property when the text box has the focus. At other
control is placed - in this case the same as times, the Text variable doesn't exist at all. When
Forms!frmFindStay. We ask Access to find the the Text property is empty, it is a zero-length text
subStayList control. Finally we ask Access to call the with the value "", similar to a VBA string variable.
procedure Requery in this control. The result is that
subStayList recomputes the query behind the subform. OldValue: The value before the user started editing the
Since the query gets its where-condition from txtName, text. For bound controls (bound to a field in the
the stay list should change. database), this is the same as the value in the
database. When the entire record is complete,
Omitting Me. In most cases you can omit Me. You Access transfers Value to OldValue. OldValue is
may for instance write useful when the editing must be undone for some
subStayList . Requery
reason. For unbound controls, OldValue and Value
are always the same.
The exception is when some built-in function or
property has the same name as the control. In the
Form
Form
module
Define new
record source
Shared procedure Later, the SQL engine will compare the dates in their
When should this tricky piece of program be executed? number form, and everything works fine.
If the screen used an ordinary Search button, the
program should be the event procedure for the button. Why all this fuzz? Couldn't we simply write:
" AND arrival = " & txtArrival
However, we want to make a live search. In this case
the program must be executed whenever one of the When Access appends txtArrival, it converts it to text
criteria changes value, i.e. for a whole bunch of event format by means of the CStr function. This function
procedures. produces a date in the regional date format set up in
MS Windows. With a European date format, we would
The solution is to make the program piece a separate
get this SQL fragment:
procedure. It starts with Private Sub and ends with End AND arrival = 21-10-07
Sub (see more on procedures in section 6.2). The
appropriate event procedures call this procedure as SQL would reject it since dates have to be enclosed by
shown for txtName_Change, txtArrival_AfterUpdate # #. So what about this:
and chkBooked_AfterUpdate. " AND arrival = #" & txtArrival & "#"
chkCheckedIn
txtStreet
chkOther
txtPhone
txtArrival
Now we would get this fragment: state (a NULL state). Stays with a NULL state are not
" AND arrival = #21-10-07# real stays, but records generated by the outer join for
guests that don't have a stay.
Looks all right, but unfortunately, SQL expects dates in
the US format. In this case it would reject 21 as a non- Initialize the form
existing month, in other cases produce a wrong result. A final touch is to make the Find Guest screen
initialize itself properly:
Fortunately, the Access database engine can treat dates
as double numbers, but other databases may not. In • Let the three checkboxes have a mark initially.
such cases you would have to generate the dates with Otherwise, the stay list will be empty initially and
an explicit US-format, for instance in this way: the user may panic. To do this, set the initial value
" AND arrival = #" & Format(txtArrival, "mm/dd/yyyy") & "#"
of the boxes through their property sheet.
Empty texts
• Let the Form's Load event procedure call the
An empty field in the database has the value Null for
search procedure in order to make the stay list
all types of fields. The same applies for the Value in an
match the initial criteria:
empty text box. To test for an empty field , we would
ask for IsNull(f). In a VBA string variable, an empty Private Sub Form_Load()
text is a zero-length text with the value "". Since Call search
copyName is a string variable, we ask whether the text End Sub
is different from "".
Try it out
Null values You should try to make the solution work in practice. It
Notice how the program deals with Other stays, i.e. is a challenge, but fun. Make a copy of frmFindStay
stays that are neither booked nor checked-in. These and use it for the experiment.
stays include those with state > 2 and those without a
foreUpdate procedure may cancel further event User clicks outside Access. The text box does not re-
processing so that the database is not updated. ceive any events (except for MouseMove events).
All properties survive. When the user clicks on the
If everything is okay, Access stores all the bound Form again - even on the title bar - focus will be
control values in the database and in OldValue, back at the text box, but it receives no events at that
then calls the AfterUpdate procedure. Finally, point.
Access calls the Current procedure to signal that a
new record is selected. The OldValue property allows the program to undo
changes to bound controls until the moment when the
User clicks outside the form, but inside the same form receives an AfterUpdate event. To undo a change,
Access window. Access generates a LostFocus the program sets Value=OldValue.
event to show that typing will go to another object
in Access. In this case the Text property does not Key Preview. In some cases we want a key or key
disappear. combination to perform the same thing no matter
which control is selected. One example is function
User clicks in form again. The active text box keys. For instance we may want F2 to mean Reset
receives a GotFocus event. The Text property has Criteria no matter where in the form the cursor is. This
survived. can be handled by letting the Form look at the key
before any control gets to know about it. See section
5.5.9 for this Key Preview function.
Docking and undocking, Access 2000 and 2003 You cannot open several versions of the form just by
The Visual Basic window may contain many frames. In clicking multiple times in the database window, but
Access 2000 and 2003, they may be docked inside the you may do it from Visual Basic. Then you get more
window or undocked, i.e. floating as separate small form objects, appearing as other open copies of the
windows. By accident you may dock or undock them, form. Each copy has its own variables, but the same
and it may be very frustrating trying to get them back code. When the code uses addresses such as
where you want. So better learn how to deal with it: Me.txtName, it refers to the controls and variables of
this particular copy of the form.
2. Make sure the Visual Basic window occupies only
part of the screen. When you create a form, you don't get its form module
3. Make sure the Docking settings are all on: Go to until you create its first event procedure.
Tools -> Options -> Docking and make sure that all
the checkboxes are set. Close the option box. Class module. A class module corresponds to a class
4. Click the Project Explorer icon. The window may in other object-oriented languages. It has proce-
now look like Figure 5.3A with a list of forms at the dures and declares variables, and you can create
left (the Explorer frame) and some program code at multiple objects based on the class, each with their
the right. However, it may also show only the Ex- own variables. The only difference between form
plorer frame, or show the Explorer frame as a small, modules and class modules is that the latter are not
floating window. visible to the user and have no controls.
5. Drag the Explorer frame to somewhere outside the
Visual Basic window. The Explorer frame is now See section 5.7 on how to create modules.
undocked.
6. Drag the Explorer frame to somewhere inside the Module (simple). A simple module is similar to a class
Visual Basic window. The frame will dock some- but there is only one object based on the module.
where along a side of the window. Where it docks The system creates this object automatically. The
does not depend on where the frame is when you first versions of Visual Basic had no class modules,
release the mouse button. It depends on where the only simple modules.
mouse pointer is when you release the button.
7. Try dragging the frame around in the Visual Basic Code window
window. Notice that when the mouse pointer is To the right in the Visual Basic window, you see the
close to one of the sides, the frame changes to a thin code window with the Visual Basic program. Figure
line showing the new shape of the frame. Release 5.3A shows the code module for frmFindStay. You see
the button to dock the frame accordingly. three event procedures for the txtName control. You
8. Try double-clicking the title bar of the frame. The can scroll to other event procedures and controls, or
frame toggles between docked and undocked. you can select them by means of the two combo boxes
Leave it docked as on Figure 5.3A. at the top.
Project explorer:
One class per form
Code module
if you have created the event procedure while looking the code doesn't happen until you close the form it be-
at the code in debug mode (see page 82). longs to. Sometimes you may want to save the code
explicitly. Use File -> Save (or Ctrl+S).
The cure may be to select the event procedure from the
property box and compile the module (Debug -> Debug command and event logging
Compile), then close and open the form. The statement Debug.Print prints its parameters in the
Immediate window (also called the debug window). As
9. Create the missing event procedures for txtName as an example, when the AfterUpdate event occurs, the
shown on the Figure. Use the combo boxes to event procedure will print the text "After" followed by
create them. If you have followed the previous steps the current Value and Text. The event procedure for
closely, only the Enter procedure should be miss- Change behaves similarly, and as a result, the Immedi-
ing. ate window will show a log of what happened. Try it:
10. Type the debug statements shown in the proce-
dures, for instance: 11. Open the Immediate window with Ctrl+G. Adjust
Debug.Print "After", Me.txtName, Me.txtName.Text
the sizes so that you can see the Immediate window
When executed, this statement prints something in as well as frmFindStay.
the Immediate window. We will explain more on 12. Type something in txtName. The Immediate win-
the Debug statement below. dow should log what happens.
Note that VBA automatically capitalizes the words that This is the hard way to find out exactly which events
it recognizes. If it doesn't, it may be because you have occur and what the situation is at these points. During
spelled the word incorrectly, but it may also be because such experiments, you may want to temporarily disable
the word comes after the bang operator (!). VBA can- some statements. For instance, we might want to dis-
not recognize things after the bang at edit time, but it able one of the debug statements to avoid too many
will check all the words at execution time. At that time lines in the Immediate window.
it will give you an error message if it cannot recognize
the word. 13. Comment-away statements that you don't want for
the moment. Set an apostrophe (a "ping") at the be-
Deleting an event procedure. If you want to delete an ginning of the line. When you move the cursor, the
event procedure, simply delete all the lines of the pro- line turns green to show that this is a comment for
cedure. Don't try to delete it on the Event tab. humans only.
Closing the VBA window. You can close the VBA
window at any time. It only hides the window. Saving
16. Current value. Try moving the cursor to an ex- Pop-up help
pression, for instance the Debug parameter You have probably noticed that as you type a state-
Me.txtName. After a moment, VBA shows the cur- ment, Access often shows a list of what you can type at
rent value of this expression. You can see the value this point. You may bring up several kinds of lists:
of expressions in the procedure where the execution
stopped, but not values in other procedures because • Ctrl+J brings up the property list. It shows you
they are not active at present. the possible properties, procedures, and controls at
this point of typing. You may choose one with the
17. You can use the Immediate window to try out vari- Tab-key.
ous statements. The statements are executed as if
they were written where the breakpoint is. Try for • Ctrl+Shift+J brings up the Constant list. It shows
instance you the possible named constants at this point of
Me.txtName.Text = "abc" (should change the text in typing. (Ctrl+J may be used too, but it brings up
the form) the full list of named constants.)
? Me.subStayList.Form!name (should print the
name of the first guest in the list) • Ctrl+I brings up the Quick Info list. It shows the
data type you are dealing with, or the list of pa-
In section 5.1 we used the Immediate window with rameters to the procedure you call, or the value of
statements such as a named constant.
Forms!frmFindStay!txtName = . . .
Now you can use Me. The reason is that now the Im- Try the pop-up help
mediate window runs in the context of the event proce- 18. Start entering this statement in the Change proce-
dure. It can address the form object in the same way as dure (Figure 5.3B):
the program. MsgBox "test", vbYesNo
When you have typed MsgBox, use Ctrl+I to bring
Correcting bugs at breakpoints. Using the Immediate up the list of parameters. Notice that most of the pa-
window in connection with a breakpoint is an impor- rameters are optional (enclosed in brackets). When
tant way to find out what the program does. If you find you have typed the comma, bring up the list of pos-
an error in the program, you may usually correct it sible constants with Ctrl+Shift+J. Select the right
while at the breakpoint. However, sometimes VBA constant with the Tab-key.
cannot do the correction, for instance if you delete an
event procedure. It asks whether you want to "reset the When executed, the MsgBox statement will show a
project". This question sounds threatening, but simply message to the user and ask for a Yes or No. (In section
means "stop the program execution and restart it from 3.6, we used MsgBox to print messages for a tool-
the beginning". Nothing to worry about - nothing is based mockup.)
lost.
19. Put the cursor on RecordSource and use Ctrl+J to
Continue after breakpoint. When you have made bring up a list of possible properties and procedures
your experiments at the breakpoint, you can resume at this point.
ordinary program execution. There are several ways to
resume execution (Figure 5.3A): 20. Put the cursor on txtName and use Ctrl+J to bring
up a list of possible controls at this point.
Property list
Ctrl+J
Debug actions:
Run F5
Step Into F8
Step Over Shift F8
Break Ctrl+Break
Open Immediate window Ctrl+G
Breakpoint
Quick Info Constant list
Ctrl+I Ctrl+Shift+J
Object Browser
F2
Sometimes a
good explanation.
Else use F1.
Object Browser (F2) and Help (F1) cursor on the word in the VBA code, then use F1. Usu-
The Object Browser gives an overview of the classes in ally you get good help. Try it:
Access, Visual Basic, and your own database. Open the
browser with F2 (Figure 5.3B). To the left you see a 21. Put the cursor on the word MsgBox and use F1.
list of the classes, to the right all properties and proce- You get an excellent explanation of the procedure
dures of the selected class (called "members" in the and its parameters.
window). In the figure we have selected the SubForm
22. Put the cursor on the word RecordSource in the
class. The right-hand side shows the properties and
code and use F1. You get a reasonable explanation.
event procedures.
23. The quality of the help information varies. Put the
For some properties and procedures, VBA shows a
cursor on the word Debug in the code and use F1.
good explanation at the bottom of the window, for oth-
You may get the message Can't find project or li-
ers you get a good explanation with F1 (Help). The
brary. Or you get an explanation, but not an excit-
Object Browser doesn't show all the classes available.
ing one. You may try F1 with the cursor on the
For instance you will look in vain for the Debug class,
word Print. You get a lot of information, although it
which we have used several times already.
is hard to find out what it means.
Another way to get help about classes, properties, and
language structures such as if-then, is to position the
Open a form to show only one record Default button and Cancel button
You may use OpenForm to open a specific stay: A form may have a default button. If the focus is
somewhere on the form or its subforms, and the user
3. Try to change the procedure body to presses Enter, the default button gets a click event.
DoCmd.OpenForm "frmStay", , , "stayid=2" (The exception is when the focus is on another button.
Then this button gets the click, of course.)
If you try it in user mode, you see that stay 2 is visible
and the user may edit it. Parameter 4 did the trick. It is You may define any button as the default. In the prop-
a filter - a text that automatically enters an SQL- erty box for the button, select the Other tab and set
WHERE clause and restricts the visible stays. In prac- Default = Yes
tice, the program must compute the filter text so that 2
in the example becomes the stayID the user has chosen. Access will automatically set Default = No for the
previous default button on the form.
Section 5.5.2 explains more about opening a form for
various purposes. Section 5.5.3 explains the many A form may also have a cancel button. If the focus is
parameters for OpenForm. somewhere on the form or its subforms, and the user
presses Escape, the cancel button gets a click event.
Make a button reset the search criteria You may select any button as the cancel button. In the
The real FindGuest window has a button for resetting property box, select the Other tab and set
the search criteria. Try to add it: Cancel = Yes
4. In design mode, add a button to frmFindStay. Give You will typically let the cancel button close the form
it the label Reset criteria and the name cmdReset. without saving anything. This can be done with these
statements in the event procedure:
5. Define the event procedure for the OnClick event. Me.Undo
DoCmd.Close
The procedure body should be:
Me.subStayList.Form.RecordSource = _
"select * from qryStayList; " The Undo procedure sets all fields on the form to their
Me.txtName = "" OldValue. As a result, Access will not save them at
close.
Deactivate( ). This event signals that the form will lose ues on the form's data tab. You can change them at run
focus. It occurs during close and when the user time through Visual Basic:
clicks in another main form. It doesn't occur when
the user clicks in another application. Subforms AllowEdits. If True, the user can edit fields in existing
never receive a Deactivate event. records. Default=True.
Close( ). This is the last event the form receives. The AllowDeletions. If True, the user can delete a record
form is still visible and all subforms are still open. with the Del key. (Requires that the record selector
When Close returns, the form becomes invisible, all is displayed in the form.) Default=True.
subforms are closed, and data structures are deleted.
AllowAdditions. If True, there will be an empty record
5.5.2 CRUD control in Forms at the end of the list for adding new records.
When you create a form in the default way and open it Default = True.
with DoCmd.OpenForm, the user is allowed to step
Filter. A text that works as a WHERE clause in the
through all records, and add several new records. De-
query behind the form. For instance, you may set
pending on circumstances, we may want a more re-
filter to "stayID=740". The result will be that the
stricted behavior. We may for instance want the user to
user only sees the stay with ID=740. (Don't write
see and edit only one single stay. Or we may want the
the word WHERE itself.)
user to create only one new record.
FilterOn. If True, the Filter property works and selects
CRUD and filter properties records. If False, the filter has no effect. FilterOn is
The way to control this is through the following prop- a property that you cannot see in the form's prop-
erties, called CRUD properties because they specify erty box, but you can set it through the program or
Create, Read, Update, and Delete of records (Figure
5.5B gives an overview). You can see their initial val-
All of these properties are dialog variables that don't Open the form with
survive closing of the form. What you have specified in DoCmd.OpenForm "frmStay"
the form's property box are simply the default value for
these variables. You may try out the properties in this Prevent two blank lines in datasheet
way: The method above for creating a single record works
okay when the Form is shown in normal mode. In
1. Open frmStay in Datasheet mode. You should see Datasheet mode, however, the user may be confused at
all records from the stay query. At the end of the what happens. The user sees the blank record at the end
datasheet you should see an empty line for entering of the datasheet, but as soon as he starts typing in it,
a new record. another blank line appears.
2. Open the Immediate window with Ctrl+G. Adjust Access creates the second blank line right after calling
the sizes so that you see the datasheet and the Im- the Dirty-event procedure. However, it is impossible to
mediate window at the same time. Try to change adjust things at this point because the user's character
the settings with statements such as: hasn't yet ended where it should be.
Forms!frmStay.AllowAdditions = False
Forms!frmStay.Filter = "stayid=740"
Forms!frmStay.FilterOn = True
The solution is to store something in the new blank
The datasheet should change accordingly, removing record before the user types anything. This initiates a
the empty line, showing only one record, etc. temporary record for further editing. Then AllowAddi-
tions is set to False to prevent further new records. This
See and edit a single record can be done at the Current event:
In order to see and edit a single record, set these prop- Private Sub Form_Current()
erties in the form's property box (see overview in Fig- If Not IsNull(Me.someField) Then Exit Sub
ure 5.5B): Me.someField = " "
AllowEdits = True Me.AllowAdditions = False
AllowDeletions, AllowAdditions, DataEntry = False Me.someField = Null
End Sub
Then open the form with a filter, for instance:
DoCmd.OpenForm "frmStay", , , "StayID=2" The first statement checks whether something has been
stored in the field to be initialized. In this case the user
In practice, the program must compute the filter text so has selected an existing record and nothing should be
that 2 in the example becomes the stayID the user has done now. The next statement stores a space in the
chosen. field. This creates the temporary record. Next Allow-
Additions is set to False. This prevents further records
Update the record. When the user enters something, it being created. Finally the field is set back to the initial
is not stored in the database until he closes the form (or empty state.
enters a subform). Sometimes storing is needed earlier.
The program can do it with this statement: The solution must be extended with means to add an-
Me.Recordset.Move(0) other record, for instance a button or a response to
Enter. Care must also be taken not to leave empty
AllowEdits
DataEntry
Deletions
Additions
AllowEdits: See and edit data
Allow-
Allow-
AllowDeletions: Del-key works
AllowAdditions: Empty record at end
DataEntry: Old data invisible To see and edit a single record: True False False False
Filter: e.g. StayID=740 (Also set the filter)
FilterOn: Use filter To create a single record:
AllowFilters: User-controlled filter Initially x False True True
At Form_AfterUpdate x False False True
acFormEdit True True True False
acFormAdd True True True True
acFormReadOnly False False False False
acFormPropertySettings (default) x x x x
records in the database in case the user doesn't enter user can do anything to the records. At run time
something. these settings overrule the initial settings from the
Form's property box.
5.5.3 The OpenForm parameters
From VBA you normally open a form with As another example, acFormPropertySettings (the
DoCmd.OpenForm. A lot of parameters determine default), doesn't set any of the CRUD properties but
what goes on (see the details on Figure 5.5B). lets the initial settings rule.
Form name. The first parameter is the name of the WindowMode. Specifies whether the form is to be
form, for instance "frmStay". shown as a dialog box, a hidden window, an icon,
or a normal window. A dialog box keeps the focus
View. The second parameter specifies whether the until the user closes it. It has borders that cannot be
form is to be shown in design view, as a datasheet, resized. A hidden window is like a normal window
as a normal form (default), or as a preview of a except that it is invisible. The event procedures can
print. make it visible with me.Visible=True. The window
mode acIcon seems to have no effect.
Filter name. The name of a query, for instance
"qryStay". (I have not been able to figure out what OpenArgs. This parameter is stored in the property
this parameter does.) OpenArgs in the Form. It can tell the event proce-
dures in the Form what to do. The parameter might
Filter. A WHERE condition for the records to show, for instance specify whether the form is to be used
for instance "StayID=5". for creating a new record or viewing an existing
one, in this way allowing the Form to do it in its
CRUDproperties. A choice of CRUD combinations own way. Below we show how this is used in the
that determine whether records can be edited, hotel system to let the same form handle a new
added, etc. Figure 5.5B shows that acFormEdit sets guest, a new stay, or an existing stay.
AllowEdits, AllowDeletions, AllowAdditions to
True and DataEntry to False. In other words, the
Notice the If-Then-Else-EndIf construct that makes the First we set the guest name to a single space character.
program choose alternative paths, either executing the This creates a new guest record, and Access gives it an
Then-statements or the Else-statements. The line- AutoNumber. Notice how we address name with a
breaks must be as shown, with Else and EndIf on lines bang ( ! ) to distinguish it from the built-in Name prop-
of their own. erty.
Notice also the comment we have put after Then to Next we set the foreign key of the stay record to this
explain to the reader what situation the program deals guest. This creates the stay record and also links it
with after Then. properly to the guest. Then we set the name back to a
Null so that the user doesn't see a name starting with a
cmdShowStay space when he enters the guest name. Notice the
Here we have three situations. (1) A stay is selected. strange use of square parentheses. We explain more
(2) A guest without stay is selected. (3) The list is about them below.
empty. In the last two cases, the program cannot open a
stay. The current value of stayID allows the program to
Bind to one
old Stay
Finally we set AllowAdditions to False so that Page- Finally we set AllowAdditions to False to prevent fur-
Down will not bring the user to a new empty record. ther record creation.
OpenArgs is > 0: The guest exists already and Open- The strange [ ]
Args is the guestID. First we allow additions and data The program has a strange use of square parentheses.
entry. Data entry makes the current record a new The reason is that the query behind the form has two
"blank line". Next we set the foreign key of the stay guestID fields, one from each table. None of these are
record. This creates the stay record and links it to the visible on the form, but they are available anyway as
guest. Access automatically joins it to the guest, so the fields. The SQL-engine has given them the names
guest data is now in the "blank line".
Note that we use the dot-operator after Me to address Notice that we write MsgBox with parameters in a pa-
these fields. This allows us to use Ctrl+J to get a list of renthesis when we need to look at the return value. Use
all properties and fields. You will find tblStay.guestID F1 to see the help information about MsgBox. There is
on the list, but you have to add the parenthesis your- an excellent explanation of all the details.
self.
Multi-line message
See the mechanisms live When the message is more than one line, you have to
The Load event is the more interesting part of the solu- compose it in this way:
MsgBox "line 1" & Chr(10) & "line 2", vbYesNo
tion. You may try it out in this way:
1. Open frmStay and use the property sheet to give it a Chr(10) is the new-line character, or more precisely a
Load-event procedure. Type in the procedure from text consisting of the new-line character. In Visual
Figure 5.5C. Set a breakpoint at the first line. Close Basic you cannot write a new-line character inside the
the form. text string itself.
2. Open the Immediate window with Ctrl+G. Now Use OpenForm with acDialog
simulate the NewStay button with this command in • Make a Form in the usual way with data fields,
the Immediate window: buttons, etc. Open it with DoCmd and the acDia-
docmd.OpenForm "frmstaymono",acFormDS , , , , ,2 log parameter, for instance:
DoCmd.OpenForm "form name", , , , ,acDialog
3. The command starts opening the form and will
show it in Datasheet mode. The Load procedure OpenForm sets the right border on the form and makes
stops at the breakpoint, but the form is not yet visi- it modal. There is no return value. The form has to
ble. Make it visible with this command in the Im- store results somewhere, for instance in global vari-
mediate window: ables or in the database.
me.Visible = True
Set the dialog properties of the Form
4. The form should now appear in Datasheet mode. The last way to make a dialog box is to set the Form
Step through the Load procedure with F8 to see properties yourself:
what happens to the datasheet. When the procedure
sets DataEntry, the datasheet should reduce to a • On the Format tab for the form, set Border Style to
blank line. When the procedure stores OpenArgs in Dialog. The effect of this is that the border of the
the record, the guest data should appear in the blank Form is narrow and the user cannot drag the bor-
line. ders to change the size.
You may try out the other cases in the same manner. • On the Other tab set PopUp to Yes. When you
open the form, it stays on top of other forms. It is
5.5.5 Dialog boxes (modal dialog) not modal however. You can work with other
Many of the boxes you see in Windows applications forms while the dialog form is open.
are dialog boxes. When a dialog box pops up, you have
to fill in what is asked for and then close the box. You • On the Other tab set Modal to Yes. In Access 97
cannot look around at other windows until you have this makes the form modal. When open, the user
closed that dialog box. This is a modal dialog. One cannot work with other forms. In Access 2000 and
example is a message box. Another example is the Op- 2003 the Modal property has no effect at all (al-
tions box that many applications have. though the help text says it has).
A dialog box is a special case of a Form. Apart from You may then open the form with DoCmd.OpenForm
being modal, it has narrow borders, and the user cannot without specifying the acDialog parameter.
drag the borders to change the size of the box.
Move and see one record The recordset has a FindFirst function with a condition
First we will look at the situation where we want to as a parameter. It finds the first record in the set that
navigate to the guest selected by the guestID. The user matches this condition. Then it makes this record the
should not see other guests than this one. current record.
The solution is to set the filter properties of the Form The two arrow buttons also work directly on the re-
when the user selects something with the combo box. cordset. They use the MovePrevious and MoveNext
The AfterUpdate procedure for the combo box could functions for moving current to the previous or next
look like this (top left of the figure): record. Actually, these buttons do the same as Page-
Down and PageUp. Section 5.6 explains more about
Private Sub cboGuestID_AfterUpdate() recordsets.
Me.FilterOn = True
Me.Filter = "guestID=" & Me.cboGuestID
End Sub
Unbound Scroll to
control previous
Scroll to
next
Remember that each column corresponds to a control SelWidth (attribute, read and write). The number of
on the form (section 3.2.1). As an example, column 2 columns selected. Hidden columns are included. If
on Figure 5.5E corresponds to a text box control bound SelWidth is zero, no area is selected, but the record
to the description field of the table. The column is still selected.
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
The program can detect and set column order, etc. columns are numbered from 1 and up. In Access
through these three attributes of the controls: 2000 they are numbered from 2 and up when an
area is selected.
ColumnHidden (attribute, read and write). True or
False. Beware: The strange change from 1-based numbering
to 2-based numbering must have puzzled quite a
ColumnOrder (attribute, read and write). The number number of developers. Obviously it is an error. The
of the column. Columns are counted from 1 and up situation is even stranger when only a record is
in the sequence that the user sees. However, hidden selected, but not an area. In this case you set SelLeft
columns are included in the counts. with 1-based numbers, but retrieve it with 2-based
numbers.
ColumnWidth (attribute, read and write). The width of
the column measured in twips (see section 5.5.13). If columns have been reordered, their numbers follow
If the program sets ColumnWidth to -2, the width is what the user sees. However, hidden columns are
adjusted to fit the data in the column. Width = -1 included in the counts.
means a default width. Width = 0 makes the column
hidden (and you have to explicitly unhide it - Keeping the area selected
setting the width is not enough). In the hotel system, the user selects rooms through a
form like Figure 5.5E. In principle this is easy.
Example: Suppose the program needs to change the However, as soon as focus moves from the subform to
column width of Class (the description control) in the main form, Access removes the area selection. This
Figure 5.5E. An event procedure on the main form happens for instance when the user clicks a button in
could do it in this way: the main form. The event procedure behind the button
wouldn't even know which area was selected.
Me.subRoomGrid.Form ! description.ColumnWidth = 1500
The program has to compensate for Access's strange
behavior. When focus leaves the subform, the Exit
5.5.8 Area selection, SelTop, etc. event procedure saves the area size attributes before
When a subform is shown in table view, the user can
Access removes the area selection. When some control
select a rectangular area of the subform. Figure 5.5E
on the main form gets the focus, its event procedure
shows an example where the user has selected column
sets them back. There is no common event on the main
3 and 4 from the fourth and fifth row (see more about
form that can do it, so each control has to act.
the room grid in sections 7.4 and 7.5).
Main form
The program can detect which area the user has
In the main form, declare these variables:
selected. The figure shows how we can try it out
through the Immediate window (Ctrl+G). We address Public aWidth As Long, aHeight As Long, aLeft As Long
the first open form, its subform control, and the ' These variables keep track of the current area size. aleft is
subform it is bound to. When asking for the value of the correct column number, not the Access2000 distortion.
SelTop is not saved since current record always is the top.
SelTop, we get 4 because the top row in the selected
area is record number 4. 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.
SelTop = 4
ColumnHidden = False SelLeft = 4 (3 in Access 97)
ColumnOrder = 2 SelHeight = 2
ColumnWidth = 920 (twips) SelWidth = 2
Private Sub subRoomGrid_Exit(Cancel As Integer) But what happens if the user clicks a cell to move the
' This event happens when focus moves from the subform to
area to another room? Access will remove the area
the main form. Save the current area size before Access
removes it. selection and the user will just see the cursor flashing
aWidth = subRoomGrid.Form.SelWidth in the cell he clicked. The user may drag the cursor to
aHeight = subRoomGrid.Form.SelHeight select an area, or use Shift + click, but it is not
aLeft = subRoomGrid.Form.SelLeft - 1
intuitive. The program should make sure that a suitable
' -1 to correct for the Access2000 error
End Sub area is selected at any time.
Private Sub resetSelection() ' Shared procedure One problem is that as soon as the user clicks a cell,
subRoomGrid.Form.SelWidth = aWidth Access removes the area selection before calling any
subRoomGrid.Form.SelHeight = aHeight
event procedure. The program cannot catch the old
' Make sure to set Width and Height before setting Left
subRoomGrid.Form.SelLeft = aLeft + 1 selection at this point.
' +1 to compensate for the Access 2000 error.
End Sub The solution is to let the program act at MouseUp and
KeyUp. There are two situations:
Private Sub txt . . ._GotFocus()
' Reset the area size when a main-form control gets the focus.
Call resetSelection()
An area is selected: This happens if the user expanded
End Sub the area with Shift + arrow key, or if the user
dragged an area with the mouse. The program
. . . (GotFocus for other controls) should save the area size.
Private Sub subRoomGrid_Enter()
No area is selected: This happens if the user has
' When focus moves from the main form to the subform.
Access removes the area selection just before this event clicked a cell or moved to the cell with an arrow
(Access 2000 only). So reset the area size key. The program should reset the area size so that
Call resetSelection() the selected cell becomes the top left of the area.
End Sub
Each single control on the subform has to react this
User actions in the subform way. Although there is a Form_MouseUp and a
The solution above works fine when an area is selected Form_KeyUp, they don't respond to clicks inside the
in the subform. For the hotel system, the program grid. They respond only to clicks on the grid border,
selects a suitable area corresponding to a free room. If i.e. the headings and the record selectors.
the user clicks Check in, for instance, everything is
okay. In the hotel system, fsubRoomGrid has one control for
each column in the grid. The names of these controls
If the user expands the area by means of Shift + arrow are cbo1, cbo2, etc. (see section 7.5 for details). So we
key, everything is fine too. need these procedures on the subform:
5.5.9 Key preview Me refers to the subform, and Me.Parent refers to the
When the user clicks something on the keyboard, the parent form, i.e. the master form.
control in focus will receive several key events. How-
ever, the Form can have a look at the key events before Return value
the control gets the events. This is useful when no spe- At return from the preview procedure, Access contin-
cific control has to take action. ues processing the event. It sends it to the control in
focus, or makes its own response to the event. In case
One example is function keys. For instance we may of F2 the control ignores F2, but Access has a standard
want F2 to mean Reset Criteria no matter where in the response: it toggles between selecting the entire field
form the cursor is. For the Find Guest window it can be and just showing the simple cursor.
done in this way:
The preview procedure can skip further processing by
• On frmFindStay, open the Form-property box, se- setting KeyCode=0 at return. In case of F2, the result
lect the Event tab and set Key Preview to Yes. would be that Access didn't toggle between field selec-
tion and the simple cursor.
• Define an event procedure for the Form's KeyDown
event. It should look like this: 5.5.10 Error preview
Access detects various kinds of user errors, for instance
Private Sub Form_KeyDown (KeyCode As Integer, _ that the user enters a non-existing date, or the user
Shift As Integer)
If KeyCode = vbKeyF2 Then forgets to select a related record where referential
Me.subStayList. Form. RecordSource = _ integrity is required. Access will show an error
"select * from qryStayList; " message that often is entirely gibberish to the user.
Me.txtName = "" When for instance the referential integrity is violated,
End If
End Sub Access will say:
The KeyDown event has two parameters. KeyCode (an You cannot add or change a record because a related
integer) shows which key is pressed. There are prede- record is required in table 'tblGuest'.
fined constants for the various keys. For instance
vbKeyF2 means the F2 key, vbKeyA the A-key and How can the program show a meaningful message
vbKeyEscape the Esc key. instead? The BeforeUpdate procedure is intended to let
the program check the data, but when Access detects
If the procedure returns with KeyCode = 0, Access the error it doesn't even call BeforeUpdate.
stops further processing of the KeyDown event.
However, the Form has an Error event procedure that
Shift (an integer) shows whether some of the shift keys can interfere before Access shows the error message to
were down at the same time (Shift, Alt, Ctrl). There are the user. To use it, define the event procedure for the
predefined constants for the shift keys: acShiftMask Error event:
(=1), acCtrlMask (=2), acAltMask (=4). The program
Private Sub Form_Error(DataErr As Integer, _
can for instance test whether Shift and Ctrl are down at Response As Integer)
the same time with this statement If DataErr = 3201 Then
If Shift = acShiftMask + acCtrlMask Then . . . MsgBox "Select a guest", vbOKOnly + vbExclamation
Response = acDataErrContinue
In the example above, the procedure tests whether the End If
End Sub
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 The Error event has two parameters. DataErr is an ID
empty text. 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 =
When the Form opens, it sets the timer interval to one A long computation runs in a loop until the computa-
minute and waits for some event. The event may be the tion is finished. Once in the loop it displays the
user doing something or one minute having passed. progress of the computation by storing a result in a text
When the minute has passed, Access calls the Timer box control. It could look like this:
event procedure, which recalculates all bound data in
the form. Private Sub cmdCompute_Click() ' Plain loop
While . . . ' Until calculation is finished
. . . ' One step in a long calculation
The next timer event will occur one minute after the
txt . . . = . . . ' Display progress
first timer event, even if the recalculation has taken Wend
several seconds. How would the program stop the timer End Sub
events? Set the timer interval to 0:
In order to respond to the stop, we modify the loop so
Me.TimerInterval = 0 ' Stop the timer. 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
Private Sub cmdCompute_Click() ' Test once in loop Set stay2 = New Form_frmStay
bStop = False Call stay2.Init(753)
While Not bStop And . . . ' Until stop or calculation finished stay2.Visible = True
. . . ' One step in a long calculation
txt . . . = . . . ' Display progress The first line declares two references to an open form,
SendKeys "{home}", True ' Type a character and wait.
' Allows Access to handle all events and stay1 and stay2. The declarations may be in a global
' update the screen. module that remains open as long as Access is open.
Wend
End Sub In an event procedure somewhere else, the statement
Set stay1 = . . . creates a new object of the class
SendKeys has two parameters. The first is a string of Form_frmStay. The reference stay1 will now refer to this
characters. They are sent to Access as if the user typed object. During the creation, the form object receives the
them. The second is a Boolean. If it is True, SendKeys usual open-events: Open, Load, Activate. The form is
waits for Access to process all pending events and still invisible.
update the screen. The string can hold several
characters, e.g. "abc{home}+-". Special characters are When we open a form in this way, we cannot give it
shown as a mnemonic in { }. We have chosen {home} OpenArgs like those we use with DoCmd.OpenForm.
since it is a rather harmless character in the user dialog. The form has a property called OpenArgs, but it is
ReadOnly so we cannot store anything in it. For this
SendKeys takes around 1 ms on a plain computer. In reason we have written a procedure for initializing the
the IT world this is quite slow. If the calculation step form, the procedure Init. We call it with a parameter
for instance takes 0.01 ms, the program suddenly that tells it to open stay 740. The procedure will set the
becomes 100 times slower. filter of the form to show this stay.
One solution is to let the program listen and update the Finally, we set the Visible attribute of the form to True.
screen only once a second. In the procedure below, we This generates the Current event and makes the form
use the Time() function to find out when a second has visible.
passed. It returns the number of seconds since
midnight, with fractional seconds too. The Time Then we repeat the whole thing using stay2 instead of
function itself takes around 0.0005 ms, so it will rarely stay1. The result is that one more form instance opens,
slow down the loop significantly. this time showing stay 753.
Private Sub cmdCompute_Click() ' Test once a second Closing the forms
Dim startTime As Double
Usually the form closes itself, for instance when the
bStop = False
startTime = Timer() +1 user clicks the close button. Access automatically sets
While Not bStop And . . . ' Until stop or calculation finished references to it to Nothing. We may also close the form
. . . ' One step in a long calculation by setting references to it to Nothing, like this:
If Timer() > startTime Then ' A second has passed
txt . . . = . . . ' Display progress Set stay2 = Nothing
startTime = startTime + 1 ' Next in one second
SendKeys "{home}", True ' "Type" a character and wait.
' Allows Access to handle all events and The reason we have stay1 and stay2 in a global module
' update the screen. is that they have to be there all the time. If we made
End If them local variables in a procedure, the opened forms
Wend would close as soon as the procedure returned. See
End Sub
more about variables and their life-time in section 6.2.
5.5.12 Multiple form instances Handling many open forms
When we open a form with DoCmd.OpenForm, we get When we need many open instances, we have to make
only one instance of the form. If we try to open it an array of references. A click event could then open a
again, nothing happens. new instance in this way:
In order to open multiple instances, we need to use the Dim stay(1 To 10)
basic VBA mechanism of creating an object. Here is a ...
program piece that opens two instances of frmStay: Private Sub cmdOpenStay_Click( )
' Find an unused reference, let it be j
Dim stay1 As Form, stay2 As Form Set stay(j) = New Form_frmStay
' References to open forms Call stay(j).Init( . . . )
... stay(j).Visible = True
End Sub
down =
WindowTop
height =
WindowHeight
right = width =
WindowLeft WindowWidth
Declare variables.
Private Sub cmdCheckin_Click() Local for this procedure.
Dim s As String
Dim rs As Recordset Compute SQL
end of the set. If the record set is empty, EOF is True Move to next record. The rs.MoveNext statement
from the beginning and the loop terminates immedi- moves to the next record, that now becomes the current
ately. record. If there is no next record, EOF becomes True
and the loop will terminate. When EOF is true, there is
Updating a record. The first statement inside the loop no current record in the record set, and attempts to
starts editing the current record. VBA transfers the address fields in it will fail.
fields to an edit buffer. If we don't transfer the fields to
the edit buffer, the program can read the fields but not Updating the stay record. When the loop is finished,
change them. the program sets Me!state to 2, thus updating the stay
record as well.
The next statement changes the state of the RoomState
record to 2, meaning CheckedIn. The change takes Close the Recordset. The last statement closes the re-
place in the edit buffer - not in the database table. No- cord set. It is customary to do so, but in this case it is
tice the bang-operator that ensures that we get the field, unnecessary. When the procedure returns, VBA will
not a possible built-in property. delete the local variables. Since rs contains a reference
to a record set, it will close it before deleting rs.
The rs.Update statement transfers the changed fields to
the database table. At this time Access checks that Try the program
mandatory fields are filled in, that referential integrity 6. Close the program and try out the CheckIn button.
is okay, etc. There are several ways the program can You should see the state field change in the stay
catch these errors so that the user doesn't see the cryp- window.
tic messages produced by Access. See sections 5.5.10
and 6.1. 7. To ease experimentation with the system, program
the Book button so that it does exactly the same as
BOF
Recordset ...
Dim rs As Recordset, rc As Recordset
s = "SELECT * . . . “
Set rs = CurrentDb.OpenRecordset(s)
Set rc = rs.Clone
MovePrevious Edit buffer
Current record If rs.roomID = rc.roomID Then . . .
MoveNext While . . .
rs.AddNew Add
rs!fieldX = . . . records
rs.Update
Clone rs.MoveNext
MovePrevious Edit buffer Wend
Current record
MoveNext While Not rs.EOF
rs.Delete Delete
rs.MoveNext records
EOF
Wend
BOF
Me.Recordset
Recordset
Me.RecordsetClone
MovePrevious Edit buffer
Current record
MoveNext
EOF
Me.RecordsetClone.FindFirst(“stayID=740”)
Me.Recordset.Bookmark=Me.RecordsetClone.Bookmark
BOF (attribute, read only). True if the program has FindPrevious(Criterion As String). Similar to Find-
tried to move current record before the first record. First, but searches backwards from the current
Also True for empty record sets. When True there record.
is no current record.
GetRows(n As Long). Copies n records to an array of
Bookmark (attribute, read and write). A unique iden- variant data. The first record is current. Moves cur-
tification of the current record in the record set. rent forward n records to the first record after the
You can set the bookmark property to the book- ones copied. If there are less than n records left,
mark of some other record in the same record set. GetRows only transfers what is left.
This will make this record current. The method is
advantageous to setting AbsolutePosition because Example: Assume that rs is a record set. The
bookmarks don't change when records are inserted records have 3 fields.
or deleted. Dim A( )
A = rs.GetRows(7)
Clone( ). Creates a clone object that behaves like a re- ' A(f, r) is now field f of record r
cord set but works on the same set of records. It has
its own pointer to a current record. Returns a refer- This program piece transfers the next 7 records and
ence to the Clone object. Example: sets the range of A to A(0 To 2, 0 To 6). The in-
Set cloneRecordset = rs.Clone dexes of A are zero-based and A(0, 3) will thus
contain the first field of the fourth record.
Close( ). Closes the record set and frees the associated
memory. LastUpdated (attribute, read only). The date and time
the current record was last changed. Only available
DateCreated (attribute, read only). The date and time when the record set is based on a table, not on an
the current record was created. Only available when SQL-query. This means that it must be opened like
the record set is based on a table, not on an SQL- this:
query. This means that it must be opened like this: Set rs = currentDB.OpenRecordset ("tblGuest")
Set rs = currentDB.OpenRecordset ("tblGuest") (See also OpenRecordset below.)
(See also OpenRecordset below.)
Move(n As Long). Moves current n records away.
Delete( ). Deletes the current record. After Delete there When n>0 the movement is forward, if n<0 back-
is no current record, but after a MoveNext the ward. Move(0) is useful in bound record sets
record after the deleted record will be current. (Me.Recordset) to make Access store the current
record in the database.
Edit( ). Transfers the current record to the edit buffer.
Edits can then take place in the edit buffer. The MoveFirst( ), MoveLast( ). Moves current to the first
Update operation will transfer the edit buffer to the or last record in the record set.
current record in the record set. Any operation that
moves current record (e.g. MoveNext or Find) will MoveNext( ) , MovePrevious( ). Moves current one
cancel what is in the edit buffer. record forward or one record backward. If the
movement goes beyond the ends of the record set,
EOF (attribute, read only). True if the program has EOF or BOF become True.
tried to move beyond the last record. Also True for
Form module
Form
Simple module
basCommon
Ask user to
select a stay window
4. Close VBA, right-click the toolbar area and select 5.7.3 Managing modules and class modules
Customize. You can create a module through the database
window's Module tab, but the usual way is to do it
5. Roll down the Stays menu, right-click CancelStay, through the VBA editor. However, things work in a
and select Properties (Figure 5.7B). Set the strange way here. Figure 5.7C shows how to manage.
OnAction property to:
=mniCancelStay( ) • To create a module, right click an item in the
Project Explorer window. Select Insert and either
6. Close the customize boxes and try out the menu: Module or Class Module.
Open a stay through FindGuest, select the stay,
and use the menu point CancelStay. Unless you are You can now edit the module in the code window.
very, very lucky and careful, there will be errors in
your mni-procedure. Don't worry - it is normal. • To name or rename a module, select it and use the
Find the errors and repair them. property icon on the tool bar. (You cannot right
click to change it.) Edit the name in the property
You may later set the stay back to booked or checked- window.
in with the buttons in the stay window. Also check that
the program behaves correctly when you use Cancel- • To delete a module, select it, right click and use
Stay without having a stay window in focus. Remove . . .
Call mniCancelStay
Properties for
Cancel stay menu item
Select properties
to change module name Property
window
Right click to
insert Module or
Class Module
Project
explorer
Delete module
2. Open the module for frmFindStay and create the The system will work exactly as before except that it
load procedure as shown. gets the real today instead of the simulated one.
Simple module
Global variable basCommon
Access function
Form module
frmFindStay
Reference to
global variable
Property box
cboArrival
If you want additional explanation, you have to use the Also notice that when you use the functions from
on-line help or experiment on your own. Be prepared VBA, you get excellent help and excellent error mes-
that the official documentation (on-line help) is often sages, but when using them in SQL or ControlSource,
incomplete or outright wrong, in the sense that the you get little help and very confusing error reactions.
system does something different than described. The
examples we show in the figures are based on testing
what the system actually does.
6.1 Statements
Line continuation. A simple VBA statement consists Conditional statements
of a line of text. If the statement is too long for a line, Conditional statements are executed when some condi-
you can split it into two or more lines. To do this, you tion is met. They are examples of compound state-
write a space followed by an underscore at the end of ments, which may consist of more than one simple
the line (Figure 6.1A). You cannot break the line in the statement. As Figure 6.1A shows, there are several
middle of a text string. You have to compose a long kinds of conditional statements.
text from shorter texts joined with the &-operator.
Simple If-Then. The simplest version consists of an If-
Comment. You can write a comment at the end of the Then clause followed by a single statement, which is
line. It starts with an apostrophe ( ' ). The compiler then executed when the condition is True. It must all be on
ignores the rest of the line. You can only have com- one line, possibly broken with line continuations.
ments on the last of the continued lines.
If-Then-Else. The more general version consists of an
Assignment statement. An assignment statement If-Then clause followed by one or more statements,
computes a value and stores it in the variable to the left which may be compound themselves. These statements
of the =. The Set statement is a special version of as- are executed when the condition is True. If the condi-
signment. It doesn't store a computed value, but a ref- tion is False, the program continues with any ElseIf-
erence to some object. The figure shows how it stores a Then clauses, each testing their own condition, and
reference to the first open Form, how it creates a new passing the control on if the condition is False. If all
open form object and stores a reference to it, and how these conditions are False, the program continues with
it can set the reference to point at nothing. the statements after any Else clause. The net result is
that the statements after at most one of the clauses are
Whenever you set a reference, VBA checks whether executed.
this overwrites an earlier reference. If so, VBA also
checks whether this is the last reference to the object, Select-Case is often a more elegant way to choose
and if so it deletes the object since nobody can refer to between statements. In the example, we test the vari-
it any more (this is called garbage collection). able zip. If zip is 4000 the program executes the state-
ments after Case 4000. If zip is 4001 or between 5000
and 5999, it executes the statements after this clause.
And if none of this is True, the program executes any
Case-Else statements. Again, the net result is that the
statements after at most one of the clauses are exe-
cuted.
When Access detects a program error, it sets an Err On Error GoTo fail ‘ Enable error handler
...
object with information about the error. Err has several
fail: MsgBox( . . . ) ‘ Continue here at error
properties, for instance
On Error GoTo 0 ‘ Let VBA handle errors
Err.Number (or just Err): The error ID. Err = 0
means no error.
Err.Source: The program that generated the error.
If Try something else actually succeeds, Err is still > 0
Err.Description: A text describing the error (or giving
and the program gives up by mistake. The right pattern
the error message).
is to use
Err.Clear or Err = 0
Notice that Access doesn't clear the Err object until the
procedure returns. This can be confusing in program just before Try something else.
patterns where the program tries various things to
The main problem when using the Err object is to find
succeed:
out what the error ID means. I have not seen a list of
On Error Resume Next the codes. The idea is that each subsystem defines its
. . . Do something that may cause an error own error ID's, but this makes it even harder to know
If Err > 0 Then the ID's.
. . . Try something else
If Err > 0 Then . . . Give up
Loops
While a<10 ‘ Maybe empty loop
c=c*2
... ‘ Exit not allowed
Wend
Do While a<10 ‘ Maybe empty loop
c=c*2
. . . Exit Do ‘ Exit optional
...
Loop
Do ‘ Loop at least once
c=c*2
. . . Exit Do ‘ Exit optional
...
Loop While a<10
For i=1 To last Step 2 ‘ Step optional
c=c*2 ‘ Maybe empty loop
. . . Exit For ‘ Exit optional
...
Next i
‘ Don’t trust value of i when loop ends without Exit
For Each f In Forms ‘ Scan collection
call print(f.name . . . )
. . . Exit For ‘ Exit optional
...
Next
function call as shown in Figure 6.2A, while you have Enumeration type - constant declaration
to call a subroutine with the word Call. You may call You can define enumeration types as shown on Figure
either of them with the parenthesis-free notation as 6.2A. A variable of type RoomState can have the value
shown on the figure. It means exactly the same, but rmBooked, rmOccupied or rmRepair.
you cannot store the result in case you call a function
this way. VBA doesn't restrict the value of the variables to
rmBooked, etc. The Enum declaration is primarily a
The figure also shows how subroutines and functions structured way of defining the constants rmBooked, etc.
are declared. Note how you specify that a specific type See section 6.3 for other ways of defining constants.
of parameter is required, and how you specify that a
parameter may be omitted (optional). The procedure
can check whether an optional parameter is present
with the operator IsMissing.
Color values consist of 8 bits for the amount of blue, Constant declaration. You can declare constants, i.e.
followed by 8 bits for green and 8 bits for red. This is give them a name. In the example, we have given the
conveniently written in hex, e.g. &h09A0FF. Note that constant 10 the name max and the constant 24th March
colors on the web (HTML) are similar, but use the 2002 the name start. VBA has many predefined con-
opposite sequence, RGB. stants, for instance vbKeyA to denote the Ascii value
of the letter A and vbYes to denote the result of
String constants are enclosed in quotes. There is no MsgBox when the user has chosen Yes.
way to write special characters inside the string con-
stant. You have to generate a string with a single spe- Define constants for your project in a simple module,
cial character using Chr(x), where x is the Ascii value e.g. basCommon.
of the character. As an example, Chr(10) is a line feed.
Next you concatenate these string parts by means of the Addressing variables and objects
&-operator. A quote inside a string constant is written Figure 6.3 also shows the various ways to address a
as two quotes. When you have to compute SQL-state- variable or an object. The first examples address the
ments in VBA, these statements will often include members of the Forms collection in different ways. The
string constants in quotes. Fortunately, SQL accepts first version uses an integer index as a reference, the
double quotes as well as single quotes. Generate the second a computed string as the name of the Form, the
single quotes-version with VBA to avoid conflicts with third a short-hand notation with a fixed string as the
VBA double quotes. name of the form.
Date/time constants are enclosed in # #. The date The next examples address Form properties and fields
format between # and # is always US format mm/dd/yy from code in the Form itself. The property Name is
or mm/dd/yyyy. Time may be part of the date/time addressed with the dot-operator, while the name field is
constant as shown. addressed with the bang-operator. In this case there is a
name conflict between the two meanings of name. If
Null and Empty can be used for testing, for instance there was no conflict, the dot could also be used to
If x = Empty Then . . . address the field. A property in a subform is addressed
If IsNull(x) Then . . . with the name of the subform control followed by
If x = Null Then ' Always gives Null, never True Form to get a reference to the open subform object.
From a subform, the main form can be addressed with
Notice that comparing with Null always gives Null. Me.Parent.
You have to use IsNull to test whether something is
Null. See section 6.4 for more on Null. Note that Me and Parent are of type Object, not type
Form as one might expect.
You can assign Null to a variant variable. You cannot
assign an empty string to a record field in a database, In most cases, you can omit Me. The exception is when
you have to assign Null: a built-in function has the same name as the property or
rs . f = Null ' Okay, works as an empty string control.
Constants Addressing
23, -23, 0, -4.9E-20 Decimal numbers Forms(i) Element in collection
&h09A0FF, &o177 Hex and Octal, color: bgr Forms(“frmCst” & i)
“Letter to:” Strings Forms!frmCst2 Bang-operator
Chr(65), Chr(vbKeyA) The text “A” Me.Name, Me!name Property and Control in
“John” & Chr(10) & “Doe” Two-line text this Object (e.g. form)
“Don’t say “”No”” “ Don’t say “no” Me.subLst.Form.name Property in subform
“select * from g where a=‘simpson’ ;” Me.Parent.txtName Control in main form
Single quotes are suited for SQL
basCommon.simDate Var in foreign module
True, False Booleans c(row, col) Indexing an array
Date/time custTable(i).custID Field in array of records
#10/24/02# 24th Oct 2002
#10/24/02 14:15:00# 24th Oct 02 at 14:15 With Me.Recordset Apply before dot and bang
#10/24/02 2:15 pm# 24th Oct 02 at 14:15 .addr = .addr & zip
!name = Null
Null, Empty Special values !phone = “ “
Nothing Object reference to nothing .MoveNext
Constant declaration ...
Const max=10, start=#3/24/2# End With
A public variable in a foreign, simple module can be With-End. There is a short-hand notation for address-
addressed as moduleName.variableName as shown. ing an object. The With-End statement specifies a par-
Array elements are addressed with indexes in paren- tial address, for instance an object. Inside the With-
thesis. Arrays of records are addressed with index and End, all dot and bang-operators are automatically pre-
the dot-operator to get a field in element i. fixed with this partial address.
The Is-operator compares two object references to see The character # means any digit here. The sequence
whether they refer to the same object. It may also help [ad3] means either a, d, or 3 here. The sequence [a-d3]
checking whether an object reference is Nothing, i.e. means either a letter between a and d here, or the digit
refers to no object. 3. We can even negate the rules: [!ad3] means neither
a, d, or 3 here. The Like operator is also called the
wildcard operator.
Conversion to Integer, Double, Date, etc. some dates are ambiguous. For instance the string
There is a conversion function for most of the types. It "02/03/04" can be interpreted in many ways as a date.
converts an expression of another type to its own type. In these cases, CDate uses the regional setting for the
For instance, CInt(D) converts D to an integer - if pos- date format.
sible - and returns the result as the value of the func-
tion. D might for instance be a string. If D is a decimal CStr( ) can convert a number to a string. It never puts a
number, it is rounded to the nearest integer (up or space (blank) in front of the digits. In contrast, Str( )
down). puts a space in front of numbers >= 0, a minus in front
of negative numbers. Notice that both functions
The function Round(D) does exactly the same as convert dates to the regional date format. To avoid this,
CInt(D) but returns the integer as a Double. See Math use the Format function to convert to a specific date
Functions, section 6.5, for rounding down with Int and format.
Fix.
CVar(X) converts X to a variant type. It doesn't really
CDate(D) converts D to a date/time value (technically a change anything, but sets a type tag on the result it re-
Double number). Often D is a string, and CDate is turns.
quite liberal in its interpretation of the string. However,
• Designer environment: The designer defines List separator. In Central Europe, the list separator is
controls and their properties. He may also define a semicolon since the comma is used to denote the
queries through the query grid. decimal separator. In the US the list separator is a
comma. As a result, the designer has to write for in-
• Programmer environment: The programmer stance Iif(a; b; c) in the query grid or in Control Source
works in Visual Basic and defines SQL-statements properties, while the programmer has to write Iif(a, b,
rather than queries through the grid. c). To make things worse, if the designer uses commas
in the Control Source or the grid, the error reaction is
• User environment: The user sees data through completely confusing.
controls and datasheets.
Fortunately, there is no reason to worry about the user
Input date. Let us first look at the entry of dates. In the environment for the list separator. Users normally don't
designer and programmer environments, Access makes see it at all.
a good guess at the format for dates. If we for instance
enter #2003-13-5# into the query grid or SQL, Access Number format. The number format in Central
guesses what is year, month and day. However, in the Europe has comma as decimal separator and dot as
many situations where this is impossible, such as the thousand separator. In the designer environment you
date #02-03-04#, Access guesses differently in the two have to use these characters, while you use the US
environments. In the designer environment, it uses the format in the programmer environment. In the user en-
regional date format, which in Central Europe is #dd- vironment, the Format property and the Input mask
mm-yy#. In the programmer environment, it uses the determine the format.
US format #mm/dd/yy#.
Practice. Developers are sometimes designers and
Input mask property. What about the user environ- sometimes programmers. It is no wonder that they usu-
ment? Here the designer defines what the user sees by ally set up their development system in pure US mode.
means of the Input mask in the Control Property box. They can ship the system to the users with little change
When the user starts entering a date, the field switches since the users don't see the designer stuff. But if they
to showing the date according to the Input mask. This have to change something at the user's site, they have
mask is similar to the format string in the Format func- to take care since the developer formats now look dif-
tion, but uses quite different placeholders. When an ferent from back home.
We have four kinds of SQL queries available: The INSERT statement can also insert a bunch of
records extracted from another table or query. The
INSERT INTO . . . (Create) bottom of the figure shows an example (explained
This query inserts new records into a table. below).
SELECT . . . FROM . . . (Read) AutoNumber
This query extracts data from the tables. It is the Assume you have a table with a field of type Auto-
query we have used in the rest of the booklet Number. When you insert a record into the table,
(e.g. sections 4.1 to 4.4). In many cases it also Access will automatically fill in the AutoNumber field.
allows the user to edit the data. We will not However, if you explicitly specify a value for the field,
explain it further in this section. Access will accept it if it is unique (different from
other values of this field).
UPDATE . . . (Update)
This query changes records in a table. When Access generates the number automatically, it
will use the highest used number plus one. (The highest
DELETE FROM . . . (Delete) used number may have been used by a record now
This query deletes records from a table. deleted.) As an example, assume we want the booking
numbers in the hotel to consist of a year number and a
The term action query means INSERT, UPDATE or sequence number, for instance 20040001, 20040002
DELETE - the queries that change something in the etc. We can then generate the first booking of the year
database. with this query:
We will explain the mechanisms through the examples INSERT INTO tblStay (stayID) VALUES (20040001)
in Figure 7.1. To execute an action query, we use this
VBA function: New stay records will then be auto-numbered from this
value and on. Deleting some of the records still makes
CurrentDb.Execute ”INSERT . . . ", dbFailOnError
the number grow sequentially.
CurrentDB is the database currently used by the UPDATE
program. We ask it to execute the INSERT query. The UPDATE statement sets room state to 2 and
Usually the query statement is a computed string that personCount to 1 for all roomState records where
includes dialog variables, for instance the customer the roomID=14 and date=27/10/2002. In this case there is
user works with. only one such record because roomID and date make
up the primary key for the table.
The last parameter dbFailOnError is optional. It asks
the database to give an error message if something goes Confused about the dates? Access SQL uses American
wrong during the query, for instance if we try to insert dates, while the explanations in the booklet use middle
a record that violates referential integrity. If we omit it, European dates. See more in section 6.6.
there is no response in case of errors - nothing changes
in the database, and the program doesn't know about it. DELETE
Include dbFailOnError, particularly while you are The DELETE statement deletes all roomState records
testing the program. where roomID=14 and date=27/10/2002. In this case
there is only one such record.
INSERT
The figure shows a full INSERT statement. It inserts a 7.1.1 Temporary table for editing
single record into tblRoomState. It sets the roomID to
The user (or the program) can edit the fields in a simple
14, the date to 27th Oct 2002, and the state to 1. The
query. However, if the query contains a GROUP BY,
an outer join, or another complex query, the result can computes the SQL statement from several parts, one of
not be edited. From a usability perspective, there is them being me.StayID, the current stay number the user
often a need to edit such a query anyway. It can either is working on. Since the SQL statement is very long, it
be done through a dialog box (low usability), or is split into two lines with an underscore at the end of
through a temporary table that holds a copy of the the first line.
GROUP BY result. The user will edit the temporary
table directly. The bottom of the figure shows an Note also the SELECT * part that extracts all fields of
example of this. Let us see how it works. the query. This only works if tblTemp has the same
fields - and with the same names. It is possible to
In the hotel system the user sees a single record for extract only some of the fields - or rename them - in
each room booked by the guest. In the database there is order to match the fields in tblTemp. We could for
a roomState record for each date the room is booked, instance extract roomID (renamed to Room) and
but on the user interface we aggregate it to a single line Nights:
with start date and number of nights. The query
qryStayRooms does this, but the user can not edit the INSERT INTO tblTemp SELECT roomID AS Room, Nights . . .
result directly.
After the INSERT, the program can show tblTemp in a
The solution is to have a temporary table tblTemp with form so that the user can edit it. The program may
the same fields as qryStayRooms. The first statement either update the real database each time the user
deletes the present records in tblTemp. The next changes something in tblTemp, or it may update the
statement copies all the query records that belong to the whole thing when the user closes the form.
current stay into tblTemp. Note how the program
You can use subqueries in this way: Example 2: List of free rooms using NOT EXISTS
The second example lists only the free rooms. Again
EXISTS (SELECT * FROM . . . )
we select roomID from tblRoom, but in the WHERE
True if the subquery returns one or more records.
clause we check that the room has no roomStates in the
You can also write NOT EXISTS (. . .)
period we consider.
v IN (SELECT w FROM . . . )
True if the set of w's returned by the query contains Example 3: List of free rooms using NOT IN
the value v. You can also write v NOT IN (. . .) The third example also lists the free rooms, but selects
them in a different way. In the WHERE clause the
v IN (3, 7, a+b) subquery lists all roomStates in the period we consider.
True if v is in the list of values 3, 7, a+b. This is not The WHERE clause checks that the roomID from
really a subquery, because we write the list of tblRoom is not in this list.
values explicitly.
Correlated queries
v > ANY (SELECT w FROM . . . ) SQL specialists talk about correlated queries. The
True if the value v is larger than one or more of the subquery is correlated with the main query if it
w's returned by the query. You can also write v=, references fields in the main query. In the figure, the
v>, v>=, etc. You can write SOME instead of ANY. first two queries have correlated subqueries while
The meaning to the computer is the same. qryTest3 has not.
v > ALL (SELECT w FROM . . . )
Example 4: DISTINCT values only
True if the value v is larger than all of the w's A query may produce a list of records with duplicates.
returned by the query. You can also write v=, v>, QryTest 4 shows an example. It extracts all roomState
v>=, etc. records in the period we consider, and selects the
roomID. Since many rooms are occupied multiple
An alternative to v > ANY is:
v > (SELECT Min(w) FROM . . . ) times in this period, the same roomID will appear
Similarly an alternative to v > ALL is: many times.
v > (SELECT Max(w) FROM . . . )
QryTest 4A has added the word DISTINCT. This
EXISTS (SELECT . . . ) AND v > ANY (SELECT . . .) causes SQL to remove all duplicates.
You can combine subqueries in the same way as
other logical expressions. Example 5: Self-correlation
The fifth example shows how we can select records
In summary, you can use a subquery in a logical based on other records in the same table.
expression. You cannot join a subquery with other
subqueries, group it, etc. In order to do such things, The example is from the hotel system. A guest may
you have to store the subquery as a named query (we have booked several rooms for different periods. When
show an example in section 7.4). guests arrive, we want the receptionist to see a list of
the rooms booked for the arrival date.
Example: Used and free rooms
The first example in Figure 7.3 shows a list of rooms The query looks at each roomState record to see
with an indication of those used in the period 23/10 to whether it must be included in the list. It must be if the
26/10. date of this roomState is the arrival date for the
corresponding stay. The subquery finds the arrival date
In principle the query is quite simple. We query by looking at all roomStates that belong to this stay.
tblRoom and select the roomID and a computed value. The arrival date is the first date among these
The computed value makes the trick. It tests whether roomStates.
the room has a roomState record in the period 23/10 to
26/10. The result will be True or False. We give the Note how the subquery selects tblRoomState. It gives
result the name Used. the table an alias-name (T2) in order to compare these
room states with those from the outermost query. This
In this case we show the result as Yes/No. We have set is called a self-correlation.
Property -> Format to Yes/No for the Used field.
qryTest:
SELECT roomID,
EXISTS (SELECT * FROM tblRoomState
WHERE tblRoomState.roomID = tblRoom.roomID AND
(tblRoomState.date BETWEEN #10/23/02# AND #10/26/02#))
AS Used
FROM tblRoom; Room list with indication
whether the room was used
between 23/10 and 26/10.
qryTest2:
SELECT roomID FROM tblRoom WHERE
NOT EXISTS (SELECT * FROM tblRoomState
WHERE tblRoomState.roomID = tblRoom.roomID AND
(tblRoomState.date BETWEEN #10/23/02# AND #10/26/02#));
qryTest4:
SELECT roomID FROM tblRoomState
WHERE tblRoomState.date BETWEEN #10/23/02# AND
#10/26/02#;
qryTest4A:
SELECT DISTINCT roomID FROM tblRoomState
WHERE tblRoomState.date BETWEEN #10/23/02# AND
#10/26/02#;
qryTest5:
SELECT stayID, roomID, [date] FROM tblRoomState
WHERE [date] =
(SELECT Min([date]) FROM tblRoomState AS T2 WHERE
tblRoomState.stayID = T2.stayID)
ORDER BY stayID;
Self-correlation with alias.
Named query. The solution is to use a named query The solution is to make a Form based on the query, and
for each of the parentheses. Figure 7.4 shows the full set its default view to Datasheet. (A datasheet looks
solution. Using the query grid in Access, we create a like a table, but behaves differently.) We may use the
query for each of the date columns in the result. The form as it is or connect it to a subform control. Now we
figure shows qryRoom1, the query for the first date can control the appearance of the datasheet, and the
column. It simply selects the roomState records for the program can "see" the user's selections of data cells.
first date in the period. QryRoom2 selects the
Unfortunately this introduces another problem. The
roomState records for the second date, and so on.
query doesn't any more determine the headings. They
The room grid is now computed with the big query. It are defined by the field labels on the form. In the next
follows the idea above, but uses the named queries section we show how to manage all of this.
qryRoom1:
SELECT roomID, state FROM tblRoomState
WHERE tblRoomState.date=#10/21/2002#;
qryRoomGrid:
SELECT tblRoom.roomID, tblRoomType.description, qryRoom1.state AS [21-10-02],
qryRoom2.state AS [22-10-02], qryRoom3.state AS [23-10-02]
FROM tblRoomType INNER JOIN (((tblRoom
LEFT JOIN qryRoom1 ON tblRoom.roomID = qryRoom1.roomID)
LEFT JOIN qryRoom2 ON tblRoom.roomID = qryRoom2.roomID)
LEFT JOIN qryRoom3 ON tblRoom.roomID = qryRoom3.roomID)
ON tblRoomType.roomType = tblRoom.roomType;
txtDate
subRoomGrid
frmRooms:
Private Sub cmdFindRoom_Click()
. . . WHERE date = 37550;
Dim s As String
s = "SELECT roomID, state From tblRoomState WHERE date = "
CurrentDb.QueryDefs!qryRoom1.SQL = s & CDbl(Me.txtDate) & ";"
CurrentDb.QueryDefs!qryRoom2.SQL = s & CDbl(Me.txtDate+1) & ";" . . . WHERE date = 37551;
CurrentDb.QueryDefs!qryRoom3.SQL = s & CDbl(Me.txtDate+2) & ";"
Me.subRoomGrid.Form!L1.Caption = Me.txtDate
Me.subRoomGrid.Form!L2.Caption = Me.txtDate + 1 Set RecordSource again.
Me.subRoomGrid.Form!L3.Caption = Me.txtDate + 2 (Requery doesn’t notice that the
Me.subRoomGrid.Form.RecordSource = "qryRoomGrid" named queries changed.)
End Sub
Me.subRoomGrid.Form("cbo" & j).ColumnHidden=True The queries are also more complex because the user
can ask for rooms that are free in a certain period,
See section 5.5.7 for more on hiding columns, rooms of a certain type, etc. However, the solution
adjusting their width, etc. follows the ideas above.
The real rooms window Another issue is to let the user select rooms and period
In the real hotel system, the user sees the days around from the matrix. This is discussed in section 5.5.8.
the first day he asks for and the days around the last
day. When the period is long, all the days in the middle
are lumped together with a heading saying ". . . ".
Query ->
Crosstab
qxtbRoomGrid:
Ordinary SELECT TRANSFORM First(tblRoomState.state) AS FirstOfstate
. . . GROUP BY SELECT tblRoomState.roomID
FROM tblRoomState
GROUP BY tblRoomState.roomID
PIVOT tblRoomState.date;
Value in cell –
Column heading Row heading aggregate data
Restricting the date range The IN-clause also causes the computer to discard all
In practice we cannot show a matrix with all the dates. dates that are not in the IN-list. Thus we don't need the
It would require hundreds of columns. We want to WHERE-clause anymore.
restrict the query to a specific range of dates.
If you try to look for the IN-clause in design view (the
In Figure 7.6D we restrict the range of computed dates query grid), you won't find it. It has become a property
to those between 24-10-02 and 27-10-02 (European of the query grid. To see it, right click inside the
date format). diagram area and select Properties.
The result is as shown. We got rid of the <> column Unfortunately, the result looks ugly. The column
and see only the dates in the restricted range. The only headings have the programmer format, and I have not
problem is that we don't get a column for every date in been able to find a way to change it. For other fields of
the range. For instance 26-10-02 is missing because no the query, we can set field properties, for instance the
room is used this date. display format. But not for the column headings. (I
would call this a bug in Access.)
Including all dates in the range
We might include all dates in the range by means of an The IN-trick works okay if we use something else than
additional outer join. To do this we need a table of all dates for the headings. We will see an example now.
dates in the range, but this is not an existing part of the
data model. The program might generate such a table, Using the query in a subform
but let as utilize another feature in the crosstab. In a good user interface, we wouldn't present the query
directly to the user, but embed it in a subform. To make
In Figure 7.6E we have added an IN-clause to the the column headings change dynamically, we make the
PIVOT part of the SQL statement. The PIVOT part same trick as in section 7.5.
specifies the column headings, and now we have
explicitly asked for all the dates in the range. Notice We let the query generate column headings that are the
that we have to use US date formats in SQL. texts C0, C1, C2 etc. They will bind to the controls C0,
C1, etc. in the subform. The program will dynamically
set the labels of these controls to the real dates.
26-10-2002 is
now included
qxtbRoomGrid4:
TRANSFORM First(tblRoomState.state) AS FirstOfstate
SELECT tblRoom.roomID, tblRoomType.description
FROM tblRoomType INNER JOIN (tblRoom LEFT JOIN tblRoomState ON . . . )
GROUP BY tblRoom.roomID, tblRoomType.description
PIVOT IIf(IsNull(date), #10/24/2002#, date)
IN (#10/24/2002#, #10/25/2002#, #10/26/2002#, #10/27/2002#);
qxtbRoomGrid5:
TRANSFORM First(tblRoomState.state) AS FirstOfstate
SELECT . . .
PIVOT IIf(IsNull(date), "C0", "C" & (date-#10/24/2002#))
IN ("C0", "C1", "C2", "C3");
The bottom of Figure 7.6E shows the query to be used. Crosstab restrictions
We compute the column heading with this expression: Crosstab is great for quick and dirty solutions where
you want to show the relationship between two
IIf( IsNull(date), "C0", "C" & (date - #10/24/2002#)) attributes in a query. This is for instance the case for
experimental data mining where the user defines
Null dates are replaced by the text C0. Other dates are
queries on his own.
replaced by a C followed by the date index: 0 for the
first day in the period, 1 for the next day, and so on. However, when designing a production system such as
a hotel system, Crosstab provides too little flexibility.
Notice that although the IN-clause has the texts in
Crosstab can for instance not handle subqueries or
quotes, the result shows the text in user format without
Having, even if they occur in named queries.
quotes.
148 8. References
Index
! (bang operator), 41, 69 - datasheet, VBA, 94
! versus dot, 71 B combo box
" (quotes inside quotes), 74 bang operator ( ! ), 41, 69 - column width, 34
" (strings), 122 - versus dot, 71, 122 - enumeration type, 14, 24
#Error, 40 BeforeUpdate (form event), 86 - hiding the code (Column
#Name?, 41 BeforeUpdate (text box event), 78 Width), 34
& (concatenation, VBA), 124 BETWEEN (operator), 124 - table lookup, 26
& (label control), 22 BETWEEN (SQL), 138 - with a New choice, 136
* (all fields), 52 BOF (begin-of-file, recordset), command (in menus), 48
[ ] (name parenthesis), 40, 54, 91 108 command button, 84
+ - * / (VBA), 124 BookMark (recordset), 108 - Cancel (property), 84
< <= . . . (VBA), 124 Boolean field, 11 - Click (event), 84
= (assignment, VBA), 116 Border Style (on form), 21 - Default (property), 84
= (computed control), 40 bound column (in combo box), 26 - drawing and Wizard, 18
bound controls, 40 comment (VBA), 116
A bound forms, 32 compact database, 15
Abs function (VBA), 130 bound forms (main and subform), Compare Option (VBA), 128
AbsolutePosition (recordset), 108 66 comparison of dates, 76
Access file formats, 8 bound recordset, 106 comparison of texts (Like), 64,
Access versions, 6 break program, 98 124
action queries, 134 breakpoints (Visual Basic code), composite search, 76
Activate (form event), 86 82 computed controls, 40
ActiveForm, 110 button. See command button computed fields (query), 58
AddNew (recordset), 108 computed SQL, 74, 76, 102
addressing forms and objects, 69 C conditional color and format, 42
addressing variables (VBA), 122 calendar control, 22 conditional statements (VBA),
AfterUpdate (form event), 86 calendar control (DT Picker), 22 116
AfterUpdate (text box event), 72, Cancel (button property), 84 constant list (Visual Basic help),
78 capital letters 82
aggregate query, 58 - query, 54 constants (VBA), 122
- editing the data, 67, 134 - Visual Basic, 41, 81 - Null, Empty, Nothing, 122
- in subform, 66 Caption (on form), 21 continuous form, 30
alias (query, AS), 58 Caption (on text box label), 28 Control Box (on form), 21
align Cartesian product (query), 54 Control Source (text box), 28
- controls, 20 Case Select (VBA), 116 control tips (pop-up help), 50
- controls (grid), 18 Change (event), 73 controls, 70
- text in text box, 28 Change (text box event), 78 - adding a field, 34
ALL (SQL), 138 checkbox - align, 20
AllowAdditions (form), 87 - drawing, 22 - align (grid), 18
AllowDeletions (form), 87 Choose function (VBA), 128 - bound and unbound, 40
AllowEdits (form), 87 Chr (VBA), 128 - calendar, 22
And, Or . . . (composite search), CInt, CDate . . . (VBA), 125 - checkbox, 22
76 class module. See module - combo box
And, Or, Not . . . (VBA), 124 Click (command button), 84 - enumeration type, 24
application title (startup settings), clone (recordset), 104, 106, 108 - hiding the code (Column
48 Close (form event), 87 Width), 34
area selection (datasheet, VBA), Close (recordset), 108 - table look up, 26
94 color - command button, 18
arrays (data type, VBA), 120 - datasheet, 34, 42 - properties, 84
AS (query, alias), 58 - on controls, 22 - computed, 40
Asc (VBA), 128 - value dependent, 42 - DateTime picker, 22
assignment (VBA), 116 column format, 136 - front/back, 22
autoformat (on form), 21 column hidden (VBA), 94 - label, 18, 28
AutoNumber field, 11 column sequence (subform), 34 - label (delete/add), 20
- setting the value, 134 column sequence (VBA), 94 - line, 22
average (AVG, query), 60 column width - moving and sizing, 20
- combo box, 34 - name (programmer's), 28
- datasheet view, 34 - option group/radio button, 44
Index 149
- rectangle, 22 - single, 120 DLookup, DMin . . . (VBA), 128
- subform, 32 - static, 122 Docking windows, 80
- tab order, 28 - string, 120 double field (in table), 11
- tab sheet, 44 - text, 10, 120 dynaset, 52
- text box, 18 - type, 120 - editing the data, 53
- events, 72, 78 - variant, 120 - group by, 61
- properties, 28, 72 - yes/no, 11
ControlTip (text box), 28 database E
conversion functions (type, - compaction, 15 Edit (recordset), 108
VBA), 125 - creation, 6 Empty (VBA), 122
correlated query (SQL), 138 - multiple connectors, 16 Enabled (text box property), 28
COUNT (query), 60 - network structure, 16 engine (database), 55, 68
create - objects, 68 Enter (text box event), 78
- controls, 18 - self-reference, 16 enumeration type
- database, 6 - SQL-engines, 55, 68 - combo box, 24
- event procedure, 80 - tree structure, 16 - table field, 10
- forms, 18 - versus files, 6 - VBA, 121
- menu, 46 DataEntry (form property), 88 EOF (end-of-file, recordset), 108
- module (class), 110, 112 datasheet Err (error object), 117
- relationships, 12 - area selection (VBA), 94 Error (in computed value), 40
- single record (VBA), 88 - as subform, 31, 36 error handling
- table and fields, 7 - column hidden (VBA), 94 - before update, 78
criteria (composite), 76 - column sequence, 34 - Error preview, 97
criteria (live search), 74 - column sequence (VBA), 94 - MsgBox, 50
criteria (user-defined), 64 - column width, 34 - On Error GoTo, 110, 117
Crosstab (SQL), 144 - column width (VBA), 94 error messages (MsgBox), 50
CRUD (Create, Read . . . ) - font and color, 34, 42 events, 72–79
- form, 87, 90 - mockup, 36 - Activate (form), 86
- recordset, 102 - sorting, 34 - AfterUpdate (form), 86
- SQL, 134 - versus form view, 38 - AfterUpdate (text box), 72, 78
currency (data type), 120 date comparison, 76 - BeforeUpdate (form), 86
Current (form event), 78, 86 date format - BeforeUpdate (text box), 78
current record (in a form), 38, 93 - Format function, 126 - Change (text box), 73, 78
current record (in recordset), 68, - in controls, 126 - Click (text box), 84
104 - regional settings, 132 - Close (form), 87
CurrentDb, 102, 134 date/time field, 11 - command button, 84
date/time functions (VBA), 130 - creating event procedures, 80
D DateCreated (recordset), 108 - Current (form), 78, 86
DAO 3.6 (recordset), 102 DateTime Picker, 22 - Deactivate (form), 87
data entry dbFailOnError, 134 - deleting event procedures, 81
- into table, 8 Deactivate (form event), 87 - Dirty (form), 86
- shortcut keys, 8 debug (Visual Basic code), 81 - Enter (text box), 78
data type, 10, 120 debug window (immediate), 50, - Error (form), 97
- array, 120 69 - form, 86
- AutoNumber, 11 declarations (variables, VBA), - form (sequence), 86
- AutoNumber (setting the 120 - GotFocus (form), 86
value), 134 Default (button property), 84 - GotFocus (text box), 78
- Boolean, 11, 120 DELETE (record, SQL), 134 - KeyDown (text box), 78
- Byte, 120 Delete (recordset), 108 - KeyPress (text box), 78
- currency, 120 delete event procedures, 81 - KeyUp (text box), 78
- date/time, 11, 120 designer role, 6 - Load (form), 86
- double, 11, 120 detail area (connected to a list), - logging, 81
- enumeration (lookup), 14 42 - MouseDown, Click, etc., 78
- foreign key (type match), 11, developer role, 6, 132 - Open (form), 86
12 Dialog box (form), 92 - Resize (form), 86, 100
- form, 120 Dim (declaration, VBA), 120 - text box, 72
- integer, 11, 120 Dirty (form event), 86 - text box (sequence), 78
- long integer, 11, 120 display formats (regional - Timer, 98
- memo, 10, 120 settings), 132 - Unload (form), 86
- number, 10 DISTINCT (SQL), 138 - wait for, 98
- object, 120 Execute (CurrentDb), 134
150 Index
EXISTS (SQL), 138 - form view versus datasheet - in subform, 66
view, 38
F - Forms object, 69 H
field (in table). See also data type - GotFocus (event), 86 HAVING (aggregate query), 54,
- combo box, 14 - grid, 18 60
- enumeration type, 10 - KeyPreview, 97 Height (size property), 28, 100
field list (for adding controls), 34 - Load (event), 86 help (for field types), 7
file formats (Access versions), 8 - main form, 30 help (in Visual Basic code), 82,
filter properties (form), 87 - menu connection, 48 83
financial functions (VBA), 130 - MinMax buttons, 21 help (pop-up), 50
FindFirst, FindNext . . . - mockup, 36 Hex function (VBA), 130
(recordset), 108 - moving and sizing (VBA), 100 hotel system, 4
FIRST/LAST (query), 60 - multiple instances, 99 hotel system (room grid), 4, 94,
focus - navigation buttons, 21 140
- dialog box, 89 - New (create open form), 99
- GotFocus (form event), 86 - open, 86 I
- SetFocus, 86 - Open (event), 86 If Then (VBA), 116
font (datasheet), 34, 42 - OpenArgs, 89 IIf function (VBA), 128
For Next statements (VBA), 118 - OpenForm parameters, 89 immediate window (debug), 50,
foreign key, 11, 12 - record selection (current 69
- combo box, 26 record), 93 IN (operator), 124
form, 86–101 - record selector, 21 IN (SQL), 138
- Activate (event), 86 - Recordset, 106 initial values (VBA), 120
- ActiveForm (current), 110 - RecordSetClone, 106 Input Mask (text box), 28
- AfterUpdate (event), 86 - Resize (event), 86, 100 INSERT (record, SQL), 134
- AllowAdditions, 87 - saving, 22 integer field (in table), 11
- AllowDeletions, 87 - Scroll Bars, 21 IRR (internal-rate-of-return,
- AllowEdits, 87 - SelTop, SelHeight, etc. (VBA), VBA), 130
- area selection (VBA), 94 94 Is operator (VBA), 124
- autoformat, 21 - size, 100 IsArray, IsDate . . . (VBA), 130
- BeforeUpdate (event), 86 - sorting datasheet rows, 34
- Border Style, 21 - style (AutoFormat), 21 J
- bound, 32 - subform, 30–39 Jet engine (database), 68
- bound main and subform, 66 - Timer, 98 JOIN (multiple joins), 140
- caption, 21 - Unload (event), 86 JOIN (query), 52, 54, 56
- close, 86 Form reference (in subform), 71,
- Close (event), 87 K
122
- column sequence, 34 key field (foreign key), 11, 12
format
- column sequence (VBA), 94 key field (primary key), 7
- columns, 136
- column width, 34 keyboard handling
- date in controls, 126
- column width (VBA), 94 - function keys, 97
- Format function (VBA), 126
- continuous form, 30 - shortcut keys on controls, 22
- input mask, 28, 132
- Control Box, 21 - shortcuts (built in), 8, 22
- regional settings, 132
- create single record (VBA), 88 - tab order, 28
- text box property, 28
- creation, 18 KeyCode (VBA), 97
Forms object, 69
- CRUD (multi-purpose, VBA), KeyDown (text box event), 78
front/back (on forms), 22
90 KeyPress (text box event), 78
function (in menu), 112
- CRUD control, 87 KeyPreview (form), 97
function (procedure), 110, 120
- Current (event), 78, 86 KeyUp (text box event), 78
function keys (F2, F3 . . .VBA),
- DataEntry property, 88 97 L
- datasheet view for subform, 31
label (Caption), 28
- Deactivate (event), 87 G
label (delete/add), 20
- design view (shortcut keys), 22 GetRows (recordset), 96, 108
label (for text box), 18
- Dialog box, 92 global variables (in modules), 114
LastUpdated (recordset), 108
- Dirty (event), 86 GotFocus (form event), 86
LBound (array bound, VBA), 128
- edit single record (VBA), 88 GotFocus (text box event), 78
LCase function (VBA), 128
- error preview, 97 grid (for queries), 52
Left (position property), 28, 100
- event sequence, 86 grid (on forms), 18
Left function . . . (VBA), 128
- Filter, 87 GROUP BY (aggregate query),
Len function (VBA), 128
- form view for subform, 36 54, 58
Like (text comparison), 64, 124
- editing the data, 67, 134
Index 151
limit to list (in Combo Box), 24 name of control, 28 primary key, 7
line (on form), 22 Name? (error in computed value), printing (mockup screens), 50
list separator (regional settings), 41 procedure (in menu), 112
132 named query (SQL), 140, 142 procedure (public function), 110,
list with detail area, 42 national settings, 132 120
live search, 74 navigation (current record), 93 procedure (shared), 76
Load (form event), 86 navigation (shortcut keys), 8 program break, 98
Locked (text box property), 28 navigation buttons (on form), 21 project explorer (VBA tool), 80
logging (of events), 81 network structure (in database), properties
logical operators (And, Or), 124 16 - AllowAdditions (form), 87
long integer field (in table), 11 New (create new object), 99 - AllowDeletions (form), 87
lookup field new-line character Chr(10), 92 - AllowEdits (form), 87
- combo box, 26 NoMatch (recordset), 109 - Border Style (form), 21
- enumeration type, 14 Nothing (VBA), 122 - Cancel (command button), 84
- hiding the code (Column NPV (net-present-value, VBA), - Caption (label), 28
Width), 34 130 - Caption (on form), 21
lookup Wizard, 14, 24, 26 Null - ColumnHidden (text box, etc.),
loop (forced break), 98 - blank fields at outer join, 56 94
Loop statements (VBA), 118 - comparing with, 62, 77, 122 - ColumnOrder (text box, etc.),
- in computations, 62, 124 94
M number field (in table), 10 - ColumnWidth (text box, etc.),
main form, 30 number format (regional 94
math functions (VBA), 130 settings), 132 - command button, 84
matrix presentation (with SQL), - Control Box (form), 21
140, 142 O - Control Source (text box), 28
Me (as parameter, VBA), 121 object browser (Visual Basic), 83 - ControlTip, 28
Me (this object/form, VBA), 72, objects - DataEntry (form), 88
122 - addressing, 69 - Default (command button), 84
memo field (in table), 10 - controls, 70 - Enabled (text box), 28
menus (toolbars), 46–49 - database, 68 - Filter (form), 87
- commands, 48 - Forms, 69 - Format (text box), 28
- commands (VBA), 112 - in Access, 68–71 - KeyPreview (form), 97
- connect to form, 48 - recordset, 68 - label, 28
- creation, 46 - screen, 110 - Left, 28, 100
- pop-up, 46 - through VBA, 69 - Locked (text box), 28
- show all on list, 20 Oct function (VBA), 130 - MinMax buttons (form), 21
- startup settings, 48 OldValue (text box), 72 - Name (programmer's), 28
messages (MsgBox), 50, 92 On Error (VBA), 117 - OldValue (text box), 72
MIN/MAX (query), 60 Open (form event), 86 - record set, 104, 108
MinMax buttons (on form), 21 OpenArgs (form parameter), 89 - Scroll Bars (on form), 21
mockup OpenForm (parameters), 89 - Scroll Bars (text box), 28
- datasheet, 36 OpenRecordSet, 109 - SelHeight (on form), 94
- screen prints, 50 operators (VBA), 124 - SelLeft (on form), 94
Modal dialog (form), 92 Option Compare (VBA), 128 - SelTop (on form), 94
module (class), 80, 110, 122 option group (radio buttons), 44 - SelWidth (on form), 94
- creating and naming, 112 ORDER BY (query), 54, 62 - size and position, 28, 100
- global variables, 114, 122 ordering - subform, 38, 70
MouseDown, MouseUp (events), - controls (front/back), 22 - Tab Index, 28
78 - controls (tab order), 28 - Text (text box), 72
Move, MoveFirst . . . (recordset), - datasheet rows, 34 - Text Align (text box), 28
108 - records in query, 62 - text box, 28, 72
moving and sizing OUTER JOIN (query), 56 - through VBA, 69, 70
- controls, 20 - TimerInterval, 98
- forms, 100 P - Top, 28, 100
MsgBox, 50, 92 Parent reference (from subform), - Value (text box), 72
multiple connectors (in database), 97, 122 - Width, 28, 100
16 partial referential integrity, 12 property list (Visual Basic help),
multiple form instances, 99 Partition (operator), 124 82
pop-up help, 50 public (function), 110
N pop-up menu, 46
Name (recordset), 109 prefixes (for controls, etc.), 38
152 Index
Q - bound to form, 106 - datasheet rows, 34
queries, 52–67 - clone, 104, 106 - records in query, 55, 62
- action, 134 - CRUD control, 102 Space function (VBA), 128
- aggregate, 58 - DAO 3.6, 102 SQL. See also queries
- in subform, 66 - properties, 104, 108 - computed, 74, 76, 102
- alias (AS), 58 rectangle (on form), 22 - how it works, 54
- ALL, 138 reference card (VBA), 116 SQL engine (database), 55, 68
- all fields (*), 52 referential integrity, 12 square brackets [name
- ANY, 138 regional settings, 132 parenthesis], 40, 54, 91
- average (AVG), 60 relationships, 12 startup settings (menus etc.), 48
- Cartesian product, 54 - referential integrity, 12 statements (VBA), 116
- computed fields, 58 repair database, 15 Static (declaration, VBA), 122
- computed SQL, 74, 76, 102 Requery (recordset), 109 StDev (query), 60
- correlated, 138 Resize (form event), 86, 100 stop program, 98
- COUNT, 60 role StrComp (VBA), 128
- Crosstab, 144 - developer, 6, 132 string functions (VBA), 128
- CRUD, 134 - user, 6, 132 strings ", 122
- DELETE, 134 room grid (hotel system), 4, 94, strings (nested), 74
- DISTINCT, 138 140 strings, multi-line, Chr(10), 92
- dynaset, 52 row source (in Combo Box), 26 style (on form), 21
- group by, 61, 134 subform, 30–39
- editing the data, 53, 134 S - columnar, 31
- EXISTS, 138 save - datasheet versus form view, 38
- FIRST/LAST, 60 - form, 22 - datasheet view, 31
- grid, 52 - module (class), 110 - Form property, 71, 122
- GROUP BY, 54, 58 - record, 8 - form view, 36, 38
- editing the data, 67, 134 - record (VBA), 88 - Parent property, 97, 122
- in subform, 66 - Visual Basic code, 81 - properties, 38, 70
- HAVING, 54, 60 Screen (VBA object), 110 - subform control, 32
- IN, 138 Scroll Bars (on form), 21 subquery (SQL), 138
- INSERT, 134 Scroll Bars (on text box), 28 subroutine (procedure, VBA),
- JOIN, 52, 54 searching 120
- JOIN (multiple), 140 - live search, 74 SUM (query), 60
- live search, 74 - many criteria, 76
- MIN/MAX, 60 - user criteria in grid, 64 T
- named (stored), 140, 142 security (when opening a Tab Index (cursor movement), 28
- ORDER BY, 54, 62 database), 8 tab sheet (control), 44
- OUTER JOIN, 56 SELECT (query), 54, 55 table
- SELECT, 54, 55 Select Case (VBA), 116 - as subform, 36
- SQL, 54 self-reference (in computed - creation, 7
- SQL-engine, 55, 68 expressions), 40 - data entry, 8
- StDev, 60 self-reference (in database), 16 - shadow copy, 16
- subquery, 138 SelTop, SelHeight, SelWidth, Text (text box property), 72
- SUM, 60 SelLeft (datasheet, VBA), 94 text box, 18
- UNION, 136 SendKeys, 98 - AfterUpdate (event), 72, 78
- UPDATE, 134 Set statement (VBA), 116 - BeforeUpdate (event), 78
- user criteria in grid, 64 SetFocus (VBA), 86 - Change (event), 73, 78
- Var, 60 shadow table, 16 - Control Source, 28
- WHERE, 54 shared procedure, 76 - ControlTip, 28
QueryDefs (VBA), 142 Shift (KeyCode, VBA), 97 - Enabled, 28
quick info (Visual Basic help), 82 shortcut keys - Enter (event), 78
quotes (nested "), 74 - data entry, 8 - event sequence, 78
- for change of view, 22 - events, 72
R - navigation, 8 - Format, 28
radio buttons (option group), 44 - underlined letter on control, 22 - GotFocus (event), 78
record shortcut menu (pop-up menu), 46 - KeyDown (event), 78
- saving, 8 size unit (twips), 100 - KeyPress (event), 78
- saving (VBA), 88 sizing and moving - KeyUp (event), 78
record selector (on form), 21 - controls, 20 - Locked, 28
RecordCount (recordset), 109 - forms, 100 - MouseDown, Click, etc., 78
recordset, 68, 102–9 sorting - OldValue, 72
Index 153
- properties, 28, 72 undo drawing, 20 - debug command, 81
- Scroll Bars, 28 undo lookup-Wizard, 14 - help, 83
- Text (property), 72 undo update (VBA), 79, 84 - object browser, 83
- Text Align, 28 UNION (SQL), 136, 138 - objects in Access, 68–71
- Value, 72 Unload (form event), 86 - pop-up help, 82
text comparison (Like), 64, 124 unsafe expressions (when - project explorer, 80
text field (in table), 10 opening a database), 8 - reference card, 116
time/date field (in table), 11 update (record), 8 - saving the code, 81
time/date functions (VBA), 130 UPDATE (record, SQL), 134 - tools, 80–83
Timer event, 98 update (record, VBA), 88
TimerInterval, 98 Update (recordset), 109 W
toolbars. See menus user role, 6, 132 wait for event, 98
toolbox (for drawing), 18 user windows (forms), 18 week number (Format function),
tools (Visual Basic), 80–83 126
Top (position property), 28, 100 V WHERE (query), 54
tree structure (in database), 16 Value (text box), 72 While statements (VBA), 118
trim functions (VBA), 128 Var (query), 60 Width (size property), 28, 100
twips (size unit), 100 variables (declarations, VBA), wildcarding (text comparison),
type check functions (VBA), 130 120 64, 124
type conversion (VBA), 125 variables (initial values), 120 windows (forms), 18
type declaration (VBA), 120 variant data type (VBA), 120 With-End (VBA), 123
types of data. See data type VarType (VBA), 130 Wizard
vbKey . . . (VBA), 97 - form, 31
U view (of form) - lookup, 14
UBound (array bound, VBA), - datasheet advantages, 38
128 - form view advantages, 38 Y
Ucase function (VBA), 128 - shortcut keys for changing, 22 yes/no field, 11
unbound controls, 20, 40 Visual Basic, 68–115, 116–34
undo data entry, 8 - breakpoints, 82
154 Index