2012-12-13 17 views
6

Mam sytuacji gdzie mam TImage i na górze nim TPanel pokrywając je częściowo i dzielą ten sam nadrzędny:Jak przekierować zdarzenia myszy do innej kontrolki?

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

Panel1 odbiera myszy dół/MOVE/up wydarzenia i przetwarzanie go (tak robi Image1), ale w pewnej sytuacji chciałbym "przekierować" wiadomość w dół do Image1, jakby chciał zasymulować kliknięcie Image1 zamiast Panel1.

Oto co zrobiłem:

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; 

To działa zgodnie z oczekiwaniami, ale nie jestem pewien, że to właściwa droga.
Moje pytanie brzmi: czy robię to dobrze? czy istnieje lepszy lub zalecany sposób robienia tego?


Update (1): Handling WM_NCHITTEST jako sugerowane jest ważny odpowiedź i myślałem o tym również. nawet ustawienie Panel1.Enabled na False przekieruje komunikaty myszy do kontrolki Image1.

jednak rozważyć tę sytuację, gdy kliknę lokalizację x na panelu i nadal trzeba przekierować wiadomość do Image1 (!):

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

Moja metoda działa, ale WM_NCHITTEST nie znajduje zastosowania w opisanym scenariuszu . Nadal nie otrzymałem odpowiedzi, jeśli moja metoda jest ważna, czy nie. (A może powinienem zadać kolejne pytanie z powyższym scenariuszu?)

+0

Powiedziałbym, że najlepiej jest zrobić to na poziomie pętli wiadomości. Wdrożyć procedurę obsługi 'OnMessage' dla' TApplication'. Lub może zrobić to samo z obiektem 'TApplicationEvents'. Tam możesz zmienić uchwyt okna docelowego dla wiadomości, które Cię interesują. –

+1

@David, nie jest widoczny w pierwszym widoku z tego pytania, ale OP naprawdę chce przekierować wiadomości. Tak, to jest droga. – TLama

+0

@ TLama Nie mam pragnienia, aby napisać odpowiedź tutaj. Proszę, nie czuj się zahamowany, robiąc to samemu! –

Odpowiedz

5

w przypadku, gdy kontrola, z którego chcesz przekierować zdarzenia myszy nie będą w całym jego obszarze roboczym wewnątrz kontroli, do którego powinien zostać przekierowany te wydarzenia (jak już pokazano w aktualizacji zapytania), następnie wiadomość WM_NCHITTEST może zostać wysłana do innej kontrolki. Wtedy jedynym sposobem na wykorzystanie IMHO, przekierowanie wszystkich wiadomości myszy.

Jak wspomniano w komentarzu do @David, można przekierować tę wiadomość w sposób globalny, pisząc procedurę obsługi zdarzeń dla zdarzenia OnMessage dla TApplication. Lub użyj obiektu TApplicationEvents.

W poniższym przykładzie można zdefiniować zakres komunikatów, które zostaną przekierowane, a także określić listę kontrolek źródłowych i docelowych dla tego przekierowania. Do przekierowania używane jest zdarzenie OnMessage obiektu TApplication, ale ponieważ Twój cel jest w tym przypadku potomkiem TGraphicControl, możesz nie tylko zmienić adresata przychodzącej wiadomości, ale musisz zjeść ten komunikat i wykonać wiadomość na celu samodzielnie kontrolować metodą Perform.

Oto kod pokazujący, jak przekierować wszystkie wiadomości myszy od Panel1 do Image1. Możesz uzyskać cały testowy projekt from here, jeśli chcesz:

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

Nie pamiętam, ze względu na "Break" używane w pętli wyszukiwania kontroli źródła (gdy kontrola znajduje się na liście przekierowań), można określić tylko jedno przekierowanie dla kontrolki źródłowej. Wszystkie pozostałe zostaną zignorowane. Jeśli usuniesz "Break", będziesz mógł * nadawać * wiadomość do więcej niż jednego kontrolka celu. – TLama

5

można czerpać swoją klasę panel do obsługi WM_NCHITTEST wiadomości do powrotu HTTRANSPARENT regionu chcesz kontrolę pod panelem otrzymane wiadomości myszy. Np .:

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; 

Oczywiście jest to tylko test, można publikować pole w składniku na to, aby rozwiązać gdzie rezyduje że kontrola etc ..

7

Uchwyt wm_NCHitTest wiadomości wysyłanych do panelu i powrót htTransparent. System operacyjny wyśle ​​komunikat myszy do następnej kontroli bez żadnego dalszego przetwarzania wymaganego od twojego programu. (Z punktu widzenia systemu operacyjnego "następna kontrola w dół" jest nadrzędną kontrolą zarówno panelu, jak i obrazu, VCL zajmuje się routingiem komunikatu myszy z powrotem do kontroli obrazu, tak jak ma to miejsce w przypadku wszystkich potomków TGraphicControl, ponieważ nie są one „t prawdziwy okienkiem kontroli)

coś takiego:.

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

przypisać tę metodę do sposobu WindowProc panelu. Zapisz poprzednią wartość właściwości w polu formularza.

var 
    FPrevPanelWindowProc: TWndMethod; 

FPrevPanelWindowProc := Panel.WindowProc; 
Panel.WindowProc := Self.PanelWindowProc; 
Powiązane problemy