2012-01-05 25 views
7

Tôi đang làm việc trên một cái gì đó mà sẽ yêu cầu giám sát của nhiều hình thức. Từ bên ngoài biểu mẫu và không đặt bất kỳ mã nào bên trong biểu mẫu, tôi cần phải bằng cách nào đó nắm bắt các sự kiện từ các biểu mẫu này, rất có thể dưới dạng thông báo của cửa sổ. Nhưng làm thế nào bạn sẽ chụp được các thông điệp của Windows từ bên ngoài lớp mà nó liên quan đến?Tôi làm cách nào để nắm bắt các sự kiện nhất định của biểu mẫu từ bên ngoài biểu mẫu?

Dự án của tôi có đối tượng bao bọc từng biểu mẫu mà nó đang theo dõi và tôi cho rằng việc xử lý này sẽ xuất hiện trong đối tượng này. Về cơ bản, khi tôi tạo một biểu mẫu tôi muốn theo dõi, tôi tạo một đối tượng tương ứng mà lần lượt được thêm vào danh sách tất cả các biểu mẫu đã tạo. Quan trọng nhất, khi hình thức đó được đóng lại, tôi phải biết để tôi có thể loại bỏ đối tượng bao bọc của biểu mẫu này khỏi danh sách.

Những sự kiện này bao gồm:

  • Minimize
  • Tối đa hóa
  • Restore
  • Đóng
  • Focus in/out

Những gì tôi KHÔNG muốn:

  • Bất kỳ mã bên dưới mọi hình thức, đơn vị hình thức xử lý này
  • Thừa hưởng các hình thức từ bất kỳ hình thức cơ bản tùy chỉnh
  • Sử dụng các sự kiện của hình thức như OnClose vì họ sẽ được sử dụng cho các mục đích khác

gì tÔI muốn:

  • Xử lý thông điệp cửa sổ cho những sự kiện này
  • Bất cứ lời khuyên về cách ge thông điệp t cửa sổ từ bên ngoài lớp
  • Những thông điệp cửa sổ tôi cần phải lắng nghe cho

Câu hỏi tái bằng văn bản với cùng một thông tin nhưng cách tiếp cận khác nhau

+0

Tôi không chắc lắm nhưng tôi nghĩ Bạn cũng có thể xem xét việc tiêm mã được thực hiện bởi một số khung công tác AOP. – menjaraz

+0

Bạn biết rằng bạn có thể thay thế các sự kiện biểu mẫu bằng trình xử lý của riêng bạn nhưng giữ lại giá trị cũ và sau đó gọi trình xử lý cũ, từ trình xử lý thay thế của bạn, phải không? Đó là đơn giản hơn so với "mã tiêm" thực sự hoặc "hooking" thực sự. Điều này rất giống với cách "các trình xử lý ngắt" hoạt động trong hầu hết các hệ điều hành. Chúng tôi gọi nó là "thay thế vectơ". –

+0

@WarrenP Tôi biết điều này, và có lẽ sẽ làm điều đó nếu David không đề cập đến một phương pháp rõ ràng hơn. Nhưng chiến lược này (ít nhất là theo ý kiến ​​của tôi) có lẽ là 90-95% hiệu quả (tôi có thể thấy trước một số vấn đề sẽ làm rối loạn tình trạng này). Giải pháp của David hiệu quả 100%. –

Trả lời

6

Dưới đây là ví dụ hoàn chỉnh hơn về giải pháp mà David cung cấp:

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; 
1

Một giải pháp tốt hơn so với cố gắng làm việc bên ngoài biểu mẫu sẽ làm cho mọi hình thức xuất phát từ một biểu mẫu cơ sở chung thực hiện chức năng. Trình xử lý sự kiện biểu mẫu chính xác là nơi thích hợp để thêm mã này nhưng bạn sẽ viết tất cả trong biểu mẫu tổ tiên. Bất kỳ hình thức hậu duệ vẫn có thể sử dụng các sự kiện biểu mẫu và miễn là chúng luôn được gọi kế thừa ở đâu đó trong trình xử lý sự kiện, mã tổ tiên vẫn sẽ thực thi.

+1

Tôi đồng ý với điều này nhưng đôi khi bạn bao gồm mã bên thứ 3 trong dự án của mình, điều này có thể khiến khó đạt được –

+0

Đây là kế hoạch ban đầu của tôi, nhưng lý do chính này không hiệu quả là vì - như đã đề cập trong câu hỏi của tôi - tôi không thể đặt bất kỳ mã nào bên trong bất kỳ biểu mẫu nào cho điều này. Tạo một biểu mẫu cơ sở không khác gì so với việc đặt mã bên trong biểu mẫu. –

+0

@Jerry - đã hiểu nhưng bạn cũng chỉ ra rằng bạn cuối cùng sẽ thêm mã vào các trình xử lý sự kiện biểu mẫu cá nhân cũng đang đặt mã bên trong các biểu mẫu. –

8

Bạn cần nghe các tin nhắn cửa sổ cụ thể được gửi tới biểu mẫu. Cách dễ nhất để làm điều này là gán thuộc tính WindowProc của biểu mẫu. Hãy nhớ giữ một giá trị trước đó của WindowProc và gọi nó từ thiết bị thay thế của bạn.

Trong đối tượng wrapper của bạn tuyên bố một lĩnh vực như thế này:

FOriginalWindowProc: TWndMethod; 

Sau đó, trong constructor của wrapper làm điều này:

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

Cuối cùng, thực hiện các thủ tục cửa sổ thay thế:

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

Điều đó có vẻ đầy hứa hẹn, nhưng không thể nói chắc chắn cho đến khi tôi về nhà và chạy thử. –

+0

Tôi sắp hỏi bạn cách xử lý các tin nhắn trong 'NewWindowProc' nhưng Mike đánh tôi với nó - Tôi sẽ phải chấp nhận câu trả lời của anh ấy ngay bây giờ :(vẫn +1 cho câu trả lời gốc –

+0

Oh Tôi nghĩ đó là một chút dễ dàng Tôi chỉ làm một chút, tôi chỉ giải quyết được việc chặn tin nhắn vì toàn bộ tâm điểm của câu hỏi và nhận xét là phần đó. –

0

Sử dụng tin nhắn Windows thực sự có thể đạt được một fine granularity (Vâng, một phần của yêu cầu của bạn!) Nhưng trong một số trường hợp sử dụng nơi dựa chỉ trên VCL Event Framework đủ, một giải pháp tương tự có thể được đề nghị:

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. 
+3

Điều này là tốt cho đến khi hình thức nạn nhân quyết định sửa đổi xử lý sự kiện của riêng mình và sau đó trò chơi của nó hơn. –

+0

@ David Heffernan: Bạn nói đúng. Bây giờ tôi thấy chúng không được bảo vệ chút nào. Tôi sợ cách duy nhất để đi theo cách này trong trường hợp này là làm một số mã tiêm vào mẫu "nạn nhân" (đầu cơ). – menjaraz

0

Một tùy chọn khác là tạo TApplicationEvents và gán một trình xử lý cho sự kiện OnMessage. Một lần nếu nó bị sa thải, hãy sử dụng chức năng FindControl và Msg.hWnd để kiểm tra xem đó có phải là kiểu biến dạng và làm những gì bạn muốn mà không cần hookin