К простым относятся свойства числового, строкового и символьного типов. Их значения просто редактируются в Инспекторе объектов с помощью клавиатуры.
Однако и здесь есть особенности, связанные с различной реализацией редакторов свойств одного и того же типа, в зависимости от того, какое имя имеет свойство. Например, при редактировании строкового свойства Name любого компонента оно не обновляется до тех пор, пока не будет нажата клавиша Enter или строка редактирования в Инспекторе объектов не потеряет фокус. В то же время при редактировании свойства Caption (тоже строкового) значение свойства изменяется с каждым нажатием клавиши.
Для создания свойства перечислимого типа нужно воспользоваться существующим или объявить свой перечислимый тип и объявить свойство этого типа.
Напомню, что перечислимыми (enumerated) называются типы данных, заданные исчерпывающим списком всех возможных значений для переменных этого типа.
Не путать перечислимый (enumerated) тип с перечисляемым (ordinal) типом, у которых каждое значение, кроме первого, имеет уникального предшественника, а каждое значение, кроме последнего, имеет уникального последователя
Простой пример объявления перечислимого типа:
Свойства перечислимых типов можно редактировать в Инспекторе объектов выбирая требуемое значение из выпадающего списка (рис. 2.2).
При этом для некоторых типов, элементы которых имеют графическое представление, в списке дополнительно появляются маленькие картинки, иллюстрирующие значение элемента.
Для свойств перечислимых типов сохраняется возможность изменять их значения и с клавиатуры, но при этом нужно следить за тем, чтобы введенное значение было одним из элементов перечислимого типа.
Тип множество является производным от перечислимого типа и фактически представляет собой набор из элементов перечислимого типа. Объявляется множество с использованием языковой конструкции set of.
Пример 2.5. Пример объявления типа множество
TAnswer = (ansYes, ansNo, ansMaybe);
TAnswerSet= set of TAnswer;
Инспектор объектов для редактирования и изменения множеств предоставляет следующий способ.
Слева от имени свойства возникает квадрат с крестиком, нажатие на который приводит к распахиванию дерева элементов. Включение элемента в множество показывается с помощью логического значения: True, если элемент включен в множество и False – если не включен.
Свойства объекта могут в свою очередь сами являться объектами. Свойства-объекты разворачиваются в Инспекторе объектов таким образом, чтобы его свойства могли быть модифицированы.
При использовании свойств-объектов возможны два варианта: во-первых, свойство-объект может являться объектом, создаваемым внутри компонента, а во-вторых – свойство может являться ссылкой на другой компонент. Рассмотрим оба варианта.
Для того чтобы опубликованные свойства-объекта могли быть сохранены в файле формы, объект должен являться наследником TPersistent. Выполнить это требование очень просто, поскольку TPersistent является прямым наследником TObject.
Так как свойство представляет собой объект, а все объекты в Delphi являются динамически размещаемыми в памяти, то использование свойств-объектов влечет за собой необходимость создания экземпляра в конструкторе класса и уничтожения его в деструкторе.
Например, в разрабатываемом классе необходимо использовать некоторую картинку. Для этого можно использовать класс TBitmap, объявить свойство соответствующего типа и создать в конструкторе экземпляр класса. После использования объекта в деструкторе необходимо уничтожить объект-свойство, высвободив занимаемую им память.
Пример 2.6. Пример использования свойства-объекта
type
TControl2 = class(TGraphicControl)
private
FBitmap: TBitmap;
procedure SetBitmap(const Value: TBitmap);
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Bitmap: TBitmap read FBitmap write SetBitmap;
end;
implementation
...
constructor TControl2.Create(AOwner: TComponent);
begin
inherited;
FBitmap:=TBitmap.Create;
end;
destructor TControl2.Destroy;
begin
FBitmap.Free;
inherited;
end;
procedure TControl2.Paint;
begin
inherited;
Canvas.Draw(0,0,FBitmap);
end;
procedure TControl2.SetBitmap(const Value: TBitmap);
begin
FBitmap.Assign(Value);
Invalidate;
end;
Обратите внимание, что имеет определенные особенности и метод записи свойства объектного типа. Вместо прямого присвоения используется метод Assign, копирующий все атрибуты объекта.
При создании свойства, являющейся фактически ссылкой на другой компонент не нужно создавать экземпляр в конструкторе. Достаточно объявить свойство нужного типа, задать методы чтения и записи.
Но в этом случае существует другая сложность. Если компонент, на который мы создали ссылку будет удален, то ссылка на него окажется некорректной и возникнет ошибка доступа к несуществующему объекту. Для устранения такого нежелательного эффекта следует переопределять метод Notification, который вызывается когда объект-ссылка создается или уничтожается.
Пример 2.7. Пример использования метода Notification
type
TControl2 = class(TGraphicControl)
private
FFileLabel: TLabel;
protected
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
published
property FileLabel: TLabel read FFileLabel write FFileLabel;
end;
implementation
...
procedure TControl2.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if (Operation=opRemove) and (AComponent=FFileLabel) then FFileLabel:=nil;
end;
Достаточно проконтролировать момент уничтожения объекта-ссылки и установить внутренне поле в nil.