Продолжаем разбираться в Ribbon Controls без использования мышей. Сегодня научимся создавать элементы управления и работать с механизмом действий (Actions).
В прошлый раз мы остановились на том, что создали вкладку и разместили на ней одну группу. В целом на всё про всё ушло не так уж и много времени. Сегодня задачка будет немного по сложнее, а именно научиться создавать кнопки и присваивать им выполнение определенных действий.
Для того, чтобы понять о чем в принципе будет идти речь сегодня, советую освежить свои знания и прочитать этот пост.
Как Вы уже знаете кнопки у Ribbon могут быть трёх типов: маленькие (bsSmall) с иконкой 16х16, большие (bsLarge), занимающие по высоте всю высоту группы (Ribbon Group) и большие split-кнопки выполняющие несколько действий — верхняя часть кнопки выполняет основное действие и нижняя содержит список дополнительных действие (Actions). Давайте вначале попробуем создать простую маленькую кнопку. Что нам для этого необходимо?
Во-первых, ActionManager и ImageList с хотя бы одной иконкой 16х16. Можно и без иконок, но с ними как-то по-красивее будет.
Во-вторых, нам необходимо, чтобы наша кнопка выполняла какое-либо действие. Следовательно нужен обработчик.
Т.к. ActionManager и ImageList у Вас уже должны остаться на форме от прошлой части нашей работы, то сразу перейдем к написанию обработчика, чтоб потом к нему не возвращаться. Само по себе действие Action:TBasicAction — это объект наследник от (TCompoent) у которого определено следующее свойство:
property OnExecute: TNotifyEvent read FOnExecute write SetOnExecute;
Тип TNotifyEvent это ни что иное как стандартное событие:
TNotifyEvent = procedure(Sender: TObject) of object;
Следовательно, создаем, например такую процедуру у главной формы приложения:
[...] private procedure MyCustomAction(Sender: TObject); [...]
и тело процедуры. Пусть это будет что-нибудь простое и одновременно информативное, например:
procedure TForm5.MyCustomAction(Sender: TObject); begin ShowMessage('It is my Action!!!'); end;
Сразу понятно, что сработало :). Обработчик есть. Куда его теперь примостить? Создаем наш первый элемент управления Ribbon без использования мыши. Для этого в обработчике onClick у единственной кнопки нашего приложения пишем:
[...] Ribbon1.ActionManager:=ActionManager1;//ассоциировали мэнеджер c Ribbon ActionManager1.ActionBars.Add;//добавили новую коллекцию ActionManager1.ActionBars[0].ActionBar:=Group;//добавили в коллекцию новую панель with ActionManager1.ActionBars[0].Items.Add do begin action:=TAction.Create(self); //создали новое действие action.OnExecute:=MyCustomAction;//присвоили ему обработчик caption:='Caption'; //надпись visible:=True; //сделали видимым end; [...]
Теперь запускаем приложение и любуемся результатом:
Можете проверить — по клику на кнопке «Caption» сработает наш самописный обработчик. Пока вроде бы ничего особенно сложного и непонятного нет, неправда ли? Думаю, для Вас не составит никакого труда присвоить такой кнопке картинку из ImageList. А теперь попробуем создать большую кнопку. Пардон, за дурной тон, но я процитирую одну из фраз, которую сам же писал в одной из статей про Ribbon:
Для того, чтобы новая кнопка выглядела как полагается, т.е. сверху располагалась иконка 32?32 пикселя и снизу подпись, необходимо выбрать действие на панели, перейти в Object Inspector, выбрать вкладку свойств CommandProperties и указать в свойстве ButtonSize значение bsLarge. При этом ширина панели Ribbon автоматически изменится
Всё, что было сказано — чистая правда и так оно и есть. Логично предположить, что у нашего самодельного Action как минимум есть свойство ActionClients, а у него в свою очередь CommandProperties и т.д. и т.п. Логично, что следующий код:
ActionClients[0].CommandProperties.ButtonSize:=bsLarge;
сработает? На первый взгляд логично, а на деле оказывается, что CommandProperties вообще не имеет ничего похожего на ButtonSize.
Вот всё, что есть у этого типа данных (модуль ActnMan):
TCommandProperties = class(TPersistent) public type TChangeState = (csIgnore, csRecreate, csRebuild); protected type TCommandPropertiesChangeEvent = procedure (Sender: TObject; State: TChangeState) of object; private FActionClientItem: TActionClientItem; FOnChange: TCommandPropertiesChangeEvent; FChangeState: TChangeState; function GetControl: TCustomActionControl; protected procedure InvalidateControl(const ResetBounds: Boolean); inline; procedure DoChanged; virtual; public constructor Create(AItem: TActionClientItem); virtual; procedure Assign(Source: TPersistent); override; property Control: TCustomActionControl read GetControl; property ActionClientItem: TActionClientItem read FActionClientItem write FActionClientItem; property OnChange: TCommandPropertiesChangeEvent read FOnChange write FOnChange; end;
Признаться, когда я увидел описание типа я немного даже растерялся. Ведь не может быть, чтоб в Инспекторе свойство есть, а у типа данных его нет :) Ну, как говориться, старость не радость. Опять же сказывается отсутствие базового образования по программированию. Секрет прост. Если вы откроете модуль ActnMan и посмотрите его содержимое то достаточно быстро обнаружите такой тип данных:
TButtonProperties = class(TCommandProperties) public type TButtonSize = (bsSmall, bsLarge); TButtonType = (btNone, btDropDown, btSplit, btGallery); TGroupPosition = (gpNone, gpStart, gpMiddle, gpEnd, gpSingle); TTextAssociation = (taImage, taDropdown); private FButtonSize: TButtonSize; FButtonType: TButtonType; FGroupPosition: TGroupPosition; FTextAssociation: TTextAssociation; procedure SetButtonType(const Value: TButtonType); procedure SetButtonSize(const Value: TButtonSize); procedure SetGroupPosition(const Value: TGroupPosition); procedure SetTextAssociation(const Value: TTextAssociation); public constructor Create(AItem: TActionClientItem); override; procedure Assign(Source: TPersistent); override; published property ButtonSize: TButtonSize read FButtonSize write SetButtonSize default bsSmall; property ButtonType: TButtonType read FButtonType write SetButtonType default btNone; property GroupPosition: TGroupPosition read FGroupPosition write SetGroupPosition default gpNone; property TextAssociation: TTextAssociation read FTextAssociation write SetTextAssociation default taImage; end;
Вот собственно, тот самый тип данных, который нам нужен для работы со всевозможными «конфигурациями» кнопок. Дописываем наш обработчик onClick:
[...] with ActionManager1.ActionBars[0].Items.Add do begin [...] (ActionClients[0].CommandProperties as TButtonProperties).ButtonSize:=bsLarge; ImageIndex:=0; end; [...]
Снова запускаем приложение и на этот раз видим нормальную большую и красивую кнопку Ribbon:
Надеюсь, дальнейшая работа с простыми кнопками не составит для Вас особого труда. А мы в следующий раз начнем работу по созданию Split-Button’ов. Там, мягко говоря, неподготовленному человеку без бутылки водки не разобраться — будем разбираться вместе :)
Подписывайтесь на RSS и ждите продолжения цикла статей про Ribbon Controls в Delphi.
1. Спасибо автору за статью. Представленная информация имеет большую ценность с практической точки зрения.
2. Вопрос: кто-нибудь знает, как в Runtime допавть на панель csComboBox? Вроде бы надо TRibbonComboBox создавать..
Здравствуйте?
Так как все же работать с раздвоенной кнопкой?
Спасибо.
Вот так вот со split-кнопками работать
http://www.webdelphi.ru/2009/10/ribbon-controls-shag-za-shagom-shag-2-dopolnitelnye-elementy-interfejsa/
я подумал что ты опишешь как в рантайме с ними работать :)
у меня при добавлении в сплит новых пунктов, картинка добавляемого элемента съезжает
Здравствуйте. Не подскажите, как передать какие либо параметры в процедуру MyCustomAction?
Можно через объект Sender
или через глобальные переменные
Как через Sender?
Спасибо, сам разобрался