Для создания нового компонента в среде Delphi рекомендуется воспользоваться специальным мастером, который вызывается с использованием пункта меню Component/New VCL Component…
Процесс создания заготовки нового компонента показан на рис. 2.1 и описан ниже.
В появившемся окне мастера (см. Рисунок 1) нужно выбрать класс, от которого наследуется новый компонент.
Для графического элемента управления следует выбрать класс TGraphicControl.
Для ускорения выбора имени класса в появившемся списке можно последовательно нажимать клавиши с первыми буквами имени класса-наследника.
Нажмите кнопку Next>>.
В появившемся втором окне мастера (Рисунок 2) нужно ввести необходимые данные.
1. В поле Class Name (Имя класса) нужно ввести имя класса нового компонента.
Пример 2.1.
В качестве примера будет создан компонент, обеспечивающий изображение почтового индекса.
К имени класса предъявляются те же требования, что и к имени переменных и констант: оно должно начинаться с латинской буквы и состоять только из латинских букв, цифр и знаков подчеркивания.
Пробелы в имени класса недопустимы!
Настоятельно рекомендуется название класса начинать с прописной буквы T. Для обозначения авторства компонента можно или после буквы T или в конце имени класса ввести свои инициалы.
Начиная с версии 2006 года, Delphi поддерживает символы национальных алфавитов в именах переменных и классов. Пользоваться этой возможностью без необходимости не следует, так как это неминуемо приведет к дополнительным сложностям в управлении проектом.
2. В поле Palette Page (Страница палитры компонентов) нужно ввести название палитры компонентов, на которой будет размещаться новый компонент. Его можно выбрать из выпадающего списка (который появиться после нажатия на рядом расположенной кнопке) или ввести имя новой страницы. Здесь допустимы любые символы, в том числе и из национальных алфавитов и пробелы.
3. В поле Unit Name (Имя модуля) нужно ввести имя модуля, который будет содержать объявление и реализацию нового класса.
Модулю следует давать имя созвучное с именем класса. Самый простой вариант – имя класса без первой буквы T.
В том случае, если модуль будет содержать более одного класса (а в реальных библиотеках так чаще всего и бывает), то модуль следует назвать каким-то общим именем.
Пример 2.3.
Например, если в модуле реализованы классы TTestClock и TCounterClock, представляющие собой различные виды часов (счетчиков). Тогда модуль можно назвать, например, Clocks. Однако, такое название не совсем удачно, так как оно может совпасть с именем модуля из совершенно другой библиотеки. Достаточно добавить к имени свои инициалы (разумеется, латинскими буквами) и «проблема» будет решена.
Избегайте «нарицательных» названий модулей. Включайте в них свои инициалы.
4. Поле Search Path (Путь поиска) можно оставить без изменения. Он нужен для того, чтобы компилятор нашел все модули, расположенные в разных папках.
После того, как все данные будут введены, следует нажать кнопку Next>> и перейти к завершающему диалогу мастера (Рисунок 3).
В конечном диалоге нечего изменять, а можно лишь согласиться с созданием модуля, нажав кнопку Finish.
Если бы перед созданием компонента был создан пакет (Package), то в диалоге присутствовал бы еще вариант Add unit to package, обеспечивающий автоматическое включение вновь созданного модуля в пакет для последующей регистрации. О процедуре регистрации см. ниже.
Коечным результатом работы мастера является модуль на подобии приведенного ниже.
Пример 2.4. Пример сгенерированного модуля
unit syPost; interface uses SysUtils, Classes, Controls; type TsyPostZip = class(TGraphicControl) private { Private declarations } protected { Protected declarations } public { Public declarations } published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Юшинин', [TsyPostZip]); end; end.
Не смотря на то, что имя модуля было явно указано, он не сохранен в файл. Сохранить его можно и нужно с помощью пункта меню File/Save As… или сочетания клавиш <Ctrl+S>.
Из приведенного листинга видно, что в интерфейсном разделе (interface) модуль содержит объявление класса, при этом в разделе uses упомянут модуль, в котором реализован класс-предок. В данном случае это – модуль Controls.
Узнать, в каком модуле объявлен класс (тип, переменная или константа) можно с помощью справочной системы. Для этого нужно поставить курсор на название класса (в приведенном примере - TGraphicControl) и нажать клавишу <F1>. В появившемся окне справки можно найти упоминание, в каком именно модуле объявлен тот или иной элемент.
Так же в интерфейсной части размещено объявление в реализационной части (implementation) процедуры Register, отвечающей за регистрацию компонента.
Как видно из кода модуля, пока новый класс ничем не отличается от своего предка. Однако для того, чтобы продолжить над ним работы необходимо создать тестовое приложение для испытания нового компонента.
Создается тестовое приложение, как простой проект c помощью пункта меню New/ VCL Forms Application – Delphi for Win32.
Точное название пункта меню зависит от версии Delphi.
Имеет смысл сохранить вновь созданный проект в той же папке, в которой только что был сохранен модуль компонента.
Строго говоря, можно сохранить проект и в совершенно другом месте, а Delphi настроить так, чтобы вновь созданный компонент был доступен для любых проектов, но об этом – в следующих лабораторных работах.
Проект следует сохранить под «осмысленным» именем, чтобы в дальнейшем было понятно, для чего он предназначен.
Пример 2.5.
Например, тестовое приложение для испытания почтового компонента может называться TestPost.
Начинать тестирование далекого от готовности, так сказать «сырого», компонента имеет смысл с этапа выполнения в откомпилированной программе (runtime), а уже потом, после первичной отладки – регистрировать его и проверять его поведение во время проектирования (designtime).
Так и поступим. Для главной формы тестового приложения создадим обработчик события OnCreate.
Сделать это можно с помощью Object Inspector (инспектора объектов) (если его не видно – нажмите клавишу <F11>) на закладке Events (события). Для этого нужно сделать двойной щелчок в строке OnCreate.
Для обработчика события по умолчанию (а для формы это именно OnCreate) есть еще более простой способ - двойной щелчок по форме по время проектирования.
В появившейся заготовке обработчика кода объявим локальную переменную того же типа, что и разработанный класс.
Редактор кода подчеркнул название типа красной волнистой линией? Все правильно. Разработанный класс еще не доступен тестовому проекту, поскольку модуль, в котором он объявлен, не включен в список используемых проектом модулей с помощью.
Для того чтобы тип (класс) стал доступен нужно модуль, в котором он объявлен, добавить в перечень используемых модулей Uses.
Собственно в обработчике события теперь можно создать экземпляр компонента, вызвав конструктор класса.
Чтобы не ошибиться в названии класса, можно воспользоваться подсказкой редактора кода. Для этого нужно набрать одну или несколько первых букв из названия класса и нажать сочетание клавиш <Ctrl>+<Пробел> - редактор кода сформирует список доступных языковых конструкций. Остается лишь выбрать из них нужную и нажать <Enter>.
После ввода имени класса нужно нажать <.> и ввести название конструктора Create. Конструктор принимает единственный параметр - ссылку на компонент-хозяин, который должен будет разрушить созданный экземпляр в случае необходимости. Чаще всего за это в ответе форма приложения, поэтому можно передать параметр по имени Self.
Другим важным свойством является Parent – компонент, который в ответе за отображение на экране. Его тоже можно установить в Self.
Пример 2.6.
procedure TForm1.FormCreate(Sender: TObject); var TestZIP1:TsyPostZip; begin TestZIP1:=TsyPostZip.Create(Self); TestZIP1.Parent:=Self; end;
Теперь можно запустить тестовое приложение. Если все сделано правильно, то вы увидите пустую форму. Точнее будет сказать, что наш новый компонент присутствует на форме, но с нулевыми размерами и при этом с неопределенным методом отрисовки. Поэтому он никак себя не проявляет.
Для того чтобы каждый экземпляр нашего класса имел уже заданные размеры по умолчанию необходимо переопределить конструктор класса.
Для добавления в класс новой функциональности нужно переопределить некоторые из его методов. Установка размеров осуществляется в конструкторе, поэтому его и нужно переопределить.
Для переопределения конструктора откроем модуль, в котором определен наш класс.
Проще всего это сделать, удерживая клавишу <Ctrl> щелкнув по имени модуля в разделе uses. Нужный модуль откроется в редакторе кода.
Того эффекта можно достичь, поставив курсор на имя модуля и нажав сочетание клавиш <Ctrl>+<Пробел>.
В интерфейсной части класса поставим курсор в секции public и вызовем подсказку (<Ctrl>+<Пробел>). Отфильтруем ее, начав набирать первые буквы имени конструктора Create – “cr”.
В появившемся списке можно выбрать переопределение конструктора. При этом в интерфейсной части появиться следующая строка:
Откомпилировать приложение в таком состоянии не удастся, так как компилятор выдаст сообщение об неудовлетворительном предварительном или внешнем объявлении конструктора:
Пример 2.8.
[Pascal Error] SyPOST.pas(14): E2065 Unsatisfied forward or external declaration: 'TsyPostZip.Create'
Это означает, что конструктор объявлен, но не реализован . Так реализуем же его. Для этого в реализационном разделе (implementation) нужно написать следующую конструкцию:
Естественно, что имя класса должно соответствовать ранее объявленному.
Набирать вручную такую заготовку нет необходимости: достаточно поставить курсор на объявление конструктора и нажать сочетание клавиш <Ctrl>+<Shift>+<C>. Заготовка появиться в реализационном разделе. Запомните это полезное сочетание клавиш, так как применять его можно практически к любым декларациям класса.
Теперь в конструкторе можно установить необходимые размеры компонента по умолчанию, а для того, чтобы компонент как-то проявил себя на форме – установить курсор в отличное от умолчания значение.
Первая строка в заготовке реализации метода содержит одно слово inherited, обеспечивающая вызов унаследованного метода, в данном случае конструктора.
Пример 2.10.
constructor TsyPostZip.Create(AOwner: TComponent); begin inherited; Width:=300; Height:=50; Cursor:=crHandPoint; end;
Откомпилируйте и запустите тестовый проект, наведите курсор мыши на область, которую занимает компонент (в примере – 300 пикселей в ширину и 50 – в высоту) и убедитесь, что курсор принял форму «руки».
Для того, чтобы реализовать внешний вид компонента, необходимо переопределить метод Paint.
Для этого в модуле компонента в секции protected объявления класса нужно записать объявление метода:
Для создания этого объявления также можно воспользоваться подсказкой, вызвав ее комбинацией клавиш <Ctrl>+<Пробел> и подтвердив выбор мышью или клавишей Enter. Для создания реализационной заготовки нажмите <Ctrl>+<Shift>+<C>.
Ключевое слово override (переопределить) означает, что новая реализация метода становится актуальной.
Для начала нужно убедится, что все сделано верно. Для этого в реализации Paint нужно записать простой оператор отрисовки, не забыв первой строкой вызвать унаследованный метод Paint.
Теперь метод Paint необходимо доработать самостоятельно в соответствии с вариантом задания, например, как показано на рисунке.
Пример 2.13. Вариант реализации метода Paint
procedure TsyPostZip.Paint; var i,h2,d, Ofd: integer; begin inherited; d:=(ClientHeight- fOffset*2) div 2; h2:=ClientHeight div 2; Ofd:=fOffset+d; with Canvas do begin Pen.Style:=psClear; // Контур выключить Rectangle(ClientRect); // Клиентский прямоугольник Pen.Style:=psDot; // отрисовка знакомест for I := 0 to 5 do begin Polyline([Point(ofd*i+fOffset,fOffset), Point(ofd*(i+1),fOffset)]); // верхняя черта Polyline([Point(ofd*i+fOffset,h2), Point(ofd*(i+1),h2)]); // средняя черта Polyline([Point(ofd*i+fOffset,ClientHeight-fOffset), Point(ofd*(i+1),ClientHeight-fOffset)]); // нижняя черта Polyline([Point(ofd*i+fOffset,fOffset), Point(ofd*i+fOffset,h2)]); // верхняя левая черта Polyline([Point(ofd*i+fOffset,h2), Point(ofd*i+fOffset,ClientHeight-fOffset)]); // нижняя левая черта Polyline([Point(ofd*(i+1),fOffset), Point(ofd*(i+1),h2)]); // верхняя правая черта Polyline([Point(ofd*(i+1),h2), Point(ofd*(i+1),ClientHeight-fOffset)]); // нижняя правая черта Polyline([Point(ofd*(i+1),fOffset), Point(ofd*i+fOffset,h2)]); // верхяя диагональ Polyline([Point(ofd*(i+1),h2), Point(ofd*i+fOffset,ClientHeight-fOffset)]); // нижняя диагональ end; end; end;