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;
8
9 //Then in the implementation of the OnResize event we have:
10
11 Form1.FormResize(Sender: TObject);
12 begin
13 WW := Form1.ClientWidth; {new value of width after the resizing}
14 HH := Form1.ClientHeight; {new value of height after the resizing}
15 {put your cyclically resizing code here}
16 {actual dimension: they will be the old dimensions in the next resizing }
17 PW := WW;
18 PH := HH;
19 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:
20 { ... }
21 for I := 0 to (Form1.ControlCount - 1) do
22 begin
23 ChangeDims(Form1.Controls[i], WW, PW, HH, PH);
24 end;
"ChangeDims" is the procedure which resizes (with recursion) the child controls.
25
26 procedure ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
27 var
28 L, T, W, H: integer;
29 begin
30 if Control is TWinControl then
31 (Control as TWinControl).DisableAlign;
32 try
33 if Control is TWinControl then
34 ScaleCtrls(Control as TWinControl, WW, PW, HH, PH);
35 L := MulDiv(Control.Left, WW, PW);
36 T := MulDiv(Control.Top, HH, PH);
37 W := MulDiv(Control.Left + Control.Width, WW, PW) - L;
38 H := MulDiv(Control.Top + Control.Height, HH, PH) - T;
39 Control.SetBounds(L, T, W, H);
40 finally
41 if Control is TWinControl then
42 (Control as TWinControl).DisableAlign;
43 end;
44 end;
45
46 procedure ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
47 var
48 I: Integer;
49 begin
50 for I := 0 to Control.ControlCount - 1 do
51 ChangeDims(Control.Controls[i], WW, PW, HH, PH);
52 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:
53 unit Resize;
54
55 interface
56
57 uses
58 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
59
60 type
61 { THackControl = class(TControl); }
62
63 TResize = class(TComponent)
64 private
65 { Private declarations }
66 FForm1: TForm;
67 PW, PH: integer;
68 FOnResizeEvent: TNotifyEvent;
69 procedure ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
70 procedure ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
71 procedure OnResizeEvent(Sender: TObject);
72 procedure Resize();
73 protected
74 { Protected declarations }
75 public
76 { Public declarations }
77 constructor Create(AOwner: TComponent); override;
78 destructor Destroy(); override;
79 published
80 { Published declarations }
81 end;
82
83 procedure register;
84
85 implementation
86
87 constructor TResize.Create(AOwner: TComponent);
88 begin
89 inherited Create(AOwner);
90 if csDesigning in ComponentState then
91 Exit;
92 FForm1 := (Owner as TForm);
93 PW := FForm1.ClientWidth;
94 PH := FForm1.ClientHeight;
95 if Assigned(FForm1.OnResize) then
96 begin
97 FOnResizeEvent := FForm1.OnResize;
98 end;
99 FForm1.OnResize := OnResizeEvent;
100 end;
101
102 destructor TResize.Destroy();
103 begin
104 if not (csDesigning in ComponentState) then
105 FForm1.OnResize := FOnResizeEvent;
106 inherited Destroy();
107 end;
108
109 procedure TResize.ScaleCtrls(Control: TWinControl; WW, PW, HH, PH: Integer);
110 var
111 I: Integer;
112 begin
113 for I := 0 to Control.ControlCount - 1 do
114 ChangeDims(Control.Controls[i], WW, PW, HH, PH);
115 end;
116
117 procedure TResize.ChangeDims(Control: TControl; WW, PW, HH, PH: Integer);
118 var
119 L, T, W, H: integer;
120 begin
121 if Control is TWinControl then
122 (Control as TWinControl).DisableAlign;
123 try
124 if Control is TWinControl then
125 ScaleCtrls(Control as TWinControl, WW, PW, HH, PH);
126 L := MulDiv(Control.Left, WW, PW);
127 T := MulDiv(Control.Top, HH, PH);
128 W := MulDiv(Control.Left + Control.Width, WW, PW) - L;
129 H := MulDiv(Control.Top + Control.Height, HH, PH) - T;
130 { THackControl(Control).Font.Size := (PW div WW) *
131 THackControl(Control).Font.Size;}
132 Control.SetBounds(L, T, W, H);
133 finally
134 if Control is TWinControl then
135 (Control as TWinControl).DisableAlign;
136 end;
137 end;
138
139 procedure TResize.OnResizeEvent(Sender: TObject);
140 begin
141 if Assigned(FOnResizeEvent) then
142 begin
143 FOnResizeEvent(Sender);
144 end;
145 Resize();
146 end;
147
148 procedure TResize.Resize();
149 var
150 L, T, R, B, W, H, WW, HH, I: Integer;
151 begin
152 WW := FForm1.ClientWidth;
153 HH := FForm1.ClientHeight;
154 for I := 0 to (FForm1.ControlCount - 1) do
155 begin
156 ChangeDims(FForm1.Controls[i], WW, PW, HH, PH);
157 end;
158 PW := WW;
159 PH := HH;
160 end;
161
162 procedure register;
163 begin
164 RegisterComponents('Carlo Pasolini', [TResize]);
165 end;
166
167 end.
|