如何突出显示特定的弹出菜单项?

How to HIGHLIGHT a specific popup menu item?

我有这个弹出菜单:

object pmTest: TPopupMenu
  OnPopup = pmTestPopup
  Left = 32
  Top = 24
  object mTest1: TMenuItem
    Caption = 'Test 1'
    OnClick = mTest1Click
  end
  object mTest2: TMenuItem
    Caption = 'Test 2'
    OnClick = mTest2Click
  end
end

在使用 Popup 方法显示弹出菜单后,我需要以编程方式突出显示(而不是单击!)特定菜单项,所以我尝试了这个:

Winapi.Windows.HiliteMenuItem(pmTest.WindowHandle, pmTest.Handle, 1, MF_BYPOSITION or MF_HILITE);

但是不行。

如何以编程方式突出显示特定的弹出菜单项?

默认调用TPopupMenu.WindowHandle property is set to the Application.Handle window, not to the private HWND that TPopupMenu actually uses internally to actually dispatch its WM_COMMAND messages to. That window is established when the TPopupMenu.Popup()方法,不更新TPopupMenu.WindowHandle属性.

尝试在 Vcl.Menus 单元中使用 TPopupList.Window property instead of the TPopupMenu.WindowHandle property for the HWND to pass to HiliteMenuItem(). There is a global PopupList 对象:

procedure TMyForm.pmTestPopup(Sender: TObject);
begin
  Winapi.Windows.HiliteMenuItem({pmTest.WindowHandle}PopupList.Window, pmTest.Handle, 1, MF_BYPOSITION or MF_HILITE);
end;

如果仍然无效,请尝试使用 Win32 SetMenuItemInfo() 函数,该函数不需要 HWND 作为输入:

procedure TMyForm.pmTestPopup(Sender: TObject);
var
  mii: MENUITEMINFO;
begin
  ZeroMemory(@mii, sizeof(mii));
  mii.cbSize := sizeof(mii);
  mii.fMask := MIIM_STATE;
  mii.fState := MFS_HILITE;

  Winapi.Windows.SetMenuItemInfoW(pmTest.Handle, 1, True, mii);
end;

更新:经过进一步审查,TPopupMenu.OnPopup 事件在菜单可见之前触发,TPopupMenu 可能会在 [= 之后重新创建菜单30=] 已被调用,并且在菜单实际显示之前。因此,您最好的选择可能是 subclass the TPopupList window so you can intercept the WM_ENTERMENULOOP 消息,然后自定义您的菜单项。例如:

type
  TPopupListEx = class(TPopupList)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

procedure TPopupListEx.WndProc(var Message: TMessage);
begin
  inherited;
  if (Message.Msg = WM_ENTERMENULOOP) and (Message.WParam = 1) then
  begin
    // customize pmTest items as needed...
  end;
end;

initialization
  Popuplist.Free; //free the "default", "old" list
  PopupList := TPopupListEx.Create; //create the new one
  // The new PopupList will be freed by
  // finalization section of Menus unit.
end.