|
|
Книги-onlineEvent Oriented Programming. 32 урока по Delphi Урок 28: Редакторы свойств
Содержание урока 28:
При написании новых объектов часто требуется создавать для них свои Редакторы Свойств и Редакторы Компонент. В данном уроке и рассказывается, как это сделать и приводятся примеры. Краткое описание инструментов среды Delphi и модулей, в которых реализованы соответствующие API: API Экспертов - Позволяет создать свои собственные эксперты; модули EXPINTF.PAS и VIRTINTF.PAS API Контроля Версий - Дает возможность создать свою систему Контроля Версий или подключить систему третьей фирмы; модули VCSINTF.PAS и VIRTINTF.PAS API Редакторов Компонент - Создание диалогов, связанных с объектом во время дизайна. Пример - Menu Designer для TMenu или Fields Editor для TTable; модуль DSGNINTF.PAS API Редакторов Свойств - Создание редакторов для использования их при редактировании свойств в Инспекторе Объектов; модуль DSGNINTF.PAS Модули можно найти в библиотеке визуальных компонент (в директории X:\DELPHI\SOURCE\VCL). Необходимо отметить, что большинству людей никогда не придется использовать вышеперечисленные API. Однако, некоторым программистам они очень могут пригодиться, особенно разработчикам новых объектов.
Перейдем в окно Инспектора
Объектов и выберем свойство Color - отметьте,
что справа есть маленькая стрелка, она означает, что мы можем выбрать цвет
из списка. Нажмите мышкой на эту стрелку (рис.1)
Столкнувшись с этим в первый раз Вы могли подумать, что этот список цветов является некоей функцией, жестко заданной разработчиками среды программирования Delphi. В действительности, для свойства Color используется соответствующий Редактор Свойств. И Вам вовсе не нужно работать в компании Borland, чтобы создать подобные Редакторы Свойств. Точно так же, как Вы добавляете новые компоненты в Delphi, Вы можете добавить свой собственный Редактор Свойств в среду разработки.
Взглянем на иерархию классов Редакторов Свойств. Базовым является класс TPropertyEditor: TPropertyEditor TOrdinalProperty TIntegerProperty TColorProperty TModalResultProperty TTabOrderProperty TCharProperty TEnumProperty TSetProperty TShortCutProperty TFloatProperty TStringProperty TComponentNameProperty TFontNameProperty TCaptionProperty TSetElementProperty TClassProperty TFontProperty TMethodProperty TComponentProperty
Названия классов в большинстве своем очевидны. Класс TFloatProperty связан со свойствами, которые имеют тип Float, класс TSetProperty связан со свойствами, которые имеют тип Set. Некоторые редакторы имеют специальное назначение. Так, например, TTabOrderProperty нужен для того, чтобы предотвратить изменение свойства TabOrder (тип Integer) при выборе на форме нескольких компонент одновременно. TPropertyEditor = class private FDesigner: TFormDesigner; FPropList: PInstPropList; FPropCount: Integer; constructor Create(ADesigner: TFormDesigner; APropCount: Integer); function GetPrivateDirectory: string; procedure SetPropEntry(Index: Integer; AInstance: TComponent; APropInfo: PPropInfo); protected function GetPropInfo: PPropInfo; function GetFloatValue: Extended; function GetFloatValueAt(Index: Integer): Extended; function GetMethodValue: TMethod; function GetMethodValueAt(Index: Integer): TMethod; function GetOrdValue: Longint; function GetOrdValueAt(Index: Integer): Longint; function GetStrValue: string; function GetStrValueAt(Index: Integer): string; procedure Modified; procedure SetFloatValue(Value: Extended); procedure SetMethodValue(const Value: TMethod); procedure SetOrdValue(Value: Longint); procedure SetStrValue(const Value: string); public destructor Destroy; override; procedure Activate; virtual; function AllEqual: Boolean; virtual; procedure Edit; virtual; function GetAttributes: TPropertyAttributes; virtual; function GetComponent(Index: Integer): TComponent; function GetEditLimit: Integer; virtual; function GetName: string; virtual; procedure GetProperties(Proc: TGetPropEditProc);virtual; function GetPropType: PTypeInfo; function GetValue: string; virtual; procedure GetValues(Proc: TGetStrProc); virtual; procedure Initialize; virtual; procedure SetValue(const Value: string); virtual; property Designer: TFormDesigner read FDesigner; property PrivateDirectory: string read GetPrivateDirectory; property PropCount: Integer read FPropCount; property Value: string read GetValue write SetValue; end; Методы, приведенные ниже, можно переопределять (override) для изменения поведения Редактора свойств. ( "SetXxxValue" используется для представления одного из методов SetFloatValue, SetMethodValue, SetOrdValue или SetStrValue. "GetXxxValue" обозначает GetFloatValue, GetMethodValue, GetOrdValue или GetStrValue)
paSortList: Инспектор объектов будет сортировать список, полученный от GetValues. paSubProperties: Свойство имеет подсвойства, которые будут показываться ниже в виде иерархии (outline). Если GetProperties будет генерировать объекты-свойства, то этот атрибут должен быть установлен. paDialog: Показывает, что метод Edit будет вызывать диалог. Если данный атрибут установлен, то появится кнопка '...' справа от свойства в Инспекторе Объектов. paMultiSelect: Позволяет свойству оставаться в Инспекторе Объектов, когда на форме выбрано сразу несколько объектов. Некоторые свойства не годятся для множественного выбора, например, Name. paAutoUpdate: Если этот атрибут установлен, то метод SetValue будет вызываться при каждом изменении, произведенном в редакторе, а не после завершения редактирования (пример - свойство Caption). paReadOnly: Значение менять нельзя.
Свойства и методы полезные при создании нового класса Редактора свойств:
Button1.Hint:=’Line1’#13#10’Line2’; Теперь подсказка будет состоять из двух строк. Но это достаточно неудобно, более удобно было бы формировать многострочную подсказку во время дизайна, однако редактор TStringProperty такой возможности не дает. Давайте создадим новый редактор, который мог бы это сделать. В нашем случае будет достаточно выбрать в качестве предка редактор TStringProperty и переписать некоторые методы. Во-первых, нужно переопределить метод Edit, в котором будет вызываться диалог для ввода строк подсказки. Во-вторых, нужно переопределить функцию GetAttributes, которая возвращает набор параметров, описывающих данное свойство. В частности, должен быть установлен атрибут paDialog, при этом в Инспекторе Объектов у свойства появится кнопка ‘…’ для вызова диалога. И вообще-то нужно изменить метод GetValue, который используется для отображения значения свойства в Инспекторе Объектов. Назовем новый Редактор Свойств THintProperty, декларация нового класса: THintProperty = class(TStringProperty) public function GetAttributes: TPropertyAttributes; override; function GetValue : String; override; procedure Edit; override; end; Рассмотрим по порядку методы нового класса. Функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (появляется кнопка ‘…’) и paReadOnly (свойство нельзя редактировать непосредственно в Инспекторе Объектов, а только в диалоге, вызываемом через кнопку ‘…’): function THintProperty.GetAttributes: TPropertyAttributes; begin Result := inherited GetAttributes + [paDialog, paReadOnly]; end; Функция GetValue заменяет “неправильные” символы #10 и #13 (перевод каретки и переход на новую строку) на символ “>”: function THintProperty.GetValue : string; var i : Byte; begin result:=inherited GetValue; for i:=1 to Byte(result[0]) do if result[i]<#32 then result[i]:='>'; end; Процедура Edit вызывает диалог для ввода строк подсказки. Диалог можно было бы нарисовать свой собственный, однако можно воспользоваться уже готовым. Несколько разных диалогов лежит в директории X:\DELPHI\SOURCE\LIB. Мы воспользуемся модулем STREDIT.PAS, в котором есть необходимый диалог редактирования строк. Итак, процедура Edit: procedure THintProperty.Edit; var HintEditDlg : TStrEditDlg; s : string; begin HintEditDlg:=TStrEditDlg.Create(Application); with HintEditDlg do try Memo.MaxLength := 254; s:=GetStrValue+#0; Memo.Lines.SetText(@s[1]); UpdateStatus(nil); ActiveControl := Memo; if ShowModal = mrOk then begin s:=StrPas(Memo.Lines.GetText); if s[0]>#2 then Dec(Byte(s[0]),2); SetStrValue(s); end; finally Free; end; end; Строка if s[0]>#2 then Dec(Byte(s[0]),2) нужна, так как Memo.Lines.GetText возвращает все строки с символами #13#10.
procedure Register; begin RegisterPropertyEditor(TypeInfo(String), TControl, 'Hint', THintProperty); end; Как уже сообщалось выше,
один и тот же редактор свойств можно “привязать” к свойствам, в зависимости
от их названия или типа объекта. Это определяется параметрами (второй и
третий), которые передаются во время регистрации в процедуре RegisterPropertyEditor.
Возможны четыре варианта:
Если указан только класс, то редактор относится ко всем свойствам указанного типа для объектов указанного класса. Если указано только имя, то редактор относится к свойствам указанного типа, которые имеют указанное имя. В нашем случае Редактор Свойств зарегистрирован для всех свойств, которые имеют тип String, относятся к компоненте класса TControl или наследника от него и имеют имя ‘Hint’.
В диалоге нажмите “OK” и запустите программу. Полный текст модуля с Редактором Свойств см. в примерах к данному уроку.
Давайте взглянем на класс TComponentEditor в модуле DSGNINTF.PAS: TComponentEditor = class private FComponent: TComponent; FDesigner: TFormDesigner; public constructor Create(AComponent: TComponent; ADesigner: TFormDesigner); virtual; procedure Edit; virtual; procedure ExecuteVerb(Index: Integer); virtual; function GetVerb(Index: Integer): string; virtual; function GetVerbCount: Integer; virtual; procedure Copy; virtual; property Component: TComponent read FComponent; property Designer: TFormDesigner read FDesigner; end; Редактор Компонент создается для каждого выбранного объекта на форме основываясь на классе объекта. При двойном щелчке на объекте вызывается метод Edit Редактора Компонент. При вызове контекстного меню (popup menu) по правой кнопке мыши, то для построения этого меню вызываются методы GetVerbCount и GetVerb. Если в этом меню выбирается пункт, то вызывается метод ExecuteVerb. Copy вызывается при копировании компонента в Clipboard. Редактор Компонент по умолчанию (TDefaultEditor) при двойном щелчке на объекте создает (или переходит на) в Редакторе Исходного Текста заготовку для событий OnCreate, OnChanged или OnClick (какое первым попадется). При создании Редактора Компонент вы должны переопределить либо метод Edit, либо три следующих метода: GetVerb, GetVerbCount и ExecuteVerb. Можно переопределять все четыре метода. Если Редактор Компонент был вызван и изменил компонент, то всегда обязательно нужно вызвать метод Designer.Modified, чтобы Дизайнер об этом узнал. Методы и свойства TComponentEditor: Create(AComponent, ADesigner): Конструктор Редактора Компонент. AComponent - редактируемый компонент. ADesigner - интерфейс к Дизайнеру среды Delphi. Edit: Вызывается при двойном щелчке мышью на компоненте. Редактор Компонент может вызвать какой-нибудь диалог или эксперт. ExecuteVerb(Index): Выполняется, когда был выбран пункт номер Index из контекстного меню. Считается, что Редактор Компонент знает, как проинтерпретировать это значение. GetVerb(Index): Редактор Компонент должен вернуть в этом методе строку, которая будет показана в виде пункта контекстного меню. Можно использовать обычные для пунктов меню символы, например &. GetVerbCount: Возвращает число, которое определяет на какие значения будут отвечать методы GetVerb и ExecuteVerb. Например, если это число равно 3, то в меню будет добавлено три пункта со значениями Index от 0 до 2. Copy: Вызывается, когда компонент нужно скопировать в Clipboard. На самом деле, образы полей компонента уже находятся в Clipboard. Просто предоставляется возможность скопировать различные типы форматов, которые игнорируются Дизайнером, но которые могут быть распознаны другими приложениями. Декларация нового класса Редактора Компонент: TButtonEditor = class(TComponentEditor) private procedure HiThere; public procedure Edit; override; procedure ExecuteVerb(Index: Integer); override; function GetVerb(Index: Integer): string; override; function GetVerbCount: Integer; override; end; Процедура HiThere и будет показывать сообщение и изменять свойство Caption: procedure TButtonEditor.HiThere; begin MessageDlg('Hi! It replaces Default Component Editor.', mtInformation, [mbOK], 0); (Component as TButton).Caption:='Hi!'; Designer.Modified; end; Процедуры Edit и ExecuteVerb только вызывают HiThere: procedure TButtonEditor.Edit; begin HiThere; end; procedure TButtonEditor.ExecuteVerb(Index: Integer); begin if Index = 0 then HiThere; end; Процедуры GetVerb и GetVerbCount определяют вид контекстного меню: function TButtonEditor.GetVerb(Index: Integer): string; begin result:='&Get message ...' end; function TButtonEditor.GetVerbCount: Integer; begin result:=1; end; Здесь в контекстное меню добавляется один пункт “Get message …”. Редактор Компонент готов. Необходимо зарегистрировать новый Редактор Компонент, это делается аналогично регистрации Редактора Свойств, только проще: procedure Register; begin RegisterComponentEditor(TButton, TButtonEditor); end; После того, как Вы подключите новый Редактор Компонент в среду Delphi, а это делается в пункте меню “Options|Install Components”, создайте новый проект, положите на форму объект TButton и щелкните дважды на нем - появится диалог:
После того, как Вы нажмете “OK”, текст на кнопке изменится. Созданный нами Редактор Компонент заместит Редактор по умолчанию для всех объектов класса TButton и его наследников, например, TBitBtn. Полный текст Редактора Компонент приведен в файле SBEDIT.PAS в примерах к данному уроку. Внимание! Если у вас не получилось найти нужную информацию, используйте рубрикатор или воспользуйтесь поиском . книги по программированию исходники компоненты шаблоны сайтов C++ PHP Delphi скачать |
|