• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1093
  • Last Modified:

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
0
Stuart_Johnson
Asked:
Stuart_Johnson
  • 6
  • 5
  • 3
  • +1
1 Solution
 
SchweizerDCommented:
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
0
 
Stuart_JohnsonAuthor Commented:
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
0
 
kretzschmarCommented:
? how will you detect the minimize-event, david

listening . . .
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
kretzschmarCommented:
hi stu,

here is a solution from freshman3k provided at q
http://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 ;-)
0
 
SchweizerDCommented:
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
0
 
TOndrejCommented:
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
0
 
Stuart_JohnsonAuthor Commented:
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
0
 
TOndrejCommented:
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.
0
 
TOndrejCommented:
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.
0
 
kretzschmarCommented:
well, could be, tondrej, can't check it myself yet,
but maybe the defwindowproc of
each child can be hooked by the mainform
0
 
TOndrejCommented:
OK, here is the code which requires no changes to the child forms. (I still like the previous method more.)

Again, this example is based on the default template generated by the MDI application wizard.

unit MAIN;

interface

uses Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,
  StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, ComCtrls, StdActns,
  ActnList, ToolWin, ImgList;

type
  TMainForm = class(TForm)
    ...
  private
    function MDIChildMaximize(ChildForm: TForm): Boolean;
    function MDIChildMinimize(ChildForm: TForm): Boolean;
    function MDIChildRestore(ChildForm: TForm): Boolean;
    procedure CreateMDIChild(const Name: string);
  public
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses CHILDWIN, about;

const
  BoolStrings: array[Boolean] of string = ('False', 'True');

function TMainForm.MDIChildMaximize(ChildForm: TForm): Boolean;
begin
  Result := False;
  OutputDebugString(PChar(Format('%s [%s] maximize: %s', [ChildForm.Name, ChildForm.ClassName, BoolStrings[Result]])));
end;

function TMainForm.MDIChildMinimize(ChildForm: TForm): Boolean;
begin
  Result := True;
  OutputDebugString(PChar(Format('%s [%s] minimize: %s', [ChildForm.Name, ChildForm.ClassName, BoolStrings[Result]])));
end;

function TMainForm.MDIChildRestore(ChildForm: TForm): Boolean;
begin
  Result := True;
  OutputDebugString(PChar(Format('%s [%s] restore: %s', [ChildForm.Name, ChildForm.ClassName, BoolStrings[Result]])));
end;

function MDIChildProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  C: TWinControl;
begin
  Result := 0;
  C := FindControl(hwnd);
  if Assigned(C) and (C is TForm) then
  begin
    case uMsg of
      WM_SYSCOMMAND:
        begin
          case wParam and $FFF0 of
            SC_MINIMIZE:
              with TMainForm(Application.MainForm) do
                if not MDIChildMinimize(TForm(C)) then
                  Exit;
            SC_MAXIMIZE:
              with TMainForm(Application.MainForm) do
                if not MDIChildMaximize(TForm(C)) then
                  Exit;
            SC_RESTORE:
              with TMainForm(Application.MainForm) do
                if not MDIChildRestore(TForm(C)) then
                  Exit;
          end;
        end;
      WM_DESTROY:
        begin
          SetWindowLong(hwnd, GWL_WNDPROC, C.Tag);
          C.Tag := 0;
        end;
    end;
    Result := CallWindowProc(Pointer(C.Tag), hwnd, uMsg, wParam, lParam);
  end;
end;

procedure TMainForm.CreateMDIChild(const Name: string);
var
  Child: TMDIChild;
begin
  { create a new MDI child window }
  Child := TMDIChild.Create(Application);
  Child.Tag := SetWindowLong(Child.Handle, GWL_WNDPROC, Integer(@MDIChildProc));
  Child.Caption := Name;
  if FileExists(Name) then Child.Memo1.Lines.LoadFromFile(Name);
end;

...

end.

HTH
TOndrej
0
 
Stuart_JohnsonAuthor Commented:
This is GOOD!

Thanks TOndrej!

Stu
0
 
Stuart_JohnsonAuthor Commented:
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
0
 
TOndrejCommented:
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.
0
 
Stuart_JohnsonAuthor Commented:
Mmmm.  I tried that.  It didn't work for me.  Thanks anyway.

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

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 6
  • 5
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now