However, one of the things I discovered today is that some of my applications don't respect the second monitor. For example, I have a class called SFPersistentForm that I drop on most of my forms. It saves the form size and position when the form is closed and restores it when the form is reopened, giving the user the experience they expect when working with that form. However, I discovered that if I opened a form and moved it to the second monitor then closed it, when I reopened it, the form displayed on the primary monitor instead (this was a form with Desktop set to .T. so it can exist outside the application's window).
I quickly found out why: the persistence code was trying to prevent the situation where the form may open outside the screen boundaries, making it invisible. The following code handled that:
Thisform.Width = min(max(Thisform.Width, 0, Thisform.MinWidth), ;("- 50" is used to ensure the form doesn't start at exactly the right or bottom boundaries of the monitor, making it essentially invisible.)
_screen.Width)
Thisform.Height = min(max(Thisform.Height, 0, Thisform.MinHeight), ;
_screen.Height)
Thisform.Left = min(max(Thisform.Left, 0), _screen.Width - 50)
Thisform.Top = min(max(Thisform.Top, 0), _screen.Height - 50)
There are actually two problems with this code. First, the reliance on _SCREEN assumed the form exists within _SCREEN; with a top-level form or one with Desktop set to .T., that's not necessarily the case. Second, even if _SCREEN is maximized, that only fits it in the current monitor. If the form is on the other monitor, _SCREEN's dimension are irrelevant.
I initially changed the code to:
if Thisform.Desktop or Thisform.ShowWindow = 2However, it turns out that SYSMETRIC() only returns values for the primary monitor. So, I changed those two statements to:
lnWidth = sysmetric(1)
lnHeight = sysmetric(2)
else
lnWidth = _screen.Width
lnHeight = _screen.Height
endif Thisform.Desktop ...
Thisform.Width = min(max(Thisform.Width, 0, Thisform.MinWidth), ;
lnWidth)
Thisform.Height = min(max(Thisform.Height, 0, Thisform.MinHeight), ;
lnHeight)
Thisform.Left = min(max(Thisform.Left, 0), lnWidth - 50)
Thisform.Top = min(max(Thisform.Top, 0), lnHeight - 50)
declare integer GetSystemMetrics in Win32API integerNow the form reopens in the exact same size and position, including the monitor it was on.
#define SM_CXVIRTUALSCREEN 78 && Virtual Width
#define SM_CYVIRTUALSCREEN 79 && Virtual Height
lnWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN)
lnHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN)
Update: As I posted in a comment, this code correctly handle the situation where the form was originally placed on a second monitor but now that monitor doesn't exist. However, it didn't handle the case where the secondary monitor is on the left, so the starting X position is negative. See the code in the comment to deal with that.
6 comments:
You should try out UltraMon. I posted about it recently - http://flynnosborn.com/jamie/blog/?p=14
I find it a really great addition when using multiple monitors.
(I'm not a salesman, I'm a VFP developer).
Thanks for the comment, Jamie. I'll check that out.
How does the form react if it was last opened on the 2nd monitor but now your working without a 2nd monitor? I have seen applications that will open the form where the 2nd monitor would be but you can't see it. To make matters worse the form didn't support the move keyboard short cut.
Yes, it handles that no problem because the sysmetric values will change. However, in thinking about this, it doesn't handle the case where the secondary monitor is on the left, so the starting X position is negative. I changed the code to the following to deal with that:
if Thisform.Desktop or Thisform.ShowWindow = 2
declare integer GetSystemMetrics in Win32API integer
#define SM_XVIRTUALSCREEN 76 && virtual left
#define SM_YVIRTUALSCREEN 77 && virtual top
#define SM_CXVIRTUALSCREEN 78 && virtual width
#define SM_CYVIRTUALSCREEN 79 && virtual height
lnMaxLeft = GetSystemMetrics(SM_XVIRTUALSCREEN)
lnMaxTop = GetSystemMetrics(SM_YVIRTUALSCREEN)
lnMaxWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN)
lnMaxHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN)
else
lnMaxLeft = 0
lnMaxTop = 0
lnMaxWidth = _screen.Width
lnMaxHeight = _screen.Height
endif Thisform.Desktop ...
Thisform.Width = min(max(Thisform.Width, 0, Thisform.MinWidth), ;
lnMaxWidth)
Thisform.Height = min(max(Thisform.Height, 0, Thisform.MinHeight), ;
lnMaxHeight)
do case
* If we're past the left edge, move it to the left edge.
case Thisform.Left < lnMaxLeft
Thisform.Left = lnMaxLeft
* If we're past the right edge of the screen, move it to the right edge.
case Thisform.Left + Thisform.Width > lnMaxWidth + lnMaxLeft
Thisform.Left = lnMaxWidth + lnMaxLeft - Thisform.Width
endcase
do case
* If we're past the top edge, move it to the top edge.
case Thisform.Top < lnMaxTop
Thisform.Top = lnMaxTop
* If we're past the bottom edge of the screen, move it to the bottom edge.
case Thisform.Top + Thisform.Height > lnMaxHeight + lnMaxTop
Thisform.Top = lnMaxHeight + lnMaxTop - Thisform.Height
endcase
Where can you place that Code?I tried to place it in Ini() Event, unfortunately it does not work
See https://doughennig.blogspot.com/2010/02/multiple-monitor-class.html for a newer class and some sample code.
Post a Comment