Как узнать, выпал ли MenuItem TToolButton? - PullRequest
3 голосов
/ 30 марта 2011

В контексте панели инструментов рисования владельца, используемой для размещения элементов меню (TToolButtons с их установленными свойствами MenuItem и Grouped), я хочу знать, был ли удален соответствующий элемент меню.Проблема заключается в том, что свойство State в OnAdvancedCustomDrawButton не отражает эту информацию.

При нажатии кнопки инструментов свойство Down имеет значение true, но в конкретном случае выше (MenuItem set и Grouped = True),сразу после того, как меню было удалено, запускается еще один OnAdvancedCustomDrawButton, но на этот раз со значением «Вниз», установленным в «ложь».

Это означает, что я заканчиваю рисованием кнопки с состоянием НЕ внизу.

Просмотристочник VCL, по-видимому, информация о том, какая кнопка инструмента была отброшена, хранится в поле FMenuButton private TToolBar, и Windows уведомляется о горячем состоянии с помощью Perform (TB_SETHOTITEM), однако ни один из них не обеспечивает чтение-access ...

Также VCL выполняет раскрытие через частное FTempMenu, дескриптор которого, таким образом, недоступен.

PS: FWIW при использовании хакерского решения,кажется, что единственное частное поле, которое можно использовать, это FButtonMenu, который нужно сравнить с вашим прикладом.on.MenuItem в CustomDraw, остальные частные поля либо не установлены достаточно рано (например, FMenuButton), либо являются закрытыми переменными (например, MenuButtonIndex) с переменным местоположением.Все еще не слишком удовлетворяющий все же.

Ответы [ 3 ]

2 голосов
/ 30 марта 2011

Получение статуса выпадающего меню проблематично, код, который делает всплывающее меню довольно запутанным, использует некоторые перехватчики сообщений.Как правило, это не тот код, к которому вы бы хотели прикоснуться.К счастью, сама панель инструментов отслеживает состояние выпадающего меню, используя переменную FMenuDropped.К сожалению, эта переменная private , вы не можете получить к ней доступ извне, «взломанный» трюк не работает.Будучи закрытым, он также не предлагает RTTI!

Существует два возможных решения:

Изменить VCL и добавить свойство, которое делает FMenuDropped доступным извне

Перейти к ComCtrls.pas, найдите объявление TToolBar = class(TToolWindow), перейдите в публичный раздел и добавьте следующее:

property MenuDropped:Boolean read FMenuDropped;

Из своего кода вы сможете проверить панель инструментов, есть ли у нее выпадающее меню или нет,К сожалению, это требует внесения изменений в VCL.Никогда не хорошая идея, трудно синхронизировать между несколькими программистами.

Используйте хак для прямого доступа к полю FMenuDropped без изменения VCL

. Для этого вам нужно получить смещение FMenuDropped поле.Как только вы это получите, вы можете написать что-то вроде этого:

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

865 на самом деле правильная константа для Delphi 2010!Вот очень быстрый способ получения константы.

  • Перейдите в настройки компилятора, установите флажок "compile using debug DCU's" *
  • Откройте ComCtrls.pas, перейдите кprocedure TToolButton.Paint, поместите туда точку торможения.
  • Запустите приложение, возьмите лист бумаги и ручку.Когда программа остановится в точке торможения, откройте Debug Inspector.Для этого просто наведите курсор на название поля, любого поля и нажмите Alt + F5 .В окне «Инспектор отладки» нажмите Ctrl + N , чтобы открыть общий редактор Inspect, который позволяет вам что-либо проверять.Введите Integer(FToolbar).Запишите результат на листе бумаги.
  • Снова нажмите Ctrl + N , на этот раз введите Integer(@FToolBar.FMenuDropped).Запомните это второе число.
  • Вам нужна постоянная разница между вторым и первым. Вот и все!

Есть, конечно, некоторые возможные проблемы.Прежде всего, это зависит от используемой версии точной Delphi.Если код должен быть скомпилирован на разных версиях компилятора Delphi, необходимо использовать умный $IFDEF.Тем не менее, это выполнимо.

(Правка) : Вы можете использовать эту же технику для доступа к любому приватному полю любого класса.Но вам нужно будет подумать много раз, прежде чем делать это, потому что частные поля по какой-то причине становятся личными.

1 голос
/ 02 апреля 2012

Используйте помощник класса.

Например.

TToolBarHelper = class helper for TToolBar
private
    function GetMenuDropped: Boolean;
public
    property MenuDropped: Boolean read GetMenuDropped;
end;

...

function TToolBarHelper.GetMenuDropped: Boolean;
begin
    Result := Self.FMenuDropped;
end;

Теперь, где бы вы ни использовали TToolBar, теперь вы можете получить доступ к новому свойству с именем MenuDropped.

0 голосов
/ 30 марта 2011

При нажатии на выпадающую кнопку форма отправляет уведомление 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;
  [...]
 

Обратите внимание, что переменная DroppedDown в OnAdvancedCustomDrawButton не синхронизирована с состоянием кнопки «Вниз», она отражает только состояние «вниз» стрелки со стрелкой вниз.

Это, я полагаю,причина проблемы в этом вопросе: когда панель инструментов имеет расширенный стиль 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 не знает о состоянии кнопок, возникнет одна проблема;всякий раз, когда VCL обновляет кнопки, необходимо будет переустанавливать стили.

...