Язык программирования Delphi. Создание собственных компонентов:
Тебе предстоит познакомится с технологией создания собственных компонентов. Я расскажу, как можно создать компонент Delphi (не путай с компонентами ActiveX). В качестве примера я выбрал достаточно простой, но напичканый математикой пример - часы. Наши часы смогут работать как аналоговые и числовые. По этим часам будет Москва сверятся :).
Компоненты - это самая удобная вещь. С самого появления языков программирования, все программеры стремятся добится многократности использования кода. Для этого мы перешли к процедурному программирования, затем к объектному и сейчас нам предлагают перейти к компонентному программированию. То, что предлагает нам MS - использовать компоненты ActiveX я назову настоящей лажей. Эти компоненты требую гемороя для регистрации их на машине клиента, слежка за версиями и многое другое. Borland предложила свои компоненты. По умолчанию они встраиваются прямо в запускной файл и не требуют никакой регистрации и великолепно работают.
Я думаю, что я достаточно расхвалил компоненты Delphi, пора бы и написать один для примерчика.
Для создания нового компонента выбери меню Component-> New Component. Перед тобой откроется окно, как на рис 1.
Рис 1. Новый компонент
Давай расмотрим каждое окошечко в отдельности:
Ancestor type - Тип предка. Это имя объекта, от которого мы порадим наш объект. Это даст нашему объекту все возможности его предка, плюс мы добавим свои. Для часиков нам понадобится TGraphicControl.
Class Name - Имя нашего будущего компонента. Я его назвал TGraphicClock.
Palette Page - Имя палитры компонентов, куда будет помещён наш компонент после инсталляции. Я оставил значение по умолчанию "Samples". Но ты можешь поместить его даже на закладку "Standart".
Unit file name - имя и путь к модулю, где будет располагатся исходный код компонента. Хотябы посмотри под каким именем сохранят твой компонент и где.
Search path - Сюда можно даже не заглядывать.
Жмём "ОК" (именно "ОК", а не Install) и получаем созданный Дульфой код:
unit GraphicClock;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TGraphicClock = class(TGraphicControl)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TGraphicClock]);
end;
end.
В принципе простейший компонент готов и его можно инсталировать в Дельфу. Но этот компонент ничего не умеет и он пока является полным аналогом своего предка TGraphicControl. Чтобы он стал отличатся, сейчас мы добавим ему разные свойства.
Начнём написание нашего компонента с конструктора и деструктора. конструктор - это простая процедура, которая автоматически вызывается при создании компонента. Деструктор - тоже процедура, только она автоматически вызывается при уничтожении компонента. В конструкторе мы проинициализируем все наши переменные, а в деструкторе уничтожим.
Итак, напиши в разделе public:
constructor Create(AOwner: TComponent); override;
Теперь нажми сочетание клавишь CTRL+SHIFT+C. Дельфи сам создаст заготовку для конструктора:
constructor TGraphicClock.Create(AOwner: TComponent);
begin
inherited;
end;
Поправим её до вот такого вида:
constructor TCDClock.Create(AOwner: TComponent);
begin
//Вызываем конструктор предка
inherited Create(AOwner);
//Устанавливаем значения ширины и высоты по умолчанию
Width := 50;
Height := 50;
//Устанавливаем переменную ShowSecondArrow в true.
//Она будет у нас отвечать за показ секундной стрелки
ShowSecondArrow := true;
//Инициализируем остальные переменные
PrevTime := 0;
CHourDiff := 0;
CMinDiff := 0;
//Инициализируем растры TBitmap, в которых будут хранится
//фон и сам рисунок часов.
FBGBitmap:= TBitmap.Create;
FFont:=TFont.Create;;
FBitmap:= TBitmap.Create;
FBitmap.Width := Width;
FBitmap.Height := Height;
//Выставляем формат времени
DateFormat:='tt';
//Запускаем таймер
Ticker := TTimer.Create( Self);
//Интервал работы таймера - одна секунда
Ticker.Interval := 1000;
//По событию OnTimer будет вызыватсья процедура TickerCall
Ticker.OnTimer := TickerCall;
//Включаем таймер
Ticker.Enabled := true;
//Устанавливаем цвета поумолчанию
FFaceColor := clBtnFace;
FHourArrowColor := clActiveCaption;
FMinArrowColor := clActiveCaption;
FSecArrowColor := clActiveCaption;
end;
Ключевое слово inherited вызывает конструктор предка (в нашем случае TGraphicClock). В остальном, я надеюсь, что с конструктором всё ясно.
Теперь создадим деструктор. Для этого также опишем его в разделе public:
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
Кстати, ключевое слово override; после имени этих процедур говорит о том, что мы хотим переписать уже существующую у предка функцию с таким именем. Теперь жмём Ctrl+Shift+C и получаем заготовку для деструктора и поправляем её до вида :
destructor TGraphicClock.Destroy;
begin
Ticker.Free;
FBitmap.Free;
FBGBitmap.Free;
inherited Destroy;
end;
Заметь, что в конструкторе я вызывал предка в самом начале inherited, а в деструкторе в самом конце. В конструкторе сначала нужно, чтобы проинициализировался предок (он проинициализирует необходимые ссылки), а потом можно инициализировать свои вещи. Если в деструкторе мы сначала вызовем предка, то последующая работа с компонентом уже будет невозможна, потому что предок уничтожит все ссылки, поэтому я ставлю этот вызов в конце.
Теперь опишем все необходимые нам переменные в разделе private:
private
//Для часов обязательно понадобится таймер
Ticker: TTimer;
//Картинки часов и фона
FBitmap, FBGBitmap: TBitmap;
/События
FOnSecond, FOnMinute, FOnHour: TNotifyEvent;
//Центральная точка
CenterPoint: TPoint;
//Радиус
Radius: integer;
//Остальные параметры, которые мы рассмотрим в процессе.
LapStepW: integer;
PrevTime: TDateTime;
ShowSecondArrow: boolean;
FHourArrowColor,FMinArrowColor, FSecArrowColor: TColor;
FFaceColor: TColor;
CHourDiff, CMinDiff: integer;
FClockStyle:TClockStyle;
FDateFormat: String;
FFont: TFont;
Теперь в разделе private опицем процедуру TickerCall. Мы её уже использовали в конструкторе. Она у нас вызывается по событию от таймера:
Жмём CTRL+SHIAT+C и модифицируем созданную функцию:
procedure TCDClock.TickerCall(Sender: TObject);
var
H,M,S,Hp,Mp,Sp: word;
begin
//Если компонент создан в дезайнере, то выход
if csDesigning in ComponentState then exit;
//Иначе это уже запущеная программа
//Получить время
DecodeCTime( Time, H, M, S);
//Получить предыдущее время.
DecodeCTime( PrevTime, Hp, Mp, Sp);
//Сгенерировать событие OnSecond
if Assigned( FOnSecond) then FOnSecond(Self);
//Сгенерировать событие OnMinute
if Assigned( FOnMinute) AND (Mp < M) then FOnMinute(Self);
//Сгенерировать событие OnHour
if Assigned( FOnHour) AND (Hp < H) then FOnHour(Self);
//Сохранить текущее время в PrevTime
PrevTime := Time;
if ( NOT ShowSecondArrow) AND (Sp <= S) then exit;
//Прорисовать часы.
DrawArrows;
end;
DrawArrows напичкана математикой и она сейчас не имеет для нас особого значения. Немного ниже я приведу её в полном исходнике, а сейчас я просто расказываю тебе о том, как создавать компонента, поэтому мы рассмотрим сабытия генерируемые в функции TickerCall.
У нас уже объявлено три события:
FOnSecond, FOnMinute, FOnHour: TNotifyEvent;
Все они появятся на закладке Events в окне Object Inspector, когда ты поставишь компонент на форму. Чтобы приложения могло поймать эти события, мы объявили переменные типа TNotifyEvent и генерируем эти события (с помощью конструкции типа FOnSecond(Self) для события OnSecond), когда изменилась секунда, изменилась минута или изменился час.
Помимо этого, в разделе published мыдолжны описать свойство OnSecond:
С событиями вроде всё ясно (если нет, то посмотри на исходник в конце статьи). Теперь переходим к свойствам. В разделе published мы можем создавать свойства, которые будут отображатся в Object Inspector при выделении наших часиков. Все свойства, которые есть у предка нужно просто описать
Слово property говорит о том, что мы описываем свойство. Для них не нужны процедуры или функции, потому что эти свойства уже есть у предка. Нам надо только описать их и всё. Я описал только маленькую часть из доступных у TGraphicControl функций. Ты можешь добавить любые из доступных. Чтобы узнать, какие функции можно добавлять, открой помощь (меню Help->Delphi Help) и найди там объект TGraphicControl. Щёлкни по нему дважды и в появившейся справке выбери пункт Properties (вверху окна) (рис 2). Появится окно с перечнем всех свойств. Ты можешь добавить любое из них. Например, чтобы добавить свойство Action нужно написать в разделе published:
published
property Action;
Чтобы добавить своё свойство, нужно немного попатеть. Например. Добавим возможность, чтобы пользователь мог менять картинку фона. Для этого описываем в разделе published свойство BGBitmap:
//свойство Имя :Тип читать из FBGBitmap записывать с помощью SetBGBitmap
property BGBitmap:TBitmap read FBGBitmap write SetBGBitmap;
Коментарий поможет тебе разобраться в написанном здесь. Итак, мы объявили свойство BGBitmap типа ТBitmap. Для чтения используется простая переменная FBGBitmap (можно использовать и функцию, но нет смысла, потому что можно прямо читать из переменной), для записи используется процедура SetBGBitmap. Процедура выглядит так:
procedure TCDClock.SetBGBitmap(Value: TBitmap);
begin
FBGBitmap.Assign(Value);
invalidate;
end;
Теперь ты можешь изменять фон простой операцией GraphicClock1.BGBitmap:=bitmap.
Если ты хочешь создать свойство с выпадающим списком (как например у свойства Align), по щелчку которого выпадает список возможных параметров, то тут уже немного сложнее. В моих часах есть такой параметр, который делает выбор, какого типа будут часы - аналоговые или цыфровые. Объявление делается так:
//свойство Имя :Тип Это нам известно Значение по умолчанию
property ClockStyle:TClockStyle read FClockStyle write SetStyleStyle default scAnalog;
Мы объявляем свойство ClockStyle типа TClockStyle. Тип TClockStyle мы должны описать в самом начале, до описания нашего объекта TGraphicClock:
Строка TClockStyle = (scAnalog, scDigital) - объявляет список переменных, которые и будут выпадать по выбору свойства.
Всё остально происходит так же, за исключением нового слова default Которое устанавливает значение по умолчанию - scAnalog.
Вот и всё, что я хотел тебе рассказать.
Рис 3. Установка компонента
Чтобы установить компонент в системе нужно щёлкнуть меню Component->Install Component. Перед тобой появится окно, как на рис 3.
В строке Unit file name нужно указать полный путь к файлу. Для облегчения выбора используй кнопку Browse справа. Перед тобой появится запрос на компиляцию пакета. Соглашайся. Если запрос не появился, то в любом случае появится окно пакета (рис 4).
Рис 4. Окно пакета
В этом окне ты можешь откомпелировать пакет с помощью кнопки Compile и установить в Delphi с помощью Install .
Вот и всё. Наслаждайся новым компонентом в твоей палитре.
На сегодня хватит. Статья и так получилась достаточно большая. Увидимся в следующий раз.
Напоследок у меня есть небольшая просьба. Пиши мне, о чём бы ты хотел прочитать на моих страницах. Некоторые разделы я неуспеваю писать, а некоторые я просто уже не знаю, о чём тебе рассказать. Ты просто пиши мне, а там разберёмся.