Stuart_Johnson
asked on
Detect Minimized MDI Children
Hi,
Is it possible to detect when an MDI Child has been minimized WITHOUT adding code to each form? What I want to do is, as each form is created, add an event handler too it. When the form is minimzed, my main form handles the minimize and the rest of my code kicks in.
I don't want to have to add any code to any of the existing 100+ forms.
Any ideas?
Thanks in advance,
Stu
Is it possible to detect when an MDI Child has been minimized WITHOUT adding code to each form? What I want to do is, as each form is created, add an event handler too it. When the form is minimzed, my main form handles the minimize and the rest of my code kicks in.
I don't want to have to add any code to any of the existing 100+ forms.
Any ideas?
Thanks in advance,
Stu
ASKER
Hi David,
Doesn't that mean that I have to modify each form to get this to work? I still don't see how I can get an OnMinimize event (or similar) out of your code. Perhaps I am just missing something?? Can you ellaborate at all?
Cheers!
Stu
Doesn't that mean that I have to modify each form to get this to work? I still don't see how I can get an OnMinimize event (or similar) out of your code. Perhaps I am just missing something?? Can you ellaborate at all?
Cheers!
Stu
? how will you detect the minimize-event, david
listening . . .
listening . . .
hi stu,
here is a solution from freshman3k provided at q
https://www.experts-exchange.com/delphi/Q.20290095.html
just expand it to your needed events
--partial paste
private
{ Private declarations }
FClientInstance : TFarProc;
FPrevClientProc : TFarProc;
procedure ClientWndProc(var aMessage: TMessage);
implementation
procedure TfrmMForm.FormShow(Sender: TObject);
begin
FPrevClientProc := Pointer(GetWindowLong(Clie ntHandle, GWL_WNDPROC));
FClientInstance := MakeObjectInstance(ClientW ndProc);
SetWindowLong(ClientHandle , GWL_WNDPROC, LongInt(FClientInstance));
end;
procedure TfrmMForm.ClientWndProc(va r aMessage: TMessage);
procedure DoDefault;
begin
with aMessage do
Result := CallWindowProc(FPrevClient Proc, ClientHandle, Msg, wParam,
lParam);
end;
begin
with aMessage do
case Msg of
WM_MDICREATE:
begin
{this event is when an application wants to create a child window}
{put your procedure here}
DoDefault;
end;
WM_MDIDESTROY:
begin
{this event is when when an MDI Child is closed.So this event is what you need
{put your procedure here}
DoDefault;
end;
WM_MDIACTIVATE:
begin
DoDefault;
{this event is when a new MDI child window is activated
{put your procedure here}
end;
WM_MDINEXT:
begin
DoDefault;
{this event is when a next or previous child window is activated }
{put your procedure here}
end;
else
DoDefault;
end;
end;
--- end paste
code must be placed into the mainform
meikl ;-)
here is a solution from freshman3k provided at q
https://www.experts-exchange.com/delphi/Q.20290095.html
just expand it to your needed events
--partial paste
private
{ Private declarations }
FClientInstance : TFarProc;
FPrevClientProc : TFarProc;
procedure ClientWndProc(var aMessage: TMessage);
implementation
procedure TfrmMForm.FormShow(Sender:
begin
FPrevClientProc := Pointer(GetWindowLong(Clie
FClientInstance := MakeObjectInstance(ClientW
SetWindowLong(ClientHandle
end;
procedure TfrmMForm.ClientWndProc(va
procedure DoDefault;
begin
with aMessage do
Result := CallWindowProc(FPrevClient
lParam);
end;
begin
with aMessage do
case Msg of
WM_MDICREATE:
begin
{this event is when an application wants to create a child window}
{put your procedure here}
DoDefault;
end;
WM_MDIDESTROY:
begin
{this event is when when an MDI Child is closed.So this event is what you need
{put your procedure here}
DoDefault;
end;
WM_MDIACTIVATE:
begin
DoDefault;
{this event is when a new MDI child window is activated
{put your procedure here}
end;
WM_MDINEXT:
begin
DoDefault;
{this event is when a next or previous child window is activated }
{put your procedure here}
end;
else
DoDefault;
end;
end;
--- end paste
code must be placed into the mainform
meikl ;-)
To detect the minimize event, you could define a message handler for the wm_syscommand:
type
TMyForm = class(TForm)
(...)
protected
procedure wmSysCommand(var msg: TMessage); message WM_SYSCOMMAND;
(...)
end;
(...)
procedure TMyForm.wmSysCommand(var msg: TMessage);
begin
if msg.wParam = SC_MINIMIZE then
begin
showmessage('Hallo!');
end
else inherited
end;
Something like that.
You don't need to modify each form (apart from the type declaration change), you place the code ONCE in the parent class).
HTH
David
type
TMyForm = class(TForm)
(...)
protected
procedure wmSysCommand(var msg: TMessage); message WM_SYSCOMMAND;
(...)
end;
(...)
procedure TMyForm.wmSysCommand(var msg: TMessage);
begin
if msg.wParam = SC_MINIMIZE then
begin
showmessage('Hallo!');
end
else inherited
end;
Something like that.
You don't need to modify each form (apart from the type declaration change), you place the code ONCE in the parent class).
HTH
David
I agree that it's best/easiest to use inheritance although you'll have to change the declarations of all your MDI child windows.
Here's what I quickly wrote for you (based on the default template created by the MDI app wizard):
unit CHILDWIN;
interface
uses Windows, Messages, Classes, Graphics, Forms, Controls, Dialogs, StdCtrls;
type
TSysCmdEvent = procedure(Sender: TObject; var Allow: Boolean) of object;
TMDIChild = class(TForm)
Memo1: TMemo;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FOnMaximize: TSysCmdEvent;
FOnMinimize: TSysCmdEvent;
FOnRestore: TSysCmdEvent;
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
protected
function DoMaximize: Boolean; dynamic;
function DoMinimize: Boolean; dynamic;
function DoRestore: Boolean; dynamic;
public
property OnMaximize: TSysCmdEvent read FOnMaximize write FOnMaximize;
property OnMinimize: TSysCmdEvent read FOnMinimize write FOnMinimize;
property OnRestore: TSysCmdEvent read FOnRestore write FOnRestore;
end;
implementation
{$R *.dfm}
procedure TMDIChild.WMSysCommand(var Message: TWMSysCommand);
begin
case Message.CmdType and $FFF0 of
SC_MINIMIZE:
if not DoMinimize then
Exit;
SC_MAXIMIZE:
if not DoMaximize then
Exit;
SC_RESTORE:
if not DoRestore then
Exit;
end;
inherited;
end;
function TMDIChild.DoMaximize: Boolean;
begin
Result := True;
if Assigned(FOnMaximize) then
FOnMaximize(Self, Result);
end;
function TMDIChild.DoMinimize: Boolean;
begin
Result := True;
if Assigned(FOnMinimize) then
FOnMinimize(Self, Result);
end;
function TMDIChild.DoRestore: Boolean;
begin
Result := True;
if Assigned(FOnRestore) then
FOnRestore(Self, Result);
end;
procedure TMDIChild.FormClose(Sender : TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
end.
unit MAIN;
interface
uses Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,
StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, ComCtrls, StdActns,
ActnList, ToolWin, ImgList, CHILDWIN;
type
TMainForm = class(TForm)
...
private
procedure ChildMaximize(Sender: TObject; var Allow: Boolean);
procedure ChildMinimize(Sender: TObject; var Allow: Boolean);
procedure ChildRestore(Sender: TObject; var Allow: Boolean);
procedure CreateMDIChild(const Name: string);
public
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses about;
procedure TMainForm.ChildMaximize(Se nder: TObject; var Allow: Boolean);
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] maximize', [Name, ClassName]));
end;
procedure TMainForm.ChildMinimize(Se nder: TObject; var Allow: Boolean);
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] minimize', [Name, ClassName]));
end;
procedure TMainForm.ChildRestore(Sen der: TObject; var Allow: Boolean);
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] restore', [Name, ClassName]));
end;
procedure TMainForm.CreateMDIChild(c onst Name: string);
var
Child: TMDIChild;
begin
{ create a new MDI child window }
Child := TMDIChild.Create(Applicati on);
Child.OnMaximize := ChildMaximize;
Child.OnMinimize := ChildMinimize;
Child.OnRestore := ChildRestore;
Child.Caption := Name;
if FileExists(Name) then Child.Memo1.Lines.LoadFrom File(Name) ;
end;
...
end.
HTH
TOndrej
Here's what I quickly wrote for you (based on the default template created by the MDI app wizard):
unit CHILDWIN;
interface
uses Windows, Messages, Classes, Graphics, Forms, Controls, Dialogs, StdCtrls;
type
TSysCmdEvent = procedure(Sender: TObject; var Allow: Boolean) of object;
TMDIChild = class(TForm)
Memo1: TMemo;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FOnMaximize: TSysCmdEvent;
FOnMinimize: TSysCmdEvent;
FOnRestore: TSysCmdEvent;
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
protected
function DoMaximize: Boolean; dynamic;
function DoMinimize: Boolean; dynamic;
function DoRestore: Boolean; dynamic;
public
property OnMaximize: TSysCmdEvent read FOnMaximize write FOnMaximize;
property OnMinimize: TSysCmdEvent read FOnMinimize write FOnMinimize;
property OnRestore: TSysCmdEvent read FOnRestore write FOnRestore;
end;
implementation
{$R *.dfm}
procedure TMDIChild.WMSysCommand(var
begin
case Message.CmdType and $FFF0 of
SC_MINIMIZE:
if not DoMinimize then
Exit;
SC_MAXIMIZE:
if not DoMaximize then
Exit;
SC_RESTORE:
if not DoRestore then
Exit;
end;
inherited;
end;
function TMDIChild.DoMaximize: Boolean;
begin
Result := True;
if Assigned(FOnMaximize) then
FOnMaximize(Self, Result);
end;
function TMDIChild.DoMinimize: Boolean;
begin
Result := True;
if Assigned(FOnMinimize) then
FOnMinimize(Self, Result);
end;
function TMDIChild.DoRestore: Boolean;
begin
Result := True;
if Assigned(FOnRestore) then
FOnRestore(Self, Result);
end;
procedure TMDIChild.FormClose(Sender
begin
Action := caFree;
end;
end.
unit MAIN;
interface
uses Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,
StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, ComCtrls, StdActns,
ActnList, ToolWin, ImgList, CHILDWIN;
type
TMainForm = class(TForm)
...
private
procedure ChildMaximize(Sender: TObject; var Allow: Boolean);
procedure ChildMinimize(Sender: TObject; var Allow: Boolean);
procedure ChildRestore(Sender: TObject; var Allow: Boolean);
procedure CreateMDIChild(const Name: string);
public
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses about;
procedure TMainForm.ChildMaximize(Se
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] maximize', [Name, ClassName]));
end;
procedure TMainForm.ChildMinimize(Se
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] minimize', [Name, ClassName]));
end;
procedure TMainForm.ChildRestore(Sen
begin
with Sender as TComponent do
ShowMessage(Format('%s [%s] restore', [Name, ClassName]));
end;
procedure TMainForm.CreateMDIChild(c
var
Child: TMDIChild;
begin
{ create a new MDI child window }
Child := TMDIChild.Create(Applicati
Child.OnMaximize := ChildMaximize;
Child.OnMinimize := ChildMinimize;
Child.OnRestore := ChildRestore;
Child.Caption := Name;
if FileExists(Name) then Child.Memo1.Lines.LoadFrom
end;
...
end.
HTH
TOndrej
ASKER
Hi Folks,
Writing a TForm decendant wouldn't have been a problem. It was my first thought. However, as I said, I didn't want to add code to any of the existing forms.
Meikl, that solution you posted. Will that detect when a child form is minimized by the main form without adding that code to the child forms? It looks like it, but I just need to be sure :)
Thanks to all for the help thus far.
Stu
Writing a TForm decendant wouldn't have been a problem. It was my first thought. However, as I said, I didn't want to add code to any of the existing forms.
Meikl, that solution you posted. Will that detect when a child form is minimized by the main form without adding that code to the child forms? It looks like it, but I just need to be sure :)
Thanks to all for the help thus far.
Stu
Just to make this clear: you don't have to *add any code* to any of your MDI child forms; all you need to do is *derive* from the base MDI child form class.
kretzschmar, I'm not sure if that solution helps; I don't think that minimize messages (WM_SYSCOMMAND with SC_MINIMIZE) are sent to the MDI parent. These are messages defined for MDI child windows (sent to the MDI parent):
WM_MDIACTIVATE
WM_MDICASCADE
WM_MDICREATE
WM_MDIDESTROY
WM_MDIGETACTIVE
WM_MDIICONARRANGE
WM_MDIMAXIMIZE
WM_MDINEXT
WM_MDIREFRESHMENU
WM_MDIRESTORE
WM_MDISETMENU
WM_MDITILE
As you can see, there's no WM_MDIMINIMIZE... which makes me think you have to catch WM_SYSCOMMAND in each MDI child form.
WM_MDIACTIVATE
WM_MDICASCADE
WM_MDICREATE
WM_MDIDESTROY
WM_MDIGETACTIVE
WM_MDIICONARRANGE
WM_MDIMAXIMIZE
WM_MDINEXT
WM_MDIREFRESHMENU
WM_MDIRESTORE
WM_MDISETMENU
WM_MDITILE
As you can see, there's no WM_MDIMINIMIZE... which makes me think you have to catch WM_SYSCOMMAND in each MDI child form.
well, could be, tondrej, can't check it myself yet,
but maybe the defwindowproc of
each child can be hooked by the mainform
but maybe the defwindowproc of
each child can be hooked by the mainform
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
This is GOOD!
Thanks TOndrej!
Stu
Thanks TOndrej!
Stu
ASKER
Thank you VERY much for that code. It worked perfectly.
Thank-you to everyone else as well :)
Just a quick question... Does anyone know of a hack to hide an MDIChild? :)
Cheers,
Stu
Thank-you to everyone else as well :)
Just a quick question... Does anyone know of a hack to hide an MDIChild? :)
Cheers,
Stu
Hide it:
if Assigned(ActiveMDIChild) then
ShowWindow(ActiveMDIChild. Handle, SW_HIDE);
Show all hidden children:
for I := 0 to MDIChildCount - 1 do
ShowWindow(MDIChildren[I]. Handle, SW_SHOW);
But... be warned. There is code in the VCL (TCustomForm.VisibleChangi ng) which raises an exception 'Cannot hide an MDI Child Form' when you try to hide it by usual VCL methods (Hide; Visible := False;).
I'm sure Borland knew what they were doing, so perhaps you should not even try to hide an MDI child form...
It could lead to some unexpected problems. Just my 2c.
if Assigned(ActiveMDIChild) then
ShowWindow(ActiveMDIChild.
Show all hidden children:
for I := 0 to MDIChildCount - 1 do
ShowWindow(MDIChildren[I].
But... be warned. There is code in the VCL (TCustomForm.VisibleChangi
I'm sure Borland knew what they were doing, so perhaps you should not even try to hide an MDI child form...
It could lead to some unexpected problems. Just my 2c.
ASKER
Mmmm. I tried that. It didn't work for me. Thanks anyway.
Stu
Stu
Strange, it did work in my test project :-/
You could implement this by defining a new form class (descendant from TCustomForm or TForm), and add all functionality there. The obvious places are overriding the Create method and (if you need to set event handlers) the Loaded method as well.
Like this:
type
TMyForm =
class(TCustomForm)
public
constructor Create(aOwner: TComponent); override;
procedure Loaded; override;
end;
Then re-name the parent class of all forms you have to TMyForm, and voila!
HTH
David