2012-12-13 10 views
6

Ich habe eine Situation, wo ich eine TPanel davon TImage und obendrein haben bedeckt sie teilweise und sie die gleichen Eltern teilen:Wie umleiten Mausereignisse an ein anderes Steuerelement?

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

Panel1 empfängt die Maus nach unten/bewegen/Veranstaltungen und Verarbeitung (so tut Image1), aber in einigen Situationen möchte ich die Maus-down-Nachricht zu Image1 "umleiten", als ob man simulieren würde, dass Image1 anstatt Panel1 angeklickt wurde.

Hier ist, was ich getan habe:

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; 

Es funktioniert wie erwartet, aber ich bin nicht sicher, es ist der richtige Weg.
Meine Frage ist, mache ich es richtig? Gibt es einen besseren oder empfohlenen Weg?


Update (1): Handhabung WM_NCHITTEST wie vorgeschlagen eine gültige Antwort und ich dachte darüber auch. Wenn Sie Panel1.Enabled auf False setzen, werden die Mausnachrichten an das zugrunde liegende Image1-Steuerelement weitergeleitet.

Aber diese Situation betrachtet, in dem ich die x Lage auf dem Panel klicken und muß noch die Nachricht an Image1 zu routen (!):

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

Meine Methode funktioniert, aber WM_NCHITTEST ist nicht anwendbar in dem beschriebenen Szenario . Ich habe immer noch keine Antwort bekommen, ob meine Methode gültig ist oder nicht. (oder vielleicht sollte ich eine andere Frage mit dem obigen Szenario?)

+0

Ich würde sagen, dass die beste Wahl dieses bei der Nachrichtenschleife Ebene zu tun ist. Implementieren Sie einen 'OnMessage'-Handler für' TApplication'. Oder machen Sie das gleiche mit einem TApplicationEvents-Objekt. Dort können Sie den Zielfensterpunkt der Nachrichten von Interesse ändern. –

+1

@David, es ist nicht auf eine erste Sicht von dieser Frage sichtbar, aber OP möchte wirklich Nachrichten umleiten. Also, das ist der Weg zu gehen. – TLama

+0

@TLama Ich habe keinen Durst, hier eine Antwort zu schreiben. Bitte fühlen Sie sich nicht davon abgehalten, dies selbst zu tun! –

Antwort

5

Im Fall, wenn die Steuerung von dem aus Sie Mausereignisse umleiten möchten nicht sein wird in seiner gesamten Client-Bereich innerhalb die Steuerung, an die die Ereignisse umgeleitet werden (wie Sie in Ihrer Frage Update gezeigt haben), dann könnte die Nachricht WM_NCHITTEST an ein anderes Steuerelement gesendet werden. Dann bleibt der einzige Weg, IMHO zu verwenden, alle Mausnachrichten umzuleiten.

Wie @David in seinem Kommentar erwähnt, können Sie diese Nachrichtenumleitung auf eine globale Weise tun, indem Sie einen Ereignishandler für das Ereignis OnMessage für TApplication schreiben. Oder verwenden Sie ein Objekt TApplicationEvents.

Im folgenden Beispiel können Sie den Bereich der Nachrichten definieren, die umgeleitet werden, sowie die Liste der Quellen- und Zielsteuerelemente für diese Umleitung angeben. Für die Weiterleitung wird das OnMessage Ereignis des TApplication Objekts verwendet, aber da Ihr Ziel in diesem Fall TGraphicControl ist, können Sie nicht nur den Empfänger der eingehenden Nachricht ändern, sondern Sie müssen diese Nachricht essen und die Nachricht auf dem Ziel ausführen Kontrolle durch die Perform Methode selbst.

Hier ist der Code, der zeigt, wie alle Mausnachrichten von Panel1 zu Image1 umgeleitet werden. Sie können das gesamte Testprojekt erhalten from here wenn Sie wollen:

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

Aufgrund der in der Suchschleife für die Quellcodeverwaltung verwendeten 'Unterbrechung' wurde vergessen, dies zu beachten (wenn das Steuerelement in der Umleitungsliste gefunden wird), kann nur eine Umleitung für eine Quellcodeverwaltung angegeben werden. Alle anderen werden ignoriert. Wenn Sie diese "Pause" entfernen, können Sie die Nachricht an mehr als ein Ziel-Steuerelement senden. – TLama

5

Sie können Ihre Panel-Klasse ableiten, WM_NCHITTEST Nachrichten HTTRANSPARENT für die Region, die das Steuerelement unter dem Panel erhalten Mausmeldungen zurückgeben. Z.B .:

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; 

Natürlich ist dies nur ein Test, können Sie ein Feld in Ihrer Komponente veröffentlichen für sie zu beheben, dass die Kontrolle etc befindet ..

7

Handle wm_NCHitTest Nachrichten an das Panel gesendet und htTransparent zurückzukehren. Das Betriebssystem sendet die Mausnachricht an die nächste Steuerung, ohne dass eine weitere Verarbeitung von Ihrem Programm erforderlich ist. (Aus Sicht des Betriebssystems ist die "nächste Steuerung nach unten" das übergeordnete Steuerelement sowohl des Steuerfelds als auch des Bilds. Die VCL sorgt dafür, dass die Mausnachricht wie bei allen TGraphicControl Nachkommen an das Bildsteuerelement weitergeleitet wird, da sie nicht vorhanden sind ‚t echte Fenster Kontrollen)

Etwas wie folgt aus:.

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

Weisen Sie diese Methode der WindowProc Methode des Panels. Speichern Sie den vorherigen Wert der Eigenschaft in einem Feld des Formulars.

var 
    FPrevPanelWindowProc: TWndMethod; 

FPrevPanelWindowProc := Panel.WindowProc; 
Panel.WindowProc := Self.PanelWindowProc; 
Verwandte Themen