Monitor Scaling Issue for Moveable Access Form using MoveWindow

ghz 7months ago ⋅ 69 views

I am trying to build a fully custom window frame from an Access popup form. Therefore, I had to disable the border style property from the form property sheet and replace it with 4 shapes that I use as my top, left, right and bottom borders. By doing so I lost the ability to drag/move the form while holding my mouse. As a result, had to use the mouse down/up/move functions on the top shape to make it draggable/moveable.

Initially used the build in function “Form.Move”, but soon realized that it will be a problem for a multi-monitor setup. Therefore, had to switch and use the win32 api to benefit from the “MoveWindow” function, as well as the “GetPhysicalCursorPos” function.

With the provided code bellow if my three monitors are scaled at the same percentage from the Windows Settings > System > Display option regardless of the screens resolution the form is moving flawlessly. However, as my third monitor is scaled at 300% compared to my other two at 150% once the form is on the 300% scaled monitor it starts to behave rather strangely. Further investigation from my Debug Print of the mouse x and y coordinates as well as top left x y coordinates of my form showed a huge jumps between the x,y positions. Therefore, making the form jump from one place of the screen to another instead of transitioning smoothly when I slide my mouse.

I would be grateful if someone could provide me with any hints on how I can tackle this scaling issue to prevent this from happening. Thanks in advance!

Debug Output:

975 and 1018 .................. 678 and 1463
1463 and 678 .................. 1018 and 975
975 and 1018 .................. 678 and 1463
1463 and 678 .................. 1018 and 975
975 and 1018 .................. 678 and 1463
1463 and 678 .................. 1018 and 975
975 and 1018 .................. 678 and 1463

Form code:

Option Compare Database
Option Explicit

Dim moveFormStatus As Boolean

Private Sub rctgl_formTopBorder_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    moveFormStatus = True
End Sub

Private Sub rctgl_formTopBorder_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
    If moveFormStatus = True Then
        Dim myrect As RECT
        Dim llCoord As POINTAPI
        GetWindowRect Me.hwnd, myrect
        GetPhysicalCursorPos llCoord
        MoveWindow Me.hwnd, llCoord.Xcoord, llCoord.Ycoord, myrect.right - myrect.left, myrect.bottom - myrect.top, True
        
        Debug.Print myrect.top & " and " & llCoord.Xcoord & " .................. " & myrect.left & " and " & llCoord.Ycoord
    End If
End Sub

Private Sub rctgl_formTopBorder_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
    moveFormStatus = False
End Sub

Module Code:

Option Compare Database
Option Explicit

Public Declare PtrSafe Function MoveWindow Lib "user32.dll" (ByVal hwnd As LongPtr, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Boolean) As Boolean
Public Declare PtrSafe Function GetWindowRect Lib "user32.dll" (ByVal hwnd As LongPtr, ByRef lpRect As RECT) As Boolean
Public Declare PtrSafe Function GetPhysicalCursorPos Lib "user32.dll" (ByRef lpPoint As POINTAPI) As Long

Public Type POINTAPI
   Xcoord As Long
   Ycoord As Long
End Type

Public Type RECT
    left As Long
    top As Long
    right As Long
    bottom As Long
End Type

Answers

It seems that the issue you're facing is related to how the screen scaling affects the coordinates you obtain when moving the form. Since the screen scaling is different on your monitors, the coordinates may not be translated correctly, resulting in the form jumping around instead of smoothly transitioning.

One possible approach to tackle this issue is to adjust the coordinates obtained based on the screen scaling factor. You can retrieve the scaling factor for each monitor and adjust the coordinates accordingly.

Here's a modified version of your code that attempts to adjust the coordinates based on the screen scaling factor:

Option Compare Database
Option Explicit

Dim moveFormStatus As Boolean
Dim scalingFactorX As Double
Dim scalingFactorY As Double

Private Sub rctgl_formTopBorder_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    moveFormStatus = True
    ' Get the screen scaling factors
    scalingFactorX = GetScalingFactorX()
    scalingFactorY = GetScalingFactorY()
End Sub

Private Sub rctgl_formTopBorder_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
    If moveFormStatus = True Then
        Dim myrect As RECT
        Dim llCoord As POINTAPI
        Dim adjustedX As Long
        Dim adjustedY As Long
        
        GetWindowRect Me.hwnd, myrect
        GetPhysicalCursorPos llCoord
        
        ' Adjust the coordinates based on scaling factor
        adjustedX = llCoord.Xcoord / scalingFactorX
        adjustedY = llCoord.Ycoord / scalingFactorY
        
        MoveWindow Me.hwnd, adjustedX, adjustedY, myrect.right - myrect.left, myrect.bottom - myrect.top, True
        
        Debug.Print myrect.top & " and " & adjustedX & " .................. " & myrect.left & " and " & adjustedY
    End If
End Sub

Private Sub rctgl_formTopBorder_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
    moveFormStatus = False
End Sub

Private Function GetScalingFactorX() As Double
    ' Get the horizontal scaling factor for the primary monitor
    Dim hWnd As LongPtr
    hWnd = 0 ' Primary monitor
    
    GetDpiForMonitor hWnd, 0, scalingFactorX, 0
    
    GetScalingFactorX = scalingFactorX / 100
End Function

Private Function GetScalingFactorY() As Double
    ' Get the vertical scaling factor for the primary monitor
    Dim hWnd As LongPtr
    hWnd = 0 ' Primary monitor
    
    GetDpiForMonitor hWnd, 0, 0, scalingFactorY
    
    GetScalingFactorY = scalingFactorY / 100
End Function

In this modified version, GetScalingFactorX and GetScalingFactorY functions retrieve the horizontal and vertical scaling factors for the primary monitor, respectively. These scaling factors are then used to adjust the coordinates obtained from GetPhysicalCursorPos. This adjustment should help in ensuring smooth form movement across monitors with different scaling factors.