How do I catch certain events of a form from outside the form?

百般思念 提交于 2019-11-30 20:15:53

Here's a more complete example of the solution that David Provided:

private
  { Private declarations }
  SaveProc : TWndMethod;
  procedure CommonWindowProc(var Message: TMessage);

...

procedure TForm1.Button1Click(Sender: TObject);
var
  f : tForm2;
begin
  f := tForm2.Create(nil);
  SaveProc := f.WindowProc;
  f.WindowProc := CommonWindowProc;
  f.Show;
end;

procedure TForm1.CommonWindowProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_SIZE : Memo1.Lines.Add('Resizing');
    WM_CLOSE : Memo1.Lines.Add('Closing');
    CM_MOUSEENTER : Memo1.Lines.Add('Mouse enter form');
    CM_MOUSELEAVE : Memo1.Lines.Add('Mouse leaving form');
    // all other messages will be available as needed
  end;
  SaveProc(Message); // Call the original handler for the other form
end;

You need to listen for particular windows messages being delivered to the form. The easiest way to do this is to assign the WindowProc property of the form. Remember to keep a hold of the previous value of WindowProc and call it from your replacement.

In your wrapper object declare a field like this:

FOriginalWindowProc: TWndMethod;

Then in the wrapper's constructor do this:

FOriginalWindowProc := Form.WindowProc;
Form.WindowProc := NewWindowProc;

Finally, implement the replacement window procedure:

procedure TFormWrapper.NewWindowProc(var Message: TMessage);
begin
  //test for and respond to the messages of interest
  FOriginalWindowProc(Message);
end;

A better solution than trying to work outside of the form would be to make every form descend from a common base form that implements the functionality. The form event handlers are exactly the right place to add this code but you'd write it all in the ancestor form. Any descendant form could still use the form events and as long as they always call inherited somewhere in the event handler the ancestor code would still execute.

Using Windows Messages can really attain a fine granularity (Yes, its part of your requirements!) but in some user cases where relying just on the VCL Event Framework suffices, a similar solution can be suggested:

unit Host;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  THostForm = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FFormResize: TNotifyEvent;
    FFormActivate: TNotifyEvent;
    FFormDeactivate: TNotifyEvent;
    FFormDestroy: TNotifyEvent;

    procedure _FormResize(Sender: TObject);
    procedure _FormActivate(Sender: TObject);
    procedure _FormDeactivate(Sender: TObject);

    procedure InternalEventHandlerInit(const AForm:TForm);
  public
    procedure Log(const Msg:string);
    procedure Logln(const Msg:string);
  end;

var
  HostForm: THostForm;

implementation

{$R *.dfm}

procedure THostForm.Button1Click(Sender: TObject);
var
  frm: TForm;
begin
  frm := TForm.Create(nil);
  frm.Name := 'EmbeddedForm';
  frm.Caption := 'Embedded Form';
  //
  InternalEventHandlerInit(frm);
  //
  Logln('<'+frm.Caption+'> created.');
  //
  frm.Show;
end;


procedure THostForm.InternalEventHandlerInit(const AForm: TForm);
begin
  FFormResize := AForm.OnResize;
  AForm.OnResize := _FormResize;
  //
  FFormActivate :=  AForm.OnActivate;
  AForm.OnActivate := _FormActivate;
  //
  FFormDeactivate :=  AForm.OnDeactivate;
  AForm.OnDeactivate := _FormDeactivate;
end;

procedure THostForm.Log(const Msg: string);
begin
  Memo1.Lines.Add(Msg);
end;

procedure THostForm.Logln(const Msg: string);
begin
  Memo1.Lines.Add(Msg);
  Memo1.Lines.Add('');
end;

procedure THostForm._FormActivate(Sender: TObject);
begin
  Log('Before OnActivate <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormActivate) then
    FFormActivate(Sender) // <<<
  else
    Log('No OnActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnActivate <'+(Sender as TCustomForm).Caption+'>');
end;

procedure THostForm._FormDeactivate(Sender: TObject);
begin
  Log('Before OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormDeactivate) then
    FFormDeactivate(Sender)
  else
    Log('No OnDeActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
end;

procedure THostForm._FormResize(Sender: TObject);
begin
  Log('Before OnResize <'+(Sender as TCustomForm).Caption+'>');
  //
  if Assigned(FFormResize) then
    FFormResize(Sender)
  else
    Log('No OnResize Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
  //
  Logln('After OnResize <'+(Sender as TCustomForm).Caption+'>');
end;

end.

Another option is create TApplicationEvents and assign a handler to OnMessage event. Once if it fired, use the FindControl function and Msg.hWnd to check if it is the tform type and do what ever you want without hookin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!