Link to home
Start Free TrialLog in
Avatar of Stuart_Johnson
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
Avatar of SchweizerD
SchweizerD

Stu,

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
Avatar of Stuart_Johnson

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
Avatar of kretzschmar
? how will you detect the minimize-event, david

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(ClientHandle, GWL_WNDPROC));
  FClientInstance := MakeObjectInstance(ClientWndProc);
  SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance));
end;

procedure TfrmMForm.ClientWndProc(var aMessage: TMessage);

  procedure DoDefault;
  begin
     with aMessage do
       Result := CallWindowProc(FPrevClientProc, 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 ;-)
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
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(Sender: TObject; var Allow: Boolean);
begin
  with Sender as TComponent do
    ShowMessage(Format('%s [%s] maximize', [Name, ClassName]));
end;

procedure TMainForm.ChildMinimize(Sender: TObject; var Allow: Boolean);
begin
  with Sender as TComponent do
    ShowMessage(Format('%s [%s] minimize', [Name, ClassName]));
end;

procedure TMainForm.ChildRestore(Sender: TObject; var Allow: Boolean);
begin
  with Sender as TComponent do
    ShowMessage(Format('%s [%s] restore', [Name, ClassName]));
end;

procedure TMainForm.CreateMDIChild(const Name: string);
var
  Child: TMDIChild;
begin
  { create a new MDI child window }
  Child := TMDIChild.Create(Application);
  Child.OnMaximize := ChildMaximize;
  Child.OnMinimize := ChildMinimize;
  Child.OnRestore := ChildRestore;
  Child.Caption := Name;
  if FileExists(Name) then Child.Memo1.Lines.LoadFromFile(Name);
end;

...

end.

HTH
TOndrej
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
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.
well, could be, tondrej, can't check it myself yet,
but maybe the defwindowproc of
each child can be hooked by the mainform
ASKER CERTIFIED SOLUTION
Avatar of TOndrej
TOndrej

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
This is GOOD!

Thanks TOndrej!

Stu
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
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.VisibleChanging) 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.
Mmmm.  I tried that.  It didn't work for me.  Thanks anyway.

Stu
Strange, it did work in my test project :-/