Monday, May 26, 2008

Form disappeared

After I started working on dual monitors, I have found another interesting issue:

  1. Create a new application with a main form and a sub-form.
  2. Set the property Position of the sub-form to poMainFormCenter
  3. Create a button "Show Sub-Form" on main form.
  4. Move more than 50% area of the main form out of the main screen.
  5. Click the button, we created.
The sub-form disappeared! Because the popup position of sub-form is out of the screen. I did a similar test with Internet Explorer. Its dialog will be shown at the corner of the screen. It can be regarded as a bug, so I have reported this issue to QualityCentral [1] as well.

Solution

This time you have to modify Forms.pas
  1. Copy Forms.pas to your project folder,
  2. find and replace with the following code,
  3. and then recompile your project again.

  1. {$IFDEF FIXUP_FORM_POPUP_POSITION}  
  2. function GetPrimaryMonitor: TMonitor;  
  3. var  
  4.   I: Integer;  
  5. begin  
  6.   for I := 0 to Screen.MonitorCount - 1 do  
  7.   begin  
  8.     Result := Screen.Monitors[I];  
  9.     if Result.Primary then  
  10.       Exit;  
  11.   end;  
  12.   Result := Screen.Monitors[0];  
  13. end;  
  14.   
  15. procedure CenterFormToPrimaryMonitorCenter(var X, Y: Integer;   
  16.   AForm: TCustomForm);  
  17. var  
  18.   R: TRect;  
  19. begin  
  20.   R := GetPrimaryMonitor.WorkareaRect;  
  21.   X := R.Left + (R.Right - R.Left - AForm.Width) div 2;  
  22.   Y := R.Top + (R.Bottom - R.Top - AForm.Height) div 2;  
  23. end;  
  24.   
  25. procedure SnapFormToMonitorEdgeOnDemand(var X, Y: Integer;   
  26.   AForm: TCustomForm);  
  27. var  
  28.   DistanceLeft, DistanceRight: Integer;  
  29.   CenterMon: TMonitor;  
  30.   R: TRect;  
  31. begin  
  32.   CenterMon := Screen.MonitorFromPoint(  
  33.     Point(X + AForm.Width div 2, Screen.DesktopHeight div 2));  
  34.   R := CenterMon.WorkareaRect;  
  35.   // Adjust X-pos  
  36.   DistanceLeft  := X - R.Left;  
  37.   DistanceRight := R.Right - (X + AForm.Width);  
  38.   if (DistanceLeft < 0and (DistanceRight < 0then  
  39.   begin  
  40.     if DistanceLeft >= DistanceRight then  
  41.          X := R.Left // Snap to left edge  
  42.     else X := R.Right - AForm.Width; // Snap to right edge  
  43.   end  
  44.   else if (DistanceLeft < 0or (DistanceRight < 0then  
  45.   begin  
  46.     if DistanceLeft < 0 then  
  47.          X := R.Left // Snap to left edge  
  48.     else X := R.Right - AForm.Width; // Snap to right edge  
  49.   end;  
  50.   // Adjust Y-pos  
  51.   if Y < R.Top then  
  52.     Y := R.Top  
  53.   else if Y > R.Bottom - AForm.Height then  
  54.     Y := R.Bottom - AForm.Height;  
  55. end;  
  56. {$ENDIF}  
  57.   
  58. procedure TCustomForm.CMShowingChanged(var Message: TMessage);  
  59. // ...  
  60.     if (FPosition = poScreenCenter) or  
  61.        ((FPosition = poMainFormCenter) and (FormStyle = fsMDIChild)) then  
  62.     begin  
  63.       if FormStyle = fsMDIChild then  
  64.       begin  
  65.         X := (Application.MainForm.ClientWidth - Width) div 2;  
  66.         Y := (Application.MainForm.ClientHeight - Height) div 2;  
  67.       end else  
  68.       begin  
  69.       {$IFDEF FIXUP_FORM_POPUP_POSITION}  
  70.         CenterFormToPrimaryMonitorCenter(X, Y, Self);  
  71.       {$ELSE}  
  72.         X := (Screen.Width - Width) div 2;  
  73.         Y := (Screen.Height - Height) div 2;  
  74.       {$ENDIF}  
  75.       end;  
  76.     {$IFDEF FIXUP_FORM_POPUP_POSITION}  
  77.       SnapFormToMonitorEdgeOnDemand(X, Y, Self);  
  78.       SetBounds(X, Y, Width, Height);  
  79.       //SetWindowToMonitor() will cause unexpected popup position change!  
  80.     {$ELSE}  
  81.       if X < Screen.DesktopLeft then  
  82.         X := Screen.DesktopLeft;  
  83.       if Y < Screen.DesktopTop then  
  84.         Y := Screen.DesktopTop;  
  85.       SetBounds(X, Y, Width, Height);  
  86.       if Visible then SetWindowToMonitor;  
  87.     {$ENDIF}  
  88.     end  
  89.     else if FPosition in [poMainFormCenter, poOwnerFormCenter] then  
  90.     begin  
  91.       CenterForm := Application.MainForm;  
  92.       if (FPosition = poOwnerFormCenter) and (Owner is TCustomForm) then  
  93.         CenterForm := TCustomForm(Owner);  
  94.       if Assigned(CenterForm) then  
  95.       begin  
  96.         X := ((CenterForm.Width - Width) div 2) + CenterForm.Left;  
  97.         Y := ((CenterForm.Height - Height) div 2) + CenterForm.Top;  
  98.       end else  
  99.       begin  
  100.       {$IFDEF FIXUP_FORM_POPUP_POSITION}  
  101.         CenterFormToPrimaryMonitorCenter(X, Y, Self);  
  102.       {$ELSE}  
  103.         X := (Screen.Width - Width) div 2;  
  104.         Y := (Screen.Height - Height) div 2;  
  105.       {$ENDIF}  
  106.       end;  
  107.     {$IFDEF FIXUP_FORM_POPUP_POSITION}  
  108.       SnapFormToMonitorEdgeOnDemand(X, Y, Self);  
  109.       SetBounds(X, Y, Width, Height);  
  110.       //SetWindowToMonitor() will cause unexpected popup position change!  
  111.     {$ELSE}  
  112.       if X < Screen.DesktopLeft then  
  113.         X := Screen.DesktopLeft;  
  114.       if Y < Screen.DesktopTop then  
  115.         Y := Screen.DesktopTop;  
  116.       SetBounds(X, Y, Width, Height);  
  117.       if Visible then SetWindowToMonitor;  
  118.     {$ENDIF}  
  119.     end  
  120.     else if FPosition = poDesktopCenter then  
  121. // ...  
  122. end;  

Conclusion

This issue is nothing. The motivation of this article [2] is to suggest you figure out the differences among DesktopRect, BoundsRect and WorkAreaRect. They are almost the same on one monitor environment. But they are totally different on dual monitors, especially when TaskBar is not at its default position.

References

  1. Related discuss at QualityCentral.
  2. The Chinese version of this article (on my blog @csdn)
 

No comments: