Page 1 Page 2

First Published 22 July 2026 Last Updated 26 July 2026 Difficulty level :   Moderate



The idea for this article came from an email request I received recently from Jonathan Blau:

"Do you know how to position the active window in Access, say a query or form, so it appears in the lower right corner of the Access window? I want to use the DoCmd.MoveSize method, but I haven’t been able to find a way to get the position of the lower-right corner of the Access window in twips. I have done a few searches, such as in StackOverflow, but I’ve come up empty."

This was surprisngly easy to achieve using the code from the class module clFormWindow used in Form2 of my article: Centre Form on Screen

The class module is supplied with the example app used in this article and is used to get the position and dimensions of the Access window.

Example App

The attached example app contains four very similar borderless forms which open centred on the Access window. The first two are standard forms, the others are popups.
The forms can be dragged to any position by holding the left mouse button down over the Access button in the form header.

Two Borderless Forms with Position Selectors
Each form can be docked to 9 different positions on the Access window using a selection in a combo box or option group. For example, with the ribbon and navigation pane both maximized:

Dock Forms Example 1
The next screenshot shows the code used with the navigation pane and ribbon minimized.

Dock Forms Example 2
This example shows full-screen mode with the ribbon and status bar removed. The navigation pane is also hidden:

Dock Forms Example 3
The code also works perfectly where the Access window only fills part of the monitor:

Dock Forms Example 4
Finally, as the form dimensions are checked when forms are loaded, the code also handles cases where the Default Zoom value has been changed in Access Options. For example at 120%:

Dock Forms Example 5


Form Code

The code used to centre the forms on load is:

Private Sub Form_Load()
Dim fw As New clFormWindow

fw.hWnd = Me.hWnd
With fw
.Top = (.Parent.Height - .Height) / 2
.Left = (.Parent.Width - .Width) / 2
End With
Set fw = Nothing
End Sub


The combo box after update event has the code used to dock the form to specified positions is:

Private Sub cboPosition_AfterUpdate()

Dim fw As New clFormWindow

fw.hWnd = Me.hWnd

With fw

Select Case Me.cboPosition

Case 1 'Top Left
.Top = 0
.Left = 0

Case 2 'Centre Left
' Access window - form - Access window scrollbar
.Top = (.Parent.Height - .Height) / 2
.Left = 0

Case 3 'Bottom Left
' Access window - form - Access window scrollbar
.Top = .Parent.Height - .Height - 1440 / 255
.Left = 0

Case 4 'Top Centre
' Access window - form - Access window scrollbar
.Top = 0
.Left = (.Parent.Width - .Width) / 2

Case 5 'Centred
.Top = (.Parent.Height - .Height) / 2
.Left = (.Parent.Width - .Width) / 2

Case 6 'Bottom Centre
' Access window - form - Access window scrollbar
.Top = .Parent.Height - .Height - 1440 / 255
.Left = (.Parent.Width - .Width) / 2

Case 7 'Top Right
' Access window - form - Access window scrollbar
.Top = 0
.Left = .Parent.Width - .Width - 1440 / 255

Case 8 'Centre right
' Access window - form - Access window scrollbar
.Top = (.Parent.Height - .Height) / 2
.Left = .Parent.Width - .Width - 1440 / 255

Case 9 'Bottom right
' Access window - form - Access window scrollbar
.Top = .Parent.Height - .Height - 1440 / 255
.Left = .Parent.Width - .Width - 1440 / 255

End Select

End With
Set fw = Nothing

End Sub


The option group after update event code is identical to that shown above.

The code used to drag the forms to any other position (by holding down the left mouse button over the 'Access' button) is:

Private Sub cmdDragForm_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'drag form to new position
X = ReleaseCapture()
X = SendMessage(Me.hWnd, WM_NCLBUTTONDOWN, HT_CAPTION, 0)
End Sub


NOTE: The above code depends on two APIs which are supplied in the standard module, modMoveForm:

Option Compare Database
Option Explicit

'APIs to move a form with a mouse down event

Public Const WM_NCLBUTTONDOWN = &HA1

Public Const HT_CAPTION = &H2

Public Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As LongPtr, _
ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr

Public Declare PtrSafe Function ReleaseCapture Lib "user32.dll" () As Long


In response to the question I was asked by email:
1.   Import the class module clFormWindow to your app
2.   Use the following code in the Form_Load event to open that form at the bottom right of the Access window:

Private Sub Form_Load()
Dim fw As New clFormWindow

fw.hWnd = Me.hWnd
With fw
.Top = .Parent.Height - .Height - 1440 / 255
.Left = .Parent.Width - .Width - 1440 / 255
End With
Set fw = Nothing
End Sub


Substitute the relevant code from the AfterUpdate event above to open the form at any other specified position.

NOTE: Queries do not have event code. However, the query datasheet could be docked in the same way by opening it in a form.



Class Module Code

Option Compare Database
Option Explicit

'*************************************************************
' Class module: clFormWindow *
'*************************************************************
' Moves and resizes a window in the coordinate system *
' of its parent window. *
' N.B.: This class was developed for use on Access forms *
' and has not been tested for use with other window *
' types. *
'*************************************************************

'*************************************************************
' Type declarations
'*************************************************************

Private Type RECT 'RECT structure used for API calls.
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type


Private Type POINTAPI 'POINTAPI structure used for API calls.
X As Long
Y As Long
End Type

'*************************************************************
' Member variables
'*************************************************************

Private m_hWnd As LongPtr 'Handle of the window.
Private m_rctWindow As RECT 'Rectangle describing the sides of the last polled location of the window.


'*************************************************************
' Private error constants for use with RaiseError procedure
'*************************************************************

Private Const m_ERR_INVALIDHWND = 1
Private Const m_ERR_NOPARENTWINDOW = 2


'*************************************************************
' API function declarations
'*************************************************************
Private Declare PtrSafe Function apiIsWindow Lib "user32" Alias "IsWindow" (ByVal hWnd As LongPtr) As Long

Private Declare PtrSafe Function apiMoveWindow Lib "user32" Alias "MoveWindow" (ByVal hWnd As LongPtr, ByVal X As Long, ByVal Y As Long, _
ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
'Moves and resizes a window in the coordinate system of its parent window.

Private Declare PtrSafe Function apiGetWindowRect Lib "user32" Alias "GetWindowRect" (ByVal hWndPtr As LongPtr, lpRect As RECT) As Long
'After calling, the lpRect parameter contains the RECT structure describing the sides of the window in screen coordinates.

Private Declare PtrSafe Function apiScreenToClient Lib "user32" Alias "ScreenToClient" (ByVal hWnd As LongPtr, lpPoint As POINTAPI) As Long
'Converts lpPoint from screen coordinates to the coordinate system of the specified client window.

Private Declare PtrSafe Function apiGetParent Lib "user32" Alias "GetParent" (ByVal hWnd As LongPtr) As Long
'Returns the handle of the parent window of the specified window.

'*************************************************************
' Private procedures
'*************************************************************

Private Sub RaiseError(ByVal lngErrNumber As Long, ByVal strErrDesc As String)
'Raises a user-defined error to the calling procedure.

Err.Raise vbObjectError + lngErrNumber, "clFormWindow", strErrDesc

End Sub

Private Sub UpdateWindowRect()
'Places the current window rectangle position (in pixels, in coordinate system of parent window) in m_rctWindow.

Dim ptCorner As POINTAPI

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
apiGetWindowRect m_hWnd, m_rctWindow 'm_rctWindow now holds window coordinates in screen coordinates.

If Not Me.Parent Is Nothing Then
'If there is a parent window, convert top, left of window from screen coordinates to parent window coordinates.
With ptCorner
.X = m_rctWindow.Left
.Y = m_rctWindow.Top
End With

apiScreenToClient Me.Parent.hWnd, ptCorner

With m_rctWindow
.Left = ptCorner.X
.Top = ptCorner.Y
End With

'If there is a parent window, convert bottom, right of window from screen coordinates to parent window coordinates.
With ptCorner
.X = m_rctWindow.Right
.Y = m_rctWindow.Bottom
End With

apiScreenToClient Me.Parent.hWnd, ptCorner

With m_rctWindow
.Right = ptCorner.X
.Bottom = ptCorner.Y
End With
End If
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Sub

'*************************************************************
' Public read-write properties
'*************************************************************

Public Property Get hWnd() As LongPtr
'Returns the value the user has specified for the window's handle.

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
hWnd = m_hWnd
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property


Public Property Let hWnd(ByVal lngNewValue As LongPtr)
'Sets the window to use by specifying its handle.
'Only accepts valid window handles.

If lngNewValue = 0 Or apiIsWindow(lngNewValue) Then
m_hWnd = lngNewValue
Else
RaiseError m_ERR_INVALIDHWND, "The value passed to the hWnd property is not a valid window handle."
End If

End Property

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

Public Property Get Left() As Long
'Returns the current position (in pixels) of the left edge of the window in the coordinate system of its parent window.

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
Left = m_rctWindow.Left
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property


Public Property Let Left(ByVal lngNewValue As Long)
'Moves the window such that its left edge falls at the position indicated
'(measured in pixels, in the coordinate system of its parent window).

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
apiMoveWindow m_hWnd, lngNewValue, .Top, .Right - .Left, .Bottom - .Top, True
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property

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

Public Property Get Top() As Long
'Returns the current position (in pixels) of the top edge of the window in the coordinate system of its parent window.

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
Top = m_rctWindow.Top
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property


Public Property Let Top(ByVal lngNewValue As Long)
'Moves the window such that its top edge falls at the position indicated
'(measured in pixels, in the coordinate system of its parent window).

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
apiMoveWindow m_hWnd, .Left, lngNewValue, .Right - .Left, .Bottom - .Top, True
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property

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

Public Property Get Width() As Long
'Returns the current width (in pixels) of the window.

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
Width = .Right - .Left
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property


Public Property Let Width(ByVal lngNewValue As Long)
'Changes the width of the window to the value provided (in pixels).

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
apiMoveWindow m_hWnd, .Left, .Top, lngNewValue, .Bottom - .Top, True
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property

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

Public Property Get Height() As Long
'Returns the current height (in pixels) of the window.

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
Height = .Bottom - .Top
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property


Public Property Let Height(ByVal lngNewValue As Long)
'Changes the height of the window to the value provided (in pixels).

If m_hWnd = 0 Or apiIsWindow(m_hWnd) Then
UpdateWindowRect
With m_rctWindow
apiMoveWindow m_hWnd, .Left, .Top, .Right - .Left, lngNewValue, True
End With
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

End Property

'*************************************************************
' Public read-only properties
'*************************************************************

Public Property Get Parent() As clFormWindow
'Returns the parent window as a clFormWindow object.
'For forms, this should be the Access MDI window.

Dim fwParent As New clFormWindow
Dim lngHWnd As LongPtr

If m_hWnd = 0 Then
Set Parent = Nothing
ElseIf apiIsWindow(m_hWnd) Then
lngHWnd = apiGetParent(m_hWnd)
fwParent.hWnd = lngHWnd
Set Parent = fwParent
Else
RaiseError m_ERR_INVALIDHWND, "The window handle " & m_hWnd & " is no longer valid."
End If

Set fwParent = Nothing

End Property




Popup Forms

The same code also works using a popup form but the positions are relative to the entire Access application window

Dock Popup Forms Example


Known Limitations

The above code still works with popup forms for a non-maximized window BUT only if the top left of the Access window is at the top left of the monitor.

Dock Popup Forms Example 2
For this code to work correctly with popup forms where the Access window is offset, the code would need modifying to allow for the position of the top left of the Access window on the screen.
I haven't done this as, in my opinion, this is an edge use case with little value.

A more important issue is that the code doesn't take into account any changes to zooming after the form has opened.
This is because the Access window dimensions are not re-calculated after the form has loaded.
As the use of zooming is a likely scenario, I will add code to manage that in the second article in this series.



Download

Click to download:
     DockForms_v2.0      ACCDB file - approx 0.9 MB (zipped) - designed for Access 365 version 2607 or later

NOTE: Zooming of standard forms is only supported in Access 365 from version 2605. For popup forms, version 2607 or later is required

As is the case for all files downloaded from the internet, first unblock the downloaded file, unzip it, then save to a trusted location.
For more details, see my article: Unblock downloaded files by removing the Mark of the Web



Feedback

Please use the email button in the contact form below to let me know whether you found this article useful or if you have any questions.

Please also consider making a donation towards the costs of maintaining this website. Thank you

Colin Riddington Mendip Data Systems Last Updated 26 July 2026




Return to Access Articles Page Page 1 of 2 1 2 Return To Top