delphi 如何在不打开菜单的情况下在MouseOver上显示第一级TMainMenu项的提示?

bqf10yzr  于 2022-11-04  发布在  SEO
关注(0)|答案(2)|浏览(174)

在 Delphi 11 Alexandria的Windows 10中的一个32位VCL应用程序中,我有一个TMainMenu,每个菜单项上都有一个提示:

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

当我将鼠标指针悬停在“文件”菜单项上时,没有应用程序提示!只有在打开文件菜单后,当我将鼠标指针悬停在“文件”菜单项上时,我确实得到了一个Application.Hint
那么,当我将鼠标指针悬停在mFile菜单项上而不打开菜单时,如何获得通知呢?

xwbd5t1u

xwbd5t1u1#

如果您只想对鼠标光标移动(而不是键盘输入)做出React,则处理WM_NCMOUSEMOVE消息:

interface

  TfrmMain= class( TForm )
    mnuMain: TMainMenu;  // The menu, containing at least one top item
  protected
    procedure WmNcMouseMove(var vMsg: TWMNCMouseMove); message WM_NCMOUSEMOVE;
  end;

implementation

procedure TfrmMain.WmNcMouseMove(var vMsg: TWMNCMouseMove);
var
  iItem: Integer;
  vR: TRect;
  vP: TPoint;
  oItem: TMenuItem;
begin
  inherited;

  // Only react to menu related mouse cursor moves
  if vMsg.HitTest= HTMENU then begin
    oItem:= nil;  // Not found yet

    for iItem:= 0 to self.mnuMain.Items.Count- 1 do begin  // All topmost items
      if GetMenuItemRect( self.Handle, self.mnuMain.Handle, iItem, vR ) then begin
        // Also checking Y is needed, as a menu can have more than 1 line.
        // Consider sizing your window width to the minimum to see this effect.
        vP.X:= vMsg.XCursor;
        vP.Y:= vMsg.YCursor;
        if PtInRect( vR, vP ) then begin  // X>= left< right; Y>= top< bottom
          oItem:= self.mnuMain.Items[iItem];  // Found the item under the mouse cursor
          break;
        end;
      end else break;  // Makes no sense to continue on any error
    end;

    // Now get the .hint or otherwise display empty text
    if oItem<> nil then self.Caption:= oItem.Hint else self.Caption:= '';
  end else self.Caption:= '';  // Any other NC area should reset text, too
end;
  • 在Windows 7 x64上使用 Delphi 7成功测试,禁用主题(Windows 95外观),只有1个监视器,菜单有1行甚至2行(非常短的窗口宽度):将鼠标光标悬停在菜单项上而不单击它们将显示正确的提示。
  • 也许跨多个显示器的桌面需要额外的工作。
  • 您的代码(TApplicationEvents.OnHint)已经涵盖了键盘输入(Alt或F10)。
  • Delphi: Menu Hint bug中发现GetMenuItemRect()

和X1 E3 F1 X中的X1 M3 N1 X。

  • PtInRect()精确比较X和Y:X的>=在矩形的左边,但X的<在右边(不是<=); Y也一样。只有左/上包含在内,而右/下不包含在内。
hkmswyz6

hkmswyz62#

我改变了你想法的逻辑,这太棒了!

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, Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    mFile: TMenuItem;
    mEdit: TMenuItem;
    mOpen: TMenuItem;
    StatusBar1: TStatusBar;
    ApplicationEvents1: TApplicationEvents;
    procedure ApplicationEvents1Hint(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
    procedure WmNcMouseMove(var vMsg: TWMNCMouseMove); message WM_NCMOUSEMOVE;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  CodeSiteLogging;

procedure TForm1.ApplicationEvents1Hint(Sender: TObject);
begin
  StatusBar1.SimpleText := Application.Hint;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  StatusBar1.SimpleText := '';
end;

procedure TForm1.WmNcMouseMove(var vMsg: TWMNCMouseMove);
var
  iItem: Integer;
  vR: TRect;
  oItem: TMenuItem;
begin
  inherited;

  StatusBar1.SimpleText := '';

  if vMsg.HitTest = HTMENU then
  begin
    oItem := nil;

    for iItem := 0 to Self.MainMenu1.Items.Count - 1 do
    begin
      if GetMenuItemRect(Self.Handle, Self.MainMenu1.Handle, iItem, vR) then
      begin
        if (vMsg.XCursor >= vR.Left) and (vMsg.XCursor <= vR.Right) and (vMsg.YCursor >= vR.Top) and (vMsg.YCursor <= vR.Bottom) then
        begin
          oItem := Self.MainMenu1.Items[iItem];
          BREAK;
        end;
      end
      else
        BREAK;
    end;

    if Assigned(oItem) then
      StatusBar1.SimpleText := oItem.Hint;
  end;
end;

end.

你觉得呢?

相关问题