Author: Tomas Rutkauskas
How to resize all controls when the parent form is resized
Answer:
I would like to describe a technique I used in order to resize (with recursion) all
the child controls of a TForm when the user resizes the form itself. I think it
works relatively well, at least for my requirements. The key is the implementation
of the OnResize event of the TForm. Every time the TForm is resized we chnage from
an initial height PH to a final height HH and from an initial width PW to a final
width WW.
At the very beginning (for example in the OnCreate event) we register the value of
PW and PH:
1 Form1.FormCreate(Sender: TObject);
2 begin
3 { ... }
4 PW := Form1.ClientWidth;
5 PH := Form1.ClientHeight;
6 { ... }
7 end;
//Then in the implementation of the OnResize event we have:
Form1.FormResize(Sender: TObject);
begin
WW := Form1.ClientWidth; {new value of width after the resizing}
HH := Form1.ClientHeight; {new value of height after the resizing}
{put your cyclically resizing code here}
{actual dimension: they will be the old dimensions in the next resizing }
PW := WW;
PH := HH;
end;
Now let's go deep into details with the cyclically resizing code. This is the code
we place inside the implementation of the OnResize event of the TForm:
8 { ... }
9 for I := 0 to (Form1.ControlCount - 1) do
10 begin
11 ChangeDims(Form1.Controls[i], WW, PW, HH, PH);
12 end;
"ChangeDims" is the procedure which resizes (with recursion) the child controls.
13 procedure ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
14 var
15 L, T, W, H: integer;
16 begin
17 if Control is TWinControl then
18 (Control as TWinControl).DisableAlign;
19 try
20 if Control is TWinControl then
21 ScaleCtrls(Control as TWinControl, WW, PW, HH, PH);
22 L := MulDiv(Control.Left, WW, PW);
23 T := MulDiv(Control.Top, HH, PH);
24 W := MulDiv(Control.Left + Control.Width, WW, PW) - L;
25 H := MulDiv(Control.Top + Control.Height, HH, PH) - T;
26 Control.SetBounds(L, T, W, H);
27 finally
28 if Control is TWinControl then
29 (Control as TWinControl).DisableAlign;
30 end;
31 end;
32
33 procedure ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
34 var
35 I: Integer;
36 begin
37 for I := 0 to Control.ControlCount - 1 do
38 ChangeDims(Control.Controls[i], WW, PW, HH, PH);
39 end;
By making the above mentioned changes to your Form you can achieve the autoresizing
of all child controls on a form. After verifing the validity of the code I
encapsulated its functionality in a component. I didn't want to fill every time
with extra code my form's implementation: I wanted to drop a component to my form
and stop. This component assign the implementation of the OnResize event of the
form after having assigned the aventually already present OnResize implementation.
As a consequence the OnResize event of the Form will consist in the call to the
user defined implementation (if any) followed by the above mentioned implementation
which is responsible for the resizing. In this way you can assign your
implementation to the OnResize event of the Form avoiding its hiding because of the
presence of the cyclically resizing code.
Here is the complete code for the component:
40 unit Resize;
41
42 interface
43
44 uses
45 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
46
47 type
48 { THackControl = class(TControl); }
49
50 TResize = class(TComponent)
51 private
52 { Private declarations }
53 FForm1: TForm;
54 PW, PH: integer;
55 FOnResizeEvent: TNotifyEvent;
56 procedure ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
57 procedure ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
58 procedure OnResizeEvent(Sender: TObject);
59 procedure Resize();
60 protected
61 { Protected declarations }
62 public
63 { Public declarations }
64 constructor Create(AOwner: TComponent); override;
65 destructor Destroy(); override;
66 published
67 { Published declarations }
68 end;
69
70 procedure register;
71
72 implementation
73
74 constructor TResize.Create(AOwner: TComponent);
75 begin
76 inherited Create(AOwner);
77 if csDesigning in ComponentState then
78 Exit;
79 FForm1 := (Owner as TForm);
80 PW := FForm1.ClientWidth;
81 PH := FForm1.ClientHeight;
82 if Assigned(FForm1.OnResize) then
83 begin
84 FOnResizeEvent := FForm1.OnResize;
85 end;
86 FForm1.OnResize := OnResizeEvent;
87 end;
88
89 destructor TResize.Destroy();
90 begin
91 if not (csDesigning in ComponentState) then
92 FForm1.OnResize := FOnResizeEvent;
93 inherited Destroy();
94 end;
95
96 procedure TResize.ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
97 var
98 I: Integer;
99 begin
100 for I := 0 to Control.ControlCount - 1 do
101 ChangeDims(Control.Controls[i], WW, PW, HH, PH);
102 end;
103
104 procedure TResize.ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
105 var
106 L, T, W, H: integer;
107 begin
108 if Control is TWinControl then
109 (Control as TWinControl).DisableAlign;
110 try
111 if Control is TWinControl then
112 ScaleCtrls(Control as TWinControl, WW, PW, HH, PH);
113 L := MulDiv(Control.Left, WW, PW);
114 T := MulDiv(Control.Top, HH, PH);
115 W := MulDiv(Control.Left + Control.Width, WW, PW) - L;
116 H := MulDiv(Control.Top + Control.Height, HH, PH) - T;
117 { THackControl(Control).Font.Size := (PW div WW) *
118 THackControl(Control).Font.Size;}
119 Control.SetBounds(L, T, W, H);
120 finally
121 if Control is TWinControl then
122 (Control as TWinControl).DisableAlign;
123 end;
124 end;
125
126 procedure TResize.OnResizeEvent(Sender: TObject);
127 begin
128 if Assigned(FOnResizeEvent) then
129 begin
130 FOnResizeEvent(Sender);
131 end;
132 Resize();
133 end;
134
135 procedure TResize.Resize();
136 var
137 L, T, R, B, W, H, WW, HH, I: Integer;
138 begin
139 WW := FForm1.ClientWidth;
140 HH := FForm1.ClientHeight;
141 for I := 0 to (FForm1.ControlCount - 1) do
142 begin
143 ChangeDims(FForm1.Controls[i], WW, PW, HH, PH);
144 end;
145 PW := WW;
146 PH := HH;
147 end;
148
149 procedure register;
150 begin
151 RegisterComponents('Carlo Pasolini', [TResize]);
152 end;
153
154 end.
|