Sign in Join
Wiki copytemporary

Remember form position and size: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Remember form position and size

OldNewLine
1+# Using TForm.SessionProperties and TXMLPropStorage
2+
3+In some applications, it is nice if the user modified settings for the position and size of the form are saved at program end and reused the next time the program starts. Lazarus has a fairly easy way to do this using [[TXMLPropStorage]] or [[TINIPropStorage]] available.
4+
5+A simple example will illustrate these functions:
6+
7+- create new application (main menu Project -\> New Project -\> Application)
8+- pass an TXMLPropStorage component on the form (find it on the Misc tab of the component palette)
9+
10+<figure>
11+<img src="KomponentenpaletteTXMLPropStorage.png" title="KomponentenpaletteTXMLPropStorage.png" />
12+<figcaption>KomponentenpaletteTXMLPropStorage.png</figcaption>
13+</figure>
14+
15+- in the Object Inspector: XMLPropStorage1 -\> FileName eg set "session.xml"
16+- in the Object Inspector: Form1 -\> Edit sessionProperties (the \[...\] button simply click)
17+- now select each of the Form1 properties Left, Top, Width, Height and add it
18+- confirm with OK and restart project
19+- change and move the form size
20+- end the program and restart it and it will return to the size and position it ended with last time.
21+
22+Further information under [[TXMLPropStorage]]
23+
24+# General method
25+
26+## Simple example
27+
28+A basic approach to keep form position and size between application restart is to store these state values to some persistent storage like XML file, INI file or Windows registry. You need at least store TForm properties Left, Top, Width and Height. But if form is maximized state you need also store values of TForm RestoredLeft, RestoredTop, RestoredWidth and RestoredHeight so form will be correctly sized if user click on maximize botton again to switch back to normal state.
29+
30+For simplification TXMLConfig component is used as persistent storage. Place this component on your form and set its filename property to Config.xml.
31+
32+``` pascal
33+procedure TForm1.StoreFormState;
34+begin
35+ with XMLConfig1 do begin
36+ SetValue('NormalLeft', Left);
37+ SetValue('NormalTop', Top);
38+ SetValue('NormalWidth', Width);
39+ SetValue('NormalHeight', Height);
40+
41+ SetValue('RestoredLeft', RestoredLeft);
42+ SetValue('RestoredTop', RestoredTop);
43+ SetValue('RestoredWidth', RestoredWidth);
44+ SetValue('RestoredHeight', RestoredHeight);
45+
46+ SetValue('WindowState', Integer(WindowState));
47+ end;
48+end;
49+```
50+
51+Restoration of stored form properties is a little bit complicated as properties have to be set in right order to prevent form resize animation or values to be overwritten by internal form mechanism. There is a rule that if normal and restored bounds are equal then form was in normal state. If normal and restored bounds are different then form is probably in maximized state and restored bounds are set to original position and size before maximization. As we have stored WindowState then we can use it to decide if form is in maximized state. To restore maximized state we have to first set form to normal state then set TForm.BoundsRect and after that set wsMaximized state. This is because Restored properties are read only and we can set them directly.
52+
53+``` pascal
54+procedure TForm1.RestoreFormState;
55+var
56+ LastWindowState: TWindowState;
57+begin
58+ with XMLConfig1 do begin
59+ LastWindowState := TWindowState(GetValue('WindowState', Integer(WindowState)));
60+
61+ if LastWindowState = wsMaximized then begin
62+ WindowState := wsNormal;
63+ BoundsRect := Bounds(
64+ GetValue('RestoredLeft', RestoredLeft),
65+ GetValue('RestoredTop', RestoredTop),
66+ GetValue('RestoredWidth', RestoredWidth),
67+ GetValue('RestoredHeight', RestoredHeight));
68+ WindowState := wsMaximized;
69+ end else begin
70+ WindowState := wsNormal;
71+ BoundsRect := Bounds(
72+ GetValue('NormalLeft', Left),
73+ GetValue('NormalTop', Top),
74+ GetValue('NormalWidth', Width),
75+ GetValue('NormalHeight', Height));
76+ end;
77+ end;
78+end;
79+```
80+
81+Functions should be executed from OnShow and OnClose handlers.
82+
83+``` pascal
84+procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
85+begin
86+ StoreFormState;
87+end;
88+
89+procedure TForm1.FormShow(Sender: TObject);
90+begin
91+ RestoreFormState;
92+end;
93+```
94+
95+## Support for more forms
96+
97+To support storing state for multiple forms we need to extend our functions to support Form parameter and allow store their values to different XMLConfig paths.
98+
99+``` pascal
100+procedure TForm1.StoreFormState(Form: TForm);
101+begin
102+ with XMLConfig1 do begin
103+ SetValue('Forms/' + Form.Name + '/NormalLeft', Form.Left);
104+ SetValue('Forms/' + Form.Name + '/NormalTop', Form.Top);
105+ SetValue('Forms/' + Form.Name + '/NormalWidth', Form.Width);
106+ SetValue('Forms/' + Form.Name + '/NormalHeight', Form.Height);
107+
108+ SetValue('Forms/' + Form.Name + '/RestoredLeft', Form.RestoredLeft);
109+ SetValue('Forms/' + Form.Name + '/RestoredTop', Form.RestoredTop);
110+ SetValue('Forms/' + Form.Name + '/RestoredWidth', Form.RestoredWidth);
111+ SetValue('Forms/' + Form.Name + '/RestoredHeight', Form.RestoredHeight);
112+
113+ SetValue('Forms/' + Form.Name + '/WindowState', Integer(Form.WindowState));
114+ end;
115+end;
116+
117+procedure TForm1.RestoreFormState(Form: TForm);
118+var
119+ LastWindowState: TWindowState;
120+begin
121+ with XMLConfig1 do begin
122+ LastWindowState := TWindowState(GetValue('Forms/' + Form.Name + '/WindowState', Integer(Form.WindowState)));
123+
124+ if LastWindowState = wsMaximized then begin
125+ Form.WindowState := wsNormal;
126+ Form.BoundsRect := Bounds(
127+ GetValue('Forms/' + Form.Name + '/RestoredLeft', Form.RestoredLeft),
128+ GetValue('Forms/' + Form.Name + '/RestoredTop', Form.RestoredTop),
129+ GetValue('Forms/' + Form.Name + '/RestoredWidth', Form.RestoredWidth),
130+ GetValue('Forms/' + Form.Name + '/RestoredHeight', Form.RestoredHeight));
131+ Form.WindowState := wsMaximized;
132+ end else begin
133+ Form.WindowState := wsNormal;
134+ Form.BoundsRect := Bounds(
135+ GetValue('Forms/' + Form.Name + '/NormalLeft', Form.Left),
136+ GetValue('Forms/' + Form.Name + '/NormalTop', Form.Top),
137+ GetValue('Forms/' + Form.Name + '/NormalWidth', Form.Width),
138+ GetValue('Forms/' + Form.Name + '/NormalHeight', Form.Height));
139+ end;
140+ end;
141+end;
142+
143+procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
144+begin
145+ StoreFormState(Self);
146+end;
147+
148+procedure TForm1.FormShow(Sender: TObject);
149+begin
150+ RestoreFormState(Self);
151+end;
152+```
153+
154+## Minimized and Fullscreen states
155+
156+TForm.WindowState have two additional states wsMinimized and wsFullscreen. These special states need special care if they should be supported.
157+
158+## Ensure that restored form is visible
159+
160+As size of screen can change in time we need to check restored form position and size to ensure that form is visible to user and so your can manipulate with them.
161+
162+``` pascal
163+function TForm1.CheckFormIsEntireVisible(Rect: TRect): TRect;
164+var
165+ Width: Integer;
166+ Height: Integer;
167+begin
168+ Result := Rect;
169+ Width := Rect.Right - Rect.Left;
170+ Height := Rect.Bottom - Rect.Top;
171+ if Result.Left < (Screen.DesktopLeft) then begin
172+ Result.Left := Screen.DesktopLeft;
173+ Result.Right := Screen.DesktopLeft + Width;
174+ end;
175+ if Result.Right > (Screen.DesktopLeft + Screen.DesktopWidth) then begin
176+ Result.Left := Screen.DesktopLeft + Screen.DesktopWidth - Width;
177+ Result.Right := Screen.DesktopLeft + Screen.DesktopWidth;
178+ end;
179+ if Result.Top < Screen.DesktopTop then begin
180+ Result.Top := Screen.DesktopTop;
181+ Result.Bottom := Screen.DesktopTop + Height;
182+ end;
183+ if Result.Bottom > (Screen.DesktopTop + Screen.DesktopHeight) then begin
184+ Result.Top := Screen.DesktopTop + Screen.DesktopHeight - Height;
185+ Result.Bottom := Screen.DesktopTop + Screen.DesktopHeight;
186+ end;
187+end;
188+```
189+
190+If requirement for entire form surface to be visible is too strong then we could check that at least part of form is visible. Here Part is size in pixels which should be at least visible for both horizontal and vertical position and size.
191+
192+``` pascal
193+function TForm1.CheckFormIsPartialVisible(Rect: TRect; Part: Integer): TRect;
194+var
195+ Width: Integer;
196+ Height: Integer;
197+begin
198+ Result := Rect;
199+ Width := Rect.Right - Rect.Left;
200+ Height := Rect.Bottom - Rect.Top;
201+ if Result.Right < (Screen.DesktopLeft + Part) then begin
202+ Result.Left := Screen.DesktopLeft + Part - Width;
203+ Result.Right := Screen.DesktopLeft + Part;
204+ end;
205+ if Result.Left > (Screen.DesktopLeft + Screen.DesktopWidth - Part) then begin
206+ Result.Left := Screen.DesktopLeft + Screen.DesktopWidth - Part;
207+ Result.Right := Screen.DesktopLeft + Screen.DesktopWidth - Part + Width;
208+ end;
209+ if Result.Bottom < (Screen.DesktopTop + Part) then begin
210+ Result.Top := Screen.DesktopTop + Part - Height;
211+ Result.Bottom := Screen.DesktopTop + Part;
212+ end;
213+ if Result.Top > (Screen.DesktopTop + Screen.DesktopHeight - Part) then begin
214+ Result.Top := Screen.DesktopTop + Screen.DesktopHeight - Part;
215+ Result.Bottom := Screen.DesktopTop + Screen.DesktopHeight - Part + Height;
216+ end;
217+end;
218+```
219+
220+# See also
221+
222+- [[Form Tutorial]]
223+
224+---
225+*From the Free Pascal wiki, page "Remember form position and size", as it stood on 2020-02-25 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
226+Category: Lazarus
227+Category: Tutorials

Back to history