【问题标题】:Display a ToolTip hint on a disabled menu item of a popup menu在弹出菜单的禁用菜单项上显示 ToolTip 提示
【发布时间】:2009-01-22 20:37:32
【问题描述】:

所以我有一个 TMenuItem 附加到 TDBGrid 的 TPopupMenu 上的 TAction(实际上是第 3 方,但你明白了)。根据网格中选定的行,启用或禁用 TAction。我想要的是能够向用户显示一个提示,解释为什么该项目被禁用。

至于为什么我想要一个禁用菜单项的提示,让我们说我是in agreement with Joel

所有的 TMenuItem 都有一个提示属性,但我可以说它们仅用于 TApplicationEvent.OnHint 事件处理程序将提示粘贴到 TStatusBar 或其他一些特殊处理中。我found an article 关于如何为 TMainMenu 的 TMenuItems 创建自己的偶数窗口,但它不适用于 TPopupMenu 的 TMenuItem。它通过处理 WM_MENUSELECT 消息来工作,据我所知,它不是在 TPopupMenu 上发送的。

【问题讨论】:

  • 如果你真的想为用户提供最好的体验,你应该重新考虑。状态栏帮助(立即出现)和提示窗口之间的可发现性存在重要差异。用户看不到的帮助文本并没有真正的帮助。特别是如果它是非标准的。
  • 我同意,在一定程度上。大多数用户希望得到他们眼睛所在位置的提示:通过鼠标指针。我使用了只使用状态栏提示的应用程序,不得不寻找提示很烦人。此应用程序在数百个表单中的任何一个上都没有使用状态栏提示,我希望保持一致。

标签: delphi user-interface delphi-2007 tooltip


【解决方案1】:

WM_MENUSELECT 确实也用于弹出菜单中的菜单项,但不是由包含(弹出)菜单的窗体的 windows proc 处理,而是由 Menus.PopupList 创建的不可见帮助窗口处理。幸运的是,您可以(至少在 Delphi 5 下)通过 Menus.PopupList.Window 获得这个 HWND。

现在您可以使用老式方法对窗口进行子类化,例如在此CodeGear article 中描述的那样,也可以为弹出菜单处理 WM_MENUSELECT。 HWND在第一个TPopupMenu创建后到最后一个TPopupMenu对象销毁前有效。

对问题中链接文章中的演示应用进行快速测试,应该会发现这是否可行。

编辑:确实有效。我更改了the linked example 以显示弹出菜单的提示。步骤如下:

为表单添加一个 OnDestroy 的处理程序、一个旧窗口 proc 的成员变量和一个新窗口 proc 的方法:

TForm1 = class(TForm)
  ...
  procedure FormCreate(Sender: TObject);
  procedure FormDestroy(Sender: TObject);
  procedure ApplicationEvents1Hint(Sender: TObject);
private
  miHint : TMenuItemHint;
  fOldWndProc: TFarProc;
  procedure WMMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;
  procedure PopupListWndProc(var AMsg: TMessage);
end;

改变窗体的 OnCreate 处理程序以子类化隐藏的 PopupList 窗口,并在 OnDestroy 处理程序中实现窗口 proc 的正确恢复:

procedure TForm1.FormCreate(Sender: TObject);
var
  NewWndProc: TFarProc;
begin
  miHint := TMenuItemHint.Create(self);

  NewWndProc := MakeObjectInstance(PopupListWndProc);
  fOldWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
    integer(NewWndProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  NewWndProc: TFarProc;
begin
  NewWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
    integer(fOldWndProc)));
  FreeObjectInstance(NewWndProc);
end;

实现子类窗口过程:

procedure TForm1.PopupListWndProc(var AMsg: TMessage);

  function FindItemForCommand(APopupMenu: TPopupMenu;
    const AMenuMsg: TWMMenuSelect): TMenuItem;
  var
    SubMenu: HMENU;
  begin
    Assert(APopupMenu <> nil);
    // menuitem
    Result := APopupMenu.FindItem(AMenuMsg.IDItem, fkCommand);
    if Result = nil then begin
      // submenu
      SubMenu := GetSubMenu(AMenuMsg.Menu, AMenuMsg.IDItem);
      if SubMenu <> 0 then
        Result := APopupMenu.FindItem(SubMenu, fkHandle);
    end;
  end;

var
  Msg: TWMMenuSelect;
  menuItem: TMenuItem;
  MenuIndex: integer;
begin
  AMsg.Result := CallWindowProc(fOldWndProc, Menus.PopupList.Window,
    AMsg.Msg, AMsg.WParam, AMsg.LParam);
  if AMsg.Msg = WM_MENUSELECT then begin
    menuItem := nil;
    Msg := TWMMenuSelect(AMsg);
    if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then begin
      for MenuIndex := 0 to PopupList.Count - 1 do begin
        menuItem := FindItemForCommand(PopupList.Items[MenuIndex], Msg);
        if menuItem <> nil then
          break;
      end;
    end;
    miHint.DoActivateHint(menuItem);
  end;
end;

对循环中的所有弹出菜单执行此操作,直到找到第一个匹配项或子菜单。

【讨论】:

  • 我喜欢你的坚韧。我会对此进行测试并通知您。
  • 在 PopupListWndProc 中,如果 POPUPMENU MenuItem 有子菜单,则 FindItem 不起作用。 (它在主菜单中工作)。因此,我添加了此代码以使其适用于所有菜单项: menuItem := nil;消息 := TWMMenuSelect(AMsg);如果 (Msg.MenuFlag $FFFF) 或 (Msg.IDItem 0) 然后开始 menuItem := PopupMenu1.FindItem(Msg.IDItem, fkCommand);如果未分配(menuItem),则开始 SubMenu := GetSubMenu(HMENU(AMsg.LParam), { IDItem } LOWORD(AMsg.WParam)); menuItem := PopupMenu1.FindItem(SubMenu, fkHandle);结尾;结尾; miHint.DoActivateHint(menuItem);
  • 你还要声明:SubMenu: HMENU;
  • @mghie - 我已经实现了上面的代码并添加了 popupmenu2。当我将鼠标悬停在 popupmenu2 上时,它会显示一个提示。但是,显示的提示属于 popupmenu1。
  • @CapeCodGunny:确实,请使用附加行再试一次。它似乎在这里工作,现在......
【解决方案2】:

不确定它是否有帮助,但我创建了自己的多行提示窗口(用于 Delphi7),以便能够显示多于一行的文本。 它是开源的,你可以找到它here

在屏幕上的正确位置显示它需要做一些工作,但你可以完全控制它。

【讨论】:

  • 是的,我考虑过,但我有点希望有一种方法不涉及管理和移动我自己的提示窗口。谢谢!
  • 我希望有一个多行提示,然后写了我自己的 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
相关资源
最近更新 更多