Энциклопедия C++ Builder
ГРАФИЧЕСКИЕ КОМПОНЕНТЫ
7. ГРАФИЧЕСКИЕ КОМПОНЕНТЫ
Операционная система Windows предоставляет разработчикам приложении мощные средства Интерфейса Графических Устройств GDI (Graphics Device Interface) для построения графических изображений независимо от типа используемого устройства вывода. К сожалению, GDI обременяет программистов множеством дополнительных действий (в частности, по управлению системными ресурсами), которые отвлекают разработчика от его основной задачи - создания графики.
C++Builder берет на себя всю вспомогательную работу GDI, освобождая разработчиков от непродуктивного программирования с поиском утерянных дескрипторов изображений и не освобожденных ресурсов памяти. Это вовсе не означает, что прямое обращение приложений к отдельным функциям Windows GDI запрещается - вы всегда сможете при необходимости вызывать их. Однако, инкапсуляция графических функций
Windows визуальными компонентами представляет собой более перспективную методику создания графики в вашем приложении.
Мы вкратце ознакомились с графическими элементами Библиотеки Визуальных Компонент. Теперь остановимся на них более подробно и в заключение разработаем программу анимации, демонстрирующую интересные возможности графических компонент.
7.1 Поддержка графики в C++Builder
C++Builder инкапсулирует функции Windows GDI на разных уровнях. Наиболее важным здесь является способ, посредством которого графические компоненты представляют свои изображения на экране монитора. При прямом вызове функции GDI необходимо передавать им дескриптор контекста устройства (device context handle), который задает выбранные вами орудия рисования - перья, кисти ц шрифты. После завершения работы с графическими изображениями, вы обязаны восстановить контекст устройства в исходное состояние и только потом освободиться от него.
Вместо того, чтобы вынуждать вас работать с графикой на таком уровне детализации, C++Builder предоставляет вам простой и завершенный интерфейс посредством свойства Canvas (Канва) графических компонент. Это свойство про-инициализирует правильный контекст устройства и освободит его в нужное время, когда вы прекратите рисование. По аналогии с функциями Windows GDI канва имеет вложенные свойства, представляющие характеристики пера, кисти и шрифта.
Единственное, что должен сделать пользователь, работая с графическими компонентами, - это определить характеристики используемых орудий рисования. Вам не потребуется следить за системными ресурсами при создании, выборе и освобождении орудий. Канва сама позаботится об этом.
Одно из достоинств, которые проявляет C++Builder при работе с графикой, -это использование кэшированной памяти для графических ресурсов системы. Если ваша программа повторно создает, использует и освобождает, скажем, перо некоторого конкретного вида, вам приходится повторять эти шаги каждый раз, когда вы используете такое перо. Поскольку C++Builder использует кэш-память для хранения графических ресурсов, увеличивается вероятность, что часто употребляемое орудие рисования будет повторно выбираться из кэш-памяти, а не воссоздаваться каждый раз заново. Очевидно, что вследствие этого заметно возрастет эффективность повторяющихся операций вашего графического приложения.
Листинг 7.1 содержит два фрагмента кода, которые наглядно иллюстрируют, насколько C++Builder упрощает программирование графики. Первый фрагмент применяет стандартные функции GDI для того, чтобы нарисовать в окне OWL приложения для Windows
желтый эллипс, обведенный синим контуром. Во втором фрагменте та же задача решается посредством канвы рисования.
void TMyWindow::Paint(TDC& PaintDC, bool erase, TRect& rect) {
HPEN PenHandle, OldPenHandle;
HBRUSH BrushHandle, OldBrushHandle;
PenHandle = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
OldPenHandle = SelectObject(PaintDC, PenHandle);
BrushHandle = CreateSolidBrush(RGB(255, 255, 0));
OldBrushHandle = SelectObject(PaintDC, BrushHandle);
Ellipse(10, 10, 50, 50);
SelectObject(OldBrushHandle) ;
DeleteObject(BrushHandle) ;
SelectObject(OldPenHandle);
DeleteObject(PenHandle) ;
)
void_fastcall TFormI::FormPaint(TObject *Sender) {
Canvas->Pen->Color = clBlue; //
выбрать цвет контура Canvas->Brush->Color = clYellow; // выбрать цвет заливки Canvas->Ellipse(10, 20, 50, 50); // нарисовать эллипс }
Листинг 7.1. Использование функций
Windows GDI u свойства канвы на примере рисования эллипса.
7*2 Использование канвы
Объектный класс канвы инкапсулирует графические функции Windows на различных уровнях, начиная с функций высокого уровня для рисования отдельных линий” фигур и текста. Далее идут свойства и методы среднего уровня для манипуляций с канвой для рисования. Наконец, на нижнем уровне обеспечивается доступ к самим функциям Windows GDI. В следующей таблице обобщаются характеристики основных методов и свойств канвы.
| Уровень | Действие | Методы | Свойства |
| Высокий | Определяет текущую позицию пера | MoveTo | PenPos |
| Рисует прямую до заданной точки | LineTo | PenPos | |
| Рисует прямоугольник заданного размера | Rectangle | |
|
| Рисует эллипс заданного размера | Ellipse | |
|
| Выводит текстовую строку | TextOut | |
|
| Задает высоту, отведенную для вывода текстовой строки | TextHeight | |
|
| Задает ширину, отведенную для вывода текстовой строки | TextWidth | |
|
| |
Вывод текста внутри прямоугольника | TextRect | |
| Заливка указанного прямоугольника цветом и текстурой текущей кисти | FillRect. | |
|
| Заливка области канвы (произвольной формы) заданным цветом | FloodFill | |
|
| Средний | Используется для установки цвета, стиля, ширины и режима пера | |
Pen |
| Используется для установки цвета и текстуры при заливке графических фигур и фона канвы. | |
Brush | |
| Используется для установки шрифта заданного цвета, размера и стиля | |
Font | |
| Используется для чтение и записи цвета заданного пикселя канвы | |
Pixels | |
| Копирует прямоугольную область канвы в режиме CopyMode |
CopyRect | CopyMode | |
| Копирует прямоугольную область канвы с заменой цвета | BrushCopy | |
|
| Рисует битовый образ, пиктограмму, метафайл в заданном месте канвы | Draw | |
|
| Рисует битовый образ, пиктограмму или метафайл так, чтобы целиком заполнить заданный прямоугольник | StretchDraw | |
|
| Низкий | Используется как параметр при вызове функций Windows GDI | |
Handle |
7.3 Работа с рисунками
Основное содержание графических работ, которые выполняются в среде C++Builder, состоит непосредственно в рисовании на канве вашей формы или других размещенных на ней компонент. C++Builder также обслуживает поддержку внешних изображений - битовых образов, метафайлов и пиктограмм, включая автоматическое управление палитрами.
При работе с рисунками в среде C++Builder следует принимать во внимание три важных аспекта.
7.3.1 Рисунок, графика или канва9
В среде C++Builder существует три рода объектов, которые имеют отношение к графике:
• Канва предоставляет битовую карту поверхности для рисования на форме,
графической компоненте, принтере или на другом битовом образе. Канва не является самостоятельным объектом, она всегда является свойством какого-то другого графического объекта.
• Графика представляет растровое изображение некоторого файла или ресурса (битового образа, пиктограммы или метафайла). C++Builder определяет производные от базового класса TGraphic объектные классы
TBitmap, Ticon и TMetafile. Конечно, вы можете объявить свои собственные классы графических объектов. TGraphic предоставляет минимальный стандартный интерфейс для использования в вашем приложении всех видов графики.
• Рисунок представляет собой контейнер для графики, т.е. он может содержать любые классы графических объектов. Таким образом, контейнерный класс TPicture может содержать битовый образ, пиктограмму, метафайл или некоторый другой графический тип, определенный пользователем, а приложение может стандартизовано обращаться ко всем объектам контейнера посредством объекта "рисунок". Действительно, большинство компонент управления изображениями имеют свойство Picture
объектного типа TPicture, которое дает возможность представления изображений различных видов графики.
Отметим, что объект "рисунок" всегда содержит некоторую графику, которой в свою очередь может потребоваться иметь канву (единственный стандартный графический класс с канвой - это TBitmap). Обычно, имея дело с рисунком, вы работаете только с той частью графического объекта, которая открыта для доступа через контейнер TPicture. Если вам надо указать доступ к конкретному графическому объекту, задайте его в свойстве Graphic данного рисунка.
7.3.2 Графические файлы
В любой момент работы вашего приложения C++Builder поддерживает загрузку и сохранение рисунков и графиков в файлах изображений стандартного формата. Имена сохраняемого и исходного файла изображений могут совпадать или различаться.
Для загрузки изображения в рисунок из файла воспользуйтесь методом рисунка
LoadFromFile. Чтобы сохранить изображение в файле, вызовите метод рисунка SaveToFile.
Единственным параметром этих методов является имя файла. Метод LoadFromFile использует расширение файла для распознавания вида графического объекта, который он создаст и загрузит. Метод SaveToFile сохраняет файл с расширением, соответствующим виду сохраняемого графического объекта.
Листинг 7.2 содержит инструкцию, которую вы должны записать в тексте кодового модуля для загрузки битового образа в компонентный объект рисунка.
void_fastcall TFormI::FormCreate(TObject *Sender) {
Imagel->Picture->LoadFromFile("c:\\windows\\clouds.bmp");
}
Листинг 7.2. Загрузка битового образа из файла clouds, bmp.
Рисунок распознает стандартное расширение файлов битовых образов .bmp и создает свою графику как объект класса TBitmap, а затем вызывает метод LoadFromFile загрузки изображения из файла с указанным именем.
7.3.3 Обслуживание палитр
Большинство элементов пользовательского интерфейса не нуждаются в какой бы то ни было палитре. Однако компонентам, содержащим графические изображения, может потребоваться взаимодействие с Windows и его экранным драйвером для того, чтобы обеспечить надлежащее отображение данных компонент. В документации по операционной системе Windows
этот процесс называется реализацией палитр (palette realizing).
Реализация палитр призвана обеспечить, чтобы самое верхнее (видимое на экране как ближайшее к вам) активное окно использовало полную цветовую палитру, в то время как фоновые окна максимально использовали оставшиеся цвета своих палитр. Это означает, что фоновые окна должны изменять свои цвета на ближайшие подобные из доступных в "реальной" палитре. По мере того, как окна меняются местами, перекрывая друг друга, Windows постоянно осуществляет реализацию оконных палитр.
Замечание. C++ Builder не содержит самостоятельных средств для создания и обслуживания иных палитр, нежели палитры битовых образов. Однако, если вы получили дескриптор некоторой палитры, графические компоненты смогут работать с ним.
При работе с устройством типа дисплея или принтера, компоненты C++Builder автоматически поддерживают механизм реализации палитр. Таким ;
образом, если ваша компонента имеет палитру, вы можете воспользоваться двумя методами GetPalette и
PaletteChanged, наследованными от базового компонентного класса
TControl, чтобы управлять тем, как Windows
обращается с этой палитрой:
• Связь палитры с компонентой. Если требуется использовать некоторую палитру для графической компоненты, ваше приложение должно узнать об этом. Чтобы ассоциировать палитру с вашей компонентой, перегрузите ее объектный метод GetPalette
так, чтобы он возвращал дескриптор
(handle) этой палитры. При этом вы, во-первых, сообщаете приложению, что определенной палитре вашей компоненты необходимо быть реализованной; а во-вторых, определяете, какая конкретно палитра используется при реализации.
• Реакция на изменение палитры. Когда ваша компонента ассоциируется с некоторой палитрой посредством перегрузки метода GetPalette, C++Builder автоматически берет на себя реакцию на сообщения Windows от палитр с помощью метода PaletteChanged. При нормальной работе вам никогда не придется переопределять поведение этого метода, установленное по умолчанию. Основная задача метода PaletteChanged состоит в том, чтобы определить вид реализации палитры (для фоновых или активных окон). C++Builder продвигается на шаг вперед, по сравнению с реализацией палитр в
Windows: с помощью оконных дескрипторов реализуются не только палитры "стопки" перекрывающихся окон, но и палитры наложенных друг на друга компонент активного окна. Вы можете переопределить такое, принятое по умолчанию, поведение палитр, если захотите, чтобы некоторая компонента приобрела полную цветовую палитру и выглядела на экране как ближайшая к вам.
7.4 Внеэкранные битовые образы
Общепринятая методика программирования сложных графических приложении для
Windows заключается в создании внеэкранного битового образа, рисовании или заполнении его конкретным изображением и, наконец, копировании всего изображения из битового образа на указанное место экранного окна. Благодаря этому уменьшается заметное глазу мерцание экрана монитора, вызванное повторным рисованием непосредственно в экранном окне.
C++Builder позволяет создавать объекты класса TBitmap в вашем приложении для представления изображении файлов и других ресурсов, которые также способны работать как внеэкранные изображения.
7.4.1 Копирование битовых образов.
C++Builder предусматривает четыре способа копирования изображении с одной канвы на другую. Выбирайте нужный метод копирования из следующей таблицы, в зависимости от результата, который необходимо получить.
| Требуемый результат | Метод |
| Полное копирование графики | Draw |
| Копирование с масштабированием | StretchDraw |
| Копирование прямоугольного участка канвы | CopyRect |
| Копирование с растровыми операциями | BrushCopy |
7.4.2 Создание и обслуживание
При создании сложных графических изображений следует избегать их непосредственного рисования на канве формы или компоненты, представленной в экранном окне вашего приложения. Вместо этого вы сможете сконструировать некий объект битового образа и рисовать на его канве, а затем скопировать изображение на канву экрана. При рисовании на канве внеэкранного битового образа наиболее часто используется метод
Paint графических компонент.
Пример рисования сложного изображения на внеэкранном битовом образе дает Индикатор (Gauge),
представленный на вкладке Samples Палитры компонент. Исходные файлы Gauges.cpp
и Gauges.h
программного модуля компоненты TGauge можно найти в каталоге: \.. .\CBuilder\Examples\Controls\Source.
Фрагмент файла Gauges.cpp (Листинг 7.3) показывает основные операции, выполняемые компонентным методом Paint
рисования на канве внеэкранного битового образа TBitmap. Сначала функция PaintBackground закрашивает прямоугольную фоновую область индикатора цветом, выбранным в свойстве Color. Затем в соответствии с заданным значением свойства Kind контур нужной формы обводится цветом свойства
ForeColor и заливается цветом свойства BackColor (в нашем случае индикатора со стрелкой Kind = gkNeedle это выполняет функция PaintAsNeedle). Заключительные инструкции устанавливают свойство режима копирования канвы CopyMode, снабжают индикатор текстом (метод PaintAsText) и только затем (с помощью метода Draw)
канва внеэкранного битового образа отображается на экране.
void _fastcall TGauge::Paint()
{
std::auto_ptr
(new Graphics::TBitmap() ) ;
std::auto_ptr
TRect PaintTRect;
The Image->Height = Height;
TheImage->Width = Width;
PaintBackgroundtThelmage.get()) ;
PaintTRect = ClientRect;
if (FBorderStyle == bsSingle)
InflateRect(&RECT(PaintTRect), -1, -1);
OverlayImage->MakeLike(Thelmage.get() ) ;
PaintBackground(Overlay Image.get());
switch(FKind) {
case gkText:
PaintAsNothing(Overlay Image.get(), PaintTRect); break;
case gkHorizontalBar:
case gkVerticalBar:
PaintAsBar(Overlaylmage.get(), PaintTRect); break;
case gkPie:
PaintAsPie(Overlay Image.get(), PaintTRect); break;
case gkNeedle:
PaintAsNeedle(Overlaylmage.get(), PaintTRect); break;
}
The Image->Canvas->CopyMode = cmSrcInvert;
TheImage->Canvas->Draw(0, 0, Overlaylmage.get()) ;
TheImage->Canvas->CopyMode = cmSrcCopy;
if (ShowText == true)
PaintAsText(Thelmage.get(), PaintTRect);
Canvas->CopyMode = cmSrcCopy;
Canvas->Draw(0, 0, Thelmage.get()) ;
}
Листинг 7 3 Метод рисования на канве компоненты индикатора.
Рис. 7.1 показывает, что текст программы, реализующей перемещение стрелки индикатора по сигналам таймера, состоит всего из двух строк обработчика события OnTimer компоненты TTimer из вкладки System. Первая инструкция функции обработки события Timer1Timer устанавливает значение 1000 (принятое по умолчанию) для односекундного интервала времени объекта Timer1, а вторая инкрементирует значение свойства Progress объекта Gauge 1.

Рис. 7.1. Индчкппюр в виде шкалы со стрелкой (см. свойство Kind).
Если вы соберете и запустите это приложение, то сможете убедиться, что при движении стрелки индикатора с любой скоростью, мерцание экрана монитора действительно отсутствует.
7.4.3 Реакция на изменения
Все графические объекты - включая канвы для рисования - и объекты, которыми они владеют (перья, кисти и шрифты), содержат встроенные события, отвечающие за изменения, произошедшие с объектом. С помощью этих событий вы можете заставить ваши компоненты и, следовательно, использующее их приложение перерисовывать свои изображения в ответ на произошедшие изменения.
Реакция на изменения объектов графической компоненты особенно важна в том случае, если эти объекты объявлены в исходном файле модуля компоненты как published. Тогда единственный способ обеспечить, чтобы вид компоненты на стадии проектирования приложения соответствовал свойствам, установленным Инспектором объектов, заключается в подключении обработчика события, который будет реагировать на изменения компонентного объекта. Для графических компонент вы должны предусмотреть реакцию на событие OnChange.
class TMyShape : public TGraphicControl
{
public:
virtual _fastcall TMyShape(TComponent* Owner);
__publi shed:
TPen *FPen;
TBrush *FBrush;
void_fastcall StyleChanged(TObject *Sender) ;
};
_fastcall TMyShape::TMyShape(TComponent* Owner)
: TGraphicControl(Owner) {
Width = 64;
Height = 64;
PPen = new TPen;
FPen->OnChange = StyleChanged; //
изменить стиль пера
FBrush = new TBrush;
FBrush->OnChange = StyleChanged; //
изменить стиль кисти }
void_fastcall TMyShape::StyleChanged(TObject *Sender) (
Invalidate();
}
Листинг 7.4. Содержание файлов Unit1.h и Unit1.cpp компоненты TMyShape.
Графическая компонента рисования геометрических фигур TShape Библиотеки Визуальных Компонент объявляет свои свойства, представляющие перо и кисть, в секции _published.
Конструктор компонентного объекта присваивает метод StyleChanged
событию OnChange, заставляя компоненту перерисовывать изображенные на ней фигуры при любом изменении пера или кисти. Хотя оригинальный текст компоненты TShape написан на языке Объектный Паскаль, Листинг 7.4 приводит его трансляцию на C++ с новым именем TMyShape.
7.5 Разработка графического приложения
Вот уже более 20 лет автор специализируется на системах обработки изображений, полученных при космической съемке Земли и других объектов Солнечной системы. Создан существенный задел алгоритмов и программ, реализованных на Borland C++ версии 4.5 для операционной системы Windows. Сюда относятся программы статистического анализа и сжатия многозональных изображений, геометрического трансформирования, ортогональных преобразований в область пространственных частот и многие другие. Заканчивая книгу, я мог бы предложить читателю проследить на выбранном примере за переводом этих приложений в среду C++Builder. Именно этой деятельностью я и занимаюсь в последнее время.
Однако мне кажется, что такой пример представлял бы узко профессиональный интерес для ограниченного числа прикладных программистов. Поэтому я выбрал задачу из совершенно другой области, которая интриговала меня много лет, с момента появления первых качественных игровых программ. Как реализуется эффективная компьютерная графика в приложениях анимации? Неужели можно достичь требуемого быстродействия на языке высокого уровня, таком как C++, не прибегая к утомительному программированию на ассемблере?
Возможно, многие читатели задавали себе подобные вопросы и, так же как и автор, не находили доходчивых ответов в литературе, не рискнули "потерять время даром" и попробовать свои силы самостоятельно запрограммировать подобную задачу. Любопытному читателю адресуется разработка в среде C++Builder простого графического приложения анимации, за основу которого взят алгоритм Давида Свэнни.
Идея алгоритма заключена в понятии спрайта - прямоугольного контейнера, внутри которого происходит "оживление" движущейся фигуры, т.е. рисование на канве внеэкранного изображения отдельных фаз анимации. Для решения этой задачи вводится объявление относительно простого класса SpriteClass.
Фактически приложение MOVEIT и заключается в использовании объектов Sprite
класса SpriteClass
с буферизацией графики. Более полная иллюзия движения фигур на экране достигается за счет автоматической прокрутки изображения фона. Большая часть времени ушла на подготовку изображений битовых образов фона и различных движущихся фигур (самолетов и вертолетов). Я использовал для этой цели систему Corel
Draw!, хотя любой другой имеющийся в вашем распоряжении редактор изображений справился бы с рисованием простейших картинок.
7.5.1 Проектирование формы
Начнем проектирование приложения, перетаскивая из Палитры на форму следующие компоненты:
=> Три невидимых и пустых контейнера TImage из вкладки Additional и один видимый и пустой контейнер TPaintBox из вкладки System. Объекты DrawBox класса TImage и PaintBox класса TPaintBox совместно реализуют двойную буферизацию графики: сначала фигуры и фон рисуются на канве внеэкранного битового образа, в объекте DrawBox, а затем изображение канвы копируется на экран, в объект PaintBox.
=> Панель инструментов TPanel из вкладки Standard.
=^> Таймер TTimer из вкладки System.
=> Компоненту редактирования TSpinEdit с кнопками "а" и "-у" из вкладки Samples для управления скоростью анимации.
=> Три быстрые кнопки TSpeedButton из вкладки Additional для управления работой программы.
=^ Компоненту TOpenDialog из вкладки Dialogs диалога открытия файлов.
Рис. 7.2 показывает форму приложения, фоном которой служит изображение облачного неба из файла Clouds.bmp,
а в качестве движущихся фигур выступают два самолета из файла Planes, bmp
или два вертолета из файла Helicopters.bmp. Редактор изображений открыт двойным щелчком мышью в графе значений свойства Picture компонентного объекта Figures.
Обратите внимание, что изображение каждой фигуры представлено тремя битовыми образами - первые два отображают возможные фазы анимации фигуры, а третий представляет собой бинарную маску внешнего контура. Обе фигуры различаются только цветом и имеют одинаковую форму, поэтому разделяют общую пару масок. Такая совокупность составляет банк данных спрайта.

Рис. 7.2. Форма приложения анимации.
7.5.2 Программный модуль
К файлу модуля Unit1.h (Листинг 7.5) добавлено описание структуры FigureType, включающей позицию (X,Y), смещение (DX,DY) фигуры и номер объекта спрайта (SD); а в секции public класса формы объявлены четыре переменные размеров изображений, две фигуры Fig [2] и соответствующие им две пары спрайтов Sprite [4]. Класс SpriteClass и его методы определены в файлах Sprite.h и Sprite.cpp, соответственно.
ttifndef UnitlH
#define UnitlH
#include
#include
#include
typedef struct { int X, Y, DX, DY, SD; } FigureType;
class TFormI : public TForm (
_published: // IDE-managed Components TPaintBox *PaintBox;
TImage *DrawBox;
TImage *Background;
TImage *Figures;
TTimer *Timerl;
TPanel * Panel 1;
TSpeedButton *SpeedButtonl;
TSpeedButton *SpeedButton2;
TSpeedButton *SpeedButton3;
TSpinEdit *SpinEditl;
TOpenDialog *OpenDialog;
void _fastcall TimerlTimer(TObject * Sender);
void _fastcall SpinEditlKeyUp(TObject *Sender, WORD &Key, TShiftState Shift);
void _fastcall SpinEditlKeyDown(TObject * Sender,
WORD &Key, TShiftState Shifts-void_fastcall SpeedButtonlClick(TObject *Sender) ;
void_fastcall SpeedButton2Click(TObject * Sender);
void_fastcall SpeedButton3Click(TObject * Sender);
private: // User declarations public: // User declarations _fastcall
TFormI(TComponent* Owner);
int W, H, w, h;
FigureType Fig[2];
SpriteClass Sprite[4];
};
extern TFormI *Forml;
#endif
Листинг 7.5. Содержание файла Unii1.h.
Файл модуля Unit1.cpp (Листинг 7.6) содержит 5 обработчиков событий от нажатия кнопок управления и обработчик события таймера.
//-_---------_____________-___-_-----__-____________________-
^include
#pragma hdrstop
#include "Unitl.h"
#pragma link "sampreg"
#pragma resource "*.dfm" TFormI *Forml;
//----------------——--——--—------———------—-----------
_fastcall TFormI::TFormI(TComponent* Owner) : TForm(Owner) ( // Конструктор формы устанавливает размеры по умолчанию W = Н = 400; w = h =61;
} //----------------------------------------------------------
void_fastcall TFormI::SpeedButtonlClick(TObject *Sender) { if
(OpenDialog->Execute())
{ // Открыть файл изображения фона
Background->Picture->LoadFromFile(OpenDialog->FileName) ;
W = Background->Picture->Width;
H = Background->Picture->Height;
}
} //----—----------——-------—-——---—-—-—-------------
void_fastcall TFormI::SpeedButton2Click(TObject *Sender) { if
(OpenDialog->Execute())
{ // Открыть файл изображения фигур
Figures->Picture->LoadFromFile(OpenDialog->FileName);
w = (Figures->Picture->Width)/6;
h = Figures->Picture->Height;
//______---------------------------------------------------
void_fastcall TFormI::SpeedButton3Click(TObject *Sender)
{ // Инициализировать поля структуры обеих фигур Fig[0].X = W/4; Fig[l].X = 3*W/4;
Fig[0].Y = H/4; Fig[l].Y = 3*H/4;
Fig[0].DX = 1; Fig[l].DX = -1;
Fig[0].DY = 1; Fig[l].DY = -1;
Fig[0].SD = 0; Fig[l].SD = 0;
// Подготовить экземпляры спрайта
Sprite[0].SetSprite(Figures->Canvas, 0,0, 4*w,0, w,h);
Sprite[1].SetSprite(Figures->Canvas, 2*w,0, 4*w,0, w,h);
Sprite[2].SetSprite(Figures->Canvas, w,0, 5*w,0, w,h);
Sprite[3].SetSprite(Figures->Canvas, 3*w,0, 5*w,0, w,h) ;
Timerl->Enabled = true;
}
void _fastcall TFormI::SpinEditlKeyUp(TObject *Sender,
WORD &Key, TShiftState Shift) { Timerl->Interval++; } void _fastcall
TFormI::SpinEditlKeyDown(TObject *Sender,
WORD &Key, TShiftState Shift) { Timerl->Interval--; } //-.____-_-_-_-_.....____.__________________________________
int Shift = 0; // переменная прокрутки фона void _fastcall
TFormI::TimerlTimer(TObject *Sender) ( //
Сместить фигуры в пределах периметра
Fig[0].X += Fig[0].DX; Fig[0].Y += Fig[0].DY;
if (Fig[0].X > (W/2-w)) Fig[0].DX = -1;
if (Fig[0].X < 20) Fig[0].DX = 1;
if (Fig[0].Y > (H-2*h)) Fig[0].DY = -1;
if (Fig[0].Y < 20) Fig[0].DY = 1 ;
Fig[l].X += Fig[l].DX; Fig[l].Y += Pig[l].DY;
if (Fig[l].X > (W-w)) Fig[l].DX = -1;
if (Fig[l].X < (W/2+w)) Fig[l].DX = 1;
if (Fig[l].Y > (H-h)) Fig[l].DY = -1;
if (Fig[l].Y < 30) Fig[l].DY = 1 ;
// Оживить фигуры, переключая экземпляр спрайта
Fig[0].SD = (Fig[0].SD == 0) ? 2 : 0;
Fig[l].SD = (Fig[l].SD == 1) ? 3 : 1 ;
// Нарисовать фон и сдвинуть его влево
if (Shift == 0)
{ DrawBox->Canvas->CopyMode = cmSrcCopy;
DrawBox->Canvas->CopyRect(Rect(О, О, W, H),
Background->Canvas, Rect(0, 0, W, H));
}
else
{ DrawBox->Canvas->CopyMod^s = cmSrcCopy;
DrawBox->Canvas->CopyRect(Rect(0, 0, W-Shift, H),
Background->Canvas, Rect(Shift, 0, W, H) ) ;
DrawBox->Canvas->CopyRect(Rect(W-Shift, 0, W, H),
Background->Canvas, Rect(0, 0, Shift, H)) ;
}
Shift += 2;
if (Shift >= W) Shift -= W;
// Нарисовать фигуры на канве внеэкранного битового образа
Sprite[Fig[0].SD].Draw(DrawBox->Canvas,Fig[0].X, Fig[0].Y) ;
Sprite[Fig[l].SD].Draw(DrawBox->Canvas,Fig[1].X, Fig[l].Y) ;
// Скопировать изображение на экран
PaintBox->Canvas->CopyMode = cmSrcCopy;
PaintBox->Canvas->CopyRect(Rect(0, 0, W, H),
DrawBox->Canvas, Rect(0, 0, W, H)) ;
}
Листинг 7 6 Содержание файла Unit1 cpp.
Быстрые кнопки компонент TSpeedButton служат для управления программой. Первая кнопка открывает и загружает объект Background
класса TImage
из файла с изображением фона (размером w на H). Вторая кнопка открывает и загружает объект Figures класса TImage из файла с изображениями фигур (размером w на h каждая).
Третья кнопка устанавливает фигуры в начальные позиции и подготавливает объекты спрайта посредством обращении к методу
SetSprite., а затем запускает таймер.
Нажатие кнопок компоненты редактирования TSpinEdit вызывает увеличение или уменьшение на единицу значения интервала таймера (по умолчанию устанавливается значение 40, соответствующее кадровой частоте 1/25 сек).
Ядром приложения является обработчик события OnTimer объекта Timerl. Первый блок кода реакции на прерывание от таймера отвечает за перемещение фигур, изменяя направление движения всякий раз, когда они "утыкаются" в фиксированные границы отведенного периметра. Рис. 7.3 изображает структуру канвы объектов DrawBox и PaintBox.

Рис. 7.3. Структура канвы для рисования движущихся фигур.
Следующий блок реализует прокрутку фона справа-налево, делая анимацию более реальной (можете называть этот процесс" виртуальной реальностью, если хотите). Фигуры могут сходиться и расходиться, но общее впечатление их движения слева-направо сохраняется. Для начала надо скопировать изображение фона целиком в буфер внеэкранного изображения, а затем организовать циклический сдвиг влево данных буфера на величину, установленную переменной
Shift. Заключительный блок рисует, при помощи метода Draw, фигуры на канве невидимого объекта DrawBox, а затем копирует изображение канвы на экран, в видимый объект PaintBox.
7.5.3 Спрайты
Экземпляры класса SpriteClass (Листинг 7.7) обеспечивают анимацию одной маскируемой фигуры. Банк данных спрайта содержит канву невидимой компоненты TImage с битовыми образами фаз. Естественно, что все изображения банка данных спрайта должны быть одинакового размера. Изображения фаз анимации должны иметь черный фон, маска контура - белый.
class SpriteClass
private:
TCanvas *SpriteCanvas;
int ImageLeft, ImageTop;
int MaskLeft, MaskTop;
int Width, Height;
public:
void SetSprite(TCanvas *aSpriteCanvas,
int aImageLeft, int aImageTop, int aMaskLeft, int aMaskTop, int
aWidth, int aHeight);
void Draw( TCanvas *aDrawCanvas, int aLeft, int aTop) ;
);
Листинг 7.7. Объявление класса спрайта в файле Spite, h.
В классе спрайта определены только два метода установки и рисования спрайта (Листинг 7.8). При вызове метода SetSprite аргументы aImageLeft и aImageTop определяют позицию спрайта на канве aSpriteCanvas, аргументы aMaskLeft и
MaskTop - позицию маски, аргументы aWidth и aHeight - размеры спрайта. При вызове метода Draw
аргумент aDrawCanvas задает канву рисования, а аргументы aLeft и aTop - позицию спрайта на этой канве.
void SpriteClass::SetSprite(TCanvas *aSpriteCanvas,
int aImageLeft, int aImageTop, int aMaskLeft, int aMaskTop, int aWidth, int aHeight) { SpriteCanvas = aSpriteCanvas;
ImageLeft = aImageLeft;
ImageTop = aImageTop;
MaskLeft = aMaskLeft;
MaskTop = aMaskTop;
Width = aWidth;
Height = aHeight;
} void SpriteClass::Draw(TCanvas *aDrawCanvas,
int aLeft, int aTop) { aDrawCanvas->CopyMode = cmSrcAnd;
aDrawCanvas->CopyRect(Rect(aLeft, aTop,
aLeft+Width, aTop+Height), SpriteCanvas, Rect(MaskLeft, MaskTop, MaskLeft+Width, MaskTop+Height)) ;
aDrawCanvas->CopyMode = cmSrcPaint;
aDrawCanvas->CopyRect(Rect(aLeft, aTop,
aLeft+Width, aTop+Height), SpriteCanvas,
Rect(ImageLeft, ImageTop, ImageLeft+Width.ImageTop+Height));
}
Листинг 7.8. Определение методов класса спрайта в файле Sprite.cpp.
Довольно трудно составить впечатление от работы приложения анимации по снимку одного кадра - тем более в черно-белом исполнении (Рис. 7.4). Книга не является подходящим носителем для распространения компьютерных мультфильмов. Если проблема вас заинтересовала, нет другого способа изучать ее дальше, как придумать и подготовить картинки и разработать собственное приложение, используя приведенные листинги в качестве прототипа.

Рис. 7.4. Снимок с экрана работающего приложения MOVEIT.
Сами фигуры представляют собой массив типа структуры, а не экземпляр какого-то класса. Однако не надо быть гениальным программистом, чтобы ввести логику работы с фигурами в соответствующий класс FigureClass, который будет инкапсулировать, в частности, свойство периметра и метод перемещения. Однако не стоит наследовать SpriteClass от класса фигур. Дело в том, что экземпляры этих классов имеют разную природу: спрайты - это графические объекты, а фигуры - логические конструкции. Если бы вам потребовалось произвести анимацию более, чем двух одинаковых фигур, все они по-прежнему будут разделять единственный класс спрайта, используя поле структуры Fig [ I ] . SD для связи I-фигуры с нужным экземпляром спрайта.
Взгляните на изображения фаз анимации самолета или вертолета. На второй фазе каждой фигуры пропеллер просто отсутствует. Постоянно переключая фазы, можно создать иллюзию вращения пропеллера. Зная, что программа не моделирует фактическое вращение, спросите, тем не менее, у наблюдающего за работой приложения: "В какую сторону вращается пропеллер?" - и любой уверенный ответ убедит вас в том, что время на программирование примера было потрачено не зря. Можно придумать какой-нибудь другой впечатляющий эффект, например, стреляющего пулемета. Расширяя банк данных спрайта дополнительными фазами фигур и соответствующими масками, можно добиваться различных эффектов
ИНСТРУМЕНТЫ ВИЗУАЛЬНОЙ РАЗРАБОТКИ ПРИЛОЖЕНИЙ
Эта глава содержит описание основных инструментов визуальной разработки программных приложений. Именно благодаря этим средствам, в сочетании с мощностью новейшего компилятора с языка C++ и инкрементального линкера, C++Builder обеспечивает скорость визуальной разработки, продуктивность более 100 интерфейсных компонент и гибкость управления базами данных.Интегрированная среда разработки C++Builder IDE (Integrated Development Environment), предназначенная для эффективной разработки приложений, поможет вам воспользоваться ее новшествами и в полной мере ощутить преимущества нового стиля программирования. Вы сможете быстро переводить приложение из стадии начального прототипа в рабочую программу. В главе 2 читатель уже составил общее представление о работе с базовыми инструментальными средствами C++Builder - Палитрой компонент. Редакторами форм и кода. Инспектором и Хранилищем объектов. В данной главе мы продолжим знакомство с этими и другими инструментами среды C++Builder, уделяя особое внимание описанию визуальных компонент, представленных на Палитре.
4.1 Администратор проекта
Администратор проекта предназначен для манипуляций с текущим проектным файлом с расширением .срр. Чтобы открыть окно администратора (Рис. 4.1), выполните команду View [ Project Manager.
Окно содержит список всех составляющих файлов, ассоциированных с текущим проектом. С помощью кнопок панели быстрого вызова или опций контекстного меню вы сможете добавлять, вычеркивать, сохранять и копировать выбранный файл в текущем проекте.

| Запись изменений проекта |
| Установка проектных опций |
| Включение редактора форм |
| Включение редактора кода |
| Вычеркивание файла из списка |
| Добавление файла к проекту |
| Список ассоциированных файлов проекта |
| Контекстное меню администратора проекта |
Рис. 4.1. Окно администратора проекта.
Открыть контекстное меню администратора проекта можно щелчком правой кнопкой мыши в любом месте окна администратора или нажатием клавиш Alt+FlO. Контекстное меню содержит следующие опции:
1. Командой File | New Application создайте новое приложение.
2. Командой File | New Form ( кнопка быстрого вызова) создайте новую форму.
3. Командой File | Open Project (кнопка быстрого вызова) откройте существующий проект.
Добавить компоненту к форме можно одним из способов:
1. Найдите нужную компоненту на вкладках Палитры компонент и щелкните на ней левой кнопкой мыши. Переведите курсор в окно Редактора и вновь щелкните мышью.
2. Дважды щелкните левой кнопкой мыши на нужной компоненте.
Закрыть активное окно Редактора форм можно одним из способов:
1. Кнопкой в правом верхнем углу окна или клавишами Alt+F4.
2. Командой File | Close.
Закрыть все окна, кроме окна Инспектора объектов, можно командой File | Close All.
Если вы модифицировали текущую форму и не сохранили изменения, C++Builder выдаст предупреждение и откроет диалог команды File | Save As, чтобы вы могли ввести новое имя модуля (по умолчанию, Unit1). Если вы модифицировали текущий проект и не сохранили изменения, C++Builder выдаст предупреждение и откроет диалог команды File | Save As, чтобы вы могли ввести новое имя файла проекта (по умолчанию. Project 1).
Чтобы получить контекстно-зависимую справку при работе с Редактором форм, выберите компоненту, о которой вы хотите получить справочные сведения, и нажмите клавиши Ctrl+Fl. Если искомые сведения не обнаружены, справочная служба Help
выдаст сообщение "Help Topic Does Not Exist".
4.3 Инспектор объектов
Инспектор объектов занимает активную рабочую позицию между визуальными компонентами управления разрабатываемым приложением и программным кодом, который обеспечивает его работу. Инспектор объектов имеет две вкладки:
Свойства (Properties) и События (Events).
Вкладка свойств дает вам возможность манипулировать свойствами компонент, помещаемых на форму (и свойствами самой формы) на стадии проектирования, определяя тем самым начальное состояние компонент. Установка свойств компонент во время выполнения программы требует написания соответствующих текстов кода для обработки событий. Свойство может, в свою очередь, включать вложенные свойства, что отмечается знаком "+" в левой позиции соответствующей графы базового свойства.
Вкладка событий содержит список возможных программных событий и позволяет соединять компоненты (или формы) с происходящими событиями. Если дважды щелкнуть мышью на некотором событии, C++Builder создаст функцию обработчика (поначалу с пустым телом) и переключится на Редактор кода. Редактор кода установит курсор на тело обработчика события, которое вы должны заполнить кодом, определяющим реакцию компоненты на данное событие.
В верхней части Инспектора (Рис. 4.2) расположено поле селектора объектов. которое отображает объектный тип выбранной компоненты (или формы) и позволяет раскрыть список всех компонент, помещенных на активную 4юрму. С помощью селектора легко выбирать рабочие компоненты текущей формы. Ширину столбцов Инспектора можно менять, перетаскивая мышью разделительные линии.
Значение fsNormal свойства FormStyle самой формы означает, что вы создаете SDI
приложение с однодокументным интерфейсом, а значение fsMDIForm - MDI приложение с многодокументным интерфейсом. Обратите внимание, что свойству Sorted компоненты TListBox присвоено значение
true, поэтому элементы списка будут упорядочены в алфавитном порядке.

Рис. 4.2. Свойства компоненты TListBox в окне Инспектора объектов.
Открыть контекстное меню Инспектора объектов можно щелчком правой кнопкой мыши в любом месте окна Инспектора или нажатием клавиш
Alt+FlO. Контекстное меню содержит следующие опции управления и установок:
• Revert to Inherited
восстанавливает исходное унаследованное поведение объекта. Применяйте эту операцию, когда вы забыли внесенные изменения свойств.
• Expand показывает вложенные свойства объекта. Такое же действие вызывает двойной щелчок мышью по выбранному базовому свойству.
• Collapse прячет отображение вложенных свойств объекта. Такое же действие вызывает двойной щелчок мышью по выбранному базовому свойству.
• Stay On Top
располагает окно Инспектора поверх остальных окон и диалогов среды C++Builder.
• Hide прячет активное окно Инспектора, которое открывается вновь клавишей F11 или по команде View | Object Inspector.
• Help вызывает соответствующую страницу справочной службы.
В дальнейшем изложении подразумевается, что установка значений свойств и включение обращений к методам в обработчики событий производятся с помощью Инспектора объектов.
4.4 Хранилище объектов
Хранилище объектов обеспечивает возможность разделения (sharing) или повторного использования (reuse) содержащихся в нем объектов. В качестве объектов хранения могут выступать либо подготовленные C++Builder, или созданные вами формы, проекты, модули данных и эксперты. Кроме самих объектов, вы можете адресовать в хранилище их шаблоны, в которых зафиксированы только неизменяемые в процессе разработки компоненты.
Хранилище в сущности является индексным текстовым файлом, содержащим ссылки на страницы, объединяющие указанные объекты.
4.4.1 Разделение объектов
При разделении объектов разработчик приложения может преследовать разные цели:
Разделение объектов между проектами.
Добавляя формы, диалоги и модули данных к хранилищу, вы делаете их доступными другим проектам.
Например, все ваши проекты могли бы использовать диалог часто используемой команды меню About,
содержащий, наряду с фиксированными текстами и кнопками управления, пустые поля для записи фамилий авторов приложения, года выпуска и т.д. Из этого шаблонного диалога все ваши проекты произведут диалоги стандартизованного вида, заполняя переменные поля новым конкретным содержанием.
Разделение объектов внутри проекта.
Вы можете наследовать формы, уже включенные в проект. Открывая командой File | New диалог New Items, вы увидите вкладку с именем вашего проекта. Если щелчком мыши выбрать эту вкладку, откроются списки всех форм, диалогов и модулей данных этого проекта. Затем можно произвести новый объект - форму из существующей и приспособить ее по назначению.
Например, некоторому приложению СУБД может понадобиться несколько форм, отображающих одни и те же данные, но с разными кнопками управления. Вместо того, чтобы создавать ряд почти идентичных форм, добавьте к хранилищу единую форму с общими элементами отображения данных, а затем из этой шаблонной формы производите формы с разными кнопками. Тщательно планируя содержание форм вашего проекта, вы сэкономите массу времени и усилий, которые понадобились бы при повторной разработке сходных форм в одном проекте.
Разделение проектов.
Добавляя к хранилищу проект целиком, вы создаете шаблон для будущих проектов. Если вы планируете разработать ряд сходных приложений, все они могут базироваться на единой стандартизованной модели.
Использование экспертов.
Хранилище содержит ссылки на экспертов - небольшие программы, которые проводят пользователя через серию руководящих диалогов по созданию формы или проекта. C++Builder предусматривает ряд готовых экспертов, к которым вы можете добавлять свои собственные.
4.4.2 Включение объектов в проект
Существует три способа включения в проект объектов из хранилища с целью их разделения:
Копирование объектов хранилища.
Простейший вариант разделения заключается в копировании точного дубликата объекта в ваш проект. Будущие изменения этого объекта в хранилище никак не отразятся на вашей копии, и наоборот, модификации копии не коснутся оригинала объекта в хранилище.
Замечание. Копирование представляет единственную возможность использования проектных шаблонов.
Наследование объектов хранилища.
Наиболее гибкий и мощный вариант разделения заключается в построении нового производного класса от объекта хранилища и добавлении его в ваш проект. Когда вы перекомпилируете проект, любые изменения этого объекта в хранилище отразятся на производном классе, однако модификации вашей производной никак не отразятся на оригинале объекта в хранилище. Замечание.
Наследование разрешено для форм, диалогов и модулей данных, но запрещено для проектных шаблонов и экспертов. Наследование представляет единственную возможность повторного использования объектов в одном и том же проекте.
Прямое использование объектов хранилища.
Наименее гибкий вариант разделения заключается в том, что при использовании объекта хранилища, он просто добавляется к вашему проекту, как если бы был создан вами как часть проекта. Поэтому изменения объекта на стадии проектирования проявятся во всех проектах, которые прямо используют или наследуют от него. Чтобы избежать изменений в других проектах, модифицируйте объект только во время выполнения программы.
Замечание. Разрешено прямое использование форм, диалогов, модулей данных и экспертов. Использование экспертов фактически не создает разделяемые процедуры, а представляет собой запуск некоторого процесса, который генерирует свой собственный код.
Разработка нового проекта может начинаться не с пустого проекта (с именем Project 1 по умолчанию), а с некоторого шаблона из хранилища объектов.
Чтобы открыть новый проект на базе шаблона:
1. Выполните команду File | New, которая откроет диалог New Items.
2. Выберите вкладку Projects.
3. Укажите нужный шаблон и нажмите кнопку ОК.
4. В открывшемся диалоге Select Directory укажите каталог, в котором хранятся
ваши проектные файлы. Если каталог не существует, он будет создан. Копия проекта на базе шаблона откроется в указанном каталоге. Вы можете работать с проектом, добавляя новые формы и другие элементы, или, оставив его без изменений, переключиться на редактирование кода обработчиков событий. В любом случае, сделанные вами модификации будут отражаться только на открытом проекте. Оригинал шаблона останется без изменений и может быть использован повторно.
4.4.3 Добавление объектов к хранилищу
Когда вы создали новую форму или проект (или их шаблоны) и удостоверились, что они работоспособны и послужат в будущем благородной цели стандартизации облика разрабатываемых в вашей организации приложений, сохраните такие объекты в хранилище.
Чтобы добавить новый объект к хранилищу:
1. Выполните команду Project | Add To Repository, которая откроет диалог, показанный на Рис. 4.3.
2. В область редактирования Title введите название объекта.
3. В область Description введите описание объекта.
4. Из выпадающего списка Page выберите имя страницы, в которой вы хотите завести новый объект.
5. В область Author введите фамилию автора.
6. Нажмите кнопку Browse, чтобы выбрать пиктограмму, которая будет представлять объект в хранилище.
7. Нажмите кнопку ОК, чтобы сохранить текущий объект.
![]() |
| Имя объекта Кнопка выбора пиктограммы |
| Описание объекта |
| Автор объекта |
| Страница объектов указанного типа |
Рис. 4.3. Добавление формы к хранилищу объектов.
Чтобы добавить к хранилищу проект или форму в виде шаблона:
1. Если необходимо, откройте проект или форму, шаблоны которых вы хотите добавить к хранилищу.
2. Выполните команду Tools | Repository.
3. На вкладке Project Templates (или Form Templates) выберите команду
Add, которая отроет диалог Save Project Template (или Save Form Template).
4. В область редактирования Title введите название проекта или формы.
5. В область Description введите описание проекта или формы.
6. Из выпадающего списка Page выберите имя страницы Projects (или Forms), в которой вы хотите завести соответствующий шаблон.
7. В область Author введите фамилию автора.
8. Нажмите кнопку Browse, чтобы выбрать пиктограмму, которая будет представлять данный шаблон в хранилище.
9. Нажмите кнопку ОК, чтобы сохранить текущий проект или форму в виде шаблона.
Замечание. Если позже вы внесете изменения в шаблон, эти изменения автоматически отразятся в новых проектах или формах, произведенных от этого шаблона, но никак не скажутся на уже созданных производных.
Чтобы увидеть описание выбранного объекта:
1. Выполните команду File | New, которая откроет диалог New Items.
2. Выберите вкладку объектов нужного типа.
3. Щелкните правой кнопкой мыши по выбранному объекту.
4. Из появившегося контекстного меню выберите опцию View Details. Описание объекта появится в столбце Description.
4.4.4 Проект и формы, создаваемые по умолчанию
Проект по умолчанию для нового приложения открывается с помощью команды File |
New Application. Если вы не меняли соответствующих установок хранилища объектов, C++Builder создаст пустой проект с пустой формой. Вы можете сменить шаблон проекта по умолчанию, а также назначить запуск некоторого эксперта проектов, который проведет вас через ряд диалогов, руководящих созданием нового проекта.
Чтобы сменить проект по умолчанию:
1. Выполните команду Tools | Repository, чтобы открыть диалог хранилища Object Repository.
2. Выберите страницу с именем Projects из списка Pages.
3. Укажите проект, который вы хотите создавать по умолчанию, в списке Objects.
4. Выберите опцию New Project.
5. Нажмите кнопку OK для регистрации новой установки по умолчанию.
Новая форма по умолчанию для существующего проекта открывается с помощью
команд File | New Form (кнопка быстрого вызова). Если вы не меняли соответствующих установок хранилища объектов, C++Builder создаст пустую форму. Вы можете сменить шаблон формы по умолчанию, а также назначить запуск некоторого эксперта форм, который проведет вас через ряд диалогов, руководящих добавлением новой формы к проекту.
Чтобы сменить форму по умолчанию для существующего проекта:
1. Выполните команду Tools | Repository, чтобы открыть диалог хранилища Object Repository.
2. Выберите страницу с именем Forms из списка Pages.
3. Укажите форму, которую вы хотите создавать по умолчанию, в списке Objects.
4. Выберите опцию New Form.
5. Нажмите кнопку OK для регистрации новой установки по умолчанию.
Основная форма (или эксперт форм) по умолчанию для нового проекта устанавливается в хранилище объектов точно так же, как и форма для существующего проекта.
Чтобы сменить основную форму по умолчанию для нового проекта:
1. Выполните команду Tools | Repository, чтобы открыть диалог хранилища Object Repository.
2. Выберите страницу с именем Forms из списка Pages.
3. Укажите форму, которую вы хотите создавать по умолчанию, в списке Objects.
4. Выберите опцию Main Form.
5. Нажмите кнопку OK для регистрации новой установки по умолчанию.
4.5 Редактор кода
Редактор кода программ предоставляет удобное и надежное средство для просмотра и редактирования текста программного модуля (Unit), независимо компилируемого в объектный файл. Модуль состоит из двух частей: файла объявлений с расширением .h и кодового файла с расширением .срр.
Окно Редактора может содержать несколько вкладок с редактируемыми файлами. Например, когда вы начинаете работу с открытия некоторого проекта, он становится первой вкладкой в окне Редактора; вкладки всех других отрываемых файлов занимают последовательные позиции в ряду вкладок.
C++Builder автоматически отрывает в окне Редактора новую вкладку с текстом модуля в следующих ситуациях:
1. Командой File | New Application создается новое приложение.
2. Командой File | New Form (кнопка IS1 быстрого вызова) создается новая фор ма.
3. Командой File | New Unit создается новый модуль.
4. Командой File | Open Project (кнопка 1в 1 быстрого вызова) открывается существующий проект.
5. Командой File | Open (кнопка 1^1 быстрого вызова) открывается существующий файл текста модуля. По умолчанию вам будут предложены кодовые файлы с расширением .срр. Вы можете сменить установки умолчания для текущего каталога и расширений файлов на вкладке Directories/Conditionals
диалога команды Options | Project.
6. Командой Project | Add to Project (кнопка Ц.а| быстрого вызова) к проекту добавляется текущая форма или модуль.
Чтобы редактировать текст модуля:
1. Выберите вкладку окна Редактора с именем нужного модуля или укажите его
в списке, предлагаемом командой
View | Units (кнопка IQH быстрого вызова). Если вы хотите создать новое окно Редактора, воспользуйтесь командой View | New Edit Window.
2. Поместите курсор в то место текста, которое вы хотите редактировать.
3. Редактируйте текст с помощью операций, принятых стандартными редакторами
Microsoft Windows. Вводимые строки программы разделяются нажатием
клавиши Enter.
Если вы модифицировали текст текущего модуля и не сохранили изменения, C++Builder выдаст предупреждение и откроет диалог Save As, чтобы вы могли ввести новое имя файла модуля (по умолчанию, Uniti). Если вы модифицировали текущий проект и не сохранили изменения, C++Builder выдаст предупреждение и откроет диалог Save As, чтобы вы могли ввести новое имя файла проекта (по умолчанию, Project1).
Закрыть модуль в окне Редактора кода можно одним из способов:
1. Кнопкой в правом верхнем углу окна или клавишами Alt+F4.
2. Командой File | Close.
3. Окно Редактора закрывается только тогда, когда все находящиеся в нем файлы были закрыты.
Важнейшей особенностью C++Builder является автоматическая генерация строк программы. Когда вы добавляете компоненту к форме, в тексте файла Unit1.h появляется объявление переменной экземпляра класса данной компоненты. Например, перенос на пустую форму компоненты кнопки TButton
сгенерирует объявление объекта Button 1, а определение события OnClick -
объявление метода ButtonlClick обработчика этого события (Рис. 4.4).

Рис. 4.4. C++Biiilder генерирует объявления в файле модуля Unit1.h.
Открыть контекстное меню Редактора кода можно щелчком правой кнопкой мыши в любом месте окна Редактора или нажатием клавиш AIt+FlO. Контекстное меню содержит следующие опции для просмотра исходного текста вашей программы и ее отладки:
• Swap Cpp/Hdr Files
переключает файлы составляющих редактируемого модуля Unit.cpp/Unit.h.
• Close Page закрывает текущую вкладку с текстом редактируемого файла модуля.
• Open File At Cursor
открывает текстовый файл модуля, начиная с текущей позиции курсора.
• New Edit Window
открывает новое окно Редактора кода.
• Topic Search выдает контекстно-зависимую справку о слове в тексте модуля, на которое наведен курсор. Если искомые сведения не обнаружены, справочная служба выдаст сообщение "Help Topic Does Not Exist".
• Toggle Breakpoint
включает и выключает останов программы в точке текста, указанной курсором. Чтобы модифицировать свойства точки останова, выполните команду View | Breakpoints и в списке Breakpoint list щелкните правой кнопкой мыши на выбранной точке.
• Run to Cursor запускает загруженную программу до точки текста, указанной курсором. Такое же действие вызывает команда Run | Run To Cursor.
После того, как программа остановится перед тем местом, где вы подозреваете ошибку, перейдите к пошаговому исполнению программы по командам
Run | Step Over или Run | Trace Into
• Go to Address переходит к заданному адресу в ассемблерной программе (см. опцию View CPU).
• Inspect открывает окно инспекции выделенного символа. Символ может быть обнаружен не только в текущем, но и в любом исходном файле, скомпилированном и собранном как часть одного проекта. Чтобы поиск символа производился, необходимо разрешить опции компиляции Local symbols и Symbol info на вкладке Pascal диалога команды
Options | Project.
• Evaluate/ Modify открывает диалог, который дает возможность вычислить значение некоторого выражения или изменить его значение. Такое же действие вызывает команда Run | Evaluate/Modify.
• Add Watch at Cursor
открывает диалог, который позволяет следить за изменением значения выражения, на которое указывает курсор. Все отслеживаемые выражения заносятся в список Watch List. Подобное действие вызывает команда Run | Add Watch .
• Read Only
устанавливает атрибут "только чтение", запрещающий делать какие-либо изменения текущего файла. Это состояние файла отображается в нижней строке состояния Редактора кода.
• Message View открывает окно сообщений и ошибок, полученных на фазах компиляции и сборки программы.
• View CPU открывает окно отладчика нижнего уровня - ассемблерных команд.
• Properties позволяют просматривать и изменять текущие установки Редактора кода.
4.6 Палитра компонент
C++Builder поставляется вместе с усовершенствованной 32-разрядной Библиотекой Визуальных Компонент VCL (Visual Component Library),
содержащей более 100 повторно используемых компонент для построения прототипов сложнейших приложений. Основные компоненты Библиотеки представлены на инструментальной панели Палитры компонент, значки которых перетаскиваются на форму вашей программы.
Библиотека включает полную инкапсуляцию стандартных интерфейсных объектов Графического Интерфейса Пользователя операционных систем Windows и Windows 95 (области редактируемого ввода, простые и комбинированные списки и многие другие), наряду со специализированными компонентами, среди которых особое место занимают компоненты для управления реляционными базами данных. Читателям, заинтересованным общей методикой и особенностями разработки приложений для баз данных, адресована отдельная глава 5.
C++Builder в полной мере использует возможности объектно-ориентированного программирования (ООП) для создания надежных и эффективных приложений. Поскольку C++Builder это среда ООП, введение управляющих элементов OLE (OCX) не вызывает особых трудностей. Используйте имеющиеся компоненты Библиотеки, расширяйте возможности производных компонент так, чтобы наилучшим образом удовлетворить требованиям вашей задачи.
Ключевая характеристика C++Builder заключается в его способности не только пользоваться готовыми компонентами в процессе визуальной разработки программ, но и создавать новые компоненты. Новые компоненты могут быть такими же простыми, как исходные, со слегка расширенными функциональными возможностями, или отличаться совершенно оригинальным видом, поведением и кодовым содержанием. Эти проблемы рассматриваются подробно в главе 6. Создание компонент базируется на механизме наследования ООП, практически не имеет ограничений и проходит через следующие стадии:
• наследование от существующего компонентного типа;
• определение новых свойств, методов и событий;
• регистрация созданной компоненты.
Описание основных компонент приводится в данной главе. Для удобства поиска Палитра разделена вкладками, объединяющими функционально сходные компоненты. Открыть контекстное меню выбранной компоненты можно, щелкнув по ней правой кнопкой мыши.
4.6.1 Стандартные компоненты
Компоненты вкладки Standard палитры компонент

осуществляют включение в вашу программу 14 стандартных интерфейсных элементов
Windows.
4.6.1.1 TMainlVlenu
Создает панель команд главного меню и соответствующие им выпадающие меню для формы. Подробная инструкция по конструированию меню приведена в разделе "Дизайнер меню".
Идентификаторы всех команд меню определяются свойством Items, которое имеет доступ к любой конкретной команде меню. Свойство AutoMerge вместе с методами
Merge и Unmerge управляют процессом слияния меню разных форм.
4.6.1.2 TPopUpMenu
Создает специальное меню для формы или для другой компоненты. Заметьте, что именно для этой цели любая прочая компонента имеет свойство PopUpMenu, в котором вы можете задать ссылку на связанное с ней меню. Подробная инструкция по конструированию меню приведена в разделе "Дизайнер меню".
Если вы хотите, чтобы специальное меню появлялось при нажатии правой кнопки мыши на форму или другой элемент, которому приписана данная компонента, установите значение true свойства AutoPopup. Работающее таким образом специальное меню называется контекстным. С помощью обработчика события ОпРорир
можно определить процедуру, которая будет выполняться непосредственно перед появлением специального меню.
4.6.1.3 TLabel
Отображает на форме прямоугольную область статического текста, который нельзя редактировать. Обычно текст представляет собой название другой компоненты.
Текст названия является значением свойства Caption. Свойство Alignment определяет способ выравнивания текста. Чтобы размер шрифта автоматически соответствовал максимальному заполнению области, установите значение true свойства AutoSize. Чтобы весь текст можно было увидеть внутри короткой области, задайте значение true свойства WordWrap. Установкой значения true свойства Transparent вы можете оставить видимой часть другой компоненты сквозь название, расположенное прямо на ней.
4.6.1.4 TEdit
Отображает прямоугольную область редактируемого ввода одиночной строки информации на форме. Начальное содержимое области редактирования определяет строка, являющаяся значением свойства Text.
TEdit является прямой производной от класса TCustomEdit, полностью наследуя его свойства, методы и события.
4.6.1.5 ТМето
Отображает прямоугольную область редактируемого ввода множественных строк информации на форме. Начальное содержимое области редактирования определяет массив строк, являющийся значением свойства Lines. Окно редактора элементов списка открывается кнопкой в графе значении этого свойства.
TMemo является прямой производной от класса TCustomMemo, полностью наследуя его свойства, методы и события.
4.6.1.6 TButton
Создает прямоугольную кнопку с надписью. Нажатие на кнопку инициирует некоторое действие в программе.
Кнопки чаще всего используются в диалоговых окнах. Кнопка по умолчанию, выбранная значением true свойства Default, запускает обработчик события OnClick всякий раз, когда нажимается клавиша Enter в окне диалога. Кнопка прерывания, выбранная значением true свойства Cancel, запускает обработчик события OnClick всякий раз, когда нажимается клавиша Escape в окне диалога.
TButton является производной от класса TButtonControl.
4.6.1.7 TCheckBox
Создает квадратный чек-бокс с двумя состояниями и описательным текстом, специфицирующим его назначение.
Состояние бокса "check" соответствует выбору некоторого варианта (отмечается перечеркиванием бокса), а состояние "uncheck" соответствует снятию выбору - при этом свойство компоненты Checked меняется соответственно и возникает событие OnClick. Описательный текст хранится в свойстве Caption. Затемнить бокс (подкрасить серым цветом) можно установкой значения true свойства AllowGrayed. Свойство State отражает текущее состояние и цвет бокса.
TCheckBox является производной от класса TButtonControl.
4.6.1.8 ЕЙ TRadioButton
Создает круглую кнопку с двумя состояниями и описательным текстом, специфицирующим ее назначение.
Радио-кнопки представляют набор взаимоисключающих вариантов выбора:
только одна кнопка может быть выбрана в данный момент времени (отмечается внутренним черным кружком), а с ранее выбранной кнопки выбор автоматически снимается. При нажатии радио-кнопки свойство компоненты
Checked меняется соответственно и возникает событие OnClick.
Обычно радио-кнопки размещаются внутри предварительно установленного на форме группового контейнера. Если выбрана одна кнопка, выбор всех прочих кнопок той же группе автоматически снимается. Например, две радио-кнопки на форме могут быть выбраны одновременно только в том случае, когда они размещены в разных контейнерах. Если группировка радио-кнопок явно не задана, то по умолчанию, все они группируются в одном из оконных контейнеров (TForm, TGroupBox или TPanel).
TRadioButton является производной от класса TButtonControl.
4.6.1.9 TListBox
Отображает прямоугольную область списка текстовых вариантов для выбора, добавления или вычеркивания.
Если все элементы списка не умещаются в отведенную область, то список можно просматривать с помощью линейки прокрутки. Элементы списка содержатся в свойстве Items, а номер элемента, который будет выбран во время выполнения программы, - в свойстве
Itemlndex. Окно текстового редактора элементов
списка открывается кнопкой в графе значений свойства Items.
Можно динамически добавлять, вычеркивать, вставлять и перемещать элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
ListBoxl->Items->Add("Последний элемент списка");
Значение true
свойства Sorted
устанавливает сортировку элементов списка по алфавиту.
TListBox является производной от класса TCustomListBox, полностью наследуя его свойства, методы и события.
4.6.1.10 TComboBox
Создает комбинацию области редактирования и выпадающего списка текстовых вариантов для выбора.
Значение свойства Text заносится непосредственно в область редактирования. Элементы списка, которые может выбирать пользователь, содержатся в свойстве Items,
номер элемента, который будет выбран во время выполнения программы, -в свойстве
Itemlndex, а сам выбранный текст - в свойстве SelText. Свойства SelStart и SelLength позволяют установить выборку части текста или обнаружить, какая часть текста выбрана.
Можно динамически добавлять, вычеркивать, вставлять и перемещать элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
ComboBoxl->Items->Insert(0, "Первый элемент списка");
Значение true
свойства Sorted
задает сортировку элементов списка по алфавиту. Вид компоненты TComboBox можно выбрать в свойстве Style.
TComboBox является производной от класса TCustomComboBox, полностью наследуя его свойства, методы и события.
4.6.1.11 TScrollBar
Создает линейку прокрутки с бегунком для просмотра содержимого окна, формы или другой компоненты, например, для перемещения внутри заданного интервала значений некоторого параметра.
Поведение прокручиваемого объекта определяется обработчиком события OnScroll. Насколько должен продвинуться бегунок, когда пользователь щелкает мышью на самой линейке (по обеим сторонам от бегунка), определяет значение свойства LargeChange. Насколько должен продвинуться бегунок, когда пользователь щелкает мышью по кнопкам со стрелками (на концах линейки) или нажимает клавиши позиционирования, определяет значение свойства SmallChange
Значения свойств Min и Мах устанавливают интервал допустимых перемещений бегунка. Ваша программа может установить бегунок в нужную позицию, определяемую значением свойства
Position. Метод SetPcirums определяет значения всех свойств Min, Мах и Position одновременно.
4.6.1.12 TGroupВох
Создает контейнер в виде прямоугольной рамки, визуально объединяющий на форме логически связанную группу некоторых интерфейсных элементов. Эта компонента представляет собой инкапсуляцию одноименного объекта
Windows.
4.6.1.13 TRadioGroup
Создает контейнер в виде прямоугольной рамки, визуально объединяющий на форме группу логически взаимоисключающих радио-кнопок.
Радио-кнопки "группируются" при помещении их в один и тот же контейнер. Только одна кнопка из данной группы может быть выбрана. Добавление кнопок к компоненте TRadioGroup выполняется редактированием свойства Items.
Присвоение названия очередной строке свойства Items приводит к появлению этой кнопки в группирующей рамке. Значение свойства Itemlndex определяет, какая радио-кнопка выбрана в настоящий момент. Вы можете группировать радиокнопки в несколько столбцов, устанавливая соответствующее значение свойства Columns.
4.6.1.14 TPanel
Создает пустую панель, которая может содержать другие компоненты. Вы можете использовать TPanel для создания на вашей форме панелей инструментов или строк состояния.
TPanel является производной от класса TCustomPanel, полностью наследуя его свойства, методы и события.
4.6.2 Компоненты Win95
Компоненты вкладки Win95 палитры компонент

осуществляют включение в вашу программу 12 интерфейсных элементов Windows 95.
4.6.2.1 TTabControl
Отображает набор частично перекрывающих друг друга картотечных вкла-док. Названия вкладок
вводятся в список свойства Tabs кнопкой
в графе значений этого свойства. Рис. 4.5 показывает заготовку формы приложения для работы с алфавитным библиотечным указателем. Если все поля не умещаются на форме в один ряд, то можно установить значение true свойства MultiLine, или прокручивать вкладки с помощью кнопок со стрелками.

Рис. 4.5. Картотечные вкладки с названиями.
Установка значения false свойства Enabled запретит выборку отдельных вкладок.
4.6.2.2 TPageControl
Отображает набор полей, имеющих вид частично перекрывающих друг друга картотечных вкладок, для организации многостраничного диалога.
Чтобы создать новую страницу диалога с соответствующей вкладкой, выберите опцию New Page из контекстного меню данной компоненты. Вы можете активизировать конкретную страницу одним из следующих способов: с помощью мыши, выбрав ее из выпадающего списка свойства ActivePage, а также перелистывая вкладки с помощью опций Next Page и Previous Page контекстного меню. Свойство Pagelndex содержит номер активной страницы. Установкой значения false свойства Tab Visible можно сделать эту страницу невидимой.

Рис. 4.6. Многостраничный диалог.
Рис. 4.6 показывает заготовку формы приложения с многостраничным диалогом для второй активизированной страницы. Работу с вкладками реализует встроенная компонента управления TTabSheet. Если все вкладки не умещаются в один ряд, компонента выводит кнопки прокрутки. Задайте значение true свойства MultiLine, чтобы отобразить вкладки в несколько рядов.
Отображает поле с иерархическим (древовидным) перечнем элементов - заголовков документов, записей в указателе, файлов или каталогов на диске. Действие этой компоненты можно увидеть во многих приложениях Windows 95.
Свойство Items ссылается на объект TTreeNodes, которое содержит редактируемый список элементов дерева. Окно редактора элементов дерева (Рис. 4.7) открывается кнопкой в графе значений этого свойства. Каждый элемент дерева состоит из метки, списка ассоциируемых с ним субэлементов и ряда битовых образов (если таковые имеются). Щелкая мышью на элементе, пользователь может раскрывать или закрывать соответствующий список суб-элементов. Двойной щелчок мышью раскрывает один уровень родительского узла дерева, показывая только его прямых потомков. Свойство ShowButtons отвечает за отображение кнопки (со знаком "+", если данный узел не раскрыт и содержит суб-элементы, или со знаком "-" в противном случае) слева от родительского узла: нажатие этой кнопки является альтернативой двойного щелчка мышью по родительскому элементу.

Рис. 4.7. Конструирование дерева компоненты TTreeView.
Число раскрываемых потомков задается значением свойства Indent. Чтобы упорядочить списки потомков в алфавитном порядке, установите значение stText
для свойства SortType. Значение true свойства Visible вызывает отображение линий - ветвей дерева, связывающих родителей с их потомками.
Элементы можно добавлять и вставлять в список динамически с помощью следующих методов для объекта Items ->TTreeNode: AddChildFirst, AddChild, AddChildObjectFirst, AddChildObject, AddFirst, Add, AddObjectFirst,AddObject.Insert,InsertObject.
4.6.2.4 TListView
Отображает поле с иерархическим (древовидным) списком элементов в различных видах. Свойство ViewStyle определяет вид отображения элементов списка: по столбцам с заголовками, вертикально, горизонтально, с малыми или с большими пиктограммами.
Свойство Items позволяет добавлять, вычеркивать и модифицировать подписи, а также подбирать пиктограммы для элементов списка. Редактор списка вызывается кнопкой в графе значений этого свойства.
Свойство Columns содержит редактируемый список названий заголовков столбцов в списке. Окно редактора столбцов открывается кнопкой
графе значений этого свойства. Чтобы увидеть заголовки, задайте значение vsReport для свойства ViewStyle, а значение true для свойства
ShowColumnHeaders. Установка значения true
свойства ColumnClick определяет поведение заголовка аналогичное кнопке: когда пользователь щелкает мышью по подписи, возникает событие OnColumnClick. События OnEdiling и OnEcUtecl возникают, когда пользователь начинает и завершает редактирование подписи.
Для выбора источника пиктограмм из выпадающего списка свойств Largelmages (Smalllmages) задайте значения
vslcon (vsSmallIcon) для свойства ViewStyle. В режиме AutoArrange свойства IconOptions пиктограммы выравниваются в соответствии с выбранным значением свойства Arrangement, а свойство WrapText указывает необходимость переноса текста подписи, когда она не умещается на пиктограмме по ширине. Рис. 4.8 показывает процесс конструирования древовидного списка, причем источником больших пиктограмм является компонента TImageList, на объект которой указывает свойство Largelmages.


Рис. 4.8. Конструирование древовидного списка и
его отображение на форме.
4.6.2.5 TImageList
Создает контейнер для коллекции из графических изображений одинакового размера K *k, каждое из которых можно выбирать по его индексу в интервале значении от 0 до п-1.
Графические коллекции используются для эффективного обслуживания больших наборов битовых образов или пиктограмм, которые хранятся как единый битовый образ шириной k*n. Монохромные изображения с масками отображаются как прозрачные трафареты. TImageList имеет методы, облегчающие процессы записи, выборки и вывода хранимых изображений.

Рис. 4.9. Конструирование коллекции пиктограмм.
Окно редактора коллекции изображений (Рис. 4.9) открывается двойным щелчком мышью по компоненте или опцией ImageList Editor из ее контекстного меню.
TImageList является производной от класса TCustomImageList, полностью наследуя его свойства, методы и события.
4.6.2.6 THeaderControl
Создает контейнер для набора заголовков столбцов, ширину которых можно менять в процессе выполнения программы. Заголовки, перечисленные в свойстве Sections, можно размещать над информационными полями, например, над списками компонент TListBox. Окно редактора заголовочных секций (Рис. 4.10) открывается кнопкой в графе значений этого свойства.

Рис. 4.10. Конструирование секций заголовков.
Поскольку секции заголовков можно подогнать буквально к любым другим компонентам (Рис. 4.11). манипуляция с их шириной автоматически не вызовет адекватного изменения ширины ассоциированных компонент. Если же вы хотите, чтобы ширина столбца изменялась согласно изменениям ширины секции, вам придется написать обработчик события OnSectionResize, ответственный за эти действия.

Рис. 4.11. Конструирование библиографического справочника.
4.6.2.7 TRichEdit
Отображает область редактируемого ввода множественных строк информации в формате RTF (Rich Text Format),
который включает различные вариации атрибутов шрифта и форматирования параграфов. Данный формат принимают многие профессиональные текстовые процессоры, например, Microsoft Word.

Рис. 4.12. Проектирование и исполнение приложения для чтения RTF
файла.
В окне Редактора кода (средняя часть Рис. 4.12) выделена единственная инструкция обработчика события OnАctivate, возникающего при активизации формы и вызывающего чтение файла OVER VIEW.RTF в объект
RichEditI компоненты редактируемого ввода. Нижняя часть рисунка демонстрирует работу скомпилированного и собранного приложения.
TRichEdit является прямой производной от класса TCustomRichEdit, полностью наследуя его свойства, методы и события.
4.6.2.8 TStatusBar
Создает строку панелей состояния (обычно выравниваемую по нижней границе формы) для отображения статусной информации, выдаваемой при работе программы.
Каждая панель представлена в списке свойства Panels. Панели нумеруются слева-направо, начиная с индекса 0. Окно редактора панелей (Рис. 4.13) открывается кнопкой в графе значений этого свойства. Свойство SimplePanel используется для переключения вида отображения строки состояния (одно- или много- панельная).

Рис. 4.13. Конструирование строки панелий состояния.
В окне Редактора кода (средняя часть Рис. 4.14) выделена единственная инструкция обработчика события OnMoiiseMovc', возникающего при перемещении мыши по форме и вызывающего вывод координатор курсора в панель Panels->Item[l] объекта StatusBarl компоненты строки состояния. Нижняя часть рисунка демонстрирует работу скомпилированного и собранного приложения.

Рис. 4.14. Вывод информации па петель строки состояния.
Эта компонента представляет собой инкапсуляцию одноименного объекта Windows.
4.6.2.9 TTrackBar
Создает шкалу с метками и регулятором текущего положения (вариант линейки прокрутки).
Свойства Min
и Мах устанавливают интервал значений шкалы, причем свойство Position отражает текущую позицию регулятора внутри заданного интервала. Число изображаемых меток специфицирует свойство Frequency. На сколько меток должен продвинуться регулятор, когда пользователь щелкает мышью на самой шкале (по обеим сторонам от регулятора) или нажимает клавиши PageUp
и PageDown,
определяет значение свойства PageSize. На сколько меток должен продвинуться регулятор, когда пользователь нажимает клавиши позиционирования курсора, определяет значение свойства LineSize.
Чтобы изменить вид шкалы, используйте свойства TickStyle и
TickMarks. Значения свойств SelStart и
SelEnd устанавливают границы разрешенных перемещений регулятора.
4.6.2.10 TProgressBar
Создает индикатор, который отслеживает процесс выполнения некоторой процедуры в вашей программе. По мере выполнения процедуры, прямоугольный индикатор постепенно окрашивается слева направо заданным цветом.
Свойства Min и Мах устанавливают интервал значений индикатора. Свойство Step задает шаг изменения значения свойства Position всякий раз, когда позиция индикатора меняется.
C++Builder поставляется вместе с шуточным примером, который демонстрирует работу прогресс-индикатора в тесте для измерения "скорострельности" работы машинисток
=> По команде главного меню File | Open Project
откройте диалог выбора проектов. => Войдите в каталог \...\CBuilder\Examples\Apps\Wpm. => Выберите проектный файл Wpm и нажмите кнопку Open.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки приложения.
Код программного модуля WPMMAIN.CPP чрезвычайно лаконичен и не нуждается в дополнительных комментариях. Вы легко сможете адаптировать поведение приложения в соответствии со своим вкусом, в частности, переведя его на родной язык. Результат самотестирования автора книги (Рис. 4.15) показывает его совершенную непригодность к машинописи.

Рис. 4.15. Работа приложения для тестирования машинисток.
4.6.2.11 TUpDown
Создает спаренные кнопки со стрелками л.
(вверх) и •”• (вниз). Нажатие этих кнопок вызывает, соответственно, увеличение или уменьшение численного значения свойства Position.
Эта компонента обычно используется вместе с сопровождающим элементом управления, задаваемым свойством Associate. Когда сопровождающим элементом служит область редактируемого ввода, значение свойства Position определяет форматирование вводимого текста. Если свойство Associate не специфицировано, значение свойства
Position содержит числовую величину.
4.6.2.12 И THotKey
Используется для установки клавиш быстрого вызова (shortcut)
во время выполнения программы. Пользователь может ввести комбинацию "горячих" клавиш, обычно состоящую из модификатора
(Ctrl, Alt или
Shift) и любого символа, включая функциональные клавиши
F1,..F12.
Введенную комбинацию, записанную в свойстве HotKey, можно присвоить свойству Shortcut другой компоненты. Чтобы выбрать горячие клавиши на стадии проектирования, используйте свойства HotKey и Modifiers, а чтобы отменить их - свойство InvalidKeys. Чтобы изменить комбинацию во время выполнения программы, удерживайте нажатой клавишу модификатора и одновременно введите новый символ.
4.6.3 Дополнительные компоненты
Компоненты вкладки Additional палитры компонент

осуществляют включение в вашу программу 9 элементов управления, разработанных корпорацией Borland специально для среды C++Builder.
4.6.3.1 TBitBtn
Создает кнопку с изображением битового образа. Такие кнопки чаще всего используются в специальных диалоговых окнах.
Графические кнопки имеют свойства для спецификации битовых образов, их вида и размещения на кнопке. Вы можете пользоваться готовыми стилями графических кнопок из отдельного каталога изображений, входящего в поставку C++Buider или собственными картинками, созданными одной из систем редактирования изображений. Различным состояниям кнопки (например, "нажата", "отпущена", "запрещена" и т.п.) могут соответствовать разные битовые образы.

Окно редактора файлов изображений с расширением bmp (Рис. 4.16) открывается кнопкой
значений свойства Glyph. Свойство Kind поможет вам создать стандартизованные кнопки, снабженные надписями и соответствующей графикой: ОК, Cancel. Helo и другие.
Рис. 4.16. Редактор изображений файлов битовых образов с расширением bmp.
4.6.3.2 TSpeed Button
Создает графическую кнопку, обычно располагаемую на панели (TPanel) быстрого вызова определенных команд меню или установки режимов.
Различным состояниям быстрой кнопки (например, "нажата", "отпущена", "запрещена" и т.п.) могут соответствовать разные графические образы. Имеются свойства для выбора заменяющих друг друга изображений и текста надписи. Окно редактора файлов изображений с расширением (Рис. 4.16) открывается кнопкой графе значений свойства Glyph. Другие свойства быстрых кнопок организуют их работу в некоторой группе.
Создает контролируемую прямоугольную область редактируемого ввода данных специфического формата. Корректность вводимого текста проверяется посредством маски, кодирующей разрешенные форматы, в которых текст может быть введен и представлен пользователю (дата, время, телефонный номер и т.п.). Свойство
EditMask хранит код текущей маски. Окно редактора масок (Рис. 4.17)
открывается кнопкой в графе значений этого свойства.

Рис. 4.17. Создание маски для ввода телефонных номеров.
TMaskEdit является прямой производной класса TCustomMaskEdit.
Создает регулярную сетку для отображения символьных последовательностей по строкам или столбцам.
Названия и назначение всех свойств этой компоненты, которыми вы можете манипулировать в период проектирования, полностью идентичны свойствам компоненты TDrawGrid, описываемой в следующем параграфе.
Все объекты, связанные с символьными последовательностями, заключены в свойстве Objects, которое позволяет обращаться к нужному объекту. Во время выполнения программы символьные последовательности и связанные с ними объекты некоторого столбца сетки адресуются свойством Cols. Свойство Rows позволяет подобным образом оперировать со строками сетки. Все символьные последовательности сетки содержатся в свойстве Cells, которое адресует нужную ячейку сетки.
4.6.3.5 TDrawGrid
Создает регулярную сетку для отображения структурированных графических данных по строкам или столбцам. Свойства RowCount и ColCount задают число ячеек сетки по вертикали и по горизонтали.
Значение свойства Options позволяет изменить вид сетки (например, с разделительными линиями между столбцами) и ее поведение (например, с переходом от столбца к столбцу по клавише Tab). Ширина разделительных линий сетки задается свойством GridLineWidth, а линейки прокрутки добавляются свойством ScrollBars. Свойства FixedCols и FixedRows позволяют запретить прокрутку столбцов и строк, а свойство FixedColor присваивает определенный цвет всем столбцам и строкам.
Значение true
свойства DefauItDrawing вызывает автоматическую прорисовку содержимого ячеек сетки, причем ее фон, канва и цвет выбираются по умолчанию. Установка значения false свойства DefauItDrawing требует написания обработчика события OnDrawCell для заполнения ячеек сетки "вручную". С помощью свойств DefaultColWidths и DefaultRowHeights можно установить ширину всех столбцов и высоту всех строк, выбираемых по умолчанию. Свойства ColWidth и RowHeight
специфицируют ширину конкретного столбца и высоту конкретной строки.
Во время работы программы вы можете получить в свое распоряжение область для рисования некоторой ячейки с помощью метода
CellRect. Метод MouseToCell возвращает координаты номера столбца и строки ячейки, на которую установлен курсор мыши. Выбранная ячейка сетки становится значением свойства Selection.
Можно определить, какая строка является в момент выполнения верхней строкой сетки или поставить указанную строку в верхнее положение с помощью свойства TopRow. Чтобы определить, какой столбец будет первым видимым столбцом сетки, воспользуйтесь свойством LeftCol. Значения свойств VisibleColCount и VisibleRowCount специфицируют общее число видимых столбцов и строк сетки.
4.6.3.6 HTImage
Создает на форме контейнер графического изображения (битового образа, пиктограммы или метафайла).
Окно редактора файлов изображений (Рис. 4.16) открывается кнопкой
в графе значений свойства Picture. Чтобы контейнер изменил свои размеры так, чтобы вместить изображение целиком, установите значение
true свойства
AutoSize. Чтобы исходное изображение меньшего размера растянулось на весь контейнер, задайте значение true свойства Stretch.
Используйте методы LoadFromFile и SaveToFile объектного свойства Picture для динамической загрузки и сохранения файлов изображений с помощью инструкций типа:
Image->Picture->LoadFromFile("<имя файла>") ;
Image->Picture->SaveToFile("<имя файла>");
4.6.3.7 TShape
Рисует простые геометрические фигуры - окружность и эллипс, квадрат и прямоугольник (можно с закругленными углами).
Вид выбранной геометрической фигуры определяется свойством Shape, а цвет и способ ее окраски - двумя вложенными в Brush свойствами Color и Style. Размеры фигур определяются соответствующими свойствами.
4.6.3.8 TBevel
Создает линии, боксы или рамки, которые выглядят объемными, как бы вырезанными стамеской.
Рисуемый компонентой объект определяется свойством Shape, а значение свойства Style меняет вид объекта, делая его выпуклым или вдавленным. Чтобы сохранить относительное положение объекта неизменным, даже если пользователь изменит размеры формы, установите значение true свойства Align.
4.6.3.9 TScrollBox
Создает в окне бокс переменного размера, который автоматически снабжается линейками прокрутки, если необходимо.
С помощью бокса прокрутки можно защитить некоторые области окна от прокрутки. Например, чтобы защитить панель инструментов и панель состояния, сначала спрячьте линейки прокрутки окна, а затем поместите бокс прокрутки в области клиента между панелью инструментов и панелью состояния. Линейки прокрутки бокса будут выглядеть принадлежностью окна, однако прокрутка будет производиться только внутри бокса.
Другое использование боксов прокрутки заключается в возможности создания множественных прокручиваемых областей (видов) в некотором окне. Виды часто присутствуют в коммерческих текстовых процессорах, бухгалтерских программах и в программах планирования проектов. Бокс прокрутки может содержать другие компоненты, например, TButton и
TCheckBox.
4.6.4 Компоненты доступа к базам данных
Невидимые компоненты вкладки Data
Access палитры компонент обеспечивают соединения с базами данных, что позволяет разработчику сфокусировать
внимание на обслуживании данных, не обращая внимания на организацию взаимодействия с пользователем.

Компоненты осуществляют включение в вашу программу 8 интерфейсных элементов доступа к наборам данных (datasets) - таблицам, запросам, хранимым процедурам, сессиям и другим основным сущностям баз данных.
Доступ к базам данных поддерживает его основа - 32-разрядный механизм BDE (Borland Database Engine).
4.6.4.1 TDataSource
Представляет собой интерфейс между прочими компонентами доступа к наборам данных и видимыми компонентами управления, размещенными на форме. Именно посредством соединения с источником данных пользователь получает возможность отображения, навигации и редактирования содержимого баз данных.
Каждый набор данных должен ассоциироваться с источником, чтобы манипулировать данными посредством компонент управления. С другой стороны, каждая компонента управления должна ассоциироваться с источником, чтобы она могла принимать данные и манипулировать ими.
Компоненты TDataSource также способны организовать парные связи таблиц между собой по принципу master-detail и поддерживать синхронизацию обмена.
Установка значения false свойства AutoEdit запрещает режим редактирования набора данных. Свойство DataSet указывает, с каким набором данных (таблицей, запросом) связан их источник.
4.6.4.2 ТТаble
Представляет собой интерфейс между механизмом BDE и компонентой TDataSource, которая, в свою очередь, образует соединение с такими компонентами управления, как TDBGrid.
Именно посредством ТТаЫе, обеспечивается доступ на этапе проектирования к живым данным (live data) из таблицы локальной базы данных: все записи или столбцы адресуемой таблицы сразу же становятся доступными для приложения.
Свойство Active устанавливает активное состояние связи с таблицей, чтобы можно было увидеть на форме живые данные. Свойство DatabaseName содержит псевдоним адресуемой базы данных или полный путь к ее каталогу, а свойство TableName - имя таблицы.
Установите значение true свойства ReadOnly, если хотите запретить изменения содержимого таблицы. Установите значение true
свойства Exclusive, если хотите запретить другому приложению обращаться к таблице, пока вы ее используете сами.
4.6.4.3 TQuery
Подобно TTable, компонента TQuery представляет собой интерфейс между сервером локальной (или удаленной) базы данных и компонентой TDataSource, обеспечивая доступ на этапе проектирования к живым данным из одной или нескольких таблиц.
Благодаря командам на языке структурированных запросов SQL (Structured Query Language), компонента TQuery получает групповой доступ к таблице. В дальнейшем мы будем ссылаться только на четыре базовые команды, которые поддерживаются всеми версиями стандарта SQL: SELECT - для выбора данных;
INSERT - для добавления новых данных; UPDATE - для модификации таблиц;
DELETE -для удаления данных. Синтаксис построения и параметры этих команд поясняются на конкретных примерах, приведенных в главе 5.
C++Builder передает запросы BDE серверу (или SQL серверу), который интерпретирует их и возвращает вашему приложению результирующий набор
(result set) - запрошенную группу записей или столбцов.
Свойство Active устанавливает активное состояние связи с таблицей, чтобы можно было увидеть на форме живые данные, полученные в результате обработки SQL запроса. Свойство DatabaseName содержит псевдоним базы данных или полный
путь к ее каталогу для направления запроса.
Свойство Params специфицирует значения параметров динамического запроса, передаваемого во время выполнения программы. Чтобы ввести символическую запись самой команды статического или динамического запроса, нажмите
кнопку в графе значений свойства SQL.
4.6.4.4 TStoredProc
Разрешает приложению клиента выполнять процедуры, хранимые на удаленном сервере базы данных с передачей результатов клиенту. Операции над большими группами строк в таблице базы данных, агрегатные или математические функции — подходящие кандидаты для хранимых процедур. Перемещая на мощный сервер такие повторяющиеся задачи с интенсивными вычислениями, можно заметно улучшить производительность вашего приложения. Общая загруженность сети при этом снижается, поскольку обработка происходит там же, где находятся сами данные.
Свойство DatabaseName содержит псевдоним базы данных сервера, на котором находится хранимая процедура, а свойство StoredProcName - имя процедуры.
Подобно TQuery, свойство Params специфицирует значения входных и выходных параметров, перечисляемых свойством в порядке их объявления хранимой процедурой. Воспользуйтесь редактором параметров, если вы не знаете точно порядок параметров в данной хранимой процедуре. Чтобы активизировать редактор параметров, нажмите кнопку в графе значений свойства Params.
Хранимую процедуру необходимо подготовить к запуску: на стадии проектирования - с помошью редактора параметров. а во время выполнения программы - с помощью метода Prepare
Исполнение хранимой процедуры реализует метод ЕхесРгос (если процедура возвращает одиночный результат в виде единственной записи) или метод Open (если процедура возвращает результирующий набор в виде множественных записей).
4.6.4.5 TDatabase
Эта компонента не участвует в организации доступа на уровне наборов данных, хотя предоставляет возможность соединения клиент/сервер с одиночной базой данных в одной сессии. Компонента TDatabase используется для выдачи специфических команд управления базой данных или создания временного локального псевдонима некоторой удаленной базы данных, но особенно целесообразна для организации управления обработкой транзакций на удаленном
SQL сервере.
Для первого соединения с сервером вы можете написать обработчик события OnLogin, с помощью которого можно, скажем, автоматически подставлять значения параметров прав доступа к защищенной базе данных.
4.6.4.6 TSession
Предоставляет вашему приложению средства глобального обслуживания групповых соединений с несколькими базами данных. C++Builder автоматически создает глобально доступную компоненту стандартной сессии (с именем Session, по умолчанию) для всех приложений, использующих компоненты управления данными.
Компонента TSession организует стандартные сессии, множественные сетевые сессии с файлами базы данных Paradox и сессии приложений с повторными вхождениями.
Сетевое приложение, которое одновременно обращается к таблицам базы данных
Paradox в различных узлах сети, устанавливает множественные сессии -по одной для каждого узла. Приложение, которое использует множественные конкурентные соединения с одиночной базой данных (например, одновременно выдает несколько запросов к одним и тем же данным), устанавливает сессию с повторными вхождениями.
Вы можете управлять поведением сессии во время работы приложения, обращаясь к ее свойствам, событиям и методам.
4.6.4.7 TBatchMove
Разрешает вашему приложению выполнять пакетные операции над группами записей или целыми таблицами. Эта компонента обычно используется в приложениях, ориентированных на администратора базы данных, чтобы предоставить ему возможности пересылки больших объемов данных, вплоть до всего содержимого базы данных. К пакетным операциям относятся:
• добавление группы записей в некотором наборе данных к таблице назначения базы данных:
• вычеркивание группы записей в некотором наборе данных из таблицы назначения базы данных;
• копирование набора данных с созданием новой таблицы назначения или с обновлением содержания существующей таблицы.
Выпадающие списки свойств Source и Destination позволяют выбрать имя таблицы источника и таблицы назначения, соответственно. Установите нужную пакетную операцию
(batAppend, batUpdate, batAppendUpdate, batCopy или batDelete) в свойстве Mode и обратитесь к методу Execute, чтобы выполнить ее. Свойство ProblemTableName инструктирует BDE, в какую таблицу заносить те записи источника, обращение с которыми вызвало проблемы в ходе пакетной операции.
Реализовать пакетные операции, хотя и с некоторыми ограничениями, можно также при помощи компоненты TTable.
4.6.4.8 TUpdateSQL
Позволяет использовать механизм котируемых обновлений (cached updates) в стиле Delphi для обслуживания результирующих наборов с атрибутом "только чтение", возвращаемых некоторыми запросами. Кэшируемые обновления заметно ускоряют отклик SQL сервера за счет уменьшения общего числа сетевых обменов с клиентом. Будучи упакованными, множественные коммуникации проявляют себя как одиночные транзакции, тем самым снижая загруженность сервера и улучшая производительность вашего приложения.
Установка свойства UpdateObject обеспечивает связь данной компоненты с набором данных.
Свойство ModifySQL представляет собой SQL команду UPDATE, которая выполняется, когда кэшируемое обновление является модификацией существующей записи. Свойство InsertSQL представляет собой команду INSERT, которая выполняется, когда кэшируемое обновление содержит вставку новой записи. Свойство DeleteSQL представляет собой команду DELETE, которая выполняется, когда кэшируемое обновление состоит в вычеркивании некоторой записи. Перечисленные свойства обеспечивают нормальную передачу параметров для кэ-шируемых обновлений. Используйте префикс
"OLD_" с именем поля, чтобы получить его значение перед тем, как кэшируемое обновление было разрешено. Доступ к старому значению поля обычно требуется при создании конструкции WHERE перечисленных команд SQL.
Метод Apply
используется для принудительного исполнения команд SQL, например, из обработчика события OnUpdateRecord. Этот метод сочетает обращение к SetParams
(установка связки параметров) и к ExecSQL (фактическое выполнение команды SQL).
4.6.5 Компоненты управления данными
Видимые компоненты вкладки Data Controls палитры компонент обеспечивают взаимодействие пользователя с источниками данных вашего приложения.

Компоненты осуществляют включение в вашу программу 12 элементов управления визуализацией и редактированием записей или столбцов, хранимых в таблицах и запросах реляционной базы данных.
4.6.5.1 TDBGrid
Осуществляет отображение и редактирование записей, содержащихся в наборе данных и представляемых на регулярной сетке. Совместное использование сетки с компонентой
TDBNavigator позволяет наилучшим способом организовать просмотр и редактирование содержимого базы данных.
Вы должны связать TDBGrid с набором данных посредством компоненты источника TDataSource, который идентифицируется значением свойства DataSource. Свойство Columns содержит редактируемый список названий заго-ловков столбцов в сетке. Окно редактора столбцов (Рис. 4.18) открывается кнопкой в графе значений этого свойства или опцией Columns Editor из контекстного меню компоненты.

Рис. 4.18. Редактор столбцов компоненты сетки.
Значение свойства Options позволяет изменить вид сетки (например, с разделительными линиями между столбцами) и ее поведение (например, с переходом от столбца к столбцу по клавише Tab). Значение свойства
TitleFont определяет шрифт, используемый при написании заголовков столбцов сетки. Свойство Fields содержит массив всех полей набора данных, изображаемых в сетке во время работы программы, свойство FieldCount - их число, а свойство
SelectedField - поле, выбранное в данный момент.
Значение true
свойства ReadOnly запрещает, а значение false разрешает редактирование данных при условии, что набор данных находится в режиме редактирования. Прервать редактирование можно нажатием клавиши Esc или аналогичной кнопки на панели навигатора. Фактическое изменение данных в полях происходит только после того, как выбрана новая запись, или при выходе из программы.
4.6.5.2 TDBNavigator
Навигатор базы данных TDBNavigator используется для перемещений по записям набора данных и выполнения операций по их просмотру и редактированию.
Панель управления навигатора размещается на форме вместе с другими компонентами визуализации данных, как правило с сеткой.
Когда пользователь нажимает одну из кнопок на панели навигатора (Рис. 4.19), выполняется надлежащее действие над записью в наборе данных, с которым связан навигатор. Например, при нажатии кнопки "+" пустая запись вставляется в таблицу перед текущей записью.

Перейти к первой записи Перейти к предыдущей записи Перейти к следующей записи Перейти к последней записи Вставить новую запись Вычеркнуть текущую запись Включить режим редактирова-Записать изменения записи Прервать редактирование Обновить набор данных
Рис. 4.19. Форма с панелью навигатора и компонентой TDBGrid.
Вы должны связать TDBNavigator с набором данных посредством компоненты источника TDataSource, который определяется значением свойства DataSource.
4.6.5.3 TDBText
Отображает, по аналогии с компонентой TLabel, поле текущей записи в наборе данных в виде названия - статического текста, который нельзя редактировать
Вы должны связать TDBText с набором данных посредством компоненты источника, который определяется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Свойство Alignment определяет способ выравнивания текста. Чтобы автоматическая установка размера шрифта позволяла бы отображать текст названия целиком, установите значения true свойства AutoSize. Чтобы можно было просмотреть длинный текст названия по частям, установите значения true
свойства Wordwrap. Задавая значение true свойства Transparent, вы можете оставить видимой часть графики сквозь название.
4.6.5.4 TDBEdit
Создает, по аналогии с компонентой
TEdit, однострочную прямоугольную область для отображения и редактирования короткого поля текущей записи в наборе данных.
Свойство Text
содержит поле в наборе данных, рассчитанное на одиночную строку. TDBEdit использует маску контроля корректности ввода в поле базы данных.
4.6.5.5 TDBMemo
Создает, по аналогии с компонентой
TMemo, многострочную прямоугольную область с линейкой прокрутки для отображения и редактирования длинного поля текущей записи в наборе данных.
Свойство Text
адресует указанное поле в наборе данных, содержащее многострочную алфавитно-цифровую последовательность или Большой Бинарный Объект (BLOB).
ИСПОЛЬЗОВАНИЕ И СОЗДАНИЕ ВИЗУАЛЬНЫХ КОМПОНЕНТ
6. ИСПОЛЬЗОВАНИЕ И СОЗДАНИЕ ВИЗУАЛЬНЫХ КОМПОНЕНТ
Эта глава посвящена основе построения приложений в C++Builder- Библиотеке Визуальных Компонент VCL (Visual Component Library). Обсуждается иерархическая структура компонент VCL, объясняется назначение общих свойств, методов и событий, присущих различным базисным уровням в иерархии.
Глава дает "взгляд изнутри" на принципы построения различных компонент и порождающих их классов. Для понимания материала, изложенного в данной главе, читатель должен быть знаком с языком C++, терминологией ООП и методикой использования компонент в интегрированной среде визуальной разработки C++Builder.
Библиотека Визуальных Компонент была впервые введена системой программирования
Delphi 1.0 на языке Объектный Паскаль и модифицирована в Delphi 2.0 для поддержки 32-разрядных приложений. Delphi оказалась наиболее популярной на рынке систем быстрой разработки программных приложений, однако, многие потребители высказывали интерес к подобной системе для языка C++, которая в конце концов и воплотилась в C++Builder. C++Builder унаследовал версию Библиотеки Визуальных Компонент Delphi 2.0 без каких-либо изменений.
Библиотека VCL интегрирована в среду C++Builder, что, в отличие от других систем программирования, позволяет манипулировать классами визуальных компонент при проектировании приложения, на стадии создания его прототипа. Поведение и вид ваших компонент определяются по мере разработки приложения, хотя можно модифицировать их и в процессе выполнения программы.
6.1 Назначение и устройство VCL
Библиотека Визуальных Компонент позволяет программистам визуально создавать программные приложения, не прибегая более к кодированию классов "вручную", или кодированию в рамках стандартных библиотек MFC (Microsoft Foundation Class), или OWL (Object Windows Library).
C++ программистам теперь не надо создавать или манипулировать объектами интерфейса с пользователем путем написания соответствующего кода. Подавляющее большинство приложений вы будете разрабатывать визуально с помощью Редактора форм C++Builder, добавляя лишь несколько строк к .обработчикам ключевых событии компонент. Используйте объекты всегда, когда это возможно; твердо сопротивляйтесь позыву написать новый код то тех пор, пока все другие возможности не будут исчерпаны.
Для создания новых компонент можно с одинаковым успехом пользоваться средствами C++Builder или Delphi, однако если разработанные компоненты предлагаются для внешнего применения, автор обязан удостовериться, что они работают в рамках обеих систем.
6.1.3 Компоненты VCL
Компоненты — это строительные кирпичи, из которых конструируется интерфейс программы с пользователем, с помощью которых "здание" программы приобретает новый внешний облик и скрытые особенности. Для прикладного программиста любая компонента VCL представляет собой объект, который можно "перетащить" из вкладок Палитры компонент (Рис. 6.1) на форму создаваемого приложения. Поместив компоненту на форму, можно манипулировать ее свойствами (посредством Редактора форм) и кодом (с помощью Редактора кода), придавая компоненте специфическое поведение.

Рис. 6.1. Палитра компонент с выбранной пиктограммой TLahel.
Для разработчика компоненты представляют собой объекты на C++ или на Объектном Паскале. Некоторые компоненты инкапсулируют поведение типовых элементов управления, предоставляемых операционными системами Windows. Другие компоненты вводят совершенно новые видимые и невидимые элементы, программный код которых полностью определяет их поведение.
Сложность компонент различна. Так
TLabel из вкладки Standard Палитры компонент способна лишь отображать статистический текст. Можно сконструировать значительно более сложную компоненту, которая, например, инкапсулирует законченное обслуживание бухгалтерских документов специализированной базы данных.
6.2 Типы компонент
С точки зрения прикладного программиста компонентный объект представляет собой законченную конструкцию, содержащую свойства, методы и события. В отличие от разработчика компонент, пользователю компонент безразлично, от какого класса произведена данная компонента.
Прикладные программисты принимают как факт, что каждая компонента имеет свойства Тор и Left, которые определяют положение компоненты на форме-владельце; для них не существенно, что эти свойства унаследованы от общего предшественника TComponent. Напротив, когда вы создаете компоненту, вы обязаны знать, от какого родителя можно заимствовать нужные вам свойства, а также все остальные его характеристики, так, чтобы их можно было наследовать, а не создавать вновь.
Из определения объектных классов вы знаете, что при определении некоторого класса (наследника) вы производите его от существующего объектного типа (непосредственного предшественника). Стандартный абстрактный тип TObject является, по умолчанию, первым предшественником (прародителем) всех объектов Библиотеки Визуальных Компонент.
Компоненты по сути представляют собой объекты специальных типов. Лишь несколько исключении выделяют структуру компонент из общих правил структурирования объектов на языке C++:
• Большинство компонент представляют собой элементы управления интерфейсом с пользователем, причем некоторые обладают весьма сложным поведением.
• Все компоненты являются прямыми или косвенными потомками одного общего класса-прародителя (TComponent).
• Компоненты обычно используются непосредственно, путем манипуляций с их свойствами; они сами не могут служить базовыми классами для построения новых подклассов.
• Компоненты размещаются только в динамической памяти с помощью оператора new.
Понимание VCL основано на трех фундаментальных принципах. Во-первых, вам придется ознакомиться со специальными характеристиками четырех базисных типов компонент: стандартного управления, оригинального управления (custom control),
графического управления и невидимых компонент. Во-вторых, вы должны понимать структуру Библиотеки, в которую встроены описания и реализации кодов компонент. В третьих, вы должны знать положение упомянутых четырех типов компонент в иерархии VCL.
6.2.1 Стандартные компоненты
Некоторые компоненты VCL инкапсулируют поведение таких типовых элементов управления операционной системы Windows, как TButton, TListbox и TEdit. Вы найдете стандартные компоненты на вкладках Standard и Win95 Палитры компонент.
Любая стандартная компонента выглядит и ведет себя точно так же, как и инкапсулированный ею элемент управления Windows. VCL добавляет обрамление, которое никак не меняет свойств исходного элемента управления, а лишь делает доступной модификацию вида и поведения компоненты посредством свойств и методов.
Если вы собираетесь использовать стандартные компоненты без изменений, вам не нужно вникать в правила построения обрамлений VCL. Разработчик компонент может открыть файл исходных текстов стандартных компонент, входящий в поставку версий C++Builder Professional или Client/Server Suite, чтобы понять, каким образом известные элементы управления Windows обрамляются при включении в Библиотеку.
Например, компонента TListBox отображает элементы списка в один столбец, хотя инкапсулирует класс простого списка LISTBOX
из Windows,
который может отображать список в несколько столбцов. Чтобы изменить поведение компоненты, вам придется реализовать перегрузку метода создания данной компоненты, принятого по умолчанию.
6.2.2 Оригинальные компоненты
В отличие от стандартных компонент, оригинальные компоненты представляют собой элементы управления, у которых нет ни метода для собственного отображения, ни заранее определенного поведения. Разработчик компонент должен предусмотреть код, реализующий рисование самой компоненты и код, определяющий поведение компоненты, когда пользователь взаимодействует с ней. Примерами
оригинальных компонент являются TPanel и TStringGrid.
Следует отметить, что как стандартные, так и оригинальные компоненты всегда ассоциируются с некоторым окном управления, поэтому иногда называются оконными (windowed components). Данный аспект подробно обсуждается в параграфе, описывающем класс TWinControl. Оконные компоненты обладают следующими свойствами: они могут быть активизированы (принять фокус ввода), используют системные ресурсы и могут служить контейнерами, т.е. являться родителями других элементов управления. Примером контейнерной компоненты является TPanel.
6.2.3 Графические компоненты
Графические компоненты представляют собой видимые элементы управления, которые не могут принять фокус ввода, т.к. не являются оконными. Графические компоненты обеспечивают отображение объектов без использования системных ресурсов, они требуют меньших "накладных расходов", нежели стандартные или оригинальные компоненты. Примерами графических компонент являются TImage и TShape.
Графические компоненты не могут служить контейнерами для других элементов управления, т.е. не могут владеть другими компонентами.
6.2.4 Невидимые компоненты
Во время выполнения программы невидимые компоненты не появляются на форме в виде каких-либо элементов управления. Поведение невидимых компонент определяется на этапе проектирования, путем инкапсуляции нужных свойств объекта.
С помощью Инспектора объектов вы можете модифицировать свойства невидимых компонент и предусматривать код обработчиков событий для них. Примерами таких компонент являются TOpenDialog, TTable или TTimer.
6.2.5 Контейнерные компоненты
Некоторые компоненты в VCL могут владеть другими компонентами или являться родителями других компонент. Указанные аспекты имеют разное смысловое значение, что и проясняется в следующих параграфах.
6.2.5.1 Право владения
Любая компонента может находиться во владении (ownership) других компонент, но не все компоненты могут являться владельцами. Свойство компоненты
Owner (Владелец) содержит ссылку на компоненту, которая ею владеет. Рис. 6.2 показывает иерархию владения некоторой формы.

Рис. 6.2. Пример иерархии владения.
Владелец ответствен за освобождение тех компонент, которыми владеет, когда сам разрушается. Так в процессе конструирования формы, она автоматически становится владельцем всех компонент, размещенных на ней, даже если часть их размещена на другой компоненте, такой как TPanel.
Владение применимо не только к видимым, но и к невидимым (Ttimer, DataSource) компонентам.
Когда компонента создается динамически в процессе выполнения программы, конструктору компоненты передается ее владелец в качестве параметра. В следующем примере неявный владелец формы (this)
передается конструктору компоненты
TButton как параметр. TButton выполнит присваивание значения переданного параметра свойству Owner
кнопки MyButton:
MyButton = new TButton(this);
Когда форма, владеющая компонентой
TButton освобождается, автоматически уничтожается и кнопка
MyButton.
Вы можете создать компоненту, у которой нет владельца, передавая значение параметра 0 конструктору компоненты. Однако, когда эта компонента перестает быть нужной, ее уничтожение выполняется принудительно (с помощью оператора delete). Следующий пример иллюстрирует обращение с компонентой TTable, не имеющей владельца:
TTable* MyTable = new TTable(0)
// Код, реализующий работу с MyTable
delete MyTable;
Свойство Components типа массив содержит перечень компонент, которыми владеет данная компонента. Листинг 6.1 содержит фрагмент кода обработчика события OnClick с циклом отображения имен классов всех компонент, которыми владеет некоторая форма.
void _fastcall TFormI::ButtonlClick(TObject *Sender) {
for (int i=0; i
ShowMessage(Components[i]->ClassName()) ;
}
Листинг 6.1. Использование свойства
Components.
6.2.5.2 Родительское право
Понятие родительского права
(parentship) существенно отличается от права владения и применимо только к видимым (оконным) компонентам.Родитель компоненты не может быть ее владельцем.
Родительские компоненты обращаются к соответствующим внутренним функциям, чтобы вызвать отображение компонент-потомков. Родитель также ответствен за освобождение своих потомков, когда сам родитель уничтожается. Свойство компоненты Parent (Родитель) содержит ссылку на компоненту, которая является ее родителем. Рис. 6.3 показывает родительскую иерархию некоторой формы.

Рис. 6.3. Пример родительской иерархии.
Многие свойства видимых компонент (например. Left, Width, Top, Height)
относятся к родительским элементам управления. Другие свойства (например, ParentColor и ParentFont) позволяют потомкам использовать свойства родителей.
К оконным компонентам относятся такие видимые элементы, как TEdit, TListBox и TMemo. Чтобы отобразить оконную компоненту, ей надо присвоить родителя, ответственного за отображение. Это присваивание выполняется автоматически на стадии проектирования, когда вы перетаскиваете нужную компоненту из Палитры компонент на форму. Напротив, при создании компоненты во время выполнения программы вы должны явно записать это присваивание, иначе компонента не будет отображена (Листинг 6.2).
void _fastcall TFormI::FormCreate(TObject *Sender)
{
MyEdit = new TEdit(this); //
Передать this как владельца MyEdit->Parent = this;
// Передать
this как родителя
}
Листинг 6.2. Создание компоненты TEdit во время выполнения.
6.2.5.3 Поточность
Поточность (streaniabilily) компоненты выражается в способе хранения самой компоненты и информации, относящейся к значениям ее свойств, в файле или в отведенной области памяти. Например, создаваемый C++Builder ресурсный файл с расширением .dfm содержит информацию о форме и компонентах, размещенных на ней. Эта информация автоматически сбрасывается в поток ресурсного файла.
Разработчики компонент VCL должны разбираться в механизме поточного ввода/вывода, поскольку им придется вручную сбрасывать в поток специальные данные о новых компонентах. VCL не автоматизирует этот процесс.
6.3 Свойства компонент
Определение класса в языке C++ содержит инкапсуляцию членов данных и методов, оперирующих с данными и определяющих поведение объекта. Эта концепция всех систем ООП принята в VCL.
VCL позволяет манипулировать видом и функциональным поведением компонент не только с помощью методов (как это делается с обычными классами), но и посредством свойств и событий, присущих только классам компонент.
Свойства представляют собой расширение понятия членов данных. Разрешены любые типы свойств, за исключением файлового типа. В отличие от члена данных, свойство не хранит данные, однако его методы чтения и записи позволяют получить доступ к защищенному члену данных объекта.
Таким образом, присваивание значения члену данных посредством присваивания свойству вызывает "побочный эффект", за которым могут скрываться сложные операции над компонентой. Например, побочный эффект при изменении свойства Caption (Название) некоторой формы проявляется в немедленном изменении названия заголовка окна этой формы, при отсутствии явного обращения к методу, реализующему операцию изменения заголовка.
6.3.1 Зачем нужны свойства?
Наиболее очевидное достоинство свойств проявляется в том, что они представлены в окне Инспектора объектов. Это существенно упрощает работу программиста: вместо того, чтобы реализовывать ввод и обработку параметров при конструировании объектов во время выполнения, вы всего лишь считываете значения. присвоенные пользователем.
С точки зрения пользователя, компоненты выглядят как обычные переменные. Пользователь может считывать или менять значения свойств точно так же, как это делает программа с членами данных во время выполнения. Единственное, что пользователи не могут делать, - это передавать указатели свойств методам в качестве параметров. С точки зрения разработчика компонент, в свойствах заключены гораздо большие возможности, нежели в обычных членах данных. Действительно:
• Пользователи могут устанавливать значения свойств на стадии проектирования. Это очень важная характеристика, поскольку в отличие от методов, проявляющих себя во время работы программы, свойства дают возможность пользователю манипулировать поведением компоненты до момента запуска приложения. В целом, ваши компоненты не должны содержать много методов: большинство из них, вероятно, будут инкапсулированы свойствами.
• В отличие от члена данных, свойство может прятать детали реализации от пользователя, например, данные свойства могут храниться в зашифрованном виде и появляться в нормальном виде только при чтении или установке его значений. Хотя фактическим значением свойства может являться обычное число, компонента может, скажем, выбирать его из базы данных или выполнять сложные вычисления, чтобы выдать это значение.
• Применение свойств приводит к побочным эффектам в операторах присваивания членов данных. Эти внешне простые операторы могут подразумевать вызов метода, который, в принципе, может сделать все, что угодно. Простым примером служит свойство всех компонент под названием Тор. Присваивание не только меняет значение этого свойства, но и, благодаря побочному эффекту, приводит к перемене положения компоненты на форме и перерисовке ее содержания. Отметим, что побочный эффект установки свойств может не ограничиваться данной компонентой. Например, установка значения true свойства Down в одной из компонент группы быстрых кнопок приводит к изменению свойств всех других быстрых кнопок этой группы на значение false.
• Методы реализации свойства могут быть виртуальными, т.е. в разных компонентах одно и то же свойство может быть связано с различными действиями.
6.3.2 Объявление свойств
C++Builder использует ключевое слово _property для идентификации свойств. Синтаксис описания свойства имеет вид:
property <тип свойства> <имя свойства> = {<список атрибутов>} ;
где список атрибутов содержит перечисление следующих атрибутов свойства:
write = < член данных или метод записи >;
read = < член данных или метод чтения >;
default = < булева константа, управляющая сохранением значения>;
stored = < булева константа или функция, сохраняющая значение >.
Внимательный читатель заметит, что определение свойства уже давалось в главе 3. Дело в том, что информация, необходимая разработчикам новых компонент для реализации оригинальных операции со значениями свойств, требует более детального изложения.
6.3.2.1 Доступ к внутренним данным свойств
C++Builder не вводит ограничений на способ хранения значений свойств. Однако, все компоненты VCL придерживаются следующих соглашений:
• Значения свойств хранят члены данных объекта.
• Идентификаторы членов данных, хранящих значения свойств, образуются добавлением префикса F к имени этого свойства. Так исходные значения свойства Width
компоненты TControl хранит член данных под именем
FWidth.
• Идентификаторы членов данных, хранящих значения свойств, должны быть объявлены как private. При этом компонента, объявившая эти свойства, имеет к ним доступ, а пользователь данной компоненты и ее производные - нет.
• Производные компоненты должны использовать само наследованное свойство, не пытаясь осуществить прямой доступ к памяти внутренних данных.
Целесообразность изложенных соглашений поддерживает простой принцип ООП: только методы, реализующие свойство, имеют право доступа к своим значениям. Если какому-то другому методу или компоненте понадобилось изменить эти значения, они должны осуществлять это посредством данного свойства, а не обращаясь напрямую к его внутренним данным.
Прямой доступ является простейшим способом обращения к значениям свойств. Атрибуты read и write объявления свойства указывают, что чтение или присваивание значений внутренним членам данных свойства происходит непосредственно, без вызова соответствующих методов. Прямой доступ чаще всего используется для чтения значений свойств. При этом побочный эффект не возникает, однако данное свойство будет представлено в окне Инспектора объектов.
Методы чтения и записи замещают имена членов данных в атрибутах read и write объявления свойства. Вне зависимости от конкретной реализации методов, они должны быть объявлены как приватные, а производные компоненты должны использовать наследованные методы для чтения и записи значений свойства. Объявление методов приватными защищает пользователя производной компоненты от случайного вызова неадекватных методов, модифицирующих свойства не так, как ожидалось.
Значение свойства по умолчанию представляет собой именно то начальное значение инициализации свойства, которое устанавливает конструктор данной компоненты. C++Builder использует объявленное значение свойства по умолчанию default, чтобы определить, следует ли сохранять свойство в файле формы с расширением .dim (если атрибут stored явно не запрещает это).
Листинг 6.3 объявляет компоненту с единственным свойством IsTrue, имеющим значение по умолчанию
true, а также конструктор, который устанавливает это значение при инициализации компонентного объекта. Заметим, что если свойство имеет значение по умолчанию false, то не нужно явно устанавливать его в конструкторе, поскольку все объекты (включая компоненты) всегда инициализируют свои члены данных значением 0, т.е. false.
class TMyComponent : public TComponent { private:
Boolean FIsTrue;
public:
virtual _fastcall TMyComponent(TComponent* AOwner);
_J?ublished:
_property Boolean IsTrue =
{ read=FIsTrue, write=FIsTrue, default=true };
};
_fastcall TMyComponent::
TMyComponent (TComponent* AOwner) : TComponent (AOwner) {
FIsTrue = true;
}
Листинг 6.3. Установка конструктором значения свойства по умолчанию
6.3.2.2 Свойства обеспечивают доступ к членам данных
Существует два способа, посредством которых свойства обеспечивают доступ к внутренним членам данных компонент: прямой или косвенный посредством методов чтения/записи.
class _declspec(delphiclass) TCustomEdit;
class _declspec(pascalimplementation) TCustomEdit :
public Controls::TWinControl { private:
int FMaxLength;
void _fastcall SetMaxLength(int Valued-protected:
property int MaxLength = { read=FMaxLength, write=SetMaxLength, default=false };
};
Листинг 6.4. Способы доступа свойства к членам данных.
Листинг 6. 4 содержит код объявления компоненты TCustomEdit, взятый из VCL. TCustomEdit — это базовый класс для таких компонент редактирования как, TEdit и TMemo.
TCustomEdit имеет внутренний член данных FMaxLength типа
int, который определяет максимальное значение длины символьной строки, которую пользователь может ввести в данный элемент управления. Вместо прямого присваивания FMaxLength этого значения, выполняется присваивание свойству MaxLength, которое обеспечивает доступ к члену FMaxLength.
Определение свойства MaxLength содержит тип, имя свойства и объявления атрибутов: прямого чтения члена данных FMaxLength,
косвенного присваивания свойству
MaxLength посредством метода записи SetMaxLength и значения по умолчанию 0.
6.3.2.3 Методы записи и чтения свойств
Метод записи имеет единственный параметр того же типа, что и свойство, и не возвращает значения. По соглашению, название функции образуется из слова "Set", за которым следует имя свойства. Например, метод записи свойства MaxLength имеет имя SetMaxLength и тип void. Метод записи свойства присвоит значение своего параметра соответствующему члену данных. Одна из причин использования метода записи — вызвать побочный эффект как результат операции присваивания свойству.
Листинг 6.5 представляет пример реализации ранее объявленного метода записи SetMaxLength.
void TCustomEdit::SetMaxLength(int Value) {
if (FMaxLength i= Value) {
FMaxLength = Value;
if (HandleAllocated) SendMessagefHandle, EM_LIMITTEXT, Value, 0);
} }
Листинг 6.5. Пример реализации метода записи.
Метод SetMaxLength проверяет, не присваивается ли свойству то значение, которое уже в нем хранится. Если нет, то новое значение присваивается члену данных FMaxLength. Кроме того, метод выдает сообщение EM_LIMITTEXT Windows в то окно, которое инкапсулирует компонента TCustomEdit. По этому сообщению устанавливается верхний предел длины текста, который пользователь может ввести в данный элемент управления. Последнее действие представляет собой простейший вариант побочного эффекта, вызываемого методом записи свойства.
Метод чтения представляет собой функцию без параметров (или с параметрами индексов для свойства типа массив), которая возвращает типизированное значение свойства. По соглашению, название функции образуется из слова
"Get", за которым следует имя свойства. Например, метод чтения свойства MaxLength имеет имя GetMaxLength и возвращает значение типа int.
Метод чтения может осуществлять преобразование типа члена данных. Такие операции производят, например, методы AsString, AsFloat и Aslnteger, реализованные в компоненте TField.
6.3.3 Переопределение свойств
Все компоненты наследуют свойства своих предшественников, причем абстрактные базовые классы обычно объявляют свои свойства преимущественно в секциях public или protected. Чтобы такие свойства стали доступными пользователям производных компонент (как на стадии проектирования, так и во время выполнения программы), они обязаны переопределить их с ключевым словом _published.
Как видно из предыдущего примера, свойство MaxLength было определено в секции protected базовой компоненты
TCustomEdit. Листинг 6.6 содержит переопределение свойства
MaxLength как _published в производной компоненте TEdit.
class TEdit : public TCustomEdit { published:
property int MaxLength = { nodefault } ;
// Другие объявления };
Листинг 6.6. Переопределение свойства в производном классе.
Такое переопределение только снимает ограничения свойства, т.е. вы можете переопределить protected свойство как public, но не можете "спрятать" свойство, объявив его как protected. При переопределении свойства достаточно указать его имя, однако вы можете изменить значения атрибутов stored и default. Отметим также, что свойство MaxLength теперь вообще не имеет значения по умолчанию, хотя в унаследованном свойстве оно было задано.
Другая принципиальная особенность свойств заключается в том, что их можно изменять во время работы программы с помощью Инспектора объектов.
Правила видимости, объявленные ключевыми словами private, protected и public, действуют на свойства так же как и на обычные члены данных и методы. Единственное отличие объявлений, сделанных в секции _published, от объявлений в секции public, проявляется в том, что во время работы программы Инспектору объектов передается информация RTTI (Run-Time Type Identification) о типах членов данных и свойств.
6.3.4 Типы свойств
Свойство может быть любого типа, который способна возвратить функция (так как реализация свойства может возлагаться на функцию). Разные типы свойств по-разному представлены в окне Инспектора объектов и определяют разные варианты их редактирования, предлагаемые Инспектором. Более того, мы уже знаем, что некоторые свойства имеют собственные редакторы. .
Правилами языка C++ устанавливаются следующие обобщенные группы типов компонентных свойств:
| Тип свойства | Действия Инспектора объектов |
| Simple | Простые числовые, символьные и строчные свойства показываются Инспектором в виде чисел, символов или символьных строк, соответственно. Можно непосредственно вводить и редактировать значения простых свойств. |
| Enumerated | Свойства перечисляемого типа (в том числе булевы) показываются Инспектором в виде значений, определенных в исходном тексте программы. Можно выбирать возможные значения из выпадающего списка перечислений. |
| Set | Свойства типа множества показываются Инспектором в виде элементов множества. При расширении множества следует обращаться с каждым его элементом как с булевым значением: true, если элемент принадлежит множеству, или false в противном случае. |
| Object | Свойства, которые сами по себе являются объектами, обычно обслуживаются своими собственными редакторами. Инспектор позволяет индивидуально редактировать те объектные свойства, которые объявлены какpublished. Объектные свойства должны быть производными от TPersistent |
| Array | Свойства типа массив должны обслуживаться своими собственными редакторами свойств. Инспектор не имеет встроенных средств для редактирования таких свойств. |
6.3.4.1 Свойства типа множество
Как известно из главы 3, C++Builder объявляет несколько шаблонных классов для встроенных типов Delphi, которых нет в языке C++.

В частности, типы стиля шрифта определяются следующим образом:
enum TFontStyle
{ fsBold, fsltalic, fsUnderline, fsStrikeOut } ;
typedef Set
TFontStyle является перечисляемым типом. TFontStyles определен как множество — неупорядоченная коллекция типа
TFontStyle. Инспектор объектов позволяет переключать булевы значения элементов множества, выбирая значения false или
true. Рис. 6.4 показывает пример манипуляций свойством Style типа множество в окне Инспектора. Это свойство, определяющее основные характеристики шрифта, имеют многие компоненты, в том числе, сама форма.
Рис. 6.4. Свойство Style.
6.3.4.2 Свойства типа массив
Эти свойства имеют множественные элементы, каждый из которых соответствует некоторому индексному значению. Например, свойство Lines стандартной компоненты TMemo представляет собой индексируемый список (массив) текстовых строк, составляющих многострочное поле редактирования. В данном случае свойство Lines предоставляет пользователю естественный доступ к указанному элементу массива (строке) в большом тексте.
Листинг 6.7 содержит объявление свойства String и метода чтения GetNumberSize,
возвращающего индексируемую строку массива.
class TDemoComponent : public TComponent { private:
String _fastcall GetNumberSize(int Index);
public:
property String Number[int Index] = { read=GetNumber };
// Другие объявления };
String _fastcall TDemoComponent::GetNumberSize(int Index) {
String Result;
switch (Index) {
case 0: Result = "Zero"; break;
case 100: Result = "Medium"; break;
case 1000: Result = "Large"; break;
default: Result = "Unknow size";
) : .. return Result;
}
Листинг 6. 7. Свойство типа массив и его метод чтения.
Объявление свойств типа массив имеет следующие особенности:
• Объявление свойства включает один или несколько индексов, любого типа (по числу размерностей массива). В этом состоит первое отличие от обычных массивов, где индекс всегда имеет тип int.
• Доступ к элементам массива реализуется посредством методов чтения/записи, которые имеют дополнительные параметры - индексы массива, перечисляемые в том же порядке, что и при объявлении методов. В этом состоит второе отличие от обычных массивов, где допускаются ссылки не только на отдельные элементы, но и на весь массив.
6.4 События
События представляют собой средства, с помощью которых компонента сообщает пользователю о том, что на нее оказано некоторое предопределенное воздействие. Типичные простые события — нажатие кнопки на форме или клавиши на клавиатуре. Вкладка "События" (Events) Инспектора объектов позволяет получить доступ к событиям выбранной компоненты.
6.4.1 Зачем нужны события?
В общих словах событие определяют как механизм связи происшествия с некоторым кодом. Более точно событие - это closure, указатель на специфический метод в специфическом экземпляре класса.
С точки зрения прикладного программиста, событие представляет собой имя, которому можно присвоить некоторый вызываемый метод. Например, экземпляр компоненты TButton - кнопка Buttoni имеет методы для события OnClick. По умолчанию C++Builder генерирует обработчик события с именем ButtonlClick и присваивает его событию OnClick. Когда происходит нажатие кнопки, объект вызывает метод, присвоенный событию OnClick, в данном случае, ButtonlClick.
Таким образом, прикладной программист воспринимает событие как способ указания, к какому из написанных им методов должна обращаться программа, когда данное событие происходит. С точки зрения разработчика компонент, в понятии события заключено значительно большее. Автор компоненты обязан предусмотреть программный интерфейс, к которому можно подключить методы, вызываемые при определенных воздействиях на компоненту. Ваша компонента предоставляет "разъемы", в которые прикладной программист сможет вставлять специфические коды реакции на события.
6.4.2 Определение событий
Формально C++Builder определяет событие как типизированный указатель на метод в специфическом экземпляре класса:
<тип> (_closure * <имя метода>) (<список параметров>)
Для разработчика компонент closure представляет собой некоторую программную заглушку: когда пользователь определяет реакцию на некоторое событие, место заглушки занимает его обработчик, который вызывается вашей программой при возникновении этого события.
Когда пользователь выполняет присваивание некоторому событию, происходит присваивание не просто метода с конкретным именем, а метода в специфическом экземпляре класса. В качестве экземпляра класса, указатель которого this передается как скрытый параметр, обычно (но не всегда) выступает форма, которая содержит данную компоненту.
все символы к верхнему регистру. Для этого надо определить следующий обработчик события нажатия клавиши:
void _fastcall TFormI::EditlKeyPress
(TObject *Sender, char SKey) {
Key = UpCase(Key) ;
)
Передача адресных аргументов может также использоваться для переопределения поведения обработчика события по умолчанию.
Присваивание обработчиков всем возможным событиям вашей компоненты вовсе не обязательно. Этот принцип оказывает существенное влияние на разработку ваших компонент и их событий. Очевидно, работа вашей компоненты не должна нарушаться из-за того, что пользователь просто не предусмотрел обработчика какого-то события.
При разработке компонент нужно учитывать следующие аспекты обработчиков событий:
• Прикладные программисты не обязаны обрабатывать события. Различные события возникают практически постоянно при работе любого приложения Windows. Простое смещение курсора по компоненте вызывает передачу многочисленных сообщений Windows данной компоненте о передвижении мыши, которые компонента транслирует в события OnMouseMove. Если поведение компоненты не зависит от манипуляций мышью, то в большинстве случаев программа просто не обращает внимание на такие события.
• Прикладные программисты могут написать любой код обработки события. Компоненты VCL реализуют свои события так, чтобы свести к минимуму риск неверной реакции вследствие логических ошибок в обработчике события. Конечно, невозможно защититься от всех ошибок, однако можно, например, перед вызовом обработчика выполнить инициализацию всех структур данных, чтобы пользователи не получали неопределенной информации.
6.4.2.3 Стандартные события
Все компоненты VCL наследуют большинство общих сообщений Windows стандартные события. Такие события встроены в защищенные секции компонент, поэтому пользователи не могут подсоединять к ним свои обработчики.
Существует две категории стандартных событий: определенные для всех компонент и определенные только для оконных компонент (стандартных и оригинальных). Все компоненты наследуют от базового абстрактного класса Tcontrol следующие стандартные события:
OnClick______OnDragDrop___OnEndDrag____OnMouseMove
OnDblClick____| OnDragOver___| OnMouseDown | OnMouseUp
В дополнение к этим событиям, оконные компоненты наследуют от базового абстрактного класса TWinControl
еще и следующие стандартные события:
| OnEnter | OnKeyDown | OnKeyPress |
| OnKeyUp | OnExit |
class TMyControl : public TCustomControl {
_published:
_property OnClick; // OnClick
стало видимым Инспектору };
Листинг 6.9. Переопределение стандартного события
Все стандартные события имеют соответствующие защищенные динамические методы, унаследованные от TControl,
имена которых образованы от названия события без частицы "On". Например, события OnClick вызывают метод Click.
Как правило, вы сначала обращаетесь к наследованному методу, разрешая пользовательскому обработчику события произвести свои действия перед тем, как сработает код вашего переопределения. Предположим, вы пишете новую компоненту, в которой хотите модифицировать реакцию на щелчки мышью. Вместо того, чтобы присвоить соответствующий обработчик события OnClick, как это сделал бы прикладной программист, вы переопределяете защищенный метод Click (Листинг 6.10).
void _fastcall TMyControl::Click() {
// Стандартное обслуживание, включающая вызов обработчика
TWinControl::Click() ;
// Далее следует ваш код переопределения метода
}
Листинг 6.10. Переопределение защищенного метода.
6.4.2.4 Собственные события
Необходимость в определении совершенно новых событий возникает довольно редко. Скорее всего окажется достаточным переопределить обработку уже существующих событий. Если вы все же придумали компоненту с совершенно оригинальным поведением, вам придется ввести и новые события.
Существует две причины возникновения событий: воздействие пользователя на компоненту и изменение ее состояния. События в результате воздействия пользователя почти всегда вызываются сообщениями Windows, указывающими, что на это воздействие может потребоваться некоторая реакция. События в результате изменения состояния компоненты могут также соотносится с сообщениями Windows (изменение фокуса или запрещение компоненты), хотя чаще они являются следствием изменений свойств компоненты. Разработчик компонент должен полностью контролировать причины возникновения собственных событий, чтобы пользователи знали как их использовать.
Определение события проходит через четыре этапа, для реализации которых вам необходимо:
1. Знать, какое действие вызывает событие. Для некоторых событий ответ очевиден. Например, при нажатии левой кнопки мыши Windows посылает сообщение
WM_LBUTTONDOWN. Принимая это сообщение, компонента вызывает метод
MouseDown, который в свою очередь обращается к обработчику события, который пользователь подсоединил к событию OnMouseDown.
Некоторые события менее очевидно связаны с внешними воздействиями. Так линейка прокрутки имеет событие OnChange, которое возникает по разным причинам - в результате щелчков мышью, нажатия управляющих клавиш или изменений в других родственных компонентах.
2. Определить тип обработчика события. Обработчик события может просто опознавать событие нотификации
(объектного типа TNotifyEvent) или содержать обработку событий специфического типа. TNotifyEvent задает единственный аргумент - указатель объекта, благодаря которому обработчик "узнает" компоненту, сгенерировавшую это событие. Например, щелчки мышью генерируют события нотификации. Обработчик этих событий знает только то, на какой компоненте пользователь щелкнул мышью. Передача дополнительных адресных аргументов используется, например, обработчиком события типа TKeyPressEvent,
вызываемого при нажатии любой клавиши на клавиатуре.
3. Объявить тип и свойство для события. Инспектор объектов определяет, что данное свойство является событием, обнаруживая тип свойства
closure, и представляет его на вкладке События. Давайте событиям значимые и описательные имена, по которым пользователь догадается о том, что это за событие. C++Builder рекомендует начинать имена событий с частицы "On.".
4. Создать виртуальный метод, который вызывает обработчик события пользователя и обеспечивает обработку по умолчанию. Правильное функционирование вашей компоненты не должно зависеть от конкретной реакции, которую пользователь заложил в обработчик события. В частности, пустой обработчик события так же допустим, как и его отсутствие. Более того, пользователь имеет право переопределить обработку по умолчанию. Чтобы предоставить ему такую возможность, передайте в обработчик дополнительный адресный аргумент, значение которого можно проверять при возврате. При этом пустой обработчик события не изменит значения аргумента, и обработка по умолчанию всегда будет иметь место после возврата из пустого обработчика.
6.4.2.5 События и сообщения Windows
Опытный программист определенно заметит сходство некоторых событий C++Builder и сообщений Windows. В следующей таблице приведен краткий список событий объекта TForm и соответствующих сообщений Windows, которые вы использовали бы в обычной программе на языке С:
| Событие VCL | Сообщение Windows |
| OnCreate | WM CREATE |
| OnClose | WM DESTROY |
| OnReSize | WM SIZE |
| OnActivate, OnDeactivate | WMACTIVATE |
| OnShow, OnHide | WM SHOWWINDOW |
| OnKeyDown | WM KEYDOWN |
| OnKeyUp | WM KEYUP |
| OnKeyDown | WM KEYDOWN |
| OnMouseDown | WM LBUTTONDOWN, WM RBUTTONDOWN |
| On Mouse Up | WM LBUTTONUP,WM RBUTTONUP |
| OnMouseMove | WMMOUSEMOVE |
| OnDblClkk | WM LBUTTONDBLCLK, WMRBUTTONDBLCLK |
| OnPaint | WM PAINT |
С другой стороны, некоторые события VCL расширяют функциональность встроенных сообщений Windows. Так события OnDragOver и OnDragDrop Объекта TForm просто и прямолинейно реализуют операции перетаскивания
(drag-and-drop) в вашей программе. Большинство компонент на вкладках Standard и
Win95 Палитры компонент лишь специальным образом обрамляют известные элементы управления Windows. Компоненты на других вкладках представляют совершенно новые элементы управления (и события) для особых областей функционирования.
Компоненты вкладок Standard и Win95 инкапсулируют стандартные элементы управления
Windows. За взаимодействие между пользователем и программой, которое ранее поддерживалось реакцией на сообщения
Windows, теперь отвечают обработчики событий компонент VCL. Однако, в некоторых ситуациях возникает необходимость "взять на себя" те сообщения
Windows, которые не имеют соответствующих событий VCL или не адекватны им. Для таких случаев в VCL предусмотрена методика ООП, обеспечивающая непосредственный отклик на события Windows,
подобно средствам библиотек базовых классов OWL или MFC. Эта методика, реализуемая с помощью макросов BEGIN_MESSAGE_MAP, MESSAGE.HANDLER и END_MESSAGE_MAP, весьма трудоемка и здесь не рассматривается. Поэтому предварительно тщательно просмотрите имеющиеся в VCL компоненты, которые могут содержать подходящие события.
6.4.3 Обработка событий
Событиям программист ставит в соответствие коды обработчиков событий, выполняющиеся всякий раз, когда соответствующее событие происходит. Некоторым событиям в Инспекторе объектов предписаны выпадающие списки, содержащие опции стандартных функций обработки событий, объявления и вызов которых C++Builder генерирует автоматически.

Рис. 6.5 показывает вкладку Events Инспектора объектов с выбранным событием OnClick компоненты TButton. Это событие возникает при нажатии кнопки Button 1. Если дважды щелкнуть мышью на поле события, C++Builder автоматически сгенерирует соответствующее объявление метода в файле объявлений Unitl.h, и Редактор кода переведет вас в нужную позицию в кодовом файле Unitl.cpp, где вы сможете написать код, реализующий этот метод.
Нижеследующий пример иллюстрирует процесс создания кода обработчика события OnClick (Нажата кнопка Button 1) для компоненты
TButton.
Опытному программисту уже стало понятно, что событие будет содержать указатель функции обработки этого события.
Чтобы связать ваш собственный обработчик с событием OnClick компоненты TButton воремя выполнения программы, вы должны сначала создать метод для обслуживания этого события. Как и любой метод, он должен принадлежать существующему объекту — форме, которая владеет компонентой TButton и на которой она размещена.
Рис. 6.6 показывает окно Редактора кода с файлами Unitl.h и
Unintl.cpp программного модуля, реализующего обработку события OnClick.
Объявленный метод становится обработчиком события, когда событию Buttonl->OnClick присваивается указатель некоторого метода MyOnClickEvent.
Указанное присваивание можно также сделать динамически при работе программы в обработчике события OnCreate вашей формы. Результат будет таким же, как и при создании обработчика событий с помощью Инспектора объектов на этапе проектирования, за исключением того, что в этом случае C++Builder сохраняет связь события с его обработчиком в ресурсном файле с расширением .dfm. При запуске приложения VCL считывает форму из ресурсного файла и динамически присваивает значения свойств и событий компонент, размещенных на форме.

Рис. 6.6. Определение метода обработки события OnClick.
Когда вы определяете методы для обработчиков событий, эти методы должны быть того же типа, что и типы свойства и членов данных, на которые ссылается свойство. Например, событие OnClick ссылается на внутренний член данных FOnClick функционального типа TNotifyEvent.
6.5 Методы
Компонентные методы ничем не отличаются от других объектных функции-членов. Хотя
C++Builder не вводит никаких специальных ограничении на оформление компонентных методов, имеется ряд правил, которых стоит придерживаться:
1. Минимизируйте число методов, которые вызывает прикладной программист, чтобы использовать вашу компоненту. Многое из того, что вы намеревались реализовать в виде методов, вероятно, лучше инкапсулировать в свойства компоненты. В отличие от свойств, методы работают только во время исполнения программы.
2. Избегайте взаимной зависимости методов, максимально изолируя методы друг от друга. Нельзя требовать, чтобы методы выполнялись в определенном порядке. Никакой метод не должен переводить компоненту в такое состояние, когда вызовы других методов станут запрещенными, т.к. смогут нарушить нормальное функционирование компоненты.
3. Придерживайтесь соглашения об именах методов. Названия должны быть описательными, т.е. включать глагол действия и отражать возвращаемое значение (например, метод PasteFromClipboard передает в компоненту данные из доски объявлений, а метод GetHorizontalPosition возвращает горизонтальную позицию некоторой величины).
4. Правильно защищайте методы. Все методы (включая конструкторы и .деструкторы), к которым имеют право обращаться пользователи вашей компоненты, следует объявлять как public. Все методы, к которым имеют право обращаться производные объекты вашей компоненты, следует объявлять как protected, что запрещает пользователям преждевременно вызывать методы, данные для которых еще не созданы. Только те методы, которые реализуют доступ к свойствам компоненты, должны быть объявлены как private, поскольку пользователи оперируют со значениями свойств напрямую.
5. Объявляйте методы виртуальными, когда хотите обеспечить полиморфное поведение переопределенных версий в разных классах.
6.5.1 Вызовы статических методов
Все объектные методы определяются как статические (static) по умолчанию, если вы не объявили их иначе. Вызовы статических методов выглядят как вызовы обычных функций: определяемый при компиляции адрес метода связывает его с объектом. Поэтому обращения к статическим методам выполняются быстрее, нежели к виртуальным, адреса которых неизвестны на этапе компиляции.
class TFirstComponent : public Tcomponent
{
public:
void _fastcall Move() ;
void _fastcall Flash();
};
class TSecondComponent : public TPirstComponent { public:
void _fasfccall Move(); //
отличен от наследованного метода
int _fastcall Flash(int HowOften); //
тоже отличается };
Листинг 6.11. Переопределение статических методов.
Адреса статических методов не меняются при наследовании, а значит, статические методы не следует переопределять - они призваны выполнять одинаковые действия во всех родственных компонентных объектах. Листинг 6.11 показывает, что переопределение статических методов в производном классе по сути заменяет родительские методы.
6.5.2 Вызовы виртуальных методов
Вызов виртуального метода записывается точно так же, как вызовы других функций, однако механизм обращения слегка усложняется, делая его более гибким. Поскольку виртуальные методы могут быть переопределены в производных классах, их адреса невозможно определить на стадии компиляции. Фактические адреса виртуальных методов становятся известными только во время выполнения программы.
Ключевое слово virtual в объявлении метода создает запись в таблице виртуальных методов объекта (VMT). При выполнении программы эти записи преобразуются в фактические адреса всех виртуальных методов данного компонентного класса. Когда вы производите новый объектный класс, он наследует VMT всех своих предшественников, добавляя к таблице дополнительные виртуальные методы, объявленные в производном классе. Кроме того, производный класс может переопределять наследованные виртуальные методы. Любой конструктор компоненты всегда должен быть виртуальным.
6.6 Иерархия классов VCL
Ранее в этой главе мы ознакомились с характеристиками четырех базисных типов компонент: стандартные, оригинальные, графические и невидимые. Теперь покажем место и назначение этих типов в иерархии объектов.
Рис. 6.7 показывает ключевые классы в иерархической структуре, от которых произведены все компоненты VCL. Каждый объект представляет некоторый набор методов, событий и свойств и имеет специальное назначение.
TObiect
TPersistent
TComponent
TControl
TGraphicControl TWinControl
TCustomControl
Рис. 6.7. Иерархия ключевых базовых классов VCL.
Подобно тому как TObject является базовым классом для всех порождаемых классов, TComponent является базовым классом для всех порождаемых компонент. $
Невидимые компоненты произведены от класса TComponent. Графические компоненты, не ассоциированные с оконными элементами управления, произведены от класса TGraphicControl.
Являясь оконными элементами, компоненты стандартного управления произведены непосредственно от класса TWinControl, а оригинальные компоненты — косвенно от класса
TCustomControl, восходящего к TWinControl. Именно на уровне TWinControl
и вводится "оконный дескриптор"
(window handle). Рис. 6.8 S продолжает иерархическую структурную схему компонентных классов VCL.
* TButtonControl
о TButton
+ TBitBtn о
TCustomCheckBox
+ TCheckBox
+ TDBCheckBox о
TRadioButton
* TCustomComboBox о TComboBox о
TDBComboBox о
TDriveComboBox о
TFilterComboBox
* TCustoinControl
о TCustomGrid
+ TCustomDBGrid + TDBGrid + TDBLookupList
+ TPopupGrid + TCustomOutline
+ TOutline + TDrawGrid
+ TStringGrid о
TCustomGroupBox
+ TCustomRadioGroup + TDBRadioGroup + TRadioGroup
+ TGroupBox о
TCustomPanei
+ TDBNavigator
+ TPanel о TDBImage о TDBLookupControl
+ TDBLookupComboBox
+ TDBLookupListBox
+ TPopupDataList о
THeader о THintWindow о TMediaPlayer о TNotebook о TOleContainer о TPage о TScroller о TTabSet
* TCuatonEdit
о TCustomMaskEdit
+ TDBEdit
+ TInplaceEdit
+ TMaskEdit о
TCustomMemo
+ TCustomRichEdit + TRichEdit
+ TDBMemo
+ TMemo о
TDBLookupCombo о TEdit
* TCustomHotKey о THotKey
* TCuafcomListBox о TDBListBox о
TDirectoryListBox о
TFileListBox о
TListBox
* TCuetomLietView о TListView
* TCuetomTabControl о TPageControl о TTabbedNotebook о TTabControl
* TCustomTreeView о TTreeView
* TCustomUpDown 0 TUpDown
* TDBCtrlGrid
* TDBCtrlPanel
* THeaderControl
* TOleControl
* TProgressBar
* TScrollBar
* TScrollingWinControl
о TForm
+ TDesignWindow + TInputReqDialog + TLoginDialog + TPasswordDialog
о TScrollBox
* TStatueBar
* TTabPage
* TTabSheet
* TTrackBar
Puc. 6.8. Дерево производных компонент от TCustomControl и TWinConlrol.
6.6.1 TObject
TObject является базовым классом для всех прочих порождаемых классов. TObject инкапсулирует общее для всех объектов системы
C++ Builder функциональное поведение, обусловленное методами, которые обеспечивают:
• Способность конструктора создавать, а деструктора разрушать объект-экземпляр класса в динамической памяти. Конструктор
TObject возвращает указатель на создаваемый объект.
• Информацию RTTI об имени, типе производного объекта и его свойствах, которые объявлены как _published.
• Поддержку обработки сообщений.
Большинство этих методов предназначены для внутреннего использования средой C++Builder, поэтому не следует прямо обращаться к ним из вашей программы. Часть методов TObject объявлены как статические (с ключевым словом static). Это означает, что вам не нужно создавать экземпляр данного класса для того, чтобы обратиться к его статическим методам.
Все компоненты должны порождаться непосредственно от класса TComponent или от его потомков.
TComponent, будучи в свою очередь потомком TObject, наследует его члены данных, методы и свойства.
Используйте TObject для объявления простых объектов, которые не являются компонентами и не нуждаются в поточности и присваивании. Среди полезных не компонентных классов отметим TStringList, TIniFile и TPrinter.
6.6.2 TPersistent
Класс TPersistent непосредственно произведен от
TObject. Этот абстрактный класс не определяет никаких специальных свойств или событий, однако его производные приобретают особые способности присваивания и поточности.
TPersistent определяет ряд поточных методов, используемых разработчиками компонент, которые могут быть перегружены производными компонентами:
• Assign позволяет присваивать значения свойствам.
• AssignTo позволяет присваивать содержимое одного объекта другому (например, как делает это производный от TPersistent класс TClipboard).
• DefineProperties позволяет определить процедуру загрузки и сохранения в потоке особых дополнительных свойств. По умолчанию сохраняются только свойства, объявленные как _published.
6.6.3 TComponent
Класс TComponent
непосредственно произведен от
TPersistent. Как уже было сказано, все компоненты являются производными от TComponent и могут находится в его владении. TComponent инкапсулирует общее для всех компонент функциональное поведение, обусловленное свойствами и методами, которые обеспечивают:
• Перенос на форму из Палитры компонент и манипуляции в окне Редактора
форм.
• Способность владения и обслуживания других компонент.
• Специальные характеристики поточности, с которыми может манипулировать Инспектор объектов на этапе проектирования.
• Возможность манипулирования некоторыми невидимыми компонентами на стадии проектирования.
Класс TComponent
определяет ряд свойств, которые придают объекту особую функциональность:
Свойство Назначение
Owner_______| Ссылается на владельца компоненты.
ComponentCount | Число компонент в перечне, которыми владеет данная компонента.
Componentlndex Индекс компоненты в перечне, начиная с 0.
Components | Свойство, содержащее перечень компонент, которыми владеет данная компонента.
ComponentState Текущее состояние компоненты.
ComponentStyle Стиль, определяющий поведение компоненты.
Name Имя компоненты.
Tag | Свойство типа int, которое не имеет предопределенного значения и может содержать любые данные или указате-ли, по усмотрению программиста.
Designlnfo | Используется Редактором форм.
Класс TComponent
определяет ряд методов, которые придают объекту право владения другими компонентами и возможность доступа к ним посредством Инспектора объектов:
• Destroying и
DestroyComponents устанавливают атрибуты данной компоненты и компонент, которыми она владеет, в состояние, указывающее на то, что они подлежат уничтожению.
• HasParent возвращает булево значение, указывающее на наличие родителя компоненты. Обращаться к этому методу следует до ссылок к родителю данной компоненты. Отметим, что наличие владельца компоненты не идентифицируется.
• insertComponent добавляет компоненту, передаваемую в качестве параметра, к перечню компонент, которыми владеет данная компонента, а RemoveComponent удаляет компоненту из этого перечня.
• FindComponent возвращает указатель экземпляра компоненты, о которой известно только имя, но неизвестна ссылка на владельца. Допустим, что форма содержит экземпляр компоненты TEdit с именем Editl. Чтобы получить указатель на экземпляр Editl и адресовать его текст, используйте следующий код:
void_fastcall TFormI::ButtonlClick(TObject *Sender)
{
TEdit * Editlnstance = FindComponent("Editl");
(TEdit *)(FindComponent("Editl"))->Text = "Hello";
}
He создавайте экземпляров класса TComponent. Используйте TComponent в качестве базового класса при создании невидимых компонент.
6.6.4 TControl
Класс TControl
определяет общие для видимых компонент члены данных, методы и события. Поскольку элементы TControl обладают способностью отображать себя, некоторые его свойства оперируют с положением, размером и видом объекта (Top, Left, Width, Height и Cursor, Hint), а другие свойства относятся к параметрам области клиента (ClientRect, ClientWidth и
ClientHeight).
TControl также вводит свойства, устанавливающие видимость, доступность” цвет и шрифт элементов управления (Visible, Enabled, Color и Font). Свойств” Text и
Caption обеспечивают установку редактируемых текстов и названий.
Наличие свойства Parent (Родитель), содержащего соответствующую ссылку, обусловлено возможностью класса TControl иметь родителя. Этот родитель может быть производным от TWinControl, поскольку родители обязаны быть оконными элементами управления.
TControl содержит ряд событий, возникающих при манипуляциях мышью над, видимыми элементами управления (OnClick, OnDblClick, OnMouseDowit, OnMouseMove, OnMouseUp, OnDragOver, OnDragDrop и OnEndDrag).
Поскольку TControl редко используется непосредственно, его события большинство свойств объявлены в секции protected. Разработчики производных компонент могут, таким образом, выбирать, какие свойства и события перенести в секцию
public или _published, расширяя тем самым права доступа.
Большинство компонент являются производными от TWinControl или TGraphicControl. Эти базовые классы рассматриваются в следующих параграфах.
6.6.5 TWinControl
Класс TWinControl инкапсулирует оконные элементы управления с дескрипторами. Некоторые производные от TWinControl (компоненты TEdit, TListBox и TComboBox)
инкапсулируют стандартные элементы управления Windows — поля редактирования, простые и комбинированные списки и т.д. Поэтому вам не придется манипулировать с ними посредством стандартных функций Windows API, a пользоваться свойствами и методами, предоставляемыми самими компонентами.
Производные компоненты от TWinControl обладают тремя основными характеристиками: они имеют оконные дескрипторы, способны принимать фокус ввода и могут являться родителями других элементов управления. Поэтому многие свойства TWinControl предназначены для изменения фокуса, обслуживания событий клавиатуры и отображения потомков компоненты:
| Свойство | Назначение |
| Brush | Управляет цветом и орнаментом канвы, используемой при заливке графических фигур и фона. |
| Controls | Содержит список элементов управления, для которых TWinControl является родителем. |
| ControlCount | Содержит число элементов управления, для которых TWinControl является родителем. |
| Ctl3d | Определяет, требуется ли трехмерное отображение компоненты. |
| Handle | Ссылается на оконный дескриптор объекта Windows, который инкапсулирует TWinControl. Это свойство передается тем стандартным функциям Windows API, которые принимают дескриптор как параметр. |
| HelpContext | Задает номер контекстной справки, соответствующий некоторому окну в файле помощи с расширением .hip. Это свойство позволяет организовать контекстно-чувствительный поиск в справочной службе для отдельных компонент. |
| Showing | Указывает, видима или невидима компонента. |
| TabStop | Указывает, можно ли использовать клавишу табуляции для выбора нужной компоненты. |
| TabOrder | Определяет позиции табулятора выбора компонент. |
• Broadcast используется для рассылки сообщений всем потомкам TWinControl.
• CanFocus возвращает булево значение, которое определяет, может ли TWinControl принять фокус ввода. Например, компонента не сможет принять фокус, если ее свойство Visible имеет значение false.
• ContainsControl определяет, содержится ли данный элемент управления внутри класса TWinControl. Этот метод не сообщает о том, является ли данный элемент потомком по отношению к TWinControl. Например, внешний класс TWinControl может быть родителем другого элемента, и эта родительская преемственность может продолжаться далее. Однако, все внутренние элементы содержатся во внешнем классе TWinControl.
• ContrblAtPos возвращает ссылку на потомка, если элемент управления заключен в заданных координатах области клиента родителя. Таким образом можно найти относительное положение потомка по отношению к родителю.
• DisableAlign и
EnableAlign используются для временного запрещения или разрешения выравнивания компонент внутри
TWinControl.
• Focused возвращает значение true,
если TWinControl
находится в фокусе ввода, т.е. является активным элементом формы, на которой он размещен.
• HandleAl located возвращает значение true,
если элемент управления имеет оконный дескриптор. HandleNeeded создает новый дескриптор, если он еще не был создан. Аналогичное действие выполняется автоматически при прямом обращении к свойству Handle.
• InsertControl добавляет элемент управления к свойству Controls (типа массив), делая
TWinControl своим родителем. Лучший способ добавить потомка во время работы программы — просто присвоить ссылку на родителя свойству Parent. RemoveControl
удаляет элемент управления из массщ Controls.
• Invalidate и
Repaint выполняют перерисовку компоненты. Мето;
Repaint обрабатывает сообщение WMJPAINT, обращается к метол Update, который в свою очередь вызывает функцию Windows АP UpdaleWindow. PaintTo может использоваться для перерисовки содержимого TWinControl в область (device context) другого элемента управления. ReAlign вызывает повтор выравнивания компонент внутри
TWinControl. ScaleBy используется для масштабирования TWinControl в заданном процентном отношении к исходному размеру. ScrollBy можно использовать, если вам не нравится логика прокрутки TWinControl, принятая по умолчанию.
• SetBounds устанавливает свойства границ компоненты (Left, Top, Width, Height) для
TWinControl. Прямое изменение каждого из указанных свойств менее эффективно, поскольку всякий раз сопряжено с перерисовкой SetFocus активизирует TWinControl.
Другие методы, применяемые разработчиками компонент, предназначены для создания и уничтожения инкапсулированных
TWinControl окон и их дескрипторов: .
• CreateWnd создает оконный элемент управления, инкапсулированны TWinControl, посредством последовательного обращения к
CreateParams и CreateWindowHandle.
• CreateParams инициирует начальные значения всех оконных параметров Перегрузка этого метода позволяет менять оконные параметры, установлен ные по умолчанию.
• CreateWindowHandle создает оконный дескриптор путем обращения функции Windows API Create WindowEx.
• DestroyWnd уничтожает инкапсулированный оконный элемент
управления путем обращения к методу DestroyWindowHandle,
который в свою очередь обращается к функции Windows API Destroy Window.
TWinControl имеет события, вызываемые взаимодействием с клавиатурой i изменением фокуса (OnKeyDown, OnKey Press, OnKeyUp, OnEnter и OnExif).
Разрабатываемые компоненты редко происходят непосредственно от TWinControl. Лучше производить новые компоненты от общего класса TCustomControl, который предоставляет канву для рисования и обрабатывает сообщение WM_PAINT, или от некоторых более специализированных классов (TButtonControl, TCustomComboBox, TCustomEdit или TCustomListBox).
6.6.6 TGraphicControl
Производные от абстрактного класса
TGraphicControl, в отличие от TWinControl, не имеют оконного дескриптора, не могут принять фокус ввода и не могут являться родителями других элементов управления.
Производные TGraphicControl используются в тех ситуациях, когда вы хотите изобразить на форме текст или графику, не обращаясь к функциональным возможностям обычных оконных элементов управления. Отметим следующие достоинства такого подхода. Во-первых, TGraphicControl не пользуется системными ресурсами Windows, так как не требует оконного дескриптора. Во-вторых, метод рисования TGraphicControl исполняются немного быстрее за счет того, что перерисовка компоненты не связана с диспетчеризацией сообщений Windows, a реализуется процессом рисования, заложенным в родителе данного элемента.
Производные TGraphicControl имеют обработчики событий, вызываемые манипуляциями с мышью.
TGraphicControl возлагает на пользователя операции перерисовки. Этот класс содержит свойство Canvas (Канва), которое обеспечивает доступ к отведенной для рисования поверхности, и виртуальный метод Paint, который вызывается в ответ на сообщение WM_PAINT, принимаемое родительским элементом управления.
6.6.7 TCustomControl
Стандартные компоненты, как производные от TWinControl (например, TEdit и
TListbox), уже имеют способности собственного отображения, предоставленные инкапсулированными элементами управления Windows. А как создать оконную компоненту, которая отображает себя в виде, соответствующем оригинальным требованиям пользователя? Решение именно этой задачи и обеспечивает TCustomControl.
Будучи производным от класса
TWinControl, TCustomControl является оконным элементом управления и, следовательно, может принять фокус ввода. Разработанные компоненты могут быть произведены от
TCustomControl. Как и TGraphicControl, TCustomControl
содержит свойство Canvas (Канва), которое предоставляет возможность произвольного рисования на выбранной прямоугольной области. По существу, производные компоненты от TCustomControl предоставляют в ваше распоряжение виртуальный метод Paint, перегрузка которого позволит рисовать компоненты так, как вы пожелаете.
6.7 Схема разработки компонент
Процесс разработки собственной компоненты (мы будем называть ее TMyComponent) проходит через выполнение следующих этапов:
1. Создание модуля для новой компоненты.
2. Наследование производного класса от существующего базового компонентного класса.
3. Добавление нужных свойств, событий и методов.
4. Регистрация компоненты в C++Builder.
5. Отладка.
6. Инсталляция компоненты на Палитру.
7. Сохранение файлов компоненты.
Далее будет показано, как некоторые из перечисленных действий, программируемых далее вручную. Мастер компонент способен выполнить автоматически (создание файлов модуля, наследование компонентного класса, объявление нового конструктора и регистрация компоненты).
6.7.1 Создание модуля компоненты
Программный модуль состоит из двух файлов МуСотр.срр и MyComp.h, которые компилируются в объектный файл с расширением MyComp.obj C++Builder использует модули в различных целях - каждая форма и большинств компонент (или их логических групп) имеют свой собственный модуль. При paзработке компоненты вы либо создаете новый модуль для компоненты, или до бавляете ее к существующему модулю.
Чтобы создать модуль, выполните команду File | New и в открывшемся диалоre New Items выберите значок Unit. Чтобы добавить компоненту к существую щему модулю, выполните команду File | Open и в открывшемся диалоге отьшите ваш файл МуСотр.срр. Имея открытый модуль в окне Редактора кода, вы можете приступить к разработке компоненты. Для начала перечислите в МуСотр.h необходимые файлы фазы предкомпиляции и объявите производный класс ваше компоненты (Листинг 6.12).
^include
#include
#include
#include
class TMyComponent : public <
базовый компонентный класс >
{
};
Листинг 6.12. Заготовка файла MyComp.h модуля компоненты. 1
Пока мы создали компоненту, которая ничем не отличается от своего родите", ля. В следующем разделе описываются варианты наследования в зависимости от выбранного типа базового компонентного класса.
6.7.2 Наследование компоненты
Любая компонента является производной от общего прародителя TComponent, от его более специализированных наследников (таких как TControl или TGraphicControl) или от существующего компонентного класса. Компонеитой может стать практически любой элемент вашей программы, поведением которого вы хотите управлять на стадии проектирования.
| Цель | Базовый класс |
| Модификация существующей компоненты | Любая существующая компонента, например, TListBox, или абстрактный компонентный класс TCustomListBox. |
| Создание оригинальной компоненты | TCustomControl |
| Создание графической компоненты | TGraphicControl |
| Создание невидимой компоненты | TComponent |
6.7.2.1 Модификация существующих компонент
Простейший способ построить новую компоненту - это начать с существующей и изменить ее свойства. Вашей целью может являться добавление, исключение или замена значений по умолчанию некоторых свойств компоненты-образца. Вы можете использовать для этой цели любой подходящий абстрактный класс VCL, в название которого входит слово "Custom".
Например, вы можете произвести новую компоненту списка со специальными свойствами, которых нет в стандартном классе
TListBox. Поскольку нельзя прямо модифицировать TListBox, вы должны начать с ее ближайшего предшественника в иерархии классов. Для этой цели лучше всего подходит
TCustomListBox, которая реализует все мыслимые свойства производных компонент списка, однако не выставляет всех их в секции _published.
Наследуя вашу компоненту от одного из абстрактных типов (таких как TCustomListBox), вы всего лишь объявляете в секции _published те свойства, которые хотите включить в вашу компоненту, оставляя остальные в секции protected.
6.7.2.2 Создание оригинальных оконных компонент
С оконным интерфейсным элементом, видимым во время работы программы, пользователь обычно может взаимодействовать. Все оконные компоненты являются производными от базового класса TWinControl. Стандартный элемент оконного управления характеризует так называемый "оконный дескриптор" (window handle), который заключен в свойстве Handle.
Благодаря оконному дескриптору, Windows "узнает" данную компоненту, в частности, что она может принять фокус ввода и передавать оконный дескриптор функциям Windows API для идентификации рабочего окна.
Хотя вы можете создать оригинальный интерфейсный элемент (который не имеет существующих аналогов и никак не связан с ними), используя TWinControl как отправную точку, C++Builder предоставляет компоненту TCustomControl как раз для этой цели. TCustomControl - это специализированный оконный элемент управления, который упрощает рисование сложных визуальных изображений. Если вашей компоненте не нужно принимать фокус ввода, вы можете наследовать ее от графического элемента управления, который дает экономию системных ресурсов. Все компоненты стандартного оконного управления: кнопки, списки, поля редактирования (за исключением TLabel, которая никогда не принимает фокус ввода) - являются косвенными производными TWinControl.
6.7.2.3 Создание графических компонент
Оригинальные оконные и графические компоненты очень сходны. Однако в отличие от производных TCustomControl,
графические компоненты лишены оконного дескриптора и не могут принять фокус ввода. Windows "не узнает" графические компоненты, поэтому их применение не приводит к дополнительным накладным расходам на передачу дескриптора. Графические компоненты обеспечивают отображение объектов без использования системных ресурсов.
Вы должны производить новые графические компоненты от базового абстрактного класса TGraphicControl (который, в свою очередь, является потомком TControl). TGraphicControl предоставляет выбор канвы для рисования и обрабатывает сообщения WM_PAINT. Все, что вам следует сделать, это переопределить метод рисования Paint в соответствии заданными требованиями.
6.7.2.4 Создание невидимых компонент
Мы знаем, что все без исключения компоненты имеют общего прародителя абстрактный класс TComponent. Однако, только невидимые компоненты можно наследовать непосредственно от TComponent.
Любая производная от TComponent наследует все встроенные в нее свойства и методы. Невидимые компоненты встречаются довольно редко и используются, главным образом, в качестве интерфейсных элементов с другими компонентами (доступа к базам данных или как держатели диалоговых окон.
6.7.3 Добавление свойств, событий и методов.
Свойства представляют главную отличительную черту компонент, главным образом потому, что пользователи могут видеть и манипулировать ее свойствами во время проектирования, немедленно наблюдая реакцию времени выполнения программы. Мы уже знаем, что свойства, в отличие от члена данных, сами не хранят данные, однако методы чтения и записи организуют к ним доступ. Помните об этом, когда решите создать или изменить компонентные свойства.
События играют чрезвычайно ответственную роль в поведении компонент, Событие - это связь между некоторым происшествием в системе (таким как воздействие пользователя на компоненту или изменение фокуса) и кодом-обработчиком события, который реагирует на это происшествие. Обработчик события почти всегда пишется прикладным программистом, ибо только он знает, какой должна быть реакция на данное событие. Используя события, прикладной программист может приспособить поведение компонент к своим требованиям, без необходимости изменения самих объектов. Предоставить прикладному программисту такую возможность в отношении новой компоненты и является задачей разработчика компоненты. События, возникающие в результате наиболее типичных действий пользователя (например, движений мышью) встроены во все стандартные компоненты VCL, однако вы также можете определить новые события. Мы уже знаем, что C++Builder реализует события как свойства. Помните об этом, когда решите создать или изменить компонентные события.
Мы уже знаем, что компонентные методы ничем не отличаются от других объектных функций-членов. Хотя C++Builder не вводит никаких специальных ограничений на оформление компонентных методов, имеется ряд правил, которых стоит придерживаться. Помните об этом, когда решите создать или изменить компонентные методы.
6.7.4 Регистрация компоненты
Перед тем как ваша компонента сможет работать на стадии проектирования приложения, C++Builder должен зарегистрировать ее. При регистрации становится известным положение новой компоненты в Палитре компонент C++Builder.
Регистрация компоненты - это простой процесс, который информирует C++Builder о том, какая компонента добавляется к VCL и на какой вкладке Палитры она должна появиться.
Чтобы зарегистрировать компоненту:
1. Добавьте функцию Register к файлу МуСотр.срр, заключив ее имя в пространство имен (заметьте, что название пространства имен начинается с заглавной буквы, а все остальные буквы - прописные).
2. В теле функции Register объявите массив типа TComponentClass, в который введите регистрируемую компоненту.
3. В теле функции Register вызовите функцию
RegisterComponents стремя параметрами: название вкладки Палитры компонент, массив компонентных классов и размер компонентных классов.
namespace Mycomp {
void _fastcall Register()
{
TComponentClass classes[1] = {_claesid(TMyComponent)};
RegisterComponents("Samples", classes, 0) ;
) }
Листинг 6.13. Регистрация компоненты.
Листинг 6.13 представляет включение в файл МуСотр.срр кода для регистрации компоненты TMyComponent на вкладке Samples Палитры компонент.
Когда компонента зарегистрирована, вы можете испытать и инсталлировать ее на Палитру, завершая тем самым процесс разработки новой компоненты.
6.7.5 Отладка неинсталлированной компоненты
Вам следует испытать поведение созданной компоненты при выполнении программы до ее инсталляция на Палитру. По существу, вам придется смоделировать те действия, которые производит C++Builder, когда пользователь перемещает компоненту из Палитры на форму. Этот процесс требует выполнения следующих шагов:
1. Включите файл модуля MyComp.h вашей компоненты в заголовочный файл некоторой формы.
2. Добавьте объектный член данных, представляющий испытываемую компоненту, в конец секции public объявлений класса формы, вне области объявлений, которые делает C++Builder.
3. Подсоедините обработчик к событию
OnCreate формы.
4. Вызовите конструктор компонентного объекта (компонента отвечает за самоуничтожение, когда наступит время) из обработчика этого события, передав ему параметр, указывающий на владельца компоненты. Обычно параметром служит указатель this на объект, который содержит этот метод. В нашем примере параметр this ссылается на форму.
5. Сразу же за вызовом конструктора установите свойство Parent - родителя компоненты, обычно представляющего собой форму, группирующую рамку или панель инструментов. Обычно значением этого свойства является указатель this. Внимание: Если ваша компонента не является элементом управления, т.е. вы
не наследовали ее от TControl, пропустите этот шаг.
6. Инициируйте значения других свойств компоненты.
Предположим, вы собираетесь испытать компоненту TMyComponent в модуле с именем МуСотр. Создайте новый проект, а затем следуйте перечисленным шагам процесса подготовки модуля формы. Листинг 6.14 содержит законченный образец текста модуля формы отлаживаемой компоненты.
#ifndef TestFormH
#define TestFormH
//---_-_______-_-__-________-_-_._-_____________-_-___-_-_
ttinclude
#include
#include
#include "MyComp.h" // 1. //----—---——----—-----—-----—---—_--____-_----------
class TFormI : public Tform {
publ i shed:
private:
public:
TMyComponent* MyComponentI; // 2,
virtual _fastcall TFormI (TComponent* Owner); •// 3,
};
//----—--—---—------—-—-———---—--——-—------——--
extern TFormI *Forml;
//_--__--______----—-----—---------------------—--------
#endif
// Это файл
MyComp.cpp модуля формы:
#include
#pragma hdrstop
#include "TestForm.h" //——-----——-——--—------—-—--——---—--—----—--—--
ftpragma resource "*.dfm"
TFormI *Forml;
//---------—--—-------------------—---—--------------—
_fastcall TFormI::TFormI(TComponent* Owner) : TForm(Owner)
{
MyComponentI = new TMyComponent(this); // 4. MyComponent->Parent = this;
// 5. MyComponentl->Left = 12; // 6.
}
Листинг 6.14. Текст модуля формы новой компоненты.
6.7.6 Инсталляция компоненты на Палитру
Компонентные модули, написанные на C++, имеют расширение .срр, а компоненты, написанные на Объектном Паскале, имеют расширение
.pas.
При инсталляции новой компоненты или при выполнении команды Component | Rebuild Library, Библиотека Визуальных Компонент перестраивается, и C++Builder создает временный файл CMPLIB32.CPP исходных текстов VCL. Чтобы сохранить этот файл, с помощью команды Options | Environment | Library откройте диалог опций и установите флаг Save Library Source Code.
Чтобы добавить к VCL компоненту, выполните следующие шаги:
1. С помощью команды Component | Install откройте диалоговое окно инсталляции компонент.
2. Нажмите кнопку Add, которая открывает диалог добавления модуля. Введите имя модуля непосредственно в поле Module Name или найдите его местоположение, нажав на кнопку поиска Browse. Имя добавленного вами компонентного модуля появится внизу списка названий группы Installed Components. В списке Component classes вы увидите имена компонентных классов, уже находящихся в выбранной группе Библиотеки. У вновь введенного модуля имя компонентного класса отсутствует.
3. Нажмите кнопку ОК, закрывая диалог инсталляции компонент. Библиотека будет перестроена и новая компонента установлена на ту вкладку Палитры, которую вы определили как параметр функции регистрации (см. Листинг 6.13).
Чтобы удалить компоненту из VCL, выполните следующие шаги:
1. Выполните команду Component | Install, которая открывает диалоговое окно установки компонент.
2. Найдите ненужный вам более компонентный класс в списке Component classes выбранной группы Библиотеки и нажмите кнопку Remove.
3. Нажмите кнопку ОК. Библиотека будет перестроена и новая компонента удалена из Палитры.
Чтобы перестроить Палитру, выполните следующие шаги:
1. Откройте диалог установки опций Палитры с помощью команд Component | Configure Palette или Options | Environment | Palette.
2. Нажмите кнопку Add и выберите имя для новой вкладки. Имя добавленной вами вкладки появится внизу списка Pages названий вкладок.
3. Перетащите мышью выбранную компоненту в списке Components
на нужную вкладку списка Pages.
4. Нажмите кнопку ОК. Библиотека и Палитра будут перестроены.
6.7.7 Сохранение файлов новой компоненты
Когда вы закончите процесс разработки, созданная компонента будет представлена следующими файлами:
• объектный файл результата компиляции MyComp.obj;
• заголовочный файл объявлений, сгенерированный компилятором (MyComp.h для исходного текста на C++ или MyComp.hpp для исходного текста на Объектном Паскале);
• файл битового образа Палитры
(MyComp.res или
MyComp.dcr);
• файл формы MyComp.dfm, если компонента использует форму.
Перед тем, как использовать вновь созданную компоненту, перепишите в каталог \..
.\CBuilder\LIB\OBJ следующие файлы компоненты МуСотр: все двоичные файлы (с расширениями .dfm, .res и .dcr), все исходные файлы (с расширениями .срр или .pas), все объектные файлы (с расширениями .obj и .Но) и все заголовочные файлы (с расширениями .h или .hpp).
Желательно создать и сохранить контекстно-справочный файл (с расширением .hip).
6.8 Разработка простой компоненты
Перед тем, как приступить к разработке новой компоненты, вы должны заранее четко представить себе, что точно она должна делать и как будет реализовано ее оригинальное поведение. Удостоверьтесь, что ни одна из имеющихся компонент не обладает требуемыми вами способностями. Поскольку в cтaндapтнoм варианте поставки C++Builder отсутствует Инструкция по написанию компонент и исходные тексты VCL, пришлось заимствовать элементарное руководство Криса Эриксона, которое я скачал по сети Internet. Его компонента моделирует бинарный индикатор, который меняет цвет при изменении состоянии. Пока очевидно только, что некоторое свойство компоненты будет хранить текущее состояние (true, если индикатор включен, и false - в противном случае).
Из данной главы мы уяснили, что желательно выбрать для наследования наиболее близкий в иерархии VCL базовый компонентный класс; очевидно, что индикатор представляет собой графическую компоненту семейства TGraphicControl. Поскольку мы разрабатываем "ну очень простую" компоненту, пусть она будет иметь тривиальную форму окружности, а не более хитроумный битовый образ. На первый взгляд, компонента TShape из вкладки Палитры Additional выглядит ближайшим родственником. При внимательном рассмотрении TShape имеет больше свойств и событий, чем нам требуется, хотя и обладает всей нужной функциональностью.
Внимание: Вы не сможете удалить свойства или события из базовой компоненты, а только подняться выше по иерархии объектов на пути к общему предку TComponent. Это означает, что выбрав слишком далекого предшественника, вы рискуете запутать потенциального пользователя (да и самого себя тоже) обилием избыточных свойств и событий, которые по сути не нужны вашей компоненте. Напротив, выбрав слишком близкого предшественника, вам придется самостоятельно и полностью программировать функциональное поведение своей компоненты.
Итак, все что мы хотим изменить при наследовании от TShape - это форму; индикатора и цвет кисти при его переключении. Звучит достаточно просто. По";
еле того, как мы точно сформулировали, что будет делать создаваемая компонента, настало время перейти к ее фактической разработке.
6.8.1 Форма тестового приложения
Пока мы не убедились, что разрабатываемая компонента работает надлежащим образом (а это может занять много времени даже с простейшим элементом управления), ее нельзя включать в VCL. Сначала следует создать тестовое приложение с прототипом новой компоненты LED:
=> С помощью команды File | New
Application создайте пустую форму. => Разместите кнопку TButton на форме.
=> С помощью команды File | Save All сохраните форму и проект приложения в файлах под именами
LEDForm.cpp и
LEDProj.mak.
6.8.2 Модуль тестового приложения
Мастер компонент (Component Wizard) упрощает начальные шаги создания компоненты. Мастер не может автоматически добавлять компоненту к существующему модулю, вам придется проделать это вручную.
=> Выполните команду Component | New и в открывшемся диалоге Мастера компонент заполните поля диалога указанными значениями (Рис. 6.9). Нажмите кнопку ОК.

Рис. 6.9. Диалог Мастера компонент.
=> С помощью команды File | Save или File | Save As сохраните файл
Unitl.cpp под именем LED.cpp.
Теперь можно посмотреть в окне Редактора кода, что сделал C++Builder для подготовки нашей компоненты. Файл LED.h будет содержать объявление нового компонентного класса с конструктором, а также несколько заголовочных файлов предкомпиляции. Файл LED.cpp будет содержать пустой конструктор объекта и функцию Register для регистрации компоненты. Не слишком много для автоматизированного начала...
6.8.3 Члены данных, свойства и методы
Ознакомившись с заготовками программного модуля компоненты, которые создал для нас C++Builder, можно приступить к написанию собственно кода компоненты. Прежде всего, в файле LED.h опишем булеву переменную состояния индикатора и две переменные перечисляемого типа TColor для хранения цветов, отображающих оба состояния. Из главы 3 об основах объектно-ориентированного программирования мы знаем как ограничивать область видимости и уяснили, что эти члены данных следует спрятать в секции private объявлений класса. Там же расположим прототипы функций записи соответствующих свойств, а сами свойства объявим в секции _published (Листинг 6.15).
//---------------________________________________-_____-_--
#ifndef LEDH
#define LEDH //------------_____-_____--_________________________-___--.
#include
#include
#include
#include
class TLED : public TShape { private:
bool FOnOff; TColor FOnColor; TColor FOffColor;
void _fastcall SetOnOff(const bool Value) ;
void _fastcall ' SetOnColor
void _fastcall SetOffColor (conet TColor OffColor) ;
protected:
public:
_fastcall TLED(TComponent* Owners-published:
property bool LEDOn= { read= FOnOff,write= SetOnOff) ; property ТСоlог OnColor= { read=FOnColor,write=SetOnColor} ; :.,. __property TColor OffColor=
{ read=FOff Color, write= SetOffColor} ;
};
//_____________-___-_----____________------------------——
#endif
Листинг 6.15. Добавления в файл LED.h модуля компоненты.
Еще проще окажутся добавления в файл LED.cpp (Листинг 6.16). Необходимо написать три функции для присвоения значений свойств соответствующим членам данных, а также наполнить конструктор компоненты инструкциями для инициализации переменных.
#include
#pragma hdrstop
# include "LED.h" //_-------—----—-____--------—---.————---—---—----—--
static inline TLED *ValidCtrCheck() {
return new TLED(MULL) ;
)
void _fastcall TLED::SetOnOf?(const bool Value) {
POnOff = Value;
Brush->Color ” (POnOff) ? FOnColor : POffColor?
}
//_———————————————-——-^-.-——.————.——————
void _fastcall TLED::SetOnColor(const TColor OnColor) {
POnColor = OnColor;
Brush->Color = (FOnOff) ? FOnColor : FOffColor;
}
//-—---——-„--—--------————----------——--———---—--
void _fastcall TLED::SetOЈfColor(const TColor OffColor) {
FOffColor = OffColor;
Brush->Color = (FOnOff) ? FOnColor : FOffColor;
}
//---_------------------------------------------------------
_fastcall TLED::TLED(TComponent* Owner) : TShape(Owner)
{
Width = 15; // тирина по умолчанию Height = 15; // высота по умолчанию FOnColor = cILime; //
зеленый, когда включен FOffColor = cIRed; // красный, когда выключен FOnOff = false; //
выключен по умолчанию Shape ? atEllipse; //в форме эллипса по умсвдчани Pen->Color = clBlack; //
черный контур по умолчанию Pen->Width = 2; // ширина контура по уйолчанию Brush->Color ^ FOffColor; // цвет заливки по умолчанию
}
//---------------____-______________________--________.
namespace Led
{
void _fastcall Register () {
TComponentClass classes[1] = {__classid(TLED)} ;
RegisterComponents("Samples", classes, 0) ;
} }
Листинг 6.16. Добавления в файл LED.cpp модуля компоненты.
Установленные конструктором значения членов данных по умолчанию па вятся в окне Инспектора объектов при создании объекта индикатора. Дейстительно, при помещении компоненты на форму конструктор вызывается автоматически. В результате появляется возможность менять значения свойств компоненты не только во время выполнения программы, но и на стадии проектирования приложения. *
6.8.4 Испытание компоненты
Теперь, когда мы закончили с написанием текста модуля компоненты, проверим работает ли она.
=> С помощью команды File | Save All сохраните все сделанные добавления. => Выбрав вкладку LEDForm.cpp в окне Редактора кода, по команд File | Include Unite Hdr включите строку
#include "LED. h" в заголовок файла формы. По неведомой причине, эта команда не работает с файлом LEDForm.h, поэтому строку
#include "LED.h"
приходится вставлять вручную. К секции private этого же файла добавьте описание объекта индикатора:
private: // User declarations TLED* LED1;
=> Активизируйте форму Formi и в окне Инспектора объектов дважды щелкните мышью в графе значений события OnCreate. С помощью Редактора кода введите обработчик этого события в файл LEDForm.cpp.
Следующий код создаст компоненту TLED динамически (определяя ее родителя Parent и помещая в в центре родительской формы) во время выполнения тестового приложения:
void_fastcall TFormI::FormCreate(TObject *Sender)
t
LED1 = new TLED(this);
LED1->Parent = this;
// Центрировать компоненту по ширине формы LEDl->Left = (Width/2)-(LEDl->Width/2);
// Центрировать компоненту по высоте формы LEDl->Top = (Height/2)-(LEDl->Height/2);
}
Чтобы кнопка управляла индикатором, дважды щелкните мышью в графе значений события OnClick объекта Buttoni в окне Инспектора объектов. С помощью Редактора кода введите следующую инструкцию в тело обработчика события:
void_fasfccall TPormI::ButtonlClick(TObject *Sender) (
LEDl->LEDOn = !LEDl->LEDOn;
}
=> Наконец, скомпилируйте и запустите тестовое приложение посредством команды Run | Run.
Если компилятор не выдаст ошибок (а их не должно быть, если вы точно следовали изложенной процедуре), то посередине формы тестового приложения вы увидите красный индикатор в состоянии "выключен". Нажав кнопку, вы включите индикатор и он окрасится зеленым цветом (Рис. 6.10).
Теперь осталось создать битовый образ пиктограммы, которой новая компонента будет представлена в Палитре. Из меню редактора изображений, открывающегося по команде Tools | Image Editor,
выберите File | New | Resource File, a затем - Resource | New) Bitmap.
В диалоге свойств битового образа установите размеры пиктограммы 24х24 и число цветов VGA (16 Colors).
Переименуйте битовый образ компоненты (TLED) по команде Resourse | Rename и дважды щелкните мышью на выбранном имени в древовидном списке ресурсных файлов, чтобы нарисовать подходящую картинку индикатора (например, зеленый кружок). Командой File | Save As сохраните ресурсный файл LED.res в своем рабочем каталоге и закройте Редактор изображений.

Рис. 6.10. Динамическое создание компоненты индикатора.
6.8.5 Инсталляция компоненты
Перед тем, как приступить к инсталляции новой компоненты на Палитру, выполните последний раз команду File | Save All.
=> С помощью команды Component | Install откройте диалоговое окно инсталляции компонент. Нажмите кнопку Add, которая открывает диалог добавления модуля. Найдите местоположение модуля LED.cpp, нажав на кнопку поиска Browse. Нажмите кнопку ОК и приготовьтесь ждать окончания перестройки VCL и установки новой компоненты на Палитру.
=> Выполните команду File | Close All, а затем File | New
Application. Поместите новую компоненту LED и кнопку TButton на форму. Снова определите обработчик события OnClick кнопки управления индикатором:
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
LEDl->LEDOn = lLEDl->LEDOn;
}
=> Выполните команду Run | Run и вы увидите, что компонента действительно работает.
Порадуйтесь тому, как просто все оказалось на деле, сохраните на всякий случай все рабочие файлы (Borland
рекомендует использовать каталог \..
ACBuilder\LIB\OBJ) и приступайте к планированию вашей следующей компоненты.
6.9 Итоги
Планируете ли вы использовать Библиотеку Визуальных Компонент при создании прикладного программного обеспечения, или развивать существующую Библиотеку при разработке новых компонент — глубокие знания состава и уст роиства VCL
будут способствовать успешному решению поставленных задач
Компоненты вкладки Data Access
2.2.4 Компоненты вкладки Data AccessКомпоненты этой вкладки осуществляют включение в ваше приложение следующих элементов, обеспечивающих доступ к базам данных:
| TDataSource | Действует как интерфейс между TTable, TQuery и TStoredProc и компонентами управления данными типа TDBGrid. |
| ТТаЫе | Обеспечивает доступ к таблицам базы данных. |
| TQuery | Обеспечивает передачу команд на языке структурированных запросов SQL серверу локальной или удаленной базы данных. |
| TStoredProc | Разрешает выполнение хранимых процедур сервера удаленной базы данных. |
| TDatabase | Эта компонента не участвует в организации доступа к базам данных, хотя предоставляет возможность управления в приложениях клиент/сервер. |
| TSession | Предоставляет средства глобального управления групповых соединении с несколькими базами данных. |
| TBatchMove | Разрешает выполнять пакетные операции над группами записей или целыми таблицами. |
| TUpdateSQL | Предоставляет специальный механизм обновления данных, возвращаемых некоторыми запросами SQL. |
2.2.5 Компоненты вкладки Data Controls
Компоненты этой вкладки осуществляют включение в ваше приложение следующих элементов управления визуализацией и редактированием подсоединенной базы данных:
| TDBGrid | Отображает и позволяет редактировать записи таблицы или запроса базы данных на сетке. |
| TDBNavigator | Используется для перемещений по записям таблицы или запроса базы данных и для выполнения операций по их просмотру и редактированию. |
| TDBText | Отображает статический текст названия текущей записи таблицы или запроса базы данных. |
| TDBEdit | Создает однострочную область редактируемого ввода в текущую запись таблицы или запроса базы данных. |
| TDBMemo | Создает многострочную область редактируемого ввода (включая данные в формате BLOB) в текущую запись таблицы или запроса базы данных. |
| TDBImage | Создает контейнер для представления графического изображения, которое хранится в поле текущей записи в формате BLOB. |
| TDBListbox | Создает список, выбранный элемент которого становится новым значением поля текущей записи таблицы или запроса базы данных. |
| TDBComboBox | Создает комбинацию области редактирования и выпадающего списка текстовых строк для изменения значения текущей записи в наборе данных. |
| TDBCheckBox | Создает элемент управления с двумя состояниями, связанными с конкретным полем записи таблицы или запроса базы данных. |
| TDBRadioGroup | Создает контейнер для группы логически взаимоисключающих радио-кнопок, связанных с конкретными полями записи таблицы или запроса базы данных. |
| TDBLookupList | Создает список ссылок для заполнения полей данными из другой таблицы или запроса базы данных. |
| TDBLookup ComboBox | Создает комбинацию области редактирования и выпадающего списка ссылок для заполнения полей данными из другой таблицы или запроса базы данных. |
Компоненты этой вкладки осуществляют включение в ваше приложение следующих стандартных диалоговых элементов
Windows:
| TOpenDialog | Открывает доступ к диалогу открытия файлов. |
| TSaveDialog | Открывает доступ к диалогу сохранения файлов. |
| TFontDialog | Открывает доступ к диалогу выбора шрифтов и их атрибутов. |
| TColorDialog | Открывает доступ к диалогу выбора цветов. |
| TPrintDialog | Открывает доступ к диалогу печати (выбор принтера, диапазона печатаемых страниц, числа копий и т.п.). |
| TPrinterSetup Dialog | Открывает доступ к диалогу предварительных установок принтера перед печатью. |
| TFindDialog | Открывает доступ к диалогу поиска текста. |
| TReplaceDialog | Открывает доступ к диалогу поиска текста с заменой. |
Компоненты этой вкладки осуществляют включение в ваше приложение следующих специализированных системных элементов управления:
| TTimer | Вызывает возникновение события OnTimer по прошествии заданного временного интервала. |
| TPaintBox | Предоставляет возможность рисования на форме внутри заданной прямоугольной области. |
| TFiIeListBox | Перечисляет все файлы в текущем каталоге, доступные программе во время ее работы. |
| TDirectoryListBox | Отображает древовидную структуру каталогов текущего диска, доступных программе во время ее работы. |
| TDriveComboBox | Отображает комбинированный редактируемый список дисков, доступных программе во время ее работы. |
| TfilterComboBox | Представляет комбинированный редактируемый список фильтров для выбора имен файлов с расширениями. |
| TMediaPlayer | Отображает стандартную панель управления устройствами мультимедиа (звуковая плата, компакт диск, видеокамера, AVI плеер, MIDI секвенсор и др.), которые поддерживаются MCI-драйверами Windows. |
| TOIeContainer | Организует связь с OLE объектами или непосредственно включает их в ваше приложение. |
| TddeClientConv | Устанавливает режим динамического обмена данными DDE клиента. |
| TDDEClientltem | Определяет элемент динамического обмена данными DDE клиента. |
| TDDEServerConv | Устанавливает режим динамического обмена данными DDE сервера. |
| TDDEServerItem | Определяет элемент динамического обмена данными DDE сервера. |
2.3 Приложения управления базами данных
Если вы не чувствуете себя достаточно уверенно, чтобы сразу перейти к самостоятельному программированию приложении СУБД в среде C++Builder, просмотрите готовые примеры, входящие в комплект поставки системы. Обратите внимание на методику адаптации приложении применительно к специфике ваших задач. 1 ,,
Доступ к базам данных поддерживает его основа - высокопроизводительный механизм BDE (Borland Database Engine),
включающий 32-разрядные драйверы для баз данных dBASE, Paradox; Sybase, Oracle, DB2, Microsoft SQL Server, Informix, InterBase и Local Interbase. Кроме того C++Builder имеет быстродействующий механизм ODBC для связи с такими базами данных, как Excel, Access, FoxPro и Btrieve в архитектуре клиент/сервер. Поскольку в наших примерах используется только демонстрационная база данных BCDEMOS, удостоверьтесь предварительно, что ее локальный псевдоним (alias) установлен с помощью утилиты конфигурации BDE. Если вы располагаете другими источниками данных, укажите их.
2.3.1 Пример из существующего проекта
Данный пример демонстрирует работу компонент доступа TDBGrid, TDBText, TDBImage, TDataSource, TTable и TDBMemo, а также - компоненты стандартного диалога TSaveDialog. Уже на стадии проектирования вы сможете просматривать соответствующую таблицу базы данных и с помощью линейки прокрутки компоненты сетки TDBGrid открывать "живые" факты из жизни обитателей моря. Чтобы начать работу с существующим проектом приложения, выполните следующие действия:
\...\CBuilder\Examples\Dbtasks\FishFact.
(Рис.2.14).

Рис. 2.14. Пример приложения, использующего существующий проект.
Источник данных приложения определяется свойствами компоненты TTable:
свойство DatabaseName указывает на базу данных BCDEMOS, а свойство TableName - на таблицу BIOLIFE.
Если захотите, скомпилируйте и соберите этот пример. После запуска вы снова получите доступ к изображениям и описаниям рыб в установленной таблице базы данных (Рис. 2.15). Сохраните информацию о заинтересовавшей вас рыбе в виде текстового файла, открывая кнопкой
Save стандартный диалог компоненты TSaveDialog. Кнопка Exit осуществляет выход из приложения.

Рис. 2.15. Приложение FISH FACTS в работе.
2.3.2 Пример использования модулей данных
Архитектура приложений C++Builder для баз данных основана на применении технологии RAD и объектно-ориентированного программирования как к пользовательскому интерфейсу, так и к бизнес-логике баз данных. Модули данных (Data Modules) поддерживают естественное отделение кодов бизнес-логики обработки данных и компонент пользовательского интерфейса, размещенных на форме.
Объекты модулей данных, доступные на этапе визуального проектирования и скрытые во время работы приложения, действуют как централизованный источник информации, который организует не только сам доступ к базам данных, но и бизнес-логику их обслуживания.
Реализация различных способов доступа к элементам баз данных сосредоточена в компонентах вкладки Data Access Палитры компонент. Эти интерфейсные элементы заключают в себе такие характеристики источника информации, как спецификации конкретной базы данных, таблиц, запросов и ссылок к полям в некоторой записи.
Использование модулей данных из Хранилища объектов дает вам возможность моментально адаптировать их к вашей задаче, перетаскивая нужные вам объекты и компоненты на форму создаваемого приложения. Изменения, внесенные вами в бизнес-логику конкретного модуля данных или в интерфейсные компоненты, никак не отражаются на использовании того же модуля другими разработчиками.
Чтобы создать на основе готового модуля приложение "Заказчики" с данными о заказчиках товара, производимого некоторой компанией, выполните следующие действия:
=> Выполните команду File | New из главного меню. => На вкладке Data Modules выберите значок
Customer Data, а затем нажмите
кнопку ОК.
=> Дважды щелкните мышью на таблице
Customers модуля данных. Откроется список Customerdata->Customers полей таблицы заказчиков (Рис. 2.16).

Рис. 2.16. Поля таблицы заказчиков
Customers модуля данных Customer Data.
=> Удерживая нажатой клавишу Shift, выберите нужные вам поля из списка. => Перетащите выбранные поля и разместите их на форме. => Перетащите компоненту навигатора базы данных DBNavigator
из вкладки Data Control Палитры и в ее свойстве DataSource укажите источник данных
CustomerData->CustomerSource.
Теперь вы сможете просматривать "живые" данные всех записей в таблице заказчиков уже на стадии проектирования формы приложения (Рис. 2.17). Для этого нажимайте кнопки со стрелками на панели, расположенной в верхней части списка полей таблицы заказчиков. С помощью Инспектора объектов модифицируйте по своему усмотрению свойства компонент полей (например, в свойствах Caption замените все названия на русскоязычные).

Рис. 2.17. Форма приложения для доступа к таблице заказчиков.
Когда вы скомпилируете и соберете приложение, вы увидите те же данные, что и на стадии проектирования.
![]() Рис. 2.18. Приложение "Заказчики" в работе. |
Теперь, пользуясь кнопками панели навигатора, вы сможете редактировать данные -вставлять новые записи, Bычеркивать ненужные, менять содержание полей и т.д. Рис. 2.18 показывает результат введения в таблицу атрибутов вымышленного отечественного заказчика (после нажатия кнопки "+"). Внимание: На этом примере вы можете впервые столкнуться со сложностями в использовании русского шрифта при попытках модифицировать оригинальные таблицы демонстрационной базы данных BCDEMOS, входя щей в комплект C++Builder |
На этом примере вы можете впервые столкнуться со сложностями в использовании русского шрифта при попытках модифицировать оригинальные таблицы демонстрационной базы данных BCDEMOS, входя щей в комплект C++Builder
Standard. He думаю, что такое ограничение корпорация Borland ввела сознательно. Не относитесь к этой проблеме слишком серьезно - ведь нашей задачей является обучение современной методике программирования СУБД, для чего демонстрационная база дает достаточный экспериментальный материал (хотя и англоязычный), а вовсе не модификация самой BCDEMOS.
Естественно, что разработчики настоящих приложении имеют дело с реальными базами данных, внутреннее устройство которых они обязаны знать досконально.
2.3.3 Пример использования Мастера форм
Прежде, чем начать работу с последним примером этой главы, просмотрите Словарь баз дачных (Database Dictionary),
выполнив команду главного меню Database | Explorer (Проводник базы данных). Открывшееся окно словаря (Рис. 2.19) состоит из двух панелей. В древовидном перечне левой панели найдите таблицу базы данных, которую вы собираетесь связать с формой проектируемого приложения. Для разработки приложения "Моя записная книжка" я создал в Excel таблицу с именем NOTEBOOK. He беда, если вы не захотите построить собственную рабочую таблицу в рамках Excel, Access, Paradox или какой-нибудь другой системы - при изучении примера можно воспользоваться англоязычным прототипом CLIENTS из базы данных BCDEMOS.
Описание выбранной таблицы представлено тремя вкладками правой панели. На вкладке
Definition вы найдете характеристики таблицы (дата, время создания, тип). Вкладка Enter SQL позволяет вводить и редактировать команды на языке структурированных запросов SQL к таблицам базы данных, расположенных на локальном или удаленном сервере. Если вы вводите команды изменения данных таблицы INSERT, UPDATE или DELETE, убедитесь, что опция меню Options | Live Queries не выбрана. Чтобы выполнить текущую команду SQL, нажмите кнопку с изображением молнии справа от поля редактирования. Вкладка Data показывает текущее содержание таблицы:

Рис. 2.19. Содержание таблицы NOTEBOOK а окне Проводника базы данных.
Для использования Мастера форм при создании приложения выполните следующие действия:
=> С помощью команды Database | Form Wizard из главного меню вызовите Мастер форм, который проведет вас по диалоговым страницам характеристик создаваемой формы.
=> На первой странице (Рис. 2.20) укажите, что создаете простую форму на основе компонентных объектов таблицы базы данных (Create a form using TTable objects). Нажмите кнопку Next.

Рис. 2.20. Первая страница диалога Мастера форм.
=> На второй странице (Рис. 2.21) найдите вашу собственную таблицу NOTEBOOK.DBF (или ее прототип
CLIENTS.DBF) из базы данных с псевдонимом BCDEMOS, которая будет использоваться формой приложения. В данном примере обе таблицы хранятся в каталоге CAProgram Files\Borland\CBuilder\Examples\DATA.
Нажмите кнопку Next.

Рис. 2.21. Вторая страница диалога Мастера форм.
=> На третьей странице (Рис. 2.22) выберите нужные вашему приложению поля таблицы из списка Available Fields и нажатием кнопки ">" перенесите их в список Ordered Selected Fields. В данном примере выбраны 5 полей. Нажмите кнопку Next.

Рис. 2.22. Третья страница диалога Мастера форм.
=^> На четвертой странице (Рис. 2.23) задайте расположение выбранных полей на форме - вертикально. Нажмите кнопку Next.

Рис. 2.23. Четвертая страница диалога Мастера форм.
=> На пятой странице (Рис. 2.24) задайте положение названий относительно полей - слева. Нажмите кнопку Next.

Рис. 2.24. Пятая страница диалога Мастера форм.
=> На шестой странице (Рис. 2.25) можно дополнительно потребовать создать для формы новый модуль данных. Нажмите кнопку
Finish, завершая разработку формы приложения базы данных под управлением Мастера форм.

Рис. 2.25. Шестая страница диалога Мастера форм.
Далее, как обычно, можно воспользоваться Инспектором объектов, чтобы адаптировать компоненты формы к более удобному для вас представлению (Рис. 2.26).

Рис. 2.26. Форма приложения, созданная Мастером форм.
Когда вы скомпилируете и соберете приложение, исходные данные таблицы появятся в том виде. как вы их видели в Словаре баз данных.
![]() Рис. 2.27. Приложение "Моя записная книжка' и работе. |
Теперь, пользуясь кнопками панели навигатора, вы сможете редактировать содержимое записной книжки - вставлять новые записи, вычеркивать ненужные, редактировать содержание полей и т.д. Рис. 2.27 отображает запись, которая стала начальной в результате вычеркивания первой записи в исходной таблице NOTEBOOK (после нажатия кнопки "-"). Естественно, что оригинальная таблица CLIENTS способна предоставить вам другие данные (Рис. 2.28). |

Рис. 2.28. Работа приложения базы данных с таблицей CLIENTS.
В последнем примере Мастер форм проявил себя как чрезвычайно полезный для новичков и удобный в обращении встроенный инструмент
C++Builder, который позволяет разработать полностью функциональное приложение базы данных буквально за несколько минут. Проектирование формы приложения, по существу, заключается в выборе опций, предлагаемых диалоговыми страницами Мастера форм. Мастер форм самостоятельно закладывает фундамент прототипа вашего приложения, вообще не прибегая к "ручному" программированию. Конечно, чтобы удовлетворить конкретным требованиям поставленной задачи, вам не обойтись без написания специфических кодов для обработки событий, ошибок и отображения данных в различных видах.
2.4 Итоги
C++Builder для операционных систем Windows 95 и NT
обеспечивает высокую скорость визуальной разработки методом перетаскивания компонент на форму приложения. Мы убедились в высокой продуктивности и гибкости повторно используемых компонент, в особенности - элементов пользовательского интерфейса с базами данных, что в сочетании с высокопроизводительными компилятором и загрузчиком действительно позволяет быстро переходить от начального прототипа приложения к работающей программе.
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ И C++
3. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ И C++
Объект - это абстрактная сущность, наделенная характеристиками объектов окружающего нас реального мира. Создание объектов и манипулирование ими - это вовсе не привилегия языка C++, а скорее результат методологии программирования, воплощающей в кодовых конструкциях описания объектов и операции над ними. Каждый объект программы, как и любой реальный объект, отличается собственными атрибутами и характерным поведением. Объекты можно классифицировать по разным категориям: например, мои цифровые наручные часы
"Cassio" принадлежат к классу часов. Программная реализация часов входит, как стандартное приложение, в состав операционной системы вашего компьютера.
Каждый класс занимает определенное место в иерархии классов, например, все часы принадлежат классу приборов измерения времени (более высокому в иерархии), а класс часов сам включает множество производных вариаций на ту же тему. Таким образом, любой класс определяет некоторую категорию объектов, а всякий объект есть экземпляр некоторого класса.
Объектно-ориентированное программирование (ООП) — это методика, которая концентрирует основное внимание программиста на связях между объектами, а не на деталях их реализации. В этой главе основные принципы ООП (инкапсуляция, наследование, полиморфизм, создание классов и объектов) интерпретируются и дополняются новыми понятиями и терминологией, принятыми интегрированной средой визуальной обработки C++Builder. Приводится описание расширений языка новыми возможностями (компоненты, свойства, обработчики событий) и последних дополнений стандарта ANSI C++ (шаблоны, пространства имен, явные и непостоянные объявления, идентификация типов при выполнении программы, исключения).
Глава носит обзорный характер, она призвана познакомить читателя со специальной терминологией ООП, к которой автор вынужден прибегать на протяжении всей книги. Это вызвано тем, что C++Builder является типичной системой ООП и претендует на кульминационную роль в истории его развития.
представляет член данных этого объекта, а кнопки управления - объектные методы. Нажимая кнопки часов, можно оперировать с установками времени на дисплее, т.е. следуя терминологии ООП, методы модифицируют состояние объекта путем изменения членов данных.
C++Builder вводит понятие компонент
(components) - специальных классов, свойства которых представляют атрибуты объектов, а их методы реализуют операции над соответствующими экземплярами компонентных классов. Понятие метод обычно используется в контексте компонентных классов и внешне не отличается от термина функция-член обычного класса. C++Builder позволяет манипулировать видом и функциональным поведением компонент не только с помощью методов (как это делают функции-члены обычных классов), но и посредством свойств и событий, присущих только классам компонент. Работая в среде C++Builder, вы наверняка заметите, что манипулировать с компонентным объектом можно как на стадии проектирования приложения, так и во время его выполнения.
Свойства (properties) компонент представляют собой расширение понятия членов данных и хотя не хранят данные как таковые, однако обеспечивают доступ к членам данных объекта. C++Builder использует ключевое слово _property для объявления свойств. При помощи событий (events) компонента сообщает пользователю о том, что на нее оказано некоторое предопределенное воздействие. Основная сфера применения методов в программах, разрабатываемых в среде C++Builder -это обработчики событий (event handlers), которые реализуют реакцию программы на возникновение определенных событий. Легко заметить некоторое сходство событий и сообщений операционной системы Windows. Типичные простые события —нажатие кнопки или клавиши на клавиатуре. Компоненты инкапсулируют свои свойства, методы и события.
На первый взгляд компоненты ничем не отличаются от других объектных классов языка C++, за исключением ряда особенностей, среди которых пока отметим следующие:
• Большинство компонент представляют собой элементы управления интерфейсом с пользователем, причем некоторые обладают весьма сложным поведением.
• Все компоненты являются прямыми или косвенными потомками одного общего класса-прародителя (TComponent).
• Компоненты обычно используются непосредственно, путем манипуляции с их свойствами; они сами не могут служить базовыми классами для построения новых подклассов.
• Компоненты размещаются только в динамической памяти кучи (heap) с помощью оператора
new, а не на стеке, как объекты обычных классов.
• Свойства компонент заключают в себе RTTI - идентификацию динамических типов.
• Компоненты можно добавлять к Палитре компонент и далее манипулировать с ними посредством Редактора форм интегрированной среды визуальной разработки C++Builder.
ООП интерпретирует взаимодействие с объектами как посылку запросов некоторому объекту или между объектами. Объект, принявший запрос, реагирует вызовом соответствующего метода. В отличие от других языков ООП, таких как SmallTalk, C++ не поощряет использование понятия "запрос". Запрос - это то, что делается с объектом, а метод - это то, как объект реагирует на поступивший запрос.
При ближайшем рассмотрении метод оказывается обычной функцией-членом, которая включена в определение класса. Чтобы вызвать метод, надо указать имя функции в контексте данного класса или в обработчике некоторого события.
Именно скрытая связь метода с включающим классом выделяет его из понятия простой функции. Во время выполнения метода он имеет доступ ко всем данным своего класса, хотя и не требует явной спецификации имени этого класса. Это обеспечивается передачей каждому, без исключения, методу скрытого параметра - непостоянного указателя this на экземпляр класса. При любом обращении метода к членам данных класса, компилятор генерирует специальный код, использующий указатель this.
3.3 Наследование
Одной из самых восхитительных особенностей живой природы является ее способность порождать потомство, обладающее характеристиками, сходными с характеристиками предыдущего поколения. Заимствованная у природы идея наследования решает проблему модификации поведения объектов и придает ООП исключительную силу и гибкость. Наследование позволяет, практически без ограничений, последовательно строить и расширять классы, созданные вами или кем-то еще. Начиная с самых простых классов, можно создавать производные классы по возрастающей сложности, которые не только легки в отладке, но и просты по внутренней структуре.
Последовательное проведение в жизнь принципа наследования, особенно при разработке крупных программных проектов, хорошо согласуется с техникой нисходящего структурного программирования (от общего к частному), и во многом стимулирует такой подход. При этом сложность кода программы в целом существенно сокращается. Производный класс (потомок) наследует все свойства, методы и события своего базового класса (родителя) и всех его предшественников в иерархии классов.
При наследовании базовый класс обрастает новыми атрибутами и операциями. В производном классе обычно объявляются новые члены данных, свойства и методы. При работе с объектами программист обычно подбирает наиболее подходящий класс для решения конкретной задачи и создает одного или нескольких потомков от него, которые приобретают способность делать не только то, что заложено в родителе. Дружественные функции позволяют производному классу получить доступ ко всем членам данных внешних классов.
Кроме того, производный класс может перегружать (overload)
наследуемые методы в том случае, когда их работа в базовом классе не подходит потомку. Использование перегрузки в ООП всячески поощряется, хотя в прямом понимании значения этого слова перегрузок обычно избегают. Говорят, что метод перегружен, если он ассоциируется с более чем одной одноименной функцией. Обратите внимание, что механизм вызовов перегруженных методов в иерархии классов полностью отличается от вызовов переопределенных функций. Перегрузка и переопределение - это разные понятия. Виртуальные методы
используются для переопределения функций базового класса.
Чтобы применить концепцию наследования, к примеру, с часами, положим, что следуя принципу наследования, фирма "Casio" решила выпустить новую модель, дополнительно способную, скажем, произносить время при двойном нажатии любой из существующих кнопок. Вместо того, чтобы проектировать заново модель говорящих часов (новый класс, в терминологии ООП), инженеры начнут с ее прототипа (произведут нового потомка базового класса, в терминологии ООП). Производный объект унаследует все атрибуты и функциональность родителя. Произносимые синтезированным голосом цифры станут новыми членами данных потомка, а объектные методы кнопок должны быть перегружены, чтобы реализовать их дополнительную функциональность. Реакцией на событие двойного нажатия кнопки станет новый метод, который реализует произнесение последовательности цифр (новых членов данных), соответствующей текущему времени. Все вышесказанное в полной мере относится к программной реализации говорящих часов.
3.4 Разработка классов
В классы разрабатываются для достижения определенных целей. Чаще всего программист начинает с нечетко очерченной идеи, которая постепенно, по мере разработки проекта, пополняется деталями. Иногда дело заканчивается несколькими классами, весьма похожими друг на друга. Чтобы избежать подобного дублирования кодов в классах, следует разбить их на две части, определив общую часть в родительском классе, а отличающиеся оставить в производных.
Объявление класса должно предшествовать его использованию. Как правило, прикладной программист пользуется готовыми базовыми классами, причем ему вовсе не обязательно разбираться во всех спецификациях и во внутренней реализации. Однако, чтобы использовать базовый класс C++, надо обязательно знать какие члены данных и методы вам доступны (а если применяется компонента C++Builder - еще и предоставляемые свойства и события).
3.4.1 Объявление базового класса
C++Builder дает вам возможность объявить базовый класс, который инкапсулирует имена своих свойств, данных, методов и событий. Помимо способности выполнять свою непосредственную задачу объектные методы получают определенные привилегии доступа к значениям свойств и данных класса.
Каждое объявление внутри класса определяет привилегию доступа к именам класса в зависимости от того, в какой секции имя появляется. Каждая секция начинается с одного из ключевых слов: private, protected и public. Листинг 3.1 иллюстрирует обобщенный синтаксис объявления базового класса.
class className
private:
<приватные члены данных> <приватные конструкторы> <приватные методы>
protected:
<защищенные члены данных> <защищенные конструкторы> <защищенные методы>
public:
<общедоступные свойства> <общедоступные члены данных> “збщедоступные конструкторы> <общедоступный деструктор> общедоступные методы>
Листинг 3.1. Объявление базового класса.
Таким образом, объявление базового класса на C++ предоставляет следующие права доступа и соответствующие области видимости:
• Приватные private имена имеют наиболее ограниченный доступ, разрешенный только методам данного класса. Доступ производных классов к приватным методам базовых классов запрещен.
• Защищенные protected имена имеют доступ, разрешенный методам данного и производных от него классов.
• Общедоступные public имена имеют неограниченный доступ, разрешенный методам всех классов и их объектов.
Следующие правила применяются при образовании различных секций объявления класса:
1. Секции могут появляться в любом порядке, а их названия могут встречаться повторно.
2. Если секция не названа, компилятор считает последующие объявления имен класса приватными. Здесь проявляется отличие объявлений класса и структуры - последняя рассматривается по умолчанию как общедоступная.
3. По мере возможности не помещайте члены данных в общедоступную секцию, если только вы действительно не хотите разрешить доступ к ним отовсюду. Обычно их объявляют защищенными, чтобы разрешить доступ только методам производных классов.
4. Используйте методы для выборки, проверки и установки значений свойств и членов данных.
5. Конструкторы и деструкторы являются специальными функциями, которые не возвращают значения и имеют имя своего класса. Конструктор строит объект данного класса, а деструктор его удаляет.
6. Методы (так же как конструкторы и деструкторы), которые содержат более одной инструкции C++, рекомендуется объявлять вне класса.
Листинг 3.2 представляет попытку наполнить объявление базового класса некоторым конкретным содержанием. Отметим характерное для компонентных классов C++Builder объявление свойства Count в защищенной секции, а метода SetCount, реализующего запись в член данных FCount -
в приватной секции.
class TPoint { private:
int FCount; // Приватный член данных void _fastcall SetCount(int Value);
protected:
_property int Count = // Защищенное свойство
{ read= FCount, write=SetCount };
double x; // Защищенный член данных
double у; // Защищенный член данных public:
TPoint(double xVal, double yVal); // Конструктор |
double getX(); |
double getY() ;
Листинг 3.2. Объявление базовой компоненты TPoint.
Объявления и определения методов хранятся в разных файлах (с расширениями .h и .срр, соответственно). Листинг 3. 3 показывает, что когда методы определяются вне класса, их имена следует квалифицировать. Синтаксис такой квалификации метода, определяющей его область видимости, имеет следующий вид:
<имя класса>::<имя метода>
TPoint::TPoint(double xVal, double yVal)
( // Тело конструктора
void _fastcall TPoint::SetCount( int Value )
{
l
if ( Value i= FCount ) // Новое значение члена данных? {
FCount = Value; // Запись нового значения Update(); // Вызов метода Update } }
double TPoint::getX()
// Тело метода getX,
квалифицированного в классе^TPoint
}
Листинг 3.3. Определения конструктора и методов вне класса.
После того, как вы объявили класс, его имя можно использовать как идентификатор типа при объявлении объекта этого класса (например,
TPoint* MyPoint;).
3.4.2 Конструкторы и деструкторы
Как следует из названий, конструктор - это метод, который строит в памяти объект данного класса, а деструктор - это метод, который его удаляет. Конструкторы и деструкторы отличаются от других объектных методов следующими особенностями:
• Имеют имя, идентичное имени своего класса.
• Не имеют возвращаемого значения.
• Не могут наследоваться, хотя производный класс может вызывать конструкторы и деструкторы базового класса.
• Автоматически генерируются компилятором как public, если не были вами объявлены иначе.
• Автоматически вызываются компилятором, чтобы гарантировать надлежащее создание и уничтожение объектов классов.
• Могут содержать неявные обращения к операторам new и delete,
если объект требует выделения и уничтожения динамической памяти.
Листинг 3.4 демонстрирует обобщенный синтаксис объявлений конструкторов и деструктора.
class className
{ public:
// Другие члены данных className(); // Конструктор по умолчанию | className(<список параметров;-);// Конструктор с аргументами | className(const className&); // Конструктор копирования
// Другие конструкторы "className(); // Деструктор
// Другие методы };
Листинг 3.4. Объявления конструкторов и деструктора.
Класс может содержать любое число конструкторов, в том числе ни одного. Конструкторы не могут быть объявлены виртуальными. Не помещайте все конструкторы в защищенной секции и старайтесь уменьшить их число, используя значения аргументов по умолчанию. Существует три вида конструкторов:
• Конструктор по умолчанию не имеет параметров. Если класс не содержит ни одного конструктора, компилятор автоматически создаст один конструктор по умолчанию, который просто выделяет память при создании объекта своего класса.
• Конструктор с аргументами позволяет инициализировать объект в момент его создания - вызывать различные функции, выделять динамическую память, присваивать переменным начальные значения и т.п.
• Конструктор копирования предназначен для создания объектов данного класса путем копирования данных из другого, уже существующего объекта этого класса. Такие конструкторы особенно целесообразны для создания копий объектов, которые моделируют динамические структуры данных. Однако, по умолчанию компилятор создает так называемые конструкторы поверхностного копирования (shallow copy constructors), которые копируют только члены данных. Поэтому если какие-то члены данных содержат указатели, сами данные не будут копироваться. Для реализации "глубокого" копирования в код конструктора надо включить соответствующие инструкции.
Класс может объявить только один общедоступный деструктор, имени которого, идентичному имени своего класса, должен предшествовать знак ~ (тильда). Деструктор не имеет параметров и может быть объявлен виртуальным. Если класс не содержит объявления деструктора, компилятор автоматически создаст его.
Обычно деструкторы выполняют операции, обратные тем, что выполняли соответствующие конструкторы. Если вы создали объект класса файл, то в деструкторе этот файл, вероятно, будет закрываться. Если конструктор класса выделяет динамическую память для массива данных (с помощью оператора new), то деструктор, вероятно, освободит выделенную память (с помощью оператора delete) и т.п.
3.4.3 Объявление производных классов
C++ Builder дает возможность объявить производный класс, который наследует свойства, данные, методы и события всех своих предшественников в иерархии классов, а также может объявлять новые характеристики и перегружать некоторые из наследуемых функций. Наследуя указанные характеристики базового класса, можно заставить порожденный класс расширить, сузить, изменить, уничтожить или оставить их без изменений.
Наследование позволяет повторно использовать код базового класса в экземплярах производного класса. Концепция повторного использования
имеет параллель в живой природе: ДНК можно рассматривать как базовый материал, который каждое порожденное существо повторно использует для воспроизведения своего собственного вида. <
Листинг 3.5 иллюстрирует обобщенный синтаксис объявления производного класса. Порядок перечисления секций соответствует расширений привилегий защиты и областей видимости заключенных в них элементов: от наиболее ограниченных к самым доступным.
class className : [^спецификатор доступа;”] parentClass {
<0бъявления дружественных классов>
private:
<приватные члены данных>
<приватные конструкторы>
<приватные методы> protected:
<защищенные члены данных>
<защищенные конструкторы>
<защищенные методы> public:
<общедоступные свойства>
<общедоступные члены данных>
<общедоступные конструкторы>
<общедоступный деструктор>
<общедоступные методы> _published:
•<общеизвестные свойства>
<общеизвестные члены данных>
<Объявления дружественных функций>
Листинг 3.5. Объявление производного класса.
Отметим появление новой секции с ключевым словом _published - дополнение, которое C++Builder вводит в стандарт ANSI C++ для объявления общеизвестных элементов компонентных классов. Эта секция отличается от общедоступной только тем, что компилятор генерирует информацию RTTI о свойствах, членах данных и методах объекта и C++Builder организует передачу этой информации Инспектору объектов во время исполнения программы. В главе 6 мы остановимся на этом более подробно.
Помимо способности выполнять свою непосредственную задачу объектные методы получают определенные привилегии доступа к значениям свойств и данных других классов.
Когда класс порождается от базового, все его имена в производном классе автоматически становятся приватными по умолчанию. Но его легко изменить, указав следующие спецификаторы доступа базового класса:
• private. Наследуемые (т.е. защищенные и общедоступные) имена базового класса становятся недоступными в экземплярах производного класса.
• public. Общедоступные имена базового класса и его предшественников будут доступными в экземплярах производного класса, а все защищенные останутся защищенными.
Можно порождать классы, которые расширяют
возможности базового класса:
он вполне приемлем для вас, однако содержит функцию, требующую небольшой доработки. Написание заново нужной функции в производном классе является пустой тратой времени. Вместо этого надо повторно использовать код в базовом классе, расширяя его настолько, насколько это необходимо. Просто переопределите в производном классе ту функцию базового класса, которая вас не устраивает. Подобным образом можно порождать классы, которые ограничивают возможности базового класса: он вполне приемлем для вас, но делает что-то лишнее.
Рассмотрим применение методик расширения и ограничения характеристик на примере создания разновидностей объекта кнопки - типичных производных классов, получаемых при наследовании базовой компоненты TButtonControl из Библиотеки Визуальных Компонент
C++Builder. Кнопки различного вида будут часто появляться в диалоговых окнах графического интерфейса ваших программ.

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

Чтобы создать простую кнопку без рамки (Рис. 3.2), нужно построить производный класс
SimpleButton, использовав в качестве родительского TButtonControl, и перегрузить метод
Draw с ограничением его функциональности (Листинг 3.6)
class SimpleButton: public : TButtonControl { public:
SimpleButton(int x, int y) ;
void Draw() ;
-SimpleButton() { }
};
SimpleButton::SimpleButton(int x, int y) :
TButtonControl(x, y) { }
void SimpleButton::Draw()
{ ; outline->Draw();
}
Листинг 3.6. Ограничение характеристик базового класса.
Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.

Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод
Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.
Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод
SimpleButton:-.Draw () отображает его. :
class Text { public:
Text(int x, int y, char* string) { } void Draw() { } };
class TextButton: public : TButtonControl {
Text* title;
public:
TextButton(int x, int y, char* title);
void Draw();
-TextButton() { } );
TextButton::TextButton(int x, int y, char* caption)
TButtonControl(x, y) {
title = new Text(x, y, caption);
}
void TextButton::Draw () {
TextButton::Draw() ;
title->Draw() ;
}
Листинг 3.7. Расширение характеристик базового класса.
В заключение раздела с изложением методики разработки базовых и производных классов приводится фрагмент C++ программы (Листинг 3.8), в которой объявлена иерархия классов двух простых геометрических объектов: окружности и цилиндра.
Программа составлена так, чтобы внутренние значения переменных г-радиус окружности и h-высота цилиндра определяли параметры создаваемых объектов. Базовый класс
Circle моделирует окружность, а производный класс Cylinder моделирует цилиндр.
class SimpleButton: public : TButtonControl { public:
SimpleButton (int x, int y) ;
void Draw() ;
-SimpleButton() { } );
SimpleButton::SimpleButton(int x, int y) :
TButtonControl(x, y) { }
I void SimpleButton::Draw()
I { i outline->Draw();
1 )
Листинг 3.6. Ограничение характеристик базового класса.
Единственная задача конструктора объекта для SimpleButton - вызвать базовый класс с двумя параметрами. Именно переопределение метода SimpleButton: : Draw () предотвращает вывод обводящей рамки кнопки (как происходит в родительском классе). Естественно, чтобы изменить код метода, надо изучить его по исходному тексту базовой компоненты TButtonControl.

Теперь создадим кнопку с пояснительным названием (Рис. 3.3). Для этого нужно построить производный класс TextButton из базового TButtonControl, и перегрузить метод
Draw с рас-Рис. 3.3. Кнопка с текстом, ширением его функциональности.
Листинг 3.7 показывает, что объект названия title класса Text создается конструктором TextButton, а метод
SimpleButton: : Draw () отображает его.
const double pi = 4 * atan(l);
class Circle { protected:
double r ;
public:
Circle (double rVal =0) : r(rVal) {}
void setRadius(double rVal) { r = rVal; }
double getRadiusO { return r; } .double Area() { return
pi*r*r; }
void showData() ;
};
class Cylinder : public Circle { protected:
double h;
public:
Cylinder(double hVal = 0, double rVal = 0)
: getHeight(hVal), Circle(rVal) { }
void setHeight(double hVal) { h = hVal; }
double getHeight() { return h; }
double Area() { return 2*Circle::Area()+2*pi*r*h; }
void showData() ;
void Circle::showData() {
cout “ "Радиус окружности = " “ getRadius() “ endl
“ "Площадь круга = " “ Area О “ endl “ endl;
}
void Cylinder::showData()
{
cout “ "Радиус основания = " “ getRadius() “ endl
“ "Высота цилиндра = " “ getHeight() “ endl
“ "Площадь поверхности = " “ Area () “ endl;
}
void main()
{
Circle circle(2) ;
Cylinder cylinder(10, 1);
circle.showData () ;
cylinder.showData() ;
Листинг 3.8. Простая иерархия классов окружности и цилиндра.
Объявление класса Circle содержит единственный член данных r, конструктор и ряд методов. При создании объекта конструктор инициализирует член данных r начальным значением радиуса окружности. Отметим новый синтаксис конструктора: при вызове он может обратиться к конструктору базового класса, а также к любому члену данных, указанному после двоеточия. В нашем случае член данных r "создается" обращением к нему с параметром rVal и инициализируется нулевым значением.
Метод setRadius
устанавливает, a getRadius возвращает значение члена данных г. Метод Area возвращает площадь круга. Метод
showData выдает значения радиуса окружности и площади круга.
Класс Cylinder,
объявленный как производный от Circle, содержит единственный член данных h,
конструктор и ряд методов. Этот класс наследует член данных г для хранения радиуса основания цилиндра и методы setRadius и getRadius. При создании объекта конструктор инициализирует члены данных г и h начальными значениями. Отметим новый синтаксис конструктора: в нашем случае член данных h инициализируется значением аргумента hVal, а член данных г - вызовом конструктора базового класса
Circle с аргументом rVal.
Функция setHeight устанавливает, a getHeight возвращает значение члена данных h. Circle::Area перегружает унаследованную функцию базового класса, чтобы теперь возвращать площадь поверхности цилиндра. Функция showData выдает значения радиуса основания, высоты и площади поверхности цилиндра.
Функция main
создает окружность circle класса Circle с радиусом 2 и цилиндр cylinder класса Cylinder с высотой 10 и радиусом основания 1, а затем обращается к showData для вывода параметров созданных объектов:
Радиус окружности = 2 Площадь круга = 12.566
Радиус основания = 1 Высота цилиндра = 10 Площадь поверхности = 69.115
3.5 Полиморфизм
Слово полшюрфизм от греческих слов poly (много) и morphos (форма) означает множественность форм. Полиморфизм - это свойство родственных объектов (т.е. объектов, классы которых являются производными от одного родителя) вести себя по-разному в зависимости от ситуации, возникающей в момент выполнения программы. В рамках ООП программист может влиять на поведение объекта только косвенно, изменяя входящие в него методы и придавая потомкам отсутствующие у родителя специфические свойства.
Для изменения метода необходимо перегрузить его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющие разную кодовую реализацию и, следовательно, придающие объектам разное поведение. Например, в иерархии родственных классов геометрических фигур (точка, прямая линия, квадрат, прямоугольник, окружность, эллипс и т.д.) каждый класс имеет метод Draw, который отвечает за надлежащий отклик на событие с требованием нарисовать эту фигуру.
Благодаря полиморфизму, потомки могут перегружать общие методы родителя с тем, чтобы реагировать специфическим образом на одно и то же событие.
3.5.1 Виртуальные функции
В ООП полиморфизм достигается не только описанным выше механизмом наследования и перегрузки методов родителя, но и виртуализацией,
позволяющей родительским функциям обращаться к функциям потомков. Полиморфизм реализуется через архитектуру класса, но полиморфными могут быть только функции-члены.
В C++ полиморфная функция привязывается к одной из возможных одноименных функций только в момент исполнения, когда ей передается конкретный объект класса. Другими словами, вызов функции в исходном тексте программы лишь обозначается, без точного указания на то, какая именно функция вызывается. Такой процесс известен как позднее связывание. Листинг 3.9 показывает, к чему может привести не полиморфное поведение обычных функций-членов.
I class Parent { public:
double Fl(double x) { return x*x; }
double F2(double x) { return Fl(x)/2; }
class Child : public Parent { public:
double Fl(double x) { return x*x*x; } };
void main() {
Child child;
cout “ child.F2(3) “ end1;
}
Листинг 3.9. Неопределенное позднее связывание.
Класс Parent
содержит функции-члены Fl и F2, причем F2 вызывает, El,. Класс Child, производный от класса Parent,
наследует функцию F2, однако переопределяет функцию Fl. Вместо ожидаемого результата 13.5 программа выдаст значение 4.5. Дело в том, что компилятор оттранслирует выражение child. F2 (3) в обращение к унаследованной функции Parent: :F2, которая в свою очередь вызовет Parent: :F1, а не Child: :F1, что поддержало бы полиморфное поведение.
C++ однозначно определяет позднее связывание в момент выполнения и обеспечивает полиморфное поведение функций посредством их виртуализации. Листинг 3.10 обобщает синтаксис объявления виртуальных функций в базовом и производном классах.
jclass classNamel {
// Другие функции-члены
virtual returnType functionName(<список параметров>) ;
};
class className2 : public classNamel {
// Другие функции-члены
virtual returnType functionName(
};
Листинг 3.10. Объявление виртуальных функции в иерархии классов.
Чтобы обеспечить полиморфное поведение функции F1 в объектах классов Parent и Child, необходимо объявить ее виртуальной. Листинг 3.11 содержит модифицированный текст программы.
class Parent {
public:
virtual double F1(double x) { return x*x; }
double F2(double x) { return Fl(x)/2; }
};
class Child : public Parent { public:
virtual double F1(double x) { return x*x*x; }
);
void main() {
Child child;
cout “ child.F2(3) “ endl;
}
Листинг 3.11. Позднее связывание виртуальных функций.
Теперь программа выдаст ожидаемый результат 13.5. Компилятор оттранслирует выражение child. F2 (3) в обращение к унаследованной функции Parent: : F2, которая в свою очередь вызовет переопределенную виртуальную функцию потомка Child: :F1.
Если функция объявлена в базовом классе как виртуальная, ее можно переопределять только в производных классах и обязательно с тем же списком параметров. Если виртуальная функция производного класса изменила список параметров, то ее версия в базовом классе (и во всех его предшественниках) станет недоступной. Поначалу такая ситуация может показаться тупиковой - и на деле оказывается таковой в языках ООП, которые не поддерживают механизм перегрузки. C++ решает проблему, допуская использовать не виртуальные, а перегруженные функции с тем же именем, но с другим списком параметров.
Функция, объявленная виртуальной, считается таковой во всех производных классах - независимо от того, объявлена ли она в производных классах с ключевым словом virtual, или нет.
Используйте виртуальные функции для реализации специфического поведения объектов данного класса. Не объявляйте все ваши методы виртуальными - это приведет к дополнительным вычислительным затратам при их вызовах. Всегда объявляйте деструкторы виртуальными. Это обеспечит полиморфное поведение при уничтожении объектов в иерархии классов.
3.5.2 Дружественные функции
Дружественные функции, хотя и не принадлежат какому-то классу, однако имеют доступ ко всем приватным и защищенным членам данных внешних классов. Листинг 3.12 обобщает синтаксис объявления дружественных функций с помощью ключевого слова friend перед указанием возвращаемого типа.
class className
( 1
public: ~
className(); // Конструктор по умолчанию // Другие конструкторы friend
returnType friendFunction(<список параметров>) ;
};
Листинг 3.12. Объявление дружественных функций.
Если обычные функции-члены имеют автоматический доступ ко всем данным своего класса за счет передачи скрытого параметра - указателя this на экземпляр класса, то дружественные функции требуют явной спецификации этого параметра. Действительно, объявленная в классе Х дружественная функция F не принадлежит этому классу, а, значит, не может быть вызвана операторами х. F и xptr->F (где х- экземпляр класса X, a xptr- его указатель). Синтаксически корректными будут обращения F (& х) или F (xpt r).
Таким образом, дружественные функции могут решать задачи, реализация которых посредством функций-членов класса оказывается неудобной, затруднительной и даже невозможной.
3.6 Новые возможности языка C++
C++Builder обеспечивает не только поддержку последних нововведении стандарта ANSI
C++, но и расширяет язык новыми возможностями. Компоненты, свойства, методы, обработчики событии, а также шаблоны, пространства имен, явные и непостоянные объявления, RTTI и исключения - вся мощь этих средств доступна программистам, использующим C++Builder для визуальной разработки приложений.
Важно понять, что расширения языка никогда не являются самоцелью, и вы по-прежнему сможете компилировать тексты, написанные в рамках стандартного C++. Однако, чтобы воспользоваться в полной мере преимуществами, которые предоставляет C++Builder для технологии быстрой разработки приложений (RAD), вам придется принять введенные расширения языка.
Некоторые из расширений (например, _classid) C++ Builder резервирует, главным образом, для внутреннего использования. Другие расширения совершенно очевидны (_int8, _intl6 и т.д.), и здесь не рассматриваются. Наше внимание будет сфокусировано на наиболее значимых расширениях C++, которые, в основном, относятся к компонентным классам и будут постоянно встречаться как в тексте книги, так и в ваших приложениях, разрабатываемых в среде C++Builder.
3.6.1 Компоненты
Компоненты часто достигают более высокой степени инкапсуляции, нежели стандартные C++ классы. Проиллюстрируем это на простом примере разработки диалога, содержащего кнопку. В типичной C++ программе для Windows нажатие мышью на кнопку приводит к генерации сообщения
WM_LBUTTONDOWN. Это сообщение должно быть "поймано" программой либо в операторе switch, либо в соответствующей строке таблицы откликов (RESPONSE_TABLE), а затем передано процедуре реакции на это сообщение. Так, приложение, написанное в рамках OWL (Object Windows Library), использует макрос
DEFINE_RESPONSE_TABLE1(TEventTestDlgClient, TDialog)
//({TEventTestDlgClientRSP_TBL_BEGIN}}
EV_BN_CLICKED(IDEVENTBUTTON, EventBNClicked),
//({TEventTestDlgClientRSP_TBL_END}}
END_RESPONSE_TABLE;
чтобы ассоциировать событие (сообщение WM_LBUTTONDOWN),
генерируемое кнопкой IDEVENTBUTTON в диалоге TEventTestDlgClient, с функцией реакции EventBNClicked.
C++Builder покончил с этими трудно осваиваемыми программистскими трюками. Компонента кнопки уже запрограммирована так, чтобы реагировать на нажатие кнопки событием OnClick. Все, что надо сделать - это выбрать готовый (или написать собственный) метод и с помощью Инспектора объектов включить его в обработчик данного события.
3.6.1.1 Объявления компонентных классов
Опережающие объявления классов Библиотеки Визуальных Компонент VCL, входящей в состав C++Builder, используют модификатор _declspec:
_declspec(<спецификатор>)
Это ключевое слово может появляться в любом месте перечня объявлений, а не только непосредственно перед модифицируемым объявлением, причем спецификатор принимает одно из следующих значений:
delphiclass используется для опережающего объявления прямых или косвенных производных от VCL класса TObject. Он определяет правила совместимости VCL при обращении с RTTI, конструкторами, деструктором и исключениями.
delphireturn используется для опережающего объявления прямых или косвенных производных от VCL классов
Currency, AnsiString, Variant, TDateTime и Set. Он определяет правила совместимости VCL при обращении с параметрами и возвращаемыми значениями функций-членов.
pascalimplementation указывает, что компонентный класс реализован на Объектном Паскале.
VCL класс имеет следующие ограничения:
• Запрещено наследование виртуальных базовых классов.
• Компонентные классы сами не могут служить базовыми классами для наследования.
• Компонентные объекты создаются в динамической памяти кучи с помощью оператора new.
3.6.1.2 Объявления свойств
C++BuiIder использует модификатор _property для идентификации свойств компонентных классов. Синтаксис описания свойства имеет вид:
property <тип свойства> <имя свойства> = {<список атрибутов>} ;
где список атрибутов содержит перечисление следующих атрибутов свойства:
write = < член данных или метод записи > определяет способ присваивания значения члену данных;
read = < член данных или метод чтения > определяет способ получения значения члена данных;
default = < булева константа > разрешает или запрещает сохранение значения свойства по умолчанию в файле формы с расширением .dim;
stored = < булева константа или функция > определяет способ сохранения значения свойства в файле формы с расширением .dfm.
C++BuiIder использует модификатор
_published для спецификации тех свойств компонент, которые будут отображаться Инспектором объектов на стадии проектирования приложения. Если разработчик компоненты желает разрешить модификацию значения некоторого свойства, оно не объявляется как _published. Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости членов данных, методов и свойств, объявленных как public. Единственное отличие проявляется в том, что во время работы программы Инспектору объектов передается информация RTTI.
3.6.1.3 Объявления обработчиков событий '
C++BuiIder использует модификатор _closure для объявления функции обработчиков событий:
<тип> (_closure *
Это ключевое слово определяет указатель функции с именем name. В отличие от 4-байтового адресного указателя обычной функции (который передается в кодовые регистры CS:IP) 8-байтовый _closure передает еще и скрытый параметр (непостоянный указатель
this на экземпляр текущего класса).
Введение 8-байтовых указателей делает возможным не только вызывать некоторую функцию определенного класса, но и обращаться к функции в определенном экземпляре этого класса. Эта способность была заимствована из Объектного Паскаля, а _closure оказался жизненно необходимым для реализации механизма событий в Библиотеке Визуальных Компонент.
3.6.1.4 Объявления автоматизированных свойств и методов
OLE Automation - это разновидность механизма связи Object Linking and Embedding,
позволяющего приложениям для Windows управлять друг другом. Автоматизированный OLE контроллер является приложением, которое способно автоматизировать другое приложение - автоматизированный OLE сервер. По существу, OLE Automation представляет собой протокол обмена, посредством которого контроллер управляет действиями сервера. Все компонентные
OLE объекты, экспортируемые автоматизированным сервером своим контроллерам, являются производными от базового класса TAutoObject.
При создании автоматизированного сервера необходимо определить его интерфейс с контроллером, содержащий объявления свойств и методов OLE
объекта с тем, чтобы контроллер получил к ним доступ. Никогда не удаляйте уже включенные в интерфейс свойства и методы - это приведет к ошибкам в работе существующих контроллеров. C++Builder использует модификатор _automated в объявлениях автоматизированного метода (Листинг 3.13). Это объявление может заканчиваться еще одним новым ключевым словом _dispid, которое ассоциирует значение идентификатора диспетчеризации OLE Automation с данной функцией.
class MyOLEAutoClass : TAutoObject
{ _automated: void_fastcall function(void) _dispid(1000); };
Листинг 3.13. Объявление автоматизированного метода.
Правила видимости, определяемые этим ключевым словом, не отличаются от правил видимости, объявленных в секции public. Единственное отличие проявляется в том, что генерируемая компилятором информация о типах свойств и методов
OLE Automation, делает возможным создание автоматизированных серверов.
C++Builder поставляется вместе с примером (удивительным по внешней простоте и лаконичности кода) взаимодействия приложений контроллера и сервера посредством механизма OLE Automation. Этот пример проливает свет на данную методику, изложенную в системной документации весьма сбивчиво и туманно. Автоматизированный сервер
Autosrv демонстрирует использование:
• компонентных объектов TAutoObject и TAutoClassInfo;
• метода RegisterAutoClass автоматизированного объектного класса для регистрации сервера;
• свойств и методов, объявленных с ключевым словом _automated. Контроллер Autocon управляет сервером и демонстрирует:
• установку и выборку свойств объекта сервера;
• использование вариантов (детальная информация об типе Variant содержится в параграфе 3.6.1.6 "Расширенные типы данных Delphi".
Чтобы испытать на практике, что дает взаимодействие приложений OLE Automation, выполните следующие действия:
=> По команде главного меню File [ Open Project откройте диалог выбора проектов. => Войдите в каталог \...\CBuilder\Examples\Apps\Autosrv => Выберите проектный файл с именем Autosrv и нажмите кнопку
Open.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки автоматизированного сервера. => Снова откройте диалог выбора проектов, войдите в каталог
\...\CBuilder\Examples\Apps\Autocon,
выберите проектный файл с именем
Autocon и нажмите кнопку Open. => Запустите процесс компиляции и сборки контроллера.
Вводя сообщения в области редактируемого ввода и нажимая кнопки контроллера, вы можете моделировать некоторые процессы управления сервером. получая результаты, которые отображены на нижеследующих рисунках:

Рис. 3.4. Контроллер готовит сообщение и посылает его серверу.

Рис. 3.5. Контроллер принимает сообщение, "обработанные" сервером.

Рис. 3.6. Контроллер снимает старое сообщение с сервера.
Чтобы узнать, как реализовано такое взаимодействие, необходимо разобраться в текстах модулей автоматизированного сервера (Листинг 3.14 и Листинг 3.15) и контроллера (Листинг 3.16 и Листинг 3.17), которые заслуживают того, чтобы привести их целиком, снабдив необходимыми комментариями. Сервер содержит единственный объект Edit1
компоненты TEdit для редактируемого ввода и приема сообщений от контроллера в свойство
Text. Контроллер записывает введенное пользователем сообщение в свойство Text
своего объекта Edit1, а управляет сервером посредством трех кнопок Buttoni, Button2 и Buttons компоненты TButton (с названиями "Послать", "Принять" и "Очистить").
#ifndef Auto2H
#define Auto2H
ftinclude
#include
// Класс сервера ButtonServer.
производный от TAutoOb-iect class
ButtonServer : public TAutoObject {
private: // Приватные свойства и методы AnsiString _fastcall
GetEditStr() ;
void _fastcall SetEditStr(AnsiString NewVal);
int _fastcall GetEditNum() ;
void _fastcall SetEditNum(int NewVal);
_automated: // Автоматизированные свойства и методы property AnsiString EditStr = // свойство типа AnsiString / / с атрибутами функции чтения/записи значении {read=GetEditStr, write=SetEditStr} ;
property int EditNum = //
свойство типа int / / с атрибутами функций чтения/записи значений {read=GetEditNum, write=SetEditNum); :
void _fastcall Clear (); //
метод очистки сообщения void_fastcall
SetThreeStr // метод составления текстового / / сообщения из строк в параметрах si, s2, s3 типа AnsiString (AnsiString si, AnsiString s2, AnsiString s3);
void _fastcall SetThreeNum // метод составления текстового / / сообщения из чисел в параметрах п 1, п2, п3 типа int (int п1, int п2, int п3) ;
public: // Общедоступные свойства и методы
_fastcall ButtonServer О; // конструктор объекта сервера
};
//----------------------------------------------------------
#endif
Листинг 3.14. Файл объявлений Auto1.h модуля автоматизированного сервера.
#include
#pragma hdrstop
#undef RegisterClass
#include "Auto2.h"
#include "Autol.h"
int Initialization();
static int Initializer = Initialization();
// Создание объекта автоматизированного сервера _fastcall
ButtonServer::ButtonServer() : TAutoObject()
{ }
// Чтение текстового значения автоматизированного свойства AnsiString _fastcall ButtonServer::GetEditStr() { return
Forml->Editl->Text;
// Запись текстового значения автоматизированного свойства void _fastcall ButtonServer:-.SetEditStr (AnsiString NewVal) { Forml->Editl->Text = NewVal;
} // Чтение численного значения автоматизированного свойства
int _fastcall ButtonServer: :GetEditNum()
( int val;
sscanf(Forml->Editl->Text.c_str(), "%d", &val) ;
return val;
// Запись численного значения автоматизированного свойства void _fastcall ButtonServer::SetEditNum(int NewVal) { Forml->Editl->Text = NewVal;
}
// Очистка значения автоматизированного свойства void_fastcall
ButtonServer::Clear() { Forml->Editl->Text = "";
// Составление текстового значения свойства из трех строк void _fastcall ButtonServer::SetThreeStr (AnsiString si, AnsiString s2, AnsiString s3) { Forml->Editl->Text = si + ", " + s2 + ", " + s3;
) // Составление текстового значения свойства из трех чисел
void _fastcall ButtonServer::SetThreeNum (int nl, int n2, int
n3) { AnsiString sl(nl), s2(n2), s3(n3);
Forml->Editl->Text = si + ", " + s2 + ", " + s3;
// Регистрация объекта автоматизированного сервера void _fastcall RegisterButtonServer() { TAutoClassInfo AutoClassInfo;
// Инициализация полей структуры типа TAutoClassInfo AutoClassInfo.AutoClass = _classid(ButtonServer) ;
AutoClassInfo.ProgID = "BCBAutoSrv.EditServer" ;
AutoClassInfo.ClassID =
"{61E124E1-C869-11CF-9EA7-OOA02429B18A}";
AutoClassInfo.Description =
"Borland C++Builder AutoSrv Example Server Class";
AutoClassInfo.Instancing = acMultiInstance;
// Регистрация класса автоматизированного сервера Automation->RegisterClass(AutoClassInfo) ;
}
// Инициализация объекта автоматизированного сервера
int Initialization()
{ RegisterButtonServer() ;
return 0;
}
Листинг 3.15. Кодовый файл Auto2cpp модуля автоматизированного сервера.
#ifndef AutolH #efine AutolH
class TFormI : public TForm {
published: // IDE-managed Components
TEdit *Editl;
TButton *Buttonl
TButton *Button2
TButton *Button3
TLabel * Label 1;
void _fastcall ButtonlClick(TObject *Sender) ;
void _fastcall Button2Click(TObject * Sender);
void _fastcall Button3Click(TObject * Sender);
private: // User declarations
Variant AutoServer;
public: // User declarations
virtual _fastcall TFormI(TComponent *0wner) ;
extern TFormI *Forml;
#endif
Листинг 3.16. Файл объявлений Auto1.h, все строки которого (за исключением выделенной строки объявления варианта) C++Builder генерирует автоматически при размещении компонент на форме контроллера.
include
#include "autol.h"
#pragma resource "*.dfm" #ormI *Forml;
_fastcall TFormI::TFormI(TComponent *0wner) : TForm(Owner) { try
{ // Создание автоматизированного сервера как объекта OLE AutoServer = CreateOleObject("BCBAutoSrv.Edit Server");
}
catch (...) { // Обработка исключения
ShowMessage("Сначала компилируйте и запустите AutoSrv");
Application-terminate () ;
}
} // Обработчик события при нажатии кнопки Buttoni
void _fastcall TFormI::ButtonlClick(TObject *Sender) { // Установка автоматизированного свойства сервера AutoServer.OlePropertySet("EditStr", Editl->Text);
)
// Обработчик события при нажатии кнопки Button2
void _fastcall TFormI::Button2Click(TObject *Sender)
{ // Чтение автоматизированного свойства сервера
Editl->Text = AutoServer.OlePropertyGet("EditStr");
// Обработчик события при нажатии кнопки Button3 void _fastcall
TFormI::Button3Click(TObject *Sender) { //
Очистка автоматизированного свойства сервера AutoServer.OleProcedure("Clear") ;
Листинг 3.17. Кодовый файл Auto1.cpp модуля контроллера.
3.6.1.5 Быстрый вызов функций
При объявлении функций, параметры которых передаются через процессорные регистры, используется модификатор _fastcall:
<возвращаемый тип> _fastcall
Это ключевое слово определяет, что первые три типизированных параметра функции с именем name (слева направо по списку) передаются не через стек, а через процессорные регистры AX, BX и DX. Регистры не используются, если значение параметра не умещается в регистр, т.е. при передаче через параметр чисел с плавающей точкой, структур и функций.
Строго говоря, быстрый вызов функций не является прерогативой компилятора
C++Builder. В предыдущей главе я уже обращал внимание читателя на использование _fastcall в объявлениях функций обработки событий, которые C++Builder генерирует автоматически.
3.6.1.6 Расширенные типы данных Delphi
C++Builder не позволяет посредством известного ключевого слова typedef просто переопределить некоторые сложные типы данных Объектного Паскаля. C++Builder реализует такие расширенные типы в виде обычных или шаблонных классов
(template class). Каждый такой класс содержит все необходимые конструкторы, деструкторы, свойства и объектные методы. Многие компоненты VCL используют реализацию расширенных типов, а кроме того, они требуются при разработке новых компонент на базе оригиналов из Delphi.
Ниже приводится сводная таблица встроенных типов Delphi и соответствующих им типов C++Builder:
| Delphi | Длина и значения | C++Builder | Реализация |
| Shortint | 8-битовое целое | char | typedef |
| Smallint | 16-битовое целое | short | typedef |
| Longint | 32-битовое целое | long | typedef |
| Byte | 8-битовое целое без знака | unsigned char | typedef |
| Word | 16-битовое целое без знака | unsigned short | typedef |
| Integer | 32-битовое целое | int | typedef |
| Cardinal | 32-битовое целое без знака | unsigned long | typedef |
| Boolean | true/false | bool | typedef |
| ByteBool | true/false или 8-битовое целое без знака | unsigned char | typedef |
| WordBool | true/false или 16-битовое целое без знака |
unsigned short | typedef |
| LongBool | true/false или 32-битовое целое без знака |
unsigned long | typedef |
| AnsiChar | 8-битовый символ без знака | unsigned char | typedef |
| WideChar | Слово - символ Unicode | wchar t | typedef |
| Char | 8-битовый символ | char | typedef |
| String | Текстовая строка Delphi | AnsiString | typedef |
| Single | 32-битовое плавающее число | float | typedef |
| Double | 64-битовое плавающее число | double | typedef |
| Extended | 80-битовое плавающее число | long double | typedef |
| Real | 32-битовое плавающее число | float | typedef |
| Comp | 64-битовое плавающее число | double | typedef |
| Pointer | 32-битовый указатель | void * | typedef |
| PChar | 32-битовый указатель на символы без знака | unsigned char * | typedef |
| PansiChar | 32-битовый указатель на ANSI символы без знака | unsigned char * | typedef |
| Set | Множество 1..32 байт | Set |
template class |
| AnsiString | Текстовая строка Delphi | AnsiString | class |
| Variant | Вариантное значение, 16 байт | Variant | class |
| TdateTime | Значение даты и времени, 64-битовое плавающее число | TDateTime | class |
| Currency | Валюта, 64-битовое плавающее число, 4 цифры после точки | Currency | class |
Set (множество) служит для спецификации типа параметров объектных методов VCL
или типа значений, возвращаемых этими методами. C++Builder реализует этот встроенный тип Delphi с помощью одноименного шаблонного класса Set
type тип элементов множества (обычно, int. char или enum):
minval минимальное (положительное) значение, которое могут принимать элементы множества;
maxval максимальное (не более 255) значение, которое могут принимать элементы множества.
Подстановка разных значений параметров приводит к созданию экземпляров шаблонного класса Set
различных типов, поэтому оператор сравнения if (si == s 2) объектов, описанных как
Set
Set
вызовет ошибку компиляции. Для создания множественных экземпляров типа Set необходимо использовать ключевое слово typedef. Например, объявив typedef Set
s1 “ 'А' “ 'В' “ 'С' ;
s2 “ 'X' “ 'Y' “ '?.' ;
AnsiString используется для спецификации типа текстовых строк произвольной длины, имеющих следующую характерную внутреннюю структуру:
C++Builder реализует этот встроенный тип
Delphi как одноименный класс. Если при создании экземпляров данного класса не указано начальное значение строки, конструктор AnsiString автоматически присваивает всем переменным нулевые значения. Среди методов данного класса отметим наиболее часто вызываемый метод с str (), который возвращает указатель на символьный массив, оканчивающийся 0 и содержащий копию символов, заключенных в исходном объекте типа AnsiString. Листинг 3.18 иллюстрирует "применение методов чтения и записи значения члена данных FNames свойства Names типа AnsiString в экземпляре MyFamily объявленного компонентного класса Family". Предыдущее предложение кажется полной абракадаброй, если не проникнуться терминологией объектно-ориентированного программирования. Рассматривайте его как своеобразное словесное упражнение по краткому курсу ООП.
#include
{
private:
AnsiString FNames[10]; // массив имен AnsiString GetName(int Index); // метод чтения void
SetName(int, AnsiString); // метод записи public:
_property AnsiString Names[int Index] =
{read=GetName, write=SetName} ;
Family(){} // .конструктор -Family(){) // деструктор
};
AnsiString Family::GetName(int i)
{
return FNames[i]; // GetName
возвращает значение }
void Family::SetName(int i, const AnsiString s) { FNames[i]=s; // SetName присваивает значение
}
void main()
{
Family My Family; // создание объекта MyFamily // Инициализация 4- х строк массива имен методом SetName() MyFamily.Names[0]="Иван" ;
MyFamily.Names[1]="Анна" ;
MyFamily.Names[2]="Марья";
MyFami ly. Names [ 3 ] = "
Андрей " ;
// Вывод 4-х строк массива имен методом GetName() for (int i=0; i<=3; i++)
puts(MyFamily.Names[i].c_str()) ;
}
Листинг 3.18.
Пример использования типа AnsiString в C++ программе с компонентным классом Family
(Семья).
Variant служит для спецификации значений, меняющих тип динамически. Переменная вариантного типа, в отличие от обычных статически типизированных переменных, способна менять свой тип во время исполнения программы. C++Builder объявляет этот тип Delphi как class __declspec(delphireturn) Variant: public TVarData. Заметим, чтс синтаксис вариантов, принятый в Delphi,
например:
V: Variant;
V := VarArrayCreate([0,Hi9hVal,0,HighVal],varlnteger) ;
отличается от способа записи вариантного массива в C++Builder:
Variant V(OPENARRAY(int,(0,HighVal,0,HighVal)),varlnteger);
Вариант может быть сконструирован из следующих типов данных: short, int, float, double.
Currency, TDateTime, bool, WordBool, Byte, AnsiString&, char *, wchar_t
* const. 01e2::lDispatch* const или 01e2::IUnknown* const.
Компилятор автоматически выполнит необходимые преобразования типа. При создании вариантных переменных они всегда инициализируются специальным значением Unassigned (не определено). Специальное значение Null
указывает, что данные отсутствуют.
Внимание: Варианты предоставляют ООП чрезвычайную гибкость, однако требуют большей памяти, нежели статически типизированные переменные, а операции над ними выполняются заметно медленнее.
TDateTime используется для спецификации переменных даты и времени. C++Builder реализует этот встроенный тип Delphi как одноименный класс, который инкапсулирует член данных типа double, содержащий значение даты в целой части числа, а значение времени в мантиссе (считая от полудня 30 декабря 1899 года). В следующей таблице приведены значения переменной типа TDateTime и их эквиваленты в выражениях даты и времени:
| Значение | Дата | Время | Примечания |
| 0 | 12/30/1899 | 12:00 | +0 дней, +0 часов |
| 2.75 | 01/01/1900 | 18:00 | +2 дня, +6 часов |
| -1.25 | 12/29/1899 | 06:00 | -1 день, -б часов |
| 35065 | 01/01/1996 | 12:00 | +35065 дней, +0 часов |
3.6.2 Расширения стандарта ANSI C++
Рассматриваемые расширения стандарта ANSI C++, в основном, представляют интерес для разработчиков новых классов и компонент, а также для программистов, которые работают в большом коллективе над созданием сложного проекта.
Вы можете прочитать остаток главы "по диагонали" или вовсе пропустить его, если не занимаетесь указанной деятельностью, и тонкости языка вас не интересуют.
3.6.2.1 Шаблоны
Шаблоны (параметризованные типы)позволяют конструировать семейство связанных функций или классов. Обобщенный синтаксис определения шаблона имеет вид
template <список шаблонных типов> { объявление } ;
Различают шаблоны функций и шаблоны классов.
Шаблон функции задает образец определений перегружаемых функций. Рассмотрим шаблон функции тах(х, у), которая возвращает больший из двух аргументов, которые могут быть любого, допускающего упорядочивание, типа:
template
причем тип данных, представленный аргументом шаблона
int i;
Myclass a, b;
int j = max(i, 0); // тип аргументов int Myclass m = max(a, b); // тип аргументов
Myclass
Фактические типы должны быть известны во время компиляции. Без шаблонов пришлось бы многократно перегружать функцию max - для каждого поддерживаемого типа, хотя код всех версий функции по существу был бы идентичным. Стандарт C++ настоятельно не рекомендует использовать для этой цели макрос:
#define max(x,y) ((х > у) ? х : у) из-за блокировки механизма проверки типов, который дает такие преимущества языку C++ над обычным С. Очевидно, задача функции тах(х, у) - сравнить совместимые типы. К сожалению, использование макроса допускает сравнение несовместимых типов, например, int и
struct.
Шаблон классов задает образец определений семейства классов. Рассмотрим пример шаблона Vector - генератора классов одномерного массива данных:
template
Над типизированными элементами этого класса выполняются одинаковые базовые операции (вставить, вычеркнуть, индексировать и т.д.), вне зависимости от конкретного типа элементов. Если обращаться с типом как с параметром, то компилятор будет генерировать классы векторов с элементами заданного типа.
Как и в случае шаблонов функций, разрешается явно переопределять тип шаблон классов:
class Vector
причем символ Vector должен всегда сопровождаться типом данных, заключенным в угловые скобки.
3.6.2.2 Пространства имен
Большинство нетривиальных приложений состоят из нескольких файлов с исходным текстом программы. Эти файлы могут создаваться и обслуживаться группой программистов. В конце концов, все файлы собираются вместе и проходят через финальную процедуру сборки готового приложения. Традиционно принято, чтобы все имена, не заключенные в некоторой локальной области (функции, теле класса или модуле трансляции), разделяли общие глобальные имена. Поэтому повторное определения имен, обнаруженное в процессе сборки отдельных модулей, приводит к необходимости каким-то образом различать каждое имя. Решение этой проблемы в C++ возложено на механизм пространства имен (namespace).
Этот механизм позволяет разбить приложение на ряд подсистем, причем каждая подсистема свободна в отношении выбора имен, и ее автор не должен беспокоиться о том, что такие же имена будет использовать кто-то другой. Каждая подсистема идентифицирует свое появление в общем пространстве глобальных имен уникальным идентификатором, который следует за ключевым словом namespace:
namespace
<идентификатор> { [<объявления>] } Существует три способа доступа к элементам идентифицированного пространства имен:
• Явная квалификация доступа к конкретному элементу:
ALPHA :: varl; // доступ к переменной из ALPHA BETA :: Fl; // доступ к функции из BETA
• Доступ ко всем элементам:
using namespace :: ALPHA; // доступ ко всем именам из ALPHA
• Объявление нового идентификатора в локальном пространстве имен:
using :: new_name; //
добавление идентификатора
3.6.2.3 Явные объявления
Обычно объектам класса, в котором объявлен конструктор с одним параметром, можно присвоить значения, тип которых автоматически (неявно) преобразуется к своему классовому типу. При объявлении конструктора можно использовать модификатор explicit:
explicit <объявление конструктора> Тогда при объявлении конструкторов данного класса с ключевым словом explicit всем объектам класса можно присвоить только те значения, тип которых явно преобразуется к классовому типу (Листинг 3.19). Другие присваивания приведут к ошибке компиляции.
class X
public:
explicit X(int);
explicit X(const char*, int = 0);
};
void f(X arg)
(
X a = X (1) ;
X b = Х("строка",0);
a = Х(2);
} :(
Листинг 3.19. Явные объявления конструкторов.
Явные объявления конструкторов требуют, чтобы значения в операторах присваивания были преобразованы к тому классовому типу, объектам которого эти значения присваиваются.
3.6.2.4 Непостоянные объявления
При объявлении переменной, которая может быть изменена фоновой задачей, обработчиком прерывания или портом ввода-вывода, используется модификатор volatile:
volatile <тип> <имя объекта>;
В C++ применение ключевого слова
volatile распространяется на классы и функции-члены. Это ключевое слово запрещает компилятору делать предположения относительно значения указанного объекта, поскольку при вычислении выражений, включающих этот объект, его значение может измениться в любой момент. Кроме того. непостоянная переменная не может быть объявлена с модификатором register. Листинг 3.20 показывает пример реализации таймера, в котором переменная ticks модифицируется обработчиком временных прерываний.
volatile int ticks;
void timer( ) // Объявление функции таймера
ticks++;
void wait (int interval)
ticks = 0;
while (ticks < interval); // Цикл ожидания
}
Листччг 3.20. Изменение непостоянной переменной volatile.
Положим, что обработчик прерывания
timer был надлежащим образом ассоциирован с аппаратным прерыванием от часов реального времени. Процедура wait реализует цикл ожидания, пока значение переменной
ticks не станет равным интервалу времени, заданному ее параметром. Компилятор C++ обязан перезагружать значение переменной
volatile ticks перед каждым сравнением внутри цикла - несмотря на то, что внутри цикла значение переменной не изменяется. Некоторые оптимизирующие компиляторы могли бы допустить эту "роковую" ошибку.
Другой вид непостоянной переменной, которая может быть изменена даже если она входит в константное выражение, объявляется с помощью модификатора mutable:
mutable <имя переменной>;
Назначение ключевого слова mutable состоит в спецификации членов данных некоторого класса, которые могут быть модифицированы константными функциями этого класса. Листинг 3.21 показывает пример, в котором член данных count модифицируется константной функцией F1.
class A {
public: mutable int count; int F1 (int p = 0) const
// Объявление функции F1
count = p++ return count; //PI
возвращает count
) I
void main() {
A a;
cout “ a.Fl(3) “ end.1; // main
выдает значение 4 )
Листинг 3.21. Изменение непостоянной переменной mutable.
3.6.2.5 Идентификация типов RTTI
Идентификация типов при выполнении программы RTTI (Run-Time Туре Identification) позволяет вам написать переносимую программу, которая способна определять фактический тип объекта в момент выполнения даже в том случае, если программе доступен только указатель на этот объект. Это дает возможность, например, преобразовывать тип указателя на виртуальный базовый класс в указатель на производный тип фактического объекта данного класса. Таким образом, преобразование типов может происходить не только статически - на фазе компиляции, но и динамически - в процессе выполнения. Динамическое преобразование указателя в заданный тип осуществляется с помощью оператора dynamic_cast.
Механизм RTTI также позволяет проверять, имеет ли объект некоторый определенный тип, или принадлежат ли два объекта одному и тому же типу. Оператор typeid определяет фактический тип аргумента и возвращает указатель на объект класса typeinfo, который этот тип описывает.
Передавая RTTI Инспектору объектов во время выполнения, C++Builder информирует его о типах свойств и членов данного класса.
3.6.2.6 Исключения
Язык C++ определяет стандарт обслуживания исключений в рамках ООП. C++Builder предусматривает специальные механизмы для обработки исключений (ошибок), которые могут возникнуть при использовании Библиотеки Визуальных Компонент. C++Builder также поддерживает обработку исключений самой операционной системы и модель завершения работы приложения.
Когда программа встречает ненормальную ситуацию, на которую она не была рассчитана, можно передать управление другой части программы, способной справиться с этой проблемой, и либо продолжить выполнение программы, либо завершить работу. Переброс исключений (exception throwing) позволяет собрать в точке переброса информацию, которая может оказаться полезной для диагностики причин, приведших к нарушению нормального хода выполнения программы. Вы можете определить обработчик исключения (exception handler), выполняющий необходимые действия перед завершением программы. Обслуживаются только так называемые синхронные исключения, которые возникают внутри программы. Такие внешние события, как нажатие клавиш Ctrl+C, не считаются исключениями.
Блок кода, который может сгенерировать исключение, начинается ключевым словом try и заключается в фигурные скобки. Если блок try обнаруживает исключение внутри этого блока, происходит программное прерывание и выполняется следующая последовательность действий:
1. Программа ищет подходящий обработчик исключения.
2. Если обработчик найден, стек очищается и управление передается обработчику исключения.
3. Если обработчик не найден, вызывается функция terminate для завершения приложения.
Блок кода, который обрабатывает возникшее исключение, начинается ключевым словом catch и заключается в фигурные скобки. По меньшей мере один кодовый блок обработчика исключения должен следовать непосредственно за блоком try. Для каждого исключения, которое может сгенерировать программа, должен быть предусмотрен свой обработчик. Обработчики исключений просматриваются по порядку и выбирается обработчик исключения, тип которого соответствует типу аргумента в операторе catch. При отсутствии в теле обработчика операторов goto, выполнение программы будет возобновлено, начиная с точки, следующей за последним обработчиком исключений данного блока try. Листинг 3.22 демонстрирует обобщенную схему обработки исключений.
try
i
// Любой код, который может сгенерировать исключение
} 1
catch (Т X) |
{ I
// Обработчик исключения Х типа Т, которое могло быть |
// ранее сгенерировано внутри предыдущего блока try Я // Обработчики других исключений предыдущего блока
try catch (...)
// Обработчик любого исключения предыдущего блока try
Листинг 3.22. Обработка исключении.
Когда возникает исключение, выражение присваивания в операторе throw <выражение> инициализирует временный объект, тип которого соответствует типу аргумента выражения. Другие копии этого объекта могут быть сгенерированы, например, при помощи конструктора копирования объекта исключения.
Следование принятой стандартной методике обработки ошибок - одна из гарантий построения надежных приложений, хотя операционная система и постарается сама "вытянуть" исполняемую программу из непредвиденных ситуаций. Осознавая важность этого элемента ООП, я включил в развернутые примеры по программированию баз данных главы 5 необходимые коды для обработки исключений.
3.7 Итоги
Мы вкратце ознакомились с терминологией объектно-ориентированного программирования на языке C++ и с некоторыми расширениями языка. ООП оперирует абстрактными понятиями классов, объектов, методов, инкапсуляции, наследования и полиморфизма. Читатель быстро научится находить аналоги терминов ООП в реальном мире, что облегчит освоение в общем-то простых понятий с довольно туманными и, зачастую, неудачными определениями.
Мы по достоинству оценили введение новых компонентных классов и подготовились к манипулированию с ними в рамках интегрированной визуальной среды быстрой разработки приложений.
ОБЩИЕ ХАРАКТЕРИСТИКИ
1. ОБЩИЕ ХАРАКТЕРИСТИКИ
И ОСОБЕННОСТИ ПРИМЕНЕНИЯ
Новейшая система объектно-ориентированного программирования C++ Builder производства корпорации Borland предназначена для операционных систем Windows 95 и NT. Интегрированная среда C++ Builder обеспечивает скорость визуальной разработки, продуктивность повторно используемых компонент в сочетании с мощью языковых средств C++, усовершенствованными инструментами и разномасштабными средствами доступа к базам данных.
C++ Builder может быть использован везде, где требуется дополнить существующие приложения расширенным стандартом языка C++, повысить быстродействие и придать пользовательскому интерфейсу качества профессионального уровня.
Эта глава носит обзорный и отчасти рекламный характер, что объясняется давнишней приверженностью автора программным продуктам корпорации Borland, в особенности базирующимся на языке C++. Если излагаемый материал покажется вам скучным или малоинформативным - просто пропустите его. Большие возможности системы и маленькие секреты программирования без проблем откроются перед вами в следующих главах книги.
1.1 Основные характеристики
Рассмотрим отличительные характеристики, которые выдвигают Borland C++ Builder на авангардные позиции систем объектно-ориентированного программирования для быстрой разработки современного математического обеспечения персональных компьютеров. Borland C++ Builder выпускается в трех вариантах: Standard, Professional и Client/Server Suite.
| Характеристики | Standard | Professional | Client/Server Suite | ||
| Язык C++ с поддержкой расширений стандартов ANSI/ISO |
V | v | v | ||
| Высокопроизводительный 32-разрядный оптимизирующий компилятор | V | v | v | ||
| Быстрый инкрементальный компоновщик приложений | V | v | v | ||
| Интегрированная среда разработки IDE | V | v | v | ||
| Механизмы двунаправленной разработки | V | v | v | ||
| Интегрированный отладчик CPU View и инструменты командной строки | V | v | v | ||
| Создание библиотек DLL, LIB и исполняемых программных файлов ЕХЕ | V | v | ^ | ||
| Полный комплект общих элементов управления Windows 95 |
V | v | v | ||
| Объекты модулей данных | V | v | v | ||
| Полный доступ к Windows API | V | v | v | ||
| Хранилище объектов | V | v | v | ||
| Визуальное наследование форм | V | v | v | ||
| Контроллеры и серверы OLE Automation | V | v | v | ||
| Библиотека Визуальных Компонент VCL | V | v | v | ||
| Мастер для помощи в создании компонент | V | v | v | ||
| Компоненты для работы с базами данных | V | v | v | ||
| Расширенная справочная служба on-line | V | v | v | ||
| Проводник баз данных | V | v | v | ||
| Компоненты для создания отчетов | V | v | v | ||
| Исходные тексты VCL | | v | v | ||
| Масштабируемый словарь баз данных | | v | v | ||
| Поддержка соединений ODBC | | v | v | ||
| Компонента визуализации и редактирования баз данных Multi-Object Grid | | v | v | ||
| 32-разрядный однопользовательский сервер Local InterBase | | v | v | ||
| Полный комплект печатной документации | | V | V | ||
| Генератор дистрибутивов InstallShield Express | | V | V | ||
| Internet Solutions Pack для разработки Web-приложений | | V | V | ||
| WinSight32 для мониторинга сообщений Windows | | V | V | ||
| Открытые инструменты API | | V | V | ||
| Расширенный набор компонент ActiveX | | V | V | ||
| Драйверы SQL Links для баз данных Oracle, Sybase, Informix, DB2, Microsoft SQL Server и InterBase | | | V | ||
| SQL Database Explorer | | | V | ||
| SQL Monitor | | | V | ||
| Visual Query Builder | | | V | ||
| Интегрированная система контроля версий в коллективных проектах Intersolv PVCS | | | V | ||
| InterBase NT | | | V | ||
| Механизм кэшируемых обновлений | | | V |
1.1.1 Скорость визуальной разработки
Интегрированная среда разработки
объединяет Редактор форм. Инспектор объектов. Палитру компонент. Администратор проекта и полностью интегрированные Редактор кода и Отладчик - инструменты быстрой разработки программных приложений, обеспечивающие полный контроль над кодом и ресурсами.
Профессиональные средства языка C++ интегрированы в визуальную среду разработки. C++Builder
предоставляет быстродействующий компилятор с языка Borland C++, эффективный инкрементальный загрузчик и гибкие средства отладки как на уровне исходных инструкций, так и на уровне ассемблерных команд - в расчете удовлетворить высокие требования программистов-профессионалов.
Конструирование по способу
"drag-and-drop "
позволяет создавать приложение простым перетаскиванием захваченных мышью визуальных компонент из Палитры на форму приложения. Инспектор объектов предоставляет возможность оперировать со свойствами и событиями компонент, автоматически создавая заготовки функций обработки событий, которые наполняются кодом и редактируются в процессе разработки.
Механизмы двунаправленной разработки (two-way-tools)
устраняют барьеры между программистом и его кодом. Технология двунаправленной разработки обеспечивает контроль за вашим кодом посредством гибкого, интегрированного и синхронизированного взаимодействия между инструментами визуального проектирования и Редактором кода.
Свойства, методы и события - это именно те элементы языка, которые обеспечивают быструю разработку приложений в рамках объектно-ориентированного программирования. Свойства
позволяют легко устанавливать разнообразные характеристики объектов. Методы производят определенные, иногда довольно сложные, операции над объектом. События
связывают воздействия пользователя на объекты с кодами реакции на эти воздействия. События могут возникать при таких специфических изменениях состояния объектов как обновление данных в интерфейсных элементах доступа к базам данных. Работая совместно, свойства, методы и события образуют среду RAD (Rapid Application Development) быстрого и интуитивного программирования надежных приложений для Windows.
Визуальное наследование форм воплощает важнейший аспект объектно-ориентированного программирования в удобном для пользования инструменте визуального проектирования. Характеристики новой формы приложения могут быть унаследованы от любой другой существующей формы, что обеспечивает централизованную репродукцию изменений пользовательского интерфейса, облегчает контроль за кодом и уменьшает временные затраты на введение новых качественных атрибутов.
Испытание прототипа позволяет без труда переходить от прототипа приложения к полностью функциональному, профессионально оформленному программному продукту, действуя в пределах интегрированной среды. Чтобы удостовериться, что ваша программа производит ожидаемые результаты, раньше приходилось многократно проходить по циклу редактирование => компиляция => сборка, непроизводительно расходуя время.
C++Builder объединяет три этапа разработки в единый производственный процесс. В результате удается строить приложения, базирующиеся на текущих требованиях заказчика, вместе с тем гибкие настолько, чтобы быстро адаптировать их к новым запросам пользователей.
Мастер инсталляции руководит созданием унифицированных дистрибутивных пакетов для разработанных приложений.
Исходные тексты Библиотеки Визуальных Компонент облегчают разработку новых компонент на базе готовых примеров.
Отрытые инструменты API могут быть непосредственно интегрированы в визуальную среду системы. Вы сможете подключить привычный текстовый редактор или создать собственного мастера для автоматизации выполнения повторяющихся процедур.
Расширенная математическая библиотека
содержит дополнительные унифицированные функции статистических и финансовых вычислений.
1.1.2 Продуктивность компонент
Библиотека Визуальных Компонент VCL приобрела статус нового промышленного стандарта и в настоящее время применяется более чем полумиллионом пользователей, существенно ускоряя разработку надежных приложений любой степени сложности. VCL содержит около 100 повторно используемых компонент, которые реализуют все элементы пользовательского интерфейса операционной системы Windows
95. Кроме того, VCL предоставляют в распоряжение программистов такие оригинальные объекты, как записные книжки с закладками, табличные сетки для отображения содержимого баз данных и даже органы управления устройствами мультимедиа. Находясь в среде объектно-ориентированного Программирования C++Builder, компоненты можно использовать непосредственно, менять их свойства, облик и поведение или порождать производные элементы. обладающие нужными отличительными характеристиками.
Хранилище объектов является инструментом новой методики хранения и повторного использования модулей данных, объектов, форм и программной бизнес-логики. Поскольку построение нового приложения на существующем фундаменте значительно экономит временные затраты, хранилище объектов предоставляет для повторного использования готовые структуры: формы и законченные программные модули. Создавая прототип нового приложения, вы можете наследовать, ссылаться или просто копировать существующую структуру - точно так же архитектор приступает к проектированию нового здания.
Компонента ChartFX обеспечивает немедленное построение на вашей форме разнообразных графиков, диаграмм, таблиц и предусматривает проверку правописания на многих языках. В варианте C++Builder Standard эта компонента является единственным представителем группы
ActiveX.
Интеграция компонент ActiveX позволяет расширить Библиотеку Визуальных Компонент, включив компоненты стандарта ActiveX для разработки приложений в сети Internet.
Если ваш компьютер имеет выход в
Internet, соединитесь с сервером по адресу
http://www.borland.com/devsupport/bcppbuilder/netmanage.html
и скачайте файл автоматической интеграции OCXINTEG.ZIP (12 Кбайт) и файл управления сетью AX602.ZIP (6.5 байт), разработанный фирмой NetManage. Файл интеграции автоматически проведет для вас процедуру добавления новых компонент к VCL и установит их на Палитру. В результате вы получите возможность разрабатывать приложения для Internet, вплоть до создания собственного сетевого коммуникатора
(HTML browser).
1.1.3 Мощность языковых средств C++
Оптимизирующий 32-разрядный компилятор
построен по проверенной ведущей компиляторной технологии корпорации Borland, обеспечивающей исключительно падежную и быструю оптимизацию как длины выходного исполняемого кода, так и расходуемой памяти.
Новые элементы стандарта ANSI/ISO языка C++ представлены шаблонами, пространствами имен, исключениями, информацией о типах времени выполнения (RTTI), наряду с расширением набора ключевых слов
bool, explicit, mutable, typename. automated
и др.
Инкрементальный линкер осуществляет быструю и надежную сборку приложении в формате ЕХЕ файлов сравнительно меньшего размера. Автоматически устраняя повторную сборку не изменившихся исходных объектных файлов и подключение неиспользуемых функций, инкрементальный линкер строит эффективную выполняемую программу с минимальными потерями времени.
Чистый и доступный код приложений, которые C++Builder строит на основе предоставляемых разработчику компонентных свойств, событий и методов, исключает скрытые и трудные в отладке макросы.
Поддержка промышленных стандартов ActiveX, OLE, СОМ, MAPI, Windows Sockets TCP/IP, ISAPI. NSAPI, ODBC, Unicode и MBCS.
Отладчик низкого уровня CPU View позволяет проникнуть в специфику работы вашего приложения на уровне машинных кодов. Окно отладчика разделено на пять панелей. Панель ассемблерных команд интерпретирует исполнение исходной C++ программы. Панель памяти показывает содержимое блока памяти, доступного загруженному и исполняемому в данный момент модулю. Панель стека отображает текущее содержимое верхушки программного стека. Панель регистров и панель флагов показывают текущие значения регистров и служебных битов центрального процессора. Каждая панель включает собственное меню, управляющее ее видом и поведением.
Инструменты командной строки включены в систему по требованию профессионалов, которые всегда стремятся сохранить детальный контроль над процессами компиляции и сборки своих программных файлов.
Создание DLL, LIB, и ЕХЕ файлов предоставляет свободу выбора формата целевого приложения в соответствии с требованиями конкретного проекта.
Прямое обращение к системным функциям Windows 95 и NT дает возможность программистам, работающим в среде C++Builder. при необходимости воспользоваться всеми усовершенствованиями современных операционных систем.
Механизм OLE Automation предоставляет вашему приложению возможность управлять другими типовыми программными комплексами для Windows (такими как Microsoft Word, Excel, Visual Basic, Lotus 1-2-3, dBASE и Paradox) по схеме сетевого взаимодействия контроллер/сервер.
1.1.4 Масштабируемые соединения с базами данных
Разработка по способу
"drag-and-drop"
многократно упрощает и ускоряет обычно трудоемкий процесс программирования СУБД в архитектуре клиент/сервер. Широкий выбор компонент управления визуализацией и редактированием позволяет легко изменять вид отображаемой информации и поведение программы. C++Builder использует Проводник баз данных (Database Explorer) и масштабируемый Словарь данных (Data Dictionary ), чтобы автоматически настроить средства отображения и редактирования применительно к специфике вашей информации.
Проводник баз данных предоставляет графический способ проводки пользователя по содержимому базы данных, обеспечивая создание и модификацию таблиц, иерархических указателей и псевдонимов.
Словарь данных поддерживает целостность изменяющейся информации о содержимом таблиц баз данных. Пользователь может динамически модифицировать состав Словаря. Словарь содержит информацию о расширенных атрибутах полей в записях: минимальные и максимальные значения, свойства отображения, маски редактирования и т.п.
Живые данные (live data) предоставляются разработчику в процессе визуального проектирования прототипов и при испытании приложений баз данных. Вам не потребуется более писать тестовые ловушки или многократно перетранслировать и запускать приложение - данные на стадии проектирования будут точно такими же и представлены точно так же, как их увидит пользователь законченной программы.
Механизм BDE ( Borland Database Engine) поддерживает высокопроизводительный 32-разрядный доступ к базам данных dBASE, Paradox: Sybase. Oracle, DB2. Microsoft SQL Server. Informix, InterBase и Local InterBase. C++Builder
использует контроллер ODBC (Open Database Connectivity) производства Microsoft для связи с серверами баз данных Excel, Access, FoxPro и Btrieve. Являясь фундаментом любого приложения базы данных, BDE тесно связан с Хранилищем объектов и Модулями данных.
Объекты Модулей данных действуют как связующий каркас приложения - они определяют источники и бизнес-логику базы данных, фиксируют взаимосвязи компонент. В централизованной модели доступа к данным бизнес-логика отделена от разработки графического интерфейса с пользователем (GUI). Любое изменение бизнес-логики вашей базы данных сказывается на поведении только соответствующего Модуля данных, а результаты изменения проявляются немедленно во всех приложениях, использующих данный модуль. Работая с модулями данных, вы однократно устанавливаете связи вашего приложения с адресуемой базой данных, а затем по способу
"drag-and-drop" можете перетаскивать поля записей на новые формы - в любой узел вашей сети. Никакого дополнительного кодирования при этом не требуется.
Фильтры поля ссылок устанавливают ограничения поиска и отображения информации базы данных простым нажатием кнопки. Изменяя значения свойства Filter в компонентах доступа, можно специфицировать некоторое подмножество интересующих вас данных. Ссылки обеспечивают автоматическое отображение данных из нескольких таблиц.
Копируемые обновления (cached updates) заметно ускоряют отклик SQL сервера за счет уменьшения общего числа сетевых обменов с клиентом. Будучи упакованными, множественные коммуникации проявляют себя как одиночные транзакции, тем самым снижая загруженность сервера и улучшая производительность вашего приложения.
Отчеты Quick Reports позволяют визуально конструировать стилизованные отчеты по данным, поставляемым любым источником, включая таблицы и запросы компонент доступа к базам данных. Отчеты могут содержать поля заголовков, колонтитулов, сносок и итогов. Quick Reports предоставляют мощные средства отображения отчетов в разных видах, автоматического подведения итогов и подсчета полей - на любом уровне группировки данных.
1.2 Другие программные продукты Borland
Корпорация Borland давно завоевала приоритетную позицию на рынке мощных систем разработки программного обеспечения персональных компьютеров. Появление C++Builder ни в коей мере не затормозило развитие других программных продуктов, имеющих своих приверженцев и потребителей.
Borland C++ остается одной из самых популярных систем разработки математического обеспечения на языке C++. Система предоставляет в распоряжение программистов-профессионалов высокопродуктивные средства разработки 16- и 32-разрядных приложений для операционных систем Windows 95, NT. Windows 3.1, и DOS.
Модифицированные библиотеки Object Windows Library (OWL) и Microsoft Foundation Classes (MFC), а также инструменты Visual Database Tools (VDBT) для визуальной разработки эффективных приложений баз данных - выводят Borland
C++ на новый качественный уровень систем объектно-ориентированного программирования. Borland C++ "понимает" последние языковые расширения стандарта ANSI/ISO, включая пространства имен, стандартные шаблоны и др. Последние версии Borland C++ также содержат новые инструменты программирования на языке Java.
Delphi появился на рынке в начале 1995 года и быстро завоевал титул первой системы быстрой разработки приложений для Windows,
сочетающей в единой среде высокопроизводительный компилятор, визуальные механизмы двунаправленного проектирования и новую методику масштабируемого доступа к базам данных. Уже через два года число зарегистрированных потребителей Delphi достигло полумиллиона. Новый 32-разрядный оптимизирующий компилятор Delphi 2.0 для Windows 95 и
NT дает 3 - 4-кратное повышение производительности по сравнению с Delphi 1.0 и работает в 15-20 раз быстрее, чем интерпретаторы языка Паскаль. Delphi 2.0 также включает так называемый Borland Internet Solutions Puck -
собрание компонент ActiveX для построения приложений в сетях Internet и Intranet.
IntraBuilder - это мощный инструментарий визуальной разработки приложений на языке Java. предназначенных для работы в сети Internet.
Система поддерживает масштабируемые соединения с популярными серверами сетевых коммуникаторов Netscape. Microsoft и O'Reilly.
InterBase - это масштабируемый высокопроизводительный SQL сервер баз данных, совершенный по простоте инсталляции, использования и обслуживания. Выпуск нескольких версий InterBase
для операционных систем Windows, NT, NetWare и UNIX делает его независимым от платформы, что позволяет обеспечить надежную работу реляционных баз данных в рамках локальной компьютерной сети крупного отдела или фирмы.
Visual dBASE
предоставляет проверенные двунаправленные инструменты и легкий в освоении язык для быстрой визуальной разработки приложений базы данных в сети Intranet
под Windows З.х. Windows95 или NT. Пользователи Visual dBASE легко осваивают навыки объектно-ориентированного программирования в архитектуре клиент/сервер.
1.3 Проблемы совместимости
C++ Builder предоставляет свою мощность и широкие возможности языка C++ всему семейству систем объектно-ориентированного программирования. C++ Builder может быть использован везде, где требуется дополнить существующие приложения расширенным промышленным стандартом языка C++, повысить быстродействие и придать пользовательскому интерфейсу профессиональный облик.
1.3.1 C++ Builder и
Borland C++
Между программными продуктами C++ Builder и Borland C++ существует полная и взаимная функциональная совместимость. C++ Builder добавляет процессу программирования новое качество - быструю визуальную разработку приложений на языке C++. Если вы привыкли работать в системах программирования Borland C++ (версий 3.1. 4.5 и 5.0), то ни переход к новой системе, ни возврат к старой не окажутся слишком затруднительными. В вашем распоряжении остаются удобный администратор проектных файлов, компилятор и инкрементальный загрузчик.
Поначалу C++ Builder разочарует многих разработчиков тем, что вместо привычных библиотек OWL и MFC он использует неведомую дотоле библиотеку VCL. Каждая библиотека представляет собой специфическую модель объектно-ориентированного программирования под Windows. Каждая библиотека решает проблемы ООП по-своему, и нельзя сказать, что какая-то одна справляется с задачей лучше или хуже других. Впервые инсталлировав C++Builder, я продолжал разделять приверженность к
OWL с большинством C++ программистов. Уже через несколько дней работы я начал ощущать всю прелесть, легкость и удобство использования компонентной модели. Чем больше я проникал в суть VCL, тем больше она мне нравилась, и теперь уже не могу отказаться от работы в среде C++Builder. Время от времени я обращаюсь к проверенному в боях заслуженному ветерану ВС45, чтобы повозиться со старыми OWL программами, постоянно ловя себя на мысли: "Это было бы гораздо легче сделать с помощью C++Builder". И эта мысль отдаляет меня от OWL
все дальше и дальше, так что скоро я вовсе распрощаюсь со своей прежней привязанностью.
Borland C++ версий 5.0 и выше поддерживает внедрение в ранее разработанные OWL или
MFC программы типовых форм, спроектированных в среде C++Builder на базе VCL. Разработчик идет на такого рода переделки из-за простой причины: C++Builder позволяет включить в форму все диалоги с пользователем чрезвычайно быстро, придавая приложению для Windows законченный профессиональный облик, устраняя из программы всю шелуху вспомогательного кода и оставляя только содержательную часть, воплощающую основную идею алгоритма. Таким образом, совместимость обеспечивается на уровне форм. Перенос отдельных компонент из VCL оказывается затруднительным, да и не нужным, поскольку в предельном случае форма может содержать единственную компоненту. Далее ваша комбинированная программа сможет использовать форму как окно диалога или окно клиента. Подробное описание процедур интеграции OWL или MFC программ с VCL формами и примеры создания комбинированных приложений можно найти в сети Internet no
адресу http://www.borland.com/bcppbuilder/papers
Не удивительно, что совместная работа с двумя библиотеками объектных классов заметно увеличивает размер ЕХЕ файла.
1.3.2 C++Builder и
Delphi
Все компоненты, формы и модули данных, которые накопили программисты, работающие в
Delphi, могут быть повторно использованы в приложениях C++Builder для
Windows без каких бы то ни было изменении. Delphi пока еще продолжает оставаться самой легкой в использовании и самой продуктивной системой RAD. Поэтому C++Builder идеально подойдет тем разработчикам, которые предпочитают выразительную мощность языка C++, однако хотят сохранить продуктивность Delphi. Уникальная взаимосвязь этих систем программирования позволяет при создании приложения без труда переходить из одной среды разработки в другую.
Политика, проводимая руководством отделов математического обеспечения большинства фирм и организаций, допускает свободу разработчиков в выборе языка программирования, при условии совместимости производимых ими объектов и кодов. Неукоснительно следуя этой политике, C++Builder сохраняет материальные вложения в
Delphi, вобрав в себя Библиотеку Визуальных Компонент, интуитивную интегрированную среду, визуальные механизмы двунаправленной разработки, методику наследования форм и разномасштабные средства доступа к базам данных.
Гамлетовская проблема выбора между двумя альтернативами вообще не ставится - в мире программистов достаточно жизненного пространства для "мирного сосуществования" обеих систем. Оставьте ненужные споры политического толка о преимуществе одной системы над другой. Используйте тот аппарат программирования, который сделает решение ваших задач более продуктивным, который вам больше нравится и который вам представляется более выразительным. C++ и Delphi равноправны в вашей работе - почти все, что вы написали в Delphi, можно легко использовать в C++Builder, и наоборот. Не забывайте об основном направлении своей профессиональной деятельности - создании качественных и надежных программ под операционную систему
Windows.
Преимущества разделения кода между C++Builder ч Delphi:
• Программисты могут работать в той среде, которая лучше и быстрее обеспечит реализацию поставленной текущей задачи. Delphi и C++Builder скомпилируют и соберут готовое приложение с одинаковым успехом.
• Написав некоторый объект для проекта Delphi, вы сможете повторно использовать его, без изменений, в проекте C++Builder.
• Delphi предлагает программистам очень простой, легкий в освоении синтаксис языка. Ваше начальство может не опасаться, что разработки Delphi не пойдут на C++Builder. При реализации сложных проектов большой командой программистов любой ее участник волен выбрать язык Объектный Паскаль или C++ в соответствии с индивидуальным вкусом, навыками и приверженностью. В любых комбинациях результатом совместной разработки будет единая высокоэффективная исполняемая программа.
• Выбирайте Delphi, когда вам надо как можно скорее создать какой-нибудь простой модуль или объект, даже если вы привыкли работать на C++.
Таким образом, C++Builder в симбиозе с Delphi образует исключительно продуктивную пару систем для быстрой коллективной разработки сложнейших приложений для Windows.
1.3.2.1 Какую систему выбрать?
Для тех, кто впервые остановился на перепутье, неизменным ответом на этот сакраментальный вопрос будет: "Смотря для чего".
Вы должны знать (если уже не знаете), что Delphi использует язык Объектный Паскаль, который преподается во многих специализированных школах и учебных институтах. Если вы только начинаете пробовать свои силы в программировании для
Windows, лучше, видимо, выбрать Delphi.
Придуманный Н.Виртом как средство для обучения студентов программированию, язык Паскаль стараниями специалистов корпорации
Borland видоизменился в Объектный Паскаль для Windows и стал основой мощной профессиональной системы, которой по силам любые задачи - от создания простых вычислительных программ, до разработки сложнейших реляционных СУБД.
C++Builder, как следует из названия, построен на языке C++, который наиболее распространен в крупных фирмах, занимающихся разработкой математического обеспечения профессионального уровня. Если вы обладаете определенными познаниями в языке C++ и программирование должно стать, или уже является, вашей основной специальностью - выбирайте C++Builder. Не излишне заметить, что на западном рынке профессиональные C++ разработки пользуются гораздо большим спросом, нежели продукты системы Delphi, да и оплачиваются C++ программисты несколько выше, чем "паскальщики".
Современные программисты, как правило, владеют по меньшей мере одним из этих языков. Если вы уже работаете в среде Delphi, переход на C++Builder не покажется слишком болезненным, и наоборот. Обе системы находятся на примерно одинаковом уровне популярности среди потребителей. Обе системы построены на идентичном фундаменте - Библиотеке Визуальных Компонент.
1.3.2. 2 Какая система является более мошной?
"Конечно C++Builder" - вот моя первая реакция на подобный вопрос. Однако, хорошо известно, что опытные разработчики способны справиться с поставленной задачей на Паскале лучше, чем большинство рядовых C++ программистов. Я знаком с несколькими настоящими мастерами программирования на Паскале и могу сказать, что в их руках этот инструмент способен производить удивительно красивые, эффективные и надежные творения. Если вы намерены специализироваться в области разработки компонент для VCL, используйте более удобные и гибкие средства Delphi, хотя созданные вами компоненты предназначены для внедрения в обе системы.
Для большинства рядовых пользователей обе системы покажутся поначалу (и надолго будут считаться) одинаково мощными.
1.3.2.3 Как использовать код Delphi в C++Builder
C++Builder не делает различия между тем, какие программные модули вы добавляете к проекту своего приложения - написаны они на C++ (файлы с расширением СРР) или на Delphi (файлы с расширением PAS). Компилятор свободно принимает следующие кодовые конструкции модулей Delphi 2.0: компоненты,формы, объекты, константы, простые методы и функции - все перечисленные коды можно прямо подставлять в свои проектные файлы. Технология визуального наследования форм дает возможность модифицировать формы Delphi в среде C++Builder без каких бы то ни было проблем.
Опытным программистам откроются следующие возможности системы C++Builder:
• Подгружать к проекту динамические библиотеки Delphi (файлы с расширением DLL). Разделение кода с помощью динамически подгружаемых DLL особенно целесообразно при ограниченном объеме оперативной памяти.
• Использовать OLE объекты Delphi и компоненты ActiveX. Связь OLE объектов позволяет разделять код не только с Borland C++Builder, но и с программными комплексами производства других фирм
(Microsoft Word, Excel или Visual Basic).
• Разделять с Delphi общие объекты посредством унифицированных Таблиц Виртуальных Методов (VMTs).
1.3.2.4 Как использовать код C++Builder в Delphi
В C++ разрешен ряд синтаксических конструкций (таких как множественное наследование и перегрузка функций), которые делают невозможным прямой перенос кода C++Builder в проект для Delphi. Однако, существуют следующие обходные пути:
• Компоновать СОМ объекты, написанные на C++, которые поддерживают типизированные библиотеки - все необходимые для сборки проекта приложения файлы Delphi 97 генерирует автоматически из соответствующих библиотек.
• Любая функция, написанная на C++ и помещенная в DLL, может быть прямо вызвана из Delphi - Delphi поддерживает все общие спецификаторы вызова функций CDECL, PASCAL и STDCALL.
• Обращаться к объектам, написанным на C++, из Delphi посредством упомянутых Таблиц Виртуальных Методов.
В принципе, существует возможность подгружать объектные модули (файлы с расширением
OBJ) непосредственно к Delphi в процессе сборки приложения. Однако, эта мощная методика имеет ряд ограничений, которые зачастую бывает трудно выдержать.
1.3.2.5 Перевод кода Delphi на C++Builder
Быть может, вы действительно собираетесь ретранслировать все свои программы из Delphi в C++Builder. Или, скорее всего, вам понадобилось наскоро переложить какие-то интересные примеры Delphi на язык C++. В любом случае следует остерегаться подстерегающих вас ловушек и "ложных друзей переводчика". Безусловно, вы преодолеете принципиальные различия синтаксиса объектно-ориентированных языков, а компилятор поможет вам в этом. Опасность скорее таится в ошибочной трактовке простых операторов и понятий, на которые следует обратить внимание.
Оператор членства используется для доступа к любой функции, свойству или переменной, принадлежащих некоторому классу - объявленному на Паскале или на C++. Все объекты Паскаля создаются в куче (heap) и для обращения к перечисленным членам класса используется оператор "точка" (.), например:
Label.Text := 'Hello There;';
Ha C++ дело обстоит несколько сложнее, поскольку объекты могут быть созданы не только в куче, но и на стеке. Для обращения к объекту C++, созданному на стеке, служит оператор прямого членства "точка", например:
TMyCiass myClass;
myClass.DoSomething() ;
Синтаксически приведенная кодовая конструкция сходна с паскалевой. Различие заключается в способе распределения памяти для хранения объекта. Для создания объекта в куче C++ использует оператор new, а для доступа к нему - оператор косвенного членства (->):
TMyCiass* myClass = new TMyCiass;
myClass->DoSomething() ;
delete myClass;
Важно отметить, что если вы можете создавать собственные объекты на стеке или в куче, по своему усмотрению, то объекты VCL (и производные от VCL классов) размещаются только в куче. Поэтому в операциях с объектами VCL всегда используется оператор косвенного членства.
Оператор присваивания в Паскале записывается в виде двоеточия со знаком равенства (: =), а на C++ - это одиночный знак равенства (=). Таким, образом код Delphi:
Label.Top := 20;
должен выглядеть на C++ как:
Label->Top = 20;
Приобретенная в Паскале привычка записи оператора присваивания приводит к раздражающему обилию синтаксических ошибок на C++, впрочем, легко обнаруживаемых компилятором.
Оператор логического равенства в Паскале записывается в виде одиночного знака равенства (=), а на С - это удвоенный знак равенства (==). Таким, образом код Delphi:
if Labell.Text = Label2.Text then
... должен записываться на C++ как:
if (Labell->Text == Label2->Text) { ... } Если вы случайно написали одиночный знак равенства в предыдущей инструкции сравнения, то она будет воспринята как операция присваивания - не совсем так, как вы подразумевали. Предупреждение "Possibly incorrect assignment", которое выдаст компилятор, здесь надо трактовать как ошибку. Вообще следует приучить себя доводить программу до полного отсутствия такого рода предупреждений -неизвестно, к каким последствиям они могут привести.
Одиночные и двойные кавычки служат для обозначения текстовых строк. Код Delphi:
Label. Text := 'Иван Иванов' должен выглядеть на C++ как:
Label->Text = "Иван Иванов";
Это различие будет, вероятно, раздражать вас не меньше, чем разница в записи операторов присваивания. Запутывая ситуацию еще более, C++ также использует одиночные кавычки, но совсем для другой цели.
Символьный тип служит для объявления байтовых величин. В Паскале значения символов типа Char
заключены в диапазоне от 0 до 255. В C++ символ без знака
(unsigned char) эквивалентен описанию Char Паскаля, однако символ со знаком
(signed char) имеет другой диапазон значении от -127 до +128. Два варианта присваивания значения некоторому символу на Паскале выглядят как:
с : Char ;
с := #65;
с := Char(0rd('А' ) ) ;
что эквивалентно следующему C++ коду:
char с ;
с = 65;
с = 'А'; // Здесь используются одиночные кавычки.
Заглавные и строчные буквы различаются в C++, поэтому C++Builder требует внимательного обращения с клавишей Shift. Для Паскаля же не имеет значения, верхний или нижний регистр клавиатуры использовался при подготовке текста программы. Если в C++ программе вы используете переменную, объявленную с заглавной буквы, то такая инструкция как:
top = 20;
вызовет ошибку компиляции "Undefined symbol "top"'. Булевы значения "истина" и "ложь" могут записываться на Паскале как true. True, TRUE, TruE и т.д. - как вам больше нравится, хотя по традиции используется нотация True и False (с заглавной буквы). Компилятор C++Builder разрешает использовать типизированные булевы переменные
bool, значения которых принято писать строчными буквами.
Ключевое слово Паскаля "with" на C++ не имеет эквивалента, поэтому переводя программу в C++Builder, вам придется добавлять спецификатор класса ко всем свойствам и методам. Таким образом, фрагмент кода на Паскале:
with Label do begin
Top := 20;
Left := 20;
Caption := 'Здравствуй, мир!' ;
end;
на C++ следует переписать в следующем виде:
Label ->Top =20;
Label->Left = 20;
Label->Caption = "Здравствуй, мир!";
Ключевое слово Паскаля "as" на C++ снова не имеет эквивалента, поэтому, переводя программу в C++Builder, следует использовать оператор динамического преобразования типа dynamic_cast. Этот оператор кажется менее удобным, однако его преимущество заключается в том, что проверка допустимости преобразования данного типа не связана с анализом исключений. Таким образом, фрагмент кода на Паскале:
with Sender as TLabel do
Caption := 'Здравствуй, мир1';
на C++ следует переписать в следующем виде:
TLabel* label = dynamic_cast
if (label)
label->Caption = "Здравствуй, мир1";
Паскалевы множества. C++ не поддерживает концепцию типа множества (set), как такового. Вместо множества C++ программисты используют битовые поля, которые не менее эффективны, однако более сложны для понимания и обращения. Поскольку VCL повсеместно использует паскалевы множества, для их интерпретации в C++Builder введен шаблонный класс Set. Множества очищаются посредством метода Clear. Для добавления элементов к множеству используется оператор вставки (“), а для удаления - оператор экстракции (”).
ПЕРВЫЕ ОПЫТЫ
2. C++BUILDER:
ПЕРВЫЕ ОПЫТЫ
В этой главе мы будем следовать плану первоначального знакомства со средой визуальной разработки C++Builder, изложенному в разделе Быстрый Тур (Quick Tour) системной документации.
C++Builder предназначен для быстрой разработки приложений (RAD), построенных на современном фундаменте объектно-ориентированного программирования (ООП). C++Builder сам постепенно будет помогать вам овладевать премудростями RAD, ООП и языка C++, поначалу требуя лишь минимальных предварительных знаний. Ваши навыки будут расширяться по мере роста сложности ваших разработок: чем сложнее задача, тем больший по объему код потребуется написать для ее реализации. Получение знаний перестает быть самоцелью. Вы сами убедитесь в том, что C++Builder в корне меняет процесс разработки насколько легче и быстрее вы сможете получать работающие и надежные программы для операционных систем Windows, чем при использовании традиционных интерфейсных оболочек других систем.
Иллюстрировать мощность и гибкость C++Builder будем на примере построения тестовых приложений, развивая их от простейших прототипов до законченных рабочих программ. Хотя наше первое приложение сложнее предлагаемого Быстрым Туром, автор надеется, что понять логику его разработки сможет новичок не только в ООП, но и в языке C++.
2.1 Первое знакомство
Как начинающие, так и опытные программисты начинают знакомство с новой системой с попытки создать простую программу, а чтение документации откладывается до лучших времен. При этом оценивают разные аспекты разработки:
насколько полезными оказываются ваши интуиция и опыт, лаконичность и объем кода, достоинства сервиса среды, временные затраты, удобства отладки и многое-многое другое. Последуем и мы по этому пути знакомства со средой программирования C++Builder.
Мы получим общее представление о работе со следующими основными инструментами интегрированной среды:
• Палитра компонент
содержит более 100 повторно используемых компо-нент,предлагаемых для построения приложений.
• Редактор форм
предназначен для создания интерфейса программы с пользователем.
• Редактор кода предназначен для написания текста программы, в частности, функций обработки событий.
• Инспектор объектов
позволяет визуально устанавливать свойства объектов без необходимости рутинного программирования и содержит события, которые можно связывать с кодами реакции объектов на их возникновение.
• Хранилище объектов
содержит такие объекты, как формы и модули данных, которые разделяются многими приложениями с целью уменьшения временных затрат при разработке.
C++Builder реализует визуальную методику построения приложений посредством выбора из Палитры компонент нужных управляющих элементов (Рис. 2.1). С каждой компонентой (например, кнопкой) связаны свойства, которые меняют ее вид и поведение. Любая компонента может вызывать серию событий, которые определяют ее реакцию на различные воздействия. В дальнейшем изложении символы => обозначают те действия, которые вы будете совершать в среде C++Builder.
=> Вызовите C++Builder и начните работу над новым приложением по команде File | New
Application из главного меню.
=> Щелкая мышью по вкладкам Палитры компонент, просмотрите имеющийся ассортимент элементов интерфейса программы с пользователем.

Рис. 2.1. Основные визуальные инструменты интегрированной среды C++ Builder.
Переходя от вкладки к вкладке Палитры, можно заметить, что набор доступных компонент меняется. Когда курсор мыши останавливается на значке компоненты, возникает подсказка с ее названием. Если нажать клавишу F1, справочная служба системы выдаст полную информацию о выбранной компоненте. Назначение всех компонент вы найдете в данной главе, а подробное их описание - в главе 4.
2.1.1 Визуальное проектирование
Наше первое приложение будет генерировать детскую считалочку "Десять негритят". В начальной версии потребуется только три объекта: список, поле редактирования и кнопка. Перенесем компоненты на форму проектирования и начнем постепенно развивать приложение. Метод перетаскивания (drag-and-drop) состоит в следующем: нажмите кнопку мыши на выбранной компоненте, переведите курсор на любое место формы, а затем снова нажмите кнопку мыши. Для начала ограничимся "стандартными" компонентами Палитры:
=> Выберите вкладку Standard.
=> Перетащите компоненту списка
ListBox на форму.
=> Перетащите компоненту поля редактируемого ввода EditBox.
2.1 Первое знакомство
Как начинающие, так и опытные программисты начинают знакомство с новой системой с попытки создать простую программу, а чтение документации откладывается до лучших времен. При этом оценивают разные аспекты разработки:
насколько полезными оказываются ваши интуиция и опыт, лаконичность и объем кода, достоинства сервиса среды, временные затраты, удобства отладки и многое-многое другое. Последуем и мы по этому пути знакомства со средой программирования C++Builder.
Мы получим общее представление о работе со следующими основными инструментами интегрированной среды:
• Палитра компонент
содержит более 100 повторно используемых компо-нент.предлагаемых для построения приложений.
• Редактор форм
предназначен для создания интерфейса программы с пользователем.
• Редактор кода предназначен для написания текста программы, в частности, функций обработки событий.
• Инспектор объектов
позволяет визуально устанавливать свойства объектов без необходимости рутинного программирования и содержит события, которые можно связывать с кодами реакции объектов на их возникновение.
• Хранилище объектов
содержит такие объекты, как формы и модули данных, которые разделяются многими приложениями с целью уменьшения временных затрат при разработке.
C++Builder реализует визуальную методику построения приложений посредством выбора из Палитры компонент нужных управляющих элементов (Рис. 2.1). С каждой компонентой (например, кнопкой) связаны свойства, которые меняют ее вид и поведение. Любая компонента может вызывать серию событий, которые определяют ее реакцию на различные воздействия. В дальнейшем изложении символы ==> обозначают те действия, которые вы будете совершать в среде C++Builder.
=> Вызовите C++Builder и начните работу над новым приложением по команде File | New
Application из главного меню.
=> Щелкая мышью по вкладкам Палитры компонент, просмотрите имеющийся ассортимент элементов интерфейса программы с пользователем.
=> Перетащите компоненту кнопки
Button.
=> Расположите компоненты и измените их размеры так, как вы хотели бы их видеть в окне вашего приложения.
С помощью Инспектора объектов определите начальные значения свойств компонент. В графе значений свойства Items списка нажмите кнопку в открывшемся окне редактора введите 7 первых строк стихотворения. В свойстве Caption формы и кнопки укажите их смысловые названия (соответственно, "Десять негритят" и "Результат"). В свойстве Text поля редактирования задайте строку подсказки результата ("Девять негритят").

Рис. 2.2. Визуальное проектирование формы приложения.
Рис. 2.2 показывает вид формы приложения и свойства компоненты списка TListBox после выполнения указанных действий. Теперь можно переключиться на Редактор кода и написать, как было принято, любую программу на языке C++, включая последние расширения стандарта ANSI/ISO. Однако, попытаемся сначала воспользоваться новыми средствами быстрой разработки приложений и дополнительными атрибутами компонент, заложенными в
C++Builder.
2.1.2 Свойства, методы и события
Быстрая разработка приложений подразумевает поддержку свойств, методов и событий компонент в рамках объектно-ориентированного программирования. Свойства позволяют вам легко устанавливать разнообразные характеристики компонент, такие как названия, контекстные подсказки или источники данных. Методы
(функции-члены) производят определенные операции над компонентным объектом, в том числе и такие сложные как воспроизведение или перемотка устройства мультимедиа. События связывают воздействия пользователя на компоненты, такие как активизация, нажатие кнопок или редактируемый ввод - с вашими кодами реакции на эти воздействия. Кроме того, события могут возникать при таких специфических изменениях состояния компонент как обновление данных в интерфейсных элементах доступа к базам данных. Работая совместно, свойства, методы и события образуют среду RAD интуитивного программирования надежных приложений для Windows.
=> В Инспекторе объектов укажите вкладку Событий (Events),
чтобы увидеть все события, ассоциированные с выбранным объектом.
=> Дважды щелкните мышью по компоненте кнопки, которую вы поместили на форму.
=> В открывшемся окне Редактора кода курсор покажет позицию для ввода инструкций в тело функции ButtonlClick, предназначенной для обработки события OnClick,
возникающего при нажатии кнопки.
Рис. 2.3 показывает простой код, который в ответ на очередное нажатие кнопки "Результат" присоединяет предыдущую подсказку
prev к концу списка и заносит следующую подсказку next в поле редактирования. Инструкция ListBoxl->Items->Append (prev) присоединяет, с помощью метода Append, строку prev к свойству Items объекта списка ListBoxl.
Инструкция Editl->Text = next присваивает строку
next свойству
Text объекта редактируемого ввода Editl. Строки подсказок хранятся в двумерном массиве count и индексируются целой переменной типа static, сохраняющей текущее значение между вызовами функции обработки события, возникающего при нажатии кнопки Buttonl.

Рис. 2.3. Редактор кода обеспечивает ввод и редактирование текста исполняемого модуля в файле Unit1.cpp.
Этап проектирования первой версии приложения на этом завершается и можно приступить к созданию рабочей программы.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки приложения.
=> После вызова программы несколько раз нажмите на кнопку "Результат".

Рис. 2.4 показывает вид формы приложения после девяти нажатий на кнопку "Результат". Чтобы игнорировать последующие нажатия кнопки, в функции обработчика события предусмотрена инструкция возврата
if (i == 9)
return;
Разумеется, вместо предлагаемых программой подсказок, можно вводить любую строку результата - синтаксический (и, тем более, семантический) анализ текста не предусмотрен.
Рис. 2.4. Работа первой версии приложения "Десять негритят".
2.1.3 Технология двунаправленной разработки
C++ Builder не ставит никаких барьеров между программистом и его кодом. Технология двунаправленной разработки Two-Way Tools
обеспечивает контроль за вашим кодом посредством гибкого, интегрированного и синхронизированного взаимодействия между инструментами визуального проектирования и Редактором кода.
Чтобы проследить за тем, как действуют инструменты двунаправленной разработки, выполните следующие операции:
=> Откройте контекстное меню Редактора кода щелчком правой кнопки мыши, а затем с помощью опции Swap Cpp/Hdr Files переключитесь на файл объявлений Unit1.h..
=> Организуйте экранное отображение инструментов так, чтобы одновременно видеть проектируемую форму и файл Unitl.h в окне Редактора кода.
=> Перетащите еще одну компоненту кнопки OK Button на форму. В свойстве Caption кнопки укажите ее смысловое название "Новый куплет".

Рис. 2.5. C++Builder автоматически генерирует объявления в файле Unit1.h,
поддерживая постоянный контроль программиста над его кодом
Проследите за тем, что как только вы перенесли кнопку на форму, объявление объекта Button2 моментально появится в файле Unit1.h, а определение события OnClick
сгенерирует объявление метода
Button2Click обработчика этого события (Рис. 2.5). Такая синхронизация процессов проектирования формы и автоматической генерации кода действительно ускоряет визуальную разработку C++ приложения, полностью сохраняя контроль над исходным текстом программы.
Сделаем еще один шаг в разработке нашего первого приложения - заставим его генерировать куплеты автоматически. Для этого придется наполнить содержанием функцию обработки события OnClick при нажатии кнопки "Новый куплет".

Рис. 2.6. Новый обработчик события в файле Unit1.cpp.
Рис. 2.6 показывает простой код, который в ответ на очередное нажатие кнопки "Новый куплет" выводит друг за другом семь строк нового куплета, причем первая и третья строки берутся из переменной prev. Поскольку значение этой переменной присваивает обработчик события кнопки "Результат", пришлось переопределить ее в секции public пользовательских объявлений класса формы (выделенная строка на Рис. 2.5), чтобы сделать доступной обработчикам событий обеих кнопок. Смысл этого, пока еще непонятного действия ООП, прояснится в главе 3.

Рис. 2. 7 показывает работу законченной версии приложения "Десять негритят" после девяти парных нажатии на кнопки "Результат" и "Новый куплет". Список приобрел вертикальную линейку прокрутки, чтобы можно было просматривать все стихотворение целиком.
Следует запомнить, что C++Builder ассоциирует с каждым приложением три исходных файла со следующими именами по умолчанию:
• Unitl.cpp хранит исполняемый код реализации вашего приложения. Именно в нем вы записываете обработчики событий, отвечающие за реакцию программы при воздействии пользователя на объекты компонент.
• Unitl.h содержит объявления всех объектов и их конструкторов. Обратите внимание на ключевое слово _fastcall в объявлениях функций обработки событий, которые C++Builder генерирует автоматически. Благодаря _fastcall передача параметров организуется не через стек, а через регистры центрального процессора. Вызовы обработчиков событий происходят очень часто, поэтому экономия времени, затрачиваемого на выборку параметров из памяти стека, оказывается весьма ощутимой. Здесь кроется одна из причин высокого быстродействия приложений, которые компилирует и собирает C++Builder.
• Projectl.cpp
обслуживает все объекты, заключенные в приложении. Любая новая форма, программный модуль или модуль данных автоматически включаются в проектный файл. Вы можете просмотреть в окне Редактора кода содержание исходного текста проектного файла с помощью команды главного меню View | Project Source или выбрав одноименную опцию из контекстного меню Администратора проекта. Ни в коем случае не редактируйте проектный файл вручную!
Быть может, завершив разработку первого приложения, вы захотите сохранить исходные файлы для следующего сеанса, выполнив одно из следующих действий:
=> Команда File | Save All сохраняет все исходные файлы приложения.
Команда File | Save сохраняет оба файла программного модуля, а команда File
| Save As позволяет дать им новое имя.
Команда File | Save Project As сохраняет изменения всех составляющих проектного файла, используя текущие имена файлов.
2.1.4 Использование проектных шаблонов
Использование готовых проектных шаблонов из Хранилища объектов дает вам возможность начать разработку, опуская типичные для многих приложений предварительные операции по составлению главного меню и панели кнопок быстрого вызова, организации стандартных диалогов вызова и сохранения файлов. Внесенные вами изменения никак не отражаются на использовании того же проектного шаблона другими разработчиками.
Чтобы создать на основе проектного шаблона прототип приложения для работы в режиме многодокументного интерфейса (MDI), произведите следующие действия:

Рис. 2.8. Проектные шаблоны. усмотрению свойства компонент главной формы. В окне редактора фильтров, в графе значений свойства Filter компоненты TOpenDialog, укажите название и расширение файлов текстовых документов.
Рис. 2.9 показывает главную форму прототипа приложения, спроектированную на базе готового шаблона.

Рис. 2.9. Главная форма прототипа MDJ приложении для роботы с текстовыми файлами.
Если вы скомпилируете и соберете такое приложение, то увидите, что оно "умеет" только оперировать с окнами в режиме
MDI и вызывать диалог открытия файлов, не заполняя окна текстовым содержимым выбранных файлов. То есть прототип оказался нефункциональным и практически бесполезным. Чтобы придать приложению некоторое осмысленное поведение, выполните следующие действия:

Дайте команду View | Forms из главного меню и выберите из списка дочернюю форму с именем MDIChild (Рис. 2.10).
Memo
Перетащите компоненту
многострочного поля редактирования из вкладки Standard Палитры на дочернюю форму.
Очистите поле редактирования компоненты TMemo, вызвав нажатием строчный редактор свойства
Lines. Установите значение alClient свойства Align с тем, чтобы поле редактирования занимало все дочернее окно. Установите значение ssBoth свойства ScrolBars, чтобы сделать просмотр длинных текстовых файлов более удобным (Рис. 2.11).

Рис. 2.11
Проектирование дочерней формы MDlChild.
=> Снова вернитесь к главной форме, активизировав ее с помощью мыши, и выберите команду File | Open из меню приложения.
=> В окне Редактора кода курсор покажет позицию для ввода инструкции в обработчик события OnClick, возникающего при выборе соответствующего элемента меню. C++Builder автоматически генерирует объявление этой функции для компоненты главной формы TOpenDialog
(из вставки Dialogs Палитры компонент).

Рис. 2.12. Реализация загрузки дочернего окна в файле Main.cpp.
Выделенная инструкция загружает строки Lines объекта Memo1 дочернего окна Child содержимым открытого текстового файла с именем
OpenDialog->FileName.
Конечно, разработка этого приложения еще далека до завершения. Когда вы скомпилируете и соберете его, то сможете редактировать текстовые файлы одновременно в нескольких окнах (Рис. 2.13). Однако сохранение результирующих файлов пока не предусмотрено - читатель без труда сам напишет код для команд меню File [Save и File |Save As.

Рис. 2.13. Работа приложения "MDI Application ".
Логичным развитием приложения, превращающим его в простейший текстовый редактор, было бы добавление команд поиска и замены в выпадающий список элемента главного меню под названием Edit.
2.2 Палитра компонент - краткий обзор
2.2.1 Компоненты вкладки Standard
Компоненты этой вкладки осуществляют включение в ваше приложение следующих типовых интерфейсных элементов Windows:
| ТМашМепи | Создает панель команд главного меню для формы. |
| TPopUpMerm | Создает "выскакивающее" меню для формы или для другой компоненты. |
| TLabel | Отображает на форме текст названия, который нельзя редактировать. |
| TEdit | Отображает область редактируемого ввода одиночной строки информации на форме. |
| TIVlemo | Отображает область редактируемого ввода множественных строк информации на форме. |
| TButton | Создает кнопку с надписью. |
| TCheckBox | Создает элемент управления с двумя состояниями. |
| TRadioButton | Создает элемент управления с двумя состояниями. |
| TListBox | Отображает область списка текстовых строк. |
| TComboBox | Создает комбинацию области редактирования и выпадающего списка текстовых строк. |
| TScrollBar | Создает линейку прокрутки для просмотра содержимого окна, формы, списка или диапазона значений. |
| TGroupBox | Создает контейнер, объединяющий на форме логически связанную группу некоторых компонент. |
| TRadioGroup | Создает контейнер, объединяющий на форме группу логически взаимоисключающих радио-кнопок. |
| TPanel | Создает панель инструментов или строк состояния. |
2.2.2 Компоненты вкладки Win95
Компоненты этой вкладки осуществляют включение в ваше приложение следующих типовых интерфейсных элементов Windows 95:
| TTabControl | Отображает набор полей, имеющих вид частично перекрывающих друг друга картотечных вкладок. |
| TPageControl | Отображает набор полей, имеющих вид частично перекрывающих друг друга картотечных вкладок, для организации многостраничного диалога. |
| TTreeView | Отображает древовидный перечень элементов - заголовков документов, записей в указателе, файлов или каталогов на диске. |
| TListView | Отображает древовидный перечень элементов в различных видах - по столбцам с заголовками, вертикально, горизонтально, с пиктограммами. |
| TImageList | Создает контейнер для группы изображений. |
| THeaderControl | Создает контейнер для заголовков столбцов. |
| TRichEdit | Отображает область редактируемого ввода множественных строк информации в формате RTF. |
| TStatusBar | Создает строку панелей состояния для отображения статусной информации. |
| TTrackBar | Создает шкалу с метками и регулятором текущего положения. |
| TProgressBar | Создает индикатор процесса выполнения некоторой процедуры в приложении. |
| TUpDown | Создает спаренные кнопки со стрелками "вверх" и "вниз". Нажатие этих кнопок вызывает увеличение или уменьшение значения свойства Position. |
| THotKey | Используется для установки клавиш быстрого вызова во время выполнения программы. |
Компоненты этой вкладки осуществляют включение в ваше приложение следующих специализированных интерфейсных элементов Windows:
| TBitBtn | Создает кнопку с изображением битового образа. |
| TSpeedButton | Создает графическую кнопку быстрого вызова. |
| TMaskEdit | Создает область редактируемого ввода данных специфического формата. |
| TStringGrid | Создает сетку для отображения строк по строкам или столбцам. |
| TDrawGrid | Создает сетку для отображения графических данных по строкам или столбцам. |
| TImage | Создает на форме контейнер для отображения битового образа, пиктограммы или метафайла. |
| TShape | Рисует простые геометрические фигуры. |
| TBevel | Создает линии и рамки с объемным видом. |
| TScrollBox | Создает контейнер переменного размера с линейками прокрутки, если это необходимо |
Следуя пионерской традиции нисходящего визуального
ПРЕДИСЛОВИЕ
Следуя пионерской традиции нисходящего визуального стиля программирования
Delphi на Объектном Паскале, корпорация Borland выпускает на рынок систем быстрой разработки приложений RAD (Rapid Application Development) мощную систему под названием C++ Builder на языке C++.
Вместо отдельного инструментария, оперирующего визуальными элементами управления, в C++ Builder
интегрирована так называемая Палитра компонент, разделенная картотечными вкладками на несколько функциональных групп. Функциональные возможности поставляемых компонент можно достаточно просто модифицировать, а также разрабатывать компоненты, обладающие совершенно новым оригинальным поведением.
Система содержит Библиотеку из более 100 повторно используемых визуальных компонент, которые перетаскиваются мышью на форму и сразу становятся элементами управления прототипа вашей программы. Помимо известных элементов управления Windows (кнопки, линейки прокрутки, поля редактирования, простые и комбинированные списки и т.д.) Библиотека содержит новые компоненты поддержки диалогов, обслуживания баз данных и многие другие
После размещения компонент на форме. Инспектор объектов поможет вам устанавливать их свойства и предписывать событиям коды обработки. Ваш проект будет строиться постепенно, на фоне производимых вами изменений в свойствах, событиях и функциях используемых элементов. Хорошо продумано разделение и редактирование программного модуля по двум его частям: интерфейсной и собственно кодовой.
Опытным C++ программистам понравится синтаксис и структура кода разрабатываемых на C++Builder программ, хотя его графическое обрамление заметно отличается от традиционных оболочек систем разработки. Благодаря графическим средствам интегрированной среды C++Builder, новички смогут быстрее освоить стиль объектно-ориентированного программирования на C++, чем при использовании традиционного программно-текстового интерфейса других систем.
C++Builder поддерживает основные принципы объектно-ориентированного программирования - инкапсуляцию, полиморфизм и множественное наследование, а также нововведенные спецификации и ключевые слова в стандарте языка
C++.
C++ Builder обеспечивает высокое быстродействие при компиляции и сборке 32-разрядных приложений для современных операционных систем Windows 95 и Windows NT, включая OLE взаимодействие клиент-сервер. Система даже отображает время, затраченное на основные этапы построения программ. Результирующие программы хорошо оптимизированы по скорости исполнения и затратам памяти. Хотя отладочный режим низкого уровня полностью интегрирован в среду C++Builder, к отладке также пришлось привыкать. Дизайнер форм. Инспектор объектов и другие средства остаются доступными во время работы программы, поэтому вносить изменения можно в процессе отладки.
C++Builder поставляется в трех вариантах: Standard (стандартный). Professional (для профессионалов разработчиков, ориентированных на сетевую архитектуру) и Client/Server Suite (для разработки систем в архитектуре клиент/сервер). Последние два варианта дополняют стандартный исходными текстами визуальных компонент, разномасштабным словарем данных, новыми функциями языка запросов SQL для баз данных, пакетом поддержки систем
Internet, службой мониторинга программ, а также рядом других средств.
Эксперименты с тестовыми программами в рамках стандартного варианта легли в основу материала, излагаемого в книге. Испытывая систему, я переложил на C++ Builder несколько приложений, ранее написанных на Borland C++ версии 4.5. Благодаря визуальным компонентам, из программ исчезла "кодовая шелуха" обработки сообщений Windows и ресурсных файлов, и остался только содержательный код. Пользовательский интерфейс приложений приобрел законченный профессиональный облик.
Хотя C++ Builder
представляется весьма надежной системой, корпорации еще предстоит опровергнуть расхожее утверждение, что в каждой отлаженной программе (в том числе и в коммерческой) есть по меньшей мере одна ошибка. Видимо, именно этим стремлением объясняется излишняя, на мой взгляд, поспешность с рекламированием "улучшенной и расширенной" версии Borland C++ версии 5.02.
C++ Builder поддерживает связь с различными базами данных 3-х видов:
dBASE и Paradox: Sybase, Oracle, InterBase и Informix; Excel, Access, FoxPro и Btrieve. Механизм BDE (Borland Database Engine)
придает обслуживанию связей с базами данных удивительную простоту и прозрачность. Проводник Database Explorer
позволяет изображать связи и объекты баз данных графически. Используя компоненты баз данных, я построил электронную записную книжку по таблице dBASE за полчаса работы на компьютере. Наследование готовых форм и их "подгонка" под специфические требования заметно сокращают временные затраты на решение подобных задач.
Справочная служба C++ Builder оказывала мне помощь в этой и многих других подобных ситуациях. Имеется полное описание каждой управляющей компоненты, включая списки свойств и методов, а также многочисленные примеры. Изложение материала в книге было значительно улучшено и систематизировано благодаря сведениям, почерпнутым мною из справочной службы.
Благодаря средствам управления проектами, двусторонней интеграции приложения и синхронизации между средствами визуального и текстового редактирования, а также встроенному отладчику (с ассемблерным окном прокрутки, пошаговым исполнением, точками останова, трассировкой и т.п.) - C++ Builder корпорации Borland предоставляет собой впечатляющую среду разработки, которая, видимо, выдержит конкурентную борьбу с такими модными продуктами как Developer Studio фирмы Microsoft.
TDBImage
4.6.5.6 TDBImage
Создает, по аналогии с компонентой
TImage, контейнер для представления графического изображения, которое хранится в текущей записи набора данных в виде Большого Бинарного Объекта (BLOB). Установка значения
false свойства Readonly разрешает модификацию изображения.
Вы должны связать контейнер изображения с набором данных посредством компоненты источника, который определяется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Свойство AutoDisplay управляет автоматическим масштабированием контейнера. Чтобы исходное изображение меньшего размера растянулось на весь контейнер, установите значение true свойства Stretch. Если вы передумали и хотите вернуть изображение в исходное состояние, нажмите клавишу Esc - конечно, до того, как перейти к другой записи. Во время выполнения программы вы можете вырезать, копировать и восстанавливать изображение из базы данных, нажимая типовые комбинации клавиш (Ctrl+X, Ctrl+C и Ctrl+V).
4.6.5.7 TDBListBox
Создает, по аналогии с компонентой
TListBox, список, выбранный элемент которого становится новым значением поля текущей записи в наборе данных.
Вы должны связать список с набором данных посредством компоненты источника, который идентифицируется значением свойства
DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Элементы списка, которые может выбирать пользователь, содержатся в свойстве
Items, а номер выбранного элемента - в свойстве Itemlndex. Вы можете динамически добавлять, вычеркивать и вставлять элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
DBListBoxl->Items.Delete(3);
4.6.5.8 TDBComboBox
Создает, по аналогии с компонентой
TComboBox, комбинацию области редактирования и выпадающего списка текстовых вариантов для выбора. Текст, введенный в область редактирования или выбранный из списка, становится новым значением поля текущей записи в наборе данных, при условии, что свойство Readonly имеет значение false.
Вы должны связать комбинированный список с набором данных посредством компоненты источника, который идентифицируется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Элементы списка, которые может выбирать пользователь, содержатся в свойстве
Items, номер выбранного элемента - в свойстве Itemlndex, а сам выбранный текст - в свойстве SelText. Свойства SelStart и SelLength позволяют установить выборку части текста или обнаружить, какая часть текста выбрана. Вы можете динамически добавлять, вычеркивать и вставлять элементы списка с помощью методов Add, Append, Delete и Insert объекта Items, например:
DBComboBoxl->Items->Insert(4, "Пятый элемент списка");
Правила сортировки списка задаются свойством Sorted. Вид и поведение компоненты TDBComboBox можно выбрать в свойстве Style.
4.6.5.9 TDBCheckBox
Предоставляет по аналогии с компонентой TCheckBox чек-бокс выбора состояния, связанного с конкретной записью в наборе данных.
Вы должны связать чек-бокс с набором данных посредством компоненты источника, который определяется значением свойства
DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Если содержимое поля текущей записи совпадает со значением свойства ValueChecked, бокс переходит в начальное состояние "checked". Если содержимое поля текущей записи совпадает со значением свойства ValueUnchecked, бокс переходит в начальное состояние
"unchecked". В зависимости от установленного пользователем состояния бокса, текущая запись приобретает значение свойства ValueChecked или ValueUnchecked, при условии, что свойство Readonly имеет значение false и набор данных находится в режиме редактирования. Если вы хотите запретить пользователю изменять поля записей, установите значение true
свойства ReadOnly.
4.6.5.10 TDBRadioGroup
Предоставляет по аналогии с компонентой TRadioGroup контейнер для группы логически взаимоисключающих радио-кнопок, связанных с конкретными полями записей в наборе данных.
Радио-кнопки "группируются" при помещении их в один и тот же контейнер. Только одна кнопка из данной группы может быть выбрана. Когда пользователь выбирает кнопку, ее "значение" становится содержимым поля текущей записи в наборе данных. Значения кнопок можно редактировать в свойстве Values: первой строке Values соответствует первая строка свойства Items
и т.д.
Группу радио-кнопок можно использовать для обеспечения единственного выбора из альтернатив, представленных полями записей, а также - для отображения данных поля, имеющего несколько возможных значений.
Вы должны связать группу радио-кнопок с набором данных посредством компоненты источника, который идентифицируется значением свойства DataSource. Свойство DataField содержит поле в наборе данных, к которому вы хотите обратиться.
Добавление кнопок к компоненте
TDBRadioGroup выполняется редактированием свойства Items. Присвоение названия очередной строке свойства Items приводит к появлению этой кнопки в группирующей рамке. Значение свойства Itemlndex определяет, какая радио-кнопка выбрана в настоящий момент. Вы можете группировать радио-кнопки в несколько столбцов, устанавливая соответствующее значение свойства Columns.
4.6.5.11 TDBLookupListBox
Создает таблицу ссылок для заполнения полей информацией из другого набора данных.
Небольшая по объему таблица ссылок (lookup table) содержит коды разрешенных значений, которые может принимать некоторое поле. Например, в базе данных электронной записной книжки обязательно имеются записи почтовых адресов. Название города и улицы из конкретного адреса можно закодировать в таблицах ссылок "Города" и "Улицы". Компоненты будут эффективно обращаться с короткими кодами полей (например, "МК" вместо "Москва" или "ЛНП" вместо "Ленинградский проспект"), структура базы данных станет равномерной, а пользователь по-прежнему будет оперировать с полями полных названий.
Вы должны связать компоненту TDBLookupListBox с набором данных посредством компоненты источника TDataSource, который идентифицируется значением свойства
DataSource. Свойство DataField указывает либо на поле в наборе данных, либо на поле в таблице ссылок, к которому вы хотите обратиться. В последнем случае вам не надо определять прочие свойства - компонента сделает это самостоятельно.
Не забудьте поместить на форму еще пару компонент TTable и TDataSource, чтобы получать информацию из таблицы ссылок. Свойство ListSource идентифицирует источник таблицы ссылок. Свойство ListField представляет поле в таблице ссылок, содержащее код ссылки как таковой. Свойство KeyField определяет индекс поля ссылки, которое вы хотите скопировать в DataField.
4.6.5.12 TDBLookupComboBox
Создает комбинацию области редактирования и выпадающей таблицы ссылок для заполнения полей информацией из другого набора. Смысловое содержание свойств компонент TDBLookupComboBox и TDBLookupListBox полностью совпадает.
4.6.6 Компоненты Win 3.1
Компоненты вкладки Win 3.1 палитры компонент

представляют 7 интерфейсных элементов Windows 3.1 для включения в вашу программу.
4.6.6.1 TDBLookupList
Эта компонента поддерживает совместимость с системой Windows
3.1; ее аналогом является компонента TDBLookupListBox из вкладки Data Controls.
4.6.6.2 TDBLookupCombo
Эта компонента поддерживает совместимость с системой Windows
3.1; ее аналогом является компонента TDBLookupComboBox из вкладки Data Controls.
4.6.6.3 [TTabSet
Отображает ряд горизонтальных вкладок, нажатие на которые инициирует некоторые действия в вашей программе. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента TTabControl из вкладки Win95.
Названиявкладок вводятся в список свойства Tabs кнопкой в графе значений этого свойства. TTabSet обычно используется совместно с компонентой TNotebook для представления ряда страниц блокнота в одном диалоговом окне. В этом случае обработчик события OnClick сначала соединяет все страницы блокнота с вкладками, а затем меняет текущую страницу блокнота при нажатии на новую вкладку:
TabSetl->Tabs = Notebookl->Pages;
Notebookl->PageIndex = TabSetl->TabIndex;
Номер выбранной вкладки содержится в свойстве Tablndex. Чтобы определить, какая вкладка является (или сделать вкладку) первой видимой в наборе, воспользуйтесь свойством Firstlndex.
Несколько свойств управляют видом компоненты. Значение alBottom свойства Align требует разместить вкладки внизу формы. Цветовое различие выбранной и невыбранных вкладок задают свойства SelectedColor и UnselectedColor. Свойства BackgroundColor и DitherBackground меняют цвет и яркость фона под набором вкладок. Расстояние вкладок от краев компоненты определяют свойства StartMargin и EndMargin. Чтобы появлялись кнопки прокрутки, когда не хватает места для отображения всех вкладок, установите значение true свойства AutoScroll. Выбор между надписями и графическими изображениями на вкладках осуществляет свойство Style.
4.6.6.4 TOutline
Представляет механизм построения многоуровневой древовидной структуры для определенной иерархии данных.
Отрезки линий обводки (ветви дерева) пронумерованы, начиная с 1, и содержатся в массиве свойства Items.
Одна и та же, например, первая (верхняя) ветвь некоторого дерева Outlinel может адресоваться прямо, как Outlinel[l], или косвенно, как Outlinel. Items[l]. Дерево имеет смысл строить во время выполнения программы. Добавляйте новые родительские ветви к дереву с помощью методов Add и
AddObject. Добавляйте новые ветви потомков с помощью методов AddChild и AddChildObject. Для замены существующей ветви используйте методы Insert и InserObject. Метод Delete вычеркивает указанную ветвь.
Для ускорения выполнения перечисленных действий заключите их между методами
BeginUpdate и
EndUpdate, что избавит от избыточной перенумерации ветвей в процессе создания дерева. Значение свойства
Selectedltem отражает номер текущей выбранной ветви.
Узлы дерева могут сопровождаться идентифицирующими картинками. Свойство OutlineStyle определяет очертание дерева и тип узловых картинок, а их вид конкретизируют свойства PictureLeaf, PictureMinus, PicturePlus, PictureOpen и PictureClosed.
4.6.6.5 THeader
Отображает секционированный заголовок и позволяет менять размеры его секций, манипулируя кнопками мыши. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента THeaderControl из вкладки Win95.
Секции заголовка содержатся в свойстве Sections. Можно изменять размер выбранной секции и перетаскивать ее границы в новое положение, удерживая нажатой кнопку мыши: на стадии проектирования - правую кнопку, а во время выполнения программы - левую. Размеры других секций остаются без изменения.
Если свойство AllowResize разрешает изменение размера, то в начале изменения происходит событие OnSizing, а по окончании изменения - событие OnSized. Обработчики этих событий могут, например, выровнять текст под заголовком в соответствии с новой шириной секции.
4.6.6.6 TTabbedNotebook
Представляет ряд страниц блокнота, каждая из которых содержит собственный набор элементов управления. Нажимая на закладку, которая выступает из верхней части страницы, пользователь выбирает ее. Эта компонента поддерживает совместимость с Windows 3.1; ее аналогом является компонента TPageControl из вкладки Win95.
Доступные страницы блокнота с закладками представляют собой символьные последовательности, перечисленные как значения свойства Pages.
Окно редактора блокнота (Рис. 4.20) открывается кнопкой в графе значений этого свойства. Вы можете активизировать конкретную страницу одним из следующих способов: выбрав ее из выпадающего списка свойства ActivePage, a также перелистывая закладки с помощью опций Next Page и Previous Page контекстного меню.

Рис. 4.20. Конструирование заготовки блокнота.
Если вы хотите переопределить значение указателя Pagelndex для конкретной страницы, обратитесь к методу GetIndexForPage. Свойство TabsPerRow определяет число закладок в одном ряду. Если в блокноте имеется больше страниц, чем закладок, умещающихся в ряду, автоматически достраивается следующий ряд закладок. Шрифт надписей на закладках задается свойством TabFont.
4.6.6.7 TNotebook
Представляет ряд страниц блокнота. Пользователь выбирает нужную страницу, щелкая по ней мышью. Эта компонента обычно используется совместно с компонентой TTabSet, чтобы нажатием на соответствующие вкладки выбирать страницы. Совместное использование компонент описано в п.4.6.6.3.
Доступные страницы блокнота представляют собой символьные последовательности, перечисленные как значения свойства
Pages. Обратиться к конкретной странице блокнота можно посредством свойств Pagelndex
или ActivePage.
4.6.7 Диалоговые компоненты
Компоненты вкладки Dialogs палитры компонент

осуществляют включение в вашу программу 8 диалоговых элементов Windows, являющихся прямыми производными от абстрактного класса
TCommonDialog.
4.6.7.1 TOpenDialog
Открывает в вашей программе доступ к диалогу открытия файлов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал файл нужного типа и нажал кнопку ОК, имя файла заносится в свойство FileName. С помощью свойства
Filter пользователь определяет, какие файлы сделать видимыми в списке файлов. Свойство Filterlndex определяет фильтр по умолчанию.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, разрешить выборку нескольких файлов одновременно; имена этих файлов сохранятся в списке свойства Files. Если вы хотите, чтобы расширения автоматически приписывались к именам файлов, печатаемых в области редактируемого ввода, используйте свойство DefaultExt.
4.6.7.2 TSaveDialog
Открывает в вашей программе доступ к диалогу сохранения файлов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал файл нужного типа и нажал кнопку ОК, имя файла заносится в свойство FileName. С помощью свойства
Filter пользователь определяет, какие файлы сделать видимыми в списке файлов. Свойство Filterlndex определяет фильтр по умолчанию.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты. например, разрешить выборку нескольких файлов одновременно; имена этих файлов сохранятся в списке свойства Files. Если вы хотите, чтобы расширения автоматически приписывались к именам файлов, печатаемых в области редактируемого ввода, используйте свойство DefaultExt.
4.6.7.3 TFontDialog
Открывает в вашей программе доступ к диалогу выбора шрифтов и их атрибутов. Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал нужный шрифт и нажал кнопку ОК, выбранный шрифт заносится в свойство Font. Свойство Device позволяет выбрать устройство, на которое повлияет сделанное изменение шрифта.
Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, включить кнопку Help в окно диалога или разрешить появление в списке только шрифтовых файлов с расширением .ttf(True Type Fonts).
4.6.7.4 TColorDialog
Открывает в вашей программе доступ к диалогу выбора цветов. Метод Execute активизирует окно диалога во время выполнения программы.
После того, как пользователь выбрал нужный цвет и нажал кнопку ОК, диалог закрывается, и выбранный цвет заносится в свойство
Color.
4.6.7.5 TPrintDialog
Открывает в вашей программе доступ к диалогу печати. Метод Execute активизирует окно диалога во время выполнения программы.
С помощью этого диалога можно выбрать принтер, задать диапазон печатаемых страниц, число копий с подбором (collate) страниц в копиях, а также - требование печати в файл. Указанные параметры отражаются значениями соответствующих свойств данной компоненты.
При нажатии на кнопку Setup происходит вызов диалога установок принтера (TPrinterSetupDialog). Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, отменить появление опции печати в файл.
4.6.7.6 TPrinterSetupDialog
Открывает в вашей программе доступ к диалогу предварительных установок принтера перед печатью. Метод Execute активизирует окно диалога во время выполнения программы.
4.6.7.7 TFindDialog
Открывает в вашей программе доступ к диалогу поиска текста. Метод Execute активизирует окно диалога во время выполнения программы.
Значением свойства FindText является искомый текст. Манипулируя свойством Options, вы можете изменить вид и поведение компоненты, например, отменить появление встроенных компонент TCheckBox под названиями Match Case, Whole Word и др.
Когда пользователь вводит искомый текст и нажимает кнопку Find Next, обработчик возникающего события OnFind производит поиск текста, являющегося значением свойства FindText.
4.6.7.8 TReplaceDialog
Открывает в вашей программе доступ к диалогу поиска текста с заменой. Метод Execute активизирует окно диалога во время выполнения программы. Эта компонента обладает всей функциональностью предыдущей, кроме того, позволяя заменять найденный текст новым.
4.6.7.9 Использование диалоговых компонент текстовым редактором. Приемы отладки
C++Builder поставляется вместе с примером текстового редактора файлов формата RTF.
который демонстрирует работу диалоговых компонент TOpenDialog, TSaveDialog, TFontDialog и TPrintDialog. Приложение также использует ранее описанные компоненты TMainMenu, TRichEdit, TPanel, TEdit, TSpeedButton. TComboBox, TUpDown, TLabel, TBevel и способно предоставлять контекстно-зависимую помощь из файла
RICHEDIT.HLP.
=> По команде главного меню File |
Open Project откройте диалог выбора проектов.
=> Войдите в каталог
\...\CBuilder\Examples\Apps\RichEdit.
=> Выберите проектный файл с именем
RichEdit и нажмите кнопку Open.

Рис. 4.21. Форма текстового редактора.
Рис. 4.21 показывает главную форму текстового редактора с перечнем имен основных обработчиков событий, коды которых, как правило, и составляют содержание файлов программных модулей приложений для C++Builder.
Компонента TRichEdit занимает всю свободную область формы редактора, причем вложенные свойства
RichEditl->Paragraph содержат атрибуты форматирования параграфов документа. Объект FontName
представляет название шрифта, выбранное пользователем из выпадающего списка
TComboBox. Для установки размера шрифта служит объект FontSize области редактируемого ввода TEdit, сопряженный с кнопками TUpDown. Под панелью инструментов рас-
положена измерительная линейка с тремя регуляторами форматирования (объекты типа TLabel): левого отступа параграфа Leftind, ширины красной строки Firstind и правого отступа параграфа Rightind (последний регулятор появится после запуска программы). Внизу формы расположена панель строки состояния
StatusBar, предназначенная для вывода пояснений к командам меню.
Меню редактора включает типовые команды управления, некоторым из которых поставлены в соответствие быстрые кнопки на панели инструментов.
| File | Edit | Help | ||
| New Open Save Save As | |
Undo | Contents Search for Help On How to Use Help | |
| Cut Copy Paste | ||||
| About | ||||
| Font | |
|||
| Exit | |
RICHEDIT.RTF из каталога \...\CBuilder\Examples\Apps\RichEdit\Help, а затем собрать новую версию RICHEDIT.HLP с помощью утилиты
BUILDHLP.BAT. Наличие действующей команды меню Help придает программе профессиональный облик и облегчает работу пользователя.
После того, как вы запустите редактор и испытаете что он умеет, придет черед разобраться в том как он это делает. Листинг 4.1 содержит полный текст файла кодового модуля Romain.cpp с необходимыми комментариями. Несмотря на большой размер, целесообразно привести текст целиком, поскольку им вполне можно руководствоваться при создании собственных стилизованных приложений профессионального уровня с меню, быстрыми кнопками, диалогами и контекстной помощью.
ttinclude
#include "Romain.h"
^include "RichAbt.h"
const float RulerAdj = 4.0/3.0; //
цена деления линейки
const int GutterWid = 6; //
ширина поля подшивки
//-----_-------________--________--________--_____-__--_____
#pragma resource "*.dfm" TMainForm *MainForm;
// Конструктор главной формы приложения _fastcall
TMainForm::TMainForm(TComponent *0wner)
: TFormfOwner) { SetFileName((AnsiString)"Untitled") ;
)
// Установка текущих атрибутов Форматирования текста void _fastcall TMainForm::SelectionChange(TObject*) { char sizebuf[6];
try { FUpdating = true;
FirstInd->Left = // левая граница красной строки int(RichEditl->Paragraph->FirstIndent*RulerAdj)-
4+GutterWid;
LeftInd->Left = // левый отступ параграфа int((RichEditl->Paragraph->LeftIndent+
RichEditl->Paragraph->FirstIndent)*RulerAdj)-
4+GutterWid;
RightInd->Le?t = II правый отступ параграфа •" Ruler->ClientWidth-6-
int((RichEditl->Paragraph->RightIndent+GutterWid)*
RulerAdj) ;
BoldButton->Down = //
состояние кнопки "жирный" RichEditl->SelAttributes->Style.Contains(fsBold) ;
ItalicButton->Down = //состояние кнопки "курсив" RichEditl->SelAttributes->Style.Contains(fsltalic) ;
UnderlineButton->Down = //
состояние кнопки "подчерк." RichEditl->SelAttributes->Style.Contains(fsUnderline) ;
BulletsButton->Down = //
состояние кнопки "нумерация" bool(RichEditl->Paragraph->Numbering) ;
FontSize->Text = // размер шрифта itoa(RichEditl->SelAttributes->Size, sizebuf, 10);
FontName->Text = // название шрифта RichEditl->SelAttributes->Name;
// Состояние кнопок выравнивания параграфа switch((int)RichEditl->Paragraph->Alignment) { case 0: LeftAlign->Down = true; break;
case 1: RightAlign->Down = true; break;
case 2: CenterAlign->Down = true; break;
} }
catch (...)
{ FUpdating = false; } //
ошибка (поймано исключение) FUpdating =
false;
}
// функция возвращает установленные атрибуты текущего текста TTextAttributes *_fastcall TMainForm::CurrText(void) { return
RichEditl->SelAttributes;
}
// функция добавляет указанный шрифт к списку имеющихся int
EnumFontsProc(TLogFontA &LogFont, TTextMetricA &, int,
Pointer Data) { ((TStrings *)Data)->Add((AnsiString)LogFont.IfFaceName);
return 1;
}
// функция выбирает имена имеющихся шрифтов void _fastcall
TMainForm::GetFontNames(void) { HDC hDC = GetDC(O) ;
void * cTmp = (void *)FontName->Items;
EnumFonts(hDC, NULL, (FONTENUMPROC) EnumFontsProc, (long) cTmp ) ;
ReleaseDC(0,hDC) ;
FontName->Sorted = true;
}
// Включение имени Файла в строку заголовка приложения void _fastcall TMainForm::SetFileName(const AnsiString
FileName) ( LPSTR IpBuf = new char[MAX_pATH];
sprintf(IpBuf, "%s-%s", ExtractFileName(FileName).c_str(), Application->Title.c_str()) ;
Caption = (AnsiString)IpBuf;
FFileName = FileName;
delete IpBuf;
}
// Реакция в диалоге "Сохранить изменения?" void _fastcall TMainForm::CheckFileSave(void) { if (RichEditl->Modified) {
switch(M&ssageBox(Handle, "Save Changes?", "Confimation",
MB_YESNOCANCEL I MB_ICONQUESTION)) { case ID_YES : FileSaveClick(this) ;
case ID_CANCEL : Abort() ;
};
) }
// Запись рисок измерительной линейки ширины параграфов void _fastcall TMainPorm::SetupRuler(void) { int iCtr = 1;
char sTmp[201] ;
while (iCtr < 200) (
sTmp[iCtr++] = 9; // табулятор sTmp[iCtr++] = 'I'; // риска } Ruler->Caption = (AnsiString)sTmp;
}
// Информирует Windows о текущем размере окна редактирования void _fastcall TMainForm::SetEditRect(void) ( TRect Ret = Rect(GutterWid, 0,
RichEditl->ClientWidth-GutterWid, ClientHeight) ;
SendMessage(RichEditl->Handle, EM_SETRECT, 0, long(&Rct));
}
// Инициализирует компонентные объекты формы приложения void _fastcall TMainForm::FormCreate(TObject* /*Sender*/) { Application->OnHint = &ShowHint;
OpenDialog->InitialDir = ExtractFilePath(ParamStr(0)) ;
SaveDialog->InitialDir = OpenDialog->InitialDir;
GetFontNames() ;
SetupRuler() ;
SelectionChange(this); // атрибуты форматирования
}
// Выдает пояснения к командам меню в строку состояния void_fastcall
TMainForm::ShowHint(TObject* /*Sender*/) { StatusBar->SimpleText = Application->Hint;
}
// Создание пустого безымянного Файла по команде File I New void_fastcall
TMainForm::FileNewClick(TObject* /*Sender*/) { CheckFileSavef); // сохранить изменения? SetFileName((AnsiString)"Untitled") ;
RichEditl->Lines->Clear() ;
RichEditl->Modified = false;
}
// Загрузка выбранного Файла командой File I Open или кнопкой void _fastcall TMainForm::FileOpenClick(TObject*) { CheckFileSave(); // сохранить изменения? if (OpenDialog->Execute()) {
RichEditl->Lines->LoadFromFile(OpenDialog->FileName) ;
SetFileName(OpenDialog->FileName) ;
RichEditl->SetFocus() ;
RichEditl->Modified = false;
RichEditl->ReadOnly =
OpenDialog->Options.Contains(ofReadOnly) ;
} }
// Запись Файла командой File I Save или кнопкой void?*_fastcall
TMainForm::FileSaveClick(TObject* Sender) { if (!strcmp(FFileName.c_str(), "Untitled"))
FileSaveAsClick(Sender); // нет имени else
{ RichEditl->Lines->SaveToFile(FFileName); // то же имя RichEditl->Modified = false;
} )
// Запись Файла под выбранным именем командой FilelSaveAs void _fastcall TMainForm::FileSaveAsClick(TObject*) { if (SaveDialog->Execute()) {
RichEditl->Lines->SaveToFile(SaveDialog->FileName) ;
SetFileName(SaveDialog->FileName) ;
RichEditl->Modified = false;
} }
// Диалог печати Файла по команде File I Print или кнопкой void _fastcall TMainForm::FilePrintClick(TObject*) { if (PrintDialog->Execute()) RichEditl->Print( FFileName );
}
// Выход из программы по команде File I Exit void _fastcall
TMainForm::FileExitClick(TObject*) { Close() ;
}
// Отмена редактирования командой Edit I Undo или кнопкой void _fastcall TMainForm::EditUndoClick(TObject*) ( if (RichEditl->HandleAllocated())
SendMessage(RichEditl->Handle, EM_UNDO, 0, 0) ;
}
// Вырезка выбранного текста командой Edit I Cut или кнопкой void _fastcall TMainForm::EditCutClick(TObject*) { RichEditl->CutToClipboard();
}
// Копирование текста командой Edit I Copy или кнопкой void _fastcall TMainForm::EditCopyClick(TObject*) { RichEditl->CopyToClipboard();
}
// Вставка текста командой Edit I Paste или кнопкой void _fastcall TMainForm::EditPasteClick(TObject*) { RichEditl->PasteFromClipboard() ;
}
// Вывод указателя Файла помощи по команде Help I Contents void _fastcall TMainForm::HelpContentsClick(TObject*) { Application->HelpCommand(HELP_CONTENTS, 0) ;
}
// Поиск заданной справки по команде Help I Search...
void _fastcall TMainForm::HelpSearchClick(TObject*)
{ Application->HelpCommand(HELP_PARTIALKEY, (long) "");
)
// Вывод оглавления Файла помощи по команде Help I Нои to... void _fastcall
TMainForm::HelpHowToClick(TObject*) { Application->HelpCommand(HELP_HELPONHELP, 0) ;
}
// Диалог с логотипом редактора по команде Help I About
void_fastcall TMainForm::HelpAboutClick(TObject*)
{ Porm2 = new TPorm2(Application); // создать объект формы
Form2->ShowModal(); //
активировать диалог
delete Porm2; // уничтожить объект }
// Диалог выбора шрифта и его атрибутов по команде
Edit I font void _fastcall TMainPorm::SelectFont(TObject*) { FontDialogl->Font->Assign(RichEditl->SelAttributes);
if (FontDialogl->Execute())
CurrText()->Assign(FontDialogl->Font) ;
RichEditl->SetFocus() ;
}
// Адаптация длины линейки к текущему окну редактирования void _fastcall TMainForm::RulerResize(TObject*) { RulerLine->Width =
(int)Ruler->ClientWidth-(RulerLine->Left*2) ;
}
// Реакция на изменение пользователем размеров формы
void _fastcall TMainForm::FormResize(TObject* Sender)
{ SetEditRect(); // послать сообщение Windows
SelectionChange(Sender); //
атрибуты форматирования
)
// Перерисовка формы
void _fastcall TMainForm::FormPaint(TObject* Sender) { SetEditRect(); // послать сообщение
Windows
}
// Реакция на нажатие кнопки "Стиль шрифта жирный" void _fastcall TMainForm::BoldButtonClick(TObject*) { if (iFUpdating)
{ if (BoldButton->Down) //
изменить данный стиль CurrText()->Style = CurrText()->Style “ fsBold;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsBold;
} }
// Реакция на нажатие кнопки "Стиль шрифта курсив" void _fastcall TMainForm::ItalicButtonClick(TObject*) { if (iFUpdating)
{ if (ItalicButton->Down) //
изменить данный стиль CurrText()->Style = CurrText()->Style “ fsltalic;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsltalic;
} }
// Реакция на нажатие кнопки "Стиль шрифта подчеркнутый" void _fastcall TMainForm::UnderlineButtonClick(TObject*) { if (iFUpdating)
{ if (UnderlineButton->Down) //
изменить данный стиль
CurrText()->Style = CurrText()->Style “ fsUnderline;
else // сбросить данный стиль CurrText()->Style = CurrText()->Style ” fsUnderline;
) }
// Изменение размера шрифта в допустимом интервале значений void _fastcall TMainForm::FontSizeChange(TObject*) {
int fontsize = atoi(FontSize->Text.c_str());
if ((iFUpdating) && (fontsize)) { if (fontsize < 1)
{ ShowMessage("Number must be between 1 and 1638.");
FontSize->Text =1; } else if (fontsize > 1638)
{ ShowMessage("Number must be between 1 and 1638");
FontSize->Text = 1638; } CurrText()->Size =
atoi(FontSize->Text.c_str()) ;
} }
// Реакция на нажатие одной из кнопок выравнивания текста void _fastcall TMainForm::AlignClick(TObject* Sender) { if (IFUpdating)
{ TControl *oAliBtn = (TControl*)(Sender);
RichEditl->Paragraph->Alignment = (TAlignment)oAliBtn->Tag;
} }
// Реакция на выбор нового названия шрифта из списка void _fastcall TMainForm::FontNameChange(TObject*) { if (iFUpdating)
{ CurrText()->Name =
FontName->Items->Strings[FontName->ItemIndex] ;
} }
// Реакция на нажатие кнопки "Нумерованный список" void _fastcall TMainForm::BulletsButtonClick(TObject*) { if (iFUpdating)
RichEditl->Paragraph->Numbering =
(TNumberingStyle)BulletsButton->Down;
}
// Типовая проверка возможности выхода из приложения void _fastcall TMainForm::FormCloseQuery(TObject*, bool & CanClose)
{ try
{ CheckFileSaveO; } // сохранить изменения? catch (...)
{ CanClose = false; } //
ошибка (поймано исключение) }
// Определение позиции регулятора линейки, выбранного мышью void_fastcall TMainForm::RulerItemMouseDown(TObject* Sender,
TMouseButton Button, TShiftState Shift, int X, int Y) { TLabel * oTmpLabel = (TLabel *)Sender;
FDragOfs = oTmpLabel->Width / 2;
oTmpLabel->Left = oTmpLabel->Left+X-FDragOfs;
FDragging = true;
}
// Перемещение мышью выбранного регулятора линейки void _fastcall TMainForm:':RulerItemMouseMove(TObject* Sender,
TShiftState Shift, int X, int /*Y*/) ( if (FDragging)
{ TLabel * oTmpLabel = (TLabel *)Sender;
oTmpLabel->Left = oTmpLabel->Left+X-FDragOfs;
) }
// Определение позиции регулятора ширины красной строки void_fastcall TMainForm::FirstIndMouseUp(TObject* Sender,
TMouseButton Button, TShiftState Shift, int X, int Y) { FDragging = false;
RichEditl->Paragraph->FirstIndent =
int((FirstInd->Left+FDragOfs-GutterWid) / RulerAdj);
LeftIndMouseUp(Sender, Button, Shift, X, Y) ;
}
// Определение позиции регулятора левого отступа параграфа void _fastcall TMainForm::LeftIndMouseUp(TObject* Sender,
TMouseButton, TShiftState, int, int) { FDragging = false;
RichEditl->Paragraph->LeftIndent =
int((LeftInd->Left+FDragOfs-GutterWid) / RulerAdj)-RichEditl->Paragraph->FirstIndent;
SelectionChange(Sender); //
атрибуты форматирования
}
// Определение позиции регулятора правого отступа параграфа
void_fastcall TMainForm::RightIndMouseUp(TObject* Sender,
TMouseButton, TShiftState, int, int) { FDragging = false;
RichEditl->Paragraph->RightIndent =
int((Ruler->ClientWidth-Rightlnd->Left+FDragOfs-2) / RulerAdj)-2*GutterWid;
SelectionChange(Sender); //
атрибуты форматирования }
// Активизация Файла помощи на Форме void _fastcall
TMainForm::FormActivate(TObject* /*Sender*/) { Application->HelpFile = "RICHEDIT.HLP";
RichEditl->SetFocus() ;
Листинг 4.1. Кодовый файл Romain.cpp модуля редактора.
Рис. 4.22 демонстрирует работу со встроенным отладчиком на примере приложения редактора. Предположим, вы хотите узнать, правильно ли занеслось имя открытого файла в переменную FFileName. В процессе отладки вы будете пользоваться опциями контекстного меню Редактора кода, активизируемого нажатием правой кнопки мыши:
=> Найдите инструкцию, за работой которой вы хотите проследить, и нажмите клавишу
F5 (или щелкните мышью слева от выбранной инструкции). Красный цвет отмечает строку останова программы. Повторное нажатие клавиши или повторный щелчок мышью снимет точку останова. => Командами меню Run [ Run (клавиша F9), Run | Run to Cursor (клавиша
F4) или одноименной опцией запустите приложение.
=> Если программа дошла до точки останова (а это должно случиться при работе отлаженных приложений), строка окрасится синим цветом, а перед ней появится символ " ^ ".
; => Командой Run | Step Over (клавиша F8) выполните выбранную инструк-; цию в пошаговом режиме.
=> Двойным щелчком мышью выберите переменную FFileName в тексте ин-|§ струкции и опцией
Inspect (клавиши Alt+F5)
проинспектируйте начальное ti4' значение -
"Untitled".

Рис. 4.22. Некоторые приемы отладки.
=> Продолжите отладку (клавиша F9) и откройте любой файл в диалоге команды File | Open меню редактора.
=> Снова выполните выбранную инструкцию в пошаговом режиме. => Проинспектируйте новое значение выбранной переменной - она должна
содержать имя открытого файла и полный путь к нему. => Продолжите работу в режиме отладки или завершите ее командой
Run | Program Reset.
4.6.8 Системные компоненты
Компоненты вкладки System палитры компонент

осуществляют включение в вашу программу 12 специализированных системных элементов управления.
4.6.8.1 TTimer
Эта компонента инкапсулирует таймерные функции Windows API: SetTimer и KillTimer и сама обрабатывает сообщения WM_TIMER.
Свойство Interval задает частоту возникновения события OnTimer. По умолчанию Interval=1000 (одна секунда). Временной интервал передается функции SetTimer в качестве параметра.
Для каждого таймера вашей программы заведите отдельную компоненту.
4.6.8.2 TPaintBox
Предоставляет вашей программе возможность рисования на форме только внутри заданной прямоугольной области, предотвращая выход за границы поля рисования.
Если компонента TPaintBox перенесена на форму, ваша программа может рисовать на поверхности поля рисования (канве) с помощью обработчика события OnPciinl. Цвет и шрифт, используемые при инициализации объектов канвы (TCanvas),
определяются свойствами Color и Font. Если вы хотите рисовать на всей форме, воспользуйтесь обработчиком события OnPaint самой формы.
Чтобы сохранить относительное положение поля рисования неизменным, даже если пользователь изменит размеры формы, установите значение true свойства Align.
4.6.8.3 TFileListBox
Отображает список файлов в текущем каталоге, доступных программе во время ее работы. Смена текущего каталога отражается значением свойства Directory.
Свойство Mask
задает типы, а свойство FileType - атрибуты файлов, которые появятся в списке файлов. Имя и расширение выбранного файла можно будет вводить в область редактируемого ввода, если свойство
FileEdit указывает на соответствующий объект класса TEdit. Чтобы снабдить имена файлов пиктограммами. установите значение
true свойства
ShowGlyphs.
TFileListBox является производной от класса TCustomListBox, причем ее функционирование определяется, в основном, компонентой
TListBox.
4.6.8.4 TDirectoryListBox
Отображает древовидную структуру каталогов текущего диска, доступных программе во время ее работы. Смена текущего каталога отражается значением свойства Directory, а смена текущего дисковода - значением свойства Drive.
TDirectoryListBox является производной от класса TCustomListBox, причем ее функционирование определяется, в основном, компонентой
TListBox.
4.6.8.5 TDriveComboBox
Отображает комбинированный редактируемый список дисков, доступных программе во время ее работы. Смена текущего дисковода отражается значением свойства Drive.
Синхронизировать работу трех компонент TDriveComboBox, TDirectoryListBox и TFileListBox можно обработчиками событий OnChange первой и второй компоненты:
TForml->DriveComboBoxl->Change(Sender: TObject);
TForml->DirectoryListBoxl->Change(Sender: TObject);
Теперь, когда пользователь меняет дисковод в списке дисков, содержимое списков каталогов и файлов также обновится. Другой способ решения проблемы синхронизации связан с установкой значения DirectoryListBoxl свойства DirList в списке дисков, а также значения TFileListBox 1 свойства FileList в списке файлов".
4.6.8.6 TFilterComboBox
Представляет комбинированный редактируемый список фильтров для выбора имен файлов с расширениями.
Свойство Filter задает фильтры, которые появятся в списке фильтров, а свойство Mask - конкретный фильтр, выбранный пользователем в поле редактируемого ввода.
Обычно, список фильтров сопровождается списком файлов. Синхронизировать работу этих компонент можно обработчиком события OnChange первой компоненты:
TForml->FilterComboBoxl->Change(Sender: TObject) ;
Теперь, когда пользователь меняет фильтр, содержимое списка файлов обновится соответственно. Другой способ решения той же проблемы связан с установкой значения FileListBoxl свойства FileList списка фильтров.
4.6.8.7 TMediaPlayer
Элемент управления устройствами мультимедиа, которые поддерживаются MCI-драйверами
(Media Control Interface)
операционной системы Windows. Данная компонента отображает стандартную панель (Рис. 4.23) с кнопками (Play, Stop, Eject и др.), позволяющими управлять такими устройствами как звуковая плата, компакт диск, видеокамера, AVI плеер, MIDI секвенсор.

Рис. 4.23. Петель управления устройствами мультимедиа.
Конкретное устройство задается свойством DeviceType (например, dtWaveAudio или dtVideodisc). Значение dtAutoSelect свойства DeviceType вызовет попытку автоматически выбрать тип устройства, а значение true
свойства AutoOpen - открыть его вызовом метода Open. Если устройство сохраняет свои данные в файле, задайте имя этого файла в свойстве FileName.
4.6.8.8 TOleContainer
Организует связь с OLE объектами (Object Linking and Embedding) или непосредственно включает их в вашу программу.
Механизм OLE,
являющийся расширением операционных систем Windows, реализует передачу данных от программы-сервера к другой программе-контейнеру. Например, ячейки электронной таблицы можно переносить в документ текстового процессора. Передача данных осуществляется через динамически отводимую память. В отличие от механизма обмена данными через доску объявлений (Clipboard), контейнеру OLE не требуются знания формата принимаемых данных. Любой сервер может выдавать свои данные любому контейнеру, который принимает их, без какой бы то ни было интерпретации формата.
Чтобы дать возможность пользователю создать OLE объект в режиме диалога, достаточно обратиться к методу InsertObjectDialog. Методы CreateObject и CreateObjectFromFile
отвечают за создание включенного объекта, а метод CreateLinkToFile - за создание связанного с файлом объекта.
TOleContainer автоматически обслуживает процесс слияния меню, т.е. объединения контейнера элемента меню вашей формы с объектом замещения, который активизируется OLE сервером.
4.6.8.9 TDdeClientConv
Устанавливает режим динамического обмена данными (Dynamic Data Exchange) для программы DDE клиента. Используйте эту компоненту совместно с TDdeClientIlem, чтобы сделать ваше приложение DDE клиентом.
Механизм DDE,
являющийся расширением операционных систем Windows, реализует асинхронный обмен элементами данных между программой сервера (которая должна быть запущена первой) и программой клиента. Элементом обмена может быть любая выбранная порция текста, например, ячейка таблицы базы данных. Клиент инициирует соединение с сервером и выдает запросы на обмены элементами данных между двумя участниками "разговора".
Для соединения с сервером и превращения вашего приложения в DDE клиента на этапе проектирования, установите имя программы сервера в свойстве
DdeService, а тему разговора (имя формы или имя текстового файла) - в свойстве DdeTopic. В окне диалога установок режима DDE Info нажмите кнопку Paste Link.
Во время работы программы эти действия реализует метод Setlink. Чтобы послать текстовую строку связанному элементу сервера, воспользуйтесь методом PokeData,
а для посылки макрокоманды на исполнения сервером обратитесь к методу ExecuteMacro.
Если значением свойства ConnectMode задан автоматический режим разговора ddeAutomatic, клиент попытается установить связь при создании компоненты в момент исполнения программы. Если значением свойства
ConnectMode задан режим разговора ddeManual, то для установки связи вы должны написать обработчик события ОпОреп, который вызывает метод OpenLink.
4.6.8.10 TDdeClientltem
Определяет элемент динамического обмена данными DDE
клиента. Укажите имя объекта компоненты TDdeClientConv в свойстве DdeConv, a элемент обмена - в свойстве Ddeltem. Если компонента TDdeClientConv установила связь с DDE сервером, он будет автоматически и последовательно обновлять данные клиента, пока разговор не завершится.
Каждый раз, когда сервер производит обновление данных клиента, новая текстовая строка автоматически заносится в свойство
Text, и возникает событие OnChange. При обмене строками длиннее 255 символов они будут передаваться через список свойства
Lines, причем первая строка Lines
переносится в Text.
4.6.8.11 TDdeServerConv
Устанавливает режим динамического обмена данными для программы DDE сервера. Используйте эту компоненту совместно с TDdeServerItem, чтобы сделать ваше приложение DDE сервером. Тема разговора является значением свойства Name.
Если клиент посылает макрокоманды серверу, вы должны написать обработчик события OnExecuteMacro, которое возникает при приеме запроса на ее исполнение.
Использование компоненты TDdeServerConv не обязательно: если вы не поместили ее на форме, клиент будет по-прежнему посылать запросы на обновление своих данных непосредственно из компоненты TDdeServerItem. В этом случае темой разговора является имя формы, на которой находится компонента TDdeServerItem.
4.6.8.12 TDdeServerltem
Определяет элемент динамического обмена данными DDE
сервера. При использовании этой компоненты вместе с TDdeServerConv имя сервера указывается значением свойства ServerConv.
Каждый раз. когда клиент посылает запрос на обновление своих данных. сервер посылает ему содержимое свойства Text, и возникает событие OnCliange. При обмене строками длиннее 255 символов они будут передаваться через список свойства
Lines, причем первая строка Lines переносится в Text.
Чтобы протестировать связь с DDE клиентом, воспользуйтесь методом CopyToClipboard, который будет копировать содержимое свойства Text (или Lines) и информацию о связи на доску объявлений. При активизации клиента вы сможете забрать DDE данные с доски объявлений в программу клиента.
4.6.9 Компоненты отчетов
Вкладка QReport палитры компонент

содержит 11 компонент для создания и манипуляций с предварительно определенными отчетами.
Эти компоненты позволяют визуально конструировать стилизованные отчеты по данным, поставляемым любым источником, включая таблицы и запросы компонент доступа к базам данных TTable и
TQuery. Отчеты могут содержать поля заголовков, колонтитулов, сносок и итогов. QReport
предоставляет мощные средства отображения отчетов в разных видах, автоматического подведения итогов и подсчета полей - на любом уровне группировки данных отчета.
4.6.9.1 TQuickReport
Представляет и распечатывает данные в виде стилизованных отчетов QuickReport. Это основная компонента используется совместно с TDataSource и одной или несколькими TQRBand. Дважды щелкнув мышью по компоненте или выбрав опцию Preview Report из контекстного меню, вы откроете окно просмотра отчета. Укажите источник данных в свойстве DataSource. Чтобы заполнить окно просмотра отчета или напечатать его, обратитесь к методам Preview или Print.
4.6.9.2 TQRBand
Каждый образец этой компоненты отвечает за представление и печать своего поля (полосы) отчета. Свойство BandType содержит выпадающий список вариантов полос отчета (заголовки, колонтитулы, сноски, вложенные поля деталировок, итоги и т.д.), из которого вы выбираете ту полосу, которую будет обсуживать данная компонента. Распечатка одних полос отчета будет происходить автоматически, а распечатка других потребует связи с компонентами TQRGroup или TQRDetailLink.
4.6.9.3 TQRGroup
Поддерживает работу с групповой полосой отчета.
4.6.9.4 TQRDetailLink
Поддерживает работу с перекрестными ссылками на вложенные полосы деталировки, осуществляя связь по принципу master-detail.
4.6.9.5 TQRLabel
Отображает текст в виде заголовков столбцов отчета. Вы можете менять статический текст заголовка в свойстве Caption в любой момент во время подготовки отчета. Если вам требуется выводить текст поля записи некоторого набора базы данных, следует воспользоваться компонентой QRDBText.
4.6.9.6 TQRMeino
Отображает, по аналогии с компонентой TDBMemo, многострочный текст поля текущей записи в наборе данных источника.
4.6.9.7 MTQRDBText
Отображает, по аналогии с компонентой TDBText, однострочный текст поля текущей записи в наборе данных источника.
4.6.9.8 TQRDBCalc
Автоматизирует процесс суммирования и подсчета полей в наборе данных источника.
4.6.9.9 TQRSysData
Включает в отчет системную информацию определенного вида, выбираемого из выпадающего списка свойства Data.
4.6.9.10TQRShape
Отображает в отчете прямые линии, рамки и простые геометрические фигуры (прямоугольник, эллипс), выбираемые из выпадающего списка свойства Shape.
4.6.9.11 TQRPreview
Облегчает процесс создания различных видов просмотра отчета, включая операции прокрутки и масштабирования. Несколько компонентных методов позволяют управлять поведением отчета во время просмотра. Например, вызов метода
ZoomToFit в обработчике события OnClick по нажатию некоторой кнопки будет масштабировать страницу в установленное поле просмотра.
4.6.9.12 Пример использования компонент отчетов
C++Builder поставляется вместе с примером, который демонстрирует следующие разновидности работы с компонентами отчетов:
• создание этикеток для почтовых отправлений;
• создание простого отчета;
• модификация оригинальных предварительных видов печати;
• разработка отчетов по принципу
master-detail;
• сохранение выборочных текстовых файлов детализации.

Рис. 4.24. Форма приложения для работы с отчетами.
Удостоверьтесь предварительно, что локальный псевдоним (alias)
демонстрационной базы данных BCDEMOS установлен с помощью утилиты конфигурации
BDE Configutation. Чтобы вызвать проект приложения, выполните следующие действия:
=? По команде главного меню File | Open Project откройте диалог выбора проектов.
=> Войдите в каталог \..
.\CBuilder\Examples\Dbtasks\Quickrpt.
=> Выберите проектный файл с именем
Qrdemo и нажмите кнопку Open. Рис. 4.24 показывает форму демонстрационного приложения Quick Report.
=> Командой главного меню Run | Run запустите процесс компиляции и сборки приложения.
=> После вызова программы поэкспериментируйте с разными опциями.
4.6.10 Компонента ActiveX
Входящие в варианты поставки C++Builder Professional и
C++Builder Client/Server Suite
компоненты обеспечивают поддержку промышленного стандарта ActiveX/OCX. Созданные вами или третьими лицами компоненты Delphi ActiveX можно интегрировать в среду так, чтобы они вошли в Палитру компонент для немедленного использования. В частности, вы можете расширить стандартный вариант Палитры новыми компонентами ActiveX, включив пакет NetManage для обучения и разработки приложений в сети Internet. В конце главы 6 вы найдете подробную инструкцию того, как это делается. В типовом варианте поставки C++Builder Standard вкладка ActiveX палитры компонент

содержит единственную компоненту
ChartFX для построения на вашей форме разнообразных диаграмм, графиков, таблиц и проверки правописания на многих языках.
4.6.10.1 ChartFX
Дважды щелкнув мышью по компоненте или выбрав опцию Properties из ее контекстного меню. вы откроете диалоговое окно установок вида и множества других характеристик диаграммы. Свойства, методы и события компоненты ChartFX обеспечивают выполнение следующих основных операций над диаграммами:
• Создание простых диаграмм.
• Передача исходных данных в новую диаграмму.
• Редактирование данных в существующей диаграмме.
• Изменение легенд, заголовков и других визуальных атрибутов диаграммы (видов, цветов, орнаментов заливки, шрифтов, координатных сеток и т.д.).
• Создание инструментов и других визуальных элементов управления диаграммой.
Подробное руководство по использованию компоненты ChartFX можно вызвать из справочной службы при нажатии клавиши CtrI+Fl.
4.7 Дизайнер меню
Дизайнер меню (Menu Designer) облегчает процесс создания меню для вашей формы. Вы можете легко добавлять, вычеркивать и переупорядочивать команды меню непосредственно в окне дизайнера.
Чтобы начать процесс создания меню: __
1. Поместите значок компоненты MainMenu или PopUpMenu из вкладки стандартных компонент на вашу форму.
2. Откройте окно дизайнера меню. Оставив значок компоненты выбранным, дважды щелкните на нем левой кнопкой мыши или выберите опцию
Menu Designer из контекстного меню компоненты. (Открыть контекстное меню дизайнера можно щелчком правой кнопкой мыши в любом месте его окна или нажатием клавиш Alt+FlO).
3. Введите имя меню и нажмите клавишу
Enter, если желаете изменить имя, установленное по умолчанию (MainMenul).
Чтобы добавить команды в меню:
1. Выберите позицию, в которую вы хотите поместить новую команду.
2. Введите название команды (или символ "-", если вы хотите добавить разделительную черту в выпадающий список команд) и нажмите клавишу Enter. Указанному названию (Caption) C++Builder автоматически поставит в соответствие некоторый идентификатор (Name) и занесет в Инспектор объектов. Так команде меню
File ставится в соответствие идентификатор Filel, а русскоязычному названию Файл - порядковый номер идентификатора с префиксом N. Если названию предшествует символ "&", то обращение к соответствующей команде меню можно выполнять совместным нажатием клавиш Alt+первая (подчеркнутая) буква названия. Можно задать любую комбинацию "горячих" клавиш или выбрать ее из списка ShortCut Инспектора объектов.
3. Нажмите клавишу Enter, чтобы перейти к добавлению следующей команды выпадающего списка или клавишу Esc, чтобы вернуться в главное меню. С помощью левой кнопки мыши или клавиш позиционирования <-, ->, Г, ^ можно перемещаться по списку и переходить в главное меню. Повторное нажатие клавиши Enter завершает процесс.
Чтобы вставить поле для новой команды в меню:
1. Выберите элемент меню, рядом с которым вы собираетесь сделать вставку.
2. Нажмите клавишу Ins или выберите опцию
Insert из контекстного меню. Поле новой команды вставляется над выбранным элементом в выпадающем списке или слева от выбранного элемента в главном меню.
Чтобы удалить команду из меню:
1. Выберите команду, которую вы собираетесь удалить.
2. Нажмите клавишу Del или выберите опцию
Delete из контекстного меню.
Чтобы создать поле для вложенного подменю:
1. Выберите элемент, для которого вы хотите создать подменю.
2. Нажмите клавиши Ctrl+-> или выберите опцию
SubMenu из контекстного меню.
Чтобы переместить элемент:
1. Выберите элемент меню и, удерживая левую кнопку мыши, перенесите его на новое место. Вид курсора поможет вам выбрать разрешенную позицию.
2. Отпустите кнопку мыши.
Вы можете конструировать меню, включая в него те элементы, которые вам требуются, или начать конструирование с шаблонов, содержащих часто употребляемые команды.
Чтобы добавить шаблон к вашему меню:
1. Выберите опцию Insert From Template из контекстного меню.
2. Выберите нужный шаблон из предложенного списка и нажмите кнопку ОК или клавишу Enter. Элементы шаблона образуют подменю элемента, на который указывал курсор в выпадающем списке команд, или создадут новый элемент в главном меню.
Чтобы сохранить ваше меню в виде шаблона:
1. Выберите опцию Save As Template из контекстного меню.
2. Введите имя шаблона и нажмите кнопку ОК или клавишу Enter.
Чтобы удалить шаблон:
1. Выберите опцию Delete Templates из контекстного меню.
2. Выберите нужный шаблон из списка и нажмите кнопку ОК или клавишу Enter. Внимание: Данный шаблон будет удален не только из списка, но и из файла шаблонов BCB.DMT
на жестком диске.
В процессе работы программы можно динамически добавлять в меню новые исполняемые или информационные команды. Операции с множественными меню позволяют динамически менять порядок расположения элементов активного (главного) меню, вводя или замещая альтернативные элементы.
Чтобы организовать слияние меню во время выполнения:
1. Выберите меню, которое вы хотите активизировать из выпадающего списка свойства Menu вашей формы. Операции слияния применимы только к активному меню. Если форма содержит несколько компонент меню, вы можете переключать активное меню во время выполнения программы, устанавливая новые значения свойства Menu, например: Forml->Menu = ВтороеМеню;
2. Определите порядок, в котором команды меню из разных форм будут выстраиваться
на разделяемой панели главного меню: чем меньше номер свойства Grouplndex, тем
левее располагается соответствующая команда. Пусть главное (активное) меню содержит только две команды: "Файл" (Grouplndex=0) и "Справка" (5). Тогда между ними разрешается вставить до четырех команд из меню другой формы-потомка с номерами 1,2,3, и 4, например, "Редактор" (1) и "Формат" (3). Далее можно заместить команду "Формат" (3) на команду "Работа" (3) из меню другой формы-потомка.
Рис. 4.25 иллюстрирует процесс проектирования меню на форме прототипа тестового приложения, предназначенного для обработки файлов. Открыв контекстное меню дизайнера, обратите внимание на опцию Insert From Resource. Судя по названию, она должна давать возможность создавать меню на базе ресурсных файлов с расширением .гс. К сожалению, автору не удалось заставить дизайнер использовать ресурсы приложений, ранее написанных на Borland C++ версии 4.5. Будем надеяться, что в широко рекламируемой версии 5.02 эта недоработка будет исправлена.

Сконструированное вами меню всегда отображается на вашей форме в том виде, в котором оно появится при запуске программы, поэтому нет необходимости компилировать и запускать программу, чтобы увидеть результат проектирования. Вы можете продолжить редактирование меню в окне Инспектора объектов, активизировав его щелчком левой кнопки мыши.
4.8 Итоги
Мы научились пользоваться основными инструментами интегрированной среды визуальной разработки C++Builder, уделив основное внимание назначению элементов Палитры компонент - "кирпичей", составляющих основу вашего приложения.
ВИЗУАЛЬНАЯ РАЗРАБОТКА ПРИЛОЖЕНИЙ БАЗ ДАННЫХ
5. ВИЗУАЛЬНАЯ РАЗРАБОТКА ПРИЛОЖЕНИЙ БАЗ ДАННЫХ
Разработка Систем Управления Базами Данных (СУБД) раньше всегда была очень трудоемким и медленным процессом, отягощенным необходимостью учитывать массу специфических деталей подсистем обслуживания различных баз данных на низком уровне. C++Builder
принимает на себя выполнение этих рутинных операций позволяя сосредоточиться на решении основной задачи.
Все приложения СУБД, создаваемые в среде C++Builder, являются клиентами в архитектуре программного взаимодействия клиент/сервер. Клиент выдает запросы к серверу базы данных на получение или передачу информации. Сервер обрабатывает запросы от множества клиентов одновременно, координируя доступ к данным и их обновление.
Все приложения СУБД, создаваемые в среде C++Builder, основаны на компонентах пользовательского интерфейса с некоторой базой данных, которые предоставляют удивительно легкие в использовании средства разработки специальных приложений. Львиная доля времени процесса разработки уходит на визуальную установку свойств выбранных компонент. Удачно спроектированное приложение всегда обеспечивает простоту просмотра и редактирования данных пользователем, независимо от сложности структуры используемой модели данных. Данная глава с очевидностью покажет, что формы приложений СУБД для типично сложной системы в архитектуре взаимодействия клиент/сервер действительно могут быть созданы в интегрированной среде C++Builder весьма быстро и с малыми усилиями.
Воздействия на компоненты многогранны: их реакция на события обеспечивается стандартными методами, а установка значений свойств может производиться во время работы приложения. Таким образом, простое вначале приложение в процессе разработки постепенно усложняется, чтобы в конце концов выглядеть совершенно профессиональным программным изделием.
5.1 Организация доступа к базам данных
C++Builder организует доступ приложения к данным таким образом, чтобы полностью отстранить разработчика от специфики обслуживания конкретной базы дан

Рис. 5.2. Основная страница Drivers утилиты конфигурации BDE.
5.1.2.1 Страница Drivers
Используется для модификации установок, применяемых драйверами BDE при создании, сортировке и обслуживании таблиц базы данных. В графе Driver Name
перечисляются типы драйверов, инсталлированных на вашей рабочей станции. Драйверы
STANDARD обеспечивают доступ к базам данных Paradox и dBASE, а прочие драйверы - соединения с серверами SQL и ODBC. В графе
Parameters перечислены все прослеженные утилитой параметры выбранного типа драйвера вместе с их текущими установками. Чтобы изменить некоторую установку, выберите имя нужного драйвера и измените его старое значение. При первой инсталляции драйвера выбираются значения по умолчанию. В графе Description кратко описывается назначение выбранного параметра.
Чтобы получить доступ к ODBC (например, к базе данных Microsoft Access), надо сначала создать соответствующий источник данных, и только потом вызвать утилиту конфигурации BDE для подключения к этому источнику. Кнопка New ODBC Driver
открывает диалог добавления соединения ODBC к имеющемуся списку (Рис. 5.3). Кнопка Delete ODBC Driver разрешает вычеркнуть выбранный драйвер.

Рис. 5.3. Диалог добавления драйвера соединения ODBC.
5.1.2.2 Страница Aliases
Используется для выполнения операций вычеркивания и модификации псевдонимов баз данных типа STANDARD, SQL или ODBC. Alias Names перечисляет все имеющиеся псевдонимы. Позже вы будете использовать псевдоним для указания имени нужной базы данных в свойстве DatabaseName компонент таблицы TTable или запроса TQuery. Графа Parameters содержит, в частности, тип сервера и полный путь к каталогу, содержащему нужные таблицы. В графе Description кратко описывается назначение выбранного параметра.
Кнопка New Alias открывает диалог добавления нового псевдонима выбранного типа к имеющемуся списку (Рис. 5.4). Кнопка Delete Alias разрешает вычеркнуть выбранный псевдоним.

Рис. 5.4. Дисиог добавления псевдониме!.
5.1.2.3 Страница System
Используется для модификации установок системных и сетевых параметров, которые BDE использует при запуске приложения. Эта информация хранится в регистрационном файле Windows. В графе Parameters перечислены все прослеженные утилитой системные и сетевые параметры вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.4 Страница Date
Используется для установки формата отображения даты. В графе Parameters перечислены все прослеженные утилитой параметры даты вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.5 Страница Time
Используется для установки формата отображения времени. В графе Parameters перечислены все прослеженные утилитой параметры времени вместе с их текущими значениями. Чтобы изменить установку, выберите нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.2.6 Страница Number
Используется для установки формата отображения числовых величин. В графе Parameters
перечислены все прослеженные утилитой параметры числовых величин вместе с их текущими значениями. Чтобы изменить установку, выберите
нужный параметр и измените его старое значение. В графе Description кратко описывается назначение выбранного параметра.
5.1.3 "Живые данные"
Основная задача быстрой разработки приложений управления базами данных состоит в том, чтобы оперативно обнаруживать проблемы в экранном представлении базы данных и ошибки в запросах к ней.
Большинство систем программирования реляционных баз данных требуют написания специализированных процедур для выборки данных из таблиц и заполнения полей в соответствующих диалогах. Чтобы удостовериться, что ваша программа производит ожидаемые результаты, раньше приходилось многократно проходить по циклу редактирование => компиляция => сборка, непроизводительно расходуя время. ^
Иначе обстоит дело при работе в визуальной среде C++Builder,
компоненты которой поддерживают поступление "живых данных". Когда вы поместите на форму компоненты доступа и управления, определите их свойства, отвечающие за связь с базой данных, вам будут представлены данные точно так же, как их увидит пользователь. C++Builder объединяет три этапа разработки в единый производственный процесс. В результате удается строить СУБД, базирующиеся на текущих требованиях заказчика, вместе с тем гибкие настолько, чтобы быстро адаптировать их к новым запросам пользователей.
Форма приложения будет выглядеть одинаково на стадии проектирования и во время работы программы. Вы сможете производить необходимые коррекции экранного представления базы данных и запросов к ней, даже не компилируя программу. Способность интерактивного испытания запросов ставит процесс разработки СУБД на самый высокий качественный уровень.
5*2 Использование визуальных компонент
Одним из важнейших достоинств интегрированной среды C++Builder является наличие удобных средств быстрой визуальной разработки приложений СУБД -специализированных компонент баз данных. В отличие от разделяемых элементов управления VBX, C++Builder компилирует компоненты в единую исполняемую программу, что существенно повышает ее надежность и быстродействие. Только очень опытные программисты способны создать программу подобного уровня качества и гибкости, используя исключительно прямые обращения к соответствующим функциям Windows API. При таком подходе даже простое приложение требует написания непомерного по объему кода.
C++Builder предоставляет разработчикам интерфейсные элементы баз данных из Библиотеки Визуальных Компонент на следующих двух вкладках Палитры компонент:
• Компоненты управления данными
Data Control (такие как область редактирования TDBEdit или сетка TDBGrid) обеспечивают отображение и редактирования записей на форме приложения.
• Компоненты доступа к данным Data
Access (такие как таблица TTable или запрос TQuery) адресуют фактические данные, хранящиеся в файле базы данных, а компонента источника TDataSource служит как интерфейс межкомпонентной связи.
Для. работы с базами данных необходимо проанализировать и правильно установить значения ключевых свойств компонент доступа и управления. В дальнейшем будем выделять ключевые свойства, методы и события подчеркиванием.
C++Builder поддерживает "трехступенчатую" модель разработки приложения баз данных. В этой модели компонента управления связана с компонентой источника, а та, в свою очередь, получает фактические данные таблицы или запроса посредством механизма BDE. Рис. 5.5 показывает пример взаимосвязи компонент.

Рис. 5.5. Взаимосвязь компонент управления и доступа к содержимому баз данных.
Среднее звено, компонента TDataSource, допускает менять фактическую таблицу на стадии проектирования формы без какого бы то ни было перепрограммирования самого приложения - все отображаемые элементы связаны с источником, а не с питающими его таблицей или запросом. Кроме того, компоненты источников берут на себя задачу синхронизации обмена данными между парами таблиц по принципу master-detail.
5.2.1 Компоненты доступа к данным
В этом разделе мы подробно рассмотрим работу пяти компонент доступа, первые три из которых составляют основу любого приложения баз данных. Описание остальных компонент можно найти в главе 4.
5.2.1.1 Источники данных
Как уже было сказано, невидимая компонента TDataSource действует как интерфейс между некоторым объектом набора данных (таблица, запрос) и визуальной компонентой управления. С одной стороны, все наборы данных должны быть ассоциированы с некоторым источником. С другой стороны, каждая компонента управления должна быть ассоциирована с источником, чтобы получать данные для отображения и редактирования. Каждой компоненте набора данных должна соответствовать по меньшей мере одна компонента источника. Далее мы увидим, что все компоненты управления имеют свойство
DataSource, значение которого замыкает трехступенчатую связь. Рис. 5.6 показывает свойства компоненты источника в окне Инспектора объектов:
![]() |
Рис. 5.6. Свойства источники.
AutoEdit
разрешает или запрещает режим редактирования записей, вводимых в поля компонент управления. Значение true включает режим редактирования по умолчанию.
DataSet
определяет имя конкретного набора данных (таблицы или запроса), который питает данный источник. Можно переключаться с одного набора данных на другой "на лету", во время выполнения программы. Следующий простейший код реализует попеременное подключение объекта источника DataSourcel к таблице заказчиков "Заказчики" или к таблице "Заказы":
DataSourcel->DataSet = "Заказы";
else
DataSourcel->DataSet = "Заказчики";
Чтобы синхронизировать работу компонент управления на двух формах, достаточно установить свойство DataSet на один и тот же набор данных:
void_fastcall TForm2::FormCreate (TObject *Sender)
{
DataSourcel->Dataset = Forml->Tablel;
Name определяет содержательное название данной компоненты, отличающее ее от других источников данных вашего приложения. Целесообразно давать источнику имя. которое отражает название ассоциированного с ним набора данных. Например, если вы собираетесь работать с таблицей клиентов
"Clients", заданной свойством DataSet, то пусть значением свойства Name будет "ClientsSource" или подобное имя.
С компонентой TDataSource связаны три события:
• OnDdlciC/iange
возникает при перемещении курсора на новую запись и используется при необходимости "ручной" синхронизации поведения компонент управления.
• OnStciteChcinse
возникает при изменении свойства State наборов данных. Например, обработчик события (Листинг 5.1) будет отслеживать изменения состояния таблицы MyTable, выводя па форму соответствующие текстовые отметки.
void_fastcall TFormI::StateChange(TObject *Sender)
{
char S[20];
switch (MyTabie->State) {
case dslnactive:
strcpy(S,"Таблица неактивна");
break;
case dsBrowse:
strcpytS, "Идет просмотр");
break;
case dsEdit:
strcpytS, "Идет редактирование");
break;
case dslnsert:
strcpy(S, "Идет вставка записи");
break;
} // Вывод текстовой строки S
}
Листинг 5.1. Обработчик события
OnStateChange источника.
• OnUpdateData возникает перед обновлением текущей записи и используется при необходимости синхронизации поведения обычных компонент с изменениями некоторого набора данных.
5.2.1.2 Таблицы
Компонента таблицы представляет собой один из самых фундаментальных и гибких компонентных классов. TTable
устанавливает прямую связь с таблицей базы данных посредством BDE, причем все записи или столбцы этой таблицы становятся доступными для приложения - как раздельно, так и внутри определенного интервала адресов. Рис. 5.7 показывает свойства компоненты таблицы в окне Инспектора объектов:
![]() Рис. 5.7. Свойства таблицы. |
Active разрешает или запрещает режим просмотра "живых данных" таблицы на этапе проектирования. Значение true или метод Open открывают просмотр таблицы. Значение false (устанавливается по умолчанию) или метод Close закрывают просмотр. DatabaseName содержит псевдоним базы данных или полный путь к ее каталогу. Использование псевдонима всегда предпочтительнее: вы можете переназначить физический носитель данных, например, заменив локальный дисковод на сетевой. Перекомпиляция приложения не требуется - просто измените путь на вкладке Aliases в утилите конфигурации BDE. |
TableName
позволяет выбрать фактическое имя таблицы из выпадающего списка с именами всех таблиц в адресуемой базе данных.
Exclusive
разрешает или запрещает другому приложению обращаться к таблице, пока вы ее используете сами. Значение false запрещает исключительный доступ по умолчанию.
IndexFiles
открывает диалог выбора индексного файла для таблицы.
IndexName
задает правило отображаемых сортировки данных, отличное от упорядочивания по первичному ключу (primary key order).
Filter позволяет устанавливать критерий фильтрации, в соответствии с которым адресуется некоторое подмножество записей таблицы.
ReadOnly управляет правами доступа в процессе выполнения программы. Значение
false разрешает запись по умолчанию.
MasterFields и MasterSource
участвуют в образовании связи двух таблиц (ведущей и ведомой) по принципу master-detail.
Следующая процедура иллюстрирует процесс создания простой формы для демонстрационной базы данных BCDEMOS, в которой пользователь может прокручивать записи таблицы заказчиков CUSTOMER с отображением всех заказов таблицы ORDERS, сделанных текущим заказчиком:дексного файла для таблицы.
IndexName задает правило отображаемых сортировки данных, отличное от упорядочивания по первичному ключу
(primary key order).
Filter позволяет устанавливать критерий фильтрации, в соответствии с которым адресуется некоторое подмножество записей таблицы.
ReadOnly управляет правами доступа в процессе выполнения программы. Значение false
разрешает запись по умолчанию.
MasterFields и
MasterSource участвуют в образовании связи двух таблиц (ведущей и ведомой) по принципу master-detail. Следующая процедура иллюстрирует процесс создания простой формы для демонстрационной базы данных BCDEMOS, в которой пользователь может прокручивать записи таблицы заказчиков CUSTOMER с отображением всех заказов таблицы
ORDERS, сделанных текущим заказчиком:
| 1. Выполните команду главного меню File | New Data Module, чтобы открыть контейнер нового модуля данных DataModule2. В этот модуль поместите две пары компонент доступа к базам данных TTable и TDataSource (Рис. 5.8). 2. Установите свойства объекта ведущей таблицы Tablet DatabaseName = BCDEMOS TableName = CUSTOMER.DB Name = CustomersTable |
![]() Рис. 5.8. Модуль данных. |
DatabaseName = BCDEMOS TableName = ORDERS.DB Name = OrdersTable
4. Установите свойства объекта источника DataSourcel DataSet = CustomersTable Name = CustomersSource
5. Установите свойства объекта источника DataSource2 DataSet = OrdersTable Name = OrdersSource
6. Поместите на форму пару компонент управления сеткой TDBGrid.
7. Выполните команду File | Include Unit Hdr, чтобы указать, что данная форма должна использовать созданный модуль данных.
8. Установите свойство объекта первой сетки DBGridI
DataSource = DataModule2->CustomersSource и свойство объекта второй сетки
DBGrid2
DataSource = DataModule2->OrdersSource
9. Активизируйте таблицу OrdersTable модуля данных и установите свойство
MasterSource = CustomersSource, чтобы связать ведущую таблицу CUSTOMER с ведомой таблицей
ORDERS.
10. Дважды щелкните мышью в графе значений свойства MasterFields, и в открывшемся окне дизайнера связи полей
- выберите номер заказчика CustNo (связующее поле таблиц) из выпадающего списка Available Indexes;
- задайте CustNo в списках Detail Fields и Master Fields;
- нажмите кнопку Add, чтобы добавить в список Joined Fields соединение CustNo -> CustNo;
- нажмите кнопку OK, подтверждая сделанный выбор.
11. Установите свойство Active = true для таблиц CustomersTable и OrdersTable с тем, чтобы сразу же отобразить живые данные в сетках на форме.
12. Скомпилируйте и запустите приложение. Рис. 5.9 показывает работу связанных таблиц: при прокрутке записей таблицы заказчиков вы увидите только те записи в таблице заказов, которые относятся к текущему заказчику.

Рис. 5.9. Связь таблич по принципу
muster-detail.
Методы Locate
и Lookup
используются для поиска указанных записей как в индексных таблицах, так в таблицах с ключами: эти методы реализуют самый быстрый из возможных способов поиска в данной таблице. Если столбцы для поиска индексированы и индекс совместим с указанными опциями, используется способ индексного поиска. В противном случае методы создают для BDE соответствующий фильтр.
Locate производит поиск специфической записи и позиционирует курсор на нее. В простейшем варианте вы передаете методу название столбца для поиска, искомое значение ключа записи и флаг опций. Листинг 5.2 содержит фрагмент кода, обеспечивающего поиск в столбце "Имя" таблицы MyTable первой записи со значением "Иван". Если поиск завершился успешно. Locate возвращает значение true,
и найденная запись становится текущей. Если искомая запись не найдена. Locate возвращает значение false, и позиция курсора не меняется.
( boot Success;
TLocateOptions Options;
Options “ loPartialKey;
Success = MyTable->Locate("Имя", "Иван", Options);
}
Листинг 5.2. Простейший пример использования метода Locate.
Возможности метода проявляются при поиске вариантных значений записи в нескольких столбцах таблицы. Обобщенный синтаксис описания метода имеет следующий вид:
bool _fastcall Locate(const AnsiString KeyFields, const Variant SKeyValues, TLocateOptions Options);
В главе 3 отмечалось, что различные типы искомых значений объявляются в шаблонном классе Variant (с помощью которого C++Builder реализует одноименный встроенный тип Delphi). Названия столбцов для поиска разделяются в текстовой строке параметра KeyFields символом точка с запятой.
Lookup выбирает значения столбца той записи, которая удовлетворяет заданным значениям поиска. Позиция курсора не меняется. В простейшем варианте вы передаете методу название столбца для поиска, искомое значение ключа записи и возвращаемые поля этой записи. Листинг 5.3 содержит фрагмент кода, обеспечивающего поиск в таблице MyTable
первой записи, у которой в столбце "Фирма" имеется значение "Borland". Если поиск завершился успешно. Lookup возвращает в массиве типа Variant название фирмы, фамилию ее представителя и номер телефона. Если искомая запись не найдена. Lookup возвращает значение Null.
{
Variant Results;
Results = MyTable->Lookup("Фирма", "Borland",
"Фирма;Представитель;Телефон") ;
}
Листинг 5.3. Простейший пример использования метода Lookup.
Возможности метода проявляются при поиске вариантных значений полей записи в нескольких столбцах таблицы. Обобщенный синтаксис описания метода имеет следующий вид:
Variant _fastcall Lookup(const AnsiString KeyFields, const Variant sKeyValues, const AnsiString ResultFields);
Названия столбцов для поиска разделяются в текстовой строке параметра KeyFields символом точка с запятой.
С компонентой TTable также связаны следующие методы:
• GotoCurrent
синхронизирует перемещения курсора по нескольким табличным компонентам, ассоциированным с одной и той же фактической таблицей базы данных.
• First, Next, Prior, Last и MoveBy используются
для навигации по данным таблицы.
• SetKey, FindKey, FindNearest, GotoKey
и GotoNearest
используются для поиска по специфическим значениям ключей.
• Append, Insert, AppendRecord и InsertRecord добавляют новую запись к таблице. Delete вычеркивает текущую запись. Edit разрешает приложению модифицировать записи, a Post вызывает фактическое изменение содержимого базы данных.
• EditRangeStart, EditRangeEnd, SetRangeStart, SetRangeEnd, ApplyRange и SetRange устанавливают границы интервала адресов записей, возвращаемых приложению при групповом доступе.
5.2.1.3 Запросы
Компоненты таблиц являются полноправными, гибкими и легкими в использовании компонентами доступа, достаточными для многих приложении СУБД. TTable
возвращает все строки и столбцы единственной таблицы, если доступ не ограничивается установкой интервалов и фильтров. Компоненты запросов предоставляют разработчикам альтернативные возможности. TQuery обеспечивает доступ к нескольким таблицам одновременно и способна адресовать некоторое подмножество записей. Вид возвращаемого набора данных (result set) зависит от формы запроса, который может быть либо статическим, когда все параметры запроса задаются на стадии проектирования, или динамическим, когда параметры определяются во время выполнения программы.
Указанные действия записываются и реализуются на стандартизованном языке структурированных запросов SQL (Structured Query Language), принятом большинством удаленных серверов реляционных баз данных, таких как Sybase, Oracle, InterBase и SQL Server. Ha SQL можно сформулировать весьма изощренные запросы к базам данных. C++Builder передает запросы серверу, который интерпретирует их и возвращает результаты вашему приложению.
Рис. 5.10 показывает свойства компоненты запроса в окне Инспектора объектов:
![]() Рис. 5.10. Свойства запроса. |
Active разрешает или запрещает режим просмотра "живых данных", возвращаемых запросом на этапе проектирования. Значение false устанавливается по умолчанию. DatabaseName содержит псевдоним базы данных или полный путь к ее каталогу, необходимые для разрешения запроса. RequestLive разрешает или запрещает BDE сделать попытку возвратить "живой" результирующий набор. Значение false (устанавливается по умолчанию) указывает, что результаты запроса нельзя модифицировать. Значение true гарантирует возврат результирующего набора при условии, что синтаксис команды SELECT согласуется с требованиями запрашиваемых данных. SQL используется для ввода команды SQL посредством строчного редактора списка, который открывается двойным щелчком мышью в графе значений этого свойства. Локальные и удаленные серверы баз данных обеспечивают выполнение четырех команд SQL: SELECT - для выбора существующих данных из таблиц; INSERT - для добавления |
новых данных в таблицы; UPDATE - для модификации данных таблиц; DELETE -для удаления данных из таблиц. Результаты обработки запроса возвращаются приложению клиента. Следующая процедура иллюстрирует процесс создания формы со статическим запросом к таблице
EMPLOYEE всей информации о служащих, зарплата которых превышает заданную величину:
1. Поместите компоненту TQuery на форму.
2. Установите псевдоним адресуемой базы данных сервера в свойстве DatabaseName. В примере используется псевдоним BCDEMOS локальной демонстрационный базы данных, содержащей, в частности, таблицу служащих некоторого предприятия.
3. Откройте строчный редактор списка, введите команду SQL
SELECT * FROM EMPLOYEE WHERE Salary>40000 и нажмите кнопку ОК.
4. Поместите на форму компоненту
TDataSource и установите ее свойство DataSet = Queryl.
5. Поместите на форму компоненту управления сеткой TDBGrid и установите ее свойство DataSource = DataSourcel.
6. Установите свойство Active = true для запроса Queryl с тем, чтобы сразу же отобразить живые данные в сетке (Рис. 5.11).

Рис. 5.] ]. Форма приложения со статическим запросом к таблице служащих.
Свойство SQL
имеет объектный тип TStrings и заключает список текстовых строк наподобие массива. Листинг 5.4 показывает обработчик события ButtonlClick, реализующий ввод запроса пользователем при нажатии кнопки на форме. Введенная команда SQL записывается в строчный массив (того же типа TStrings) свойства Memol->Lines компоненты редактирования TMemo. Результаты запроса можно, как и в предыдущем примере, отобразить на сетке.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
// Проверить, введена ли какая-то строка в Memol if
(strcmp(Memol->Lines->Strings[0].c_str(), "") == 0) (
MessageBox(0, "No SQL Statement Entered", "Error", MB_OK) ;
return;
} else
(
// Деактивировать предыдущий запрос, если он имел место Queryl->Close ();
// Очистить свойство SQL от предыдущего запроса Queryl->SQL->Clear () ;
// Присвоить введенный в Memol текст свойству SQL Queryl->SQL->Add(Memol->Lines->Strings[0].c_str()) ;
try
{
Queryl->0pen(); // выполнить команду SQI }
catch(EDBEngineError* dbError) .//
обработка ошибок BDE {
for (int i=0; i
} ) }
Листинг 5.4. Ввод и исполнение команды SQL.
Params позволяет специфицировать имена, типы и начальные значения параметров запроса. C++Builder дает возможность конструировать команду SQL динамического запроса с параметрами. Чтобы указать нужный параметр динамического запроса, используйте символ двоеточия перед именем этого параметра. Например, параметр номера служащего в таблице employee идентифицируется следующей командой SQL: SELECT * FROM employee WHERE EmpNo = :EmpNo.
Увидеть или поменять атрибуты выбранного параметра можно посредством диалогового редактора (Рис. 5.12), который открывается двойным щелчком мышью в графе значений этого свойства. Нажатие кнопки ОК подготавливает SQL сервер к запросу и вызывает попытку его выполнения на стадии проектирования приложения.

Рис. 5.12. Редактор параметров запроса.
Свойство Params содержи-. указатель на объект типа TParams. Поэтому изменить значение параметра во время выполнения программы можно по ^ндексу в массиве I terns объекта типа TParams: ;
Queryl->Params->Items[0]->AsInteger = 4;
или по имени параметра, посредством метода ParamByName:
Queryl->ParamFjyName ("FirstName") ->AsString = "John";
Листинг 5.5 дает законченный пример использования метода ParamByName во время исполнения программы. Параметр имени служащего FirstName идентифицируется следующей командой SQL:
SELECT * FROM EMPLOYEE WHERE FirstName = :FirstName
Заметим, что обработчик события первым делом обращается к методу подготовки запроса Prepare, посылая команду SQL
серверу. Сервер выделяет ресурсы и оптимизирует динамический запрос только однажды, до первого исполнения. Все, что необходимо теперь, - это подставлять новые значения параметров и выполнять команду запроса с помощью метода Open.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
// Деактивировать предыдущий запрос, если он имел место Queryl->Close () ;
if (!Queryl->Prepared)
Queryl->Prepare(); //
подготовить запрос // Заменить значение параметра на введенное пользователем Queryl->ParamByName("FirstName")->AsString =
Editl->Text.c_str() ;
try
{
Query 1 ->0pen(); // выполнить команду SQL }
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.5. Изменение значения параметра динамического запроса.
Рис. 5.13 показывает работу предыдущего примера: пользователь вводит значение параметра FirstName и в результате выполнения запроса получает список всех служащих с указанным именем.

Рис. 5.13. Результат выполнения динамического запроса с параметром.
DataSource указывает на источник другого набора данных, отличный от источника данного запроса, из которого вам надо выбрать значения текущего поля. Объект запроса будет сравнивать имя параметра в команде SQL с именами полей дочернего набора данных. Когда имена совпадают, параметр автоматически приобретает значение соответствующего поля. Следующая процедура иллюстрирует процесс создания формы, в которой запрос к таблице заказчиков CUSTOMER соединяется с таблицей заказов ORDERS по номеру заказчика CustNo:
1. Поместите компоненты TQuery и TTable на форму и установите псевдоним
BCDEMOS в их свойствах DatabaseName.
void _fastcall TPormI::DataSource2DataChange(TObject
*Sender, TField *Field) {
// Связь запроса с таблицей ORDERS
установлена? if (Queryl->DataSource != NULL)
return; // да, возврат // Деактивировать предыдущий запрос, если он имел место Queryl->Close() ;
if (!Queryl->Prepared)
Queryl->Prepare(); //
подготовить запрос // Выбрать значение параметра запроса из поля таблицы Queryl->ParamByName("CustNo")->AsInteger =
Tablel->Fields[1]->AsInteger;
try
{
Query 1 ->0pen(); // выполнить команду SQL }
catch(EDBEngineError* dbError) //
обработка ошибок BDE (
for (int i=0; i
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.6. Соединение запроса с таблицей по событию источника.
Для динамического формирования текста командной строки SQL во время исполнения программы удобно использовать стандартную функцию Windows sprintf. Эта функция замещает параметры форматирования (%s, %d, %n и т.д.) передаваемыми значениями, например, в результате подстановки значений параметров форматирования:
tbIName = "EMPLOYEE";
fIdName = "EmpNo";
fIdValue = 3;
sprintf(sqls, "SELECT * FROM %s WHERE %s = %d", tbIName, fIdName, fIdValue)
символьный массив sqls будет содержать следующую команду:
"SELECT * FROM EMPLOYEE WHERE EmpNo =3"
Листинг 5.7 иллюстрирует применение функции sprintf для
формирования команды SELECT динамического запроса к таблице EMPLOYEE.
Методы Clear и Add используются для занесения этой команды в свойство SQL. Поскольку подготовленный запрос использует ресурсы сервера и нет никакой гарантии, что новый запрос будет работать с данными одной и той же таблицы, C++Builder снимает готовность при любом изменении свойства SQL (т.е. устанавливает значение false свойства Prepared). При очередном исполнении запроса готовность автоматически восстанавливается.
void _fastcall TFormI::ButtonlClick(TObject *Sender)
{
char sqls[250]; // массив для хранения команды SQL
char fmts[50]; // массив для значения зарплаты
// Присвоить
fmts значение, введенное пользователем
if ( i (strcmp(SalaryEdit->Text .c_str () , " ") ==0)) strcpytfmts, SalaryEdit->Text.c_str()) ;
else
strcpy(fmts, "100000"); //
максимальная зарплата
// Деактивировать предыдущий запрос, если он имел место
Queryl->Close() ;
// Очистить свойство SQL от предыдущего запроса
Queryl->SQL->Clear() ;
// Построить команду SELECT с помощью функции sprintf
sprintf(sqls, "SELECT * FROM EMPLOYEE WHERE Salary<%s", fmts);
// Присвоить сформированную команду SELECT свойству SQL
Queryl->SOL->Add(sqls);
try
{
Query 1 ->0pen(); // выполнить команду SELECT )
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i
MB_OK);, ) 1
Листинг 5.7. Формирование команды SQL с помощью функции
sprintf.
Рис. 5.15 показывает работу предыдущего примера: в результате выполнения сформированного запроса пользователь получает список всех служащих, зарплата которых (выбранная из столбца Salary таблицы) оказывается меньше введенного значения.

Рис. 5.15. Результат выполнения сформированного динамического запроса.
Методы Open и ExecSQL предназначены для передачи серверу команды SQL для исполнения. В предыдущих примерах все запросы выдавали единственную команду SELECT. Результат запроса по команде select рассматривается как набор данных, точно так же, как при работе с таблицей. Существуют другие команды
SQL, например, команда UPDATE, которая обновляет содержимое некоторой записи, но не возвращает какой бы то ни было результат. Для исполнения сервером таких запросов следует использовать метод ExecSQL вместо метода Open.
Листинг 5.8 представляет собой некоторое обобщение всех рассмотренных ранее операций с динамическими запросами и их параметрами.
Внимание: это приложение предназначено для управляемой модификации столбца Salary таблицы служащих
EMPLOYEE из демонстрационной базы данных BCDEMOS. Изменения, внесенные командой UPDATE, необратимы, поэтому перед запуском собранного приложения позаботьтесь о том, чтобы сохранить копию оригинальной таблицы. По умолчанию С-н-Builder помещает все таблицы в каталоге инсталляции \.. .\CBuilder\Examples\Data.
void_fastcall TFormI::ButtonlClick(TObject *Sender) { •
char sqls[250]; // массив для хранения команды SELECT char fmts[50]; // массив для зарплаты и надбавки int mins, adds; //
десятичные эквиваленты
// Присвоить
fmts зарплату, введенную пользователем
strcpy(fmts, SalaryEdit->Text.c_str());
mins = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL
Queryl->Close О
;
Queryl->SQL->Clear () ;
sprintf(sqls, "SELECT * FROM EMPLOYEE WHERE Salary<%s", fmts);
// Присвоить сформированную команду SELECT свойству SQL
Queryl->SQL->Add(sqls) ;
try
{
Queryl->0pen (); // выполнить команду SELECT
} catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i
}
// Присвоить
fmts надбавку [в %], введенную пользователем
strcpy(fmts, AddEdit->Text.c_str());
adds = atoi(fmts);
// Деактивировать предыдущий запрос и очистить SQL Queryl->Close () ;
Queryl->SQL->Clear() ;
// Присвоить команду UPDATE свойству SQL Queryl->SQL->Add("UPDATE EMPLOYEE set Salary =
(Salary+((Salary*:adds))/100) WHERE (Salary < :mins)");
Queryl->ParamByName("mins")->AsInteger = mins;
Queryl->ParamByName("adds")->AsInteger = adds;
try
{ Queryl->ExecSQL(); //
выполнить команду UPDATE
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i
) // Деактивировать предыдущий запрос и очистить SQL
Queryl->Close ();
Queryl->SQL->Clear();
// Восстановить команду SELECT Queryl->SQL->Add(sqls) ;
try
{
Queryl->0pen (); // выполнить команду SELECT }
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i
MessageBox(0, dbError[i].Message.c_str(), "SQL Error", MB_OK) ;
} }
Листинг 5.8. Использование метода
ExecSQL с командой UPDATE.
Рис. 5.16 показывает пример работы приложения, вызывающего повышение заданной параметром mins минимальной зарплаты на величину процентной надбавки, определяемой параметром adds. Приложение представляет пользователю список служащих, зарплата которых осталась меньше установленного минимума. Сравните последний столбец таблицы с оригинальными значениями (Рис. 5.15).

Рис. 5.16. Результат выполнения динамического запроса с модификацией данных.
Аналогично таблице, компонента запроса также инкапсулирует следующие методы:
• First, Next, Prior, Last и MoveBy используются для навигации по результатам динамического запроса.
• Append, Insert, AppendRecord и InsertRecord добавляют новую запись к таблице. Delete вычеркивает текущую запись. Edit разрешает приложению модифицировать записи, a
Post вызывает фактическое изменение содержимого базы данных.
5.2.1.4 Хранимые процедуры
Хранимая процедура представляет собой инкапсуляцию команд и данных (таблиц, индексов, областей значений) в некотором мета-объекте базы данных удаленного сервера. Компонента TStoredProc позволяет выполнить часто повторяющуюся процедуру, хранимую на сервере, и передать результаты приложению клиента.
Операции над большими группами строк в таблице базы данных, агрегатные или математические функции — подходящие кандидаты для хранимых процедур. Перемещая на мощный сервер такие повторяющиеся задачи с интенсивными вычислениями, можно заметно улучшить производительность вашего приложения. Общая загруженность сети при этом снижается, поскольку обработка происходит там же, где находятся сами данные.
Для примера рассмотрим приложение, задачей которого является вычисление единственной величины - среднеквадратичного отклонения значений по большой выборке записей. Для реализации этой функции ваше приложение должно получить по сети от сервера все значения, участвующие в вычислении, а затем произвести подсчет СКО. Цель вашего приложения - конечный результат в виде единственного числа - можно было бы достичь гораздо более эффективно с помощью хранимой на сервере процедуры, которая считывает данные "на месте" и передает именно то конечное значение, которое требовалось вашим приложением.
Рис. 5.17 показывает свойства компоненты хранимой процедуры в окне Инспектора объектов:
![]() Рис. 5.17. Свойства хранимой процедуры. |
Active разрешает или запрещает режим просмотра "живых данных", возвращаемых процедурой на этапе проектирования. Значение false устанавливается по умолчанию. DatabaseName содержит псевдоним адресуемого сервера базы данных. StoredProcName позволяет выбрать имя нужной процедуры из выпадающего списка имен процедур, хранимых на данном сервере. ParamBindMode задает метод, по которому фактические параметры ставятся в соответствие формальным параметрам в описании хранимой процедуры. Значение pbByName (по умолчанию) определяет соответствие по именам, а значение pbByNumber - по порядку перечисления в процедуре. Params используется для ввода параметров хранимой процедуры (если таковые имеются). Редактор параметров, который открывается двойным щелчком мышью в графе значений этого свойства, обращается к серверу для выборки информации о входных и выходных параметрах. Для некоторых серверов |
полная информация, необходимая для запуска хранимой процедуры, может оказаться недоступной. В таком случае вам придется самому определить тип каждого параметра (Input, Output, Result), тип его данных и, возможно, значения входных параметров. Редактор параметров отображает параметры в том поряд-
ке, в котором они перечислены в описании данной хранимой процедуры (Рис. 5.18). Нажатие кнопки ОК подготавливает сервер и вызывает запуск хранимой процедуры на стадии проектирования приложения. Только база данных Oracle позволяет добавлять, вычеркивать или удалять все параметры в определении хранимой на сервере процедуры, производя таким образом ее перегрузку. Отвечающие за такие действия кнопки редактора параметров Add, Delete и Clear обычно запрещены, поскольку ваше приложение клиента не может модифицировать хранимые процедуры других серверов.

Рис. 5.18. Редактор параметров хранимой процедуры.
Аналогично запросу, свойство Params содержит указатель на массив объектного типа TParams. Поэтому изменить значение параметра во время выполнения программы можно по индексу в массиве Items объектов типа TParams:
StoredProcl->Params[0]->Items[1]->AsString = Editl->Text;
или по имени параметра, посредством метода ParamByName:
StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger++;
а затем подготовить сервер методом
Prepare и выполнить процедуру методом ЕхесРгос:
StoredProcl->Prepare () ;
StoredProcl->ExecProc ();
Хранимая процедура возвращает результаты через выходные параметры или через результирующий набор. Доступ к выходным параметрам во время выполнения программы (как и модификация значений входных параметров перед запуском хранимой процедуры) осуществляется по индексам в массиве Items объектов типа TParams:
Editl->Text = StoredProcl->Params[0]->Items[0]->AsString;
или по имени параметра, посредством метода ParamByName:
Edit2->Text =
StoredProcl->ParamByName("ЧИСЛО_СЛУЖАЩИХ")->AsInteger;
Хранимые процедуры некоторых серверов (например, Sybase) возвращают, подобно запросу, результирующий набор, для отображения которого надо использовать подходящую компоненту управления - чаще всего сетку. Процедура проектирования формы приложения с компонентой TStoredProc аналогична той, которую мы использовали для отображения результатов запроса:
1. Установите псевдоним адресуемой базы данных сервера в свойстве DatabaseName.
2. Поместите на форму компоненту
TDataSource и установите ее свойство DataSet = StoredProcl.'
3. Поместите на форму компоненту управления сеткой TDBGrid и установите ее свойство DataSource = DataSourcel.
4. Поместите компоненту TStoredProc на форму.
5. Укажите имя процедуры в свойстве
StoredProcName.
6. Установите свойство Active = true для процедуры
StoredProcl с тем, чтобы сразу же отобразить результаты в сетке.
7. Откройте редактор параметров, введите (если надо) их значения и нажмите кнопку ОК.
5.2.1.5 Соединения с базой данных и транзакции
Компонента TDatabase позволяет создавать в приложении локальный BDE псевдоним базы данных, таким образом не требуя его наличия в конфигурационном файле BDE. Этот локальный псевдоним может использоваться другими компонентами доступа. Кроме того, с помощью TDatabase можно разработать оригинальный процесс первого соединения с сервером (login), подавляя некоторые подсказки и автоматически подставляя значения необходимых параметров. Наконец, и что наиболее важно, TDatabase способна поддерживать одиночное соединение с базой данных, концентрируя в себе все необходимые операции для поддержания транзакции.
Классическим примером транзакции является перевод денежных средств банковских счетов. Такая транзакция обычно состоит в добавлении определенной суммы перевода к новому счету и вычитании этой суммы из исходящего счета. Если выполнение любой из этих операций терпит неудачу, весь трансферт считается незавершенным. SQL серверы дают возможность "прокручивать назад" команды при возникновении ошибки, не производя никаких изменений в базе данных. Именно управление транзакциями является функцией компоненты TDatabase. Как правило, транзакция содержит несколько команд, поэтому начало транзакции надо отметить методом StartTransaction. Как только транзакция началась, все ее исполняемые команды находятся во временном состоянии, до тех пор, пока один из методов Commit или Rollback () не отметят конец транзакции. Вызов Commit
фактически модифицирует данные, а вызов Rollback отменяет всякие изменения.
Рис. 5.19 показывает свойства компоненты соединения с базой данных в окне Инспектора объектов:
![]() Рис. 5.19. Свойства соединения с базой дачных. |
AliasName содержит псевдоним существующей базы данных, определенный утилитой конфигурации BDE. Указание этого свойства является альтернативой значения DriverName. DatabaseName позволяет создать локальный псевдоним базы данных в дополнение к значениям AliasName или DriverName. DriverName содержит имя драйвера BDE при создании локального псевдонима по значению DatabaseName. Указание этого свойства является альтернативой значения AliasName. Params содержит строчный массив параметров одиночного соединения. |
дактирования EditOld, заменяется на новый, введенный в область редактирования EditNew. В этом примере компонентный объект Databasel
использовался для одиночного соединения с базой данных, поддерживающего выполнение одиночной транзакции. Этот объект необходимо каким-то образом связать с псевдонимом базы данных - или установкой соответствующих свойств компоненты, или определив параметры соединения (такие как тип драйвера, имя сервера, имя пользователя, пароль) во время выполнения программы. Сначала опробуем первый способ соединения на стадии проектирования формы приложения, установив значения свойств компоненты, как показано на Рис. 5.19.
void_fastcall TFormI::ButtonlClick(TObject *Sender) {
char sqls[250]; // массив для хранения команды SQL try
{
Databasel->StartTransaction ();
Queryl->SQL->Clear () ;
// Изменить
EditOld на EditNew в таблице CUSTOMER sprintf(sqls, "UPDATE CUSTOMER set Addrl = \"%s\" WHERE (Addrl = \"%s\")", EditNew->Text.c_str(), Edit01d->Text.c_str());
Queryl->SQL->Add(sqls) ;
Queryl->ExecSQL ();
Queryl->SQL->Clear () ;
// Изменить
EditOld на EditNew в таблице ORDERS sprintf(sqls, "UPDATE ORDERS set ShipToAddrl = \"%s\" WHERE (ShipToAddrl = \"%s\")", EditNew->Text.c_str(), Edit01d->Text.c_str()) ;
Queryl->SQL->Add(sqls) ;
Queryl->ExecSQL();
// Внести все изменения, сделанные до этого момента Databasel->Commit();
Tablel->Refresh();
Table2->Refresh() ;
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE {
for (int i=0; i
Databasel->Rollback() ;
return;
} catch (Exception* exception) //
обработка исключений
{
MessageBox (0, exception->Message.c_str (), "Error", MB_OK) ;
Databasel->Rollback() ;
return;
) }
Листинг 5.9. Транзакция, реализующая смену адреса фирмы.
Конечно, показанный на Рис. 5.20 пример прохождения транзакции в соединении с локальным сервером не имеет особой практической ценности и приводится здесь только с целью проиллюстрировать работу компоненты
TDatabase.

Рис. 5.20. Результат прохождения транзакции.
Листинг 5.10 показывает, как соединиться с сервером базы данных во время выполнения программы, не создавая ее псевдонима. Ключевые моменты заключаются в том, чтобы указать DriverName и заполнить массив параметров информацией, необходимой для первого соединения (в нашем примере пользователь вводит свое имя и пароль в объекты компонент редактирования Editi и Edit2). Мы определили только те параметры соединения, которые не установлены для данного драйвера в утилите конфигурации BDE. Значение параметра SQLPASSTHRU MODE определяет один из трех возможных способов взаимодействия табличных методов Add, Append и Insert с компонентами запросов, которые соединены с той же базой данных. Использованное в примере значение NOT SHARED означает, что табличные методы и запросы используют два раздельных соединения с сервером. Сервер рассматривает их как соединения с двумя разными пользователями. До тех пор, пока транзакция не завершится, табличные методы не применяются, хотя результаты выполнения запросов могут менять содержимое базы данных, раздельно от действий активной транзакции. Два других значения SHARED NOAUTOCOMMIT и SHARED AUTOCOMMIT указывают, что табличные методы и запросы разделяют одно общее соединение с сервером. Если вам нужно включить табличные методы в транзакцию, используйте способы SHARED NOAUTOCOMMIT или NOT SHARED.
void_fasfccall TFormI::ButtonlClick(TObject *Sender) (
char name[20]; // буфер для имени пользователя char pass [20]; // буфер для пароля try
{
// Закрыть базу данных и установить параметры Databasel->Close() ;
Databasel->DriverName = "STANDARD";
Databasel->KeepConnection = true;
Databasel->LoginPrompt = false;
Databasel->Params->Add("SERVER NAME=
...\\CBuilder\\EXAMPLES\\DATA\\EMPLOYEE.DB") ;
Databasel->Params->Add("SCHEMA CACHE=8") ;
Databasel->Params->Add("OPEN MODE=READ/WRITE") ;
Databasel->Params->Add("SQLPASSTHRU MODE=NOT SHARED");
sprintffname, "USER NAME=%s", Editl->Text.c_str ());
Databasel->Params->Add(name);
sprintf(pass, "PASSWORD=%s", Edit2->Text.c_str());
Databasel->Params->Add(pass);
// Снова открыть базу данных и указанную таблицу Databasel->0pen() ;
Tablel->0pen() ;
}
catch(EDBEngineError* dbError) //
обработка ошибок BDE
{
for (int i=0; i
} )
Листинг 5.10. Соединение с сервером без псевдонима.
Конечно, первое вхождение в локальную демонстрационную базу данных BCDEMOS не требует задания имени пользователя и пароля, однако, используя такую методику, вы сможете стандартизовать процесс соединения с обычно защищенными базами данных удаленных серверов. Рис. 5.21 иллюстрирует работу прототипа подобного приложения.

Рис. 5.21. Первое соединение с "защищенной" базой дачных.
5.2.2 Компоненты управления данными
Компоненты управления служат для отображения и редактирования наборов данных на форме в удобном для пользователя виде. Свойство DataSource замыкает трехступенчатую связь любой из компонент управления с компонентагли доступа к содержимому базы данных. Вы должны связать выбранную компоненту управления с набором данных посредством компоненты источника TDataSource, который определяется значением свойства DataSource.
Устройство и работа большинства из этих интерфейсных элементов довольно очевидны, их эквиваленты знакомы нам по вкладке
Standard стандартных Windows Палитры компонент, а глава 4 содержит подробное их описание.
Остановимся на особенностях использования исключительно важной и мощной компоненты навигатора базы данных TDBNavigator. Эта компонента придает приложениям СУБД новый стандартизованный облик с панелью управления как у видеомагнитофона (Рис. 5.22).

Рчс. 5.22. Панель навигатора.
Нажимая на кнопки First, Prior, Next и Last, пользователь перемещается от записи к записи, а с помощью кнопок
Insert, Delete, Edit, Post, Cancel и Refresh про-
изводит редактирование записей. Именно навигатор обеспечивает ту степень функциональности, в которой нуждается любое современное приложение баз данных. Все кнопки навигатора видимы по умолчанию.
Рис. 5.23 показывает свойства компоненты навигатора базы данных в окне Инспектора объектов:
![]() Рис. 5.23. Свойства навигатора базы данных. |
DataSource соединяет кнопки управления панели навигатора с компонентами доступа к наборам данных через компоненту источника. Изменяя значение этого свойства во время выполнения программы, можно использовать одну и ту же компоненту для навигации по разным таблицам. Разместите на форме две компоненты редактируемого ввода DBEditI и DBEdit2, связанные с таблицами CustomersTable и OrdersTable через источники данных CustomersSource и OrdersSource, соответственно. Когда пользователь выбирает название фирмы (поле Company в DBEditI), навигатор тоже должен соединяться с источником CustomersSource, а когда активизируется номер заказа (поле OrderNo в DBEdit2), навигатор должен переключаться на источник OrdersSource. Чтобы реализовать подобную схему работы навигатора, необходимо написать обработчик события OnEnter для одного из объектов компоненты редактирования, а затем разделить это событие с другим объектом (Листинг 5.11). VisibleButtons позволяет убрать ненужные кнопки, например, все кнопки редактирования на форме, предназначенной исключительно для просмотра данных. В приложениях клиент/сервер рекомендуется запретитькнопки First и Last, так как их нажатие может проявляться в длительном прохождении запросов. |
Во время выполнения программы можно динамически прятать или вновь показывать кнопки навигатора - в ответ на определенные действия пользователя или на изменения состояния приложения. Предположим, вы предусмотрели единый навигатор для редактирования таблицы
CustomersTable и для просмотра таблицы OrdersTable. Когда навигатор подключается ко второй таблице, желательно спрятать кнопки редактирования Insert, Delete, Edit, Post, Cancel и Refresh, а при подключении к первой таблице - снова показать их. Листинг 5.11 показывает законченный текст обработчика события OnEnter с добавлениями кода для манипуляций кнопками панели навигатора.
void_fastcall TFormI:: DBEditlEnter(TObject *Sender) {
if (Sender == DBEditI)
{
DBNavigator->DataSource = CustomersSource;
Set
btnShow “ nbFirst“nbPrior“ nbNext“nbLast“nbInsert“ nbDelete“nbEdit“nbPost“nbCancel“nbRefresh;
DBNavigator->VisibleButtons = btnShow;
}
else
{
DBNavigator->DataSource = OrdersSource;
Set
btnShow “ nbFirst“nbPrior“nbNext“nbLast;
DBNavigator->VisibleButtons = btnShow;
} }
Листинг 5.11. Переключение значения свойства DataSource навигатора и манипуляции со свойством VisibleButtons.
ShowHint разрешает или запрещает высвечивать подсказку с названием кнопки навигатора, когда на нее наведен курсор. Значение false
(устанавливается по умолчанию) запрещает подсказки для всех кнопок.
Hints содержит массив текстовых подсказок для каждой кнопки навигатора. Используя редактор списка, который открывается двойным щелчком мышью в графе значений этого свойства, можно дать оригинальные названия кнопкам.
Рис. 5.24 показывает работу приложения, использующего все описанные возможности навигатора.

Рис. 5.24. Разделение навигатора между двумя таблицами.
5.3 Итоги
C++Builder - это система программирования общего назначения, которая может использоваться для быстрой разработки любых приложений, в том числе одних из самых сложных — Систем Управления Базами Данных.
Визуальное построение простого интерфейса пользователя с таблицей некоторой базы данных оказывается делом нескольких часов, а иногда и нескольких минут. Не намного больше времени потребуется для организации SQL запросов к локальному или удаленному серверу базы данных, даже с переменным числом параметров или кэшируемыми обновлениями.
Итак, проектирование формы приложения СУБД в среде C++Builder в простейшем случае требует выполнения всего лишь трех или четырех действий:
1. Перенесите на форму компоненту набора данных (TTable или
TQuery) из вкладки Палитры Data Access и установите ее свойства в соответствии со специфическими требованиями выбранной базы данных.
2. Перенесите на форму компоненту источника данных[ТОа1а5оигсе и в свойстве DataSet укажите ссылку на объект набора данны^ (например. Table 1 или Query 1).
3. Перенесите на форму нужные компоненты отображения и редактирования данных из вкладки Data Controls и в их свойстве DataSource задайте источник данных (например, DataSourcel). Определите отображаемое поле набора данных в свойстве DataField. '
4. Если на предыдущем шаге вы выбрали компоненту сетки TDBGrid,
то используйте ее совместно с компонентой навигатора TDBNavigator.
Биржевая торговля: Механические торговые системы - Создание - Программирование
- Механические торговые системы (МТС)
- Технический анализ и МТС
- Разработка механических торговых систем
- Механические торговые системы
- GNU механические торговые системы
- Тестирование механических торговых систем
- MetaStock - механические торговые системы
- Omega Trade Station - механические торговые системы
- МТС - обзор языков программирования
- Си для механических торговых систем
- C# для механических торговых систем
- C++ для механических торговых систем
- Borland C++ для механических торговых систем
- C++ Builder для механических торговых систем
- Visual C++ для механических торговых систем









