Restoring popup to same position
Over the years I have many times tried to add the functionality of restoring a popup to the same location/size in which it was closed. But so far this always came with a risk with multi monitor setup. Say a user had the app on the left screen and placed the popup on the right screen. If he then moved the app to the right screen, well then the popup would open to the right of the screen, and thus be off screen with no way for the user to restore it to a place visible to the user.
While the possibility of it happening might be remote, I never wanted to take that risk. But recently I came across a solution. Using the api MonitorFromWindow to ensure that the form is actually ON a monitor. If the API call returns 0 that means the form is not found on any monitor, and when that happens I can return the form to be centered.
Private Declare Function apiMonitorFromWindow Lib "User32.dll" Alias "MonitorFromWindow" (ByVal hWnd As Long, ByVal dwFlags As Long) As Long Public Function GetMonitorHandleFromWindow(hWnd As Long, Optional ReturnValueIfNotOnScreen As guiMonitorFromWindow = MONITOR_DEFAULTTONULL) As Long 'Returns the monitor ID based on which monitor has the largest intersection with the window GetMonitorHandleFromWindow = apiMonitorFromWindow(hWnd, ReturnValueIfNotOnScreen) End Function
But my troubles weren’t really done there. Initially I wanted to “stay within Access” as much as possible, and just wanted to use form.WindowLeft to get the forms position. However the form.WindowLeft is limited to an integer, and this means if you move to form far to the right (beyond 32000) then it overflows and becomes negative.
Jack Leach wrote some GUI handling code as a class module a while ago which overcomes this part of the problem, relying on windows API calls. So I incorporated his code and believe I finally have a solution I trust to produce a good user experience in all scenarios.
I did take it one step further and added a couple of checks. I.e. if the application monitor handle has changed since the form was last opened, then don’t make any changes (i.e. open the form to design time defaults), which for me means AutoCenter=true.
I have tested the solution in various scenarios, including 3 monitor setup, cases where I move the access application around, add-remove monitors in between closing and opening and such. In my tests, I have used Popup=true, and Autocenter=true.
You can find my modified code in a attachment at the bottom of this post, I have added it to the the GUI class module that Jack Leach designed, with his permission. You can find the original version at utteraccess Original GUI class module. It relies so heavily on this functions that I felt it made the most sense incorporate it into that module, rather than adding my own module.
So from your popup form, you call the code like so:
Private Sub Form_Close() GUI.StoreFormPosition Me End Sub Private Sub Form_Load() GUI.RestoreFormPosition Me End Sub
and what is does can be seen below:
Public Sub StoreFormPosition(frm As Form) 'Get the location of the form and the access window Dim plFormWindow As GUI_PLACEMENT GUI.WindowPlacement frm.hWnd, plFormWindow, ContainerScreen 'Save form values to registry SaveSetting CurrentProject.Name, frm.Name, "Left", plFormWindow.Left SaveSetting CurrentProject.Name, frm.Name, "Top", plFormWindow.Top SaveSetting CurrentProject.Name, frm.Name, "Width", plFormWindow.Width SaveSetting CurrentProject.Name, frm.Name, "Height", plFormWindow.Height SaveSetting CurrentProject.Name, frm.Name, "LastApplicationMonitorHandle", GUI.GetMonitorHandleFromWindow(Application.hWndAccessApp) End Sub
Public Sub RestoreFormPosition(frm As Form) Dim plFormWindowOld As GUI_PLACEMENT Dim plFormWindowRestore As GUI_PLACEMENT Dim LastApplicationMonitorHandle As Long 'Note whatever width and height the form opened with, into a placement type GUI.WindowPlacement frm.hWnd, plFormWindowOld, ContainerScreen LastApplicationMonitorHandle = GetSetting(CurrentProject.Name, frm.Name, "LastApplicationMonitorHandle", 0) If LastApplicationMonitorHandle <> 0 Then 'if 0, then no settings are stored yet With plFormWindowRestore .Left = GetSetting(CurrentProject.Name, frm.Name, "left", plFormWindowOld.Left) .Top = GetSetting(CurrentProject.Name, frm.Name, "top", plFormWindowOld.Top) .Width = GetSetting(CurrentProject.Name, frm.Name, "Width", plFormWindowOld.Width) .Height = GetSetting(CurrentProject.Name, frm.Name, "Height", plFormWindowOld.Height) End With 'Has the App monitor changed since form location was stored (user moved the application to different monitor) If GUI.GetMonitorHandleFromWindow(Application.hWndAccessApp) <> LastApplicationMonitorHandle Then 'We do nothing. Leave form as it was opened by access itself (i.e. default settings) Else 'Move form to where the user closed it GUI.SetWindowPlacement frm.hWnd, plFormWindowRestore, ContainerScreen End If End If 'Finally check that the form is witin visible screen If GUI.GetMonitorHandleFromWindow(frm.hWnd) = 0 Then 'Form is off screen, center it GUI.SetWindowGeneralPlacement frm.hWnd, PlaceXCenter, placeYCenter End If End Sub
And the full download sample for download is here:
If you end up using this solution, please take the time to leave a comment here.