РАБ_C++BUILDER

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ
РОССИЙСКОЙ ФЕДЕРАЦИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ
БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

«ВЯТСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»








В.В. ЧУРКИН







РАБОТА В C++ BUILDER

Учебно-методическое пособие


















Киров
2013




УДК 004.42(07)

Рекомендовано к изданию методическим советом
факультета автоматики и вычислительной техники
ФГБОУ ВПО «ВятГУ»
Допущено редакционно-издательской комиссией методического совета ФГБОУ ВПО «ВятГУ» в качестве учебно-методического пособия по дисциплине «Работа в C++Builder» для студентов направления подготовки 220400.62 «Управление в технических системах», профиль «Управление и информатика в технических системах» всех форм обучения

Рецензент:
доктор технических наук, профессор кафедры ЭВМ Страбыкин Д.А.

Чуркин В.В.
Работа в C++Builder: учебно-методическое пособие для студентов направления подготовки 220400.62 «Управление в технических системах», профиль «Управление и информатика в технических системах» всех форм обучения / В. В. Чуркин. – Киров: ПРИП ФГБОУ ВПО «ВятГУ», 2013. – 176 с.

Учебно-методическое пособие (УМП) содержит описания практических занятий, в которых навыки работы в среде C++Builder приобретаются в ходе выполнения заданий. В УМП изложена отладка приложений, приведены необходимые сведения о свойствах и событиях компонентов с примерами использования компонентов библиотеки C++Builder 6 и рассмотрен порядок работы с динамически присоединяемыми библиотеками. Для контроля знаний студентов описание каждого занятия завершается перечнем контрольных вопросов. УМП охватывает весь объем программы дисциплины «Работа в C++Builder».

УДК 004.42(07)

© ПРИП ФГБОУ ВПО «ВятГУ», 2013

ISBN ______________________
СОДЕРЖАНИЕ
Введение..4
Ознакомление с Интегрированной Средой Разработки (ИСР)
C++Builder6 .......6
Библиографический список.......15
ЗАНЯТИЕ 1.Формы и компоненты16
Контрольные вопросы...........................................................................28
Библиографический список...29
ЗАНЯТИЕ 2. Отладка приложений30
Контрольные вопросы...........................................................................43
Библиографический список...44
ЗАНЯТИЕ 3. Компоненты ввода и отображения
текстовой информации..45
Контрольные вопросы...........................................................................63
Библиографический список...63
ЗАНЯТИЕ 4. Диаграммы и графики..64
Контрольные вопросы...........................................................................72
Библиографический список...72
ЗАНЯТИЕ 5. Список изображений. Компоненты отображения
иерархических данных. Полоса состояния.
Перетаскивание объектов – технология Drag&Drop..73
Контрольные вопросы...........................................................................88
Библиографический список...89
ЗАНЯТИЕ 6. Главное меню. Контекстное всплывающее меню.
Горячие клавиши90
Контрольные вопросы.........................................................................102
Библиографический список..102
ЗАНЯТИЕ 7. Отображение хода длительных процессов.
Кнопки, индикаторы, управляющие элементы.
Панели и компоненты внешнего оформления...103
Контрольные вопросы.........................................................................130
Библиографический список..131
ЗАНЯТИЕ 8. Системные диалоги.132
Контрольные вопросы.........................................................................146
Библиографический список..146
ЗАНЯТИЕ 9. Технология разработки приложений.
Диспетчеризация действий..147
Контрольные вопросы.........................................................................161
Библиографический список......162
ЗАНЯТИЕ 10. Динамически присоединяемые библиотеки DLL...163
Контрольные вопросы..........................................................................176
Библиографический список...176


ВВЕДЕНИЕ

При составлении данного учебно-методического пособия (УМП) была поставлена задача – при изучении дисциплины обеспечить условия приобретения студентами практических навыков использования интегрированной среды разработки приложений C++Builder 6 в объеме, предусмотренном программой курса «Работа в C++Builder».
УМП содержит описания десяти занятий, выделенных тематически и поэтому разных по продолжительности выполнения.
Занятие 1 (Формы и компоненты) содержит три раздела: включение в проект новой формы, размещение компонентов на форме и депозитарий – хранилище форм и проектов. В разделах соответственно показаны: способы включения в проект новой формы; способы переноса компонентов на форму, смысл понятий родителей и владельцев компонентов, понятия Z-последовательности, действия пользователя в случае многослойного размещения компонентов на форме, способы поиска «пропавших» компонентов, порядок работы с окном Дерева Объектов, представление связей между компонентами в виде диаграммы, работа с группой компонентов; сохранение форм и проектов в депозитарии, способы заимствования формы из депозитария.
Занятие 2 (Отладка приложений) содержит описания команд для компиляции и компоновки проекта, примеры сообщений компилятора и компоновщика, описания действия пользователя в случае ошибки выполнения – работа с окном наблюдения, окном оценки и модификации, пошаговое выполнение приложения. Кроме того, рассмотрено использование точек прерывания, Журнала событий и окна Инспектора Отладки.
Занятие 3 (Компоненты ввода и отображения текстовой информации) посвящено изучению компонентов для отображения различных надписей на форме, однострочных и многострочных окон редактирования, компонентов, обеспечивающих выбор из списка, и компонента - таблицу, предназначенную для отображения текстовой информации.
Занятие 4 (Диаграммы и графики) содержит примеры использования компонентов, позволяющих строить различные диаграммы и графики.
Занятие 5 (Список изображений. Компоненты отображения иерархических данных. Полоса состояния. Перетаскивание объектов – технология Drag&Drop) начинается с описания списка изображений – компонента, представляющего собой набор изображений одинаковых размеров. Затем рассмотрены компоненты, служащие для отображения иерархических данных в виде дерева с возможностью выбора нужного узла или узлов. Далее рассмотрен компонент, отображающий полосу состояния. Перетаскивание объектов – технология Drag&Drop рассмотрена на примере работы с деревом. В заключение рассмотрен компонент, позволяющий отображать данные в виде списков, таблиц, крупных и мелких пиктограмм.
Занятие 6 (Главное меню. Контекстное всплывающее меню.Горячие клавиши) позволяет изучить компоненты, необходимые для создания интерфейса практически любого приложения – главное меню и контекстное меню, в сочетании с вспомогательным компонентом, обеспечивающим быстрый доступ к разделам меню.
Занятие 7 (Отображение хода длительных процессов. Кнопки, индикаторы, управляющие элементы. Панели и компоненты внешнего оформления) включает в себя изучение компонентов для отображения хода процессов, занимающих заметное время; кнопок, индикаторов, управляющих элементов – ползунков и полос прокрутки; компонентов-заголовков, компонента-таймера, компонента-календаря; панелей общего назначения, многостраничных панелей, инструментальных панелей, перестраиваемых панелей.
Занятие 8 (Системные диалоги) посвящено изучению компонентов работы с файловой системой, реализующих стандартные диалоги - открытия и сохранения файлов, компонентов - фрагментов указанных диалогов (списки дисков, фильтров, каталогов и файлов, дерево каталогов), которые облегчают пользователю создание собственных диалоговых окон. Здесь же изучаются диалог выбора шрифта и диалоги выбора цвета.
Занятие 9 (Технология разработки приложений. Диспетчеризация действий) имеет цель – приобретение навыков проектирования интерфейса через общий подход, который начинается с составления списка действий для работы с проектируемым проиложением. На примерах показано, как список действий реализуется с помощью компонентов – диспетчеров действий.
Занятие 10 (Динамически присоединяемые библиотеки DLL) содержит описание назначения и способов реализации динамически присоединяемых библиотек DLL. Приведен пример создания DLL. Также на примерах показано, как реализовать статическое и динамическое связывание DLL с проектируемым приложением.



Ознакомление с Интегрированной Средой Разработки (ИСР)
C++Builder 6

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

Проекты C++Builder
Проект C++Builder состоит из форм( модулей с их заголовочными файлами и файлами реализации( установок параметров проекта( ресурсов и т(д( Вся эта информация размещается в файлах( Многие из этих файлов автоматически создаются C++Builder в процессе построения приложения:

Головной файл
проекта (.cpp)
C++Builder создает файл .cpp для головной функции WinMain, инициирующей приложение и запускающей его на выполнение

Файл опций проекта (.bpr)
Этот текстовый файл содержит установки опций проекта и указания на то( какие файлы должны компилироваться и компоноваться в проект( Файл сохраняется в формате XML

Файл ресурсов проекта (.res)
Двоичный файл( содержащий ресурсы проекта: пикто-граммы( курсоры и т(п( По умолчанию содержит только пиктограмму проекта( Может дополняться с помощью Редактора Изображений

Файл реализации модуля (.cpp)
Каждой создаваемой пользователем форме соответствует текстовый файл реализации модуля( используемый для хранения кода( Пользователь может создавать модули( не связанные с формами

Заголовочный файл модуля (.h)
Каждой создаваемой пользователем форме соответствует не только файл реализации модуля( но и его заголовочный файл с описанием класса формы( Пользователь может и сам создавать необходимые заголовочные файлы

Файл формы (.dfm)
Это двоичный или текстовый файл( который C++Builder создает для хранения информации о фор-
мах пользователя( Пользователь может смотреть этот файл в текстовом виде или в виде формы( Каждому файлу формы соответствует файл модуля (.cpp)

Заголовочный файл компонента (.hpp)
Файл создается при создании пользователем нового компонента( Пользователю также часто приходится подключать к проекту эти файлы из библиотеки компонентов C++Builder( расположенные в каталоге Include\VCL

Файл группы проектов (.bpg)
Текстовый файл( создаваемый в C++Builder при созда-нии пользователем группы проектов

Файлы пакетов (.bpl и .bpk)
Эти двоичные файлы используются C++Builder при работе с пакетами: .bpl – файл самого пакета( .bpk – файл( определяющий компиляцию и компоновку пакета

Файл рабочего стола проекта (.dsk)
В этом текстовом файле C++Builder хранит информацию о последнем сеансе работы с проектом: открытых окнах( их размерах и положении( Благодаря этому файлу в новом сеансе работы пользователь сразу видит тот же экран( который был в предыдущем сеансе( Файл создается только при включении опции Опции автосохранения |Рабочий стол проекта ( на странице Предпочтения–многостраничного окна Опции среды общих настроек среды( вызываемого командой Инструменты|Опции среды)

Файлы резервных копий (.~bp, .~df, .~cp, .~h)
Это соответственно файлы резервных копий для файлов проекта( формы( реализации модуля и заголовочного( Если пользователь что-то безнадежно испортит в своем проекте( он может соответственно изменить расширения этих файлов и таким образом вернуться к предыдущему не испорченному варианту




Следующая группа файлов создается компилятором:
Исполняемый
файл (.exe)
Это исполняемый файл проектируемого приложения( Он является автономным исполняемым файлом( для которого больше ничего не требуется( если только пользователь не использует библиотеки( содержащиеся в пакетах( DLL, OCX и т(д(

Объектный файл модуля (.obj)
Это откомпилированный файл модуля (.cpp)( который компонуется в окончательный исполняемый файл

Динамически присоединяемая библиотека (.dll)
Этот файл создается в случае( если пользователь проектирует свою собственную DLL

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

Файлы выбороч-ной компоновки (.il?)
Файлы с расширением( начинающемся с il (.ilc, .ild, .ilf, .ils), позволяют повторно компоновать только те файлы( которые были изменены после последнего сеанса


C++Builder может использовать файлы Windows:
Файлы справки (.hlp)
Это стандартные файлы справки Windows, которые могут быть использованы приложением C++Builder

Файлы изображе-ний или графичес-кие файлы (.wmf, .bmp, .ico)
Эти файлы обычно используются в приложениях Win-dows для создания привлекательного и дружественного пользовательского интерфейса


Из всех перечисленных файлов (а могут использоваться еще и другие) важнейшими являются файлы .cpp, .h, .dfm, .bpr, .res. Это файлы( которые необходимо перенести на другой компьютер( чтобы продолжить на нем работу над проектом( Все остальные файлы C++Builder создаст автоматически в процессе компиляции проекта и его отладки(
Главной частью приложения является головной файл .cpp (файл проекта( исходный файл проекта) с функцией WinMain, с которой начинается выполнение программы и которая обеспечивает инициализацию других модулей( Она создается и модифицируется C++Builder автоматически в процессе разработки приложения( Имя( которое дает пользователь файлу проекта( когда сохраняет его( становится именем исполняемого файла(
Все изменения файла проекта при добавлении новых форм( изменении имен форм и т(п( поддерживаются C++Builder автоматически( Для просмотра исходного файла проекта надо выполнить команду Проект|Вид источника. Но обычно просмотр не требуется(
Информация о формах C++Builder хранится в трех файлах: .dfm, .cpp, и .h. Информация о внешнем виде формы( ее размерах( местоположении на экране и т(д( хранится в файле с расширением .dfm, который по умолчанию имеет текстовый вид( Для хранения файла формы в двоичном виде нужно щелкнуть на форме правой кнопкой мыши( и во всплывшем меню выключить индикатор Текст DFM.
Основной файл( с которым работает пользователь( – это файл реализации модуля .cpp, в котором хранится код( соответствующий данной форме( В текстовом заголовочном файле с расширением .h хранится объявление класса используемой формы( Весь основной текст этого файла C++Builder формирует автоматически по мере проектирования пользователем формы( Но иногда требуется вручную вводить в этот файл объявления каких-то функций( типов( переменных( В C++Builder заголовочный файл загружается в окно Редактора Кода автоматически(
Имена всех файлов( описывающих модуль( одинаковы( Это имя задает пользователь( когда в первый раз сохраняет модуль(
Рекомендуется создавать в приложении модуль( не связанный с формой( в который помещают описания типов( констант( переменных( функций( используемых другими модулями( Это способствует хорошей структурированности программы( поддерживает единое понимание типов( констант( переменных во всех модулях и уменьшает количество взаимных ссылок модулей друг на друга( Тем самым упрощается модификация и сопровождение программы(
Чтобы создать в проекте новый модуль( не связанный с какой-либо формой( надо выполнить команду Файл|Новый|Другое и в открывшемся многостраничном окне Новые элементы дважды щелкнуть на пиктограмме Модуль страницы Новый.
По окончании работы над проектом следует удалить вспомогательные файлы – .obj, .res, .tds, .il?, ~*. Объем файлов .tds может быть очень большим (мегабайты)(

Основное окно ИСР
Основное окно ИСР появляется на экране после запуска C++Builder 6.
В верхней части окна ИСР находится полоса главного меню( Главное меню имеет следующие разделы (меню)(

Файл – позволяет создать новый проект( новую форму( открыть ранее созданный проект или форму( сохранить проекты или формы в файлах с заданными именами(

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

Поиск – позволяет осуществлять поиск и контекстные замены в коде приложения(

Вид – позволяет вызывать на экран различные окна( необходимые для проектирования(

Проект – позволяет добавлять и убирать из проекта формы( задавать опции проекта( компилировать проект без его выполнения и т(д(
Запуск – дает возможность выполнять проект в нормальном или отладочном режимах( продвигаясь по шагам( останавливаясь в указанных точках кода( просматривая значения переменных и т(д(

Компонент – позволяет создавать и устанавливать новые компоненты( конфигурировать палитру компонентов( работать с пакетами(

База данных – позволяет использовать инструментарий для работы с базами данных(

Инструменты – позволяет настраивать ИСР и выполнять различные вспомогательные программы( например: вызывать редактор изображений( работать с программами( конфигурирующими базы данных и т(д( Кроме того( в это меню можно включать любые разделы( вызывающие те или иные приложения(

Окно – позволяет ориентироваться среди массы окон( обычно одновременно открытых в процессе проектирования и переключаться в нужное окно(

Помощь – содержит разделы( помогающие работать со встроенной в C++Builder справочной системой(

Помимо главного меню в C++Builder имеется система контекстных всплывающих меню( которые появляются( если пользователь поместил курсор мыши в каком-то окне или на каком-то компоненте и щелкнул правой кнопкой мыши( Большинство разделов контекстных меню дублирует основные разделы главного меню( Но есть и разделы( отсутствующие в главном меню и позволяющие ускорить программирование(
Ниже полосы главного меню в окне ИСР расположены две инструментальные панели( Левая панель (состоит из нескольких панелей) содержит два ряда быстрых кнопок( дублирующих некоторые наиболее часто используемые команды меню( Правая панель содержит палитру компонентов библиотеки визуальных компонентов( Палитра компонентов содержит ряд страниц( закладки которых видны в ее верхней части(
Правее полосы главного меню размещена еще одна небольшая инструментальная панель( содержащая выпадающий список и две быстрые кнопки( Это панель сохранения и выбора различных конфигураций окна ИСР( которые пользователь может создавать и запоминать(
В основном поле окна слева видны два окна: сверху – Дерева Объектов, под ним – Инспектора Объектов (Инспектора проекта). Окно Дерева Объектов будет отображать иерархическую связь визуальных и невизуальных компонентов и объектов проектируемого приложения( А Инспектор Объектов – это основной инструмент( с помощью которого пользователь задает свойства компонентов и обработчики событий( Правее этих окон видно окно пустой формы( готовой для переноса на нее компонентов( Под ним расположено окно Редактора Кода( Чтобы увидеть его( окно формы необходимо сдвинуть(

Проектирование приложения – расчет по формуле z=f(x,y)
Создайте для проекта приложения каталог (папку Windows) с именем PROBA и запустите C++Builder 6(
Создайте новый проект командой Файл|Новый|Приложение(
Сразу сохраните файл модуля и проект командой Файл|Сохранить все. Во время работы над проектом удобно использовать соответствующую быструю кнопку( При первом сохранении C++Builder спросит сначала имя файла сохраняемого модуля( а затем – имя головного файла проекта( Файл модуля и головной файл проекта имеют одинаковое расширение .cpp и поэтому они должны различаться именами( чтобы не затереть на диске друг друга( Итак( файлу модуля дайте имя Formula( а файлу проекта – PR_Formula( В последующих сеансах работы можно открыть сохраненный проект командой Файл|Открыть проект ( или Повторно открыть).
Перейдите в Инспектор Объектов( щелкнув левой кнопкой мыши на свойстве Caption (надпись)( Данное по умолчанию значение Form1 свойства Caption замените на значение РАСЧЕТ ПО ФОРМУЛЕ( Перейдите в форму( щелкнув на ней левой кнопкой(
В процессе первого проектирования приложения полезно иметь в качестве справки результат проектирования, представленный на рис.1.


Рис.1 – форма по окончании проектирования

Перенесите па пустую форму кнопку Button со страницы Стандарт палитры компонентов( Для этого выделите пиктограмму кнопки (она седьмая слева)( При этом первая слева кнопка-указатель приобретет вид ненажатой кнопки( Это означает намерение поместить компонент (в данном случае кнопку) на форму( Если нажать кнопку-указатель( то это будет означать отказ от размещения выбранного компонента( Итак( выделив пиктограмму кнопки( щелкните курсором мыши в нижней части формы( посредине( На форме появится кнопка( которой C++Builder присвоит имя по умолчанию – Button1. Теперь свойствам перенесенного на форму компонента – кнопке присваивают нужные значения( (Во время присваивания компонент должен оставаться выделенным( т(е( окруженным рамкой с маркерами( Для выделения компонента достаточно щелкнуть на нем левой кнопкой мыши( Для удаления ошибочно перенесенного на форму компонента нужно выделить его и нажать клавишу Delete.) Перейдите в Инспектор Объектов и измените ее свойство Caption (надпись)( которое по умолчанию равно Button1( на РАСЧЕТ( Установите свойство кнопки Height (высота) равным 30( Раскройте двойным щелчком свойство Font (шрифт)( установите свойство шрифта Height (высота) равным 10( затем также двойным щелчком раскройте подсвойство Style (стиль) и установите в true свойство fsBold (жирный)( Затем перейдите в форму( щелкнув на ней курсором мыши( Выделение с кнопки будет снято(
Над кнопкой РАСЧЕТ поместите на форме панель Panel1( На странице Стандарт палитры компонентов пиктограмма панели – вторая справа( Результат расчета по формуле будем выводить в панель( и поэтому( перейдя в Инспектор Объектов( очистите свойство Caption у панели( Высоту шрифта установите равной 20( Поварьируйте такими свойствами панели( как BevelInner и BevelOuter, которые определяют вид (утопленный – bvLowered или выпуклый – bvRaised) основного поля и рамки панели( Установите BevelInner = bvLowered и BevelOuter = bvRaised.
Над панелью поместите метку Label1 (на странице Стандарт – четвертая слева)( Укажите на метке надпись РЕЗУЛЬТАТ жирным шрифтом( высотой 20(
Перенесите в верхнюю часть формы со страницы Дополнительно два окна редактирования с присоединенными к ним метками LabeledEdit( расположив их на одной горизонтали( Измените надписи в метках компонентов LabeledEdit на ЗНАЧЕНИЕ X и ЗНАЧЕНИЕ Y соответственно( Для этого щелкните на символе +’ в свойстве EditLabel этих компонентов и измените надпись в свойстве Caption раскрывшихся списков свойств меток на ЗНАЧЕНИЕ X и ЗНАЧЕНИЕ Y соответственно( Задайте для меток жирный шрифт высотой 10( Закройте свойство EditLabel, щелкнув на символе -’ перед этим свойством. Затем высоте шрифта присвойте значение 10, а свойству Text компонентов - значения 1,3 и 5,6 соответственно(
Выше окон редактирования с метками( на одной вертикали с кнопкой( панелью и меткой( перенесите на форму еще одну метку Label2( в свойстве Caption которой запишите программируемую формулу так( чтобы она легко читалась на экране(
Выделите кнопку РАСЧЕТ на форме( щелкнув на ней курсором мыши( Перейдите в Инспектор Объектов( откройте в нем страницу событий (События)( найдите (второе сверху) событие кнопки OnClick (щелчок) и сделайте двойной щелчок в окне справа от имени этого события( Это стандартный способ задания обработчиков любых событий( Но перейти в обработчик события OnClick (только этого события) можно и иначе: достаточно сделать двойной щелчок на кнопке( В обоих случаях окажетесь в окне Редактора Кода и увидите там функцию с заголовком( который складывается из имени класса формы (TForm1), имени компонента (Button1) и имени события без префикса On (Click). Курсор находится в позиции( с которой вписывается обработчик события( Например( в случае формулы

13 EMBED Equation.3 1415

он может выглядеть так (курсив):
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double x,y,z;
x=StrToFloat(LabeledEdit1->Text);
y=StrToFloat(LabeledEdit2->Text);
z=(exp(x*x+2.37)-pow(x,1.5))/sqrt(fabs(y*y*y+atan2(y,x)));
Panel1->Caption="z="+FloatToStr(z);
}
Перейдите в этом файле выше и после директивы #include”Formula.h” добавьте директиву #include .
Сохраните проект быстрой кнопкой Сохранить все и запустите приложение на выполнение командой Запуск|Запустить или нажмите соответствующую быструю кнопку( или нажмите «горячую» клавишу F9. После недолгой компиляции( сопровождающейся временным появлением на экране окна компилятора( появится окно приложения( Щелкнув на кнопке РАСЧЕТ( в панели получите результат выполнения приложения (рис.2)(

Рис.2 – форма с результатом выполнения приложения

Очевидно, нужен форматированный вывод результата. Для перехода в окно Редактора Кода закройте окно с результатом щелчком на крестике в правом верхнем углу формы.
Введите форматированный вывод в строку в обработчике( Для этого используется функция FloatToStrF с синтаксисом:

System::AnsiString FloatToStrF(Extended Value,
TFloatFormat Format, int Precision, int Digits).
Число Value функция преобразует в строку с помощью формата типа TfloatFormat с синтаксисом: enum TfloatFormat{ffGeneral, ffExponent, ffFixed, ffNumber, ffCurrency};
Возможные значения формата определяют следующие правила форматирования:

ffGeneral
Основной числовой формат( Число преобразуется по формату с фиксированной точкой или научному в зависимости от того( какой из них оказывается короче( Начальные нули удаляются( десятичная точка ставится только при необходимости( Фиксированный формат используется( если число разрядов слева от точки не больше указанной точности Precision и если значение не меньше 0(00001. В противном случае используется научный формат( в котором параметр Digits определяет число разрядов степени – от 0 до 4

ffExponent
Научный формат( Число преобразуется в строку вида
“–d.dddE+dddd”( Общее число цифр( включая одну перед десятичной точкой( задается параметром Precision. После символа “E” всегда следует знак “+” или “–“ и до четырех цифр( Параметр Digits определяет минимальное число разрядов степени – от 0 до 4

ffFixed
Формат с фиксированной точкой( Число преобразуется в строку “–ddd.ddd”( По крайней мере одна цифра всегда предшествует десятичной точке( Число цифр после десятичной точки задается параметром Digits( который может лежать в пределах от 0 до 18( Если число разрядов слева от десятичной точки больше указанного параметром Precision( то используется научный формат


Соответствующую строку в обработчике измените следующим образом:
Panel1->Caption=”z=”+FloatToStrF(z,ffExponent,5,2);
После ввода команды Сохранить все запустите приложение на выполнение (рис.3).


Рис.3 – форматирование результата

Теперь в окна редактирования можно вводить значения 13 EMBED Equation.3 1415 и 13 EMBED Equation.3 1415(между целой и дробной частью числа ставьте запятую!) и получать результат 13 EMBED Equation.3 1415( нажимая на кнопку РАСЧЕТ( Поэкспериментируйте с форматами ffGeneral и ffFixed.
Выйдите из среды C++Builder(

Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 25–29( 42–89, 941–942( 960(
Интегрированная среда разработки приложений Borland C++ Builder 6: Метод. указания к лаб. работам. Специальность 210100. Дисциплина «Программирование и основы алгоритмизации» / ВятГУ, ФАВТ, каф. АТ, сост. В.В. Чуркин. – Киров, 2005. – 142 с.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.

ЗАНЯТИЕ 1

Формы и компоненты

Включение в проект новой формы

Обычно проект содержит несколько форм; для включения новой пустой формы выполняют команду Файл|Новый|Форма или нажимают соответствующую быструю кнопку(
Новая форма включается в проект также в случае, когда уже готовый проект содержит форму( совпадающую с требуемой( или близкую к ней(
Готовую форму включают в проект командой Проект|Добавить к проекту или соответствующей быстрой кнопкой( Если имя включаемой формы совпадает с именем находящейся в проекте( то включения не произойдет( и будет выдано соответствующее предупреждение( В этом случае нужно форму в новом проекте переименовать (задать новое имя в свойстве Name)( Если совпадут имена модулей (включения также не произойдет)( то включаемый модуль нужно предварительно сохранить командой Файл|Сохранить как( дав ему новое имя( Следует учитывать( что включенная форма будет принадлежать обоим приложениям(
Чтобы избежать совместного владения формой несколькими приложениями( нужно перейти в окне Редактора Кода в модуль включенной формы и выполнить команду Файл|Сохранить как( сохранив модуль в каталоге нового приложения (имя изменять необязательно)( В этом случае разные приложения будут использовать совершенно разные копии одной формы и изменения одной из них не затронут другие приложения(
Копию формы можно получить( копируя файл модуля .cpp( его заголовочный файл .h и файл изображения .dfm. Файл объектного модуля будет создан в процессе компиляции(
Еще один способ создания автономной копии формы – выполнить команду Файл|Открыть, указав файл открываемой формы( а затем – команду Файл|Сохранить как( которая сохранит ее в указанном каталоге под указанным именем( C++Builder автоматически скопирует файлы .cpp, .h, .dfm(
В процессе работы над проектом возникает потребность посмотреть формы и модули из других проектов( Открывается какой-либо модуль( без включения его в текущий проект( командой Файл|Открыть( Файл модуля окажется в окне Редактора Кода( Сейчас можно просматривать его и соответствующую ему форму( копировать через буфер обмена Clipboard какие-то операторы или компоненты в свои модули( Чтобы закрыть просмотренный модуль( щелкните на его коде правой кнопкой мыши и из всплывшего меню выберите команду Закрыть страницу. Страница Редактора Кода с текстом модуля и его форма будут закрыты(

Размещение компонентов на форме

Перенос компонентов со страниц библиотеки на форму

Первый вариант переноса состоит из двух действий: выделить курсором компонент на странице библиотеки и щелкнуть мышью в нужном месте формы( Другой вариант – двойной щелчок на компоненте( затем буксировка компонента из центра формы в нужное место(
Если нужно разместить несколько компонентов одного типа( то нажав и удерживая клавишу Shift, выделите компонент на странице( Отпустив Shift, щелкайте в нужных местах формы( Для прерывания размещения нажмите кнопку-указатель со стрелкой(
Для поиска компонента по имени пользуются командой Вид|Список компонентов. При ее выполнении откроется диалоговое окно Компоненты( содержащее алфавитный список всех компонентов( По мере набора первых символов имени в окне быстрого поиска Поиск по имени указатель будет перемещаться на имя нужного компонента( Чтобы перенести его на форму( нажимают кнопку Добавить к форме внизу окна или делают двойной щелчок на выбранном компоненте(
После переноса компонента на форму его можно буксировать на нужное место и изменять его размеры( Для этого выделяют компонент( щелкнув на нем мышью( Появится рамка с маркерами( Размер компонента изменится( если потянуть курсором за один из маркеров( При задержке курсора над размещенным на форме компонентом появляется ярлычок с его именем( а при нажатии левой кнопки мыши на компоненте( при его буксировке или изменении размеров появляется ярлычок с размерами компонента(

Родители компонентов – Parent

Часто компоненты размещаются не непосредственно на формах( а на панелях( зрительно объединяющих группы компонентов по их назначению( В этом случае сначала на форме должны быть размещены панели( а потом на них располагаются компоненты(
Оконный компонент – форма( панель и т(д(( включающий в себя как контейнер другие компоненты( выступает по отношению к ним как родительский компонент( У каждого компонента есть родитель( Им может быть форма или другой оконный компонент( В процессе выполнения приложения родителя того или иного компонента можно узнать по его свойству Parent. Это свойство можно читать и изменять только во время выполнения( в Инспекторе Объектов его нет(
Компонент может наследовать многие свойства своего родителя( Для всех визуальных компонентов в Инспекторе Объектов есть такие свойства( как ParentFont и ParentShowHint, для оконных компонентов есть еще свойство ParentCtl3D. Эти свойства указывают (если их значения установлены в true)( что дочерний компонент наследует от родительского соответственно атрибуты шрифта( показ ярлычков( атрибуты своего оформления( Кроме того( значения свойств Left и Top, которые имеются в Инспекторе Объектов для любого визуального компонента и которые определяют положение левого верхнего угла компонента( измеряются в системе координат родительского компонента( Поэтому при перемещении родительского компонента будут синхронно перемещаться и все его дочерние компоненты(
Свойство Anchors определяет привязку дочерних компонентов к границам родительского компонента( Это свойство может обеспечивать изменение местоположения и размеров дочернего компонента при изменении границ родительского компонента(
К важным свойствам( связывающим дочерние компоненты с родительским( относятся Visible (видимый) и Enabled (доступный)( Если в процессе выполнения приложения сделать в родительском компоненте Visible равным false( то станет невидимым не только родительский( но и все его дочерние компоненты( Аналогично( если в процессе выполнения приложения сделать в родительском компоненте Enabled равным false( то станут недоступными все его дочерние компоненты( А это означает( что пользователь не сможет нажимать кнопки и производить любые другие действия в пределах данного родительского компонента( Таким образом( понятие родительского компонента имеет большое практическое значение(

Владельцы компонентов – Owner

Свойство Owner – владелец данного компонента устанавливается в момент создания компонента в процессе выполнения приложения( Владелец компонента – это тот компонент( при уничтожении которого (освобождении занимаемой им памяти) уничтожается и данный компонент(
По умолчанию родителем и владельцем всех компонентов( размещенных на форме( является сама форма( Но если компонент размещается на панели( то родителем и владельцем для него становится эта панель(
Все дочерние компоненты в оконном элементе располагаются в так называемой Z-последовательности( Для перекрывающихся компонентов (располагающихся друг на друге) Z-последовательность определяет( какой из них будет виден( Виден тот( который расположен в этой последовательности выше(
Обычно последовательность компонентов соответствует той( в которой они помещались на форму( Однако неоконные компоненты типа меток всегда лежат в Z-последовательности ниже любых оконных компонентов типа панелей и кнопок(

«Многослойное» размещение компонентов на форме

Если компонент перенесен не на форму( а на панель( то эта панель становится для него родительской( и переместить его за пределы панели становится невозможным( Переместить компонент на другую панель или непосредственно на форму можно через буфер обмена Clipboard. Выделите курсором переносимый компонент и вырежьте его в Clipboard командой Правка|Вырезать или «горячими» клавишами Ctrl-X. Затем щелкните на форме или на той панели( куда хотите перенести компонент( и выполните команду Правка|Вставить, дублируемую «горячими» клавишами Ctrl-V. Компонент перенесется из Clipboard на новое место и обретет нового родителя – панель или форму(
«Многослойное» размещение компонентов на форме получается( когда панели помещаются друг на друга( Будут ли при этом накрытые компоненты видны или нет( определяется их местом в Z-последовательности( Метки заведомо будут невидимы( поскольку они располагаются в Z-последовательности всегда ниже оконных компонентов( к которым принадлежат панели( Видимостью оконных компонентов – панелей( кнопок( окон редактирования можно управлять командами меню Правка|Придвинуть к фронту (перемещает на самый верх) и Правка|Отправить назад (перемещает на самый низ)( Эти команды можно выполнить и проще: щелкнув правой кнопкой мыши и выбрав их в пункте Управление из всплывающего меню(

Задание 1( Поместите на форму три панели: две из них непосредственно на форме( а третью – на одной из предыдущих( Разместите на форме и на панелях различные метки( кнопки и окна редактирования( Перемещайте компоненты по панелям( а сами панели – по форме( выполняйте команды Правка|Придвинуть к фронту и Правка|Отправить назад и наблюдайте при этом видимость различных компонентов(

Поиск «пропавших» компонентов

Приведем причины( по которым не удается найти помещенный на форму компонент( Пусть используется метка Label с установленным в true свойством AutoSize (автоматическое изменение размера по размерам надписи)( Если стереть значение надписи Caption( то горизонтальный размер метки уменьшается до нуля и ее не будет видно на форме( пока во время выполнения приложения значение Caption не изменится( Компонент может «пропасть» также( если он накрыт другим компонентом( расположенным выше в Z-последовательности( Не исключен такой выбор цветов( при котором компонент сливается с фоном(
Есть два способа поиска «пропавших» элементов( Первый – выбрать его имя в выпадающем списке( расположенном вверху окна Инспектора Объектов( Этот список содержит все компоненты( размещенные на форме( Если выбрать в нем нужный компонент( то на форме вокруг него появится рамка с маркерами( видимая даже в случае( если компонент накрыт сверху какой-нибудь панелью или другим компонентом( При этом в Инспекторе Объектов станут видны страницы свойств и событий найденного компонента( Этого достаточно( чтобы задать для компонента значения свойств или задать обработчики событий( Если же необходимо все-таки добраться до компонента( то нужно или временно куда-то сдвинуть закрывающие его компоненты( или выполнить для них команду Правка|Отправить назад( которая сдвинет их в низ Z-последовательности( Второй способ – выделить его вершину в окне Дерева Объектов (см( ниже)(
Обычная задача: как( ничего не сдвигая( добраться до нижних панелей( если на них лежат другие панели( или как добраться до формы( если она вся накрыта панелями( Эту задачу можно решить проще( чем описано выше( Выделяют верхнюю панель и нажимают клавишу Esc( В окне Инспектора Объектов откроются страницы( связанные с нижележащей панелью( Нажмите Esc еще раз и откроются страницы следующего слоя и т(д( до тех пор( пока не откроются страницы формы( Если же есть только один слой панели( то страницы формы откроются при первом нажатии Esc.

Окно Дерева Объектов

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

Задание 2( На форме разместите панели Panel1 и Panel3. На панели Panel1 разместите компоненты Edit1, Label1, а также Panel2( содержащую компоненты Button1 и Button2. Панель Panel3 содержит компоненты Edit2 и Label2. Убедитесь, что все наглядно отображено в Дереве Объектов(
Работа с Деревом Объектов( Если выделить какую-то вершину в дереве( соответствующий компонент выделится на форме( а в Инспекторе Объектов откроются его страницы свойств и событий( Так легко находится «потерявшийся» на форме компонент( Можно переносить компоненты из одного родительского контейнера в другой( просто перетаскивая в Дереве Объектов соответствующую вершину в другую родительскую вершину( например на другую панель( Компонент на форме при этом переместится в поле нового родителя(

Страница диаграмм Редактора Кода

Для представления связей между компонентами в виде диаграммы окно Дерева Объектов используется совместно со страницей Диаграмма Редактора Кода( Откройте эту страницу( В окне Имя назовите строящуюся диаграмму( например «Компоновка Form1». В окне Описание можно поместить текстовое описание диаграммы, например: «Диаграмма описывает компоновку компонентов на форме Form1: соотношение родительских и дочерних компонентов».
Выпадающий список слева вверху позволяет выбрать нужную диаграмму из нескольких диаграмм( составленных для данного модуля (в данный момент таковые отсутствуют)( Левая быстрая кнопка рядом со списком позволяет начать создание новой диаграммы( Правее ее расположена быстрая кнопка( удаляющая диаграмму(
Сначала основное поле диаграммы будет пустым( Чтобы занести в него компоненты( надо переместить мышью их вершины из окна Дерева Объектов( Между изображениями перемещенных компонентов автоматически появятся стрелки( описывающие связи между ними( Остается только расположить их так( чтобы диаграмма была наглядной( При этом можно перемещать и компоненты( и стрелки связей между ними( буксируя их мышью( Если потянуть одну из средних точек стрелки( стрелка изогнется( Так можно сделать линию стрелки кусочно-линейной( огибая ею какие-то препятствия(
Щелкнув на изображении какого-то компонента или стрелки правой кнопкой мыши( вызывают контекстное меню( в котором устанавливают цвет фона изображения или стрелки (раздел Цвет). Разделы От начала и С конца нужны( если изображения на диаграмме накладываются друг на друга полностью или частично( В этом случае первая команда выдвигает выделенное изображение на первый план( а вторая – перемещает на задний план(
Левая быстрая кнопка с надписью «A B C» позволяет создать на диаграмме блок( в котором можно написать произвольный поясняющий текст (Задание 2)(
Вторая слева кнопка Allude connector позволяет нанести на диаграмму связи( не возникающие автоматически( Например( если кнопка Button1 обрабатывает текст в окне Edit2( а кнопка Button2 обрабатывает текст окна Edit1( можно провести между этими программно связанными компонентами соединения( Для этого надо нажать кнопку Allude connector( затем нажать левую кнопку мыши на изображении того компонента( от которого нужно провести стрелку( а затем( не отпуская кнопку( провести линию к изображению другого компонента( Тогда соединительная линия в виде стрелки появится на экране( Можно щелкнуть на ней правой кнопкой и выбрать из контекстного меню один из разделов( определяющих начертание стрелки: Запуск с – изображение начала стрелки и Конец с – изображение ее конца, Цвет – цвет начала и конца стрелки, Цвет заливки – цвет линии.

Работа с группой компонентов

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

Перемещать компоненты группы одновременно( потянув курсором за один из выделенных компонентов; при этом взаимное расположение компонентов в группе не изменяется(
Задавать в Инспекторе Объектов общие для всей группы свойства( При выделении группы на странице свойств в Инспекторе Объектов будут видны только их общие свойства(
Задать общий для всех компонентов группы обработчик какого-то события(
Скопировать всю группу в буфер обмена Clipboard командой Правка|Копировать или «горячими» клавишами Ctrl-C. После этого можно всю группу перенести на новую форму из Clipboard командой Правка|Вставить или «горячими» клавишами Ctrl-V. Это простой способ переносить фрагменты с одной формы на другую( Так же можно переносить группы компонентов с одной панели на другую(
Выравнивать компоненты группы по размеру и взаимному расположению(

Задание 3( Разместить на форме метку Label и ниже( друг под другом( три окна редактирования с разными размерами – Edit1, Edit2 и Edit3( Выделить произвольную группу двумя способами( Выполнить доступные для группы операции (п.1, п.2, п.4).

Выравнивание компонентов по размеру и положению

При размещении компонентов на форме обычно трудно получить аккуратный вид окна( симметричное расположение компонентов( одинаковый размер их по горизонтали и вертикали( Для ре шения этих задач используют команды Правка|Выровнять – выравнивание размещения( Правка|Размер – выравнивание размеров и Правка|Масштаб – масштабирование( Те же команды проще выбрать не из меню Правка, а из раздела Позиция контекстного меню( которое всплывает( если щелкнуть правой кнопкой мыши на одном из компонентов группы (если щелкнуть в стороне( то выделение группы снимется)(
При выполнении команды выравнивания размеров откроется окно Размер( левая часть которого устанавливает ширину( а правая – высоту компонентов( Можно выбрать варианты: Без изменения – не изменять( По наименьшему – уменьшить до размера минимального из компонентов группы( По наибольшему – увеличить до размера максимального из компонентов группы( Ширина(Высота) – задать в окне рядом с этой радиокнопкой ширину(высоту) компонента в пикселах(
Пусть окна редактирования нужно сделать одинаковыми( Тогда надо выделить их в группу( выполнить команду выравнивания размеров и для обоих измерений указать( например( вариант По наименьшему (
При выполнении команды выравнивания размещения откроется окно Выравнивание( левая часть которого устанавливает выравнивание компонентов по горизонтали( Можно выбрать варианты: Без изменений – не изменять( Левая сторона – выровнять компоненты по их левым сторонам (т(е( левые стороны компонентов будут расположены друг под другом)( Центр – выровнять компоненты по их центрам( Правая сторона – выровнять компоненты по их правым сторонам( По месту – разместить с равными интервалами между компонентами( Центр окна – расположить в центре окна(
Правая часть окна устанавливает выравнивание компонентов по вертикали( Варианты аналогичны: Без изменений – не изменять( Верх – вы-ровнять компоненты по их верхним сторонам( Центр – по их центрам( Низ – по их нижним сторонам( По месту – разместить с равными интервалами по вертикали между компонентами( Центр окна – расположить в центре окна(

Примечания

При выравнивании по границам компонентов (Левая сторона, Правая сторона, Верх, Низ, Центр) на месте остается самый левый (выравнивание по горизонтали) или самый нижний (выравнивание по вертикали) компонент( а местоположение остальных подгоняется под него(
В режиме По месту крайние компоненты (левый и правый при выравнивании по горизонтали и верхний и нижний при выравнивании по вертикали) не перемещаются( Получающиеся интервалы определяются положением этих крайних компонентов и числом промежуточных( Следовательно( выравнивание интервалов можно применять после размещения крайних элементов( Если компоненты имеют разные размеры( то они могут даже наложиться друг на друга( Поэтому выравнивание по расстояниям имеет смысл только для компонентов равных размеров(
Режим Центр окна – расположить в центре окна – означает( что в центре окна расположится центр выделенной группы( а относительные сдвиги компонентов сохранятся неизменными( Если компоненты расположены на панели( то подразумевается центр этой панели(

Задание 4( На форме (см. задание 3) выполнить выравнивание размеров окон редактирования( Затем все компоненты формы – метку и три окна редактирования – разместить по горизонтали в центре экрана с одинаковыми вертикальными расстояниями между окнами редактирования(

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

Задание 5( Разместить на форме три панели( разместить на каждой из них по 3-4 различных компонента и выровнять с помощью палитры выравнивания панели и компоненты на панелях(

Команда Правка|Масштаб позволяет пропорционально изменить масштаб всего расположенного на форме (увеличить или уменьшить до ста раз)( В появляющемся диалоговом окне задают масштабирующий коэффициент в процентах(

Фиксация компонентов

После размещения и выравнивания компонентов их нужно зафиксировать( так как при выделении курсором можно сдвинуть тот или иной компонент( Чтобы этого не произошло( выполняют команду Правка|Контроль блокировки( Повторение команды снимет блокировку компонентов(

Депозитарий – хранилище форм и проектов

Чтобы попасть в Депозитарий( выполняют команду Файл|Новый| Другое. При этом открывается окно Депозитария – многостраничное диалоговое окно Новые элементы, в котором можно выбрать готовые формы или мастеры( Можно использовать Депозитарий и для хранения собственных разработок как на имеющихся, так и на собственных страницах.
Депозитарий позволяет не просто хранить формы( но и наследовать их( т(е( создавать иерархию форм( Это важно( поскольку в сложном приложении( содержащем много форм( все эти формы должны быть спроектированы в едином стиле( с единообразным расположением органов управления( ввода и редактирования данных( в единой цветовой гамме и т(п( Это легко делается созданием иерархии форм(

Задание 6. Сохранение формы в Депозитарии опробуйте на любой созданной ранее форме( например( на форме приложения( вычисляющего значение по формуле( Командой Файл|Открыть проект откройте проект PR_Formula. Добавьте на форму кнопки Button2 и Button3. Перед занесением формы в Депозитарий ее модуль должен быть обязательно сохранен в файле( Затем щелкните на форме правой кнопкой мыши и выберите во всплывшем контекстном меню раздел Добавить в хранилище. Откроется диалоговое окно Добавить к хранилищу( которое заполняется следующим образом( В окне Название пишут название формы Формула( которое станет подписью под пиктограммой формы при входе в Депозитарий( В окне Описание пишут развернутое пояснение Форма вычисляет z=f(x,y)( В выпадающем списке Страница выбирают страницу Депозитария( на которой будет помещена пиктограмма формы( Можно указать и новую страницу с новым заголовком (укажите Мои формы). В окно Автор впишите Я. Можно отказаться от стандартной пиктограммы (представлена внизу слева от Выбор значка для этого объекта) и выбрать другую( щелкнув на кнопке Обзор. После выполнения всех этих процедур щелкните на кнопке OK, и форма окажется включенной в Депозитарий(
Теперь эту форму можно использовать в последующих приложениях( Для этого нужно выполнить команду Файл|Новый|Другое и в открывшемся окне Новые элементы на странице Мои формы отыскать занесенную в Депозитарий форму( Для получения описания формы правой кнопкой мыши вызовите контекстное меню и выберите пункт Вид деталей.
Способ заимствования формы из Депозитария определяют радиокнопки в нижней части окна: Копия – копировать( Унаследовать – наследовать( Использовать – использовать( Если включена Копия, то файлы формы будут скопированы в приложение( и никакой дальнейшей связи между исходной формой и копией не будет( При включении Унаследовать помещенная в проект форма будет наследовать форму в Депозитарии( Это значит( что если в последней будет что-то изменено( то изменение отразится при перекомпиляции во всех проектах( которые наследуют эту форму( Однако изменения в наследуемых формах никак не скажутся на свойствах формы( хранящейся в Депозитарии( При включении Использовать используется сама форма( хранящаяся в Депозитарии( Заимствование формы из Депозитария завершается нажатием OK(

Задание 7. Опробовать режимы заимствования форм из Депозитария(
Начните новый проект( удалите из него пустую форму (команда Проект|Удалить из проекта или соответствующая быстрая кнопка) и введите в него форму из Депозитария (команда Файл|Новый|Другое) при включенной кнопке Копия. Назовите форму (свойство Name) FCopy и сохраните ее модуль (команда Файл|Сохранить как) под именем UFCopy.
Введите в проект второй экземпляр формы из Депозитария при включенной кнопке Унаследовать, назовите ее FInherit и сохраните модуль этой формы под именем UFInherit.
Введите в проект третий экземпляр формы из Депозитария при включенной кнопке Использовать и также сохраните. Имя этой формы и имя модуля при сохранении изменять не надо(
Используя быструю кнопку Вид формы, установите во всех формах свойство Visible (видимость) в true(
Сохраните проект( Выполните его и убедитесь( что все три формы одинаковы( Для просмотра форм можно использовать быструю кнопку Вид формы или «горячие» клавиши Shift-F12.
Теперь удалите Button2 в форме FCopy( сохраните проект( откомпилируйте модуль и выполните проект( Убедитесь( что изменение затронуло только форму FCopy(
Теперь удалите Button3 в форме( введенной с помощью кнопки Использовать. Сохраните проект и откомпилируйте модуль( После выполнения проекта убедитесь( что изменения отражаются не только в этой форме( но и в форме FInherit(
Изменив в форме Finherit РАСЧЕТ на ВЫЧИСЛИТЬ( сохраните проект( откомпилируйте модуль и выполните проект( Убедитесь( что обратной связи от формы FInherit к форме( хранящейся в Депозитарии( нет( Более того( если изменить какое-то свойство в Finherit( оно перестает наследоваться( Последующие изменения этого свойства в основной форме (измените РАСЧЕТ на ПУСК) никак не повлияют на FInherit( Оказывается( что в форме FInherit удалить какой-либо компонент невозможно (например, Button2)( Тогда ненужные компоненты делают невидимыми (установить свойство Visible равным false) и недоступными (свойство Enabled – в false). Тогда во время выполнения (убедитесь в этом на Button2 в форме FInherit ) они как бы исчезнут с формы(
В Депозитарий можно включать целые проекты( Чтобы включить проект в Депозитарий( нужно открыть проект и выполнить команду Проект|Добавить к хранилищу. Дальше – как при включении формы(
Выбор проекта из Депозитария начинается с команды Файл|Новый| Другое в режиме Копия. Сразу же предлагается диалоговое окно выбора каталога( в котором нужно сохранить копию проекта( После этого можно обычным образом работать с этой копией и вносить в нее любые изменения(
Для удаления из Депозитария форм и проектов используется команда Инструменты|Хранилище. При этом открывается окно объектов Депозитария Хранилище объекта( позволяющее реорганизовать Депозитарий( То же самое произойдет( если в окне Депозитария Новые элементы щелкнуть правой кнопкой мыши и выбрать из контекстного меню раздел Свойства.
В левой панели окна выбирают одну из страниц( а в правой панели просматривают содержимое этой страницы( Можно добавить( удалить( переименовать страницы Депозитария( поменять их последовательность с помощью кнопок со стрелками( выделить один из хранящихся объектов и удалить его( отредактировать информацию об объекте(
Можно также выделить в правой панели одну из форм и включить флажок Главная форма( Тогда при открытии нового проекта будет появляться не обычная пустая форма( а именно эта помеченная в Депозитарии как главная(
Если для одной из форм включить флажок Новая форма( то именно эта форма( а не пустая( будет включаться в проект при выполнении команды Файл|Новый|Форма.
Если выделить в правой панели не форму( а проект (включенный пользователем или один из расположенных на странице Проекты), то вместо индикаторов Главная форма и Новая форма появится индикатор Новый проект. Если его включить( то именно этот проект будет в дальнейшем открываться при создании нового проекта: при выполнении команды Файл|Новый|Приложение и при щелчке на пиктограмме приложения в окне Новые элементы.

Задание 8( Включить проект в Депозитарий( выбрать проект из Депозитария( удалить форму( проект и новую страницу (если она была введена) из Депозитария( Удалить все файлы и каталоги( созданные на жестком диске во время выполнения работы(




Контрольные вопросы

Когда и как включают в проект новую форму?
Как избежать совместного владения формой несколькими приложениями?
Как получить автономную копию формы?
Как просмотреть формы и модули из других проектов?
Расскажите о вариантах переноса компонентов из библиотеки на форму.
Как найти компонент по имени и перенести его на форму?
Как узнать размеры компонента?
Какие компоненты являются родительскими? Как узнать родителя какого-либо компонента?
Какие свойства своего родителя наследует компонент?
Какие свойства связывают дочерние компоненты с родительскими?
Какой компонент является владельцем данного компонента? Приведите примеры.
Что такое Z-последовательность? Как она создается? Как ей пользоваться?
Как переносить компоненты с панели на другую панель или на форму?
Как управлять видимостью компонентов (и каких) при «многослойном» размещении компонентов на форме?
Когда не удается найти помещенный на форму компонент? (три случая).
Расскажите о двух способах поиска «пропавших» компонентов.
Где можно наблюдать связи между компонентами?
Как работать с деревом объектов?
Как построить диаграмму связей между компонентами?
Как работать с группой компонентов (выделить, одновременно перемещать, задавать общие свойства, задать общий обработчик события, скопировать, переносить на новую форму, выравнивать компоненты группы по размеру и взаимному расположению)?
Как выравнивать компоненты по размеру и положению?
Как изменить масштаб всего расположенного на форме?
Как зафиксировать положение компонентов?
Расскажите о возможностях, предоставляемых депозитарием.
Как сохранить форму в депозитарии? Как использовать сохраненную форму в последующих приложениях? Как удаляются формы из депозитария?
Расскажите о режимах заимствования форм из депозитария.
Как проекты включаются в депозитарий, выбираются и удаляются из депозитария?
Как реорганизовать депозитарий?





Библиографический список

1. Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 97–113(
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.



ЗАНЯТИЕ 2

Отладка приложений

Компиляция и компоновка проекта

Компиляция с последующим выполнением приложения осуществляется командой Запуск|Запустить или соответствующей быстрой кнопкой( или «горячей» клавишей F9. В этом случае производится компиляция программы( ее компоновка (соединение)( и, если не будут обнаружены неисправимые ошибки( то создается выполняемый модуль .exe и запускается на выполнение(
В процессе компиляции и компоновки на экране появляется окно( в верхней строке которого выводится имя компилируемого проекта( во второй строке – текущая операция (компиляция определенного модуля или компоновка)( в третьей строке – текущая строка модуля( обрабатываемая компилятором( и общее число строк в модуле( в нижней строке отображается обнаруженное на данный момент число замечаний( предупреждений и ошибок( Клавиша Отмена позволяет прервать процесс компиляции и компоновки(
При компиляции проекта( состоящего из нескольких модулей( компилируются только те модули( тексты которых были изменены с момента предыдущей компоновки проекта( Это существенно экономит время компиляции(
При выполнении команды Запустить можно задать командную строку( если приложение предусматривает передачу в него каких-то параметров( Для этого надо сначала выполнить команду Запуск|Параметры и в открывшемся окне написать требуемую командную строку(
В случаях( когда требуется проверить на правильность последние изменения кода( не теряя время на выполнение проекта( пользуются командами: Проект|Компилировать модуль, Проект|Сделать проект или Проект|Создать проект.
Команда Компилировать модуль выполняет компиляцию только того модуля( который выделен в окне Редактора Кода или в Менеджере Проектов( и позволяет наиболее быстро проверить наличие ошибок или замечаний( Если компиляция прошла успешно( создается объектный файл .obj откомпилированного модуля(
Команда Сделать проект выполняет компиляцию всех тех модулей проекта( тексты которых были изменены с момента предыдущей компоновки проекта( Если компиляция прошла успешно( создаются объектные файлы модулей .obj и осуществляется компоновка программы( Если и она прошла успешно( то создается выполняемый модуль .exe( Таким образом( отличие Сделать проект от Запустить только в том( что после компоновки не производится выполнение приложения(
Команда Создать проект отличается от Сделать проект только тем( что компилируются все модули( независимо от того( когда они в последний раз изменялись( Выполнение этой команды требует наибольшего времени(
Есть еще две команды компиляции – для всех проектов группы –Сделать все проекты и Создать все проекты.

Сообщения компилятора и компоновщика

На примере простого приложения с ошибочными операторами выясним( какие сообщения об ошибках и какие предупреждения выдает компилятор( Начните новое приложение( перенесите на форму метку Label и кнопку Button( В обработчик щелчка кнопки введите следующие операторы:
void _fastcall TForm1::Button1Click(Tobject *Sender)
{
int i,j;
double A;
for(i=0; i<50;i++)
A*=10000; //Увеличение A в 10000 раз
Label1->Caption = “A = “ +B;
}
Установите режим( наиболее удобный для отладки( при котором компилятор отображает все свои замечания( Для этого в окне опций проекта( вызываемом командой Проект|Опции на странице Компилятор в группе опций Предупреждения( нужно включить опцию Все. Затем выполните команду Запуск|Запустить или нажмите соответствующую быструю кнопку( или нажмите клавишу F9.
Внизу окна Редактора Кода появятся сообщения о замечаниях и ошибках( Первое сообщение:
[C++ Warning] Unit1.cpp (23): W8013 Possible use of A’ before definition
([C++ Предупреждение] модуль Unit1.cpp( строка 23: W8013 Переменная A’( возможно( используется до того( как ей присвоено значение)
Это предупреждение о том( что переменная A не инициализирована и ее значение к моменту первого выполнения оператора в строке 23 не определено( Чтобы узнать( что это за строка( нужно дважды щелкнуть на этом предупреждении( В окне Редактора Кода выделится соответствующая строка:
A*=10000; //Увеличение A в 10000 раз
Следовательно( объявление переменной нужно изменить так:
double A=1;
Чтобы в переменной A накапливался результат при каждом щелчке на кнопке( сделаем ее глобальной(
Перейдем к второму сообщению компилятора:
[C++ Error] Unit1.cpp (24) : E2451 Undefined symbol B’
([C++ Ошибка] модуль Unit1.cpp( строка 24: E2451 Необъявленный идентификатор B’)
Это уже сообщение об ошибке(
+В операторе
Label1->Caption = “A = “ +B;
вместо переменной A указана переменная B( которая не была объявлена( Строка кода с этой ошибкой выделена в окне Редактора Кода, и курсор остановился около необъявленного идентификатора(
Поскольку ошибка неисправима( выполняемый модуль не формируется и приложение не выполняется(
Третье сообщение компилятора:
[C++ Warning] Unit1.cpp (25): W8080 j’ is declared but never used
([C++ Предупреждение] модуль Unit1.cpp( строка 25: W8080 Переменная j’ объявлена( но нигде не используется)
Объявление переменной j как ненужной нужно удалить из текста(
Последнее сообщение компилятора:
[C++ Warning] Unit1.cpp (25): W8057 Parameter Sender’ is never used
([C++ Предупреждение] модуль Unit1.cpp( строка 25: W8057 Параметр Sender’ нигде не используется)
Параметр Sender передается в обработчик событий и является компонентом( в котором произошло событие( В данном обработчике он не нужен( поэтому это предупреждение можно игнорировать(
Итак( приложение не откомпилировалось из-за ошибки с использованием необъявленной переменной B(
После исправления в ошибочном операторе переменной B на A появится новое сообщение об ошибке:
[C++ Error] Unit1.cpp (24) : E2060 Illegal use of floating point
([C++ Ошибка] модуль Unit1.cpp( строка 24: E2060 Недопустимое использование плавающей точки)
В правой части оператора Label1->Caption = “A = “ +A;
складываются строка “A = “ и переменная с плавающей точкой A( Это ошибка использования несовместимых типов(
После всех исправлений код примет вид
double A=1;
void _fastcall TForm1::Button1Click(Tobject *Sender)
{
int i;
for(i=0; i<50;i++)
A*=10000; //Увеличение A в 10000 раз
Label1->Caption = “A = “ + FloatToStr(A);
}
Теперь откомпилируем приложение и выполним его(

Действия в случае ошибки выполнения

Щелкнув на кнопке( убедимся( что приложение работает( В метке появится текст “A=1E200”. Однако при повторном щелчке выполнение прерывается( На экране появится окно с сообщением отладчика( которое после перевода выглядит так: “Проект Project1.exe вызвал генерацию исключения класса EOverflow с сообщением Переполнение при операции с плавающей запятой’. Процесс остановлен( Используйте команды Step или Run для продолжения”.
Это сообщение об ошибке( приведшей к генерации исключения( Исключения (exceptions) генерируются при различных ошибках – исключительных ситуациях(
Щелкнув на кнопке OK( попадем в окно Редактора Кода и увидим в нем код программы с выделенной строкой( около которой стоит зеленая стрелка( Это тот оператор( при выполнении которого произошла ошибка(

Дальнейшие действия

Командой Запуск/Сброс программы, или нажав Ctrl-F2, прерывают выполнение и отладку приложения( Поступают так в случае( когда понятно( где ошибка и как ее исправить( Если же ошибка непонятна( то( прежде чем прервать сеанс работы с приложением( надо получить дополнительную информацию о состоянии переменных( т(е( провести отладку(
Можно выполнить команду Запуск|Запустить (или нажать соответствующую быструю кнопку( или клавишу F9)( чтобы попытаться( несмотря на ошибку( продолжить вычисления( При этом появится окно с сообщением о виде ошибки (“Floating point overflow”)( после закрытия которого можно продолжить работу с приложением( Но это ничего не даст( так как при очередном щелчке на кнопке ситуация с ошибкой повторится(
Можно пройти часть программы по шагам (см( далее)( Однако предварительно нужно получить соответствующую информацию, не прерывая сеанс работы с приложением. Итак( если ошибка неясна, надо получить информацию о происходящих в приложении процессах( приведших к ошибке( Простейший способ – воспользоваться Мастером оценки выражений( Для этого подведем курсор мыши к имени переменной( например A, и увидим текст: “A=1E+308”. Так можно узнать значения переменных программы в данный момент(

Окно наблюдения Список Часов

Это окно позволяет иметь значения нескольких переменных сразу и сравнивать их, что удобно при отладке сложных приложений. Чтобы сделать окно видимым, нужно выполнить команду Вид|Windows отладки|Часы или подвести курсор к требуемой переменной (или выделить ее) и нажать Ctrl-F5. При этом окно наблюдения автоматически откроется и в нем появится имя переменной и ее значение A:1E+308 (значение переменной появится только при остановке выполнения приложения). Можно выделить и выражение, например, Label1->Caption, нажать Ctrl-F5, и в окне появится это выражение. Сообщение “Undefined symbol Label1’” – не определен символ Label1’ объясняется тем, что для окна наблюдения компонент Label1 по умолчанию не считается принадлежащим текущей форме. Добавив Form1-> перед Label1 в обработчике, после выделения выражения Form1->Label1->Caption и нажатия Ctrl-F5 в окне наблюдения увидим выражение и его значение “A:1E200”.
Работа с окном наблюдения. Перейдя в него, после щелчка правой кнопкой во всплывшем меню можно выбрать ряд команд. При выборе команд Правка часов и Добавить часы, попадаем в окно Свойства часов. Попасть в окно Свойства часов можно и по-другому – в окне наблюдения нажать Ctrl-F5 или сделать двойной щелчок. В окне Выражение можно записать имя любой переменной или любое выражение, содержащее переменные, константы, функции. Окно Повтор счета используется при наблюдении массивов и позволяет задать число наблюдаемых элементов массива. Например, если в окне Выражение указать имя массива, то в окне наблюдений будут отображаться все элементы этого массива. Если же указать первый элемент массива (с индексом 0), а в окне Повтор счета написать 5, то в окне наблюдений будут отображаться только первые 5 элементов массива. Окно Цифр указывает число выводимых значащих разрядов чисел с плавающей запятой. Окно Включить позволяет отключить вывод в окно наблюдения соответствующего выражения во время выполнения приложения, что повышает производительность выполнения. А после того, как приложение остановлено и данное выражение нужно посмотреть в окне наблюдения, выражение выделяют в этом окне и делают на нем двойной щелчок. Откроется окно Свойства часов с загруженным в него выражением и останется только включить индикатор Включить и щелкнуть OK. Индикатор Позволить побочный разрешает или запрещает отображение таких выражений, которые способны вызвать побочные эффекты. Например, в окне Выражение записано ++A. Если индикатор Позволить побочный выключен (он выключен по умолчанию), то в окне наблюдений рядом с выражением ++A появится текст: “Side effects are not allowed.” (побочные эффекты запрещены). Очевидно, если отображать указанное выражение, то значение переменной A в программе изменится. Отображение значения, на единицу большего A, будет иметь место при включении индикатора Позволить побочный. Но следует учесть, что это будет изменять значение переменной A. Радиокнопки в нижней части окна Свойства часов задают формат вывода значения переменной или выражения. По умолчанию формат определяется автоматически по типу отображаемого выражения. Выпадающий список в окне Выражение позволяет выбрать выражение из использовавшихся ранее. В окне Свойства часов имеется выпадающий список
· имя группы. По умолчанию создается одна группа
· Часы. Закладка этой группы видна в окне Список часов. Но можно разбить наблюдаемые величины на несколько групп (страниц). Для задания новой группы в контекстном меню окна наблюдения надо выбрать раздел Добавить группу и затем указать имя новой группы. Тогда в окне наблюдения появится новая страница, и на нее можно будет заносить новые наблюдаемые величины, выбирая в окне Свойства часов в списке Имя группы соответствующее имя.
Список выражений в окне наблюдения можно редактировать. Для удаления выражения его выделяют и нажимают Delete. Чтобы отредактировать ошибочное выражение, делают на нем двойной щелчок, а затем редактируют в окне Свойства часов.
Вернемся к примеру. В момент появления ошибки выполнения значение A равно 13 EMBED Equation.3 1415, а следующее значение должно быть 13 EMBED Equation.3 1415, которое переменная типа double хранить не может. Теперь можно нажимать Ctrl-F2, прерывать выполнение и исправлять код.

Окно оценки и модификации Оценить|Изменить

Это окно позволяет в процессе отладки не только наблюдать, но и изменять значения переменных. Сделать это окно видимым можно командой Запуск|Оценить|Изменить или командой Отладка|Оценка|Изменение, которую выбирают из контекстного меню, всплывающего при щелчке правой кнопкой в окне Редактора Кода. В окне Выражение можно ввести имя переменной или выражение. Щелкнув на кнопке Оценка, увидим в окне Результат значение этого выражения. Если же в окне Выражение указать имя переменной, а не выражение, то становится доступной кнопка Изменение, позволяющая изменить значение переменной. Следовательно, имеется возможность вмешиваться в процесс выполнения приложения и насильственно изменять значения переменных.
Итак, в окне Выражение укажем имя переменной A, щелкнем на кнопке Оценка и в окне Результат увидим ее значение. В окне Новое значение напишем A/2e300 и щелкнем на кнопке Изменение. В результате значение переменной в приложении изменится, что видно в окне Результат (50000000). Перейдя в окно наблюдения (кнопка Часы), там также увидим измененное значение (A:50000000). Поскольку значение A было резко уменьшено, можно продолжить выполнение приложения. Нажав F9, убедимся, что ошибка переполнения появилась вновь.
Отметим, что возможность оперативно исправить значения переменных, вызванные ошибкой, и продолжить отладку, не запуская выполнение опять сначала, позволяет существенно ускорить отладку при большом времени выполнения приложения.

Пошаговое выполнение приложения

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

Команда
«Горячие»
клавиши
Пояснения

Через шаг
F8
Пошаговое выполнение строк программы, считая вызов функции за одну строку, т.е. вход в функции не производится

След в
F7
Пошаговое выполнение программы с заходом в вызываемые функции

Шаг на следу-ющую линию
Shift+F7
Переход к следующей исполняемой строке

Выполнить до курсора
F4
Команда выполняет программу до того выполняемого оператора, на котором рас-
положен курсор в окне редактора кода

Выполнить до возвращения
Shift+F8
Выполнение программы до выхода из текущей функции, останов на операторе, следующем за вызовом этой функции

Показать точку выполнения

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


Испытаем команды на рассматриваемом примере. Выведем переменную A и выражение Form1->Label1->Caption в окно наблюдения Список часов. Это окно можно встроить в Инспектор Объектов. В режиме проектирования окно наблюдения будет храниться на отдельной странице позади Инспектора Объектов, а во время выполнения приложения страницы Инспектора Объектов будут исчезать, и при остановах можно наблюдать в окне наблюдения значения переменных. Для этого надо сохранить описанную (или любую другую) конфигурацию отладочных окон с помощью команды Вид|Рабочие столы|Сохранить рабочий стол и затем командой Вид|Рабочие столы|Настройка отладки рабочего стола задать эту конфигурацию как отладочную. Проще осуществлять подобные операции с конфигурациями соответствующими быстрыми кнопками вверху окна ИСР.
Выведя переменную и выражение в окно наблюдения Список часов и встроив это окно в Инспектор Объектов, перейдем в код и откомпилируем модуль (Alt+F9). Поместим курсор на строке с оператором A*=10000; Теперь нажмем F4, чтобы приложение выполнялось до строки с курсором. Приложение начнет выполняться. Нажмем в нем кнопку. Перейдем в Редактор Кода. В нем будет выделена строка, на которой стоял курсор перед выполнением. Теперь, нажимая F7 или F8 (в данном случае все равно), выполняем операторы по шагам, а в окне наблюдений следим за изменением переменной и выражения.
Через несколько циклов переведите курсор на оператор, следующий после цикла и задающий значение Label1->Caption. Нажмите F4. Тем самым отладчику указано, что ему надо без остановов выполнять приложение до строки, указанной курсором. Все оставшиеся проходы цикла будут выполнены без остановов, и программа остановится на указанной строке. Если после этого нажать клавишу F7 или F8, то результат будет различным. При нажатии F8 произойдет переход к закрывающей фигурной скобке. А при нажатии F7 сначала попадем в заголовочный файл dstring.h. В этом файле объявлены функции работы со строками типа AnsiString, к которым неявно обращается оператор программы. После нескольких нажатий F7 произойдет возврат в программу и переход к закрывающей фигурной скобке.

Точки прерывания

Чтобы ввести простую (безусловную) точку прерывания, достаточно в окне Редактора Кода щелкнуть на полоске левее кода требуемой строки. Строка станет красной, и на ней появится красная точка. (Для удаления точки прерывания достаточно щелкнуть на красной точке.) Если запустить приложение на выполнение, то произойдет прерывание выполнения, как только управление перейдет к строке, в которой указана точка прерывания. Можно одновременно указать несколько точек прерывания в разных местах кода и в разных модулях. Кроме того, в этих точках можно установить условия прерывания.
Точки прерывания можно устанавливать только на выполняемых операторах. Если, например, попробовать установить точку прерывания на строке, содержащей объявление переменной, то в момент запуска приложения в красной точке появится крестик. Это предупреждение о том, что здесь прерывания не будет. Установим контрольную точку на операторе
A*=10000; //Увеличение A в 10000 раз
Если задержать курсор над красной точкой, то увидим всплывший ярлык с характеристиками данной точки прерывания. По умолчанию никаких условий останова не задается. Зададим следующие условия: нужно остановиться на введенной точке прерывания, но только на 26-м цикле, причем только при повторном проходе цикла (повторном щелчке пользователя на кнопке приложения).
Щелкните правой кнопкой на красной точке и из всплывшего меню выберите Свойства контрольной точки. Откроется окно Исходные Свойства Контрольной точки.
Два верхних окна редактирования
· Имя файла и Номер линии (номер строки файла) в данном режиме недоступны. Они автоматически заполнились в момент, когда была установлена точка прерывания.
В окне Условие укажем условие останова i==26. В окне Счет прохода зададим значение 2.
Окно Группа позволяет задать имя группы, к которой относится данное прерывание. Группировка прерываний позволяет сделать целиком ту или иную группу доступной или недоступной. Это делается с помощью окон Вкл. группу и Выкл. группу, которые становятся доступными после нажатия кнопки Дополнительно. В этих окнах имя группы выбирается из выпадающих списков, содержащих имена введенных ранее групп. В целом, в нижней части окна задания свойств прерывания указываются некоторые действия, выполняемые в момент прерывания. Индикатор Перерыв, установленный по умолчанию, обеспечивает стандартную реакцию
· останов выполнения приложения. Индикаторы Игнорировать последующее исключение и Обрабатывать последующее исключение (установлен может быть только один) определяют появление при генерации очередного исключения сообщения отладчика. Если установлен второй индикатор, то сообщение появляется. Если не установлен ни один индикатор, то все определяется настройками отладчика на странице Исключения языка окна настройки отладчика, вызываемого командой Инструменты|Опции отладчика. Пока в перечисленные элементы окна Исходные Свойства Контрольной точки (начиная с окна Группа) никакие изменения вносить не будем.
Список введенных точек прерывания хранится в памяти, и поэтому в любой момент его можно увидеть (и до выполнения программы, и при останове), выполнив команду Вид | Windows отладки | Контрольные точки. Окно Список контрольных точек можно встраивать в окно наблюдения Список часов и фиксировать такую конфигурацию окон как отладочную. Тогда в моменты останова будут видны и наблюдаемые величины, и информация о точках прерывания. В Список часов внесите переменную A и переменную i.
После внесения данных в окно Исходные Свойства Контрольной точки щелкните на OK, откомпилируйте модуль (команда Проект|Компилировать модуль) и запустите выполнение приложения. После первого нажатия кнопки приложения останова не произойдет, а после второго нажатия выполнение остановится. Отметим, что в этот момент A=1E+304, а i=26. Затем, находясь в Редакторе Кода, пройдите по шагам (клавишей F7) последний цикл перед появлением ошибки, наблюдая за окнами Список контрольных точек и Список часов.
В окне Исходные Свойства Контрольной точки, нажав Дополнительно, в окно Log сообщение запишем сообщение “останов при i=26 во второй раз”, которое будет появляться в момент прерывания в окне Журнал событий, вызываемого командой Вид|Windows отладки|Журнал событий. В окно Eval выражение запишем некоторое выражение (в данном случае просто имя переменной
· A), которое будет вычисляться при прерывании. При установленном индикаторе Log результат это выражение и результат его вычисления можно увидеть в том же окне Журнал событий. Выключим индикатор Перерыв и включим индикатор Игнорировать последующее исключение. При этом никаких остановов выполнения приложения в точке прерывания или при генерации исключения не будет происходить. Но после завершения выполнения приложения в Журнале событий окажутся соответствующие сообщения. Нажмем Ok.
Откомпилируем модуль (Alt+F9) и запустим выполнение приложения. После генерации исключения и выполнения команды Вид|Windows отладки|Журнал событий увидим строки со следующим содержанием:
Breakpoint Message: останов при i=26 во второй раз Process Project1.exe Breakpoint Expression A: 1E+304 Process Project1.exe
Это введенное ранее сообщение (останов при i=26 во второй раз) и выражение (A) с посчитанным результатом.
В нашем примере, где каждый проход запускается нажатием кнопки приложения, очевидно, что ошибка происходит именно при втором проходе цикла. В более сложном варианте определение прохода, на котором происходит ошибка, позволяет сделать условная точка прерывания. Для этого в окне Исходные Свойства Контрольной точки укажем большое значение Счет прохода, например 50. Очевидно, ошибка будет зафиксирована раньше этой точки. Включим индикатор Перерыв и выключим индикатор Игнорировать последующее исключение. Откомпилируем модуль и запустим выполнение приложения. В окне Список контрольных точек будет указано, сколько произошло проходов через точку прерывания с выполнением заданного условия (2 из 50).
В окне Список контрольных точек щелкнем правой кнопкой и во всплывшем меню выберем Добавить. Предлагается три варианта: Исходная контрольная точка
· точка прерывания в файле приложения, Адрес точки
· прерывание при переходе управления по заданному адресу, и Точка данных
· прерывание при изменении данных по заданному адресу. Однако, если работа в окне Список контрольных точек производится не во время выполнения приложения, две последние команды будут недоступны. Это связано с тем, что пока приложение не выполняется, адреса команд и переменных еще не известны.
Выполним пример использования прерывания при изменении данных по заданному адресу. В нашем приложении только один оператор изменяет значение переменной A. В более сложном приложении таких операторов может быть несколько, и поэтому будет неизвестно, какой из них увеличивает значение A настолько, что дальнейшее выполнение приложения приведет к переполнению, или какой оператор заносит в A недопустимое значение. В подобных случаях помогает установка прерывания при изменении данных по заданному адресу
· в нашем примере по адресу, отведенному для A. Здесь не устанавливают, на какой строке кода хотят прервать выполнение. Прерывание произойдет на том операторе, который занесет в указанную переменную некоторое значение, если будут выполнены указанные условия.
Воспользуемся оператором, заносящем новое значение в адрес A, причем при значении A>13 EMBED Equation.3 1415. Поскольку отладчик не знает адреса переменной, пока приложение не загружено в память, то прежде чем задавать точку прерывания, надо запустить приложение на выполнение. Затем, не закрывая приложения и ничего с ним не делая, т.е. не щелкая на кнопке приложения, вводим команду Вид|Windows отладки|Контрольные точки и в открывшемся окне Список контрольных точек щелкнем правой кнопкой мыши. Во всплывшем меню выберем команду Добавить. Из предложенных трех вариантов выберем вариант Точка данных
· прерывание при изменении данных по заданному адресу. Откроется окно Добавить контрольную точку данных, подобное окну Исходные Свойства Контрольной точки.
Окно заполняем следующим образом. В окно Адрес заносим переменную A, при изменении которой наступает прерывание. Окно Длина заполняется автоматически. В окна: Условие заносим A>1e299, Счет прохода
· 0, Группа
· 1, ниже кнопки Дополнительно: Сообщение
· значение A>1e299, Eval выражение
· A, Вкл. группу
· 1, Выкл. группу
· 0. Последним действием (занесли 0 в окно Выкл. группу) начинаем отключение прежней точки прерывания. Для завершения отключения выделим ее в окне Список контрольных точек, щелкнем правой кнопкой и выберем Свойства. В открывшемся окне Свойства исходных контрольных точек в окно Группа занесем 0, в окно Вкл. группу
· 1, а в окно Выкл. группу
· 0. Однако выключить прежнюю точку прерывания можно проще: после щелчка правой кнопкой на строке прежней точки прерывания в окне Список контрольных точек в контекстном меню выбрать Удалить.
Теперь вернемся в выполняющееся приложение и щелкнем кнопкой приложения. Приложение будет выполняться, но значительно медленнее, чем раньше. После второго щелчка приложение остановится на операторе
A*=10000; //Увеличение A в 10000 раз
при состоянии приложения, когда A=1E+300, в чем можно убедиться в окне Список часов и в Журнале событий. Следовательно, прерывание, как и было заказано, произошло после того, как переменная A стала больше 1e299.

Примечание. Доступность точки прерывания по изменению переменной сохраняется только в течение данного сеанса выполнения приложения. По завершению выполнения точка становится недоступной, и при следующем запуске ее надо повторно активизировать. Для этого в контекстном меню, которое появится после щелчка правой кнопкой в окне Список контрольных точек, нужно выбрать Включить группу, а затем
· 1.
Добавить точки прерывания можно также командой Запуск|Добавить точку прерывания, первые три первых варианта которой
· Контрольная точка источника, Адрес контрольной точки и Данные контрольной точки совпадают с рассмотренными выше.
Имеется также возможность задать точку прерывания по изменению переменной из всплывающего меню окна Список часов (команда Перерыв при замене, доступная только во время выполнения). Это прерывание необходимо, когда надо понять, почему переменная начинает изменяться, а должна быть неизменной. Соответствующая точка возникает в списке точек прерывания, но доступна только в течение данного сеанса выполнения приложения. По завершению выполнения точка становится недоступной и при следующем запуске ее надо повторно активизировать, устанавливая с помощью контекстного меню индикатор Включить.

Использование окна Инспектора Отладки

Инспектор Отладки позволяет получить исчерпывающую информацию о любой переменной в приложении и дает возможность, как и окно Оценить|Изменить, изменить значение переменной и продолжить выполнение приложения с этим новым значением.
Вызов этого инструмента отладки возможен только во время выполнения приложения при останове средствами отладки или вследствие генерации исключения. При останове нужно поставить курсор в окне Редактора Кода и выполнить команду Запуск|Осмотреть. Другой способ
· вызвать команду Отладка|Осмотреть из всплывшего контекстного меню после щелчка правой кнопкой в окне Редактора Кода. Проще всего
· нажать «горячие» клавиши Alt-F5. Откроется окно Осмотреть, в которое занесем имя переменной A и щелкнем OK. Появится окно Инспектор Отладки, в котором содержатся сведения о переменной A. Нажав кнопку с многоточием, откроем окно Замена, в котором можно изменить значение переменной A, и оно изменится в выполняемой программе. Следовательно, при продолжении выполнения приложения оно будет выполняться с измененным значением переменной.
Инспектор Отладки позволяет исследовать различные данные: переменные, массивы, классы, функции, указатели. В качестве примера в окно Осмотреть занесем Form1
·>Label1 и, щелкнув OK, перейдем в окно Инспектор Отладки, которое имеет три страницы
· Данные, Методы, Свойства. Выбрав страницу Свойства, увидим все свойства компонента, их значения и функции их чтения и записи.
Если нужно изменить какое-то свойство (оно не может быть свойством только для чтения), его выделяют и нажимают появившуюся около него кнопку с многоточием. Появится окно изменения Замена, в котором можно ввести новое значение свойства.
Не все значения свойств могут быть в момент останова досчитаны до конца. В этом случае, если выделить курсором это свойство, около него появляется кнопка со знаком ?’. Выделим свойство Caption и увидим, что значение надписи метки не посчитано и не выведено в окне. Если нажать кнопку со знаком ?’, то значение будет досчитано и, пока эта кнопка нажата, при каждом останове выполнения свойство будет досчитываться до конца.
Находясь в окне Инспектора Отладки, можно щелкнуть правой кнопкой и выбрать одну из следующих команд:

Диапазон
Просмотр данных в заданном диапазоне

Замена
Перейти в окно Замена для изменения значения элемента

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

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

Сортировка по
Два варианта: по порядку объявления и по именам

Осмотреть
Открывает новое окно для выделения элемента данных. Это позволяет детальнее исследовать данные типа структур, классов, массивов и т.п.

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

Новое выражение
Эта команда позволяет задать новое выражение для анализа

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


Другие средства отладки

В ИСР имеется также инструментарий, позволяющий проводить отладку на более детальном уровне. Окно CPU (команда Вид|Windows отладки|CPU) позволяет отследить ход выполнения проекта на уровне команд макроассемблера. Окно FPU (команда Вид|Windows отладки|FPU) позволяет следить за ходом выполнения операций с плавающей запятой. Окно Стек запроса (команда Вид|Windows отладки|Стек запроса) позволяет определить последовательность вызванных функций, не только тех, которые вызываются явно приложением, но и всех неявно вызываемых функций библиотек. Окно Модули (команда Вид|Windows отладки|Модули) показывает список всех модулей, загруженных в память при выполнении приложения. Это окно позволяет, в частности, если выделить на его левой верхней панели имя модуля .exe, увидеть в левой нижней панели список использованных заголовочных файлов. Двойной щелчок на строке соответствующего файла загрузит его в Редактор Кода, и можно попробовать понять, что именно из данного файла использует приложение.
Контрольные вопросы
Какой командой осуществляется компиляция и компоновка проекта с последующим выполнением приложения? Перечислите способы ввода этой команды.
Что выводится в окно компиляции и компоновки?
Как компилируется проект, состоящий из нескольких модулей?
Какая команда позволяет задать командную строку? Какие возможности предоставляет командная строка?
Какие команды компиляции используются в случаях, когда требуется проверить на правильность последние изменения кода?
Каково назначение команды Компилировать модуль? Каков результат компиляции?
Как выполняется команда Сделать проект?
Чем отличается команда Сделать проект от команды Запустить?
Чем отличается команда Сделать проект от команды Создать проект?
Выполнение какой команды компиляции требует наибольшего времени?
Какие команды компиляции используются для всех проектов группы?
Приведите примеры сообщений компилятора и компоновщика.
Какой режим наиболее удобен для отладки? Как его установить?
Как установить оператор, при выполнении которого произошла ошибка?
Какими средствами отладки можно остановить выполнение приложения?
Какой командой прерывают выполнение и отладку приложения? Перечислите способы ввода этой команды.
Как пользоваться Мастером оценки выражений?
Расскажите о возможностях, предоставляемых окном наблюдения Список часов. Как сделать его видимым?
Расскажите о работе с окном наблюдения Список часов.
Каковы действия пользователя в случае ошибки выполнения?
Расскажите о возможностях, предоставляемых окном оценки и модификации Оценить/Изменить. Как сделать это окно видимым?
Как использовать окно оценки и модификации в процессе отладки?
Какие команды используются при пошаговом выполнении приложения?
Как окно Список часов встроить в Инспектор Объектов? Как пользоваться окном в режиме проектирования и при остановах во время выполнения?
Какой командой сохранить конфигурацию отладочных окон и какой командой задать эту конфигурацию как отладочную?
Расскажите о пошаговом выполнении приложения в сочетаниях F4-F7 и F4-F8.
Как вводить и удалять простые и условные точки прерывания? Приведите примеры.
Как узнать о выполнимости точки прерывания? Как узнать характеристики точки прерывания?
Как увидеть список введенных точек прерывания до выполнения программы и при останове?
Как пользоваться Журналом событий?
Расскажите об использовании прерывания командами Добавить/Исходная контрольная точка, Добавить/Адрес точки, Добавить/Точка данных. Какими командами еще можно сделать то же самое?
Когда используется команда Перерыв при замене из всплывшего меню окна Список часов?
Какие возможности предоставляет окно Инспектор Отладки?
Когда и как возможен вызов Инспектора Отладки?
Какие данные позволяет исследовать Инспектор Отладки?
Расскажите об инструментарии, позволяющем проводить отладку на детальном уровне.

Библиографический список
Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 120–140(
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.

ЗАНЯТИЕ 3

Компоненты ввода и отображения текстовой информации

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

Компонент
Страница
Описание

Label
(метка)
Стандарт
Отображение текста, который не изменяется пользователем. Оформления текста не предусмотрено, кроме цвета метки и текста. Основное свойство
· Caption

StaticText
(метка
с бордюром)
Дополнительно
Подобен Label, но обеспечивает возможность задания стиля бордюра. Основное свойство
· Caption

Panel (панель)
Стандарт
Контейнер для группирования органов управления. Может использоваться для отображения текста с возможностью объемного оформления. Основное свойство
· Caption

Edit
(окно редак-тирования)
Стандарт
Отображение, ввод и редактирование однострочных текстов. Можно оформить объемный бордюр. Основное свойство
· Text

MaskEdit
(окно маски-
рованного ре-
дактирования)
Дополнительно
Используется для форматирования данных или для ввода символов в соответствии с шаблоном. Основные свойства
· Text и EditText

LabeledEdit
(окно редакти-рования с меткой)
Дополнительно
Комбинация Label и Edit. Основные свойства
· Text и EditLabel.Caption

Memo
(многострочное окно редакти-рования)
Стандарт
Отображение, ввод и редактирование многострочных текстов. Можно оформить объемный бордюр. Основное свойство
· Lines

RichEdit
(многострочное окно редакти-рования в фор-
мате RTF)
Win32
Окно редактирования в стиле Windows в обогащенном формате RTF, позволяющее производить выбор атрибутов шрифта, поиск текста и др. Основное свойство
· Lines

ListBox
(окно списка)
Стандарт
Отображение стандартного окна списка Windows, позволяющего выбирать из него пункты. Основное свойство
· Items

CheckListBox
(список с инди-
каторами)
Дополнительно
Комбинация списка ListBox и индикато-ров CheckBox

ValueListEditor
(список специ-
ального вида)
Дополнительно
Окно редактирования списков вида
“имя = значение”. Основные свойства: Keys
· имена, Values
· значения

ComboBox
(редактируемый список)
Стандарт
Объединяет функции ListBox и Edit. Можно либо ввести текст, либо выбрать его из списка. Основное свойство
· Items

ComboBoxEx
(список текстов и изображений)
Win32
Выпадающий список с возможностью отображения текстов и изображений. Основное свойство
· Items

StringGrid
(таблица строк)
Дополнительно
Отображение текстовой информации в таблице из строк и столбцов с возможностью перемещаться по строкам и столбцам и осуществлять выбор. Основное свойство
· Cells



Компоненты Label, StaticText, Panel

Компоненты Label, StaticText, Panel используются для отображения различных надписей на форме. Основное назначение панели Panel
· компоновка компонентов в окне формы. Однако панель можно использовать и для вывода текстов.
Тексты, отображаемые в компонентах, являются значениями их свойства Caption. Его можно устанавливать в процессе проектирования или задавать и изменять программно во время выполнения приложения. Например: Label1->Caption = “Введен массив”;
Свойство Caption имеет тип строки AnsiString. При присваивании этому типу числовой информации происходит ее автоматическое преобразование в строку. Например, оператор Label1->Caption=3.4; приведет к появлению в метке надписи «3,4». Но если нужно занести в метку смешанную информацию, состоящую из строк символов и чисел, то необходимо воспользоваться функциями FloatToStr и IntToStr, переводящими соответственно числа с плавающей запятой и целые в строку, и операцией «+», которая для строк означает их конкатенацию (склеивание).
Во всех компонентах цвет фона определяется свойством Color, а цвет надписи
· подсвойством Color свойства Font.
Для метки Label цвет и шрифт
· единственно доступные элементы оформления надписи. Компоненты StaticText и Panel имеют еще свойство BorderStyle, определяющее рамку текста
· бордюр, и Panel
· BevelInner, позволяющее разнообразно оформлять надписи.
Вертикальный и горизонтальный размеры Label и StaticText определяются размером надписи, если свойство AutoSize установить в true.
В метке Label имеется свойство WordWrap
· допустимость переноса слов длинной надписи, превышающей длину компонента, на новую строчку. Для осуществления переноса нужно WordWrap установить в true, AutoSize
· в false, а горизонтальный и вертикальный размеры метки сделать такими, чтобы в ней могло поместиться нужное число строчек приемлемой длины.
В метке StaticText перенос длинного текста осуществляется автоматически, если AutoSize установить в false, а вертикальный размер метки
· достаточным для размещения нескольких строчек.
В панели размещение надписи в нескольких строчках невозможно.

Рассмотрим использование компонентов Label и StaticText на примере формирования и обменной сортировки массива из целых чисел.
Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект и командой Сохранить все сохраните файл модуля и проект под разными именами.
Перенесите в середину верхней части пустой формы метку Label1, установите ее свойство Color таким, чтобы метка была видна на форме, в свойство Caption впишите Исходный массив, в свойстве Font (шрифт) установите подсвойства Color
· clHotLight, Size
· 8 и, нажав на кнопку с тремя точками, установите шрифт
· курсив, цвет
· черный.
Перенесите на форму вторую метку Label2. Установите мышью ее размеры такими, чтобы она вместила 100 двузначных чисел, а также свойства Color
· clYellow, AutoSize
· false, WordWrap
· true, шрифт
· полужирный.
Перенесите в середину формы, ниже второй метки Label2, метку StaticText1. Установите шрифт курсив, размером 12. В свойство Caption напишите Отсортированный массив, в Color установите clCream.
Перенесите на форму вторую метку StaticText2. Установите свойство AutoSize в false, остальные
· аналогично второй метке Label2.
Перенесите в нижнюю часть формы кнопку Button1 и измените ее свойство Caption на ПУСК.
В заголовочном файле добавьте директиву #include
Задайте обработчик события (щелчок кнопки) следующим образом:
const int n=100;
//--------------------------------------------------------------------------
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·s1; // вывод исходного массива
// обменная сортировка массива
for(i=0;i for(j=i+1;j if(k[i]>k[j]) {z=k[i]; k[i]=k[j]; k[j]=z;}
for(i=0;i StaticText2->Caption=s2; // вывод отсортированного массива
}
//---------------------------------------------------------------------------
Сохраните проект и выполните приложение. Нажимая на кнопку ПУСК, убедитесь в работоспособности приложения.


Компоненты Edit, LabeledEdit, MaskEdit

Приведем вначале общие свойства однострочных окон редактирования Edit, LabeledEdit и MaskEdit.
Внешнее оформление определяется свойством BorderStyle. Вводимый и выводимый текст содержится в свойстве Text типа AnsiString. Это свойство можно устанавливать в процессе проектирования или задавать программно. Перенос строк невозможен. Текст, не помещающийся по длине в окно, просто сдвигается, и можно перемещаться по нему с помощью курсора. Свойство AutoSize позволяет устанавливать автоподстройку под размер текста только высоты, но не ширины окна.
Свойство AutoSelect определяет, будет ли автоматически выделяться весь текст при передаче фокуса в окно редактирования. Оно задается равным true, когда при переключении в данное окно пользователь будет заменять текущий текст, а не исправлять его.
Свойства только времени выполнения SelLength, SelStart, SelText определяют соответственно длину выделенного текста, позицию перед первым символом выделенного текста и сам выделенный текст.
Окна редактирования можно использовать и просто как компоненты отображения текста. Для этого надо установить в true их свойство ReadOnly и целесообразно установить AutoSelect в false. В этом случае пользователь не сможет изменять отображаемый текст, и окно редактирования становится подобным меткам. Отличия от последних
· окна иначе оформлены и могут вмещать текст, превышающий их длину. В этом случае пользователь может прокручивать текст, перемещая курсор в окне.
Свойство Text имеет тип строки AnsiString. При присваивании этому типу числовой информации происходит ее автоматическое преобразование в строку. Например, выполнение оператора Edit1->Text=5./2; приведет к появлению в окне текста «2,5». Но при вводе из окна числовой информации надо использовать функции StrToFloat
· преобразование строки в значение с плавающей запятой и StrToInt
· преобразование строки в целое значение.
Свойство MaxLength определяет максимальную длину вводимого текста. Если MaxLength=0, то длина текста не ограничена. В противном случае MaxLength указывает максимальное число символов, которое может ввести пользователь.
Свойство Modified, доступное только во время выполнения, показывает, проводилось ли редактирование текста в окне. В момент начала работы пользователя с текстом свойство надо поставить в false. Тогда при последующем обращении к нему можно по его значению (true или false) установить, было или не было редактирование.
В компоненте LabeledEdit, помимо самого окна редактирования, не отличимого по виду от Edit, имеется метка. Она задается свойством EditLabel с подсвойствами Caption, Color, Font, WordWrap и др.
Свойство LabelPosition компонента LabeledEdit указывает, с какой стороны от окна размещается метка: lpAbove
· сверху, выровненная по левому краю, lpBelow
· снизу, выровненная по левому краю, lpLeft
· слева, lpRight
· справа.
В компоненте MaskEdit можно задавать строку маски в свойстве EditMask. Это позволяет обеспечить синтаксически безошибочный ввод пользователем таких данных, как номера телефонов, паспортные данные, адреса, даты, время и т.п.

Рассмотрим использование компонентов Edit и LabeledEdit на примере ввода и вывода одномерного массива.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
На пустую форму, слева, в ее верхнюю часть, перенесите метку Label1, ниже ее
· окно Edit1, справа от окна
· кнопку Button1. В свойство Caption метки запишите Размер массива, свойство Text окна очистите, в свойство Caption кнопки запишите Ввод размера массива.
В правую верхнюю часть формы перенесите окно LabeledEdit1, правее окна поместите вторую кнопку Button2. Метку окна поместите сверху. В подсвойство Caption свойства EditLabel запишите Элемент массива, в свойство Caption второй кнопки
· Ввод элемента массива.
Ниже указанных выше компонентов поместите на форме второе окно LabeledEdit2, метку которого расположите слева. Очистите подсвойство Caption свойства EditLabel.
Ввод массива начинается с ввода его размера, и поэтому необходимо, чтобы в первый момент при выполнении приложения оказался в фокусе компонент Edit1. Для этого в свойство формы ActiveControl запишем Edit1.
Обработчики щелчков кнопок запишем следующим образом:
int n=0, i=0, k[100];
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit1->SetFocus();
LabeledEdit2->Text="";
LabeledEdit1->EditLabel->Caption="Элемент массиваk["+
IntToStr(i)+"]=";
n=StrToInt(Edit1->Text);
Label1->Caption="Размер массива = "+IntToStr(n);
Edit1->Text="";
LabeledEdit1->SetFocus();
i=0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
k[i]=StrToInt(LabeledEdit1->Text);
LabeledEdit1->SetFocus();
i++;
LabeledEdit1->EditLabel->Caption="Элемент массива k["+
IntToStr(i)+"]=";
if(i==n){LabeledEdit1->EditLabel->Caption="Массив введен!";
LabeledEdit2->EditLabel->Caption="Введен массив";
AnsiString s1="";
for(i=0;i LabeledEdit2->Text=s1;
Edit1->Text="";
LabeledEdit1->Text="";
Edit1->SetFocus();
i=0; }
}
//---------------------------------------------------------------------------
Сохраните проект и выполните приложение. Убедитесь в работоспособности приложения, вводя массивы разных размеров.


Компоненты Memo, RichEdit

Компоненты Memo и RichEdit являются окнами редактирования многострочного текста. Рассмотрим сначала основные отличия компонентов.
В Memo формат (шрифт, его атрибуты, выравнивание) одинаков для всего текста и определяется свойством Font.
RichEdit работает с текстом в обогащенном формате RTF. Атрибуты вновь вводимого фрагмента текста можно задать свойством SelAttributes, которое имеет подсвойства: Color (цвет), Name (имя), Size (размер), Style (стиль) и др. Устанавливаемые атрибуты влияют на выделенный текст или, если ничего не выделено, на атрибуты нового текста, вводимого, начиная с текущей позиции курсора (определяется свойством SelStart). В RichEdit есть также свойство DefAttributes (доступно только во время выполнения), содержащее атрибуты по умолчанию, которые действуют, пока не изменятся атрибуты в свойстве SelAttributes. Но значения атрибутов в DefAttributes сохраняются и в любой момент могут быть методом Assign присвоены атрибутам свойства SelAttributes, чтобы вернуться к прежнему стилю.
За выравнивание, отступы и т.д. в пределах текущего абзаца отвечает свойство Paragraph, которое имеет ряд подсвойств:

Alignment
Определяет выравнивание текста. Может принимать значения taLeftJustify (влево), taCenter (по центру) или taRightJustify (вправо)

FirstIndent
Число пикселов отступа красной строки.

Numbering
Управляет вставкой маркеров, как в списках. Может принимать значения nsNone
· отсутствие маркеров, nsBullet
· маркеры ставятся

LeftIndent
Отступ в пикселах от левого поля

RightIndent
Отступ в пикселах от правого поля

TabCount
Количество позиций табуляции

Tab
Значения позиций табуляции в пикселах


Значения подсвойств свойства Paragraph можно задавать только в процессе выполнения приложения, например при нажатии кнопки.
Теперь рассмотрим общее основное свойство Memo и RichEdit
· Lines, содержащее текст окна в виде списка строк. Начальное значение текста можно установить в процессе проектирования, нажав кнопку с многоточием около свойства Lines в окне Инспектора Объектов. В открывшемся окне Редактор строки списка можно редактировать или вводить текст.
Во время выполнения приложения можно заносить текст в окно с помощью методов свойства Lines.
Весь текст, представленный одной строкой типа String, внутри которой используются разделители типа символов возврата каретки и перевода строки, содержится в свойстве Text.
Доступ к отдельной строке текста, например первой, можно получить так: Memo1->Lines->String[0].
Свойство только для чтения Count указывает число строк в тексте.
Для очистки текста в окне используется функция Clear().
Для занесения новой строки в конец текста окна редактирования пользуются методами Add или Append свойства Lines.
Для работы с файлом используются методы LoadFromFile и SaveToFile. Например,
RichEdit1->Lines->LoadFromFile(“text.rtf”);
RichEdit1->Lines->SaveToFile(“text.rtf”);

Рассмотрим использование компонентов Memo и RichEdit на примере работы с двумерным массивом (матрицей) чисел.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
В верхней части формы, слева поместите окно LabeledEdit1. У свойства EditLabel зачистите значение подсвойства Caption.
Правее поместите кнопку Button1. В свойство Caption впишите Ввод размеров матрицы (<=10).
Ниже поместите вторую кнопку Button2 и зачистите у нее свойство Caption.
Ниже, на одной горизонтали поместите окна Memo1 и RichEdit1 и сделайте их размеры равными и достаточными для размещения двух матриц из двузначных чисел, размерами 10x10. Для обоих окон установите имя шрифта Courier (символы этого шрифта равны по ширине).
Впишите обработчики щелчков кнопок, как показано ниже:
int k=0,n,m;
void __fastcall TForm1::Button1Click(TObject *Sender)
{ if(k==0) {LabeledEdit1->EditLabel->Caption="Число строк=";
LabeledEdit1->SetFocus();
}
if(k==1) {n=StrToInt(LabeledEdit1->Text);
LabeledEdit1->EditLabel->Caption="Число столбцов=";
LabeledEdit1->SetFocus();
}
if(k==2) {m=StrToInt(LabeledEdit1->Text);
LabeledEdit1->EditLabel->Caption="Размер матрицы : " +
IntToStr(n)+" x "+IntToStr(m);
LabeledEdit1->Text="";
Button2->Caption="Вывод матриц";
}
if(k==0||k==1)k++;
else k=0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
int x[10][10],i,j;
Memo1->SetFocus();
AnsiString s[10][10],s1[10],s2[10];
Memo1->Clear();
Memo1->Lines->Add("ИСХОДНАЯ МАТРИЦА");
for(i=0;i s1[i]="";
for(j=0;j x[i][j]=random(101)-50;
if(x[i][j]<-9) s[i][j]=" "+IntToStr(x[i][j]);
else if(x[i][j]<0||x[i][j]>9) s[i][j]=" "+IntToStr(x[i][j]);
else s[i][j]=" "+IntToStr(x[i][j]);
s1[i]+=s[i][j];}
Memo1->Lines->Add(s1[i]);}
Memo1->Lines->Add("");
Memo1->Lines->Add("ТРАНСПОНИРОВАННАЯ МАТРИЦА");
for(j=0;j s2[j]="";
for(i=0;i s2[j]+=s[i][j];
Memo1->Lines->Add(s2[j]);}
RichEdit1->SetFocus();
RichEdit1->Clear();
/*установка выравнивания по центру*/
RichEdit1->Paragraph->Alignment=taCenter;
/*установка жирного шрифта*/
RichEdit1->SelAttributes->Style =
RichEdit1->SelAttributes->Style << fsBold;
RichEdit1->Lines->Add("ИСХОДНАЯ МАТРИЦА");
/*восстановление атрибутов по умолчанию*/
RichEdit1->SelAttributes->Assign(RichEdit1->DefAttributes);
/*установка выравнивания по левому краю*/
RichEdit1->Paragraph->Alignment=taLeftJustify;
for(i=0;iLines->Add(s1[i]);
RichEdit1->Lines->Add("");
/*установка размера шрифта*/
RichEdit1->SelAttributes->Size=12;
/*установка цвета шрифта*/
RichEdit1->SelAttributes->Color= clHotLight;
/*установка стиля шрифта fsItalic*/
RichEdit1->SelAttributes->Style=
RichEdit1->SelAttributes->Style< /*установка выравнивания по центру*/
RichEdit1->Paragraph->Alignment=taCenter;
RichEdit1->Lines->Add("ТРАНСПОНИРОВАННАЯ МАТРИЦА");
/*восстановление атрибутов по умолчанию*/
RichEdit1->SelAttributes->Assign(RichEdit1->DefAttributes);
for(j=0;jLines->Add(s2[j]);
}
//---------------------------------------------------------------------------
Сохраните проект и выполните приложение. Убедитесь в работоспособности приложения, используя матрицы разных и одинаковых (повторным нажатием кнопки Вывод матриц) размеров.


Компоненты ListBox, CheckListBox, ValueListEditor

Эти компоненты обеспечивают выбор из списка и, следовательно, безошибочный ввод информации в тех случаях, когда пользователь должен выбрать ответ из конечного множества альтернатив.
Компоненты ListBox и CheckListBox отображают списки строк и позволяют пользователю выбрать в них нужную строку. Основное свойство обоих компонентов, содержащее список строк,
· Items, имеющее тип TStrings. Для заполнения его во время проектирования нужно нажать кнопку с многоточием около этого свойства в окне Инспектора Объектов. Во время выполнения работать с этим свойством можно, пользуясь свойствами и методами класса TStrings
· Clear, Add, Delete, Insert и др. Этот же класс позволяет поставить в соответствие каждой строке некоторый объект. Тогда выбор пользователем строки в списке можно программно соотносить с этим объектом.
В ListBox есть свойство MultiSelect, разрешающее пользователю множественный выбор в списке, если установить его в true. Если MultiSelect=false (значение по умолчанию), то пользователь может выбрать только один элемент списка. Индекс выбранной строки можно узнать из свойства ItemIndex, доступного только во время выполнения. Если ни одна строка не выбрана, то ItemIndex=-1. При MultiSelect=true значение ItemIndex соответствует тому элементу списка, который находится в фокусе. При множественном выборе поиск выбранных элементов осуществляется по свойству Selected[int Index] типа bool. Для множественного выбора необходимо задействовать клавиши Shift (для выделения интервала элементов) и Ctrl, которые будут работать, если свойство ExtendedSelect = true.

Рассмотрим пример использования компонента ListBox, используя в качестве строк строки матрицы.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
На пустую форму, слева перенесите компонент ListBox1. Свойства ExtendedSelect и MultiSelect установите в true, шрифт
· Courier.
Справа по вертикали поместите четыре кнопки Button и назовите их так: Очистить, Ввести строки, Удалить одну выделенную строку, Удалить все выделенные строки.
В соответствующие обработчики впишите :
для кнопки Очистить:
ListBox1->Items->Clear();
Caption="";
для кнопки Ввести строки:
int n=10,m=10,x[10][10];
void __fastcall TForm1::Button2Click(TObj
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·для кнопки Удалить одну выделенную строку:
if(ListBox1->ItemIndex==-1) Caption="Строка не выделена";
else{
Caption="Удалена строка :"+
ListBox1->Items->Strings[ListBox1->ItemIndex];
ListBox1->Items->Delete(ListBox1->ItemIndex);
ListBox1->ItemIndex=-1;}
для кнопки Удалить все выделенные строки:
Caption="";
int i,j=0;
for(i=0;iItems->Count;i++)
if(ListBox1->Selected[i])j++;
else ListBox1->Items->Strings[i-j]=ListBox1->Items->Strings[i];
for(;j>0;j--)
ListBox1->Items->Delete(ListBox1->Items->Count-1);
ListBox1->ItemIndex=-1;

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

Компонент CheckListBox
· это список с индикаторами. Индикаторы позволяют реализовать множественный выбор элементов списка. Рассмотрим пример использования компонента CheckListBox на примере обработки целочисленной матрицы с целью нахождения строк с положительными суммами элементов.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
На пустой форме поместите: слева
· компонент CheckListBox1, а справа, по вертикали, три кнопки
· Button1, Button2, Button3. В CheckListBox1 установите шрифт Font
· Courier, полужирный. В свойство Caption Button1 занесите очистить, Button2
· ввод матрицы, Button3
· строки с положительными суммами элементов.
В обработчики щелчков кнопок соответственно впишите
для Button1:
CheckListBox1->Items->Clear();
Caption="";
для Button2:
int i,j,n=10,m=10,x[10][10];
void __fastcall TForm1::Button2Click(TObject *Sender)
{
String s[10][
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
· для Button3:
CheckListBox1->Items->Add("");
int sum,k=0;
for(i=0;i sum=0;
for(j=0;j if(sum>0) {CheckListBox1->Checked[i]=true; k++;}
CheckListBox1->Items->Add("Сумма элементов в строке "+
IntToStr(i)+" равна "+IntToStr(sum));}
Caption="Количество строк с положительными суммами
элементов - "+ IntToStr(k);
Сохраните проект и выполните приложение. Убедитесь в работоспособности приложения.

Компонент ValueListEditor
· это простой и удобный редактируемый список, содержащий строки вида «имя = значение». Именно этот компонент представляет большинство свойств в окне Инспектора Объектов. Окно компонента имеет две колонки с заголовками «Key» для имен и «Value» для значений. Заголовки можно изменить во время проектирования или программно, используя свойство TitleCaptions типа TStrings. Нажатием на кнопку (три точки) свойства вызывается Редактор строки списка, первая строка которого соответствует именам, вторая
· значениям.
Свойство Strings типа TStrings содержит список всех строк. Во время проектирования он может быть заполнен Редактором значения списка, вызываемым кнопкой с тремя точками. Во время выполнения пользователь может заполнять его, если разрешено редактирование (в свойстве KeyOptions подсвойству keyEdit присвоено значение true). Методы класса TStrings позволяют присвоить свойству Strings другой список, манипулировать списком (добавлять строки, удалять и т.п.).
Свойство Keys является индексированным списком имен. Индексы начинаются с единицы. Свойство AnsiString Values[const AnsiString Key] позволяет задать или прочитать значение из строки с именем Key. Если свойство используется для задания значения, а имени Key в списке нет, то в список добавляется новая строка с указанным именем и значением. Если свойство используется для чтения, а имени Key в списке нет, то возвращается пустая строка.
Доступ к именам и значениям дают также свойства Strings->Names и Strings->Values (как в обычных списках типа TStrings).
Еще один способ доступа к элементам строк дает свойство AnsiString Cells[int ACol][int ARow], причем ACol=0 соответствует колонке имен, ACol=1
· колонке значений, ARow=0 соответствует строке заголовков, а ARow>0
· строкам списка.
Свойство только для чтения RowCount показывает число строк, включая строку заголовка.
Свойство KeyOptions определяет операции, доступные пользователю при редактировании колонки имен. Это свойство является множеством, пустым (значения всех подсвойств равны false) или содержащим элементы (подсвойства) keyEdit, keyAdd, keyDelete, keyUnique (теперь значения их
· true). Элемент keyEdit позволяет редактировать имена в первой колонке. Элемент keyAdd позволяет добавлять в список новые строки, используя клавишу Insert или перемещая курсор клавишей со стрелкой ниже позиции последней строки. Элемент keyAdd можно включать только вместе с keyEdit. Элемент keyDelete позволяет удалять выделенную строку клавишей Delete. Элемент keyUnique не позволяет добавить новую строку с именем, уже имеющимся в списке.
Свойство только времени выполнения ItemProps позволяет управлять способом редактирования значения каждого из элементов списка. Элемент задается или его именем, или индексом строки (начинаются с нуля).
Свойство DisplayOptions является множеством, которое может содержать элементы doColumnTitles
· строка заголовка фиксирована (не прокручивается), doKeyColFixed
· ширина колонки имен неизменна, doAutoColResize
· при изменении ширины компонента ширина колонок автоматически изменяется.
Свойство Options содержит множество опций, управляющих отображением линий таблицы, а также возможность для пользователя изменять размеры колонок и т.п.

Рассмотрим пример использования компонента ValueListEditor, взяв в качестве имен строки матрицы, а значений
· суммы элементов по строкам.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
На форме поместите: слева
· компонент ValueListEditor1, под ним
· компонент Memo1; справа по вертикали, вдоль правой границы формы – компоненты Button1, Button2, LabeledEdit1, Button3.
Для Button1 в Caption занесите очистить, а в обработчик щелчка
ValueListEditor1->Strings->Clear();
Caption="";
Memo1->Clear();
Для Button2 в Caption занесите ПУСК, а в обработчик щелчка
int i,j,n=10,m=10,x[10][10],f;
void __fastcall TForm1::Button2Click(TObject *Sender)
{
f=0;
Memo1->Clear();
Memo1->Lines->Add("СООБЩЕНИЯ");
int sum[10];
AnsiString s[10][10],s1[10];
for(i=0;i s1[i]="";
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·я строка " + ValueListEditor1->Strings->Names[0]+
" сумма = "+ValueListEditor1->Strings->Values
[ValueListEditor1->Strings->Names[0]];
Memo1->Lines->Add("Введено "+IntToStr(ValueListEditor1->RowCount-1)
+" строк");
LabeledEdit1->SetFocus();
}
Для LabeledEdit1 в свойство Text занесите 1, в подсвойство Caption свойства EditLabel
· номер удаляемой строки.
Для Button3 в Caption занесите удаление строки, а в обработчик
·
int d=StrToInt(LabeledEdit1->Text);
if(f==1){Memo1->Lines->Add("Список пуст !");return;}
if(d > ValueListEditor1->RowCount-1)
Memo1->Lines->Add(IntToStr(d)+ "-й строки нет!");
else {Memo1->Lines->Add("Удалена " + IntToStr(d) + "-я строка");
Memo1->Lines->Add(ValueListEditor1->Cells[0][d]+ " " +
ValueListEditor1->Cells[1][d]);
if(ValueListEditor1->RowCount==2)f=1;
ValueListEditor1->Strings->Delete(d-1);
if(d==1&&f==1)Memo1->Lines->Add("Список пуст !");}
Memo1->Lines->Add("Осталось "+
IntToStr(ValueListEditor1->RowCount-1)+" строк");
LabeledEdit1->SetFocus();
В компоненте ValueListEditor1 перечисляемым свойствам присвойте следующие значения: DisplayOptions
· все подсвойства в true, Font
· Courier, полужирный, KeyOptions
· подсвойство keyUnique
· false, остальные
· true, TitleCaptions
· кнопкой с тремя точками вызвать окно Редактора строки списка и занести в первую строку через 10 пробелов
· строки, во вторую строку через три пробела
· суммы в строках. После первого запуска приложения установить ширину окна и границу между колонками строки и суммы в строках так, чтобы строки матрицы и суммы были видны полностью.
В компоненте Memo1 установить шрифт Font
· Courier, полужирный. Ширину окна Memo1 установить равной ширине окна компонента ValueListEditor1.
Сохраните проект и выполните приложение. Убедитесь в работоспособности приложения, заполняя список разным количеством строк, удаляя и редактируя строки.





Компонент StringGrid

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

AnsiString Cells
[int Acol][int ARow]
Строка, содержащаяся в ячейке с индексами столбца и строки ACol и ARow

TStrings* Cols[int Index]
Список столбцов и связанных с ними объектов, содержащихся в столбце с индексом Index

TStrings* Rows[int Index]
Список строк и связанных с ними объектов, содержащихся в строке с индексом Index

TObject* Object
[int ACol][int ARow]
Объект, связанный со строкой, содержащийся в ячейке с индексами столбца и строки ACol и ARow


Все эти свойства доступны во время выполнения. Задавать тексты можно программно или по отдельным ячейкам, или сразу по столбцам и строкам с помощью методов класса TStrings.
Свойства ColCount и RowCount определяют соответственно число столбцов и строк, свойства FixedCols и FixedRows
· число фиксированных (непрокручиваемых) столбцов и строк. Цвет фона фиксированных ячеек определяется свойством FixedColor.
Из множества подсвойств свойства Options отметим важное
· goEditing
· возможность редактировать содержимое таблицы.
В основном компонент StringGrid используется для выбора пользователем каких-то значений, отображенных в ячейках. Свойства Col и Row показывают индексы столбца и строки выделенной ячейки.
Среди множества событий компонента StringGrid отметим событие OnSelectCell, возникающее в момент выбора пользователем ячейки. В обработчик этого события передаются целые параметры ACol и ARow (индексы столбца и строки выделенной ячейки) и булев параметр CanSelect
· допустимость выбора. Этот параметр можно использовать для запрета выделения ячейки, задав его значение равным false.

Рассмотрим использование компонента StringGrid при вычислении произведения двух матриц.
Создайте новый проект и сохраните файл модуля и проект под разными именами в созданной ранее папке.
В правый верхний угол пустой формы поместите первую кнопку, в свойство Caption впишите конец, а в обработчик
· Close();
Примечание. Во всех компонентах приложения используйте шрифт полужирный, размером 10.
Начнем подготовку компонентов для формирования первой матрицы произведения. В верхней части формы, посредине, поместите первую метку и назовите ее число столбцов.
Под первой меткой поместите первый компонент ввода целых чисел
· CSpinEdit1 (со страницы Примеры библиотеки компонентов). В свойство MaxValue занесите 10, MinValue
· 0, Value
· 3.
Под первым CSpinEdit1 поместите первый компонент StringGrid1 (первую таблицу), слева от него
· вторую метку и назовите ее число строк, под меткой поместите второй компонент CSpinEdit2 и установите в нем те же значения свойств, что и в первом.
Правее первой таблицы поместите вторую кнопку, назовите ее ввод 1-й матрицы, а в обработчик впишите
int i,j;
Caption="";
StringGrid3->RowCount=0;
StringGrid3->Co
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
Для первого компонента StringGrid1 установите: FixedCols
· 0, FixedRows
· 0, в подсвойстве goEditing свойства Options
· false. На этом подготовка компонентов для формирования первой матрицы завершается.
Подготовка компонентов для формирования второй матрицы произведения совпадает с подготовкой для первой матрицы, только в подсвойство goEditing свойства Options второго компонента StringGrid2 внесите true, третью кнопку назовите ввод 2-й матрицы, а в обработчик щелчка по ней впишите
Caption="";
StringGrid3->RowCount=0;
StringGrid3->ColCount=0;
StringGrid3->Cells[0][0]="";
StringGrid2->RowCount=CSpinEdit3->Value;
StringGrid2->ColCount=CSpinEdit4->Value;
Итак, вторая таблица с ее компонентами находится на форме под первой таблицей. Третью таблицу (для матрицы-произведения) поместите под второй, а справа от третьей таблицы
· четвертую кнопку с названием произведение, в обработчик щелчка по которой впишите
int i,j,k,z;
if(StringGrid1->ColCount!= StringGrid2->RowCount){
Caption="число столбцов 1-й матрицы не равно числу строк 2-й матрицы!";
return;}
StringGrid3->RowCount=CSpinEdit1->Value;
StringGrid3->ColCount=CSpinEdit4->Value;
for(i=0;iRowCount;i+
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
Свойства третьего компонента StringGrid3 установите такими же, как у первого компонента StringGrid1.
Сохраните проект и запустите на выполнение. Для ввода первой матрицы достаточно нажать на соответствующую кнопку. Для ввода второй матрицы после щелчка на кнопке ввод 2-й матрицы нужно заполнить предложенные ячейки второй таблицы. Для получения матрицы-произведения достаточно щелкнуть на кнопке произведение. Чтобы быстро убедиться в правильности вычислений, сделайте вторую матрицу единичной. Проверьте соотношение числа строк и столбцов матриц-сомножителей для получения матрицы-произведения.
Для завершения работы с приложением щелкните на кнопке конец.



Контрольные вопросы

Перечислите компоненты ввода и отображения текстовой информации.
Приведите примеры использования компонентов Label, StaticText, Panel.
Приведите примеры использования компонентов Edit, LabeledEdit, MaskEdit.
Расскажите о свойствах и использовании компонентов Memo, RichEdit.
Каковы свойства и назначения компонентов ListBox, ChecklistBox, ValueListEditor? Приведите примеры использования.
Что представляет собой компонент StringGrid? Каковы его свойства и подсвойства? Приведите примеры использования.





Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 146–172(
Шамис В.А. Borland C++ Builder 6. Для профессионалов / В.А. Шамис.
· СПб.: Питер, 2003.
· 798 с. – С. 287
·329.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.





ЗАНЯТИЕ 4

Диаграммы и графики

Компонент Chart

Компонент Chart позволяет строить различные диаграммы и графики. Компонент Chart имеет множество свойств, методов, событий. Рассмотрим только основные характеристики.
Компонент Chart является контейнером объектов Series типа TChartSeries
· серий данных, характеризующихся различными стилями отображения. Каждый компонент может включать несколько серий. Например, при отображении графика каждая серия будет соответствовать одной кривой на графике. Очевидно, что в этом случае серии можно наложить друг на друга. В случае же диаграмм, например круговых, наложение затруднит понимание представленных данных. Однако в последнем случае можно задать для одного компонента Chart несколько серий одинаковых данных с разным типом диаграммы. Тогда, делая в каждый момент времени активной одну из них, можно предоставить пользователю выбор типа диаграммы, отображающей интересующие его данные.
Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА CHART и командой Сохранить все сохраните файл модуля и проект под разными именами.
Разместите на пустой форме компонент Chart со страницы Additional и просмотрите открывшиеся в Инспекторе Объектов свойства. Поясним некоторые из них.

AllowPanning
Определяет возможность пользователя прокручивать наблюдаемую часть графика во время выполнения, нажимая правую кнопку мыши. Возможные значения: pmNone
· прокрутка запрещена, pmHorizontal, pmVertical или pmBoth
· разрешена соответственно прокрутка только в горизонтальном направлении, только в вертикальном или в обоих направлениях

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

Title
Определяет заголовок диаграммы

Foot
Определяет подпись под диаграммой. По умолчанию отсутствует. Текст подписи определяется подсвойством Text

Frame
Определяет рамку вокруг диаграммы

Legend
Легенда диаграммы
· список обозначений

MarginLeft,
MarginRight,
MarginTop,
MarginBottom
Значения левого, правого, верхнего и нижнего полей

BottomAxis,
LeftAxis,
RightAxis
Эти свойства определяют характеристики соответственно нижней, левой и правой осей. Задание этих свойств имеет смысл для графиков и некоторых типов диаграмм

LeftWall,
BottomWall,
BackWall
Эти свойства определяют характеристики соответственно левой, нижней и задней граней области трехмерного отображения графика

SeriesList
Список серий данных, отображаемых в компоненте

View3D
Разрешает или запрещает трехмерное отображение диаграммы

View3DOptions
Характеристики трехмерного отображения

Chart3DPercent
Масштаб трехмерности (например, толщина диаграммы и ширина лент графика)


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

Перейдем к проектированию приложения для построения диаграмм и графиков.
Сделайте двойной щелчок на компоненте Chart1. Появится окно Редактора Диаграмм (Editing Chart1) с открывшейся страницей Chart, которая имеет несколько закладок. На странице Series щелкните на кнопке Add
· добавить серию. В появившемся окне выбирают тип диаграммы или графика. Выберите Pie
· круговую диаграмму. После щелчка на OK снова появится окно Редактора Диаграмм. Сместив его в сторону, увидите, что в компоненте Chart1 на форме отображается вид диаграммы с занесенными в нее условными данными. Благодаря этому пользователь сразу может наблюдать результат применения различных опций к приложению.
Перейдите на закладку Titles. В окне редактирования, которое в данный момент соответствует Title
· заголовку диаграммы, сотрите TChart и напишите ДИАГРАММА РЕЗУЛЬТАТОВ ЭКЗАМЕНОВ. С помощью выпадающего списка перейдите в окно редактирования Foot и напишите Успеваемость студентов в %.
Перейдите на закладку Panel. Теперь можно задать вид панели, на которой отображается диаграмма. В частности, можно задать два варианта цвета панели
· постоянный (кнопка Panel Color) и градиентный (в группе Gradient после установки индикатора Visible задается начальный цвет панели кнопкой StartColor и конечный
· кнопкой EndColor). Выберите и установите оба варианта цвета панели. Индикатор Visible позволяет переключать варианты.
На закладке 3D установите желаемый внешний вид диаграммы: размер, наклон, сдвиг по горизонтали и вертикали, толщину и т.д.
Изучите возможности закладки Legend, которая позволяет задать параметры отображения легенды диаграммы (списка обозначений) или вообще убрать ее с экрана.
Перейдите на страницу Series Редактора Диаграмм. На закладке Format включите опцию Circled Pie, которая обеспечит при любом размере компонента Chart отображение диаграммы в виде круга.
На закладке Marks кнопки группы Style определяют, что будет написано на ярлычках, относящихся к отдельным сегментам диаграммы: Value
· значение, Percent
· проценты, Label
· названия данных и т.д. Включите кнопку Percent, а на закладке General установите шаблон процентов только с целыми процентами.
Добавьте на компонент Chart1 еще одну тождественную серию, нажав на закладке Series страницы Chart кнопку Clone, а затем для этой новой серии нажмите кнопку Change (изменить) и выберите другой тип диаграммы
· Bar. Очевидно, что наложение двух разных типов диаграммы на одном рисунке затруднит понимание представленных данных. Поэтому выключите индикатор этой новой серии на закладке Series страницы Chart. На этом задание свойств первого компонента Chart1 завершается. Выйдите из Редактора Диаграмм.
Перенесите на форму второй компонент Chart2, разместив его под первым компонентом. Для задания свойств перейдите в Редактор Диаграмм Editing Chart2.
На закладке Series страницы Chart нажмите кнопку Addи выберите тип диаграммы Line для графика. Нажав OK, на закладке Series страницы Chart нажмите кнопку Title и в открывшемся окне напишите SIN. Затем повторите все действия этого пункта, но только напишите COS.
Перейдите на закладку Axis для задания координатных характеристик осей. В группе Axis нажата кнопка Left. На закладке Title в окно редак-тирования Title: впишите F, в Angle:
· 0, размер шрифта Font
· равным 16. В группе Axis нажмите кнопку Bottom, в Title: впишите X тем же шрифтом.
Перейдите на закладку Titles и в окно редактирования для Title впишите ГРАФИКИ SIN И COS.
На закладке Panel установите градиентный вариант цвета панели, отличный от установленного для панели первого компонента.
Изучите возможности закладки Walls для задания характеристик трехмерных граней графика.
Перейдите на закладку 3D и выключите индикатор 3Dimensions. Изучите другие возможности задания свойств графика на этой закладке.
Для задания толщины графиков перейдите на страницу Series, а в ней
· на закладку Format. В группе Line нужно нажать кнопку Border и в окне Border Color Editor установить Width равным 3.

На этом проектирование внешнего вида приложения завершается. Осталось написать код, задающий данные, которые нужно отображать. Зададим в круговой диаграмме константные данные, а в графиках
· функции синуса и косинуса.
Для задания отображаемых значений используют методы серий Series. Метод Clear очищает серию от занесенных ранее данных.
Метод Add
long int Add(const double AValue, const String ALabel, TColor AColor);
позволяет добавить в диаграмму новую точку. Параметр AValue соответствует добавляемому значению, параметр ALabel
· название, которое будет отображаться на диаграмме и в легенде, AColor
· цвет. Параметр ALabel
· необязательный, его можно задать пустым: “ ”.
Метод AddXY
long int AddXY(double AXValue, double AYValue, const String ALabel, TColor);
позволяет добавить новую точку в график функции. Параметры AXValue и AYValue соответствуют аргументу и функции. Параметры ALabel и AColor
· те же, что и в методе Add.

Поместите на форме вверху, справа, кнопку Button1 и назовите ее КОНЕЦ, а в обработчик щелчка на этой кнопке впишите Close();.
Ниже поместите вторую кнопку Button2 и назовите ее НАРИСОВАТЬ, а в обработчик щелчка на ней впишите:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
int A1=5;
int A2=8;
int A3=5;
int A4=2;
const Pi=3.14159;
Series1->Clear();
Series2->Clear();
Series1->Add(A1,"двоешники",clYellow);
Series1->Add(A2,"троешники",clBlue);
Series1->Add(A3,"хорошисты",clRed);
Series1->Add(A4,"отличники",clPurple);
Series3->Clear();
Series4->Clear();
for(int i=0; i<=100; i++)
{
Series3->AddXY(0.02*Pi*i,sin(0.02*Pi*i),"",clRed);
Series4->AddXY(0.02*Pi*i,cos(0.02*Pi*i),"",clBlue); }
Series2->Assign(Series1);
Series2->Active=false;
}

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

void __fastcall TForm1::Chart1ClickSeries(TCustomChart *Sender,
TChartSeries *Series, int ValueIndex, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Series1->Active=!Series1->Active;
Series2->Active=!Series2->Active;
}

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

Компонент Image

Компонент Image находится на странице Дополнительно библиотеки компонентов. Он имеет свойство Canvas (канва, холст), представляющее собой область компонента, на которой можно рисовать или отображать готовые изображения. Канва содержит свойства и методы, существенно упрощающие графику C++Builder.
Каждая точка канвы имеет координаты X и Y. Система координат канвы, как и везде в C++Builder, имеет началом левый верхний угол канвы. Координата X возрастает при перемещении слева направо, а координата Y
· при перемещении сверху вниз.
Координаты измеряются в пикселах. Пиксел
· это наименьший элемент поверхности рисунка, с которым можно манипулировать. Важнейшее свойство пиксела
· его цвет. Для описания цвета используется тип TColor. В C++Builder определено множество констант типа TColor. Одни из них непосредственно определяют цвета (например, clBlue
· синий), другие определяют цвета элементов окон, которые могут меняться в зависимости от выбранной пользователем палитры цветов Windows (например, clBtnFace
· цвет поверхности кнопок).


Рисование по пикселам

Для рисования по пикселам используется свойство канвы Pixels. Это свойство представляет собой двумерный массив Canvas->Pixels[int X][int Y], который отвечает за цвета канвы. Например, Canvas->Pixels[10][20] соответствует цвету пиксела, 10-го слева и 20-го сверху. С массивом пикселов можно обращаться как с любым свойством: изменять цвет, задавая пикселу новое значение, или определять его цвет по хранящемуся в нем значению. Например, Canvas->Pixels[10][20]=clBlack
· это задание пикселу черного цвета.
Предположим, нужно нарисовать график некоторой функции F(X) на канве компонента Image1, если известен диапазон ее изменения Ymax и Ymin и диапазон изменения аргумента Xmin и Xmax. Это можно сделать так:
float X,Y; // координаты функции
int PX,PY; // координаты пикселов
for(PX=0;PX<=Image1->Width;PX++)
{
// X - координата, соответствующая пикселу с координатой PX
X=Xmin+PX*(Xmax-Xmin)/Image1->Width;
Y=F(X);
// PY - координата пиксела, соответствующая координате Y
PY=Image1->Height-(Y-Ymin)* Image1->Height/(Ymax-Ymin);
// Устанавливается красный цвет выбранного пиксела
Image1->Canvas->Pixels[PX][PY]=clRed;
}

Цикл выполняется по всем значениям горизонтальной координаты пикселов PX компонента Image1. Сначала выбранное значение PX пересчитывается в соответствующее значение X. Затем производится вызов функции и определяется ее значение Y. Это значение пересчитывается в вертикальную координату пиксела PY. И в заключение цвет пиксела с координатами (PX, PY) устанавливается красным.
Ниже приведено приложение для функции sin(X), для которой Xmin=0, Xmax=413 EMBED Equation.3 1415 (2 периода), Ymin=-1 и Ymax=1.



Рисование пером

У канвы есть свойство
· Pen
· перо. Это объект, в свою очередь имеющий ряд свойств. Одно из них Color
· цвет, которым наносится рисунок. Второе свойство
· Width (ширина линии). Ширина задается в пикселах. По умолчанию ширина равна 1.
Свойство Style определяет вид линии и может принимать следующие значения:

psSolid
Сплошная линия

psDash
Штриховая линия

psDot
Пунктирная линия

psDashDot
Штрих-пунктирная линия

psDashDotDot
Линия, чередующая штрих и два пунктира

psClear
Отсутствие линии

psInsideFrame
Сплошная линия, но при Width>1 допускающая цвета, отличные от палитры Windows


Все стили со штрихами и пунктирами доступны только при Width=1. В противном случае линии этих стилей рисуются как сплошные.
Стиль psInsideFrame
· единственный, который допускает произвольные цвета. Цвет линии при остальных стилях округляется до ближайшего из палитры Windows.
У канвы имеется свойство PenPos. Это свойство определяет в координатах канвы текущую позицию пера. Перемещение пера без прорисовки линии, т.е. изменение PenPos, производится методом канвы MoveTo(X,Y). Здесь (X,Y)
· координаты точки, в которую перемещается перо. Эта текущая точка становится исходной, от которой методом LineTo(X,Y) можно провести линию в точку с координатами (X,Y). При этом текущая точка перемещается в конечную точку линии, и новый вызов LineTo будет проводить линию из этой новой текущей точки.

Нарисуем по пикселам и пером графики синуса и сравним качество графиков.
Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА IMAGE и командой Сохранить все сохраните файл модуля и проект под разными именами.
На пустую форму перенесите два компонента Image и разместите их по горизонтали рядом. Размеры обоих компонентов сделайте абсолютно одинаковыми, так как на этом для экономии размера кода основана программа. Сделать размеры компонентов абсолютно одинаковыми можно, выделив их оба и воспользовавшись командой всплывающего меню Size.
Над первым, левым компонентом поместите первую метку Label1 и назовите ее рисунок по пикселам, над вторым
· Label2, с названием рисунок пером.
В правом верхнем углу поместите кнопку Button1 и назовите ее КОНЕЦ. В обработчик щелчка кнопки впишите Close();.
Под компонентами поместите вторую кнопку Button2, назовите ее НАРИСОВАТЬ, а в обработчик щелчка впишите:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
#define Pi 3.14159
float X,Y; // координаты функции
int PX,PY; // координаты пикселов
// Переводится перо в начало координат второго графика -
// на левый край канвы в середину ее высоты
Image2->Canvas->MoveTo(0,Image2->Height/2);
// Устанавливается красный цвет рисования пером
Image2->Canvas->Pen->Color=clRed;
// Устанавливается ширина линии рисования в три пиксела
Image2->Canvas->Pen->Width=3;
for(PX=0;PX<=Image1->Width;PX++)
{
// X - координата, соответствующая пикселу с координатой PX
X=PX*4*Pi/Image1->Width;
Y=sin(X);
// PY - координата пиксела, соответствующая координате Y
PY=Image1->Height-(Y+1)* Image1->Height/2;
// Устанавливается красный цвет выбранного пиксела
Image1->Canvas->Pixels[PX][PY]=clRed;
// Проводится линия на втором графике
Image2->Canvas->LineTo(PX,PY);
}
}

В заголовочный файл модуля впишите #include . Сохраните проект и запустите на выполнение. Нетрудно видеть, что качество графиков сильно различается . В левом на крутых участках сплошной линии нет
· она распадается на отдельные точки
· пикселы. А правый график весь сплошной. Это показывает, что при прочих равных условиях рисовать лучше не по пикселам, а пером.

Отметим еще одно ценное свойство компонента Image и его канвы
· можно задавать координаты пикселов, выходящие за пределы размеров канвы. Это позволяет не заботиться о том, какая часть рисунка попадает в рамку Image, а какая нет. Например, соответствующий оператор изменим так: Y=2*sin(X);. Сохраним проект и выполним. Увидим, что изобразилась только та часть рисунка, которая помещается в рамку канвы. Это позволяет без затруднений осуществлять приложения, в которых пользователю предоставляется возможность увеличивать и просматривать в деталях какие-то фрагменты графиков.



Контрольные вопросы

Расскажите о назначении и свойствах компонента Chart.
Приведите примеры использования компонента Chart.
Расскажите о порядке проектирования приложения для построения диаграмм и графиков.
Какие методы используются при построении графиков?
Расскажите о назначении и свойстве Canvas компонента Image.
Приведите пример рисования по пикселам. Какое свойство канвы при этом используется и что представляет собой это свойство?
Приведите пример рисования пером. Какие свойства канвы и методы при этом используются?
Расскажите о свойстве канвы Pen и его подсвойствах.
Расскажите о свойстве канвы PenPos. Какие методы канвы использует это свойство?




Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 189–194, 384
·388.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.



ЗАНЯТИЕ 5

Список изображений.
Компоненты отображения иерархических данных.
Полоса состояния.
Перетаскивание объектов – технология Drag&Drop

Список изображений – компонент ImageList (находится на странице Win32 библиотеки компонентов) представляет собой набор изображений одинаковых размеров (например, пиктограмм), на которые можно ссылаться по индексам, начинающимся с нуля. Во многих компонентах, в том числе и в компонентах отображения иерархических данных, имеются свойства, представляющие собой ссылки на компонент ImageList.
Изображения в компонент ImageList могут быть загружены в процессе проектирования с помощью редактора списков изображений. Окно редактора вызывается двойным щелчком на компоненте ImageList или щелчком правой кнопки мыши и выбором команды контекстного меню Редактор ImageList. В окне редактора можно добавить в списки изображения (кнопка Добавить), удалить изображение из списка кнопкой Удалить, очистить весь список кнопкой Очистить.
При добавлении изображения в список, которое начинается с нажатия кнопки Добавить, открывается окно открытия файлов изображений, в котором можно выбрать нужный файл. Множество изображений, размещаемых обычно на кнопках, содержится в папке \Program Files\Common Files\Borland Shared\Images\Buttons.
Следует помнить, что размер всех изображений в списке должен быть одинаковым. Как правило, это размер, используемый для пиктограмм в меню, списках, кнопках. При добавлении в список изображений для кнопок надо иметь в виду, что они часто содержат не одно, а два и более изображений. В этих случаях после выбора имени файла изображений при щелчке на кнопке Открыть задается вопрос: “Bitmap dimensions for are greater then imagelist dimensions. Separate into separate bitmaps?” (“Размерность изображения больше размерности списка. Разделить на отдельные изображения?”). Если ответить отрицательно, то все изображения уменьшатся в горизонтальном размере и лягут как одно изображение. Использовать его в дальнейшем будет невозможно. Поэтому на заданный вопрос надо ответить положительно. Тогда загружаемая битовая матрица автоматически разделится на отдельные изображения, а затем те из них, которые не нужны, удаляют.
Каждое загруженное в список изображение получает индекс. Именно на эти индексы впоследствии можно ссылаться в соответствующих свойствах разделов меню, списков, кнопок и т.д., когда надо загрузить в них то или иное изображение. Чтобы изменить последовательность изображений в списке, перемещают изображение мышью на новое место.
В редакторе списков изображений, выделив то или иное изображение, можно установить его свойства: Прозрачный цвет и Цвет за. Для пиктограмм эти свойства устанавливаются в clNone.

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

TreeView
(окно дерева данных)
Win32
Просмотр структуры иерархических данных в виде дерева в стиле Windows 95/98/2000

Outline
(окно дерева данных)
Win3.1
Просмотр структуры иерархических данных в виде дерева в стиле Windows 3.x

ListView
(список данных в стиле Windows 95)
Win32
Отображение в стиле папок Windows списков в виде колонок или пиктограмм


Компоненты TreeView и Outline служат для отображения иерархических данных в виде дерева, в котором пользователь может выбрать нужный ему узел или узлы. С каждым узлом дерева могут быть связаны некоторые данные. Возможности компонента TreeView несколько шире, чем компонента Outline. Поэтому изучим только компонент TreeView.
Основным свойством TreeView, содержащим информацию об узлах дерева, является Items. Свойство Items объектного типа TTreeNodes содержит список узлов дерева. Доступ к информации об отдельных узлах осуществляется через свойство Items->Item[int Index]. Например, TreeView1–> Items–>Item[0] – это узел дерева с индексом 0 (первый узел дерева). Каждый узел является объектом типа TTreeNodes, обладающим своими свойствами и методами.

В качестве примера иерархических данных возьмем фрагмент структуры факультета.
Сначала рассмотрим формирование дерева в процессе проектирования.
Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА TREEVIEW_1 и командой Сохранить все сохраните файл модуля и проект под разными именами.
Разместите на пустой форме компонент TreeView1 со страницы Win32 и просмотрите открывшиеся в Инспекторе Объектов свойства.
Во время проектирования формирование дерева осуществляется в окне редактора элементов компонента TreeView1 (узлов дерева). Вызовите окно двойным щелчком на компоненте или нажатием кнопки с многоточием около свойства Items в окне Инспектора Объектов. Окно редактора содержит две панели: Элементы и Свойства элемента. Панель Элементы содержит группу кнопок.
Кнопка Новый элемент позволяет добавить в дерево новый узел. Он будет расположен на том же уровне, на котором расположен узел, выделенный курсором в момент щелчка на кнопке Новый элемент.
Кнопка Новый подэлемент позволяет добавить в дерево дочерний узел. Он будет расположен на уровень ниже уровня того узла, который выделен курсором в момент щелчка на кнопке Новый подэлемент.
Кнопка Удалить удаляет выделенный узел дерева.
Кнопка Загрузить позволяет загрузить структуру дерева из файла. Файл, хранящий структуру дерева – это обычный текстовый файл, содержащий тексты узлов. Уровни узлов обозначаются отступами. Например, файл проектируемого дерева должен иметь вид
Кафедра АТ
ИТ
ИТ-11
ИТ-12
УТ
Кафедра ЭВМ
ИВТ
Для каждого нового узла в панели Свойства элемента можно указать ряд свойств. Свойство Текст – это надпись, появляющаяся в дереве около данного узла. Свойства Индекс образа и Выделенный индекс определяют индекс пиктограммы, отображаемой для узла, который соответственно не выделен и выделен пользователем в данный момент. Эти индексы соответствуют списку пиктограмм (в общем случае – изображений, образов), хранящихся в отдельном компоненте ImageList. Указание на этот компонент задается в свойстве Images компонента TreeView. Индексы начинаются с нуля. Если указать индекс -1 (значение по умолчанию), пиктограммы изображаться не будут. Последнее свойство – Индекс статус-образа позволяет добавить еще одну пиктограмму в данный узел, не зависящую от состояния узла. Подобная пиктограмма может просто служить дополнительной характеристикой узла. Индекс, указанный как Индекс статус-образа, соответствует списку пиктограмм, хранящихся в отдельном компоненте ImageList, указанном в свойстве StateImages компонента TreeView.
Разместите на форме два компонента – ImageList1 и ImageList2. Загрузите в первый компонент не менее 14 пиктограмм, во второй – не менее 7 пиктограмм.
Выделите компонент TreeView1. В свойстве Images задайте ImageList1, а в свойстве StateImages – ImageList2.
Спроектируйте дерево согласно содержимому приведенного выше файла, задавая для каждого узла по 3 пиктограммы. Следует учесть, что Индекс статус-образа не должен быть равным 0. Например, для первого узла можно задать индексы пиктограмм – 0, 1, 1, для второго узла – 2, 3, 2 и т.д.
В правом верхнем углу формы поместите кнопку Button1 и назовите ее КОНЕЦ. В обработчик щелчка кнопки впишите Close();.
Сохраните все и запустите приложение на выполнение. Убедитесь в работоспособности приложения.

Теперь рассмотрим формирование дерева во время выполнения приложения. Для этого имеется ряд методов объектов типа TTreeNodes.

Следующие методы позволяют вставлять в дерево новые узлы:

Add(TTreeNode* Node,
const System::AnsiString S)
Добавляет новый узел с текстом S как последний узел уровня, на котором расположен Node

AddFirst(TTreeNode* Node,
const System::AnsiString S)
Вставляет новый узел с текстом S как первый из узлов уровня, на котором находится Node. Индексы последующих узлов увеличиваются на 1

Insert(TTreeNode* Node,
const System::AnsiString S)
Вставляет новый узел с текстом S сразу после узла Node на тот же уровень. Индексы последующих узлов увеличиваются на 1

AddChild(TTreeNode* Node,
const System::AnsiString S)
Добавляет узел с текстом S как последний дочерний узла Node

AddChildFirst(TTreeNode* Node,
const System::AnsiString S)
Вставляет новый узел с текстом S как первый из дочерних узлов узла Node. Индексы последующих узлов увеличиваются на 1


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

AddObject(TTreeNode* Node,
const System::AnsiString S, void* Ptr)
Добавляет новый узел с текстом S и объектом Ptr как последний узел уровня, на котором расположен Node

AddObjectFirst(TTreeNode* Node,
const System::AnsiString S, void* Ptr)
Вставляет новый узел с текстом S и объектом Ptr как первый из узлов уровня, на котором находится Node. Индексы последующих узлов увеличиваются на 1

InsertObject(TTreeNode* Node,
const System::AnsiString S, void* Ptr)
Вставляет новый узел с текстом S и объектом Ptr сразу после узла Node на тот же уровень. Индексы последующих узлов увеличиваются на 1

AddChildObject(TTreeNode* Node,
const System::AnsiString S, void* Ptr)
Добавляет узел с текстом S и объектом Ptr как последний дочерний узла Node

AddChildObjectFirst(TTreeNode* Node, const System::AnsiString S,
void* Ptr)
Вставляет новый узел с текстом S и объектом Ptr как первый из дочерних узлов узла Node. Индексы последующих узлов увеличиваются на 1


Каждый из приведенных выше методов возвращает вставленный узел.

Откройте новый проект и командой Сохранить все сохраните файл модуля и проект под разными именами. Назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА TREEVIEW_2. На форму перенесите компоненты TreeView1, Button1 (назовите КОНЕЦ) и Button2 (назовите ЗАДАТЬ ДЕРЕВО).
Обработчики щелчков кнопок задайте соответственно такими:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
TreeView1->Items->Clear();
TreeView1->Items->Add(NULL,"Кафедра АТ");
TreeView1->Items->AddChild(TreeView1->Items->Item[0],"ИТ");
TreeView1->Items->AddChild(TreeView1->Items->Item[0],"УТ");
TreeView1->Items->Add(TreeView1->Items->Item[0],"Кафедра ЭВМ");
TreeView1->Items->AddChild(TreeView1->Items->Item[3],"ИВТ");
TreeView1->Items->AddChild(TreeView1->Items->Item[1],"ИТ-11");
TreeView1->Items->AddChild(TreeView1->Items->Item[1],"ИТ-12");
}

Запустите приложение на выполнение и убедитесь в его работоспособности. Сохраните приложение.

Текст, связанный с некоторым узлом, можно найти с помощью его свойства Text. Например, TreeView1->Items->Item[1]->Text – это надпись ”ИТ".
Объект, связанный с некоторым узлом, можно найти с помощью его свойства Data. Например, TreeView1->Items->Item[1]->Data.
Для удаления узлов есть два метода: Clear(void), очищающий все дерево, и Delete(TTreeNode* Node), удаляющий указанный узел Node и все его узлы-потомки. При удалении узлов, связанных с объектами, объекты не удаляются.
Реорганизация дерева, связанная с созданием или удалением многих узлов, может вызывать неприятное мерцание изображения. Избежать этого можно с помощью методов BeginUpdate (запрещает перерисовку дерева) и EndUpdate (разрешает перерисовку дерева). Таким образом, изменение структуры дерева может осуществляться так:
TreeView1->Items->BeginUpdate();
<операторы изменения дерева>
TreeView1->Items->EndUpdate();
Если метод BeginUpdate применен подряд несколько раз, то перерисовка дерева произойдет только после того, как столько же раз будет применен метод EndUpdate.
Свойство узла Count – это число узлов, управляемых данным, т.е. дочерних узлов, их дочерних узлов и т.п. Если значение Count равно 0, узел является листом дерева.
Важным свойством компонента TreeView является Selected. Это свойство указывает узел, который выделен пользователем. Пользуясь этим свойством, можно запрограммировать операции, которые надо выполнить для выбранного пользователем узла. Если ни один узел не выделен, значение Selected равно NULL.
При выделении пользователем нового узла происходят события OnChanging (перед изменением выделения) и OnChange – после выделения. В обработчик события OnChanging передаются параметры TTreeNode* Node – узел, который выделен в данный момент, и bool &AllowChange – разрешение на перенос выделения. Если в обработчике задать AllowChange=false, то переключение выделения не произойдет. В обработчик события OnChange передается параметр TTreeNode* Node – выделенный узел. В этом обработчике предусматривают действия, которые должны производиться при выделении узла.
Ряд событий компонента TreeView связан с развертыванием и свертыванием узлов. При развертывании узла происходят события OnExpanding (перед развертыванием) и OnExpanded (после развертывания). В обработчики этих событий передается параметр TTreeNode* Node – развертываемый узел. Кроме того, в обработчик OnExpanding передается параметр bool &AllowExpansion, который нужно задать равным false, чтобы запретить развертывание. При свертывании узла происходят события OnCollapsing (перед свертыванием) и OnCollapsed (после свертывания). Так же, как и в событиях, связанных с развертыванием, в обработчики передается параметр TTreeNode* Node – свертываемый узел, а в обработчик OnCollapsing дополнительно передается параметр bool &AllowCollapse, разрешающий или запрещающий свертывание.
Свойство ReadOnly компонента TreeView позволяет запретить пользователю редактировать отображаемые данные. Если редактирование разрешено, то при редактировании возникают события OnEditing и OnEdited, аналогичные рассмотренным ранее (в обработчике OnEditing параметр bool &AllowEdit позволяет запретить редактирование).
Ряд свойств компонента TreeView: ShowButtons, ShowLines, ShowRoot – позволяют отображать или убирать из дерева кнопки, показывающие раскрытия узла, линии, связывающие узлы, и корневой узел.
Свойство SortType позволяет автоматически сортировать ветви и узлы дерева. По умолчанию это свойство равно stNone, что означает, что дерево не сортируется. Если установить SortType равным stText, то узлы будут автоматически сортироваться по алфавиту. Возможно также проводить сортировку по связанным с узлами объектам Data (значение SortType равно stData), одновременно по тексту и объектам Data (значение SortType равно stBoth) или любым иным способом.
Для использования этих возможностей сортировки надо написать обработчик события OnCompare, в который передаются параметры TTreeNode*Node1 и TTreeNode*Node2 – сравниваемые узлы, и параметр int &Compare, в который надо заносить результат сравнения: отрицательное число, если узел Node1 должен располагаться ранее Node2, нуль, если эти узлы считаются эквивалентными, и положительное число, если узел Node1 должен располагаться в дереве после Node2. Например, следующий оператор в обработчике события OnCompare обеспечивает обратный алфавитный порядок расположения узлов:

OnCompare = -AnsiStrIComp(Node1->Text.c_str(),Node2->Text.c_str());

События OnCompare наступают после задания любого значения SortType, отличного от stNone, и при изменении пользователем свойств узла (например, при редактировании им надписи узла), если значение SortType не равно stNone. После сортировки первоначальная последовательность узлов в дереве теряется. Поэтому последующее задание SortType=stNone не восстанавливает начальное расположение узлов, но исключает дальнейшую генерацию событий OnCompare, т.е. автоматическую перестановку узлов, например, при редактировании их надписей. Если же требуется изменить характер сортировки или провести сортировку с учетом новых созданных узлов, то надо сначала задать значение SortType=stNone, а затем задать любое значение SortType, отличное от stNone. При этом будут сгенерированы новые обращения к обработчику событий OnCompare.
Имеются и другие возможности сортировки. Например, метод AlphaSort(void) обеспечивает алфавитную последовательность узлов независимо от значения SortType, но при отсутствии обработчика событий OnCompare (если обработчик есть, то при выполнении метода AlphaSort происходит обращение к этому обработчику). Отличие метода AlphaSort от задания значения SortType=stText заключается в том, что изменение надписей узлов приводит к автоматической пересортировке дерева только при SortType=stText.
При разработке дополнения в нашем проекте удобно использовать обработчики событий OnKeyDown и OnKeyUp. Событие OnKeyDown/OnKeyUp наступает, если компонент находится в фокусе, при нажатии/отжатии пользователем любой клавиши, включая функциональные и вспомогательные, такие, как Shift, Alt и Ctrl. В обработчики событий передаются, кроме обычного параметра Sender, указывающего на компонент, в котором произошло событие, параметры Key и Shift. Параметр Key определяет нажатую/отжатую клавишу клавиатуры. Параметр Shift является множеством, которое может быть пустым или включать в себя следующие элементы: ssShift, ssAlt, ssCtrl (нажата клавиша Shift, Alt, Ctrl соответственно).

Компонент StatusBar (находится на странице Win32) представляет собой ряд панелей, отображающих полосу состояния в стиле Windows. Обычно эта полоса размещается внизу формы.
Свойство SimplePanel определяет, включает ли полоса состояния одну или множество панелей. Если SimplePanel = true, то вся полоса состояния представляет собой единственную панель, текст которой задается свойством SimpleText. Если же SimplePanel = false, то полоса состояния является набором панелей, задаваемых свойством Panels. В этом случае свойство SizeGrip определяет, может ли пользователь изменять размеры панелей в процессе выполнения приложения.
Каждая панель полосы состояния является объектом типа TStatusPanels. Свойства панелей задают специальным редактором наборов, который можно вызвать тремя способами: из Инспектора Объектов кнопкой с многоточием около свойства Panels, двойным щелчком на компоненте StatusBar или из контекстного меню, выбрав команду Panels Editor. В окне редактора можно перемещаться по панелям, добавлять новые или уничтожать существующие панели. При этом в окне Инспектора Объектов будут видны их свойства.
Основное свойство каждой панели – Text, в который заносится отображаемый в панели текст. Его можно занести в процессе проектирования, а затем можно изменять программно во время выполнения. Другое существенное свойство – Width (ширина).
Программный доступ к текстам отдельных панелей возможен через свойство Panels и его индексированное подсвойство Items. Например, оператор:
StatusBar1->Panels->Items[0]->Text=”текст 1”;
выведет текст “текст1” в первую панель.
Количество панелей полосы состояния можно определить из подсвойства Count свойства Panels. Поэтому тексты всех панелей можно очистить так:
for(int i=0; iPanels->Count; i++)
{ StatusBar1->Panels->Items[i]->Text=””;}

Вернемся к сохраненному выше проекту. Перенесем на форму компонент StatusBar1, его свойство SimplePanel установим равным true. В код модуля добавим глобальную переменную bool ShiftPressed и обработчики событий OnChange, OnKeyDown, OnKeyUp:

void __fastcall TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node)
{
String pn="",cn="";
TTreeNode*ParentNode=Node->Parent;
if(ParentNode) pn="..Родитель: "+ParentNode->Text;
TTreeNode*ChildNode=Node->getFirstChild();
if(ChildNode) cn="..Наследник:.."+ChildNode->Text;
Caption=pn+cn;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::TreeView1KeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
TTreeNode*Node=TreeView1->Selected;
switch(Key)
{ case VK_DELETE:
if(Node)
{ StatusBar1->SimpleText="Удалить узел: "+Node->Text;
Node->Delete();
} ShiftPressed=false; break;
case VK_INSERT:
if(Node)
{ StatusBar1->SimpleText="Создать узел: "+Node->Text;
TreeView1->Items->Insert(Node,"Новый узел");
} ShiftPressed=false; break;
case VK_SHIFT:
ShiftPressed=t
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
Убедитесь в работоспособности дополненного приложения, добавляя новые узлы клавишей Insert и удаляя выделенный узел клавишей Delete. Сохраните приложение.

Перейдем к рассмотрению процесса перемещения с помощью мыши информации из одного объекта в другой (технология Drag&Drop), коротко называемого перетаскиванием. Считается, что любой профессиональный программный продукт должен обладать этой полезной способностью.
Все свойства, методы и события, связанные с процессом перетаскивания, определены в классе TControl, являющемся прародителем всех визуальных компонентов C++Builder. Поэтому они являются общими для всех компонентов.
Начало процесса перетаскивания определяется свойством DragMode, которое может устанавливаться в процессе проектирования или программно равным dmManual или dmAutomatic. Значение dmAutomatic (автоматическое) определяет автоматическое начало процесса перетаскивания при нажатии пользователем кнопки мыши над компонентом. Значение же dmManual (ручное) говорит о том, что начало процесса перетаскивания должен определять программист. Для этого он должен в соответствующий момент вызвать метод BeginDrag. Например, он может поместить вызов этого метода в обработчик события OnMouseDown, наступающего в момент нажатия кнопки мыши. В этом обработчике он может проверить предварительно какие-то условия (режим работы приложения, нажатие тех или иных кнопок мыши и вспомогательных клавиш) и при выполнении этих условий вызвать BeginDrag.
Во время перетаскивания объекта обычный вид курсора изменяется. Пока курсор перемещается над формой или компонентами, которые не могут принять информацию из перетаскиваемого объекта, он обычно имеет тип crNoDrop: «кольцо с перемычкой». Если же курсор перемещается над компонентом, готовым принять информацию из перетаскиваемого объекта, то он приобретает вид, определяемый свойством перетаскиваемого объекта DragCursor. По умолчанию это свойство равно crDrag, что соответствует изображению «стрелка из квадрата». Подчеркнем, что вид курсора определяется свойством DragCursor перетаскиваемого объекта, а не того объекта, над которым перемещается курсор.
В процессе перетаскивания компоненты, над которыми перемещается курсор, могут сообщать о готовности принять информацию от перетаскиваемого объекта. Для этого в компоненте должен быть предусмотрен обработчик события OnDragOver, наступающего при перемещении над данным компонентом курсора, перетаскивающего некоторый объект. В этом обработчике надо проверить, может ли данный компонент принять информацию перетаскиваемого объекта, и если не может, задать значение false передаваемому в обработчик параметру Accept. По умолчанию этот параметр равен true, что означает возможность принять перетаскиваемый объект.
Значение параметра Accept, задаваемое в обработчике события OnDragOver, определяет вид курсора, перемещающегося при перетаскивании над данным компонентом. Если в компоненте не описан обработчик события OnDragOver, то считается, что данный компонент не может принять информацию перетаскиваемого объекта.
Процедура приема информации от перетаскиваемого объекта записывается в обработчике события OnDragDrop принимающего компонента. Это событие наступает, если после перетаскивания пользователь отпустил кнопку мыши над данным компонентом. В обработчик этого события передаются параметры Source (объект-источник) и X и Y координаты курсора.
После завершения или прерывания перетаскивания наступает событие OnEndDrag, в обработчике которого можно предусмотреть какие-то дополнительные действия. Имеется также связанное с перетаскиванием событие OnStartDrag, которое позволяет произвести какие-то операции в начале перетаскивания. Это событие полезно при автоматическом начале перетаскивания, когда иным способом этот момент нельзя зафиксировать.

Вернемся к дополненному выше проекту.
Свойство DragMode компонента TreeView1 установим равным dmAutomatic.
В код модуля добавим обработчики событий OnDragOver, OnDragDrop:

void __fastcall TForm1::TreeView1DragOver(TObject *Sender, TObject *Source,
int X, int Y, TDragState State, bool &Accept)
{
Accept=Source-
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·ndent)) AttachMode=naInsert;
else return;
if(ShiftPressed)
{ StatusBar1->SimpleText="Копировать узел: "+ TreeView1->Selected->Text;
Node->Assign(TreeView1->Selected);
}
else
{ StatusBar1->SimpleText="Переместить узел: "+ TreeView1->Selected->Text;
TreeView1->Selected->MoveTo(Node,AttachMode);
}
TreeView1->Update();
}

Убедитесь в работоспособности дополненного приложения, используя для перемещения узлов левую кнопку мыши, а для копирования узлов – левую кнопку мыши и нажатую клавишу Shift.

Компонент ListView позволяет отображать в стиле Windows данные в виде списков, таблиц, крупных и мелких пиктограмм. Стиль отображения определяется свойством ViewStyle, которое может устанавливаться в процессе проектирования или программно во время выполнения. Свойство может принимать значения: vsIcon – крупные значки, vsSmallIcon – мелкие значки, vsList – список, vsReport – таблица.

Рассмотрим использование компонента ListView в процессе проектирования.
Создайте новый проект, назовите форму ДЕМОНСТРАЦИЯ КОМПОНЕНТА LISTVIEW и командой Сохранить все сохраните файл модуля и проект под разными именами.
Разместите на форме два компонента – ImageList1 и ImageList2. Загрузите в первый компонент не менее 11 пиктограмм, во второй – не менее 2 пиктограмм.
Разместите на пустой форме компонент ListView1 со страницы Win32 и просмотрите открывшиеся в Инспекторе Объектов свойства.
Основное свойство компонента, описывающее состав отображаемой информации – Items. Рядом с этим свойством в окне Инспекторе Объектов имеется кнопка с многоточием, щелчком по которой вызовем Редактор элементов ListView.
В редакторе задайте структуру:
ИТ
ИТ-11
ИТ-12
УТ
УТ-11
УТ-12
УТ-13,
устанавливая индексы образа равными 0, 1, 6, а индексы статус-образа (для узлов ИТ и УТ) – 0 и 1. Для перехода из одного окна в другое пользуйтесь клавишей Tab.
Выделите ListView1. Свойствам LargeImages и SmallImages задайте значение ImageList1, а свойству StateImages – ImageList2.
Свойство Columns определяет список заголовков таблицы в режиме vsReport. Двойным щелчком но компоненте ListView1 или щелчком на кнопке с многоточием рядом со свойством Columns вызовите редактор заголовков (Правка List). Крайняя левая кнопка позволяет добавить новую секцию в заголовок, вторая слева – удалить секцию, а две кнопки со стрелками позволяют изменять последовательность секций. После того, как секция добавлена и на ней установлен курсор, в окне Инспектора Объектов появится множество свойств этого объекта. В свойстве Caption последовательно задавайте тексты секций заголовка – специальность, группы 1, группы 2, группы 3, а в свойстве ImageIndex укажите индексы пиктограмм, которые появятся перед текстами секций заголовка – 7, 8, 9, 10 соответственно. Свойства MinWidth и MaxWidth определяют соответственно минимальную и максимальную ширину заголовка в пикселах. Только в этих пределах пользователь может изменять ширину заголовка курсором мыши. Для всех заголовков значение MaxWidth поставьте равным 300. Значение ширины по умолчанию задается значением свойства Width.
Выполните приложение при значениях свойства ViewStyle: vsIcon – крупные значки, vsSmallIcon – мелкие значки, vsList – список, vsReport – таблица. Курсором мыши поварьируйте шириной заголовков. Сохраните приложение для последующего дополнения.

Дополненное приложение предоставит пользователю возможность изменять вид списка в окне ListView1 и, кроме того, позволит при стилях vsIcon и vsSmallIcon перетаскивать пиктограммы мышью в любое место окна.

На форму сохраненного приложения со страницы Стандарт перенесите компонент MainMenu1. Это, как и ImageList, невизуальный компонент, т.е. место его размещения на форме в процессе проектирования не имеет никакого значения для пользователя – во время выполнения приложения он увидит не сам компонент, а только меню, сгенерированное им.
Основное свойство компонента MainMenu1 – Items. Его заполнение производится с помощью Конструктора Меню, вызываемого двойным щелчком на компоненте MainMenu или нажатием кнопки с многоточием рядом со свойством Items в окне Инспектора Объектов. В открывшемся окне нужно спроектировать меню с разделами Крупные значки (имя MIcon), Мелкие значки (имя MSmallIcon), Список (имя MList), Таблица (имя MReport). Название раздела (курсив) заносится в свойство Caption, а имя – в свойство Name соответствующего раздела. Новые разделы можно вводить, помещая курсор в рамку из точек, обозначающую место расположения нового раздела. Если раздел ввелся не на нужном месте, его можно отбуксировать мышью в нужное место.
Установите во всех разделах одинаковый, не равный нулю, индекс GroupIndex и свойства RadioItem в true, чтобы обеспечить работу каждого из разделов в режиме радиокнопки с другими разделами.
Впишите обработчики щелчков для разделов меню и для событий OnDragOver, OnDragDrop компонента ListView1:

void __f
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
Выполните построенное приложение. Убедитесь, что с помощью меню можно изменять вид списка в окне ListView1 и, кроме того, при стилях vsIcon и vsSmallIcon можно перемещать пиктограммы мышью в любое место окна.
Метод Arrange:

void _fastcall Arrange(TListArrangement Code);

позволяет упорядочить пиктограммы в режимах vsIcon и vsSmallIcon. Параметр Code определяет способ упорядочивания:

arAlignBottom
выравнивание вдоль нижнего края области

arAlignLeft
выравнивание вдоль левого края области

arAlignRight
выравнивание вдоль правого края области

arAlignTop
выравнивание вдоль верхнего края области

arDefault
выравнивание (по умолчанию) вдоль верхнего края области

arSnapToGrid
размещение каждой пиктограммы в ближайшем узле сетки


Добавьте в меню раздел Выравнивание и в обработчик щелчка на нем запишите оператор:
ListView1->Arrange(arAlignTop);
Тогда после перемещения элементов всегда можно упорядочить их расположение, выбрав этот раздел меню.




Контрольные вопросы

Когда используется компонент ImageList? Как в процессе проектирования загружаются изображения? Какие свойства имеют изображения?
Приведите примеры иерархических данных. Какие компоненты способны отображать такие данные и в каком виде?
Как осуществляется доступ к отдельным узлам дерева?
Как проектировать дерево?
Какие свойства и как можно указать для каждого нового узла дерева?
Расскажите о формировании дерева во время выполнения приложения.
Какие методы позволяют вставлять в дерево новые узлы?
Какие методы позволяют вставлять в дерево новые узлы, если с каждым узлом может быть связан некоторый объект?
Как найти текст и объект, связанный с некоторым узлом дерева?
Как удаляются узлы дерева?
Как избежать мерцания изображения дерева при его реорганизации?
Как узнать число узлов дерева, управляемых данным узлом?
Какое свойство компонента TreeView указывает выделенный узел?
Какие события происходят при выделении узла дерева? Какие параметры передаются в обработчики этих событий?
Какие события компонента TreeView связаны с развертыванием и свертыванием узлов? Какие параметры передаются в обработчики этих событий?
Как запретить и разрешить пользователю редактировать отображаемые деревом данные?
Какие свойства компонента TreeView позволяют отображать или убирать из дерева кнопки, показывающие раскрытия узла, линии, связывающие узлы, и коренной узел?
Какое свойство и как позволяет автоматически сортировать ветви и узлы дерева?
Какой метод и как можно использовать для сортировки узлов?
Как используются обработчики событий «нажать/отжать клавишу» для работы с деревом?
Что представляет собой компонент StatusBar? Какие возможности предоставляют его свойства?
Как задать свойства панелей?
Как занести в панель текст в процессе проектирования и изменять его программно во время выполнения?
На примере расскажите о применении технологии Drag&Drop.
Какие свойства, общие для всех визуальных компонентов, связаны с процессом перетаскивания?
Какие события и какой метод связаны с процессом перетаскивания?
Расскажите о назначении компонента ListView.
Расскажите о свойствах компонента ListView. Как пользоваться редактором элементов ListView? Редактором заголовков?
Расскажите об использовании компонента ListView в процессе проектирования.
Как с помощью меню можно изменять вид списка в окне ListView и перемещать пиктограммы?
Как упорядочить расположение пиктограмм?





Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 176–186, 209–214, 229–230, 325–328.
Шамис В.А. Borland C++Builder 6. Для профессионалов / В.А. Шамис.
· СПб.: Питер, 2003.
· 798 с. – С. 340
·345.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.





ЗАНЯТИЕ 6

Главное меню.
Контекстное всплывающее меню.
Горячие клавиши

Меню представляют два компонента, расположенные на странице Стандарт: MainMenu – главное меню и PopupMenu – всплывающее меню.
Компонент MainMenu является невизуальным компонентом, т.е. место его размещения на форме в процессе проектирования не имеет значения для пользователя – во время выполнения он увидит не сам компонент, а только меню, сгенерированное компонентом.
Обычно на форму помещается один компонент MainMenu. В этом случае его имя автоматически заносится в свойство формы Menu. Но в случае необходимости можно поместить на форму несколько компонентов MainMenu с разными наборами разделов, соответствующими различным режимам работы приложения. В этом случае во время проектирования свойству Menu формы присваивается ссылка на один из этих компонентов, а в процессе выполнения в нужные моменты это свойство можно изменять, меняя соответственно состав главного меню приложения.
Основное свойство компонента MainMenu – Items. Его заполнение производится с помощью Конструктора Меню, вызываемого двойным щелчком на компоненте MainMenu или нажатием кнопки с многоточием рядом со свойством Items в окне Инспектора Объектов. При работе в окне Конструктора Меню вводят новые разделы и помещают их в нужное место.
При выборе но
·вого раздела (щелчком на разделе) в Инспекторе Объектов появится множество свойств данного раздела, поскольку каждый раздел меню, т.е. каждый элемент свойства Items является объектом типа TMenuItem, обладающим своими свойствами, методами, событиями. Рассмотрим основные свойства раздела.
Свойство Caption обозначает надпись раздела. Правила заполнения этого свойства следующие.
В надписи можно предусмотреть использование клавиши ускоренного доступа, выделяя для этого один из символов надписи. Перед символом, который должен соответствовать клавише ускоренного доступа, ставится символ амперсанда '&’. Этот символ не появляется в надписи, а следующий за ним символ оказывается подчеркнутым. Тогда пользователь может вместо щелчка на кнопке нажать в любой момент клавишу Alt совместно с клавишей выделенного символа.
Если в качестве значения Caption очередного раздела ввести символ минус, то вместо раздела в меню появится разделитель.
Свойство Name задает имя объекта, соответствующего разделу меню. Рекомендуется давать этим объектам осмысленные имена.
Свойство ShortCut определяет клавиши быстрого доступа к разделу меню – «горячие» клавиши, с помощью которых пользователь, даже не заходя в меню, может в любой момент вызвать выполнение действий, связанных с данным разделом. Чтобы определить клавиши быстрого доступа, надо открыть выпадающий список свойства ShortCut в окне Инспектора Объектов и выбрать из него нужную комбинацию клавиш. Эта комбинация появится в строке раздела меню.
Свойство Default определяет, является ли данный раздел разделом по умолчанию, т.е. разделом, выполняемым при двойном щелчке пользователя на головном разделе. Подменю может содержать только один раздел по умолчанию, выделяемый жирным шрифтом.
Свойство Break используется в длинных меню, чтобы разбить список разделов на несколько столбцов. Возможные значения Break: mbNone – отсутствие разбиения меню (это значение принято по умолчанию), mbBarBreak и mbBreak – в меню вводится новый столбец разделов, отделенный от предыдущего полосой (mbBarBreak) или пробелами (mbBreak).
Свойство Checked, установленное в true, указывает, что в разделе меню будет отображаться маркер флажка, который можно использовать для показа того, что данный раздел выбран. Однако при выборе и отсутствии выбора маркер сам по себе не переключается, и поэтому в обработчик события OnClick раздела нужно вставлять оператор типа
<имя_раздела>->Checked =! <имя_раздела>->Checked;
Свойство AutoCheck, установленное в true, обеспечивает при каждом выборе пользователем данного раздела автоматическое переключение маркера, указывая то на выбранное состояние, то на отсутствие выбора.
Еще одним свойством, позволяющим вводить маркеры в разделы меню, является RadioItem. Это свойство, установленное в true, определяет, что данный раздел должен работать в режиме радиокнопки совместно с другими разделами, имеющими то же значение свойства GroupIndex. По умолчанию значение GroupIndex равно нулю. Если же при RadioItem = true для нескольких разделов задать значение GroupIndex большим нуля и одинаковым, то все эти разделы могут быть отмечены маркерами флажков, но только в выбранном разделе появится маркер флажка. Если задать программно в одном из этих разделов Checked = true, то в остальных разделах Checked автоматически сбросится в false.
Для подобных групп разделов, работающих как радиокнопки, можно установить в true свойство AutoCheck. Тогда при щелчке пользователя на невыбранном разделе все будет работать нормально: этот раздел включится, а ранее включенный выключится. Но если пользователь щелкнет на выбранном разделе, то он выключится и окажется, что все разделы данной группы выключены. Если по смыслу это допустимое состояние, то все можно оставить без изменений. Но если в любом случае один из разделов должен быть включен, то надо отказаться от использования свойства AutoCheck (установить его в false) и переключать свойство Checked программно. Например, в обработчики щелчков этих разделов можно ввести оператор:
if(! ((TMenuItem *) Sender)->Checked)
((TMenuItem *) Sender)->Checked) = true;
Он переключает маркер только в случае, если сделан щелчок на невыбранном разделе меню. Оператор записан в общем виде и в качестве источника события (параметр Sender) может фигурировать любой раздел меню.
Маркеры флажков в режиме радиокнопок и в обычном режиме используются для разделов меню, представляющих собой различные опции, взаимоисключающие или совместимые.
Для каждого раздела могут быть установлены пользователем во время проектирования или программно во время выполнения свойства Enabled (доступен) и Visible (видимый). Если установить Enabled = false, то раздел будет изображаться серой надписью и не будет реагировать на щелчок пользователя. Если же задать Visible = false, то раздел вообще не будет виден, а остальные разделы сомкнутся, заняв место невидимого. Свойства Enabled и Visible используются для того, чтобы изменять состав доступных пользователю разделов в зависимости от режима работы приложения.
Имеется также возможность ввода в разделы меню изображений. Для этого используют свойства разделов Bitmap и ImageIndex. Первое из них позволяет непосредственно ввести изображение в раздел, выбрав его из указанного пользователем файла. Второе позволяет указать индекс изображения, хранящегося во внешнем компоненте ImageList. Указание на этот компонент задается в свойстве Images компонента MainMenu. Индексы начинаются с нуля. Если указать индекс -1 (значение по умолчанию), изображения не будет.
Основное событие раздела – OnClick, возникающее при щелчке пользователя на разделе или при нажатии «горячих» клавиш быстрого доступа.
В общем случае приложения имеют одну главную и несколько вспомогательных форм. И главная, и вспомогательная формы имеют свои главные меню – компоненты MainMenu. Обычно нужно, чтобы эти меню сливались в одно меню главной формы, так как пользователю неудобно работать одновременно с несколькими окнами, каждое из которых имеет свое меню. Возникает вопрос объединения главных меню вспомогательных форм с меню главной формы.
Наличие или отсутствие объединения определяется свойством AutoMerge компонентов MainMenu. Если требуется, чтобы меню вспомогательных форм объединялись с меню главной формы, то в каждой такой вспомогательной форме надо установить AutoMerge в true. При этом свойство AutoMerge главной формы должно оставаться в false.
Способ объединения определяется свойством разделов GroupIndex. Если требуется объединение меню, то разделам надо задать большие нуля и неубывающие значения GroupIndex. Тогда, если разделы встраиваемого (вспомогательного) меню имеют те же значения GroupIndex, что и какие-то разделы основного меню (меню главной формы), то разделы встраиваемого меню заменяют соответствующие разделы основного меню. В противном случае разделы вспомогательного меню встраиваются между элементами основного меню в соответствии с номерами GroupIndex. Например, в главной и вспомогательной формах структуры меню имеют следующие значения GroupIndex:
Форма 1 Форма 2
2 – 4 1 – 3
| | | |
2 4 1 3
| | |
2 4 1
Тогда в момент, когда активизируется вторая форма, в первой появляется меню со структурой:
1 – 2 – 3 – 4
| | | |
1 2 3 4
| | |
1 2 4
В этом примере отсутствовали разделы, имеющие в обеих формах одинаковые значения GroupIndex. Если бы такие были, то при активизации второй формы соответствующие разделы ее меню заменили бы аналогичные разделы первой формы .
Если в меню имеются разделы, работающие как радиокнопки, то их взаимодействие также определяется свойствами GroupIndex.
Рассмотрим пример использования компонента MainMenu и варианты объединения главного меню вспомогательной формы с главным меню главной формы.
Создайте новое приложение и командой Сохранить все сохраните файл модуля и проект под предложенными по умолчанию именами – Unit1 и Project1 в созданной для приложения папке (каталоге).
В свойство Caption формы Form1 впишите ГЛАВНАЯ ФОРМА.
Со страницы Стандарт на форму перенесите компонент MainMenu1, двойным щелчком на нем перейдите в Конструктор Меню (окно Form1-> MainMenu1) и сконструируйте меню с двумя головными разделами Формирование квадратных матриц, Операции с матрицей.
Формирование квадратных матриц
квадратная 5x5 Alt+A
произвольная
диагональная
треугольная
симметрическая
ортогональная
Отметим, что головные разделы Формирование квадратных матриц, Операции с матрицей вводятся точно так же, как и названия разделов. В разделе квадратная 5x5 Alt+A свойство Default следует установить в true, а в свойство ShortCut («горячие» клавиши) – вписать Alt+A.
В указанные ниже разделы введите следующие подразделы:
произвольная | неособенная диагональная | единичная
| особенная | трехдиагональная
| ленточная
треугольная | нижняя | квазидиагональная
| верхняя

симметрическая | общего вида
| положительно определенная
| кососимметрическая
Для этого, не выходя из Конструктора Меню, после выделения нужного раздела щелчком правой кнопкой вызовите контекстное меню и выберите пункт Создать подменю.
Операции с матрицей
вычисление определителя Ctrl+A обращение | вычисление m - нормы
вычисление ранга Ctrl+B | вычисление k – нормы
вычисление следа Ctrl+C | вычисление l - нормы
––––––––––––––––––––––––––––––
транспонирование Ctrl+D
«Горячие» клавиши в разделах под головным разделом Операции с матрицей выбираются в свойстве ShortCut из выпадающего списка. В свойство Caption раздела, следующего за разделом вычисление следа Ctrl+C, введите символ '–, чтобы вместо раздела появился разделитель. Для раздела обращение свойство Break установите в mbBreak, а для раздела вычисление m–нормы – в mbBarBreak.
Со страницы Стандарт перенесите на форму компонент Memo1. Установите шрифт (Font) Courier, полужирный. С этой же страницы перенесите на форму кнопку Button1, назовите ее ПЕРЕХОД НА 1-Ю ВСПОМОГАТЕЛЬНУЮ ФОРМУ, а в обработчик щелчка на кнопке впишите оператор: Form2->Show();
Этот оператор покажет пользователю окно 1-й вспомогательной формы Form2.
В файле Unit1.cpp после определения конструктора поместите следующие объявления и определения (курсив):
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
extern int r[50][50],a[50][50],n,m;
extern AnsiString s[50][50],s1[50];
//---------------------------------------------------------------------------
void form()
{ int i,j;
for(i=0;i for(j=0;j r[i][j]=random(10);
Form1->Memo1->SetFocus();
Form1->Memo1->Clear();
for(i=0;i s1[i]="";
for(j=0;j s[i][j]=" "+IntToStr(r[i][j]);
s1[i]+=s[i][j];}
Form1->Memo1->Lines->Add(s1[i]);}
}
//---------------------------------------------------------------------------
void transp()
{
int i,j;
for(i=0;i for(j=0;j a[j][i]=r[i][j];
i=n;n=m;m=i;
Form1->Memo1->SetFocus();
Form1->Memo1->Clear();
for(i=0;i s1[i]="";
for(j=0;j s[i][j]=" "+IntToStr(a[i][j]);
s1[i]+=s[i][j];}
Form1->Memo1->Lines->Add(s1[i]);}
}
В обработчик щелчка на разделе квадратная 5x5 Alt+A впишите:
n=m=5;
form();
В обработчик щелчка на подразделе единичная впишите:
n=m=5;
int i,j;
for(i=0;i for(j=0;j if(i==j)r[i][j]=1;
else r[i][j]=0;
Memo1->SetFocus();
Memo1->Clear();
for(i=0;i s1[i]="";
for(j=0;j s[i][j]=" "+IntToStr(r[i][j]);
s1[i]+=s[i][j];}
Memo1->Lines->Add(s1[i]);}
В обработчик щелчка на разделе транспонирование Ctrl+D впишите:
transp();
Командой Файл|Новый|Форма добавьте в проект первую вспомогательную форму Form2 и назовите ее 1-Я ВСПОМОГАТЕЛЬНАЯ ФОРМА. Сохраните файл модуля под именем по умолчанию Unit2. Со страницы Стандарт на форму перенесите компонент MainMenu1, двойным щелчком на нем перейдите в Конструктор Меню (окно Form2-> MainMenu1) и сконструируйте следующее меню:
Формирование прямоугольных матриц Операции с двумя матрицами
заполненная 4x5 сложение
заполненная общего вида вычитание
разреженная умножение
строка объединение по горизонтали
столбец объединение по вертикали
Поместите на 1-ю вспомогательную форму кнопку Button1, назовите ее ПЕРЕХОД НА ГЛАВНУЮ ФОРМУ, а в обработчик щелчка на ней впишите оператор:
Form1->Show();
Этот оператор покажет пользователю окно главной формы Form1. Чтобы компилятор понял операторы Form2->Show(); и Form1->Show();
надо в модуль главной формы Unit1.cpp вставить ссылку на заголовочный файл модуля 1-й вспомогательной формы Unit2.h #include “Unit2.h” и в модуль 1-й вспомогательной формы Unit2.cpp вставить ссылку на заголовочный файл модуля главной формы Unit1.h: #include “Unit1.h”.
Примечания. Для перехода из одного модуля в другой пользуйтесь быстрой кнопкой Вид модуля (в самом верхнем ряду кнопок первая слева) или комбинацией клавиш Ctrl+F12. Для перехода с одной формы на другую пользуйтесь быстрой кнопкой Вид формы (в самом верхнем ряду кнопок вторая слева) или комбинацией клавиш Shift+F12. Для перехода с формы на модуль пользуйтесь быстрой кнопкой Переключатель Форма|Модуль (в самом верхнем ряду кнопок третья слева) или клавишей F12.
В файле Unit2.cpp после определения конструктора поместите следующие объявления (курсив):
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
int r[50][50],a[50][50],n,m;
AnsiString s[50][50],s1[50];
void form();
void transp();
В обработчик щелчка на разделе заполненная 4x5 впишите:
n=4; m=5;
form();
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что во время выполнения пользователю доступны обе формы.
В окне Form2->MainMenu1 Конструктора Меню установите для головного раздела Операции с двумя матрицами и для его разделов GroupIndex=3, RadioItem=true, AutoCheck=true. После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что данные разделы стали работать как радиокнопки.
Для головного раздела Формирование прямоугольных матриц и для его разделов установите GroupIndex=2, для головного раздела Формирование квадратных матриц и для его разделов и подразделов установите GroupIndex=2, а для головного раздела Операции с матрицей и для его разделов установите GroupIndex=4.
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что объединения меню не происходит при переходе на 1-ю вспомогательную форму (тогда она активизируется).
Теперь для компонента MainMenu1 1-й вспомогательной формы установите свойство AutoMerge в true. После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что при переходе на 1-ю вспомогательную форму появляется объединенное меню на главной форме с заменами разделов меню главной формы разделами меню 1-й вспомогательной формы, имеющих GroupIndex=2.
И, наконец, для головного раздела Формирование прямоугольных матриц и для его разделов установите GroupIndex=1. После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что при переходе на 1-ю вспомогательную форму на главной форме появляется объединенное меню со всеми разделами объединяемых меню.
В заключение для компонента MainMenu1 1-й вспомогательной формы установите свойство AutoMerge в false, введите команду Сохранить все и запуском на выполнение убедитесь в работоспособности приложения.

Перейдем к рассмотрению контекстного всплывающего меню. Это меню привязано к конкретным компонентам. Оно всплывает, если во время, когда данный компонент в фокусе, пользователь щелкнет правой кнопкой мыши. Обычно в контекстное меню включают те команды главного меню, которые в первую очередь могут потребоваться при работе с данным компонентом.
Контекстному меню соответствует компонент PopupMenu. Поскольку в приложении могут быть несколько контекстных меню, то и компонентов PopupMenu может быть несколько. Оконные компоненты: формы, панели, окна редактирования, а также метки и др. имеют свойство PopupMenu, которое по умолчанию пусто, но куда можно поместить имя того компонента PopupMenu, с которым будет связан данный компонент.
Формирование контекстного всплывающего меню производится с помощью Конструктора Меню, вызываемого двойным щелчком на PopupMenu, что совпадает с действиями для главного меню. Однако конструирование контекстного меню можно упростить по сравнению с конструированием главного меню. Поскольку разделы контекстного меню обычно повторяют некоторые разделы уже сформированного главного меню, то можно обойтись копированием соответствующих разделов.

Задание. Сформировать контекстное меню из разделов квадратная 5x5 Alt+A и транспонирование Ctrl+D.
На главную форму сохраненного выше проекта со страницы Стандарт перенесите компонент PopupMenu1 и двойным щелчком на нем перейдите в Конструктор Меню (окно Form1-> PopupMenu1). Затем щелкните правой кнопкой мыши и во всплывшем меню выберите команду Выбор менюВ окне Выбор меню выберите MainMenu1 и нажмите ОК.
В появившемся главном меню выделите нужный раздел или разделы (при нажатой клавише Shift выделяют разделы в заданном диапазоне, при нажатой клавише Ctrl выделяют совокупность разделов, не являющихся соседними). Затем скопируйте выделенный раздел или разделы в буфер обмена, нажав клавиши Ctrl+C. После этого опять щелкните правой кнопкой мыши, выберите команду Выбор меню и вернитесь в контекстное меню. Укажите курсором место, в которое нужно вставить скопированный раздел или разделы, и нажмите клавиши чтения из буфера обмена – Ctrl+V. Разделы меню вместе со всеми их свойствами (но не событиями!) будут скопированы в создаваемое контекстное меню. Если же разделы, требуемые для переноса из главного меню в контекстное, находятся под разными заголовками (как в задании), то описанные выше действия необходимо выполнять для каждого раздела.
В свойство PopupMenu компонентов Form1 и Memo1 внесите PopupMenu1.
В обработчик щелчка на разделе квадратная 5x5 Alt+A впишите: n=m=5;
form();
В обработчик щелчка на разделе транспонирование Ctrl+D впишите:
transp();
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в том, что контекстное меню появляется при щелчке правой кнопкой мыши как на главной форме, так и на компоненте Memo1.

Компонент HotKey, расположенный на странице Win32, является вспомогательным, обеспечивающим возможность задания самим пользователем «горячих» клавиш, определяющих быстрый доступ к разделам меню. К тому же этот компонент позволяет задать такие сочетания «горячих» клавиш, которые не предусмотрены в выпадающем списке свойства разделов меню ShortCut.
Основное свойство компонента – HotKey, равное по умолчанию комбинации клавиш Alt+A. Это свойство можно прочесть и присвоить свойству ShortCut какого-то раздела меню. Например, оператор
MOpen->ShortCut = HotKey1->HotKey;
присваивает разделу меню с именем MOpen комбинацию клавиш, заданную в компоненте HotKey1.
В заключение рассмотрим пример использования компонента HotKey и настройки «горячих» клавиш для меню на 1-й вспомогательной форме в процессе выполнения приложения.

Командой Файл|Новый|Форма добавьте в проект вторую вспомогательную форму Form3 и назовите ее 2-Я ВСПОМОГАТЕЛЬНАЯ ФОРМА – ГОРЯЧИЕ КЛАВИШИ. Сохраните файл модуля под именем по умолчанию – Unit3.
На 1-й вспомогательной форме добавьте в меню раздел настройка в головной раздел Формирование прямоугольных матриц. В обработчик команды настройка вставьте оператор
Form3->ShowModal();
Этот оператор покажет пользователю окно 2-й вспомогательной формы как модальное – т.е. пользователь не сможет вернуться в другую форму, пока не закроет 2-ю вспомогательную. Чтобы компилятор понял этот оператор, надо в модуль 1-й вспомогательной формы Unit2.cpp вставить ссылку на заголовочный файл модуля 2-й вспомогательной формы Unit3.h
#include “Unit3.h”.
Поскольку из модуля Unit3.cpp надо будет видеть меню модуля Unit2.cpp, то аналогичным образом введите и обратную связь – свяжите модуль Unit3 с Unit2: #include “Unit2.h”.
На 2-ю вспомогательную форму перенесите со страницы Стандарт два списка ListBox, разместив их по горизонтали в верхней части формы, кнопку Button1, поместив ее внизу справа, а со страницы Win32 – компонент HotKey1, поместив его внизу слева. В компоненте HotKey1 сотрите с помощью Инспектора Объектов значение по умолчанию (Alt+A) свойства HotKey. Кнопку назовите OK. Затем со страницы Стандарт перенесите три метки Label. Первую – поместите над первым списком ListBox1 и назовите МЕНЮ, вторую – поместите над вторым списком ListBox2 и назовите РАЗДЕЛ, третью – поместите над HotKey1 и назовите ГОРЯЧИЕ КЛАВИШИ.
В списке ListBox1 будут отображаться головные разделы меню, в ListBox2 – разделы меню, соответствующие выбранному головному разделу. В окне HotKey1 в место предварительно помещенного туда курсора отобразится сочетание нажатых «горячих» клавиш, соответствующее выбранному разделу. При нажатии кнопки OK будет зафиксирован в меню сделанный пользователем выбор «горячих» клавиш и будет закрыта 2-я вспомогательная форма.
Теперь впишите обработчики событий:
void __fastcall TForm3::FormShow(TObject *Sender)
{
/*Загрузка ListBox1 головными разделами меню
при событии OnShow формы Form3 */

ListBox1->Clear();
for(int i=0; iMainMenu1->Items->Count; i++)
ListBox1->Items->Add(Form2->MainMenu1->Items->Items[i]->Caption);
ListBox1->ItemIndex=0;

//Обращение к процедуре загрузки ListBox2
ListBox1Click(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm3::ListBox1Click(TObject *Sender)
{
/* Загрузка ListBox2 разделами меню
MainMenu1->Items->Items[ListBox1->ItemIndex],
выделенного в ListBox1 пользователем и
при событии OnShow формы Form3 головного раздела меню */

ListBox2->Clear();
for(int i=0; iMainMenu1->Items->Items[
ListBox1->ItemIndex]->Count; i++)
ListBox2->Items->Add(Form2->MainMenu1->Items->Items[
ListBox1->ItemIndex]->Items[i]->Caption);
ListBox2->ItemIndex=0;

// Обращение к процедуре ListBox2Click
ListBox2Click(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm3::ListBox2Click(TObject *Sender)
{
/* Занесение «горячих» клавиш выделенного в ListBox2 раздела
в компонент HotKey1*/

HotKey1->HotKey= Form2->MainMenu1->Items->Items[
ListBox1->ItemIndex]->Items[
ListBox2->ItemIndex]->ShortCut;
}
//---------------------------------------------------------------------------
void __fastcall TForm3::Button1Click(TObject *Sender)
{
/* Изменение «горячих» клавиш выбранного раздела меню
и закрытие 2-й вспомогательной формы */

Form2->MainMenu1->Items->Items[
ListBox1->ItemIndex]->Items[ListBox2->ItemIndex]->ShortCut
=HotKey1->HotKey;
Close();
}
//---------------------------------------------------------------------------

При событии OnShow формы Form2 происходит загрузка списка ListBox1 головными разделами меню. Цикл загрузки перебирает индексы от нуля до Form2->MainMenu1->Items->Count-1. Свойство Count равно числу головных разделов в меню.
При щелчке пользователя на списке ListBox1 происходит загрузка списка ListBox2. При этом к соответствующим разделам меню осуществляется доступ с помощью выражения Form2->MainMenu1->Items->Items[ ListBox1->ItemIndex]->Items[i]. В этом выражении Form2->MainMenu1->Items->Items[ListBox1->ItemIndex] – головной раздел меню, выбранный пользователем в ListBox1. Каждый такой раздел можно рассматривать как элемент массива меню и в то же время он сам является массивом разделов второго уровня. Поэтому его свойство Items[i] указывает на подраздел с индексом i. В конце данной процедуры вызывается процедура ListBox2Click, загружающая данные в HotKey1.
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в правильной работе приложения. При щелчке на разделе настройка откроется 2-я вспомогательная форма. При щелчке пользователя на списке ListBox2 происходит загрузка компонента HotKey1 символами «горячих» клавиш выбранного пользователем раздела. Если раздел не имеет «горячих» клавиш, то в окне HotKey1 отображается текст “Нет”. Далее пользователь может войти в окно HotKey1 и нажать сочетание «горячих» клавиш, которое он хочет назначить выбранному им разделу меню. Обработка щелчка на кнопке OK фиксирует это сочетание в разделе меню и закрывает 2-ю вспомогательную форму.




Контрольные вопросы

Какие компоненты представляют меню? Как форма связывается с меню?
Как в процессе выполнения приложения изменять состав главного меню?
Расскажите об основном свойстве главного меню. Как его заполнить?
Как вводить в разделы меню изображения?
Как объединять главные меню вспомогательных форм с меню главной формы?
Как назначается способ объединения главных меню?
Какие компоненты и как связываются с контекстным всплывающим меню?
Как формировать контекстное всплывающее меню?
Как назначаются «горячие» клавиши? Приведите пример.




Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 209–217.
Шамис В.А. Borland C++ Builder 6. Для профессионалов / В.А. Шамис.
· СПб.: Питер, 2003.
· 798 с. – С. 290
·293, 338–339.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.


ЗАНЯТИЕ 7

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

Компоненты ProgressBar и Cgauge

Компоненты ProgressBar со страницы Win32 и Cgauge со страницы Примеры предназначены для отображения хода процессов, занимающих заметное время, например, копирования больших файлов, настройку приложения, установку приложения на компьютере и т.п. Основные свойства этих компонентов различаются лишь именами:



Свойство
Progressbar
Свойство
CGauge
Описание

Max
MaxValue
Максимальное значение позиции (Position, Progress), которое соответствует завершению отображаемого процесса. По умолчанию задается в процентах
· 100

Min
MinValue
Начальное значение позиции (Position, Progress), которое соответствует началу отображаемого процесса

Position
Progress
Позиция, которую можно задавать по мере протекания процесса, начиная со значения Min или Min-Value в начале процесса, и кончая значением Max или MaxValue в конце. Если минимальное и максимальное значения выражены в процентах, то позиция
· это процент завершенной части процесса

Smooth

·
Непрерывное (при значении true) или дискретное отображение процесса

Step

·
Шаг приращения позиции, используемый в методе Steplt. Значение по умолчанию
· 10

Orientation

·
Ориентация шкалы компонента: pbHorizontal
· горизонтальная, pbVertical
· вертикальная. Если задана pbVertical, то компонент надо вытянуть по вертикали


·
ForeColor
Цвет заполнения


·
ShowText
Текстовое отображение процента выполнения на фоне диаграммы


·
Kind
Тип диаграммы: gkHorizontalBar
· горизонтальная полоса, gkVerticalBar
· вертикальная полоса, gkPie
· круговая диаграмма, gkNeedle
· секторная диаграмма, gkText
· отображение текстом


Отображение хода процесса можно осуществлять, задавая значение позиции
· Position в ProgressBar или Progress в CGauge. Например, если полная длительность процесса характеризуется значением целой переменной Count, а выполненная часть
· целой переменной Current, то задавать позицию диаграммы в случае, если используются значения минимальной и максимальной позиции по умолчанию (т.е. 0 и 100), можно операторами
ProgressBar1->Position=100*Current/Count;
или
CGauge1->Progress=100*Current/Count;
соответственно для ProgressBar и CGauge.
Можно поступать иначе: задать сначала значение максимальной величины равным Count, а затем в ходе процесса задавать позицию равной Current. Например:
CGauge1->MaxValue=Count;
..
CGauge1->Progress=Current;
Компонент ProgressBar имеет два метода, которыми тоже можно воспользоваться для отображения процесса: StepBy(Delta: Integer)
· увеличение позиции на заданную величину Delta, и StepIt
· увеличение позиции на один шаг, величина которого задается свойством Step.

Задание 1. Ознакомиться с примерами применения компонентов ProgressBar и CGauge.
Создайте для проекта приложения каталог (папку Windows), запустите C++Builder 6, создайте новый проект и командой Сохранить все сохраните файл модуля и файл проекта под разными именами.
В свойство Caption формы впишите ProgressBar и CGauge. В левой части формы поместите панель Panel1, в свойство Caption которой впишите ProgressBar жирным шрифтом, красного цвета, размером 18.
На панель поместите два компонента ProgressBar. В одном компоненте свойство Smooth задайте равным true, в другом
· равным false. Свойство Orientation в одном компоненте задайте равным pbHorizontal, во втором
· равным pbVertical. Второй компонент следует вытянуть по вертикали.
В правой части формы сверху разместите метку Label1, в свойство Caption которой впишите CGauge жирным шрифтом, красного цвета, размером 18. На одной вертикали с меткой поместите четыре компонента CGauge, а пятый
· слева от четвертого. Свойство Kind задайте соответственно равным gkText, gkHorizontalBar, gkNeedle, gkPie, gkVerticalBar. Свойство ForeColor задайте во всех компонентах по-разному, свойство ShowText
· равным true, шрифт Font
· жирным, размером 18.
Под панелью разместите кнопку Button1, свойство Caption которой замените на слово ЗАПУСК жирным шрифтом, размером 18. В обработчик щелчка на кнопке впишите (курсив)
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int count=1000;
ProgressBar2->Max=count;
ProgressBar2->Step=1;
for(int i=1;i<=count;i++)
{
ProgressBar1->Position=100*i/count;
ProgressBar2->StepIt();
CGauge1->Progress=100*i/count;
CGauge2->Progress=100*i/count;
CGauge3->Progress=100*i/count;
CGauge4->Progress=100*i/count;
CGauge5->Progress=100*i/count;
for(int j=0;j<100000;j++);
}
}
Сохраните проект и выполните приложение. Нажимая на кнопку ЗАПУСК, убедитесь в работоспособности приложения.

Кнопки, индикаторы, управляющие элементы

В нижеприведенной таблице дана краткая характеристика основных управляющих элементов.

Компонент
Страница
Описание

Button
(командная кнопка)
Стандарт
Используется для создания кнопок, которыми пользователь выполняет команды в приложении

BitBtn
(кнопка с графикой)
Дополни-тельно
Используется для создания кнопок, на которых располагается битовая графика (например, кнопка OK с галочкой)

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

RadioGroup
(группа радиокнопок)
Стандарт
Является комбинацией группового окна GroupBox с набором радиокнопок RadioButton; служит специально для создания групп радиокнопок. Можно размещать в компоненте несколько радиокнопок, но никакие другие органы управления не разрешены

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

GroupBox
(групповое окно)
Стандарт
Является контейнером, объединяющим группу связанных органов управления, таких, как радиокнопки RadioButton, контрольные индикаторы CheckBox и т.д.

UpDown
(кнопка-счетчик)
Win32
Кнопка-счетчик, в сочетании с компонентами Edit и другими позволяющая вводить цифровую информацию

CheckBox
(контрольный индикатор с флажком)
Стандарт
Позволяет пользователю включать и выключать опции программы

CheckListBox
(список с индикаторами)
Дополни-тельно
Компонент является комбинацией свойств списка ListBox и индикаторов CheckBox в одном компоненте

TrackBar
(ползунок)
Win32
Используется как элемент управления в виде ползунка

ScrollBar
(линейка прокрутки)
Стандарт
Представляет собой стандартную линейку прокрутки Windows и служит для управления положением видимой части форм или компонентов

HeaderControl, Header (заголовки)
Win32
Используются для управления расположенных под ними панелей

Timer
(таймер)
Система
Используется для запуска процедур, функций и событий в указанные интервалы времени

DateTimePicker
(календарь)
Win32
Компонент ввода и отображения дат и времени



В качестве управляющих кнопок используются обычно Button или, если желательно иметь на кнопке пиктограмму
· BitBtn.
Кнопки SpeedButton применяются в качестве быстрых кнопок и для построения инструментальных панелей, в которых требуется фиксация нажатого состояния.
Компонент RadioGroup применяется для формирования группы регулярно размещенных радиокнопок, из которых в любой момент времени может быть включена только одна.
Если регулярное размещение радиокнопок нежелательно, то используются компоненты RadioButton, размещенные желательным образом в панели GroupBox.
Для введения в приложение различных опций, которые пользователь может включать и выключать, используются индикаторы CheckBox.
Большинство управляющих элементов имеет свойство Action. Это ссылка на некоторое действие, введенное тем или иным диспетчером действий. Ссылка на это действие заменяет установку для данного управляющего элемента множества свойств и написание для него обработчика события. Все эти свойства и обработчик наследуются элементом от того действия, ссылка на которое находится в свойстве Action.

Управляющие кнопки Button и BitBtn

Сначала рассмотрим общие для кнопок свойства.
Основным из них является свойство Caption (надпись). В надписях кнопок можно предусматривать использование клавиш ускоренного доступа, выделяя для этого один из символов надписи. Перед этим символом ставится &’, который не появляется в надписи, а следующий символ оказывается подчеркнутым. Тогда вместо щелчка на кнопке можно нажать клавишу Alt совместно с клавишей выделенного символа.
Общее для кнопок событие
· OnClick, возникающее при щелчке на ней. Именно в обработчике этого события записываются операторы, которые должны выполняться при щелчке пользователя на кнопке. Обработчик пишется тогда, когда данная кнопка не связана свойством Action с каким-то действием.
При установленном в true свойстве Cancel нажатие клавиши Esc будет эквивалентно щелчку на данной кнопке. Это свойство целесообразно задавать равным true для кнопок Отменить в различных диалоговых окнах, чтобы можно было выйти из диалога, нажав на эту кнопку или нажав на клавишу Esc.
При установленном в true свойстве Default нажатие клавиши Enter будет эквивалентно нажатию на данную кнопку, даже если в этот момент находится в фокусе какой-то оконный компонент. Если же в фокусе находится другая кнопка, то она и сработает.
В обычных приложениях свойство ModalResult, которое используется в модальных формах, устанавливается в mrNone.
Выполнение общего для кнопок метода Click эквивалентно щелчку на кнопке, т.е. вызывает событие OnClick. Этим можно воспользоваться, чтобы продублировать какими-то другими действиями щелчок на кнопке.
Пусть, например, нужно, чтобы при нажатии клавиши с символом C’ или c’ в любой момент работы с приложением выполнялись операции, предусмотренные в обработчике события OnClick кнопки Button1. Поскольку неизвестно, какой компонент будет находиться в фокусе в момент этого события, надо перехватить его на уровне формы. Такой перехват осуществляется, если установить свойство формы KeyPreview в true. Тогда в обработчике события формы OnKeyPress можно написать оператор
if((Key==’C’)||(Key==’c’))Button1->Click();
Если пользователь ввел символ C’ или c’, то будет выполнен обработчик щелчка кнопки Button1.

Задание 2. Изучить управляющие кнопки.
Начните новый проект. Перенесите на форму две панели Panel, зачистите у них свойство Caption. На каждую панель поместите по метке Label, надписи в которых замените на Button и BitBtn соответственно. На первую панель под меткой поместите кнопку Button с надписью Выполнить, а на вторую панель под меткой
· две кнопки BitBtn с надписью Открыть.
Перейдем к рассмотрению особенностей кнопки с пиктограммой BitBtn. Изображение на этой кнопке задается свойством Glyph. Выделите кнопку BitBtn. При нажатии кнопки с многоточием в строке свойства Glyph в Инспекторе Объектов вызывается окно Редактор картинки. Нажав в нем кнопку Загрузить, переходят в окно открытия файла рисунка Загрузить картинку и выбирают файл битовой матрицы .bmp, содержащей желаемое изображение. Большое количество изображений для кнопок расположено в папке Program Files\Common Files\Borland Shared\Images\Buttons. Выбрав файл bookopen, нажмите кнопку Открыть, а в окне Редактор картинки нажмите кнопку OK. Выбранное изображение появится на кнопке BitBtn левее надписи. Проделайте то же для другой кнопки BitBtn.

Файл изображения для кнопки может содержать до четырех изображений пиктограмм размера 16Ч16. Самое левое соответствует отжатой кнопке. Второе слева соответствует недоступной кнопке, когда ее свойство Enabled равно true. Третье слева изображение используется при нажатии пользователя на кнопку при ее включении. Четвертое слева изображение используется в кнопках с фиксацией SpeedButton, для изображения кнопки в нажатом состоянии. Большинство изображений для кнопок использует две пиктограммы. Число пиктограмм можно узнать из свойства кнопки NumGlyphs, которое после загрузки изображения покажет число пиктограмм в нем.
Расположение изображения и надписи на кнопке определяются свойствами Margin, Layout и Spacing. Если свойство Margin равно -1 (значение по умолчанию), то изображение и надпись размещаются в центре кнопки. При этом положение изображения по отношению к надписи определяется свойством Layout, которое может принимать значения: blGlyphLeft (слева, по умолчанию, для первой кнопки), blGlyphRight (справа), blGlyphTop (вверху), blGlyphBottom (внизу, задайте для второй кнопки). Если же Margin > 0, то в зависимости от значения Layout изображение и надпись смещаются к той или иной кромке кнопки, отступая от нее на число пикселов, заданное значением Margin. Убедитесь в этом.
Свойство Spacing задает число пикселов, разделяющих изображение и надпись на поверхности кнопки. По умолчанию Spacing = 4. Если задать Spacing = 0, изображение и надпись будут размещены вплотную друг к другу. Если задать Spacing = -1, то текст появится посередине между изображением и краем кнопки.
Еще одно свойство BitBtn
· свойство Kind определяет тип кнопки. По умолчанию значение этого свойства равно bkCustom
· заказная. Но можно установить и множество других предопределенных типов: bkOk, bkCancel, bkHelp, bkYes, bkNo, bkClose, bkAbort, bkRetry, bkIgnore, bkAll. В этих типах уже сделаны соответствующие надписи, введены пиктограммы, заданы еще некоторые свойства. Однако рекомендуется использовать заказной тип.


Кнопка с фиксацией SpeedButton

Кнопки SpeedButton имеют возможность отображения пиктограмм и могут использоваться как обычные управляющие кнопки или как кнопки с фиксацией нажатого состояния. Обычно они используются в качестве быстрых кнопок, дублирующих различные команды меню, и в инструментальных панелях, в которых требуется фиксация нажатого состояния.
Свойство Caption (надпись) обычно оставляется пустым, так как вместо надписи используется пиктограмма. Изображение на кнопке задается свойством Glyph точно так же, как для кнопок BitBtn.
Особенностью кнопок SpeedButton являются свойства GroupIndex (индекс группы), AllowAllUp (разрешение отжатого состояния всех кнопок группы) и Down (исходное состояние
· нажатое). Если GroupIndex = 0, то кнопка ведет себя так же, как Button и BitBtn. При нажатии пользователем кнопки она погружается, а при отпускании возвращается в нормальное состояние. В этом случае свойства AllowAllUp и Down не влияют на поведение кнопки.
Если GroupIndex > 0 и AllowAllUp = true, то кнопка при щелчке пользователя на ней погружается и остается в нажатом состоянии. При повторном щелчке на кнопке она освобождается и переходит в нормальное состояние (именно для того, чтобы освобождение кнопки состоялось, необходимо задать AllowAllUp = true). Если свойство Down во время проектирования установлено равным true, то исходное состояние кнопки
· нажатое.
Если есть несколько кнопок, имеющих одинаковое ненулевое значение GroupIndex, то они образуют группу взаимосвязанных кнопок, из которых нажатой может быть только одна. Если одна кнопка находится в нажатом состоянии и пользователь щелкает на другой, то первая кнопка освобождается, а вторая фиксируется в нажатом состоянии. Поведение нажатой кнопки при щелчке на ней зависит от значения свойства AllowAllUp. Если оно равно true, то кнопка освободится, поскольку в этом случае возможно состояние, когда все кнопки группы отжаты. Если же AllowAllUp = false, то щелчок на нажатой кнопке не приведет к изменению вида кнопки. Впрочем, и в этом случае, как и при любом щелчке на кнопке, возникает событие OnClick, которое может быть обработано.
Состояние кнопки во время выполнения можно определить по значению свойства Down: если значение свойства равно true, то кнопка нажата. Во время события OnClick значение Down уже равно тому состоянию, которое примет кнопка в результате щелчка на ней.

Продолжим выполнение задания 2.
Перенесите на форму третью панель Panel, зачистите у нее свойство Caption. На панели разместите метку Label с надписью SpeedButton и три кнопки SpeedButton. На первую кнопку поместите изображение из файла brush, на вторую и третью
· из файла pencil. В свойство Caption третьей кнопки впишите Карандаш.
Для первой кнопки установите GroupIndex = 0, для второй
· GroupIndex = 1, AllowAllUp = true, для третьей
· GroupIndex = 2, AllowAllUp = true, Down = true.
Сохраните все и запустите приложение на выполнение. Убедитесь в правильном функционировании кнопок.
Установите для всех кнопок GroupIndex = 1, AllowAllUp = true, Down = false. Сохраните все и запустите приложение на выполнение. Убедитесь в правильном функционировании кнопок SpeedButton.
Установите для одной из кнопок AllowAllUp = false и после сохранения снова выполните приложение. Обратите внимание на изменение в функционировании кнопок.




Группы радиокнопок
· компоненты RadioGroup, RadioButton и GroupBox

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

Задание 3. Изучить группы радиокнопок и индикаторы.
Начните новый проект. На форму перенесите два компонента RadioGroup
· панели, которые могут содержать регулярно расположенные столбцами и строками радиокнопки. В свойство Caption компонентов впишите слово ГРУППЫ, которое появится в левом верхнем углу панелей.
Надписи кнопок и их количество определяются свойством Items, имеющим тип TStrings. Щелкнув на кнопке с многоточием около этого свойства в окне Инспектора Объектов, попадете в окно Редактор строки списка. В нем заносят надписи, которые должны быть около кнопок, по одной в строке. По окончании ввода нажимают клавишу OK. В первый и второй компоненты занесите соответственно строки:

УТ Управление
ИТ Информационные системы
УТ-11 УТ-11
ИТ-11 ИТ-11
УТ-12 УТ-12
ИТ-12 ИТ-12
УТ-13 УТ-13

Кнопки, появившиеся в панели после задания значений Items, можно разместить в несколько столбцов (не более 17), задав свойство Columns. По умолчанию Columns = 1, т.е. кнопки размещаются друг под другом. Для обеих панелей задайте Columns = 4.
Работа с компонентом RadioGroup показывает, что он удобен, если надписи кнопок имеют примерно одинаковую длину и если число кнопок в каждом столбце одинаково. В компоненте RadioGroup при размещении кнопок используется надпись максимальной длины, что приводит к нерациональному использованию пространства формы (во втором компоненте RadioGroup).
Возможность нерегулярного расположения кнопок дают компоненты RadioButton, сгруппированные панелью GroupBox. Перенесите на форму панель GroupBox. В свойство Caption компонента впишите слово ГРУППЫ, которое появится в левом верхнем углу панели. Назначение панели (сама по себе она пустая)
· служить контейнером для других управляющих элементов, в данном случае для радиокнопок RadioButton.

Рассмотрим свойства радиокнопки RadioButton. Свойство Caption содержит надпись, появляющуюся около кнопки. Значение свойства Alignment определяет, с какой стороны от кнопки появится надпись: taLeftJustify
· слева, taRightJustify
· справа (это значение принято по умолчанию).
Свойство Checked определяет, выбрана данная кнопка пользователем, или нет. Поскольку в начале выполнения приложения обычно необходимо, чтобы одна из кнопок группы была выбрана по умолчанию, ее свойство Checked надо установить в true в процессе проектирования. Отметим, что в true можно установить значение Checked только у одной кнопки из группы.

Разместите радиокнопки в панели GroupBox в два ряда так, чтобы заменить второй компонент RadioGroup.
Сохраните все и выполните приложение. Убедитесь в правильной работе кнопок.

Радиокнопки RadioButton могут размещаться не только в панели GroupBox, но и в любой панели другого типа, а также непосредственно на форме. Группа взаимосвязанных кнопок в этих случаях определяется тем оконным компонентом, который содержит кнопки. В частности, для радиокнопок, размещенных непосредственно на форме, контейнером является сама форма. Таким образом, все кнопки, размещенные непосредственно на форме, работают как единая группа, т.е. только в одной из этих кнопок можно установить значение Checked в true.


Индикаторы CheckBox и CheckListBox

Индикаторы с флажком CheckBox используются в основном для того, чтобы пользователь мог включать и выключать какие-то опции, или для индикации состояния.
При каждом щелчке пользователя на индикаторе его состояние изменяется, проходя последовательно через три значения (при значении свойства AllowGrayed равном true): промежуточное (серое окно индикатора и серая галочка), выделение (появление черной галочки) и не выделенное (пустое окно индикатора).
Этим трем состояниям соответствуют три значения свойства State: cbGrayed, cbChecked, cbUnchecked. При AllowGrayed = false (значение по умолчанию), допускается только два состояния: выделенное и невыделенное. И State, и AllowGrayed можно устанавливать во время проектирования или программно во время выполнения.
Проверять состояние индикатора можно не только по значению State, но и по значению свойства Checked. Если Checked равно true, то индикатор выбран, т.е. State = cbChecked. Если Checked равно false, то State равно cbUnchecked или cbGrayed. Установка Checked в true во время проектирования или выполнения автоматически переключает State в cbChecked.
В индикаторе CheckBox надпись задается свойством Caption, а ее размещение по отношению к индикатору
· свойством Alignment.
Список CheckListBox
· это еще один компонент, имеющий индикаторы (см. раздел «Компоненты ввода и отображения текстовой информации»). Каждая строка списка имеет индикатор, состояние которого пользователь может изменять.
Состояния индикаторов определяют два свойства: State и Checked. Оба эти свойства можно рассматривать как индексированные массивы, каждый элемент которого соответствует индексу строки. Общее количество элементов определяется свойством Count (только для чтения). Поскольку индексы начинаются с нуля, то индекс последнего элемента равен Count – 1.
Свойства State и Checked можно устанавливать программно или читать, определяя установки пользователя. Например, операторы
CheckListBox1->Checked[1] = true;
CheckListBox1->State[2] = cbGrayed;
Устанавливают индикатор второй строки списка CheckListBox1 в состояние выбранного, а индикатор третьей строки
· в промежуточное состояние.
Оператор
for(int i=0; i < CheckListBox1->Items->Count; i++)
if(CheckListBox1->Checked[i]
проверяет состояние всех индикаторов списка, и для выбранных пользователем строк осуществляет какие-то действия (многоточие).
В компоненте CheckListBox имеется событие OnClickCheck, возникающее при каждом изменении пользователем состояния индикатора. Его можно использовать для обработки результатов изменения.

Продолжим выполнение задания 3.
Перенесите на форму панель Panel, на которой по горизонтали разместите метку Label с надписью CheckBox и два индикатора CheckBox с надписями опция 1 и опция 2 соответственно. В первом индикаторе свойство AllowGrayed установите в false, а во втором
· в true. Выполните приложение. Убедитесь в том, что первый индикатор имеет два положения, а второй
· три.


Ползунки и полосы прокрутки
· компоненты TrackBar и ScrollBar

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

Задание 4. Изучить ползунки и полосы прокрутки.
Начните новый проект. В надпись формы внесите Ползунки, на форму перенесите четыре компонента TrackBar, три
· по вертикали, друг под другом, четвертый
· справа.

Основное свойство ползунка
· Position. Это свойство можно задавать во время проектирования или программно во время выполнения. При перемещении пользователем ползунка можно прочитать значение Position, характеризующее позицию, в которую пользователь переместил ползунок. Для возможности такого чтения служит событие OnChange. В обработчике этого события можно прочитать значение Position и использовать его для управления каким-то компонентом.
Свойство Position
· целое, значение которого может изменяться в пределах, задаваемых свойствами Min и Max. По умолчанию Min = 0, Max = 10, так что Position может принимать только 11 значений
· от 0 до 10. Если задать большее значение Max, соответственно увеличится количество возможных значений Position в диапазоне Min
· Max.
Для верхнего ползунка задайте Max = 5.
Свойство Orientation определяет ориентацию ползунка (по умолчанию
· trHorizontal
· горизонтальная). Для правого ползунка задайте trVertical
· вертикальная.
Свойства SelStart и SelEnd позволяют визуально выделить на шкале некоторый диапазон значений. Для среднего ползунка задайте соответственно 4 и 8.
Свойство TickMarks указывает размещение шкалы относительно компонента. Для верхнего и правого задайте tmBottomRight
· снизу или справа (в зависимости от ориентации ползунка), для нижнего
· tmTopLeft
· сверху или слева в зависимости от ориентации ползунка, для среднего
· tmBoth
· с обеих сторон.
Выполните приложение. Убедитесь, что ползунки можно перемещать как мышью, так и клавишами.

Свойство TickStyle определяет способ изображения шкалы. Оно может принимать значения: tsAuto
· автоматическая прорисовка шкалы, tsNone
· отсутствие шкалы, tsManual
· программное рисование шкалы с помощью метода SetTick(Value:Integer), который помещает метку шкалы в позицию, соответствующую величине Value. Метки, соответствующие началу и концу шкалы, автоматически размещаются в случае TickStyle = tsManual.
При TickStyle = tsAuto частота меток шкалы определяется свойством Frequency. Это свойство задает, сколько возможных значений Position лежит между метками. Например, если Frequency = 2, то метки будут соответствовать только каждому второму возможному значению позиции

В верхнем ползунке установите Max =10, при TickStyle = tsAuto установите Frequency = 2. Выполните приложение. Убедитесь в новом варианте работы верхнего ползунка.

Компонент ScrollBar (полоса или линейка прокрутки) по своим функциям похож на ползунок. Основные свойства ScrollBar
· Position, Min и Max те же, что у TrackBar. Свойство Kind, определяющее горизонтальное или вертикальное расположение полосы и принимающее соответственно значения sbHorizontal или sbVertical, аналогично свойству Orientation ползунка. Имеются два свойства, отсутствующие у TrackBar: SmallChange и LargeChange (по умолчанию они равны 1). Они определяют соответственно «малый» сдвиг при щелчке на кнопке в конце полосы или при нажатии клавиши со стрелкой, и «большой» сдвиг при перемещении на страницу щелчком рядом с бегунком или нажатием клавиш PageUp или PageDown.

Перенесите на форму две полосы прокрутки ScrollBar. Одну из них расположите горизонтально, другую
· вертикально. Увеличьте, насколько возможно, длины полос. Для горизонтально расположенной полосы установите SmallChange = 3, LargeChange = 5.
Выполните приложение. Убедитесь в правильном функционировании полос прокрутки.
Событие, соответствующее перемещению пользователем бегунка полосы прокрутки
· OnScroll. В процедуру обработчика этого события передается по ссылке параметр ScrollPos
· позиция бегунка, которую можно читать, но можно и изменять, и передается параметр ScrollCode, характеризующий вид перемещения бегунка. Этот параметр может иметь значения:

scLineUp,
scLineDown
«Малый» сдвиг: перемещение соответственно вверх или налево и вниз или вправо после нажатия кнопки полосы прокрутки или клавиши со стрелкой

scPageUp,
scPageDown
«Большой» сдвиг: перемещение на страницу щелчком рядом с бегунком или нажатием клавиш PageUp или PageDown

scPosition
Пользователь переместил и освободил бегунок

scTrack
Пользователь перемещает бегунок

scTop,
scBottom
Бегунок перемещен соответственно в крайнюю верхнюю или левую позицию и в крайнюю нижнюю или правую позицию

scEndScroll
Окончание перемещения


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


Заголовки
· компоненты HeaderControl и Header

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

Задание 5. Изучить компоненты заголовков.
Начните новый проект. Перенесите на форму со страницы Win32 компонент HeaderControl. По умолчанию свойство Align задано равным alTop, что обеспечивает размещение компонента вверху окна формы. Измените это свойство на alNone и разместите компонент в любом месте.
Основное свойство компонента HeaderControl
· Sections. Оно является списком объектов типа THeaderSection, каждый из которых описывает одну секцию заголовка.
Свойство Sections можно задать во время проектирования, нажав кнопку с многоточием рядом с этим свойством в Инспекторе Объектов или просто сделав двойной щелчок на компоненте HeaderControl и выбрав в появившемся меню пункт редактор секций. В обоих случаях откроется окно Правка. Левая быстрая кнопка позволяет добавить новую секцию в заголовок. Следующая быстрая кнопка позволяет удалить секцию. Кнопки со стрелкой позволяют изменять последовательность секций.
После того, как секция добавлена, в окне Инспектора Объектов появится множество свойств этого объекта.
В свойстве Text задайте текст заголовка, например секция 1.
Свойства MinWidth и MaxWidth определяют соответственно минимальную и максимальную ширину секции в пикселах. Только в этих пределах пользователь может изменять во время выполнения ширину секции курсором мыши. Значение ширины по умолчанию задается свойством Width.
Задайте 3-4 секции.
Выполните приложение. Убедитесь, что ширина секций во время выполнения изменяется курсором мыши, а при щелчке пользователя на секции она ведет себя как кнопка.

При изменении ширины секции во время выполнения генерируется событие OnSectionResize. В обработчик этого события надо вставить операторы, синхронно изменяющие ширину того, заголовком чего является секция: это может быть какая-то панель, таблица, изображение и т.п.
Свойство AllowClick, равное по умолчанию true, определяет поведение секции как кнопки при щелчке пользователя на ней. В этом случае при щелчке генерируется событие OnSectionClick, в обработчик которого и надо вставить операторы, выполняющие необходимые действия.
Свойство Style может иметь значение hsText
· в этом случае в заголовке отображается значение свойства Text, или hsOwnerDraw
· в этом случае отображается то, что рисуется непосредственно на канве операторами, записанными в обработчике события OnDrawSection.
Компонент Header обладает существенно меньшими возможностями, чем HeaderControl, поэтому рассматривать его не будем.

Таймер
· компонент Timer

Компонент Timer позволяет задавать в приложениях интервалы времени. Таймер
· невизуальный компонент, который может размещаться в любом месте формы. Он имеет два свойства, позволяющие им управлять: Interval
· интервал времени в миллисекундах и Enabled
· доступность. Свойство Interval задает период срабатывания таймера. Через заданный интервал времени после предыдущего срабатывания, или после программной установки свойства Interval, или после запуска приложения, если значение Interval установлено во время проектирования, таймер срабатывает, вызывая событие OnTimer. В обработчике этого события записываются необходимые операции.
Если задать Interval = 0 или Enabled = false, то таймер перестает работать. Чтобы запустить отсчет времени, надо или задать Enabled = true, если установлено положительное значение Interval, или задать положительное значение Interval, если Enabled = true.
Например, если требуется, чтобы через 5 секунд после запуска приложения закрылась форма
· заставка, отображающая логотип приложения, на ней надо разместить таймер, задать в нем интервал Interval = 5000, а в обработчик события OnTimer вставить оператор Close, закрывающий окно формы.
Если необходимо в некоторой процедуре запустить таймер, который отсчитал бы заданный интервал, например, 5 секунд, после чего выполнить некоторые операции и отключить таймер, это можно сделать следующим образом. При проектировании таймер делается доступным (Enabled = true), но свойство Interval задается равным 0. Таймер не будет работать, пока в момент, когда нужно запустить таймер, не выполнится оператор
Timer1->Interval = 5000;
Через 5 секунд после этого наступит событие OnTimer. В его обработчике надо задать оператор
Timer1->Interval = 0;
который отключит таймер, после чего можно выполнять требуемые операции.
Другой эквивалентный способ решения задачи
· использование свойства Enabled. Во время проектирования задается значение Interval = 5000 и значение Enabled = false. В момент, когда надо запустить таймер, выполняется оператор
Timer1->Enabled = true;
В обработчик события OnTimer, которое наступит через 5 секунд после запуска таймера, можно вставить оператор
Timer1->Enabled = false;
который отключит таймер.


Календарь
· компонент DateTimePicker

Это наиболее удобный компонент ввода и отображения дат и времени.
Компонент удобен за счет появления выпадающего календаря: обеспечивает безошибочный ввод дат и времени. Его свойство Kind определяет режим работы компонента: dkDate – ввод даты, dtkTime – ввод времени.
При вводе дат можно задать свойство DateMode равным dmComboBox – наличие выпадающего календаря, или равным dmUpDown – наличие кнопок увеличения и уменьшения, как в CSpinEdit. Но в данном случае можно независимо устанавливать число, месяц и год. Формат представления дат определяется свойством DateFormat, которое может принимать значения dfShort – краткий формат (например, «04.02.13»), или dfLong – полный формат (например, «4 Февраль 2013г.»).
Значение даты по умолчанию можно задать в Инспекторе Объектов через свойство Date. Это же свойство читается для определения заданной пользователем даты. Тип этого свойства – TDateTime, представляющий собой число с плавающей запятой, целая часть которого содержит число дней, отсчитанное от некоторого начала календаря, а дробная часть равна части 24-часового дня, т.е. характеризует время и не относится к дате. За начало календаря принята дата 12/30/1899 00 часов.
Для преобразования значения свойства Date в строку можно воспользоваться функцией DateToStr. Напимер, оператор

Memo1->Lines->Add(“Дата: “ + DateToStr(DateTimePicker1->Date));

добавит в окно Memo1 строку вида “Дата: 04.02.13”.
При вводе дат можно задать значения свойств MaxDate и MinDate, определяющих соответственно максимальную и минимальную дату, которую может задать пользователь.
В режиме ввода времени dtkTime введенное пользователем значение можно найти в свойстве Time, тип которого – также TdateTime. Для преобразования времени в строку используется функция TimeToStr.


Панели и компоненты внешнего оформления

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


Компонент
Страница
Описание

GroupBox
(групповое окно)
Стандарт
Является контейнером, объединяющим группу связанных органов управления, таких, как радиокнопки RadioButton, контрольные индикаторы CheckBox и т.д.

Panel
(панель)
Стандарт
Является контейнером для группирования органов управления и меньших контейнеров. Панель можно использовать также для построения полос состояния, инструментальных панелей, палитр инструментов

Bevel
(рамка)
Дополни-тельно
Используется для рисования прямоугольной рамки, изображенной как выступающая или утопленная

ScrollBox (окно с прокруткой)
Дополни-тельно
Используется для создания зон отображения с прокруткой

Splitter
(разделитель панелей)
Дополни-тельно
Используется для создания в приложении панелей с изменяемыми пользователем размерами

ControlBar (инструментальная панель)
Дополни-тельно
Используется для размещения компонентов инструментальной панели

TabControl
(страница с закладкой)
Win32
Позволяет добавлять закладки в стиле Windows, которые может выбирать пользователь

PageControl
(многостраничное окно)
Win32
Позволяет создавать страницы в стиле Windows, управляемые закладками или иными органами управления, для экономии места на рабочем столе

StatusBar
(полоса состояния)
Win32
Полоса состояния приложения, при необходимости
· на нескольких панелях

ToolBar
(инструментальная панель)
Win32
Инструментальная панель для быстрого доступа к часто используемым функциям приложения

CoolBar (инструментальная перестраиваемая панель)
Win32
Контейнер инструментальной панели, размеры которой могут изменяться пользователем

PageScroller
(прокрутка страниц)
Win32
Обеспечивает прокрутку больших окон, например, инструментальных панелей

ActionToolBar
(инструментальная панель)
Дополни-тельно
Инструментальная панель, связанная с ActionManager

TabSet
(блокнот с закладками)
Win3.1
Используется для создания блокнота с закладками

TabbedNoteBook
(многостраничная форма)
Win3.1
Используется для создания многостраничных форм с закладками

NoteBook
(пачка страниц)
Win3.1
Используется для создания пачки страниц, может применяться совместно с TabSet

Frame
(фрейм)
Standart
Используется как проектируемый в виде отдельного окна контейнер любых компонентов. Обладает возможностью наследования, может включаться в Депозитарий



Панели общего назначения
· компоненты Panel,
GroupBox, Bevel, ScrollBox, Splitter

Панели Panel используются наиболее широко. С их помощью компонуются различные элементы интерфейса (кнопки, окна редактирования, списки), функционально связанные друг с другом. Панели Panel могут также использоваться для организации инструментальных панелей, полос состояния и т.п.
Одним из назначений панелей является также группирование таких управляющих элементов, как RadioButton
· радиокнопки. Все радиокнопки, расположенные на панели, работают как согласованная группа: в любой момент может быть выбрана только одна из них. Аналогично согласованной группой работают и расположенные на панели быстрые кнопки SpeedButton, если они имеют одинаковое значение свойства GroupIndex.
Внешний вид панели Panel определяется совокупностью параметров BevelInner
· стиль внутренней части панели, BevelOuter
· стиль внешней части панели, BevelWidth
· ширина внешней части панели, BorderStyle
· стиль бордюра, BorderWidth
· ширина бордюра.
Разделитель Splitter предоставляет пользователю возможность перемещать границы, разделяющие различные панели, изменяя их относительные размеры. Разделитель имеет свойства Beveled и ResizeStyle, определяющие его вид, и свойство MinSize, ограничивающее минимальный размер панелей по обе стороны от разделителя.
Панель GroupBox имеет встроенную рамку с надписью, которая обычно используется для выделения на форме группы функционально объединенных компонентов.
Компонент Bevel формально не является панелью, он не может служить контейнером для компонентов. Например, с помощью Bevel нельзя сгруппировать радиокнопки.
Стиль отображения Bevel определяется свойством Style, которое может принимать значения bsLowered
· утопленный, и bsRaised
· приподнятый. А контур компонента определяется свойством Shape, которое может принимать значения: bsBox
· прямоугольник, bsFrame
· рамка, bsSpacer
· пунктирная рамка, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine
· соответственно верхняя, нижняя, левая и правая линии. В зависимости от значения Style линии могут быть утопленными или выступающими.
Панель с прокруткой ScrollBox предназначена для создания области, в которой могут размещаться компоненты, занимающие площадь большую, чем сам ScrollBox. Например, ScrollBox можно использовать для размещения длинных текстовых строк или больших инструментальных панелей, которые, исходя из соображений экономии площади окна, нецелесообразно отображать целиком. Если размеры ScrollBox меньше, чем размещенные компоненты, то появляются полосы прокрутки, которые позволяют пользователю перемещаться по всем размещенным в ScrollBox компонентам.
Разместить в пределах небольшой области ScrollBox большие компоненты или много компонентов, занимающих в сумме большую площадь, можно в процессе проектирования следующим образом. Размеры ScrollBox увеличивают временно так, чтобы в полосе прокрутки поместилось все, что нужно разместить, а затем сокращают размеры ScrollBox до требуемых.
Свойство BorderStyle определяет стиль рамки компонента ScrollBox. Свойство AutoScroll позволяет задать автоматическое появление необходимых полос прокрутки, если размер размещенных компонентов превышает размер области по горизонтали, вертикали или в обоих измерениях. Но можно и самим управлять появлением горизонтальной и вертикальной полос с помощью свойств HorzScrollBar и VertScrollBar соответственно. В этом случае надо самим задавать ряд свойств полосы прокрутки и, прежде всего, Range
· размер в пикселах прокручиваемой области. Значение перемещения при однократном нажатии пользователем кнопки прокрутки может рассчитываться компонентом автоматически исходя из размеров области и окна, если свойство Smooth установлено в true. В противном случае нужно задать величину единичного перемещения в свойстве Increment.


Многостраничные панели
· компоненты TabControl,
PageControl, TabSet, TabbedNotebook, Notebook

Многостраничные панели позволяют экономить пространство окна приложения, размещая на одном и том же месте страницы разного содержания.
Перенесите компонент PageControl на форму. Чтобы задавать и редактировать страницы этого компонента, щелкните на нем правой кнопкой мыши. Во всплывшем меню имеются команды: Новая страница
· создать новую страницу, Следующая страница
· переключиться на следующую страницу, Предыдущая страница
· переключиться на предыдущую страницу. Каждая создаваемая страница является объектом типа TTabSheet. Это панель, на которой можно размещать любые управляющие компоненты, окна редактирования и т.п.
Создайте 3-4 страницы, размещая на них компоненты RichEdit, Memo, Panel, GroupBox. Затем выделите одну из страниц и просмотрите ее свойства в Инспекторе Объектов. Страница имеет следующие основные свойства:

Name
Имя, по которому можно ссылаться на страницу

Caption
Надпись, которая появляется на ярлычке закладки

PageIndex
Индекс страницы, по которому можно ссылаться на страницу

ImageIndex
Индекс изображения, которое может появляться на ярлычке закладки


Общие свойства компонента PageControl таковы:
Style
Определяет стиль отображения компонента: tsTabs
· закладки, tsButtons
· кнопки, tsFlatButtons
· плоские кнопки

MultiLine
Определяет, будут ли закладки размещаться в несколько рядов, если все они не помещаются в один ряд

TabPosition
Определяет место расположения ярлычков закладок: tpBottom
· внизу, tpLeft
· слева, tpRight
· справа и tpTop
· вверху компонента (значение по умолчанию)

TabHeight
и
TabWidth
Высота и ширина ярлычков закладок в пикселах. Если значения этих параметров заданы равными 0, то размеры ярлычков определяются автоматически по размерам надписей на них

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

ScrollOpposite
Определяет способ перемещения закладок при размещении их в несколько рядов

ActivePage
Имя активной страницы

Pages
[int Index]
Доступ к странице по индексу (первая страница имеет индекс 0). Свойство только для чтения

PageCount
Количество страниц. Свойство только для чтения


В упомянутом выше всплывающем меню есть ряд команд, позволяющих оперировать страницами, создавать их, уничтожать, переключать.
Основные события компонента
· OnChanging и OnChange. Первое из них происходит непосредственно перед переключением на другую страницу после щелчка на новой закладке. При этом в обработчик события передается по ссылке параметр AllowChange
· разрешение переключения. Если в обработчике задать AllowChange = false, то переключение не произойдет. Событие OnChange происходит сразу после переключения.
Компонент TabControl внешне выглядит так же, как PageControl, и имеет много тех же свойств: Style, MultiLine, TabPosition, TabHeight, TabWidth, Images, ScrollOpposite, те же события OnChanging и OnChange.
Но принципиальное отличие его от PageControl заключается в том, что TabControl не имеет множества панелей (страниц). Компонент представляет собой одну страницу с управляющим элементом типа кнопки со многими положениями. И надо написать соответствующие обработчики событий OnChanging и OnChange, чтобы определить, что именно должно происходить на панели при переключениях закладок пользователем.
У компонента имеется еще одно свойство
· MultySelect, позволяющее множественный выбор закладок. Если это свойство установлено в true, то в обработчиках событий надо описать реакцию на такой выбор пользователя.
Число закладок и их надписи определяются свойством Tabs типа TStrings. В нем можно задать надписи закладок. Число закладок будет равно числу строчек надписей.
Текущее состояние переключателя определяется свойством TabIndex. Его можно установить в процессе проектирования, чтобы определить исходное состояние переключателя. А затем в обработчиках событий OnChanging и OnChange можно читать это свойство, чтобы определить, что именно выбрал пользователь.
Применять компонент TabControl рекомендуется в тех приложениях, в которых нужен многопозиционный переключатель.
Компоненты TabSet, TabbedNotebook и Notebook рассматривать не будем, так как они могут быть заменены компонентом PageControl и их не рекомендуется использовать в 32-разрядных приложениях.


Инструментальные панели
· компоненты ToolBar и PageScroller

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

Задание 6. Изучить инструментальную панель ToolBar.
Начните новый проект. Сохраните файл модуля и файл проекта. Поместите панель ToolBar на форму. По умолчанию она расположится вверху, поскольку ее свойство Align = alTop. Задав Align = alNone, можно придать панели любую форму и расположить ее в любом месте. Свойства, определяющие вид панели ToolBar: BorderWidth
· ширина бордюра, EdgeInner и EdgeOuter
· стиль изображения внутренней и внешней части панели (утопленный или выступающий), EdgeBorders
· определяет изображение отдельных сторон панели (левой, правой, верхней, нижней).
Занесите компоненты
· кнопки на панель ToolBar. Это можно сделать обычным способом
· переносом их из палитры компонентов. Однако воспользуйтесь более простым вариантом: щелкните на ToolBar правой кнопкой мыши и из всплывшего меню выберите команду Новая кнопка. На форме появится кнопка
· объект типа TTollButton. Вид и поведение этой особой кнопки определяется ее свойством Style, которое по умолчанию равно tbsButton
· кнопка. В отличие от кнопки SpeedButton, изображение на ней определяется свойством ImageIndex, в котором находится индекс изображения, хранящегося в компоненте ImageList. Указание на этот компонент может задаваться такими свойствами компонента ToolBar, как Images, DisAbledImages (указывает на список изображений кнопок в недоступном состоянии) и HotImages (указывает на список изображений кнопок в моменты, когда над ними перемещается курсор мыши).
Занесите четыре кнопки на панель ToolBar. Загрузите в компонент ImageList файлы изображений fldropen, filesave, print, tools из каталога Program Files\Common Files\Borland Shared\Images\Buttons для четырех кнопок на панели ToolBar. В свойстве Images панели ToolBar сошлитесь на компонент ImageList.
Сохраните все и выполните приложение. Убедитесь, что кнопки на панели ToolBar работают как кнопки Button.
Задайте стиль кнопок Style = tbsCheck. Выполните приложение. Убедитесь, что после щелчка пользователя на кнопке она остается в нажатом состоянии. Повторный щелчок на кнопке возвращает ее в отжатое состояние. Следовательно, поведение кнопок подобно кнопкам SpeedButton. Отметим, что по умолчанию свойство AllowAllUp = false. Установите во всех кнопках AllowAllUp = true. Выполнив приложение, убедитесь, что поведение кнопок не изменилось. Для одной из кнопок задайте Down = true и снова выполните приложение. Задайте свойство Grouped =true во всех кнопках и убедитесь, что сейчас кнопки работают как группа, в которой только одна кнопка может находиться в нажатом состоянии. В указанной выше кнопке установите Down = false и снова выполните приложение.
Щелкните на ToolBar правой кнопкой мыши и из всплывшего меню выберите команду Новая кнопка. В появившейся на панели новой кнопке установите Style = tbsSeparator. Так заносится на панель ToolBar горизонтальный разделитель, позволяющий отделить друг от друга кнопки различных функциональных групп. Затем добавьте еще одну кнопку и выполните приложение. Удалите разделитель и добавленную кнопку.
Для занесения на панель ToolBar вертикального разделителя в добавляемой кнопке нужно установить Style = tbsDivider. Однако проще ввести вертикальный разделитель из контекстного меню ToolBar, выбрав команду Новый сепаратор. Убедитесь в этом, добавив на панель вертикальный разделитель и пятую кнопку. После выполнения приложения удалите разделитель и пятую кнопку.
Свойство кнопки Indeterminate задает ее третье состояние
· не нажатая и не отпущенная. Это свойство можно устанавливать в true во время выполнения, если в данном режиме кнопка недоступна. Одну из кнопок выделите свойством Marked, задав его равным true. Выполните приложение. Снимите выделение с кнопки.
Свойство Wrap, установленное в true, приводит к тому, что после этой кнопки ряд кнопок на панели прерывается, и следующие кнопки размещаются в следующем ряду. Убедитесь в этом, добавив на панель 6-7 кнопок, изменив значение Wrap на true у второй добавляемой кнопки. Выполните приложение. Удалите все добавленные кнопки.

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

Со страницы Стандарт перенесите на форму компонент MainMenu и
сконструируйте меню с подменю следующего вида:
ФАЙЛ ПРАВКА ИНСТРУМЕНТЫ
Открыть Экран
Сохранить Шаблон 1
Печать Шаблон 2
Автосохранение
В свойстве Images компонента MainMenu укажите ImageList. Для разделов Открыть, Сохранить, Печать, ИНСТРУМЕНТЫ в свойстве ImageIndex укажите соответственно 0, 1, 2, 3. Для разделов Открыть, Сохранить, Печать в свойство Hint впишите соответственно открыть текстовый файл, сохранить текстовый файл, печать текстового файла. Все сохраните и выполните приложение.
В свойство MenuItem кнопок панели ToolBar внесите соответственно номера разделов Открыть, Сохранить, Печать, ИНСТРУМЕНТЫ, а в свойство ShowHint для первых трех кнопок внесите true. Сохраните все и выполните приложение. Убедитесь, что при нажатии четвертой кнопки в панели ToolBar появляется подменю головного раздела ИНСТРУМЕНТЫ. Сопоставьте и объясните полученные результаты в последних двух случаях.
Для четвертой кнопки, соответствующей разделу ИНСТРУМЕНТЫ, установите Style = tbsDropDown. Кнопка приобретет вид выпадающего списка. Выполните приложение и убедитесь, что в выпадающем списке появляются разделы выпадающего меню.
При Style = tbsDropDown можно вместо свойства MenuItem задать свойство DropDownMenu, определяющее контекстное меню (компонент PopupMenu), которое будет отображаться в выпадающем списке.
Со страницы Стандарт перенесите на форму компонент PopupMenu и
сконструируйте контекстное меню с разделами копировать, вставить. Добавьте на панель ToolBar пятую кнопку, в ее свойство DropDownMenu внесите PopupMenu и задайте для нее Style = tbsDropDown. Выполните приложение. Убедитесь, что при щелчке на пятой кнопке появляется контекстное меню. Удалите пятую кнопку.
В инструментальных панелях используются и другие компоненты. В качестве эксперимента поместим на панель компонент ComboBox (со страницы Стандарт) и компонент CSpinEdit (со страницы Примеры). Выполните приложение и убедитесь в его правильной работе.

Из общих свойств компонента ToolBar следует еще отметить ButtonHeight и ButtonWidth
· высота и ширина кнопок в пикселах, и Wrapable
· автоматический перенос кнопок в следующий ряд панели, если они не помещаются в предыдущем. Такой перенос осуществляется и во время проектирования, и во время выполнения при изменении пользователем размеров панели.
Полоса инструментальной панели может быть очень длинной и не помещаться в отведенном ей месте формы (например палитра компонентов C++Builder). В этих случаях используют компонент PageScroller, обеспечивающий прокрутку панели (может прокручивать любой компонент, но только один и только в одном направлении
· горизонтальном или вертикальном).
Основное свойство компонента PageScroller
· Control. Оно указывает компонент, который должен размещаться и прокручиваться в окне PageScroller. Продолжим проектирование приложения.

Поместите компонент PageScroller на форму. В его свойстве Control установите ToolBar. В этот момент панель ToolBar переместится в окно компонента PageScroller и появится, если необходимо, кнопка прокрутки. Свойство Margin компонента PageScroller определяет размер полей в пикселах, которые оставляются между краем окна PageScroller и прокручиваемым компонентом. По умолчанию эти поля равны нулю и надо задать Margin некоторое положительное значение (например 10), чтобы края прокручиваемого компонента были лучше видны. Выполните приложение и поэкспериментируйте с компонентом PageScroller.





Перестраиваемые панели
· компоненты CoolBar и ControlBar

Перестраиваемые панели являются дальнейшим развитием инструментальных панелей. Только в перестраиваемых панелях сами инструментальные панели обычно являются компонентами более сложных образований. Примером перестраиваемой панели может служить панель ИСР C++Builder 6, включающая в себя ряд более мелких панелей быстрых кнопок и палитру компонентов. Пользователь может настраивать их, изменять местоположение панелей и т.п.
Компонент CoolBar позволяет строить перестраиваемые панели, состоящие из полос (bands). В полосы могут включаться инструментальные панели ToolBar и любые другие оконные компоненты: окна редактирования, панели и т.п. Каждый из этих компонентов автоматически снабжается средствами перемещения его пользователем в пределах окна CoolBar. В полосы могут вставляться и не оконные компоненты, например метки. Но они не будут перемещаемыми.

Задание 7. Изучить перестраиваемую панель CoolBar.
Начните новое приложение, сохраните файл модуля и файл проекта под разными именами.
Перенесите на форму компонент ImageList1 и загрузите в него из каталога Program Files\Common Files\Borland Shared\Images\Buttons следующие файлы изображений под указываемыми индексами: fldropen
· 0, filesave
· 1, print
· 2, monitor
· 3, arrow1ur
· 4, arrow1r
· 5, arrow1d
· 6, arrow1l
· 7, arrow1u
· 8.
Поместите на форму компонент CoolBar. В его свойство Images внесите ImageList, в свойство Hint
· компонент CoolBar, а свойство ShowHint
· true.
Перенесите на компонент CoolBar панель ToolBar. Ей будет отведена отдельная полоса и она растянется на всю ширину CoolBar.
Задайте свойства полосы, на которой разместилась панель ToolBar. Свойства полос задаются редактором полос в окне Правка. Вызвать окно можно тремя способами: из Инспектора Объектов кнопкой с многоточием около свойства Bands, двойным щелчком на компоненте CoolBar или из контекстного меню, выбрав команду Редактор полосВ окне Правка можно перемещаться по полосам, добавлять новые полосы или уничтожать существующие. Пока в окне находится одна полоса: 0
· TCoolBand. Задайте ей свойство Text
· Панель 1, свойство ImageIndex
· 0.
В свойство Images панели ToolBar внесите ImageList1. Поместите на панель четыре кнопки, внося в свойство ImageIndex соответственно значения 0, 1, 2, 3, а в свойство Wrap второй кнопки
· true.
Перенесите на компонент CoolBar вторую панель ToolBar. Ей будет отведена вторая полоса. Для этой полосы задайте свойство Text
· Панель 2, свойство ImageIndex
· 4.
В свойство Images второй панели ToolBar внесите ImageList. Поместите на панель четыре кнопки, внося в свойство ImageIndex соответственно значения 5, 6, 7, 8.
Около каждого компонента, перенесенного на панель CoolBar, слева появляется полоска, за которую компонент можно перемещать курсором мыши. Переместите вторую полосу в верхний ряд правее первой так, чтобы кнопки в первой панели ToolBar расположились в двух рядах. Возможно, для этого придется увеличить высоту полос.
Перенесите на компонент CoolBar два окна редактирования Edit. В свойство Text полос панели CoolBar с окнами редактирования впишите соответственно Окно 1, Окно 2.
Полосу Окно 2 переместите в один ряд с полосой Окно 1, разместив ее справа от последней.
Сохраните все и выполните приложение. Убедитесь, что панель CoolBar можно легко перестраивать и во время выполнения.

Укажем еще несколько свойств полос. Свойство Control определяет размещенный на полосе компонент. Свойство Break определяет, занимает ли полоса весь соответствующий размер контейнера CoolBar, или обрывается. В приведенном выше задании в левых полосах автоматически установится Break = true, а в правых Break = false. Свойства MinHeight и MinWidth определяют минимальную высоту и ширину полосы при перестроениях пользователем полос панели. Свойство FixedSize определяет, фиксирован ли размер данной полосы или он может изменяться пользователем. По умолчанию для всех полос FixedSize = false, т.е. все полосы перестраиваются. Но при желании размеры некоторых полос можно зафиксировать, задав для них FixedSize = true.
Свойство BandMaximize компонента CoolBar определяет действие, которым пользователь может установить максимальный размер полосы, не перетаскивая ее границу: bmNone
· такое действие не предусмотрено, bmClick
· щелчком мыши, bmDblClick
· двойным щелчком. Не рекомендуется действие bmClick, которое приводит к резкому перестроению полос даже при случайном щелчке мыши.
Свойство FixedOrder, если установить его в true, не разрешит пользователю в процессе перемещений полос изменять их последовательность.
Свойство Vertical указывает вертикальное или горизонтальное расположение полос. По умолчанию Vertical = false, что соответствует горизонтальным полосам.

Еще большую свободу перестроений дает пользователю панель ControlBar. В ней может широко применяться техника перетаскивания и встраивания Drag&Doc.
Задание 8. Ознакомиться с перестраиваемой панелью ControlBar.
Начните новое приложение. Поместите на форму компонент ControlBar и перенесите на него две инструментальных панели ToolBar и два окна редактирования Edit. Обратите внимание на то, что каждый компонент, попадая на ControlBar, получает полосу захвата, свойственную технологии Drag&Doc.
На одну панель ToolBar поместите пять кнопок в два ряда, на другую панель ToolBar
· восемь кнопок в два ряда, с вертикальным разделителем между шестой и седьмой кнопками. Сохраните все и выполните приложение. Обратите внимание на возможности перестройки панели.
У компонентов, размещенных на ControlBar, установите свойство DragMode = dmAutomatic и DragKind = dkDoc. Это означает автоматическое выполнение операций Drag&Doc. Сохраните все и выполните приложение. Сравните возможности перестройки в этом случае и в предыдущем случае.
Свойства компонента ControlBar RowSize и RowSnap определяют процедуру встраивания. Убедитесь в том, что свойство RowSize задает размеры полос, в которые могут встраиваться компоненты, а RowSnap определяет захват полосами встраиваемых компонентов.
Убедитесь в том, что свойство AutoDrag определяет, можно (при значении true), или нельзя простым перетаскиванием вынести полосу за пределы ControlBar, а также в том, что при AutoDrag = true можно вынимать из панели отдельные компоненты и они становятся самостоятельными окнами.
Примечание. Чтобы компонент вынуть из панели, нужно курсором мыши щелкнуть на его границе (верхней, правой, нижней), после чего компонент окажется в темной рамке и станет готовым к перемещению в любое место экрана.
Контрольные вопросы
Расскажите о назначении и свойствах компонентов ProgressBar и CGauge.
Приведите примеры использования компонентов ProgressBar и CGauge.
Назовите общее свойство управляющих кнопок Button и BitBtn. Как в надписях кнопок предусмотреть использование клавиш ускоренного доступа?
Как используются свойства Cancel и Default кнопок Button и BitBtn?
Как продублировать другими действиями щелчок на кнопках Button и BitBtn?
Как задать изображение на кнопке BitBtn? Как установить предопределенный тип кнопки BitBtn?
Что является особенностью и как используются кнопки SpeedButton?
Где и как могут размещаться радиокнопки? Как используются группы радиокнопок?
Как используются индикаторы CheckBox и CheckListBox? Приведите примеры.
По значениям каких свойств и как проверяются состояния индикаторов CheckBox и CheckListBox? Какое событие есть в компоненте CheckListBox?
Расскажите о применении и свойствах компонентов TrackBar и ScrollBar. Какое событие имеет компонент ScrollBar? Что передается в обработчик этого события и как его можно использовать?
Как используется компонент HeaderControl? Какие свойства имеет компонент? Какое свойство является основным и как оно задается?
Какие события имеет компонент HeaderControl? Как используются эти события?
С какой целью используется компонент Timer?
Какими свойствами и как управляется компонент Timer? Как использовать обработчик события компонента Timer?
Расскажите об использовании компонента DateTimePicker.
Перечислите панели и компоненты внешнего оформления. Как они используются?
Расскажите о назначениях и свойствах панелей общего назначения – компонентах Panel, GroupBox, Bevel, ScrollBox, Splitter.
Перечислите многостраничные панели и их назначение.
Как задавать и редактировать страницы панели PageControl? Какие свойства имеет страница?
Какие общие свойства и события имеет панель PageControl?
Расскажите о свойствах и событиях компонента TabControl.
Расскажите о возможностях, предоставляемых инструментальными панелями.
Каков порядок выполняемых действий при создании инструментальной панели на основе компонента ToolBar?
Как связать кнопки инструментальной панели с разделами главного или контекстного меню?
Когда и как используют компонент PageScroller?
Расскажите о возможностях, предоставляемых перестраиваемыми панелями. Приведите пример такой панели.
Расскажите о порядке проектирования перестраиваемой панели CoolBar.
Расскажите о порядке проектирования перестраиваемой панели ControlBar. Как при этом использовать технологию Drag&Doc?

Библиографический список
Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 196–209( 217–229(
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.


ЗАНЯТИЕ 8

Системные диалоги

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

Компонент
Страница
Описание

OpenDialog
« Открыть файл»
Диалоги
Предназначен для создания окна диалога «Открыть файл»

SaveDialog
«Сохранить файл»
Диалоги
Предназначен для создания окна диалога «Сохранить файл»

OpenPictureDialog
« Открыть рисунок»
Диалоги
Предназначен для создания окна диалога «Открыть рисунок», открывающего графический файл

SavePictureDialog
«Сохранить рисунок»
Диалоги
Предназначен для создания окна диалога «Сохранить рисунок» – сохранение изображения в графическом файле

FontDialog
«Шрифты»
Диалоги
Предназначен для создания окна диалога «Шрифты» – выбор атрибутов шрифта

ColorDialog
«Цвет»
Диалоги
Предназначен для создания окна диалога «Цвет» – выбор цвета

ColorBox
выбор цвета
Дополнительно
Выпадающий список для выбора пользователем цвета

PrintDialog
«Печать»
Диалоги
Предназначен для создания окна диалога «Печать»

PrinterSetupDialog
«Установка принтера»
Диалоги
Предназначен для создания окна диалога «Установка принтера»

FindDialog
«Найти»
Диалоги
Предназначен для создания окна диалога «Найти» – контекстный поиск в тексте

ReplaceDialog
«Заменить»
Диалоги
Предназначен для создания окна диалога «Заменить» – контекстная замена фрагментов текста

CustomizeDlg
настраиваемый диалог
Дополнительно
Настраиваемый диалог, связанный со стандартными действиями

FileListBox
(список файлов)
Win 3.1
Отображает список всех файлов каталога

DirectoryListBox
(структура каталогов)
Win 3.1
Отображает структуру каталогов диска

DriveComboBox
(список дисков)
Win 3.1
Выпадающий список доступных дисков

FilterComboBox
(список фильтров)
Win 3.1
Выпадающий список фильтров для поиска файлов

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


Все диалоги являются невизуальными компонентами, так что место их размещения на форме не имеет значения. При обращении к этим компонентам вызываются стандартные диалоги, вид которых зависит от версии Windows и настройки системы.
Основной метод (функция), которым производится обращение к любому диалогу, – Execute. Эта функция открывает диалоговое окно и, если пользователь произвел в нем какой-то выбор, то функция возвращает true. При этом в свойствах компонента-диалога запоминается выбор пользователя, который можно прочитать и использовать в дальнейших операциях. Если же пользователь в диалоге нажал кнопку Отмена или клавишу Esc, то функция возвращает false. Поэтому стандартное обращение к диалогу имеет вид
if(<имя компонента-диалога>->Execute())
<оператор, использующий выбор пользователя >;


Диалоги открытия и сохранения файлов – компоненты
OpenDialog, SaveDialog, OpenPictureDialog, SavePictureDialog

Компоненты OpenDialog – законченный диалог «Открыть файл» и SaveDialog – законченный диалог «Сохранить файл как» используются в большинстве приложений.
Создайте новый проект, перенесите на форму компоненты OpenDialog и SaveDialog и, поочередно выделяя, просмотрите их свойства. Видно, что свойства этих компонентов одинаковы, только смысл несколько различен для открытия и сохранения файлов. Двойной щелчок на компонентах вызывает диалоговые окна Открытие файла и Сохранение соответственно.
Основное свойство, в котором возвращается в виде строки выбранный пользователем файл, – FileName. Значение этого свойства можно задать и перед обращением к диалогу, т.е. перед вызовом диалогового окна. Убедитесь в том, что тогда оно появится в диалоге как значение по умолчанию в окне Имя файла.
В свойстве Filter задаются типы искомых файлов, появляющиеся в диалоге в выпадающем списке Тип файла. В процессе проектирования это свойство задают с помощью редактора фильтра, который вызывается нажа-тием кнопки с многоточием около имени этого свойства в Инспекторе Объектов. В левой панели Имя фильтра окна редактора фильтра записывают тот текст, который увидит пользователь в списке Тип файла диалога, а в правой панели Фильтр записываются шаблоны фильтра. Например,
Имя фильтра Фильтр
текстовые (*.txt, *.doc) *.txt; *.doc
все файлы *.*
Убедитесь в том, что после выхода из окна редактора фильтра заданные шаблоны появятся в свойстве Filter в виде строки вида:
текстовые (*.txt, *.doc)|*.txt; *.doc|все файлы|*.*|
В аналогичном виде можно задавать свойство Filter программно во время выполнения приложения.
Свойство FilterIndex определяет номер фильтра, который будет по умолчанию показан пользователю в момент открытия диалога. Например, значение FilterIndex=1 задает по умолчанию первый фильтр.
Свойство InitialDir определяет начальный каталог, который будет открыт в момент начала работы пользователя с диалогом. Если значение этого свойства не задано, то открывается текущий каталог или тот, который был открыт при последнем обращении пользователя к соответствующему диалогу в процессе выполнения данного приложения.
Свойство DefaultExt определяет значение расширения файла по умолчанию. Убедитесь в том, что если оно не задано, то пользователь должен указать в диалоге полное имя файла с расширением, и в том, что если оно задано, то можно писать в диалоге имя без расширения, так как будет принято заданное расширение.
Свойство Title позволяет задать заголовок диалогового окна. Если это свойство не задано, окно открывается с заголовком, определенным в системе (например, Открытие файла и Сохранение). Но можно задать и свой заголовок, подсказывающий пользователю ожидаемые действия. Например, Укажите имя и тип сохраняемого файла. Убедитесь в этом.
Свойство Options определяет условия выбора файла. Множество опций, которые можно установить программно или во время проектирования, включает:

ofAllowMultiSelect
Позволяет пользователю выбирать несколько файлов

ofCreatePrompt
В случае, если пользователь написал имя несуществующего файла, появляется замечание и запрос, надо ли создать файл с заданным именем

ofEnableIncludeNotify
Разрешает посылать в диалог сообщения

ofEnableSizing
Разрешает пользователю изменять размер диалогового окна

ofExtensionDifferent
Этот флаг, который можно прочитать после выполнения диалога, показывает, что расширение файла, выбранного пользователем, отличается от DefaultExt

ofFileMustExist
В случае, если пользователь написал имя несуществующего файла, появляется сообщение об ошибке

ofHideReadOnly
Удаляет из диалога индикатор Открыть только для чтения

ofNoChangeDir
После щелчка пользователя на кнопке OK восстанавливает текущий диалог, независимо от того, какой каталог был открыт при поиске файла

ofNoDereferenceLinks
Запрещает переназначать клавиши быстрого доступа в диалоговом окне

ofNoLongNames
Отображаются только не более 8 символов имени и трех символов расширения

ofNoNetworkButton
Убирает из диалогового окна кнопку поиска в сети. Действует только, если флаг ofOldStyleDialog включен

ofNoReadOnlyReturn
Если пользователь выбрал файл только для чтения, то генерируется сообщение об ошибке

ofNoTestFileCreate
Запрещает выбор в сети защищенных файлов и недоступных дисков при сохранении файла

ofNoValidate
Не позволяет писать в именах файлов неразрешенные символы, но не мешает выбирать файлы с неразрешенными символами

ofOldStyleDialog
Создает диалог выбора файла в старом стиле

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

ofPathMustExist
Генерирует сообщение об ошибке, если пользователь указал в имени файла несуществующий каталог

ofReadOnly
По умолчанию включает индикатор Открыть тол-ко для чтения при открытии диалога

ofShareAware
Игнорирует ошибки нарушения условий коллективного доступа и разрешает, несмотря на них, производить выбор файла

ofShowHelp
Отображает в диалоговом окне кнопку Справка


По умолчанию все перечисленные опции в свойстве Options, кроме ofHideReadOnly, выключены. Однако из описания опций можно сделать вывод, что многие из них полезно включить перед вызовом диалогов.
Если разрешить с помощью опции ofAllowMultiSelect множественный выбор файлов, то список выбранных файлов можно прочитать в свойстве Files типа Strings. При включении опции ofOldStyleDialog изменяется вид диалогового окна. Убедитесь в этом.
В компонентах диалогов открытия и сохранения файлов предусмотрена возможность обработки ряда событий. Она может потребоваться, если опций не хватает, чтобы установить все требуемые ограничения на выбор файлов.
Событие OnCanClose возникает при нормальном закрытии пользователем диалогового окна после выбора файла. При отказе пользователя от диалога – нажатии кнопки Отмена, клавиши Esc и т.д. событие OnCanClose не наступает. В обработке события OnCanClose можно произвести дополнительные проверки выбранного пользователем файла и, если по условиям задачи этот выбор недопустим, можно известить об этом пользователя и задать значение false передаваемому в обработчик параметру CanClose. Это не позволит пользователю закрыть диалоговое окно.
В обработчиках событий: OnFolderChange – изменение каталога, OnSelectionChange – изменение имени файла, OnTypeChange – изменение типа файла – можно предусмотреть какие-то сообщения пользователю.

Задание. Построить приложение, содержащее MainMenu1 и окно редактирования RichEdit1, в которое по команде меню Открыть загрузить текстовый файл и после каких-то изменений, сделанных пользователем, – сохранить текст по команде Сохранить в том же файле, а по команде Сохранить как – в файле с другим именем. Для действий с файлами использовать диалоги OpenDialog и SaveDialog.
Начните новое приложение, в свойство Caption формы впишите ДИАЛОГИ OPENDIALOG, SAVEDIALOG.
Поместите на форму компоненты OpenDialog1, SaveDialog1, MainMenu1 и RichEdit1.
Сконструируйте меню с командами Открыть, Сохранить как и Сохранить.
После чтения файла нужно запомнить его имя, чтобы знать, под каким именем потом его сохранять. Для хранения имени файла определите переменную с именем MyFName и объявите ее как глобальную в модуле формы:
AnsiString MyFName = "";
Впишите обработчик команды Открыть:
if(OpenDialog1->Execute())
{
MyFName = OpenDialog1->FileName;
RichEdit1->Lines->LoadFromFile(OpenDialog1->FileName);
}
Этот оператор вызывает диалог OpenDialog1, проверяет, выбрал ли пользователь файл (если выбрал, то функция Execute возвращает true), после чего имя выбранного файла (OpenDialog1->FileName) сохраняется в переменной MyFName и файл загружается в текст RichEdit1 методом LoadFromFile.
Впишите обработчик команды Сохранить как:
SaveDialog1->FileName = MyFName;
if(SaveDialog1->Execute())
{
MyFName = SaveDialog1->FileName;
RichEdit1->Lines->SaveToFile(SaveDialog1->FileName);
}
Первый из этих операторов присваивает свойству FileName компонента SaveDialog1 имя выбранного файла. Это имя по умолчанию будет предложено пользователю при открытии диалога Сохранить как. Следующий оператор открывает диалог и, если пользователь выбрал в нем файл, запоминает новое имя файла и сохраняет в файле с этим именем текст компонента RichEdit1.
Впишите обработчик команды Сохранить:
if(MyFName!="")
RichEdit1->Lines->SaveToFile(MyFName);
else
if(SaveDialog1->Execute())
{
MyFName = SaveDialog1->FileName;
RichEdit1->Lines->SaveToFile(SaveDialog1->FileName);
}
Если имя файла MyFName не равно пустой строке, т.е. имя файла известно, то нет необходимости обращаться к какому-то диалогу, и текст сохраняется методом SaveToFile. Если же имя файла неизвестно, то текст сохраняется с помощью диалога SaveDialog1 так же, как было рассмотрено выше.
После команды Сохранить все запустите приложение на выполнение. Убедитесь в работоспособности приложения.

Выше были рассмотрены диалоги открытия и сохранения файлов произвольного типа. Однако в библиотеке C++Builder имеются также специализированные диалоги открытия и закрытия графических файлов: OpenPictureDialog и SavePictureDialog. С помощью компонента OpenPictureDialog откройте графический файл ..Program Files\Common Files\Images\Splash\16 Color\earth. Убедитесь в том, что окна, открываемые данными компонентами, отличаются удобной возможностью просматривать изображения в процессе выбора файла.
Свойства компонентов OpenPictureDialog и SavePictureDialog ничем не отличаются от свойств компонентов OpenDialog и SaveDialog. Единственное отличие – заданное значение по умолчанию свойства Filter в OpenPictureDialog и SavePictureDialog. В редакторе фильтра перечислены все типы графических файлов, с которыми может работать диалог. Пользователю остается удалить фильтры тех файлов, с которыми он не будет работать, и, если нужно, добавить фильтр “Все файлы(*.*)”.


Фрагменты диалогов – компоненты
DriveComboBox, DirectoryListBox,
FilterComboBox, FileListBox, CDirectoryOutline

К компонентам работы с файловой системой, представляющим собой фрагменты диалогов, относятся выпадающие списки дисков (драйверов) – DriveComboBox и фильтров (масок) файлов – FilterComboBox, списки каталогов – DirectoryListBox и файлов – FileListBox, дерево каталогов – CDirectoryOutline. Эти компоненты работы с файловой системой облегчают пользователю создание собственных диалоговых окон.
Начните новое приложение.
В свойство Caption формы впишите ФРАГМЕНТЫ ДИАЛОГОВ.
На форме слева, сверху вниз, разместите метку Label1 с надписью FileListBox, окно редактирования Edit1, компонент FileListBox1, метку Label2 с надписью FilterComboBox и компонент FilterComboBox1. Компоненты Edit1, FileListBox1 и FilterComboBox1 установите по ширине одинаковыми.
По середине формы, сверху вниз, разместите метку Label3 с надписью DirectoryListBox, метку Label4 с надписью путь к текущему каталогу, компонент DirectoryListBox1, метку Label5 с надписью DriveComboBox и компонент DriveComboBox1. Компоненты DirectoryListBox1 и DriveComboBox1 установите по ширине одинаковыми.
В правой части формы разместите метку Label6 с надписью CDirectoryOutline и компонент CDirectoryOutline1.
Для надписей используйте полужирный шрифт размером 12.
Сохраните все и запустите приложение на выполнение. Ознакомьтесь с возможностями данного приложения во время выполнения.

Рассмотрение компонентов работы с файловой системой начнем с компонента DriveComboBox – выпадающего списка дисков (драйверов). При размещении на форме этот компонент автоматически отображает список имеющихся на компьютере дисков. Во время выполнения приложения имя выбранного пользователем диска можно прочитать в свойстве Drive, а строку, содержащуюся в окне списка – в свойстве Text. Свойство TextCase задает регистр отображения: tcUpperCase – в верхнем регистре, tcLowerCase – в нижнем.
Во время проектирования компонент DriveComboBox через его свойство DirList можно связать с компонентом DirectoryListBox – списком каталогов. Программно связь этих двух компонентов обеспечивается включением в обработчик события OnChange компонента DriveComboBox оператора
DirectoryListBox1->Drive = DriveComboBox1->Drive;
Этот оператор задает имя диска, выбранное пользователем в компоненте DriveComboBox1, свойству Drive списка каталогов DirectoryListBox1.
Аналогичным оператором можно обеспечить связь компонента DriveComboBox1 с деревом каталогов и файлов в компоненте CDirectoryOutline1:
CDirectoryOutline1->Drive = DriveComboBox1->Drive;
Перейдем к рассмотрению выпадающего списка фильтров – компонента FilterComboBox1. Его основное свойство – Filter, которое задается так же, как описано выше. Доступ к отдельным частям фильтра – тексту и маске, осуществляется через свойства Text и Mask соответственно. Связь компонента FilterComboBox1 со списком файлов FileListBox1 устанавливается через свойство FileList компонента FilterComboBox1.
Компонент DirectoryListBox1 отображает список каталогов диска, заданного свойством Drive. Значение свойства Drive можно установить программно во время выполнения. Связь этого свойства с выбранным пользователем диском в компоненте DriveComboBox1 устанавливается или программно, или во время проектирования с помощью свойства DirList компонента DriveComboBox1. Это свойство может указывать на компонент DirectoryListBox1. Связь списка каталогов, т.е. компонента DirectoryListBox1 с компонентом FileListBox1, отображающим список файлов, осуществляется с помощью свойства FileList компонента DirectoryListBox1. Можно также использовать результаты выбора пользователем каталога, читая свойство Directory в обработчике события OnChange. С компонентом DirectoryListBox1 можно также связать метку типа Label. В этой метке будет отображаться путь к текущему каталогу. Если путь не умещается в метке, то он автоматически отображается в сокращенном виде с помощью функции MinimizeName. Метка, отображающая каталог, указывается в свойстве DirLabel компонента DirectoryListBox1.
Список файлов содержится в компоненте FileListBox1. Его свойства Drive, Directory и Mask определяют соответственно диск, каталог и маску файлов. Эти свойства можно устанавливать программно или связывая указанным выше способом компонент FileListBox1 с компонентами DriveComboBox1, DirectoryListBox1 и FilterComboBox1. Свойство FileType позволяет включать в список не все файлы, а только те, которые имеют соответствующие атрибуты. Свойство FileType представляет собой множество, указывающее типы включаемых файлов. Элементы этого множества могут иметь значения: ftReadOnly – только для чтения, ftHidden– невидимые, ftSystem – системные, ftVolumeID – обозначения дисков, ftDirectory – каталоги, ftArchive – архивные, ftNormal – не имеющие особых атрибутов.
Свойство ShowGlyphs разрешает или исключает показ пиктограмм файлов. Свойство MultiSelect разрешает выбор нескольких файлов.
Основное свойство, в котором можно прочитать имя выбранного пользователем файла – FileName.
Со списком файлов может быть связано окно редактирования Edit1, в котором отображается выбранный файл. На этот список указывает устанавливаемое во время проектирования свойство FileEdit компонента FileListBox1.
Рассмотрение закончим компонентом CDirectoryOutline1, который содержит дерево каталогов. Значение диска устанавливается свойством Drive. Текущий каталог, выбранный пользователем, можно прочитать в свойстве Directory. Свойство TextCase определяет стиль отображения имен каталогов: tcLowerCase – преобразование к нижнему регистру, tcUpperCase – к верхнему, tcAsIs – без преобразования.
Продолжим проектирование приложения.
Свяжите компоненты друг с другом: DriveComboBox1 свойством DirList с DirectoryListBox1, FilterComboBox1 свойством FileList с FileListBox1, DirectoryListBox1 свойством FileList с FileListBox1, DirectoryListBox1 свойством DirLabel с меткой Label4, FileListBox1 свойством FileEdit с Edit1.
Сохраните все и запустите приложение на выполнение. Сравните выполнение данного приложения с выполнением приложения в п. 5.


Диалог выбора шрифта – компонент FontDialog

Перенесите на форму компонент FontDialog.
Двойной щелчок на компоненте FontDialog вызывает диалоговое окно выбора атрибутов шрифта, в котором пользователь может выбрать имя шрифта, его стиль (начертание), размер и другие атрибуты.
Основное свойство компонента – Font типа TFont, в котором можно задать начальные установки атрибутов шрифта и в котором можно прочесть значения атрибутов, выбранные пользователем в процессе диалога.
Свойства MaxFontSize и MinFontSize устанавливают ограничения на максимальный и минимальный размеры шрифта. Значения по умолчанию равны нулю, и тогда ограничения на размер отсутствуют.
Свойство Device определяет, из какого списка возможных шрифтов будет предложен выбор в диалоговом окне: fdScreen – из списка экрана (по умолчанию), fdPrinter – из списка принтера, fdBoth – из обоих.
Свойство Options содержит множество опций, из которого выделим следующие:

fdAnsiOnly
Отображать только множество шрифтов символов Windows, не отображать шрифтов со специальными символами

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

fdEffects
Отображать в диалоге индикаторы специальных эффектов (подчеркивание и др.) и список Цвет

fdFixedPitchOnly
Отображать только шрифты с постоянной шириной символов

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

fdLimitSize
Разрешить использовать свойства MaxFontSize и Min-FontSize, ограничивающие размеры шрифта

fdNoFaceSel
Открывать диалоговое окно без предварительно установленного имени шрифта

fdScalableOnly
Отображать только масштабируемые шрифты, удалять из списка не масштабируемые (шрифты bitmap)

fdNoSizeSel
Открывать диалоговое окно без предварительно установленного размера щрифта

fdNoStyleSel
Открывать диалоговое окно без предварительно установленного начертания шрифта

fdWysiwyg
Предлагать в списке только шрифты, доступные и для экрана, и для принтера, удаляя из него аппаратно зависимые шрифты


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

Рассмотрим примеры применения компонента FontDialog.
Начните новое приложение. На форме с надписью ДИАЛОГ FONTDIALOG разместите компоненты MainMenu1, Memo1, RichEdit1, FontDialog1, FontDialog2, FontDialog3.
Сконструируйте меню с командами Шрифт_M, Шрифт_R, Шрифт_F.
В обработчик команды Шрифт_M впишите
if(FontDialog1->Execute())
Memo1->Font->Assign(FontDialog1->Font);
Приведенный оператор вызывает диалог выбора атрибутов шрифта и, если пользователь произвел выбор, значения всех выбранных атрибутов, содержащиеся в свойстве FontDialog1->Font, присваиваются атрибутам окна редактирования, содержащимся в свойстве Memo1->Font. Шрифт в окне Memo1 немедленно изменится. Убедитесь в этом, запустив приложение на выполнение.
В компоненте FontDialog1 установим опцию fdApplyButton в true и напишем обработчик события OnApply:
Memo1->Font->Assign(FontDialog1->Font);
Выполним приложение. Теперь пользователь может наблюдать изменения в окне Memo1, нажимая в диалоговом окне кнопку Применить и не прерывая диалога. Это очень удобно, так как позволяет пользователю правильно подобрать атрибуты шрифта.
Используем в качестве окна редактирования RichEdit1. Теперь в диалоговом окне пользователь выбирает атрибуты шрифта для выделенного фрагмента текста или для вновь вводимого текста. Эта возможность реализуется следующим обработчиком команды Шрифт_R:
if(FontDialog2->Execute())
RichEdit1->SelAttributes->Assign(FontDialog2->Font);
Сохраните все и выполните приложение.
Можно разрешить пользователю изменять шрифт не только отдельных компонентов, но и всех компонентов и надписей на форме. Это осуществляется следующим обработчиком команды Шрифт_F:
if(FontDialog3->Execute())
Font->Assign(FontDialog3->Font);
В этом операторе свойство Font без ссылки на компонент подразумевает шрифт формы.
Запустите приложение на выполнение и убедитесь в работоспособности приложения.


Диалоги выбора цвета – компоненты ColorDialog, ColorBox

Начните новое приложение, перенесите на форму компоненты ColorDialog1, MainMenu1 и Memo1. В свойство Caption формы впишите ДИАЛОГ COLORDIALOG.
Компонент ColorDialog1 вызывает диалоговое окно выбора цвета, в котором пользователь может выбрать цвет из базовой палитры или, нажав кнопку Определить цвет, раскрыть дополнительную панель, позволяющую синтезировать цвет, отличный от базовых. Синтез цвета производится перемещением курсора по горизонтали для выбора оттенка и по вертикали для выбора контрастности. Яркость регулируется перемещением по вертикальной шкале справа от матрицы цветов. Синтезированный цвет можно добавить кнопкой Добавить в набор в палитру дополнительных цветов на левой панели и использовать его в дальнейшем.
Основное свойство компонента ColorDialog1 – Color. Это свойство соответствует тому цвету, который выбрал в диалоге пользователь. Если при вызове диалога желательно установить некоторое начальное приближение цвета, это можно сделать, установив Color предварительно во время проектирования или программно.
Сконструируйте меню с командой Цвет, в обработчик которой впишите оператор задания цвета фона компонента Memo1:
if(ColorDialog1->Execute())
Memo1->Color = ColorDialog1->Color;
Установите произвольное значение свойства Color, запустите приложение на выполнение. После ввода команды Цвет появится диалоговое окно выбора цвета, в котором нажмите OK. Цвет фона Memo1 совпадет с установленным в свойстве Color цветом.
Свойство CustomColors типа TStrings позволяет задать заказные цвета дополнительной палитры или прочитать заказной цвет, сформированный пользователем в диалоге. Каждый цвет определяется строкой вида
<Имя цвета>=<шестнадцатеричное представление цвета>;
Имена цветов задаются от ColorA (первый цвет) до ColorP (шестнадцатый, последний). Значение цвета задается в виде 4-байтового шестнадцатерич-ного числа, три младших разряда которого представляют собой интенсивности красного, зеленого и синего цветов в формате RGB. Например, значение 0x00FF0000 соответствует чистому синему цвету, 0x0000FF00 – чистому зеленому, 0x000000FF – чистому красному. 0x00000000 – черный цвет, 0x00FFFFFF – белый. Если старший байт равен нулю (00), то берется ближайший к заданному цвет из системной палитры. Если старший байт равен единице (01), то берется ближайший к заданному цвет из текущей палитры. Если старший байт равен двум (02), то берется ближайший к заданному цвет из логической палитры контекста данного устройства.
Нажав кнопку с многоточием в свойстве CustomColors, вызовите редактор строки списка, в окно которого поместите строки
ColorA=0000FF
ColorD=00FF00
ColorP=FF0000
ColorF=808022
и нажмите кнопку OK. Запустите приложение на выполнение. По команде Цвет появится окно выбора цвета с четырьмя дополнительными цветами. Выбранный цвет задаст цвет фона Memo1. Убедитесь в этом.
Выполняйте приложение, задавая в окне Цвет основные и дополнительные цвета.

Свойство Options компонента ColorDialog1 содержит множество следующих опций:

cdFullOpen
Отображать сразу при открытии диалогового окна панели определения заказных цветов

cdPreventFullOpen
Запретить появление в диалоговом окне кнопки Определить цвет, так что пользователь не сможет определять новые цвета

cdShowHelp
Добавить в диалоговое окно кнопку Справка

cdSolidColor
Указать Windows использовать сплошной цвет, ближайший к выбранному (это обедняет палитру)

cdAnyColor
Разрешить пользователю выбирать любые не сплошные цвета (такие цвета могут быть не ровными)


По умолчанию все опции выключены.
Рассмотренный компонент ColorDialog вызывает стандартный диалог Windows, возможности которого зачастую избыточны. Пользователю удобнее выбирать цвет с помощью выпадающего списка. Такую возможность предоставляет компонент ColorBox (страница Дополнительно).
Перенесите на форму компонент ColorBox и ознакомьтесь с его свойствами.
Свойство Style является множеством, элементы которого определяют, какие именно категории цвета представлены в списке. Элементы множества означают следующее:

cbStandardColors
16 стандартных цветов типа clRed, clBlack и т.п.

cbExtendedColors
Набор дополнительных цветов clMoneyGreen, clSkyBlue, clCream, clMedGray

cbSystemColors
Системные цвета, установленные в Windows

cbIncludeNone
Список включает в себя строку «clNone». Какой именно цвет будет отображаться в квадратике этой строки, определяется свойством NoneColorColor компонента Color-Box, а истинный цвет определяется компонентом, воспринимающим этот цвет. Эта опция влияет только при включенной опции cbSystemColors

cbIncludeDefault
Список включает в себя строку «clDefault» – цвет по умолчанию. Какой именно цвет будет отображаться в квадратике этой строки, определяется свойством DefaultColorColor компонента ColorBox, а истинный цвет определяется компонентом, воспринимающим этот цвет. Эта опция влияет только при включенной опции cbSystemColors

cbCustomColor
Первой строкой в списке появляется «Custom». При выборе пользователем этой строки открывается диалоговое окно выбора цвета, в котором пользователь может определить заказной (нестандартный) цвет

cbPrettyNames
Строки в списке обозначают цвета, а не их имена: например, «Black», а не «clBlack»


Как видно из приведенной таблицы, в список, помимо различных цветов, могут включаться строки «clDefault» – цвет компонента по умолчанию, и «clNone» – цвет, зависящий от версии Windows – белый для Windows 98 и черный для Windows 2000/XP/NT. Если присвоить цвет clDefault какому-то компоненту, то компонент будет рисоваться цветом, который заложен в него по умолчанию. Аналогично, присваивание clNone тоже приведет к тому, что истинный цвет будет определяться самим компонентом.
Свойство DefaultColorColor определяет, квадратиком какого цвета будет помечена в списке строка «clDefault». Свойство NoneColorColor определяет, квадратиком какого цвета будет помечена в списке строка «clNone». В действительности же присваиваемые цвета будут определяться теми компонентами, в которые они передаются.
Узнать цвет, выбранный пользователем в списке, позволяет свойство Selected. Для этого используют событие OnSelect, наступающее в момент выбора пользователем цвета. Оператор
Memo1->Color = ColorBox1->Selected;
помещенный в обработчик этого события, задаст фону окна Memo1 цвет, выбранный пользователем.
Свойство только времени выполнения Colors является индексированным массивом цветов в списке (индексы начинаются с нуля). Свойство ColorNames – аналогичный массив строк с именами цветов.
Большинство остальных свойств, методов, событий компонента ColorBox подобны компоненту ComboBox. В частности, список всех строк содержится в свойстве Items типа TStrings. Индекс строки цвета, которая будет показана пользователю в момент начала выполнения приложения, определяется свойством только времени выполнения ItemIndex. Чтобы в первый момент показать пользователю определенный цвет, в обработчике события формы OnCreate определяют ItemIndex с помощью метода IndexOf. Например, следующий оператор в первый момент показывает пользователю строку «clDefault»:
ColorBox1->ItemIndex = ColorBox1->Items->IndexOf(“clDefault”);

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



Контрольные вопросы

Перечислите компоненты, реализующие стандартные диалоги и объясните их назначение.
Приведите и объясните вид стандартного обращения к диалогу.
Расскажите о свойствах компонентов OpenDialog и SaveDialog – Filter, FilterIndex, InitialDir, DefaultExt, Title.
Расскажите о свойстве Options компонентов OpenDialog и SaveDialog.
Расскажите о событиях OnCanClose, OnFolderChange, OnSelectionChange, OnTipeChange.
Приведите пример обработчика команды Открыть.
Приведите пример обработчика команды Сохранить как.
Приведите пример обработчика команды Сохранить.
Расскажите о диалогах открытия и закрытия графических файлов.
Перечислите компоненты работы с файловой системой и объясните их назначение.
Расскажите об использовании компонентов работы с файловой системой.
Как компоненты работы с файловой системой связать друг с другом?
Расскажите о свойствах и применении диалога выбора шрифта.
Приведите примеры применения диалога выбора шрифта (в Memo, в RichEdit, на форме).
Расскажите о диалогах выбора цвета – ColorDialog, ColorBox.
Как задавать основные и дополнительные цвета?
Какие свойства имеет компонент ColorDialog?
Какие свойства имеет компонент ColorBox?


Библиографический список

Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 237–256, 1062–1064, 1136.
Шамис В.А. Borland C++ Builder 6. Для профессионалов / В.А. Шамис.
· СПб.: Питер, 2003.
· 798 с.– С. 434–437.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.


ЗАНЯТИЕ 9

Технология разработки приложений.
Диспетчеризация действий

Прежде чем начать программирование приложения, нужно составить список действий, которые должны быть доступны будущему пользователю через разделы меню, инструментальные панели, кнопки и другие элементы управления.
Действие (Action) - реализация некоторого поведения, являющегося реакцией на поступок пользователя, такой, как щелчок на кнопке или на разделе меню – инициаторе действия или интерфейсном компоненте действия. Примерами действий являются открытие файла и его загрузка в какой-то приемник, сохранение информации в файле на диске, установка атрибутов шрифта и выравнивание текстов в окнах редактирования, поиск и замена фрагментов текста, навигация по набору данных, выполнение какогог-то внешнего приложения, вызов справки и многое другое.
Перечисленные примеры относятся к стандартным действиям. В действительности таких действий много больше. Все стандартные действия реализованы в C++Builder классами, наследующими базовому классу действий TAction. Обработчики подобных действий в ряде случаев писать вообще не надо, так как они реализованы в соответствующих классах.
Помимо стандартных действий в реальных приложениях имеются и нестандартные, связанные с какими-то расчетами, обработкой данных и т.п. Объекты таких действий пользователь должен создавать сам и сам должен писать для них обработчики, реализующие данные действия.
При программировании список действий реализуется специальными компонентами, обеспечивающими диспетчеризацию действий, – ActionList и ActionManager. Редактор диспетчера действий ActionList позволяет сформировать список действий, написать обработчики, выполняющие задуманные действия, задать основные свойства будущих интерфейсных элементов – пиктограммы, надписи, быстрые кнопки, тексты подсказок и т.п. Диспетчер действий ActionManager намного мощнее диспетчера ActionList.
После того, как список действий создан, надо сформировать полосы действий. Это полосы, на которых располагаются интерфейсные компоненты действий – полоса главного меню и инструментальные панели. При использовании диспетчера ActionList полосы действий добавляют на форму в виде отдельных компонентов, создают на них инициаторы действий (разделы меню, быстрые кнопки), а затем связывают инициаторы с соответствующими действиями из списка диспетчера ActionList. При таком связывании свойства, заложенные в действия, автоматически передаются интерфейсным компонентам. В диспетчере ActionManager создание полос действий упрощается. Они создаются и формируются непосредственно из редактора ActionManager простым перетаскиванием мышью.
Интерфейсные компоненты действий обычно должны содержать поясняющие их изображения. Изображения собираются в списке изображений – компоненте ImageList. Для нестандартных действий изображения загружаются в ImageList пользователем. А изображения для стандартных действий загружаются в ImageList автоматически по мере формирования списка в диспетчере действий.
Таким образом, последовательность формирования списка действий и проектирования меню и инструментальных панелей сводится к следующим шагам.
Составляется список действий, которые должны быть доступны будущему пользователю через разделы меню, инструментальные панели, кнопки и другие элементы управления.
Для тех нестандартных действий, которые должны быть доступны из быстрых кнопок инструментальной панели, готовится список пиктограмм на кнопках в компоненте ImageList.
На главную форму переносится компонент диспетчеризации действий: ActionList или ActionManager. Компонент связывается с ImageList. Формируется список стандартных и нестандартных действий.
Каждому действию задается набор характеристик: Name (имя), Caption (надпись, в которой выделяется символ быстрого доступа), ShortCut («горячие» клавиши), ImageIndex (номер изображения в ImageList), Hint (тексты подсказок), HelpContext или HelpKeyWord (ссылка на тему справки) и др. Для нестандартных действий все эти характеристики записываются пользователем. Для стандартных действий они заносятся автоматически. Нужно только надписи и подсказки перевести на русский язык и, может быть, исправить ссылки на неприемлемые стандартные изображения и комбинации «горячих» клавиш. Если в приложении предусматривается контекстная справка, то надо задать ссылки на соответствующие темы. Естественно, в начале проектирования справки еще нет. Так что свойства HelpContext и HelpKeyWord задают позднее.
Записываются обработчики событий выполнения для всех нестандартных действий. Стандартные действия обрабатываются автоматически и для многих из них достаточно задать некоторые свойства обработки. Но иногда надо писать обработчики и для стандартных действий.

Дальнейшие шаги зависят от используемого диспетчера. Для диспетчера ActionList далее надо сделать следующее.
На форму переносится компонент MainMenu – главное меню, связывается с ImageList, в компоненте формируется меню и в его разделах даются ссылки на действия, описанные в ActionList.
На форме создается инструментальная панель (обычно компонент ToolBar). Панель связывается с ImageList, а в ее кнопках даются ссылки на действия, описанные в ActionList.

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

Диспетчеризация действий на основе компонента ActionList

Создайте новое приложение и командой Сохранить все сохраните файл модуля и проект. В свойство Caption формы впишите Тест нестандартных действий.
Перенесите на форму со страницы Стандарт компонент ActionList1. Перенесите также со страницы Дополнительно компонент ImageList1 и сошлитесь на него в свойстве Images компонента ActionList1.
Загрузите в компонент ImageList1 не менее трех изображений. Изображения в компонент ImageList1 могут быть загружены в процессе проектирования с помощью редактора списков изображений. Окно редактора вызывается двойным щелчком на компоненте ImageList1 или щелчком правой кнопки мыши и выбором команды контекстного меню Редактор ImageList. В окне редактора можно добавить в списки изображение (кнопка Добавить), удалить изображение из списка кнопкой Удалить, очистить весь список кнопкой Очистить.
При добавлении изображения в список, которое начинается с нажатия кнопки Добавить, открывается окно открытия файлов изображений, в котором можно выбрать нужный файл. Множество изображений, размещаемых обычно на кнопках, содержится в папке \Program Files\Common Files\Borland Shared\Images\Buttons.
Следует помнить, что размер всех изображений в списке должен быть одинаковым. Как правило, это размер, используемый для пиктограмм в меню, списках, кнопках. При добавлении в список изображений для кнопок надо иметь в виду, что они часто содержат не одно, а два и более изображений. В этих случаях после выбора имени файла изображений при щелчке на кнопке Открыть задается вопрос: “Bitmap dimensions for are greater then imagelist dimensions. Separate into separate bitmaps?” (“Размерность изображения больше размерности списка. Разделить на отдельные изображения?”). Если ответить отрицательно, то все изображения уменьшатся в горизонтальном размере и лягут как одно изображение. Использовать его в дальнейшем будет невозможно. Поэтому на заданный вопрос надо ответить положительно. Тогда загружаемая битовая матрица автоматически разделится на отдельные изображения, а затем те из них, которые не нужны, удаляют.
Каждое загруженное в список изображение получает индекс. Именно на эти индексы впоследствии ссылаются в соответствующих свойствах разделов меню, списков, кнопок и т.д., когда надо загрузить в них то или иное изображение. Чтобы изменить последовательность изображений в списке, перетаскивают изображение мышью на новое место.
Сделайте на компоненте ActionList1 двойной щелчок, чтобы попасть в Редактор Действий (окно Редактирование Form1->ActionList1), позволяющий вводить и упорядочивать действия. Колонка Категории: пока не будет иметь отношения к проектированию приложения. Щелчок правой кнопкой мыши или щелчок на маленькой кнопке со стрелкой вниз правее первой быстрой кнопки окна редактирования позволяет выбрать одну из команд: Новое действие или Новое стандартное действие. Первая из них относится к вводу нового действия любого типа. Пока будем пользоваться только командой Новое действие. Трижды выберите эту команду. В колонке Действия: появятся имена действий по умолчанию: Action1, Action2, Action3.
Выделите Action1. В Инспекторе Объектов указанным ниже свойствам объекта действия Action1 присвойте следующие значения: Caption – &Формирование, Hint – размер 3х5, ImageIndex – 0, Name – AMatr, ShortCut – Ctrl+A. Для Action2: Caption – &Транспонирование, Hint – однократное, ImageIndex – 1, Name – ATrans, ShortCut – Ctrl+B. И для Action3: Caption – &Стереть, Hint – очистка Memo1, ImageIndex – 2, Name – ADel, ShortCut – Ctrl+C.
На странице событий Инспектора Объектов для каждого действия определено три события: OnExecute, OnUpdate и OnHint. Событие OnExecute возникает в момент, когда пользователь инициализировал действие, например, щелкнув на компоненте (разделе меню, кнопке), связанном с данным действием. Обработчик этого события должен содержать процедуру, реализующую данное действие. Событие OnUpdate периодически возникает в промежутках между действиями. Возникновение этих событий прекращается только во время реализации события или во время, когда пользователь ничего не делает и компьютер находится в состоянии ожидания действий. Обработчик события OnUpdate может содержать какие-то настройки, подготовку ожидаемых дальнейших действий или выполнение каких-то фоновых операций. Событие OnHint возникает в момент, когда на экране отображается ярлычок подсказки в результате того, что пользователь задержал курсор мыши над компонентом, инициализирующим событие. Наличие в объекте действия событий OnUpdate и OnHint расширяет возможности по проектированию приложения.
Со страницы Стандарт перенесите на форму компонент Memo1. Установите шрифт (Font) Courier, полужирный, размер – 12.
В файле реализации модуля поместите следующие объявления и обработчики событий OnExecute (курсив):

__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

int r[50][50],a[50][50],n=3,m=5;
AnsiString s[50][50],s1[50];
//---------------------------------------------------------------------------
void __fastcall TForm1::AMatrExecute(TObject *Sender)
{
int i,j;
for(i=0;i for(j=0;j r[i][j]=random(10);
Memo1->SetFocus();
Memo1->Clear();
for(i=0;i s1[i]="";
for(j=0;j s[i][j]=" "+IntToStr(r[i][j]);
s1[i]+=s[i][j];}
Memo1->Lines->Add(s1[i]);}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ATransExecute(TObject *Sender)
{
int i,j;
for(i=0;i for(j=0;j a[j][i]=r[i][j];
i=n;n=m;m=i;
Memo1->SetFocus();
Memo1->Clear();
for(i=0;i s1[i]="";
for(j=0;j s[i][j]=" "+IntToStr(a[i][j]);
s1[i]+=s[i][j];}
Memo1->Lines->Add(s1[i]);}
i=n;n=m;m=i;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ADelExecute(TObject *Sender)
{
Memo1->Clear();
}
//---------------------------------------------------------------------------

Перенесите на форму со страницы Стандарт компонент MainMenu1. В свойство Images внесите ImageList1. Двойным щелчком на компоненте MainMenu1 перейдите в окно Form1-> MainMenu1 Конструктора Меню. В свойство Caption головного раздела меню впишите &МАТРИЦА. В трех создаваемых затем разделах в свойство Action вносите соответственно значения: AMatr, ATrans, ADel. Как показывает Инспектор Объектов, при этом в разделы меню переносятся свойства соответствующего объекта действия. Более того, в событие – щелчок на разделе – подставится обработчик, предусмотренный для данного действия. Таким образом, если в свойстве Action раздела указать имя объекта действия, то свойства и обработчики событий объекта действия будут перенесены на этот раздел меню.
Со страницы Win32 перенесите на форму инструментальную панель – компонент ToolBar1. По умолчанию он расположится вверху, поскольку его свойство Align по умолчанию равно alTop. Установите Align=alNone, чтобы панели можно было придать любую форму и расположить ее в любом месте.
В свойство Hint впишите инструментальная панель, в свойство Images внесите ImageList1, в свойства ShowCaptions и ShowHint – true. Щелкните правой кнопкой мыши на компоненте ToolBar1 и из всплывшего меню выберите команду Новая кнопка. В свойство Action кнопки внесите AMatr, а в свойство ShowHint – true. Повторите эту команду еще для двух кнопок, внося в свойство Action соответственно ATrans, ADel, в свойство ShowHint – true. Отметим, что свойства и обработчики событий объекта действия будут перенесены на соответствующие кнопки инструментальной панели.
Со страницы Дополнительно перенесите на форму три кнопки BitBtn. Кнопка BitBtn отличается от кнопки Button возможностью отобразить на ее поверхности изображение. В свойство Action кнопок внесите значения AMatr, ATrans, ADel соответственно, в свойство ShowHint – true. При этом свойства и обработчики событий объекта действия будут перенесены на соответствующие кнопки BitBtn.
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь в работоспособности приложения.

Примечание. Связь между свойствами объекта действия и свойствами управляющих элементов выполняется классом TActionLink и его наследниками. Передаются такие свойства действия, как Caption, Checked, HelpContext, Hint, ImageIndex, ShortCut, Visible. Однако в любом компоненте разработчик может изменить переданное в него свойство. Обратной связи TActionLink с компонентами нет, так что эти изменения будут локальными и не отразятся на других компонентах. Если же требуется изменить свойства всех связанных с одним действием компонентов, надо изменять свойство объекта действия. Это облегчает программное управление компонентами, связанными с одним и тем же действием.

Перейдем к рассмотрению другой команды окна Редактора Действий–Новое стандартное действие. Она открывает окно Стандартные Классы Действия, в котором можно выбрать из списка необходимое стандартное действие (или сразу несколько действий). Действия в списке сгруппированы по категориям. Список включает 26 действий. Стандартные действия охватывают операции редактирования текстов (категория Правка), форматирования текстов (категория Формат), поиска в текстах (категория Поиск), работу со справками (категория Помощь), с файлами (категория Файл) и другие. Выполним эксперимент, который позволит понять отличие стандартных действий от нестандартных.

Создайте новое приложение и командой Сохранить все сохраните файл модуля и проект. В свойство Caption формы впишите Тест стандартных действий.
Перенесите на форму компоненты ActionList1 и ImageList1. Сошлитесь в свойстве Images компонента ActionList1 на список ImageList1. Перейдите в окно Редактора Действий, вызовите окно Стандартные Классы Действия, выделите в нем все действия категории Правка и действие TFileExit категории Файл, нажмите кнопку OK.
В окне Редактора Действий увидите, что в список автоматически занеслись выбранные действия, причем большинство из них имеют пиктограммы, хотя в компоненте ImageList1 пиктограмм не было. Выделите ImageList1 и убедитесь, что в нем находятся пиктограммы выбранных действий.
Обратите внимание на то, что каждый вид стандартного действия имеет класс, производный от TAction. Названия этих классов даны в окне Стандартные Классы Действия. В Инспекторе Объектов также видно, что в стандартные действия заложены общепринятые значения множества свойств: надписи (Caption), подсказки (Hint), «горячие» клавиши (ShortCut). Но самое главное отличие стандартных объектов действий от нестандартных заключается в том, что для стандартных действий не надо писать обработчики событий OnExecute. Все операции, необходимые для выполнения стандартных действий, уже заложены в их объекты. Они не только не требуют обработчиков событий OnExecute, но могут реализовываться через «горячие» клавиши даже без инициаторов действий – разделов меню, кнопок и т.п. Чтобы убедиться в этом, продолжите эксперимент. Добавьте на форму инструментальную панель ToolBar1 и свяжите ее свойством Images со списком изображений ImageList1. Установите в ToolBar1 свойство ShowHint в true.
Создайте на инструментальной панели быстрые клавиши для всех действий, имеющих пиктограммы. В каждой новой кнопке ссылайтесь в свойстве Action на соответствующее действие.
Добавьте на форму окна редактирования RichEdit1 и Edit1.
После ввода команды Сохранить все запустите приложение на выполнение. Убедитесь, что вначале доступна только кнопка выхода. Причины такого состояния: выделенного текста нет, поэтому нечего копировать или вырезать; редактирования не было, поэтому нечего отменять. Если выделить текст в одном из окон редактирования, то, пока это окно находится в фокусе, будут доступны кнопки Копировать (Copy), Вырезать (Cut), Удалить (Delete). Проведите какую-либо операцию редактирования и убедитесь, что стала доступной кнопка Отменить (Undo). Но если переместить курсор в другое окно редактирования, где нет выделения и не было редактирования, то эти кнопки станут недоступны.
Чтобы убедиться в том, что доступ к стандартным действиям через «горячие» клавиши сохраняется, даже если в приложении отсутствуют их инициаторы или они недоступны, задайте для действия FileExit1, обеспечивающего выход из приложения, «горячие» клавиши Ctrl+E. Сделайте невидимой инструментальную панель ToolBar1 (Visible = false) и снова запустите приложение. Ни один управляющий элемент не будет доступен. Но все «горячие» клавиши будут действовать. Нажмите Ctrl+E, и приложение закроется.

Диспетчеризация действий на основе компонентов ActionManager, ActionMainMenuBar, ActionToolBar, CustomizeDlg

Диспетчер действий ActionManager не только создает и хранит набор действий, как и ActionList, но и управляет полосами действий – визуальными компонентами, на которых располагаются элементы пользовательского интерфейса. К таким компонентам относятся ActionMainMenuBar – полоса главного меню, и ActionToolBar – инструментальная панель. Во время проектирования эти компоненты могут вводиться в приложение непосредственно из палитры компонентов, или создаваться простым перетаскиванием на них необходимых действий из окна Редактора Действий ActionManager. Компонент ActionManager запоминает информацию о составе набора действий и конфигурации полос действий в текстовом или двоичном файле на диске.
Пользователь может настраивать меню и инструментальные панели во время выполнения. Настройка во время выполнения может осуществляться или вызовом соответствующего стандартного действия, или обращением к специальному компоненту CustomizeDlg, перенесенному на форму. В обоих случаях используется упрощенный, по сравнению с временем проектирования, вариант диалогового окна Редактора Действий. Это окно позволяет пользователю настраивать меню и инструментальные панели, перенося на них новые действия, убирая прежние, делая те или иные инструментальные панели видимыми или невидимыми. Компонент ActionManager обеспечивает сохранение настроек в файле на диске и автоматическую загрузку их в следующем сеансе работы с приложением.
Рассмотрим основной компонент всей этой системы – ActionManager. Перенесите на форму компонент ImageList1 (страница Win32) и компонент ActionManager1 (страница Дополнительно). Свойство Images компонента ActionManager указывает на компонент ImageList, содержащий пиктограммы, используемые для обозначения действий. Сошлитесь в свойстве Images компонента ActionManager1 на список ImageList1.
Свойство State определяет реакцию на действия пользователя. Значение asNormal соответствует нормальной рабочей реакции: при щелчке пользователя на доступных интерфейсных компонентах действий выполняются соответствующие действия. Два других значения – asSuspended и asSuspendedEnabled отключают возможность выполнения действий. Щелчок пользователя не приводит ни к каким результатам. Эти значения State используются при настройке пользователем меню и инструментальных панелей. Различие этих двух значений в том, что первое не изменяет свойства Enabled действий, а второе переводит во всех действиях Enabled в true. Таким образом, полосы действий перейдут в состояние настройки после выполнения оператора
ActionManager1->State = asSuspended;
Теперь нажатие пользователем кнопки инструментальной панели или раздела меню не будет вызывать выполнение соответствующего действия и может быть выполнено программой как-то иначе.
Свойство FileName задает имя файла, в котором ActionManager хранит информацию о составе связанных с ним полос действий. В начале выполнения приложения ActionManager читает информацию из этого файла и в соответствии с ней формирует полосы действий. А при любых изменениях настройки в процессе выполнения ActionManager записывает в этот файл проведенные изменения. Так что при следующем сеансе работы состав полос действий будет таким, каким сделал его пользователь в предыдущем сеансе.

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

Если в процессе проектирования впервые задается значение FileName, надо просто записать в этом свойстве имя файла с путем к нему. При отсутствии пути файл будет создан в том каталоге, в котором расположен проект. Если же нужно задать в качестве значения FileName имя уже существующего файла, то можно воспользоваться для его выбора кнопкой с многоточием около свойства FileName в окне Инспектора Объектов.
Перейдем к рассмотрению основного инструмента проектирования – Редактора Действий компонента ActionManager1. Двойным щелчком на компоненте вызывается окно Редактора Действий (Редактирование Form1->ActionManager1) на странице Действия с пустыми панелями Категории: и Действия:. Щелкнем правой кнопкой мыши и, выполнив соответствующие команды, введем стандартные действия категорий Правка и Файл. После этого в панели Действия: появятся имена объектов этих действий, а в панели Категории: – их категории. Если выделить какое-то действие, то в Инспекторе Объектов будут видны свойства, которые можно изменить: Caption, Hint, ShortCut и другие. Вторая часть строки свойства Hint отображается в окне Редактора Действий в панели Описание. Можно изменить также категории действий – их свойства Category. Если в компоненте ActionList понятия категорий лишены определенного смысла, то здесь – при создании меню названия категорий станут надписями головных разделов меню.
Перейдите на страницу Панели окна Редактора Действий. Сейчас оно пусто. Со страницы Дополнительно перенесите на форму компоненты ActionMainMenuBar1 (полоса главного меню) и ActionToolBar1 (панель). Снова перейдите в окно Редактора Действий. Страница Панели остается пустой. Перейдите на страницу Действия и перетащите с нее мышью в полосу меню категории Правка и Файл, а на панель – категорию Правка. Действия можно перетаскивать и поодиночке. Но если в меню перетаскивается категория, то она становится головным разделом меню. Снова перейдите на страницу Панели и убедитесь, что в ее окне появился список из управляемых диспетчером ActionManager1 полосы главного меню (компонент ActionMainMenuBar1) и инструментальной панели (компонент ActionToolBar1). Кнопкой Новыйдобавьте вторую инструментальную панель и перетащите на нее категорию Файл. Следовательно, компонент ActionToolBar можно добавлять на форму как переносом из палитры компонентов, так и с помощью Редактора Действий. Кнопкой Удалить на странице Панели можно удалить выделенную панель или меню из списка и вообще из приложения.
Приложение, содержащее меню и две инструментальные панели, запустите на выполнение. Убедитесь в работоспособности приложения.
Вызовите окно Редактора Действий и перейдите на страницу Панели. Индикаторы (пометки) около названий полос действий (меню, инструментальных панелей) управляют их видимостью. Если выключить индикатор, то во время выполнения соответствующая полоса будет невидимой. Убедитесь в этом.
Выделите одну из полос действий на странице Панели окна Редактора Действий и просмотрите в Инспекторе Объектов ее свойства. В свойствах Caption (надпись) измените значения соответственно на Главное меню, Инструментальная панель 1, Инструментальная панель 2. Обратите внимание на свойство AllowHiding. Оно разрешает (при значении true) или запрещает делать полосу невидимой в процессе выполнения. Если задать AllowHiding=false, то в Редакторе Действий такая полоса отображается серой (полоса Главное меню). Для такой полосы ни во время проектирования, ни во время выполнения невозможно переключить индикатор видимости. Полоса будет видна всегда.
Выпадающий список Опции надписи управляет способом отображения надписей действий в выделенной инструментальной панели в окне Панели:. Убедитесь, что значение None соответствует отсутствию надписей. Отображаются только пиктограммы. Но если пиктограммы нет, то надпись все равно отображается. Значение позволяет для каждого действия задать видимость надписи индивидуально. Значение All обеспечивает отображение надписей для всех действий. Индикатор Применить опции надписи ко всем панелям распространяет выбранную опцию на все инструментальные панели.
Если в списке Опции надписи задано значение Selective, то, выделив на панели то или иное действие (например, Удалить), можно в Инспекторе Объектов установить значение свойства ShowCaption, определяющего видимость надписи (true или false). Но если для действия пиктограммы нет (например, Выбрать все), то ShowCaption установить в false невозможно.
Если в списке Опции надписи задано значение None или All, то установка значений свойств ShowCaption отдельных действий ни на что не влияет. Все определяется заданным свойством инструментальной панели.
Страница Опции Редактора Действий позволяет задать некоторые опции отображения. Индикатор Показ меню последних используемых элементов делает меню перестраиваемым, причем первыми располагаются разделы, которые недавно использовались. Если этот индикатор включен, то диспетчер действий при каждом очередном выполнении приложения проверяет, давно ли пользователь обращался к тому или иному разделу меню. Если на протяжении нескольких сеансов работы какие-то разделы не использовались, они делаются невидимыми. Их можно увидеть, только развернув меню полностью. Таким образом, меню перестраивается от сеанса к сеансу, делая видимыми и легко доступными те разделы, к которым пользователь обращается чаще всего.
Кнопка Сброс данных восстанавливает первоначальные установки полос действий. Индикатор Большие значки приводит к отображению в полосах действий больших пиктограмм. Индикатор Показ подсказок панели управляет появлением всплывающих ярлычков у элементов инструментальных панелей. А индикатор Показ подсказок ярлыков определяет включение в тексты этих ярлычков обозначений «горячих» клавиш. Выпадающий список Анимация меню определяет анимацию при отображении меню.

Задание. Построить приложение, содержащее окно редактирования Memo, полосу главного меню с разделами Файл, Правка и Сервис, а также две инструментальные панели, одна из которых должна дублировать основные разделы меню Правка, а другая – основные разделы меню Файл. Меню Файл должно содержать разделы Открыть, Сохранить, Сохранить как, Выход. Меню Правка должно содержать разделы Вырезать, Копировать, Вставить, Выделить все, Отменить, Удалить. Меню Сервис должно иметь один раздел Настройка, позволяющий пользователю настроить полосу главного меню и инструментальные панели.

Начните новое приложение, в свойство Caption формы впишите Тестовое приложение, перенесите на форму компоненты Memo1, ImageList1, ActionMainMenuBar1 и ActionManager1. В компоненте ActionManager1 установите свойство Images равным ImageList1, связав тем самым диспетчер действий со списком изображений.
Сделайте двойной щелчок на компоненте ActionManager1. В открывшемся окне Редактора Действий перейдите на страницу Панели и дважды нажмите на кнопку Новый, чтобы добавить в приложение две инструментальные панели.
Перейдите в окне Редактора Действий на страницу Действия и щелкните правой кнопкой мыши. Выберите в контекстном меню команду Новое стандартное действие. В открывшемся списке стандартных действий выделите все действия категории Правка (для выделения группы действий, расположенных подряд, надо держать нажатой клавишу Shift), действия TFileOpen, TFileSaveAs и TFileExit категории Файл (для выделения группы действий, не расположенных подряд, надо держать нажатой клавишу Ctrl) и действие TCustomizeActionBars категории Tools. Нажмите кнопку OK.
В окне Редактора Действий на странице Действия появятся введенные действия. Чтобы ввести еще одно, нестандартное действие – Сохранить, перейдите в левой панели окна в категорию Файл и нажмите кнопку New Action (Ins) или выберите аналогичный раздел из контекстного меню. В правой панели окна появится новое действие с именем Action2.
Выделите действие Action2. В Инспекторе Объектов задайте имя (Name) FileSave1, надпись (Caption) &Сохранить, текст подсказки (Hint) Сохранить|Сохранить активный документ в файле и «горячие» клавиши (ShortCut) F2. Задайте этому действию пиктограмму, добавив в ImageList1 изображение filesave.bmp из набора изображений Buttons.
Выделите действие Выход. Задайте текст подсказки (Hint) Выход|Завершение работы.
Выделите действие Customize категории Tools. Задайте надпись (Caption) Настройка, свойство Category – Сервис. Задайте этому действию пиктограмму, добавив в ImageList1 изображение many2mny.bmp из набора изображений Buttons.
С помощью кнопок со стрелками установите для каждой категории нужную последовательность действий. Например, для категории Файл действие Сохранить поместите между действиями Открыть... и Сохранить как .
Перенесите действия на полосы действий следующим образом. Выделите в левой панели окна Редактора Действий на странице Действия категорию Файл и перетащите ее мышью на полосу главного меню ActionMainMenuBar1. То же проделайте для категорий Правка и Сервис. В главном меню появятся выпадающие меню Файл, Правка и Сервис со всеми разделами данных категорий. Аналогичным образом можно формировать и инструментальные панели ActionToolBar. Если перетащить на панель категорию, на нее перенесутся все действия. Но можно перетаскивать и действия по одному. Если окажется, что на панель или в меню перетащено лишнее действие, его можно удалить, просто перетащив мышью. Но это можно сделать и при закрытом Редакторе Действий. Итак, на первую инструментальную панель перетащите категорию Правка, затем – Сервис, а на вторую – по одному – действия категории Файл.
Задайте в свойстве FileName компонента ActionManager1 имя файла, в котором будет сохраняться информация о конфигурации меню и панелей. Со страницы Стандарт перенесите на форму индикатор с флажком CheckBox1 и задайте ему надпись Индикатор. После этого выполните приложение. Вопреки ожиданию, может оказаться сразу доступной кнопка Вставить. Это означает, что в данный момент в буфере обмена оказался текст. Нажмите клавишу Print Screen, занеся в буфер обмена изображение экрана, и кнопка Вставить станет недоступной. Убедитесь в правильном функционировании приложения. Команды редактирования в меню и инструментальных панелях доступны, если в фокусе находится окно редактирования. (Для перевода фокуса щелкните на компоненте CheckBox1.) При этом команды Вырезать, Копировать, Удалить доступны, только если в окне выделен какой-то текст. Команда Вставить доступна, только если в буфере обмена в данный момент находится текст. Команда Отменить доступна, только если была проведена какая-то операция редактирования.
Выполните команду Настройка. Откроется диалоговое окно Customize, подобное окну Редактора Действий, с такими же страницами – Actions (Действия), ToolBars (Панели), Options(Опции). Отличия: на странице Actions отсутствуют кнопки создания и удаления действий и нет контекстного меню. На странице ToolBars отсутствуют кнопки Новый, Удалить, а вместо них имеется кнопка Reset, восстанавливающая начальное состояние панелей и меню. Следовательно, пользователь имеет возможность во время выполнения произвести настройку меню и панелей, сделать какие-то инструментальные панели невидимыми и т.п. Поэкспериментируйте с настройками на страницах ToolBars и Options.
В предыдущем пункте вызов диалогового окна настройки осуществлялся стандартным действием TCustomizeActionBars. То же самое можно сделать с помощью компонента CustomizeDlg. Добавьте на форму компонент CustomizeDlg1 (страница Дополнительно) и свяжите его свойством ActionManager с диспетчером действий ActionManager1. А в обработчик щелчка на какой-нибудь кнопке, но, поскольку ее нет, то в обработчик двойного щелчка на форме (событие OnDblClick) вставьте оператор
CustomizeDlg1 ->Show();
Этот оператор вызовет диалог настройки и позволит реализовать все его возможности. Убедитесь в этом, запустив приложение на выполнение.
Перейдем к реализации команд работы с файлами. Пока команда Сохранить вообще недоступна, так как не написан обработчик соответствующего события. А команды Открыть и Сохранить как вызывают соответствующие стандартные диалоговые окна, но результат выбора файла в этих окнах нигде не используется. Да и сами диалоги выглядят неполноценными, так как в них отсутствуют шаблоны типов файлов. Откройте двойным щелчком на ActionManager1 Редактор Действий и на странице Действия выделите действие Открыть. В Инспекторе Объектов откроются свойства этого действия, и среди них – свойство Dialog. Это объект того диалога, который вызывается данным действием. Раскройте щелчком на Dialog его свойства и задайте расширение по умолчанию (DefaultExt) txt и фильтры (Filter) в редакторе фильтра следующим образом
Имя фильтра Фильтр
текстовые(*.txt, *.doc) *.txt; *.doc
все файлы *.*
и нажмите кнопку OK. Проведите аналогичную операцию с диалогом, вызываемым действием Сохранить как.
Перейдем к составлению операторов, использующих результаты вызова этих диалогов. Введем глобальную переменную
AnsiString FileName;
для имени файла, в котором надо сохранять текст окна Memo1. При выполнении действий, связанных с вызовом диалога, наступают события BeforeExecute (перед вызовом диалога), OnAccept (если пользователь в диалоге произвел выбор и нажал OK), и OnCancel (если пользователь в диалоге не произвел выбор и нажал кнопку Отказ или клавишу Esc). Для команды Открыть надо написать только обработчик события OnAccept:
FileName=FileOpen1->Dialog->FileName;
Memo1->Lines->LoadFromFile(FileName);
Первый оператор этого обработчика запоминает имя выбранного пользователем файла в переменной FileName. Это имя потребуется, если в дальнейшем пользователь захочет сохранить отредактированный текст в файле. Второй оператор загружает файл в компонент Memo1. Чтобы написать этот обработчик, надо в окне Редактора Действий выбрать действие Открыть и затем обычным образом, щелкнув в Инспекторе Объектов рядом с соответствующим событием, занести в текст указанные операторы.
Для команды Сохранить как надо написать обработчики событий BeforeExecute и OnAccept. В первом из них надо занести в диалог имя файла FileName как предлагаемое по умолчанию:
FileSaveAs1->Dialog->FileName=FileName;
В обработчике события OnAccept надо запомнить имя выбранного пользователем файла и сохранить в этом файле текст из окна Memo1:
FileName=FileSaveAs1->Dialog->FileName;
Memo1->Lines->SaveToFile(FileName);
Действие Сохранить – нестандартное, и поэтому его реализация записывается в обработчике события OnExecute. Но прежде надо проверить, запомнено ли в переменной FileName имя файла. Если запомнено, то текст окна Memo1 надо сохранить в этом файле. А если переменная FileName пуста, значит еще неизвестно, в каком файле надо сохранять текст. В этом случае надо вызвать действие Сохранить как, чтобы пользователь мог указать имя файла. Для вызова любого действия используется метод Execute. Так что требуемую логику можно осуществить оператором:
if(FileName!="")
Memo1->Lines->SaveToFile(FileName);
else FileSaveAs1->Execute();
После команды Сохранить все запустите приложение на выполнение. Сделайте вывод о возможностях приложения. Выйдите из среды Builder.


Контрольные вопросы

Как составляется список действий пользователя для работы с приложением?
Какие действия относятся к стандартным и какие – к нестандартным?
Какими компонентами реализуется список действий пользователя?
Что представляют собой полосы действий при использовании компонента ActionList?
Что представляют собой полосы действий при использовании компонента ActionManager?
Какие требования предъявляются к интерфейсным компонентам действий?
Как загружаются изображения в ImageList для стандартных и нестандартных действий?
Перечислите шаги последовательности формирования списка действий и проектирования меню и инструментальных панелей в случае использования диспетчера ActionList.
Перечислите шаги последовательности формирования списка действий и проектирования меню и инструментальных панелей в случае использования диспетчера ActionManager.
Расскажите о возможностях, предоставляемых пользователю Редактором Действий компонента ActionList. Каков порядок работы с Редактором Действий в случае стандартных и в случае нестандартных действий?
Как формируется список действий в компоненте ActionList? Какие свойства имеет объект действия? Как присвоить им значения?
Какие события имеет объект действия компонента ActionList? Что могут содержать обработчики этих событий?
Как перенести свойства и обработчики событий объектов действия ActionList на разделы меню, кнопки инструментальных панелей и на кнопки, расположенные на форме?
Какие дополнительные возможности, по сравнению с диспетчером ActionList, предоставляет пользователю диспетчер ActionManager?
Какие действия с полосами действий могут выполняться во время проектирования и какие – во время выполнения?
Расскажите о свойствах компонента ActionManager. Как и когда следует задавать значение свойства FileName?
Расскажите о работе с Редактором Действий компонента ActionList.

Библиографический список
Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – С( 259–279.
Шамис В.А. Borland C++ Builder 6. Для профессионалов / В.А. Шамис.
· СПб.: Питер, 2003.
· 798 с. – С. 310
·311, 328–333.
Архангельский А(Я( Компоненты C++Builder. Справочное и методическое пособие. – М(: ООО «Бином-Пресс», 2013( – 960 с(: ил.


ЗАНЯТИЕ 10

Динамически присоединяемые библиотеки DLL

Назначение DLL

Использование динамически присоединяемой библиотеки DLL является одной из возможностей повторного использования готового программного обеспечения. DLL – это специального вида исполняемый файл с расширением .dll, используемый для хранения функций и ресурсов отдельно от исполняемого файла. Обычно, когда программист пишет программу и создает функции, ресурсы и т.п., все они компонуются в исполняемый файл. Это называется статическим связыванием. При использовании DLL вызываемые из нее функции используются модулем в процессе его выполнения. DLL делает часто используемые функции доступными сразу для многих приложений одновременно, хотя работа ведется только с одной ее копией на диске и в памяти. Обычно DLL не загружается в память, пока это не станет необходимым, но, будучи однажды загружена, она делает свои функции и ресурсы доступными для любой программы. Нетрудно сделать вывод, что использование DLL позволяет экономить как оперативную память, так и затраты пространства на дисках.

Статическое и динамическое связывание DLL с приложением

Библиотеки DLL могут связываться с приложением двумя путями: статическим связыванием или динамическим связыванием.
При статическом связывании DLL загружается сразу, как только начинает выполняться приложение, которое будет ее использовать. Это наиболее простой способ использования DLL. Вызов функций в приложении при этом почти не отличается от вызова любых других функций. Здесь время загрузки увеличивается и нельзя выполнять приложение, если нет соответствующего файла DLL. Кроме того, без DLL приложение не может выполняться и в сокращенном, также рабочем варианте. Недостатком статического связывания является и то, что DLL занимает память все время, в течение которого выполняется приложение, независимо от того, вызываются ли в данном сеансе работы с приложением какие-то функции библиотеки, или нет.
Статическое связывание подразумевает, что для DLL создан специальный файл описаний импортируемых функций (import library file). Этот файл имеет расширение .lib и то же имя, что и соответствующая DLL, и должен быть связан с приложением на этапе компиляции.
При динамическом связывании DLL загружается только в тот момент, когда необходимо выполнить какую-то хранящуюся в ней функцию. Затем DLL можно выгрузить из памяти. Но при более эффективном использовании памяти вызов функций DLL существенно усложняется, и время вызова увеличивается.
Для вызова библиотечной функции надо сначала загрузить библиотеку функцией LoadLibrary API Windows. Затем с помощью функции GetProcAddress надо получить указатель на нужную функцию библиотеки. Только после этого можно выполнять функцию. А затем с помощью функции FreeLibrary надо выгрузить библиотеку из памяти.
Предположим, создана библиотека mydll.dll, содержащая некоторую функцию char* MyFunction(char*). Тогда для загрузки DLL надо выполнить оператор вида:

//загрузка DLL
HINSTANSE dllInstanse = LoadLibrary(“mydll.dll”);

Получить указатель на импортируемую функцию можно следующим кодом:

//получение указателя на функцию
typedef char* (__import FType(char*));
FType * MyFunc;
MyFunc = (FType*) GetProcAddress(dllInstanse, “_MyFunction”);

Объявление typedef вводит пользовательский тип (тип-функция) с произвольным именем FType. Введенный тип используется для задания типа указателя на функцию MyFunc. Для получения значения этого указателя используется функция API Windows GetProcAddress. Она принимает в качестве параметров указатель на загруженный модуль DLL и имя функции, а возвращает указатель на функцию. Этот указатель приводится к типу указателя на используемую функцию библиотеки.
Вызов функции осуществляется с помощью указателя на неё:

//вызов функции
char* S = MyFunc(“Привет!”);

Когда работа с DLL завершена, её можно выгрузить из памяти оператором вида:

//выгрузка DLL
FreeLibrary(dllInstanse);

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


Создание DLL

Создание своей DLL начинается с выполнения команды Файл/Новый/Другое и выбором в окне Новые элементы на странице Новый пиктограммы Мастер DLL. На форме появляется окно задания опций создания DLL (рис.10.1).


Рис.10.1 - окно задания опций создания DLL

В окне выбран язык DLL – C++; индикатор Исп.VCL позволит создать DLL, которая может содержать компоненты библиотеки VCL. При этом в модуль включится файл vcl.h и установятся опции компоновки, обеспечивающие совместимость с объектами VCL. Индикатор VC++ Style DLL обеспечит создание DLL в стиле Microsoft Visual C++. После установки опций согласно рис.10.1 и щелчка на OK попадают в окно Редактора Кода с заготовкой модуля DLL. В тексте комментария заготовки рекомендуется при передаче строк в функцию и из функции использовать тип char*, а не, например, AnsiString. Приведем пример такой функции для включения в DLL.

char * Code(char * s, char Key)
{
for (int i = 0; ; i++)
{
if (s[i] == ’\0’) break;
s[i] = s[i] ^ Key;
}
return s;
}

Функция воспринимает строку типа char* и ключ типа char и возвращает зашифрованную этим ключом строку. Кодировка такова: каждый символ строки складывается с ключом по операции исключающее ИЛИ. При дешифровании достаточно с закодированной строкой выполнить ту же операцию с тем же ключом. Далее имя функции заменено на Code_Dec.
Текст комментария нужно удалить.
В заготовке модуля DLL строка #include подключает заголовочный файл vcl.h. Он будет нужен, если при создании DLL будут использоваться какие-то классы, формы, функции, связанные с библиотекой визуальных компонентов. Иначе – эту строку можно удалить из текста DLL.
В конце текста заготовки модуля DLL используется функция DllEntryPoint, необходимая для загрузки и выгрузки библиотеки. Она создает дескриптор hinst создаваемой DLL. Он требуется при выполнении некоторых функций, например, LoadIcon, LoadCursor и др. В этих случаях можно использовать параметр hinst для создания соответствующей глобальной переменной.
Перед описанием функции DllEntryPoint необходимо вставить директиву включения головного файла #include “UMyDLL.h”. Файла UMyDLL.h пока нет, - он будет создан ниже.
После выполнения последовательности очевидных действий и сохранения модуля под именем UMyDLL, а проекта – под именем MyDLL, получим файл реализации с именем UMyDLL.cpp (рис.10.2).


Рис.10.2 – файл реализации UMyDLL.cpp библиотеки

Теперь создадим заголовочный файл UMyDLL.h библиотеки. Выполним команду Файл/Новый/Другое и выберем в окне Новые элементы на странице Новый пиктограмму Файл Заголовка. Сохраним файл под именем UMyDLL.h. В появившееся окно Редактора Кода загрузим текстовый файл – запишем в файл следующий код (рис.10.3):


Рис.10.3 – заголовочный файл UMyDLL.h библиотеки

Идентификаторы _UMYDLL_H и DLL_EI могут быть любыми.
Логика работы первого фрагмента в заголовочном файле следующая: если не определен идентификатор _UMYDLL_H (ifndef _UMYDLL_H), то он определяется, т.е. замещается пустой строкой (define _UMYDLL_H ). Этот фрагмент не позволяет многократно включать заголовочный файл в файлы реализации библиотеки или приложения. Логика работы второго фрагмента в заголовочном файле следующая: если определен идентификатор __DLL__ (ifdef __DLL__), то идентификатор DLL_EI раскрывается как __declspec(dllexport); если же идентификатор __DLL__ не определен (else), то идентификатор DLL_EI раскрывается как __declspec(dllimport). C++Builder автоматически определяет __DLL__ в случае, если создается проект DLL, и не определяет этот идентификатор при создании объекта приложения. Таким образом, в зависимости от того, включается ли заголовочный файл в библиотеку или в приложение, он будет выглядеть по-разному. При компиляции библиотеки строка, определяющая функцию Code_Dec, после раскрытия макроса будет восприниматься как

extern "C" char * __declspec(dllexport) Code_Dec (char *s, char Key);

Здесь конструкция __declspec(dllexport) означает, что функция может экспортироваться из библиотеки, т.е. может вызываться внешними приложениями. Подобным же образом должны быть перечислены все функции DLL, предназначенные для прямого использования в приложениях. Помимо таких функций в DLL могут быть вспомогательные функции-утилиты, предназначенные только для использования другими функциями. Подобные утилиты не должны определяться как экспортируемые.
Когда тот же заголовочный файл включается в приложение, то эта же строка после раскрытия макроса имеет другой вид:

extern "C" char * __declspec(dllimport) Code_Dec (char *s, char Key);

Здесь конструкция __declspec(dllimport) означает, что функция импортируется, т.е. вносится в модуль приложения из DLL. Таким образом, один и тот же заголовочный файл может использоваться и при создании DLL, и в приложениях, обращающихся к данной DLL.
Продолжим создание DLL.
Выполним команду Проект/Опции и в окне опций проекта на странице Компоновщик убедимся, что включен индикатор опции Создать библиотеку импорта. Эта опция обеспечит при создании DLL автоматическую генерацию файла .lib, необходимого для статического присоединения DLL к проектам. Индикатор опции Создать библиотеку импорта будет доступен только при наличии проекта MyDLL, файлов UMyDLL.cpp и UMyDLL.h.
Выполним команду Проект/Создать MyDLL. В результате будут созданы файлы MyDLL.dll и MyDLL.lib. Убедитесь в этом.


Рис.10.4 – сообщение об ошибке

Попробуем протестировать создаваемую DLL и выполним команду Запуск/Запустить (F9). Получим сообщение об ошибке (рис.10.4), которое означает, что сначала с помощью команды Запуск/Параметры нужно определить хост – тестирующее приложение.


Примеры статического и динамического связывания DLL

Продолжим работу с DLL и напишем тестирующее приложение. Чтобы более гибко переключаться между отлаживаемой библиотекой и кодом теста, объединим с помощью Менеджера проекта модуль DLL и тестирующий проект в одну группу. Сначала рассмотрим реализацию статического связывания.
Выполним команду Вид/Менеджер проекта. Откроется окно Менеджера проекта с одной вершиной MyDLL.dll. Развернем её (рис.10.5) и снова свернем.


Рис.10.5 – окно Менеджера проекта с загруженной DLL


Рис.10.6 – в окне Менеджера проекта появилась новая вершина

В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма. Выполним команду Вид/Менеджер проекта и увидим, что в окне Менеджера проекта появилась вершина Project1.exe, соответствующая создаваемому тестовому приложению (рис.10.6).
Выполним команду Файл/Сохранить проект как и сохраним модуль тестового приложения под именем UTestDLL, а проект – под именем PTestDLL.


Рис.10.7 – окно Менеджера проекта с загруженной DLL и тестом

Введем команду Файл/Сохранить все и сохраним проект под именем TESTDLL. Снова выполним команду Вид/Менеджер проекта и в окне Менеджера проекта увидим загруженную DLL и вершину PTestDLL.exe, соответствующую создаваемому тестовому приложению (рис.10.7).
Разместим на форме окно редактирования Edit1 и кнопку Button1 (рис.10.8).
а) б)

Рис.10.8 – тестовое приложение DLL: исходный текст в окне (а)
и результат кодировки (б)

В модуле приложения в обработчик щелчка на кнопке поместим оператор:
Edit1->Text = Code_Dec(Edit1->Text.c_str(),’A’);
Он берет текст, занесенный пользователем в окно редактирования Edit1, кодирует его с помощью функции Code_Dec и возвращает закодированную строку в Edit1.
Включим в модуль приложения после директивы препроцессора #pragma hdrstop директиву, подключающую заголовочный файл библиотеки:
#include “UMyDLL.h”

Приведем заголовочный файл модуля UTestDLL.h:
//---------------------------------------------------------------------------

#ifndef UTestDLLH
#define UTestDLLH
//---------------------------------------------------------------------------
#include
#include
#include
#include
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TButton *Button1;
void __fastcall Button1Click(Tobject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

и файл реализации модуля UTestDLL.cpp:

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

#include
#pragma hdrstop
#include “UMyDLL.h”
#include “UTestDLL.h”
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource “*.dfm”
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(Tcomponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(Tobject *Sender)
{
Edit1->Text = Code_Dec(Edit1->Text.c_str(),’A’);
}
//---------------------------------------------------------------------------

Осталось подключить к тестирующему приложению файл .lib, обеспечивающий статическое связывание библиотеки. Для этого сначала нужно активизировать приложение в окне Менеджера проекта, выделив вершину проекта PTestDLL.exe и нажав на кнопку Активизировать (рис.10.9).


Рис.10.9 – подготовка к активизированию приложения

Введём команду Проект/Добавить к проекту . Во всплывшем диалоговом окне выберем шаблон файлов «Файл библиотеки (*.lib)» и выберем файл MyDLL.lib. Файл библиотеки окажется включенным в тестирующее приложение (рис.10.10). Это обеспечит при запуске приложения статическое связывание библиотеки.
Теперь можно сохранить проект, откомпилировать и выполнить его. Результаты работы приложения показаны на рис.10.8.


Рис.10.10 – файл библиотеки включен в тестирующее приложение

Перейдем к рассмотрению варианта динамического связывания. В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма.
Выполним команду Файл/Сохранить проект как и сохраним (в том же каталоге, где находится DLL) модуль под именем UTestDLL2, а проект – под именем PTestDLL2. С помощью окна Менеджера проекта убедимся, что второе тестирующее приложение добавлено (рис.10.11).


Рис.10.11 - окно Менеджера проекта с загруженной DLL,
первым тестом PTestDLL.exe и вторым тестом PTestDLL2.exe


Рис.10.12 – тестовое приложение динамического связывания DLL

Разместим на форме окно редактирования Edit1 и три кнопки Button1,2,3 (рис.10.12). Кнопка Загрузить (имя BLoad) будет обеспечивать загрузку DLL, кнопка Выгрузить (имя BFree) будет выгружать библиотеку из памяти, а кнопка Кодировать/Декодировать (имя Button1) аналогична той, которая была в первом приложении.
В заголовочном файле второго приложения в описание класса формы нужно внести объявления (см. ниже).

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

#ifndef UTestDLL2H
#define UTestDLL2H
//---------------------------------------------------------------------------
#include
#include
#include
#include
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TButton *Button1;
TButton *BLoad;
TButton *BFree;
void __fastcall BLoadClick(TObject *Sender);
void __fastcall BFreeClick(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
// Объявление указателя на DLL
HINSTANCE dllInstance;
// Объявление указателя на функцию
typedef char* (__import FType(char *, char));
FType * C_D;
public: // User declarations
__fastcall TForm2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif

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

Переменная dllInstance будет содержать указатель на загруженный модуль DLL. Вводимый тип FType соответствует типу функции Code_Dec, которая будет вызвана из библиотеки. А переменная C_D будет содержать указатель на эту функцию.
Приведем файл реализации модуля формы второго приложения:

#include
#pragma hdrstop

#include "UTestDLL2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm2::BLoadClick(TObject *Sender)
{
// Загрузка DLL
dllInstance = LoadLibrary("MyDLL.dll");
if(dllInstance)
// получение указателя на функцию
C_D = (FType *)GetProcAddress(dllInstance, "_Code_Dec");
else ShowMessage(«Не удалось загрузить 'MyDLL.dll'»);
}
//---------------------------------------------------------------------------
void __fastcall TForm2::BFreeClick(TObject *Sender)
{
// выгрузка DLL
FreeLibrary(dllInstance);
C_D = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
if (C_D)
Edit1->Text = C_D(Edit1->Text.c_str(),'A');
else ShowMessage(
" Функция 'Code_Dec' из 'MyDLL.dll' недоступна");
}
//---------------------------------------------------------------------------
Обработчик BLoadClick загружает библиотеку функцией LoadLibrary. Если загрузить библиотеку не удалось, то значение dllInstance окажется равным NULL. В этом случае пользователю выдается сообщение «Не удалось загрузить 'MyDLL.dll'». А если загрузка прошла успешно, то методом GetProcAddress в переменную C_D заносится указатель на импортируемую функцию.
Обработчик Button1Click обеспечивает вызов функции с помощью указателя C_D. А если указатель равен NULL (например, в библиотеке не оказалось требуемой функции, что в нашем случае невозможно, или перед выполнением Button1Click не была загружена библиотека, что в нашем случае возможно), то пользователю выдается соответствующее сообщение об ошибке.
Обработчик BFreeClick выгружает функцией FreeLibrary из памяти модуль DLL и обнуляет указатель C_D.
Выполним данное приложение и убедимся, что все работает нормально (рис.10.12).
Проведите эксперимент, размещая исполняемый файл тестового приложения и файл библиотеки в различных каталогах.

Контрольные вопросы

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

Библиографический список

1. Архангельский А(Я( Программирование в C++Builder 6. – М(: ЗАО «Издательство БИНОМ», 2003( – 1152 с( – с( 539–547.










13PAGE 15


13PAGE 15


13PAGE 1417715








Root Entry

Приложенные файлы

  • doc 70787
    Размер файла: 7 MB Загрузок: 0

Добавить комментарий