Solved

Detect Minimized MDI Children

Posted on 2002-04-25
16
1,038 Views
Last Modified: 2012-06-27
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
Comment
Question by:Stuart_Johnson
  • 6
  • 5
  • 3
  • +1
16 Comments
 
LVL 1

Expert Comment

by:SchweizerD
Comment Utility
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
 
LVL 6

Author Comment

by:Stuart_Johnson
Comment Utility
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
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
? how will you detect the minimize-event, david

listening . . .
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
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
 
LVL 1

Expert Comment

by:SchweizerD
Comment Utility
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
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
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
 
LVL 6

Author Comment

by:Stuart_Johnson
Comment Utility
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
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
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
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
well, could be, tondrej, can't check it myself yet,
but maybe the defwindowproc of
each child can be hooked by the mainform
0
 
LVL 8

Accepted Solution

by:
TOndrej earned 150 total points
Comment Utility
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
 
LVL 6

Author Comment

by:Stuart_Johnson
Comment Utility
This is GOOD!

Thanks TOndrej!

Stu
0
 
LVL 6

Author Comment

by:Stuart_Johnson
Comment Utility
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
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
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
 
LVL 6

Author Comment

by:Stuart_Johnson
Comment Utility
Mmmm.  I tried that.  It didn't work for me.  Thanks anyway.

Stu
0
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
Strange, it did work in my test project :-/
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now