>So (all) we have to determine is if the previously saved position of a form is valid in the current monitor setup; if it is not we have to adjust its Left and Top to fall in the Primary or the Nearest monitor or display a message.
That is pretty much it. When loading the application, we have to find a monitor which is closest to the saved window position.
Of course, the application will find the same monitor if the configuration is not changed.
If the configuration is changed or the user is running the application from another machine, and there is no monitor present at the saved location, we have to find a monitor closest to that location and move the window on that monitor instead.
See the working example which demonstrate this. It also shows how to get a layout of all monitors on the virtual screen using the EnumDisplayMonitors function.
1. Start a new project, set the border style of Form1 to Fixed Single.
2. Place a Label on the form. Set its name to lblMonitors and make it a control array by setting its Index to 0.
3. Insert the following code in Form1.
___
[tt]
Private Sub Form_Load()
EnumMonitors Me
LoadPosition hwnd
End Sub
Private Sub Form_Unload(Cancel As Integer)
SavePosition hwnd
End Sub[/tt]
___
4. Add a module and insert the following code and test the program with different monitor configurations.
___
[tt]
Option Explicit
Private Declare Function EnumDisplayMonitors Lib "user32" (ByVal hdc As Long, lprcClip As Any, ByVal lpfnEnum As Long, dwData As Any) As Long
Private Declare Function MonitorFromRect Lib "user32" (ByRef lprc As RECT, ByVal dwFlags As Long) As Long
Private Declare Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As Long, ByRef lpmi As MONITORINFO) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function UnionRect Lib "user32" (lprcDst As RECT, lprcSrc1 As RECT, lprcSrc2 As RECT) As Long
Private Declare Function OffsetRect Lib "user32" (lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type MONITORINFO
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
Const MONITOR_DEFAULTTONEAREST = &H2
Dim rcMonitors() As RECT 'coordinate array for all monitors
Dim rcVS As RECT 'coordinates for Virtual Screen
Function EnumMonitors(F As Form) As Long
Dim N As Long
EnumDisplayMonitors 0, ByVal 0&, AddressOf MonitorEnumProc, N
With F
.Move .Left, .Top, (rcVS.Right - rcVS.Left) * 2 + .Width - .ScaleWidth, (rcVS.Bottom - rcVS.Top) * 2 + .Height - .ScaleHeight
End With
F.Scale (rcVS.Left, rcVS.Top)-(rcVS.Right, rcVS.Bottom)
F.Caption = N & " Monitor" & IIf(N > 1, "s", vbNullString)
F.lblMonitors(0).Appearance = 0 'Flat
F.lblMonitors(0).BorderStyle = 1 'FixedSingle
For N = 0 To N - 1
If N Then
Load F.lblMonitors(N)
F.lblMonitors(N).Visible = True
End If
With rcMonitors(N)
F.lblMonitors(N).Move .Left, .Top, .Right - .Left, .Bottom - .Top
F.lblMonitors(N).Caption = "Monitor " & N + 1 & vbLf & _
.Right - .Left & " x " & .Bottom - .Top & vbLf & _
"(" & .Left & ", " & .Top & ")-(" & .Right & ", " & .Bottom & ")"
End With
Next
End Function
Private Function MonitorEnumProc(ByVal hMonitor As Long, ByVal hdcMonitor As Long, lprcMonitor As RECT, dwData As Long) As Long
ReDim Preserve rcMonitors(dwData)
rcMonitors(dwData) = lprcMonitor
UnionRect rcVS, rcVS, lprcMonitor 'merge all monitors together to get the virtual screen coordinates
dwData = dwData + 1 'increase monitor count
MonitorEnumProc = 1 'continue
End Function
Sub SavePosition(hwnd As Long)
Dim rc As RECT
GetWindowRect hwnd, rc 'save position in pixel units
SaveSetting "Multi Monitor Demo", "Position", "Left", rc.Left
SaveSetting "Multi Monitor Demo", "Position", "Top", rc.Top
End Sub
Sub LoadPosition(hwnd As Long)
Dim rc As RECT, Left As Long, Top As Long, hMonitor As Long, mi As MONITORINFO
GetWindowRect hwnd, rc 'obtain the window rectangle
'move the window rectangle to position saved previously
Left = GetSetting("Multi Monitor Demo", "Position", "Left", rc.Left)
Top = GetSetting("Multi Monitor Demo", "Position", "Top", rc.Left)
OffsetRect rc, Left - rc.Left, Top - rc.Top
'find the monitor closest to window rectangle
hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST)
'get info about monitor coordinates and working area
mi.cbSize = Len(mi)
GetMonitorInfo hMonitor, mi
'adjust the window rectangle so it fits inside the work area of the monitor
If rc.Left < mi.rcWork.Left Then OffsetRect rc, mi.rcWork.Left - rc.Left, 0
If rc.Right > mi.rcWork.Right Then OffsetRect rc, mi.rcWork.Right - rc.Right, 0
If rc.Top < mi.rcWork.Top Then OffsetRect rc, 0, mi.rcWork.Top - rc.Top
If rc.Bottom > mi.rcWork.Bottom Then OffsetRect rc, 0, mi.rcWork.Bottom - rc.Bottom
'move the window to new calculated position
MoveWindow hwnd, rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top, 0
End Sub[/tt]