我有一种情况,我有一个
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
不适用于所描述的场景。我仍然没有得到我的方法是否有效的答案。 (或者也许我应该针对上述场景问另一个问题?)
处理发送到面板的
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;
如果您要重定向鼠标事件的控件不在其整个客户区域内部这些事件应重定向到的控件(如您在问题更新中所示),则
WM_NCHITTEST
消息可能会发送到另一个控件。那么唯一的一种方法仍然是使用恕我直言,重定向所有鼠标消息。
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.
WM_NCHITTEST
。例如:
HTTRANSPARENT
显然这只是一个测试,您可以在组件中发布一个字段以供其解析该控件所在的位置等..