Copyright: Crimson Help ©2001 By A. Zenchenko


Обзор создания компонентов
 Руководство разработчика компонентов

Обзор создания компонентов

Список тем	См. также

Данный набор тем представляет собой описание процесса разработки компонентов для C++Builder. Материал, представленный здесь, предполагает, что вы хорошо знакомы с C++Builder и его стандартными компонентами. Основными обсуждаемыми темами являются:

•	Библиотека Визуальных Компонентов (VCL).

•	Компоненты и классы.

•	Как вы создаете компоненты?

•	Что входит в компонент?

•	Создание нового компонента.

•	Тестирование неустановленных компонентов.

•	Тестирование установленных компонентов.

•	Установка компонента в Палитру компонентов (Component palette).

Для информации об установке новых компонентов смотрите раздел "Установка пакетов компонентов".


Библиотека Визуальных Компонентов (VCL)
 Руководство разработчика компонентов

Библиотека Визуальных Компонентов (VCL)

Список тем	См. также

Все компоненты C++Builder являются частью иерархии классов Библиотеки Визуальных Компонентов (VCL). Следующий рисунок показывает взаимоотношение выбранных классов, составляющих VCL. Для более подробной информации об иерархии классов и отношений наследования между классами, смотрите раздел "Объектно-ориентированное программирование для создателей компонентов".

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

.

Когда вы создаете компонент, вы добавляете в VCL новый класс, наследуя его от одного из существующих классов в иерархии.


Компоненты и классы
 Руководство разработчика компонентов

Компоненты и классы

Список тем	См. также

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

Коротко, существуют два основных отличия между созданием компонентов и использованием их в приложениях. При создании компонентов вы:

•	Имеете доступ к частям класса, которые недоступны разработчикам приложений.

•	Добавляете новые части (такие, как свойства) в ваши компоненты.

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


Как вы создаете компоненты?
 Руководство разработчика компонентов

Как вы создаете компоненты?

Список тем	См. также

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

•	Модификация существующего компонента.

•	Создание оконных компонентов.

•	Создание графических компонентов.

•	Сабклассинг (subclassing) элементов управления Windows.

•	Создание невизуальных компонентов.

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

Цель создания компонента	Базовый класс
Модификация существующего компонента.	Любой существующий компонент, например TButton или TListBox, или абстрактный класс компонента, такой, как TCustomListBox.
Создание оконных компонентов.	TWinControl.
Создание графического элемента управления.	TGraphicControl
Сабклассинг элемента управления Windows.	Любой элемент управления Windows.
Создание невизуального компонента.	TComponent.
Вы можете также порождать классы, которые не являются компонентами и не могут управляться на форме. C++Builder включает много таких классов, например TRegIniFile и TFont.


Модификация существующего компонента
 Руководство разработчика компонентов

Модификация существующего компонента

Список тем	См. также

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

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

Например, вы можете захотеть создать специальный список, который не содержит некоторых свойств стандартного класса TListBox. Вы не можете удалить (скрыть) свойства, унаследованные от базового класса, так что вам нужно породить ваш компонент от чего-либо, расположенного в иерархии классов выше, чем TListBox. Вместо того, чтобы заставлять вас начинать с абстрактного класса TWinControl и заново изобретать все функции списка, VCL предоставляет класс TCustomListBox, который реализует свойства списка, но не публикует все из них. Когда вы порождаете компонент от абстрактного класса, такого, как TCustomListBox, вы публикуете только те свойства, которые вы хотите сделать доступными в вашем компоненте, а остальные оставляете защищенными.

Раздел "Создание свойств" объясняет публикацию унаследованных свойств. Раздел "Модификация существующего компонента" и раздел "Модификация сетки" показывают примеры модификации существующих элементов управления.


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

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

Список тем	См. также

Оконные элементы управления – это объекты, которые отображаются во время выполнения и с которыми пользователь может взаимодействовать. Каждый оконный элемент управления имеет дескриптор окна, доступный через свойство Handle, который позволяет Windows идентифицировать элемент управления и оперировать им. Дескриптор позволяет элементу управления получать фокус ввода, а также он может быть передан в функции Windows API.

Все оконные элементы управления происходят от класса TWinControl, включая большинство стандартных элементов управления Windows, таких как кнопки, списки и поля ввода. Хотя вы можете породить оригинальный элемент управления (то есть, не относящийся ни к одному из существующих элементов управления) непосредственно от TWinControl, C++Builder предоставляет для такой цели компонент TCustomControl. TCustomControl является специализированным оконным элементом управления, облегчающим рисование сложных графических образов.

Раздел "Модификация сетки" представляет пример создания оконного элемента управления.


Создание графического элемента управления
 Руководство разработчика компонентов

Создание графического элемента управления

Список тем	См. также

Если ваш элемент управления не нуждается в получения фокуса ввода, вы можете его сделать графическим элементом управления. Графические элементы управления похожи на оконные, но у них отсутствует дескриптор окна, следовательно, графические элементы управления потребляют меньше системных ресурсов. Такие компоненты, как  TLabel, которые никогда не получают фокус ввода, являются графическими элементами управления. Хотя подобные элементы управления и не могут получать фокус ввода, вы можете разрабатывать их таким образом, чтобы они реагировали на сообщения мыши.

C++Builder поддерживает создание графических элементов управления через TGraphicControl. TGraphicControl является абстрактным классом, порожденным от TControl. Хотя вы можете порождать компоненты непосредственно от TControl, лучшим решением является начать с TGraphicControl, который предоставляет канву (canvas) для рисования и обрабатывает сообщения WM_PAINT; все, что вам необходимо – это переопределить метод Paint.

Раздел "Создание графического компонента" представляет пример создания графического элемента управления.


Сабклассинг элемента управления Windows
 Руководство разработчика компонентов

Сабклассинг элемента управления Windows

Список тем	См. также

В традиционном Windows-программировании вы создаете специальные элементы управления, определяя новый класс окна и регистрируя его в Windows. Класс окна (который похож на объекты или классы в объектно-ориентированном программировании) содержит информацию, разделяемую между экземплярами элементов управления одного типа; вы можете основывать новый оконный класс на существующем классе, что называется сабклассингом (subclassing). Затем вы можете поместить ваш элемент управления в динамически подключаемую библиотеку (DLL), подобно стандартным элементам управления Windows, и предоставить интерфейс к нему.

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

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


Создание невизуального компонента
 Руководство разработчика компонентов

Создание невизуального компонента

Список тем	См. также

Невизуальные компоненты используются как интерфейсы к таким элементам, как базы данных (TDataSet) и системный таймер (TTimer), а также как "заполнители" (placeholders) для диалоговых окон (TCommonDialog и его потомки). Большинство компонентов, которые вы создадите, вероятно, будут визуальными элементами управления. Невизуальные компоненты могут быть унаследованы непосредственно от TComponent, абстрактного базового класса для всех компонентов.


Что входит в компонент?
 Руководство разработчика компонентов

Что входит в компонент?

Список тем	См. также

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

•	Устранение зависимостей.

•	Свойства, методы и события.

•	Инкапсуляция графики.

•	Регистрация.


Устранение зависимостей
 Руководство разработчика компонентов

Устранение зависимостей

Список тем	См. также

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

Прекрасным примером устранения зависимостей является свойство Handle у TWinControl. Если вы писали когда-либо приложения Windows, вы знаете, что одним из самых трудных и подверженных ошибкам аспектов, является уверенность в том, что вы не пытаетесь получить доступ к окну или элементу управления до того, как вы его создали функцией API CreateWindow. Оконные элементы управления C++Builder избавляют пользователей от необходимости следить за доступностью дескриптора окна. Используя свойство для предоставления дескриптора окна, компонент может проверить, было ли окно уже создано; если дескриптор недействителен, то компонент создает окно и возвращает его дескриптор. Таким образом, когда бы код приложения не обращался к свойству Handle, оно всегда получит действительный дескриптор окна.

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

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


Свойства, методы и события
 Руководство разработчика компонентов

Свойства, методы и события

Список тем	См. также

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

Свойства

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

Есть несколько преимуществ от использования свойств:

•	Свойства доступны во время разработки. Разработчик приложения может установить или изменить начальные значения свойств без написания кода.

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

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

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

Раздел "Создание свойств" объясняет, как добавить свойства в ваш компонент.

События

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

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

Раздел "Создание событий" объясняет, как реализовать стандартные события и как определить новые.

Методы

Методы класса – это функции, которые оперируют самим классом, а не конкретными экземплярами класса. Например, конструктор любого компонента является методом класса. Методы компонентов – это функции, которые оперируют экземплярами компонентов. Разработчики приложений используют методы для того, чтобы "заставить" компонент выполнить определенное действие или вернуть значение, не содержащееся в каком-либо свойстве.

Поскольку методы требуют выполнения кода, то они могут быть вызваны только во время выполнения. Методы полезны в силу нескольких причин:

•	Методы инкапсулируют функциональность компонента в том же объекте, где хранятся данные.

•	Методы могут скрыть сложные процедуры за простым интерфейсом. Разработчик приложения может вызвать метод AlignControls компонента не зная, как работает данный метод, или чем он отличается от метода AlignControls в другом компоненте.

•	Методы позволяют обновить значение нескольких свойств одним вызовом.

Раздел "Создание методов" объясняет, как добавить методы в ваш компонент.


Инкапсуляция графики
 Руководство разработчика компонентов

Инкапсуляция графики

Список тем	См. также

C++Builder упрощает работу с графикой Windows, инкапсулируя различные графические инструменты в канву (canvas). Канва представляет собой поверхность для рисования окна или элемента управления и содержит другие классы, такие, как перо, кисть и шрифт. Канва наиболее похожа на контекст устройства Windows, но она берет на себя все заботы об учете системных ресурсов.

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

С C++Builder вы не должны беспокоиться об этих вещах. Для рисования на форме или другом компоненте, вы получаете доступ к свойству Canvas компонента. Если вы хотите изменить перо или кисть, вы устанавливаете их цвет или стиль. Когда вы закончите, C++Builder освободит ресурсы. C++Builder также кэширует ресурсы, чтобы избежать повторного их создания, если ваше приложение часто использует одни и те же виды ресурсов.

Вы по-прежнему имеете полный доступ к Windows GDI, но вы часто будет находить, что ваш код проще и работает быстрее, если вы используете канву, встроенную в компоненты C++Builder. Особенности графики поясняются в разделе "Использование графики в компонентах".


Регистрация
 Руководство разработчика компонентов

Регистрация

Список тем	См. также

Перед тем, как установить ваш компонент в IDE C++Builder, вы должны его зарегистрировать. Регистрация сообщает C++Builder, на какой странице Палитры компонентов (Component palette) вы хотите поместить свой компонент. Вы также можете изменить способ, которым C++Builder хранит ваши комопненты в файле формы. Для информации о регистрации комопнентов смотрите раздел "Регистрация компонентов".


Создание нового компонента
 Руководство разработчика компонентов

Создание нового компонента

Список тем	См. также

Вы можете использовать любой из методов для создания компонента с минимальной функциональностью, готового к установке в Палитру компонентов (Component palette). После установки вы можете добавить новый компонент к форме и протестировать его как во время разработки, так и во время выполнения. Затем вы можете добавить функциональность в компонент, обновить Палитру компонентов и продолжить тестирование.

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

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

2.	Порождение вашего компонента от существующего типа компонента.

3.	Добавление свойств, методов и событий.

4.	Регистрация компонента в среде C++Builder.

5.	Создание файла справки для вашего компонента и его свойств, методов и событий.

6.	Создание пакета (специальной библиотеки динамической компоновки), чтобы вы могли инсталлировать в C++Builder IDE.

Когда вы закончите, компонент будет включать в себя следующие файлы:

•	Пакет (.BPL) или коллекция пакетов (.PCE).

•	Библиотека (.LIB) для пакета.

•	Библиотека импорта Borland (.BPI) для пакета.

•	Откомпилированный модуль (.OBJ).

•	Откомпилированный файл ресурса (.RES) для Палитры компонентов.

•	Файл справки (.HLP).

Создание файла справки для пользователей компонента необязательно. Включение файла справки обязательно, только если он создан.


Использование Мастера компонентов
 Руководство разработчика компонентов

Использование Мастера компонентов

Список тем	См. также

Мастер компонентов (Component wizard) упрощает начальные шаги создания компонентов. Когда вы используете Мастер компонентов, вам необходимо определить следующее:

1.	Класс, от которого вы порождаете компонент.

2.	Страницу Палитры компонентов, на которую вы хотите установить компонент.

3.	Имя модуля компонента.

4.	Местонахождение модуля компонента.

5.	Имя пакета, в который вы помещаете компонент.

Мастер компонентов выполняет те же задачи, что и вы, создавая компонент вручную:

•	Создание модуля компонента (.CPP файл и связанный с ним заголовочный файл).

•	Порождение компонента.

•	Объявление нового конструктора.

•	Регистрация компонента.

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

Для запуска Мастера компонентов используйте один из этих методов:

Выберите в меню Component|New Component.

Выберите в меню Choose File|New и два раза щелкните на Component.

Заполните поля в мастере компонентов:

•	В поле Ancestor Type укажите класс, от которого вы порождаете новый компонент.

•	В поле Class Name укажите имя класса нового компонента.

•	В поле Palette Page укажите страницу Палитры компонентов, на которую вы хотите установить новый компонент.

•	В поле Unit file name укажите имя модуля, в котором будет находиться объявление класса компонента.

•	Если модуль не находится в пути поиска, отредактируйте путь поиска в поле Search Path при необходимости.

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

Предупреждение: если вы порождаете компонент от класса VCL, чье имя начинается со слова "custom" (например, TCustomControl), не пытайтесь поместить новый компонент на форму до тех пор, пока вы не переопределили какой-либо из абстрактных методов исходного компонента. C++Builder не может создать экземпляры объектов класса, у которого есть абстрактные свойства или методы.

После того, как вы заполните поля в Мастере компонентов, нажмите ОК. C++Builder создаст новый модуль, состоящий из CPP файла и связанного с ним файла заголовка.

CPP файл появляется в Редакторе кода (Code editor). Он содержит конструктор для компонента и функцию Register, которая регистрирует компонент, информируя C++Builder, какой компонент добавить в библиотеку компонентов и на какой странице Палитры компонентов он должен появиться. Файл также содержит оператор #include, который определяет файл заголовка. Например:

#include <vcl\vcl.h>

#pragma hdrstop

#include "NewComponent.h"

#pragma package(smart_init);

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

static inline TNewComponent *ValidCtrCheck()

{

return new TNewComponent(NULL);

}

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

__fastcall TNewComponent::TNewComponent(TComponent* Owner)

: TComponent(Owner)

{

}

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

namespace Newcomponent

{

void __fastcall PACKAGE Register()

{

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

RegisterComponents("Samples", classes, 0);

}

}

Для открытия заголовочного файла в Редакторе кода, поместите курсор на имени заголовочного файла, щелкните правой кнопкой мыши для отображения контекстного меню, и выберите в меню Open File at Cursor.

Заголовочный файл содержит объявление нового класса, содержащее объявление конструктора и несколько операторов #include для поддержки нового класса. Например:

#ifndef NewComponentH

#define NewComponentH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TNewComponent : public TComponent

{

private:

protected:

public:

__fastcall TNewComponent(TComponent* Owner);

__published:

};

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

#endif

Сохраните .CPP файл под значащим именем перед продолжением.


Создание компонента вручную
 Руководство разработчика компонентов

Создание компонента вручную

Список тем	См. также

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

Для создания компонента вручную следуйте перечисленным ниже пунктам:

1.	Создание файла модуля.

2.	Порождение компонента.

3.	Объявление нового конструктора.

4.	Регистрация компонента.


Создание файла модуля
 Руководство разработчика компонентов

Создание файла модуля

Список тем	См. также

Модуль C++Builder представляет собой сочетание .CPP и .H файла, которое компилируется в .OBJ файл.

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

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

Чтобы создать модуль для компонента, используйте один из следующих методов:

•	В меню выберите File|New Unit.

•	В меню выберите File|New для отображения диалогового окна New Items, перейдите в нем на закладку New, выберите Unit и нажмите OK.

C++Builder создаст .CPP файл и связанный с ним файл заголовка и отобразит .CPP файл в Редакторе кода. Сохраните файл под значащим именем.

Для открытия заголовочного файла в Редакторе кода, поместите курсор на имени заголовочного файла, щелкните правой кнопкой мыши для отображения контекстного меню, и выберите в меню Open File at Cursor. Для открытия существующего модуля выберите в меню File|Open модуль, в который вы хотите добавить свой компонент.

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

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


Порождение компонента
 Руководство разработчика компонентов

Порождение компонента

Список тем	Пример

Каждый компонент является классом, порожденным от TComponent, от одного из более специализированных его потомков (TControl или TGraphicControl) или от существующего класса компонента. Раздел "Как вы создаете компоненты?" описывает, от какого класса порождать различные типы компонентов.

Порождение классов более подробно описывается в разделе "Определение новых классов".

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

Простой класс компонента представляет собой невизуальный компонент, унаследованный напрямую от TComponent.


Пример: порождение компонента
 Руководство разработчика компонентов

Пример: порождение компонента

Для создания класса простого компонента добавьте следующее объявление класса в ваш заголовочный файл:

class PACKAGE TNewComponent : public TComponent

{

};

Макрос PACKAGE разворачивается в оператор, который позволяет классам быть импортируемыми и экспортируемыми. Вы должны также добавить необходимые операторы #include, которые определяют .HPP файлы, необходимые новому компоненту. Ниже приведены наиболее общие операторы #include, которые вам понадобятся:

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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


Объявление нового конструктора
 Руководство разработчика компонентов

Объявление нового конструктора

Список тем	См. также

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

Внутри объявления класса объявите виртуальный конструктор в открытой секции класса. Вы можете узнать больше об открытой секции класса в разделе "Управление доступом".

Пример:

class PACKAGE TNewComponent : public TComponent

{

public:

  virtual __fastcall TNewComponent(TComponent* AOwner);

};

В .CPP файле реализуйте конструктор:

__fastcall TNewComponent::TNewComponent(TComponent* AOwner): TComponent(AOwner)

{

}

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


Регистрация компонента
 Руководство разработчика компонентов

Регистрация компонента

Список тем	Пример

Регистрация – это простой процесс, который сообщает C++Builder, какие компоненты добавить в библиотеку компонентов, и на какой странице Палитры компонентов они должны появиться. Для более подробного обсуждения процесса регистрации смотрите раздел "Доступность компонентов во время разработки".

Для регистрации компонента:

1.	Добавьте функцию Register в .CPP файл модуля, поместив ее внутри пространства имен (namespace). Пространство имен представляет собой имя файла, в котором находится компонент, за вычетом расширения файла, написанное строчными буквами, за исключением первой буквы. Например, этот код находится внутри пространства имен Newcomp, где Newcomp – имя .CPP файла.

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

  }

}

2.	Внутри функции Register объявите открытый массив типа TComponentClass, который содержит массив компонентов, которые вы регистрируете. Синтаксис должен выглядеть следующим образом:

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

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

3.	Внутри функции Register вызовите функцию RegisterComponents для каждого компонента, который вы хотите зарегистрировать.

RegisterComponents – это функция, которая принимает три параметра: имя страницы Палитры компонентов, массив классов компонентов и количество классов минус один. Если вы добавляете компонент в существующую регистрацию, вы можете либо добавить новый компонент в существующий оператор, либо добавить новый оператор, который вызывает функцию RegisterComponents.

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


Пример: регистрация компонента
 Руководство разработчика компонентов

Пример: регистрация компонента

Для регистрации компонента, названного TNewComponent, и помещения его на страницу Samples Палитры компонентов, добавьте следующую функцию Register в .CPP файл модуля, который объявляет TNewComponent:

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

Вызов Register помещает TNewComponent на страницу Samples Палитры компонентов.

Как только вы зарегистрировали компонент, вы может его протестировать и окончательно установить его на Палитру компонентов. Это подробно описано в разделе "Установка компонента на Палитру компонентов".


Тестирование неустановленных компонентов
 Руководство разработчика компонентов

Тестирование неустановленных компонентов

Список тем	Пример

Вы можете протестировать поведение компонента во время выполнения перед установкой его в Палитру компонентов. Это особенно полезно для отладки нового компонента, но эта же техника работает с любым компонентом, независимо от того, установлен ли он в Палитру компонентов, или нет. Для информации по тестированию установленных компонентов, смотрите раздел "Тестирование установленных компонентов".

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

Вы тестируете неустановленные компоненты, имитируя действия, выполняемые C++Builder, когда компонент выбирается в Палитре компонентов и помещается на форму.

Для тестирования несутановленного компонента выполните следующее:

1.	Создайте новое приложение или откройте существующее.

2.	Выберите пункт меню Project|Add to Project для добавления модуля компонента в ваш проект.

3.	Включите .H файл вашего компонента в .H файл модуля формы.

4.	Добавьте объект данных в форму для представления компонента.

Это одно из основных различий в способе, которым вы добавляете компонент, и которым добавляет компонент C++Builder. Вы добавляете объект данных в открытую (public) часть класса формы. C++Builder добавляет объект данных в опубликованную (published) часть класса.

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

5.	Создайте компонент в конструкторе формы.

Когда вы вызываете конструктор компонента, вы должны в него передать параметр, определяющий владельца компонента (владелец – компонент, ответственный за разрушение компонента). Вы почти всегда передаете this в качестве владельца. Внутри метода this указывает на класс, содержащий метод. В случае, например, использования this в обработчике события OnCreate формы, он указывает на форму.

6.	Присвоить значение свойству Parent.

Установка свойства Parent всегда является первой задачей после создания компонента. Родитель – это компонент, который визуально содержит элемент управления. Родителем чаще всего является форма, но может быть панель (panel) или контейнер (group box). Обычно, вы устанавливаете значение свойства Parent в this, то есть, родителем является форма. Всегда устанавливайте Parent перед установкой других свойств элемента управления.

7.	Установите остальные свойства компонента.


Пример: тестирование неустановленных компонентов
 Руководство разработчика компонентов

Пример: тестирование неустановленных компонентов

Предположим, вы хотите протестировать новый компонент, класс которого называется TNewControl, а модуль – NewCtrl. Создайте новый проект, затем пройдите все этапы, описанные выше. Файлы должны выглядеть следующим образом:

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

#ifndef TestFormH

#define TestFormH

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

#include <vcl\Classes.hpp>

#include <vcl\Controls.hpp>

#include <vcl\StdCtrls.hpp>

#include <vcl\Forms.hpp>

#include "NewCtrl.h"

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

class TForm1 : public TForm

{

__published:

private:

public:

    TNewControl* NewControl1;

    __fastcall TForm1(TComponent* Owner);

};

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

extern TForm1 *Form1;

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

#endif

Оператор #include, который включает NEWCTRL.H предполагает, что файлы компонента находятся в директории проекта или в директории, которая прописана в путях включения проекта.

#include <vcl\vcl.h>

#pragma hdrstop

#include "TestForm.h"

#include "NewCtrl.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TForm1 *Form1;

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

static inline TNewControl *ValidCtrCheck()

{

    return new TNewControl(NULL);

}

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

__fastcall TForm1::TForm1(TComponent* Owner)

    : TForm(Owner)

{

    NewControl1 = new TNewControl(this);

    NewControl1->Parent = this;

    NewControl1->Left = 12;

}

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

namespace Newctrl

{

    void __fastcall PACKAGE Register()

    {

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

        RegisterComponents("Samples", classes, 0);

    }

}


Тестирование установленных компонентов
 Руководство разработчика компонентов

Тестирование установленных компонентов

Список тем	См. также

Вы можете протестировать поведение компонента во время разработки после установки его в Палитру компонентов. Это особенно полезно для отладки нового компонента, но эта же техника работает с любым компонентом, независимо от того, установлен ли он в Палитру компонентов, или нет. Для информации по тестированию неустановленных компонентов, смотрите раздел "Тестирование неустановленных компонентов".

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

Тестируйте установленные компоненты, используя второй запущенный экземпляр C++Builder:

1.	В меню C++Builder IDE выберите Project|Options|Debug Source Path и установите путь к файлу с исходным кодом компонента.

2.	Затем выберите Tools|Debugger Options| и разрешите исключения, которые вы хотите отслеживать.

3.	Откройте файл с исходным кодом компонента и установите точки останова.

4.	Выберите Run|Parameters и установите поле Host Application в имя и путь к исполняемому файлу C++Builder.

5.	В диалоговом окне Run Parameters нажмите кнопку Load для запуска второго экземпляра C++Builder.

6.	Затем поместите тестируемые компоненты на форму и начинайте тестирование.


Установка компонента в Палитру компонентов
 Руководство разработчика компонентов

Установка компонента в Палитру компонентов

Список тем	См. также

Установка компонента состоит из двух этапов. Первый – обеспечение доступности исходного кода компонента, и второй – использование диалога Install Component для действительного добавления компонента.

Расположение файлов компонента.

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


Расположение файлов компонента
 Руководство разработчика компонентов

Расположение файлов компонента

Список тем	См. также

Все файлы, используемые компонентом, должны находиться в одной директории. Это относится к файлам с исходным кодом (.CPP and .PAS) и двоичным файлам (.DFM, .RES, .RC и .DCR). Заголовочные файлы (.H and .HPP) должны находиться в директории Include (или в директории, прописанной в путях поиска IDE или проекта).

Процесс добавления компонента приводит к созданию некоторого количества файлов. Эти файлы автоматически помещаются в директории, указанные в опциях IDE (используйте пункт меню Tools|Environment Options, закладка Library). Файлы .LIB помещаются в директорию BPI/LIB (ее местоположение определяется в опциях проекта). Если добавление компонента влечет за собой создание нового пакета, файл .BPL помещается в директорию BPL и файлы .BPI помещаются в директорию BPI/LIB.


Добавление компонента
 Руководство разработчика компонентов

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

Список тем	См. также

Для добавления компонента в библиотеку компонентов,

1.	Выберите в меню пункт Choose Component|Install Component.

Появится диалоговое окно Install Component.

2.	Выберите, устанавливать ли новый компонент в существующий или в новый пакет, переходя на соответствующую закладку.

3.	Введите имя .CPP файла, содержащего новый компонент, или нажмите Browse для выбора модуля компонента.

4.	Добавьте путь поиска, если .CPP файл для нового компонента не находится в пути поиска по умолчанию.

5.	Введите имя пакета, в который будет добавляться компонент или нажмите Browse для поиска пакета.

6.	Если компонент устанавливается в новый пакет, дополнительно можно ввести значащее описание пакета.

7.	Выберите ОК для закрытия диалогового окна Install Component. Это перестроит пакет и установит компонент в Палитру компонентов.

Примечание: Новые компоненты изначально появляются в Палитре компонентов на странице, которая определена разработчиком компонента. Вы можете переместить компонент на другую страницу после установки компонента, используя диалоговое окно Component|Configure Palette.


Объектно-ориентрованное программирование для авторов компонентов
 Руководство разработчика компонентов

Объектно-ориентрованное программирование для авторов компонентов

Список тем	См. также

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

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

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

1.	Определение новых классов.

2.	Предки, потомки и иерархия классов.

3.	Контроль доступа.

4.	Диспетчеризация методов.

5.	Абстрактные элементы класса.

6.	Классы и указатели.


Определение новых классов
 Руководство разработчика компонентов

Определение новых классов

Список тем	См. также

Разница между разработчиками компонентов и разработчиками приложений состоит в том, что разработчики компонентов создают новые классы, в то время как разработчики приложений манипулируют экземплярами классов.

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

Например, довольно распространенной задачей является создание формы с двумя кнопками: OK и Cancel. Каждая из кнопок представляет собой экземпляр класса TButton, но, присваивая различные значения их свойствам Caption и различные обработчики их событиям OnClick, вы заставляете эти два экземпляра вести себя по-разному.


Порождение нового класса
 Руководство разработчика компонентов

Порождение нового класса

Список тем	См. также

Для порождения нового класса существуют две причины:

1.	Изменение настроек класса для избежания повторений.

2.	Добавление к классу новых возможностей.

В любом случае, целью является создание повторно используемого кода. Если вы разрабатываете компонент, подразумевая его повторное использование, вы впоследствии сэкономите много времени.


Изменение настроек класса для избежания повторений
 Руководство разработчика компонентов

Изменение настроек класса для избежания повторений

Список тем	См. также

Большинство программистов стремятся избегать повторений. То есть, если вы обнаруживаете, что пишете одни и те же строки кода еще и еще, вы помещаете этот код в функцию, или создаете библиотеку функций, которую вы можете использовать во многих программах. То же относится и к компонентам. Если вы обнаруживаете, что вы все время изменяете одни и те же свойства, или вызываете одни и те же методы, вы можете создать новый компонент, который будет делать эти вещи по умолчанию.

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

Изменение существующего компонента демонстрирует пример изменения свойств компонента по умолчанию.

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


Добавление к классу новых возможностей
 Руководство разработчика компонентов

Добавление к классу новых возможностей

Список тем	См. также

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

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

Например, если вы хотите добавить новых возможностей окну списка, вы можете породить ваш компонент от TListBox. Тем не менее, если вы хотите добавить новые, но исключить некоторые из старых возможностей окна списка, вам нужно порождать ваш компонента от TCustomListBox, предка TListBox. Затем вы вновь создадите (или сделаете видимыми) только те возможности окна списка, которые вам необходимы, и добавите новые.

Изменение сетки демонстрирует пример изменения абстрактного базового класса.


Определение класса нового компонента
 Руководство разработчика компонентов

Определение класса нового компонента

Список тем	Пример.

В дополнение к стандартным компонентам C++Builder предоставляет множество абстрактных классов, разработанных в качестве основ для порождения новых компонентов. Раздел "Как вы создаете компоненты?" показывает, с каких классов вы можете начать создавать свои собственные компоненты.

Для объявления класса нового компонента добавьте объявление класса в заголовочный файл компонента.


Пример: определение класса нового компонента
 Руководство разработчика компонентов

Пример: определение класса нового компонента

Ниже приведено объявление класса простого графического компонента:

class PACKAGE TSampleShape : public TGraphicControl

{

public:

    virtual __fastcall TSampleShape(TComponent* Owner);

};

Не забудьте включить макрос PACKAGE (определенный в Sysmac.h), который позволяет классам быть импортируемыми и экспортируемыми.

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


Предки и наследники
 Руководство разработчика компонентов

Предки и наследники

Список тем	См. также

Разработчики приложений воспринимают как должное, что каждый элемент управления имеет свойства Top и Left, которые определяют его положение на форме. Для них может не иметь значения, что все элементы управления наследуют эти свойства от общего предка, TControl. Однако когда вы создаете компонент, вы должны знать, от какого класса вам необходимо его порождать, чтобы компонент унаследовал необходимые возможности. И вы должны знать, что наследует ваш элемент управления, чтобы вы могли воспользоваться преимуществом наследуемых возможностей без необходимости создавать их заново.

Класс, от которого вы порождаете ваш компонент, называется непосредственным предком. Каждый компонент наследуется от своего непосредственного предка, и от непосредственного предка непосредственного предка, и т. д. Все классы, от которых наследуется компонент, называются его предками; компонент является наследником своих предков.

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

Если вы не указываете непосредственного предка, C++Builder порождает ваш компонент от предка по умолчанию, TObject. TObject является абсолютным предком всех классов в иерархии объектов.

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


Контроль доступа
 Руководство разработчика компонентов

Контроль доступа

Список тем	См. также

Существует пять уровней контроля доступа, также называемого видимостью, над свойствами, методами и элементами данных. Видимость определяет, какой код может иметь доступ к части класса. Указывая видимость, вы определяете интерфейс к вашим компонентам.

Нижеприведенная таблица демонстрирует уровни видимости, от наиболее ограниченных до наиболее доступных:

Видимость	Значение	Использование
private	Доступность только коду в модуле, где определен класс.	Сокрытие деталей реализации.
protected	Доступность коду в модуле (модулях), где определен класс и его потомки.	Определение интерфейса для разработчиков компонентов.
public	Доступность всему коду.	Определение интерфейса времени выполнения.
__automated	Доступность всему коду.	Только автоматизация OLE.
__published	Доступность всему коду и через Инспектор объектов.	Определение интерфейса времени разработки.


Сокрытие деталей реализации
 Руководство разработчика компонентов

Сокрытие деталей реализации

Список тем	Пример.

Объявление части класса как private делает эту часть невидимой для кода вне класса, кроме дружественных функций класса. Закрытые (private) части класса в основном полезны для сокрытия деталей реализации от пользователей класса. Поскольку пользователи класса не имеют доступа к закрытым частям, вы можете изменить внутреннюю реализацию класса без влияния на код пользователей.

Если вы не определили уровень доступа для элемента данных, метода или свойства, то по умолчанию применяется уровень доступа private.


Пример: сокрытие деталей реализации
 Руководство разработчика компонентов

Пример: сокрытие деталей реализации

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

Первая часть примера – это модуль формы, состоящий из файла заголовка и .CPP файла, который присваивает значение закрытому элементу данных в обработчике OnCreate формы. Поскольку обработчик события объявлен внутри класса TSecretForm, модуль компилируется без ошибок.

#ifndef HideInfoH

#define HideInfoH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TSecretForm : public TForm

{

__published:    // IDE-managed Components

    void __fastcall FormCreate(TObject *Sender);

private:

    int FSecretCode; // объявляем закрытый элемент данных

public:         // User declarations

  __fastcall TSecretForm(TComponent* Owner);

};

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

extern TSecretForm *SecretForm;

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

#endif

Соответствующий CPP файл:

#include <vcl.h>

#pragma hdrstop

#include "hideInfo.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TSecretForm *SecretForm;

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

__fastcall TSecretForm::TSecretForm(TComponent* Owner)

  : TForm(Owner)

{

}

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

void __fastcall TSecretForm::FormCreate(TObject *Sender)

{

  FSecretCode = 42; // это компилируется безошибочно

}

Вторая часть этого примера представляет собой другой модуль формы, который пытается присвоить значение элементу данных FSecretCode в форме SecretForm. Заголовочный файл формы:

#ifndef TestHideH

#define TestHideH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TTestForm : public TForm

{

__published:    // IDE-managed Components

    void __fastcall FormCreate(TObject *Sender);

public:         // User declarations

    __fastcall TTestForm(TComponent* Owner);

};

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

extern TTestForm *TestForm;

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

#endif

Ниже приведен соответствующий CPP файл. Поскольку обработчик события OnCreate пытается присвоить значение элементу данных, объявленному в TSecretForm закрытым, компиляция завершится неудачей с сообщением об ошибке "TSecretForm::FSecretCode is not accessible" (TSecretForm::FSecretCode не доступен).

#include <vcl.h>

#pragma hdrstop

#include "testHide.h"

#include "hideInfo.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TTestForm *TestForm;

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

__fastcall TTestForm::TTestForm(TComponent* Owner)

  : TForm(Owner)

{

}

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

void __fastcall TTestForm::FormCreate(TObject *Sender)

{

  SecretForm->FSecretCode = 13; //компилятор останавливается

                                // с сообщением об ошибке

}

Хотя программа, использующая модуль HideInfo, может использовать классы типа TSecretForm, она не может иметь доступ к элементу данных FSecretCode в любом из этих классов.


Определение интерфейса для разработчиков компонентов
 Руководство разработчика компонентов

Определение интерфейса для разработчиков компонентов

Список тем	См. также

Объявление части класса защищенной (protected) делает эту часть видимой только для самого класса и его потомков.

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


Определение интерфейса времени выполнения
 Руководство разработчика компонентов

Определение интерфейса времени выполнения

Список тем	Пример.

Объявление части класса открытой (public) делает эту часть видимой всему коду, который имеет доступ к классу в целом. Открытые части доступны во время выполнения всему коду, так что они определяют интерфейс времени выполнения класса. Интерфейс времени выполнения полезен для элементов, которые не являются значимыми во время разработки, таких как свойства, зависящие от ввода во время выполнения или являющиеся свойствами только для чтения. Методы, которые предназначены для вызова разработчиками приложений, также должны быть открытыми.


Пример: определение интерфейса времени выполнения
 Руководство разработчика компонентов

Пример: определение интерфейса времени выполнения

Ниже приведен пример, который демонстрирует два свойства только для чтения, объявленных как часть интерфейса времени выполнения компонента:

class PACKAGE TSampleComponent : public TComponent

{

private:

    int FTempCelsius;

    int GetTempFahrenheit();

public:

    ...

    __property int TempCelsius = {read=FTempCelsius};

    __property int TempFahrenheit = {read=GetTempFahrenheit};

};

Метод GetTempFahrenheit в .CPP файле:

int TSampleComponent::GetTempFahrenheit()

{

  return FTempCelsius * (9 / 5) + 32;

}


Определение интерфейса времени разработки
 Руководство разработчика компонентов

Определение интерфейса времени разработки

Список тем	Пример.

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

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

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

Свойства только для чтения не могут быть частью интерфейса времени разработки, поскольку разработчики приложений не могут присвоить им значения. Свойства только для чтения должны быть объявлены как public вместо published.


Пример: определение интерфейса времени разработки
 Руководство разработчика компонентов

Пример: определение интерфейса времени разработки

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

class PACKAGE TSampleComponent : public TComponent

{

private:

    int FTemperature;

  ...

__published:

    __property int Temperature = {read=FTemperature, write=FTemperature};

};


Выполнение методов
 Руководство разработчика компонентов

Выполнение методов

Список тем	См. также

Выполнение (dispatch) – это термин, используемый для описания того, как ваше приложение определяет, какой метод класса должен быть вызван, когда приложение сталкивается с вызовом метода. Когда вы пишете код, который вызывает метод класса, он выглядит как любой другой вызов функции. У классов, тем не менее, есть два различных способа выполнения методов.

Два типа методов по способу выполнения:

1.	Регулярные (не виртуальные) методы.

2.	Виртуальные методы.


Регулярные методы
 Руководство разработчика компонентов

Регулярные методы

Список тем	См. также

Методы класса являются регулярными (или не виртуальными), пока вы конкретно не объявите их виртуальными, или они не переопределяют виртуальные методы в базовом классе. Компилятор может определить точный адрес регулярного члена класса во время компиляции. Это известно как раннее связывание (связывание на этапе компиляции).

Регулярный метод базового класса наследуется производными классами. В следующем примере, объект типа Derived может вызывать метод Regular(), как будто он является его собственным методом. Объявление метода в производном классе с тем же именем и параметром, что и у регулярного метода в классе-предке, замещает метод предка. В следующем примере, когда вызывается d->AnotherRegular(), он "отсылается" к замещению класса Derived для AnotherRegular().

class Base

{

public:

    void Regular();

    void AnotherRegular();

    virtual void Virtual();

};

class Derived : public Base

{

public:

    void AnotherRegular(); // заменяет Base::AnotherRegular()

    void Virtual(); // переопределяет Base::Virtual()

};

void FunctionOne()

{

  Derived *d;

  d = new Derived;

  d->Regular(); // вызов Regular(), как если бы он был членом Derived

                         // То же самое, что и вызов d->Base::Regular()

  d->AnotherRegular(); // вызов переобъявленного AnotherRegular(),

                       // замещения Base::AnotherRegular()

  delete d;

}

void FunctionTwo(Base *b)

{

  b->Virtual();

  b->AnotherRegular();

}


Виртуальные методы
 Руководство разработчика компонентов

Виртуальные методы

Список тем	См. также

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

В предыдущем примере, если бы вы вызвали функцию FunctionTwo() с указателем на объект типа Derived, была бы вызвана функция Derived::Virtual(). Виртуальный механизм динамически проверяет тип класса объекта, передаваемого во время выполнения, и вызывает соответствующий метод. Но вызов регулярной функции b->AnotherRegular() всегда будет вызывать Base::AnotherRegular(), поскольку адрес AnotherRegular() был определен во время компиляции.

Для объявления нового виртуального метода, предварите объявление метода ключевым словом virtual.

Когда компилятор встречает ключевое слово virtual, он создает запись в таблице виртуальных методов (VMT) класса. VMT содержит адреса всех виртуальных методов класса. Эта поисковая таблица используется во время выполнения для определения того, что b->Virtual должен вызывать Derived::Virtual(), а не Base::Virtual().

Когда вы порождаете новый класс от существующего класса, новый класс получает свою собственную VMT, которая включает записи от VMT предка, плюс любые дополнительные виртуальные методы, объявленные в новом классе. В дополнение, класс-потомок может переопределить любой из унаследованных виртуальных методов.


Переопределение методов
 Руководство разработчика компонентов

Переопределение методов

См. также	Пример.

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


Пример: переопределение методов
 Руководство разработчика компонентов

Пример: переопределение методов

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

class PACKAGE TFirstComponent : public TComponent

{

public:

  void Move(); // регулярный метод

  virtual void Flash(); // виртуальный метод

};

class PACKAGE TSecondComponent : public TFirstComponent

{

public:

  void Move(); // объявляет новый метод, скрывающий TFirstComponent::Move()

  void Flash(); // переопределяет виртуальный метод

// TFirstComponent::Flash в TFirstComponent

};


Абстрактные члены класса
 Руководство разработчика компонентов

Абстрактные члены класса

Список тем	См. также

Когда метод объявлен абстрактным в классе-предке, вы должны переобъявить и реализовать его в любом компоненте-наследнике перед тем, как вы сможете использовать новый компонент в приложениях. C++Builder не может создать экземпляры классов, которые содержат абстрактные члены. Для большей информации смотрите раздел "Создание свойств" и "Создание методов".


Классы и указатели
 Руководство разработчика компонентов

Классы и указатели

Список тем	См. также

Каждый класс (и, следовательно, каждый компонент) в действительности является указателем. Статус класса как указателя становится важным, когда вы передаете класс в качестве параметра. В общем, вы должны передавать классы по значению, а не по ссылке. Причина в том, что классы уже являются указателями, которые являются ссылками; передача класса по ссылке эквивалентна передача ссылки на ссылку.


Создание свойств
 Руководство разработчика компонентов

Создание свойств

Список тем	См. также

Свойства являются наиболее явными частями компонентов. Разработчики приложений могут их видеть и манипулировать ими во время разработки и получить немедленную обратную связь – реакцию компонента в Дизайнере формы. Хорошо спроектированные свойства делают ваши компоненты легче в использовании для других и проще в сопровождении для вас.

Для использования свойств в ваших компонентах наилучшим образом вы должны понять следующее:

1.	Зачем создавать свойства?

2.	Типы свойств.

3.	Опубликование унаследованных свойств.

4.	Создание свойств-массивов.

5.	Сохранение и загрузка свойств.


Зачем создавать свойства?
 Руководство разработчика компонентов

Зачем создавать свойства?

Список тем	См. также

С точки зрения разработчиков приложений, свойства выглядят как переменные. Разработчики могут устанавливать или читать значения свойств как если бы они были элементами данных (единственное, что вы можете делать с переменной и не можете делать со свойством – это передавать его в качестве аргумента в метод по ссылке).

Свойства предоставляют больше возможностей, чем простые элементы данных, поскольку:

1.	Разработчики приложений могут устанавливать значения свойств во время разработки. В отличие от методов, которые доступны только во время выполнения, свойства позволяют разработчикам настраивать компоненты перед запуском приложения. Свойства могут появляться в Инспекторе объектов, который упрощает работу программиста: вместо обработки нескольких параметров при создании объекта, вы позволяете C++ Builder читать значения из Инспектора объектов. Инспектор объектов также проверяет правильность значений свойств непосредственно при вводе.

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

3.	Свойства могут быть виртуальными. Таким образом, то, что выглядит простым свойством для разработчика приложения, может быть по-разному реализовано в разных компонентах.

Простым примером является свойство Top всех элементов управления. Присваивание нового значения свойству Top не только изменяет хранимое значение; оно репозиционирует и перерисовывает элемент управления. И эффект установки значения свойства не обязательно должен ограничиваться отдельным компонентом; например, установка свойства Down у кнопки быстрого вызова (speed button) в true устанавливает свойства Down остальных кнопок быстрого вызова в группе в false.


Типы свойств
 Руководство разработчика компонентов

Типы свойств

Список тем	См. также

Свойство может быть любого типа. Разные типы по-разному отображаются в Инспекторе объектов, который проверяет правильность присваиваний значений свойствам, производимых во время разработки.

Тип свойства	Обработка свойств в Инспекторе объектов
Простой	Числовые, символьные и строковые свойства отображается в виде чисел, символов и строк. Разработчик приложения может непосредственно редактировать значение свойства.
Перечислимый	Свойства перечислимых типов (включая булев тип) отображаются в виде редактируемых строк. Разработчик приложения может также циклически перемещаться по значениям свойства, используя двойной щелчок мышью на значении свойства. Также имеется выпадающий список, который отображает все возможные значения.
Множество	Свойства-множества отображаются в виде наборов значений. Выполняя двойной щелчок мышью на значении свойства, разработчик приложения может развернуть набор и трактовать каждый элемент как булеву величину (true, если значение включено во множество).
Объект	Свойства, сами по себе являющиеся классами, часто имеют свои редакторы свойств, указанные в процедуре регистрации компонента. Если класс, к которому принадлежит свойство, сам содержит в своем составе опубликованные свойства, Инспектор объектов позволяет разработчику развернуть (путем двойного щелчка мышью) список для редактирования этих свойств по отдельности. Свойства-объекты должны наследоваться от TPersistent.
Массив	Свойства-массивы должны иметь свои собственные редакторы свойств. Инспектор объектов не имеет встроенной поддержки для редактирования таких свойств. Вы должны указать редактор при регистрации вашего компонента.


Опубликование унаследованных свойств
 Руководство разработчика компонентов

Опубликование унаследованных свойств

Список тем	Пример.

Все компоненты наследуют свойства от их классов-предков. Когда вы порождаете новый компонент от существующего, ваш новый компонент наследует все свойства его непосредственного предка. Если вы порождаете компонент от одного из абстрактных классов, многие из унаследованных свойств являются либо закрытым, либо открытым, но не опубликованным.

Чтобы сделать защищенное или открытое свойство доступным во время разработки в Инспекторе объектов, вы должны переобъявить свойство как published. Переобъявление означает добавление объявления для унаследованного свойства в объявление класса-потомка.


Пример: опубликование унаследованных свойств
 Руководство разработчика компонентов

Пример: опубликование унаследованных свойств

Если вы порождаете компонент от TWinControl, например, он наследует защищенное свойство DockSite. Переобъявляя DockSite в вашем новом компоненте, вы можете изменить уровень защиты на открытый или опубликованный.

Следующий код показывает переобъявление свойства DockSite как опубликованного, делая его доступным во время разработки.

class PACKAGE TSampleComponent : public TWinControl

{

__published:

    __property DockSite;

};

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

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


Определение свойств компонента
 Руководство разработчика компонентов

Определение свойств компонента

Список тем	См. также

Этот раздел показывает, как определить новые свойства и объясняет некоторые из соглашений, которым следуют стандартные компоненты. Рассматриваются следующие аспекты:

1.	Объявление свойства.

2.	Внутренне хранение данных.

3.	Прямой доступ.

4.	Методы доступа.

5.	Значения свойств по умолчанию.


Объявление свойства
 Руководство разработчика компонентов

Объявление свойства

Список тем	Пример.

Свойство объявляется в части объявлений своего класса. Для объявления свойства вы указываете:

1.	Имя свойства.

2.	Тип свойства.

3.	Методы, используемые для чтения и записи значения свойства. Если метод записи не объявлен, свойство является свойством только для чтения.

Свойства, объявленные в разделе __published объявления класса компонента, могут редактироваться в Инспекторе объектов во время разработки. Значение опубликованного свойства сохраняется вместе с компонентом в файле формы. Свойства, объявленные в разделе public объявления класса компонента, доступны во время выполнения, и их значения могут быть прочитаны или установлены в коде программы.


Пример: объявление свойства
 Руководство разработчика компонентов

Пример: объявление свойства

Ниже приведено типичное объявление для свойства Count.

class PACKAGE TYourComponent : public TComponent

{

private:

    int FCount;   // элемент данных для хранения значения свойства

    int __fastcall GetCount();     // метод чтения

    void __fastcall SetCount( int ACount );   // метод записи

public:

// объявление свойства

    __property int Count = {read=GetCount, write=SetCount};

  ...

};


Внутреннее хранение данных
 Руководство разработчика компонентов

Внутреннее хранение данных

Список тем	См. также

Не существует ограничений на то, как вы храните данные для свойства. Тем не менее, компоненты C++Builder следуют нижеприведенным соглашениям:

1.	Данные свойства хранятся в элементах данных класса.

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

3.	Идентификаторы для этих элементов данных состоят из буквы F, за которой следует имя свойства. Например, данные для свойства Width, определенном в TControl, хранятся в элементе данных FWidth.

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


Прямой доступ
 Руководство разработчика компонентов

Прямой доступ

Список тем	Пример.

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

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


Пример: прямой доступ
 Руководство разработчика компонентов

Пример: прямой доступ

Этот пример показывает свойство, использующее прямой доступ как для чтения, так и для записи.

class PACKAGE TSampleComponent : public TComponent

{

private:     // внутреннее хранилище данных объявлено в секции private

    bool FReadOnly; // объявление элемента данных

                    // для хранения значения свойства

  ...

__published:     // делаем свойство доступным во время разработки

    __property bool ReadOnly = {read=FReadOnly, write=FReadOnly};

};


Методы доступа
 Руководство разработчика компонентов

Методы доступа

Список тем	Пример.

Вы можете определить метод доступа вместо элемента данных в частях чтения или записи объявления свойства. Методы доступа должны быть защищенными, и обычно объявляются как virtual; это позволяет компонентам-потомкам переопределять реализацию свойства.

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


Пример: методы доступа
 Руководство разработчика компонентов

Пример: методы доступа

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

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

  int __fastcall GetDateElement(int Index); // обратите внимание

// на параметр Index

  void __fastcall SetDateElement(int Index, int Value);

public:

  __property int Day = {read=GetDateElement, write=SetDateElement, index=3, nodefault};

  __property int Month = {read=GetDateElement, write=SetDateElement, index=2, nodefault};

  __property int Year = {read=GetDateElement, write=SetDateElement, index=1, nodefault};

};

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

Вот метод чтения, который получает элемент даты:

int __fastcall TSampleCalendar::GetDateElement(int Index)

{

  unsigned short AYear, AMonth, ADay;

  int result;

  FDate.DecodeDate(&AYear, &AMonth, &Aday); // разбивает дату на элементы

  switch (Index)

  {

    case 1: result = AYear;  break;

    case 2: result = AMonth; break;

    case 3: result = ADay;   break;

    default: result = -1;

  }

  return result;

}

А это метод записи, устанавливающий соответствующий элемент даты:

void __fastcall TSampleCalendar::SetDateElement(int Index, int Value)

{

  unsigned short AYear, AMonth, ADay;

  if (Value > 0) // все элементы должны быть положительными

  {

    FDate.DecodeDate(&AYear, &AMonth, &ADay); // получаем элементы даты

    switch (Index)

    {

      case 1: AYear = Value;  break;

      case 2: AMonth = Value; break;

      case 3: ADay = Value;   break;

      default: return;

    }

 }

 FDate = TDateTime(AYear, AMonth, ADay); // кодируем модифицированную дату

 Refresh(); // обновляем календарь

}


Метод чтения
 Руководство разработчика компонентов

Метод чтения

Список тем	См. также

Метод чтения для свойства – это функция, которая не принимает ни одного параметра (за исключением ситуации, описанной ниже), и возвращает значение того же типа, что и свойство. По соглашению, имя функции состоит из Get и имени свойства. Например, метод чтения для свойства Count должен называться GetCount.

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

Если вы не объявляете метод чтения, то свойство является свойством только для записи. Такие свойства используются достаточно редко.


Метод записи
 Руководство разработчика компонентов

Метод записи

Список тем	См. также

Метод чтения для свойства – это функция, которая принимает один параметр (за исключением ситуации, описанной ниже) того же типа, что и свойство. Параметр может быть передан по ссылке или по значению, и может иметь любое выбранное вами имя. Однако, по соглашению, метод записи состоит из Set и имени свойства. Например, метод записи для свойства Count должен называться SetCount. Значение, переданное в качестве параметра, становится новым значением свойства; метод записи должен выполнять любые необходимые манипуляции для помещения соответствующих данных во внутреннее хранилище свойства.

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

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

Методы записи обычно проверяют, отличается ли новое значение свойства от текущего, перед изменением значения свойства. Ниже представлен простой метод для целого свойства Count, которое хранит свое текущее значение в элементе данных FCount.

void __fastcall TMyComponent::SetCount( int Value )

{

  if ( Value != FCount )

  {

    FCount = Value;

    Update();

  }


Значение свойства по умолчанию
 Руководство разработчика компонентов

Значение свойства по умолчанию

Список тем	См. также

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

Для объявления значения по умолчанию, добавьте знак равенства после имени свойства и две скобки, которые содержат ключевое слово default и значение по умолчанию. Например:

__property bool IsTrue = {default=true};

Примечание: объявление значения по умолчанию не устанавливает свойство в это значение. Конструктор компонента должен присвоить значение свойству. Тем не менее, поскольку объекты всегда инициализируют значения своих элементных данных нулем, нет необходимости для конструктора явно устанавливать значение целых свойств в 0, строк в null и булевых свойств в false.


Указание отсутствия значения по умолчанию
 Руководство разработчика компонентов

Указание отсутствия значения по умолчанию

Список тем	Пример.

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

Чтобы указать, что свойство не имеет значения по умолчанию, добавьте знак равенства после имени свойства и две скобки, которые содержат ключевое слово nodefault. Например:

__property int NewInteger = {nodefault};

Когда вы объявляете свойство в первый раз, нет необходимости указывать nodefault. Отсутствие объявления значения по умолчанию указывает, что значения по умолчанию не имеется.


Пример: указание отсутствия значения по умолчанию
 Руководство разработчика компонентов

Пример: указание отсутствия значения по умолчанию

Ниже приведено объявление компонента, который включает в себя простое булево свойство IsTrue со значением по умолчанию true, и определение конструктора компонента, который устанавливает это значение по умолчанию.

class PACKAGE TSampleComponent : public TComponent

{

private:

     bool FIsTrue;

public:

    virtual __fastcall TSampleComponent( TComponent* Owner );

__published:

    __property bool IsTrue = {read=FIsTrue, write=FIsTrue, default=true};

};

__fastcall TSampleComponent::TSampleComponent ( TComponent* Owner )

  : TComponent ( Owner )

{

  FIsTrue = true;

}


Создание свойств-массивов
 Руководство разработчика компонентов

Создание свойств-массивов

Список тем	Пример.

Некоторые свойства являются индексированными, наподобие массивов. Например, свойство Lines TMemo является индексированным списком строк, составляющих текст многострочного элемента редактирования. Вы можете рассматривать этот список в качестве массива строк. Lines предоставляют естественный доступ к отдельному элементу (строке) в большем наборе данным (текст элемента управления). Свойства-массивы объявляются подобно другим свойствам, за исключением:

1.	Объявление включает в себя один или более индексов. Индексы могут быть любого типа.

2.	Части чтения и записи объявления свойства, если таковые указаны, должны быть методами. Они не могут быть элементами данных.

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

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


Пример: создание свойств-массивов
 Руководство разработчика компонентов

Пример: создание свойств-массивов

Следующий пример показывает объявление свойства, которое возвращает строку, основанную на целом индексе.

class PACKAGE TDemoComponent : public TComponent

{

private:

    System::AnsiString __fastcall GetNumberSize(int Index);

public:

    __property System::AnsiString NumberSize[int Index] = {read=GetNumberSize};

  ...

};

Метод GetNumberSize в файле.CPP:

System::AnsiString __fastcall TDemoComponent::GetNumberSize(int Index)

{

  System::AnsiString Result;

  switch (Index)

  {

    case 0:

      Result = "Zero";

      break;

    case 100:

      Result = "Medium";

      break;

    case 1000:

      Result = "Large";

      break;

    default: Result = "Unknown size";

  }

  return Result;

}