Reputation: 2321
I have a combobox that has two options and the two options are triggering the form resizing. The idea is I'm going to hide and show additional controls based on the selection.
When I change the form width, the size changes as desired, however the form is now no longer in the center of the screen. Can I move the form's XY position back and forth while changing the form width?
procedure TReportFrm.SpecialFilesComboBoxChange(Sender: TObject);
begin
if(SpecialFilesComboBox.ItemIndex = 0) then begin
//No special files
Width := 412;
Height := 423;
...
end
else begin
//Yes special files
Width := 893;
Height := 423;
...
end;
end;
Upvotes: 3
Views: 2392
Reputation: 612794
Multi-monitor systems are common nowadays. Placing at the centre of the screen may spread the form across multiple monitors. That's undesirable.
So I'd centre the form on its monitor:
R := Form.Monitor.WorkAreaRect;
Form.Left := (R.Left+R.Right-Form.Width) div 2;
Form.Top := (R.Top+R.Bottom-Form.Height) div 2;
As @bummi points out, you can write:
Form.Position := poScreenCenter;
This almost works as you would wish. I will center the form on a screen. However, it always picks the default monitor. So using that code could result in your form being moved onto a different monitor which I think would never be desirable.
Rather forcing the form to the centre, you may decide instead to grow or shrink it on all sides:
procedure TReportFrm.SpecialFilesComboBoxChange(Sender: TObject);
var
NewLeft, NewTop, NewWidth, NewHeight: Integer;
begin
if(SpecialFilesComboBox.ItemIndex = 0) then begin
//No special files
NewWidth := 412;
NewHeight := 423;
...
end
else begin
//Yes special files
NewWidth := 893;
NewHeight := 423;
...
end;
NewLeft := Left + (Width-NewWidth) div 2;
NewTop := Top + (Top-NewHeight) div 2;
NewBoundsRect := Rect(NewLeft, NewTop, NewLeft+NewWidth, NewTop+NewHeight);
BoundsRect := NewBoundsRect;
end;
And if you wanted to get really cute you'd adjust the bounds rect so that the newly sized and positioned form did not go off the edge of the monitor.
procedure MakeAppearOnScreen(var Rect: TRect);
const
Padding = 24;
var
Monitor: HMonitor;
MonInfo: TMonitorInfo;
Excess, Width, Height: Integer;
begin
Monitor := MonitorFromPoint(Point((Rect.Left+Rect.Right) div 2, (Rect.Top+Rect.Bottom) div 2), MONITOR_DEFAULTTONEAREST);
if Monitor=0 then begin
exit;
end;
MonInfo.cbSize := SizeOf(MonInfo);
if not GetMonitorInfo(Monitor, @MonInfo) then begin
exit;
end;
Width := Rect.Right-Rect.Left;
Height := Rect.Bottom-Rect.Top;
Excess := Rect.Right+Padding-MonInfo.rcWork.Right;
if Excess>0 then begin
dec(Rect.Left, Excess);
end;
Excess := Rect.Bottom+Padding-MonInfo.rcWork.Bottom;
if Excess>0 then begin
dec(Rect.Top, Excess);
end;
Excess := MonInfo.rcWork.Left+Padding-Rect.Left;
if Excess>0 then begin
inc(Rect.Left, Excess);
end;
Excess := MonInfo.rcWork.Top+Padding-Rect.Top;
if Excess>0 then begin
inc(Rect.Top, Excess);
end;
Rect.Right := Rect.Left+Width;
Rect.Bottom := Rect.Top+Height;
end;
Then the previous code sample would be altered like this:
NewBoundsRect := Rect(NewLeft, NewTop, NewLeft+NewWidth, NewTop+NewHeight);
MakeAppearOnScreen(NewBoundsRect);
BoundsRect := NewBoundsRect;
Upvotes: 7
Reputation: 17203
A very simple ReCenter function may look like this:
procedure TReportFrm.ReCenter;
var
LRect: TRect;
X, Y: Integer;
begin
LRect := Screen.WorkAreaRect;
X := LRect.Left + (LRect.Right - LRect.Left - Width) div 2;
Y := LRect.Top + (LRect.Bottom - LRect.Top - Height) div 2;
SetBounds(X, Y, Width, Height);
end;
procedure TReportFrm.SpecialFilesComboBoxChange(Sender: TObject);
begin
if(SpecialFilesComboBox.ItemIndex = 0) then begin
//No special files
Width := 412;
Height := 423;
...
end
else begin
//Yes special files
Width := 893;
Height := 423;
...
end;
ReCenter;
end;
It centers the window to the WorkArea of the screen, you may want to center it to other reference, in that case it's up to you to determine the meaningful rectangle.
Upvotes: 6