6

したがって、TDBGrid の TPopupMenu の TAction に TMenuItem がアタッチされています (実際にはサード パーティですが、アイデアはわかります)。グリッドで選択した行に基づいて、TAction が有効または無効になります。私が欲しいのは、アイテムが無効になっている理由を説明するヒントをユーザーに表示できるようにすることです。

無効なメニュー項目に関するヒントが必要な理由については、 Joel に同意しているとだけ言っておきましょう。

すべての TMenuItem にはヒント プロパティがありますが、TApplicationEvent.OnHint イベント ハンドラを使用してヒントを TStatusBar またはその他の特別な処理に貼り付けるだけであることがわかります。TMainMenu の TMenuItem 用に独自の偶数ウィンドウを作成する方法に関する記事を見つけましたが、TPopupMenu の TMenuItem では機能しません。これは WM_MENUSELECT メッセージを処理することで機能しますが、これは私が知る限り TPopupMenu では送信されません。

4

2 に答える 2

6

WM_MENUSELECT は、ポップアップ メニューのメニュー項目に対しても実際に処理されますが、(ポップアップ) メニューを含むフォームの Windows プロシージャではなく、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;

非表示の PopupList ウィンドウをサブクラス化するようにフォームの OnCreate ハンドラーを変更し、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;

これは、一致する最初の項目またはサブメニューが見つかるまで、ループ内のすべてのポップアップ メニューに対して行われます。

于 2009-01-22T22:18:20.680 に答える
3

Not sure if it helps, but I have created my own multi-line hint window (for Delphi7) to be able to show more then just one line of text. It's open source and you can find it here.

There is some work involved showing it on the right location on the screen, but you have full control over it.

于 2009-01-22T20:53:57.200 に答える