К простым относятся свойства числового, строкового и символьного типов. Их значения просто редактируются в Инспекторе объектов с помощью клавиатуры.
Однако и здесь есть особенности, связанные с различной реализацией редакторов свойств одного и того же типа, в зависимости от того, какое имя имеет свойство. Например, при редактировании строкового свойства 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.