| 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.
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:
The next screenshot shows the code used with the navigation pane and ribbon minimized.
This example shows full-screen mode with the ribbon and status bar removed. The navigation pane is also hidden:
The code also works perfectly where the Access window only fills part of the monitor:
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%:
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 = NothingEnd Sub
The combo box after update event has the code used to dock the form to specified positions is:
Private SubcboPosition_AfterUpdate()DimfwAs NewclFormWindowfw.hWnd=Me.hWndWithfwSelect Case Me.cboPositionCase1'Top Left.Top= 0.Left= 0Case2'Centre Left' Access window - form - Access window scrollbar.Top= (.Parent.Height- .Height) / 2.Left= 0Case3'Bottom Left' Access window - form - Access window scrollbar.Top= .Parent.Height- .Height- 1440 / 255.Left= 0Case4'Top Centre' Access window - form - Access window scrollbar.Top= 0.Left= (.Parent.Width- .Width) / 2Case5'Centred.Top= (.Parent.Height- .Height) / 2.Left= (.Parent.Width- .Width) / 2Case6'Bottom Centre' Access window - form - Access window scrollbar.Top= .Parent.Height- .Height- 1440 / 255.Left= (.Parent.Width- .Width) / 2Case7'Top Right' Access window - form - Access window scrollbar.Top= 0.Left= .Parent.Width- .Width- 1440 / 255Case8'Centre right' Access window - form - Access window scrollbar.Top= (.Parent.Height- .Height) / 2.Left= .Parent.Width- .Width- 1440 / 255Case9'Bottom right' Access window - form - Access window scrollbar.Top= .Parent.Height- .Height- 1440 / 255.Left= .Parent.Width- .Width- 1440 / 255End SelectEnd WithSetfw=NothingEnd 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 SubcmdDragForm_MouseDown(ButtonAs Integer,ShiftAs Integer,XAs Single,YAs Single)'drag form to new positionX=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 DatabaseOption Explicit'APIs to move a form with a mouse down eventPublic ConstWM_NCLBUTTONDOWN= &HA1Public ConstHT_CAPTION= &H2Public Declare PtrSafe FunctionSendMessageLib"user32"Alias"SendMessageA"(ByValhWndAs LongPtr, _ByValwMsgAs Long,ByValwParamAs LongPtr,lParamAs Any)As LongPtrPublic Declare PtrSafe FunctionReleaseCaptureLib"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 SubForm_Load()DimfwAs NewclFormWindowfw.hWnd=Me.hWndWithfw.Top= .Parent.Height- .Height- 1440 / 255.Left= .Parent.Width- .Width- 1440 / 255End WithSetfw=NothingEnd 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 DatabaseOption 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 TypeRECT'RECT structure used for API calls.LeftAs LongTopAs LongRightAs LongBottomAs LongEnd TypePrivate TypePOINTAPI'POINTAPI structure used for API calls.XAs LongYAs LongEnd Type'*************************************************************' Member variables'*************************************************************Privatem_hWndAs LongPtr'Handle of the window.Privatem_rctWindowAsRECT'Rectangle describing the sides of the last polled location of the window.'*************************************************************' Private error constants for use with RaiseError procedure'*************************************************************Private Constm_ERR_INVALIDHWND= 1Private Constm_ERR_NOPARENTWINDOW= 2'*************************************************************' API function declarations'*************************************************************Private Declare PtrSafe FunctionapiIsWindowLib"user32"Alias"IsWindow"(ByValhWndAs LongPtr)As LongPrivate Declare PtrSafe FunctionapiMoveWindowLib"user32"Alias"MoveWindow"(ByValhWndAs LongPtr,ByValXAs Long,ByValYAs Long, _ByValnWidthAs Long,ByValnHeightAs Long,ByValbRepaintAs Long)As Long'Moves and resizes a window in the coordinate system of its parent window.Private Declare PtrSafe FunctionapiGetWindowRectLib"user32"Alias"GetWindowRect"(ByValhWndPtrAs LongPtr,lpRectAsRECT)As Long'After calling, the lpRect parameter contains the RECT structure describing the sides of the window in screen coordinates.Private Declare PtrSafe FunctionapiScreenToClientLib"user32"Alias"ScreenToClient"(ByValhWndAs LongPtr,lpPointAsPOINTAPI)As Long'Converts lpPoint from screen coordinates to the coordinate system of the specified client window.Private Declare PtrSafe FunctionapiGetParentLib"user32"Alias"GetParent"(ByValhWndAs LongPtr)As Long'Returns the handle of the parent window of the specified window.'*************************************************************' Private procedures'*************************************************************Private SubRaiseError(ByVallngErrNumberAs Long,ByValstrErrDescAs String)'Raises a user-defined error to the calling procedure.Err.Raise vbObjectError+lngErrNumber, "clFormWindow",strErrDescEnd SubPrivate SubUpdateWindowRect()'Places the current window rectangle position (in pixels, in coordinate system of parent window) in m_rctWindow.DimptCornerAsPOINTAPIIfm_hWnd= 0OrapiIsWindow(m_hWnd)ThenapiGetWindowRect m_hWnd,m_rctWindow'm_rctWindow now holds window coordinates in screen coordinates.If Not Me.ParentIs Nothing Then'If there is a parent window, convert top, left of window from screen coordinates to parent window coordinates.WithptCorner.X=m_rctWindow.Left.Y=m_rctWindow.TopEnd WithapiScreenToClientMe.Parent.hWnd,ptCornerWithm_rctWindow.Left=ptCorner.X.Top=ptCorner.YEnd With'If there is a parent window, convert bottom, right of window from screen coordinates to parent window coordinates.WithptCorner.X=m_rctWindow.Right.Y=m_rctWindow.BottomEnd WithapiScreenToClientMe.Parent.hWnd,ptCornerWithm_rctWindow.Right=ptCorner.X.Bottom=ptCorner.YEnd WithEnd IfElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd Sub'*************************************************************' Public read-write properties'*************************************************************Public Property GethWnd()As LongPtr'Returns the value the user has specified for the window's handle.Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenhWnd=m_hWndElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd PropertyPublic Property LethWnd(ByVallngNewValueAs LongPtr)'Sets the window to use by specifying its handle.'Only accepts valid window handles.IflngNewValue= 0OrapiIsWindow(lngNewValue)Thenm_hWnd=lngNewValueElseRaiseError m_ERR_INVALIDHWND, "The value passed to the hWnd property is not a valid window handle."End IfEnd Property'----------------------------------------------------Public Property GetLeft()As Long'Returns the current position (in pixels) of the left edge of the window in the coordinate system of its parent window.Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectLeft=m_rctWindow.LeftElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd PropertyPublic Property LetLeft(ByVallngNewValueAs 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).Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowapiMoveWindow m_hWnd,lngNewValue, .Top, .Right- .Left, .Bottom- .Top,TrueEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd Property'----------------------------------------------------Public Property GetTop()As Long'Returns the current position (in pixels) of the top edge of the window in the coordinate system of its parent window.Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectTop=m_rctWindow.TopElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd PropertyPublic Property LetTop(ByVallngNewValueAs 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).Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowapiMoveWindow m_hWnd, .Left,lngNewValue, .Right- .Left, .Bottom- .Top,TrueEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd Property'----------------------------------------------------Public Property GetWidth()As Long'Returns the current width (in pixels) of the window.Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowWidth= .Right- .LeftEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd PropertyPublic Property LetWidth(ByVallngNewValueAs Long)'Changes the width of the window to the value provided (in pixels).Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowapiMoveWindow m_hWnd, .Left, .Top,lngNewValue, .Bottom- .Top,TrueEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd Property'----------------------------------------------------Public Property GetHeight()As Long'Returns the current height (in pixels) of the window.Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowHeight= .Bottom- .TopEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd PropertyPublic Property LetHeight(ByVallngNewValueAs Long)'Changes the height of the window to the value provided (in pixels).Ifm_hWnd= 0OrapiIsWindow(m_hWnd)ThenUpdateWindowRectWithm_rctWindowapiMoveWindow m_hWnd, .Left, .Top, .Right- .Left,lngNewValue,TrueEnd WithElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfEnd Property'*************************************************************' Public read-only properties'*************************************************************Public Property GetParent()AsclFormWindow'Returns the parent window as a clFormWindow object.'For forms, this should be the Access MDI window.DimfwParentAs NewclFormWindowDimlngHWndAs LongPtrIfm_hWnd= 0ThenSetParent=NothingElseIfapiIsWindow(m_hWnd)ThenlngHWnd=apiGetParent(m_hWnd)fwParent.hWnd=lngHWndSetParent=fwParentElseRaiseError m_ERR_INVALIDHWND, "The window handle "&m_hWnd& " is no longer valid."End IfSetfwParent=NothingEnd Property
Popup Forms
The same code also works using a popup form but the positions are relative to the entire Access application window
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.
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
|
|
|