Как я могу поймать определенные события формы извне формы?
Я работаю над тем, что потребует мониторинга многих форм. Из-за пределов формы и без помещения какого-либо кода в форму мне нужно каким-то образом захватывать события из этих форм, скорее всего, в форме сообщений Windows. Но как бы вы захватили сообщения Windows вне класса, с которым это связано?
У моего проекта есть объект, который оборачивает каждую форму, которую он отслеживает, и я предполагаю, что эта обработка будет идти в этом объекте. По сути, когда я создаю форму, которую хочу отслеживать, я создаю соответствующий объект, который, в свою очередь, добавляется в список всех созданных форм. Самое главное, когда эта форма закрыта, я должен знать, чтобы я мог удалить объект-оболочку этой формы из списка.
Эти события включают в себя:
- Минимизировать
- максимизировать
- Восстановить
- близко
- Фокус в / из
Чего я не хочу:
- Любой код внутри любых форм или блоков форм для этой обработки
- Наследование форм от любой пользовательской базовой формы
- Использование событий формы, таких как
OnClose
потому что они будут использоваться для других целей
Что я хочу:
- Обработка сообщений Windows для этих событий
- Любые советы о том, как получить сообщения Windows из-за пределов класса
- Какие сообщения Windows мне нужно слушать
Вопрос переписан с той же информацией, но с другим подходом
5 ответов
Вот более полный пример решения, предоставленного Дэвидом:
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;
Вам необходимо прослушивать конкретные сообщения Windows, доставляемые в форму. Самый простой способ сделать это - назначить WindowProc
свойство формы. Не забудьте сохранить предыдущее значение WindowProc
и позвони из твоей замены.
В вашем объекте-обертке объявите поле, подобное этому:
FOriginalWindowProc: TWndMethod;
Затем в конструкторе обёртки сделайте это:
FOriginalWindowProc := Form.WindowProc;
Form.WindowProc := NewWindowProc;
Наконец, реализуйте процедуру замены окна:
procedure TFormWrapper.NewWindowProc(var Message: TMessage);
begin
//test for and respond to the messages of interest
FOriginalWindowProc(Message);
end;
Лучшим решением, чем пытаться работать вне формы, было бы сделать так, чтобы каждая форма происходила из общей базовой формы, которая реализует эту функциональность. Обработчики событий формы - это как раз то место, куда нужно добавить этот код, но вы бы написали все это в форме предка. Любая форма-потомок может по-прежнему использовать события формы, и до тех пор, пока они всегда вызывают унаследованные где-то в обработчике событий, код предка все равно будет выполняться.
Другой вариант - создать TApplicationEvents и назначить обработчик для события OnMessage. Один раз, если он сработал, используйте функцию FindControl и Msg.hWnd, чтобы проверить, является ли он типом tform, и делайте то, что вы хотите, без зацепки.
Использование сообщений Windows может действительно достичь fine granularity
(Да, это часть ваших требований!), Но в некоторых случаях, когда пользователь полагается только на VCL Event Framework
Достаточно, можно предложить аналогичное решение:
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.