I'm hoping to address an intermittent issue with forms that display off-screen. We'd like an elegant way to determine this, and if so, correct the issue by bringing the form on-screen.
We have a particular form that saves the position of the form on close to provide a consistent experience for users. However, if the user happens to move this form to their secondary monitor and then attempt to use the application when not connected to their dock this issue occurs. Walking users through how to correct is an option but not a long-term solution. Hoping the experts here can point me in the right direction.
I believe I gave you the answer to centering on multiple monitors in your other thread. However, I've also had the problem you outline in this procedure. I didn't solve the problem with an implicit-automated solution. Rather, I just checked the form (and the saved Top and Left) each time it loaded.
I tend to save this kind of information in the registry, as my application is multi-user, but each user might like their own settings regarding form-placement. Here's some code in a Form_Load of a form that saves its last position (to solve the problem you mention):
Code:
Top = GetSetting(App.Title, "Settings", "TekscanControllerTop", (ScreenHeight - Height) \ 3)
Left = GetSetting(App.Title, "Settings", "TekscanControllerLeft", (ScreenWidth - Width) \ 2)
If Top + Height > ScreenHeight Then Top = ScreenHeight - Height
If Left + Width > ScreenWidth Then Left = ScreenWidth - Width
If Top < 0 Then Top = 0
If Left < 0 Then Left = 0
And in Form_Unload, I have the following:
Code:
SaveSetting App.Title, "Settings", "TekscanControllerTop", Top
SaveSetting App.Title, "Settings", "TekscanControllerLeft", Left
I suppose I could write a general purpose procedure to check that stuff, but I don't do it all that often.
Good Luck,
Elroy
EDIT1: Also, those ScreenHeight and ScreenWidth calls are functions I wrote. You can find the code for them in the CodeBank reference I gave you in the other thread. I don't use Screen.Height or Screen.Width because they're buggy. If you put your monitor in portrait mode (and even tell Windows), VB6 doesn't necessarily get that information, and confuses Height with Width.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
This why GetWindowPlacement/SetWindowPlacement exist. They deal with this issue:
If the information specified in WINDOWPLACEMENT would result in a window that is completely off the screen, the system will automatically adjust the coordinates so that the window is visible, taking into account changes in screen resolution and multiple monitor configuration.
Apologies for the delay in responding on this thread, I had to switch gears for a while. I've now had a chance to start working with your code, thank you! However, the ScreenHeight and ScreenWidth functions you alluded to in your most recent response aren't present in the Codebank link. Could you please post?
I do have those functions, and I'll post them. But they're quite old and really designed to work on a single-monitor system. Here's a codebank entry that will work even better. Specifically, I believe the MonitorWidthPx, MonitorHeightPx, TwipsPerPixelX, & TwipsPerPixelY procedures will do precisely what you're trying to do.
Now, just to orient ourselves, the VB6 built-in Screen.Width and Screen.Height work just fine so long as your monitor isn't in portrait mode. If it is in portrait mode or you have multiple monitors, you're in trouble, and will need to do something else.
Here's my old ScreenWidth and ScreenHeight procedures (with all necessary support to execute). They will work perfectly on your primary display (even if it's in portrait mode):
Code:
Option Explicit
'
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hdc As Long) As Long
'
Public Function ScreenWidth() As Long ' Twips.
' This works even on Tablet PC. The problem is: when the tablet screen is rotated, the "Screen" object of VB doesn't pick it up.
Dim Pixels As Long
Const SM_CXSCREEN = 0
'
Pixels = GetSystemMetrics(SM_CXSCREEN)
ScreenWidth = Pixels * TwipsPerPixelX
End Function
Public Function ScreenHeight(Optional bSubtractTaskbar As Boolean) As Long ' Twips.
' This works even on Tablet PC. The problem is: when the tablet screen is rotated, the "Screen" object of VB doesn't pick it up.
Dim Pixels As Long
Const SM_CYSCREEN = 1
'
Pixels = GetSystemMetrics(SM_CYSCREEN)
If bSubtractTaskbar Then
' The taskbar is typically 30 pixels or 450 twips, or, at least, this is the assumption made here.
' It can actually be multiples of this, or possibly moved to the side or top.
' This procedure does not account for these possibilities.
ScreenHeight = (Pixels - 30) * TwipsPerPixelY
Else
ScreenHeight = Pixels * TwipsPerPixelY
End If
End Function
Public Function TwipsPerPixelX() As Single
' In a system with multiple display monitors, this value is the same for all monitors.
' It works MUCH better than Screen.TwipsPerPixelX as it recognizes portrait monitors.
Dim hdc As Long
Dim lPixelsPerInch As Long
Const LOGPIXELSX = 88 ' Logical pixels/inch in X
Const POINTS_PER_INCH As Long = 72 ' A point is defined as 1/72 inches.
Const TWIPS_PER_POINT As Long = 20 ' Also, by definition.
'
hdc = GetDC(0)
lPixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSX)
ReleaseDC 0, hdc
TwipsPerPixelX = TWIPS_PER_POINT * (POINTS_PER_INCH / lPixelsPerInch) ' Cancel units to see it.
End Function
Public Function TwipsPerPixelY() As Single
' In a system with multiple display monitors, this value is the same for all monitors.
' It works MUCH better than Screen.TwipsPerPixelY as it recognizes portrait monitors.
Dim hdc As Long
Dim lPixelsPerInch As Long
Const LOGPIXELSY = 90 ' Logical pixels/inch in Y
Const POINTS_PER_INCH As Long = 72 ' A point is defined as 1/72 inches.
Const TWIPS_PER_POINT As Long = 20 ' Also, by definition.
'
hdc = GetDC(0)
lPixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY)
ReleaseDC 0, hdc
TwipsPerPixelY = TWIPS_PER_POINT * (POINTS_PER_INCH / lPixelsPerInch) ' Cancel units to see it.
End Function
Good Luck,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Having a problem with the functions that return twips and pixels. These return correct values when on the primary monitor, but when on the secondary monitor they return zero which result in a premature exit in most cases (MonitorCount and WindowIsOnMonitor work for both monitors). Code I'm attempting to implement is below:
Code:
hWnd = oForm.hWnd
For nMon = 1 To MonitorCount
If WindowIsOnMonitor(MonitorHandle(nMon), frmMain.hWnd) Then
CenterWindowOnMonitor hWnd, MonitorHandle(nMon), bUseWorkArea
Exit For
End If
Next
Stepping into CenterWindowOnMonitor exits due to MonitorWidthPx returning zero for the secondary monitor. Essentially I'm trying to center the form being loaded on whichever monitor frmMain is on.
Having a problem with the functions that return twips and pixels. These return correct values when on the primary monitor, but when on the secondary monitor they return zero which result in a premature exit in most cases (MonitorCount and WindowIsOnMonitor work for both monitors). Code I'm attempting to implement is below:
Code:
hWnd = oForm.hWnd
For nMon = 1 To MonitorCount
If WindowIsOnMonitor(MonitorHandle(nMon), frmMain.hWnd) Then
CenterWindowOnMonitor hWnd, MonitorHandle(nMon), bUseWorkArea
Exit For
End If
Next
Stepping into CenterWindowOnMonitor exits due to MonitorWidthPx returning zero for the secondary monitor. Essentially I'm trying to center the form being loaded on whichever monitor frmMain is on.
Any thoughts?
Thanks
Brad
Got it working. My code was (in certain places) using the number of the monitor rather than the handle. Once corrected everything with this approach worked like a charm. Many thanks Elroy!