Программирование с C++ Builder

         

Builder позволяет программисту создать свой



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

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

Процесс создания компонента может быть представлен как последовательность следующих этапов:

1. Выбор базового класса.

2. Создание модуля компонента.

3. Тестирование компонента.



4. Добавление компонента в пакет компонентов.

Рассмотрим процесс создания компонента программиста на примере. Создадим компонент NkEdit, который обеспечивает ввод и редактирование целого или дробного числа. Компонент также должен контролировать вводимое значение на принадлежность заданному диапазону. Тип числа и границы диапазона должны задаваться во время разработки формы приложения, использующего компонент.
Содержание

Выбор базового класса



Приступая к разработке нового компонента, следует четко сформулировать назначение компонента. Затем необходимо определить, какой из компонентов C++ Builder наиболее близок по своему назначению, виду и функциональным возможностям к компоненту, который разрабатывается. Именно этот компонент следует выбрать в качестве базового.

Создание модуля компонента



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

Чтобы начать работу над новым компонентом, надо активизировать процесс создания нового приложения (команда File | New | Application), в меню Component выбрать команду New Component и в поля диалогового окна New Component (рис. 6.1) ввести информацию о создаваемом компоненте.



Рис. 6.1. Начало работы над новым компонентом


В поле Ancestor type надо ввести базовый тип создаваемого компонента. Для разрабатываемого компонента базовым компонентом является стандартный компонент Edit (поле ввода-редактирования). Поэтому базовым типом разрабатываемого компонента является тип TEdit.

В поле Class Name необходимо ввести имя класса разрабатываемого компонента (например, TNkEdit). Вспомните, что в C++ Builder принято соглашение, согласно которому имена типов должны начинаться буквой т. В поле Palette Page нужно ввести имя вкладки палитры компонентов, на которую будет помещен значок компонента. Название вкладки палитры компонентов можно выбрать из раскрывающегося списка. Если в поле Palette Page ввести имя еще не существующей вкладки палитры компонентов, то непосредственно перед добавлением компонента вкладка с указанным именем будет создана.

В поле Unit file name находится автоматически сформированное имя файла модуля создаваемого компонента. C++ Builder присваивает модулю компонента имя, которое совпадает с именем типа компонента, но без буквы т. Щелкнув на кнопке с тремя точками, можно выбрать каталог, в котором должен быть сохранен модуль компонента.

В результате щелчка на кнопке ОК будет сформирован модуль компонента, состоящий из двух файлов: файла заголовка (листинг 6.1) и файла реализации (листинг 6.2).

Листинг 6.1. Файл NkEdit.h

#ifndef NkEditH
#define NkEditH
#include <SysUtils.hpp>


#include <Classes.hpp>
#include <Controls.hpp>


#include <StdCtrls.hpp>
class PACKAGE TNkEdit : public TEdit {
private: protected: public:
__fastcall TNkEdit(TComponent* Owner);
__published:
};
#endif


Листинг 6.2. Файл NkEdit.cpp
#include <vcl.h>
#pragma hdrstop
#include "NkEdit.h"
 #pragma package(smart_init)
static inline void ValidCtrCheck(TNkEdit *) {
new TNkEdit(NOLL); }
__fastcall TNkEdit::TNkEdit(TComponent* Owner)
: TEdit(Owner) { }
namespace NkEdit {
void __fastcall PACKAGE Register()
{
TComponentClass classes[l] = {__classid(TNkEdit)};
RegisterComponents("Samples", classes, 0) ; } }

В файле заголовка NkEdit.h находится объявление нового класса. В файл реализации NkEdit.cpp помещена функция Register, которая обеспечивает регистрацию, установку значка компонента на указанную вкладку палитры компонентов.

В сформированный C++ Builder шаблон компонента нужно внести дополнения: объявить поля данных, функции доступа к полям данных, свойства и методы. Если на некоторые события компонент должен реагировать не так, как базовый, то в объявление класса нужно поместить объявления соответствующих функций обработки событий.

В листингах 6.3 и 6.4 приведены файлы заголовка и реализации компонента NkEdit после внесения всех необходимых изменений.

Листинг 6.3. nkedit.h
#ifndef NkEditH
 #define NkEditH
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
 #include <StdCtrls.hpp>
class PACKAGE TNkEdit : public TEdit
{
private:
bool FEnableFloat; // разрешен ввод дробного числа
// диапазон
float FMin; // нижняя граница
float FMax; // верхняя граница
/* функция SetNumb используется для изменения содержимого
поля редактирования */
void __fastcall SetNumb(float n);
/* Функция GetNumb используется
для доступа к полю редактирования */
float __fastcall GetNumb(void);
/* эти функции обеспечивают изменение
границ диапазона допустимых значений */
bool __fastcall SetMin(float min);
bool __fastcall SetMax(float max) ;
protected:
public:
__fastcall TNkEdit(TComponent* Owner);
 // конструктор
/* Свойство Numb должно быть доступно только во время
работы программы. Поэтому оно объявлено в секции public.
Если надо, чтобы свойство было доступно
во время разработки формы и его значение
 можно было задать в окне Object Inspector,
то его объявление нужно поместить в секцию published
*/
__property float Numb = {read = GetNumb }; //, write = SetNumb};
// Функция обработки события KeyPress DYNAMIC 
void __fastcall KeyPress(char Skey);
published:
// объявленные здесь свойства доступны в Object Inspector
__property bool EnableFloat = { read = FEnableFloat,
write = FEnableFloat };
__property float Min = {read = FMin,
write = SetMin };
__property float Max = {read = FMax,
write = SetMax }; };
#endif

Листинг 6.4. nkedit.cpp 
#include <vcl.h>
#pragma hdrstop
#include "NkEdit.h"
#pragma package(smart_init)
static inline void ValidCtrCheck(TNkEdit *) {
new TNkEdit(NULL); }
// конструктор
__fastcall TNkEdit::TNkEdit(TComponent* Owner)
: TEdit(Owner) {
// конструктор имеет прямой доступ к полям компонента
Text = "0";
FMin = 0;
FMax = 100;
FEnableFloat = true; }
namespace Nkedit
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[l] = {__classid(TNkEdit)};
RegisterComponents("Samples", classes, 0); } }
void __fastcall TNkEdit::SetNumb(float n)
{
Text = FloatToStr(n);
}
// возвращает значение, соответствующее строке,
// которая находится в поле редактирования
float __fastcall TNkEdit::GetNumb(void)
if (Text.Length())
return StrToFloat(Text); else return 0;
}
// функция обработки события KeyPress в поле компонента NkEdit
void __fasteall TNkEdit::KeyPress(char &Key)
{
// Коды запрещенных клавиш заменим нулем, в результате чего
 // эти символы в поле редактирования не появятся switch (Key)
{ case '0' :
case '1'
case '2'
case '3'
case '4'
case '5'
case '6'
case '7'
case '8'
case '9' break;
case '.' case ', '
Key = DecimalSeparator;
if (Text.Pos(DecimalSeparator) || (! FEnableFloat))
Key = 0; break;
case '-' : // знак "минус"
if (Text.Length()|I(FMin >= 0))
// минус уже введен или FMin >= О Key = 0; break;
case VKJ3ACK: // клавиша <Backspace> break;
default : // остальные символы запрещены
Key = 0; }
if ((Key >='0') && (Key <= '9')) {
/* Проверим, не приведет ли ввод очередной
цифры к выходу числа за границы диапазона.
 Если да, то заменим введенное
число на максимальное или минимальное*/
AnsiString st = Text + Key;
if (StrToFloat(st) < FMin) {
Key = 0;
Text = FloatToStr(FMin); } if (StrToFloat(st) > FMax) {
Key = 0;
Text = FloatToStr(FMax); } }
// вызвать функцию обработки события KeyPress базового класса
TEdit::KeyPress(Key);
}
// устанавливает значение поля FMin
bool __fastcall TNkEdit::SetMin(float min)
{
if (min > FMax) return false;
FMin = min; return true;
}
// устанавливает значение поля FMin
bool __fastcall TNkEdit::SetMax(float max)
{
if ( max < FMin) return false;
FMax = max; return true;
}

В объявлении класса TNkEdit добавлены объявления полей FEnabiedFloat, FMin и FMax. Имена полей, согласно принятому в C++ Builder соглашению, 'начинаются с буквы F (от Field, поле). Поле FEnabledFioat хранит признак возможности ввода в поле редактирования дробного числа. Поля FMin и FMax хранят границы диапазона. Доступ к полям обеспечивают соответствующие свойства: EnabiedFloat, Min и мах. Так как объявления этих свойств находятся в секции published, то они будут доступны в окне Object Inspector. Свойство Numb, представляющее собой число, которое находится в поле редактирования, объявлено в секции public, поэтому оно доступно только во время работы программы. Здесь следует обратить внимание на то, что у свойства Numb нет соответствующего поля. Значение этого свойства вычисляется во время работы программы путем преобразования в число значения свойства Text базового компонента. Свойства Min и мах получают доступ к полям данных для чтения напрямую, для записи — посредством функций SetMin и SetMax. Свойство EnabдedFдoat получает доступ к полю FEnabledFloat для чтения и записи напрямую. Так как компонент NkEdit должен обеспечить фильтрацию символов (в поле редактирования должны отображаться только цифры и, в случае, если значение свойства EnabiedFloat равно true, десятичный разделитель), то в объявление класса добавлено объявление функции KeyPress, которая предназначена для обработки соответствующего события.

Реакцию компонента NkEdit на нажатие клавиши клавиатуры определяет функция TNkEdit::KeyPress. В качестве параметра эта функция получает код нажатой клавиши. Перед вызовом функции TEdit: :KeyPress, которая обеспечивает обработку события Keypress базовым компонентом, код нажатой клавиши проверяется на допустимость. Если нажата недопустимая клавиша, то код символа заменяется на ноль. Допустимыми для компонента NkEdit, в зависимости от его настройки, являются цифровые клавиши, разделитель целой и дробной частей числа (в зависимости от настройки Windows, точка или запятая), "минус" и клавиша <Backspase>.

Здесь следует вспомнить, что в тексте программы дробная часть числовой константы отделяется от целой части точкой. Во время работы программы при вводе исходных данных пользователь должен использовать тот символ, который задан в настройке Windows. В качестве разделителя обычно используют запятую (стандартная для России настройка) или точку. Приведенная процедура обработки события onKeypress учитывает, что настройка Windows может меняться, и поэтому введенный пользователем символ сравнивается не с константой, а со значением глобальной переменной Decimalseparator, которая содержит символ-разделитель, используемый в Windows в данный момент.


Тестирование компонента



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

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

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

Вид формы приложения тестирования компонента NkEdit приведен на рис. 6.2Д

Форма содержит две метки. Первая метка обеспечивает вывод общей информации о компоненте; вторая метка (на рисунке она выделена) используется для вывода информации о настройке компонента. Самого компонента NkEdit в форме нет. Он будет создан во время работы программы, в момент ее запуска.

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

1. В текст файла реализации (срр-файл) включить следующую директиву #include "nkedit.cpp".

2. Объявить компонент NkEdit (оператор TNkEdit *NkEdit). Здесь следует вспомнить, что компонент является объектом (точнее ссылкой на объект), поэтому объявление компонента не обеспечивает создание компонента, а только создает указатель на компонент.

3. Для формы приложения создать процедуру обработки события oncreate, которая путем вызова конструктора тестируемого компонента создаст компонент и выполнит его настройку (присвоит значения свойствам компонента).



Рис. 6.2. Форма приложения "Тест компонента NkEdit"


В листинге 6.5 приведен файл реализации приложения тестирования компонента NkEdit.

 Листинг 6.5. Тест компонента NkEdit

#include <vcl.h>
#pragma hdrstop
#include "tk_.h"
#include "nkedit.cpp"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForml *Forml; // форма
TNkEdit *NkEdit; // компонент программиста
// конструктор формы
__£astcall TForml::TForml(TComponent*Owner):TForm(Owner)
{
// создадим и инициализируем компонент
NkEdit NkEdit = new TNkEdit(Forml);
NkEdit->Parent = Forml;
NkEdit->Text = "0";
NkEdit->Left = 10; NkEdit-ХГор = 100;
// настроим компонент
// зададим границы диапазона NkEdit->Min = -100;
NkEdit->Max = 100;
NkEdit->EnableFloat = true;
// разрешен ввод дробных чисел }
// обработка события FormCreate
void __fastcall TForml::FormCreate(TObject *Sender)
{
AnsiString st = "Введите ";
// информация о компоненте if (NkEdit->EnableFloat)
st = st + "дробное";
else st = st + "целое";
st = st + " число от ";
st = st + FloatToStr(NkEdit->Min);
 st = st + " до ";
st = st + FloatToStr(NkEdit->Max);
Label2->Caption = st; }



Рис. 6.З. Тестирование компонента: поле ввода — компонент NkEdit


Тестируемый компонент создает и настраивает конструктор формы. Следует обратить внимание, что свойству Parent созданного компонента обязательно надо присвоить значение. Если этого не сделать, то компонент на форме не появится. Информацию о настройках созданного компонента выводит функция FormCreate. На рис. 6.3 приведен вид окна программы "Тест компонента NkEdit во время ее работы.


Установка компонента



Для того чтобы значок компонента появился в палитре компонентов, компонент должен быть добавлен в один из пакетов (Packages) компонентов C++ Builder. Пакет компонентов — это специальная библиотека (файл с расширением bpk). Например, для компонентов, созданных программистом, предназначен пакет dclusr.bpk.

В процессе добавления компонента в пакет C++ Builder использует файл ресурсов компонента, в котором должен находиться битовый образ значка компонента. Имя файла ресурсов компонента должно совпадать с именем файла модуля компонента. Файл ресурсов имеет расширение dcr (Dynamic Component Resource). Битовый образ, находящийся внутри файла ресурсов, должен иметь имя, совпадающее с именем класса компонента.


Ресурсы компонента



Файл ресурсов компонента можно создать при помощи утилиты Image Editor, которую можно запустить из C++ Builder (команда Tools | Image Editor) или из Windows (команда Пуск | Программы | C++ Builder | Image Editor). Для того чтобы создать файл ресурсов компонента, нужно из меню File выбрать команду New и из появившегося списка выбрать тип создаваемого файла — Component Resource File (рис. 6.4).

В результате открывается окно файла ресурсов Untitledl.dcr, а в меню диалогового окна Image Editor появляется новый пункт — Resource. Теперь нужно из меню Resourse выбрать команду New/Bitmap и в открывшемся окне Bitmap Properties (рис. 6.5) установить характеристики битового образа значка компонента: Size — 24x24 пиксела, Colors — 16. В результате этих действий в создаваемый файл ресурсов компонента будет добавлен новый ресурс — битовый образ с именем Bitmap 1. Двойной щелчок на имени ресурса (Bitmap 1) раскрывает окно редактора битового образа, в котором можно нарисовать нужную картинку.

Изображение в окне графического редактора можно увеличить. Для этого необходимо выбрать команду Zoom In меню View.

Следует обратить внимание на то, что цвет правой нижней точки рисунка определяет "прозрачный" цвет. Элементы значка компонента, закрашенные этим цветом, на палитре компонентов C++ Builder не видны.



Рис. 6.4. Начало работы по созданию файла ресурсов компонента



Рис. 6.5. Диалоговое окно Bitmap Properties


Перед тем как сохранить файл ресурсов компонента, битовому образу надо присвоить имя. Имя должно совпадать с именем класса компонента. Чтобы задать имя битового образа, необходимо щелкнуть правой кнопкой мыши на имени битового образа (Bitmap!), выбрать в появившемся контекстном меню команду Rename и ввести новое имя.

Созданный файл ресурсов компонента нужно сохранить в том каталоге, в котором находится файл модуля компонента. Для этого надо из меню File выбрать команду Save.

На рис. 6.6 приведен вид окна Image Editor, в левой части которого содержится файл ресурсов компонента TNkEdit (nkedit.dcr), а в правой части — окно редактора битового образа, в котором находится изображение значка компонента. Обратите внимание, что имя файла ресурсов компонента (NkEdit.dcr) должно совпадать с именем модуля компонента (NkEdit.cpp), a имя битового образа (TNKEDIT) — с именем класса компонента (TNkEdit).



Рис. 6.6. Значок компонента NkEdit


Установка



После того как будет создан файл ресурсов компонента, можно приступить к установке компонента в пакет компонентов. Компонент можно установить в существующий пакет или создать новый пакет и затем установить в него компонент.

Чтобы установить компонент в существующий пакет, надо из меню Component выбрать команду Install Component и заполнить поля вкладки Into existing package диалогового окна Install Component (рис. 6.7).



Рис. 6.7. Диалоговое окно Install Component


В поле Unit file name (Имя файла модуля) нужно ввести имя файла модуля. Для этого удобно воспользоваться кнопкой Browse.

Поле Search path (Путь поиска) должно содержать разделенные точкой с запятой имена каталогов, в которых C++ Builder во время установки компонента будет искать необходимые файлы, в частности файл ресурсов компонента. Если имя файла модуля было введено в поле Unit file name выбором файла из списка, полученного при помощи кнопки Browse, то C++ Builder автоматически добавляет в поле Search path имена необходимых каталогов. Следует обратить внимание на то, что файл ресурсов компонента должен находиться в одном из каталогов, перечисленных в поле Search path. Если его там нет, то компоненту будет назначен значок его родительского класса.

Поле Package file name должно содержать имя пакета, в который будет установлен компонент. По умолчанию компоненты, создаваемые программистом, добавляются в пакет dclusr.bpk.

Поле Package description содержит название пакета. Для пакета dclusr.bpk это текст: Borland User Components.

После того как поля будут заполнены, надо щелчком на кнопке ОК активизировать процесс установки. Сначала на экране появляется окно Confirm (рис. 6.8), в котором C++ Builder просит подтвердить обновление пакета.



Рис. 6.8. Запрос подтверждения обновления пакета в процессе установки компонента


После щелчка на кнопке Yes процесс установки продолжается. Если он завершается успешно, то на экране появляется информационное сообщение (рис. 6.9) о том, что пакет обновлен, а компонент зарегистрирован.



Рис. 6.9. Сообщение об успешной установке компонента


После установки компонента в пакет открывается диалоговое окно Package (Редактор пакета компонентов) (рис. 6.10), в котором перечислены компоненты, находящиеся в пакете.



Рис. 6.10. Окно редактора пакета компонентов


На этом процесс установки компонента заканчивается. В результате на вкладке палитры компонентов, имя которой было задано при создании модуля компонента, появляется значок установленного компонента (рис. 6.11).



Рис. 6.11. Значок компонента NkEdit на вкладке Samples


Проверка компонента



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

Можно считать, что компонент работает правильно, если во время разработки приложения удалось поместить этот компонент в форму разрабатываемого приложения и, используя окно Object Inspector, установить значения свойств компонента, причем как новых, так и унаследованных от родительского класса.

Работоспособность компонента NkEdit можно проверить, использовав его, например, в приложении "Сила тока", вид формы которого приведен на рис. 6.12.



Рис. 6.12. Форма приложения "Сила тока" (поля ввода-редактирования компоненты NkEdit)


Внешне форма разрабатываемого приложения почти ничем не отличается от формы приложения "Сила тока", рассмотренного в гл. 2. Однако если выбрать поле ввода, то в окне Object Inspector будет указано, что текущим компонентом является компонент класса TNkEdit, а в списке свойств можно будет увидеть свойства, которых нет у стандартного компонента Edit (рис. 6.13).



Рис. 6.13. Значения свойств EnableFloat, Max и Min компонента NkEdit можно задать в окне Object Inspector


В листинге 6.6 приведен модуль приложения "Сила тока". Здесь надо обратить внимание на следующее. Первое. В программе нет кода, обеспечивающего фильтрацию символов, вводимых в поле редактирования. Тем не менее во время работы программы пользователь может ввести в поле редактирования только положительное число. Второе. В программе не используется функция strToFloat. Число, которое соответствует введенной в поле редактирования строки символов, получается путем обращения к свойству Numb.

Очевидно, что использование в программе компонента NkEdit вместо стандартного Edit освобождает программиста от рутины, сокращает размер кода и делает его более понятным.

Листинг 6.6."Сила тока"

// нажатие клавиши в поле Напряжение
void __fastcall TForml::NkEditlKeyPress(TObject *Sender, char SKey)
{
if ( Key == VK_RETORN)
NkEdit2->SetFocus(); }
// нажатие клавиши в поле Сопротивление
void __fastcall TForml::NkEdit2KeyPress(TObject *Sender, char &Key)
{
if ( Key == VK_RETORN)
Buttonl->SetFocus(); }
// нажатие кнопки Вычислить
void __fastcall TForml::ButtonlClick(TObject *Sender)
{
float u; // напряжение
float r; // сопротивление
float i; // ток
// получить исходные данные из полей ввода
 u = NkEditl->Numb;
r = NkEdit2->Nuinb;
if ( r == 0) {
ShowMessage("Сопротивление не должно быть равно нулю");
return; }
// вычислить ток i = u/r;
// вывести результат Label4->Caption = "Ток : " +
FloatToStrF(i,ffGeneral,7,3) + "А";
}


Настройка палитры компонентов



C++ Builder позволяет менять порядок следования вкладок в палитре компонентов, названия вкладок, а также порядок следования значков компонентов на вкладках. Настройка палитры компонентов выполняется в диалоговом окне Palette Properties, которое открывается выбором из меню Component команды Configure Palette (рис. 6.14).



Рис. 6.14. Диалоговое окно Palette Properties


Сначала в списке Pages необходимо выделить нужную вкладку палитры компонентов. Затем, если надо изменить порядок следования вкладок палитры компонентов, следует воспользоваться кнопками Move Up и Move Down и путем перемещения выбранного имени по списку Pages добиться нужного порядка следования вкладок.

Если надо изменить порядок следования значков компонентов на вкладке, то в списке Components следует выбрать нужный значок компонента и кнопками Move Up и Move Down переместить значок на новое место.

При необходимости изменить имя вкладки палитры следует в списке Pages выбрать имя нужной вкладки, нажать кнопку Rename и в поле Page name открывшегося диалогового окна Rename page (рис. 6.15) ввести новое имя.



Рис. 6.15. Диалоговое окно Rename page