Как создать компонент - umotnas.ru o_O
Главная
Поиск по ключевым словам:
страница 1
Похожие работы
Как создать компонент - страница №1/1

Как создать компонент

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



Шаг 1: Создание тестового приложения

  1. Создайте новое приложение (File | New Application).

  2. Добавьте на форму ближе к низу компонент TButton.

  3. Теперь выберите File | Save All, и сохраните форму как LEDForm.cpp и проект как LEDProj.

Шаг 2: Создание компонента

  1. Выберите Component | New, или, в C++Builder v1 File | New, и выберите Component в диалоге New File Items.

  2. Установите Ancestor type в TShape, имя класса TLED, а Palette Page в "Samples". Щелкните "OK", не нажимайте "Install" если вы используете версию 3 или старше (мы это проделаем потом)!

  3. Затем сохраните новый файл компонента. Если вы используете C++Builder v1 сохраните его под именем LED.cpp (несмотря на то, что по умолчанию unit1.cpp).

Теперь, когда предварительные действия завершены, давайте посмотрим, что же сгенерировал C++ Builder. Файл LED.cpp будет выглядеть примерно так ( не волнуйтесь, если будет слегка отличаться - в разных версиях по-разному):

//-----------------------------------------------------------------
#include

#pragma hdrstop


#include "LED.h"

#pragma package(smart_init)

//-----------------------------------------------------------------

//-----------------------------------------------------------------

static inline void ValidCtrCheck(TLED *)

{

new TLED(NULL);



}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner)

{

}



//-----------------------------------------------------------------

//-----------------------------------------------------------------

namespace Led

{

void __fastcall PACKAGE Register()



{

TComponentClass classes[1] = {__classid(TLED)};

RegisterComponents("Samples", classes, 0);

}

}



//-----------------------------------------------------------------

//-----------------------------------------------------------------

Первая функция, ValidCtrCheck, используется для того, чтобы убедиться, что ваш компонент не имеет чистых виртуальных функций (и соответственно, не является абстрактным классом). Вам не надо беспокоиться о ней или менять ее.

Следующая функция (первое, что действительно является частью нового класса TLED) - конструктор. Он вызывается, когда компонент помецается на форму, так что все его характеристики, в том числе и те, которые будут видны в Object Inspector получают значения по умолчанию именно здесь.

Третья функция регистрирует компонент. Пространства имен позволяют библиотекам (в данном случае компонентам) использовать одни и те же имена без конфликтов (на самом деле, конечно, тут все немного сложнее, но сейчас не надо об этом беспокоиться). TComponentClass содержит массив имен компонентов, которые надо зарегистрировать (в одной этой функции вы можете зарегистрировать столько компонентов, сколько вы желаете). Более подробно эта функция (т.е. если вы хотите написать свою, вместо той, что подставил Builder) описана в разделе "Writing The Register Function" вашего Component Writers Guide.

Построение компонент в C++Builder Часть 1- Страница 2 из
Теперь давайте взглянем на .h файл, созданный Билдером. Если этого еще не сделано, зайдите в редактор и выберите соответствующую закладку. На экране появится содержимое LED.h (если вы используете C++Builder v1, там не будет макроса PACKAGE):
//-----------------------------------------------------------------

#ifndef LEDH

#define LEDH

//-----------------------------------------------------------------

#include

#include

#include

#include

#include

//-----------------------------------------------------------------

class PACKAGE TLED : public TShape

{

private:



protected:

public:


__fastcall TLED(TComponent* Owner);

__published:

};

//-----------------------------------------------------------------



#endif

Да, не очень-то много. Только множество включений файлов, которые пришлось бы делать вручную. Теперь надо кое-что написать самим в дополнение к скелету, соданному Билдером. Нам будет нужна булева переменная для хранения статуса и специальные переменные для хранения цветов обоих состояний индикатора. Тип bool подойдет для статуса, а для хранения цвета имеется специальный класс (вернее, перечисляемый тип) TColor. Хорошее правило ООП - делать переменные внутри класса скрытыми, чтобы другие программисты имели к ним доступ только через интерфейс класса - это гарантия, что данные не будут испорчены. Поэтому все наши переменные будут в секции private. Наш класс будет выглядеть примерно так (пишем colour, как это принято в Великобритании и Канаде):


private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

Префикс F в именах переменных - стандартное соглашение именования членов компонент (позволяет использовать то же имя, только без F, для соотв. свойства (published property)). Теперь, раз у нас есть переменные, нам нужны аксессоры (accessors) - функции, осуществляющие доступ, по-английски "access", к данным класса извне, и мутаторы (mutators) - функции, меняющие значения данных класса, по английски - "mutate", (здесь еще добавлены ключевые слова __published и __property, но об этом позже). Довольно странным может показаться, что эти функции помещены в ту же секцию, что и данные, но это также будет объяснено позже:
private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

void __fastcall SetOnOff(const bool Value);

void __fastcall SetOnColour(const TColor OnColour);

void __fastcall SetOffColour(const TColor OffColour);

Также существует стандартное соглашение о том, что имена аксессоров начинаются с "Get" , а имена мутаторов - с "Set". Хотя это может показаться так же неудобным, как начинать имена переменных с F, на самом деле это облегчит жизнь в дальнейшем.

Если вы еще не использовали модификатор __fastcall для ваших функций, объясним, зачем он нужен. Это означает, что при наследовании от классов VCL все функции должны быть __fastcall.

Построение компонент в C++Builder Часть 1- Страница 3 из 9
Теперь вернемся к тому странному факту, что мутаторы объявлены как private.Здесь мы пользуемся расширением фирмы Борланд в C++, ключевым словом __published. Все свойтва объекта, которыми планируется манипулировать визуально, должны быть объявлены как __published, а аксессоры и мутаторы подключаются к ним с помощью следующего синтаксиса: Добавьте три свойства в секции __published, и класс будет выглядеть примерно так:
__published:

__property bool LEDOn = {read = FOnOff, write = SetOnOff};

__property TColor OnColour = {read = FOnColour, write = SetOnColour};

__property TColor OffColour = {read = FOffColour, write = SetOffColour};

Все объявления свойств начинаются с ключевого слова __property, затем следуют тип переменной (свойства), и объявления членов доступа к свойству. Интересно, что read указывает на переменную, а write на функцию. И read, и write могут указывать и на функцию, и на переменную, но обычно делают так, чтобы write указывал на функцию, в которой есть некоторые проверки правильности значений и т.д. Имя свойства может не иметь ничего общего с именем переменной, которую оно модифицирует (например, переменная FOnOff модифицируется свойством LEDOn). Хотя рекомендуется использовать стандартные соглашения имен - имя свойства должно быть таким же, как и имя связанной с ним переменной, за исключением префикса F в начале. Это все, что нам нужно изменить в файле заголовка класса.

Хотя введение свойств может показаться инородным, может быть, даже кощунственным для старых программистов C++, но если вы хотите полностью использовать новые возможности C++Builder, вы вынуждены будете использовать свойства.



Замечание:Следующий параграф описывает значения по умолчанию, и если вы нетерпеливы, то можете перейти к следующей странице.

У свойтва может быть значение default, то есть -


__property bool LEDOn = {read=FOnOff, write=SetOnOff, default=false};

Это значение НЕ то, что видно по умолчанию в object inspector (их вы должны задать в конструкторе компонента, мы займемся этим позже). Тут заложен другой смысл. Каждый раз при добавлении компонента на форму, Билдер сохраняет информацию о нем в файле .DFM. Так как все свойства компонента определяют, как он будет выглядеть, где будет на форме и так далее, они просто сохраняются в двойчном .DFM-файле на диске. Значение по умолчанию, которое вы присваиваете компоненту, будет определять, будет ли свойство сохраняться в .DFM-файле или нет. Если значение свойства на форме совпадает со значением по умолчанию, оно НЕ будет сохраняться, а если оно другое, то сохранится.

Свойства могут быть объявлены с ключевым словом nodefault:
__property bool LEDOn = {read=FOnOff, write=SetOnOff, nodefault};

Это означает, что они ВСЕГДА будут сохраняться. Также имеется атрибут stored. Вы можете переопределить сохраняющие действия Билдера по умолчанию с помощью него. Если вы установите stored в true, свойтво всегда будет сохраняться, если в false - никогда не будет сохраняться. Кроме того, вы можете определить его с помощью метода, возвращающего логическое значение (в нижеприведенном примере функция check возвращает булево значение).


__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=true};

__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=false};

__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=Check};

Последний атрибут, который имеет свойство - это index. Он полезен, когда нужно иметь несколько аксессоров и мутаторов с дублирующимся кодом. Вместо того, чтобы писать несколько отдельных функций, можно написать одну с использованием index. Чтобы его использовать, нужно добавить соответствующее ключевое слово в объявление свойства, и индекс будет первым элементом, который передается вашему аксессору/мутатору. Например:


__property TColor OnColour = {read=FOnColour, write=SetOnOffColour, index=1};

__property TColor OffColour = {read=FOffColour, write=SetOnOffColour, index=2};

И реализация функции SetOnOffColour должна выглядеть примерно так:
void __fastcall TLED::SetOnOffColour(int Index,TColor Colour)

{

switch(Index)



{

case 1 : FOnColour = Colour;

break;

case 2 : FOffColour = Colour;



break;

}

Brush->Color = (FOnOff)?FOnColour:FOffColour;



}

Построение компонент в C++Builder Часть 1- Страница 4 из
Вот как должен сейчас выглядеть ваш файл LED.h:
//---------------------------------------------------------------------------

#ifndef LEDH

#define LEDH

//---------------------------------------------------------------------------

#include

#include

#include

#include

#include

//---------------------------------------------------------------------------

class PACKAGE TLED : public TShape

{

private:



bool FOnOff;

TColor FOnColour;

TColor FOffColour;

void __fastcall SetOnOff(const bool Value);

void __fastcall SetOnColour(const TColor OnColour);

void __fastcall SetOffColour(const TColor OffColour);


protected:

public:


__fastcall TLED(TComponent* Owner);

__published:

__property bool LEDOn = {read = FOnOff, write = SetOnOff};

__property TColor OnColour = {read = FOnColour, write = SetOnColour};

__property TColor OffColour = {read = FOffColour, write = SetOffColour};
};

//---------------------------------------------------------------------------

#endif

Хотите верьте, хотите нет, но самая трудная часть уже позади.



Построение компонент в C++Builder Часть 1- Страница 5 из 9
Легкая часть - написание .cpp файла. Сначала давайте напишем код мутаторов:
//---------------------------------------------------------------------------

void __fastcall TLED::SetOnOff(const bool Value)

{

FOnOff = Value; // Set the state of FOnOff



Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------



void __fastcall TLED::SetOnColour(const TColor OnColour)

{

FOnColour = OnColour;



Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------



void __fastcall TLED::SetOffColour(const TColor OffColour)

{

FOffColour = OffColour;



Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------



Легче не бывает :)) Теперь надо написать конструктор. В нем будут устанавливаться значения по умолчанию, которые будут видны в object inspector:
__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner)

{

Width = 15; // Установим ширину нашего LED в 15.



Height = 15; // Установим высоту нашего LED в 15.

FOnColour = clLime; // Установим OnColour лимонно-зеленым.

FOffColour = clRed; // Установим OffColour красным.

FOnOff = false; // Сосотояние по умолчанию - выключено

Shape = stEllipse; // Форма по умолчанию - эллипс

Pen->Color = clBlack; // Цвет пера - черный

Pen->Width = 1; // Толщина пера 1

Brush->Color = FOffColour; // Цвет кисти одинаков с цветом по умолчанию

}

Опять все достаточно прозрачно. Эти значения будут установлены, когда компонент поместят на форму и при этом автоматически будет вызван конструктор. Несмотря на это, мы можем менять эти значения и во время проектирования, а не только во время выполнения.



Замечание: существует несколько способов инициализации переменных в конструкторе. Вышеприведенный пример можно было написать и так:
__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner),

FOnColour(clLime), // установить OnColour в лимонно-зеленый

FOffColour(clRed) // установить OffColour красным

{

Width = 15; // Установим ширину нашего LED в 15.



Height = 15; // Установим высоту нашего LED в 15.

Shape = stEllipse; // Форма по умолчанию - эллипс

Pen->Color = clBlack; // Цвет пера - черный

Pen->Width = 2; // Толщина пера 2

Brush->Color = FOffColour; // Цвет кисти одинаков с цветом по умолчанию

}

А можно и так:


__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner), FOnColour(clLime), FOffColour(clRed), Width(15),

Height(15), Shape(stEllipse), Pen->Width(2),Brush->Color(FOffColour)

{}


Также важно понимать, что fOnOff инициализировать не обязательно, так как тип bool имеет встроенное значение по умолчанию(false), в отличие от большинства простых типов C++, которые автоматически не инициализируются. Тем не менее, считается хорошим стилем инициализировать значения всех свойств.

Построение компонент в C++Builder Часть 1- Страница 6 из 9

Теперь, когда мы написали этот небольшой компонент, давайте убедимся, что все работает. Сначала сохраните проект (File | Save All). Затем добавьте модуль LED, который мы только что создали, к тестовому проекту. Чтобы это сделать, выберите (Project | Add to Project) и найдите led.cpp (по умолчанию сохраненный в папку lib в v3 и v4). Потом введите #include "LED.h" вверху LEDForm.h:


#include

#include

#include

#include

#include "LED.h"

Теперь добавьте в секцию private файла LEDForm.h:


private: // User declarations

TLED* LED1;

Теперь вернитесь на форму LEDForm и выберите событие OnCreate в object inspector. Введите следующий код в тело функции:
void __fastcall TForm1::FormCreate(TObject *Sender)

{

LED1 = new TLED(this);



LED1->Parent = this;

LED1->Left = (Width/2)-(LED1->Width/2);

LED1->Top = (Height/2)-(LED1->Height/2);

}

Этот код динамически создает новый компонент TLED и помещает его в центр экрана (если вы динамически создаете компонент, вы должны задать все значения вручную). Очень важно также запомнить, что в этом случае необходимо задать свойство Parent компонента! Теперь выберите событие OnClick кнопки:


void __fastcall TForm1::Button1Click(TObject *Sender)

{

LED1->LEDOn = !LED1->LEDOn;



}

Теперь самое время нажать волшебную кнопку Run. Ваша программа должна выглядеть примерно так:



Щелкая кнопку,можно менять цвет индикатора с красного на зеленый с черным и обратно. Что ж, компонент польностью готов, и единственное, что осталось - инсталлировать его в палитру. В версии 3 и выше предварительно надо еще и создать пакетную библиотеку (пакедж).



Построение компонент в C++Builder Часть 1- Страница 7 из 9

Прежде чем добавить компонент в палитру, нужно разработать для него иконку (стандартная иконка слишком неинформативна) с помощью утилитки Image Editor (Tools | Image Editor). Если вы не хотите делать иконку для своего компонента, можно перейти к следующей странице.



  • C++Builder v3 or later
    Выберите (File | New | Resource). Щелкните правой клавишей на ветке дерева "Contents" и выберите (New | Bitmap). В появившемся диалоге выберите высоту и ширину 24 и VGA(16 colours), затем "OK". После этого переименуйте иконку в LED (кликнув правой клавишей), и после этого дважды кликните на ней. Теперь можно нарисовать иконку. После этого выберите (File | Save), сохраните файл ресурса в ту же папку, что и файл LED.cpp (папка lib по умолчанию), под именем LED.res.

  • C++Builder v1
    Выберите (File | New | Bitmap), в диалоге, выберите высоту и ширину в 24, VGA (16 colours), щелкните "OK". Дальше выберите (Resource | Rename), назовите битмап TLED. Дважды кликнув, создайте рисунок. Далее, (File | Save As) и сохраните файл ресурса в ту же папку, что и файл LED.cpp под именем LED.res.

Теперь можно инсталлировать компонент.



Построение компонент в C++Builder Часть 1- Страница 8 из 9

Теперь вы готовы к инсталляции компонента. Процедура сильно отличается в зависимости от того, какую версию Билдера вы используете.



Инсталляция в C++Builder v1
Процедура инсталляции вашего компонента ничем не отличается от процедуры инсталляции любого другого. Сначала последний (File | Save All), затем выберите (Component | Install) из главного меню C++Builder. Появится диалог Install Components. Нажмите кнопку Add, и затем (из диалога Add Module) нажмите кнопку Browse. Выберите LED.cpp в диалоге Add Component, и наконец нажмите кнопку "OK" в диалоге Install Components. (Запаситесь терпением, если на вашем компьютере мало памяти). C++Builder скомпилирует и подлинкует компонент, а затем добавит его в палитру.

Инсталляция в C++Builder v3 или старше
В C++Builder v3 Borland впервые ввела пакетные библиотеки (пакеджи). Компоненты теперь сначала компилируются а пакедж, который потом инсталлируется. Таким образом обеспечивается разделение компонентного кода между приложениями (через BPL, которые представляют собой особый вид DLL).

Сначала сохраните все (File | Save All), затем закройте все файлы (File | Close All). Теперь выберите (Component | Install Component), дальше выберите "Into new package" и подключите файл LED.cpp. Вы должны назначить имя для пакеджа(но оно должно отличаться от имени вашего компонента) - например, LEDPack, и потом можете, если хотите, ввести краткое описание вашего пакеджа. Щелкните "OK". C++Builder спросит подтверждение того, что вы хотите инсталлировать компонент, после этого скомпилирует и установит компонент.



Наслаждайтесь :))

Построение компонент в C++Builder Часть 1- Страница 9 из 9