You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi VCL是否有HitTest属性?无该属性时如何实现相同功能?

VCL中实现类似FMX HitTest的功能

VCL原生没有和FMX完全一致的HitTest属性,但可以通过两种常用方式实现相同效果——禁用组件的鼠标事件响应,让鼠标操作“穿透”到下层组件。

方法一:子类化组件,拦截鼠标消息

通过自定义组件重写WndProc方法,过滤鼠标相关消息,同时保留对HitTest属性的控制权。以下是适配Delphi 12的代码示例:

unit VCLHitTest;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls;

type
  TCustomButtonWithHitTest = class(TButton)
  private
    FHitTest: Boolean;
    procedure SetHitTest(Value: Boolean);
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    property HitTest: Boolean read FHitTest write SetHitTest default True;
  end;

implementation

{ TCustomButtonWithHitTest }

procedure TCustomButtonWithHitTest.SetHitTest(Value: Boolean);
begin
  if FHitTest <> Value then
  begin
    FHitTest := Value;
    if not FHitTest then
      ReleaseCapture; // 释放可能捕获的鼠标,避免残留状态
  end;
end;

procedure TCustomButtonWithHitTest.WndProc(var Message: TMessage);
const
  // 定义需要拦截的鼠标消息集合
  MouseMessages = [WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK,
                   WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK,
                   WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK,
                   WM_MOUSEWHEEL, WM_MOUSEHWHEEL];
begin
  if (not FHitTest) and (Message.Msg in MouseMessages) then
  begin
    // 标记消息需要穿透,让下层控件接收
    Perform(WM_NCHITTEST, Message.WParam, Message.LParam);
    if Message.Result = HTTRANSPARENT then
      inherited WndProc(Message)
    else
      DefWindowProc(Handle, Message.Msg, Message.WParam, Message.LParam);
    Message.Result := 0;
  end
  else
    inherited WndProc(Message);
end;

initialization
  RegisterClass(TCustomButtonWithHitTest);

end.

使用方式

  1. 将上述单元添加到Delphi项目中
  2. 在窗体设计器中,通过“New Component”选择TCustomButtonWithHitTest添加到窗体
  3. 设置组件的HitTest属性为False,即可禁用其鼠标事件响应

方法二:动态修改窗口扩展风格

通过Windows API直接设置控件的窗口扩展风格WS_EX_TRANSPARENT,快速实现鼠标穿透效果,无需子类化组件:

procedure SetControlHitTest(AControl: TWinControl; Enabled: Boolean);
var
  ExStyle: DWORD;
begin
  if not Assigned(AControl) then Exit;

  ExStyle := GetWindowLongPtr(AControl.Handle, GWL_EXSTYLE);
  if Enabled then
    // 移除透明风格,恢复鼠标事件
    ExStyle := ExStyle and not WS_EX_TRANSPARENT
  else
    // 添加透明风格,让鼠标操作穿透
    ExStyle := ExStyle or WS_EX_TRANSPARENT;
  SetWindowLongPtr(AControl.Handle, GWL_EXSTYLE, ExStyle);
  // 刷新控件状态,确保风格生效
  AControl.Perform(WM_CHANGEUISTATE, UIS_INITIALIZE, 0);
end;

使用方式

在代码中直接调用函数,例如:

// 禁用Button1的鼠标事件
SetControlHitTest(Button1, False);
// 恢复Button1的鼠标事件
SetControlHitTest(Button1, True);

两种方法的区别

  • 子类化方式:更灵活,可精确控制拦截的消息类型,适合需要自定义逻辑的场景
  • 窗口风格修改方式:实现简单,代码量少,但会让组件完全不接收所有鼠标相关消息,适合快速实现穿透需求

内容的提问来源于stack exchange,提问作者Martin Kakhuis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:38:36