Основы программирования в среде C++Builder

реклама
Д. Н. Лясин, С. Г.Саньков
Основы программирования в
среде C++Builder
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ВОЛЖСКИЙ ПОЛИТЕХНИЧЕСКИЙ ИНСТИТУТ (филиал) ВОЛГОГРАДСКОГО
ГОСУДАРСТВЕННОГО ТЕХНИЧЕСКОГО УНИВЕРСИТЕТА
Д.Н. Лясин, С.Г.Саньков
Основы программирования в среде C++Builder
Учебное пособие
РПК «Политехник»
Волгоград 2007
УДК 004.056
Рецензенты
Волжский филиал «Московский энергетический институт (технический
университет)», доктор технических наук, профессор, заведующий кафедрой
«Автоматизации технологических процессов» Шевчук В.П.
Волжский гуманитарный институт (филиал) Волгоградского государственного
университета, доктор технических наук, заведующий кафедрой «Прикладной
математики» Мирецкий И.Ю.
Лясин Д.Н., Саньков С.Г.
Основы программирования в среде C++Builder. Учебное пособие./
ВолгГТУ, Волгоград, 2007. – 83 с.
ISBN – 5-230-04-900-6
В учебном пособии рассматриваются вопросы разработки приложений для
операционной системы Windows в среде программирования C++Builder. Дан обзор
основных инструментов разработки приложений, рассмотрены особенности
компонентного подхода к проектированию программ и визуальной технологии
программирования. Большое внимание уделено практическому аспекту разработки
приложений, дан обзор основных компонент C++Builder, приведены примеры их
использования для разработки приложений под ОС Windows с учетом особенностей
работы операционной системы. Рассмотрены также способы разработки
собственных компонент.
Предназначены для студентов, обучающихся по направлению 5528 "Информатика и вычислительная техника" и специальности 2201 "Вычислительные
машины, комплексы, системы и сети" всех форм обучения в рамках курсов
«Спецкурс по языкам программирования» и «Инструментальные средства
программирования».
Ил. 12. Табл. 5.
Библиогр.: - 6 назв.
Печатается по решению редакционно-издательского совета Волгоградского
государственного технического университета
ISBN – 5-230-04-900-6
©
Волгоградский
государственный технический
Университет, 2007
Оглавление
1. Введение в C++Builder ………………………………….…………...
1.1. Эволюция систем программирования ……………………………..
1.2. Основные достоинства системы программирования C++
Builder……………………………………………………………………………
Вывод ……………………………………………………………………...
2. Состав системы программирования C++Builder ………..………….
2.1. Панель инструментов и главное меню системы …………………..
2.2. Палитра компонент ……………………………………………….
2.3. Инспектор объектов ………………...…………………….…………
2.4. Редактор форм ………………………..………………………………
2.5. Редактор кода ……………….………………………………………..
2.6. Окно дерева объектов ….…………………………………………….
2.7. Менеджер проекта ………….………………………………………..
2.8. Хранилище объектов… …….………………………………………..
Вывод………………… ………….………………………………………..
5
5
7
11
11
12
13
14
15
16
17
18
19
20
3. Проект приложения в C++Builder…………………………………………
20
Вывод………………………………………………………………………. 28
4. Библиотека визуальных компонент С++Builder………………………... 28
4.1. Общие сведения о библиотеке VCL…………………………………
4.2. Иерархия классов VCL…………………………………….…………
Вывод………………………………………………………………………
5. Работа с формами в C++Builder……………………………………………
5.1. Создание и уничтожение форм……………………………………..
5.2. Модальный режим работы форм……………………………………
28
34
47
47
47
53
5.3. Многодокументный интерфейс (MDI)……………………………...
55
Вывод……………………………………………………………………… 57
6. Работа с клавиатурой и мышью в среде C++ Builder………………….. 58
6.1. Интерфейс работы с мышью в C++Builder…………………………
6.2. Интерфейс работы с клавиатурой в C++Builder…………………...
3
59
60
6.3. Пример использования событий мыши и клавиатуры в
C++Builder……………………………………………………………………….
61
Вывод………………………………………………………………………. 65
7. Графика в C++Builder……………………………………………………….
65
7.1. Основы графического интерфейса Windows……………………….. 65
7.2. Обзор графических компонент C++Builder………………………… 67
7.3. Компонент TCanvas…………………………………….………….
68
7.4. Компоненты для работы с изображениями………………………… 72
Вывод………………………………………………………………………. 74
8. Разработка компонент в C++ Builder……………………………………... 74
8.1. Процедура разработки компонента C++Builder……………………
75
8.2. Определение свойств и событий компонента……………………… 77
Вывод………………………………………………………………………. 81
Список литературы…………………………………………………………….. 82
4
1. Введение в C++Builder
1.1. Эволюция систем программирования
Средства разработки приложений для ЭВМ как основной инструмент
программиста должны обеспечивать возможность написания эффективного кода с
точки зрения современных технологий и при этом предоставлять комфортные
условия для работы программиста, позволяя ему сосредоточиться на решении
стоящих перед ним задач. Достичь баланса этих двух основных функций удается
далеко
не
всякой
инструментальной
системе
программирования
(ИСП).
Разработчики ИСП прошли довольно долгий путь совершенствования, прежде чем
достичь той вершины, представителем которой является описываемая в настоящем
пособии система программирования C++ Builder. Рассмотрим основные этапы
эволюции ИСП на основе языка С [4].
Первой ступенью эволюции систем программирования
стало появление
языка программирования C++ и библиотек классов C++, которые заключали в
себе сотни строк кода, необходимых даже для простейшего отображения окна на
экране, однако обращение к этим классам занимало в программе на C++ всего
несколько строк. Этот этап эволюции характеризуется наличием раздельных
элементов системы программирования – текстового редактора, компилятора,
линковщика, отладчика, справочника по функциям языка С. Процесс разработки
приложения здесь заключается в последовательном вызове этих элементов из
командной строки операционной системы. Это, очевидно, сильно замедляет
процесс программирования.
Вторым поколением средств разработки для Windows стало появление
средств интегрированной среды разработки (Integrated Development Environment,
IDE). Эти средства позволяют программисту редактировать, компилировать,
компоновать, отлаживать программы непосредственно в одном приложении.
Интегрированные средства отладки появились чуть позже и были быстро взяты на
вооружение программистами. Практически все современные ИСП строятся по
принципу IDE.
5
Следующий шаг эволюции средств разработки стал результатом развития концепции каркасов (framework). Каркас — это основа, скелет вашего приложения, на
основе которого разрабатывается как структура программы, так и детали ее
пользовательского интерфейса. Каркасы кода действительно очень похожи на стены,
балки, водопровод и электрические коммуникации строительных каркасов зданий.
Каркасы
представляют
собой
библиотеки
классов,
которые
предлагаются
разработчику как строительные блоки для создания его приложения. Каждый класс
такой библиотеки представляет собой уже готовый к использованию элемент
программного или пользовательского интерфейса приложения (окно, меню, таймер).
При этом каркасные приложения имеют стандартную структуру, реализующую
типовой алгоритм функционирования программы. Достоинства такого подхода
очевидны – типовые элементы приложения предоставляются программисту в
готовом виде, и вместо многих десятков строк кода, создающих, например, обычное
окно, он может воспользоваться классом «Окно», который содержит в себе все
необходимые для работы окна методы и данные. В качестве примера каркасов можно
назвать библиотеки MFC (Microsoft Foundation Class), OWL (Object Windows Library).
Настоящей проблемой технологии каркасов являются их ограничения. Несмотря
на то, что каркасы действительно ускоряют разработку приложений, предоставляя в
распоряжение разработчика многие из основных функций нормального Windowsприложения, они сразу же встают на пути приложения, которое не укладывается в
привычные
рамки,
требует
нестандартного
решения,
не
предусмотренного
разработчиками каркаса. Еще одно неудобство каркасных технологий – плохая
визуализация процесса сборки приложения из классов, когда и внешний вид элемента
пользовательского интерфейса, и связи между отдельными элементами становятся
видны разработчику только на этапе работы программы.
Развитием концепции каркасов стало появление компонентной модели ИСП,
применяемой в таких системах, как Delphi, C++ Builder. В основу программирования
здесь также положена библиотека, но это уже библиотека компонент (VCL, Visual
Component Library). Компоненты не диктуют строгую структуру программы, они
могут использоваться программистом по его усмотрению для достижения
поставленной
цели.
Благодаря
своей
6
многофункциональной
внутренней
архитектуре, основанной на использовании свойств и событий, компоненты можно
гораздо гибче адаптировать к требованиям той или иной задачи. Помимо
использования компонентной модели, многие современные ИСП строятся по
технологии RAD (Rapid Application Development), характеризующейся наличием
визуальной среды проектирования, представляющей еще на этапе проектирования
приложения весь его графический интерфейс в виде, аналогичном тому, каким он
будет на этапе работы программы. Также на этапе проектирования в визуальной
форме (в инспекторе объектов) задаются основные свойства компонент, связи
компонент друг с другом. По данным фирмы Borland, в современных программах
около 97% кода приходится на оформление внешнего вида, и лишь 2-3% на
основную функциональность программы. Здесь-то и приходит на помощь
концепция RAD. Благодаря мощным инструментальным средствам, входящим в
средства
программирования,
поддерживающие
RAD,
можно
значительно
автоматизировать процесс создания компонентов оформления и интерфейса,
разгрузив
программиста,
избавив
его
от
рутины,
и
дав
возможность
сконцентрировать усилия и внимание на основных функциях программы.
1.2. Основные достоинства системы программирования C++ Builder
Система
объектно-ориентированного
программирования
C++Builder
производства корпорации Borland предназначена для операционных систем
платформы Win32. С++Builder –это законченный, гибкий инструмент RAD,
сочетающий мощь современного компилятора языка С++, удобство средств
визуальной разработки приложений, достоинства компонентного подхода к
проектированию
с
открытыми
возможностями
пополнения
имеющихся
в
распоряжении разработчика компонент[2].
Интегрированная среда разработки объединяет редактор форм, инспектор
объектов,
палитру
компонент,
администратор
проекта
и
полностью
интегрированные редактор кода и отладчик - инструменты быстрой разработки
программных приложений, обеспечивающие полный контроль над кодом и
ресурсами.
7
Профессиональные средства языка C++ интегрированы в визуальную среду
разработки. C++Builder предоставляет быстродействующий компилятор языка
Borland C++, соответствующий стандарту ANSI. Для повышения скорости работы в
системе используется эффективный инкрементальный загрузчик и гибкие средства
отладки как на уровне исходных инструкций, так и на уровне ассемблерных команд.
Компилятор системы С++Builder поддерживает технологии шаблонов (templates),
именованных областей видимости (namespaces), обработки исключительных
ситуаций (exception handling), библиотеки стандартных шаблонов STL (Standard
Template Library), информации о типах времени выполнения RTTI (Run Time Type
Information). Инкрементальный компилятор и линковщик системы обрабатывают
при повторной сборке проекта только те его строки, которые изменялись с момента
предыдущей компиляции, тем самым экономя время сборки приложения.
Конструирование
по
способу
"drag-and-drop"
позволяет
создавать
приложение простым перетаскиванием захваченных мышью визуальных компонент
из палитры компонент на форму приложения. Инспектор объектов предоставляет
возможность оперировать со свойствами и событиями компонент уже на этапе
разработки приложения, автоматически задавая значения свойств компонент,
создавая заготовки функций обработки событий, которые необходимо наполнять
кодом.
Механизмы
двунаправленной
разработки
(two-way-tools)
позволяют
синхронизировать процесс визуального проектирования форм и генерацию
исходного кода, когда манипулирование с визуальными средствами редактора форм
или инспектора объектов приводит к автоматическим изменениям структуры
программы, объявлений объектов программы.
Библиотека Визуальных Компонент VCL. Компоненты библиотеки VCL
составляют основу программного инструментария программиста на С++Builder.
Компонент – это объект, который своими свойствами, методами и событиями
описывает поведение некоторого элемента пользовательского (кнопка, список строк
в окне, меню) или программного (соединение с базой данных, сокет, таймер)
интерфейса программы. Так как каждый компонент является законченным
программным объектом, то, конструируя свое приложение как совокупность
8
взаимодействующих компонент, программист избавляется от необходимости писать
большие фрагменты кода – они уже реализованы разработчиками компонент.
Находясь в среде объектно-ориентированного программирования C++Builder,
компоненты можно использовать непосредственно, менять их свойства, облик и
поведение
или
порождать
производные
элементы,
обладающие
нужными
отличительными характеристиками. Хорошим подспорьем программисту является
наличие исходных текстов библиотеки визуальных компонент, что облегчает
разработку новых компонент на базе готовых примеров. C++Builder 6 версии
Standard содержит более 80 компонент, версии Professional – более 150 компонент,
версии Enterprise – более 200 компонент [4]. Благодаря открытости архитектуры
VCL, список может быть дополнен как компонентами, приобретенными у
сторонних разработчиков, так и компонентами собственной разработки.
Использование модели “свойства-методы-события” PEM , реализованное в
С++ Builder как расширение языка С++, определяет внутреннюю структуру
компонент, превращая их в удобный инструмент визуального проектирования.
Компонент в этой модели является расширением понятия «класс» стандартного
языка С++ с добавлением понятий «свойство» и «событие». Свойство является
расширением понятия «компонентные данные класса», оно устанавливает способ
получения и сохранения значений характеристик компонент при манипулировании
ими как на этапе проектирования, так и в процессе работы программы. Событие
определяет способ реакции компонента на внешние «раздражители», которыми
могут стать сообщения, приходящие от пользователя, других компонент,
операционной системы. Для поддержки модели PEM в язык внесены такие
ключевые слова, как __property, _published, _closure и др. Подробнее о свойствах и
событиях речь пойдет в главах 4 и 8.
Доступ
к
разрабатываемых
функциям
приложений
API
за
позволяет
счет
расширять
функциональность
возможностей,
предусмотренных
операционной системой, которые, возможно, не реализованы компонентами VCL,
но которые, таким образом, не выпадают из арсенала программиста.
Хранилище объектов является инструментом новой методики хранения и
повторного использования модулей данных, объектов, форм и программной бизнес9
логики. Поскольку построение нового приложения на существующем фундаменте
значительно экономит временные затраты, хранилище объектов предоставляет для
повторного использования готовые структуры: формы и законченные программные
модули. Используя хранилище объектов, программист может начинать новый
проект на твердой платформе предшествующих разработок, лишь добавляя в них
фрагменты, характерные для стоящей перед ним задачи.
Поддержка промышленных стандартов ActiveX, OLE, СОМ, MAPI,
Windows Sockets TCP/IP, ISAPI, NSAPI, ODBC, Unicode и MBCS существенно
повышает функциональную мощь системы программирования, превращает ее в
универсальное
средство
разработки
приложений
различной
практической
направленности.
Возможности работы с базами данных. С++Builder содержит полный набор
компонент
для
работы
с
базами
данных,
которые
вообще
не
требуют
программирования. Разработка по способу "drag-and-drop" многократно упрощает и
ускоряет обычно трудоемкий процесс программирования СУБД в архитектуре
клиент/сервер.
Широкий
выбор
компонент
управления
визуализацией
и
редактированием позволяет легко изменять вид отображаемой информации и
поведение программы. Механизм BDE (Borland Database Engine) поддерживает
высокопроизводительный 32-разрядный доступ к базам данных dBASE, Paradox,
Sybase, Oracle, DB2, Microsoft SQL Server, Informix, InterBase, MySQL. C++Builder
использует контроллер ODBC (Open Database Connectivity) производства Microsoft
для связи с серверами баз данных Excel, Access, FoxPro. Начиная с версии 6
поддерживается технология SOAP для многоуровневых приложений баз данных,
добавлен независимый от архитектуры BDE набор компонентов dbExress,
позволяющий создавать приложения клиент/сервер, работающие с базами данных
различных форматов, поддерживается технология доступа к базам данных ADO.
Все это в сочетании с большим количеством визуальных компонент отображения
информации из баз данных делает C++Builder мощной и гибкой средой
программирования баз данных на языке С++.
10
Вывод
Система программирования С++Builder является современным средством
разработки приложений, поддерживающим компонентный подход и построенный
по
принципу
визуального
проектирования.
Удобные
встроенные
средства
проектирования и поддержка большинства современных технологий в области
разработки баз данных, сетевых приложений, мультимедийных приложений
превращают С++Builder в универсальный инструмент программиста любой
специализации.
2. Состав системы программирования C++Builder
C++Builder
представляет
собой
визуальную
интегрированную
среду
программирования, включающую большой набор инструментов для проектирования
приложений для операционной системы Windows. После запуска системы
программирования она разворачивается в вид, представленный на рис. 2.1. (для
C++Builder 6.0).
Можно выделить следующие основные элементы системы:
1. Палитра инструментов и главное меню системы, содержащие основные
команды по управлению проектом и настройками системы.
2. Палитра
компонент
–
содержит
список
компонентов,
доступных
программисту для добавления в проект.
3. Инспектор объектов – инструмент, предназначенный для управления
свойствами и обработчиками событий компонент проекта на этапе
разработки приложения.
4. Редактор форм – инструмент визуального представления видимых и
невидимых компонент и манипулирования ими на этапе проектирования.
5. Редактор кода – отображает код проекта, позволяет вносить в него
изменения, для чего имеются удобные встроенные средства.
11
2
1
6
4
3
5
Рис. 2.1. Внешний вид среды программирования C++Builder 6.0
6. Окно дерева объектов – удобный инструмент отображения компонент
проекта в иерархии “владелец-подчиненный».
Помимо этого, достойны упоминания элементы среды, которые не
отображаются на экране при начальной загрузке, но могут быть открыты
дополнительно. К таким элементам можно отнести хранилище объектов и менеджер
проекта.
2.1. Панель инструментов и главное меню системы
Панель инструментов представляет программисту набор кнопок для быстрой
подачи основных команд управления проектом: открытия и сохранения файлов,
запуска и отладки, переключения между элементами среды. Панель является
полностью настраиваемой, набор кнопок в ней может быть изменен по выбору
программиста в пункте меню View | Toolbars. Полный список команд системы
содержится в пунктах главного меню. Размер пособия не позволяет описать все
12
пункты меню системы C++Builder, но в процессе изложения будут рассмотрены
основные пункты, использующиеся для решения типовых задач проектировщика.
2.2. Палитра компонент
Палитра
компонент
представляет
собой
хранилище
всех
компонент,
зарегистрированных в системе. Компоненты объединены в группы, каждая из
которых хранится на отдельной закладке, название группы указывается в заголовке
закладки (рис.2.2.).
Рис. 2.2. Палитра компонент C++Builder
Набор закладок в палитре, а также компонент в каждой закладке может быть
изменен, система открыта для добавления новых компонент, разработанных как в
самой системе, так и сторонними производителями. В таблице 2.1. перечислены
основные группы компонент.
Таблица 2.1.
Перечень групп компонент системы программирования C++Builder 6.0
Название закладки Группа компонент
Data Access
Стандартные компоненты пользовательского интерфейса
Windows (кнопки, строки ввода, списки и т.п.)
Дополнительные
стандартные
компоненты
специализированного дизайна или функциональности
Компоненты пользовательского интерфейса платформы
Win32 (закладки, полоса прогресса, панели)
Компоненты, обеспечивающие доступ к системным
функциям Windows (таймер, DDE-обмен, технология OLE)
Компоненты для доступа к базам данных
Data Controls
Компоненты для отображения информации из баз данных
BDE
Компоненты для доступа к базам данных с использованием
BDE
Компоненты для создания Web-приложений
Standard
Additional
Win32
System
Internet
13
Продолжение таблицы 2.1
Qreport
Компоненты для создания отчетов
Dialogs
Компоненты для доступа к стандартным диалоговым окнам
Windows (открытия, сохранения файлов, печати и т.п.)
Компоненты пользовательского интерфейса Win 3.1
(оставлены для совместимости с ранними проектами)
Управляющие компоненты ActiveX
Win 3.1
ActiveX
Компоненты, реализующие основные Internet-протоколы
(FTP, POP3, SMTP и др.)
Оболочки VCL для распространенных серверов СОМ
офисных приложений Microsoft
FastNet
Office2k
Помимо
перечисленных
групп
в
палитре
присутствуют
несколько
дополнительных групп компонент, которые предназначены для реализаций
специфичных технологий работы с базами данных (ADO, dbExpress, DataSnap,
доступ к InterBase).
Для того, чтобы использовать нужный компонент в проекте, необходимо
выбрать его в палитре, а затем щелкнуть левой кнопкой мыши на том месте на
форме, где необходимо его поместить. Дальнейшая работа с компонентом
осуществляется в редакторе форм и инспекторе объектов.
2.3. Инспектор объектов
В окне инспектора объектов (Object Inspector) отображаются значения свойств
компонент, а также названия сопоставленных различным событиям компонента
обработчиков. Инспектор объектов отображает свойства выбранного в редакторе
форм компонента. Для выбора другого компонента может быть использован
раскрывающийся список в верхней части инспектора.
Инспектор объектов состоит из двух частей – окна свойств (Properties) и окна
событий (Events). Окно свойств содержит список опубликованных (published)
свойств компонента, а также присвоенных этим свойствам значений. Значение
любого свойства может быть изменено, это делается в редакторе свойств. Редактор в
простейшем случае представляет собой строку ввода, но для некоторых свойств он
выглядит как раскрывающийся список или диалоговое окно. Измененные значения
14
свойства обрабатываются системой уже на этапе проектирования, так, например,
если изменить ширину формы, поменяв значение свойства Width, то вид формы в
редакторе форм изменится согласно присвоенной ширине.
Окно событий отображает опубликованные события компонента, а также
сопоставленные
им
функции-обработчики.
По
умолчанию
событиям
не
сопоставлены обработчики, но если сделать двойной щелчок в поле имени
обработчика события, то это приведет к генерации пустой функции-обработчика
данного события, после чего ее текст можно набрать в редакторе кода. Если
функция-обработчик для события уже присутствует в коде программы, ее можно
сопоставить событию в раскрывающемся списке.
Показать инспектор объектов, если его нет на экране, можно командой View |
Object Inspector (F11).
2.4. Редактор форм
Редактор форм представляет на экране формы приложения в том виде, какой
они будут иметь на стадии работы приложения. Форма – это компонент С++Builder,
который объединяет в себе свойства, методы и события, описывающие окно ОС
Windows. Форма выступает владельцем хранящихся на ней управляющих
элементов. Редактор форм предоставляет возможность манипулирования внешним
видом
формы
и
ее
управляющими
элементами:
изменения
размера
и
месторасположения, выбора для изменения свойств в инспекторе объектов,
добавления и удаления компонент на форму (рис. 2.3).
Рис.2.3. Форма на этапе проектирования
15
При этом редактор форм не является сугубо визуальным инструментом,
позволяющим только конструировать внешний вид окна программы. В соответствии
с принципом двунаправленной разработки, те манипуляции, которые программист
производит с компонентами формы в редакторе форм (добавляет, удаляет),
приводят к автоматическим изменениям в коде программы.
2.5. Редактор кода
Какими бы удобными ни были инструменты визуального проектирования
приложений, они не могут полностью сформировать код приложения, некоторую
его часть приходится вводить вручную. Система C++Builder предлагает для этого
Рис.2.4. Внешний вид редактора кода C++ Builder
удобный инструмент – редактор кода (рис.2.4).
Редактор кода позволяет редактировать одновременно несколько файлов,
каждому открытому в редакторе файлу соответствует закладка в верхней части окна.
В редакторе кода C++Builder можно редактировать файлы следующих типов[4]:
 Заголовочные файлы и файлы на языке С++ (расширения .h, .hpp, .cpp).
 Файлы на языке Pascal (.pas).
 Текстовые файлы (.txt).
 Файлы из проекта C++Builder (.dfm, .bpr).
 Выражения на языке SQL для компонент типа TQuery.
16
Для
редактирования
кода
программы
в
редактор
кода
встроено
дополнительное средство Code Insight. Оно позволяет получать быструю подсказку
по свойствам и методам того объекта, имя которого введено в редакторе кода (рис.
2.5). Для ввода требуемого свойства или метода необходимо лишь выбрать его из
Рис.2.5. Подсказки Code Insight
предлагаемого списка. Еще одна функция Code Insight – подсказка по набору
формальных параметров той функции, вызов которой оформляется в коде
программы.
2.6. Окно дерева объектов
Окно дерева объектов (Object Tree View) предназначено для представления
компонент
активной
формы
в
иерархии
«владелец-подчиненный».
Его
использование удобно для быстрого поиска того или иного компонента, особенно
если компоненты перекрывают друг друга в редакторе форм и осуществить выбор
мышью затруднительно.
С помощью окна дерева объектов программист может редактировать
компонент, используя операции работы с буфером обмена, тем самым создавая
копии того или иного компонента. Показать окно дерева объектов, если его нет на
экране, можно командой View | Object TreeView (Shift-Alt-F11).
17
Рис.2.7. Окно Object Tree View
2.7. Менеджер проекта
Менеджер проекта (рис. 2.8) предназначен для управления файлами,
входящими в проект C++Builder. Он позволяет просмотреть, какие файлы включены
в проект, добавить или удалить файлы в/из проекта, осуществлять навигацию между
файлами. Для этих целей используются кнопки панели инструментов менеджера или
команды контекстного меню. Отобразить менеджер проекта на экране можно,
используя команду View | Project Manager (Ctrl-Alt-F11).
Рис.2.8. Окно менеджера проекта
18
2.8. Хранилище объектов
Хранилище объектов выполняет в системе программирования C++Builder
важную
функцию
повторного
использования
спроектированные формы, модули
объектов,
данных, проекты
когда
однажды
могут использоваться
многократно при разработке различных приложений. Вид окна хранилища объектов
представлен на рис. 2.9. Открыть его можно, выбрав команду File | New |Other. В
появившемся окне можно выбрать тот или иной объект для добавления к своему
проекту. Объекты объединены в группы, переключение между которыми возможно
с использованием закладок в верхней части окна. В качестве объектов здесь
хранятся формы, диалоговые окна, проекты, мастера, модули данных. Например, из
закладки Dialogs можно добавить в проект диалоговое окно ввода пароля Password
dialog, которое содержит все необходимые подобному окну управляющие элементы
(строка для ввода пароля, кнопки подтверждения и отмены). В итоге программист
получает готовую к использованию форму,
ему останется лишь написать код
обработки введенного пароля.
Хранилище объектов открыто для модификации. Если щелкнуть на форме
Рис. 2.9. Хранилище объектов
правой кнопкой мыши, то в контекстном меню можно увидеть команду Add to
Repository (Добавить в Хранилище), которая позволяет поместить данную форму в
хранилище объектов и использовать ее в дальнейшем в других проектах. Для
19
добавления в хранилище всего проекта используется команда Project | Add to
Repository. В этом случае проект может быть использован в дальнейшем как шаблон
для будущих разработок.
Вывод
Система программирования Borland C++Builder предоставляет программисту
удобный инструментарий для разработки Windows-приложений, позволяющий
гибко сочетать средства визуального проектирования, компонентный подход к
построению программы со стандартными средствами систем программирования
(редактор кода, отладчик, встроенная помощь).
3. Проект приложения в C++Builder
Проект в визуальной среде проектирования С++Builder состоит из нескольких
файлов и необходим для корректного проектирования и компиляции приложения. В
общем случае состав проекта зависит от типа разрабатываемого приложения
(графическое приложение, консольное приложение, динамическая библиотека,
системная служба, WEB приложение). Рассмотрим состав проекта для графического
(WIN32 GUI) приложения, поскольку именно с таким чаще всего приходится
сталкиваться прикладным программистам. Все файлы, входящие в проект, можно
разделить на две группы: файлы проекта и файлы форм. По умолчанию файлы
проекта имеют имя project1, а файлы форм unit1, unit2 и т.д. К файлам проекта
относятся следующие типы файлов: главный файл проекта, исходный файл проекта,
файл ресурсов проекта. Главный файл проекта имеет расширение *.bpr и необходим
при разработке любых приложений в среде С++Builder. Данный файл содержит
опции, предназначенные для компилятора и линковщика, и необходим для создания
выходного модуля приложения. Этот файл является аналогом файла makefile,
используемого во многих системах программирования, базирующихся на С++. Для
получения файла *.mak из главного файла проекта можно использовать пункт
20
главного меню Project | Export Makefile. Сам главный файл проекта представлен в
формате XML и состоит из нескольких секций: MACROS, OPTIONS, LINKER,
FILELIST, IDEOPTIONS, содержание которых определяет состав проекта, опции
сборки проекта в результирующий модуль, используемые библиотеки и пакеты и
т.п. Фрагмент главного файла проекта приведен в листинге 3.1. Для просмотра
файла можно использовать пункт главного меню среды разработки Project|Edit
Option Source. Однако ручное редактирование данного файла не рекомендуется, и
все изменения в данный файл вносятся автоматически при изменении опций проекта
(пункт меню Project|Options или комбинация клавиш Shift-Ctrl-F11), а также при
добавлении и удалении файлов в проект. Для добавления файлов в проект можно
использовать пункт главного меню Project|Add to Project (комбинация клавиш ShiftF11). Для удаления файлов из проекта можно использовать пункт главного меню
Project|Remove from Project.
Листинг 3.1. Файл проекта C++Builder
<?xml version='1.0' encoding='utf-8' ?>
<!-- C++Builder XML Project -->
<PROJECT>
<MACROS>
<VERSION value="BCB.06.00"/>
<PROJECT value="Project1.exe"/>
<OBJFILES value="Project1.obj " С:\Program
Files\Borland\CBuilder6\ Bin\ Unit1.obj""/>
<RESFILES value="Project1.res"/>
<SPARELIBS value="vcl.lib rtl.lib"/>
<PACKAGES value="vcl.bpi rtl.bpi dbrtl.bpi adortl.bpi
vcldb.bpi vclx.bpi vcldbx.bpi ibxpress.bpi cds.bpi dsnap.bpi
bdecds.bpi qrpt.bpi teeui.bpi teedb.bpi tee.bpi dss.bpi teeqr.bpi
visualclx.bpi visualdbclx.bpi bcbsmp.bpi vclie.bpi xmlrtl.bpi inet.bpi
inetdbbde.bpi inetdbxpress.bpi inetdb.bpi nmfast.bpi webdsnap.bpi
bcbie.bpi dclocx.bpi dbexpress.bpi dbxcds.bpi indy.bpi
bcb2kaxserver.bpi dclusr.bpi vclshlctrls.bpi"/>
. . .
</MACROS>
<LINKER>
21
<ALLOBJ value="c0w32.obj $(PACKAGES) Memmgr.Lib sysinit.obj
$(OBJFILES)"/>
<ALLRES value="$(RESFILES)"/>
<ALLLIB value="$(LIBFILES) $(LIBRARIES) import32.lib
cp32mti.lib"/>
<OTHERFILES value=""/>
</LINKER>
<FILELIST>
<FILE FILENAME="Project1.res" FORMNAME=""
UNITNAME="Project1.res" CONTAINERID="ResTool" DESIGNCLASS=""
LOCALCOMMAND=""/>
<FILE FILENAME="Project1.cpp" FORMNAME=""
UNITNAME="Project1" CONTAINERID="CCompiler" DESIGNCLASS=""
LOCALCOMMAND=""/>
<FILE FILENAME="C:\Program
Files\Borland\CBuilder6\Bin\Unit1.cpp" FORMNAME="Form1"
UNITNAME="Unit1" CONTAINERID="CCompiler" DESIGNCLASS=""
LOCALCOMMAND=""/>
</FILELIST>
. . .
</PROJECT>
Исходный файл проекта имеет расширение *.cpp и для графического
приложения содержит функцию WinMain, которая является точкой входа при
запуске приложения. Для просмотра исходного файла проекта можно использовать
пункт главного меню среды разработки Project|View Source. В данном файле
содержится код для начальной инициализации различных подсистем приложения,
код для создания автоматически создаваемых форм приложения, обработка
исключительных ситуаций, а также код для организации цикла обработки
сообщений. Поскольку выполнение приложения начинается именно с функции
WinMain, то в данный файл целесообразно добавить код, который необходимо
выполнить еще до отображения главной формы приложения. Например, обработка
ключей запуска приложения, запрещение запуска второй копии приложения,
отображения окна splash screen во время загрузки приложения. Пример исходного
файла проекта приведен в листинге 3.2.
22
Листинг 3.2. Файл проекта (project1.cpp)
#include <vcl.h>
#pragma hdrstop
USEFORM("С:\Program Files\Borland\CBuilder6\Bin\Unit1.cpp",
Form1);
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(Tform1), &Form1);
Application->Run();
}
catch (Exception &exception)
{ Application->ShowException(&exception); }
catch (...)
{ try
{ throw Exception(""); }
catch (Exception &exception)
{ Application->ShowException(&exception);
}
}
return 0;}
Двоичный файл ресурсов проекта имеет расширение *.res и содержит ресурсы
приложения. По умолчанию в данном файле находится один ресурс типа ICON с
именем MAINICON, содержащий пиктограмму главного окна приложения. Также в
данный файл может быть включена информация о версии приложения. В общем
случае файл ресурсов может содержать различные типы ресурсов, например,
BITMAP, CURSOR, DIALOG, MENU, STRINGTABLE и др. Данный файл создается
автоматически при компиляции приложения и его ручное редактирование
недопустимо. При необходимости включения в проект каких-либо дополнительных
ресурсов необходимо добавить в проект с помощью пункта меню Project| Add to
project дополнительный исходный файл ресурсов с расширением *.rc или
откомпилированный файл ресурсов с расширением *.res. Например, добавить в
файл ресурсов myres.rc пиктограмму из файла “earth.ico” можно следующим
образом:
MYICON ICON "EARTH.ICO"
23
После сборки приложения данная пиктограмма будет храниться в готовом
модуле (exe или dll). В коде приложения доступ к ней можно получить следующим
образом:
Application->Icon->Handle=LoadIcon(HInstance,"MYICON");
Глобальная переменная HInstance в приложениях C++Builder содержит
дескриптор текущего экземпляра приложения. В общем случае данный дескриптор
передается системой в качестве первого параметра функции WinMain.
Для добавления форм в проект можно использовать пункт главного меню
File|New|Form. Каждая форма приложения содержит следующие типы файлов: файл
формы, файл модуля формы, заголовочный файл формы. Файл формы имеет
расширение *.dfm и содержит информацию о компонентах, помещенных на форму.
Начиная с 5-ой версии С++Builder, файл формы имеет текстовый вид, в ранних
версиях использовался двоичный файл формы. Для совместимости с предыдущими
версиями можно вернуться к двоичному файлу, отключив опцию Text DFM в
контекстном меню редактора форм. Файл формы используется редактором форм,
изменения в него вносятся автоматически при проектировании приложения и его
ручное редактирование не рекомендуется. Для просмотра файла формы можно
использовать пункт контекстного меню View as Text редактора форм. Для возврата в
режим редактора формы можно использовать пункт контекстного меню View as
Form. Фрагмент файла формы приведен в листинге 3.3.
Листинг 3.3. Файл формы
object Form1: TForm1
Left = 192
Top = 107
BorderStyle = bsDialog
Caption = 'Test'
object Image1: TImage
Left = 40
Top = 64
Width = 105
Height = 81
end
object Button1: TButton
24
Left = 24
Top = 24
Width = 75
Height = 25
Caption = 'Close'
TabOrder = 0
end
end
Из листинга видно, что файл формы хранит значения свойств и событий
компонент формы, присвоенных им в инспекторе объектов. Это необходимо для
восстановления данных значений при последующих загрузках проекта.
Исходный файл формы имеет расширение *.cpp и содержит реализацию всех
методов класса формы и обработчиков событий. Данный файл также называется
модулем формы или form unit.
Листинг 3.4. Модуль формы
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{}
void __fastcall TForm1::FormCreate(TObject *Sender)
{ date d;
getdate(&d);
Form1->Caption="Сегодня"+IntToStr(d.da_day)+"."+
IntToStr(d.da_mon)+"."+IntToStr(d.da_year);}
В листинге 3.4 приведен фрагмент возможного содержимого файла модуля
формы. В данном примере файл содержит конструктор формы TForm1 и обработчик
события создания формы FormCreate. При генерации в инспекторе объектов новых
обработчиков их определение с пустым телом автоматически добавляется в файл
модуля формы, а задача программиста – в наполнении тела обработчика кодом.
25
Заголовочный файл формы имеет расширение *.h и содержит описание класса
формы (листинг 3.5). Заголовочный файл подключается к исходному файлу формы с
помощью директивы препроцессора #include. Для предотвращения повторного
определения класса код заголовочного файла обрамлен директивами препроцессора
#ifndef Unit1H
#define Unit1H
// код
#endif
По умолчанию в редакторе кода отображается исходный файл формы. Для
переключения между исходным файлом формы и заголовочным файлом можно
использовать пункт контекстного меню редактора кода Open Source/Header File
(комбинация клавиш Ctrl-F6). Необходимо также отметить, что в заголовочном
файле также находится внешнее определение указателя на класс формы.
extern PACKAGE TForm1 *Form1;
Поэтому для использования данной формы из других форм приложения в
исходных файлах этих форм необходимо просто подключить заголовочный файл
формы с помощью директивы препроцессора #include. Аналогичные действия
производятся автоматически при выборе пункта главного меню File| Include Unit Hdr
(комбинация клавиш Alt-F11). Ниже приведен пример исходного файла проекта.
Листинг 3.5. Заголовочный файл формы
#ifndef Unit1H
#define Unit1H
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <Graphics.hpp>
class TForm1 : public TForm
{__published:
// IDE-managed Components
TButton *Button1;
TImage *Image1;
void __fastcall FormCreate(TObject *Sender);
private:
// User declarations
26
public:
// User declarations
__fastcall TForm1(TComponent* Owner);};
extern PACKAGE TForm1 *Form1;
#endif
Содержимое заголовочного файла может формироваться как автоматически
при работе программиста с редактором форм и инспектором объектов, так и самим
программистом с клавиатуры. В приведенном примере видно, что на форму были
добавлены компоненты TButton и TImage, а в инспекторе объектов был создан
обработчик события FormCreate.
Для сохранения файлов проекта можно использовать пункт главного меню
File | Save Project As. Для сохранения файлов формы можно использовать пункт
главного меню File | Save As. При этом можно задать новые имена для файлов
проекта и файлов форм. Для сохранения изменений во всех файлах можно
использовать пункт главного меню File | Save All (комбинация клавиш Shift-Ctrl-S).
Для закрытия проекта можно использовать пункт меню File | Close All.
Для
открытия проекта можно использовать пункт меню File | Open Project (комбинация
клавиш Ctrl-F11).
При
одновременной
разработке
нескольких
связанных
приложений
(например, при разработке клиент/серверных систем) можно использовать группу
проектов. Для добавления новых и существующих проектов в группу можно
использовать пункты главного меню Project | Add New Project и Project | Add Existing
Project соответственно. Выбор текущего проекта можно произвести в менеджере
проектов, доступного через пункт главного меню View | Project Manager
(комбинация клавиш Ctrl-Alt-F11). Файл группы проектов имеет расширение *.bpg и
содержит информацию о проектах, входящих в группу.
В процессе компиляции создаются некоторые временные файлы. Файлы,
расширение которых начинается с символа “~”, являются резервными копиями
соответствующих файлов. Файлы с расширением *.obj объектные файлы
приложения и форм. Файл с расширением *.tds содержит
отладчика и инкрементного линковщика.
27
информацию
для
Кроме
того
существуют
несколько
дополнительных
типов
файлов,
используемых, например, при создании пакетов и библиотек типов. Рассмотрение
данных типов файлов выходит за рамки данного учебного пособия.
Вывод
Проект приложения в среде C++Builder состоит из нескольких файлов,
основными из которых являются: главный файл проекта (с расширением .bpr),
исходный файл проекта (по умолчанию называется project1.cpp), а также для каждой
формы
приложения
файл
формы
(расширение
.dfm),
заголовочный
файл
(расширение .h) и модуль формы (расширение .cpp). Помимо этого в состав проекта
могут входить файлы ресурсов, внешние библиотеки, пакеты. При работе с
проектом система создает также некоторые дополнительные файлы, необходимые
для сборки приложения.
4. Библиотека визуальных компонент С++Builder
4.1. Общие сведения о библиотеке VCL
Визуальная среда проектирования С++Builder, начиная с 6-ой версии,
поддерживает две библиотеки программных компонент. Visual Component Library
(VCL) и Component Library for Cross Platform (CLX). Библиотека VCL
поддерживается во всех версиях Borland Builder и содержит большой набор
компонент, предназначенных для разработки приложений, работающих под
управлением операционных систем семейства Windows. Библиотека CLX является
платформенно-независимой и позволяет разрабатывать приложения, работающие
под управлением операционных систем семейства Windows и Unix. Например,
графическое приложение, разработанное с помощью этой библиотеки, может быть
откомпилировано в среде Builder для работы в операционной системе Windows и
откомпилировано в среде Kylix для работы в графической системе X-Window
операционной системы Linux. Следует отметить, что система Kylix поддерживает
28
лишь ограниченный набор систем семейства Unix. Для систем Unix существуют
другие платформенно-независимые системы, которые поддерживают практически
все операционные системы данного семейства. Одной из таких систем является
система визуального проектирования QT. Кроме того, библиотека CLX не
поддерживает некоторых возможностей, специфичных для приложений Windows,
например, технологии ADO, OLE, COM+. Все эти и некоторые другие технологии
поддерживаются библиотекой
VCL. Поэтому при разработке приложений,
работающих под управлением операционных систем семейства Windows, более
целесообразно использовать библиотеку VCL. В дальнейшем будем рассматривать
компоненты библиотеки VCL.
Имена большинства компонентов VCL начинаются с буквы T, имена классов
исключительных ситуаций начинаются с буквы E. Компоненты являются
дальнейшим развитием классов и объектно-ориентированного программирования. В
отличие от объектов класса компоненты существуют не только на этапе выполнения
приложения, но и на этапе проектирования приложения. Поведение компонент
может различаться на этих двух этапах, например, форма на этапе проектирования
отображается с точечной сеткой и двойной щелчок левой кнопкой мыши на
свободной области формы приводит к переходу в редактор кода. Кроме того, в
дополнение к компонентным данным и компонентным функциям класса в
компонентах появляются свойства и события. Свойства являются дополнительным
интерфейсом для доступа к компонентным данным класса. Они позволяют
устанавливать различные характеристики компонент, такие как название, вид,
размер и т.д. Изменение значений свойств приводит к изменению каких-либо
внутренних данных компонента. Значения свойств могут устанавливаться как на
этапе выполнения, так и на этапе проектирования приложения с помощью
инспектора объектов на закладке Properties. Для того, чтобы свойства были
доступны в инспекторе объектов они должны быть опубликованы. Для этого их
определение в классе должно быть размещено в секции __published. Данная секция
относится к атрибутам видимости полей класса (private, protected, public). На этапе
выполнения работа со свойствами практически ничем не отличается от работы с
компонентными данными класса. Например:
29
TestButton->Caption=”New Text”;
if (TestButton->Color==clButton) TestLabel->Color=clBlack;
Необходимо помнить, что свойство - это интерфейс для доступа к полям
данных, а не само поле данных, поэтому некоторые действия, доступные для
переменных, для свойств недоступны. Например, код Form1->Caption+=”*”
некорректен, а код Form1->Caption= Form1->Caption+”*” корректен.
События
вызываются
компонентом
для
сигнализации
о
каких-либо
изменениях, связанных с состоянием этого компонента. События могут быть
вызваны
действиями
пользователя,
операционной
системой
или
самим
компонентом. Например, щелчок кнопкой мыши на области компонента,
уничтожение компонента, срабатывание таймера и т.д. С каждым событием
прикладной
программист,
использующий
компонент,
может
ассоциировать
функцию-обработчик события. Функция-обработчик должна быть методом какоголибо компонента, обычно формы. При возникновении события будет вызвана
соответствующая функция-обработчик. Разработчик компонента определяет, какие
события будут поддерживаться компонентом и условия их возникновения.
Большинство сообщений операционной системы Windows оформлены в библиотеке
VCL в виде соответствующих событий. Например, сообщения от клавиатуры,
мыши, сообщения Drag-&-Drop, сообщения органов управления Windows и т.д.
Более подробно внутренняя реализация свойств и событий будет рассмотрена в
главе 8. Здесь лишь отметим, что событие на самом деле содержит в себе указатель
на функцию обработчик. В С++Builder определен специальный тип события с
помощью ключевого слова __closure. При этом определяется указатель на метод
класса, дополнительно содержащий указатель на конкретный экземпляр класса.
Например, событие TNotifyEvent описывается следующим образом:
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject*
Sender);
Параметр Sender присутствует для каждого события и содержит указатель на
экземпляр компонента, для которого произошло событие. Кроме этого параметра
события могут содержать и другие специфичные для себя параметры, отвечающие,
например, за код нажатой клавиши. Для назначения обработчиков событиям на
30
этапе проектирования можно воспользоваться инспектором объектов (закладка
Events). Каждый компонент имеет так называемое событие по умолчанию (чаще
всего OnClick). При двойном щелчке левой кнопкой мыши на компоненте на этапе
проектирования происходит переход в редактор кода для определения кода
обработки этого события. На этапе выполнения событие по умолчанию никак не
отличается от остальных событий компонента. Обработчик события может быть
назначен или убран также и на этапе выполнения. Например, в unit1.h:
__published:
void __fastcall StartClick(TObject *Sender);
void __fastcall StopClick(TObject *Sender);
в unit1.cpp
void __fastcall TForm1::StartClick(TObject *Sender)
{// выполнение какого-либо кода
ButtonStartStop->Caption="stop";
ButtonStartStop->OnClick=StopClick;
}
void __fastcall TForm1::StopClick(TObject *Sender)
{// выполнение какого-либо кода
ButtonStartStop->Caption="start";
ButtonStartStop->OnClick=StartClick;
}
В данном примере для кнопки ButtonStartStop попеременно назначаются
различные обработчики для события OnClick, позволяя тем самым этой кнопке
выполнять при нажатии на нее различный код. Один и тот же обработчик может
быть назначен нескольким событиям различных компонентов. Например, при
разработке графической
клавиатурной
панели, у нас есть 10
кнопок
с
соответствующими цифрами. При нажатии на кнопки соответствующая цифра
должна быть добавлена в поле ввода. Поскольку все обработчики события OnCliсk в
этом случае выполняют аналогичный код, целесообразно назначить один
обработчик всем кнопкам. Внутри обработчика определить, для какой именно
кнопки было сгенерировано событие, позволяет параметр Sender. Следует отметить,
что Sender имеет тип указателя на базовый класс библиотеки VCL TObject и может
содержать указатель на объект любого производного класса. В нашем случае
31
указатель на экземпляр класса TButton. Для корректного использования методов и
свойств компонента необходимо выполнить преобразование типов. Для приведения
типов можно использовать статическое или динамическое преобразование типов.
Статическое приведение типов выглядит как обычное приведение типов языка С++
и происходит на этапе компиляции.
void __fastcall TForm1::ButtonkbClick(TObject *Sender)
{ Edit1->Text=Edit1->Text+((TButton *)Sender)->Caption; }
В этом случае соответствие типов проверяется на этапе компиляции и
проверяются только общие формальные правила соответствия. Т.е. Sender,
имеющий тип указателя на класс TObject, может быть преобразован к любому
производному классу от TObject. В результате нет возможности проверить
соответствие типов во время выполнения. Например, Sender может содержать
указатель на экземпляр класса TLabel и будет предпринята попытка преобразовать
его к TButton. В этом случае при вызове каких-либо методов характерных для
класса TButton произойдет ошибка во время выполнения программы, причем
компиляции будет пройдена без ошибок. Поэтому предпочтительней использовать
динамическое преобразование типов, при котором проверка и преобразование типов
происходит на этапе выполнения, а не на этапе компиляции. В C++Builder для
динамического преобразования типов можно использовать специальный оператор
dynamic_cast, который используется следующим образом
dynamic_cast< T > (ptr),
где T это тип класса, к которому производится преобразование указателя ptr. (T
также может быть указателем или void *). При успешном выполнении оператора
возвращается указатель, преобразованный к необходимому типу, в случае
неудачного приведения типов возвращается NULL.
void __fastcall TForm1::ButtonkbClick(TObject *Sender)
{TButton *TempButton;
TempButton=dynamic_cast<TButton *>(Sender);
If (TempButton) Edit1->Text=Edit1->Text+TempButton->Caption; }
Оператор dynamic_cast доступен только при использовании механизма RTTI.
RTTI (Run-Time Туре Identification) это идентификация типов при выполнении
программы. По умолчанию механизм RTTI разрешен в C++Builder. Принудительно
32
запретить его можно, указав -RT- при компиляции, и разрешить, указав -RT. Кроме
того, для каждого класса можно включить RTTI (если он будет по умолчанию
выключен) с помощью ключевого слова __rtti. Механизм RTTI также позволяет
проверять, имеет ли объект некоторый определенный тип, или принадлежат ли два
объекта одному и тому же типу. Оператор typeid определяет фактический тип
аргумента и возвращает указатель на объект класса typeinfo, который этот тип
описывает. Кроме того, для этого класса перегружены операции == и !=, которые
можно использовать для сравнения типов двух объектов.
Следует отметить, что все экземпляры всех компонент в С++Builder могут
быть только динамическими. Т.е. переменная, через которую происходит обращение
к компоненту, должна быть указателем на класс. Для всех компонент, помещенных
на форму, такие переменные будут созданы автоматически, кроме того,
автоматически будет добавлен код для создания и удаления компонент. При
создании компонент во время выполнения приложения все эти действия необходимо
делать вручную, т.е. определить указатель на соответствующий класс и затем
создать экземпляр класса при помощи оператора new. При этом вызывается
конструктор соответствующего класса в качестве параметра, которому необходимо
передать указатель на компонент владелец. Обычно владельцем является форма,
поэтому в любом методе, принадлежащем форме, можно передавать в качестве
этого параметра this:
TLabel *MyLabel;
MyLabel=new TLabel(this);
MyLabel->Parent=MyPanel;
Став владельцем, форма становится ответственной за удаление компонента.
При
своем
закрытии
она
вызовет
деструктор
для
данного
компонента
автоматически. Если вместо владельца указать NULL, то программист сам должен
позаботиться об удалении компонента и в необходимом месте программы вызвать
оператор delete для вызова деструктора: delete MyLabel. Свойство Parent отвечает за
старший или родительский компонент и подробнее будет рассмотрено ниже.
Экземпляры классов, не являющихся компонентами, могут быть созданы как
статически, так и динамически. Например:
33
AnsiString Str1;
Str1=”Static Str”;
AnsiString *Str2;
Str2=new AnsiString();
*Str2=”Dynamic Str”;
ShowMessage(Str1+"\n"+*Str2);
delete Str2;
В С++Builder также существует специальный тип метакласс (metaclass) или
ссылка на тип класса, который определяется следующим образом
class PACKAGE TMetaClass;
typedef TMetaClass* TClass;
Можно определить переменную этого типа, которая будет содержать ссылку
на любой класс и будет использоваться, например, для вызова статических методов
класса. Для стандартных классов доступ к метаклассу можно получить с помощью
ключевого слова __classid(ClassName). Например, __classid(TButton).
4.2. Иерархия классов VCL
Все компоненты VCL находятся в иерархии классов, основу которой
составляют базовые классы. Каждый базовый класс имеет некоторый набор
методов, событий и свойств и специальное назначение. От этих базовых классов
затем наследуются уже реальные классы компонентов и их поведение определено
базовыми классами-предками, находящимися выше них в иерархии. Базовые классы
библиотеки VCL иногда называют абстрактными, однако они не являются
абстрактными классами с точки зрения языка С++. Эти классы не имеют чистых
виртуальных функций и для них могут быть созданы экземпляры классов. Однако
для этих классов нельзя создать полноценно работающие объекты, поскольку в них
содержатся лишь общие характеристики, которые затем должны уточняться и
переопределяться в классах-потомках. Тем не менее, эти классы полезны в качестве
базовых при создании пользовательских компонент, так как содержат общие
характеристики, присущие всем классам данного семейства. Кроме того, указателю
34
на базовый класс может быть присвоен адрес любого объекта производного класса.
Полная иерархия базовых классов библиотеки VCL представлена на рисунке.
Рис. 4.1. Фрагмент схемы иерархии классов библиотеки VCL
Основой иерархий являются классы TObject, TPersistent, TComponent,
TControl, TGraphicControl, TWinControl. TObject является базовым классом для всех
порождаемых классов библиотеки VCL, TComponent является базовым классом для
всех порождаемых компонент. Невидимые компоненты произведены от класса
TComponent. Отображаемые компоненты имеют общего предка TControl, при этом
графические компоненты произведены от класса TGraphicControl, а оконные от
класса TWinControl. Причем компоненты, инкапсулирующие стандартные органы
управления Windows, произведены непосредственно от класса TWinControl, а
оригинальные компоненты от класса TCustomControl. Рассмотрим основные
базовые классы более подробно.
Класс TObject инкапсулирует общее функциональное поведение для всех
классов системы С++Builder, а именно:
- возможность создания, управления и уничтожения экземпляров объекта;
- поддержка информации RTTI об имени и типе объекта;
- поддержка механизма обработки сообщений.
Большинство этих методов предназначены для внутреннего использования
системой С++Builder. Часть методов TObject объявлены как статические (с
ключевым словом static). Таким образом, к ним можно обращаться, не создавая
экземпляр класса, используя лишь имя класса или метакласс. Рассмотрим некоторые
часто используемые методы объекта TObject.
35
ShortString TObject::ClassName(void) - статический метод, который возвращает
строку с именем класса.
TMetaClass
*
TObject::ClassType(void)
-
статический
метод,
который
возвращает указатель на метакласс.
TMetaClass * TObject::ClassParent(void) - статический метод, который
возвращает указатель на метакласс предка (для TObject возвращает NULL).
bool TObject::InheritsFrom(TMetaClass *) возвращает true, если объект является
экземпляром указанного в качестве параметра класса или класса потомка.
Например:
if (Sender->InheritsFrom(__classid(TWinControl)))
ShowMessage(«Экземпляр потомка TWinControl»);
void TObject::Dispatch (void* Message) - виртуальный метод, посылает
сообщение на обработку, вызывая необходимый обработчик. Обработчик ищется по
идентификатору сообщения, расположенному в 2-х первых байтах по адресу,
переданному в качестве параметра. В классах потомках в качестве параметра
передается
указатель
на
структуру
TMessage.
Сначала
просматриваются
обработчики в текущем классе, затем, если обработчик не был найден, - в классах
предках. Если обработчик так и не был найден, вызывается DefaultHandler.
void TObject::DefaultHandler(void* Message) - виртуальный метод, обработчик
сообщения по умолчанию. Для TObject не выполняет никаких действий (для
TWinControl вызывает стандартную API-функцию DefWndProc, которая выполняет
обработку сообщений Windows по умолчанию).
int TObject::Free(void) вызывает деструктор для уничтожения объекта.
Аналогичные действия могут быть выполнены с помощью оператора delete.
Ниже приведен пример, позволяющий вывести диалоговое окно, содержащее
иерархию для объекта переданного в качестве параметра Sender.
void __fastcall TForm1::ControlClick(TObject *Sender)
{AnsiString Str;
TClass tcl;
tcl=Sender->ClassType();
while (tcl)
{ Str+=AnsiString(tcl->ClassName())+"\n";
36
tcl=tcl->ClassParent();
}
ShowMessage(Str);}
Кроме рассмотренных выше методов существуют еще несколько сервисных
методов, использующихся в основном при разработке компонентов. Например,
FieldAddress, MethodAddress, MethodName, InitInstance, FreeInstance, InstanceSize и
т.д. От TObject наследуются в основном простые объекты, которые не являются
компонентами и не нуждаются в поточности и присваивании.
Класс TPersistent является предком всех классов, поддерживающих работу со
свойствами. Являясь потомками TPersistent, производные классы приобретают
способность поточности и присваивания, а именно:
- возможность сохранения и чтения из потока неопубликованных свойств;
- возможность присвоения значений свойствам;
- возможность присвоения данных одного объекта другому.
Запись и чтение из потока производится в формате dfm. Таким образом, под
записью/чтением в поток подразумевается возможность сохранения/загрузки
значений свойств в файл формы. В общем случае поточность предоставляет классам
возможность помещения в память и загрузки из памяти, например, при переносе
компонентов через буфер обмена. Несмотря на то, что поточность появляется,
начиная с класса TPersistent, используют эту возможность в основном компоненты,
т.е. классы, наследованные от TComponent. Большинство методов TPersistent
перегружаются в потомках. Рассмотрим методы этого класса.
void TPersistent::Assign(TPersistent *Source) позволяет данному объекту
присвоить значения свойств и атрибутов другого объекта, заданного в качестве
параметра. Например, Destination->Assign(Source);
void TPersistent::AssignTo (TPersistent* Destination) позволяет присваивать
значения свойств и атрибутов текущего объекта другому объекту, указанному в
качестве параметра. У TPersistent этот метод является виртуальным и защищенным.
Этот метод необходим при разработке новых классов для обеспечения возможности
присваивания их стандартным классам. Если метод Assign объекта A не знает как
выполнить присваивание параметров объекта B, то его метод Assign автоматически
37
вызывает метод AssignTo объекта B. Т.е. если A->Assign(B) не может выполниться
для типа объекта B, то внутри метода Assign вызывается B->AssignTo(A).
void
TPersistent::DefineProperties(TFiler
*Filer)
позволяет
определить
процедуру загрузки и сохранения в потоке неопубликованных свойств. По
умолчанию сохраняются только опубликованные свойства, т.е. объявленные в
секции __published. У TPersistent этот метод является виртуальным и защищенным.
От TPersistent наследуются классы, которые не являются компонентами, но
нуждаются в поточности и присваивании.
Класс
TComponent
является
предком
всех
компонент.
TComponent
определяет общее функциональное поведение для всех компонент системы
С++Builder, а именно:
-возможность помещения на форму из палитры компонент и манипуляции в
окне редактора форм;
-способность владения и обслуживания других компонент;
-специальные характеристики поточности, с которыми может манипулировать
инспектор объектов на этапе проектирования.
На
уровне
TСomponent
проявляется
отношение
"основной
-
вспомогательный". Это отношение распространяется на все компоненты системы
С++Builder. Конструктор TСomponent, в отличие от конструкторов предыдущих
классов, имеет параметр AOwner типа TComponent *, указывающий основной
компонент для создаваемого компонента. Кроме того, TСomponent содержит ряд
свойств, характеризующих отношение "основной - вспомогательный".
Свойство
TComponent*
TComponent::Owner
-
указатель
на
основной
компонент. Доступно только для чтения и только во время выполнения приложения
(ReadOnly, RunTime). Форма является основной для всех помещенных на нее
компонент, приложение Application является основным для всех форм.
int TComponent::ComponentCount содержит количество вспомогательных
компонент для данного компонента (ReadOnly, RunTime).
int TComponent::ComponentIndex содержит индекс данного компонента в
списке основного компонента (ReadOnly, RunTime). Нумерация начинается с 0. При
отображении компоненты с меньшим индексом прорисовываются раньше. Изменить
38
порядок для отображаемых компонент можно с помощью пунктов главного меню
Edit | Bring to Front и Edit | Send to Back, изменяющих также Z-порядок. Для
неотображаемых компонент с помощью пункта главного меню Edit | Creation Order.
TComponent* TComponent::Components[int Index] содержит массив указателей
вспомогательных компонент для данного компонента (ReadOnly, RunTime).
Нумерация начинается с 0. При вызове конструктора компонент помещается в
список Components компонента, указанного в качестве параметра. При вызове
деструктора удаляется из этого списка. Кроме того, можно добавить и удалить
компонент из списка с помощью методов void TComponent::InsertComponent
(TComponent* AComponent) и void TComponent::RemoveComponent (TComponent*
AComponent) соответственно. Внутренняя реализация этого массива представляет
собой два отдельных списка TList для отображаемых и неотображаемых компонент.
Отношение "основной - вспомогательный" отвечает за владение и удаление
компонентов. При удалении компонента сначала вызываются деструкторы всех
вспомогательных компонент. При этом для всех компонент, помещенных на формы
в процессе проектирования, при освобождении обнуляются указатели
на
соответствующие компоненты.
Свойство AnsiString TComponent::Name содержит имя компонента. При
помещении на форму имя компонента назначается автоматически на основе имени
класса. Например, Button1, Button2 и т.д. для класса TButton, это же имя будет
присвоено переменной, содержащей указатель на компонент. Рекомендуется
задавать осмысленные имена для более удобного чтения кода. Например, btnStart,
btnStop и т.д. Для динамически создаваемых во время выполнения компонент имя
может отличаться от имени переменной, допустимо значение NULL. Следует также
отметить, что для отображаемых компонент обычно существуют также свойства
Caption или Text. Они содержат текстовые строки, отображаемые на самом
компоненте (название кнопки, содержимое поля ввода и т.д.). При добавлении
компонента на форму эти свойства содержат значения, совпадающие с Name.
Однако они имеют принципиально другое назначение и используются лишь для
отображения компонента. Например, для нашего примера Caption может быть
«Старт» и «Стоп» для кнопок btnStart и btnStop.
39
TComponent*
TComponent::FindComponent
(const
AnsiString
AName)
возвращает указатель экземпляра компонента с именем, указанным в качестве
параметра. Например:
void_fastcall TForm1::ButtonlClick(TObject *Sender)
{TEdit* EditInstance;
EditInstance = dynamic_cast<TEdit *>(FindComponent("Edit1"));
if (EditInstance) EditInstance->Text="new";}
TComponentState
TComponent::ComponentState
текущее
-
состояние
компонента. Список возможных значений: (csAncestor, csDesigning, csDestroying,
csFixups, csLoading, csReading, csUpdating,csWriting).
TComponentStyle
TComponent::ComponentStyle
-
стиль,
определяющий
поведение компонента (csInheritable, csCheckPropAvail).
int
TComponent::Tag
-
свойство
целого
типа,
которое
не
имеет
предопределенного значения и может содержать любые данные или указатели, по
усмотрению программиста.
TComponent используется в качестве базового класса при создании невидимых
компонент.
Класс TControl является предком всех видимых компонент. Поскольку
элементы TControl обладают способностью отображать себя, некоторые свойства
этого класса оперируют с положением, размером и видом объекта (Top, Left, Width,
Height, BoundsRect, Cursor, Hint), а другие свойства относятся к параметрам
клиентской области (ClientRect, ClientWidth и ClientHeight). TControl также вводит
свойства, устанавливающие видимость, доступность, цвет и шрифт элементов
управления (Visible, Enabled, Color и Font). Свойства Text и Caption обеспечивают
установку редактируемых текстов и названий. Свойство ControlState задает
состояние органа управления (csLButtonDown, csClicked, csPalette, csReadingState,
csAlignmentNeeded,
csFocusing,
csCreating,
csPaintCopy,
csCustomPaint,
csDestroyingHandle, csDocking). Свойство ControlStyle определяет характеристики
органа
управления
(csAcceptsControls,
csCaptureMouse,
csDesignInteractive,
csClickEvents, csFramed, csSetCaption, csOpaque, csDoubleClicks, csFixedWidth,
csFixedHeight, csNoDesignVisible, csReplicatable, csNoStdEvents, csDisplayDragImage,
csReflector, csActionClient, csMenuEvents). Свойство Align задает выравнивание
40
компонента относительно родительского компонента (alNone, alTop, alBottom,
alLeft, alRight, alClient), а свойство Anchor задает привязку к размерам
родительского компонента (множество из akLeft, akTop, akRight, akBottom).
Свойство Constraints позволяет задать максимальные и минимальные размеры
компонента. Метод SetBounds(int ALeft, int ATop, int AWidth, int AHeight) позволяет
изменить положение и размер компонента.
Все визуальные компоненты кроме отношения "основной-вспомогательный"
находятся также в отношении "старший-младший" или "родитель-ребенок". В
аналогичном отношении находятся все органы управления Windows. Поэтому уже у
TСontrol появляются некоторые свойства характеризующие это отношение.
Свойство TWinControl* TСontrol::Parent содержит указатель на родительский
компонент. Родительскими могут быть только оконные элементы управления, и
поэтому они должны быть производным от TWinControl. Сам компонент TControl не
может быть родителем, а может быть только дочерним компонентом. Для формы
значение свойства Parent задается как NULL.
Кроме того, имеются свойства ParentColor, ParentFont и ParentShowHint типа
bool которые определяют, будут ли в качестве значений свойств Color, Font и
ShowHint использоваться значения соответствующих свойств родительского
компонента.
Все
координаты
компонента
задаются
относительно
родительского
компонента, при этом начало координат это левый верхний угол этого компонента
(так называемые локальные координаты). Существуют еще и глобальные
координаты с началом координат в верхнем левом углу экрана. Для пересчета
локальных координат в глобальные и наоборот используются функции TPoint
TControl::ClientToScreen(const TPoint &Point) и TPoint TControl::ScreenToClient(const
TPoint &Point) соответственно.
TControl содержит также методы ответственные за отображение органа
управления.
void TControl::Hide(void) делает текущий элемент управления невидимым, т.е.
устанавливает свойство Visible в false. При этом компонент всего лишь не
41
отображается на экране, он не удаляется из динамической памяти и из списка
вспомогательного компонента, и все его методы и свойства остаются доступными.
void TControl::Show(void) отображает текущий элемент управления, т.е.
устанавливает свойство Visible в true.
void TControl::Repaint(void) перерисовывает текущий элемент управления.
Перерисовка происходит немедленно. При этом, если свойство ControlStyle
содержит значение csOpaque, компонент перерисовывает себя самостоятельно, в
противном случае вызывается метод Invalidate, а затем Update для перерисовки
некоторой видимой части компонента.
void TControl::Refresh(void) вызывает метод Repaint.
void TControl::Invalidate(void) сообщает системе о том, что некоторая область
органа управления должна быть перерисована. Перерисовка произойдет при
обработке сообщения о перерисовке после обработки всех сообщений находящихся
в очереди. Причем Invalidate может быть вызван несколько раз, до того момента как
произойдет непосредственно перерисовка.
void TControl::Update(void) позволяет немедленно обработать сообщение о
перерисовке. Таким образом, если для компонента до этого был вызван метод
Invalidate, это приведет к перерисовке.
Методы void TControl::BringToFront(void) и void TControl::SendToBack(void)
управляют Z-порядком компонента, помещая его на передний или задний план
соответственно.
Метод TControl::WndProc(TMessage &Message) обрабатывает поступившие
органу управления сообщения Message класса TMessage. Адрес этого метода
хранится в свойстве
WindowProc, описанном в секции public. Это позволяет
подменять стандартный обработчик и производить самостоятельную обработку
каких-либо сообщений.
Таким образом, возможность обработки сообщений операционной системы
появляется уже в классе TControl. Этот класс обрабатывает только сообщения от
мыши, но его потомки способны обрабатывать и другие сообщения операционной
системы Windows. В результате обработки сообщений от мыши у класса TControl
появляются
следующие
события:
OnClick,
42
OnDblClick,
OnMouseDown,
OnMouseMove, OnMouseUp. Задав обработчики этих событий, можно определить
реакцию компонента на манипуляции мышью.
Кроме того, в классе TСontrol появляются также свойства, методы и события,
реализующие механизм Drag-and-Drop.
Класс TControl редко используется непосредственно, большинство компонент
являются производными от TWinControl или TGraphicControl.
Класс TWinControl является предком всех оконных элементов управления. С
каждым таким элементом управления связано окно («window») операционной
системы Windows. Производные от TWinControl компоненты обладают тремя
основными характеристиками: они имеют оконные дескрипторы (HWND), способны
принимать фокус ввода и могут являться родителями других элементов управления.
В классе TWinControl полностью определяется отношение "старший-младший", для
этого добавляются следующие свойства.
int TWinControl::ControlCount содержит количество младших элементов
управления для данного компонента (ReadOnly, RunTime).
TControl* TWinControl::Controls[int Index] содержит массив указателей
младших элементов управления для данного компонента (ReadOnly, RunTime).
Нумерация начинается с 0. Компонент добавляется и удаляется из соответствующих
массивов при изменении свойства Parent. Кроме того, для добавления и удаления
компонента в список младших компонентов можно использовать методы void
TWinControl::InsertControl(TControl *AControl) и void TWinControl::RemoveControl
(TControl *AControl).
Метод bool TWinControl::ContainsControl(TControl* Control) определяет,
может ли данный компонент управлять компонентом Control, т.е. является ли
Control младшим для данного компонента непосредственно или косвенно.
TControl*
TWinControl::ControlAtPos(TPoint
&Pos,
bool
AllowDisabled)
возвращает указатель на младший компонент расположенный в заданных
координатах клиентской области данного компонента.
Таким образом, все компоненты С++Builder находятся в отношении
"основной-вспомогательный". Как уже говорилось, это отношение отвечает за
владение компонентами. Когда уничтожается основная компонента, уничтожаются
43
и все вспомогательные компоненты. Для всех компонент, помещенных на форму,
основной является форма, для всех форм основным является приложение
Application, экземпляр класса TApplication. Таким образом, если приложение
завершается, то закрываются и все формы, и, соответственно, уничтожаются все
принадлежащие им компоненты. Причем для форм и приложения есть и обратное
воздействие. Среди всех форм одна должна быть главной, ссылка на нее содержится
в свойстве MainForm объекта Application. Обычно главной формой становится
первая созданная форма. Изменить главную форму можно в опциях проекта на
закладке Forms в поле MainForm. Закрытие главной формы приводит к завершению
приложения, что в свою очередь приводит к закрытию и всех остальных форм.
Следует особо отметить, что при запуске приложения главная форма отображается
независимо от значения свойства Visible. Затем это форма может быть скрыта
обычными способами. Однако программист в случае скрытия главной формы
должен предусмотреть какие-либо варианты завершения приложения, поскольку
закрытие пользователем всех остальных форм не приведет к завершению
приложения. Если все-таки необходимо скрыть главную форму при запуске
приложения, то можно использовать свойство ShowMainForm объекта TApplication.
Все отображаемые компоненты связаны еще отношением "старший-младший".
Старшим компонентом может быть как сама форма, так и какой-либо
группирующий компонент, например TGroupBox. В общем случае старшим может
быть любой производный от TWinControl компонент, например TButton. Отношение
"старший-младший" отвечает за отображение компонентов. Таким образом, если
старший компонент перемещается, то перемещаются и все младшие, если он
скрывается, то скрываются и все младшие, если он становится недоступным, то
недоступны и все младшие. Все координаты компонента также задаются
относительно старшего компонента.
Формы не имеют старшего компонента, и
значение свойства Parent у них равно NULL.
В классе TWinControl также появляются дополнительные характеристики,
отвечающие за отображение компонента.
44
Свойство bool TWinControl::Showing определяет, может ли компонент быть
видимым, т.е. равно ли свойство Visible этого компонента и всех старших
компонент true.
Свойство bool TWinControl::CanFocus определяет, может ли компонент
принять фокус ввода, т.е. равны ли свойства Visible и Enabled этого компонента и
всех старших компонент true.
Свойство bool TWinControl::Focused возвращает значение true, если компонент
находится в фокусе ввода.
Метод void TWinControl::SetFocus(void) активизирует компонент, т.е. передает
ему фокус ввода.
Свойство bool TWinControl::TabStop – определяет, находится ли данный
компонент в TAB-порядке старшего компонента и может ли быть активизирован
при помощи клавиши TAB.
Свойство int TWinControl::TabOrder определяет положение в TAB-порядке
старшего компонента.
Свойство HWND TWinControl::Handle содержит оконный дескриптор объекта
Windows, который инкапсулирует компонент TWinControl. Этот дескриптор может
использоваться в Win32 API функциях для работы с компонентом как с обычным
окном системы Windows.
Кроме метода WndProc класс TWinControl имеет еще невиртуальный метод
void TWinControl::MainWndProc(TMessage &Message), который сообщается системе
Windows как функция окна. Внутри этого метода происходит попытка вызвать
метод, адрес которого хранится в свойстве WindowProc (по умолчанию WndProc), и
в случае неудачи вызывается Appliction->HandleException. В отличие от TControl,
класс TWinControl позволяет обрабатывать любые сообщения Windows. В частности
происходит обработка сообщений от клавиатуры и сообщения связанные с фокусом
ввода, в результате чего у класса TWinControl появляются следующие события:
OnKeyDown, OnKeyPress, OnKeyUp, OnEnter и OnExit.
Метод void TWinControl::Broadcast(void *Message) используется для рассылки
сообщения Message всем младшим элементам управления. Благодаря этому методу
компоненты, наследованные от TControl, получают возможность обработки
45
некоторых сообщений, например, сообщений от мыши, сообщений, связанных с
перерисовкой и т.д.
Большинство компонент не наследуются непосредственно от TWinControl.
Для стандартных органов управления существуют специализированные классы
TButtonControl,
TCustomComboBox,
TCustomEdit,
TCustomListBox
и
т.д.
Дополнительные органы управления наследуются обычно от класса TCustomControl.
Компоненты, производные от класса TGraphicControl, в отличие от
TWinControl, не имеют оконного дескриптора, не имеют функции окна, не могут
принять фокус ввода и не могут являться родителями других элементов управления.
Производные TGraphicControl используются в тех ситуациях, когда необходимо
изобразить на форме текст или графику, не обращаясь к функциональным
возможностям обычных оконных элементов управления. Отметим следующие
достоинства такого подхода. Во-первых, TGraphicControl не пользуется системными
ресурсами Windows, так как не требует оконного дескриптора. Во-вторых, метод
рисования TGraphicControl исполняется немного быстрее за счет того, что
перерисовка компоненты не связана с диспетчеризацией сообщений Windows, а
реализуется процессом рисования, заложенным в родителе данного компонента.
Свойство TCanvas * TGraphicControl::Canvas возвращает указатель на класс
TCanvas, предоставляющий холст для рисования. Класс TCanvas инкапсулирует GDI
возможности, предоставляемые операционной системой Windows. Он содержит
различные свойства и методы, позволяющие производить различные графические
операции. Свойство HDC TCanvas::Handle позволяет получить доступ к контексту
отображения, связанному с клиентской областью компонента. Этот дескриптор
может быть передан Win32 API GDI функциям для работы с контекстом
отображения непосредственно.
Метод void TGraphicControl::Paint(void) вызывается в ответ на сообщение
WM_PAINT,
принимаемое
родительским элементом управления. В классе
TGraphicControl этот метод является защищенным. Метод Paint переопределяется в
потомках и рисует изображение графического элемента управления.
Основными потомками являются TСustomLabel, TShape, TImage.
46
Класс TCustomControl, является непосредственным потомком TWinControl.
Однако подобно TGraphicControl в этом классе добавляются свойство Canvas и
метод Paint. Благодаря этому упрощается процедура рисования изображения
компонента.
Остальные компоненты в иерархии содержат специфичные свойства, методы
и события для различных групп компонентов (например, TButtonControl для кнопок)
и их рассмотрение выходит за рамки данного учебного пособия.
Вывод
Система С++Builder содержит большой набор различных компонент, основная
функциональность которых определена базовыми классами. Компоненты являются
дальнейшим развитием классов, могут существовать как во время выполнения
приложения, так и на этапе проектирования. В дополнении к данным и функциям
класса, компоненты имеют также свойства и события. Все действия, доступные на
этапе проектирования (например, изменение значений свойств и назначение
обработчиков событий), программист может исполнять и на этапе выполнения. Все
компоненты
отображаемые
находятся
в
компоненты
отношении
находятся
"основной-вспомогательный",
в
отношении
все
"старший-младший".
Компоненты TWinControl позволяют обрабатывать любые сообщения Windows.
Любые
функциональные
возможности,
предоставляемые
системой
и
не
реализованные в тех или иных компонентах, могут быть реализованы с помощью
WIN32 API функций.
5. Работа с формами в C++Builder
5.1. Создание и уничтожение форм
Окна составляют основу функционирования практически любого приложения
Windows. Кроме задачи визуального представления приложения, окно в Windows
несет важную функциональную нагрузку – именно функция окна в идеологии работы
47
операционной системы должна обрабатывать все поступающие в окно сообщения.
Поэтому форма системы C++Builder является основой любого приложения. При
создании нового проекта (пункт меню File | New | Application) форма добавляется в
проект автоматически. Большинство приложений используют несколько окон.
Включение в проект новой формы осуществляется командой File | New Form или
командой File | New | Other с последующим выбором формы в Хранилище, например,
из вкладок Forms или Dialogs. По умолчанию все формы создаются автоматически при
запуске приложения, и первая из введенных в приложение форм считается главной
[1]. Главная форма отличается от прочих рядом свойств. Во-первых, именно этой
форме передается управление в начале выполнения приложения. Во-вторых,
закрытие
пользователем
главной
формы
означает
завершение
выполнения
приложения. В-третьих, существуют специальные соглашения о порядке скрытия
главной формы, о них говорилось в главе 4.2.
У программиста существует возможность изменения порядка создания
форм. Он может объявить главной любую из форм, включенных в проект. Создание
всех форм приложения при его запуске зачастую будет очень неэффективным с
точки зрения расходования памяти. Если некоторое окно должно вызываться в
определенный момент работы программы по команде пользователя на короткий срок
(например, диалоговое окно для изменения параметров системы), то хранить это
окно все время работы программы, просто не отображая его на экране, будет очень
неэффективным подходом. Для приложений, работающих в многооконном режиме
(MDI) вообще неизвестно заранее, сколько окон одновременно захочет открыть
пользователь при работе с программой.
Изменить принятые по умолчанию условия относительно форм можно в
окне опций проекта, которое вызывается командой Project | Options главного меню.
В открывшемся окне свойств проекта (Project Options) среди прочих есть закладка
Forms, которая определяет свойства добавленных в проект форм (рис 5.1).
48
Рисунок 5.1 – Окно свойств проекта, закладка «Формы»
В этом окне можно определить, будет ли та или иная форма создаваться
автоматически (в этом случае ее название необходимо поместить в левый список Autocreate forms) или будет доступной для создания уже в момент работы программы (правый
список, Available forms). Здесь же определяется, какая из форм будет считаться главной в
приложении (Main form). Главной может быть только форма из левого списка.
Согласно уже описанной выше технологии двунаправленной разработки,
сделанные в окне свойств проекта изменения приводят к изменению кода программы.
Действительно, если посмотреть на содержимое основного файла проекта (Project1.cpp),
то можно увидеть эти изменения. Во-первых, для каждой добавленной в проект формы
вставляется вызов макроса USEFORM. Назначение этого макроса – подключение файла с
кодом формы к проекту. Вызов макроса для формы Form1, например, будет выглядеть
следующим образом:
USEFORM("Unit1.cpp", Form1);
Первый параметр макроса содержит имя файла модуля, соответствующего
форме (например, "Unit1.cpp"), а второй параметр — имя формы.
Помимо этого, в текст программы добавляются команды создания тех форм,
которые определены в проекте как автоматически создаваемые. Эти команды
реализуются как вызовы метода CreateForm объекта Application:
Application->CreateForm(__classid(TForm4), &Form4);
49
Application->CreateForm(__classid(TForm3), &Form3);
При этом создание главной формы (для нашего примера – Form4) производится
первым. Остальные формы (Form1, Form2) в момент запуска программы не создаются, но
программист может в любой момент создать их, вызвав метод CreateForm. Например, из
файла модуля третьей или четвертой формы можно создать форму Form2 при помощи
следующего вызова:
Application->CreateForm(__classid(TForm2), &Form2);
Такой вызов не сработает, если не предпринять дополнительных действий. Дело в
том, что файлы каждой формы обособлены, и для файла Unit3.cpp, в котором мы
предположительно создаем новую форму, класс TForm2 и указатель Form2 неизвестны, в
связи с чем возникнет ошибка на этапе его компиляции. Решением является
подключение к файлу unit3.cpp заголовочного файла второй формы, то есть добавление
строки
#include “Unit2.cpp”
Эту строку можно ввести в код вручную, а можно воспользоваться специальной
командной оболочки File | Include Unit Hdr, которая добавит эту строку автоматически.
Подобный метод подключения форм друг к другу необходимо использовать и тогда,
когда требуется организовать взаимодействие между формами, то есть когда модуль
одной из форм обращается к свойствам, событиям или методам другой формы.
После создания формы в нее приходит ряд стандартных сообщений Windows,
которые соответствую различным стадиям формирования окна – создание объекта
«окно» в памяти, отображение окна, установка размеров окна, отображение
клиентской области окна. Для формы программы, написанной в C++Builder, эти
сообщения проявляются в виде событий. Перечень событий в порядке их
возникновения в форме можно посмотреть в таблице 5.1.
Таблица 5.1.
События, возникающие при создании формы
Событие
Описание
OnCreate
создание формы и всех ее компонент
OnShow
отображение формы на экране
50
OnActivate
Продолжение таблицы 5.1.
фокус ввода передается форме
OnResize
фокус передается компоненту формы, первому в
последовательности табуляции
переустанавливаются размеры формы
On Paint
прорисовка клиентской области формы
OnEnter
В отличие от остальных событий в таблице, событие OnCreate возникает
однократно для каждой формы. Это происходит в момент ее создания, когда ни
один компонент формы, ни сама она еще не отображены на экране. Этот факт
позволяет программистам в обработчике события
OnCreate осуществлять
начальную настройку как свойств самой формы, так и входящих в нее компонент.
При этом, если свойство OldCreateOrder формы установлена в false, то событие
OnCreate возникнет после окончания работы конструкторов всех компонентов
формы.
Событие OnShow возникает, когда форма отображается на экране либо при
создании, либо при вызове методов Show и ShowModal. Скрыть форму можно
методом Hide, при этом возникает событие OnHide.
В любой момент форму можно закрыть, для этого используется метод Close.
При этом форма не закрывается автоматически, а происходит каскадный вызов
последовательности обработчиков событий, который позволяет программисту
реализовать для пользователя возможность отмены команды закрытия окна.
Первым при обработке команды Close возникает событие OnCloseQuery. Когда
срабатывает это событие, окно остается видимым на экране. Прототип
обработчика этого события выглядит следующим образом:
void __fastcall TForm1::FormCloseQuery(TObject *Sender,
bool &CanClose);
Первый параметр, как и для большинства событий, передает адрес
компонента-источника события. Второй параметр, являясь логическим по типу,
может принимать значения true и false, причем, так как он определен в виде ссылки,
присвоенное ему значение будет выходным. В обработчике события OnCloseQuery
принимается решение – разрешить или нет процедуру закрытия окна. Это решение
может быть принято по текущему состоянию приложения, по запросу разрешения у
51
пользователя, каким либо иным способом, но оформляется оно путем присвоения
значения параметру CanClose: если присвоить ему true, то процесс закрытия окна
разрешается, если false – запрещается. По умолчанию параметру CanClose
присваивается
истинное значение. Следующий
пример
иллюстрирует,
как
реализовать запрос на разрешение закрытия программы у пользователя:
void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool
&CanClose)
{
if(MessageBox(NULL,"Действительно закрыть окно","Внимание!",
MB_OKCANCEL)!=IDOK)
CanClose=false;
}
Если в обработчике события OnCloseQuery разрешено закрытие окна или
этому событию вообще не сопоставлен обработчик, то возникает событие OnClose.
При срабатывании этого события форма по-прежнему отображена на экране,
поэтому отменить закрытие можно и в обработчике OnClose. Прототип обработчика
OnClose выглядит следующим образом:
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction
&Action)
Здесь особого внимания заслуживает параметр Action, который также
является ссылкой, а потому – выходным параметром функции-обработчика.
Параметр Action может принимать следующие значения:
caNone
-
не закрывать форму;
caHide
-
сделать форму невидимой;
caMinimize -
свернуть форму в пиктограмму на панели задач;
caFree
уничтожить форму.
-
Если в обработчике события OnClose присвоить параметру значение caNone,
то процедура закрытия окна будет прекращена, и оно останется на экране в прежнем
виде. В связи с этим пример с подтверждением закрытия окна пользователя можно
перенести в обработчик OnClose:
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction
&Action)
{
if(MessageBox(NULL,"Действительно закрыть окно","Внимание!",
MB_OKCANCEL)!=IDOK)
Action=caNone
}
52
Если параметру Action присвоить значение caHide, то окно станет невидимым,
при этом оно по-прежнему будет находиться в памяти, его свойства, свойства
компонент формы будут доступны. Окно можно сделать видимым, вызвав,
например, метод Show. Необходимо отметить, что параметр Action по умолчанию
принимает значение caHide для всех окон, стиль которых в свойстве FormStyle задан
не как fsMDIForm или fsMDIChild. Поэтому, если необходимо уничтожить такое
окно, для него недостаточно просто вызвать метод Close, потому что окно скроется
на экране, но будет оставаться в памяти. Выходом здесь может стать либо
добавление для формы обработчика события OnClose с обработчиком вида
Action=caFree, либо удаление формы после вызова метода Close стандартной
операцией delete (последнее замечание не относится к главной форме приложения,
которая удаляется при вызове метода Close).
Присвоение параметру Action значения caMinimize приводит к сворачиванию
окна в пиктограмму. Это значение присваивается по умолчанию для дочерних MDIокон.
Если же параметру Action присвоить значение caFree, то будет разрешено
удаление формы. После этого процесс удаления формы становится необратимым,
для формы сработает еще одно событие OnDestroy. В момент возникновения этого
события окно уже невидимо на экране, отменить его уничтожение невозможно,
основное назначение обработчика события OnDestroy – корректное завершение
работы формы, очистка памяти от объектов, ею использовавшихся. Уничтожить
форму можно, минуя последовательность событий OnCloseQuery, OnClose,
OnDestory. Если вызвать для формы метод Free или уничтожать ее операцией delete,
то сразу возникает событие OnDestroy и форма удалится из памяти.
5.2. Модальный режим работы форм
Если отобразить форму методом ShowModal, то она будет работать в
модальном режиме. Модальный режим означает, что весь фокус ввода в
приложении сосредоточен на данной форме, и пользователь не сможет работать с
другими формами приложения, пока не закроет модальное окно. Фактически, для
53
модального окна система создает свою очередь сообщений, и все сообщения
приложения приходят только в это окно. Модальный режим чаще всего
используется для диалоговых окон, в которых задаются параметры работы системы.
Необходимо также отметить, что модальность – это режим работы окна, а не какоето его свойство, которое необходимо определить для него при проектировании.
Одна и та же форма может быть отображена как в нормальном режиме (методом
Show), так и в модальном (методом ShowModal).
Работа модального окна отличается от обычного окна только способом
закрытия окна. За закрытие модального окна отвечает свойство ModalResult.
Первоначально это свойство хранит нулевое значение. Изменение значения этого
свойства (присвоение ему ненулевого значения) приводит к закрытию формы. Всего
для свойства ModalResult предусмотрено 11 различных значений от 0 до 10 (в
C++Builder 6). Перечень этих значений приведен в таблице 5.2.
Таблица 5.2.
Возможные значения свойства ModalResult формы
Константа С++Builder
mrNone
mrOk (idOk)
mrCancel (idCancel)
mrAbort (idAbort)
mrRetry (idRetry)
mrIgnore (idIgnore)
mrYes (idYes)
mrNo (idNo)
mrAll (idAll)
mrNoToAll (idNoToAll)
mrYesToAll (idYesToAll)
Знач.
0
1
2
3
4
5
6
7
8
9
10
Для чего используется
Значение по умолчанию
Форма закрыта нажатием кнопки Ok
Форма закрыта кнопкой Cancel, вызовом метода
Close либо кнопкой закрытия в заголовке окна
Форма закрыта нажатием кнопки Abort
Форма закрыта нажатием кнопки Retry
Форма закрыта нажатием кнопки Ignore
Форма закрыта нажатием кнопки Yes
Форма закрыта нажатием кнопки No
Форма закрыта нажатием кнопки All
Форма закрыта нажатием кнопки NoToAll
Форма закрыта нажатием кнопки YesToAll
Таким образом, присвоение ModalResult значения от 1 до 10 имитирует
нажатие одной из кнопок закрытия окна из стандартного набора Windows. Окно,
создавшее модальную форму, может узнать, каким образом было закрыто окно. Для
этого необходимо сравнить результат, возвращаемый функцией ShowModal, с
54
константами из таблицы 5.2 (этот метод, очевидно, прерывает работу кода до
момента закрытия модального окна):
Application->CreateForm(__classid(TForm2), &Form2);
int mr=Form2->ShowModal();
if(mr==mrOk)
{MessageBox(NULL,"Окно
закрыто
нажатием
кнопки
ОК","Внимание",MB_OK);
//работа с компонентами формы Form2, измененными пользова
//телем при работе с ней в модальном режиме
}
delete Form2;
В коде формы Form2 необходимо в таком случае в тот момент, когда
требуется
закрыть
форму,
выполнить
присваивание
ModalResult=mrOk.
Альтернативным вариантом является использование свойства ModalResult кнопок
типа TButton или TBitBtn. Задав для этого свойства кнопки на форме одно из
значений из таблицы 5.2, программист определит, что при нажатии этой кнопки
окно закроется, и статус закрытия будет совпадать со значением свойства
ModalResult кнопки.
5.3. Многодокументный интерфейс (MDI)
Многие современные приложения поддерживают многооконный режим
работы, когда в каждом окне осуществляется работа с отдельным документом.
Подобный интерфейс имеет устоявшийся протокол взаимодействия окон, когда
одно из окон является главным, а остальные – дочерними. Назначение главного окна
– управлять дочерними окнами, меню этого окна обычно содержит команды
создания, удаления дочерних окон, изменения порядка их расположения в
клиентской области главного окна. Закрытие родительского окна ведет к
завершению работы всего приложения. Дочерние окна предназначены для
отображения редактируемого документа. В помощь программисту для реализации
подобного интерфейса приложения Windows поддерживает спецификацию MDI
(Multiple
Document
поддерживается
Interface).
специальным
На
уровне
набором
55
операционной
API-функций
и
системы
сообщений.
MDI
Для
программиста на C++Builder MDI интерфейс доступен как набор свойств и методов
все того же компонента TForm.
Для того, чтобы объявить форму родительской MDI-формой, необходимо
присвоить ее свойству FormStyle значение fsMDIForm. Для дочерней формы это
свойство необходимо установить в fsMDIChild. Дочернюю форму необходимо
отнести к доступным для создания формам (рис. 5.1), поскольку дочерние окна
должны создаваться пользователем в процессе работы программы. Тогда код
создания дочерней формы должен выглядеть следующим образом:
void __fastcall TForm1::new1Click(TObject *Sender)
{
TForm2 *child=new TForm2(Application);
child->Show();
}
Для управления созданными дочерними окнами можно использовать
несколько свойств и методов. Прежде всего, необходимо обратить внимание на
массив MDIChildren, который для главной MDI-формы хранит адреса всех
существующих на данный момент дочерних окон. При этом количество дочерних
окон можно получить из свойства MDIChildCount. Адрес активной дочерней формы
хранится в свойстве ActiveMDIChild. Пример переименования всех дочерних окон
приведен в следующем листинге:
for(i=0;i<MDIChildCount;i++)
MDIChildren[i]->Caption="Window"+IntToStr(i+1);
Порядок расположения дочерних окон в клиентской области родительского
окна можно изменять вызовами методов Tile и Cascade для главной формы. Метод
Tile выстраивает дочерние окна рядом друг с другом, в зависимости от значения
свойства TileMode они выстраиваются по горизонтали (tbHorizontal) или по
вертикали (tbVertical). Метод Cascade выстраивает дочерние окна каскадом. Для
упорядочивания свернутых в пиктограмму дочерних окон в клиентской области
родительского окна используется метод ArrangeIcons.
Особенностью работы дочерних MDI-окон является то, что при закрытии их
методом Close или нажатием кнопки закрытия окна в его заголовке они по
умолчанию сворачиваются в пиктограмму, а не закрываются. Закрываться они будут
56
только если в обработчике события OnClose дочернего MDI-окна присваивать
параметру Action значения caFree или caHide.
Еще одна особенность взаимодействия родительского и дочерних MDI-окон –
встраивание меню дочернего окна в состав родительского. Если для дочернего окна
спроектировано меню (чаще всего оно включает команды для работы с
документом), то оно будет встроено в меню родительского окна (которое обычно
содержит команды управления окнами). Порядок следования пунктов меню
дочернего окна в родительском меню, а, следовательно, и место встраивания меню
дочернего окна определяется значением свойства GroupIndex каждого раздела меню.
Именно поэтому, если не изменять значения свойства GroupIndex у меню
родительского и дочернего окна, то в процессе работы второе может подменять
собой первое, что является очень распространенной ошибкой начинающих
программистов. Это происходит потому, что их свойствам GroupIndex по
умолчанию присваиваются одинаковые значения, и при появлении дочернего окна
его меню встраивается на те же позиции, которые должны занимать пункты
родительского меню. Задача программиста – следить, чтобы пункты меню
родительского и дочернего окон имели различные значения свойства GroupIndex.
Вывод
Форма является основным компонентом в проекте приложения на C++Builder,
что обусловлено особенной ролью окна в ОС Windows. Формы C++Builder
реализуются на основе класса TForm, который позволяет программисту отслеживать
основные стадии работы окна: создание, отображение, перерисовка, закрытие,
уничтожение, а также управлять окном и его подчиненными элементами. Для форм
предусмотрены модальный и немодальный режимы отображения. Окно в модальном
режиме фокусирует на себя весь ввод информации в приложение. Если приложение
должно работать в многодокументном режиме, то для него можно использовать
специальный MDI-интерфейс, который поддерживается классом TForm.
57
6. Работа с клавиатурой и мышью в среде C++Builder
Любое интерактивное приложение должно реагировать на события от
клавиатуры и мыши. Рассмотрим, каким образом можно работать с клавиатурой и
мышью в приложениях, разработанных в среде C++Builder. Для начала рассмотрим
общую схему аппаратного ввода, реализованную в операционных системах
семейства Windows. Все аппаратные события, связанные с клавиатурой и мышью,
обрабатываются драйверами соответствующих устройств и попадают в системную
очередь аппаратного ввода (system hardware input queue, SHIQ). При запуске
системы создается особый поток необработанного ввода (raw input thread, RIT),
который извлекает очередной элемент из очереди SHIQ и на его основе формирует
соответствующие сообщения Windows. Это сообщения: WM_KEYDOWN – нажата
клавиша на клавиатуре, WM_KEYUP – отпущена клавиша на клавиатуре,
MW_MOUSEMOVE – курсор мыши был перемещен, WM_?BUTTONDOWN –
нажата
кнопка
мыши,
WM_?BUTTONUP
–
отпущена
кнопка
мыши,
WM_?BUTTONDBLCLK – двойное нажатие кнопки мыши (вместо ‘?’ могут быть
буквы L,R и M отвечающие, соответственно, за левую, правую и среднюю кнопку
мыши). Эти сообщения направляются в очереди виртуального ввода (virtualized
input queue, VIQ) нужного потока [3]. Далее они извлекаются в цикле обработки
сообщений приложения и поступают на обработку в соответствующую функцию
окна. В упрощенном виде необходимый поток выбирается следующим образом: для
событий мыши – это поток, создавший окно, над которым находится курсор мыши;
для клавиатуры – поток, создавший окно, с которым пользователь работает в данное
время.
В приложениях, разработанных в среде С++Builder, можно непосредственно
производить обработку соответствующих сообщений WM_*. Однако среда
С++Builder упрощает обработку аппаратного ввода для визуальных компонентов с
помощью механизма событий. Как уже отмечалось выше, все визуальные
компоненты (наследники TControl) имеют возможность обработки событий от
мыши. Все же оконные компоненты (наследники TWinControl) имеют также
58
возможность обработки событий от клавиатуры. Рассмотрим основные из этих
событий более подробно.
6.1. Интерфейс работы с мышью в C++Builder
Событие void TControl::OnMouseMove (__fastcall * (__closure)(TObject*
Sender, TShiftState Shift, int X, int Y)) возникает при перемещении курсора мыши
над органом управления. Принцип работы механизма событий был описан в главе 4.
В
отличие
от
стандартных
событий,
событие
OnMouseMove
имеет
три
дополнительных параметра: Shift, X, Y. Параметры X и Y определяют координаты
курсора мыши в клиентской области объекта Sender. Параметр Shift - это
переменная типа множество, определяющая состояние функциональных клавиш Alt,
Ctrl и Shift, а также кнопок мыши в момент постановки сообщения в очередь.
Множество может содержать различные комбинации следующих флагов: ssShift –
нажата клавиша Shift, ssAlt - нажата клавиша Alt, ssCtrl - нажата клавиша Ctrl, ssLeft
- нажата левая кнопка мыши, ssRight - нажата правая кнопка мыши, ssMiddle нажата средняя кнопка мыши, ssDouble - произведено двойное нажатие кнопки
мыши.
Событие void TControl::OnMouseDown (__fastcall * (__closure)(TObject*
Sender, TMouseButton Button, TShiftState Shift, int X, int Y)) возникает при нажатии
кнопки мыши. Параметры Shift, X,Y аналогичны предыдущему событию. Параметр
Button - переменная типа перечисление, определяет, какая кнопка мыши была
нажата. Он может содержать один из следующих флагов: mbLeft - нажата левая
кнопка мыши, mbRight - нажата правая кнопка мыши, mbMiddle - нажата средняя
кнопка мыши.
Событие void TControl::OnMouseUp (__fastcall * (__closure)(TObject* Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)) возникает при отпускании
кнопки мыши. Параметры аналогичны событию OnMouseDown.
Событие void TControl::OnDblClick (__fastcall * (__closure)(TObject* Sender))
возникает при двойном нажатии левой кнопки мыши над объектом Sender. Данное
событие доступно только для компонентов с установленным флагом csDoubleClicks
59
в
свойстве
ControlStyle.
В
противном
случае
двойное
нажатие
будет
интерпретироваться как два одинарных нажатия кнопки мыши.
Событие void TControl::OnClick (__fastcall * (__closure)(TObject* Sender))
возникает когда пользователь выполняет действие “click” (щелчок) над объектом
Sender. Обычно это происходит при нажатии и отпускании левой кнопки мыши над
компонентом. Однако к аналогичному результату приводят и другие действия
пользователя. Например, для кнопки это нажатие клавиши “Spacebar” если кнопка
находилась в фокусе ввода, для кнопки-переключателя (“CheckBox”) – изменение
значения свойства Checked. Таким образом, данное событие связано не только с
обработкой сообщений от мыши.
6.2. Интерфейс работы с клавиатурой в C++Builder
Событие void TWinControl::OnKeyDown (__fastcall * (__closure)(TObject*
Sender, unsigned short &Key, TShiftState Shift)) возникает при нажатии клавиши на
клавиатуре для компонента, имеющего фокус ввода. Параметр Shift аналогичен
одноименному параметру для событий от мыши. Параметр Key содержит код
нажатой клавиши. Для алфавитно-цифровых клавиш код совпадает с ASCII кодом
соответствующего клавише символа, приведенного к верхнему регистру. Например,
‘S’ , ‘7’ и т.д. Для остальных клавиш используется виртуальный код клавиши,
задаваемый константами VK_*. Например, VK_F1, VK_TAB , VK_RETURN и т.д.
Заметим, что параметр Key передается по ссылке и может быть изменен в
обработчике.
Событие void TWinControl::OnKeyUp (__fastcall * (__closure)(TObject* Sender,
unsigned short &Key, TShiftState Shift)) возникает при отпускании ранее нажатой
клавиши на клавиатуре. Параметры аналогичны предыдущему сообщению.
Событие void TWinControl::OnKeyPress (__fastcall * (__closure)(TObject*
Sender, char &Key)) возникает при нажатии и отпускании алфавитно-цифровой
клавиши на клавиатуре. Параметр Key содержит ASCII символ, сгенерированный в
результате нажатия клавиши. Заметим, что нажатие функциональных клавиш, таких
как Ctrl или F10 не приводит к генерации данного события. Кроме того, параметр
60
Key будет всегда содержать получившийся ASCII код, независимо от способа его
получения. Например, нажатие комбинации клавиш Shift-‘S’ при выключенном
режиме CapsLock или клавиши ‘S’ при включенном приведут к генерации этого
события с ASCII кодом ‘S’. Причем нажатие клавиши Shift не приведет к генерации
OnKeyPress. Аналогично предыдущим событиям, параметр Key передается по
ссылке и может быть изменен в обработчике. Таким образом можно, например,
производить фильтрацию ввода и ограничить набор доступных для ввода символов.
При обработке событий от клавиатуры для формы есть один нюанс.
Напомним, что события от клавиатуры генерируются именно для того компонента,
который имеет фокус ввода. Таким образом, клавиатурные события для формы
будут сгенерированы только в том случае, если ни один компонент на ней не имеет
фокуса ввода. Однако существует специальное свойство KeyPreview компонента
TForm. Если оно содержит значение true, то все клавиатурные события будут
сначала сгенерированы для формы, владеющей компонентом с фокусом ввода, а
потом повторно сгенерированы для этого компонента.
6.3. Пример использования событий мыши и клавиатуры в C++Builder
Рассмотрим небольшой пример, показывающий практическое применение
всех рассмотренных выше событий. Форма содержит компонент TestStringGrid типа
TStringGrid, для которого определены обработчики OnMouseDown, OnMouseUp и
OnMouseMove. Обработчики этих событий реализуют возможность обмена
содержимого ячеек. Для этого необходимо, нажав клавиши Ctrl и Alt и удерживая
правую кнопку мыши, перетащить одну ячейку решетки на место другой. Также на
форме содержится компонент TestMemo типа TMemo, для которого определен
обработчик OnKeyDown. В данном обработчике реализованы следующие действия:
удаление текущей строки при нажатии комбинации Ctrl-Y, выделение всего текста
при нажатии комбинации Ctrl-A и изменение типа выравнивания текста при
нажатии клавиши F2. На форме содержится компонент TestEdit типа TEdit, для
которого определен обработчик OnKeyPress. Данный обработчик позволяет вводить
в поле ввода только цифры и буквы для шестнадцатеричного представления числа.
61
Кроме того, ввод возможен как в русской, так и в английской раскладке и в любом
регистре. Во всех случаях ввод будет преобразован в прописные латинские
символы. А для самой формы определен обработчик OnKeyUp, который позволяет
завершить работу приложения при нажатии комбинации клавиш Ctrl-Alt-Q.
Свойство KeyPreview должно быть установлено в true. Ниже приведен исходный
код данного примера.
Листинг 6.1. Использование событий мыши и клавиатуры в приложениях
//*************testinput.h *******************
typedef struct TTr
{char from;
char to;
} *PTr;
class TTestForm : public TForm
{__published:
// IDE-managed Components
TStringGrid *TestStringGrid;
TMemo *TestMemo;
TEdit *TestEdit;
void __fastcall FormCreate(TObject *Sender);
void __fastcall TestStringGridMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall TestStringGridMouseMove(TObject *Sender,
TShiftState Shift, int X, int Y);
void __fastcall TestStringGridMouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y);
void __fastcall TestEditKeyPress(TObject *Sender, char &Key);
void __fastcall TestMemoKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift);
void __fastcall FormKeyUp(TObject *Sender, WORD &Key,
TShiftState Shift);
private:
// User declarations
bool Moving;
TPoint MovedCell;
TTr TrChar[28];
TAlignment algn[3];
public:
// User declarations
62
__fastcall TTestForm(TComponent* Owner);
};
extern PACKAGE TTestForm *TestForm;
//*************testinput.cpp *******************
TTestForm *TestForm;
__fastcall TTestForm::TTestForm(TComponent* Owner) : TForm(Owner)
{ int i;
Moving=false;
MovedCell.x=0;MovedCell.y=0;
algn[0]=taLeftJustify;
algn[1]=taCenter;
algn[2]=taRightJustify;
for (i='a';i<='f';i++)
TrChar[i-'a'].from=i;
for (i='A';i<='F';i++)
{TrChar[i-'A'].to=i;TrChar[i-'A'+6].to=i;TrChar[i-'A'+12].to=i;}
TrChar[6].from='ф';TrChar[12].from='Ф';
TrChar[7].from='и';TrChar[13].from='И';
TrChar[8].from='с';TrChar[14].from='С';
TrChar[9].from='в';TrChar[15].from='В';
TrChar[10].from='у';TrChar[16].from='У';
TrChar[11].from='а';TrChar[17].from='А';
for (i='0';i<='9';i++)
{TrChar[i-'0'+18].from=i;TrChar[i-'0'+18].to=i;}
}
void __fastcall TTestForm::FormCreate(TObject *Sender)
{ randomize();
TestStringGrid->FixedCols=0;TestStringGrid->FixedRows=0;
for (int i=0 ;i<TestStringGrid->RowCount;i++)
for (int j=0 ;j<TestStringGrid->ColCount;j++)
TestStringGrid->Cells[i][j]=rand() %100;
}
void __fastcall TTestForm::TestStringGridMouseDown(TObject
*Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{ if (Button==mbRight && Shift.Contains(ssCtrl) &&
Shift.Contains(ssAlt))
63
{ int tx,ty;
TGridRect tgr;
Moving=true;
TestStringGrid->MouseToCell(X,Y,tx,ty);
MovedCell.x=tx;MovedCell.y=ty;
tgr.Left=tx;tgr.Top=ty;tgr.Right=tx;tgr.Bottom=ty;
TestStringGrid->Selection=tgr;
}
}
void __fastcall TTestForm::TestStringGridMouseMove(TObject
*Sender, TShiftState Shift, int X, int Y)
{ if (Moving) TestStringGrid->Cursor=crHandPoint; }
void __fastcall TTestForm::TestStringGridMouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{ if (Moving && Button==mbRight)
{ int tx,ty;
AnsiString tstr;
Moving=false;
TestStringGrid->Cursor=crDefault;
TestStringGrid->MouseToCell(X,Y,tx,ty);
tstr=TestStringGrid->Cells[tx][ty];
TestStringGrid->Cells[tx][ty]=TestStringGrid->
Cells[MovedCell.x][MovedCell.y];
TestStringGrid->Cells[MovedCell.x][MovedCell.y]=tstr;
}
}
void __fastcall TTestForm::TestEditKeyPress(TObject *Sender, char
&Key)
{int i,n;
n=sizeof(TrChar)/sizeof(TrChar[0]);
for (i=0;i<n;i++)
if (Key==TrChar[i].from) {Key=TrChar[i].to;break;}
if (i==n)
{for (i=0;i<n;i++)
if (Key==TrChar[i].to) break;
if (i==n) Key=0;}}
64
void __fastcall TTestForm::TestMemoKeyDown(TObject *Sender, WORD
&Key, TShiftState Shift)
{static int i=1;
if (Shift.Contains(ssCtrl))
{if (Key=='Y')
else if (Key=='A')
TestMemo->Lines->Delete(TestMemo->CaretPos.y);
TestMemo->SelectAll(); }
if (Key==VK_F2) TestMemo->Alignment=algn[i++%3];
}
void __fastcall TTestForm::FormKeyUp(TObject *Sender, WORD &Key,
TShiftState Shift)
{ if (Key=='Q' && Shift.Contains(ssAlt) &&
Shift.Contains(ssCtrl))
Application->Terminate();}
Вывод
Среда С++Builder предоставляет для программиста возможность обработки
большинства сообщений, отвечающих за аппаратный ввод в системах семейства
Windows. Эти сообщения доступны программисту в виде соответствующих
событий, упрощающих работу с клавиатурой и мышью. Кроме того, программист
при необходимости может дополнительно производить обработку стандартных
сообщений Windows от клавиатуры и мыши для реализации каких-либо
специфичных функций приложения.
7. Графика в C++Builder
7.1. Основы графического интерфейса Windows
В операционной системе Windows за графический интерфейс отвечает группа
специализированных функций API, имеющая общее название GDI (Graphic Device
Interface) [6]. Эти функции отвечают за отображение графических примитивов на
устройстве отображения (окружности, линии, прямоугольники и т.п.), настройку
основных инструментов отображения (цвет, тип линий, шрифт), получение
65
информации об устройстве отображения. Располагаются функции GDI в файлах
gdi.dll и gdi32.dll. Основным достоинством GDI является то, что посредством него
унифицируется работа с различными устройствами отображения. Это означает, что
вывод графической информации на экран монитора, принтер или плоттер
выполняется в приложении Windows совершенно единообразно, а особенности того
или иного устройства отображения учитываются уже самим модулем GDI в диалоге
с
драйвером
устройства.
Подобная
универсальность
достигается
за
счет
использования контекста отображения для устройства. Контекст отображения
представляет собой структуру данных, описывающую основные характеристики
устройства, с которым он связан (размер, тип, разрешение, используемая система
координат и др.). Таким образом, для вывода информации на то или иное
устройство отображения (в том числе – в окно приложения), программист должен
получить его контекст отображения и уже на него осуществлять вывод.
Взаимодействие прикладной программы и устройства отображения в Windows
показано на рис. 7.1.
Прикладная
программа
Контекст
отображения
Драйвер
устройства
Устройство
отображения
Рис. 7.1. Порядок вывода графической информации в Windows
Еще одна важная функция GDI – работа с инструментами рисования. Для
отображения
графической
информации
в
Windows
используется
большое
количество инструментов – кисти, перья, шрифты, битовые изображения, регионы.
Для того, чтобы использовать при выводе, например, шрифт, отличный от
стандартного, программист должен создать этот шрифт специальной GDI-функцией
и выбрать этот шрифт в контекст отображения. После этого функции вывода текста
будут использовать именно этот шрифт для формирования изображения. В листинге
7.1 приведен пример вывода информации в окно Windows с использованием
функций GDI.
Листинг 7.1. Пример работы с функциями GDI
HDC hdc;HPEN hPen;HFONT hFont;
hdc=GetDC(hWnd);
//Получить контекст окна с дескриптором hWnd
66
hPen=CreatePen(PS_SOLID,4,RGB(255,0,0));
SelectObject(hdc,hPen);
//создать перо
//выбрать перо в контекст отображения
Ellipse(hdc,100,100,200,200);
//нарисовать эллипс
hFont=CreateFont(20,10,0,0,FW_THIN,TRUE,TRUE,FALSE,ANSI_CHARSET,
OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,NULL);
//создать шрифт
SelectObject(hdc,hFont);
//выбрать шрифт в контекст отображения
TextOut(hdc,150,150,"HELLO",5);
ReleaseDC(hWnd,hdc);
//отобразить текст
//освободить контекст отображения
DeleteObject(hPen); DeleteObject(hFont);//уничтожить GDI-объекты
7.2. Обзор графических компонент C++Builder
Приведенный в листинге 7.1 фрагмент программы иллюстрирует проблемы
программирования с использованием API: громоздкость кода для выполнения даже
простых действий, необходимость управления системными ресурсами, высокая
требовательность к квалификации программиста. С++Builder предлагает ряд
компонент для отображения графической информации. Достоинства этих компонент
заключаются в том, что всю низкоуровневую работу вроде получения и
освобождения контекстов отображения, создания и удаления тех или иных
инструментов
рисования
они
возможность
сосредоточиться
берут
на
на
себя,
процессе
предоставляя
формирования
программисту
нужного
ему
изображения.
Перечень основных графических компонент и их место в общей схеме
иерархии классов VCL приведен на рис.7.2 (графические компоненты выделены).
Графические компоненты С++Builder охватывают различные аспекты работы с
графикой. Компонент TCanvas предоставляет пользователю удобный интерфейс для
доступа к функциям GDI. Не являясь самостоятельным компонентом, объект типа
TCanvas используется как свойство других компонент (например, TForm или
TPrinter), заменяя собой контекст отображения. Таким образом, рисуя на канве,
программист формирует изображение на том компоненте, которому данная канва
принадлежит. Компонент TGraphic
и производные от него классы позволяют
67
TObject
TPersistent
TInerfacedPersistent
TGraphic
TCanvas
TPicture
TBitmap TIcon TMetaFile TJPEGImage
Рис.7.2. Фрагмент схемы иерархии VCL с графическими компонентами
работать с растровыми изображениями некоторого ресурса (битового изображения,
метафайла, файла в формате jpeg). Компонент TPicture представляет собой
контейнер для хранения графических изображений, представленных компонентами
типа TGraphic. Таким образом, TPicture выступает как объект, в котором могут
храниться битовый образ, пиктограмма или метафайл. Сам же объект типа TPicture
может быть свойством другого компонента (например, TImage), представляя, таким
образом, универсальный способ представления графики для своего владельца.
7.3. Компонент TCanvas
Компонент
TCanvas
представляет
удобный
объектно-ориентированный
интерфейс с функциями GDI. При этом он не требует от программиста знания
нюансов реализации графического интерфейса Windows, его методы и свойства
позволяют непосредственно выводить информацию или задавать параметры вывода.
Для
опытных
пользователей
канва
предоставляет
дескриптор
контекста
отображения как одно из своих свойств, что дает возможность использовать любые
функции GDI.
Основные свойства и методы компонента TCanvas приведены в таблицах 7.1 и
7.2.
68
Таблица 7.1.
Свойства компонента TCanvas
Свойство
Назначение
Pen
Перо. Предназначено для установки параметров отображения
линий (цвет, стиль, толщина). Экземпляр класса TPen
Кисть. Предназначено для установки параметров заливки
объектов (цвет, текстура). Экземпляр класса TBrush
Шрифт. Определяет параметры выводимого текста (цвет,
размер, стиль). Экземпляр класса TFont
Текущая позиция пера. Используется как опорная точка в
некоторых функциях вывода (MoveTo, LineTo)
Определяет прямоугольный регион отсечения при выводе.
Отрисовка осуществляется только внутри этого прямоугольника
Массив цветов точек канвы. Представляет собой двумерный
массив элементов типа TColor, каждый из которых Pixels[x,y]
хранит цвет точки с координатами (x,y) холста (канвы)
Дескриптор контекста отображения устройства вывода, с
которым связана канва
Определяет способ наложения рисунков при использовании
метод CopyRect
Таблица 7.2.
Brush
Font
PenPos
ClipRect
Pixels
Handle
CopyMode
Методы компонента TCanvas
Методы
Назначение
MoveTo
Изменяет позицию пера
LineTo
Рисует линию от позиции пера к указанной точке
Arc,
Ellipse, Отображают графические примитивы (дуга, эллипс,
Rectangle,
Pie, прямоугольник, вырезанный эллипс, срезанный эллипс)
Chord
Polygon, Polyline
Рисуют ломаные по координатам точек, Polygon замыкает
ломаную и закрашивает полученную фигуру, Polyline - нет
TextOut,
Выводят текст. TextOut- в указанную точку холста,
TextRect
TextRect- в рамках заданного прямоугольника
TextHeight,
Позволяют определить какой размер (высоту, ширину и
TextWidth,
прямоугольную область соответственно) займет текстовая
TextExtent
строка при выводе для текущего шрифта
FloodFill
Закрашивает некоторую область канвы текущей кистью.
Область либо ограничена определенным цветом, либо сама
имеет заданный цвет
69
Продолжение таблицы 7.2
CopyRect
Draw, StrethDraw
Копирует прямоугольный фрагмент с одной канвы на
другую
Рисует графический ресурс (битовый образ, пиктограмму,
метафайл) на канве. Draw – в реальных размерах,
StretchDraw – с масштабированием
После обзора методов и свойств можно переписать листинг 7.1 средствами
C++Builder. Это возможно благодаря тому, что компонент TForm имеет свойство
Canvas, представляющее собой объект класса TCanvas. Получившийся код
представлен в листинге 7.2.
Листинг 7.2. Пример работы с методами и свойствами канвы
Canvas->Pen->Color=RGB(255,0,0);
Canvas->Pen->Width=4;
Canvas->Ellipse(100,100,200,200);
Canvas->Font->Height=20;
Canvas->Font->Name="Courier";
Canvas->Font->Style=Canvas->Font->Style<<fsItalic;
Canvas->TextOut(150,150,"Hello");
Отличия вывода с использованием канвы очевидны – в листинге не создаются
объекты GDI, такие как перо, шрифт, не надо получать контекст отображения, все
команды направлены только на вывод информации в том виде, как это требуется
программисту. Однако наличие в канве свойства Handle позволяет программисту
использовать и функции GDI в тех случаях, когда нужный метод в компоненте
отсутствует. Так, например, вызов метода канвы TextOut можно заменить вызовом
GDI-функции TextOut:
TextOut(Canvas->Handle,150,150,"Hello",5);
При формировании изображения на канве формы необходимо учитывать
особенности работы Windows. В связи с тем, что операционная система работает в
многооконном режиме, на экране происходят перекрытия окон, разворачивание,
сворачивание окон, что, очевидно, влияет на изображение в окне. Поэтому,
отобразив один раз что-либо в окне, например, в ответ на событие от мыши или
таймера, нельзя быть уверенным в том, что это изображение останется в окне до
70
момента его закрытия. При обновлении изображения окна, которое происходит при
таких операциях, как разворачивание окна, изменение его размеров, перекрытие
одного окна другим, содержимое окна обновляется, при этом требующая
перерисовки часть окна по умолчанию закрашивается текущей кистью фона.
Проблему иллюстрирует рис. 7.3. Если сформировать на форме некоторое
изображение (рис.7.3а), а потом перекрыть часть окна формы другим окном
(рис.7.3б), то при закрытии второго окна Windows определит, что нашей форме
в)
б)
а)
Рис. 7.3. Потери изображения на форме при перерисовке
нужно восстановить испорченную
часть, но по умолчанию эта часть просто
закрашивается фоновой кистью, в результате изображение теряет целостность
(рис.7.3в). Исправить ситуацию возможно, создав обработчик события OnPaint для
формы. Событие OnPaint возникает всякий раз, когда операционная система решает,
что форме необходима перерисовка (изменились размеры окна, часть окна
появилась из-под другого и т.п.). Программист должен в обработчике события
OnPaint формировать полное изображение на форме в том виде, как он хотел бы его
видеть в данный момент. К обработчику события OnPaint можно обратиться и
программно. Метод Repaint приводит к незамедлительному вызову обработчика
события OnPaint и перерисовке изображения на форме. Частый вызов метода Repaint
может существенно замедлить работу приложения. Метод Invalidate объявляет всю
клиентскую
область
формы
как
требующую
перерисовки,
что
заставить
операционную систему сгенерировать сообщение WM_PAINT в тот момент, когда
нет других событий для обработки. Сообщение WM_PAINT приведет к появлению
события OnPaint.
Канвой обладают не только формы. Свойство Canvas присутствует у таких
компонентов, как TPrinter, TBitmap, TImage, TToolbar. Это означает, что
71
графическое изображение, хранящееся в этих компонентах или составляющее их
внешний вид, можно изменять, используя описанные в таблицах 7.1 и 7.2 свойства и
методы.
7.4. Компоненты для работы с изображениями
Операционная
система
Windows
содержит
встроенные
средства
для
поддержки растровых изображений в формате BMP. Другие популярные форматы
(JPEG, GIF, PNG) требуют предварительного преобразования в обычный растр
перед обработкой или отображением. Это требует детального знания данных
форматов и достаточно больших усилий программиста. Однако используя
специальные компоненты VCL можно значительно упростить процедуры обработки
изображений в различных форматах. Для работы с DDB и DIB-растрами можно
использовать
компонент
TBitmap,
с
JPEG-форматом
–
TJPEGImage,
с
изображениями в формате метафайла – TMetaFile, с GIF-форматом – TGIFImage (не
входит в стандартную поставку C++Builder 6.0).
Компонент TBitmap инкапсулирует растровое изображение Windows [4] как в
формате DDB (аппаратно-зависимый растр), так и в формате DIB (аппаратнонезависимый растр). Благодаря наличию свойства Canvas программист имеет
полный доступ к любой точке растра (массив Pixels или свойство ScanLine), а также
возможность отображать растр или его фрагмент на другой канве (методы Draw,
StretchDraw, CopyRect). Для управления палитрой растра используется свойство
Palette. Компонент TBitmap поддерживает также методы загрузки/сохранения растра
в стандартном буфере обмена, файле, внешнем потоке, ресурсах приложения.
Компонент TBitmap используется в визуальных компонентах С++Builder как
свойство Bitmap компонента TPicture. TPicture выступает в этом случае как
контейнер, хранящий графику одного из поддерживаемых форматов (bitmap, icon,
metafile). Свойство Picture имеют такие компоненты, как TImage, TDBImage,
TControlBar. Листинг 7.3 представляет пример работы со свойствами и методами
компонента TBitmap.
Листинг 7.3. Работа с изображением с помощью TBitmap
72
Graphics::TBitmap *bm=new Graphics::TBitmap;//Создаем объект TBitmap
if(OpenDialog1->Execute())//Открываем файл в стандартном диалоге
{bm->LoadFromFile(OpenDialog1->FileName);
//загружаем
картинку
из
//файла в Bitmap
bm->Canvas->TextOutA(0,bm->Height-20,"Copy");//Изменяем
растр
в
//памяти (выводим текст в левом нижнем углу)
Canvas->Draw(20,20,bm); //копируем битовый образ на форму
}
delete bm;
Метод Draw позволяет отобразить некоторую графику (объект класса-потомка
TGraphic) на канве. В данном примере содержание растра bm отображается на канве
формы в позиции (20,20). Если вызов метода Draw заменить на
Canvas->StretchDraw(ClientRect,bm);
то содержимое растра будет премасштабировано до размеров указанного в первом
параметре прямоугольника (в данном случае – до размеров клиентской области
окна).
Используя массив Pixels, можно получить цвет любой точки растра, а также
изменить его. Например, первую строку растра можно закрасить белым цветом
следующим образом:
for(int i=0;i<bm->Width;i++)
bm->Canvas->Pixels[i][0]=clWhite;
Для работы с изображениями в формате JPEG предназначен компонент
TJPEGImage. Его свойства и методы позволяют загружать и сохранять jpegизображения, преобразовывать их в обычный Bitmap, управлять качеством сжатия.
Методы Assign и DIBNeeded позволяют преобразовать изображение из JPEG в BMP
формат. Для работы с файлами, потоками, буфером обмена используются методы
LoadFromFile,
LoadFromStream,
LoadFromClipboardFormat,
SaveToFile,
SaveToStream, SaveToClipboardFormat. На качество и производительность данных
JPEG влияют свойства: CompresionQuality (степень потери качества при сжатии),
Performance (скорость распаковки), Scale (относительный размер изображения при
распаковке). В листинге 7.4 приведен пример работы с JPEG-изображениями в
С++Builder.
Листинг 7.4. Пример работы с изображением в формате JPEG
73
#include <jpeg.hpp>
if (OpenDialog1->Execute())
{TJPEGImage *ji=new TJPEGImage();
ji->LoadFromFile(OpenDialog1->FileName);
ji->DIBNeeded();
Image1->Height=ji->Height;Image1->Width=ji->Width;
Image1->Picture->Bitmap->Assign(ji);
delete ji;}
Вывод
Графический интерфейс в ОС Windows реализуется подсистемой GDI, работа
с функциями которой требует высокой квалификации программиста. C++Builder
предлагает программисту набор графических компонент, которые существенно
упрощают графический вывод информации, предоставляя свойства и методы для
формирования изображений, а также для работы с графическими файлами
различных форматов. При этом графические компоненты C++Builder оставляют
возможность работы c API-функциями GDI. Все это обеспечивает эффективность и
гибкость работы с устройствами графического вывода информации.
8. Разработка компонент в C++Builder
Как уже было отмечено, среда С++Builder содержит большое количество
стандартных
Программисты
компонент,
могут
позволяющих
расширять
упростить
разработку
функциональность
системы
приложений.
благодаря
возможности создания собственных пользовательских компонент. Созданные
компоненты могут быть помещены в палитру компонент, для них могут быть
созданы редактор компонента, редакторы свойств, а также файл помощи. Таким
образом, дальнейшая работа с созданным компонентом ничем не отличается от
работы со стандартными компонентами. Созданные компоненты могут быть
объединены в пакеты для упрощения их установки. Существует большое количество
коммерческих и свободно распространяемых дополнительных пакетов компонент.
Кроме того, программисты могут сами создавать компоненты для упрощения
74
дальнейшей разработки приложений. Рассмотрим процесс создания компонент
более подробно.
8.1. Процедура разработки компонента C++Builder
При разработке нового компонента необходимо наследовать его от какоголибо стандартного класса в соответствии с принципами наследования ООП. Как уже
было отмечено, все компоненты должны быть наследованы как минимум от класса
TComponent. Процедура создания и установки компонентов в среде С++Builder
автоматизирована. Для автоматической генерации заготовки нового компонента
можно выбрать пункт главного меню
Component | New Component. Далее в
появившемся окне список выбора “Ancestor Type” позволяет выбрать имя класса, от
которого будет наследован компонент. В поле “Class Name” указывается имя
будущего класса. В соответствии с нотацией, принятой в среде С++Builder, имена
классов должны начинаться с заглавной буквы “T”. В поле “Palette Page”
указывается закладка, на которой будет располагаться компонент. В поле “Unit file
name” указывается полный путь и имя файла, который будет содержать новый
класс. После нажатия на кнопку “ОК” по указанному пути будет сгенерировано два
файла c расширениями *.cpp и *.h и именами, совпадающими с именем класса без
начальной буквы “T”. Кроме того, будет отображено окно редактирования этих
файлов. В самих файлах уже будет содержаться код для определения класса и
инсталляции компонента. Программист должен добавить в этот код все
необходимые функциональные
возможности нового компонента. Допустим, мы
выбрали в поле “Ancestor Type” класс TComponent, в поле “Class Name” –
TOurComponent, в поле “Palette Page” – OurSample, тогда будет сгенерирован файл
OurComponent.h
#ifndef OurComponentH
#define OurComponentH
#include <SysUtils.hpp>
#include <Classes.hpp>
class PACKAGE TOurComponent : public TComponent
{private:
75
protected:
public:
__fastcall TOurComponent(TComponent* Owner);
__published:};
#endif
Вместе с заголовочным файлом будет создан файл OurComponent.cpp
#include <basepch.h>
#pragma hdrstop
#include "OurComponent.h"
#pragma package(smart_init)
static inline void ValidCtrCheck(TOurComponent *)
{
new TOurComponent(NULL); }
__fastcall TOurComponent::TOurComponent(TComponent* Owner)
: TComponent(Owner){ }
namespace Ourcomponent
{ void __fastcall PACKAGE Register()
{TComponentClass classes[1] = {__classid(TOurComponent)};
RegisterComponents("OurSamples", classes, 0);}}
Как видно из приведенного примера создается заготовка для конструктора, а
также определяются две функции ValidCtrCheck и Register. Функция ValidCtrCheck
необходима для проверки наличия в создаваемом компоненте чистых виртуальных
функций. Данная функция будет вызвана при установке компонента. В ней просто
производится попытка создания экземпляра компонента, что в случае наличия
чистой виртуальной функции приведет к генерации ошибки и отмене инсталляции.
Функция Register регистрирует компонент в палитре компонентов. В первом
параметре указывается имя закладки в палитре компонент, второй - параметрмассив указателей на метаклассы регистрируемых компонент, третий содержит
количество регистрируемых компонентов минус 1.
Для
компонента
может
быть
создана
пиктограмма,
которая
будет
отображаться в палитре компонентов. Для этого необходимо использовать Image
Editor из пункта главного меню Tools. В этом редакторе необходимо выбрать пункт
File | New | Component Resource File (.dcr), а затем пункт Resource | New | Bitmap и
задать размер 24х24 пиксела. Необходимо переименовать созданный Bitmap, назвав
его так же, как и будущий класс компонента, но заглавными буквами. Для нашего
76
случая это будет имя TOURCOMPONENT. Далее можно нарисовать необходимый
рисунок, учитывая, что цвет левого нижнего квадрата будет считаться прозрачным.
Сам файл ресурса необходимо сохранить в том же каталоге, что и файлы .cpp и .h, с
тем же именем, но с расширением .dcr. Для нашего случая это будет имя
ourcomponent.dcr. После внесения изменений в файлы .cpp и .h можно приступать к
установке нового компонента. Для этого необходимо выбрать пункт главного меню
Component | Install Component и в появившемся окне в поле “Unit file name” указать
имя .cpp файла, содержащего исходный код компонента. В поле “Packege File Name”
выбрать
пакет,
в
который
будет
помещен
компонент.
По
умолчанию
пользовательские компоненты помещаются в пакет “Borland User Components”.
После нажатия кнопки “Ok” необходимо согласиться с перекомпиляцией пакета и
сохранить изменения в пакете. После этого новый компонент появится в палитре
компонентов той закладки, которая была указана при регистрации компонента. Для
удаления/добавления или перекомпиляции модулей в пакетах можно также
использовать пункт Component | Install Package. В появившемся окне необходимо
выбрать нужный пакет и нажать кнопку «Edit».
8.2. Определение свойств и событий компонента
В рассмотренном выше примере была создана всего лишь заготовка
компонента. В реальной ситуации программист должен реализовать некоторый код,
обеспечивающий функциональность нового компонента. Принцип работы с новым
компонентом ничем не отличается от обычной процедуры наследования ООП.
Таким образом, программист может определять новые компонентные данные или
функции, а также использовать наследуемые. Новым в среде С++Builder является
возможность использования свойств и событий. Как уже отмечалось, свойства
предоставляют дополнительный интерфейс для доступа к защищенным данным
класса. События позволяют определить реакцию на изменение состояния
компонента. Свойства и события могут быть определены в одной из стандартных
областей видимости класса (private, protected, public), в этом случае их доступность
определяется также как и для данных класса. Однако в среде С++Builder введена
77
еще одна директива для области видимости __published. Если свойство или событие
будет размещено в этой секции, то оно будет доступно в инспекторе объектов на
этапе проектирования. Для описания свойств и событий используется ключевое
слово __property. Описание свойств и событий в общем случае выглядит
следующим образом
__property
<type>
<id>
{
read
=
<data/function
id>,
write
=
<data/function id>}
Кроме
атрибутов
read
и
write
для
свойств
можно
устанавливать
дополнительные атрибуты, которые также называются спецификаторами свойств,
однако их рассмотрение выходит за рамки данного учебного пособия. <id>
определяет имя свойства и задается по стандартным правилам для идентификаторов
С++. Согласно нотации среды С++Builder имена событий начинаются с букв “On”.
Описание свойств и событий отличаются только типом <type>. События должны
иметь специальный тип «событие», указатель на функцию, описанный с ключевым
словом __closure. Видя такой тип, инспектор объектов размещает события на
закладке Events. Свойства могут иметь любой стандартный или производный тип,
доступный для переменных. При использовании классов VCL необходимо
использовать тип указатель на класс, поскольку статические экземпляры классов
VCL не допустимы. Кроме того, в этом случае необходимо создать экземпляр класса
в конструкторе и уничтожить его в деструкторе. Присваивание переменных,
содержащих указатели на класс, необходимо осуществлять с помощью метода
Assign. Свойства на самом деле не хранят никаких данных и являются как бы
интерфейсом для доступа к внутренним данным компонента. Где на самом деле
будут храниться данные, и как будет происходить доступ к ним, определяется с
помощью спецификаторов read и write. И read, и write могут указывать и на метод, и
на переменную (обычно объявленную в private секции класса). Чаще всего write
указывает на функцию, а read просто на переменную. В самой функции также
происходит запись значения в переменную, однако использование функции
позволяет
реализовать
дополнительные
возможности,
например,
проверку
допустимых значений при записи в свойство или изменение значения других
свойств и переменных в зависимости от данного. Имя свойства может не иметь
78
ничего общего с именем переменной, которую оно модифицирует. Однако согласно
нотации среды С++Builder имя связанной со свойством должно быть таким же, как и
имя свойства, но с префиксом F. Если write отсутствует, то свойство будет только
для чтения (но значение переменной, на которую оно указывает, может быть
изменено какими-либо другими способами, например из других методов класса).
Как уже отмечалось, события позволяют определить пользователю компонента
собственный обработчик событий. Обработчик события представляет собой
обычную функцию класса (обычно принадлежащую классу формы), в которую
передается указатель на объект, который сгенерировал события и некоторые
дополнительные данные, специфичные для каждого типа события. Если произошло
какое-либо событие, то компонент просто вызывает назначенный этому событию
обработчик, таким образом позволяя пользователю компонента выполнить
необходимые действия при возникновении этого события. Рассмотрим пример
определения свойств и событий.
Заголовочный .h файл
class PACKAGE TOurComponent : public TComponent
{private:
int FMyInteger; char FMyChar;
Graphics::TFont *FMyFont;
TNotifyEvent FOnMyError;
void __fastcall SetMyInteger(const int Value);
__published:
__property
int
MyInteger
=
{read=FMyInteger,
write=SetMyInteger};
__property char MyChar = {read=FMyChar, write=FMyChar};
__property
Graphics::TFont*
MyFont
=
{read=FMyFont,
write=SetMyFont};
__property
TNotifyEvent
OnMyError
=
{read=FOnMyError,
write=FOnMyError};}
Исходный .cpp файл
__fastcall TOurComponent::TOurComponent(TComponent* Owner)
: TComponent(Owner)
{FMyInteger=10;FMyChar='s';
FMyFont= new Graphics::TFont();}
79
__fastcall TOurComponent::~TOurComponent()
{delete FMyFont;}
void __fastcall TOurComponent::SetMyFont(const Graphics::TFont *Value)
{ FMyFont->Assign((TPersistent*)Value); }
void __fastcall TOurComponent::SetMyInteger(const int Value)
{if (Value%2) FMyInteger = Value;
else if (!ComponentState.Contains(csDesigning))
if (FOnMyError) FOnMyError (this);}
В данном примере определяется три свойства: MyChar, MyFont и MyInteger.
Свойство MyChar обеспечивает доступ к защищенной переменной FMyChar без
каких-либо дополнительных возможностей. Свойство MyFont обеспечивает доступ к
защищенной переменной FMyFont, являющейся указателем на класс типа TFont.
Еще раз отметим, что в этом случае объект должен быть создан в конструкторе и
уничтожен в деструкторе. Кроме того, как видно из функции SetMyFont, значение
переменной FMyFont присваивается с помощью метода Assign. Данный метод
обеспечит корректное копирование всех внутренних данных и свойств класса TFont.
При выполнении же присваивания вида FMyFont=Value произойдет присваивание
указателей. В результате мы потерям доступ к блоку памяти, выделенному ранее
под FMyFont, и при удалении объекта Value, указатель FMyFont станет
некорректным. Свойство MyInteger обеспечивает доступ к защищенной переменной
FMyInteger. При чтении значения свойства происходит просто чтение этой
переменной, при записи же происходит проверка: для записи в переменную
доступны лишь четные значения. При попытке же записи нечетного значения будет
сгенерировано событие FOnMyError, причем генерация события происходит, только
если компонент находится на этапе выполнения, а не на этапе проектирования. Для
проверки на каком этапе находится компонент, можно использовать свойство
ComponentState: если это свойство содержит флаг csDesigning, то компонент
находится на этапе проектирования. Далее происходит проверка, назначил ли
программист, использующий наш компонент, обработчик события OnMyError.
Поскольку переменная FOnMyError содержит адрес функции обработчика, то, если
программист не назначил обработчик, в ней будет содержаться значение NULL. В
противном случае происходит вызов назначенной функции обработчика, при этом в
80
качестве параметра передается указатель this, т.е. указатель на текущий экземпляр
класса, для которого была вызвана функция SetMyInteger. В обработчике этот
параметр будет доступен как указатель Sender, что позволит программисту
определить в обработчике, какой именно экземпляр класса вызвал данное событие.
Вывод
Среда
С++Builder
предоставляет
удобные
средства
для
создания
пользовательских компонент. Работа с созданными компонентами ничем не
отличается от работы со стандартными, и они позволяют существенно расширить
функциональность среды С++Builder.
81
Список литературы
1. Архангельский А. Я. Разработка прикладных программ для Windows в С++
Builder 5- М.: ЗАО «Издательство БИНОМ», 2000. – 256 с.:ил.
2. Елманова Н. З., Кошель С. П. Введение в BorlandC++Builder – М.:ДиалогМИФИ, 1998. – 272 с.
3. Рихтер Дж. Windows для профессионалов (программирование в Win32 API для
Windows NT 4.0 и Windows 95) / Пер. с англ. – М.: Издательский отдел
“Русская редакция” ТОО “Chanel Trading Ltd.”, 1997. – 720 c.:ил.
4. Теллес М. Borland C++Builder: библиотека программиста – СПб: Питер Ком,
1998. – 512с.: ил.
5. Холингворт Дж., Сворт Б., Кэшмэн М., Густавсон П. Borland C++Builder 6.
Руководство разработчика.: Пер. с англ. – М.: Издательский дом «Вильямс»,
2004. – 976 с.:ил.
6. Шамис В. А. Borland C++ Builder. Программирование на С++ без проблем. М.:
«Нолидж», 1997. – 266 с., ил.
82
Дмитрий Николаевич Лясин
Сергей Геннадьевич Саньков
Основы программирования в среде C++Builder
Учебное пособие
Редактор О.П. Чеботарева
План заказных изданий 2007г. поз. N 1
Лицензия ИД N04790 от 18.05.2001
Подписано в печать _________ . Формат 60 х 84
Бумага газетная. Печать офсетная. Усл. печ. л. ____ .
Уч. -изд.л. ___ . Тираж _____ . Заказ _____ .
Волгоградский государственный технический университет.
400131 Волгоград , пр. Ленина , 28.
РПК "Политехник" Волгоградского государственного технического
университета.
400131 Волгоград , ул. Советская , 35.
83
Скачать