WM_MENUSELECT确实也是为弹出式菜单中的菜单项处理,但不是由包含(弹出)菜单的窗体的窗口过程处理,而是由Menus.PopupList创建的不可见帮助窗口处理。幸运的是,你可以(至少在Delphi 5下)通过Menus.PopupList.Window得到这个HWND。
现在,您可以使用老式的方法对窗口进行子类化处理(例如在CodeGear article中所述),以处理WM_MENUSELECT也用于弹出式菜单。 HWND将在创建第一个TPopupMenu之后到最后一个TPopupMenu对象被销毁之前有效。
在问题中的链接文章中使用演示应用程序进行快速测试应该能够揭示这是否会起作用。
编辑:它确实工作。我更改了the linked example以显示弹出式菜单的提示。以下是具体步骤:
添加处理程序的OnDestroy,旧的窗口过程的一个成员变量和新的窗口过程到窗体的方法:
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处理程序正确恢复:
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;
这是针对循环中的所有弹出菜单完成的,直到找到第一个匹配项目或子菜单。
如果你真的想为用户提供最好的体验,你应该重新考虑。状态栏帮助(立即出现)和提示窗口之间的可发现性存在重要差异。用户不会看到的帮助文本并不真正有用。特别是如果它是非标准的。 – mghie 2009-01-22 21:38:48
我同意,在一定程度上。大多数用户希望提示他们的眼睛是:通过鼠标指针。我已经使用了仅使用状态栏提示的应用程序,并且非常烦恼必须查找提示。 此应用程序在任何数百形式的不使用状态栏提示,我想是一致的。 – 2009-01-22 21:48:10