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

Delphi11中未展开菜单时如何显示一级TMainMenu项悬停提示

问题场景

在Windows 10系统下使用Delphi 11 Alexandria开发32位VCL应用程序时,为TMainMenu控件的每个菜单项都设置了Hint属性,设计时界面如下:
菜单项Hint设置界面

核心单元代码如下:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.AppEvnts;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    mFile: TMenuItem;
    mEdit: TMenuItem;
    mOpen: TMenuItem;
    ApplicationEvents1: TApplicationEvents;
    procedure ApplicationEvents1Hint(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  CodeSiteLogging;

procedure TForm1.ApplicationEvents1Hint(Sender: TObject);
begin
  CodeSite.Send('TForm1.ApplicationEvents1Hint: Application.Hint', Application.Hint);
end;

end.

对应DFM窗体设计代码如下:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 366
  ClientWidth = 639
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -15
  Font.Name = 'Segoe UI'
  Font.Style = []
  Menu = MainMenu1
  Position = poScreenCenter
  ShowHint = True
  PixelsPerInch = 120
  TextHeight = 20
  object MainMenu1: TMainMenu
    Left = 248
    Top = 144
    object mFile: TMenuItem
      Caption = 'File'
      Hint = 'Click here to open the File menu'
      object mOpen: TMenuItem
        Caption = 'Open'
        Hint = 'Click here to open a File'
      end
    end
    object mEdit: TMenuItem
      Caption = 'Edit'
      Hint = 'Click here to open the Edit menu'
    end
  end
  object ApplicationEvents1: TApplicationEvents
    OnHint = ApplicationEvents1Hint
    Left = 248
    Top = 160
  end
end

异常表现

鼠标指针悬停在菜单栏未展开的一级菜单项(比如mFile、mEdit)上时,不会触发Application.OnHint事件,无任何提示输出;只有展开对应菜单后,鼠标悬停在下拉列表内的菜单项上,才能正常获取到Application.Hint的值。
需求为:不展开菜单的前提下,鼠标悬停在mFile这类一级TMainMenu菜单项时,可接收到悬停通知并显示对应提示。


解决方案

VCL默认的菜单Hint处理逻辑仅覆盖展开后的下拉菜单项,菜单栏上的顶层菜单项属于窗口非客户区绘制的系统菜单部分,不会走常规的控件Hint触发流程,需要拦截菜单相关Windows消息手动处理,操作步骤如下:

  • 在窗体类中声明消息处理方法,拦截WM_MENUSELECT消息,该消息会在鼠标悬停在任意菜单项(包括未展开的顶层菜单项)时触发
  • 从消息参数中解析当前悬停的菜单项索引、对应菜单句柄,匹配到绑定的TMenuItem实例
  • 手动将菜单项的Hint赋值给Application.Hint,触发原有OnHint事件逻辑即可

完整实现代码如下:
首先在窗体类的私有部分添加消息方法声明:

private
  procedure WMMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;

然后实现该消息处理方法:

procedure TForm1.WMMenuSelect(var Msg: TWMMenuSelect);
var
  LMenu: HMENU;
  LItem: TMenuItem;
  I: Integer;
begin
  inherited;
  // 处理菜单关闭、无效选中的场景,清除残留提示
  if (Msg.MenuFlag = $FFFF) and (Msg.IDItem = 0) then
  begin
    Application.CancelHint;
    Exit;
  end;

  LItem := nil;
  // 区分顶层菜单栏菜单项和下拉子菜单项
  if Msg.MenuFlag and MF_POPUP <> 0 then
  begin
    LMenu := Msg.Menu;
    // 遍历主菜单顶层项,通过子菜单句柄匹配当前悬停的菜单项
    for I := 0 to MainMenu1.Items.Count - 1 do
    begin
      if GetSubMenu(LMenu, I) = Msg.SubMenu then
      begin
        LItem := MainMenu1.Items[I];
        Break;
      end;
    end;
  end
  else
  begin
    // 下拉子菜单项直接通过命令ID查找
    LItem := MainMenu1.FindItem(Msg.IDItem, fkCommand);
  end;

  // 匹配到菜单项且配置了Hint时,触发提示逻辑
  if Assigned(LItem) and (LItem.Hint <> '') then
  begin
    Application.CancelHint;
    Application.Hint := LItem.Hint;
  end
  else
  begin
    Application.CancelHint;
  end;
end;

添加上述代码后无需修改原有ApplicationEvents1Hint的业务逻辑,鼠标悬停在未展开的顶层菜单项上时,即可正常触发Hint事件,输出并显示对应提示内容。

注意:如果使用了VCL样式或自定义菜单栏绘制,需确认WM_MENUSELECT消息未被自定义逻辑拦截,该方案在原生VCL菜单、Windows 10/11系统下兼容Delphi 10.x及之后的所有版本。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:54:20