3

メニューエントリ(MenuItemおよびGroupedプロパティが設定されたTToolButtons)をホストするために使用される所有者描画ツールバーのコンテキストで、対応するmenuitemがドロップされるかどうかを知りたいです。問題は、OnAdvancedCustomDrawButtonのStateプロパティがその情報を反映していないことです。

ツールボタンがクリックされると、そのDownプロパティはtrueになりますが、上記の特定のケース(MenuItemsetおよびGrouped= True)では、メニューがドロップされた直後に、別のOnAdvancedCustomDrawButtonが起動されますが、今回はDownがfalseに設定されます。

これは、ボタンを下にしない状態で描画することになることを意味します。

VCLのソースを見ると、どのツールボタンがドロップされたかに関する情報がTToolBarのFMenuButtonプライベートフィールドに格納されているようで、WindowsはPerform(TB_SETHOTITEM)によってホット状態を通知されますが、どちらも読み取りアクセスを提供しません。 ..

また、VCLはプライベートFTempMenuを介してドロップダウンを実行するため、そのハンドルにはアクセスできません。

PS: FWIWハッキーソリューションを使用している場合、使用可能な唯一のプライベートフィールドはFButtonMenuであるようです。これは、CustomDrawのButton.MenuItemと比較する必要があります。他のプライベートフィールドは、十分に早く設定されていないか(FMenuButtonなど)、プライベートです。変数の場所を持つ変数(MenuButtonIndexなど)。それでも満足のいくものではありません。

4

3 に答える 3

2

メニュー ドロップダウン ステータスを取得するのは問題があります。メニュー ポップアップを作成するコードはかなり複雑で、いくつかのメッセージ フックを使用します。通常、これは触れたくないコードです。FMenuDropped幸いなことに、ツールバー自体は、変数を使用してドロップダウン メニューのステータスを追跡します。残念ながら、その変数はprivateです。外部からアクセスすることはできません。「ハッキングされた」トリックは機能しません。プライベートであるため、RTTI も提供されません。

考えられる解決策は 2 つあります。

VCL を変更し、外部から FMenuDropped を使用できるようにするプロパティを追加します。

ComCtrls.pas に移動し、TToolBar = class(TToolWindow)宣言を見つけて、パブリック セクションに移動し、これを追加します。

property MenuDropped:Boolean read FMenuDropped;

コードから、ツールバーにドロップダウン メニューがあるかどうかを確認できます。これの残念な部分は、VCL を変更する必要があることです。決して良い考えではなく、複数のプログラマー間で同期するのは困難です。

ハックを使用して、VCL を変更せずに FMenuDropped フィールドに直接アクセスします

これを行うには、フィールドのオフセットを取得する必要がありFMenuDroppedます。それができたら、次のように書くことができます。

if PBoolean(Integer(Toolbar1) + 865)^ then
   DoStuffIfMenuIsDropped
else
   OtherStuffIfMenuIsNotDropped;

865、実際には Delphi 2010 の正しい定数です! 定数を取得する非常に簡単な方法を次に示します。

  • コンパイラ設定に移動し、「デバッグ DCU を使用してコンパイルする」をチェックします。
  • ComCtrls.pas を開き、 に移動してprocedure TToolButton.Paint、そこにブレーキポイントを配置します。
  • アプリケーションを起動し、紙とペンを用意します。プログラムがブレークポイントで停止したら、Debug Inspector を開きます。これを行うには、フィールドの名前、任意のフィールドにカーソルを置き、Alt+を押しF5ます。Debug Inspector ウィンドウでCtrl+を押すと、何でも検査できるN汎用エディターが表示されます。Inspectを入力しInteger(FToolbar)ます。一枚の紙に結果を書き留めます。
  • +をもう一度Ctrl押しNて、今度は を入力しInteger(@FToolBar.FMenuDropped)ます。この 2 番目の数字に注意してください。
  • 必要な定数は、2 番目と 1 番目の差です。それでおしまい!

もちろん、考えられる問題もいくつかあります。まず第一に、これは使用している Delphi の正確なバージョンによって異なります。コードを異なるバージョンの Delphi コンパイラでコンパイルする必要がある場合は、巧妙$IFDEFな方法を使用する必要があります。それでもなお、これは実行可能です。

(編集):これと同じ手法を使用して、任意のクラスの任意のプライベート フィールドにアクセスできます。しかし、これを行う前に何度も考える必要があります。なぜなら、private フィールドは何らかの理由で private になっているからです。

于 2011-03-30T15:54:37.967 に答える
0

ドロップダウン ボタンをクリックすると、フォームにTBN_DROPDOWN通知が送信されます。これは、メニューを起動したボタンを追跡するために使用できます。

type
  TForm1 = class(TForm)
    [...]
  private
    FButtonArrowDown: TToolButton;
    procedure WmNotify(var Msg: TWmNotify); message WM_NOTIFY;
  [...]

uses
  commctrl;

procedure TForm1.WmNotify(var Msg: TWmNotify);

  function FindButton(Bar: TToolBar; Command: Integer): TToolButton;
  var
    i: Integer;
  begin
    Result := nil;
    for i := 0 to Bar.ButtonCount - 1 do
      if Bar.Buttons[i].Index = Command then begin
        Result := Bar.Buttons[i];
        Break;
      end;
  end;

begin
  if (Msg.NMHdr.code = TBN_DROPDOWN) and
      (LongWord(Msg.IDCtrl) = ToolBar1.Handle) then begin
    FButtonArrowDown := FindButton(ToolBar1, PNMToolBar(Msg.NMHdr).iItem);
    inherited;
    FButtonArrowDown := nil;
  end else
    inherited;
end;


procedure TForm1.ToolBar1AdvancedCustomDrawButton(Sender: TToolBar;
  Button: TToolButton; State: TCustomDrawState; Stage: TCustomDrawStage;
  var Flags: TTBCustomDrawFlags; var DefaultDraw: Boolean);
var
  DroppedDown: Boolean;
begin
  DroppedDown := Button = FButtonArrowDown;
  [...]
 


「OnAdvancedCustomDrawButton」の「DroppedDown」変数は、ボタンの「下」状態と同期していないことに注意してください。ドロップダウン矢印の「下」状態のみを反映します。

これがこの質問の問題の原因だと思います。ツールバーにTBSTYLE_EX_DRAWDDARROWS拡張スタイルがあり、そのボタンにBTNS_WHOLEDROPDOWNスタイルがない場合、メニューが起動されたときにボタンのドロップダウン矢印部分だけが押されます。実際、ボタンは「下」ではありませんAFAIU、それでも押されたボタンを描画したい。残念ながら、VCL は「wholedropdown」ボタンを持つプロパティを公開していません。

ボタンにこのスタイルを設定することが可能です:

var
  ButtonInfo: TTBButtonInfo;
  i: Integer;
  Rect: TRect;
begin
  ButtonInfo.cbSize := SizeOf(ButtonInfo);
  ButtonInfo.dwMask := TBIF_STYLE;
  for i := 0 to ToolBar1.ButtonCount - 1 do begin
    SendMessage(ToolBar1.Handle, TB_GETBUTTONINFO, ToolBar1.Buttons[i].Index,
        LPARAM(@ButtonInfo));
    ButtonInfo.fsStyle := ButtonInfo.fsStyle or BTNS_WHOLEDROPDOWN;
    SendMessage(Toolbar1.Handle, TB_SETBUTTONINFO, ToolBar1.Buttons[i].Index,
        LPARAM(@ButtonInfo));
  end;

  // Tell the VCL the actual positions of the buttons, otherwise the menus
  // will launch at wrong offsets due to the separator between button face
  // and dropdown arrow being removed.
  for i := 0 to ToolBar1.ButtonCount - 1 do begin
    SendMessage(ToolBar1.Handle, TB_GETITEMRECT, 
                ToolBar1.Buttons[i].Index, Longint(@Rect));
    ToolBar1.Buttons[i].Left := Rect.Left;
  end;
end;


次に、ドロップダウン部分はボタンとは別に動作しません。より正確には、別のドロップダウン部分が存在しないため、メニューが起動されるたびにボタンのダウン/押された状態が設定されます。

しかし、VCL がボタンの状態を認識していないため、1 つの問題が発生します。VCL がボタンを更新するたびに、スタイルの再設定が必要になります。

于 2011-03-30T16:27:01.320 に答える