Содержание материала

 

Как уже было сказано, класс TGsvObjectInspectorTypeListInfo предоставляет дополнительную функциональность при работе со свойствами - перечислимыми типами. Класс переопределяет методы IntegerToString, StringToInteger и FillList, а для задания списка перечислений вводит новый виртуальный метод ListEnumItems - этот метод напоминает ChildrenInfo базового класса, но возвращает не типовые метаданные, а свойства каждого элемента перечисления - его имя и ассоциированное с ним значение - эти параметры определены записью TGsvObjectInspectorListItem. Конкретный метакласс, описывающий свойства-перечисления может быть порожден от класса TGsvObjectInspectorTypeListInfo, причем достаточно будет переопределить только метод ListEnumItems. Метод FillList выполняет итерацию по всем перечислимым значениям, вызывая ListEnumItems с монотонно возрастающим индексом до тех пор, пока ListEnumItems не вернет значение nil. Результаты итерации передаются визуальному компоненту инспектора через параметр List. Для преобразования строкового вида значения перечисления к целочисленному виду и для обратного преобразования служат методы StringToInteger и IntegerToString, алгоритм которых очень похож - оба они итерируют список перечислений, но в первом случае критерием для поиска является строковое имя, а во втором случае - ассоциированное с ним значение. Очевидно, что такой базовый класс может быть использован для любых перечислимых типов, причем даже таких, в которых значения перечисления не образуют упорядоченную монотонную последовательность.

Code:

type

TGsvObjectInspectorTypeFontInfo = class(TGsvObjectInspectorTypeInfo)

public

   classprocedure ShowDialog(Inspector: TComponent;

                   Info: PGsvObjectInspectorPropertyInfo;

                   const EditRect: TRect); override;

   classfunction ObjectToString(const Value: TObject):

                   String; override;

end;

 

classprocedure TGsvObjectInspectorTypeFontInfo.ShowDialog(

   Inspector: TComponent;

   Info: PGsvObjectInspectorPropertyInfo; const EditRect: TRect);

var

   dlg: TFontDialog;

   fnt: TFont;

begin

   ifnot Assigned(Info) then

     Exit;

   ifnot Assigned(Info^.NestedObject) then

     Exit;

   ifnot (Info^.NestedObject is TFont) then

     Exit;

   fnt := TFont(Info^.NestedObject);

   dlg := TFontDialog.Create(Inspector);

   try

     dlg.Font.Assign(fnt);

     if dlg.Execute then

       fnt.Assign(dlg.Font);

   finally

     dlg.Free;

   end;

end;

 

classfunction TGsvObjectInspectorTypeFontInfo.ObjectToString(

   const Value: TObject): String;

begin

   if Assigned(Value) then

     if Value is TFont then

       with TFont(Value) do

         Result := Format('%s, %d', [Name, Size]);

end;

 

Класс TGsvObjectInspectorTypeFontInfo демонстрирует способ создания метакласса для специфического редактора свойства, в данном случае, для свойства-шрифта, имеющего тип TFont. Здесь переопределяются два метода - ShowDialog и ObjectToString. Методу ShowDialog передаются три аргумента:

 

Inspector - родительский компонент для формы-диалога,

 

Info - метаданные свойства,

 

 

EditRect - прямоугольник, представляющий собой экранные координаты поля редактирования визуального компонента инспектора. Эти координаты можно использовать для того, чтобы расположить диалог, скажем, прямо под значением редактируемого свойства (подобно списку). Конечно, это имеет смысл только для небольших по размеру диалогов.

Для свойств, отображающих диалог, менеджер заполняет поле метаданных NestedObject - оно указывает на инспектируемый объект или его заместитель. В данном случае менежер увидит, что свойство-шрифт является объектом-классом и определит его адрес, используя адрес объекта верхнего уровня в дереве объектов-свойств и имя свойства. Если бы это было простое свойство, например, TColor, то менеджер заполнил бы поле NestedObject указателем на объект текущего уровня.

После того, как мы определили, что инспектируемое свойство действительно является объектом нужного нам типа (в данном случае TFont), мы создаем диалог, инициализируем его данные текущим значением свойства, отображаем диалог и при успешном завершении переносим новое значение свойства в инспектируемый объект.

Другой метод класса - ObjectToString определяет то, как будет выглядеть значение свойства в инспекторе. В данном случае мы считаем, что основные свойства шрифта - это его имя и размер. Такой способ отображения отличается от того, что мы видим в инспекторе Delphi - в качестве значения объекта Delphi отображает имя его типа.

Визуальный компонент инспектора

В этом разделе мы рассмотрим визуальный компонент инспектора, его основные методы и события, а также некоторые пользовательские аспекты, какие, как хинты. Причем, для простоты опустим аспекты реализации и, кроме того, будем использовать понятия "инспектор" и "визуальный компонент инспектора" как синонимы.

Как это принято в Delphi, визуальный компонент представлен в двух формах - как TGsvCustomObjectInspectorGrid и, соответственно, TGsvObjectInspectorGrid. Опуская детали реализации и не очень важные свойства, класс инспектора определяется так:

Code:

type

TGsvCustomObjectInspectorGrid = class(TCustomControl)

protected

   property LongTextHintTime: Cardinal;

   property LongEditHintTime: Cardinal;

   property AutoSelect: Boolean;

   property HideReadOnly: Boolean;

 

   property OnEnumProperties:

             TGsvObjectInspectorEnumPropertiesEvent;

   property OnGetStringValue:

             TGsvObjectInspectorGetStringValueEvent;

   property OnSetStringValue:

             TGsvObjectInspectorSetStringValueEvent;

   property OnGetIntegerValue:

             TGsvObjectInspectorGetIntegerValueEvent;

   property OnSetIntegerValue:

             TGsvObjectInspectorSetIntegerValueEvent;

   property OnFillList:

             TGsvObjectInspectorFillListEvent;

   property OnShowDialog:

             TGsvObjectInspectorShowDialogEvent;

   property OnHelp: TGsvObjectInspectorInfoEvent;

   property OnHint: TGsvObjectInspectorInfoEvent;

 

public

   procedure NewObject;

   procedure Clear;

   procedure ExpandAll;

   procedure CollapseAll;

end;

 

Вначале отметим самые простые свойства и методы:

 

AutoSelect - если AutoSelect установить в True, то при выборе свойства, доступного для редактирования весь его текст будет выделяться,

 

HideReadOnly - если установить в True, то инспектор будет скрывать все свойства, доступные только по чтению,

 

 

Clear - вызов этого метода очистит инспектор, что означает отсутствие инспектируемого объекта,

 

ExpandAll - раскрыть все вложенные веточки дерева свойств,

 

 

CollapseAll - свернуть все вложенные веточки.

Цикл событий инспектора при инспектировании начинается с вызова метода NewObject. Это приведет к тому, что инспектор начнет циклически вызывать событие OnEnumProperties. Сигнатура обработчика этого события следующая:

Code:

TGsvObjectInspectorEnumPropertiesEvent = procedure(Sender: TObject;

Index: Integer; out Info: PGsvObjectInspectorPropertyInfo) ofobject;

 

Обработчику передается монотонно увеличивающееся значение Index и, при каждом обращении, обработчик должен вернуть в out-аргументе указатель на метаданные очередного свойства или nil, если все свойства перечислены. Обработчик может выглядеть так:

Code:

procedure TForm1.OnEnumProperties(Sender: TObject; Index: Integer;

   out Info: PGsvObjectInspectorPropertyInfo);

begin

   Info := ObjectManager.PropertyInfo(Index);

end;

 

То есть, запрос на очередное свойство просто передается менеджеру. После того, как все свойства перечислены, инспектор начинает отображение имен свойств и их значений. При этом, для доступа к значениям свойств он вызывает один из обработчиков OnGetStringValue или OnGetIntegerValue в зависимости от того, имеет ли значение свойства текстовое представление или графическое (например, значения boolean-свойств отображаются как CheckBox и не имеют текста). Обработчики этих событий также выглядят очень просто, например:

Code:

procedure TForm1.OnGetStringValue(Sender: TObject;

   Info: PGsvObjectInspectorPropertyInfo; out Value: String);

begin

   try

     Value := ObjectManager.GetStringValue(Info);

   except

     on E: Exception do

       StatusMessage('Error: ' + E.Message);

   end;

end;

 

Это общий принцип - обработчик просто перенаправляет запрос менеджеру, который обрабатывает его сам, или, в свою очередь, перенаправляет метаклассам. Если пользователь изменяет значение свойства, то формируется событие OnSetStringValue (или OnSetIntegerValue). Если пользователь нажимает кнопку выпадающего списка, то формируется событие OnFillList, и после заполнения списка, инспектор отображает его. Если нажимается кнопка диалога (обозначаемого, как и в Delphi, тремя точками), формируется событие OnShowDialog. При выборе нового свойства формируется событие OnHint, которое можно обработать, например, так:

Code:

procedure TForm1.OnHint(Sender: TObject;

   Info: PGsvObjectInspectorPropertyInfo);

begin

   if Assigned(Info) then

     StatusBar.SimpleText := Info^.Hint;

end;

 

то есть, просто вывести строку хинта из метаданных в статусную строку или в специальное окно подсказок. Хинт может быть весьма длинным, чтобы ясно изложить подсказку по свойству. Это облегчает работу пользователя при большом числе объектов и их свойств. Если пользователь нажимает клавишу F1, то формируется событие OnHelp, по которому программа вызывает справочную подсистему. Всплывающие подсказки (tooltips) используются в инспекторе для других целей, а именно, для отображения длинных имен и значений, которые не вмещаются в поля инспектора, например:

Контролирует такие подсказки свойство LongTextHintTime - его значение определяет время, в течении которого "длинная" всплывающая подсказка будет отображаться. Если этому свойству присвоить 0, то подсказка отображаться не будет. Другой тип всплывающей подсказки связан с редактированием значений, текст которых не помещается в поле редактирования, например:

При отображении всплывающей подсказки редактирования курсор мыши приобретает вид стрелки вверх и перемещается на область подсказки, чтобы не мешать редактированию. Контролируются подсказки редактирования свойством LongEditHintTime аналогично LongTextHintTime.

Завершающие штрихи

Вот, собственно, и все, что мне хотелось объяснить при описании заявленной темы. Остается только добавить, что представленный в статье инспектор доступен в исходных текстах как FreeWare без каких-либо оговорок, кроме единственной - уважать авторские права. Код инспектора размещен в двух модулях:

 

GsvObjectInspectorGrid.pas - визуальный компонент,

 

GsvObjectInspectorTypes.pas - все определения, базовый и вспомогательные метаклассы, менеджер, реестр и вспомогательные процедуры.

Весь код достаточно полно комментирован, так что можно всегда обратиться к нему при возникновении вопросов.

Кроме того, к тексту инспектора приложен простенький пример, картинки из которого использованы в статье: модули UnitMainForm (главная форма примера) и UnitInfo (классы метаданных для объектов, инспектируемых в примере). Пример можно компилировать не устанавливая компонент инспектора в палитру компонентов, так как компонент создается явно во время выполнения.

 

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить