Главная » Delphi » Создание редакторов свойств

0

В главе 11, “Разработка компонентов VCL”, описано, как в окне инспектора объектов выполняется редактирование свойств самых  распространенных типов.  Средство, с по мощью которого выполняется редактирование свойства, называется редактором свойств (property editor). Для уже существующих  свойств предусмотрено несколько заранее соз данных  редакторов. Но возможна ситуация, когда ни один из уже существующих  редак торов применить не  удастся —  например, при  создании пользовательского свойства. В этом случае потребуется разработать собственный редактор свойств.

Редактировать  свойства  в  окне   инспектора  объектов  можно   двумя  способами. Один  заключается в предоставлении пользователю возможности редактирования свойства как строки текста.  Другой требует  создания специального диалогового окна, в котором и выполняется редактирование свойства. В некоторых случаях потребуется использовать оба способа для редактирования одного  свойства.

Вот основные этапы создания редактора свойств.

1.  Создание объекта класса, производного от класса редактора свойств.

2.  Редактирование свойства как текста.

3.  Редактирование свойства в диалоговом окне (необязательный этап).

4.  Определение атрибутов редактора свойств.

5.  Регистрация редактора свойств.

Все эти этапы подробно рассматриваются в следующих разделах.

Создание потомка редактора свойств

Delphi определяет несколько редакторов свойств в модуле DesignEditors.pas, причем все они  происходят от базового класса  TPropertyEditor. Создаваемый ре дактор  свойства также  должен  происходить от него или его потомков. В табл. 12.1 пе речислены классы,  производные от TPropertyEditor, используемые для редактиро вания свойств  уже существующих типов.

Таблица 12.1. Редакторы свойств, определенные в модуле DesignEditors.pas

Редактор свойств                                            Описание

TOrdinalProperty        Базовый класс  для  всех  редакторов свойств обыч ных  типов, таких  как TIntegerProperty, TEnum- Property, TCharProperty и т.п.

TIntegerProperty            Редактор целочисленных свойств  любых размеров

TCharProperty                 Редактор символьных свойств типа char и символь

ных наборов (например ‘A’..’Z’)

TEnumProperty                 Редактор свойств для всех пользовательских перечис

лимых типов

TFloatProperty                Редактор свойств вещественного типа (чисел с пла

вающей точкой)

TStringProperty             Стандартный редактор свойств  строковых типов

TSetElementProperty     Стандартный редактор свойств для отдельных эле ментов множеств. Каждый  элемент рассматривает ся как независимый логический тип

TSetProperty                  Редактор  свойств,  являющихся  множеством.  Мно

жество  редактируется по отдельным элементам TClassProperty      Редактор свойств, представляющих собой объекты TMethodProperty         Редактор  свойств,  являющихся  указателями  на  ме

тоды, т.е. событиями

TComponentProperty          Редактор  свойств,  ссылающихся на  компоненты.

Это  не то  же самое,  что  и редактор TClassProp- erty. Данный редактор позволяет пользователю задать  компонент, на который ссылается свойство, т.е. ActiveControl

TColorProperty              Редактор свойств  типа TColor (цвет)

TFontNameProperty       Редактор свойств, содержащих имя  шрифта. Этот редактор отображает раскрывающийся список названий шрифтов, доступных  в данной системе

TFontProperty     Редактор свойств типа  TFont, позволяющий редак тировать подчиненные свойства (subproperties), поскольку  он является производным от класса TClassPropertyОкончание табл. 12.1.

Редактор свойств                                            Описание

TInt64Property              Стандартный  редактор  свойства  для  всех   типов

Int64 и его производных

TNestedProperty               Этот  редактор  свойств  способен  использовать  ро

дительский редактор свойств TClassProperty Стандартный редактор свойства для объектов TMethodProperty                            Стандартный редактор свойства для методов

TInterfaceProperty          Стандартный редактор свойства для  обращений  к

интерфейсу

TComponentNameProperty         Редактор  имен  свойств.  Этот  редактор  отключает

ся, если выбрано более одного  компонента

TDateProperty           Стандартный редактор свойств для раздела  даты  в свойствах типа TDateTime

TTimePropery                Редактор свойств для раздела  времени свойств типа

TDateTime

TDateTimeProperty           Редактор свойств  типа TDateTime

TVariantProperty            Редактор свойств  для типов variant

Выбор  базового класса  для создаваемого редактора свойств основан на желаемом поведении свойства при  его  редактировании. Например, свойство может  обладать теми  же  функциональными возможностями, которые присущи  компоненту TInte- gerProperty, но при этом необходима дополнительная логика  редактирования. Сле довательно, в качестве базового класса  создаваемого редактора свойств целесообраз но использовать класс TIntegerProperty.

CОВЕТ

Иногда в создании редактора свойства для специфического типа данных нет никакой необходимости. Например, типу диапазонов автоматически назначается необходимый редактор (скажем, набор значений 1..10 будет передан компоненту TIntegerProp- erty), для перечислимых типов автоматически используются раскрывающиеся списки и т.д. Предпочтительней применять определения типов, а не пользовательские редак- торы свойств, так как они отлично поддерживаются и языком программирования во время компиляции, и используемыми по умолчанию стандартными редакторами свойств.

Редактирование свойства как текста

Редактор свойства используется для двух основных целей.  Первая достаточно ясна  и состоит в том,  чтобы  позволить пользователю отредактировать значение свойства. Дру гая, не столь  очевидная, — обеспечить строковое представление  значения свойства в окне инспектора объектов.Создавая производный класс редактора свойств, необходимо переопределить ме тоды  GetValue() и  SetValue(). Метод  GetValue() возвращает строковое  пред ставление значения свойства для  отображения в окне  инспектора объектов. Метод SetValue() осуществляет присвоение свойству  значения на основании его  строко вого представления в окне инспектора объектов.

В качестве примера рассмотрим определение класса  TIntegerProperty в модуле

DESIGNEDITORS.PAS:

TIntegerProperty = class(TOrdinalProperty)

public

function GetValue: string; override;

procedure SetValue(const Value: string); override;

end;

Как видите, методы  GetValue() и SetValue() переопределены. Ниже  показана реализация метода GetValue():

function TIntegerProperty.GetValue: string;

begin

Result := IntToStr(GetOrdValue);

end;

А вот реализация метода SetValue():

procedure TIntegerProperty.SetValue(const Value: String);

var

L: Longint;

begin

L := StrToInt(Value);

with GetTypeData(GetPropType)^ do

if (L < MinValue) or (L > MaxValue) then

raise EPropertyError.CreateResFmt(SOutOfRange,

[MinValue, MaxValue]);

SetOrdValue(L);

end;

Метод GetValue() возвращает строковое представление целочисленного свойства, а инспектор объектов использует эту строку  для отображения значения свойства. Метод GetOrdValue(), определенный в классе  TPropertyEditor, служит  для возвращения значения свойства, с которым работает редактор.

Метод  SetValue() принимает строковое значение, введенное пользователем, и присваивает его свойству  в нужном  формате. Метод  SetValue() предотвращает не которые  ошибки, осуществляя контроль вводимых пользователем значений. Метод SetOrdValue() собственно  присваивает  свойству,   с  которым  работает  редактор, введенное пользователем значение.

В классе TPropertyEditor определено несколько методов, подобных методу Ge- tOrdValue(), предназначенных для  получения  строкового представления различ ных типов.  К тому же компонент TPropertyEditor содержит методы, аналогичные методу Set, предназначенные для установки значений в соответствующих форматах. Потомки класса  TPropertyEditor наследуют  все эти  методы, используемые для по лучения  и установки значений свойств, с которыми работают редакторы. Такие  мето ды приведены в табл. 12.2.

Таблица 12.2. Методы чтения и записи свойств класса TPropertyEditor

Тип свойства

Метод “Get

Метод “Set

Число с плавающей точкой

GetFloatValue()

SetFloatValue()

Событие

GetMethodValue()

SetMethodValue()

Перечисление

GetOrdValue()

SetOrdValue()

Строка

GetStrValue()

SetStrValue()

Вариант

GetVarValue()

SetVarValue(),

SetVarValueAt()

Для иллюстрации создания нового редактора свойства вернемся к примеру с плане тами  Солнечной системы, приведенному в одной  из  предыдущих глав.  Прошлый раз был  создан  простой компонент TPlanet, представляющий одну планету.  Компонент TPlanet содержит свойство PlanetName. Его внутреннее поле  типа  Integer хранит позицию планеты в Солнечной системе, но в окне инспектора объектов оно должно  бу дет отображаться как название планеты.

Это слишком просто, поэтому усложним задачу. Допустим, необходимо предоставить пользователю возможность выбрать один из двух способов задания планеты. Он может набрать строку названия планеты, например Venus или VENUS, или VeNuS, либо ввести позицию планеты в Солнечной системе. Так, для Венеры эта позиция будет равной зна чению  2.

Ниже  приводится исходный код компонента TPlanet:

type

TPlanetName = type Integer;

TPlanet = class(TComponent)

private

FPlanetName: TPlanetName;

published

property PlanetName: TPlanetName read FPlanetName

write FPlanetName;

end;

Как видите, этот  компонент совсем небольшой. У него есть только  одно свойство PlanetName типа  TPlanetName. Специальное определение типа  TPlanetName позво ляет ему иметь собственную информацию о типах  времени выполнения и оставаться при этом целочисленным типом.

Основные функциональные возможности находятся не в компоненте TPlanet, а в редакторе свойств для типа  TPlanetName. Текст  этого  редактора приведен в листин ге 12.4.

Листинг 12.4. PlanetPE.PAS — исходный код редактора TPlanetNameProperty

unit PlanetPE;

interface usesWindows, SysUtils, DesignEditors;

type

TPlanetNameProperty = class(TIntegerProperty)

public

function GetValue: string; override;

procedure SetValue(const Value: string); override;

end;

implementation const

{ Объявление массива названий планет }

PlanetNames: array[1..9] of String[7] = (‘Mercury’, ‘Venus’,

‘Earth’, ‘Mars’, ‘Jupiter’, ‘Saturn’,

‘Uranus’, ‘Neptune’, ‘Pluto’);

function TPlanetNameProperty.GetValue: string;

begin

Result := PlanetNames[GetOrdValue];

end;

procedure TPlanetNameProperty.SetValue(const Value: String);

var

PName: string[7];

i, ValErr: Integer;

begin

PName := UpperCase(Value);

i := 1;

{ Сравнить Value с названием каждой планеты в массиве

PlanetNames. Если найдено соответствие, то переменная i

принимает значение меньше 10. }

while (PName <> UpperCase(PlanetNames[i])) and (i < 10) do

inc(i);

{ Если i меньше 10, то введено правильное название планеты.

Установка значения и выход из процедуры. }

if i < 10 then begin // Название планеты введено правильно.

SetOrdValue(i);

Exit;

end

{ Если i больше 10, то пользователь ввел недопустимый номер или

несуществующее название планеты. Использовать функцию Val для

проверки введенного значения на число. Если ValErr не равен

нулю, то неправильно введено название планеты. В противном

случае проверить введенный номер на принадлежность

диапазону (0 < i < 10). }

else begin

Val(Value, i, ValErr);

if ValErr <> 0 then

raise Exception.Create(

Format(‘Sorry, Never heard of the planet %s.’, [Value]));

if (i <= 0) or (i >= 10) then

raise Exception.Create(‘Sorry, that planet is not in OUR solar system.’); SetOrdValue(i);

end;

end;

end.Вначале  создается редактор свойства TPlanetNameProperty, являющийся по томком  TIntegerProperty. Кстати, в раздел  uses этого  модуля обязательно нужно включить модули DesignEditors и DesignIntf.

Определим массив строковых констант для представления планет  Солнечной сис темы,  в зависимости от их положения относительно Солнца.  Эти  строки будут отве чать за строковое представление планет  в окне инспектора объектов.

Как  уже  отмечалось,  необходимо переопределить  методы   GetValue() и  Set- Value(). Метод  GetValue() возвращает строку  из  массива  PlanetNames. Данный массив  индексирован по значениям свойств. Конечно, значение свойства должно  на ходиться в пределах диапазона 1–9. Поэтому  пользователю запрещено вводить в ме тод SetValue() номер, выходящий за пределы этого диапазона.

Метод  SetValue() получает  строку,  введенную  в окне  инспектора объектов. Эта

строка может  быть  как названием планеты, так и номером, определяющим позицию

планеты. Логика  кода  определяет, правильно ли  введено название планеты или  ее номер, и если да, то соответствующее значение присваивается свойству  методом Se- tOrdValue(). Если пользователь ввел неправильное название планеты или недопус тимый номер, то передается соответствующее исключение.

Вот и все определение редактора свойств. Впрочем нет, еще не все — для того что

бы свойство “узнало” о своем новом редакторе, его необходимо зарегистрировать.

Регистрация редактора свойств

Для регистрации редактора свойства следует воспользоваться процедурой Regis- terPropertyEditor(). Данный метод объявляется следующим образом:

procedure RegisterPropertyEditor(PropertyType: PTypeInfo; ComponentClass: TClass; const PropertyName: string;

EditorClass: TPropertyEditorClass);

Первый параметр PropertyType — это указатель  на информацию о типах  времени выполнения редактируемого свойства. Такую информацию можно  получить с помощью функции  TypeInfo().  Параметр  ComponentClass используется  для  обозначения класса,  к которому применяется  редактор свойства. PropertyName определяет имя свойства компонента, а параметр EditorClass — тип используемого редактора свойст ва. Для свойства TPlanet.PlanetName эта функция выглядит следующим образом:

RegisterPropertyEditor(TypeInfo(TPlanetName), TPlanet, ‘PlanetName’, TPlanetNameProperty);CОВЕТ

Пока (в иллюстративных целях) этот конкретный редактор свойств регистрируется только для использования со свойством PlanetName компонента TPlanet. Но данный редактор можно зарегистрировать таким образом, чтобы он был пригоден для работы с любым свой- ством типа TPlanetName. Для этого следует в качестве параметра ComponentClass вве- сти значение nil, а в качестве параметра PropertyName — ”.

Регистрацию  редактора  свойств  можно  оформить  вместе  с  регистрацией  компо

нента  в модуле компонента, как показано в листинге 12.5. Листинг 12.5. Planet.pas — компонент TPlanet unit Planet;

interface

uses

Classes, SysUtils;

type

TPlanetName = type Integer;

TddgPlanet = class(TComponent)

private

FPlanetName: TPlanetName;

published

property PlanetName: TPlanetName read FPlanetName

write FPlanetName;

end;

implementation end.CОВЕТ

Регистрация редактора свойств в процедуре Register() модуля компонента связы- вает код редактора с компонентом при помещении последнего в пакет. Для сложных компонентов средства времени разработки зачастую занимают больше места, чем код самих компонентов. Несмотря на то, что размер кода не играет роли для таких ма- леньких компонентов как этот, имейте в виду, что все перечисленное в разделе in- terface (интерфейс) модуля компонента (в том числе и процедура Register() вме- сте  со  всеми  классами,  регистрируемыми  ею,  такими  как  тип  класса  редактора свойств) будет скомпилировано в пакет вместе с компонентом. Поэтому регистрацию редактора свойств нужно осуществлять в отдельном регистрационном модуле. Неко- торые разработчики создают для своих компонентов как пакеты времени разработки, так и пакеты времени выполнения, причем редакторы свойств и другие средства вре- мени разработки присутствуют только в пакете разработки. Программный код приме-ров, обсуждаемых в этой книге, содержится в пакете времени выполнения DdgRT6 и пакете разработки DdgDT6.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии