Собственно построение пользовательского редактора свойств сводится к двум операциям.
Во-первых, это - описание нового класса редактора, порожденного от класса TPropertyEditor или одного из его потомков. Настройка редактора свойства выполняется перекрытием нескольких ключевых методов.
Во-вторых, регистрация редактора свойств для определенных типов свойств и классов компонентов с помощью процедуры RegisterPropertyEditor.
Для наглядности возьмем компонент TCompass и для удобства работы с ним реализуем некоторые редакторы свойств.
Например, свойство FormatAzimuth отвечает за форматирование значения угла. Было бы удобно предоставить возможность выбрать один из возможных вариантов, оставив возможность ввести значение вручную.
Важно понимать, что редакторы свойств имеют к функционированию компонента лишь косвенное, вспомогательное отношение и предназначены лишь для удобства (а иногда и просто для обеспечения возможности) редактирования значения свойства.
Редакторы свойств никак не влияют на работу компонента во время выполнения (Runtime).
Итак, необходимо для какого либо свойства строкового типа обеспечить возможность в Инспекторе объектов не только вводить значение с клавиатуры, но еще и выбрать одно из списочных значений.
Для этого необходимо унаследовать класс TStringProperty и перекрыть несколько методов.
Во-первых, необходимо перекрыть метод GetAttributes для того, чтобы оповестить инспектор объектов о том, каким образом допускается редактировать свойство.
Во-вторых, для формирования списка вариантов значений необходимо переопределить метод GetValues.
Таким образом, интерфейсная часть редактора свойства будет выглядеть следующим образом:
Пример 12.1.
TFormatAngleProperty=class(TStringProperty) public function GetAttributes: TPropertyAttributes; override; procedure GetValues(Proc: TGetStrProc); override; end;
Реализация метода-функции GetAttributes заключается в возврате определенных элементов множества типа TPropertyAttributes.
Многим элементам этого множества соответствуют методы редактора свойств.
Другими словами, если определенный элемент множества вошел в множество, возвращенное методом GetAttributes, то значит соответствующий ему метод должен быть переопределен.
Наиболее важные элементы множества и соответствующие им методы редактора свойств приведены в таблице.
Атрибуты свойств | Соответствующий метод | Описание |
paValueList | GetValues | Этот атрибут определяется для редакторов, которые требуют список элементов, подлежащих отображению в раскрывающемся списке. Если этот атрибут установлен, для заполнения списка вызывается метод GetValue. |
paSortList | Этот атрибут инструктирует Инспектор объектов выполнить сортировку списка элементов, отображаемых в раскрывающемся списке. Разрешен только в том случае, если paValueList также установлен. | |
paSubProperties | GetProperties | Этот атрибут показывает, что редактор свойства определяет подсвойство, которое должно быть отображено углубленным и ниже текущего свойства. Если этот атрибут установлен, для генерации списка свойств вызывается метод GetProperties. |
paDialog | Edit | Этот атрибут устанавливается для редакторов свойств, которые отображают диалоговое окно в своем методе Edit. Этот атрибут должен быть установлен для того, чтобы в Инспекторе объектов справа от свойства появилась кнопка с многоточием. |
paMultiSelect | Когда этот атрибут установлен, это свойство отображается при выборе нескольких компонентов. | |
paAutoUpdate | SetValue | Этот атрибут предписывает Инспектору объектов вызывать метод SetValue после каждого изменения, выполненного в значении свойства. Если этот аттрибут не определен, SetValue вызывается только в том случае, если пользователь нажимает клавишу <Enter> или перемещается из текущего свойства. |
paReadOnly | Этот атрибут устанавливается для предотвращения модификации пользователем значения свойства. | |
paVolatileSubProperties | GetProperties | Инспектор объектов "освежает" значения подсвойств, если значение свойства было изменено. |
paReference | GetComponentValue | Значение является ссылкой. Вместе с атрибутом paSubProperties означает, что объект-ссылка показывается как подствойство. |
paFullWidthName | Вместо значение свойства отображается его название. |
Например, если необходимо, чтобы в инспекторе объектов появился список возможных значений и при выборе одного из них свойство обновилось в этот же момент, реализация метода GetAttributes может быть следующей.
Пример 12.2.
function TFormatAngleProperty.GetAttributes: TPropertyAttributes; begin Result:=[paValueList, paAutoUpdate]; end;
Если нужно, чтобы значения были отсортированы, то нужно добавить атрибут paSortList.
Для того чтобы предоставить возможность изменять значение свойства сразу для нескольких выделенных компонентов, нужно добавить атрибут paMultiSelect.
В соответствии с таблицей 1, если включен атрибут paValueList, то для формирования самих значений должен быть реализован метод GetValues.
Метод GetValues имеет один параметр процедурного типа, который вызывается для каждого помещаемого в список значения.
Пример 12.3. Пример реализации метода GetValues
procedure TFormatAngleProperty.GetValues(Proc: TGetStrProc); begin inherited; Proc('%d °'); Proc('%d'); Proc('a=%d'); Proc('a=%d °'); end;
Результат работы редактора свойства приведен на рис.
Для редактирования свойства Azimuth было бы удобно ввести значение с помощью самого компонента.
Для этого необходимо спроектировать диалог, содержащий аналогичный компонент и элементы управления для подтверждения выбора (например, кнопку).
Вариант диалога приведен на рис. 3.
Для того, чтобы обеспечить изменение свойства с использованием диалога нужно в методе GetAttributes включить атрибут paDialog:
Пример 12.4.
function TAzimuzhProperty.GetAttributes: TPropertyAttributes; begin Result := [paDialog]; end;
Создание и вызов диалога редактирования производится в соответствующем атрибуту paDialog методе Edit:
Пример 12.5.
procedure TAzimuzhProperty.Edit; begin inherited; dlgAzimuth:=TdlgAzimuth.Create(Application); try dlgAzimuth.Caption:= Format('%s.%s.%s',[TComponent(GetComponent(0)).Owner.Name, TComponent(GetComponent(0)).Name ,GetName ]); dlgAzimuth.Compass1.Azimuth:=TAzimuth(GetOrdValue); if dlgAzimuth.ShowModal=mrOk then SetOrdValue(dlgAzimuth.Compass1.Azimuth); finally dlgAzimuth.Free; end; end;
После создания диалога его заголовок устанавливается равным имени "хозяина" компонента, имени компонента и имени свойства, разделяемыми точками. Затем свойство Azimuth компонента на диалоге устанавливается равным значению редактируемого свойства.
Если диалог закрылся подтверждением, то с помощью метода SetOrdValue устанавливается значение редактируемого свойства.
Как это выглядит в действии можно увидеть здесь.
Для того, чтобы спроектированная форма редактора диалога не попала в готовый проект, не включайте файл формы в модуль компонента и раздел uses проекта.
Для того, чтобы спроектированный редактор свойства был задействован для редактирования определенных свойств необходимо его зарегистрировать с помощью процедуры RegisterPropertyEditor.
Процедура RegisterPropertyEditor принимает четыре параметра:
Информация о типе редактируемого свойства, который получается с помощью функции TypeInfo;
Тип компонента, для которого регистрируется редактор. Если это параметр равен nil, то редактор будет зарегистрирован для всех свойств того типа, информация о котором передано первым параметром;
Название свойства, для которого регистрируется редактор. Если параметр равен пустой строке, то редактор будет зарегистрирован для всех свойств того типа, который передан вторым параметром.
Класс редактора свойства.
Пример 12.6.
procedure Register; begin RegisterPropertyEditor(TypeInfo(string),TCompass, 'FormatAzimuth',TFormatAngleProperty); RegisterPropertyEditor(TypeInfo(TAzimuth),TCompass,'Azimuth',TAzimuzhProperty); end;
Регистрация осуществляется внутри процедуры с предопределенным именем Register того модуля, в котором размещены классы редакторов свойств.