2012-12-13 23 views
6

Ho una situazione dove ho un TImage e sopra di esso un TPanel copre parzialmente e condividono lo stesso genitore: topoCome reindirizzare gli eventi del mouse su un altro controllo?

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

Panel1 riceve giù/muovo eventi/e elaborarlo (così fa Image1), ma in alcune situazioni mi piacerebbe "reindirizzare" il mouse verso il basso verso Image1 come se simulare che Image1 sia stato cliccato anziché Panel1.

Ecco quello che ho fatto:

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; 

Funziona come previsto, ma io non sono sicuro che sia la strada giusta.
La mia domanda è, lo sto facendo bene? c'è un modo migliore o consigliato di farlo?


Update (1): Handling WM_NCHITTEST come suggerito è una risposta valida e ho pensato anche. anche impostando Panel1.Enabled su False si assegneranno i messaggi del mouse al controllo Image1 sottostante.

Ma considerare questa situazione in cui clicco la posizione x sul Pannello e ancora bisogno di instradare il messaggio a Image1 (!):

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

mio metodo funziona, ma WM_NCHITTEST non è applicabile nello scenario descritto . Non ho ancora ricevuto una risposta se il mio metodo è valido o meno. (o forse dovrei fare un'altra domanda con lo scenario sopra?)

+0

Direi che la cosa migliore è farlo a livello di ciclo di messaggi. Implementa un gestore 'OnMessage' per' TApplication'. O forse fare lo stesso con un oggetto 'TApplicationEvents'. Qui puoi cambiare l'handle della finestra di destinazione dei messaggi di interesse. –

+1

@David, non è visibile su una prima vista da questa domanda, ma OP vuole veramente reindirizzare i messaggi. Quindi, questa è la strada da percorrere. – TLama

+0

@TLama Non ho sete di scrivere una risposta qui. Per favore non sentirti inibito dal farlo tu stesso! –

risposta

5

Nel caso in cui il controllo da cui si desidera reindirizzare gli eventi del mouse non sarà in tutta la sua area client all'interno il controllo a cui devono essere reindirizzati quegli eventi (come hai mostrato nel vostro aggiornamento domanda), quindi il messaggio WM_NCHITTEST potrebbe essere inviato a un altro controllo. Quindi rimane l'unico modo per usare IMHO, reindirizzare tutti i messaggi del mouse.

Come @David menzionato nel suo commento, è possibile eseguire il reindirizzamento di questo messaggio in modo globale scrivendo un gestore di eventi per l'evento OnMessage per TApplication. O utilizzare un oggetto TApplicationEvents.

Nell'esempio seguente, è possibile definire l'intervallo di messaggi, che verranno reindirizzati e specificare l'elenco di controlli di origine e destinazione per tale reindirizzamento. Per il reindirizzamento viene utilizzato l'evento OnMessage dell'oggetto TApplication, ma poiché il destinatario è in questo caso il discendente , è possibile non solo modificare il destinatario del messaggio in arrivo, ma è necessario mangiare questo messaggio ed eseguire il messaggio sul target controllare da solo il metodo Perform.

Ecco il codice che mostra come reindirizzare tutti i messaggi del mouse da Panel1 a Image1. È possibile ottenere l'intero progetto di test from here se si desidera:

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

Dimenticato di notare, a causa del 'Break' utilizzato nel ciclo di ricerca del controllo del codice sorgente (quando il controllo viene trovato nell'elenco di reindirizzamento), è possibile specificare un solo reindirizzamento per un controllo sorgente. Tutti gli altri saranno ignorati. Se togli quel 'Break', sarai in grado di * trasmettere * il messaggio a più di un controllo target. – TLama

5

È possibile derivare la classe del pannello per gestire i messaggi WM_NCHITTEST per restituire HTTRANSPARENT per la regione in cui si desidera che il controllo sotto il pannello riceva i messaggi del mouse. Es .:

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; 

Ovviamente questo è solo un test, è possibile pubblicare un campo nel componente per poter risolvere in cui il controllo risiede ecc ..

7

maniglia wm_NCHitTest messaggi inviati al pannello e tornare htTransparent. Il sistema operativo invierà il messaggio del mouse al successivo controllo senza ulteriori elaborazioni richieste dal programma. (Dal punto di vista dell'OS, il "prossimo controllo verso il basso" è il controllo genitore sia del pannello che dell'immagine, il VCL si occupa di indirizzare il messaggio del mouse verso il controllo dell'immagine, come fa con tutti i discendenti , poiché non sono 't vero finestrato controlli)

Qualcosa di simile a questo:.

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

assegnare tale metodo per il metodo WindowProc del pannello. Memorizza il valore precedente della proprietà in un campo del modulo.

var 
    FPrevPanelWindowProc: TWndMethod; 

FPrevPanelWindowProc := Panel.WindowProc; 
Panel.WindowProc := Self.PanelWindowProc; 
Problemi correlati