如何将鼠标事件重定向到另一个控件?

问题描述 投票:0回答:3

我有一种情况,我有一个

TImage
,上面有一个
TPanel
部分覆盖它,并且它们共享同一个父级:

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

Panel1 正在接收鼠标按下/移动/向上事件并对其进行处理(Image1 也是如此),但在某些情况下,我想将鼠标按下消息“重定向”到 Image1,就好像模拟单击了 Image1 而不是 Panel1。

这就是我所做的:

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;

它按预期工作,但我不确定这是正确的方法。
我的问题是,我这样做对吗?有更好的或推荐的方法吗?


更新(1): 按照建议处理

WM_NCHITTEST
是一个有效的答案,我也考虑过。即使将
Panel1.Enabled
设置为
False
也会将鼠标消息路由到底层 Image1 控件。

但是(!)考虑这种情况,我单击面板上的

x
位置,但仍然需要将消息路由到 Image1:

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

我的方法有效,但

WM_NCHITTEST
不适用于所描述的场景。我仍然没有得到我的方法是否有效的答案。 (或者也许我应该针对上述场景问另一个问题?)

delphi delphi-7
3个回答
8
投票

处理发送到面板的

wm_NCHitTest
消息并返回
htTransparent
。操作系统会将鼠标消息发送到下一个控件,而无需程序进行任何进一步处理。 (从操作系统的角度来看,“下一个控件”是面板和图像的父控件;VCL 负责将鼠标消息路由回图像控件,就像它对所有
TGraphicControl
后代所做的那样,因为它们不是真正的窗口控件。)

类似这样的:

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

将该方法分配给面板的 WindowProc 方法。将属性的先前值存储在表单的字段中。

var
  FPrevPanelWindowProc: TWndMethod;

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

6
投票

如果您要重定向鼠标事件的控件不在其整个客户区域内部这些事件应重定向到的控件(如您在问题更新中所示),则

WM_NCHITTEST 
消息可能会发送到另一个控件。那么唯一的一种方法仍然是使用恕我直言,重定向所有鼠标消息。

正如 @David 在评论中提到的,您可以通过为

OnMessage
事件编写事件处理程序来以全局方式执行此消息重定向。或者使用
TApplication
对象。
在以下示例中,您可以定义将重定向的消息范围,以及指定该重定向的源控件和目标控件的列表。对于重定向,使用 
TApplicationEvents

对象的

OnMessage
事件,但由于您的目标在这种情况下是 TApplication
 后代,因此您不仅可以更改传入消息的收件人,而且还必须吃掉这个消息,自己通过 
TGraphicControl
 方法在目标控件上执行该消息。

这里的代码显示了如何将所有鼠标消息从 
Perform
重定向到

Panel1

。如果您愿意,您可以获得整个测试项目

Image1


from here

您可以派生面板类来处理 
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.

5
投票
WM_NCHITTEST

。例如:


HTTRANSPARENT

显然这只是一个测试,您可以在组件中发布一个字段以供其解析该控件所在的位置等..

© www.soinside.com 2019 - 2024. All rights reserved.