2012-12-13 35 views
6

Tôi có một tình huống mà tôi có một TImage và trên đầu trang của nó một TPanel che nó một phần và họ chia sẻ cùng cha mẹ: chuộtLàm thế nào để chuyển hướng các sự kiện chuột đến một điều khiển khác?

------------------ 
| Image1  | 
| ------------ | 
| | Panel1 | | 
| ------------ | 
|    | 
------------------ 

Panel1 đang nhận được xuống/di chuyển/lên các sự kiện và xử lý nó (vì vậy hiện Image1), nhưng trong một số trường hợp tôi muốn "chuyển hướng" thông báo chuột xuống Image1 như thể để mô phỏng rằng Image1 được nhấp chứ không phải là Panel1.

Dưới đây là những gì tôi đã làm:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    if (ssLeft in Shift) then 
    Beep; 
end; 

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; 
    X, Y: Integer); 
begin 
    //... 
end; 

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    ShowMessage('boo!'); 
end; 

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
var 
    P: TPoint; 
begin 
    if FRedirectToImage then begin 
    ReleaseCapture; // do I need to send a WM_LBUTTONUP as well to the panel?   
    GetCursorPos(P); 
    P := ScreenToClient(P); 
    Image1.Perform(WM_LBUTTONDOWN, MK_LBUTTON, Longint(PointToSmallPoint(P))); 
    Exit; 
    end; 

    // Normal handling 
    if (ssLeft in Shift) then begin 
    // ... 
    end; 
end; 

Nó hoạt động như mong đợi nhưng tôi không chắc chắn Đó là cách đúng đắn.
Câu hỏi của tôi là, tôi có làm đúng không? có cách nào tốt hơn hoặc được khuyến nghị thực hiện?


Cập nhật (1): Xử lý WM_NCHITTEST như đề xuất là một câu trả lời hợp lệ và tôi nghĩ về nó cũng có. thậm chí đặt Panel1.Enabled thành False sẽ định tuyến thông báo chuột đến điều khiển Image1 cơ bản.

Nhưng xem xét tình huống này, nơi tôi nhấp vào vị trí x trên Panel và vẫn cần để định tuyến thông điệp tới Image1 (!):

------------------ 
| Image1  | 
|   -------------- 
|   | Panel1 x | 
|   -------------- 
|    | 
------------------ 

phương pháp tôi làm việc, nhưng WM_NCHITTEST không áp dụng trong kịch bản mô tả . Tôi vẫn không nhận được câu trả lời nếu phương pháp của tôi là hợp lệ hay không. (hoặc có lẽ tôi nên hỏi một câu hỏi khác với kịch bản trên?)

+0

Tôi muốn đặt cược tốt nhất của bạn là làm điều này ở cấp vòng lặp thông báo. Triển khai trình xử lý 'OnMessage' cho' TApplication'. Hoặc có thể làm tương tự với đối tượng 'TApplicationEvents'. Trong đó, bạn có thể thay đổi xử lý cửa sổ đích của các thư quan tâm. –

+1

@David, không hiển thị trên lượt xem đầu tiên từ câu hỏi này, nhưng OP thực sự muốn chuyển hướng thư. Vì vậy, đó là con đường để đi. – TLama

+0

@TLama Tôi không khát để viết câu trả lời ở đây. Xin vui lòng không cảm thấy bị ức chế làm như vậy cho mình! –

Trả lời

5

Trong trường hợp khi điều khiển từ mà bạn muốn chuyển hướng sự kiện chuột sẽ không ở khu vực toàn bộ khách hàng của nó bên sự kiểm soát mà những sự kiện đó nên được chuyển hướng (như bạn đã thể hiện trong bản cập nhật câu hỏi của bạn), sau đó thông báo WM_NCHITTEST có thể được gửi đến một điều khiển khác. Sau đó, cách duy nhất vẫn là sử dụng IMHO, chuyển hướng tất cả các tin nhắn chuột.

Như @David đã đề cập trong nhận xét của mình, bạn có thể thực hiện chuyển hướng thư này một cách toàn cầu bằng cách viết trình xử lý sự kiện cho sự kiện OnMessage cho TApplication. Hoặc sử dụng đối tượng TApplicationEvents.

Trong ví dụ sau, bạn có thể xác định phạm vi tin nhắn, sẽ được chuyển hướng cũng như chỉ định danh sách các điều khiển nguồn và đích cho chuyển hướng đó. Để chuyển hướng được sử dụng sự kiện OnMessage của đối tượng TApplication, nhưng vì mục tiêu của bạn là trong trường hợp này là hậu duệ, bạn không thể thay đổi người nhận thư đến, nhưng bạn phải ăn thư này và thực hiện thư trên mục tiêu kiểm soát thông qua phương pháp Perform của chính bạn.

Đây là mã hiển thị cách chuyển hướng tất cả thư chuột từ Panel1 đến Image1. Bạn có thể nhận toàn bộ dự án thử nghiệm from here nếu bạn muốn:

unit Unit1; 

interface 

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

type 
    TMsgRange = record 
    MsgFrom: UINT; 
    MsgTo: UINT; 
    end; 
    TRedirect = record 
    Source: HWND; 
    Target: TControl; 
    end; 

type 
    TForm1 = class(TForm) 
    Memo1: TMemo; 
    Panel1: TPanel; 
    Image1: TImage; 
    procedure FormCreate(Sender: TObject); 
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; 
     Shift: TShiftState; X, Y: Integer); 
    procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton; 
     Shift: TShiftState; X, Y: Integer); 
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; 
     Shift: TShiftState; X, Y: Integer); 
    procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton; 
     Shift: TShiftState; X, Y: Integer); 
    private 
    FRedirectList: array of TRedirect; 
    FRedirectEnabled: Boolean; 
    FRedirectMsgRange: TMsgRange; 
    procedure ApplicationMessage(var AMessage: TMsg; var Handled: Boolean); 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.ApplicationMessage(var AMessage: TMsg; var Handled: Boolean); 
var 
    I: Integer; 
begin 
    if FRedirectEnabled and (AMessage.message >= FRedirectMsgRange.MsgFrom) and 
    (AMessage.message <= FRedirectMsgRange.MsgTo) then 
    begin 
    for I := 0 to High(FRedirectList) do 
     if (AMessage.hwnd = FRedirectList[I].Source) and 
     Assigned(FRedirectList[I].Target) then 
     begin 
     Handled := True; 
     FRedirectList[I].Target.Perform(AMessage.message, 
      AMessage.wParam, AMessage.lParam); 
     Break; 
     end; 
    end; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    FRedirectEnabled := True; 
    FRedirectMsgRange.MsgFrom := WM_MOUSEFIRST; 
    FRedirectMsgRange.MsgTo := WM_MOUSELAST; 
    SetLength(FRedirectList, 1); 
    FRedirectList[0].Source := Panel1.Handle; 
    FRedirectList[0].Target := Image1; 
    Application.OnMessage := ApplicationMessage; 
end; 

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    Memo1.Lines.Add('Image1MouseDown') 
end; 

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    Memo1.Lines.Add('Image1MouseUp') 
end; 

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    Memo1.Lines.Add('Panel1MouseDown') 
end; 

procedure TForm1.Panel1MouseUp(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    Memo1.Lines.Add('Panel1MouseUp') 
end; 

end. 
+0

Quên thông báo, do 'Break' được sử dụng trong vòng tìm kiếm điều khiển nguồn (khi điều khiển được tìm thấy trong danh sách chuyển hướng), chỉ có thể chỉ định một chuyển hướng cho điều khiển nguồn. Tất cả những người khác sẽ bị bỏ qua. Nếu bạn xóa 'Break' đó, bạn sẽ có thể * phát * tin nhắn đến nhiều hơn một điều khiển đích. – TLama

5

Bạn có thể lấy lớp bảng điều khiển để xử lý WM_NCHITTEST tin nhắn trả lại HTTRANSPARENT cho khu vực bạn muốn điều khiển bên dưới bảng nhận tin nhắn chuột. Ví dụ .:

procedure TMyPanel.WMNCHitTest(var Message: TWMNCHitTest); 
var 
    Pt: TPoint; 
begin 
    Pt := ScreenToClient(SmallPointToPoint(Message.Pos)); 
    if (Pt.X < 80) and (Pt.Y < 60) then // devise your logic here... 
    Message.Result := HTTRANSPARENT 
    else 
    inherited; 
end; 

Rõ ràng đây chỉ là một bài kiểm tra, bạn có thể xuất bản một lĩnh vực trong thành phần của bạn cho nó để giải quyết nơi điều khiển mà cư trú vv ..

7

Xử lý wm_NCHitTest thông điệp gửi đến bảng điều khiển và trở về htTransparent. Hệ điều hành sẽ gửi thông báo chuột đến lần kiểm soát tiếp theo mà không cần xử lý thêm nữa từ chương trình của bạn. (Từ quan điểm hệ điều hành, "kiểm soát tiếp theo xuống" là kiểm soát cha mẹ của cả bảng điều khiển và hình ảnh; VCL chăm sóc định tuyến thông điệp chuột trở lại điều khiển hình ảnh, giống như tất cả các con cháu TGraphicControl, vì chúng 't thực cửa sổ điều khiển)

Something như thế này:.

procedure TParentForm.PanelWindowProc(var Msg: TMessage); 
begin 
    FPrevPanelWindowProc(Msg); 
    if (Msg.Message = wm_NCHitTest) and FRedirectToImage then 
    Msg.Result := htTransparent; 
end; 

Gán rằng phương pháp phương pháp WindowProc của bảng điều khiển. Lưu trữ giá trị trước đó của thuộc tính trong một trường của biểu mẫu.

var 
    FPrevPanelWindowProc: TWndMethod; 

FPrevPanelWindowProc := Panel.WindowProc; 
Panel.WindowProc := Self.PanelWindowProc; 
Các vấn đề liên quan