Для того чтобы дополнить создаваемый компонент новым свойством, надо выполнить следующие шаги:
1. Определить название и тип свойства.
2. Объявить внутреннее поле объекта выбранного типа.
3. Объявить свойство.
4. Определить методы чтения/записи свойства (если они используются для доступа к значениям свойства).
5. При необходимости установить значение по умолчанию.
6. Определить средства сохранения/загрузки значения свойства.
Рассмотрим содержание этих шагов.
Для добавления поля к объекту в секции private класса объявляется переменная необходимого типа. Предположим, что в состав VCL входит некий гипотетический класс, описывающий автомобиль, - TAutomobile. Ставится задача создать компонент-наследник TNewAutomobile, имеющий собственное свойство Speed (скорость) целого типа. Объявление поля объекта с именем fSpeed следует поместить в секцию частных объявлений:
type
TNewAutomobile = class(TAutomobile)
private
{ Private declarations }
fSpeed:cardinal; //объявление поля объекта целого типа
end;
Для того чтобы свойство отобразилось в Инспекторе объектов, необходимо объявить его в секции published и указать способ выполнения операций чтения/записи данных в поле объекта:
type
TNewAutomobile = class(TAutomobile)
private
fSpeed: cardinal;
published
property Speed: cardinal read fSpeed write fSpeed;
end;
В составе строки объявления свойства присутствуют два ключевых слова read (чтение) и write (запись). Они указывают на способ чтения и записи значения свойства. В данном примере операции чтения и записи перенаправляются сразу в поле. Если отказаться от одного из ключевых слов, то будет создано свойство, доступное только для чтения или только для записи.
Простейший вид взаимодействия свойства и соответствующего ему поля называется прямым доступом. Приведенный пример демонстрирует использование прямого доступа при организации операций чтения/записи. Здесь любое изменение значения свойства Speed сразу передается в поле fSpeed. Но на практике прямой доступ применяется не часто.
При проектировании нового компонента обычно используется более универсальный способ доступа к значениям свойства, основанный на использовании методов чтения и записи значений свойств. Эти методы объявляются в секции private после описания поля объекта:
type
TNewAutomobile = class(TCustomControl)
private
fSpeed: cardinal;
procedure SetSpeed(Value:cardinal); //установка значения в поле
function GetSpeed:cardinal; //чтение значения из поля
published
property Speed: cardinal read GetSpeed write SetSpeed;
end;
Метод, отвечающий за запись, всегда является процедурой и, как правило, обладает единственным параметром. Второй параметр понадобится только при работе со свойством типа массив. Название параметра (Value) может быть любым, но обычно используют Value . Тип параметра должен совпадать с типом свойства. В качестве параметра Value внутреннему полю объекта передается новое значение. Например, в процедуре может проверяться корректность вводимых данных и в случае успеха полю присваивается новое значение:
procedure TNewAutomobile.SetSpeed(Value:cardinal);
begin
if Value<=300 then fSpeed:=Value else ShowMessage(‘Недопустимое значение');
end;
Метод, отвечающий за чтение, всегда оформляется в виде функции, которой не передаются параметры. Единственным исключением будут функции, осуществляющие чтение из свойства типа массив (в этом случае потребуется параметр, определяющий индекс элемента в массиве) и чтение с использованием команды index. Результат, возвращаемый функцией, должен иметь такой же тип, как и у соответствующего свойства:
function TNewAutomobile.GetSpeed:cardinal;
begin
Result:=fSpeed;
end;
Методы для чтения/записи свойства могут быть статическими и виртуальными, но не динамическими.
Свойство может быть смешанным. Это означает, что при чтении свойства вызывается метод, а при записи обращаются к переменной и наоборот:
property Data:integer read GetData write FData;
property Data:integer read FData write SetData;
Одной из возникающих при разработке новых компонентов задач является публикация свойства, скрытого в родительском классе. Например, потомки класса TWinControl наследуют более 70 свойств своего предка, но по умолчанию в Инспекторе объектов отображается всего лишь 9: Cursor, Height, HelpContext, Hint, Left, Name, Tag, Top и Width. Для того чтобы сделать унаследованные свойства доступными во время разработки, необходима повторная декларация этих свойств, но уже в секции published исходного кода компонента-потомка:
type
TMyComponent = class(TWinControl)
…
published
property Align; //свойство унаследовано от TControl
end;
Примечание.
Как уже упоминалось ранее, Инспектор объектов обеспечивает доступ только к свойствам, объявленным в секции published. Свойства, опубликованные в секции public, будут доступны только из кода приложения. Кроме того, при работе со свойствами следует учитывать две особенности компилятора Delphi, а именно:
1) при объявлении нового свойства с именем, совпадающем с именем уже имеющегося свойства, ранее определенное свойство «затеняется»;
2) свойства, которые имеют доступ только для чтения или только для записи, не отображаются в инспекторе объектов, даже если они объявлены в секции published.
Программные коды методов чтения/записи свойства размещаются в разделе реализации модуля компонента как обычные процедуры.
Установка значений свойств по умолчанию и задание средств сохранения/загрузки выполняется с помощью директив default, nodefault и stored (см. п. 1.6.1).
Пример 4. Создать компонент, который представляет собой модифицированную кнопку быстрого доступа SpeedButton. Модификация состоит в том, что в дополнение к стандартному изображению кнопки должны рисоваться внутренняя и внешняя рамки выбираемых цветов.
Решение данной задачи сводится к тому, что для обеспечения возможности настройки интерфейса кнопки следует:
1) создать два свойства: одно для представления цвета внутренней рамки, а другое – для цвета внешней рамки;
2) переопределить метод рисования компонента, в котором в дополнение к изображению кнопки SpeedButton будут рисоваться две рамки: внешняя и внутренняя.
Процесс разработки модуля компонента, как и во всех предыдущих примерах, начинается с вызова мастера компонента. В окне мастера выберите базовый класс TSpeedButton, задайте имя нового компонентного класса, например, TFramedButton. Остальные параметры можно оставить такими же, как в предыдущих примерах. Выполнив действия пункта 3 в примере 1, создайте заготовку модуля нового компонента.
Разработку программы модуля компонента следует начать с определения атрибутов новых свойств компонента и их описания.
Пусть цвет внутренней рамки определяется свойством с именем InFrameColor, а цвет внешней рамки – свойством OutFrameColor. Оба свойства принадлежат классу TColor, т. е. их значениями являются различные цвета. Свойства будут создаваться с возможностью чтения и записи их значений в специально отводимых полях FInFrameColor и FOutFrameColor. Будем считать, что чтение выполняется прямым обращением к полю, а запись – с помощью методов SetInFrameColor и SetOutFrameColor. Описания полей и методов размещаются в разделе private класса TFramedButton, и будут иметь следующий вид:
type
TFramedButton = class(TSpeedButton)
private
FInFrameColor:TColor;
FOutFrameColor:TColor;
procedure SetInFrameColor(Value:TColor);
procedure SetOutFrameColor(Value:TColor);
Программные тексты процедур SetInFrameColor и SetOutFrameColor помещаются в раздел implementation модуля и имеют незначительные отличия. Процедура SetInFrameColor представляется следующим образом:
procedure TFramedButton.SetInFrameColor(Value:TColor);
begin
If FInFrameColor<>Value then {если значение действительно новое}
Begin
FInFrameColor:=Value; {записывается новое значение свойства}
Invalidate; {так как свойство изменилось компонент перерисовывается}
end;
end;
Процедура SetOutFrameColor имеет аналогичный текст.
Для воспроизведения на новом компоненте внешней и внутренней рамок, как было отмечено выше, необходимо переопределить метод рисования компонента. Таким методом является метод Paint. Переопределяя этот метод для уже реализованных графических компонентов необходимо вызвать унаследованный родительский метод Paint. Для рисования на кнопке двух рамок можно использовать средства графики классов TCanvas и TBrush. Переопределение метода рисования для создаваемого компонента будет выглядеть следующим образом:
procedure TFramedButton.Paint;
begin
inherited Paint; {вызов родительского метода}
Canvas.Brush.Color:=OutFrameColor;
Canvas.FrameRect(rect(4,4,width-4,height-4)); {рисование внешней рамки}
Canvas.Brush.Color:=InFrameColor;
Canvas.FrameRect(rect(10,10,width-10,height-10)); {рисование внутренней рамки}
end;
Здесь рамки представляются в виде прямоугольников, для рисования которых используется метод FrameRect. Область прямоугольника задается с помощью процедуры rect, в которую передаются координаты x, y верхнего левого и правого нижнего углов прямоугольника.
Метод Paint следует описать как переопределяемый (т. е. с директивой override) в разделе protected создаваемого модуля, а текст поместить в раздел implementation.
Установите компонент FramedButton на вкладку New палитры компонентов Delphi, выполнив действия пункта 4 примера 1.
Выполните тестирование нового компонента, для чего создайте новое приложение, поместите на форму созданный компонент и другие стандартные компоненты (кнопки, метки и т. д.) для обеспечения приемлемого интерфейса приложения. Проверьте работу приложения, задавая различные значения цветов внешней и внутренней рамок кнопки с помощью Инспектора объектов.
Подготовьте и зарегистрируйте в среде Delphi значок для компонента FramedButton.
|