Articles   Members Online: 3
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
How to create and Manage dynamic Forms at Runtime using Class References Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
06-Jul-03
Category
VCL-Forms
Language
Delphi 3.x
Views
226
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Marc Hoffmann

How to dynamicaly create and manage different Forms at runtime in a global manner?

Answer:

If you need to create dynamic forms at runtime and you want to manage them in a 
global manner, you may have the problem that you don't know how to administrate 
different form classes. For this case, Delphi comes with special class types of all 
common objects. But before I go into details, let me create a scenario in which 
this article may helps you.

  "I'll create an application for my customer to let him administrate serveral 
kinds of data in a local database. Each data category (such as employees, articles, 
...) have to been implemented in a specified form with individual edit fields and 
tools. I don't like to create a MDI bases application (for some reasons) but the 
customers should have the possibilty to create more than one form for each category 
(e.g. opens up to 10 forms with customer informations and 3 forms with article 
informations). He should refer to each form after a while, so all forms have to 
been non-modular > the customer can hide or minimize each form. In normal MDI 
application, Delphi helps you to manage the MDI childs form via the 
'ActiveMDIChild' property for example, but in non MDI applications you had to 
manage all child forms by yourself."

To find a workable solution we had to abstract the layer in which we could manage 
several kinds of forms. Each Delphi form inherites from TCustomForm so our first 
solution is to create a method who we pass a form reference to memorize - but how 
to keep such references? By the way, it's also possible to create a form manually 
and then pass the handle direct to the management component, but we'll create a 
method which automatically creates each kind of form. At the end of this article 
we've created a VCL component called TWindowManager which makes all of the 
discussed stuff, but now - let's start:
1   
2   function TWindowManager.CreateForm(const Form: TFormClass;
3     Name: string; Show: Boolean = False): TCustomForm;
4   begin
5     if not Form.InheritsFrom(TCustomForm) then
6       raise Exception.Create('Invalid FormClass - must be a descendant
7         of TCustomForm!'
8         Result := TCustomForm(Form.Create(Application));
9         if Name <> '' then
10          Result.Name := Name;
11        // insert code here, to store the reference
12        if Show then
13          Result.Show;
14  end;


Okay, but how to use it? First, we've created a normal Delphi application and added 
a new form called DynForm1 for example. Delphi automatically creates the following 
entry in the pas unit:

15  type
16    TDynForm1 = class(TForm)
17      ...
18    end;


For the next step we had to refer to the new unit by included the corresponding 
unit name to the uses clause. To dynamically create the new form at runtime, you 
can call the method in a way like:
19  
20  procedure TMainForm.ButtonDyn1Click(Sender: TObject);
21  begin
22    // create a new (dynamic) form.
23    WindowManager.CreateForm(TDynForm1, True);
24  end;


Don't marvel about the name WindowManager or TWindowManager in the source examples, 
I've pasted it direct from the component source I've explained earlier. 

Do you notice that we have passed the formclass to the method instead of the name 
or anythink else? It's possible, because the parameter type of the method is 
TFormClass which is implemented as TFormClass = class of TForm in Delphi's Forms 
unit.

Now we need a solution to store the form reference: 

25  type
26    { TWindowItem }
27  
28    PWindowItem = ^TWindowItem;
29    TWindowItem = packed record
30      Form: Pointer;
31    end;


Note:

It's also possible to use a TStringList for example and create items which holds 
the form handles (or references direct) but it's not a good solutions if you want 
to search for already existing form (names). Since Version 3 (I'm not sure exactly) 
Delphi comes with a special container class which gives you some more specific 
descendants from the TList class. You can use the TObjectList class, derive from it 
and overwritte the maintenance methods. In this article I use a normal record to 
store all informations - it's less code to write and you can easily add improved 
custom informations to store.

The sourcecode of the TWindowManager comes from a Delphi3 implementation I've wrote 
- if I've some spare time, I'll update it to the newer technology!

Our WindowManager also published a method to directly add already existing form 
references, so you don't need to create your forms using the CreateForm method: 
32  
33  function TWindowManager.Add(const Form: TCustomForm): Boolean;
34  var
35    WindowItem: PWindowItem;
36  begin
37    Result := True;
38    try
39      New(WindowItem);
40      WindowItem^.Form := Form;
41      FWindowList.Add(WindowItem);
42    except // wrap up
43      Result := True;
44    end; // try/except
45  end;


FWindowList is declared as FWindowList: TList to hold a list of reference records. 
Followed you'll see to complete sourcode of the TWindowManager - try to understand 
the individual methods - they are simple. The main trick is the use off class 
references I've mentioned earlier.

The main component

46  unit WindowMng;
47  
48  interface
49  
50  uses
51    Classes, Forms, SysUtils, Windows;
52  
53  type
54    { TWinNotifyEvent }
55  
56    TWinNotifyEvent = procedure(Sender: TObject; Form: TCustomForm) of object;
57  
58    { TWindowItem }
59  
60      // I used a packed record to be more flexible for futher improvements
61      // which may need to store additional informations.
62  
63    PWindowItem = ^TWindowItem;
64    TWindowItem = packed record
65      Form: Pointer;
66    end;
67  
68    { TWindowManager }
69  
70    TWindowManager = class(TComponent)
71    private
72      { Private declarations }
73      FAutoNotification: Boolean;
74      FLastIndex: Integer;
75      FWindowList: TList;
76      FOnFormAdded: TWinNotifyEvent;
77      FOnFormHandled: TNotifyEvent;
78      FOnFormRemoved: TWinNotifyEvent;
79    protected
80      { Protected declarations }
81      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
82      function GetFormByIndex(Index: Integer): TCustomForm; virtual;
83      function GetWindowItemByIndex(Index: Integer): PWindowItem; virtual;
84      function GetWindowItemByForm(const Form: TCustomForm): PWindowItem; virtual;
85    public
86      { Public declarations }
87      constructor Create(AOwner: TComponent); override;
88      destructor Destroy; override;
89      function Add(const Form: TCustomForm): Boolean; overload;
90      function Count: Integer;
91      function CreateForm(const Form: TFormClass; Name: string; Show: Boolean = 
92  False):
93        TCustomForm; overload;
94      function CreateForm(const Form: TFormClass; Show: Boolean = False): TCustomForm;
95        overload;
96      function Exists(const Form: TCustomForm): Boolean;
97      function Remove(const Form: TCustomForm): Boolean;
98      function Restore(const Index: Integer): Boolean; overload;
99      function Restore(const Form: TCustomForm): Boolean; overload;
100     property Forms[Index: Integer]: TCustomForm read GetFormByIndex; default;
101   published
102     { Published declarations }
103     property AutoNotification: Boolean read FAutoNotification write 
104 FAutoNotification;
105     property OnFormAdded: TWinNotifyEvent read FOnFormAdded write FOnFormAdded;
106     property OnFormHandled: TNotifyEvent read FOnFormHandled write FOnFormHandled;
107     property OnFormRemoved: TWinNotifyEvent read FOnFormRemoved write 
108 FOnFormRemoved;
109   end;
110 
111 procedure register;
112 
113 implementation
114 
115 // -----------------------------------------------------------------------------
116 
117 procedure register;
118 begin
119   RegisterComponents('Freeware', [TWindowManager]);
120 end;
121 
122 // -----------------------------------------------------------------------------
123 
124 { TWindowManager }
125 
126 constructor TWindowManager.Create(AOwner: TComponent);
127 begin
128   inherited Create(AOwner);
129   FAutoNotification := False;
130   FLastIndex := -1;
131   FWindowList := TList.Create;
132 end;
133 
134 destructor TWindowManager.Destroy;
135 begin
136   FWindowList.Free;
137   inherited Destroy;
138 end;
139 
140 procedure TWindowManager.Notification(AComponent: TComponent;
141   Operation: TOperation);
142 begin
143   if (FAutoNotification) and (AComponent <> nil) and (Operation = opRemove)
144     and (AComponent is TCustomForm) and (Exists(TCustomForm(AComponent))) then
145     Remove(TCustomForm(AComponent));
146   inherited Notification(AComponent, Operation);
147 end;
148 
149 function TWindowManager.Add(const Form: TCustomForm): Boolean;
150 var
151   WindowItem: PWindowItem;
152 begin
153   Result := False;
154   if not Exists(Form) then
155   try
156     New(WindowItem);
157     WindowItem^.Form := Form;
158     FWindowList.Add(WindowItem);
159     if FAutoNotification then
160       Form.FreeNotification(Self);
161     Result := True;
162     if assigned(FOnFormAdded) then
163       FOnFormAdded(Self, Form);
164     if assigned(FOnFormHandled) then
165       FOnFormHandled(Self);
166   except // wrap up
167   end; // try/except
168 end;
169 
170 function TWindowManager.Count: Integer;
171 begin
172   Result := FWindowList.Count;
173 end;
174 
175 function TWindowManager.CreateForm(const Form: TFormClass; Name: string; Show: 
176 Boolean
177   = False): TCustomForm;
178 begin
179   if not Form.InheritsFrom(TCustomForm) then
180     raise
181       Exception.Create('Invalid FormClass - must be a descendant of TCustomForm!');
182   Result := TCustomForm(Form.Create(Application));
183   if Name <> '' then
184     Result.Name := Name;
185   Add(Result);
186   if Show then
187     Result.Show;
188 end;
189 
190 function TWindowManager.CreateForm(const Form: TFormClass; Show: Boolean = False):
191   TCustomForm;
192 begin
193   Result := CreateForm(Form, '', Show);
194 end;
195 
196 function TWindowManager.Exists(const Form: TCustomForm): Boolean;
197 begin
198   Result := GetWindowItemByForm(Form) <> nil;
199 end;
200 
201 function TWindowManager.GetFormByIndex(Index: Integer): TCustomForm;
202 var
203   WindowItem: PWindowItem;
204 begin
205   Result := nil;
206   WindowItem := GetWindowItemByIndex(Index);
207   if WindowItem <> nil then
208     Result := TCustomForm(WindowItem^.Form);
209 end;
210 
211 function TWindowManager.GetWindowItemByIndex(Index: Integer): PWindowItem;
212 begin
213   Result := nil;
214   if Index < Count then
215     Result := PWindowItem(FWindowList[Index]);
216 end;
217 
218 function TWindowManager.GetWindowItemByForm(const Form: TCustomForm): PWindowItem;
219 var
220   iIndex: Integer;
221 begin
222   Result := nil;
223   FLastIndex := -1;
224   for iIndex := 0 to FWindowList.Count - 1 do
225     if GetWindowItemByIndex(iIndex)^.Form = Form then
226     begin
227       FLastIndex := iIndex;
228       Result := GetWindowItemByIndex(FLastIndex);
229       Break;
230     end;
231 end;
232 
233 function TWindowManager.Remove(const Form: TCustomForm): Boolean;
234 var
235   WindowItem: PWindowItem;
236 begin
237   Result := False;
238   WindowItem := GetWindowItemByForm(Form);
239   if WindowItem <> nil then
240   try
241     FWindowList.Delete(FLastIndex);
242     Dispose(WindowItem);
243     Result := True;
244     if assigned(FOnFormRemoved) then
245       FOnFormRemoved(Self, Form);
246     if assigned(FOnFormHandled) then
247       FOnFormHandled(Self);
248   except // wrap up
249   end; // try/except
250 end;
251 
252 function TWindowManager.Restore(const Form: TCustomForm): Boolean;
253 begin
254   Result := False;
255   if (Form <> nil) and (Exists(Form)) then
256   try
257     if IsIconic(Form.Handle) then
258       Form.WindowState := wsNormal;
259     Form.SetFocus;
260     Result := True;
261   except // wrap up
262   end; // try/except
263 end;
264 
265 function TWindowManager.Restore(const Index: Integer): Boolean;
266 begin
267   Result := Restore(GetFormByIndex(Index));
268 end;
269 
270 end.


To show you the in more detail how to work with this component, followed you'll 
find a demo application with two additional forms. You don't need to install the 
component to a package, I'll create it at runtime:

The project file

271 program WMDemo;
272 
273 uses
274   Forms,
275   MainFrm in 'MainFrm.pas' {MainForm},
276   WindowMng in 'WindowMng.pas',
277   DynFrm1 in 'DynFrm1.pas' {DynForm1},
278   DynFrm2 in 'DynFrm2.pas' {DynForm2};
279 
280 {$R *.res}
281 
282 begin
283   Application.Initialize;
284   Application.CreateForm(TMainForm, MainForm);
285   Application.Run;
286 end.
287 
288 //The MainForm file
289 
290 unit MainFrm;
291 
292 interface
293 
294 uses
295   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
296   Dialogs, StdCtrls, WindowMng;
297 
298 type
299   TMainForm = class(TForm)
300     ButtonDyn1: TButton;
301     GroupBoxForms: TGroupBox;
302     ListBoxForms: TListBox;
303     ButtonHelloWorld: TButton;
304     ButtonDyn2: TButton;
305     procedure FormCreate(Sender: TObject);
306     procedure FormDestroy(Sender: TObject);
307     procedure ButtonDyn1Click(Sender: TObject);
308     procedure ListBoxFormsDblClick(Sender: TObject);
309     procedure ButtonHelloWorldClick(Sender: TObject);
310     procedure ButtonDyn2Click(Sender: TObject);
311   private
312     { Private declarations }
313     WindowManager: TWindowManager;
314     procedure RedrawFormList(Sender: TObject);
315   public
316     { Public declarations }
317   end;
318 
319 var
320   MainForm: TMainForm;
321 
322 implementation
323 
324 uses
325   DynFrm1, DynFrm2;
326 
327 {$R *.dfm}
328 
329 procedure TMainForm.FormCreate(Sender: TObject);
330 begin
331   // create WindowManager
332   WindowManager := TWindowManager.Create(Self);
333 
334   // enable 'AutoNotification'. If this feature is turned on,
335   // WindowManager will receive a notification if a form was closed
336   // by the user, so it can fire events to recorgnize this.
337   // We use the 'OnFormHandled' event to redraw out ListBox.
338   WindowManager.AutoNotification := True;
339 
340   // link event handler to update out ListBox.
341   WindowManager.OnFormHandled := RedrawFormList;
342 end;
343 
344 procedure TMainForm.FormDestroy(Sender: TObject);
345 begin
346   // destroy WindowManager
347   WindowManager.Free;
348 end;
349 
350 procedure TMainForm.RedrawFormList(Sender: TObject);
351 var
352   i: Integer;
353 begin
354   // get all available forms and display them.
355   // we also stores the object reference to enable the 'restore' function
356   // if the user double-clicked on an item.
357   ListBoxForms.Clear;
358   for i := 0 to WindowManager.Count - 1 do
359     ListBoxForms.AddItem(WindowManager.Forms[i].Name, WindowManager.Forms[i]);
360 end;
361 
362 procedure TMainForm.ButtonDyn1Click(Sender: TObject);
363 begin
364   // create a new (dynamic) form.
365   WindowManager.CreateForm(TDynForm1, True);
366 end;
367 
368 procedure TMainForm.ButtonDyn2Click(Sender: TObject);
369 begin
370   // create a new (dynamic) form.
371   WindowManager.CreateForm(TDynForm2, True);
372 end;
373 
374 procedure TMainForm.ListBoxFormsDblClick(Sender: TObject);
375 var
376   ClickForm: TCustomForm;
377 begin
378   // extract the 'clicked' form.
379   with ListBoxForms do
380     ClickForm := TCustomForm(Items.Objects[ItemIndex]);
381 
382   // restore the form to the top order.
383   // we used the WindowManager method 'Restore' to be sure
384   // that the form will be restored also if it was iconized
385   // before.
386   WindowManager.Restore(ClickForm);
387 end;
388 
389 procedure TMainForm.ButtonHelloWorldClick(Sender: TObject);
390 begin
391   // check, if any registered forms exists.
392   if WindowManager.Count = 0 then
393   begin
394     ShowMessage('No dynamic Forms exists - please create one!');
395     Exit;
396   end;
397 
398   // check, if the first available form is 'DynForm1'.
399   // if true, call the HelloWorld method.
400   if WindowManager.Forms[0] is TDynForm1 then
401     TDynForm1(WindowManager.Forms[0]).HelloWorld
402   else
403     ShowMessage('The first Form is not a "Dynamic Form I"!');
404 end;
405 
406 end.


The MainForm resource file

object MainForm: TMainForm
  Left = 290
    Top = 255
    BorderStyle = bsSingle
    Caption = 'MainForm'
    ClientHeight = 229
    ClientWidth = 510
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    Position = poScreenCenter
    OnCreate = FormCreate
    OnDestroy = FormDestroy
    DesignSize = (
    510
    229)
    PixelsPerInch = 96
    TextHeight = 13
    object ButtonDyn1: TButton
    Left = 16
      Top = 16
      Width = 121
      Height = 25
      Caption = 'Create Dynamic Form I'
      TabOrder = 0
      OnClick = ButtonDyn1Click
  end
  object GroupBoxForms: TGroupBox
    Left = 16
      Top = 56
      Width = 481
      Height = 169
      Anchors = [akLeft, akTop, akRight, akBottom]
      Caption = 'Available Forms (Double-Click to restore)'
      TabOrder = 1
      object ListBoxForms: TListBox
      Left = 2
        Top = 15
        Width = 477
        Height = 152
        Align = alClient
        BorderStyle = bsNone
        ItemHeight = 13
        ParentColor = True
        TabOrder = 0
        OnDblClick = ListBoxFormsDblClick
    end
  end
  object ButtonHelloWorld: TButton
    Left = 344
      Top = 16
      Width = 153
      Height = 25
      Caption = 'Fire ''HelloWorld'' on DynForm1'
      TabOrder = 2
      OnClick = ButtonHelloWorldClick
  end
  object ButtonDyn2: TButton
    Left = 144
      Top = 16
      Width = 121
      Height = 25
      Caption = 'Create Dynamic Form II'
      TabOrder = 3
      OnClick = ButtonDyn2Click
  end
end

407 //The DynForm1 file
408 
409 unit DynFrm1;
410 
411 interface
412 
413 uses
414   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
415   Dialogs;
416 
417 type
418   TDynForm1 = class(TForm)
419     procedure FormClose(Sender: TObject; var Action: TCloseAction);
420   private
421     { Private declarations }
422   public
423     { Public declarations }
424     procedure HelloWorld;
425   end;
426 
427 var
428   DynForm1: TDynForm1;
429 
430 implementation
431 
432 {$R *.dfm}
433 
434 procedure TDynForm1.FormClose(Sender: TObject; var Action: TCloseAction);
435 begin
436   // be sure that our form will be freed.
437   Action := caFree;
438 end;
439 
440 procedure TDynForm1.HelloWorld;
441 begin
442   ShowMessage('HelloWorld method was fired!');
443 end;
444 
445 end.
446 
447 The DynForm2 file
448 
449 unit DynFrm2;
450 
451 interface
452 
453 uses
454   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
455   Dialogs;
456 
457 type
458   TDynForm2 = class(TForm)
459     procedure FormClose(Sender: TObject; var Action: TCloseAction);
460   private
461     { Private declarations }
462   public
463     { Public declarations }
464   end;
465 
466 var
467   DynForm2: TDynForm2;
468 
469 implementation
470 
471 {$R *.dfm}
472 
473 procedure TDynForm2.FormClose(Sender: TObject; var Action: TCloseAction);
474 begin
475   // be sure that our form will be freed.
476   Action := caFree;
477 end;
478 
479 end.


Hope this article helps you to understand how dynamic forms can be created and managed.

			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC