Articles   Members Online:
-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 use a TPanel as a host for child windows (MDI simulation) 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
28-Oct-02
Category
VCL-General
Language
Delphi 2.x
Views
96
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Jonas Bilinkevicius

I was wondering if someone can offer assistance with this application. Basically 
the application is for configuring our system. At present it is a MDI where child 
windows are various functions (security, report options, etc.). The number of 
functions are growing, currently around 15, which means an increase in different 
child forms and, overall, a growing exe. I would like the child forms to be 
standalone programs or dlls which can appear in the control program as child 
windows and also execute by themselves. Only one child form is displayed at a time 
and always maximised within the parent window. I did see some code about that 
provided for a dll as a child form, but this would not help as a standalone 
execution.

Answer:

This is an interesting problem. As it happens it is possible in Win32 to make 
another processes window appear like a child window in ones own windows. It does 
not work quite as well as a true child in your own process but takes care about 
moving the pseudo-child with your menu app.

The general design is this: the main/menu app has a form with menu, perhaps tool 
and status bars, and a client-aligned panel that will serve as the host for the 
child windows. It reads the available child apps from INI file or registry key and 
builds a menu or selection list from this info. On user request it launches the 
appropriate child app and passes the panels window handle on the commandline. The 
child app checks the command line, if there are no parameters it rans as designed, 
if there is a parameter it reads it, removes its border and bordericon, parents 
itself to the passed window handle and sizes itself to its client area. It also 
sends a message with *its* window handle to the panels parent (the main app form) 
to register itself. The main app can close the child with this handle and also 
resize it when the user resizes the main app.

Main app: has a menu with two entries (OpenMenu, CloseMenu), a toolbar with two 
buttons attached to the same events as the two menus, a statusbar, a client-aliged 
panel.

1   unit MenuApp;
2   
3   interface
4   
5   uses
6     Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
7     Menus, ExtCtrls, ComCtrls, ToolWin;
8   
9   const
10    UM_CHILDREGISTER = WM_USER + 111;
11    UM_CHILDUNREGISTER = WM_USER + 112;
12  
13  type
14    TUmChildRegister = packed record
15      msg: Cardinal;
16      childwnd: HWND;
17      unused: Integer;
18      result: Integer;
19    end;
20    TUmChildUnregister = TUmChildregister;
21  
22    TForm1 = class(TForm)
23      MainMenu1: TMainMenu;
24      OpenMenu: TMenuItem;
25      StatusBar1: TStatusBar;
26      ToolBar1: TToolBar;
27      ToolButton1: TToolButton;
28      CloseMenu: TMenuItem;
29      ToolButton2: TToolButton;
30      Panel1: TPanel;
31      procedure OpenMenuClick(Sender: TObject);
32      procedure CloseMenuClick(Sender: TObject);
33      procedure Panel1Resize(Sender: TObject);
34      procedure FormClose(Sender: TObject; var Action: TCloseAction);
35    private
36      { Private declarations }
37      FChildAppHandle: HWND;
38      procedure UMChildRegister(var msg: TUmChildRegister);
39        message UM_CHILDREGISTER;
40      procedure UMChildUnRegister(var msg: TUmChildUnRegister);
41        message UM_CHILDUNREGISTER;
42    public
43      { Public declarations }
44    end;
45  
46  var
47    Form1: TForm1;
48  
49  implementation
50  
51  uses
52    shellapi;
53  
54  {$R *.DFM}
55  
56  procedure TForm1.OpenMenuClick(Sender: TObject);
57  var
58    path, param: string;
59  begin
60    if FChildAppHandle = 0 then
61    begin
62      path := ExtractFilePath(Application.Exename) + 'childAppProj.exe';
63      param := '$' + IntTohex(panel1.handle, 8);
64      ShellExecute(handle, 'open', pchar(path), pchar(param), nil, SW_SHOWNORMAL);
65    end
66    else
67      ShowMessage('Child already loaded');
68  end;
69  
70  procedure TForm1.CloseMenuClick(Sender: TObject);
71  begin
72    if FChildAppHandle <> 0 then
73      SendMessage(FchildApphandle, WM_CLOSE, 0, 0);
74  end;
75  
76  procedure TForm1.Panel1Resize(Sender: TObject);
77  begin
78    if FChildAppHandle <> 0 then
79      MoveWindow(FchildAppHandle, 0, 0, Panel1.ClientWidth, Panel1.ClientHeight, 
80  true);
81  end;
82  
83  procedure TForm1.UMChildRegister(var msg: TUmChildRegister);
84  begin
85    FChildAppHandle := msg.childwnd;
86  end;
87  
88  procedure TForm1.UMChildUnRegister(var msg: TUmChildUnRegister);
89  begin
90    if FChildAppHandle = msg.childwnd then
91      FChildAppHandle := 0;
92  end;
93  
94  procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
95  begin
96    if FChildAppHandle <> 0 then
97      SendMessage(FchildApphandle, WM_CLOSE, 0, 0);
98  end;
99  
100 end.


Child app has a couple of edits, two buttons, a memo.

101 unit ChildApp;
102 
103 interface
104 
105 uses
106   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
107   StdCtrls, AppEvnts;
108 
109 type
110   TForm2 = class(TForm)
111     Edit1: TEdit;
112     Edit2: TEdit;
113     Edit3: TEdit;
114     Button1: TButton;
115     Memo1: TMemo;
116     Button2: TButton;
117     ApplicationEvents1: TApplicationEvents;
118     procedure Button1Click(Sender: TObject);
119     procedure ApplicationEvents1Activate(Sender: TObject);
120     procedure FormClose(Sender: TObject; var Action: TCloseAction);
121     procedure FormResize(Sender: TObject);
122   private
123     { Private declarations }
124     FMenuAppWnd: HWND;
125     FParentPanelWnd: HWND;
126   public
127     { Public declarations }
128     constructor Create(aOwner: TComponent); override;
129     procedure CreateWnd; override;
130     procedure DestroyWnd; override;
131   end;
132 
133 var
134   Form2: TForm2;
135 
136 implementation
137 
138 {$R *.DFM}
139 
140 const
141   UM_CHILDREGISTER = WM_USER + 111;
142   UM_CHILDUNREGISTER = WM_USER + 112;
143 
144 procedure TForm2.Button1Click(Sender: TObject);
145 begin
146   close;
147 end;
148 
149 procedure TForm2.ApplicationEvents1Activate(Sender: TObject);
150 begin
151   if FMenuAppWnd <> 0 then
152     SendMessage(FMenuAppWnd, WM_NCACTIVATE, 1, 0);
153   memo1.lines.add('Activated');
154 end;
155 
156 constructor TForm2.Create(aOwner: TComponent);
157 begin
158   if ParamCount > 0 then
159   begin
160     FParentPanelWnd := StrToInt(ParamStr(1));
161     FMenuAppWnd := Windows.GetParent(FParentPanelWnd);
162   end;
163   inherited;
164   if FParentPanelWnd <> 0 then
165   begin
166     Borderstyle := bsNone;
167     BorderIcons := [];
168     {remove taskbar button for the child app}
169     SetWindowLong(Application.Handle, GWL_EXSTYLE,
170       GetWindowLong(Application.Handle, GWL_EXSTYLE)
171       and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
172   end;
173 end;
174 
175 procedure TForm2.CreateWnd;
176 var
177   r: Trect;
178 begin
179   inherited;
180   if FMenuAppWnd <> 0 then
181   begin
182     SendMessage(FMenuAppWnd, UM_CHILDREGISTER, handle, 0);
183     Windows.SetPArent(handle, FParentPanelWnd);
184     Windows.GetClientRect(FParentPanelWnd, r);
185     SetBounds(r.left, r.top, r.right - r.left, r.bottom - r.top);
186   end;
187 end;
188 
189 procedure TForm2.DestroyWnd;
190 begin
191   if FMenuAppWnd <> 0 then
192     SendMessage(FMenuAppWnd, UM_CHILDUNREGISTER, handle, 0);
193   inherited;
194 end;
195 
196 procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
197 begin
198   {Closing the main form does not fire DestroyWnd for some reason}
199   if FMenuAppWnd <> 0 then
200     SendMessage(FMenuAppWnd, UM_CHILDUNREGISTER, handle, 0);
201 end;
202 
203 procedure TForm2.FormResize(Sender: TObject);
204 begin
205   memo1.width := clientwidth - memo1.Left - 10;
206   memo1.height := clientheight - memo1.Top - 10;
207 end;
208 
209 end.


One problem I noted is that sometimes the main applications caption will loose the active look when switching between main and child despite the action taken in the childs Application.OnActivate handler.

			
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