Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to find screen coordinates

Status
Not open for further replies.

tedsmith

Programmer
Nov 23, 2000
1,762
AU
I realise this may be a curly one because not many people have a multi monitor card but I have a 4 port card and 4 monitors. (You can see the effect with 2 monitors connected to a 2 port card)

My question is how can I determine the left coordinates of each monitor when I first boot up my app so I can set the left coordinate of each screen to steer it into the required monitor?

Windows Display settings show the current coordinates when you hover the mouse above each little monitor box in Display/Settings that you see once you connect up the extra monitors.

I have used fixed coordinates by first booting up with each form normalised, set each Form.left coordinate then maximise and each screen snaps to its correct position.
Eg. Form1.left=0: Form2.left=1280: Form3.left=2560: Form4.left=3926

However the monitors are different resolutions and the coordinates automatically change if you boot up the computer with one monitor missing so the wrong pic ends up in the wrong screen if you just use fixed coordinates.

The use is an airport departure lounge and I don't want people boarding the wrong plane!

 
For example if the 4 monitors are arranged in a square with the primary monitor at bottom right the following coordinates (pixels) will describe a point on each one;

-5,-5; 5,-5; 5,5; -5,5

You can then use the MonitorFromPoint API to determine the hMonitor handle for each monitor.

Once you have that you can determine the coordinates for each monitor from the GetMonitorInfo API
It is worth noting that Windows treats a connected monitor as connected AND switched on; it does not seem possible to determine if a connected monitor is actually switched on.

A recent article at Karls may be of interest.
 
Thanks
I found a simple ready made example on another forum that used a few APIs such as GetSystemMetrics and GetMonitorInfo that works fine giving the coordinates and a number to each screen. (saved a lot of working out the complicated variable stuff needed for the API's!)

The only catch is that if say you have monitors 1,2,3,4 in a row and you disconnect monitor 2 and reboot, the monitor numbers change. The card thinks there are now only 3 monitors. Monitor 3 now becomes position 2 and monitor 4 becomes position 3

Depending on which monitor you pull out, it jumps to a different arrangement like 1 4 3 instead of 1 3 4

This makes it very hard to ensure the same pic goes to the same screen.
Imagine if you had 9 monitors in a big video display and you pulled out the middle one, the picture fragments all over the place.
I can't find out (yet) how to make the positions stay as original except by a complicated routine of select case and if statements to reposition the vb forms for every combination accordingly then maximise them - not very elegant!
 
If you hardcode for a given number of monitors and layout you had better be sure that the number of monitors and their layout pre-exists.

Otherwise you should check the actual layout/ number of monitors and make the best of it; or advise the user that the layout/ number of monitors is unsuitable.

Another link I found useful at
You may need more but the following is all I use just now;
Code:
'adapted, and now not very similar, from code in [URL unfurl="true"]http://www.freevbcode.com/source/0207/Multi-MonitorFormCentering.zip[/URL]

Option Explicit

'required but declared public by me elsewhere
'Private Type RECT
'    Left As Long
'    Top As Long
'    Right As Long
'    Bottom As Long
'End Type

'Constants for the return value when finding a monitor
Private Enum dwFlags
    MONITOR_DEFAULTTONULL = &H0       'If the monitor is not found, return 0
    MONITOR_DEFAULTTOPRIMARY& = &H1   'If the monitor is not found, return the primary monitor
    MONITOR_DEFAULTTONEAREST = &H2    'If the monitor is not found, return the nearest monitor
End Enum

Private Const MONITORINFOF_PRIMARY = 1

'Structure for the position of a monitor
Private Type tagMONITORINFO
    cbSize      As Long 'Size of structure
    rcMonitor   As RECT 'Monitor rect
    rcWork      As RECT 'Working area rect
    dwFlags     As Long 'Flags
End Type

Type UDTMonitor
    handle As Long
    Left As Long
    Right As Long
    Top As Long
    Bottom As Long
    
    WorkLeft As Long
    WorkRight As Long
    WorkTop As Long
    Workbottom As Long
    
    Height As Long
    Width As Long
    
    WorkHeight As Long
    WorkWidth As Long
    
    IsPrimary As Boolean
End Type

Private Declare Function MonitorFromRect Lib "user32" (rc As RECT, ByVal dwFlags As dwFlags) As Long
Private Declare Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoA" (ByVal hMonitor As Long, MonInfo As tagMONITORINFO) As Long

Public Sub EnsureFormIsInsideMonitor(Frm As Form, Optional RefForm As Form)
    
    'typically used to determine if the previously saved position/size of a Form needs adjustment re the current monitor layout
    ' if a Form is mapped to a disconnected monitor the Form is remapped to the Primary monitor
    
    'typical usage:
    'Private Sub Form_Load()
    '   retrieve previous left and top positions from file and apply them to Me.Left and Me.Top Properties
    '   EnsureFormIsInsideMonitor Me
    'End Sub
    
    'If Reform is not specified Frm is positioned to be displayed entirely within the monitor on which most of it is currently mapped
    'If Reform is specified Frm is positioned to be displayed entirely on the same monitor as that on which most of Reform is mapped
    
    'adjusts Frm Left and Top so that all the borders of Frm are contained within the same Monitor
    
    ' if Frm.Width or Height exceed monitor.width or height Frm is positioned at Left/ Top of monitor and
    '  Width/ Height of Frm may be adjusted if Frm is Sizable
        
    Dim VFlag As Boolean, HFlag As Boolean
    Dim cMonitor As UDTMonitor
    
    If RefForm Is Nothing Then Set RefForm = Frm
    
    cMonitor = MonitorProperties(RefForm)

    With Frm
        If .Width > cMonitor.WorkWidth Then
            If .BorderStyle = vbSizable Or .BorderStyle = vbSizableToolWindow Then
                .Width = cMonitor.WorkWidth
            Else
                .Left = cMonitor.WorkLeft: HFlag = True
            End If
        End If
        If .Height > cMonitor.WorkHeight Then
            If .BorderStyle = vbSizable Or .BorderStyle = vbSizableToolWindow Then
                .Height = cMonitor.WorkHeight
            Else
                .Top = cMonitor.WorkTop: VFlag = True
            End If
        End If

        If Not HFlag Then
            If .Left < cMonitor.WorkLeft Then .Left = cMonitor.WorkLeft
            If (.Left + .Width) > cMonitor.WorkRight Then .Left = cMonitor.WorkRight - .Width
        End If
        If Not VFlag Then
            If .Top < cMonitor.WorkTop Then .Top = cMonitor.WorkTop
            If (.Top + .Height) > cMonitor.Workbottom Then .Top = cMonitor.Workbottom - .Height
        End If
    End With

End Sub

Public Function MonitorProperties(Frm As Form) As UDTMonitor
    
    'Return the properties (in Twips) of the monitor on which most of Frm is mapped
    
    Dim hMonitor As Long
    Dim MonitorInfo As tagMONITORINFO
    Dim tppx&, tppy&
    Dim Frect As RECT
    
    GetWindowRect Frm.hWnd, Frect
    hMonitor = MonitorFromRect(Frect, MONITOR_DEFAULTTOPRIMARY) 'get handle for monitor containing most of Frm
                                                                 ' if disconnected return handle (and properties) for primary monitor
    tppx = Screen.TwipsPerPixelX
    tppy = Screen.TwipsPerPixelY
    
    On Error GoTo GetMonitorInformation_Err
    MonitorInfo.cbSize = Len(MonitorInfo)
    GetMonitorInfo hMonitor, MonitorInfo
    With MonitorProperties
        .handle = hMonitor
        'convert all dimensions from pixels to twips
        .Left = MonitorInfo.rcMonitor.Left * tppx
        .Right = MonitorInfo.rcMonitor.Right * tppx
        .Top = MonitorInfo.rcMonitor.Top * tppy
        .Bottom = MonitorInfo.rcMonitor.Bottom * tppy
        
        .WorkLeft = MonitorInfo.rcWork.Left * tppx
        .WorkRight = MonitorInfo.rcWork.Right * tppx
        .WorkTop = MonitorInfo.rcWork.Top * tppy
        .Workbottom = MonitorInfo.rcWork.Bottom * tppy
        
        .Height = (MonitorInfo.rcMonitor.Bottom - MonitorInfo.rcMonitor.Top) * tppy
        .Width = (MonitorInfo.rcMonitor.Right - MonitorInfo.rcMonitor.Left) * tppx
        
        .WorkHeight = (MonitorInfo.rcWork.Bottom - MonitorInfo.rcWork.Top) * tppy
        .WorkWidth = (MonitorInfo.rcWork.Right - MonitorInfo.rcWork.Left) * tppx
        
        .IsPrimary = MonitorInfo.dwFlags And MONITORINFOF_PRIMARY
    End With
    
    Exit Function
GetMonitorInformation_Err:
    Beep
    If Err.Number = 453 Then
        'should be handled if pre win2k compatibility is required
        'Non-Multimonitor OS, return -1
        'GetMonitorInformation = -1
        'etc
    End If
End Function

So if I can't find the monitor where the Form was last time I plump for the MONITOR_DEFAULTTOPRIMARY; you may like to opt for MONITOR_DEFAULTTONEAREST or a decision after a MONITOR_DEFAULTTONULL
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top