Создание компонента Delphi

Это не очередная копия той самой статьи (сколько ей уже лет?), которая была написана еще под Borland Delphi. Ниже поэтапно описан опыт разработки своего компонента на реальном примере с более глубоким пояснением параметров компонента для Design Time. Возможно, у кого-то есть решения и получше, однако, это мой первый компонент и я очень рад, что разобрался.


Этап 1. Постановка задачи


Так как я часто использую различный диалоговые окна в своих программах, то первым же своим компонентом я решил сделать панельку с кнопками "Ок" и "Отмена". Соответственно, мне понадобиться создавать новый компонент на основе класса TPanel и внутри него создать две кнопки (класса TButton).
Также, помимо прочего, я хотел бы сразу настроить панель и кнопки на ней таким образом, каким обычно привык их видеть в своих программах, где подобную панель каждый раз приходилось делать вручную.

Итак, параметры для TPanel:
  • выравнивание по умолчанию по нижнему краю формы (или родительского компонента)
  • отсутствие рельефа ( BevelOuter поставить в значение bvNone)
  • выключен показ названия панели ( ShowCaption поставить в значение False )
Параметры для кнопок на панели:
  • все выровнены по правому краю
  • каждая из кнопок может быть показана или скрыта
  • кнопка "Ok" должна возвращать в качестве ModalResult значение mrOk, а кнопка "Cancel" - mrCancel
Как это должно выглядеть (настроено вручную):

Этап 2. Создание компонента


Далее - придерживаемся какое-то время той самой инструкции.
  1. Открываем меню Component и выбираем New Component
  2. Появляется окно со всеми классами визуальных компонентов, нам нужно выбрать "предка", которому собираемся создать "потомка" с новыми функциями
  3. С помощью поиска сразу находим TPanel
  4. Далее - выберем название и расположение на панели инструментов для создания компонентов
После этого появляется стандартный модуль компонента с уже вставленным названием, код которого мы и будем дополнять.

Этап 3. Добавление кнопок в конструктор


Обе кнопки должны быть созданы сразу после создания панели, тогда же и настроены по умолчанию вместе с панелью. Только после этого мы сможем приступить к заданию параметров их видимости.
В разделе public запишем такой код:

constructor Create(AOwner: TComponent); override;

С его помощью мы перезапишем уже существующий метод из "предка".
Также, подготовим класс к созданию кнопок, добавив в uses модуль Vcl.StdCtrls, а в класс в раздел protected две переменных:

    okButton:TButton;
    cancelButton:TButton;

Теперь описываем работу конструктора:

constructor OkCancelPanel.Create(AOwner: TComponent);
begin
  { Вызовем конструктор из "предка" }
  inherited Create(AOwner);
  { Применим новые параметры по умолчанию }
  self.ShowCaption:=false;
  self.BevelOuter:=bvNone;
  self.Align:=alBottom;
  { Начнем создавать кнопки справа налево }
  { make Cancel }
  cancelButton:=TButton.Create(AOwner);
  cancelButton.Parent:=self;
  cancelButton.Caption:='Cancel';
  cancelButton.ModalResult:=mrCancel;
  { make Ok }
  okButton:=TButton.Create(AOwner);
  okButton.Parent:=self;
  okButton.Caption:='Ok';
  okButton.Align:=alRight;
  okButton.ModalResult:=mrOk;
end;

Так как кнопки, выравниваемые по правому краю, можно легко менять местами только, когда они лежат на форме, то я решил упростить себе задачу и не писать лишних строк, а просто создать кнопки справа налево.

Если скомпилировать, а затем установить наш пакет (Package) уже сейчас, то мы можем увидеть именно то, что было показано на картинке выше. Однако, работа только началась, я ведь хотел сделать еще и включение и выключение видимости каждой кнопки.

Этап 4. Переменные для переключения видимости


Так я начинал делать, стараясь понять, как делать правильно.

В раздел private добавим две переменные:

    valueOk:boolean;
    valueCancel:boolean;

В них будет хранится информация о том, видима ли там или иная кнопка или нет. Так как эти переменные запривачены, то объявим свойства, которые будут с ними работать. А еще лучше - если работать свойства будут с функциями и процедурами, а не напрямую с переменными. Напишем так:

public
    constructor Create(AOwner: TComponent); override;
    property hasOk:boolean read getOk write setOk default True;
    property hasCancel:boolean read getCancel write setCancel default True;

Зачем же нам лишние процедуры, когда можно с помощью свойства изменять саму переменную? Затем, что при установке нового значения нужно сразу же изменить видимость кнопки. А функции для получения значения переменных я написал уже для собственного спокойствия, в них особой необходимости нет.

Объявим эти методы выше, в private:

    function getOk:boolean;
    procedure setOk(value:boolean);
    function getCancel:boolean;
    procedure setCancel(value:boolean);

Опишем хотя бы одну процедуру получения значения, они аналогичны:

function OkCancelPanel.getOk:boolean;
begin
  getOk:=valueOk;
end;

А также - одну процедуру изменения значения, они также аналогичны:

procedure OkCancelPanel.setOk(value:boolean);
begin
  valueOk:=value;
  { Здесь же - устанавливаем видимость кнопки }
  if valueOk then
    okButton.Visible:=true
  else
    okButton.Visible:=false;
end;

Итак, что мы имеем на данный момент? У нас есть включение и отключение видимости кнопки, но менять эти значения можно только с помощью редактора кода.
Что хотелось бы иметь? Редактирование видимости в Design Time с помощью панели Object Inspector.

Этап 5. Свойства, видимые в Object Inspector (немного теории)


Немного отвлечемся от написания кода и посмотрим на то самое "глубокое понимание" свойств, о котором я писал в самом-самом начале, а также о том, как можно создать группу из нескольких свойств.

Как известно, чтобы добавить эти свойства, достаточно лишь записать их в раздел published.
Например, добавим такое:

property ButtonOkIsVisible:boolean read getOk write setOk default True;

Тогда они будут отображены таким образом:


Но так может сделать кто угодно и мне это кажется не очень красивым решением. Хотя, оно довольно наглядно и вы можете сразу проверить работу метода setOk, поменяв значение свойства ButtonOkIsVisible и проверив значение Visible у кнопки "Ok".

Тут же я нашел свою первую ошибку - отсутствие имён у кнопок, позднее я напишу код, как это исправить, иначе к ним нет никакого доступа и нельзя нажать их программно (с помощью вызова из кода по имени кнопки содержащейся в ней процедуры Click).

Так примерно к середине работы я понял, что хотел бы добавить еще несколько кнопок, то мне хотелось бы, чтобы изменение их видимости было аккуратно спрятано в отдельную группу, например, вот так:


А чтобы это реализовать, мне пришлось внимательно читать исходники уже имеющихся компонентов, для примера взяв уже существующую группу Anchors. Как оказалось, эта группа записана во все компоненты как обычное свойство (смотрите сами в Vcl.StdCtrls), а корни идут гораздо глубже, аж в System.UITypes!

Что представляют из себя элементы группы Anchors? Всего лишь четыре константы в Vcl.Controls:

{ TAnchors elements }

const
  akLeft = System.UITypes.TAnchorKind.akLeft;
  akTop = System.UITypes.TAnchorKind.akTop;
  akRight = System.UITypes.TAnchorKind.akRight;
  akBottom = System.UITypes.TAnchorKind.akBottom;

которые занесены в список (в модуле System.UITypes), а оттуда - в множество:

  TAnchorKind = (akLeft, akTop, akRight, akBottom);
  TAnchors = set of TAnchorKind;

Именно последнее мы и видим в качестве группы свойств в панели Object Inspector.

Этап 6. Создание группы свойств


Эту часть я написал не сразу, потому что мне нужно было все проверить и потестировать, будет ли оно работать вообще хоть как-нибудь. В итоге - работает, смотрим результаты.

Комментарии

Отправить комментарий

Популярные сообщения из этого блога

Генератор названий оружия

Идеи: Генератор сказки