102
5.5.7 Column order, column hidden, etc.
SelTop (attribute, read and write). The number of the
top row in the selected area. The first record in the
table behind the form is row 1, the next 2, etc.
Notice that the first records may be scrolled out of
sight.
When a form (or subform) is shown in table view, the
user can rearrange and hide columns. To rearrange
columns, the user selects a column and drags it to a
new position. To hide it, he selects the column and uses
Format -> Hide columns.
SelHeight (attribute, read and write). The number of
rows selected. If SelHeight is zero, no area is
selected, but the record is still selected.
The program may need to know these settings or it may
need to change them.
SelWidth (attribute, read and write). The number of
columns selected. Hidden columns are included. If
SelWidth is zero, no area is selected, but the record
is still selected.
Remember that each column corresponds to a control
on the form (section 3.2.1). As an example, column 2
on Figure 5.5E corresponds to a text box control bound
to the description field of the table. The column
heading (Class) is the label associated with the text box
(or the name of the text box if there is no label).
SelLeft (attribute, read and write). The number of the
left column in the selected area. In Access 97 the
columns are numbered from 1 and up. In Access
2000 they are numbered from 2 and up when an
area is selected.
The program can detect and set column order, etc.
through these three attributes of the controls:
ColumnHidden (attribute, read and write). True or
False.
Beware: The strange change from 1-based numbering
to 2-based numbering must have puzzled quite a
number of developers. Obviously it is an error. The
situation is even stranger when only a record is
selected, but not an area. In this case you set SelLeft
with 1-based numbers, but retrieve it with 2-based
numbers.
ColumnOrder (attribute, read and write). The number
of the column. Columns are counted from 1 and up
in the sequence that the user sees. However, hidden
columns are included in the counts.
ColumnWidth (attribute, read and write). The width of
the column measured in twips (see section 5.5.13).
If the program sets ColumnWidth to -2, the width is
adjusted to fit the data in the column. Width = -1
means a default width. Width = 0 makes the column
hidden (and you have to explicitly unhide it -
setting the width is not enough).
If columns have been reordered, their numbers follow
what the user sees. However, hidden columns are
included in the counts.
Keeping the area selected
In the hotel system, the user selects rooms through a
form like Figure 5.5E. In principle this is easy.
However, as soon as focus moves from the subform to
the main form, Access removes the area selection. This
happens for instance when the user clicks a button in
the main form. The event procedure behind the button
wouldn't even know which area was selected.
Example: Suppose the program needs to change the
column width of Class (the description control) in
Figure 5.5E. An event procedure on the main form
could do it in this way:
Me.subRoomGrid.Form ! description.ColumnWidth = 1500
The program has to compensate for Access's strange
behavior. When focus leaves the subform, the Exit
event procedure saves the area size attributes before
Access removes the area selection. When some control
on the main form gets the focus, its event procedure
sets them back. There is no common event on the main
form that can do it, so each control has to act.
5.5.8 Area selection, SelTop, etc.
When a subform is shown in table view, the user can
select a rectangular area of the subform. Figure 5.5E
shows an example where the user has selected column
3 and 4 from the fourth and fifth row (see more about
the room grid in sections 7.4 and 7.5).
Main form
The program can detect which area the user has
selected. The figure shows how we can try it out
through the Immediate window (Ctrl+G). We address
the first open form, its subform control, and the
subform it is bound to. When asking for the value of
SelTop, we get 4 because the top row in the selected
area is record number 4.
In the main form, declare these variables:
Public aWidth As Long, aHeight As Long, aLeft As Long
' These variables keep track of the current area size. aleft is
the correct column number, not the Access2000 distortion.
SelTop is not saved since current record always is the top.
The following procedures on the main form update and
use these variables (shown in the Access 2000 version).
As the figure shows, we can also change the area size
and location. We have changed the area width to 2. The
following four attributes of the form control the area.
94
5. Access through Visual Basic
How to C#: Basic SDK Concept of XDoc.PDF for .NET You may add PDF document protection functionality into your C# program. To be specific, you can edit PDF password and digital signature, and set PDF file
pdf form save in reader; create a form in pdf VB.NET PDF: Basic SDK Concept of XDoc.PDF You may add PDF document protection functionality into your VB.NET program. To be specific, you can edit PDF password and digital signature, and set PDF file
create a fillable pdf form from a pdf; pdf form creation
75
Fig 5.5E Selected area, SelLeft, SelTop, SelWidth, SelHeight
SelTop = 4
SelLeft = 4 (3 in Access 97)
SelHeight = 2
SelWidth = 2
ColumnHidden = False
ColumnOrder = 2
ColumnWidth = 920 (twips)
Private Sub subRoomGrid_Exit(Cancel As Integer)
' This event happens when focus moves from the subform to
the main form. Save the current area size before Access
removes it.
aWidth = subRoomGrid.Form.SelWidth
aHeight = subRoomGrid.Form.SelHeight
aLeft = subRoomGrid.Form.SelLeft - 1
' -1 to correct for the Access2000 error
End Sub
Private Sub resetSelection() ' Shared procedure
subRoomGrid.Form.SelWidth = aWidth
subRoomGrid.Form.SelHeight = aHeight
' Make sure to set Width and Height before setting Left
subRoomGrid.Form.SelLeft = aLeft + 1
' +1 to compensate for the Access 2000 error.
End Sub
Private Sub txt . . ._GotFocus()
' Reset the area size when a main-form control gets the focus.
Call resetSelection()
End Sub
. . . (GotFocus for other controls)
Private Sub subRoomGrid_Enter()
' When focus moves from the main form to the subform.
Access removes the area selection just before this event
(Access 2000 only). So reset the area size
Call resetSelection()
End Sub
User actions in the subform
The solution above works fine when an area is selected
in the subform. For the hotel system, the program
selects a suitable area corresponding to a free room. If
the user clicks Check in, for instance, everything is
okay.
If the user expands the area by means of Shift + arrow
key, everything is fine too.
But what happens if the user clicks a cell to move the
area to another room? Access will remove the area
selection and the user will just see the cursor flashing
in the cell he clicked. The user may drag the cursor to
select an area, or use Shift + click, but it is not
intuitive. The program should make sure that a suitable
area is selected at any time.
One problem is that as soon as the user clicks a cell,
Access removes the area selection before calling any
event procedure. The program cannot catch the old
selection at this point.
The solution is to let the program act at MouseUp and
KeyUp. There are two situations:
An area is selected: This happens if the user expanded
the area with Shift + arrow key, or if the user
dragged an area with the mouse. The program
should save the area size.
No area is selected: This happens if the user has
clicked a cell or moved to the cell with an arrow
key. The program should reset the area size so that
the selected cell becomes the top left of the area.
Each single control on the subform has to react this
way. Although there is a Form_MouseUp and a
Form_KeyUp, they don't respond to clicks inside the
grid. They respond only to clicks on the grid border,
i.e. the headings and the record selectors.
In the hotel system, fsubRoomGrid has one control for
each column in the grid. The names of these controls
are cbo1, cbo2, etc. (see section 7.5 for details). So we
need these procedures on the subform:
5. Access through Visual Basic
95
109
Public Sub saveSelection() ' Save the current area selection
after key up or mouse up, or reset it after clicks, etc.
If SelHeight = 0 Then ' No area selected. Reset area.
' Check first that the new area is within the grid.
SelHeight = Parent.aHeight
SelWidth = Parent.aWidth
End If
Parent.aWidth = SelWidth
Parent.aHeight = SelHeight
Parent.aLeft = SelLeft - 1 ' Access 2000 compensation
End Sub
Private Sub cbo1_KeyUp(KeyCode As Integer, Shift As
Integer)
Call saveSelection()
End Sub
Private Sub cbo1_MouseUp(Button As Integer, Shift As
Integer, X As Single, Y As Single)
Call saveSelection()
End Sub
. . . (KeyUp and MouseUp for the other controls)
Notice that when saveSelection resets the area size, it
should first check that the area is within the grid. The
user may for instance have clicked at the far right with
a wide area selected.
Also notice how the program uses Parent to address
the saved area size.
This solution works okay. The user can use mouse or
keyboard to move an area around in the grid. However,
the screen flickers. It is clearly visible that Access
removes the area selection before the program resets it.
Below we explain how to deal with it.
By the way, since the subform saves the area size on its
own, there is no reason that the subform control on the
main form saves it too. So the event procedure
subRoomGrid_Exit
may be removed.
Clicking in the grid border
If the user clicks in the grid header, Access selects the
entire column. If he clicks in the record selector in the
left border, Access removes the area selection and
selects the record the user clicked. In both cases we
want the old area selection to be reset.
The place to deal with this is the MouseUp procedure
for the subform, Form_MouseUp. It should set the area
size in the same way as saveSelection(). However, here
is another Access error. Access refuses to change
SelHeight etc. The work-around is to start out with
SelHeight = 0
This is necessary even if SelHeight is 0 already (at
least in Access 2000).
Avoiding flicker
It is possible to avoid the flicker by letting the program
take action before Access removes the area selection.
Two event procedures on the subform are involved:
Form_KeyDown(KeyCode As Integer, Shift As Integer)
This is the Key preview procedure (see section
5.5.9). It must handle arrow keys and tabs to the left
or right by setting SelLeft and aLeft - after
checking that the area is inside the grid, of course.
Setting SelLeft causes Access to not remove the
area.
Form_Current()
Is called when the user uses arrow up or down, or
clicks in a new row. It is called before MouseUp or
KeyUp occurs. Sets SelWidth and SelHeight early
on to avoid flicker. If Current doesn't do anything,
the area will be set at MouseUp or KeyUp, but
Access will remove the area in between, causing
flicker.
Getting the selected data
When a function acts on the selected area, it needs to
know which data the area contains. If the area is only
one row high, it can directly address the controls in the
current subform record. In general, however, it needs to
retrieve data for several rows. The data has to be
retrieved from the recordset bound to the form.
One solution is to retrieve a number of rows from the
recordset by means of the GetRows property. Here is
an outline of the solution for the hotel case.
In the main form, declare a variant array:
Dim A()
' The A-array will receive a number of records
The basic idea is to use this piece of code inside the
button event procedure:
A = Me.subRoomGrid.Form. Recordset. GetRows(n)
We address the subform (fsubRoomGrid) in the usual
way. The property Recordset is the recordset bound to
the subform. The property GetRows copies n records
from the current record and on. It stores the copies in
the array A.
While the area size and position is 1-based (and 2-
based), the A array is zero-based. As a result, A will
hold this:
A(0, 0): The first field in the first record.
A(0, 1): The first field in the second record.
A(1, 0): The second field in the first record.
. . .
and so on.
As a side effect, GetRows moves the current record
forward so that it points to the row after the selection.
To avoid this, use the built-in clone of the recordset,
make it point to the current record, and then copy the
rows through it:
Me.subRoomGrid.Form.RecordsetClone.Bookmark = _
Me.subRoomGrid.Form.Recordset.Bookmark
A = Me.subRoomGrid.Form.RecordsetClone.GetRows(n)
Section 5.6 explains more on this.
96
5. Access through Visual Basic
107
Dealing with re-ordered columns
A small problem remains. If the user has re-ordered the
columns in the datasheet, the order of the record fields
and the datasheet columns don't match.
To overcome this, use the ColumnOrder property of the
subform controls. This property gives you the column
number (1-based) for the control. The ControlSource
property of the control gives you the field name that
the control is bound to. See section 5.5.7 for more on
ColumnOrder.
5.5.9 Key preview
When the user clicks something on the keyboard, the
control in focus will receive several key events. How-
ever, the Form can have a look at the key events before
the control gets the events. This is useful when no spe-
cific control has to take action.
One example is function keys. For instance we may
want F2 to mean Reset Criteria no matter where in the
form the cursor is. For the Find Guest window it can be
done in this way:
•
On frmFindStay, open the Form-property box, se-
lect the Event tab and set Key Preview to Yes.
•
Define an event procedure for the Form's KeyDown
event. It should look like this:
Private Sub Form_KeyDown (KeyCode As Integer, _
Shift As Integer)
If KeyCode = vbKeyF2 Then
Me.subStayList. Form. RecordSource = _
"select * from qryStayList; "
Me.txtName = ""
End If
End Sub
The KeyDown event has two parameters. KeyCode (an
integer) shows which key is pressed. There are prede-
fined constants for the various keys. For instance
vbKeyF2 means the F2 key, vbKeyA the A-key and
vbKeyEscape the Esc key.
If the procedure returns with KeyCode = 0, Access
stops further processing of the KeyDown event.
Shift (an integer) shows whether some of the shift keys
were down at the same time (Shift, Alt, Ctrl). There are
predefined constants for the shift keys: acShiftMask
(=1), acCtrlMask (=2), acAltMask (=4). The program
can for instance test whether Shift and Ctrl are down at
the same time with this statement
If Shift = acShiftMask + acCtrlMask Then . . .
In the example above, the procedure tests whether the
F2 key was used. In this case it sets record source to
the full list of stays, and it sets the search criterion to an
empty text.
Parent form
If you try it, you may notice that F2 works okay if the
focus is in the main form, but not when it is in the sub-
form. You have to define key preview actions also for
the subform if F2 is to work here too. However, from
the subform you have to address the items in the main
form slightly differently:
' In the subform:
Me. Parent. subStayList. Form. RecordSource = _
"select * from qryStayList; "
Me. Parent. txtName = ""
Me refers to the subform, and Me.Parent refers to the
parent form, i.e. the master form.
Return value
At return from the preview procedure, Access contin-
ues processing the event. It sends it to the control in
focus, or makes its own response to the event. In case
of F2 the control ignores F2, but Access has a standard
response: it toggles between selecting the entire field
and just showing the simple cursor.
The preview procedure can skip further processing by
setting KeyCode=0 at return. In case of F2, the result
would be that Access didn't toggle between field selec-
tion and the simple cursor.
5.5.10 Error preview
Access detects various kinds of user errors, for instance
that the user enters a non-existing date, or the user
forgets to select a related record where referential
integrity is required. Access will show an error
message that often is entirely gibberish to the user.
When for instance the referential integrity is violated,
Access will say:
You cannot add or change a record because a related
record is required in table 'tblGuest'.
How can the program show a meaningful message
instead? The BeforeUpdate procedure is intended to let
the program check the data, but when Access detects
the error it doesn't even call BeforeUpdate.
However, the Form has an Error event procedure that
can interfere before Access shows the error message to
the user. To use it, define the event procedure for the
Error event:
Private Sub Form_Error(DataErr As Integer, _
Response As Integer)
If DataErr = 3201 Then
MsgBox "Select a guest", vbOKOnly + vbExclamation
Response = acDataErrContinue
End If
End Sub
The Error event has two parameters. DataErr is an ID
for the error. Response tells Access what to do at re-
turn. When Response = acDataErrContinue, Access
will not show its own error message. When Response =
5. Access through Visual Basic
97
101
acDataErrDisplay (the default), Access will show its
own error message.
In the example, the event handler tests whether the
error is "You cannot add or change . . ." (ID 3201). In
this case it shows a message box with a more user-
oriented text. Then it tells Access to keep its mouth
shut about the error.
At return, Access keeps the focus on the Form where
the error occurred, so that the user can correct the data -
or use Esc to undo changes. This happens no matter
whether Response is acDataErrContinue or not.
The main problem when using the Error event is to find
out what the error ID means. I have not been able to
find a list of the codes.
Don't confuse the Error event with VBA's handling of
program errors. The Error event handles errors caused
by the user's actions. VBA handles cases where the
program tries to divide by zero, access a non-existing
object, etc. VBA has statements such as On Error
GoTo x to let the programmer deal with the error. See
section 6.1 for a detailed explanation of this kind of
error handling.
5.5.11 Timer and loop breaking
A Form responds to user actions such as editing a field
or clicking a button. Sometimes we want the Form to
act on its own. It might for instance update a list of data
every minute. This would be useful in a multi-user
application where several people update the database.
Timer handling
Start the timer by setting the Form's TimerInterval, for
instance in the Form's Load event procedure:
Me.TimerInterval = 60 000 ' One minute is 60 000 ms.
Next define an event procedure for the Timer event:
Private Sub Form_Timer()
Me.Requery
End Sub
When the Form opens, it sets the timer interval to one
minute and waits for some event. The event may be the
user doing something or one minute having passed.
When the minute has passed, Access calls the Timer
event procedure, which recalculates all bound data in
the form.
The next timer event will occur one minute after the
first timer event, even if the recalculation has taken
several seconds. How would the program stop the timer
events? Set the timer interval to 0:
Me.TimerInterval = 0 ' Stop the timer.
Let user break a loop - SendKeys
Assume the user should be allowed to stop the periodic
updating. It is easy. Provide a button labeled Stop. Let
its click procedure set the timer interval to 0.
This works because when the Form waits for the timer,
it also responds to user events. It will handle the events
in the sequence they occur.
Now imagine that the Form runs a long calculation or
scans the Internet for hours. It runs in a loop and it
doesn't wait for events at any point. How can we allow
the user to break the loop? If we provide a Stop button
as above, the user may click it, but the Form doesn't
listen to events. However, Access stores the event in a
queue for later processing.
The user could use Ctrl+Break. It will in a very rough
way stop the program, probably in the middle of
something, so that data is left in an inconsistent state.
Actually, Ctrl+Break is the only way you can stop a
very long-lasting database query. In this case the
database engine takes care that the database ends up in
a consistent state. For a long calculation in our own
program, we need a structured way to stop.
The trick is to let the program use SendKeys every now
and then to generate an event and wait for the event to
be processed. This allows Access to process all events
in the queue. It also allows Access to update the screen,
for instance to show controls that the program has
changed during the calculation.
Here is an outline of such a program. We declare a
module-level Boolean in the Form, which becomes true
when the user clicks the Stop button:
Dim bStop As Boolean
When Access responds to the click, this event
procedure sets bStop:
Private Sub cmdStop_Click()
bStop = True
End Sub
A long computation runs in a loop until the computa-
tion is finished. Once in the loop it displays the
progress of the computation by storing a result in a text
box control. It could look like this:
Private Sub cmdCompute_Click()
' Plain loop
While . . . ' Until calculation is finished
. . . ' One step in a long calculation
txt . . . = . . . ' Display progress
Wend
End Sub
In order to respond to the stop, we modify the loop so
that it runs until bStop is True or the computation is
finished. In addition we use the SendKey operation to
send a character to Access and wait for Access to
98
5. Access through Visual Basic
117
process it - and process other events too. Here is the
new program:
Private Sub cmdCompute_Click()
' Test once in loop
bStop = False
While Not bStop And . . . ' Until stop or calculation finished
. . . ' One step in a long calculation
txt . . . = . . . ' Display progress
SendKeys "{home}", True ' Type a character and wait.
' Allows Access to handle all events and
' update the screen.
Wend
End Sub
SendKeys has two parameters. The first is a string of
characters. They are sent to Access as if the user typed
them. (Or rather, to the program that has the focus on
the screen. You might for instance send characters to
MS Word in this way.) The second is a Boolean. If it is
True, SendKeys waits for Access to process all pending
events and update the screen. The string can hold
several characters, e.g. "abc{home}+-". Special
characters are shown as a mnemonic in { }. We have
chosen {home} since it is a rather harmless character in
the user dialog.
SendKeys takes around 1 ms on a plain computer. In
the IT world this is quite slow. If the calculation step
for instance takes 0.01 ms, the program suddenly
becomes 100 times slower.
One solution is to let the program listen and update the
screen only once a second. In the procedure below, we
use the Time() function to find out when a second has
passed. It returns the number of seconds since
midnight, with fractional seconds too. The Time
function itself takes around 0.0005 ms, so it will rarely
slow down the loop significantly.
Private Sub cmdCompute_Click()
' Test once a second
Dim startTime As Double
bStop = False
startTime = Timer() +1
While Not bStop And . . . ' Until stop or calculation finished
. . . ' One step in a long calculation
If Timer() > startTime Then ' A second has passed
txt . . . = . . . ' Display progress
startTime = startTime + 1 ' Next in one second
SendKeys "{home}", True ' "Type" a character and wait.
' Allows Access to handle all events and
' update the screen.
End If
Wend
End Sub
5.5.12 Multiple form instances
When we open a form with DoCmd.OpenForm, we get
only one instance of the form. If we try to open it
again, nothing happens.
In order to open multiple instances, we need to use the
basic VBA mechanism of creating an object. Here is a
program piece that opens two instances of frmStay:
Dim stay1 As Form, stay2 As Form
' References to open forms
. . .
Set stay1 = New Form_frmStay
Call stay1.Init(740)
stay1.Visible = True
Set stay2 = New Form_frmStay
Call stay2.Init(753)
stay2.Visible = True
The first line declares two references to an open form,
stay1 and stay2. The declarations may be in a global
module that remains open as long as Access is open.
In an event procedure somewhere else, the statement
Set stay1 = . . .
creates a new object of the class
Form_frmStay
. The reference stay1 will now refer to this
object. During the creation, the form object receives the
usual open-events: Open, Load, Activate. The form is
still invisible.
When we open a form in this way, we cannot give it
OpenArgs like those we use with DoCmd.OpenForm.
The form has a property called OpenArgs, but it is
ReadOnly so we cannot store anything in it. For this
reason we have written a procedure for initializing the
form, the procedure Init. We call it with a parameter
that tells it to open stay 740. The procedure will set the
filter of the form to show this stay.
Finally, we set the Visible attribute of the form to True.
This generates the Current event and makes the form
visible.
Then we repeat the whole thing using stay2 instead of
stay1. The result is that one more form instance opens,
this time showing stay 753.
Closing the forms
Usually the form closes itself, for instance when the
user clicks the close button. Access automatically sets
references to it to Nothing. We may also close the form
by setting references to it to Nothing, like this:
Set stay2 = Nothing
The reason we have stay1 and stay2 in a global module
is that they have to be there all the time. If we made
them local variables in a procedure, the opened forms
would close as soon as the procedure returned. See
more about variables and their life-time in section 6.2.
Handling many open forms
When we need many open instances, we have to make
an array of references. A click event could then open a
new instance in this way:
Dim stay(1 To 10)
. . .
Private Sub cmdOpenStay_Click( )
' Find an unused reference, let it be j
Set stay(j) = New Form_frmStay
Call stay(j).Init( . . . )
stay(j).Visible = True
End Sub
5. Access through Visual Basic
99
103
5.5.13 Resize
When the user drags the border of a form, Access calls
the Resize event procedure of the Form. The procedure
is called for every few pixels the user drags, depending
on how fast he drags. Access also calls the Resize pro-
cedure once during open.
If the program doesn't do anything at these events, the
user will just see a larger or smaller gray area. Usually
the user expects that the controls somehow adjust to the
new size, for instance that a subform area expands or
contracts.
The way to handle this is to let the Resize procedure
recompute sizes and positions for the controls. Figure
5.5F shows a simple example. The form has a couple
of controls, one of them being a text box (txtTest) and
another a subform (subTest). As the user drags the
right border, these controls adjust their width so that
they keep the same distance from the right border of
the form. Care must be taken when a control becomes
so narrow that it cannot keep its distance from the bor-
der. Trying to set its width to a negative number will
cause a run-time error - and the mouse gets trapped
inside Access! On the lower version of the form, the
text box has width zero, and as a result the controls
don't adjust their width anymore.
At the time Resize is called, the situation is as follows:
me.WindowWidth: The new width of the form, in-
cluding the border.
me.WindowHeight: The new height of the form, in-
cluding the border.
me.WindowTop: The distance between the form and
the top of the surrounding Access window.
(Access 2003 only).
me.WindowLeft: The distance between the form and
the left side of the surrounding Access window.
(Access 2003 only).
The figure shows the Resize procedure. It declares two
important variables:
formWidth: The previous width of the form. When the
form opens, the variable has its default value,
zero. It is declared as Static, meaning that it sur-
vives from one call of Resize to the next.
dif: The difference to add to the control widths. Ini-
tially, when the form opens, dif becomes zero.
Later, it is the difference between the new form
width and the previous form width.
The procedure computes the new width of txtTest, the
smallest of the controls. When the new width is larger
than zero, it adjusts the widths of txtTest and subTest.
It also saves the new form width in order to calculate
future changes relative to this.
For subforms it may be necessary to change the width
not only of the subform control, but also of the subform
itself. See below.
Size unit
All positions and sizes are computed in twips.
One twip is 1/20 typographical point =
1/567 cm =
1/1440 inch
Resizing a form
It is easy to change the width of a control, so it is
tempting to change the width of a form in a similar
way:
me.WindowWidth = me.WindowWidth + dif
However, this works in none of the Access versions.
You have to use this statement to change position or
size of a form:
DoCmd.MoveSize right, down, width, height
right: Distance between form and the left side
of the surrounding Access window. (The
same as the property WindowLeft.)
down: Distance between form and the top of the
surrounding Access window. (The same
as the property WindowTop.)
width: The width of the form including the
frame. (The same as the property Win-
dowWidth.)
height: The height of the form including the
frame. (The same as the property Win-
dowHeight.)
In order to increase the width of the form by dif, you
have to set the form in focus and use
DoCmd.MoveSize , , oldWidth + dif
When you do this, the form receives a Resize event.
Resizing a subform
Resizing with DoCmd works fine for a main form, but
not for a subform. You cannot bring the subform in
focus. However, it doesn't matter because an open sub-
form automatically has a width that matches the sub-
form control.
When resizing the subform control, you just have to
make the program resize and/or reposition the controls
on the open subform too. You can do this from the
Resize procedure in the main form. It soon becomes
messy to let the main form know about the controls
inside the subform. So a better approach is to call a
procedure in the subform that resizes its own controls,
in much the same way as its resize procedure would do
it.
When the subform is shown as a datasheet, it has no
effect to adjust widths and heights of the controls. In-
100
5. Access through Visual Basic
Documents you may be interested
Documents you may be interested