dcsimg
Results 1 to 9 of 9
  1. #1

    Thread Starter
    Member
    Join Date
    Mar 2014
    Posts
    46

    Resolved [RESOLVED] Detecting/Correcting Off-Screen Form

    Good Day,

    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.

    Any assistance is greatly appreciated!

    Best Regards
    Brad

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    4,164

    Re: Detecting/Correcting Off-Screen Form

    Hi Brad,

    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. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  3. #3
    PowerPoster
    Join Date
    Feb 2006
    Posts
    18,770

    Re: Detecting/Correcting Off-Screen Form

    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.

  4. #4

    Thread Starter
    Member
    Join Date
    Mar 2014
    Posts
    46

    Re: Detecting/Correcting Off-Screen Form

    Hi Elroy,

    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?

    Thanks again!

    Best Regards
    Brad

  5. #5
    PowerPoster
    Join Date
    Feb 2006
    Posts
    18,770

    Re: Detecting/Correcting Off-Screen Form

    Test this. You may be surprised. Should work all the way back to Windows 95.
    Attached Files Attached Files

  6. #6
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    4,164

    Re: Detecting/Correcting Off-Screen Form

    Hi Brad,

    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. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  7. #7

    Thread Starter
    Member
    Join Date
    Mar 2014
    Posts
    46

    Re: Detecting/Correcting Off-Screen Form

    Hi Elroy,

    I'm getting close, thanks!

    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

  8. #8

    Thread Starter
    Member
    Join Date
    Mar 2014
    Posts
    46

    Re: Detecting/Correcting Off-Screen Form

    Quote Originally Posted by dilettante View Post
    Test this. You may be surprised. Should work all the way back to Windows 95.
    Thank you! Will check this out as well.

    Best Regards
    Brad

  9. #9

    Thread Starter
    Member
    Join Date
    Mar 2014
    Posts
    46

    Re: Detecting/Correcting Off-Screen Form

    Quote Originally Posted by Brad. View Post
    Hi Elroy,

    I'm getting close, thanks!

    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!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width