ポップアップメニューの無効なメニュー項目にツールヒントヒントを表示する
-
19-08-2019 - |
質問
だから、TDBGridのTPopupMenuのTActionにTMenuItemをアタッチしました(実際にはサードパーティですが、アイデアは得られます)。グリッドで選択された行に基づいて、TActionは有効または無効になります。欲しいのは、アイテムが無効になっている理由を説明するヒントをユーザーに表示できるようにすることです。
無効なメニュー項目のヒントが必要な理由に関しては、ジョエルと同意。
すべてのTMenuItemにはヒントプロパティがありますが、TApplicationBarまたはその他の特別な処理にヒントを固定するためにTApplicationEvent.OnHintイベントハンドラーのみが使用されていることがわかります。独自の偶数ウィンドウを作成する方法に関する記事を見つけました TMainMenuのTMenuItems。ただし、TPopupMenuのTMenuItemでは機能しません。 WM_MENUSELECTメッセージを処理することで機能しますが、私が知る限り、TPopupMenuでは送信されません。
解決
WM_MENUSELECTは、ポップアップメニューのメニュー項目でも実際に処理されますが、(ポップアップ)メニューを含むフォームのウィンドウプロシージャではなく、Menus.PopupListによって作成された非表示のヘルパーウィンドウによって処理されます。幸いなことに(少なくともDelphi 5では)Menus.PopupList.WindowからこのHWNDにアクセスできます。
この CodeGearの例で説明されているように、今では昔ながらの方法でウィンドウをサブクラス化できます。記事、ポップアップメニューでもWM_MENUSELECTを処理します。 HWNDは、最初のTPopupMenuが作成されてから最後のTPopupMenuオブジェクトが破棄されるまでの間に有効になります。
質問のリンクされた記事にあるデモアプリを使用した簡単なテストにより、これが機能するかどうかが明らかになります。
編集:実際に機能します。 リンクされた例を変更して、ポップアップメニューにもヒントを表示しました。手順は次のとおりです。
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;
これは、最初の一致するアイテムまたはサブメニューが見つかるまで、ループ内のすべてのポップアップメニューに対して実行されます。
他のヒント
それが役立つかどうかはわかりませんが、1行以上のテキストを表示できるように、独自の複数行ヒントウィンドウ(Delphi7用)を作成しました。 オープンソースであり、ここ。
画面の適切な場所に表示する作業がいくつかありますが、完全に制御できます。