СТАТЬЯ | 19.09.01 |
Профессиональная разработка приложений с помощью Delphi5
Часть 2. Создание компонентов Delphi
Редактор свойств используется средой разработки для изменения значения свойства в инспекторе объектов. Редактор же компонента используется для изменения состояния компонента, минуя инспектор объектов. Он вызывается при щелчке правой кнопки мыши над компонентом, поставленным на форму. При этом появляется всплывающее меню, и если для данного компонента имеется редактор, то он вставляет в это меню свои команды.
Редактор компонента является потомком класса TComponentEditor. Для того чтобы в всплывающем меню компонента были вставлены новые команды, необходимо переписать два метода TComponentEditor:
TDWComponentEditor=class(TComponentEditor) public function GetVerbCount:integer; override; function GetVerb(Index:integer):string; override; end;
Сами названия этих методов позволяют догадаться, как меню, определенное программистом в редакторе компонента, вставляется во всплывающее меню компонента. Среда разработки вызывает метод GetVerbCount, и если возвращается ненулевое значение, то вызывает несколько раз метод GetVerb с соответствующим индексом в качестве параметра. Реализация методов в этом случае выглядит следующим образом:
function TDWComponentEditor.GetVerbCount:integer; begin Result:=3; end; function TDWComponentEditor.GetVerb(Index:integer):string; begin case Index of 0:Result:='Copyright (C) 2001'; 1:Result:='by me'; 2:Result:='&Edit...'; end; end;
Как и редактор свойств, редактор компонента нуждается в регистрации. Для этого используется метод RegisterComponentEditor, вызываемый средой разработки, при этом его вызов следует поместить в процедуру Register модуля DayStore.pas:
RegisterComponentEditor(TDayStore,TDWComponentEditor);
Метод зависит от двух параметров: класса компонента и класса редактора компонента. Класс компонента означает, что данный редактор компонента будет вызываться для компонента главного класса и всех его потомков. В качестве шутливого упражнения можно попробовать зарегистрировать редактор компонента с классом компонента TComponent вместо TDayStore. Теперь, какой бы компонент вы ни поместили на форму, при нажатии правой кнопки мыши верхние команды всплывающего меню будут говорить о том, что данный компонент принадлежит вам.
Если для одного из потомков класса, который имеет редактор компонента, регистрируется новый редактор компонента, то старый редактор отменяется и больше не вызывается для данного класса и его потомков.
Напоминаем, что после изменения в процедуре Register компонент обязан быть переинсталлирован.
Сколько бы мы ни нажимали на соответствующие строки во всплывающем меню, ничего происходить не будет. Этого и следовало ожидать, поскольку в данной реализации редактора компонента нигде не описано, что необходимо делать при вызове соответствующего элемента меню. Это достигается переписыванием метода
ExecuteVerb(Index:integer);
Из его объявления становится ясно, что среда разработки вызывает его, когда программист выбрал какой-либо элемент меню. Индекс выбранного элемента передается в качестве параметра этого метода. Но перед тем, как реализовать данный метода необходимо поставить задачу. В качестве простой задачи можно выбрать следующую: редакция свойства дня недели через редактор компонента – вызов диалога. Однако реализовать вызов диалога редакции дня недели из редактора компонента сложнее, чем из редактора свойств. Проблема заключается в том, что в редакторе компонента отсутствуют методы GetOrdValue и SetOrdValue. Зато в редакторе компонента имеется свойство Component:TComponent, которое содержит ссылку на экземпляр компонента в среде разработки, для которого было вызвано меню. Им и следует воспользоваться для редакции свойства. Проблема того, что свойство DayWeek не реализовано на уровне TComponent, решается явным приведением типов. Поэтому в секции implementation модуля DayPropE.pas помещаем оператор uses DayStore. Теперь оба модуля циклически ссылаются друг на друга, но иначе привести тип TComponent к типу TdayStore невозможно.
procedure TDWComponentEditor.ExecuteVerb(Index:integer); var F2:TForm2; begin if Index=2 then begin F2:=nil; try F2:=TForm2.Create(nil); F2.ListBox1.ItemIndex:=TDayStore(Component).DayWeek-1; if F2.ShowModal=mrOK then begin TDayStore(Component).DayWeek:=F2.ListBox1.ItemIndex+1; Designer.Modified; end; finally if Assigned(F2) then F2.Release; end; end else ShowMessage('E-mail: aaa@bbb.cc.ru'); end;
Обратите внимание на обращение к свойству Designer.Modified. Вызовом этого метода мы информируем среду разработки о том, что свойства компонента изменились. Это заставляет среду разработки выполнить ряд действий: обновить содержимое инспектора объектов, сделать активным меню Save.
Еще один полезный метод, который можно переписать в редакторе компонента, – метод Edit. Он вызывается при двойном щелчке мыши на компоненте на этапе разработки. Если его не переписывать, а возвращаемое значение метода GetVerbCount при этом больше нуля, то выполняется метод с нулевым индексом, то есть вызывается команда ExecuteVerb(0). В следующем разделе мы перепишем этот метод.
Класс TFiler и сохранение данных в ресурсах
При редакции значений свойств в инспекторе объектов новые значения сохраняются в ресурсах проекта. Соответственно при запуске приложения ресурсы загружаются после создания компонентов на форме. Среда разработки Delphi запоминает в ресурсах только данные, отображаемые в инспекторе объектов. Предположим, имеется свойство, которое никаким способом невозможно представить в инспекторе объектов в виде строки. Такого типа свойства встречаются довольно часто – данные в двоичном формате. Их можно редактировать на этапе разработки при помощи, например, редактора компонента с диалогом загрузки файла. Однако эти данные не будут сохранены в ресурсах проекта и, следовательно, загружены во время приложения.
Выход из данной ситуации заключается в переписывании метода DefineProperties(Filer: TFiler), который определен на уровне TComponent. TFiler заведует сохранением в ресурсах свойств, редактируемых в инспекторе объектов. По умолчанию в ресурсы попадают все опубликованные свойства компонента. TFiler имеет методы DefineProperty и DefineBinaryProperty, используя которые, можно поместить в ресурсы неопубликованные данные.
Определим задачу следующим образом. Будем помещать в ресурсы неопубликованные двоичные данные (помещать в ресурсы остальные данные неинтересно, их можно вывести в инспектор объектов). Чтобы не связываться с какими-либо форматами и упрощением ввода и редакции данных, в качестве модели двоичных данных будем рассматривать содержимое единственной строки. Строку будем редактировать в методе Edit редактора компонента.
Перед началом работы необходимо убрать все компоненты TDayStore с формы проекта. Компиляцию далее следует производить только из редактора пакета.
Итак, в компоненте – классе TDayStore в секции private определим переменную FData:string, а в секции public (не published!) определим свойство:
Data:string read FData write FData.
Затем в секции protected определим заголовок переписываемого метода:
DefineProperties(Filer :TFiler); override;
При реализации этого метода требуется вызвать метод TFiler – DefineBinaryProperties. В качестве параметров этот метод принимает ссылку на процедуру записи данных в поток и ссылку на процедуру считывания данных из потока. Их также необходимо реализовать в компоненте TDayStore, заголовки этих процедур следует поместить в секции private класса TDayStore. Заголовок TDayStore после внесения всех изменений выглядит следующим образом:
TDayStore = class(TComponent) private FDay:TDayWeek; FData:string; procedure SaveToStream(Stream:TStream); procedure LoadFromStream(Stream:TStream); protected procedure DefineProperties(Filer:TFiler); override; public constructor Create(AOwner:TComponent); override; property Data:string read FData write FData; published property DayWeek:TDayWeek read FDay write FDay; end;
В реализации переписанного метода DefineProperties в первую очередь следует вызвать метод DefineProperties класса TComponent (используя директиву Inherited). Без этого наш компонент станет вести себя некорректно: он все время будет находиться в левом верхнем углу формы. Метод DefineProperties класса TComponent записывает в ресурсы координаты левого верхнего угла пиктограммы – для невизуальных компонентов они не отображаются в инспекторе объектов:
procedure TDayStore.DefineProperties(Filer:TFiler); begin inherited DefineProperties(Filer); Filer.DefineBinaryProperty('BinData',LoadFromStream, SaveToStream,length(FData)>0); end;
Следующий оператор ‑ вызов метода TFiler DefineBinaryProperty. В качестве параметра он принимает имя нового свойства, которое не должно совпадать с именами уже имеющихся свойств, ссылку на процедуры чтения и записи двоичных данных, и последний параметр сообщает среде разработки, является содержимое пустым или нет. Если содержимое пустое, данные не запоминаются в ресурсах, а если нет – записываются в ресурсы. Если в компоненте имеется несколько различающихся двоичных типов данных, метод DefineBinaryProperties может быть вызван несколько раз с различными именами данных и разными процедурами чтения/записи данных.
Реализация методов чтения и записи данных выглядит следующим образом:
procedure TDayStore.SaveToStream(Stream:TStream); var L:longint; begin L:=length(FData); Stream.Write(L,sizeof(L)); if L>0 then Stream.Write(FData[1],L); end; procedure TDayStore.LoadFromStream(Stream:TStream); var L:longint; begin Stream.Read(L,sizeof(L)); SetLength(FData,L); if L>0 then Stream.Read(FData[1],L); end;
Реализация метода SaveToStream очевидна. В методе LoadFromStream используется процедура SetLength, которая выделяет память строке заданной длины и делает строку уникальной – с числом ссылок, равным единице. Только после вызова метода SetLength строку можно использовать как буфер чтения данных, иначе может не хватить памяти или испортится содержимое других строк, которые ссылаются на ту же самую область памяти.
Теперь необходимо переписать метод Edit в редакторе компонентов TDWComponentEditor так, чтобы мы могли менять содержимое свойства Data. Будем использовать метод InputQuery для показа старого и ввода нового значения свойства Data:
procedure TDWComponentEditor.Edit; var S:string; begin S:=TDayStore(Component).Data; if InputQuery('New property','Data:',S) then begin TDayStore(Component).Data:=S; Designer.Modified; end; end;
Обратите внимание, что не вызывается inherited-метод. В классе TComponentEditor метод Edit вызывает метод ExecuteVerb(0), что нам не требуется, поскольку у нас свой диалог!
Компонент заново регистрируется, после чего можно приступать к тестированию. Создадим новый проект, поставим компонент TDayStore на форму. Дважды щелкнем по нему и наберем какую-либо строку. После этого сохраним проект, затем остановим и загрузим заново Delphi. Вновь откроем данный проект, дважды щелкнем по компоненту. Теперь диалог показывает строку, введенную в данный диалог ранее.
Чтобы окончательно убедиться, что данные попали в ресурс проекта, можно щелкнуть правой клавишей мыши по форме и вызвать команду View as text:
object Form1: TForm1 Left = 194 Top = 107 Width = 213 Height = 183 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object DayStore1: TDayStore DayWeek = 1 Left = 128 Top = 48 BinData = {0F0000005465737420646174612073746F7265} end end
Итак, мы видим, что в ресурсах DayStore1 имеется свойство BinData.
Из сказанного выше следует, что методика создания самих компонентов настолько тривиальна, что ее без труда может освоить даже начинающий программист. Выгода использования компонентов при сборке приложений очевидна: экономия времени, упрощение процесса отладки и внесения изменений. При этом созданное приложение должно подчиняться вполне определенным требованиям, но об этом мы поговорим в следующей статье данного цикла.
Дополнительную информацию Вы можете получить в компании Interface Ltd.
Отправить
ссылку на страницу по e-mail
Обсудить на форуме Inprise/Borland
Interface Ltd. Отправить E-Mail http://www.interface.ru |
|
Ваши замечания и предложения
отправляйте автору По техническим вопросам обращайтесь к вебмастеру Документ опубликован: 19.09.01 |