Создание собственных компонентов

Создание собственных компонентов
Создание собственных компонентов:

Тебе предстоит познакомится с технологией создания собственных компонентов. Я расскажу, как можно создать компонент Delphi (не путай с компонентами ActiveX). В качестве примера я выбрал достаточно простой, но напичканый математикой пример - часы. Наши часы смогут работать как аналоговые и числовые. По этим часам будет Москва сверятся :).

Компоненты - это самая удобная вещь. С самого появления языков программирования, все программеры стремятся добится многократности использования кода. Для этого мы перешли к процедурному программирования, затем к объектному и сейчас нам предлагают перейти к компонентному программированию. То, что предлагает нам MS - использовать компоненты ActiveX я назову настоящей лажей. Эти компоненты требую гемороя для регистрации их на машине клиента, слежка за версиями и многое другое. Borland предложила свои компоненты. По умолчанию они встраиваются прямо в запускной файл и не требуют никакой регистрации и великолепно работают.

Я думаю, что я достаточно расхвалил компоненты Delphi, пора бы и написать один для примерчика.

Для создания нового компонента выбери меню Component-> New Component. Перед тобой откроется окно, как на рис 1.

Logo
Рис 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. Мы её уже использовали в конструкторе. Она у нас вызывается по событию от таймера:

  protected
    { Protected declarations }
    procedure TickerCall(Sender: TObject);

Жмём 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:

    property OnSecond: TNotifyEvent read FOnSecond write FOnSecond;
    property OnMinute: TNotifyEvent read FOnMinute write FOnMinute;
    property OnHour: TNotifyEvent read FOnHour write FOnHour;

Теперь наш компонент сможет генерировать события.

Logo
Рис 2. Помощь

С событиями вроде всё ясно (если нет, то посмотри на исходник в конце статьи). Теперь переходим к свойствам. В разделе published мы можем создавать свойства, которые будут отображатся в Object Inspector при выделении наших часиков. Все свойства, которые есть у предка нужно просто описать

  published
    property Align;
    property Enabled;
    property ParentShowHint;
    property ShowHint;
    property Visible;

Слово 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:

type
  TClockStyle = (scAnalog, scDigital);

  TGraphicClock = class(TGraphicControl)
  private
    Ticker: TTimer;

Строка TClockStyle = (scAnalog, scDigital) - объявляет список переменных, которые и будут выпадать по выбору свойства.

Всё остально происходит так же, за исключением нового слова default Которое устанавливает значение по умолчанию - scAnalog.

Вот и всё, что я хотел тебе рассказать.

Logo
Рис 3. Установка компонента

Чтобы установить компонент в системе нужно щёлкнуть меню Component->Install Component. Перед тобой появится окно, как на рис 3.

В строке Unit file name нужно указать полный путь к файлу. Для облегчения выбора используй кнопку Browse

Logo
Рис 4. Окно пакета

 справа. Перед тобой появится запрос на компиляцию пакета. Соглашайся. Если запрос не появился, то в любом случае появится окно пакета (рис 4).

В этом окне ты можешь откомпелировать пакет с помощью кнопки Compile и установить в Delphi с помощью Install .

Вот и всё. Наслаждайся новым компонентом в твоей палитре.

На сегодня хватит. Статья и так получилась достаточно большая. Увидимся в следующий раз.

Напоследок у меня есть небольшая просьба. Пиши мне, о чём бы ты хотел прочитать на моих страницах. Некоторые разделы я неуспеваю писать, а некоторые я просто уже не знаю, о чём тебе рассказать. Ты просто пиши мне, а там разберёмся.

 Исходники примера забирай здесь



Опубликовал admin
9 Май, Пятница 2003г.



Программирование для чайников.