Delphi6 - Информация книги

Информация книги

Книга подготовлена для чтения и переведена в формат CHM сайтом Дельфи и Компания. Это было сделано собственно потому, что читать такой файл намного удобнее чем выкачивать главы отдельно в сети, при этом еще попутно получая баннеры, менюшки и прочую ненужную ерунду. :)
На нашем сайте вы можете найти Русскую справку по Windows API для Delphi, которая распространяется бесплатно. Также возможно подписаться на рассылку новостей прямо на сайте или на странице Subscribe.ru.
На сайте можно получить дополнительные материалы по программированию. Например книги, советы, FAQ. Правда пока еще не очень много, но мы постараемся пополнять коллекцию матералов.
Можно зайти в раздел статьи.
Будем рады видеть вас на нашем сайте. Оставляйте свои пожелания и замечания в гостевой книге.

Приятного чтения и удачи вам в программировании." :)


Delphi6 - Оглавление

Delphi 6

Содержание

    • Введение
  • Часть I . Введение в Delphi 6

    • Глава 1. Основные отличия различных версий DELPHI
    • Глава 2. Знакомство со средой DELPHI
    • Глава 3. Основы визуального программирования
    • Глава 4. Знакомство с компонентами

  • Часть II . Язык Object Pascal
    • Глава 5. Введение в Object Pascal
    • Глава 6. Элементы языка
    • Глава 7. Типы данных
    • Глава 8. Процедуры и функции
    • Глава 9. Классы и интерфейсы
    • Глава 10. Варианты
    • Глава 11. Файлы
    • Глава 12. Модули
    • Глава 13. Динамические подключаемые библиотеки
    • Глава 14. Особенности разработки модулей-потоков
    • Глава 15. Типизированные константы и инициированные переменные
    • Глава 16.Классы общего еазначения

  • Часть III. Компоненты и программы

    • Глава 17. Общие свойства компонентов
    • Глава 18. Испрользование компонентов общего назначения
    • Глава 19. Форма
    • Глава 20. Глобальные объекты и настройка программ
    • Глава 21. Создание встроенной справочной системы
  • Часть IV. Приложения

    • Приложение 1. Среда разработчика
    • Приложение 2. Переменные, процедуры и функции модуля System
    • Приложение 3. Процедуры и функции модуля Match


Введение - ОТ АВТОРА

ОТ АВТОРА

Эта книга предназначена для тех, кто самостоятельно или под руководством опытного преподавателя пытается научиться программировать, т.е. создавать программы, работающие под управлением современных 32-разрядных графических операционных систем (ОС) Windows 95/98/NT/2000 (в дальнейшем - Windows 32).

Эволюция технических средств персональных компьютеров привела к повсеместному вытеснению старой “доброй” ОС MS-DOS значительно более мощными системами Windows, программирование для которых существенно сложнее, чем программирование для MS-DOS. Разработчики систем программирования, и прежде всего такие корпорации, как Microsoft и Borland, не замедлили выпустить соответствующие средства: уже в 1991 году, т. е. сразу после появления Windows 3.1, Borland выпускает Turbo Pascal for Windows, а в 1992 - усовершенствованную версию этой системы программирования - Borland Pascal with Objects 7.0. Эти первые специализированные инструменты требовали основательного знания Windows и были сложны в освоении. Но вот в 1993 году Microsoft выпустила первую визуальную среду программирования Visual Basic, и программирование для Windows стало даже проще, чем программирование для MS-DOS. В ответ на это Borland в 1995 году выпустила первую версию Delphi, а затем, с интервалом в 1 год, - еще 4 версии: 2, 3, 4 и 5. Наконец, в середине 2001 г. выпускается версия б, которая и положена в основу этой книги. Замечу, что материал книги можно с некоторыми (часто несущественными) ограничениями использовать и для изучения более ранних версий.

Не секрет, что лучшим языком для изучения и освоения программирования является Паскаль, а лучшей в мире системой программирования для MS-DOS - Turbo Pascal. Delphi продолжила серию Паскаль-ориентированных средств программирования и, по моему глубокому убеждению, является наиболее удобным инструментом для Windows-программирования.

Эта книга во многом использует материал книги [27], который был дополнен и изменен с учетом изменений в версиях Delphi 6 и 5. В ней рассматривается менее трети возможностей этой мощной современной системы программирования, и это не случайно. Дело в том, что версии Delphi развивались по принципу наращивания своих возможностей. Если с 1-й по 4-ю версии Delphi могла работать с базами данных только с помощью механизма BDE, то в версии б таких механизмов уже 4 - BDE, ADO, IBExpress, dbExpress. Если 8 компонентов страницы internet версии 2 давали лишь возможность использовать некоторые технологии Интернет в локальных сетях, то более 100 компонентов версии 6 обеспечивают полноценную возможность Web-программирования, т. е. создания работающих под управлением Web-сервера программ, обеспечивающих интерактивное взаимодействие с клиентом и публикацию по его требованию необходимых данных. Как бы там ни было, перед вами всего лишь “Учебный курс”, т. е. книга, в которй описываются лишь самые общие приемы программирования с помощью Delphi. Она, как уже говорилось, рассчитана, в основном, на начинающих программистов, во всяком случае ее освоение не требует специальных знаний ни в области программирования, ни в области Windows, хотя умение работать с Windows на уровне пользователя - обязательное условие (если, разумеется, вы захотите создать хотя бы одну из описываемых в книге программ).

Руководство на диске делится на 3 части.

В первой дается неформальное знакомство со средой Delphi и основными конструкциям лежащего в ее основе языка программирования Object Pascal. Изложенный в ней материал, как мне кажется, позволит вам в дальнейшем “видеть” систему целиком, не погрязая в неизбежных деталях, которым и посвящены остальные главы книги.

Вторая часть посвящена современному языку программирования Object Pascal, без детального знания которого эффективная работа с Delphi невозможна. Фактически все основные синтаксические конструкции и операторы этого языка описываются в довольно большой по объему и насыщенной короткими учебными примерами 5-й главе, а все остальные главы этой части посвящены типам данных. Особое значение имеет глава 9, в которой описывается основополагающий инструмент Object Pascal - классы и объекты. Практически все современные языки программирования (в том числе C++ и Visual Basic) поддерживают методику объектно-ориентированного программирования (ООП), позволяющую резко сократить сроки разработки программ и существенно повысить их качество. В гл. 9 описываются включенные в Object Pascal средства ООП, а в гл. 16 - классы самого общего назначения.

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

Главы 17 и 18 посвящены стандартным компонентам - основным строительным элементам любой Delphi-программы. Являющиеся частным случаем классов, компоненты играют огромную роль в практике Delphi-программирования.

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


Глава 1. Основные отличия различных версий DELPHI

  • Глава 1. ОСНОВНЫЕ ОТЛИЧИЯ РАЗЛИЧНЫХ ВЕРСИЙ DELPHI
    • ВЕРСИЯ 1
    • ВЕРСИЯ 2
    • ВЕРСИЯ 3
    • ВЕРСИЯ 4
    • ВЕРСИЯ 5
    • ВЕРСИЯ 6

Глава 1

ОСНОВНЫЕ ОТЛИЧИЯ РАЗЛИЧНЫХ ВЕРСИЙ DELPHI

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

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

1.1. ВЕРСИЯ 1

Первая версия появилась в мае 1995 г., когда еще не существовала Windows 95 (но тем не менее существовала Windows NT). Это единственная версия, работающая под управлением 16-разрядной Windows 3.1 (3.11). В ней впервые была опробована новая модель объектов, позаимствованная из различных объектно-ориентированных языков, и главным образом, из языка C++. Эта модель оказалась настолько революционной, что существовавшие в то время в поздних версиях Turbo Pascal объекты стали не нужны (их возможности полностью поглотила новая модель), а сама новая модель получила название классов.

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

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

Библиотека компонентов 1-й версии с самого начала показала основную направленность вновь разработанной системы: программирование баз данных. С этой целью первая и все последующие версии Delphi снабжаются специальным инструментом доступа к данным -BDE (Borland Database Engine - машина баз данных корпорации Borland), а также сервером баз данных InterBase (этот сервер производится филиалом Borland - компанией InterBase Software Corporation) и соответствующими средствами конфигурации сервера, его контроля и связи с ним.

Палитра компонентов первой версии состоит из 9 страниц и содержит 79 компонентов. В качестве дополнительных утилит поставлялись система генерации отчетов ReportSmith v.2.5 и входящая в ее состав автономно запускаемая утилита ReportSmith Run Time Viewer, утилита доступа к данным DatabaseDesktop v.5.1 и утилита конфигурации BDE - Database Engine Configuration v.2.5. Кроме того, утилиты WinSighte и WinSpecter предназначались для упрощения отладки разрабатываемых программ в среде 16-разрядных ОС Windows 3-х.

1.2. ВЕРСИЯ 2

Вторая и все последующие версии Delphi предназначены для работы под управлением 32-разрядных ОС Windows 95/98/2000/NT (Windows 32). В нее были внесены многочисленные изменения, связанные с переходом на качественно новую ОС, в том числе:

  • введена поддержка 16-битных (“широких”) символов и составленных из них строк;
  • введен новый формат строк “обычных” 8-битных символов произвольной длины;
  • введены новые типы данных - variant и currency;
  • введен механизм фильтрации в табличные наборы данных TTable.

Начиная с версии 2, Delphi поставляется в нескольких комплектациях, отличающихся набором инструментальных средств и компонентов. Это соответствует более гибкой ценовой политике: минимальная по возможностям комплектация стоит 200-300 долларов, в то время как цена наиболее мощной комплектации может достигать нескольких тысяч. Во 2-й версии таких комплектов было 3: Desktop, Professional и Client/Server Suite. Далее описываются возможности наиболее мощной комплектации Client/Server Suite.

Среда разработки обогатилась Репозиторием Объектов и множеством полезных программ-экспертов (Wizards).

К утилитам добавлена весьма эффективная программа SQL Explorer v.1.02, существенно облегчающая операции с базами данных, и программа переноса данных Data Migration Expert, упрощающая переход к архитектуре клиент-сервер. Утилиты ReportSmith и DatabaseDesktop представлены более совершенными версиями 3.0 и 7.1 соответственно. Утилита Database Engine Configuration предыдущей версии переименована в BDE Configuration v.3.0. Введена утилита Image Editor v.2.0, служащая для создания различного рода изображений (произвольных рисунков, пиктограмм и курсоров), которые можно включать в файл ресурсов и использовать в работающей программе, и утилита SQL Monitor v.1.0, позволяющая просматривать SQL-запросы в процессе выполнения программы.

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

1.3. ВЕРСИЯ 3

Мне довелось довольно долго работать с вышедшей в июне 1997 г. версией 3, и у меня сложилось мнение, что это одна из наиболее надежных версий Delphi.

Основные новшества этой версии:

  • использование механизма пакетов для облегчения распространения и повторного использования компонентов;
  • улучшенные свойства редактора кода: построение фрагментов кода по образцу; оперативная подсказка о типе и количестве формальных параметров при обращениях к подпрограммам; оперативный просмотр в режиме отладки содержимого полей, свойств и переменных с помощью указателя мыши;
  • поддержка шаблонов компонентов;
  • поддержка технологий СОМ, ActiveX, OLEnterprice и (частично) CORBA.

Третья версия поставлялась в 4 комплектациях: Standard, Professional, Client/Server Suite и Enterprise.

Состав утилит комплектации Client/Server Suite не изменился по сравнению с предыдущей версией, но изменились названия некоторых из них: BDE Configuration в этой и последующих версиях называется BDE Administrator, a Data Migration Expert - Data Migration Wizard. Ha 13 страницах галереи компонентов размещены 148 стандартных компонента. В модификации 3.5 введены дополнительные компоненты для реализации многозвенных баз данных.

1.4. ВЕРСИЯ 4

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

К основным нововведениям этой версии относятся:

  • изменения в языке: динамические массивы, перегружаемые методы, умалчиваемые параметры подпрограмм, новые типы int64, Real48 и Real как аналог Double;
  • изменения в кодовом редакторе: автоматическое завершение кодовых заголовков свойств и методов; свойства браузера для поиска исходного кода; простой переход от заголовка метода к его реализации и обратно;
  • технология “причаливания” инструментальных панелей Drag&Dock;
  • механизм “действий” Action для унификации внешнего вида и поведения одинаковых по функциональному назначению интерфейсных элементов;
  • улучшенная поддержка многозвенной архитектуры БД и распределенных вычислений.

На 14 страницах палитры компонентов размещены 182 стандартных компонента. В поставке Client/Server Suite утилита Data Migration Wizard получила название Data Pump.

1.5. ВЕРСИЯ 5

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

Для этого, во-первых, в Delphi 5 включена поддержка технологии ADO (ActiveX Data Objects - объекты данных, построенные как объекты ActiveX), которая усиленно развивается корпорацией Microsoft.

Во-вторых, сотрудники Borland и его подразделения InterBase Software Corporation разработали серию компонентов облегченного доступа к данным, хранящимся в таблицах сервера InterBase v.5.5 и выше (страница InterBase палитры компонентов). Эти компоненты также не требуют BDE и, таким образом, создают “облегченное” клиентское место.

Менее значительные изменения, внесенные в версию 5:

  • включен эксперт создания и настройки произвольных модулей данных с расширенными возможностями представления взаимосвязи данных;
  • улучшена технология MIDAS: для более гибкой работы с Microsoft Transaction Server введен повторно-входимый (stateless) брокер данных; упрощен процесс разработки интранет-приложений за счет компонентов новой страницы InternetExpress;
  • значительные изменения внесены в интегрированную среду разработчика ИСР; в том числе:
  1. для улучшения координации коллективной работы над одним проектом введен новый инструмент - список To-Do;
  2. программист теперь может при желании использовать несколько вариантов настройки основных окон Delphi - например, для режима кодирования на экране могут не присутствовать отладочные окна, которые, наоборот, могут понадобиться в отладочном режиме; нужный вариант настройки легко выбирается новыми интерфейсными элементами в главном окне Delphi;
  3. введены фильтрующие свойства в окне Инспектора Объектов, упрощающие выбор нужного свойства;
  4. опции Инспектора Объектов теперь могут снабжаться небольшими пиктограммами, облегчающими правильный выбор нужной опции (например, радом с названием каждого цвета показывается небольшой прямоугольник, заполненный этим цветом, рядом с названием курсора - его вид и т. п.);
  5. существенно усилены возможности встроенного отладчика: точки отладочного останова можно группировать и сделать доступной или недоступной сразу группу точек; с каждой точкой останова можно связать одно или несколько действий, которые будут выполняться при достижении этой точки; с помощью команды Run | Attach to process можно отлаживать процесс, запущенный в другом экземпляре ИСР (эта возможность существенно упрощает отладку многозвенных приложений); с помощью выбора Run | Run Until Return в главном меню можно продолжить пошаговую отладку после завершения текущей подпрограммы и т. д.;
  6. введены дополнительные возможности в Менеджер Проекта, упрощающий координацию многих разработчиков в рамках единого проекта;
  7. создан механизм Менеджера Трансляций, облегчающий разработку многоязычных программ в рамках единого проекта;
  8. внесены изменения в кодовый редактор, позволяющий настраивать используемые в нем “горячие” клавиши;
  9. несколько переработана встроенная справочная служба;
  • внесены многочисленные изменения и дополнения в галереи компонентов (в новой версии наиболее мощной комплектации Enterprise на 20 страницах расположены 218 стандартных компонентов). Версия поставляется в 3 комплектациях: Standard, Professional и Enterprise. Состав утилит, входящих в комплектацию Enterprise, полностью соответствует утилитам комплектации Client/Server Suite предыдущей версии.

1.6. ВЕРСИЯ 6

Версия 6 вышла в мае 2001 г., т. е. почти через 2 года после выхода 5-й версии. Эту задержку можно объяснить тем, что параллельно с работой над новой версией Delphi шла разработка варианта Delphi для ОС Linux - эта система программирования вышла в феврале 2001 г. и получила название Kylix. В результате 6-я версия Delphi имеет уникальную особенность: она способна создавать так называемые'межплатформенные приложения, т. е. программы, которые одинаково успешно могут работать как под управлением Windows 32, так и под Linux.

Напомню читателю предысторию, связанную с этой ОС. Еще в самом начале 70-х годов в Bell Laboratories была создана переносимая система UNIX. Переносимость системы, т. е. ее способность миграции на различные аппаратные платформы, обеспечивалась тем, что практически вся она была написана на специально для этой цели разработанном языке С. Более того, почти весь компилятор этого языка был также написан на С! В результате для переноса ОС на новую аппаратную среду нужно было закодировать лишь сравнительно небольшой машинно-зависимый участок С, после чего транслировался исходный код компилятора, а затем и системы в целом. Таким образом, ОС UNIX принципиально существовала в исходных текстах, доступных широкому кругу программистов. Это обстоятельство использовали многие фирмы, создав собственные версии UNIX-подобных ОС, но уже не предоставляя пользователям исходные коды (например, ОС Solaris фирмы Sun).

В 1991 г. финский программист Линус Торвальдс решил создать полностью открытую UNIX-подобную ОС Linux. Полная открытость системы означает, что ее исходный код всегда доступен любому желающему. Для реализации своего замысла Л. Торвальдс привлек широкий круг программистов, опубликовав предварительный исходный код Linux в Интернет и пригласив всех желающих дополнять его своими разработками, но с обязательным условием: публикацией исходного кода этих разработок. Таким образом, эта ОС изначально разрабатывалась (и продолжает разрабатываться!) как полностью открытая и бесплатная система, которую может получить любой желающий [Как показала практика, бесплатность Linux относительна: некоторые фирмы за относительно небольшую плату (часто ненамного превышающую стоимость носителей информации) не только поставляют весьма громоздкие тексты этой ОС, но и обеспечивают покупателей информационной поддержкой по ее установке и эксплуатации.]. Не нужно быть провидцем, чтобы понять, как много потребителей (по некоторым оценкам, сегодня этой ОС пользуются около 8 млн. человек) решились отказаться от услуг Microsoft, вечной политикой которой была полная закрытость исходных текстов своих ОС, в пользу Linux, обеспечивающей, по утверждениям специалистов, не меньшую надежность, чем Windows NT, при приблизительно одинаковых возможностях [ Последнее не совсем точно. Тщательно отлаженные механизмы Windows Plug&Play, СОМ, Ас-tiveX, ADO, MTS и т. п. в сочетании с мощными возможностями Office, SQL Server, Internet Information Server и других подобных программ дают конечному пользователю гораздо больше возможностей, но... за плату и без исходных текстов.]. (Надежность Linux объясняется тем, что большая часть ее пользователей - программисты, которые не только выявляют ошибки, но и устраняют их. В этом смысле говорят, что Linux - самая тщательно отлаженная ОС в мире.)

До появления Kylix единственными средствами программирования для Linux были системы на языках C/C++, Basic, Fortran. Kylix, а следом за ней - Delphi 6, открывают богатейшие возможности VCL (вариант которой под Linux/Windows называется CLX - Borland Component Library for Cross-Platform Application) для разработки прикладных программ для Linux, что позволяет говорить об этой ОС как о потенциальном и весьма опасном конкуренте Windows.

Две другие особенности Delphi 6 также достойны особого упоминания: в ней сделаны дальнейшие шаги для поддержки Web-программирования (архитектура websnap) и разработаны драйверы и компоненты для максимально быстрой связи клиентских мест с некоторыми популярными промышленными серверами баз данных без BDE (компоненты страницы dbExpress).

Архитектура WebSnap продолжает и развивает ранее существовавшую в версиях 4 и 5 технологию Web Broker для создания Web-приложений. В ее основе лежит использование протокола SOAP (Simple Object Access Protocol - простой протокол доступа к объекту). В сочетании с бесплатно распространяемым Web-сервером Apache эта технология позволяет отказаться от дорогостоящих средств Windows NT Server и Internet Information Server в пользу связки Linux + Apache.

Технология dbExpress поддерживает непосредственный доступ к таким популярным серверам БД, как MySQL, Oracle, DB2. Если учесть, что сервер MySQL шведской компании ТсХ распространяется бесплатно для домашнего применения на платформе Linux и за очень небольшую плату - для неограниченного количества подключений и примерно на тех же условиях распространяются серверы Oracle и DB2 (но без исходных кодов), становится очевидным, что популярная до недавнего времени дорогостоящая связка Windows NT Server + Microsoft SQL Server испытывает сильнейшее конкурентное давление.

Таким образом, основной идей Delphi 6 является обеспечение перехода от дорогих патентованных решений корпорации Microsoft к бесплатным (или почти бесплатным) решениям на базе Linux.

На 27 страницах палитры компонентов размещены 387 компонентов (в режиме CLX - 211 компонентов на 21 странице).


Глава 2. ЗНАКОМСТВО СО СРЕДОЙ DELPHI

  • Глава 2. ЗНАКОМСТВО СО СРЕДОЙ DELPHI
    • ГЛАВНОЕ ОКНО
      • Пиктографические кнопки
      • Настройка окон
      • Палитра компонентов
    • ОКНО ФОРМЫ
    • ОКНО ДЕРЕВА ОБЪЕКТОВ
    • ОКНО ИНСПЕКТОРА ОБЪЕКТОВ
    • ОКНО КОДА ПРОГРАММЫ

Глава 2

ЗНАКОМСТВО СО СРЕДОЙ DELPHI

Среда Delphi - это сложный механизм, обеспечивающий высокоэффективную работу программиста. Визуально она реализуется несколькими одновременно раскрытыми на экране окнами. Окна могут перемещаться по экрану, частично или полностью перекрывая друг друга, что обычно вызывает у пользователя, привыкшего к относительной “строгости” среды текстового процессора Word или табличного процессора Excel, ощущение некоторого дискомфорта. После приобретения опыта работы с Delphi это ощущение пройдет, и вы научитесь быстро отыскивать нужное окно, чтобы изменить те или иные функциональные свойства создаваемой вами программы, ибо каждое окно несет в себе некоторую функциональность, т. е. предназначено для решения определенных задач.

Запустите Delphi - и вы увидите нечто, похожее на рис. 2.1 (на рисунке показаны окна Delphi версии 6; для других версий окна могут иметь незначительные отличия). На нем изображены шесть наиболее важных окон Delphi: главное окно 1, окно Дерева объектов (Object Tree View) 2, окно Инспектора объектов 3, окно браузера 4, окно формы 5 и окно кода программы 6.

Чтобы упорядочить окна так, как они показаны на рисунке, вам придется вручную изменять их положение и размеры, т. к. обычно окно кода программы почти полностью перекрыто окном формы. Впрочем, добиваться максимального сходства того, что вы видите на экране вашего ПК, с изображением, показанным на рис. 2.1, вовсе не обязательно: расположение и размеры окон никак не влияют на их функциональность; если вы не видите на экране окна кода, но хотите его увидеть, просто нажмите клавишу F12 [Если к этому моменту активным было окно Инспектора Объектов, вам придется нажать на F12 еще раз.]. Повторное нажатие F12 вновь активизирует окно формы и т. д. - эта клавиша работает как двоичный переключатель, попеременно показывая окно формы или окно кода программы.

Замечу, что при первом запуске Delphi поверх всех окон появится окно, показанное на рис. 2.2.

Рис. 2.1. Наиболее важные окна Delphi:

Рис. 2.2. Окно связи с Inprise (Borland)

С помощью этого окна вы сможете получить доступ к Web-страницам корпорации Inprise для просмотра самой свежей информации о корпорации и ее программных продуктах, копирования дополни тельных файлов, чтения ответов на наиболее часто задаваемые вопросы и т. д. При повторных запусках Delphi это окно появляется автоматически с некоторой периодичностью, определяемой настройками на странице окна Tolls | Environment Options, связанной с закладкой Delphi Direct. Вы также сможете его вызвать в любой момент с помощью опции Help | Delphi Direct главного меню.

2.1. ГЛАВНОЕ ОКНО

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

Связано это с функциональностью главного окна: с одной стороны, оно несет в себе элементы, которые всегда должны быть под рукой у программиста, с другой - окно не должно отнимать у остальных окон Delphi значительного пространства экрана. Минимизация главного окна приводит к исчезновению с экрана других окон Delphi[Эти окна появятся, как только будут восстановлены размеры главного окна.], а его закрытие означает окончание работы программиста с системой программирования.

В главном окне располагается главное меню Delphi, набор пиктографических командных кнопок и палитра компонентов.

Главное меню [Помимо главного меню в Delphi широко используется система вспомогательных меню, доступ к которым осуществляется правой кнопкой мыши.] содержит все необходимые средства для управления проектом. Все опции главного меню представляют собой опции-заголовки, открывающие доступ к выпадающим меню второго уровня. Для начального знакомства с Delphi мы вполне обойдемся пиктографическими кнопками, и нам не понадобится обращаться к главному меню, вот почему подробное описание его опций вы найдете не здесь, а в прил. 1.

Все элементы главного окна располагаются на специальных панельках, в левой части которых имеются кнопки управления [image] , позволяющие с помощью мыши перетаскивать панельки с помещенными на них элементами. Любую панельку (кроме главного меню) можно убрать из окна (сделать ее невидимой) или “пустить плавать” по экрану в отдельном окне. Для этого нужно лишь “стащить” панельку с помощью мыши за кнопку [image] из пределов главного окна.

Для изменения состава показываемых на панельке кнопок нужно предварительно щелкнуть по ней правой кнопкой мыши. В появившемся после этого окне вспомогательного меню (рис. 2.3, а) перечислены названия всех панелек и указан их статус (отмеченные флажками панельки видны в главном окне; если отметку убрать, панелька исчезнет). После выбора customize (Настройка) появится окно настройки (рис. 2.3, б). Теперь можно “стаскивать” с панелек ненужные кнопки, выбирать из списка в окне commands (закладка Commands) нужные кнопки и перетаскивать их на экран.

а), [image] б).

Рис. 2.3. Настройка инструментальных панелек: а) вспомогательное меню; б) окно настройки с выбранной закладкой Command

Для примера на рис. 2.4 показано главное окно с нестандартным расположением панелек и набором отображаемых в них кнопок.

При его создании мне хотелось, с одной стороны, получить больше места для размещения страниц галереи компонентов, а с другой поместить в главное окно пиктограммы часто используемых мною команд меню: Project | Syntax check (проверка синтаксиса кода программы), View | Debug Windows | Watches) доступ к окну наблюдения отладочного режима) и View | Debug windows | Breakpoints (доступ к окну точек останова).

(Левая часть окна)

(Правая часть окна)

Рис. 2.4. Главное окно с нестандартным расположением своих элементов

2.1.1. Пиктографические кнопки

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

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

Группа Standard

Открывает доступ к Репозиторию Объектов. Эквивалент опции File | New | Other (этой кнопки нет в версиях 1, 2 и 3)

Открывает существующий файл. Эквивалент опции File | Open File

Сохраняет файл на диске. Эквивалент опции File | Save File (клавиши быстрого доступа Ctrl-S)

Сохраняв все файлы проекта. Эквивалент опции File | Save All

Открывает созданный ранее проект программы. Эквивалент опции File | Open Project (клавиши быстрого доступа Ctrl-F11)

Добавляет новый файл к проекту. Эквивалент опции Project | Add to project (клавиши быстрого доступа Shift-F11)

Удаляет файл из проекта. Эквивалент опции Project | Remove from Project

Группа View

Выбирает модуль из списка модулей, связанных с текущим проектом. Эквивалент опции View | units (клавиши быстрого доступа Shift-F12)

Выбирает форму из списка форм, связанных с текущим проектом. Эквивалент опции View | Forms (клавиши быстрого доступа Ctrl-F12)

Переключает активность между окном формы и окном кода программы. Эквивалент опции View | Toggle Form/Unit (клавиша быстрого доступа F12)

Создает новую форму и добавляет ее к проекту. Эквивалент опции File | New | Form (этой кнопки нет в версиях 1 и 2)

Группа Debug

Компилирует и выполняет программу. Эквивалент опции Run | Run (клавиша быстрого доступа F9)

Реализует паузу в работе отлаживаемой программы. Эквивалент опции Run | Program Pause

Осуществляет пошаговую трассировку программы с прослеживанием работы вызываемых подпрограмм. Эквивалент опции Run | Trace into (клавиша быстрого доступа F7)

Осуществляет пошаговую трассировку программы, но не прослеживает работу вызываемых подпрограмм. Эквивалент опции Run | Step Over (клавиша быстрого доступа F8)

Группа Custome

Открывает доступ к встроенной справочной службе. Эквивалент опции Help | Delphi Help (этой кнопки нет в версиях 1, 2 и 3)

Группа Desktops

Список выбора возможных вариантов настройки остальных окон Delphi

Сохраняет текущую настройку окон Delphi

Выбирает настройку окон, соответствующую отладочному режиму

Группа Internet

Начинает создание нового приложения по технологии WebSnap (для Интернет)

Создает новую страницу приложения WebSnap

Создает новый модуль данных приложения WebSnap

2.1.2. Настройка окон

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

Обычно выбираются две или три основные конфигурации окон: для режима разработки форм, для кодирования и для отладки. При разработке формы на экране должна быть видна сама форма, Дерево объектов и Инспектор объектов. Настроив соответствующие размеры и положение этих окон, такую настройку можно сохранить под именем, например, Design Desk. Для режима кодирования обычно нужно только максимально распахнутое на экран окно кода с браузером Code Explorer и пристыкованное к нему окно Дерева объектов. Такую конфигурацию можно сохранить под именем code-Desk. Наконец, в отладочном режиме к окну кода желательно “прицепить” некоторые отладочные окна, такие как Watches (окно наблюдений за переменными/выражениями) и Breakpoints (окно создания/настройки точек контрольного останова). Такую конфигурацию можно сохранить под именем DebugDesk, например. Теперь при переходе к тому или иному режиму достаточно раскрыть список и выбрать из него нужную настройку. Если вы предварительно укажете с помощью кнопки [image] настройку для отладочного режима, эта стройка появится автоматически, как только среда перейдет в режим отладки.

2.1.3. Палитра компонентов

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

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

Нет смысла удалять какой-то из компонентов, а создавать новые мы с вами пока еще не умеем, поэтому я не рекомендую вам экспериментировать с редактором палитры. Тем не менее один совет все-таки дам. В стандартном наборе страниц палитры компонентов есть страница Samples, содержащая довольно часто используемые компоненты. В то же время ее закладка в палитре закрыта двумя небольшими кнопками “прокрутки” закладок палитры. Имеет смысл поменять ее местами с менее используемой страницей, например, со страницей ADO. Вы можете “перетащить” строку Samples в окне Pages редактора палитры вверх и “положить” ее на строку ADO. После такой перестановки закладка Samples станет доступна в любой момент. Если, к тому же, вы не планируете использовать в своих проектах технологию ADO для доступа к базам данных, перетащите строку ado в самый конец списка.

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

2.2. ОКНО ФОРМЫ

Окно формы представляет собой проект Windows-окна будущей программы. Вначале это окно пусто. Точнее, оно содержит стандартные для Windows интерфейсные элементы - кнопки вызова системного меню, максимизации, минимизации и закрытия окна, полосу заголовка и очерчивающую рамку. Вся рабочая область окна обычно заполнена точками координатной сетки, служащей для упорядочения размещаемых на форме компонентов (вы можете убрать эти точки, вызвав с помощью меню Tools | Environment options соответствующее окно настроек и убрав флажок в переключателе Display Grid на окне, связанном с закладкой Preferences).

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

2.3. ОКНО ДЕРЕВА ОБЪЕКТОВ

Это окно появилось в версии 6 и предназначено для наглядного отображения связей между отдельными компонентами, размещенными на активной форме или в активном модуле данных. Щелчок по любому компоненту в этом окне активизирует соответствующий компонент в окне формы и отображает свойства этого компонента в окне Инспектора объектов. Двойной щелчок приводит к срабатыванию механизма Code Insight, который вставляет в окно кода заготовку для обработчика события OnClick. Наконец, компонент можно “перетащить” в окне и таким образом поменять его владельца (свойство parent). В предыдущих версиях такую замену можно было сделать только с помощью межпрограммного буфера обмена Clipboard.

2.4. ОКНО ИНСПЕКТОРА ОБЪЕКТОВ

Любой размещаемый на форме компонент характеризуется некоторым набором параметров: положением, размером, цветом и т. д. Часть этих параметров, например, положение и размеры компонента, программист может изменять, манипулируя с компонентом в окне формы. Для изменения других параметров предназначено окно Инспектора объектов. Это окно содержит две страницы - Properties (Свойства) и Events (События). Страница properties служит для установки нужных свойств компонента, страница Events позволяет определить реакцию компонента на то или иное событие. Совокупность cвойств отображает видимую сторону компонента: положение относительно левого верхнего угла рабочей области формы, его размеры и цвет, шрифт и текст надписи на нем и т. п.; совокупность событий - его поведенческую сторону: будет ли компонент реагировать на щелчок мыши или на нажатие клавиш, как он будет вести себя в момент появления на экране или в момент изменения размеров окна и т. п.

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

Строки таблицы выбираются щелчком мыши и могут отображать простые или сложные свойства. К простым относятся свойства, определяемые единственным значением - числом, строкой символов, значением True (Истина) или False (Ложь) и т. п. Например, свойство caption (Заголовок) представляется строкой символов, свойства Height (Высота) и width (Ширина) - числами, свойство Enabled (Доступность) - значениями True или False. Сложные свойства определяются совокупностью значений. Слева от имени таких свойств указывается значок “+”, а щелчок мышью по этому символу приводит к раскрытию списка составляющих сложного свойства. Чтобы закрыть раскрытый список, нужно щелкнуть по значку “-” сложного свойства [Для версий Delphi 1...4 раскрыть или закрыть список составляющих сложного свойства можно только двойным щелчком.].

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

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

В локальном меню окна, которое появляется после щелчка по нему правой кнопкой, имеется ряд опций, позволяющих настроить окно. В частности, после выбора stay on Top, окно Инспектора объектов будет “всплывать” над всеми другими окнами независимо от его активности. Такое состояние окна удобно при частом его использовании, например, при конструировании сложной формы, содержащей множество компонентов. Если выбрать в локальном меню опцию Arrange и затем by Category, все строки окна Инспектора объектов будут представлять собой раскрывающиеся списки свойств, упорядоченные по категориям (рис. 2.6).

Рис. 2.6. Списки свойств упорядоченные по категориям

Любые категории можно сделать невидимыми. Для этого нужно в локальном меню выбрать view и затем в дополнительном меню убрать флажок слева от категории.

В отличие от предыдущих версий Delphi в версии 6 сложные свойства, ссылающиеся на объекты, выделяются в окне Инспектора объектов цветом и показываются на странице Events. Если такое свойство определено, слева от него появляется кнопка “+”, позволяющая увидеть (и при необходимости изменить) свойства и обработчики событий связанного объекта. Для примера на рис. 2.7 показано, как с помощью окна Инспектора объектов, связанного с формой Form1, определить обработчик OnClick для одной из опций связанного с формой меню.

С помощью опции properties локального меню окна Инспектора объектов можно отменить такую его функциональность, если в списке SpeedSettings соответствующего диалогового окна выбрать Delphi 5 emulation.

Поэкспериментируйте самостоятельно с локальным меню Инспектора объектов, чтобы понять назначение тех или иных его опций. Если вы случайно или намеренно сделаете окно невидимым, нажмите F 11 или выберите опцию view | object inspector, чтобы оно вновь появилось на экране.

Рис. 2.7. Установка обработчика OnClick опции меню для формы Form1

2.5. ОКНО КОДА ПРОГРАММЫ

Окно кода предназначено для создания и редактирования текста программы. Этот текст составляется по специальным правилам и описывает алгоритм работы программы. Совокупность правил записи текста называется языком программирования. В системе Delphi используется язык программирования Object Pascal, который представляет собой расширенную и усовершенствованную версию широко распространенного языка Паскаль, впервые предложенного швейцарским ученым Н. Виртом еще в 1970 г. и усовершенствованного сотрудниками корпорации Borland (созданные ими языки назывались Turbo Pascal, Borland Pascal и Object Pascal). Несмотря на то, что визуальная среда Delphi берет на себя многие рутинные аспекты программирования, знание языка Object Pascal является непременным условием для любого программиста, работающего в этой среде.

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

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

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Frms, Dialogs;

type

TFormI = class(TForm) private

{ Private declarations } public

{ Public declarations } end;

var

Form1: TForm1;

implementation

{SR *.DFM}

end.

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

unit Unit1;

и implementation

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

{$R-*.DFM}

и

end.

в нижней части окна.

Чтобы вставить в окно новую строку (строки), нужно сначала, с помощью клавиш курсора или щелкнув по окну мышью, поставить текстовый указатель (мигающую вертикальную черту) на нужное место, а затем с помощью клавиатуры ввести текст. Обычно текст кода программы располагается в нескольких строках. Для перехода на новую строку используйте клавишу Enter. Если в процессе ввода вы ошиблись и тут же заметили свою ошибку, удалите ошибочный символ клавишей Backspace (продолговатая серая клавиша в правом верхнем углу зоны алфавитно-цифровых клавиш или расположенная там же клавиша с левой стрелкой). Клавиша Backspace удаляет символ слева от мигающего указателя, а клавиша Delete - справа от него. Если понадобится удалить сразу всю строку текста, поставьте в любое место строки мигающий указатель, нажмите клавишу Ctrl и, не отпуская ее, клавишу с латинской буквой Y. Такое совместное нажатие клавиш в дальнейшем будем обозначать символом “+”: Ctrl+Y. Чтобы отменить последнее изменение текста, нажмите Ctrl+Z или выберите

Пункт меню Edit | Undo.

Более полное руководство по работе с текстовым редактором окна кода приводится в прил. 1.

Текстовый редактор версий Delphi 4, 5 и б имеет расширенные “интеллектуальные” возможности Code Insight cвяхaнные с контролем текста по мере его ввода программистом. К сожалению, этот контроль отнимает у компьютера определенные ресурсы. Чтобы избавиться от раздражающих пауз в момент набора кода, особенно заметных на ПК с памятью менее 32 Мб, оснащенных процессорами с тактовой частотой 100 и менее МГц, раскройте список опции Tools главного меню, щелкните по опции Editor Options (для вёрсии 4-на опции Environment options) и на странице, связанной с закладкой code insight, уберите флажки в переключателях Code Completion, Code Parameters И Tooltip Symbol Insight. Увы! Вы лишитесь чудесных возможностей режима Code Insight, но зато обретете полный контроль над клавиатурой. (Переключатель Tooltip Expression Evaluation разрешает/запрещает вычисление и показ значения переменной в режиме отладки, когда нау переменной останавливается указатель мыши. Убирать флажок в этом переключателе не имеет смысла, т. к. на этапе ввода текста программы этот, сервис среды заблокирован и активизируется лишь после перехода к режиму отладки).

Вместе с окном кода обычно активизируется) браузера Code Explorer, облегчающее поиск нужных элементов в случае, когда в окне набрано много строк кода (рис. 2.8).

Рис. 2.8. Окно кода с расположенным слева браузером Code Explorer

Двойной щелчок мышью на элементе в окне браузера приводит к позиционированию текстового указателя в окне кода на описание соответствующего объекта или его первое упоминание. При разработке несложных учебных программ можно закрыть окно браузера маленькой кнопкой [image] в его верхнем правом углу. Восстановить закрытое окно можно после щелчка правой кнопкой мыши по окну кода и выбора опции view Explorer. Замечу, что окно браузера располагается на вспомогательной панели и имеет управляющую полосу в верхней части рамки. “Схватив” ее мышью, вы можете перетащить окно на любое свободное место экрана или “причалить” его к окну Инспектора объектов.

В версии 6 на нижней кромке окна кода располагаются две закладки - Сode и Diagram. Щелчок по последней активизирует страницу диаграмм (рис. 2.9). Первоначально эта страница пуста. Чтобы наполнить ее, нужно “перетащить” мышью два и более компонента из окна Инспектора компонентных связей. На рис. 2.9 это окно пристыковано к верхней части окна браузера. Чтобы перетащить компоненты в поле диаграмм, нужно нажать и удерживать клавишу Shift, после чего щелкнуть по компонентам и, продолжая удерживать Shift, переместить указатель мыши в поле диаграмм и щелкнуть по нему левой кнопкой.

Рис. 2.9. Пример страницы диаграмм

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

Страницу диаграмм можно напечатать.


Глава 3. ОСНОВЫ ВИЗУАЛЬНОГО ПРОГРАММИРОВАНИЯ

  • Глава 3. ОСНОВЫ ВИЗУАЛЬНОГО ПРОГРАММИРОВАНИЯ
    • ПУСТАЯ ФОРМА И ЕЕ МОДИФИКАЦИЯ
      • Настройка Delphi
      • Имена в Delphi
      • Изменение свойств формы
    • РАЗМЕЩЕНИЕ НОВОГО КОМПОНЕНТА
    • РЕАКЦИЯ НА СОБЫТИЯ
      • Модификация формы
      • Обработчик события OnClick
      • Динамическое изменение свойств компонента
    • НЕКОТОРЫЕ ИТОГИ

Глава 3

ОСНОВЫ ВИЗУАЛЬНОГО ПРОГРАММИРОВАНИЯ

Программирование в Delphi строится на тесном взаимодействии двух процессов: процесса конструирования визуального проявления программы (т. е. ее Windows-окна) и процесса написания кода, придающего элементам этого окна и программе в целом необходимую функциональность. Для написания кода используется окно кода, для конструирования программы - остальные окна Delphi, и прежде всего - окно формы.

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

3.1. ПУСТАЯ ФОРМА И ЕЕ МОДИФИКАЦИЯ

Как уже говорилось, окно формы содержит проект Windows-окна программы. Важно помнить, что с самого начала работы над новой программой Delphi создает минимально необходимый код, обеспечивающий ее нормальное функционирование в Windows. Таким образом, простейшая программа готова сразу после выбора опции File | New | Application, и нам остается просто запустить програм-му. Однако до этого советую выполнить две важные вещи: создать собственный рабочий каталог (папку) и нужным образом настроить Delphi.

3.1.1. Настройка Delphi

В процессе работы над проектами программ, описываемых в этой книге, вам понадобится создать множество форм и модулей. Полезно сохранять эти данные в виде дисковых файлов в отдельной папке. Более того, для каждой программы в этой папке имеет смысл создать свою вложенную папку. Тогда, чтобы освободить место на диске для серьезной программы, вам будет достаточно уничтожить основную папку, а чтобы передать ту или иную учебную программу своему коллеге - переписать на дискету содержимое соответствующей вложенной папки. Создайте папку с именем, например, my_delph, а в нем - вложенную папку для вашей первой программы. Чтобы не утруждать себя придумыванием подходящих имен для “безликих” учебных программ, советую использовать для папки имя pgm_1 или что-нибудь подобное.

Второе, что нам предстоит сделать, - это внести изменения в стандартную настройку среды Delphi. Это необходимо для того, чтобы среда автоматически сохраняла на диске последнюю версию создаваемой вами программы. Выберите опцию меню Tools | Environment options и убедитесь, что в появившемся диалоговом окне активна страница preferences. В левом верхнем углу этой страницы в группе Autosave | Options есть переключатели Editor Files и Desktop (в других версиях Delphi эти переключатели располагаются в правом верхнем углу). Активизация переключателей приведет к автоматическому сохранению текста окна кода программы и общего расположения окон Delphi перед началом очередного прогона создаваемой программы, что избавит вас от возможных потерь в случае “зависания” программы. Советую активизировать эти переключатели, щелкнув по каждому мышью. Чтобы следить за ходом компиляции, активизируйте также переключатель Show Compiler progress в группе Compiling and Running. Далее, мне кажется полезным сохранять в коде программы комментарии - текстовые фрагменты, которые не влияют на работу программы, но делают ее текст более понятным. Если вы, как и я, предпочитаете использовать комментарии на русском языке, следует выбрать соответствующий шрифт для отображения кода программы. По умолчанию редактор Delphi использует шрифт Courier New, в котором может не быть символов кириллицы. В этом случае выберите опцию Tools | Editor options и на странице Display в строке Editor Font установите Courier New Cyr (для предыдущих версий страница Display доступна в окне Tools | Environment Options).

Теперь все готово для прогона вашей первой программы. Щелкните мышью по кнопке [image] в главном окне или, что проще, нажмите клавишу F9: именно таким способом подготовленная Delphi-программа последовательно проходит три главных этапа своего жизненного цикла - этапы компиляции, компоновки и исполнения. На этапе компиляции осуществляется преобразование подготовленного в окне кода текста программы на языке Object Pascal в последовательность машинных инструкций, на этапе компоновки к ней подключаются необходимые вспомогательные подпрограммы, а на этапе исполнения готовая программа загружается в оперативную память и ей передается исполнение.

Как только вы нажмете F9, появится диалоговое окно Save Unit1 As, в котором Delphi попросит вас указать имя файла для модуля Unit1. раз и папку его размещения.

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

Лучше не идти на поводу у Delphi и не соглашаться с предложенным именем Unit1.раs, а присвоить модулю какое-нибудь более запоминающееся имя. Например, назовем модуль my_ist_u, что, по мысли автора, должно расшифровываться как “мой_первый_модуль” | разумеется, вам в голову может прийти более подходящее имя). Если в окне Save Uniti As вы укажете такое имя и нажмете Enter, Delphi попросит задать имя еще и для проекта программы в целом. Под этим именем будет создан исполняемый ЕХЕ-файл, и здесь нет предела для вашей изобретательности, например, firstpgm - вполне достойное имя для вашей первой Delphi-программы.

3.1.2. Имена в Delphi

Поскольку речь зашла об именах, я должен объяснить свою странную приверженность к сокращенным англоязычным именам. Дело в том, что Delphi принципиально не признает ни русских, ни французских, ни китайских, ни вообще каких бы то ни было имен, в которых используются символы, отличные от латинских букв. цифр и знака подчеркивания, причем имя не должно начинаться цифрой, но может начинаться знаком подчеркивания. Так как в этом перечне нет пробела, имена не могут также состоять из нескольких слов. Далее, сокращения до не более чем 8 символов - это дань ограничениям Delphi версии 1. Эта версия рассчитана на работу под управлением Windows З.х, которая не признает длинных имен файлов. Если вы работаете с Delphi 32 (т. е. с любой из версий от 2-й до 6-й), можете не сокращать имена и написать my_first_unit или даже moy_pervij_modul, потому что эти версии рассчитаны на современные 32-разрядные операционные системы, разрешающие использовать длинные имена файлов.

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

3.1.3. Изменение свойств формы

Итак, первый и самый простой шаг в Delphi-программировании вами уже сделан: вами создана нормальная Windows-программа. Разумеется, эта программа не реализует придуманного вами алгоритма, она вообще ничего путного не умеет делать, но это не беда! Впереди много времени и эта толстая книга, в которой как раз и рассказывается о том, как заставить вашу программу делать что-нибудь стоящее.

Теперь попробуем разобраться с тем, что такое модуль. В первом приближении мы можем считать модулем самостоятельный раздел программы, в чем-то подобный главе в книге. Модуль создается каждый раз, когда вы создаете новую форму (в программе может быть и, чаще, бывает не одна, а несколько - иногда несколько десятков форм и связанных с ними модулей). При компиляции программы Delphi создает файлы с расширениями pas, dfm и оси для каждого модуля: pas-файл содержит копию текста из окна кода программы, в файле с расширением dfm хранится описание содержимого окна формы, а в оси-файле - результат преобразования в машинные инструкции текста из обоих файлов. Файлы dcu создаются компилятором и дают необходимую базу для работы компоновщика, который преобразует их в единый загружаемый файл с расширением ехе.

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

(Заголовок) окна Инспектора объектов [ Это окно не видно в момент прогона программы. Если после нажатия на клавишу F11 окно Инспектора объектов не появилось на экране, проверьте состояние панели задач Windows - вы наверняка увидите в ней имя вашей программы. Закроите ее.]. Теперь правая колонка этой строки с текстом Form1 выделена цветом и справа от выделенного виден текстовый мигающий курсор. Переключите клавиатуру в режим ввода кириллицы и введите Моя первая программа, после чего еще раз нажмите F9. Новый прогон программы создаст окно с заголовком “Моя первая программа”, что несет в себе гораздо больше информации, чем просто Form1.

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

3.2. РАЗМЕЩЕНИЕ НОВОГО КОМПОНЕНТА

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

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

Новый компонент имеет стандартное имя Label1, и надпись на нем повторяет это имя. Изменим эту надпись: с помощью строки Caption окна Инспектора объектов введите надпись: я программирую на Delphi. Как только вы начнете вводить новую надпись, вид ком-понента на форме начнет меняться, динамически отражая все изменения, производимые вами в окне Инспектора объектов.

Выделим надпись цветом и сделаем ее шрифт более крупным. Для этого щелкните мышью по свойству Font окна Инспектора объектов и с помощью кнопки в правой части строки раскройте диалоговое окно настройки шрифта. В списке size (Размер) этого окна выберите высоту шрифта 24 пункта [Пункт - 1/72 дюйма, т. е. приблизительно 0,04 мм. Таким образом, 24 пункта означает высоту шрифта чуть более 9 мм.], а с помощью списка Color (Цвет) выберите нужный цвет (например, красный), после чего закройте окно кнопкой ок.

Рис. 3.1. Размещение компонента Label

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

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

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

Замечу, что все видимые компоненты[В Delphi могут использоваться как видимые, так и невидимые компоненты. Невидимые компоненты не имеют визуального отображения на этапе прогона программы.] имеют свойства Left (Слева), тор (Сверху), width (Ширина) и Height (Высота), числовые значения которых определяют положение левого верхнего угла компонента и его размеры в так называемых пикселях, т. е. в минимальных по размеру точках экрана, светимостью которых может управлять программа. При буксировании компонента или изменении его размеров мышью эти значения автоматически меняются и наоборот - изменение этих свойств в окне Инспектора объектов приводит к соответствующему изменению положения и размеров компонента. В Delphi 4, 5 и 6 значения Left и тор автоматически появляются в небольшом окне рядом с указателем мыши при буксировке компонента по форме.

3.3. РЕАКЦИЯ НА СОБЫТИЯ

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

3.3.1. Модификация формы

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

Компонент кнопка изображается пиктограммой на странице standard палитры компонентов. Поместите этот компонент на форму и расположите его ниже метки и посередине формы (рис. 3.2).

3.3.2. Обработчик события OnClick

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

Рис. 3.2 Форма со вставленной кнопкой

Этот фрагмент должен представлять собой последовательность текстовых строк, в которых программист указывает, что именно должна делать программа в ответ на нажатие кнопки. Фрагмент оформляется в виде специальной подпрограммы языка Object Pascal - процедуры.

Чтобы заставить Delphi самостоятельно сделать заготовку для процедуры обработчика события OnClick, дважды подряд без заметной паузы щелкните мышью по вновь вставленному компоненту[У начинающего пользователя Windows не всегда получается двойной щелчок. Этот нехитрый прием широко используется и в Windows, и в Delphi, так что вам придется потренироваться в его освоении: без него Delphi не вставит заготовку процедуры, и вам понадобится самостоятельно писать не только этот текст, но и производить дополнительные манипуляции с Инспектором объектов и окном кода.]. В ответ Delphi активизирует окно кода, и вы увидите в нем такой текстовый фрагмент:

procedure TFormI.ButtonlClick(Sender: TObject);

begin

end;

Попробуем разобраться в том, что он содержит. Слово procedure извещает компилятор о начале подпрограммы-процедуры (в Delphi могут использоваться также подпрограммы-функции; в этом случае вместо procedure (процедура) используется слово function (функция); разницу между процедурами и функциями мы обсудим позже). За ним следует имя процедуры TFormi.ButtonlClick. Это имя - составное: оно состоит из имени класса TForm1 и собственно имени процедуры Button1Click.

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

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

type

TFormi = class(TForm)

Button1: TButton;

Label1: TLabel;

procedure ButtonlClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

varForm1: TForm1;

Строка

TForm1 = class(TForm)

определяет новый класс Tform1, который порожден от (создан по образцу) стандартного класса TForm. Строка

Form1: Tform1;

создает экземпляр этого класса с именем Formi. Стандартный класс TForm описывает пустое Windows-окно, в то время как класс TFormI описывает окно с уже вставленными в него компонентами метка и кнопка. Описание этих компонентов содержат строки

Button1: TButton;

Label 1: TLabel;

Они указывают, что компонент Buttoni (Кнопка!) представляет собой экземпляр стандартного класса TButton, а компонент Label 1 (Метка 1) - экземпляр класса TLabel.

За именем процедуры TFormi. Buttoniciick в круглых скобках следует описание параметра вызова

Sender: TObject

(параметр с именем Sender принадлежит классу TObject). Как мы увидим дальше, процедуры могут иметь не один, а несколько параметров вызова или не иметь их вовсе. Параметры вызова (если они есть) служат для настройки реализованного в процедуре алгоритма на выполнение конкретной работы. Параметр Sender вставлен Delphi “на всякий случай”: с его помощью подпрограмма Button1Click может при желании определить, какой именно компонент создал событие OnClick. Вся строка в целом

procedure TFormI.Buttoniciick(Sender: TObject);

называется заголовком процедуры. Ее завершает символ “;”. Этот символ играет важную роль в Object Pascal, т. к. показывает компи-- лятору на конец предложения языка. Из отдельных предложений составляется весь текст программы. В конце каждого предложения нужно ставить точку с запятой - это обязательное требование синтаксиса языка.

Три следующие строки определяют тело процедуры:

begin

end;

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

Для нас важно то обстоятельство, что каждый раз при нажатии кнопки Button1 управление будет передаваться в тело процедуры, а значит, между словами begin и end мы можем написать фрагмент программы, который будет выполняться в ответ на это событие.

Чтобы убедиться в этом, сделаем нашу кнопку “звучащей”: напишите в пустой строке между словами begin...end следующее предложение:

MessageBeep (МВ_ОК);

и сделайте прогон программы[Если вы работаете с версией Delphi 1, используйте строку MessageBeep (0);], предварительно включив звуковую систему компьютера. Теперь в ответ на нажатие кнопки Button1 в динамике компьютера будет раздаваться звуковой сигнал, т. к. вставленная строка реализует обращение к стандартной процедуре, которая умеет извлекать из динамика различные стандартные для Windows звуки.

3.3.3. Динамическое изменение свойств компонента

Поскольку кнопка Buttoni в нашей программе способна “звучать”, полезно изменить ее надпись: вместо умалчиваемой надписи Buttoni, которую автоматически формирует Delphi по имени компонента, назовем кнопку, например, “Звук”. Проще всего это сделать с помощью окна формы и Инспектора объектов, т. е. на этапе конструирования формы (для этого нужно просто изменить свойство caption компонента Button1 в окне Инспектора объектов), но для более полного знакомства с Delphi мы рассмотрим другой способ - динамического[К сожалению, эта процедура работает не всегда: если ваш ПК не оснащен звуковой картой, динамик будет молчать. В этом случае вместо MessageBeep (MB_OK) напишите просто Веер. Изменения на этапе конструирования называются статическими, а в ходе прогона программы - динамическими.] изменения надписи на этапе прогона программы. Для этого создадим обработчик события OnCreate (По созданию) для формы и изменим в нем это свойство.

Событие OnCreate возникает после создания windows-окна, но до появления этого окна на экране. Чтобы создать обработчик этого события, раскройте список компонентов в верхней части окна Инспектора объектов, выберите компонент Formi и дважды щелкните по свойству OnCreate на странице Events этого компонента (щелкать нужно по правой части строки oncreate). В ответ Delphi вновь активизирует окно кода и покажет вам заготовку для процедуры TForm1. FormCreate. Отредактируйте ее следующим образом:

procedure TFormi.FormCreate(Sender: TObject);

begin

Button1.Caption:= 'Звук';

end;

Единственная вставленная нами строка представляет собой так называемый оператор присваивания языка Object Pascal. В левой части оператора указывается свойство Buttoni. caption, а в правой части - значение ' звук ', которое мы хотим придать этому свойству. Связывает обе части комбинация символов “: =”, которая читается как “присвоить значение”. Символы “:=” всегда пишутся слитно, без разделяющих пробелов, хотя перед двоеточием и после знака равенства можно для лучшей читаемости программы вставлять пробелы, что мы и сделали. Как и любое другое предложение языка, оператор присваивания завершается точкой с запятой.

Составное имя Button1.Caption необходимо для точного указания компилятору, о каком свойстве идет речь: в нашей программе используются три компонента (включая саму форму), каждый из которых имеет свойство Caption; уточняющий префикс Button1 заставит изменить это свойство у кнопки, а не у метки или формы. Присваиваемое свойству значение является текстовой строкой. По правилам Object Pascal текстовая строка должна заключаться в обрамляющие апострофы. Внутри апострофов можно написать любое количество произвольных символов - именно они (без обрамляющих апострофов) будут определять новую надпись на кнопке.

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

3.4. НЕКОТОРЫЕ ИТОГИ

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

  • Процесс создания Delphi-программы разбивается на две фазы: фазу конструирования формы и фазу кодирования.
  • Конструирование формы осуществляется с помощью выбора компонентов из палитры и размещения их на форме.
  • Программист может перемещать любой размещенный на форме компонент и изменять его размеры с помощью мыши.
  • Чтобы придать компоненту нужные свойства, используется страница Properties Инспектора объектов.
  • Чтобы компонент мог откликаться на то или иное событие, программист должен создать обработчик события и указать его имя на странице Events Инспектора объектов.
  • Обработчик события оформляется в виде процедуры, имеющей составное имя. Первая часть имени представляет собой имя класса для формы, вторая часть отделяется от первой точкой и может быть произвольной. Если Delphi автоматически формирует заготовку для обработчика, то вторая часть имени представляет собой объединение имени компонента и имени события без предлога On.
  • Тело процедуры ограничено словами begin... end и состоит из отдельных предложений (операторов) языка Object Pascal. В конце каждого предложения ставится точка с запятой.
  • Свойства компонента могут изменяться на этапе прогона программы.


Глава 4. ЗНАКОМСТВО С КОМПОНЕНТАМИ

  • Глава 4. ЗНАКОМСТВО С КОМПОНЕНТАМИ
    • ОСТРАНИЦА STANDARD
    • СТРАНИЦА ADDITIONAL
    • СТРАНИЦА WIN32
    • СТРАНИЦА SYSTEM
    • СТРАНИЦА DIALOGS
    • СТРАНИЦА WIN 3.1
    • СТРАНИЦА SAMPLES
    • СТРАНИЦА ACTIVEX
    • КОМПОНЕНТЫ ДЛЯ РАБОТЫ С БАЗАМИ ДАННЫХ
      • Страница Data Access
      • Страница Data Controls
      • Страница dbExpress
      • Страница DataSnap
      • Страница BDE
      • Страница ADO
      • Страница InterBase
      • Страница Decision Cube
      • Страница QReport
    • КОМПОНЕНТЫ ДЛЯ ДОСТУПА К ИНТЕРНЕТ
      • Страница Internet
      • Страница FastNet
      • Страница WebServices
      • Страница WebSnap
      • Страницы Indy Clients, Indy Servers, Indy Misc
    • ДОСТУП К СЕРВЕРАМ АВТОМАТИЗАЦИИ

Глава 4

ЗНАКОМСТВО С КОМПОНЕНТАМИ

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

Библиотеки компонентов для разных версий Delphi строятся по принципу расширения: в первой версии было около 70 компонентов, в то время как в состав Delphi 6 входит более 300 компонентов.

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

4.1. СТРАНИЦА STANDARD

На странице Standard палитры компонентов сосредоточены стандартные для Windows интерфейсные элементы, без которых не обходится практически ни одна программа.

Frame - рама. Наравне с формой служит контейнером для размещения других компонентов. В отличие от формы может размещаться в палитре компонентов, создавая заготовки компонентов. Впервые введен в версию Delphi 5.

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

PopupMenu - вспомогательное или локальное меню. Обычно это меню появляется в отдельном окне после нажатия правой кнопки мыши.

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

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

Button - командная кнопка. Обработчик события OnClick этого компонента обычно используется для реализации некоторой команды.

CheckBox - независимый переключатель. Щелчок мышью на этом компоненте в работающей программе изменяет его логическое свойство Checked.

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

ListBox - список выбора. Содержит список предлагаемых вариантов (опций) и дает возможность проконтролировать текущий выбор.

ComboBox - комбинированный список выбора. Представляет собой комбинацию списка выбора и текстового редактора.

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

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

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

Panel - панель. Этот компонент, как и GroupBox, служит для объединения нескольких компонентов. Содержит внутреннюю и внешнюю кромки, что позволяет создать эффекты “вдавленности” и “выпуклости”.

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

4.2. СТРАНИЦА ADDITIONAL

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

BitBtn - командная кнопка с надписью и пиктограммой.

SpeedButton - пиктографическая кнопка. Обычно используется для быстрого доступа к тем или иным опциям главного меню.

MaskEdit - специальный текстовый редактор. Способен фильтровать вводимый текст, например, для правильного ввода даты.

StringGrid - таблица строк. Этот компонент обладает мощными возможностями для представления текстовой информации в табличном виде.

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

Image - рисунок. Этот компонент предназначен для отображения рисунков, в том числе пиктограмм и метафайлов.

Shape - фигура. С помощью этого компонента вы можете вставить в окно правильную геометрическую фигуру - прямоугольник, эллипс, окружность и т. п.

Bevel - кромка. Служит для выделения отдельных частей окна трехмерными рамками или полосами.

ScrollBox - панель с полосами прокрутки. В отличие от компонента Panel автоматически вставляет полосы прокрутки, если размещенные в нем компоненты отсекаются его границами.

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

Splitter - граница. Этот компонент размещается на форме между двумя другими видимыми компонентами и дает возможность пользователю во время прогона программы перемещать границу, отделяющую компоненты друг от друга. Впервые введен в версии 3.

StaticText - статический текст. Отличается от стандартного компонента Label наличием собственного windows-окна, что позволяет обводить текст рамкой или выделять его в виде “вдавленной” части формы. Впервые введен в версии 3.

ControiBar - полоса управления. Служит контейнером для “причаливаемых” компонентов в технологии Drag&Dock. Впервые введен в версии 4.

ApplicationEvents - получатель события. Если этот компонент помещен на форму, он будет получать все предназначенные для программы сообщения Windows (без этого компонента сообщения принимает глобальный объект-программа Application). Впервые введен в версии 5.

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

LabeledEdit - комбинация однострочного редактора и метки. Впервые введен в версии 6.

ColorBox - специальный вариант ComboBox для выбора одного из системных цветов. Впервые введен в версии 6.

Chart - диаграмма. Этот компонент облегчает создание специальных панелей для графического представления данных. Впервые введен в версии 3.

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

ActionMainMenuBar - полоса меню, опции которого создаются с помощью компонента ActionManager. Впервые введен в версии 6.

ActionToolBar - полоса для размещения пиктографических кнопок, создаваемых с помощью компонента ActionManager. Впервые введен в версии 6.

CustomizeDig - диалог настройки. С помощью этого компонента пользователь может по своему вкусу настроить интерфейс работающей программы. Впервые введен в версии 6.

4.3. СТРАНИЦА WIN32

Страница Win32 содержит интерфейсные элементы для 32-разрядных операционных систем Windows 95/98/NT/2000 (в версии 2 эта страница называется win 9 5). Этой страницы нет в версии 1.

TabControl - набор закладок. Каждая закладка представляет собой прямоугольное поле с надписью и/или рисунком. Выбор той или иной закладки распознается программой и используется для управления содержимым окна компонента.

PageControl - набор панелей с закладками. Каждая панель может содержать свой набор интерфейсных элементов и выбирается щелчком по связанной с ней закладке.

ImageList - набор рисунков. Представляет собой хранилище для нескольких рисунков одинакового размера.

RichEdit - многострочный редактор форматированного текста. В отличие от компонента Memo страницы Standard текст в компоненте RichEdit подчиняется правилам Расширенного Текстового Формата (RTF - Rich Text Format) и может изменять такие свои характеристики, как шрифт, цвет, выравнивание и т. д.

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

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

UpDown - цифровой регулятор. Две кнопки этого компонента предназначены для увеличения (верхняя) или уменьшения (нижняя) связанной с компонентом числовой величины.

HotKey - управляющая клавиша. Компонент используется для ввода управляющих клавиш, таких как F1, Alt+A, Ctrl+Shift+1 и т. п.

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

DateTimePicker - селектор времени/даты. Этот компонент предназначен для ввода и отображения даты или времени. Впервые введен в версии 3.

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

ListView - панель пиктограмм. Организует просмотр нескольких пиктограмм и выбор нужной. Этот компонент способен располагать пиктограммы в горизонтальных или вертикальных рядах и показывать их в крупном или мелком масштабе.

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

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

ToolBar - инструментальная панель. Этот компонент служит контейнером для командных кнопок BitBtn и способен автоматически изменять их размеры и положение при удалении кнопок или при добавлении новых. Впервые введен в версии 3.

CoolBar - инструментальная панель. В отличие от ToolBar используется как контейнер для размещения стандартных интерфейсных компонентов Windows, таких как Edit, ListBox, ComdoBox и т. д. Впервые введен в версии 3.

РageScroller - прокручиваемая панель. Служит для размещения узких инструментальных панелей. При необходимости автоматически создает по краям панели стрелки прокрутки. Впервые введен в версии 4.

CombоВохЕх - компонент в функциональном отношении подобен comboBox (страница standard), но может отображать в выпадающем списке небольшие изображения. Впервые введен в версии 6.

4.4. СТРАНИЦА SYSTEM

На этой странице представлены компоненты, которые имеют различное функциональное назначение, в том числе компоненты, поддерживающие стандартные для Windows технологии межпрограммного обмена данными OLE (Object Linking and Embedding -связывание и внедрение объектов) и DDE (Dynamic Data Exchange -динамический обмен данными). Технология DDE в современных программах полностью вытеснена гораздо более мощной технологией СОМ, и поэтому соответствующие им компоненты в книге не рассматриваются.

Timer - таймер. Этот компонент служит для отсчета интервалов реального времени.

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

MediaPlayer - мультимедийный проигрыватель. С помощью этого компонента можно управлять различными мультимедийными устройствами.

OleContainer - OLE-контейнер. Служит приемником связываемых или внедряемых объектов.

Компоненты этой страницы имеются во всех предыдущих версиях Delphi.

4.5. СТРАНИЦА DIALOGS

Компоненты страницы Dialogs реализуют стандартные для Windows диалоговые окна.

OpenDialog - открыть. Реализует стандартное диалоговое окно “Открыть файл”.

SaveDialog - сохранить. Реализует стандартное диалоговое окно “Сохранить файл”.

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

SavePictureDialog - сохранить рисунок. Реализует специальное окно сохранения графических файлов с возможностью предварительного просмотра рисунков.

FontDialog - шрифт. Реализует стандартное диалоговое окно выбора шрифта.

ColorDialog - цвет. Реализует стандартное диалоговое окно выбора цвета.

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

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

FindDialog - поиск. Реализует стандартное диалоговое окно поиска текстового фрагмента.

ReplaceDialog - замена. Реализует стандартное диалоговое окно поиска и замены текстового фрагмента.

Компоненты OpenPictureDialog И SavePictureDialog введены в версии 3, остальные имеются в предыдущих версиях. Разумеется, интерфейс окон для Windows 16 (версия 1) отличается от интерфейса Windows 32.

4.6. СТРАНИЦА WIN31

Большинство компонентов этой страницы введены для совместимости с версией 1. В современных программах вместо них рекомендуется использовать соответствующие компоненты страницы Win32.

TabSet - набор закладок. В приложениях для Windows 32 вместо него рекомендуется использовать компонент TabControl.

OutLine - дерево выбора. В приложениях для Windows 32 вместо него рекомендуется использовать компонент Treeview.

TabbedNotebook - набор панелей с закладками. В приложениях для Windows 32 вместо него рекомендуется использовать компонент PageControl.

Notebook - набор панелей без закладок. В приложениях для Windows 32 вместо него рекомендуется использовать компонент Page-Control.

Header - управляющий заголовок. В приложениях для Windows 32 вместо него рекомендуется использовать компонент Header-Control.

FileListBox - панель выбора файлов.

DirectorybistBox - панель выбора каталогов.

DiriveComboBox - панель выбора дисков.

FilterComboBox - панель фильтрации файлов.

Компоненты FileListBox, DirectoryListBox, DriveComboBox и FilterComboBox впервые появились в версии 3. Их функции реализованы элементами стандартных окон OpenDialog и SaveDialog, которые и рекомендуется использовать в Windows 32.

4.7. СТРАНИЦА SAMPLES

Эта страница содержит компоненты разного назначения.

Gauge - индикатор состояния. Подобен компоненту ProgressBar (страница Win32), но отличается большим разнообразием форм.

СolorGrid - таблица цветов. Этот компонент предназначен для выбора основного и фонового цветов из 16-цветной палитры.

SpinButton - двойная кнопка. Дает удобное средство управления некоторой числовой величиной.

SpinEdit - редактор числа. Обеспечивает отображение и редактирование целого числа с возможностью его изменения с помощью двойной кнопки.

DirectoryOutLine - список каталогов. Отображает в иерархическом виде структуру каталогов дискового накопителя.

Calendar - календарь. Предназначен для показа и выбора дня в месяце.

4.7.1 СТРАНИЦА ACTIVE X

Компоненты ActiveX являются “чужими” для Delphi: они создаются другими инструментальными средствами разработки программ (например, C++ или Visual Basic) и внедряются в Delphi с помощью технологии OLE. На странице ActiveX представлены лишь 4 из великого множества ActiveX-компонентов, разрабатываемых повсюду в мире компаниями - производителями программных средств и отдельными программистами. И хотя в книге технология ActiveX и компоненты этой страницы не рассматриваются, я привожу их краткое описание.

Chartfx - интерактивный график. Дает программисту удобное средство включения в программу интерактивных (диалоговых) графиков.

VSSpell - спеллер. Осуществляет орфографическую проверку правильности написания английских слов.

F1Book - электронная таблица. Позволяет создавать и использовать рабочие книги электронных таблиц, подобно тому как это делает MS Excel.

VtChart - мастер диаграмм. Обеспечивает мощные средства построения двух- и трехмерных диаграмм по результатам табличных вычислений.

4.8. КОМПОНЕНТЫ ДЛЯ РАБОТЫ С БАЗАМИ ДАННЫХ

В Delphi развиты средства построения приложений, рассчитанных на работу с электронными архивами (базами данных). Причем Delphi 6 предоставляет программисту возможность выбора способа доступа к данным: это может быть стандартный для ранних версий Delphi доступ с помощью машины баз данных BDE (Borland Data

base Engine), усиленно развиваемая Microsoft технология ADO ActiveX Data Objects), прямое управление сервером InterBase с помощью технологии IBExpress, наконец, технология dbExpress для непосредственного обращения к промышленным серверам MySQL, DB2, Oracle и некоторым другим.

4.8.1. Страница Data Access

В отличие от предыдущих версий на этой странице собраны компоненты, которые не зависят от используемого доступа к базе данных (большинство компонентов с этой страницы предыдущих версий перекочевали на страницу bde). Они в основном используются в так называемых трехзвенных БД (с сервером приложений). Часть компонентов известны по другим версиям Delphi, часть появилась в версии 6.

4.8.2. Страница Data Controls

15 компонентов этой страницы предназначены для визуализации данных, их ввода и редактирования. Многие компоненты этой страницы введены еще в версии 1.

4.8.3. Страница dbExpress

7 компонентов, представленных на этой странице, поддерживают технологию dbExpress прямого доступа к некоторым промышленным серверам баз данных. Все компоненты страницы впервые введены в версии 6.

4.8.4. Страница DataSnap

На этой странице сосредоточены компоненты, реализующие взаимодействие машин в локальной сети или Интернет в типичном для БД случае, когда клиент работает с удаленными данными. Часть компонентов известны по другим версиям Delphi, часть появилась в версии 6.

4.8.5. Страница BDE

Здесь представлены компоненты, поддерживающие доступ к данным с помощью BDE - Table, Query, StoredProc И Т. П. Механизм BDЕ в равной степени успешно работает как с файл-серверными, кис клиент-серверными БД. Компоненты этой страницы есть во всех версиях Delphi.

4.8.6. Страница ADO

Компоненты этой страницы в функциональном отношении во многом подобны компонентам страницы BDE, но поддерживают доступ к данным с помощью техологии ADO (ADOTable, ADOQuery,

ADostoredproc и т. д.). Все компоненты страницы впервые введены в версии 5.

4.8.7. Страница InterBase

“Родной” для Delphi сервер баз данных InterBase (производитель - InterBase Software Corporation - является дочерним предприятием Borland) имеет непосредственную поддержку в виде компонентов этой страницы. В них используется технология IBExpress, позволяющая отказаться от BDE, ADO или иных подобных механизмов доступа к данным. Все компоненты страницы впервые введены в версии 5.

4.8.8. Страница Decision Cube

На этой странице представлены компоненты для систем принятия решений на основании анализа многомерных наборов данных. Компоненты этой страницы впервые введены в версии 3.

4.8.9. Страница QReport

Около 30 компонентов страницы предназначены для упрощения создания отчетов по материалам, хранящимся в БД. Большинство компонентов страницы впервые введено в версии 2.

4.9. КОМПОНЕНТЫ ДЛЯ ДОСТУПА К ИНТЕРНЕТ

4.9.1. Страница Internet

Компоненты этой страницы обеспечивают средства связи программы с глобальной компьютерной сетью Интернет. Эта сеть позволяет установить соединение между двумя удаленными компьютерами, один из которых (клиент) получает информацию, а другой (сервер) передает ее. Оба компьютера должны следовать протоколу TCP/IP (Transport Control Protocol/Internet Protocol - транспортный управляющий протокол/Интернет протокол), обеспечивающему логическую независимость связи от аппаратных средств компьютеров. Частью Интернет является всемирная паутина World Wide Web

(WWW), использующая межкомпьютерный обмен так называемыми HTML-страницами (HyperText Markup Language - язык разметки гипертекста) на основе HTTP-протокола (HyperText Transfer Protocol - протокол передачи гипертекста). WWW реализует удобные средства для неформального общения мирового сообщества на са-мые разные темы. В то же время возможности Интернет не ограниваются только WWW, т. к. по глобальной сети можно передавать гктронную почту, разнообразные файлы, устраивать телеконференции и даже осуществлять телефонные переговоры. В последнее время усиленно развиваются так называемые интранет-сети, в ко-торых технология Интернет используется для передачи служебной и ювой информации в рамках одного или нескольких предприятий. для создания именно таких сетей и предназначены в основном компоненты этой страницы.

4.9.2. Страница FastNet

Компоненты этой страницы предоставляют программисту возможность использования различных протоколов для передачи деловых сообщений и данных по локальным и/или глобальным сетям, в том числе и по Интернет. В версиях 2, 3 и 4 они размещались на странице internet. В версии 1 таких компонентов нет. Для меж-ллатформенных программ вместо этих компонентов следует использовать компоненты страниц Indy (см. ниже).

4.9.3. Страница WebServices

Компоненты этой страницы поддерживают технологию SOAP Simple Object Access Protocol) для создания служб Web. Служба Web - это программа, запускаемая сервером Web в ответ на клиентское требование. Служба должна подготовить отклик, который она возвращает серверу, а тот - клиенту.

4.9.4. Страница WebSnap

На этой странице сосредоточены компоненты, развивающую известную из предыдущих версий технологию Web Server. В настоящей версии эти компоненты не поддерживают межплатформенные программы.

4.9.5. Страницы Indy Clients, Indy Servers, Indy Misc

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

4.10. ДОСТУП К СЕРВЕРАМ АВТОМАТИЗАЦИИ

Многочисленные компоненты страницы servers обеспечивают удобный программный доступ к популярным СОМ-серверам, входящим в Microsoft Office'97 и доступным на любом компьютере, на котором полностью или частично установлен комплект этих программ.

Поскольку в базовом языке MS Qffice'97 - Visual Basic for Application - произошли значительные изменения по сравнению с версией, использовавшейся в MS Office'95 компоненты этой страницы будут нормально работать только с MS Office'97

Использование этих компонентов осложняется двумя обстоятельствами. Во-первых, на вашей машине или в доступном вам сетевом окружении должны быть установлены соответствующие серверы (Word, Excel, PowerPoint и т. д.). Во-вторых, все эти компоненты представляют собой так называемые контроллеры Автоматизации, т. е. существеннейшим образом используют многочисленные свойства, методы и события своих серверов. Поскольку взаимодействие с такого рода серверами требует знания интерфейса сервера (см. гл. 15), каждый компонент, с одной стороны, непривычно одинаков, показывая в окне Инспектора объектов лишь минимум свойств, общих для всех серверов Автоматизации, а с другой - разительно отличается от остальных своим уникальным набором свойств, методов и событий. Рассмотрение этих наборов далеко выходит за рамки любой книги по Delphi, кроме того, работа с СОМ и Объектами Автоматизации также не является целью этого диска.

Эти компоненты не могут использоваться в межплатформенных приложениях (Linux не поддерживает СОМ).


Глава 5. ВВЕДЕНИЕ В OBJECT PASCAL

  • Глава 5. ВВЕДЕНИЕ В OBJECT PASCAL
    • УЧЕБНАЯ ПРОГРАММА
      • Конструирование формы
      • Перенос формы в архив
    • СТРУКТУРА ПРОГРАММ DELPHI
      • Структура проекта
      • Структура модуля
      • Элементы программы
    • ТИПЫ
      • Строковый и символьный типы
      • Целые типы
    • ОПЕРАТОРЫ ЯЗЫКА
      • Составной оператор и пустой оператор
      • Условный оператор
      • Операторы повторений
      • Оператор выбора
      • Метки и операторы перехода
    • МАССИВЫ
    • ПРОЦЕДУРЫ И ФУНКЦИИ

Глава 5

ВВЕДЕНИЕ В OBJECT PASCAL

В этой и последующих главах второй части книги мы будем изучать основной инструмент Delphi - язык Object Pascal. Назначение этой главы - предоставить вам возможность неформального знакомства с основными свойствами языка без точного описания и углубленного изучения его конструкций.

5.1. УЧЕБНАЯ ПРОГРАММА

При описании свойств языка мне понадобится иллюстрировать их небольшими примерами. В таких примерах чаще всего реализуются простые действия по вводу некоторых данных, их преобразованию и выводу результатов на экран. Для реализации этих действий создадим форму, которая будет содержать все необходимые для учебных примеров интерфейсные элементы. Чтобы эта форма была доступна в любой учебной программе, мы поместим ее в так называемый репозиторий Delphi - архив, в котором сохраняются заготовки для новых программ. Замечу, что в версии 1 нет репозитория. Тем не менее полезно и в этом случае создать учебную форму: чтобы избавить себя от повторения одних и тех же действий при реализации разных примеров. Сохраните ее в файле с именем Example, например, а при создании новой программы свяжите ее с проектом (опция File | Add) и сразу же сохраните под другим именем (File | Save As).

Вид формы для учебной программы показан на рис. 5.1. На форме размещена панель (трапе!), две кнопки (TBitBtn), строка ввода (TEdit), метка (TLabel) и многострочный редактор (тмето). Панель понадобилась для того, чтобы размещенные на ней кнопки, строка ввода и метка всегда располагались в нижней части окна, даже если окно распахнуто на весь экран. Левая кнопка ок служит для сообщения программе о том, что в строке ввода подготовлен текст. Обработчик события onciick этой кнопки будет тем самым местом программы, куда мы будем помещать учебные фрагменты. Правая кнопка close служит для завершения работы программы. Ее функции аналогичны функциям системной кнопки закрытия окна, и поэтому ее можно было бы и не размещать на форме. Однако я предпочитаю использовать крупную, бросающуюся в глаза кнопку, чтобы даже неопытный пользователь сразу увидел интерфейсный элемент, с помощью которого он сможет прекратить работу с программой. Метка располагается над строкой ввода и пока не видна на экране. Она предназначена для размещения коротких сообщений, которые будет формировать программа. Для вывода многострочных сообщений служит редактор TMеmо, занимающий все остальное место окна.

Рис. 5.1. Форма для учебной программы

5.1.1. Конструирование формы

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

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

Форма

Свойство | Значение
Caption Height Name Position Width | Учебная программа 375 fmExample poScreenCenter 544

Панель (компонент panel на странице Standard). Кладется на любое свободное место формы.

Свойство | Значение
Align BevelOuter Caption Height | AlBottom BvNone Удалить 84

Кнопка OK (компонент BitBtn на странице Additional). Кладется на любое свободное место панели.

Свойство | Значение
Kind | bkOk
Left | 32
Name | bbRun
Тор | 48

Кнопка close (компонент BitBtn на странице Additional). Кладется на любое свободное место панели.

Свойство | Значение
Kind | bkClose
Left | 128
Name | BbClose
Тор | 48

Строка ввода (компонент Edit на странице standard). Кладется на любое свободное место панели.

Свойство | Значение
Name | edinput
Left | 128
Text | Удалить
Top | 16
Width | 289

Метка (компонент Label на странице Standard). Кладется на любое свободное место формы.

Свойство | Значение
Align Caption Name | alBottom Удалить IbOutput

Многострочный редактор (компонент Memo на странице Standard). Кладется на любое свободное место формы.

Свойство | Значение
Align | alClient
Lines | Удалить
Name | mmOutput
ScrollBars | ssBoth
Wordwrap | False

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

Размеры формы Height (Высота) и Width (Ширина) совпадают с умалчиваемыми. Они выбраны так, чтобы форма не казалась слишком маленькой или слишком большой на экранах с разным разрешением - они оптимальны для типичного на сегодня разрешения 800х600. Свойство Position управляет размещением формы относительно границ экрана. Умалчиваемое значение poDesigned потребует разместить форму так, как она была размещена на этапе конструирования, рекомендуемое значение poScreencenter - симметрично по центру экрана.

Свойство Align указывает способ выравнивания размещаемого компонента относительно того контейнера, в котором он размещается. Контейнером называется компонент, предназначенный для размещения в нем других компонентов. Пустая форма представляет собой контейнер, в который помещаются другие компоненты. Таким же контейнером является компонент panel. Мы разместили на форме панель и в ее свойство Align установили значение alBottom (Внизу). После установки этого значения компонент (в данном случае - панель) прижимается к нижней границе формы и растягивается по всей ее длине. Теперь панель и все размещенные на ней компоненты будут примыкать к нижнему краю окна, даже если окно распахивается на весь экран. Если бы мы разместили строку ввода и кнопки не на панели, а непосредственно на форме, они не имели бы свойства перемещения и оставались бы на своих местах при изменении размеров окна [На самом деле это не совсем так. Компонент действительно не будет перемещаться при стандартных (умалчиваемых) значениях его свойства Anchors. Подробнее об этом см. п. 16.4.]. В результате значительная часть полностью распахнутого окна осталась бы пустой и не использовалась.

Свойство caption (Заголовок) присуще всем видимым элементам. Для панели оно определяет текст, который размещается в ее центре. Поскольку мы не намерены размещать на панели никаких текстовых сообщений, это свойство у нее следует очистить.

Чтобы панель и расположенная над ней метка воспринимались как единое целое, в свойстве BevelOuter (Внешняя кромка) панели установлено значение bvNone.

Свойства Left (Слева), тор (Сверху) и width (Ширина) определяют положение левого верхнего угла компонента относительно такого же угла своего контейнера и его (компонента) ширину. Указанные в списке значения этих свойств, для строки ввода обеспечат симметричное по горизонтали расположение строки относительно сторон не распахнутого окна (если, разумеется, вы установили рекомендуемую ширину формы 544). К сожалению, симметрия будет нарушена, если окно распахнется. В Delphi есть средства изменения значений указанных свойств непосредственно в работающей программе. Создав обработчик события ОnResize (По изменению размера) для формы, мы при желании могли бы научить программу реагировать на изменение размеров окна и соответствующим образом изменять значение свойства Left для строки ввода.

Свойство Name (Имя) определяет имя, под которым компонент будет известен программе. Я настоятельно рекомендую вам давать компонентам, на которые ссылается код программы, имена “со смыслом” вместо однотипных имен, которые “придумывает” Delphi. Более того, по принятому в современной практике программирования стилю именам обычно предшествует двухбуквенный префикс. Заложенная в префиксе мнемоника служит напоминанием программисту о типе поименованного компонента или его свойства. Например, все возможные значения свойства Align имеют стандартный для Delphi префикс а1 (от Align), за которым следует вполне понятное английское слово: alBottom, alTop, alLeft и т. д. Строка ввода будет непременным участником кода большинства учебных программ, вот почему я предлагаю назвать этот компонент именем edInput: префикс ed - это сокращение английского названия типа компонента Edit, слово Input означает ввод данных в программу. Такими же осмысленными являются предлагаемые имена для метки - от Label, output - вывод программы), многострочного редактора (mm - от Memo), кнопки OK (bb - ОТ BitBtn, Run - пуск программы) и кнопки close. Панель вряд ли будет упоминаться в программном коде, который вы будете писать, поэтому для нее можно оставить созданное Delphi умалчиваемое имя panel1.

Свойство Text (Текст) у компонента Edit определяет текст, который будет содержать строка ввода в момент появления на экране. Следует очистить это свойство, чтобы начальный текст не мешал вводу данных для программы. Точно так же следует очистить свойство Caption у метки и Lines у многострочного редактора. Чтобы очистить свойство mmOutput. Lines, щелкните по этому свойству в окне Инспектора объектов и вызовите диалоговое окно изменения свойства. Затем удалите умалчиваемый текст Memo1 (или mmoutput, если к этому моменту вы уже дали компоненту рекомендуемое имя) и закройте окно кнопкой OK.

Свойство ScrollBars у компонента mmOutput имеет значение ssBoth (Оба). Такое значение заставит Delphi поместить в редактор обе полосы прокрутки - вертикальную и горизонтальную. Наличие полос поможет вам увидеть текст, отсекаемый границами компонента. В то же время для свойства wordwrap (Перенос слов) советую установить значение False, чтобы отказаться от переноса слов на другую строку редактора в длинных текстовых сообщениях. Для этого компонента свойство Align имеет значение alciient, что потребует от компонента занять всю оставшуюся незаполненной часть формы независимо от размеров окна.

Для компонентов BitBtn свойство Kind (Сорт) определяет типовые функции, которые реализует кнопка в диалоговых окнах. Значение bkCiose определит типовую пиктограмму и надпись close, ко-торые Delphi разместит на кнопке, а также типовую функцию закрытия окна, которая будет связана с этой кнопкой. В результате без малейших усилий с нашей стороны мы получили кнопку, которая завершает работу программы. Значение bkOK заставит кнопку BitBtn закрывать диалоговое окно с результатом тгок. Поскольку наша форма не является диалоговым окном, кнопка ок не будет выполнять эту свою функцию: мы использовали значение bkOK только для того, чтобы кнопка получила нужную пиктограмму и надпись.

5.1.2. Перенос формы в архив

После указанных выше изменений сохраните форму в файле с именем Example в каталоге архива Delphi. Этот каталог имеет имя Objrepos и размещается в каталоге размещения Delphi (по умолчанию - c:\Program Files\Borland\Delphi6). Архив (репозиторий)

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

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

Рис. 5.2. Диалоговое окно регистрации формы в архиве

Не советую пользоваться опцией меню Project | Add то Repositiry, т. к. в этом случае в репозиторий будет добавлен проект, а не форма. Проекты из репозитория копируются целиком вместе с многочисленными родительскими классами, что замедляет время создания нового проекта и приводит к неоправданным затратам дисковой памяти.

В строке Title напишите имя формы fmExample, В Строке Description — пояснение, например, прототип главной формы для учебной программы, раскройте список page и выберите в нем Forms. Если хотите, укажите в строке Author свою фамилию и закройте окно регистрации кнопкой ок. Теперь форма зарегистрирована, и вы в любой момент сможете выбрать ее с помощью опции File | New | other главного меню Delphi. Однако я предлагаю не успокаиваться на этом и определить нашу форму как главную, тогда Delphi при выборе опции File | New | Application будет именно ее показывать вместо пустой типовой формы. Для этого с помощью опции Tools | Repository главного меню вызовите диалоговое окно, показанное на рис. 5.3.

Рис. 5.3. Окно редактора архива

В списке pages этого окна щелкните по строке Forms и разыщите в самом конце списка объектов objects нашу форму fmExampie. Щелкните по ней, установите флажок в переключателе Main Form (Главная форма) и закройте окно клавишей ок. Когда нам в дальнейшем понадобится создавать проекты, основанные на стандартных пустых формах, вам потребуется вновь вызвать опцию Tools | Repository и убрать флажок в переключателе Main Form.

Чтобы убедиться в правильности ваших действий, закройте проект, ответив нажатием на кнопку No в ответ на предложение Delphi сохранить файл проекта, и выберите File | New | Application-на экране должна появится уже знакомая нам форма учебной программы.

5.2. СТРУКТУРА ПРОГРАММ DELPHI

Любая программа в Delphi состоит из файла проекта (файл с расширением dpr) и одного или нескольких модулей (файлы с расширениями pas). Каждый из таких файлов описывает программную единицу Object Pascal.

5.2.1. Структура проекта

Файл проекта представляет собой программу, написанную на языке Object Pascal и предназначенную для обработки компилятором. Эта программа автоматически создается Delphi и содержит лишь несколько строк. Чтобы увидеть их, запустите Delphi и щелкните по опции Project | View Source главного меню [В предыдущих версиях Delphi для просмотра кода проекта используйте опцию View | project Source.]. Delphi покажет окно кода с закладкой Project1, содержащее такой текст:

program Projecti;

uses

Forms, Unit1 in 'Unit1.pas' {fmExample};

{$R *.RES}

begin

Application.Initialize;

Application.CreateForm(TfmExample, fmExample);

Application.Run;

end.

В окне кода жирным шрифтом выделяются так называемые зарезервированные слова, а курсивом - комментарии (так же выделяются зарезервированные слова и комментарии в книге). Как видим, текст программы начинается зарезервированным словом program и заканчивается словом end с точкой за ним. Замечу, что сочетание end со следующей за ней точкой называется терминатором программной единицы: как только в тексте программы встретится такой терминатор, компилятор прекращает анализ программы и игнорирует оставшуюся часть текста.

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

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

{$R *.RES}

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

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

{Это комментарий}

(*Это тоже комментарий*)

//Все символы до конца этой строки составляют комментарий

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

uses

Forms, Unit1 in 'Unitl.pas' {fmExample};

указывают, что помимо файла проекта в программе должны использоваться модули Forms И Unit1. модуль Forms является стандартным (т. е. уже известным Delphi), а модуль Unit1 - новым, ранее неизвестным, и Delphi в этом случае указывает также имя файла с текстом модуля (in 'uniti.pas') и имя связанного с модулем файла описания формы {fmExample}.

Собственно тело программы начинается со слова begin (начать) и ограничивается терминатором end с точкой. Тело состоит из нескольких операторов языка Object Pascal. В каждом операторе реализуется некоторое действие - изменение значения переменной, анализ результата вычисления, обращение к подпрограмме и т. п. В теле нашей программы - три исполняемых оператора:

Application.Initialize;

Application.CreateForm(TfmExample, fmExample);

Application.Run;

Каждый из них реализует обращение к одному из методов объекта Application. Я вынужден забежать вперед и хотя бы кратко пояснить только что введенные термины, которые определяют важные для Object Pascal понятия. Объектом называется специальным образом оформленный фрагмент программы, заключающий в себе данные и подпрограммы для их обработки. Данные называются полями объекта, а подпрограммы - его методами. Объект в целом предназначен для решения какой-либо конкретной задачи и воспринимается в программе как неделимое целое (иными словами, нельзя из объекта “выдернуть” отдельное поле или метод). Объекты играют чрезвычайно важную роль в современных языках программирования. Они придуманы для того, чтобы увеличить производительность труда программиста и одновременно повысить качество разрабатываемых им программ. Два главных свойства объекта - функциональность и неделимость - делают его самостоятельной или даже самодостаточной частью программы и позволяют легко переносить объект из одной программы в другую. Разработчики Delphi придумали для нас с вами сотни объектов, которые можно рассматривать как кирпичики, из которых программист строит многоэтажное здание программы. Такой принцип построения программ называется объектно-ориентированным программированием (ООП). В объекте Application собраны данные и подпрограммы, необходимые для нормального функционирования Windows-программы в целом. Delphi автоматически создает объект-программу Application для каждого нового проекта. Строка

Application.Initialize;

означает обращение к методу Initialize объекта Application. Прочитав эту строку, компилятор создаст код, который заставит процессор перейти к выполнению некоторого фрагмента программы, написанного для нас разработчиками Delphi. После выполнения этого фрагмента (программисты говорят: после выхода из подпрограммы) управление процессором перейдет к следующей строке программы, в которой вызывается метод CreateForm и т. д.

5.2.2. Структура модуля

Модули - это программные единицы, предназначенные для размещений фрагментов программ. С помощью содержащегося в них программного кода реализуется вся поведенческая сторона программы. Любой модуль имеет следующую структуру [Более точное описание структуры модуля приводится в п. 12.1.]: заголовок секция интерфейсных объявлений секция реализации терминатор Заголовок открывается зарезервированным словом Unit за которым следует имя модуля и точка с запятой. Секция интерфейсных объявлений открывается зарезервированным словом Interface, a секция реализации - словом implementation. Терминатором модуля, как и терминатором программы, является end с точкой. Следующий фрагмент программы является синтаксически правильным вариантом модуля:

unit Unit1;

interface

// Секция интерфейсных объявлений

implementation

// Секция реализации

end.

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

Щелкните по закладке Unit1 окна кода, и вы увидите такой текст:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,

Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panel1: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

private

{ Private declarations } public

{ Public declarations } end;

var

fmExample: TfmExample;

implementation

$R *.DFM}

end.

Весь этот текст сформирован Delphi, но в отличие от файла проекта программист может его изменять, придавая программе нужную функциональность. В интерфейсной секции описан один тип (класс - fmExample) и один объект (переменная fmExample).

Вот описание класса:

type

TfmExample = class(TForm)

Panell: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

private

{ Private declarations } public

{ Public declarations } end;

Как мы увидим дальше, классы служат основным инструментом реализации мощных возможностей Delphi. Класс является образцом, по которому создаются объекты, и наоборот, объект - это экземпляр реализации класса. Образцы для создания элементов программы в Object Pascal называются типами, таким образом, класс TfmExamplel -это тип. Перед его объявлением стоит зарезервированное слово type (тип), извещающее компилятор о начале раздела описания типов.

Стандартный класс TForm реализует все нужное для создания и функционирования пустого Windows-окна. Класс TfmExamplel порожден от этого класса, о чем свидетельствует строка

TfmExample = class(TForm)

в которой за зарезервированным словом class в скобках указывается имя родительского класса. Термин “порожден” означает, что класс TfmExample унаследовал все возможности родительского класса TForm и добавил к ним собственные в виде дополнительных компонентов, которые, как вы помните, мы вставили в форму fmExample. Перечень вставленных нами компонентов и составляет значительную часть описания класса.

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

Объект fmExampie формально относится к элементам программы, которые называются переменными. Вот почему перед объявлением объекта стоит зарезервированное слово var (от англ. variables - переменные).

Текст модуля доступен как Delphi ,так и программисту. Delphi автоматически вставляет в текст модуля описание любого добавленного к? форме компонента, а также создает заготовки для обработчиков событии; программист может добавлять свои методы в ранее объявлённые классыйнаполвять обработчики событий конкретным .содержа,нием, вставлять собственные переменные, типы, константы и т. д. Совместное с Delphi владение текстом модуля будет вполне успешным, если программист будет соблюдать простое правило , он не должен удалять или изменять строки которые вставлены не им, а Delphi.

5.2.3. Элементы программы

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

  • зарезервированные слова;
  • идентификаторы;
  • типы;
  • константы;
  • переменные;
  • метки;
  • подпрограммы;
  • комментарии.

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

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

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

Константы определяют области памяти, которые не могут изменять своего значения в ходе работы программы. Как и любые другие элементы программы, константы могут иметь свои собственные имена. Объявлению имен констант должно предшествовать зарезервированное слово const (от англ. constants - константы). Например, мы можем определить константы const

Kbyte = 1024;

Mbyte = Kbyte*Kbyte;

Gbyte = 1024*Mbyte;

чтобы вместо длинных чисел

1048576 (1024*1024) и 1073741824

(1024*1024*1024) писать, соответственно, Mbyte и Gbyte. Тип константы определяется способом ее записи и легко распознается компилятором в тексте программы, поэтому программист может не использовать именованные константы (т. е. не объявлять их в программе явно).

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

var

inValue: Integer;

byValue: Byte;

Здесь идентификатор inValue объявляется как переменная типа integer, а идентификатор byValue - как переменная типа Byte. Стандартный (т. е. заранее определенный в Object Pascal) тип integer определяет четырехбайтный участок памяти, содержимое которого рассматривается как целое число в диапазоне от -2 147 483 648 до+2 147 483 647, а стандартный тип Byte - участок памяти длиной 1 байт, в котором размещается беззнаковое целое число в диапазоне от 0 до 2554.[Все приводимые в книге сведения относительно диапазона возможных значений и объема памяти стандартных типов относятся к Delphi 32. Для 16-разрядной версии 1 эти величины имеют другие значения, например, тип Integer в версии 1 занимает 2 банта и имеет диапазон значении от -32 768 до+32 767.]

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

label

Loop;

begin

Goto Loop;

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

// оператору, помеченному меткой Loop. .....

// Эти операторы будут пропущены

Loор:

// Оператору, идущему за этой меткой,

.....

// будет передано управление

end;

Подпрограммы - это специальным образом оформленные фрагменты программы. Замечательной особенностью подпрограмм является их значительная независимость от остального текста программы. Говорят, что свойства подпрограммы локализуются в ее теле. Это означает, что, если программист что-либо изменит в подпрограмме, ему, как правило, не понадобится в связи с этим изменять что-либо вне подпрограммы. Таким образом, подпрограммы являются средством структурирования программ, т. е. расчленения программ на ряд во многом независимых фрагментов. Структурирование неизбежно для крупных программных проектов, поэтому подпрограммы используются в Delphi-программах очень часто.

В Object Pascal есть два сорта подпрограмм: процедуры и функции. Функция отличается от процедуры только тем, что ее идентификатор можно наряду с константами и переменными использовать в выражениях, т. к. функция имеет выходной результат определенного типа. Если, например, определена функция

Function MyFunction: Integer;

и переменная var

X: Integer;

то возможен такой оператор присваивания:

Х := 2*MyFunction-l;

Имя процедуры нельзя использовать в выражении, т. к. процедура не имеет связанного с нею результата:

Procedure MyProcedure;

…

X := 2*MyProcedure-l; // Ошибка!

5.3. ТИПЫ

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

5.3.1. Строковый и символьный типы

Свое знакомство с типами мы начнем со строкового типа String. Этот тип определяет участок памяти переменной длины, каждый байт которого содержит один символ. Для символов в Object Pascal используется тип Char, таким образом, String - это цепочка следующих друг за другом символов Char. Каждый символ в String пронумерован, причем первый символ имеет номер 1. Программист может обращаться к любому символу строки, указывая его порядковый номер в квадратных скобках сразу за именем переменной:

var // Начало раздела описания переменных

S: String;

// Объявление переменной строкового типа

begin

// Начало раздела исполняемых операторов

S := 'Строка символов';

// Переменная S содержит

// значение ”Строка символов”

S[6] := 'и'; // Теперь переменная содержит значение

// ”Строки символов”

end;

// Конец раздела исполняемых операторов

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

2*S-1

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

В первом операторе присваивания в переменную s будет помещено значение строковой константы строка символов'. Строковые константы содержат произвольные символы, заключенные в обрамляющие апострофы, причем сами апострофы не входят в значение константы, поэтому после присваивания переменная примет значение Строка символов без апострофов (если понадобится включить в текстовую константу апостроф, он удваивается: ' символ ' ' - апостроф'). После первого присваивания s будет занимать участок памяти длиной 15 байт - по одному байту на каждый символ значения[ На самом деле переменная строкового типа всегда имеет длину 4 байта, которые используются для указания на область памяти, в которой помещается сама строка символов. Подробнее о механизме использования памяти строковыми переменными см. п. 6.3.1.]. Переменность размера области памяти, выделяемой для размещения строки символов, - характерная особенность типа string. Если бы, например, во втором операторе мы обратились не к 6-му по счету символу, а ко всей строке в целом:

S := 'и';

эта переменная стала бы занимать 1 байт, а следующие за ним 14 байтов оказались бы свободными. Длина строковой переменной в программе меняется автоматически при каждом присваивании переменной нового значения и может составлять от 0 до 2 Гбайт[Для версии Delphi 1 тип String соответствует “короткой” строке, длина которой не может превышать 255 байт.] (1 Гигабайт (Гбайт) соответствует константе Gbyte на стр. 84). Над строковым типом определена операция сцепления (+):

S := 'Object'+' Pascal';// S содержит “Object Pascal”

Кроме того, строки можно сравнивать с помощью таких операций отношения:

Операция | Смысл
= | Равно
<> | Не равно
> | Больше
>= | Больше или равно
< | Меньше
<= | Меньше или равно

Примечание. Символы <>, <= и >= пишутся слитно, их нельзя разделять пробелами иди комментариями.

Результат применения операции сравнения к двум строкам имеет логический тип, который характеризуется двумя возможными значениям: True (Истина; читается тру) и False (Ложь; читается фоле). Строки сравниваются побайтно, слева направо; каждая пара символов сравнивается в соответствии с их внутренней кодировкой (см. гл. 7).

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

Учебная программа COPY.TXT

Создадим программу, в которой текст из строки ввода edinput будет копироваться без изменений в метку lboutput и редактор mmoutput. Для этого загрузите Delphi и выберите опцию File | New | Application [Как уже говорилось в гл. 2, имеет смысл для каждой учебной программы создавать свой подкаталог и размещать в нем все связанные с программой файлы. Если вы еще не сделали этого, минимизируйте Delphi и создайте с помощью Проводника подкаталог COPYTXT в каталоге для ваших учебных программ. В дальнейшем я не буду напоминать вам о необходимости создания подкаталогов, однако помните, что, если вы не будете этого делать, очень скоро ваш учебный каталог станет представлять собой хаотическое нагромождение никак не связанных друг с другом файлов.].

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

procedure TfmExample.bbRunClick(Sender: TObject);

begin

lbOutput.Caption := edInput.Text;

// Повторяем текст в метке mmOutput.Lines.Add(edInput.Text);

// и в многострочном редакторе

edInput.Text := '';

// Очищаем строку ввода edInput.SetFocus;

// Передаем ей фокус ввода

end;

Нажмите F9 для прогона программы, сохраните модуль под именем CopyTxtU.pas, а проект в целом - под именем copyTxt.dpr, и вы увидите окно, показанное на рис. 5.4.

Рис. 5.4. Окно программы CopyTxt

Комментарий к программе

Вводимый текст хранится в свойстве Text компонента edInput. После выполнения первого оператора присваивания он будет перенесен в свойство caption компонента IbOutput и тут же появится на экране над строкой ввода.

Многострочньш редактор mmOutput способен сохранять и отображать на экране множество строк. Все они содержатся в его свойстве Lines, относящемуся к классу TStringList (подробнее об этом классе см. п. 12.1). Метод Add класса TStringList добавляет новую строку к имеющемуся в Lines набору строк. Добавленная с помощью второго оператора строка тут же отображается на экране. С помощью оператора

edInput.Text := ' ' ;

строка ввода очищается и подготавливается к вводу нового текста. Двойной апостроф в правой части оператора указывает “пустую” строку или, что то же, строку нулевой длины. Завершает обработчик оператор

edinput.SetFocus ;

с помощью которого строка edinput получает фокус ввода. Термином “фокус ввода” в Delphi фактически обозначается клавиатура”* “Получить фокус ввода” означает связать указанный компонент с клавиатурой. В окне программы может быть несколько компонентов, способных принимать и обрабатывать клавиатурный ввод (в нашей программе таких компонентов четыре: редактор mmoutput, строка edinput и кнопки bbRun и bbciose). Каждый из таких компонентов имеет метод SetFocus, с помощью которого программа может передать компоненту контроль над клавиатурой. Чтобы оценить действия, реализуемые оператором edinput. SetFocus, поставьте в самом начале оператора признак комментария (символы “//”), сделайте новый прогон программы и введите с ее помощью хотя бы 2-3 строки.

Замечу, что в момент появления окна программы на экране строка edinput не имеет фокуса ввода, что затрудняет ввод первой строки. Попробуйте самостоятельно создать обработчик события опАс-tivate для формы fmExampie и в нем установить фокус ввода для строки edinput. (Подсказка: раскройте список выбора в верхней части окна Инспектора объектов, выберите в нем компонент fmExampie и дважды щелкните по правой колонке свойства OnActivate на странице Events этого окна.)

5.3.2. Целые типы

Целые типы используются для хранения и преобразования целых чисел. В Object Pascal предусмотрено несколько целочисленных типов, отличающихся диапазоном возможных значений. В этой главе мы будем использовать тип integer, занимающий в памяти 4 смежных байта и предназначенный для хранения целых чисел в диапазоне от -2 147 483 648 до +2 147 483 6478. Более полные сведения о целых типах вы найдете в гл. 7.

Над целыми числами определены следующие математические операции:

+ | + сложение
- | вычитание
* | умножение
div | деление с отбрасыванием остатка
mod | получение остатка от деления

Спецификой деления является то обстоятельство, что результат может иметь дробный вид: 1/2, 2*3/5 и т. п. Для хранения дробных чисел в Object Pascal используются вещественные типы (см. п. 7.3.3), вот почему в языке имеются целых две операции деления (div и mod):

var

X,Y: Integer;

begin

X := 5 div 2; // X содержит 2

Y := 5 mod 2; // Y содержит 1

end;

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

var

X: Integer;

begin

X := 2147483647;

X := X + 1; // Ошибка! Результат +2 147 483 648 выходит

// из допустимого диапазона.

end;

Как и к строкам, к целым числам применимы операции сравнения.

Учебная программа INTMULT

В описываемой ниже программе пользователь вводит два целых числа, а программа вычисляет и показывает их произведение. В этой программе мы также познакомимся с преобразованием типов. На рис. 5.5. показан вид окна работающей программы.[В версии Delphi 1 тип Integer занимает 2 байта и содержит число в диапазоне от -32 768 до +32 767.]

Рис. 5.5. Окно программы IntMult

Прежде всего обсудим две проблемы, связанные с реализацией программы:

  1. как известить программу о вводе каждого из операндов;
  2. что будет, если пользователь подготовит неверную запись целого числа (например “12.3”).

Суть первой проблемы состоит в том, что подготовленная нами учебная форма fmExampie имеет только одну строку ввода edinput и одну кнопку bbRun (кнопка bbClose используется для прекращения работы программы и эту ее функциональность не следует изменять). Мы могли бы написать программу, которая вводит строку типа

2*3

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

Подводя итог, я предлагаю поместить на форму fmExample новую кнопку BitBtn (страница Additional галереи компонентов) так, чтобы она полностью закрыла собой имеющуюся кнопку bbRun, назвать новую кнопку bbResuit (свойство Name), установить для нее сорт (свойство Kind) bkOK и сделать ее временно невидимой (поместить в свойство visible значение False). После такой подготовки в момент начала работы новая кнопка не будет видна и не помешает пользователю щелкнуть по кнопке bbRun. В обработчике события bbRun-click следует спрятать кнопку bbRun и показать кнопку bbResuit, чтобы пользователь смог подтвердить ввод второго операнда. Сделайте необходимые изменения формы для нового проекта, а я пока перейду к обсуждению второй проблемы.

Суть второй проблемы заключается в том, что пользователь может случайно или преднамеренно ввести строку, которая не содержит правильного представления целого числа. Преобразование такой строки к целому числу может привести к аварийному завершению работы программы. Существует множество способов решения этой проблемы, я предлагаю наиболее простой: заменить обычный однострочный редактор edinput на специализированный, способный контролировать ввод пользователя. Удалите из формы редактор edinput (щелкните по нему мышкой и нажмите клавишу Delete) и поместите на его место компонент MaskEdit (страница Additional). Дайте ему прежнее имя edinput (свойство Name) и раскройте диалоговое окно свойства EditMask (рис. 5.6). Это свойство указывает маску компонента, т. е. определяет, какие символы и в какой последовательности может набирать пользователь в окне ввода.

Рис. 5.6. Окно определения маски компонента MaskEdit

В строке Input Mask введите

#99999999;1;

В окошке Character for Blanks вместо умалчиваемого символа-заполнителя “_” укажите пробел и закройте окно кнопкой ок. Сформированная нами маска заставит компонент автоматически контролировать ввод пользователя и отвергать любые символы, кроме знаков + или - в первой позиции (элемент маски “#”) и цифр (элементы “9”).

Теперь все готово к кодированию программы.

Дважды щелкните по кнопке bbResuit и введите такой код для обработчика события OnClick этой кнопки:

procedure TfniExample.bbResultClick( Sender: TObject) ;

// Обработка ввода 2-го операнда

begin // Преобразуем текст из edinput в целое число:

Y := StrToInt(Trim(edinput.Text));

// Сообщаем в Memo о вводе 2-го операнда:

mmOutput.Lines.Add('2-й операнд:'+edInput.Text);

// Вычисляем и показываем результат:

mmOutput.Lines.Add('Результат: '+ IntToStr(X)+' * '+IntToStr(Y)+' = '+IntToStr(X*Y));

edinput.Text := ''; // Очищаем ввод

edinput. SetFocus;

// и возвращаем ему фокус

IbOutput.Caption := 'Введите 1-й операнд; bbResuit.Hide;

// Прячем кнопку

bbResuit bbRun.Show;

// и показываем bbRun

end;

Для ввода обработчика события onciick кнопки bbRun нужно сначала с помощью списка выбора в верхней части окна Инспектора объектов отыскать и выбрать компонент bbRun (напомню, он полностью закрыт кнопкой bbResuit), затем дважды щелкнуть в правом столбце строки onciick на странице Events и ввести такой код:

procedure TfmExample.bbRunClick(Sender: TObject);

// Обработка ввода 1-го операнда

begin

// Преобразуем текст из edinput в целое число

Х := StrToInt(Trim(edinput.Text));

// Сообщаем в Memo о вводе 1-го операнда

mmOutput.Lines.Add('1-й операнд: '+edInput.Text);

edinput.Text := ''; // Очищаем ввод

edInput.SetFocus;// и возвращаем ему фокус

IbOutput.Caption := 'Введите 2-й операнд:';

bbResult.Show; // Показываем кнопку bbResult

bbRun.Hide; // и прячем bbRun

end;

Осталось передать фокус ввода компоненту edinput в момент старта программы и определить переменные х и y для хранения операндов. Разыщите в самом начале окна кода программы описание класса TfmExample и отредактируйте его следующим образом:

private

{ Private declarations } X,Y: Integer;.

Затем с помощью списка выбора в окне Инспектора объектов выберите форму fmExampie и после двойного щелчка на строке опАс-tivate страницы Events введите такой обработчик события:

procedure TfmExample.FormActivate(Sender: TObject);

// Подготовка к началу работы

begin

edInput.SetFocus; // Передаем фокус ввода редактору edinput

IbOutput.Caption := 'Введите 1-й операнд:';

end;

Комментарий к программе

С помощью строк

private

{ Private declarations }

X,Y: Integer;

мы определили в классе TfmExample две целочисленные переменные Х и Y, которые после этого стали доступны любым методам этого класса, в том числе обработчикам событий OnClick. Такого рода переменные (их называют полями класса) облегчают взаимодействие методов друг с другом. В нашем случае необходимость в них возникла по той причине, что первый операнд (переменная X) вводится в одном методе (bbRunciick), а используется в другом

(bbResultClick).

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

StrToInt(Trim(edinput.Text))

Это выражение состоит из вызова стандартных для Object Pascal функций: сначала вызывается функция Trim , которая возвращает строку-аргумент (в нашем случае edinput.Text) без обрамляющих ее (т. е. ведущих и ведомых) пробелов9; затем с помощью вызова StrToint строка символов преобразуется к целому числу.

Функция преобразования StrToint очень чувствительна к возможным ошибкам символьного представления целого числа. Большая часть возможных ошибок в нашей программе блокируется фильтрующими возможностями редактора MaskEdit и функцией Trim. Программа аварийно завершится, если при щелчке по кнопкам bbRun или bbResuit редактор не содержит текста. Чтобы избежать этого, перед вызовом StrToint вставьте

if edinput.Text =''' then Exit;

Этот оператор прекращает дальнейшую работу обработчика в случае, если в окне ввода компонента edinput нет текста.

Методы Show и Hide компонентов-кнопок соответственно показывают или прячут компонент. Спрятанный компонент никак не участвует во взаимодействии с пользователем.

5.4. ОПЕРАТОРЫ ЯЗЫКА

С одним из наиболее часто используемых операторов языка Object Pascal - оператором присваивания мы уже познакомились (см. п. 3.3.3). Ниже рассматриваются остальные операторы.

5.4.1. Составной оператор и пустой оператор

Составной оператор - это последовательность произвольных операторов программы, заключенная в операторные скобки - зарезервированные слова begin ... end. Составные операторы - важный инструмент Object Pascal, дающий возможность писать программы по современной технологии структурного программирования (без операторов перехода goto).

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

Функция Trim не определена в версии 1. Вместо указанной единственной строки в этом случае

следует написать:[while post' ', edinput.Text)>0 do

system.delete(edinput.Text, post' ', edinput.Text), 1);

StrToint(edinput.Text);]

begin

begin

begin

end;

end;

end;

Фактически весь раздел операторов, обрамленный словами begin ... end, представляет собой один составной оператор. Поскольку зарезервированное слово end является закрывающей операторной скобкой, оно одновременно указывает и конец предыдущего оператора, поэтому ставить перед ним символ “;” необязательно. Наличие точки с запятой перед end в предыдущих примерах означало, что между последним оператором и операторной скобкой end располагается пустой оператор. Пустой оператор не содержит никаких действий, просто в программу добавляется лишняя точка с запятой. В основном пустой оператор используется для передачи управления в конец составного оператора: как и любой другой, пустой оператор может быть помечен, и ему можно передать управление.

5.4.2. Условный оператор

Условный оператор позволяет проверить некоторое условие и в зависимости от результатов проверки выполнить то или иное действие. Таким образом, условный оператор - это средство ветвления вычислительного процесса.

Структура условного оператора имеет следующий вид:

if <условие> then <оператор1> else <оператор2>;

где if/ then/ else - зарезервированные слова (если, то, иначе);

<условие> - произвольное выражение логического типа;

<оператор1>, <оператор2> - любые операторы языка Object Pascal.

Условный оператор работает по следующему алгоритму. Вначале вычисляется условное выражение <условие>. Если результат есть True (истина), то выполняется <оператор1>, а <оператор2> пропускается; если результат есть False (ложь), наоборот, <оператор1> пропускается, а выполняется <оператор2>. Например:

var

X, Y, Max: Integer;

begin .

if X > Max then

Y := Max else

Y := X;

….

end;

При выполнении этого фрагмента переменная y получит значение переменной х, если только это значение не превышает мах, в противном случае y станет равно мах.

Условными называются выражения, имеющие одно из двух возможных значений: истина или ложь. Такие выражения чаще всего получаются при сравнении переменных с помощью операций отношения =, <>, >, >=, <, <=. Сложные логические выражения составляются с использованием логических операций and (логическое И), or (логическое ИЛИ) и not (логическое НЕ). Например:

if (а > b) and (b <> 0) then ...

В отличие от других языков программирования в Object Pascal приоритет операций отношения меньше, чем у логинеёкйх" операции, по этому отдельные составные части сложного логического вьфажёния5 заключаются в скобки. Например, такая запись предыдущего оператора будет неверной:

if a>b and b <> 0 then ...// Ошибка так как фактически (с учетом приоритета операции) компилятор будет транслировать такую строку:

if a>(b and b)<>0 then...

Часть else <оператор2> условного оператора может быть опущена. Тогда при значении True условного выражения выполняется <оператор1>, в противном случае этот оператор пропускается:

var

X, Y, Max: Integer;

begin

if X > Мах then Мах := X;

Y := X;

end;

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

Поскольку любой из операторов <оператор1> и <оператор2> может быть любого типа, в том числе и условным, а в то же время не каждый из “вложенных” условных операторов может иметь часть else <оператор2>, то возникает неоднозначность трактовки условий. Эта неоднозначность в Object Pascal решается следующим образом:

любая встретившаяся часть else соответствует ближайшей к ней сверху по тексту программы части then условного оператора. Например:

var

a,b,c,d : Integer;

begin a := 1;

b := 2;

с := 3;

d := 4;

if a < b then // Да

if с < d then // Да

if с < 0 then // Нет

с := 0 // Этот оператор не выполняется

else

а := b; // а равно 2

if а < b then // Да

if с < d then // Да

if с < 0 then // Нет

с := 0 // с равно 0

else //if с < О else //if с < d

else // If a < b

а := b; // Этот оператор не выполняется

end;

Учебная программа INTTEST

Игра “Угадай число”: программа случайным образом выбирает целое число в диапазоне О... 1000 и запоминает его. Пользователь пытается угадать его и вводит свое число. Сравнив ввод с запомненным числом, программа сообщает - больше, меньше или равно введенное число запомненному. Ввод продолжается до угадывания, после чего программа предлагает сыграть еще раз.

Поскольку пользователь вводит только числа, изменим форму fmExample: как и в предыдущей программе intmult вместо компонента edinput типа TEdit поместим на форму одноименный компонент типа TMaskEdit. Выберите опцию File | New | Application, щелкните по компоненту edinput и удалите его с помощью клавиши Delete, затем поместите на его место компонент MaskEdit (страница Additional), назовите его edInput (свойство Name) и в его свойство

EditMask поместите строку

0999;1;

В секцию private описания класса fmExampie поместите объявление целочисленного поля х:

private

{ Private declarations }

X: Integer;

Для обработчика события OnActivate формы fmExampie Напишите такой код:

procedure TfmExample.FormActivate(Sender: TObject);

begin

X := Random (1001) ; // Загадываем случайное число

edinput.SetFocus; // Передаем строке edinput фокус ввода

Caption := 'Угадай целое число в диапазоне О...1000';

IbOutput.Caption :== 'Введите число:';

end;

Поместите в обработчик события onciick кнопки bbRun такой код:

procedure TfmExample.bbRunClick(Sender: TObject);

var

Y: Integer;

begin

if edinput.Text = '' then

Exit; // Если нет входного текста, прекращаем работу

// Преобразуем ввод в число:

Y := StrToInt(Trim(edInput.Text));

edinput.Text := ''; // Очищаем ввод

edinput.SetFocus; // Устанавливаем фокус ввода

{ Параметр Tag = 0 означает угадывание числа, иначе - ответ на вопрос, будет ли пользователь играть после угадывания }

if Tag = 0 then

if X < Y then // Угадывание числа

mmOutput.Lines.Add('X < '+IntToStr(Y))

else if X > Y then

mmOutput.Lines.Add('X > '+IntToStr (Y))

else // X = Y begin

mmOutput.Lines.Add("X = +IntToStr(Y));

MessageBeep(MB_OK); // Звук для привлечения внимания

IbOutput.Caption :='Вы угадали! Введите 1, если хотите повторить:';

Tag := 1; // Следующий ввод - ответ на вопрос

end;

else // Анализ ответа

if Y = 1 then

begin

X := Random(1001); // Новое число

IbOutput.Caption := 'Введите число:';

Tag := 0; // Следующий ввод - угадывание

edinput.SetFocus

end

else

Close;

end;

Комментарий к программе

При активизации окна программы обработчик FormActivate с помощью оператора

Х := Random(1001) ;

помещает в поле X случайное целое число в диапазоне от 0 до 1001 -1 = 1000 (параметр обращения к генератору псевдослучайных чисел Random на единицу больше верхней границы диапазона).

Каждый компонент Delphi имеет целочисленное свойство Tag, которым программист может распоряжаться по своему усмотрению. Программа использует это свойство формы fmExampie для разделения алгоритма обработки события OnClick кнопки bbRun на два варианта: пользователь угадывает число (тад = 0) или отвечает на предложение сыграть еще раз (тад = 1). В момент создания экземпляра класса TfmExmple - объекта fmExample - все его поля обнуляются, поэтому первоначально Tag = 0 и программа анализирует угадывание числа. При угадывании срабатывает составной оператор

begin

mmOutput.Lines.Add('X = Ч-IntToStr(Y)) ;

MessageBeep(MB OK); // Звук для привлечения внимания

IbOutput.Caption :='Вы угадали! Введите 1, если хотите повторить:';

Tag := 1; // Следующий ввод - ответ на вопрос

end

и Tag получает значение 1 (стандартная процедура MessageBeep заставляет звуковую систему ПК издать короткий звуковой сигнал).

Теперь очередной щелчок по кнопке bbRun будет означать, что пользователь подготовил ответ на вопрос, и программа анализирует его. Если пользователь ввел число отличное от 1, произойдет обращение к методу close формы fmExample и программа прекратит работу.

5.4.3. Операторы повторений

В языке Object Pascal имеются три различных оператора, с помощью которых можно запрограммировать повторяющиеся фрагменты программ.

Счетный оператор цикла FOR имеет такую структуру:

for <параметр цикла> := <нач_знач> to <кон знач> do <оператор>;

Здесь for, to, do - зарезервированные слова (для, до, выполнить);

<параметр_цикла> - переменная типа Integer (точнее, любого по-, рядкового типа, см. гл. 7); <нач_знач> - начальное значение - выражение того же типа; <кон_знач> - конечное значение - выражение того же типа; <оператор> - произвольный оператор Object Pascal.

При выполнении оператора for вначале вычисляется выражение <нач_знач> и осуществляется присваивание <параметр_цикла> := <нач_знач>. После этого циклически повторяется:

  • проверка условия <параметр_цикла> <= <кон_знач>; если условие не выполнено, оператор for завершает свою работу;
  • выполнение оператора <оператор>;
  • наращивание переменной <параметр_цикла> на единицу.

Учебная программа INTSLJMM

В качестве иллюстрации применения оператора for рассмотрим программу, осуществляющую ввод произвольного целого числа n и вычисление суммы всех целых чисел от 1 до N.

Для нового приложения (опция File | New | Application) создайте такой обработчик bbRunСlick:

procedure TfmExample.bbRunClick(Sender: TObject);

var

i,N,Sum : Integer;

begin

try // Преобразуем ввод с контролем правильности:

N := StrToInt(edInput.Text);

except // Следующие операторы выполняются, если есть ошибка ShowMessage('Ошибка ввода целого числа');

dinput.SelectAll; // Выделяем неверный ввод

Exit // Завершаем работу обработчика

end;

edInput.Text :=' ';

edinput.SetFocus;

Sum := 0; // Начальное значение Sum

for i := 1 to N

do // Цикл формирования суммы

Sum := Sum+i;

mmOutput.Lines.Add('Сумма всех целых чисел '+'в диапазоне 1...'+IntToStr(N)+' равна '+IntToStr(Sum));

end;

Комментарий к программе

Прежде всего обратите внимание на операторы

try // Преобразуем ввод с контролем правильности:

N := StrToInt(edinput.Text);

except // Следующие операторы выполняются, если есть ошибка ShowMessage("Ошибка ввода целого числа');

edinput.SelectAll; // Выделяем неверный ввод

Exit // Завершаем работу обработчика

end;

С помощью зарезервированных слов try (попробовать), except (исключение) и end реализуется так называемый защищенный блок. Такими блоками программист может защитить программу от краха при выполнении потенциально опасного участка (подробнее см. п. 14.1). В отличие от предыдущих программ мы не изменили компонент edinput, поэтому пользователь может ввести в нем произвольный текст. Если этот текст не содержит правильное представление целого числа, попытка выполнить оператор

N := StrToInt(edInput.Text);

в обычной программе привела бы к аварийному завершению программы. Чтобы этого не произошло, мы защитили этот оператор, расположив его за try и перед except. Если ошибки нет, все операторы, стоящие за except и до end, пропускаются и обработчик нормально срабатывает. Если обнаружена ошибка, возникает так называемая исключительная ситуация (исключение) и управление автоматически передается оператору, стоящему за except, - начинается обработка исключения. Вначале с помощью стандартной процедуры ShowMessage мы сообщаем пользователю об ошибке[Если вы запустите программу из среды Delphi, исключение будет сначала перехвачено средой и на экране появится сообщение на английском языке о характере и месте возникновения ошибки. В этом случае закройте окошко с сообщением и нажмите F9 - программа продолжит свою работу, и вы увидите окно процедуры ShowMessage.], затем с помощью edInput. SeiectAll выделяем ошибочный текст в компоненте edinput и, наконец, с помощью вызова стандартной процедуры Exit аварийно завершаем работу обработчика (но не программы!).

Отметим также два обстоятельства. Во-первых, условие, управляющее работой оператора for, проверяется перед выполнением оператора <оператор>: если условие не выполняется в самом начале работы оператора for, исполняемый оператор не будет выполнен ни разу. Другое обстоятельство - шаг наращивания параметра цикла строго постоянен и равен (+1). Существует другая форма оператора:

for <пар_цик>: = <нач_знач>downto <кон_знач>do <оператор>;

Замена зарезервированного слова to на downto означает, что шаг наращивания параметра цикла равен (-1), а управляющее условие Приобретает вид <параметр__цикла> = <кон_знач>.

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

Sum := 0;

if N >= 0 then

for i := 1 to N do

Sum := Sum + i

else

for i := -1 downto N do

Sum := Sum + i ;

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

Оператор цикла WHILE с предпроверкой условия:

while <условие> do <оператор>;

Здесь while, do - зарезервированные слова {пока [выполняется условие], делать), <условие> - выражение логического типа; <оператор> - произвольный оператор Object Pascal.

Если выражение <условие> имеет значение True, то выполняется <оператор>, после чего вычисление выражения <условие> и его проверка повторяются. Если <условие> имеет значение False, оператор while прекращает свою работу.

Учебная программа EPSILON

Программа отыскивает так называемое “машинное эпсилон” -такое минимальное, не равное нулю вещественное число, которое после прибавления его к 1,0 еще дает результат, отличный от 1,0. Замечу, что для хранения и преобразования дробных чисел в Object Pascal предназначены так называемые вещественные типы (см. гл. 7). В учебной программе используется один из этих типов - Real, занимающий 8 смежных байт и представляющий дробные (вещественные) числа в диапазоне от 10-324 до 10+308 с точностью 15... 16 значащих цифр10 .

У читателя, привыкшего к непрерывной вещественной арифметике, может вызвать недоумение утверждение о том, что в дискретной машинной арифметике всегда существуют такие числа o<x<eps, что i,o+x°i,o. Дело в том, что внутреннее представление типа Real может дать “лишь” приблизительно 1019 возможных комбинаций значащих разрядов в отведенных для него 8 байтах. Конечно же, это очень большое число, но оно несопоставимо с бесконечным множеством вещественных чисел. Аппроксимация бесконечного непрерывного множества вещественных чисел конечным (пусть даже и очень большим) множеством их внутреннего машинного представления, и приводит к появлению “машинного эпсилон”.

Для нового приложения (опция File I New I Application) соз дайте такой обработчик bbRunСlick:

procedure TfmExample.bbRunClick(Sender: TObject) ;

var

Epsilon: Real;

begin

Epsilon := 1;

while l+Epsilon/2>l do

Epsilon := Epsilon/2;

IbOutput.Caption := 'Машинное эпсилон = '+FloatToStr(Epsilon)

end;

Комментарий к программе

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

var

X : Integer; begin

X := 4/2; // Ошибка!Вещественный результат нельзя

// присвоить целой переменной

end;

Стандартная функция FloatToStr преобразует вещественное число в строку символов.

Оператор цикла REPEAT... UNTIL с постпроверкой условия:

repeat <тело цикла> Until <условие>;

Здесь repeat, until - зарезервированные слова (повторять [до тех пор}, пока [не будет выполнено условие]); <тело_цикла> - произвольная последовательность операторов Object Pascal; <условие> - выражение логического типа.

Операторы <тело_цикла> выполняются хотя бы один раз, после чего вычисляется выражение <условие>: если его значение есть False, операторы <тело_цикла> повторяются, в противном случае оператор repeat... until завершает свою работу.

Обратите внимание: пара repeat... unti1 подобна операторным скобкам begin ... end, поэтому перед until ставить точку с запятой необязательно.

Замечу, что для правильного выхода из цикла условие выхода должно меняться внутри операторов, составляющих тело цикла while или repeat... until. Следующие циклы никогда не завершатся “естественным” способом:

while True do begin

end;

и

repeat

until False;

Для гибкого управления циклическими операторами for, while и repeat в состав Object Pascal включены две процедуры без параметров:

break - реализует немедленный выход из цикла; действие процедуры заключается в передаче управления оператору, стоящему сразу за концом циклического оператора;

continue - обеспечивает досрочное завершение очередного прохода цикла; эквивалент передачи управления в самый конец циклического оператора.

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

5.4.4. Оператор выбора

Оператор выбора позволяет выбрать одно из нескольких возможных продолжений программы. Параметром, по которому осуществляется выбор, служит ключ выбора - выражение любого порядкового типа (из уже рассмотренных к порядковым относятся integer, char и логический; описание остальных порядковых типов см. в гл. 7).

Структура оператора выбора такова:

case <ключ_выбора> of <список_выбора> [else <операторы>] end;

Здесь case, of, else, end - зарезервированные слова (случай, из, иначе, конец); <ключ_выбора> - ключ выбора (выражение порядкового типа); <список_выбора> - одна или более конструкций вида:

<константа_выбора> : <оператор>;

<константа_выбора> - константа того же типа, что и выражение <ключ_выбора>; <оператор> - произвольный оператор Object Pascal.

Оператор выбора работает следующим образом. Вначале вычисляется значение выражения <ключ_выбора>, а затем в последовательности операторов <список_выбора> отыскивается такой, которому предшествует константа, равная вычисленному значению. Найденный оператор выполняется, после чего оператор выбора завершает свою работу. Если в списке выбора не будет найдена константа, соответствующая вычисленному значению ключа выбора, управление передается операторам, стоящим за словом else. Часть else <операторы> можно опускать. Тогда при отсутствии в списке выбора нужной константы ничего не произойдет, и оператор выбора просто завершит свою работу.

Любому из операторов списка выбора может предшествовать не одна, а несколько констант выбора, разделенных запятыми. Например:

var

ch : Char;

begin

case ch of

'n','N','H','H': IbOutput.Caption := 'Нет';

'у','Y','д','Д': IbOutput.Caption := 'Да';

end

end;

Учебная программа CALC

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

Для упрощения ввода операндов и знака поместим на нашу учебную форму еще два компонента. Выберите File | New | Application, переименуйте компонент ibinput в ibinputi и сделайте его свойство width (длина) равным 121 (стандартная длина компонентов Edit), a свойство Left (слева) - 64. Справа от компонента ibinputi поместите компонент comboBox (страница Standard), назовите его cbSign (свойство Name) и установите для него width =41. Раскройте диалоговое окно свойства items этого компонента и введите четыре строки со знаками математических действий в такой последовательности:

+

-

*

/

И, наконец, справа от cbSign разместите еще один компонент Edit (страница Standard) и назовите его edinput2. He забудьте очистить свойства Text у всех трех компонентов.

Рис. 5.7. Окно программы CALC

Для обработчика события bbRunciick введите такой текст:

procedure TfmExample.bbRunClick(Sender: TObject);

var

X, Y, Z: Real;

begin

{ Блокируем работу, если не введены операнды и не выбрано действие }

if (edInputl.Text='') or (edlnput2.Text='') or (cbSign.Itemlndex<0) then Exit;

// Проверяем правильность ввода 1-го операнда:

try // Начало защищенного блока

Х := StrToFloat(Trim(edinputi.Text));

except // Обработка ошибки

ShowMessage('Ошибочная запись числа: '+edlnputl.Text);

edinputi.SelectAll;

Exit;

end; // Конец защищенного блока

// Проверяем правильность ввода 2-го операнда:

try // Начало защищенного блока

Y := StrToFloat(Trim(edlnput2.Text));

except // Обработка ошибки

ShowMessage('Ошибочная запись числа: '+edlnput2.Text);

edlnput2.SelectAll ;

Exit;

end;

// Конец защищенного блока

// Все правильно: вычисляем результат

case cbSign.Itemlndex of

0: Z := X + Y;

1: Z := X - Y;

2: Z := X * Y;

3: try

Z := X / Y; except

Z := l.le+38; // Бесконечность при делении на О

end;

end;

// Показываем результат

IbOutput.Caption := Trim(edinputi.Text)+' '+ cbSign.Items[cbSign.Itemlndex]+' '+ Trim(edlnput2.Text)+' = ';

if Z >= l.le+38 then

IbOutput.Caption := IbOutput.Caption+'бесконечность'

else

IbOutput.Caption := IbOutput.Caption+FloatToStr(Z);

mmOutput.Lines.Add(IbOutput.Caption) ;

// Очищаем ввод edinputi.Text := '';

ed!nput2.Text := ' ';

cbSign.ItemIndex := -1;

end;

Комментарий к программе

В качестве операндов в программе выбраны переменные вещественного типа Real. Символьное представление значений Real в общем случае имеет вид:

±ЦЦЦЦЦ.ДДДДДе+ППП

Здесь ± - знак + или -; ццццц - цифры целой части числа; ддддд -цифры дробной части; ппп - цифры десятичного порядка. Символ е (E) в такой записи читается как “умножить на 10 в степени”. Например, 1. 1e+38 читается: “один и одна десятая умножить на 10 в степени плюс тридцать восемь”. Следует учесть, что компилятор Delphi в качестве разделителя целой и дробной частей всегда использует символ “.”, в то время как аналогичный разделитель в Windows зависит от выбранной страны, и для России им обычно является запятая “,”. Это означает, что вещественные константы в тексте программы вы всегда должны записывать с разделителем “.”, а при вводе пользователем программы вещественных чисел он должен использовать разделитель Windows (этот разделитель можно выбрать В Windows 32 С помощью Пуск | Настройка 1 Панель управления | Язык и стандарты).

Поскольку правильная форма записи вещественного числа гораздо богаче формы записи целого, использовать однострочный редактор с маской для фильтрации вещественных чисел невозможно, поэтому в программе используются обычные редакторы edInput1 и edInput2. В этой ситуации контроль за правильностью записи приходится возлагать на функцию преобразования strToFioat (эта функция преобразует строку в вещественное число). Если функция обнаружит ошибку в записи вещественного числа, она инициирует исключительную ситуацию, которая обычно приводит к аварийному завершению работы программы. Чтобы этого не произошло, используются защищенные блоки (см. комментарий к программе INITSUMM). Таким же блоком защищается операция деления: если число долится на ноль или на очень малую величину, результату присваивается большая положительная величина 1,1*10+38. При выводе сообщения эта величина заменяется на слово “бесконечность”.

Для выбора операции в программе используется компонент Combовох - раскрывающийся список выбора. Свойство items этого компонента хранит набор строк, которые определяют список выбора. Эти строки пронумерованы начиная с нуля (в нашем случае cbSign.Items[0] = '+', a cbSign. Items [3] = '/'). Номер выб-ранной строки содержит свойство cbSign.ItemIndex, а если не выбрана ни одна строка, это свойство содержит -1. Программа контролирует выбор за счет условия

cbSign.ItemIndex<0

при проверке операндов, а также в операторе case:

case cbsign.itemindex of

Если вы ввели строки cbSign. items в нужной последовательности, индекс выбранной строки будет соответствовать номеру операции (0 - +, 1- -, 2 - * и 3 - /), и над операндами будет выполнено требуемое действие.

5.4.5. Метки и операторы перехода

Можно теоретически показать, что рассмотренных операторов вполне достаточно для написания программ любой сложности. В этом отношении наличие в языке операторов перехода кажется излишним. Более того, современная технология структурного программирования основана на принципе “программировать без GOTO”: считается, что злоупотребление операторами перехода затрудняет понимание программы, делает ее запутанной и сложной в отладке. Тем не менее в некоторых случаях использование операторов перехода может упростить программу.

Оператор перехода имеет вид:

goto <метка>;

Здесь goto - зарезервированное слово (перейти [на метку]); <метка> - метка.

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

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

Label

loop, Ibl, lb2;

begin

goto Ibl;

loop: .........

Ibl:lb2: ......

......

goto lb2;

end;

Действие оператора goto состоит в передаче управления соответствующему меченному оператору.

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

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

5.5. МАССИВЫ

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

При описании массива необходимо указать общее количество входящих в массив элементов и тип этих элементов. Например:

var

а : array [1..10] of Real;

b : array [0..50] of Char;

с : array [-3..4] of String;

Как видим, при описании массива используются зарезервированные слова array и of (массив, из). За словом array в квадратных скобках указывается тип-диапазон, с помощью которого компилятор определяет общее количество элементов массива. Тип-диапазон (подробнее см. в гл. 7) задается левой и правой границами изменения индекса массива, так что массив А состоит из 10 элементов, массив В - из 51, а массив С - из 8 элементов. За словом of указывается тип элементов, образующих массив.

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

var

a: array [1..10] of Integer;

b: array [0..40] of Char;

c: array [-2..2] of Boolean;

k: Integer;

begin

b[17] := 'F';

c[-2] := a[1] > 2;

for k := 1 to 10 do a[k] := 0;

end;

В правильно составленной программе индекс не должен выходить за пределы, определенные типом-диапазоном. Например, можно использовать элементы а [ 1 ], в[38], с[0], но нельзя а [0 ] илис[38] (определение массивов см. выше). Компилятор Object Pascal может[Буквальный перевод array - боевой порядок, упорядоченная масса (войск). В компьютерной терминологии array переводится словом массив.] контролировать использование индексов в программе как на этапе компиляции, так и на этапе прогона программы.

Учебная программа AVERAGE

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

procedure TfmExample.bbRunClick(Sender: TObject);

{Программа создает массив из N случайных целых чисел, равномерно распределенных в диапазоне от 0 до MAX_VALUE-1, подсчитывает среднее арифметическое этих чисел, а также минимальное и максимальное из них.} const

N = 1000; // Количество элементов массива

MAX_VALUE = 100+1;// Диапазон значений случайных чисел var

m: array [1..N] of Integer; // Массив чисел

i: Integer;// Индекс массива

max,min: Integer; // Максимальное и минимальное число

sum: Real;// Сумма чисел

begin

// Наполняем массив случайными числами:

for i := 1 to N do

m[i] := Random(MAX_VALUE);

// Задаем начальные значения переменных:

sum : = m [ 1 ] ;

max : = m [ 1 ] ;

min := m[1] ;

// Цикл вычисления суммы всех случайных чисел и поиска

// минимального и максимального:

for i := 2 to N do

begin

sum := sum + m[i];

if m[i] < min then

min := m[i]

else if m[i] > max then

max := m[i] end;

// Вычисляем среднее значение и выводим результат:

IbOutput.Caption := 'Мин = '+IntToStr(min)+' Макс = '+ IntToStr(max)+' Среднее = '+FloatToStr(sum/N) ;

end;

Для создания массива используется встроенная функция Ran-dom(Max), которая возвращает случайное целое число, равномерно распределенное в диапазоне от 0 до мах-1 (мах - параметр обращения).

5.6. ПРОЦЕДУРЫ И ФУНКЦИИ

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

Многие примеры в этой книге невелики по размерам (не более 30-40 строк), поэтому в таких программах можно обойтись и без подпрограмм. Иное дело - создание крупных программ в сотни, тысячи и десятки тысяч строк. Писать такие программы как нечто единое целое, без расчленения на относительно самостоятельные фрагменты, т. е. без структурирования, просто невозможно. Практически во всех языках программирования имеются средства структурирования. Языки, в которых предусмотрены такие механизмы, называются процедурно-ориентированными. К их числу принадлежит и Object Pascal.

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

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

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

Рис. 5.8. Взаимодействие вызывающей программы и процедуры

С примерами процедур и функций мы уже сталкивались - это

Стандартные процедуры Exit, ShowMessage, функции StrToInt, FioatToStr, Random, математические функции и др. Стандартными они называются потому, что созданы одновременно с системой Delphi и являются ее неотъемлемой частью. В Delphi имеется много стандартных процедур и функций. Наличие богатой библиотеки таких программных заготовок существенно облегчает разработку прикладных программ. Однако в большинстве случаев некоторые специфичные для данной прикладной программы действия не находят прямых аналогов в библиотеках Delphi, и тогда программисту приходится разрабатывать свои, нестандартные процедуры и функции.

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

Учебная программа UPSTRING

Не вдаваясь в дальнейшие подробности, попробуем составить собственную процедуру, чтобы пояснить сказанное. Пусть в этой процедуре преобразуется некоторая символьная строка таким образом, чтобы все строчные буквы заменялись соответствующими прописными. В Object Pascal имеется стандартная функция upcase (см. гл. 6), которая выполняет аналогичные действия над одиночным символом. Наша процедура (назовем ее upString) будет преобразовывать сразу все символы строки, причем сделаем ее пригодной не только для латинских букв, но и для букв русского алфавита[ В Delphi имеется стандартная функция AnsiUpperCase, реализующая те же действия.].

Разработку программы проведем в два этапа. Сначала сконструируем основную (вызывающую) часть программы. Ее действия очень просты: она должна получить входную строку из компонента edinput, преобразовать ее с помощью процедуры upString в выходную строку и поместить результат в iboutput.Text. Эти действия нетрудно запрограммировать в обработчике bbRunciick:

procedure TfmExample.bbRunClick(Sender: TObject);

procedure UpString(stinp: String; var stOut: String);

{ Эта процедура преобразует все буквы строки stinp в прописные и помещает результат в выходную строку stOut }

begin

stOut := stinp;

end; // UpString var

SI, S2: String;

begin

SI := edinput.Text; // Получаем исходную строку

UpString(SI,S2); // Преобразуем ее

IbOutput.Caption := S2; // Выводим результат

edinput.Text := '' ;

edinput.SetFocus ;

end;

В этой программе используется замещение процедуры UpString так называемой “заглушкой”, т. е. процедурой, в которой на самом деле не осуществляется нужных нам действий, а выходная строка просто копирует входную. (Однако эта программа синтаксически абсолютно правильна, и при желании ее можно запустить на счет.) Заглушка понадобилась нам по двум причинам. Во-первых, приведенная программа очень проста, в ней отсутствует детальная реализация процедуры, и это позволяет наглядно проиллюстрировать механизм ее описания. Во-вторых, на ее примере мы знакомимся с универсальным методом конструирования сложных программ, получившим название нисходящее программирование. В соответствии с этим методом создание программы начинается “сверху”, т. е. с разработки самого главного, генерального алгоритма. На верхнем уровне обычно еще неясны детали реализации той или иной части программы, поэтому эти части следует заменить временными заглушками. Желательно, чтобы временный вариант программы был синтаксически правильным, тогда можно его откомпилировать и убедиться в отсутствии в нем синтаксических ошибок. Такой прогон даст определенную уверенность перед разработкой и реализацией алгоритмов нижнего уровня, т. е. перед заменой заглушек реально работающими процедурами. Если реализуемый в заглушке алгоритм достаточно сложен, его вновь структурируют, выделяя главный алгоритм и применяя новые заглушки, и т. д. Процесс продолжается “вниз” до тех пор, пока не будет создан полностью работоспособный вариант программы.

Как видим, описание процедуры начинается зарезервированным словом procedure, за которым следуют имя процедуры и список формальных параметров. Список параметров заключается в круглые скобки и содержит перечень параметров с указанием их типа. Заметим, что перед параметром stout, с помощью которого в вызывающую программу возвращается результат преобразования, стоит зарезервированное слово var. Именно таким способом компилятору указываются те параметры, в которых процедура возвращает вызвавшей ее программе результат своей работы (подробнее см. гл. 10). Зарезервированное слово procedure, имя процедуры и список ее параметров образуют заголовок процедуры. За заголовком следует тело процедуры, содержащее новый раздел описаний (этот раздел пока еще пуст) и раздел исполняемых операторов (оператор stout:= stInp).

Приступим к разработке алгоритма процедуры. Для этого учтем, что в соответствии с принятой в Windows кодировкой символов (так называемой ANSI-кодировкой) символы кириллицы (русского алфавита) располагаются сплошным массивом от “А” до “я”, за исключением “ё” и “Ё”, которые предшествуют “А”. Кроме того, символ “я” имеет внутренний код 255, т. е. максимально возможное значение одного байта (для каждого символа строки выделяется по одному байту).

Вот возможный вариант процедуры:

Procedure UpString(stinp: String; var stOut: String);

{ Эта процедура преобразует все буквы строки stinp в прописные и помещает результат в выходную строку stOut } var

k: Integer;// Параметр цикла

begin

stOut := stinp;

// Цикл побуквенного преобразования

for k := 1 to Length(stOut) {Length - длина строки} do

begin

stOut[k] := UpCase(stOut[k]); // Преобразуем латиницу

if stOut[k] >= 'a' then // Строчная буква кириллицы?

st0ut[k] := // Да: преобразуем ее

Chr(ord('A') + ord(st0ut[k]) - ord('a'));

if stOut[k]='e' then

stOut[k] := 'Ё'; // Преобразуем ё в Ё

end;

end; // UpString

Комментарий к программе

В процедуре вначале с помощью оператора

stOut := stinp;

исходная строка копируется в выходную строку. Затем реализуется циклический перебор всех символов выходной строки для преобразования букв. Для доступа к отдельным символам строки используется замечательное свойство типа данных string, позволяющее рассматривать строку как набор (массив) символов. Первый символ этого набора имеет индекс 1, второй - 2 и т. д. Индекс указывается сразу за именем строки в квадратных скобках. Таким образом, stOut [i] - это г-ный символ строки stOut. Перебор символов реализован в виде счетного цикла от 1 до длины входной строки stOut (эта длина получается с помощью стандартной функции Length), в ходе которого каждый символ сначала преобразуется функцией UpCase (эта функция возвращает прописную латинскую букву для строчной буквы, передаваемой ей в параметре вызова; функция не работает с кириллицей). Затем осуществляется проверка принадлежности буквы к русской строчной (оператор if st0ut[k] >= 'a' then; символ “а” - русская буква) и осуществляется необходимая коррекция ее внутреннего кода. При коррекции используются отмеченные выше свойства непрерывности и монотонности массивов кодов, т. е. за “А” идет “Б”, за “Я” - “а”, за “а” - “б” и т. д. Стандартная функция ord возвращает внутренний код символа, который передается ей в качестве параметра обращения, а функция chr преобразует код в символ. Поскольку буква “ё” стоит особняком, ее преобразование осуществляется отдельным оператором.

Рассмотрим иной способ реализации той же программы - оформим алгоритм преобразования в виде функции:

procedure TfmExample.bbRunClick(Sender: TObject) ;

Function UpString(stinp: String): String;

{ Эта функция преобразует все буквы строки stinp в прописные и помещает результат в выходную строку Result }

var

k: Integer; // Параметр цикла begin

Result := stinp;

// Цикл побуквенного преобразования

for k := 1 to Length(Result) do

begin

Result[k] := UpCase(Result[k]);// Преобразуем латиницу

if Result[k] >= 'a' then // Строчная буква кириллицы?

Result[k] := // Да: преобразуем ее

Chr(ord('A') + ord(Result[k]) - ord('a'));

if Result[k]='e' then

Result[k] := 'Ё'; // Преобразуем ё в Ё

end;

end; // UpString

begin

{ В следующем операторе исходная строка edinput.Text преобразуется и помещается в выходную строку IbOutput.Caption: }

IbOutput.Caption := UpString(edinput.Text);

edinput.Text := '';

edinput.SetFocus ;

end;

Комментарий к программе

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

С помощью оператора

Result := stInp;

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

IbOutput.Caption := UpString(edInput.Text);

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


Глава 6. ЭЛЕМЕНТЫ ЯЗЫКА

  • Глава 6. ЭЛЕМЕНТЫ ЯЗЫКА
    • АЛФАВИТ
    • ИДЕНТИФИКАТОРЫ
    • КОНСТАНТЫ
    • ВЫРАЖЕНИЯ
    • ОПЕРАЦИИ

Глава 6

ЭЛЕМЕНТЫ ЯЗЫКА

6.1. АЛФАВИТ

Алфавит языка Object Pascal включает буквы, цифры, шестнадцатеричные цифры, специальные символы, пробелы и зарезервированные слова.

Буквы - это буквы латинского алфавита от а до z и от А до Z, а также знак подчеркивания “_”. В языке нет различия между заглавными и строчными буквами алфавита, если только они не входят в символьные и строковые выражения.

Цифры - арабские цифры от 0 до 9.

Каждая шестнадцатеричная цифра имеет значение от 0 до 15. Первые 10 значений обозначаются арабскими цифрами 0... 9, остальные шесть - латинскими буквами а ... f или а... f.

Специальные символы Object Pascal - это символы

+ - * / = , ' . : ; < > [ ] ( ) { } " @ $ #

К специальным символам относятся также следующие пары символов:

< > , < = , > = , : = , ( * , * ) , ( . , . ) , / / .

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

Особое место в алфавите языка занимают пробелы, к которым относятся любые символы в диапазоне кодов от 0 до 32. Эти символы рассматриваются как ограничители идентификаторов, констант, чисел, зарезервированных слов. Несколько следующих друг за другом пробелов считаются одним пробелом (последнее не относится к строковым константам).

В Object Pascal имеются следующие зарезервированные слова:

and | exports | mod | Shr
array | file | nil | String
as | finalization | not | then
asm | finally | object | threadvar
begin | for | of | to
case | function | or | try
class | goto | out | type
const | if | packed | unit
constructor | implementation | procedure | until
destructor | in | program | uses
dispinterface | inherited | property | var
div | initialization | raise | while
do | inline | record | with
downto | interface | repeat | xor
else | is | resourcestring |
end | label | set |
except | library | shi |

Зарезервированные слова не могут использоваться в качестве идентификаторов.

Стандартные директивы первоначально связаны с некоторыми стандартными объявлениями в программе. К ним относятся:

absolute | dynamic | name | public | safecall
abstract | export | near | published | stdcall
assembler | external | nodefault | read | stored
automated | far | override | readonly | virtual
cdecl | forward | package | register | write
contains | implements | pascal | re introduce | writeonly
default | index | private | requires |
dispid | message | protected | resident |

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

Слова private, protected, public, published И automated считаются зарезервированными внутри объявления класса и стандартными директивами - вне объявления.

6.2. ИДЕНТИФИКАТОРЫ

Идентификаторы в Object Pascal - это имена констант, переменных, меток, типов, объектов, классов, свойств, процедур, функций, модулей, программ и полей в записях. Идентификаторы могут иметь произвольную длину.

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

Примеры правильных идентификаторов:

a | MyProgramIsBestProgram | external
ALPHA | date_27_sep_39 | _beta

Примеры неправильных идентификаторов:

1 Program // начинается цифрой

block#l // содержит специальный символ

My Prog // содержит пробел

mod // зарезервированное слово

6.3. КОНСТАНТЫ

В качестве констант в Object Pascal могут использоваться целые, вещественные и шестнадцатеричные числа, логические константы, символы, строки символов, конструкторы множеств и признак непределенного указателя NIL.

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

Вещественные числа записываются со знаком или без него с использованием десятичной точки и/или экспоненциальной части.

Экспоненциальная часть начинается символом е или е, за которым могут следовать знаки “+” или “-” и десятичный порядок. Символ е (е) означает десятичный порядок и имеет смысл “умножить на 10 в степени”. Например,

3.14Е5 - 3,14 умножить на 10 в степени 5;

-17e-2 -минус 17 умножить на 10 в степени минус 2.

Если в записи вещественного числа присутствует десятичная точка, перед точкой и за ней должно быть хотя бы по одной цифре. Если используется символ экспоненциальной части е (в), за ним должна следовать хотя бы одна цифра десятичного порядка.[Только для версий 4...6. Для версии 1 диапазон от -32536 до +32535. Для версии 2 и 3 от -2147483648 до +4294967294]

Шестнадцатеричное число состоит из шестнадцатеричных цифр, которым предшествует знак доллара $ (код символа 36). Диапазон шестнадцатеричных чисел - от $ffffffffffffffff до

$7FFFFFFFFFFFFFFF (для версии 4...6).

Логическая константа - это либо слово false (ложь), либо слово true (истина).

Символьная константа - это любой символ ПК, заключенный в апострофы:

'z' - символ “z”;

'Ф' - символ “Ф”.

Если необходимо записать собственно символ апострофа, он удваивается:

'''' - символ “'” (апостроф).

Допускается использование записи символа путем указания его внутреннего кода, которому предшествует символ # (код 35), например:

#97 - символ “а”;

#90 - символ “Z”;

#39 - символ “'”;

#13 - символ “CR”.

Строковая константа - любая последовательность символов (кроме символа CR - возврат каретки), заключенная в апострофы. Если в строке нужно указать сам символ апострофа, он удваивается, например:

'Это - строка символов';

'That''s string'.

Строка символов может быть пустой, т. е. не иметь никаких символов в обрамляющих ее апострофах. Строку можно составлять из кодов нужных символов с предшествующими каждому коду символами #, например, строка #83#i2i#i09#98#ii#i08 эквивалентна строке ' Symbol '.

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

#7'Ошибка !'#13'Нажмите любую клавишу ...'#7 .

Конструктор множества - список элементов множества, обрамленный-квадратными скобками,например:

[1,2,4..7,12]

[blue, red]

[ ]

[true]

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

abs | lo | ptr | swap
chr | odd | round | trunc
hi | ord | sizeof |
length | pred | succ |

Например:

const

MaxReal = Maxint div SizeOf(real) ;

NumChars = ord('Z') - ord('a') + 1;

Ln10 = 2.302585092994;

Ln10R = 1 / Ln10;

6.4. ВЫРАЖЕНИЯ

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

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

Примеры выражений:

у

21 (а + b) * с

sin(t)

а > 2

not Flag and (a = b)

NIL

[1, 3..7] * setl

6.5. ОПЕРАЦИИ

В Object Pascal определены следующие операции:

унарные not, @ ;

мультипликативные *, /, div, mod, and, shi, shr;

аддитивные +, -, or, xor;

отношения =, <>, <, >, <=, >=, in.

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

Delphi переключателя Project | Options | Compiler | Complete

Boolean eval: при установленном переключателе вычисляются все операции отношения, при неустановленном - только те, что необходимы для однозначного определения результата исчисления.

Правила использования операций с операндами различного типа приводятся в табл. 6.1.

Таблица 6.1

Операция | Действие | Тип операндов | Тип результата
not | Отрицание | Логический | Логический
not | To же | Любой целый | Тип операнда
@ | Адрес | Любой | Указатель
* | Умножение | Любой целый | Наименьший целый
* | Тоже | Любой вещественный | Extended
* | Пересечение множеств | Множественный | Множественный
/ | Деление | Любой вещественный | Extended
div | Целочисленное деление | Любой целый | Наименьший целый
mod | Остаток от деления | Тоже | - Тоже
and | Логическое И | Логический | Логический
and | То же | Любой целый | Наименьший целый
shl | Левый сдвиг | Тоже | То же
shr | Правый сдвиг | То же | То же
+ | Сложение | Тоже | Тоже
+ | Тоже | Любой вещественный | Extended
+ | Объединение множеств | Множественный | Множественный
+ | Сцепление строк | Строковый | Строковый
- | Вычитание | Любой целый | Наименьший целый
- | Тоже | Любой вещественный | Extenden
or | Логическое или | Логический | Логический
or | Тоже | Любой целый | Наименьший целый
= | Равно | Любой простой или строковый | Логический
0 | Не равно | Тоже | Тоже
< | Меньше | Логический | Логический
<= | Меньше или равно | Тоже | Тоже
> | Больше | То же | Тоже
>= | Больше или равно | Тоже | Тоже

Унарная операция @ применяется к операнду любого типа и возвращает результат типа pointer (см. гл. 9), в котором содержится адрес операнда. Если операция @ применяется к процедуре, функции или методу в объекте, ее результатом будет адрес точки входа в эту процедуру (функцию, метод). Этот адрес можно использовать только в подпрограмме, написанной на Ассемблере, или во фрагментах

INLINE.

В Object Pascal определены следующие логические операции:

not - логическое НЕ;

and - логическое И;

or - логическое ИЛИ;

xor - исключительное ИЛИ.

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

Таблица 6.2

Логичеcкие операции над данными целого типа (поразрядно) 5
Операнд 1 | Операнд 2 | not | and | or | xor
1 | - | 0 | - | - | -
0 | - | 1 | - | - | -
0 | 0 | - | 0 | 0 | 0
0 | 1 | - | 0 | 1 | 1
1 | 0 | - | 0 | 1 | 1
1 | 1 | - | 1 | 1 | 0

К логическим же в Object Pascal обычно относятся и две сдвиговые операции над целыми числами:

i shl j - сдвиг содержимого / на j разрядов влево; освободившиеся младшие разряды заполняются нулями;

i shr j - сдвиг содержимого i на j разрядов вправо; освободившиеся старшие разряды заполняются нулями.

В этих операциях i и у - выражения любого целого типа.

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

Таблица 6.3

Логические операции над данными логического типа
Операнд 1 | Операнд 2 | not | and | or | xor
True | - | False | - | - | -
False | - | True | - | - | -
False | False | - | False | False | False
False | True | - | False | True | True
True | False | - | False | True | True
True | True | - | True | True | False

Операция отношения in применяется к двум операндам. Первым (левым) операндом должно быть выражение любого порядкового типа, вторым - множество, состоящее из элементов того же типа, или идентификатор множественного типа. Результат операции будет True, если левый операнд принадлежит множеству.


Глава 7. ТИПЫ ДАННЫХ

  • Глава 7. ТИПЫ ДАННЫХ
    • ПРОСТЫЕ ТИПЫ
      • Порядковые типы
      • Вещественные типы
      • Тип дата-время
    • СТРУКТУРИРОВАННЫЕ ТИПЫ
      • Массивы
      • Записи
      • Множества
    • СТРОКИ
      • Типы String и ShortString
      • Нуль-терминальные строки
    • УКАЗАТЕЛИ И ДИНАМИЧЕСКАЯ ПАМЯТЬ
      • Динамическая память
      • Указатели
      • Выделение и освобождение динамической памяти
      • Процедуры и функции для работы с динамической памятью
    • ПСЕВДОНИМЫ ТИПОВ

Глава 7

ТИПЫ ДАННЫХ

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

Object Pascal характеризуется разветвленной структурой типов данных (рис. 7.1). В языке предусмотрен механизм создания новых типов, благодаря чему общее количество используемых в программе типов может быть сколь угодно большим.

В этой и четырех последующих главах приводится подробное описание всех типов.

7.1. ПРОСТЫЕ ТИПЫ

К простым типам относятся порядковые, вещественные типы и тип дата-время.

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

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

Тип дата-время предназначен для хранения даты и времени. Фактически для этих целей он использует вещественный формат.

7.1.1. Порядковые типы

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

Рис. 7.1. Структура типов данных

Для целых типов функция ord(x) возвращает само значение х, т. е. Ord(X) = х для х, принадлежащего любому целому типу. Применение Ord(x) к логическому, символьному и перечисляемому типам дает положительное целое число в диапазоне от 0 до 1 (логический тип), от 0 до 255 (символьный), от 0 до 65535 (перечисляемый). Тип-диапазон сохраняет все свойства базового порядкового типа, поэтому результат применения к нему функции ord(X) зависит от свойств этого типа.

К порядковым типам можно также применять функции:

pred(x) - возвращает предыдущее значение порядкового типа (значение, которое соответствует порядковому номеру

ord (X) -1), т. е. Ord(Pred(X)) = Ord(X) - 1;

succ (X) - возвращает следующее значение порядкового типа, которое соответствует порядковому номеру ord (X) +1, т. е.

Ord(Succ(X)) = Ord(X) + 1.

Например, если в программе определена переменная

var

с : Char;

begin

с := '5';

end;

то функция PRED(C) вернет символ '4', а функция SUCC(C) - символ '6'.

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

Целые типы. Диапазон возможных значений целых типов зависит от их внутреннего представления, которое может занимать один, два, четыре или восемь байтов. В табл. 7.1 приводятся названия целых типов, длина их внутреннего представления в байтах и диапазон возможных значений. Таблица 7.1. Целые типы

Название | Длина,байт | Диапазон значений
Cardinal | 4 | 0. .. 2 147 483 647
Byte | 1 | 0...255
Shortint | 1 | -128...+127
Smallint | 2 | -32 768...+32 767
Word | 2 | 0...65 535
Integer | 4 | -2 147 483 648...+2 147 483 647
Longint | 4 | -2 147 483 648...+2 147 483 647
Int64 | 8 | -9*1018. ..+9*1018
LongWord | 4 | 0. . .4 294 967 295

Типы LongWord и Int64 впервые введены в версии 4, а типы Smallint и Cardinal отсутствуют в Delphi 1. Тип integer для этой версии занимает 2 байта и имеет диапазон значений от -32768 до +32767, т. е. совпадает с Smallint.

При использовании процедур и функций с целочисленными параметрами следует руководствоваться “вложенностью” типов, т. е. везде, где может использоваться word, допускается использование Byte (но не наоборот), в Longint “входит” Smallint, который, в свою очередь, включает в себя Shortint.

Перечень процедур и функций, применимых к целочисленным типам, приведен в табл. 7.2. Буквами b, s, w, i, l обозначены выражения соответственно типа Byte, Shortint, Word, Integer И Longint,

х - выражение любого из этих типов; буквы vb, vs, vw, vi, vl, vx обозначают переменные соответствующих типов. В квадратных скобках указывается необязательный параметр.

Таблица 7.2. Стандартные процедуры и функции, применимые к целым типам

Обращение | Тип результата | Действие
abs (x) | x | Возвращает модуль x
chr(b) | Char | Возвращает символ по его коду
dec (vx [, i] ) | - | Уменьшает значение vx на i, а при отсутствии i - на 1
inc(vx[,i]) | - | Увеличивает значение vx на i, а при отсутствии i -на 1
Hi(w) | Byte | Возвращает старший бант аргумента
Hi(I) | То же | Возвращает третий по счету байт
Lo(i) | “ | Возвращает младший байт аргумента
Lo(w) | “ | То же
odd(l) | Boolean | Возвращает True, если аргумент-нечетное число
Random(w) | Как у параметра | Возвращает псевдослучайное число, равномерно распределенное в диапазоне 0...(w-l)
sqr(x) | X | Возвращает квадрат аргумента
swap(i) | Integer | Меняет местами байты в слове
swap (w) | Word | Тоже

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

Например, при прогоне следующей программы на экране появится значение 0:

procedure TfmExample.bbRunClick(Sender: TObject) ;

var

k: Word;

begin

k := 65535; // Максимальное значение типа Word

k := k+1; // По правилам математики

k=65536 IbOutput.Caption := IntToStr(k); // На самом деле k=0!

end;

Если активизировать переключатель project | options | Compiler I Range checking и повторить компиляцию с помощью Project | Build All, компилятор вставит в программу код проверки переполнения и при прогоне программы возникнет исключительная ситуация, которую при желании можно соответствующим образом обработать. Замечу, что, если изменить программу следующим образом:

k := 65535; // Максимальное значение типа Word

IbOutput.Caption := IntToStr(k + 1);// Будет выведено 65536

переполнения не произойдет, т. к. 32-разрядный компилятор версий Delphi 32 автоматически преобразует операнды выражения k+i к 4-байтным величинам.

Логические типы. К логическим относятся типы Boolean, ByteBool, Bool, wordBool и LongBool. В стандартном Паскале определен только тип Boolean, остальные логические типы введены в Object Pascal для совместимости с Windows: типы Boolean и ByteBool занимают по одному байту каждый, Bool и WordBool - по 2 байта, LongBool - 4 байта. Значениями логического типа может быть одна из предварительно объявленных констант False (ложь) или True (истина). Для них справедливы правила:

Ord(False) = 0;

Ord(True) <> 0;

Succ(False)= True;

Pred(True) = False.

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

var

l : Boolean;

begin

for l := False to True do ....

Однако необходимо помнить, что в Delphi 32 для Boolean значение

Ord (True) = +1, В ТО Время как для других типов (Bool, WordBool И Т.Д.)

Ord (True) = -1, поэтому такого рода операторы следует использовать с осторожностью!. Например, для версии Delphi 6 исполняемый оператор showMessage (' --- ') в следующем цикле for не будет выполнен ни разу:

var

L: Bool;

k: Integer;

begin

for L := False to True do ShowMessage ('--);

end; __

Если заменить тип параметра цикла l в предыдущем примере на Boolean, цикл будет работать и сообщение дважды появится на экране.[Для Delphi версии 1 и 2 ord (True) =+1 для любого логического типа.]

Символьный тип. Значениями символьного типа является множество всех символов ПК. Каждому символу приписывается целое число в диапазоне О...255. Это число служит кодом внутреннего представления символа, его возвращает функция ord.

Для кодировки в Windows используется код ANSI (назван по имени American National Standard Institute - американского института стандартизации, предложившего этот код). Первая половина символов ПК с кодами 0... 127 соответствует таблице 7.3. Вторая половина символов с кодами 128...255 меняется для различных шрифтов. Стандартные Windows-шрифты Arial Cyr, Courier New Cyr и Times New Roman для представления символов кириллицы (без букв “ё” и “Ё”) используют последние 64 кода (от 192 до 256): “А”... “Я” кодируются значениями 192..223, “а”... “я” - 224...255. Символы “Ё” и “ё” имеют соответственно коды 168 и 184.

Таблица 7.3. Кодировка символов в соответствии со стандартом ANSI

Код | Символ | Код. | Символ | Код. | Символ | Код | Символ
0 | NUL | 32 | BL | 64 | @ | 96 | '
1 | ЗОН | 33 | ! | 65 | А | 97 | а
2 | STX | 34 | “ | 66 | В | 98 | b
3 | ЕТХ | 35 | # | 67 | С | 99 | с
4 | EOT | 36 | $ | 68 | D | 100 | d
5 | ENQ | 37 | % | 69 | Е | 101 | е
6 | ACK | 38 | & | 70 | F | 102 | f
7 | BEL | 39 | ' | 71 | G | 103 | д
8' | BS | 40 | ( | 72 | Н | 104 | h
9 | HT | 41 | ) | 73 | I | 105 | i
10 | LF | 42 | * | 74 | J | 106 | j
11 | VT | 43 | + | 75 | К | 107 | k
12 | FF | 44 | f | 76 | L | 108 | 1
13 | CR | 45 | - | 77 | М | 109 | m
14 | SO | 46 |  | 78 | N | 110 | n
15 | SI | 47 | / | 79 | 0 | 111 | о
16 | DEL | 48 | 0 | 80 | Р | 112 | P
17 | DC1 | 49 | 1 | 81 | Q | 113 | q
18 | DC2 | 50 | 2 | 82 | R | 114 | r
19 | DC3 | 51 | 3 | 83 | S | 115 | s
20 | DC 4 | 52 | 4 | 84 | Т | 116 | t
21 | NAK | 53 | 5 | 85 | U | 117 | u
22 | SYN | 54 | 6 | 86 | V | 118 | v
23 | ETB | 55 | 7 | 87 | W | 119 | W
24 | CAN | 56 | 8 | 88 | х | 120 | x
25 | EM | 57 | 9 | 89 | Y | 121 | У
26 | SUB | 58 | : | 90 | Z | .122 | z
27 | ESC | 59 | ; | 91 | t | 123 | {
28 | FS | 60 | < | 92 | \ | 124 | 1
29 | GS | 61 | = | 93 | ] | 125 | }
30 | RS | 62 | > | 94 | Л | 126 | ~
31 | US | 63 | f | 95 |  | 127 | r

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

К типу char применимы операции отношения, а также встроенные функции:

Сhr (в) - функция типа char; преобразует выражение в типа Byte в символ и возвращает его своим значением;

UpCase(CH) - функция типа char; возвращает прописную букву, если сн - строчная латинская буква, в противном случае возвращает сам символ сн (для кириллицы возвращает исходный символ).

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

type

colors = (red, white, blue);

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

type

ТипМесяц=(янв,фев,мар,апр,май,июн,июл,авг,сен,окт,ноя,дек);

var

месяц : ТипМесяц;

begin

if месяц = авг then

IbOutput.Caption := 'Хорошо бы поехать к морю!';

end.

был бы, согласитесь, очень наглядным. Увы! В Object Pascal нельзя использовать кириллицу в идентификаторах, поэтому мы вынуждены писать так:

type

TypeMonth=(jan,feb,mar,may,jun,jul,aug,sep,oct,nov,dec);

var

month: TypeMonth;

begin

if month = aug then

IbOutput.Caption := 'Хорошо бы поехать к морю!';

end.

Соответствие между значениями перечисляемого типа и порядковыми номерами этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер О, второе - 1 и т. д. Максимальная мощность перечисляемого типа составляет 65536 значений, поэтому фактически перечисляемый тип задает некоторое подмножество целого типа word и может рассматриваться как компактное объявление сразу группы целочисленных констант со значениями 0, 1 и т. д.

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

type

colors = (black, red, white);

ordenal= (one, two, three) ;

days = (Monday, Tuesday, Wednesday);

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

Ord(black)=0, ... , Ord(white)=2,

Ord(one)=0, ... , Ord(three)=2,

Ord(Monday)=0, ... , Ord(Wednesday)=2.

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

var

col : colors;

num : ordenal;

day : days ;

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

col := black;

num := Succ(two);

day := Pred(Tuesday);

но недопустимы

col := one;

day := black;

Как уже упоминалось, между значениями перечисляемого типа и множеством целых чисел существует однозначное соответствие, задаваемое функцией Ord(X). В Object Pascal допускается и обратное преобразование: любое выражение типа Word можно преобразовать в значение перечисляемого типа, если только значение целочисленного выражения не превышает мощности этого типа. Такое преобразование достигается применением автоматически объявляемой функции с'именем перечисляемого типа. Например, для рассмотренного выше объявления типов эквивалентны следующие присваивания:

col := black;

col := colors (0) ;

Разумеется, присваивание

col := 0;

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

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

var

col: (black, white, green);

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

Тип-диапазон задается границами своих значений внутри базового типа:

<мин.знач.>..<макс.знач.>

Здесь <мин. знач. > - минимальное значение типа-диапазона; <макс. знач. > - максимальное его значение.

Например:

type

digit = '0'..'9';

dig2 = 48 .. 57;

Тип-диапазон не обязательно описывать в разделе type, а можно указывать непосредственно при объявлении переменной, например:

var

date : 1. .31;

month: 1..12;

Ichr : 'А'..'Z';

При определении типа-диапазона нужно руководствоваться следующими правилами:

  • два символа “..” рассматриваются как один символ, поэтому жду ними недопустимы пробелы;
  • левая граница диапазона не должна превышать его правую границу.

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

type

days = (то,tu,we,th,fr,sa,su);

WeekEnd = sa .. su;

var

w : WeekEnd;

begin

w := sa;

end;

to ord(w) вернет значение 5, в то время как pred(W) приведет к ошибке.

В стандартную библиотеку Object Pascal включены две функции, поддерживающие работу с типами-диапазонами:

High(X) - возвращает максимальное значение типа-диапазона, к которому принадлежит переменная х;

Low (X) - возвращает минимальное значение типа-диапазона.

7.1.2. Вещественные типы

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

Таблица 7.4. Вещественные типы

Длина, байт | Название | Количество значащих цифр | Диапазон значений
8 4 8 10 8 8 | Real Single Double Extended Comp Currency | 15…16 7…8 15…16 19…20 19…20 19…20 | 5.0*10e-324…1.7*10e308 1.5*10e-45…3.4*10e38 5.0*10e324…1.7*10e308 3.4*10-4951…1.1*10e4932 -2e63…+2e63-1 +/-922 337 203 685477,5807

В предыдущих версиях Delphi 1...3 тип Real занимал 6 байт и имел диапазон значений от2, 9*10-39 до 1,7*1038. В версиях 4 и 5 этот тип эквивалентен типу Double. Если требуется (в целях совместимости) использовать 6-байтньш Real, нужно указать директиву компилятора {SREALCOMPATIBILITY ON}.

Как видно из табл. 7.4, вещественное число в Object Pascal занимает от 4 до 10 смежных байт и имеет следующую структуру в памяти ПК:

S

е

M

Здесь s - знаковый разряд числа; е - экспоненциальная часть; содержит двоичный порядок; m - мантисса числа.

Мантисса m имеет длину от 23 (для single) до 63 (для Extended) двоичных разрядов, что и обеспечивает точность 7...8 для single и 19...20 для Extended десятичных цифр. Десятичная точка (запятая) подразумевается перед левым (старшим) разрядом мантиссы, но при действиях с числом ее положение сдвигается влево или вправо в соответствии с двоичным порядком числа, хранящимся в экспоненциальной части, поэтому действия над вещественными числами называют арифметикой с плавающей точкой (запятой).

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

Например, если “машинное эпсилон” (см. пример п. 5.4.3) вычисляется с помощью такой программы:

type

RealType = Real;

var

Epsilon : RealType;

begin

Epsilon := 1;

while l+Eps4.1on/2 > 1 do

Epsilon := Epsilon/2;

IbOutput.Caption := FloatToStr(Epsilon)

end;

то независимо от объявления типа RealType (он может быть single, Real, Double или Extended) на печать будет выдан результат

1.08420217248550Е-0019

что соответствует типу Extended. Происходит это по той причине, что все операнды вещественного выражения 1+Epsilon/2 в операторе while перед вычислением автоматически преобразуются к типу Extended. Чтобы получить правильный результат, программу необходимо изменить следующим образом:

type

RealType = Real;

var

Epsilon, Epsl : RealType;

begin

Epsilon := 1;

repeat

Epsilon := Epsilon/2;

Epsl := 1 + Epsilon

until Epsl = 1;

IbOutput.Caption := FloatToStr(2*Epsilon)

end.

Особое положение в Object Pascal занимают типы comp и Currency, которые трактуются как вещественные числа с дробными частями фиксированной длины: в comp дробная часть имеет длину 0 разрядов, т. е. просто отсутствует, в currency длина дробной части -4 десятичных разряда. Фактически оба типа определяют большое целое число со знаком, сохраняющее 19...20 значащих десятичных цифр (во внутреннем представлении они занимают 8 смежных байт). В то же время в выражениях comp и currency полностью совместимы с любыми другими вещественными типами: над ними определены все вещественные операции, они могут использоваться как аргументы математических функций и т. д. Наиболее подходящей областью применения этих типов являются бухгалтерские расчеты.

Для работы с вещественными данными могут использоваться встроенные математические функции, представленные в табл. 7.5. В этой таблице Real означает любой вещественный тип, integer - любой целый тип.

Таблица 7.5. Стандартные математические функции Object Pascal

Обращение | Тип параметра | Тип результата | Примечание
abs (x) | Real, Integer | Тип аргумента Real | Модуль аргумента
Pi | - | << | П =3.141592653...
ArcTan(x) |  |  | Арктангенс (значение в радианах)
cos (x) | To же << | To же << | Косинус, угол в радианах
exp(x) | << | << | Экспонента
frac(x) | << | << | Дробная часть числа
int(x) | << | << | Целая часть числа
ln(x) | << | << | Логарифм натуральный
Random | - | << | Псевдослучайное число, равномерно распределенное в диапазоне 0...[1]
Random.fx) | Integer | Integer | Псевдослучайное целое число, равномерно распределенное в диапазоне 0...(х-1)
Randomize | - | - | Инициация генератора псевдослучайных чисел
sin (x) | Real | Real | Синус, угол в радианах
sqr(x) | To же | To же | Квадрат аргумента
sqrt(x) | << | << | Корень квадратный

На заметку Генератор псевдо случайных чисел представляет собой функцию которая берет некоторое целое число, называемое базовым, изменяет, его разряды По определенному алгоритму и выдает новое число результат. Одновременно с этим новое число становится базовым.прД следующем обращении к функций; и т. д. (Так как алгоритм процедуры не меняется: в ходе ее работы, числа называются псевдослучайна ми.) В системном модуле System, который автоматически доступен любой программе, базовое число хранится в переменной с именеД RandSeek и всегда имеет начальное значение.О. :Это означает, последовательном обращении к Random в разных, программах (или при нескольких прогонах одной программы) будет всегда вь1лапа'оД на и, та же прследовательность псевдослучайных чисел. НапримёД при; следующём обрабЬтчике bbRunCl.i.ck окно, нашей .учебной программы будет иметь вид, показанный на рис. 7.2:

procedure TfinExaniple.bbRunCliak(Sender:TObject);

var

S,3S: String;.

begin

S='';

for k :=1 to 300 do

SS := IntToStr (Random (1000))

while Length(SS)<3 do

if k mod 2С=0 then

begin

S : = '' ;

end

end

end;

(В программе выводятся 300 псевдослучайных чисел, которые группируются в строки по 20 чисел и дополняются ведущими нулями, если число имеет меньше трех цифр.) Если вы захотите повторить программу, вы получите точно такую же последовательность чисел, что н на рис. 7.2. С помощью процедуры Randomize в переменную RandSeek помещается

численное значение системного времени, что привёдет к генерации, другой последовательности. Обращение к этой процедуре нужно сделать в самом начале обработчика.

Рис. 7.2. Последовательность псевдослучайных чисел

Начиная с версии 2 в Delphi включен модуль Match, который существенно расширяет перечисленный в табл. 7.5 набор встроенных математических функций. Особенностью реализации содержащихся в нем почти 70 функций и процедур является их оптимизация для работы с арифметическим сопроцессором класса Pentium, так что все они производят необходимые вычисления за рекордно малое время.

Исходный текст содержится в файле файле Source\Rtl\Sys\Match.pas каталога размещения Delphi. В прил. 3 перечисляются подпрограммы модуля Match.

7.1.3. Тип дата-время

Тип дата-время определяется стандартным идентификатором TDateTime и предназначен для одновременного хранения и даты, и времени. Во внутреннем представлении он занимает 8 байт и подобно currency представляет собой вещественное число с фиксированной дробной частью: в целой части числа хранится дата, в дробной - время. Дата определяется как количество суток, прошедших с 30 декабря 1899 года, а время - как часть суток, прошедших с 0 часов, так что значение 36444,837 соответствует дате 11.10.1999 и времени 20:05. Количество суток может быть и отрицательным, однако значения меньшие -693594 (соответствует дате 00.00.0000 от Рождества Христова) игнорируются функциями преобразования даты к строковому типу.

Над данными типа TDateTime определены те же операции, что и над вещественными числами, а в выражениях этого типа могут участвовать константы и переменные целого и вещественного типов.

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

Таблица 7.6. Подпрограммы для работы с датой и временем

Function Date: TDateTime;

Function DateToStr(D: TDateTime): String;

Function DateTimeToStr(D: TDateTime): String;

Function FormatDateTime (Format: String;

Value: TDateTime): String;

Function Now: TDateTime;

Function Time: TDateTime;

Function TimeToStrfT: TDateTime): String;

Возвращает текущую дату

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

Преобразует дату и время в строку символов

Преобразует дату и время из параметра value в строку символов в соответствии со спецификаторами параметра Format (см. пояснения в п. 7.3.)

Возвращает текущую дату и время

Возвращает текущее время

Преобразует время в строку

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

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

IbOutput.Caption := DateToStr(Date + 21);

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

Time + StrToTime('1:30')

ИЛИ

Time+1.5/24

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

TDateTime:

procedure TfmExample.bbRunClick(Sender: TObject) ;

{Показывает вещественное число, введенное в строке edinput,в формате даты-времени}

var

k: Double;

begin

//Преобразуем строковый ввод в вещественное число

try

k := StrToFloat(edinput.Text);

except

ShowMessage('Ошибка записи вещественного числа');

edinput.SelectAll ;

Exit

end;

//Показываем результат как дату-время

IbOutput. Caption := ForrnatDateTime

('dd.mm.yyyy hh:mm:ss', k);

// Возвращаем фокус ввода в edinput edinput.SetFocus ;

end;

7.2. СТРУКТУРИРОВАННЫЕ ТИПЫ

Любой из структурированных типов (а в Object Pascal их четыре: массивы, записи, множества и файлы) характеризуется множественностью образующих этот тип элементов. Каждый элемент, в свою очередь, может принадлежать структурированному типу, что позволяет говорить о возможной вложенности типов. В Object Pascal допускается произвольная глубина вложенности типов, однако суммарная длина любого из них во внутреннем представлении не должна превышать 2 Гбайт [16-разрядные версии операционной системы Windows З.х используют так называемую “ сегментную” модель памяти, поэтому в Delphi 1 любой структурированный тип не может занимать более одного сегмента (65536 байт).].

В целях совместимости со стандартным Паскалем в Object Pascal разрешается перед описанием структурированного типа ставить зарезервированное слово packed, предписывающее компилятору по возможности экономить память, отводимую под объекты структурированного типа; но компилятор фактически игнорирует это указание: “упаковка” данных в Object Pascal осуществляется автомата- ;

чески везде, где это возможно.

7.2.1. Массивы

Массивы в Object Pascal во многом схожи с аналогичными типами данных в других языках программирования. Отличительная особенность массивов заключается в том, что все их компоненты суть данные одного типа (возможно, структурированного). Эти компоненты можно легко упорядочить и обеспечить доступ к любому из них простым указанием его порядкового номера, например:

type

digit = array [0..9] of Char;

matrix = array [byte] of Single;

var

m : matrix;

d : digit;

i : integer;

begin

m[17] := ord(d[i-l])/10;

end.

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

<имя типа> = array [ <сп.инд.типов> ] of <тип>;

Здесь <имя типа> - правильный идентификатор; array, of - зарезервированные слова {массив, из); <сп.инд.типов> - список из одного или нескольких индексных типов, разделенных запятыми; квадратные скобки, обрамляющие список, - требование синтаксиса;

<тип> - любой тип Object Pascal.

В качестве индексных типов в Object Pascal можно использовать любые порядковые типы, имеющие мощность не более 2 Гбайт (т. е. Кроме LongWord И Int64)

Определить переменную как массив можно и непосредственно при описании этой переменной, без предварительного описания типа массива, например:

var

a,b : array [1..10] of Real;

Обычно в качестве индексного типа используется тип-диапазон, в котором задаются границы изменения индексов. Так как тип <тип>, идущий в описании массива за словом of, - любой тип Object Pascal, то он может быть, в частности, и другим массивом, например:

type

mat = array [0..5] of array [-2..2] of array [Char] of Byte;

Такую запись можно заменить более компактной:

type

mat = array [0..5,-2..2,char] of Byte;

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

Если, например,

var

a: array [1..2,1..2] of Byte;

begin

а[1,1] := 1;

а[2,1] := 2;

а[1,2] := 3;

а[2,2] := 4;

end.

то в памяти последовательно друг за другом будут расположены байты со значениями 1,3,2,4 . Это обстоятельство может оказаться важным при использовании стандартной процедуры копирования памяти MoveMemory.

В Object Pascal можно одним оператором присваивания передать все элементы одного массива другому массиву того же типа, например:

var

a,b : array [1..5] of Single;

begin

а := b;

end.

После этого присваивания все пять элементов массива а получат те же значения, что и в массиве в. Замечу, что объявление

var

a: array [1..5] of Single;

b: array [1..5] of Single;

создаст разные типы массивов, поэтому оператор

а := b;

вызовет сообщение об ошибке.

Над массивами не определены операции отношения. Нельзя, например, записать

if а = b then ...

Сравнить два массива можно поэлементно, например:

var

a,b : array [1..5] of Single;

eq : Boolean;

i : Byte;

begin

eq := True; for i := 1 to 5 do

if a[i] <> b[i] then

eq := False/if eq then

end.

Динамические массивы

В версии Delphi 4 впервые введены так называемые динамические массивы. При объявлении таких массивов в программе не следует указывать границы индексов:

var

A: array of Integer;

В: array of array of Char;

C: array of array of array of Real;

В этом примере динамический массив а имеет одно измерение, массив в - два и массив с - три измерения. Распределение памяти и указание границ индексов по каждому измерению динамических массивов осуществляется в ходе выполнения программы путем инициации массива с помощью функции setLength. В ходе выполнения такого оператора:

SetLength(А,3);

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

Фактически идентификатор динамического массива ссылается на указатель (см. гл. 9), содержащий адрес первого байта памяти, выделенной для размещения массива. Поэтому для освобождения этой памяти достаточно присвоить идентификатору значение nil (другим способом является использование процедуры Finalize):

var

А,В: array of Integer;

begin

// Распределяем память:

SetLength(A,10) ;

SetLength(B,20) ;

// Используем массивы:

// Освобождаем память:

А := NIL;

Finalize(В);

end;

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

В многомерных массивах сначала устанавливается длина его первого измерения, затем второго, третьего и т. д. Например:

var

A: array of array of Integer;//Двумерный динамический массив begin

//Устанавливаем длину первого измерения (количество столбцов):

SetLength(A,3) ;

//Задаем длину каждого столбца:

SetLength(A[0],3) ;

SetLength(A[l],3) ;

SetLength(A[2] ,3) ;

end;

Обратите внимание: в отличие от обычных массивов стандартного Паскаля (и Object Pascal), динамические массивы могут иметь разную длину по второму и следующим измерениям. В предыдущем примере определен квадратный массив 3х3. Однако ничто не мешает нам создать, например, треугольный массив:

SetLength(A,3) ;

//Задаем длину каждого столбца:

SetLength(A[0],3) ;

SetLength(A[l],4) ;

SetLength(A[2],5) ;

В многомерных динамических массивах каждый элемент любого из N-1 измерений (N - количество измерений) представляет собой динамический массив и, следовательно, нуждается в инициации. Вот как, например, можно инициировать вещественный кубический массив 3х3х3:

var

A: array of array of array of Real;

i, j: Integer;

begin

SetLength(A,3) ;

for i := 0 to 2 do

begin

SetLength(A[i],3) ;

for j := 0 to 2 do SetLength{A[i,j],3) ;

end;

end;

7.2.2. Записи

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

Структура объявления типа записи такова:

<имя типа> = record <сп.полей> end;

Здесь <имя типа> - правильный идентификатор; record/ end - зарезервированные слова {запись, конец); <сп.полей> - список полей; представляет собой последовательность разделов записи, между которыми ставится точка с запятой.

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

type

BirthDay = record Day, Month: Byte;

Year : Word end;

var

a,b : Birthday;

В этом примере тип BirthDay (день рождения) есть запись с полями Day, Month и Year (день, месяц и год); переменные а и в содержат записи типа BirthDay.

Как и в массиве, значения переменных типа записи можно присваивать другим переменным того же типа, например

а := b;

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

a.day := 27;

b.year := 1939;

Для вложенных полей приходится продолжать уточнения:

type

BirthDay = record Day,Month: Byte;

Year : Word end;

var

с : record

Name : String;

Bd : BirthDay end;

begin

if c.Bd.Year = 1989 then ... end.

Чтобы упростить доступ к полям записи, используется оператор присоединения with:

with <переменная> do <оператор>;

Здесь with, do - зарезервированные слова (с, делать);

<переменная> - имя переменной типа запись, за которой, возможно,

следует список вложенных полей; <оператор> - любой оператор Object Pascal.

Например:

с.Bd.Month := 9;

Это эквивалентно

with c.Bd do Month := 9;

или with с do with Bd do Month := 9;

или with с, Bd do Month := 9;

Object Pascal разрешает использовать записи с так называемыми вариантными полями, например:

type

Forma = record Name: String;

case byte of

0: (Birthplace: String [40]);

1: (Country : String [20];

EntryPort : String [20];

EntryDate : 1..31;

ExitDate : 1..31)

end;

В этом примере тип Forma определяет запись с одним фиксированным полем Name и вариантной частью, которая задается предложением case ... of. Вариантная часть состоит из нескольких вариантов (в примере - из двух вариантов: 0 и 1). Каждый вариант определяется константой выбора, за которой следуют двоеточие и список полей, заключенный в круглые скобки. В любой записи может быть только одна вариантная часть, и, если она есть, она должна располагаться за всеми фиксированными полями.

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

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

var

Mem4 : record case Byte of

0 : (by : array [0..3] of Byte);

1 : (wo : array [0..1] of Word);

2 : (lo : Longint) ;

end;

В этом примере запись мет4 имеет три варианта, каждый из которых занимает в памяти один и тот же участок из 4 байт. В зависимости от того, к какому полю записи мы обращаемся в программе, этот участок может рассматриваться как массив из 4 байт (поле by), массив из двух целых типа word (поле wo) или, наконец, как одно целое число типа Longint (поле l0). Например, этой записи можно сначала присвоить значение как длинному целому, а затем проанализировать результат по байтам или словам:

var

х : Word;

xb: Byte;

xl: Longint;

begin

with m do begin

lo := Trunc(2*pi*x);

if wo[l] =0 then if by[l] =0 then

xb := x[0] else

x := wo[0] else

xl := lo

end;

end.

Предложение case ... of, открывающее вариантную часть, внешне похоже на соответствующий оператор выбора, но на самом деле лишь играет роль своеобразного служебного слова, обозначающего начало вариантной части. Именно поэтому в конце вариантной части не следует ставить end как пару к case ... of. (Поскольку вариантная часть - всегда последняя в записи, за ней все же стоит end, но лишь как пара к record). Ключ выбора в предложении case ... of фактически игнорируется компилятором: единственное требование, предъявляемое к нему в Object Pascal, состоит в том, чтобы ключ определял некоторый стандартный или предварительно объявленный порядковый тип.

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

7.2.3. Множества

Множества - это наборы однотипных логически связанных друг с другом объектов. Характер связей между объектами лишь подразумевается программистом и никак не контролируется Object Pascal. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256 (множество, не содержащее элементов, называется пустым). Именно непостоянством количества своих элементов множества отличаются от массивов и записей.

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

Пример определения и задания множеств:

type

digitChar = set of '0'..'9';

digit = set of 0. .9;

var

sl,s2,s3 : digitChar;

s4,s5,s6 : digit;

begin

si = ['1', '2', '3'];

s2 = ['3', '2', '1'];

s3 = ['2', '3'];

s4 = [0..3, 6];

s5 = [4, 5];

s6 = [3..9];

end.

В этом примере множества si и s2 эквивалентны, а множество S3 включено в s 2 , но не эквивалентно ему.

Описание типа множества имеет вид:

<имя типа> = set of <базовый тип>;

Здесь <имя типа> - правильный идентификатор; set, of - зарезервированные слова (множество, из); <базовый тип> - базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, кроме Word, Integer, Longint, Int64.

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

Над множествами определены следующие операции:

* пересечение множеств; результат содержит элементы, общие для обоих множеств; например, s4*s6 содержит [3], s4*s5 -пустое множество (см. выше);

+ объединение множеств; результат содержит элементы первого множества, дополненные недостающими элементами из второго множества:

S4+S5 содержит [0,1,2,3,4,5,6];

S5+S6 содержит [3, 4, 5, 6, 7, 8, 9] ;

разность множеств; результат содержит элементы из первого множества, которые не принадлежат второму:

S6-S5 содержит [3,6,7,8,9];

S4-S5 содержит [0,1, 2, 3, 6] ;

= проверка эквивалентности; возвращает True, если оба множества эквивалентны;

<> проверка неэквивалентности; возвращает True, если оба множества неэквивалентны;

<= проверка вхождения; возвращает True, если первое множество включено во второе;

>= проверка вхождения; возвращает True, если второе множество включено в первое;

in проверка принадлежности; в этой бинарной операции первый элемент - выражение, а второй - множество одного и того же типа; возвращает True, если выражение имеет значение, принадлежащее множеству:

3 in s 6 возвращает True;

2*2 in si возвращает False.

Дополнительно к этим операциям можно использовать две процедуры.

include - включает новый элемент во множество. Обращение к процедуре:

Include(S,I)

Здесь s - множество, состоящее из элементов базового типа TSet Base; I - элемент типа TSetBase, который необходимо включить во множество.

exclude - исключает элемент из множества. Обращение:

Exclude(S,I)

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

Учебная программа PRIMSET

В следующем примере, иллюстрирующем приемы работы с множествами, реализуется алгоритм выделения из первой сотни натуральных чисел всех простых чисел[Простыми называются целые числа, которые не делятся без остатка на любые другие целые числа, кроме 1 и самого себя. К простым относятся 1, 2, 3, 5, 7, 11, 13 и т. д..]. В его основе лежит прием, известный под названием “решето Эратосфена”. В соответствии с этим алгоритмом вначале формируется множество BeginSet, состоящее из всех целых чисел в диапазоне от 2 до N. В множество primerset (оно будет содержать искомые простые числа) помещается 1. Затем циклически повторяются следующие действия:

  • взять из BeginSet первое входящее в него число Next и поместить его В PrimerSet;
  • удалить из BeginSet число Next и все другие числа, кратные ему, Т. е. 2*Next, 3*Next И Т.Д.

Цикл повторяется до тех пор, пока множество BeginSet не станет пустым.

Эту программу нельзя использовать для произвольного N, так как в любом множестве не может быть больше 256 элементов.

procedure TfmExample.bbRunClick(Sender: TObject);

// Выделение всех простых чисел из первых N целых

const

N = 255; // Количество элементов исходного множества

type

SetOfNumber = set of 1..N;

var

n1,Next,i: Word; // Вспомогательные переменные

BeginSet, // Исходное множество

PrimerSet: SetOfNumber; // Множество простых чисел

S: String;

begin

BeginSet := [2..N];

// Создаем исходное множество

PrimerSet:= [1]; // Первое простое число

Next := 2; // Следующее простое число

while BeginSet о [ ] do // Начало основного цикла

begin

nl := Next; //nl-число, кратное очередному простому (Next)

// Цикл удаления из исходного множества непростых чисел:

while nl <= N do

begin

Exclude(BeginSet, nl);

n1 := nl + Next // Следующее кратное

end; // Конец цикла удаления

Include(PrimerSet, next);

// Получаем следующее простое, которое есть первое

// число, не вычеркнутое из исходного множества

repeat

inc(Next)

until (Next in BeginSet) or (Next > N)

end;

// Конец основного цикла

// Выводим результат:

S := '1';

for i := 2 to N do

if i in PrimerSet then

S := S+', '+IntToStr(i);

mmOutput.Lines.Add(S)

end;

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

type

SetOfNumber = set of 1..1;

и еще раз запустите программу из предыдущего примера. На экран будет выведено 1, 3, 5, 7

Множества BeginSet и PrimerSet состоят теперь из одного элемента, а программа сумела поместить в них не менее семи!

Секрет этого прост: внутреннее устройство множества таково, что каждому его элементу ставится в соответствие один двоичный разряд (один бит); если элемент включен во множество, соответствующий разряд имеет значение 1, в противном случае - 0. В то же время минимальной единицей памяти является один байт, содержащий 8 бит, поэтому компилятор выделил множествам по одному байту, и в результате мощность каждого из них стала равна 8 элементам. Максимальная мощность множества - 256 элементов. Для таких множеств компилятор выделяет по 16 смежных байт.

И еще один эксперимент: измените диапазон базового типа на 1..256. Хотя мощность этого типа составляет 256 элементов, при попытке компиляции программы компилятор сообщит об ошибке: Sets may have at most 256 elements (Множества могут иметь не более 256 элементов) т. к. нумерация элементов множества начинается с нуля независимо от объявленной в программе нижней границы. Компилятор разрешает использовать в качестве базового типа целочисленный тип-диапазон с минимальной границей 0 и максимальной 255 или любой перечисляемый тип не более чем с 256 элементами (максимальная мощность перечисляемого типа - 65536 элементов).

7.3. СТРОКИ

Для обработки текстов в Object Pascal используются следующие типы:

  • короткая строка shortString или string [n] , где n <= 255;
  • длинная строка string;
  • широкая строка WideString;
  • нуль-терминальная строка pchar.

Общим для этих типов является то, что каждая строка трактуется как одномерный массив символов, количество символов в котором может меняться в работающей программе: для string [n] длина строки меняется от 0 до n, для string и pchar - от 0 до 2 Гбайт.

В стандартном Паскале используются только короткие строки String [n] . В памяти такой строке выделяется n+i байт, первый байт содержит текущую длину строки, а сами символы располагаются начиная со 2-го по счету байта. Поскольку для длины строки в этом случае отводится один байт, максимальная длина короткой строки не может превышать 255 символов. Для объявления короткой строки максимальной длины предназначен стандартный тип ShortString (эквивалент String[255]).

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

В 32-разрядных версиях Delphi введен новый тип string, сочетающий в себе удобства обоих типов. При работе с этим типом память выделяется по мере надобности (динамически) и ограничена имеющейся в распоряжении программы доступной памятью. Замечу, что в Delphi 1 тип string эквивалентен String [2 55], т. е. определяет короткую строку максимально возможной длины.

Для совместимости с компонентами, основывающимися на OLE-технологии, в Delphi 32 введены также широкие строки, объявляемые стандартным типом wideString. По своим свойствам они идентичны длинным строкам string, но отличаются от них тем, что для представления каждого символа используются не один, а два байта.

Примеры объявлений строковых типов:

var

ssS: String[250];// Короткая строка длиной до 250 символов

ssMax: ShortString;// Короткая строка длиной до 255 символов

stS: String; // Длинная строка

swS: WideString;// Широкая строка

pcS: PChar; // Ссылка на нуль-терминальную строку

acS: array [0..1000] of Char; // Нуль-терминальная строка

// длиной до 1000 символов

При объявлении переменной sss компилятор выделит для ее размещения 250 + 1 = 251 байт и поместит в первый байт 0 - текущую длину строки. При выполнении такого фрагмента программы:

procedure TfmExample.bbRunClick(Sender: TObject);

var

ssS: String[250];

begin

ssS :='Строка символов';

ssS[6] := 'и'; // Символы в строке нумеруются, начиная с 1 IbOutput.Caption := ssS; // Выводится “Строки символов”

end;

сначала в переменную ssS будет помещена цепочка символов строка символов, причем 1-й байт получит значение 15 (количество символов в строке). После выполнения второго оператора символ с индексом б (индексация байтов начинается с 0, но, поскольку первый байт содержит текущую длину, первый символ в строке имеет индекс 1) будет заменен на “и”, и в переменной окажется цепочка строки символов.

Совершенно другим будет механизм работы с памятью при объявлении длинной строки sts: компилятор выделит для переменной 4 байта, достаточные для размещения номера той ячейки памяти, начиная с которой будет фактически располагаться символьная строка. Говорят, что sts ссылается на строку. Такого рода переменные-ссылки называются указателями и обсуждаются в гл. 9. При выполнении первого оператора такого обработчика:

procedure TfmExample.bbRunClick(Sender: TObject);

var

stS, stSS: String;

begin

stS :='Строка символов';

stSS := stS;

stS := 'Это - '+stS;

stS[7] := ' c' ; // Символы в строке нумеруются, начиная с 1 IbOutput.Caption := stS; //Выводится “Это - строка символов”

end;

программа (а не компилятор!) определит длину цепочки символов Строка символов, обратится к ядру перационной системы (ОС) с требованием выделить для нее участок памяти длиной 15+5=20 байт, поместит в переменную sts номер первого выделенного байта [На самом деле в stS запоминается дескриптор выделенного участка памяти, см. гл. 9.] и, начиная с него, разместит в этом участке цепочку символов, завершив ее терминальным нулем и 4-байтным счетчиком ссылок. Такое размещение на этапе прогона программы называется динамическим, в то время как размещение на этапе компиляции - статическим. Счетчик ссылок играет важную роль в механизме работы с памятью. С его помощью реализуется “кэширование” памяти: при выполнении оператора

stSS := stS;

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

stS := 'Это - '+stS;

счетчик ссылок уменьшается на единицу, выделяется новая область памяти длиной 2 о + б = 2 б байт, указатель на эту область помещается в stS, а в саму память переписывается цепочка символов Это -строка символов, терминальный ноль и содержащий единицу счетчик ссылок. Теперь переменные stS и stss будут ссылаться на разные участки памяти, счетчики ссылок которых будут содержать по единице. Выделенная для размещения строки String область памяти освобождается, если ее счетчик ссылок стал равен нулю.

Похожим образом осуществляется работа с памятью при объявлении переменной pcs типа pchar: компилятор считает эту переменную указателем и выделит для нее 4 байта:

procedure TfmExample.bbRunClick(Sender: TObject);

var

pcS: PChar;

begin

pcS :='Строка символов';

pcS[5] := 'и';{Символы в нуль-строке нумеруются,начиная с 0}

IbOutput.Caption := pcS; // Выводится “Строки символов”

end;

Программа потребует от ОС 15 + 1 = 16 байт, разместит в памяти цепочку символов и завершающий ее терминальный 0 и поместит адрес выделенного участка памяти в pcs.

В стандартном паскале, при обращении к области памяти, на которую ссылается указатель , требуется за именем указателя ставить специальный символ .“^”. В Object Раsса1 интенсивно используется динамическая, память, и поэтому, это жесткое требование смягчено: в большинстве случаев (и при обращении к типу pchar в том числе) символ "^" ставить не следует.

И, наконец, последнее объявление acs как массива символов. В Object Pascal считается совместимым с pchar одномерный массив символов с нулевой нижней границей. В отличие от pcs память для такой переменной выделяется статически (в нашем примере компилятор выделит в сегменте данных для переменной acs 1001 байт).

Для размещения в acs нуль-терминальной цепочки символов используется процедура strcopy:

procedure' TfmExample.bbRunClick(Sender: TObject) ;

var

acS: array [0..1000] of Char;

begin

StrCopy(acS,'Строка символов') ;

acS[5] := 'и';{ Символы в нуль-строке нумеруются,начиная с 0}

lbOutput.Caption := acS; // Выводится “Строки символов”

end;

Необходимость в нуль-терминальных строках возникает только при прямом обращении к API-функциям ОС. При работе с компонентами Delphi в основном используются более удобные длинные строки, которые рассматриваются в п. 7.3.1.

И несколько слов о широких строках, 32-разрядные версии Windows используют три сорта символов: однобайтный символ ANSI, двухбайтный символ и символ Unicode. Однобайтный символ связан с одним из 256 возможных значений, которые трактуются в зависимости от установленной в Windows национальной страницы (для размещения кириллицы используется страница 1251). 256 символов вполне достаточны для отображения национального алфавита любого европейского языка. Для отображения алфавитов некоторых азиатских языков этого недостаточно. В этом случае используется двухбайтный символ, в котором младший байт обычно кодируется семибитным ASCII-кодом, а старший указывает, как должен трактоваться этот код (каким символом он будет изображаться в документе или на экране). Символ Unicode в памяти занимает одно слово, которое имеет 65536 возможных значений. Специальная международная комиссия по Unicode выработала соглашение, позволяющее с помощью этого кода представить все символы всех языков мира. Двухбайтные символы и символы Unicode объявляются стандартным типом widecnar, а составленные из них строки - типом widestring. Все Windows-программы, использующие OLE-технологию обмена строками, должны кодировать символы в соответствии с Unicode.

7.3.1. Типы String и ShortString

Несмотря на разницу во внутреннем представлении, короткие строки ShortString и длинные строки string имеют для программиста одинаковые свойства.

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

while (Length(stS) о 0) and (stS[Length(stS)] = ' ') do

SetLentgh(stS, Length (stS) - 1);

В этом примере стандартная процедура setLength устанавливает новую длину строки. К строкам можно применять операцию “+” -сцепление, например:

stS := 'а' + 'b'; // stS содержит "ab"

stS := stS + 'с'; // stS содержит "abc"

Если длина сцепленной строки превысит максимально допустимую длину N короткой строки, то “лишние” символы отбрасываются. Следующая программа, например, выведет символ “I”:

Procedure TfmExamlpe.bbRunClick(Sender: TObject);

var

ssS: String[1];

begin

ssS := '123';

IbOutput := ssS;

end;

Операции отношения =, <>, >, <, >=, <= выполняются над двумя строками посимвольно, слева направо с учетом внутренней кодировки символов. Если одна строка меньше другой по длине, недостающие символы короткой строки заменяются значением #о.

Следующие операции отношения дадут значение True:

'''' < '.'

'А' > '1'

'Object' < ' Object Pascal'

'Пас' > 'Pascal'

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

Таблица 7.7. Процедуры и функции для работы со строками

Function AnsiLowerCase(const S: String): String; |  | Возвращает исходную строку S, в которой все заглавные буквы заменены на строчные в соответствии с национальной кодировкой Windows (т. е. с учетом кириллицы)
Function AnsiUpperCase(const S: String): String; |  | Возвращает исходную строку s, в которой все строчные буквы заменены на заглавные в соответствии с национальной кодировкой Windows
Function Concat(Sl [, S2, ..., SN]: String): String; |  | Возвращает строку, представляющую собой сцепление строк-параметров S1, S2, ... , SN
Function Copy(St: String; Index, Count: Integer): String; |  | Копирует из строки St count символов, начиная с символа с номером Index
Procedure Delete(St: String; Index, Count:" Integers- |  | Удаляет count символов из строки St, начиная с символа с номером index
Procedure Insert(SubSt: String; St, Index: Integer); |  | Вставляет подстроку SubSt в строку St, начиная с символа с номером Index
Function Length(St: String): Integer; |  | Возвращает текущую длину строки St
Function LowerCase(const S:String): String; |  | Возвращает исходную строку S, в которой все латинские заглавные буквы заменены на строчные
procedure OleStrToStrVar(Source: PWideChar; var Dest:String) ; |  | Копирует “широкую” (двухбайтную) строку в обычную строку Object Pascal
Function Pos(SubSt, St:String): Integer; |  | Отыскивает в строке St первое вхождение подстроки SubSt и возвращает номер позиции, с которой она начинается. Если подстрока не найдена, возвращается ноль
Procedure SetLength(St:String; NewLength: Integer); |  | Устанавливает новую (меньшую) длину NewLength строки St. если NewLength больше текущей длины строки, обращение к SetLength игнорируется
function StringOfChar(Ch:Char; Count: Integer):String; |  | Создает строку, состоящую из Count раз повторенного символа ch
function StringToOleStr(const Source: String):PWideChar; |  | Копирует обычную строку в двухбайтную
function StringToWideChar(const Source: String; Dest:PWideChar; DestSize: Integer) : PWideChar; |  | Преобразует обычную строку в строку с символами UNICODE
Function Uppercase(const S:String): String; |  | Возвращает исходную строку S, в которой все строчные латинские буквы заменены на заглавные
Подпрограммы преобразования строк в другие типы
Function StrToCurr(St: String): Currency; | Преобразует символы строки St в целое число типа Currency. Строка не должна содержать ведущих или ведомых пробелов
Function StrToDate(St: String): TDateTime; | Преобразует символы строки St в дату. Строка должна содержать два или три числа, разделенных правильным для Windows разделителем даты (в русифицированной версии таким разделителем является “.”). Первое число - правильный день, второе - правильный месяц. Если указано третье число, оно должно задавать год в формате XX или ХХХХ. Если символы года отсутствуют, дата дополняется текущим годом. Например: DateToStr(StrToDate('28.06')) даст строку '28.06.99' (см. ниже пояснения)
Function StrToDateTime(St: String): TDateTime; | Преобразует символы строки St в дату и время. Строка должна содержать правильную дату (см. StrToDate) и правильное время (см. StrToTime), разделенные пробелом, например: StrToDateTime('28.06 18:23')
Function StrToFloat(St:String): Extended; | Преобразует символы строки St в вещественное число. Строка не должна содержать ведущих или ведомых пробелов
Function StrToInt(St:String): Integer; | Преобразует символы строки St в целое число. Строка не должна содержать ведущих или ведомых пробелов
Function StrToIntDef(St:String; Default: Integer):Integer; | Преобразует символы строки St в целое число. Если строка не содержит правильного представления целого числа, возвращается значение Default
Function StrToIntRange(St:String; Min, Max: Longint):Lomgint; | Преобразует символы строки St в целое число и возбуждает исключение ERangeError, если число выходит из заданного диапазона Min.. .мах
Function StrToTime(St:String): TDateTime; | Преобразует символы строки St во время. Строка должна содержать два или три числа, разделенных правильным для Windows раздели телем времени (для русифицированной версии таким разделителем является “:”). Числа задают часы, минуты и, возможно, секунды. За послед ним числом через пробел могут следовать символы “am” или “рm”, указывающие на 12- часовой формат времени
Procedure Val(St: String; var X; Code: Integer); | Преобразует строку символов St во внутреннее представление целой или вещественной переменной х, которое определяется типом этой переменной. Параметр Code содержит ноль, если преобразование прошло успешно, и тогда в х помещается результат преобразования, в противном случае он содержит номер позиции в строке St, где обнаружен ошибочный символ, и в этом случае содержимое х не меняется. В строке St могут быть ведущие и/или ведомые пробелы. Если St содержит символьное представление вещественного числа, разделителем целой и дробной частей должна быть точка независимо от того, каким символом этот разделитель указан в Windows
Подпрограммы обратного преобразования
Function DateTimeToStr(Value: TDateTime): String; Procedure DateTime-ToString(var St: String; Format: String;- Value: TData-Time); | Преобразует дату и время из параметра в строку символов Преобразует дату и время из параметра value в строку St в соответствии со спецификаторами параметра Format (см. пояснения ниже)
Function DateToStr(Value: TDateTime): String; | Преобразует дату из параметра value в строку символов
Function FloatToStr(Value: Extended): String; | Преобразует вещественное значение value в строку символов.
Function FloatToStrF(Value: Extended; Format: TFloatPor- mat; Precision, Digits: Inte ger) : String; | Преобразует вещественное значение Value в строку символов с учетом формата Format и параметров precision и Digits (см. пояснения ниже).
Function Format(const Format: String; const Args: array of const): Strings; | Преобразует произвольное количество аргументов открытого массива Args в строку в соответствии с форматом Format (см. пояснения ниже)
Function FormatDateTime (Format: String; Value:.TDateTime): String; | Преобразует дату и время из параметра value в строку символов в соответствии со спецификаторами параметра Format (см. пояснения ниже)
Function FormatFloat(Format:String; Value: Extended): String; | Преобразует вещественное значение value в строку символов с учетом спецификаторов формата Format (см. пояснения ниже)
function IntToHex(Value: Integer; Digits: Integer):Strings; | Преобразует целое число Value в строку символьного представления шестнадцатеричного формата: Digits - минимальное количество символов в строке
Function IntToStr(Value: Integer) : String; | Преобразует целое значение Value в строку символов
Procedure Str(X [:Width[:Decimals]]; var St:String) ; | Преобразует число х любого вещественного илицелого типов в строку символов St; параметры width и Decimals, если они присутствуют, задают формат преобразования: width определяет общую ширину поля, выделенного под соответ ствующее символьное представление вещественного или целого числа х, a Decimals - количество символов в дробной части (этот параметр имеет смысл только в том случае, когда х -вещественное число)
Function TimeToStr(Value: TDateTime): String; | Преобразует время из параметра Value в строку символов

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

Обычно разделителем групп разрядов в русской языке принято считать пробел. Однако в русифицированном варианте Windows для этих целей на самом деле используется, не пробел, а символ-#160, внешне похожий на пробел Убедиться: в, этом можно с помощью такой несложной программы:

procedure TfmExample.bbRunClick (Sender : TObject) ;

var S: String;

begin

S:=FloatToStrF(1000,ffNumber,4,0);

EdOutput.Yext:=IntToStr(Ord(S[2]));

end;

Если вы действительно хотите использовать пробел, его нужно явно указать в окошке. Разделитель: групп разрядов (Пуск.| Настройка | Панель управления | Язык и стандарты | Числа)

В Delphi 4...6 изменена функция StrToDate для того, чтобы упростить ввод двух цифр года с учетом смены столетия. С этой целью в модуле system введена глобальная переменная TwoDigitYearCenturywindow, которая определяет цифровой рубеж столетия. Если две цифры года больше или равны значению этой переменной, цифры года относятся к текущему столетию, если меньше - переносятся в следующее. Пусть, например, TwoDigitYearCenturywindow содержит значение 50. Тогда любые две цифры года в диапазоне 00..49 дополняются базой 2000 (2000, 2001,...,2049), в то время как цифры в диапазоне 50..99 дополняются базой 1900 (1950, 1951,...,1999). Такой прием позволяет в известной мере смягчить проблему двухцифровых годов на рубеже столетия, однако полным ее решением будет, разумеется переход на четырехцифровое представление года. По умолчанию TwoDigitYearCenturywindow имеет значение 0, и поэтому любые две цифры года дополняются базой 1900.

Используемая в процедуре DateTimeToString и в функции FormatDateTime строка Format может содержать такие символы-спецификаторы (в примерах показаны строки для времени 19 часов 45 минут и даты 8 июня 1999 года):

Таблица 7.8. Спецификаторы формата даты/времени

с | Отображает сначала дату в формате дд.мм.гг, затем пробел и время в формате чч.мм. ее: 08.06. 99 19:45
d | Отображает день без ведущего нуля: 8
dd | Отображает день с ведущим нулем: 0 8
dddd | Отображает день недели: воскресенье (для нерусифицированной версии Windows - Sunday)
ddddd | Отображает дату в формате дд. мм. гг: 08.06.99
dddddd | Отображает дату в формате д Месяц год: 8 Июнь 1999 (для нерусифицированной версии Windows - 8 June 1999)
m | Отображает число месяца без ведущего нуля: 6
mm | Отображает число месяца с ведущим нулем: 06
nunm | Отображает сокращенное название месяца: июн.
nuninm | Отображает полное название месяца: Июнь
у или уу | Отображает две последние цифры года: 9 9
ууу или уууу | Отображает все цифры года: 1999
h | Отображает час без ведущего нуля: 19
hh | Отображает час с ведущим нулем: 19
n | Отображает минуты без ведущего нуля: 4 5
nn | Отображает минуты с ведущим нулем: 4 5
s | Отображает секунды без ведущего нуля: 0
ss | Отображает секунды с ведущим нулем: 00
t | Отображает время в формате чч:лш: 19:45
tt | Отображает время в формате чч:мм:сс: 19:45:00
am/pm | Отображает время в 12-часовом формате (am - до полудня, pm - после полудня). Для спецификаторов hh: mm am/pm получим 07:45 pm
ampm | Отображает время в 12-часовом формате, но без указания до/после полудня. Для спецификаторов hh: mm ampm получим 07:45
a/p | Отображает время в 12-часовом формате (а - до полудня, р - после полудня). Для спецификаторов hh: mm a/p получим 07:45 р
/ | Отображает используемый в Windows разделитель даты. Для спецификаторов d/m/у получим 8. 6. 99
: | Отображает используемый в Windows разделитель времени. Для спецификаторов h:n: s получим 19:45:0

Замечу, что любые другие символы, указанные в строке Format, a также заключенные в апострофы или кавычки специальные символы-спецификаторы помещаются в выходную строку без преобразования, поэтому спецификаторы 'h час n мин' дадут строку 19 час 45 мин, а 'h час "n" мин' - 19час n мин.

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

Таблица 7.9. Системные переменные, управляющие стандартным отображением данных

CurrencyString: String; | Символ или символы денежной единицы; для русифицированной Windows ими являются символы “р. ”
CurrencyFormat: Byte; | Определяет положение символов денежной единицы: 0 - $1; 1 - 1$; 2 - $ 1; 3 - 1 $; в русифицированной Windows используется формат 1 (1р.)
NegCurrFormat: Byte; | Определяет формат отрицательной суммы: 0 = ($1); 1 = -$1; 2 = S-1; 3 = $1-; 4 = (1$); 5 = -1$; 6 = 1-$; 7 =1$-; 8=-!$; 9=-$!; 10=1 $-; 11 =$1-; 12=$-!; 13= 1- $; 14 = (S 1); 15 = (1 $); в русифицированной Windows используется формат 5 (-lp.)
ThousandSeparator: Char; | Разделитель тысяч; в русифицированной Windows используется символ #166
DecimalSeparator: Char; | Разделитель дробной и целой частей числа (', ')
CurrencyDecimals: Byte; | Количество цифр после запятой в денежном формате. Обычно содержит 0, что блокирует вывод мелкой денежной единицы. Чтобы в сумме присут ствовали не только рубли, но и копейки, в переменную следует установить значение 2
DateSeparator: Char; | Разделитель даты; в русифицированной Windows используется ' . '
ShortDateFormat: String; | Обычно используется формат ' dd. mm. уу ', что соответствует, например, дате '31.05.00'. По скольку на рубеже столетий цифры года в таком формате кажутся неверными, я советую в начале каждой программы вставлять оператор Short-DateFormat := 'dd.mm.yyyy'; это даст '31.05.2000'
LongDateFormat: String; | Для русифицированной версии содержит символы 'dd MMMM yyyy г.',что дает'31 Май 2000 г. '
TimeSeparator: Char; | Разделитель времени (' : ')
TimeAMString: String; | Символы 12-часового формата для времени до полудня (не используются в русифицированной версии Windows)
TimePMString: String; | Символы 12-часового формата для времени после полудня (не используются в русифицированной версии Windows)
ShortTimeFormat: String; | Краткий формат времени (обычно ' h: mm ')
LongTimeFormat: String; | Полный формат времени (обычно ' hh: mm: ss ')
ShortMonthNames: array [1..12] of String; | Краткие имена месяцев (янв, фев, мар, апр, май, июн, июл, авг, сен, окт, ноя, дек)
LongMonthNames: array [1..12] of String; | Полные имена месяцев (Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь)
ShortDayNames: array [1..7] of String; | Краткие имена дней недели (Вс, Пн, Вт, Ср, Чт, Пт, Сб)
LongDayNames: array [1..7] of String; | Полные имена дней недели (воскресенье, понедельник, вторник, среда, четверг, пятница, суббота)
type TSysLocale = packed record DefaultLCID: LCID; PriLangID: LANGID; SubLangID: LANGID; FarEast: Boolean; MiddleEast: Boolean; end; var SysLocale: TSysLocale; | Определяет локализацию Windows: DefaultLCID- идентификатор локализации (1049); PriLangID- идентификатор первичного языка (25); SubLangID - идентификатор вторичного языка (если в качестве второго языка используется английский (США), это поле имеет значение 1); Far-East - локализация для Дальнего Востока (False): MiddleEast - локализация для Среднего Востока(False)
EraNames: array [1..7] of String; | Используется в функции DateTimeToStr в Windows для Ближнего Востока
EraYearOffsets: array [1..7] of Integer; | Используется в функции DateTimeToStr в Windows для Ближнего Востока
TListSeparator: Char; | Разделитель списка строк ('; ')

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

procedure WeekToDates(aDate: TDateTime; var Week: Byte;var Year: Word; var FirstDate, LastDate: TDateTime);

{Возвращает для указанной даты aDate год Year, номер недели от начала года Week, а также даты начала и конца недели} var

S: String;

begin

//Получаем в S символы года для даты aDate:

DateTimeToString(S,'yyyy',aDate) ;

//Формируем дату 1 января:

FirstDate := StrTo-Date('01'+DateSeparator+'01'+DateSeparator+S) ;

Year := StrToInt(S); //Возвращаем год

//Формируем последнюю дату 1-й недели:

LastDate := FirstDate;

DateTimeToString(S,'dddd',LastDate) ;

while SOLongDayNames [1] do

// LongDayNames [1 ]= 'воскресенье '

begin

LastDate := LastDate+1;

DateTimeToString(S,'dddd',LastDate);

end;

Week := 1;

//Циклически наращиваем FirstDate, LastDate и Week, пока

// LastDate не станет больше или равна aDate:

while LastDate<aDate do

begin

inc(Week);

FirstDate := FirstDate+7;

LastDate := LastDate+7

end

end; // WeekToDates

Для форматного преобразования вещественных чисел предназначены функции FloatToStrF и FormatFloat. Первая использует значение Format перечисляемого типа TFloatFormat и два дополнительных параметра - Precision и Digits. Правила использования параду метров функции FloatToStrF показаны в следующей таблице' (примеры преобразования даются для value = П = 3,141593654, Precision = 5 и Digits = 2):

Таблица 7.10. Правила использования параметров функции FloatToStrF

Значение Format | Описание
ffExponent | Научная форма представления с множителем ехх (“умножить на 10 в степени XX”). precision задает общее количество десятичных цифр мантиссы, Digits - количество цифр в десятичном порядке хх. Число округляется с учетом первой отбрасываемой цифры:3,1416Е+00
ff Fixed | Формат с фиксированным положением разделителя целой и дробной частей, precision задает общее количество десятичных цифр в представлении числа. Digits - количество цифр в дробной части. Число округляется с учетом первой отбрасываемой цифры:3,14
ff General | Универсальный формат, использующий наиболее удобную для чтения форму представления вещественного числа. Соответствует формату ff Fixed, если количество цифр в целой части меньше или равно precision, а само число - больше или равно 0,00001, в противном случае соответствует формату ff Exponent: 3,1416
ffNumber | Отличается от ffFixed использованием символа-разделителя тысяч при выводе больших чисел (для русифицированной версии Windows таким разделителем является пробел). Для value = П *1000 получим 3 141,60
ffCurrency | Денежный формат. Соответствует ffNumber, но в конце строки ставится символ денежной единицы (для русифицированной версии Windows - символы “р.”). Для value = я*1000 получим 3 141,60р.

Параметр Format в функции FormatFloat может содержать такие спецификаторы:

Таблица 7.11. Спецификаторы форматирования вещественных чисел

;

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

0

Определяет поле для цифры. Если в данной позиции форматируемое число имеет значащую цифру, она выводится, если нет - выводится 0

#

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

.

Поле для разделителя целой и дробной частей числа

,

Поле для разделителя тысяч

E+,e+

E-,e-

Признак представления числа в научном формате. В этом случае число представляется мантиссой и десятичным порядком, между которыми стоит символ Е. Спецификаторы е+ и е+ предписывают выводить знак + перед неотрицательным десятичным порядком, при использовании Е- и е- знак + перед порядком не выводится

Как и в случае даты/времени, любые другие символы строки Format, а также заключенные в апострофы или кавычки специальные символы-спецификаторы помещаются в выходную строку без преобразования: для value = 71*1000 спецификаторы'#, рубля' дадут строку 3 142 рубля. С помощью символа “;” можно задавать различные формы представления положительного, отрицательного и нулевого числа. При этом набор спецификаторов без “;” относится ко всем числам; если используется один символ “;”, то спецификаторы слева от него применяются для форматирования положительных чисел, справа - отрицательных, а вывод нулей подавляется; при использовании двух символов “; ” первый набор - для положительных, второй - для отрицательных и третий - для нулевых чисел. Например, спецификаторы '#;нуль' для value = -1 выведут нуль, а для value = о ничего не выведут, в то время как спецификаторы '#;00;нуль' для value = -1 выведут 01, а для value = 0 - строку нуль. Спецификаторы '#;;нуль' для value = -1 выведут -1, т. к. в этом случае спецификатор для отрицательных чисел не задан и для их вывода будет использован спецификатор положительных чисел, в то время как спецификаторы '#; ;нуль' подавляют вывод отрицательных чисел.

Мощная функция преобразования Format (табл. 7.7) перешла в Object Pascal из языка Си. Она позволяет преобразовать сразу несколько элементов открытого массива аргументов в соответствии с указаниями форматирующей строки. Например, Format('Строка "%s" содержит %d символов',['Паскаль',7]) даст такой результат: Строка "Паскаль" содержит 7 символов.

Элементами массива аргументов могут быть константы и/или переменные целого и вещественного типа, строки и указатели.

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

Форматирующий спецификатор всегда начинается символом процента и в общем случае имеет такую структуру (в квадратных скобках указываются необязательные элементы):

"%" [index ":"] ["-"] [width] ["." prec] type

Здесь: index ":" - индекс открытого массива, с помощью этого элемента можно явно указать аргумент, который будет обрабатывать спецификатор; "-" указывает на необходимость прижать отформатированный спецификатором текст к левой границе отведенного для него пространства; width - число, определяющее количество символов для обработанного спецификатором текста; если это число меньше требуемого, этот элемент спецификатора игнорируется, если больше - дополняется справа (если есть элемент "-") или слева (если он отсутствует) нужным количеством пробелов; "." prec - задает точность представления целых и вещественных типов; type - символ, определяющий тип форматирования (см. таблицу ниже).

Таблица 7.12. Форматирующие спецификаторы для функции Format

d | Целое десятичное число. Если задан параметр ргес, то символьное представле ние должно содержать по меньшей мере ргес десятичных цифр: если символьное представление содержит меньше цифр, оно дополняется слева символами 0, если больше - параметр ргес игнорируется. Если аргумент не является целым числом, возникает исключительная ситуация
u | Беззнаковое целое число. Используется подобно типу d, но аргумент должен быть положительным числом. Если аргумент - отрицательное целое, результат форматирования непредсказуем
е | Вещественное число в экспоненциальном представлении: символьное представление имеет вид -d, dddddddddddE+ddd. Если число положительное, ведущий минус опускается, а если его модуль меньше 1, знак плюс меняется на минус. Всегда содержит одну цифру целой части, по меньшей мере одну цифру дробной части и не меньше трех цифр десятичного порядка. Если задан параметр точности, он определяет общее количество цифр до символа Е, но не меньше двух: если параметр точности содержит 1 или 0, он заменяется на 2. Если символьное представление содержит больше символов, чем ргес, оно округляется по первой отбрасываемой цифре. Умалчиваемое значение параметра точности 15
f | Вещественное число в виде -ddd, ddd (фиксированное представление). Параметр точности (по умолчанию 2) указывает количество цифр в дробной части. Если он 0, выводится только целая часть числа
g | Вещественное число в максимально коротком представлении (экспоненциальном или фиксированном). Параметр точности (по умолчанию 15) определяет максимальное количество значащих разрядов. Если число имеет меньшее количество цифр, оно не дополняется до ргес, а если число не имеет дробной части, оно выводится как целое (без запятой)
n | Соответствует фиксированному представлению, но использует символ-разделитель тысяч
m | Денежное представление вещественного числа. Подобно типу n, но справа ставится знак денежной единицы
p | Указатель. Выводит содержимое указателя в виде 8 шестнадцатеричных цифр
s | Аргумент должен быть символом, строкой или строкой с терминальным нулем. Параметр точности, если указан, определяет максимальную длину строки: если строка больше, она усекается, если меньше - параметр точности игнорируется
x | Шестнадцатеричное представление целого числа. Параметр точности определяет минимальное количество шестнадцатеричных цифр (если число меньше, оно дополняется ведущими нулями)

Функция не чувствительна к высоте букв, указывающих тип преобразования. Параметры index, ргес и width задаются явно (числами в форматирующей строке) или неявно - с помощью символа “*”. В этом случае в качестве параметра берется значение очередного аргумента в списке аргументов (он должен быть целым числом). Например, два следующих обращения дадут одинаковый результат:

Format ('%*.*f, [8, 2, 123.456]);

Format ('%8.2f, [123.456]);

7.3.2. Нуль-терминальные строки

Нуль-терминальные строки широко используются при обращениях к так называемым API-функциям Windows (API - Application Program Interface - интерфейс прикладных программ). Поскольку компоненты Delphi берут на себя все проблемы связи с API-функциями Windows, программисту редко приходится прибегать к нуль-терминальным строкам. Тем не менее в этом разделе описываются особенности обработки таких строк.

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

procedure TfmExample.FormActivate(Sender: TObject);

var

pcS: PChar;

ssS: String;

begin

pcS := '123456';

ssS := 'X = ';

IbOutput.Caption := ssS + pcS;

end;

В строке IbOutput будет выведено х = 123456. Другой пример. В состав API-функцией входят функция MessageBox, с помощью которой на экране создается диалоговое окно с заголовком, текстовым сообщением и набором кнопок. Если в конце предыдущего примера добавить оператор

MessageBox(0, ssS + pcS, 'Заголовок окна', mb_0k);

то компилятор укажет на ошибку, т. к. вторым параметром обращения к функции должно быть выражение типа PChar, в то время как выражение sss+pcs приводится компилятором к общему типу String. Правильным будет такое обращение:

MessageBox(0, PChar (ssS + pcS), 'Заголовок окна', mb_0k) ;

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

В Delphi считается совместимым с pchar и string массив символов с нулевой нижней границей. В отличие от pchar и String такой массив распределяется статически (на этапе компиляции), поэтому наполнение массива символами и завершающим нулем осуществляется специальной процедурой Strcopy:

procedure TfmExample.bbRunClick(Sender:TObject);

var

acS: array [0..6] of Char;

begin

StrCopy(acS, '123456');

IbOutput.Caption := acS;

end;

Для работы с типом pchar используются такие же операции, как и с типом String: операция конкатенации “+” и операции сравнения >, >=, <, <=, =, <>.

Таблица 7.13. Подпрограммы для работы с нуль-терминальными строками

Function CharToOem (Str, OemStr: PChar): Bool; | Преобразует символы строки Str из кодировки ANSI в кодировку MS-DOS и помещает результат в OemStr.Всегда возвращает True
Function CharToOemBuff(Str, OemStr: PChar; MaxLen: Lorigint): Bool; | Преобразует не более MaxLen символов строки Str из кодировки ANSI в кодировку MS-DOS и помещает результат в OemStr. Всегда возвращает True
Function OemToChar (OEMStr, Str: PChar): Bool; | Преобразует символы из кодировки MS-DOS в кодировку ANSI и возвращает True
Function OemToCharBuff(OEMStr, Str: PChar;MaxLen: Longint): Bool; | Преобразует не более MaxLen символов строки OemStr из кодировки MS-DOS в кодировку ANSI и помещает результат в Str. Всегда возвращает True
Function StrCat(Dest,Source: PChar): PChar; | Копирует строку Source в конец строки Dest и возвращает указатель на строку Dest
Function StrComp (Strl,Str2: PChar): Integers; | Побайтно сравнивает строку Strl со строкой Str2 и возвращает следующий результат: =0 для Strl=Str2; >0 для Strl>Str2,- 0 для Strl<Str2
Function StrCopy(Dest,Source: PChar): PChar; | Копирует строку Source в строку Dest и возвращает указатель на Dest. StrCopy не проверяет реальный размер памяти, связанный с Dest (он должен быть не меньше StrLen(Source)+1)
Procedure StrDispose(Str: PChar) ; | Удаляет строку Str из памяти. Строка должна быть предварительно помещена в память функцией StrNew. Если Str=NlL, процедура ничего не делает
Function StrECopy(Dest, Source: PChar): PChar; | Объединяет строки. Эта функция работает в точности, как StrCat, но возвращает указатель на конец сцепленных строк, т. е. на терминальный ноль
Function StrEnd(Str: PChar): PChar; | Функция возвращает указатель на терминальный нольстроки Str
Function StrIComp(Strl,Str2: PChar): PChar; | Функция сравнивает строки, игнорируя возможную разницу в высоте букв. Возвращает такой же результат, как и StrComp. Замечу, что функция правильно работает лишь с латиницей. Для кириллицы ее нужно модифици ровать (см.ниже)
Function StrLCat(Dest,Source: PChar; MaxLen:Word): PChar; | Копирует символы строки Source в конец строки Dest до тех пор, пока не будут скопированы все символы или пока длина сцепленной строки Dest не достигнет MaxLen. Возвращает указатель на сцепленную строку. В отличие от StrCopy эта функция блокирует возможноепереполнение области памяти, связанной с Dest. Обычно в качестве MaxLen используется выражение SizeOf(Dest)-!
Function StrLComp(Dest, Source: PChar; MaxLen: Word): PChar; | В отличие от StrComp сравнивает не более MaxLen символов строк. Возвращаемый результат такой же, как и у StrComp
Function StrLCopy(Dest, Source: PChar; MaxLen: Word): PChar; | Копирует символы из строки Source в строку Dest до тех пор, пока не будет скопирована вся строка или пока не будет скопировано MaxLen символов. В отличие от StrCopy блокирует возможное переполнение области памяти, связанной с Dest. В качестве MaxLen обычно используется выражение SizeOf(Dest)-1
Function StrLen(Str:PChar): Cardinal; | Возвращает длину строки
Function StrLIComp(Strl, Str2: PChar; MaxLen: Word): PChar; | Сравнивает не более MaxLen символов строк, проверяя точное соответствие высоты букв. Возвращаемый результат см. StrComp. Функция правильно работает только с латиницей
Function StrLower(Str: PChar): PChar; | Преобразует заглавные буквы строки Str к строчным и возвращает указатель на результат. Функция правильно работает только с латиницей
Function S t rMove(Dest, Source: PChar; Count:Word): PChar; | Копирует точно Count символов строки Source в строку Dest и возвращает указатель на результат. Функция игнорирует действительные размеры строк и может выйти за их пределы
Function StrNew(Str:PChar): PChar; | Помещает строку в память
Function StrPas(Str:PChar): String; | Преобразует нуль-терминальную строку в строку String
Function StrPCopytStr: PChar; S: String):PChar; | Преобразует строку String в нуль-терминальную строку. Возвращает указатель на Str
Function StrPos(Strl, Str2: PChar): PChar; | Ищет подстроку Str2 в строке Strl и возвращает указатель на первое вхождение Str2 или MIL, если подстрока не найдена
Function StrRScan(Str: PChar; Ch: Char):PChar; | Ищет символ Ch в строке Str и возвращает указатель напоследний обнаруженный символ Ch или NIL, если символ не найден
Function StrScan(Str:PChar; Ch: Char):PChar; | Ищет символ Ch в строке Str и возвращает указатель на первый обнаруженный символ Ch или MIL, если символ не найден
Function StrUpper (Str: PChar) : PChar | Преобразует строчные буквы строки Str к заглавным и возвращает указатель на результат. Функция правильно работает только с латиницей

Функции преобразования из ANSI-кодировки в кодировку MS-DOS (charToxxx) и обратно (OеmTоххх) правильно работают с кириллицей, если в MS-DOS используется национальная страница 866 (так называемая альтернативная кодировка). А вот четыре функции, использующие преобразование высоты букв (strLower, StrUpper, Stricomp и StrLIComp), работают корректно только для букв латинского алфавита (латиницы). Для русских букв вместо обращения к этим функциям следует использовать стандартные функции AnsiLowerCase И AnsiUpperCase, которые используют как параметры String, так и PChar, но возвращают результат типа string:

var

acS: array [Byte] of Char;

begin

StrCopy(acS, 'заглавные буквы');

Caption := AnsiUpperCase(acS) end;

Аналогично для функции Stricomp:

var

apSl,apS2: array [0..1000] of Char;

begin

StrCopy(apSl,'эталон');

StrCopy(apS2,'ЭТАЛОН') ;

Caption := IntToStr(StrIComp(PChar(AnsiUpperCase(apSl)),

PChar(AnsiUpperCase(apS2))))

end;

7.4. УКАЗАТЕЛИ И ДИНАМИЧЕСКАЯ ПАМЯТЬ

7.4.1. Динамическая память

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

7.4.2. Указатели

Оперативная память ПК представляет собой совокупность ячеек для хранения информации - байтов, каждый из которых имеет собственный номер. Эти номера называются адресами, они позволяют обращаться, к любому байту памяти. Object Pascal предоставляет в распоряжение программиста гибкое средство управления динамической памятью - так называемые указатели. Указатель - это переменная, которая в качестве своего значения содержит адрес байта памяти. С помощью указателей можно размещать в динамической памяти любой из известных в Object Pascal типов данных. Лишь некоторые из них (Byte, Char, ShortInt, Boolean) занимают во внутреннем представлении один байт, остальные - несколько смежных. Поэтому на самом деле указатель адресует лишь первый байт данных.

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

var

p1 : ^Integer;

р2 : ^Real;

type

PerconPointer = "PerconRecord;

PerconRecord = record Name : String;

Job : String;

Next : PerconPointer ,

end;

Обратите внимание: при объявлении типа PerconPointer мы сослались на тип PerconRecord, который предварительно в программе объявлен не был. Как уже отмечалось, в Object Pascal последовательно проводится в жизнь принцип, в соответствии с которым перед использованием какого-либо идентификатора он должен быть описан. Исключение сделано только для указателей, которые могут ссылаться на еще не объявленный тип данных.

В Object Pascal можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. Для этого служит стандартный тип pointer, например:

var

р: Pointer;

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

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

Если, например,

var

pI1,pI2: ^integer;

pR: ^Real;

p: Pointer;

то присваивание

pI1 := pI2;

вполне допустимо, в то время как

pl1 :=pR;

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

p := pR;

pI1 := p;

и тем самым достичь нужного результата.

7.4.3. Выделение и освобождение динамической памяти

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

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

var pI,pJ: ^Integer;

pR: ^Real;

begin

New (pI) ;

New (pR) ;

end;

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

pJ^ := 2; // В область памяти pJ помещено значение 2

pl^ := 2*pi; // В область памяти pR помещено значение 6.28

Таким образом, значение, на которое указывает указатель, т. е. собственно данные, размещенные в куче, обозначаются значком ^, который ставится сразу за указателем. Если за указателем нет значка ^, то имеется в виду адрес, по которому размещены данные. Имеет смысл еще раз задуматься над только что сказанным: значением любого указателя является адрес, а чтобы указать, что речь идет не об адресе, а о тех данных, которые размещены по этому адресу, за указателем ставится ^ (иногда об этом говорят как о разыменовании указателя).

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

рR^ := Sqr(pR") + I^ - 17;

Разумеется, совершенно недопустим оператор

pR := Sqr(pR") + I^ - 17;

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

pR^ := Sqr(pR) ;

поскольку значением указателя pR является адрес и его (в отличие от того значения, которое размещено по этому адресу) нельзя возводить в квадрат. Ошибочным будет и такое присваивание:

pR^' := pJ;

так как вещественным данным, на которые указывает pR, нельзя присвоить значение указателя (адрес).

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

Dispose(pJ);

Dispose(pR);

вернут в кучу память, которая ранее была закреплена за указателями pJ и pR (см. выше).

Замечу, что процедура Dispose (pPtr) не изменяет значения указателя pPtr, а лишь возвращает в кучу память, ранее связанную с этим указателем. Однако повторное применение процедуры к свободному указателю приведет к возникновению ошибки периода исполнения. Освободившийся указатель программист может пометить зарезервированным словом nil. Помечен ли какой-либо указатель или нет, можно проверить следующим образом:

const

pR: ^Real = NIL;

begin

if pR = NIL then

New (pR) ;

Dispose(pR) ;

pR := NIL;

end;

Никакие другие операции сравнения над указателями не разрешены.

Приведенный выше фрагмент иллюстрирует предпочтительный способ объявления указателя в виде типизированной константы с одновременным присвоением ему значения nil. Следует учесть, что начальное значение указателя (при его объявлении в разделе переменных) может быть произвольным. Использование указателей, которым не присвоено значение процедурой New или другим способом, не контролируется Delphi и вызовет исключение.

Как уже отмечалось, параметром процедуры New может быть только типизированный указатель. Для работы с нетипизированными указателями используются Процедуры GetMem И FreeMem:

GetMem(P, Size); // резервирование памяти;

FreeMem(P, Size); // освобождение памяти.

Здесь р - нетипизированный указатель; size - размер в байтах требуемой или освобождаемой части кучи.

Испoльзoвaние прцeдyp GetMem/FreeMemMem, как и вообще вся работа диамияесжой памятью, требует особой осторожности и тщателвного солюдения простого правила: освобождать нужно ровно столько пайти, сколько её было зарезервировано, и именно с того адреса, с которого она была зарезёрвирована.

7.4.4. Процедуры и функции для работы с динамической памятью

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

Таблица 7.14. Средства Object Pascal для работы с памятью

Function Addr(X): Pointer; | Возвращает адрес аргумента X. Аналогичный результат возвращает операция @
Procedure Dispose (var P: Pointer) ; | Возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за типизированным указателем P
Procedure Free-Mem(var P: Pointer; Size: Integer) ; | Возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за нетипизированным указателем Р
Procedure Get-Mem(var P: Pointer; Size: Integer) ; | Резервирует за нетипизированным указателем Р фрагментдинамической памяти требуемого размера Size
Procedure New(var P: Pointer) ; | Резервирует фрагмент кучи для размещения переменной и помещает в типизированный указатель Р адрес первого байта
Function SizeOf(X): Integer; | Возвращает длину в байтах внутреннего представления указанного объекта Х

Windows имеет собственные средства работы с памятью. В табл. 7.15 перечислены соответствующие API-функции и даны краткие пояснения. За более полной информацией обращайтесь к справочной службе в файлах WIN32. hlp или WIN32S. hlp.

Таблица 7.15. Средства Windows для работы с памятью

CopyMemory | Копирует содержимое одного блока памяти в другой блок. Блоки не должны перекрываться хотя бы частично
FillMemory | Заполняет блок памяти указанным значением
GetProcessHeap | Возвращает дескриптор кучи для текущей программы
GetProcessHeaps | Возвращает дескрипторы куч для всех работающих программ
GlobalAlloc | Резервирует в куче блок памяти требуемого размера
GlobalDiscard | Выгружает блок памяти
GlobalFlags | Возвращает информацию об указанном блоке памяти
GlobalFree | Освобождает блок памяти и возвращает его в общий пул памяти
GlobalHandle | Возвращает дескриптор блока памяти, связанного с заданным указателем
GlobalLock | Фиксирует блок памяти и возвращает указатель на его первый байт
GlobalMemoryStatus | Возвращает информацию о доступной памяти (как физической, так и виртуальной)
GlobalReAlloc | Изменяет размер и атрибуты ранее зарезервированного блока памяти
GlobalSize | Возвращает размер в байтах блока памяти
GlobalUnlock | Снимает фиксацию блока памяти и делает его перемещаемым
HeapAlloc | Резервирует в куче неперемещаемый блок памяти
HeapCompact | Удаляет фрагментацию кучи
HeapCreate | Создает для программы новую кучу
HeapDestroy | Возвращает кучу в общий пул памяти
HeapFree | Освобождает блок памяти, зарезервированный функциями HeapAlloc или HeapReAlloc
HeapLock | Делает указанную кучу доступной только для текущего потока
HeapReAlloc | Изменяет размер и/или свойства кучи
HeapSize | Возвращает размер кучи в байтах
HeapUnlock | Делает указанную кучу доступной для любых потоков текущего процесса
HeapValidate | Проверяет состояние кучи или размещенного в ней блока памяти
IsBadCodePtr | Сообщает, может ли вызывающая программа читать данные из указанного адреса памяти (но не из блока памяти)
IsBadHugeReadPtr | Сообщает, может ли вызывающая программа читать данные из указанного блока памяти
IsBadHugeWritePtr | Сообщает, может ли вызывающая программа изменять содержимое указанного блока памяти
IsBadReadPtr | Сообщает, может ли вызывающая программа читать данные из указанного блока памяти
IsBadStringPtr | Сообщает, может ли программа читать содержимое строки, распределенной в куче
IsBadWritePtr | Сообщает, может ли вызывающая программа изменять содержимое указанного блока памяти
LocalAlloc | Аналогична GlobalAlloc
:: LocalDiscard | Аналогична GloalDiscard
'LocalFlags | Аналогична GlobalFlags
LocalFree | Аналогична Global Free
LocalHandle | Аналогична GlobalHandle
LocalLock | Аналогична GlobalLock
LocalReAlloc | Аналогична GlobalReAlloc
LocalSize | Аналогична GlobalSize
LocalUnlock | Аналогична GlobalUnlock
MoveMemory | Копирует один блок памяти в другой. Блоки могут перекрываться
VirtualAlloc | Резервирует блок виртуальной памяти
VirtualFree | Освобождает блок виртуальной памяти
VirtualLock | Фиксирует блок виртуальной памяти
VirtualProtect | Изменяет права доступа текущей программы к виртуальному блоку памяти
VirtualProtectEx | Изменяет права доступа указанной программы к виртуальному блоку памяти
VirtualQuery | Возвращает свойства виртуального блока памяти по отношению к вызывающей программе
VirtualQueryEx | Возвращает свойства виртуального блока памяти по отношению к указанной программе
VirtualUnloc'k | Снимает фиксацию блока виртуальной памяти
ZeroMemory | Заполняет блок памяти нулями

7.5. ПСЕВДОНИМЫ ТИПОВ

Для любого типа можно объявить сколько угодно псевдонимов. Например:

type

TMyInteger = Integer;

В дальнейшем псевдоним можно использовать так же, как и базовый тип:

var

Mylnt: TMyInteger;

begin

Mylnt := 2*Round(pi);

end;

Такого рода псевдонимы обычно используются для повышения наглядности кода программы. Однако в Object Pascal можно объявлять строго типизированные псевдонимы добавлением зарезервированного слова type перед именем базового типа:

type

TMyIntegerType = type Integer;

var

MylntVar: TMyIntegerType;

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

function MylntFunc(APar: integer): Integer;

begin

end;

то такое обращение к ней

MylntFunc(MylntVar)

будет расценено компилятором как ошибочное.

Строго типизированные псевдонимы заставляют компилятор вырабатывать информацию о типе для этапа прогона программы (RTTI - Run-Time Type Information). Эта информация обычно используется средой Delphi для обеспечения функционирования разного рода редакторов свойств и программ-экспертов.


Глава 8. ПРОЦЕДУРЫ И ФУНКЦИИ

  • Глава 8. ПРОЦЕДУРЫ И ФУНКЦИИ
    • ЛОКАЛИЗАЦИЯ ИМЕН
    • ОПИСАНИЕ ПОДПРОГРАММЫ
      • Заголовок и стандартные директивы
      • Параметры
      • Умалчиваемые параметры
    • ПАРАМЕТРЫ-МАССИВЫ И ПАРАМЕТРЫ-СТРОКИ
      • Открытые массивы
      • Конструктор массива
      • Вариантные массивы-параметры
    • ПРОЦЕДУРНЫЕ ТИПЫ
    • РЕКУРСИЯ И ОПЕРЕЖАЮЩЕЕ ОПИСАНИЕ

Глава 8

ПРОЦЕДУРЫ И ФУНКЦИИ

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

8.1. ЛОКАЛИЗАЦИЯ ИМЕН

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

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

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

Рис. 8.1. Пример структуры программы

Procedure A; Procedure Al;

begin

end {A1};

Procedure A2;

begin

end {A2};

begin {A}

end {A};

Procedure B;

Procedure Bl;

begin

end {Bl};

, Procedure B2;

Procedure B21;

И Т. Д.

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

А2, В1

И Т. Д.

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

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

Пусть имеем такое описание:

var V1 : ... ;

Procedure A;

var V2 : ...;

end {A};

Procedure В;

var V3 : . . . ;

Procedure B1;

var V4 : . . . ;

Procedure В 11;

var V5;

Из процедуры B11 доступны все пять переменных v1,...,v5, из процедуры в1 доступны переменные v1,...,v4, из центральной программы-только v1.

При взаимодействии подпрограмм одного уровня иерархии вступает в силу основное правило Object Pascal: любая подпрограмма перед ее использованием должна быть описана. Поэтому из подпрограммы в можно вызвать подпрограмму а, но из а вызвать в невозможно (точнее, такая возможность появляется только с использованием опережающего описания, см. п. 8.5.) Продолжая образное сравнение, подпрограмму можно уподобить ящику с непрозрачными стенками и дном и полупрозрачной крышей: из подпрограммы можно смотреть только “вверх” и нельзя “вниз”, т. е. подпрограмме доступны только те объекты верхнего уровня, которые описаны до описания данной подпрограммы. Эти объекты называются глобальными по отношению к подпрограмме.

В Object Pascal допускается произвольная последовательность описания констант, переменных, типов, меток и подпрограмм. Например, раздел var описания переменных может появляться в пределах раздела описаний одной и той же подпрограммы много раз и перемежаться с объявлениями других объектов и подпрограмм. Для Object Pascal совершенно безразличен порядок следования и количество разделов var, type, const и label, но при определении области действия этих описаний следует помнить, что имена, описанные ниже по тексту программы, недоступны из ранее описанных подпрограмм, например:

var V1 : ; . . ;

Procedure S;

var V2 : . . . ;

end {S};

var V3 : . . . ;

Из процедуры s можно обратиться к переменным v1 и v2, но нельзя использовать V3, так как описание этой переменной следует в программе за описанием процедуры s.

Локализованные в подпрограмме имена могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя “закрывает” глобальное и делает его недоступным, например:

var

i : Integer;

Procedure P;

var

i : Integer;

begin

IbOutput.Caption := IntToStr(i);

end {P};

begin

i := 1;

P

end;

Что выведет эта программа на экран? Все что угодно: значение внутренней переменной i при входе в процедуру p не определено, хотя одноименная глобальная переменная имеет значение 1. Локальная переменная “закроет” глобальную, и на экран будет выведено произвольное значение, содержащееся в неинициированной внутренней переменной.

Если убрать описание

var

i : integer;

из процедуры p, то на экран будет выведено значение глобальной переменной i,t. е. 1.

Таким образом, одноименные глобальные и локальные переменные - это разные переменные. Любое обращение к таким переменным в теле подпрограммы трактуется как обращение к локальным переменным, т. е. глобальные переменные в этом случае попросту недоступны.

8.2. ОПИСАНИЕ ПОДПРОГРАММЫ

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

8.2.1. Заголовок и стандартные директивы

Заголовок процедуры имеет вид:

PROCEDURE <имя> [(<сп.ф.п.>)] ;

Заголовок функции:

FUNCTION <имя> [(<сп.ф.п.>)] : <тип>;

Здесь <имя> - имя подпрограммы (правильный идентификатор);

<сп.ф.п.> - список формальных параметров; <тип> - тип возвращаемого функцией результата.

Сразу за заголовком подпрограммы может следовать одна из стандартных директив assembler, external, far, forward, inline, interrupt, near. Эти директивы уточняют действия компилятора и распространяются на всю подпрограмму и только на нее, т. е., если за подпрограммой следует другая подпрограмма, стандартная директива, указанная за заголовком первой, не распространяется на вторую.

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

external - с помощью этой директивы объявляется внешняя подпрограмма.

far - компилятор должен создавать код подпрограммы, рассчитанный на дальнюю модель вызова. Директива near заставит компилятор создать код, рассчитанный на ближнюю модель памяти. Введены для совместимости с Delphi 1, которая использовала сегментную модель памяти.

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

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

interrupt - используется при создании процедур обработки прерываний.

Помимо описанных в Object Pascal можно использовать также стандартные директивы, регламентирующие способ передачи параметров через стек и использование регистров для их передачи - такие директивы используются при работе с ядром Windows. Они перечислены в приводимой ниже таблице. Графа порядок определяет порядок размещения параметров в стеке: слева направо означает размещение в стеке по порядку описания - сначала первый параметр, затем второй и т. д.; справа налево означает размещение с конца перечисления параметров - сначала последний, затем предпоследний и т. д. Графа Очистка определяет, кто будет очищать стек: подпрограмма перед передачей управления в вызывающую программу или программа после получения управления. Графа регистры содержит да, если для передачи параметров помимо стека используются также регистры центрального процессора.

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

Директива | Порядок | Очистка | Регистры
safecall | Справа налево | Подпрограмма | Нет
Stdcall | Справа налево | Подпрограмма | Нет
Cdecl | Справа налево | Программа | Нет
Pascal | Слева направо | Подпрограмма | Нет
Register | Слева направо | Подпрограмма | Да

Все фуйкции API ядра Wmdows;скомпилированы .в режиме stidcall, а те, что доддерживают технологию СОМ - в режиме sаfеса11.

8.2.2. Параметры

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

Procedure SB(a: Real; b: Integer; с: Char);

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

Function F(a: Real; b: Real): Real;

можно написать проще:

Function F(a,b: Real): Real;

Операторы тела подпрограммы рассматривают список формальных параметров как своеобразное расширение раздела описаний:

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

Рассмотрим такой полезный пример. В Object Pascal не предусмотрена операция возведения вещественного числа в произвольную степень[Начиная с версии 2 с Delphi поставляется модуль Match, в котором есть соответствующая функция.]. Тем не менее эту задачу можно решить с использованием стандартных математических функций Ехр и Ln по следующему алгоритму:

XY = e(Y*Ln(X))

Создадим функцию с именем power и двумя вещественными параметрами а и в, которая будет возвращать результат возведения а в степень в. Обработчик события bbRunСlick нашей учебной формы fmExampie читает из компонента edInput текст и пытается выделить из него два числа, разделенных хотя бы одним пробелом. Если это удалось сделать, он обращается к функции power дважды: сначала возводит первое число х в степень второго числа y, затем х возводится в степень -y.

procedure TfmExample.bbRunClick(Sender: TObject);

Function Power(A, B: Real): Real;

{Функция возводит число А в степень В. Поскольку логарифм отрицательного числа не существует, реализуется проверка значения А: отрицательное значение заменяется на положительное, для нулевого числа результат равен нулю. Кроме того, любое число в нулевой степени дает единицу.} begin

if А > 0 then

Result := Ехр(В * Ln(A)) else if A < 0 then

Result := Ехр(В * Ln(Abs(A))) else if В = 0 then

Result := 1 else

Result := 0;

end; // Power var

S: String;

X, Y: Real; begin

{Читаем строку из edinput и выделяем из нее два вещественных числа, разделенных хотя бы одним пробелом.} S := edinput.Text;

if (S = '') or (pos(' ' ,S) = 0) then

Exit; // Лет текста или в нем нет

// пробела - прекращаем дальнейшую работу try

// Выделяем первое число:

X := StrToFloat(copy(S, I, pos(' ', S) - 1));

// Если успешно, удаляем символы до пробела // и выделяем второе число:

Delete (S, 1, pos (' ', S) ) ;

Y := StrToFloat(Trim(S)) ;

except

Exit; // Завершаем работу при ошибке преобразования end;

mmOutput.Lines.Add(FloatToStr(Power(X, Y) ) ) ;

mmOutput.Lines.Add(FloatToStr(Power(X, -Y) ) ) ;

end;

Для вызова функции Power мы просто указали ее в качестве параметра при обращении к стандартной функции преобразования вещественного числа в строку FloatToStr. Параметры х и y в момент обращения к функции power - это фактические параметры. Они подставляются вместо формальных параметров а и в в заголовке функции, и затем над ними осуществляются нужные действия. Полученный результат присваивается специальной переменной с именем Re-suit, которая в теле любой функции интерпретируется как то значение, которое вернет функция после окончания своей работы. В программе функция power вызывается дважды - сначала с параметрами х и y, а затем х и -y, поэтому будут получены два разных результата.

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

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

В предыдущем примере параметры а и в определены как параметры-значения. Если параметры определяются как параметры-переменные, перед ними необходимо ставить зарезервированное слово var, а если это параметры-константы - слово const, например:

Procedure MyProcedure(var A: Real; В: Real; const C: String);

Здесь а - параметр-переменная, в - параметр-значение, а с - параметр-константа .

Определение формального параметра тем или иным способом существенно в основном только для вызывающей программы: если формальный параметр объявлен как параметр-переменная, то при вызове подпрограммы ему должен соответствовать фактический параметр в виде переменной нужного типа; если формальный параметр объявлен как параметр-значение или параметр-константа, то при вызове ему может соответствовать произвольное выражение. Контроль за неукоснительным соблюдением этого правила осуществляется компилятором Object Pascal. Если бы для предыдущего примера был использован такой заголовок функции:

Function Power (A: Real; var В : Real): Real;

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

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

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

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

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

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

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

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

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

Procedure MyProc(var aParametr);

Нетипизированные параметры обычно используются в случае, когда тип данных несущественен. Такие ситуации чаще всего возникают при разного рода копированиях одной области памяти в другую, например, С помощью процедур BlockRead, BlockWrite, Move-Memory И Т. П.

8.2.3. Умалчиваемые параметры

В Delphi 4, 5 и 6 можно использовать так называемые умалчиваемые параметры, т. е. параметры, которые могут опускаться при обращении к подпрограмме. Умалчиваемые параметры замыкают список формальных параметров и имеют вид

<имя>:<тип> = <значение>

Например,

Procedure P(a: array of Integer; S: String = '');

В этом случае два следующих обращения идентичны:

Р([1,2,3], ' ');

Р([1,2,3]);

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

Procedure P(a: array of Integer; S: String = '';

В: Integer = 0) ;

Допустимые обращения:

Р([1,2,3]);

Р([1,2,3], 'Строка');

Р(1,2,3],",1)

8.3. ПАРАМЕТРЫ-МАССИВЫ И ПАРАМЕТРЫ-СТРОКИ

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

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

Procedure S (a: array [1..10] of real);

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

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

type

аТуре = array [1..10] of Real;

Procedure S(var a: аТуре);

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

type

InputType = String [15];

OutputType = String [30];

Function St(S: InputType): OutputType;

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

8.3.1. Открытые массивы

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

Открытый массив представляет собой формальный параметр подпрограммы, описывающий базовый тип элементов массива, но не определяющий его размерности и границы:

Procedure MyProc(OpenArray: array of Integer);

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

Procedure TfmExample.bbRunClick(Sender: TObject) ;

{Иллюстрация использования открытых массивов: программа выводит в компонент mmOutput содержимое двух одномерных массивов разной длины с помощью одной процедуры ArrayPrint) Procedure ArrayPrint(aArray: array of Integer);

var

k: Integer;

S: String;

begin

S:=' ';

for k := 0 to High(aArray) do S := S + IntToStr(aArray[k]);

mmOutput.Lines.Add(S) ;

end;

const

A: array [-1..2] of Integer = (0,1,2,3);

B: array [5..7] of Integer = (4,5,6);

begin

ArrayPrint(A);

ArrayPrint (B);

end;

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

var

С: array,[1..3,1..5] of Integer;

то обращение

ArrayPrint(С)

вызвало бы сообщение об ошибке.

8.3.2. Конструктор массива

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

const

A: array [-1..2] of Integer = (0,1,2,3);

В: array [5..7] of Integer = (4,5,6);

begin

ArrayPrint(A);

ArrayPrint(B);

end;

мы могли бы написать так:

begin

ArrayPrint ( [0,1,2,3]);

ArrayPrint([4,5,6]);

end;

8.3.3. Вариантные массивы-параметры

В Delphi 32 при передаче подпрограмме массивов переменной длины и размерности удобно использовать вариантные массивы (см. п. 7.4.3). В следующем примере с помощью функции GetAr-rayAverage определяется среднее арифметическое значение всех элементов вариантного массива произвольной длины и размерности не выше 5:

function GetArrayAverage(const V: Variant): Double;

{Возвращает среднее арифметическое значение массива произвольной длины и размерности или очень маленькую отрицательную величину, если V - не вариант или если его размерность больше 5} var

i,j,k,l,m: Integer;

Sum: Double;

NItem: Integer;

begin

Result := -1E-309;

if ((VarType(V) and VarArray) <> VarArray) or

(VarArrayDimCount(V) > 5) then Exit;

Sum := 0;

NItem := 0;

// Подсчитываем количество элементов массива

for k := 1 to VarArrayDimCount(V) do

NItem := NItem+VarArrayH'ighBound(V, k)-VarArrayLowBound(V,k) ;

// Подсчитываем сумму элементов case VarArrayDimCount(V) of

1: for i "VarArrayLowBound(V,1) to VarArrayHighBound(V,1)do

Sum := Sum+V[i] ;

2: for i =VarArrayLowBound(V,1) to VarArrayHighBound(V,1) do

for j :=VarArrayLowBound(V,2) to VarArrayHighBound(V,2) do

Sum := Sum+V[i,j] ;

3: for i: =VarArrayLowBound(V,1) to VarArrayHighBound(V,1)do

for j: =VarArrayLowBound(V,2) to VarArrayHighBound(V,2) do

for k: =VarArrayLowBound(V,3) to VarArrayHighBound(V,3) do

Sum := Sum+V[i,j,k] ;

4: for i: =VarArrayLowBound(V,1) to VarArrayHighBound(V,1)do

for j: =VarArrayLowBound(V,2) to VarArrayHighBound(V,2) do

for k :=VarArrayLowBound(V,3) to VarArrayHighBound(V,3) do

for l: =VarArrayLowBound(V,4) to VarArrayHighBound(V,4) do

Sum := Sum+V[i,j,k,1];

5: for i:=VarArrayLowBound(V,1) to VarArrayHighBound(V,1) do

for j :=VarArrayLowBound(V,2) to VarArrayHighBound(V,2) do

for k: =VarArrayLowBound(V,3) to VarArrayHighBound(V,3) do

for 1 :=VarArrayLowBound(V,4) to VarArrayHighBound(V,4) do

for m:= VarArrayLowBound(V,5) to VarArrayHighBound(V,5) do

Sum := Sum+V[i,j,k,1,m];

end;

Result := Sum/NItem

end;

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

8.4. ПРОЦЕДУРНЫЕ ТИПЫ

Основное назначение процедурных типов - дать программисту гибкие средства передачи функций и процедур в качестве фактических параметров обращения к другим процедурам и функциям.

Для объявления процедурного типа используется заголовок процедуры (функции), в котором опускается ее имя, например:

type

Proc1 = Procedure (a, b, с: Real; var d: Real); Proc2 = Procedure (var a, b);

РгосЗ = Procedure;

Func1 = Function: String;

Func2 = Function (var s: String): Real;

Как видно из приведенных примеров, существует два процедурных типа: тип-процедура и тип-функция.

В следующий программе иллюстрируется механизм передачи процедур в качестве фактических параметров вызова. Программа выводит на экран таблицу двух функций: sin1 (х) = (sin(x) + 1) * Ехр(-х) и cosi(x) = (Cos(x) + 1) * Ехр(-х) . Вычисление и печать значений этих функций реализуются в процедуре printFunc, которой в качестве параметров передается количество np вычислений функции в диапазоне х от 0 до 2*3.141592 и имя нужной функции.

Function Sinl(X: Real): Real;

begin

Result := (Sin(X) + 1) * Exp(-X) end; // Sin 1

Function Cosl(X: Real): Real;

begin

Result := (Cos(X) + 1) * Exp(-X) end; // Cosi

procedure TfmExample.bbRunClick(Sender: TObject);

type

Func = function (X: Real): Real; // Процедурный тип Procedure PrintFunc(NP: Integer; F; Func) ;

var

k: Integer;

X: Real;

begin

for k := 0 to NP do

begin

X:=k*2*pi/ NP;

mmOutput.Lines.Add(FloatToStrF(X, ffExponent, 10, 2) + #9#9 + FloatToStrF(F(X), ffExponent, 10, 2)) ;

end;

end; // PrintFunc

begin // bbRunClick

nmiOutput.Lines.Add(#9'Функция SINI:');

PrintFunc (10, Sini);

mmOutput.Lines.Add(#9'Функция COSI:');

PrintFunc (10, Cosi);

end;

Обратите внимание: передаваемые подпрограммы не могут быть локальными, т. е. процедурами или функциями, объявленными внутри другой подпрограммы. Вот почему описание подпрогра^.' sini и cosi размещаются вне обработчика bbRunciick, но выше не." по тексту модуля. Замечу, что символ #9 - это символ табуляции. который вставляется в формируемые строки для разделения колонок с цифрами.

В программе могут быть объявлены переменные процедурных типов, например,так:

var

p1 : Proc1;

fl, f2 : Func2;

ар : array [1..N] of Proc1;

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

8.5. РЕКУРСИЯ И ОПЕРЕЖАЮЩЕЕ ОПИСАНИЕ

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

Рассмотрим классический пример - вычисление факториала. Программа получает от компонента edinput целое число n и выводит в компонент lboutput значение N!, которое вычисляется с помощью рекурсивной функции Factorial.

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

procedure TfmExample.bbRunClick(Sender: TObject);

Function Factorial(N: Word): Extended;

begin

if N = 0 then

Result := 1 else

Result := N * Factorial(N-1)

end;

var

N: Integer;

begin

try

N := StrToInt(Trim(edinput.Text));

except

Exit; end;

IbOutput.Caption := FloatToStr(Factorial(N))

end;

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

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

Procedure A (i : Byte);

begin

В (i);

end;

Procedure В (j : Byte) ;

begin

а(j);

end;

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

Procedure В (j : Byte); Forward;

Procedure A (i : Byte);

begin

В (i);

end;

Procedure B; begin

A(j);

end;

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


лава 9. КЛАССЫ И ИНТЕРФЕЙСЫ

  • Глава 9. КЛАССЫ И ИНТЕРФЕЙСЫ
    • ОСНОВНЫЕ ПОНЯТИЯ
      • Инкапсуляция
      • Наследование
      • Полиморфизм
    • СОСТАВЛЯЮЩИЕ КЛАССА
      • Поля
      • Методы
      • Одноименные методы
      • Свойства
    • ОБЪЯВЛЕНИЕ КЛАССА
    • ИНТЕРФЕЙСЫ
      • Создание и использование интерфейса
      • Объекты Автоматизации и интерфейс IDispatch

Глава 9

КЛАССЫ И ИНТЕРФЕЙСЫ

Классами в Object Pascal называются специальные типы, которые содержат поля, методы и свойства. Как и любой другой тип, класс служит лишь образцом для создания конкретных экземпляров реализации, которые называются объектами. Сразу же уточню, что в предшественнике Object Pascal - Turbo Pascal объектами называются типы, имеющие много общего с классами Object Pascal. Однако существенные усовершенствования, внесенные в объектную модель Object Pascal, заставили разработчиков языка ввести для обозначения объектов специальный термин - класс, заимствованный, кстати, из Си++. Для совместимости с ранее разработанными программами системы Turbo Pascal with Objects 7.0 в Object Pascal сохранен тип-объект object, поддерживающий “старую” объектную модель. Поскольку все возможности этой модели доступны классам, я не буду рассматривать ее в этой книге, а “освободившийся” термин объект буду употреблять исключительно для обозначения конкретного экземпляра реализации класса.

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

type

TMyClass = class(TObject) Field: Integer;

end;

var

MyClass: TMyClass;

begin

MyClass^.Field := 0; // Ошибка! Следует писать так:

MyClass.Field := 0;

end;

9.1. ОСНОВНЫЕ ПОНЯТИЯ

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

9.1.1. Инкапсуляция

Класс представляет собой единство трех сущностей - полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от остальных частей программы, сделать его “самодостаточным” для решения конкретной задачи. В результате класс всегда несет в себе некоторую функциональность. Например, класс т Form содержит (инкапсулирует в себе) все необходимое для создания Windows-окна, класс тмето представляет собой полнофункциональный текстовый редактор, класс TTimer обеспечивает работу программы с таймером и т. д.

Инкапсуляция представляет собой мощное средство обмена готовыми к работе программными заготовками. Библиотека классов Delphi - это фактически набор “кирпичиков”, созданных программистами Borland для построения ваших программ.

9.1.2. Наследование

Любой класс может быть порожден от другого класса. Для этого при его объявлении указывается имя класса-родителя:

TChildClass = class (TParentClass)

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

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

TaClass = class(TObject) TaClass = class

Принцип наследования приводит к созданию ветвящегося дерева классов, постепенно разрастающегося при перемещении от TObject к его потомкам. Каждый потомок дополняет возможности своего родителя новыми и передает их своим потомкам.

Для примера на рис. 9.1 показан небольшой фрагмент дерева классов Delphi. Класс Tpersistent обогащает возможности своего родителя TObject: он “умеет” сохранять данные в файле и получать их из него, в результате это умеют делать и все его потомки. Класс TComponent, в свою очередь, умеет взаимодействовать со средой разработчика и передает это умение своим потомкам. Tcontrol не только способен работать с файлами и средой разработчика, но он еще умеет создавать и обслуживать видимые на экране изображения, а его потомок TWinControi может создавать Windows-окна и т. д.

Рис. 9.1. Фрагмент дерева классов Object Pascal

9.1.3. Полиморфизм

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

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

9.2. СОСТАВЛЯЮЩИЕ КЛАССА

9.2.1. Поля

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

type TMyClass = class

aIntField: Integer;

aStrField: String;

aObjField: TObject;

end;

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

type

TMyClass = class

FIntField: Integer;

FStrField: String; end;

var

aObject: TMyClass;

begin

aObject.FIntField := 0;

aObject.FStrField := 'Строка символов';

end;

Класс-потомок получает все поля всех своих предков и может дополнять их своими, но он не может переопределять их или удалять.

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

9.2.2. Методы

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

type

TMyClass = class

Function MyFunc(aPar: Integer): Integer;

Procedure MyProc;

end;

Доступ к методам класса, как и к его полям, возможен с помощью составных имен:

var

aObject: TMyClass;

begin

aObject.MyProc;

end;

Как уже говорилось, методы класса могут перекрываться в потомках. Например:

type

TParentClass = class Procedure DoWork;

end;

TChildClass = class(TParentClass) Procedure DoWork;

end;

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

В Object Pascal гораздо чаще используется динамическое замещение методов на этапе прогона программы. Для реализации этого метод, замещаемый в родительском классе, должен объявляться как динамический (с директивой dynamic) или виртуальный (virtual). Встретив такое объявление, компилятор создаст две таблицы -DMT (Dynamic Method Table) и VMT (Virtual Method Table) и поместит в них адреса точек входа соответственно динамических и виртуальных методов. При каждом обращении к замещаемому методу компилятор вставляет код, позволяющий извлечь адрес точки входа в подпрограмму из той или иной таблицы. В классе-потомке замещающий метод объявляется с директивой override (перекрыть). Получив это указание, компилятор создаст код, который на этапе прогона программы поместит в родительскую таблицу точку входа метода класса-потомка, что позволит родителю выполнить нужное действие с помощью нового метода.

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

type

TVisualObject = class(TWinControl)

Procedure Hide;

Procedure Show;

Procedure Draw(IsShow: Boolean); virtual;

end;

TVisualChildObject = class(TVisualObject)

Procedure Draw(IsShow: Boolean); override;

end;

Реализация методов show и Hide очень проста:

Procedure TVisualObject.Show;

begin

Draw(True) ;

end;

Procedure TVisualObject.Hide;

begin

Draw(False) ;

end;

Методы Draw у родителя и потомка имеют разную реализацию и создают разные изображения. В результате родительские методы show и Hide - прятать или показывать те или иные изображения будут в зависимости от конкретной реализации метода Draw у-любого из своих потомков. Динамическое связывание в полной мере реализует полиморфизм классов.

Разница между динамическими и виртуальными методами состоит в том, что таблица динамических методов DMT содержит адреса только -тех методов, которые объявлены как dynamic в данном классе, в то время как таблица VMT содержит адреса виртуальных методов не только данного класса, но и всех его родителей. Значительно большая по размеру таблица VMT обеспечивает более быстрый поиск, в то время как при обращении к динамическому методу программа сначала просматривает таблицу DMT у объекта, затем -у его родительского класса и так далее, пока не будет найдена нужная точка входа.

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

type

TVisualObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); virtual; abstract;

end;

TVisualChildObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); override; end;

var

aVisualObject: TVisualObject;

aVisualChild: TVisualChildObject ;

begin

aVisualObject.Show; {Ошибка/ Обращение к абстрактному методу}
aVisualChild.Show;
{Нормальное обращение. Метод Draw у класса TVisualChildObject перекрыт.)

end;

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

В состав любого класса входят два специальных метода -конструктор и деструктор. У класса TObject эти методы называются create и Destroy, так же они называются в подавляющем большинстве его потомков. Конструктор распределяет объект в динамической памяти и помещает адрес этой памяти в переменную self, которая автоматически объявляется в классе. Деструктор удаляет объект из кучи. Обращение к конструктору должно предварять любое обращение к полям и некоторым методам объекта. По своей форме конструкторы и деструкторы являются процедурами, но объявляются с помощью зарезервированных слов constructor и Destructor:

type

TMyClass = class IntField: Integer; Constructor Create(Value: Integer);

Destructor Destroy;

end;

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

var

MyObject: TMyClass;

begin

MyObject.IntField := 0;

{ Ошибка! Объект не созданконструктором!}

MyObject := TMyClass.Create;

// Надо так: создаем объект

MyObject.IntField := 0;

// и обращаемся к его полю

MyObect.Free;

// Уничтожаем ненужный объект

end;

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

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

Constructor TMyClass.Create(Value: Integer);

// Возможная реализация конструктора

begin

Inherited Create; // Вызываем унаследованный конструктор IntField := Value; // Реализуем дополнительные действия

end;

Некоторые методы могут вызываться без создания и инициации объекта. Такие методы называются методами класса, они объявляются с помощью зарезервированного слова class:

type

TMyClass = class(TObject)

class Function GetClassName: String;

end;

var

S: String;

begin

S := TMyClass.GetClassName;

end;

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

9.2.3. Одноименные методы

В отличие от остальных версий Delphi в версиях 4...6 появилась возможность в рамках одного класса иметь несколько одноименных методов. Описанный выше механизм перекрытия родительского метода одноименным методом потомка приводит к тому, что потомок “не видит” перекрытый родительский метод и может обращаться к нему лишь с помощью зарезервированного слова inherited. В Delphi 4 введено зарезервированное слово overload (перезагрузить), с помощью которого становятся видны одноименные методы как родителя, так и потомка.

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

гими параметрами. Для подавления сообщений объявление одноименного метода можно сопровождать зарезервированным словом reintrpduce (вновь ввести).

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

методов программа проверяет; тип и количество фактических параметров обращения и выбирает нужный метод

В следующем примере в классе TForm1 используются целых 4 одноименных метода close. Лишь один из них - унаследованный метод без параметра выполняет свои основные функции - закрывает окно. Три других отличаются набором параметров и выводят сообщение в заголовок окна.

Поместите на пустую форму четыре кнопки TButton и напишите такие обработчики их событий OnClick:

procedure TForm1.ButtonlClick(Sender: TObject);

begin

Close('Строка символов')

end;

procedure TFormi.Button2Click(Sender: TObject);

begin

Close(123)

end;

procedure TFormi.ButtonSClick(Sender: TObject);

begin

Close (20,300) ;

end;

procedure TFormi.Button4Click(Sender: TObject);

begin

Close end;

Теперь в раздел private класса Tform1 вставьте три таких объявления методов close:

private

{ Private declarations }

procedure Close(S: String);

reintroduce;

overload;

procedure Close(I: Integer);

reintroduce;

overload;

procedure Close(I,J: Integer);

reintroduce;

overload;

И, наконец, в разделе implementation поместите описания объявленных методов:

procedure TForm1.Close(S: String) ;

begin

Caption := S end;

procedure TFormI.Close(I: Integer);

begin

Caption := IntToStr(I) end;

procedure TFormI.close(I,J: Integers);

begin

Caption := IntToStr(i*j)

end;

Теперь после запуска программы три первые кнопки будут вызывать методы close класса Tform1 и менять заголовок окна, в то время как кнопка Button4 обратится к методу close родительского класса т Form и закроет окно.

9.2.4. Свойства

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

type

TaClass = class

IntField: Integer; Function GetField: Integer;

Procedure SetField (Value: Integers);

Property IntegerValue: Integer read GetField

write SetField;

end;

В контексте программы свойство ведет себя как обычное поле. Например, мы могли бы написать такие операторы:

var

aClass: TaClass;

Value: Integer;

begin

aClass := TaClass.Create; { Обязательное обращение к

конструктору перед обращением к полю или свойству!} aClass.IntegerValue := 0;

Value := aClass.IntegerValue;

aClass.Destroy; // Удаление ненужного объекта

end;

Более того, возможен и такой оператор присваивания:

aClass.IntField := NewValue;

Разница между этим оператором и оператором

aClass.IntegerValue := NewValue;

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

IbOutput.Caption := 'Строка';

Свойство Caption компонента Label вызывает метод setText, который не только запоминает строку символов во внутренней переменной, но и осуществляет прорисовку метки с новым текстом.

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

type

TaClass = class IntFiled: Integer;

Procedure SetFieid (Value: Integers;

Property IntegerValue:

Integer read IntFiled write SetFieid;

end;

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

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

9.3. ОБЪЯВЛЕНИЕ КЛАССА

Любой вновь создаваемый класс может содержать секции (разделы), определяемые зарезервированными словами published (опубликованные), private (закрытые), protected (защищенные), public (доступные) и automated (автоматизированные). Внутри каждой секции вначале определяются поля, а затем - методы и свойства.

Секции определяют области видимости элементов описания класса. Секция public не накладывает ограничений на область видимости перечисляемых в ней полей, методов и свойств - их можно вызывать в любом другом модуле программы. Секция published также не ограничивает область видимости, однако в ней перечисляются свойства, которые должны быть доступны не только на этапе исполнения, но и на этапе конструирования программы (т. е. в окне Инспектора объектов). Секция published используется только при разработке нестандартных компонентов. Замечу, что среда Delphi помещает описания компонентов, вставленных в форму, в специальную секцию без названия, которая располагается сразу за заголовком класса и продолжается до первой объявленной секции. Эта секция - published. Программисту не следует помещать в нее собственные элементы описания класса или удалять из нее элементы, вставленные средой. Секция private сужает область видимости до минимума: закрытые элементы описания доступны только внутри методов данного класса и подпрограммах, находящихся в том же модуле, где описан класс. Элемент, объявленный в секции private, становится недоступным даже ближайшим потомкам класса, если они размещаются в других модулях. Секция protected доступна только методам самого класса, а также любым его потомкам, независимо от того, находятся ли они в том же модуле или нет. Наконец, секция automated используется только для объявления свойств и методов, которые будут добавлены к так называемому интерфейсу OLE-объектов Автоматизации; область видимости членов этой секции не ограничена.

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

Следующий фрагмент кода поясняет области видимости.

Unit Unit1;

Interface

Uses Controls, Forms;

type

TFormI = class(TForm)

Buttoni: TButton; // Эта секция обслуживается Delphi

// Ее элементы доступны всем
// Эта секция доступна в модуле Uniti

private
FIntField: Integers
Procedure SetValue(Value: Integers);
Function GetValue: Integer;
published
// Эта секция доступна в любом модуле

Property IntField: read GetValue write SetValue;

protected // Эта секция доступна классам-потомкам

Procedure Proc1;

public // Эта секция доступна в любом модуле Procedure Proc2;

end;

var

Formi: TForm1;

Implementation Procedure TFormI.Proc1 ;

Buttoni.Color := clBtnFace;1

// Так можно

FIntField := 0;

// Так можно

IntField := 0;1

// Так можно Proc1;

// Так можно Proc2;1

// Так можно

end;

begin

Form1.Button1.Color := clBtnFace; // Так можно

Form1.FIntField := 0; // Так можно

Form1.IntField := 0; // Так можно
Form1.Proc1; // Так нельзя!
Form1.Proc2; // Так можно

end.

Unit Unit2;

Interface

Uses Controls, Unit1;

type

TForm2 = class(TFormI) Button2: TButton;

Procedure Button2Click(Sender: TObject);

end;

var

Form2: TForm2;

Implementation

Procedure TForm2.Button2Click(Sender: TObject);

begin

Buttoni.Color := clBtnFace; // Так можно

FIn'tField := 0; // Так нельзя!

IntField := 0; // Так можно

Proc1; // Так можно

Proc2; // Так можно

end;

begin

Form1.Buttoni.Color := clBtnFace; // Так можно

Form1.FIntField := 0; // Так нельзя!

Form1.IntField := 0; // Так можно

Form1.Proc1; //Так нельзя!

Form1.Proc2; // Так можно

end.

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

type

TForm2 = class(Tform1)

Public

Procedure Proc1;

end;

После этого в модуле unit2 возможно такое обращение:

Form2.Proc1;

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

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

9.4. ИНТЕРФЕЙСЫ

Интерфейсы играют главную роль в технологиях СОМ (Component Object Model - компонентная модель объектов), CORBA (Common Object Request Broker Architecture - архитектура с

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

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

9.4.1. Создание и использование интерфейса

Интерфейсы представляют собой частный случай описания типов. Они объявляются с помощью зарезервированного слова interface. Например:

type

IEdit = interface

procedure Copy; stdcall;

procedure Cut; stdcall;

procedure Paste; stdcall;

function Undo: Boolean; stdcall;

end;

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

В отличие от классов интерфейс не может содержать поля, и, следовательно, объявляемые в нем свойства в разделах read и write могут ссылаться только на методы. Все объявляемые в интерфейсе члены размещаются в единственной секции public. Методы не могут быть абстрактными (abstract), виртуальными (virtual), динамическими (dynamic) или перекрываемыми (override). Интерфейсы не могут иметь конструкторов или деструкторов, т. к. описываемые в них методы реализуются только в рамках поддерживающих их классов, которые называются интерфейсными.

Если какой-либо класс поддерживает интерфейс (т. е. является интерфейсным), имя этого интерфейса указывается при объявлении класса в списке его родителей:

TEditor = class(TInterfacedObject,IEdit)

procedure Copy; stdcall;

procedure Cut; stdcall;

procedure Paste; stdcall;

function Undo: Boolean; stdcall;

end;

В отличие от обычного класса интерфейсный класс может иметь более одного родительского интерфейса:

type

IMylnterface = interface procedure Delete; stdcall;

end;

TMyEditor = class(TInterfacedObiect, lEdit, IMylnterface)

procedure Copy; stdcall;

procedure Cut; stdcall;

procedure Paste; stdcall;

function Undo:, Boolean; stdcall;

procedure Delete; stdcall;

end;

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

IPaint = interface

procedure CirclePaint(Canva: TCanvas; X,Y,R: Integer);

procedure RectPaint(Canva: TCanvas; X1,Y1,X2,Y2: Integer);

end;

и использующий его интерфейсный класс

TPainter = class(TInterfacedObject,IPaint)

procedure CirclePaint(Canva: TCanvas; X,Y,R: Integers);
procedure RectPaint(Canva: TCanvas; X1,Y1,X2,Y2: Integer);

end;

то в разделе implementation следует указать реализацию методов:

procedure TPainter.CirclePaint(Canva: TCanvas;

X,Y,R: Integers; begin

with Canva do

Ellipse(X, Y, X+2*R, Y+2*R) ;

end;

procedure TPainter.RectPaint(Canva: TCanvas;

X1,Y1,X2,Y2: Integer);

begin

with Canva do

Rectangle(XI, Yl, X2, Y2)

end;

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

procedure TFormI.PaintBoxIPaint(Sender: TObject);

var

Painter: IPaint;

begin

Painter := TPainter.Create;

Painter.CirclePaint(PaintBoxl.Canvas,10,0,10) ;

Painter.RectPaint(PaintBoxl.Canvas,40,0,60,20);

end;

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

type

TPainter = class(TInterfacedObject, IPaint)

end;

было бы ошибкой: компилятор потребовал бы вставить описание методов CirclePaint и RectPaint.

Подобно тому как все классы в Object Pascal порождены от единственного родителя TObject, все интерфейсные классы порождены от общего предка TInterfacedObject. Этот предок умеет распределять память для интерфейсных объектов и использует глобальный интерфейс lunknow:

type

TInterfacedObject = class(TObject, lUnknown) private

FRefCount: Integer;

protected

function Querylnterface(

const IID: TGUID; out Obj): Integer; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

public

property RefCount: Integer read FRefCount;

end;

Если бы в предыдущем примере класс TPainter был описан так:

TPainter = class(IPaint)

procedure CirclePaint(Canva: TCanvas; X,Y,R: Integer);

procedure RectPaint(Canva: TCanvas; X1,Y1,X2,Y2: Integer);

end;

компилятор потребовал бы описать недостающие методы Queryinterface, _Add И _Release класса TInterfacedObject. Поле FRef Count этого класса служит счетчиком вызовов интерфейсного объекта и используется по принятой в Windows схеме: при каждом обращении к методу Add интерфейса IUnknow счетчик наращивается на единицу, при каждом обращении к Release - на единицу сбрасывается. Когда значение этого поля становится равно 0, интерфейсный объект уничтожается и освобождается занимаемая им память.

Если интерфейс предполагаетсяиспользовать в технологиях COM/DCOM или CORBA, его методы должны описывать с директивой stdcall или (для объектов Автоматизации) safecall

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

procedure PaintObjects(P: TInterfacedObject) var

X: IPaint;

begin

try

X := P as IPaint;

X.CirclePaint(PaintBoxl.Canvas,0,0,20)

except

ShowMessage('Объект не поддерживает интерфейс IPaint')

end

end;

Встретив такое присваивание, компилятор создаст код, с помощью которого вызывается метод Queryinterface интерфейса IUnknow с требованием вернуть ссылку на интерфейс IPaint. Если объект не поддерживает указанный интерфейс, возникает исключительная ситуация.

Интерфейсы, рассчитанные на использование в удаленных объектах, должны снабжаться глобально-уникальным идентификатором (guiD). Например:

IPaint = interface

['{A4AFEB60-7705-11D2-8B41-444553540000}']

procedure CirclePaint(Canva: TCanvas; X,Y,R: Integer);

procedure RectPaint(Canva: TCanvas; Xl,Yl,X2,Y2: Integer);

end;

Глобально-уникальные идентификаторы создаются по специальной технологии, гарантирующей ничтожно малую вероятность того, что два guid совпадут. Эта технология включена в Windows 32: чтобы получить guid для вновь созданного интерфейса в среде Delphi, достаточно нажать клавиши Ctrl+Shift+G. Для работы с guid в модуле System объявлены следующие типы:

type

PGUID = ^TGUID;

TGUID = record Dl: LongWord;

D2: Word;

D3: Word;

D4: array [0..7] of Byte;

end;

Программист может объявлять типизированные константы типа tguid, например:

const IID_IPaint: TGUID= ['{A4AFEB61-7705-11D2-8B41-444553540000}'] ;

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

procedure Paint(const IID: TGUID);

Paint(IPaint) ;

Paint(IID_Paint);

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

type

IMylnterface = interface procedure P1; procedure P2 ;

end;

TMyClass = class(TObject, IMylnterface)

FMyInterface: IMylnterface;

property Mylnterface: IMylnterface

read FMyInterface implements IMylnterface;

end;

Обратите внимание: в этом примере класс TMyciass не является интерфейсным, т. е. классом, в котором исполняются методы p1 и P2. Однако если из него убрать определение уполномоченного свойства Mylnterface, он станет интерфейсным, и в нем должны быть описаны методы интерфейса IMylnterface.

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

9.4.2. Объекты Автоматизации и интерфейс IDispatch

В технологии OLE активно используются так называемые объекты Автоматизации (Automation objects). Эти объекты представляют собой экземпляры интерфейсных классов, родительским интерфейсом которых является специальный интерфейс IDispatch. Отличительной особенностью IDispatch является то обстоятельство, что методы объекта Автоматизации никогда не вызываются напрямую, но всегда - с помощью метода invoke интерфейса IDispatch. Управление объектами СОМ с помощью выполнения методов IDispatch называется маршализацией (marshaling).

Для объявления класса Автоматизации используется специальное зарезервированное слово dispinterface, а перечисляемые в нем методы и свойства должны снабжаться целочисленными идентификаторами, которые вставляются в конце описания методов (свойств) после зарезервированных слов dispid:

type

IStringsDisp = dispinterface ['{EE05DFE2-5549-11DO-9EA9-0020AF3D82DA}']

property ControlDefault[Index: Integer]: OleVariant

dispid 0; default-function Count: Integer;

dispid 1;

property I tern[Index: Integer]: OleVariant dispid 2;

procedure Remove(Index: Integer); dispid 3;

procedure Clear; dispid 4;

function Add(Item: OleVariant): Integer; dispid 5;

function _NewEnum: lUnknown; dispid -4;

end;

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

Следующих типов: Byte, Currency, Real, Double, Longint, Integer, Single, Smallint, AnsiString, WideString, TDateTime, Variant, OleVariant, WordBool или любой интерфейсный тип. За исключением директивы default, которую можно указать для свойства-массива, никакие другие директивы доступа в объявлении методов и свойств не допускаются.

Для доступа к объектам Автоматизации используются переменные типа вариант (см. следующую главу). Инициация такой переменной осуществляется вызовом функции CreateOleObject, определенной в модуле comobj. Эта функция возвращает ссылку на интерфейс IDispatch, с помощью которой можно обращаться к методам и свойствам класса Автоматизации так, как если бы они были методами и свойствами варианта. Например, следующая программа вызывает текстовый процессор MS Word, вставляет в пустую страницу две строки и сохраняет полученный документ на диске:

Uses ComObj ;

var

Word: Variant;

begin

Word := CreateoieObject('Word.Basic');

Word.FileNew('Normal');

Word.Insert('Первая строка'#13);

Word.Insert('Вторая строка'#13);

Word.FileSaveAs('с:\temp\test.txf, 3) ;

end;

Параметром обращения к CreateoieObject является имя сервера Автоматизации, которое должно быть предварительно зарегистрировано в реестре Windows 32. Характерно, что методы сервера не известны на этапе компиляции программы, поэтому компилятор никак не контролирует правильность их вызовов. Названия методов не подчиняются правилам построения идентификаторов Delphi, и в них могут использоваться символы национальных алфавитов.


Глава 10. ВАРИАНТЫ

  • Глава 10. ВАРИАНТЫ
    • ОСНОВНЫЕ СВОЙСТВА ВАРИАНТА
    • ПРЕОБРАЗОВАНИЕ ВАРИАНТОВ К ДАННЫМ ДРУГИХ ТИПОВ
    • ПОДПРОГРАММЫ ДЛЯ РАБОТЫ С ВАРИАНТАМИ
    • ВАРИАНТНЫЕ МАССИВЫ
    • ПОЛЬЗОВАТЕЛЬСКИЕ ВАРИАНТЫ
      • Размещение в варианте новых значений
      • Создание наследника TCustomVariantType
      • Создание вспомогательных методов

Глава 10

ВАРИАНТЫ

10.1. ОСНОВНЫЕ СВОЙСТВА ВАРИАНТА

Вариант (в Delphi 1 он отсутствует) - это тип variant, разработанный специально для тех случаев, когда на этапе компиляции программист не может сказать, какого типа данные будут использоваться в выражении или как параметры вызова подпрограмм. Переменная-вариант занимает в памяти дополнительные 2 байта, в которые помещается информация о действительном типе переменной. Эта информация позволяет компилятору создать код, который будет осуществлять необходимое преобразование типов на этапе прогона программы.

В переменную-вариант можно поместить:

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

Варианты могут участвовать в целочисленных, вещественных, логических и время-дата выражениях при условии корректности соответствующих преобразований. Например, если варианту v присвоена строка '1.0', то выражение 1+v будет правильным вещественным значением 2,0. Однако если v := 'текст', выражение 1+v вызовет исключение EVariantError.

В Delphi определены такие константы, указывающие тип помещенных в вариант данных:

Таблица 10.1. Типы возможных значений варианта

Имя | Константа | Смысл
varEmp.ty | $0000 | Нет данных
varNull | $0001 | Неизвестный тип параметра

varSmallInt

$0002

Целый тип Smallint

varlnteger | $0003 | Целый тип Integer
varSingle | $0004 | Вещественный тип Single
varDouble | $0005 | Вещественный тип Double
varCurrency | $0006 | Вещественный тип Currency
varDate | $0007 | Тип дата-время
varOleStr | $0008 | OLE-строка в кодировке Unicode
varDispatch | $0009 | Указатель на OLE-объект
varError | $000А | Код ошибки
varBoolean | $000В | Тип WordBool
varVariant | $000С | Тип Variant (только для вариантных массивов)
varUnknow | $0011 | Неизвестный OLE-объект
varByte | $0100 | Целый тип Byte
varString | $0100 | Строковый тип
varArray | $2000 | Вариантный массив
varByRef | $4000 | Указатель на данные

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

TVarData = packed record

VType: Word;

Reservedly Reserved2, ReservedS: Word;

case Integer of

varSmallInt: (VSmallInt: Smallint);

varlnteger: (VInteger: Integer);

varSingle: (VSingle: Single);

varDouble: (VDouble: Double);

varCurrency: (VCurrency: Currency);

varDate:(VDate: Double) ;

varOleStr: (VOleStr: PWideChar) ;

varDispatch: (VDispatch: Pointer);

varError: (VError: WordBool);

varString: (VString: Pointer);

varArray: (VArray: PVarArray) ;

varByRef: (VPointer: Pointer);

end;

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

К чему приводится | Тип данных в варианте
 | varEmpty | Целые | Вещественные | Дата- Время | Строковые | Логические
К дата- 'У: время | 30.12. 1899 00:00:00 | Преобразование в Double | Преобразование в Double | Без преобразования | Преобразование в дату | Преобразование в Double
К целым | 0 | Преобразование в соответствующий тип | Округление до ближайшего целого | Округление до ближайшего целого | Преобразование в целый тип | 0 для False, иначе-1 (255 для Byte)
.'К дата- 'У: время | 30.12.1899 00:00:00 | Преобразование в Double | Преобразование в Double | Без преобразования | Преобразование в дату | Преобразование в Double
К строковым | Пустая строка | Преобразование в символьный вид | Преобразование в символьный вид | Преобразование в символьный вид | Без преобразования | '0'для False,'-!' для True
К логическим | False | False для 0, иначе True | False для 0, иначе True | False для 0, иначе True | False для 'False' и для '0', иначе True | Без преобразования

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

10.2. ПРЕОБРАЗОВАНИЕ ВАРИАНТОВ К ДАННЫМ ДРУГИХ ТИПОВ

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

Таблица 10.2. Преобразование типов для вариантов

Здесь

К целым Отнесены varByte, varSmallInt, varlnteger/ varError;

К вещественным — varSingle, varDouble/ varCurrency;

К строковым -var String, varOleStr.

10.3. ПОДПРОГРАММЫ ДЛЯ РАБОТЫ С ВАРИАНТАМИ

Для работы с вариантами можно использовать такие подпрограммы:

Таблица 10.3. Подпрограммы для работы с вариантами

function VarAsType(const V: Variant; VarType: Integer): Variant; | Преобразует данные варианта V к типу, определяемому параметром VarType
procedure VarCast(var Dest: Variant; const Source: Variant; Var Type: Integer) ; | Преобразует данные варианта Source к типу,определяемому параметром VarType, и помещает результат в переменную Dest
procedure VarClear(var V: Variant) ; | Освобождает динамическую память, если она была связана с вариантом, и дает ему тип varEmpty
procedure VarCopy(var Dest: Variant; const Source: Variants; | Копирует параметр Source в вариант Dest
function VarFrom-DateTime(DateTime: TDateTime):Variant; | Возвращает вариант, содержащий данные DateTime типа дата-время
function VarIsEmpty(const V: Variant): Boolean; | Возвращает True, если вариант V не содержит данных
function VarIsNull(const V: Vari ant) : Boolean; | Возвращает True, если вариант V содержит данные неопределенного типа (varNull) ',
function VarToDateTime(const V: Variant): TDateTime) ; | Преобразует данные варианта V к типу дата-время
function VarToStr(const V: Vari ant) : String; | Преобразует данные варианта V к строке ;
function VarType(const V: Variant) : Integer; | Возвращает тип хранящихся в варианте данных i

10.4. ВАРИАНТНЫЕ МАССИВЫ

Значением варианта может быть массив данных, такие варианты называются вариантными массивами. (Не путайте с обычным или динамическим массивом, элементами которого являются варианты!) Значениями элементов вариантного массива могут быть любые допустимые для варианта значения, кроме строк varstring. Значениями элементов вариантного массива могут быть и варианты, а это значит, что в таком массиве могут одновременно храниться данные разных типов (и в том числе строки). Например:

var

V: Variant;

begin

// Создаем одномерный вариантный массив с 5 элементами:

V := VarArrayCreate([0, 4], varVariant);

// Наполняем его:

V[0] := 1; //Тип целый

V[1] := 1234.5678; //Тип вещественный

V[2] := 'Hello world'; //Строковый тип

V[3] := True; //Логический тип

//Пятым элементом исходного массива сделаем еще один массив:

V[4] := VarArrayOf([l, 10, 100, 1000]);

Caption := V[2]; //Hello world

IbOutput.Caption := IntToStr(V[4][2]); //200

end;

Все действия с вариантными массивами осуществляются с помощью следующих процедур и функций:

Таблица 10.4. Подпрограммы для работы с вариантными массивами

function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; | Создает вариантный массив из элементов типа VarType с количеством и границами измерений, указываемых параметром Bounds
function VarArrayDimCount(const A: Variant): Integers; | Возвращает количество измерений вариантного массива А или 0, если А не массив
function VarArrayHighBound(const A: Variant; Dim: Integer): Integer; | Возвращает верхнюю границу индекса вариантного массива А по измерению Dim
function VarArrayLock(var A: Variant): Pointer; | Блокирует массив (предотвращает его возможные изменения размеров) и возвращает указатель на связанные с ним данные
function VarArrayLowBound(const A: Variant; Dim: Integer): Integers; | Возвращает нижнюю границу индекса вариантного массива А по измерению Dim
function VarArrayOf(const Values: array of Variant): Variants; | Создает одномерный вариантный массив по перечню значений, содержащихся в открытом массиве Values. Нижняя граница индексов вариантного массива в этом случае равна 0
procedure VarArrayRedim(var A: Variant; HighBound: Integer) ; | Изменяет верхнюю границу индекса вариантного массива А на величину HighBound. Вызов про цедуры игнорируется, если массив был заблоки рован функцией VarArrayLock
function VarArrayRef(const A: Variant): Variants; | Возвращает ссылку на вариантный массив. Ис пользуется при обращении к API-функциям
procedure VarArrayUnlock(var A: Variant) | Отменяет действие функции VarArrayLock

10.5. ПОЛЬЗОВАТЕЛЬСКИЕ ВАРИАНТЫ

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

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

VarCmplx.

Создайте такой обработчик bbRunClick:

uses VarCmplx; // Эта ссылка обязательна!

procedure TfmExample.bbRunClick(Sender: TObject);

var

VI, V2: Variants-begin

// Создаем два случайных комплексных числа:

VI := VarComplexCreate(Trunc(Random*1000)/100,

Trunc(Random*1000)/100) ;

V2 := VarComplexCreate(Trunc(Random*1000)/100,

Trunc(Random*1000)/100) ;

with mmOutput.Lines do

begin

// Пустая строка-разделитель

Add ( ' ' ) ;

Add('1-e число: '# 9+V1) ;

Add('2-е число: '#9+V2);

Add('Сложение'#9+(V1+V2));

Add('Вычитание'#9+(V1-V2));

Add('Умножение'# 9+(VI*V2)) ;

Add('Деление'#9#9+(V1/V2))

end

end;

Небольшой комментарий: сложная конструкция Trunc (Random*1000) /100 понадобилась только для того, чтобы реальные и мнимые части комплексных чисел содержали по три значащих цифры.

Вид экрана работающей программы показан на рис. 10.1. Как видим, новый вариант легко справляется с поддержкой комплексных чисел: функция VarComplexCreate создает вариант, содержащий комплексное число, а дальнейшее поведение варианта -стандартное (он поддерживает математические операции и преобразование к строковому типу). Однако эта легкость обманчива: исходный текст модуля VarCmplx, который, собственно, и придал варианту дополнительные свойства (по умолчанию располагается в файле Source\Rtl\Common\VarCmplx.pas), содержит более 30000 байт..

На с. 229 показана структура записи TVarData. Два первых байта в этой записи (поле VType) хранят признак значения варианта, остальные 14 могут использоваться для размещения данных.

Рис. 10.1. Демонстрация комплексных вариантов

Создание пользовательского варианта проходит в три этапа.

  1. Сначала в записи rvarData размещаются новые данные или ссылка на них.
  2. Объявляется класс, который является прямым или косвеннымпотомком специального класса TCustomVariantType. В этомклассе предусматриваются все необходимые методы для реализации свойств варианта: присваивания ему новых значений, преобразования хранящихся значений к другим типам, выполнения необходимых математических действий.
  3. Создаются вспомогательные методы для объявления потомков нового класса и определения их типов.

В результате перечисленных шагов вы получаете полноценный вариант, обогащенный новыми свойствами: он может хранить не только те значения, которые перечислены в табл. 10.2, но и любые другие, в том числе свойства и методы! (В этом последнем случае наследником для исполняемого класса нового варианта вместо TCustomVariantType является TInvokeableVariantType или TPublishableVariantType.)

10.5.1. Размещение в варианте новых значений

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

TComplexVarData = packed record

VType: TVarType;

Reserved1, Reserved2, Reserved3: Word;

VComplex: TComplexData;

Reserved4: Longint;

end;

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

type

TComplexData = class(TPersistent) private

FReal, FImaginary: Double;

end;

В этом классе предусмотрены многочисленные методы, управляющие новыми данными. Так, простой вызов VarComplexCreate

приводит к срабатыванию нескольких методов, создающих объект VComplex и наполняющих его поля:

procedure VarComplexCreateInto(var ADest: Variant;

const AComplex: TComplexData);

begin

VarClear(ADest);

TComplexVarData(ADest).VType := VarComplex;

TComplexVarData(ADest).VComplex := AComplex;

end; function VarComplexCreate(const AReal, AImaginary: Double):

Variant;

begin

VarComplexCreateInto(Result,

TComplexData.Create(AReal, AImaginary)) ;

end;

(CM. файл Source\Rtl\Common\VarCmplx.pas).

Запись в которой размещаются новые данные или ссылка на поддерживающий их обьект, должно обьявляться как packed record.

10.5.2. Создание наследника TCustomVariantType

Тип TCustomVariantType или его ближайшие Наследники TPublishableVariantType и TInvokeableVariantType Содержат методы и

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

type

TComplexVariantType =

class(TPublishableVariantType, IVarStreamable) protected

function LeftPromotion(const V: TVarData;

const Operator: TVarOp;

out RequiredVarType: TVarType): Boolean; override;

function RightPromotion(const V: TVarData;

const Operator: TVarOp;

out RequiredVarType: TVarType): Boolean; override;

function Getlnstance(const V: TVarData): TObject; override;

public

procedure Clear(var V: TVarData);

override;

function IsClear(const V: TVarData): Boolean; override;

procedure Copy(var Dest: TVarData;

const Source: TVarData;

const Indirect: Boolean);

override;

procedure Cast(var Dest:

TVarData;

const Source: TVarData);

override;

procedure CastTo(var Dest: TVarData;

const Source: TVarData;

const AVarType: TVarType);

override;

procedure BinaryOp(var Left: TVarData;

const Right: TVarData;

const Operator: TVarOp); override;

procedure UnaryOp(var Right: TVarData;

const Operator: TVarOp);

override;

function CompareOp(const Left: TVarData;

const Right: TVarData;

const Operator: Integer): Boolean;

override;

procedure Streamin(var Dest: TVarData;

const Stream: TStream) ;

procedure StreamOut(const Source: TVarData;

const Stream: TStream) ;

end;

Обратите внимание: класс TComplexVariantType - интерфейсный (см. п. 9.4.1). Помимо общих для варианта методов он реализует также два метода, специфичных для интерфейса Ivarstreamabie -Streamin и StreamOut, с помощью которых значения нового интерфейса сохраняются в потоке и считываются из него.

Задача этого класса - дать единообразные команды, способные интерпретироваться объектом vcomplex как команды преобразования типа хранящихся данных, их сравнения, реализации над ними тех или иных операций, наконец, записи их в поток и чтения из него. Например, метод cast этого класса вызывается для преобразования других типов значений к комплексному типу, метод castTo - для обратного преобразования, метод BinaryOp реализует бинарную операцию, a Unarydp - унарную и т. д.

Еще раз подчеркну, что основная работа (например, по выполнению бинарных операций) реализуется методами класса TComplex-Data. Класс TCompiexVariantType перекрывает абстрактные методы своего родителя, подключая TComplexData к решению той или иной проблемы.

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

initialization

ComplexVariantType := TCompiexVariantType.Create;

finalization

FreeAndNil(ComplexVariantType);

10.5.3. Создание вспомогательных методов

Несмотря на интенсивное использование классов TCompiexData и TCompiexVariantType, эти классы в конечном счете остаются скрытыми от пользователя нового варианта за счет набора вспомогательных

методов, таких как VarComplexCreate, VarIsComplex, VarAsComplex и т.

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


Глава 11. ФАЙЛЫ

  • Глава 11. ФАЙЛЫ
    • ДОСТУП К ФАЙЛАМ
    • ПРОЦЕДУРЫ И ФУНКЦИИ ДЛЯ РАБОТЫ С ФАЙЛАМИ
    • ТЕКСТОВЫЕ ФАЙЛЫ
    • ТИПИЗИРОВАННЫЕ ФАЙЛЫ
    • НЕТИПИЗИРОВАННЫЕ ФАЙЛЫ
    • СРЕДСТВА WINDOWS ДЛЯ РАБОТЫ С ФАЙЛАМИ
    • ОТОБРАЖЕНИЕ ФАЙЛОВ В ПАМЯТЬ
      • Создание/открытие файла
      • Создание объекта отображения
      • Создание окна просмотра
      • Освобождение ресурсов отображения
      • Пример использования
    • ОБЪЕКТНАЯ МОДЕЛЬ РАБОТЫ С ФАЙЛАМИ

Глава 11

ФАЙЛЫ

Под файлом понимается именованная область внешней памяти ПК (жесткого диска, гибкой дискеты, диска CD-ROM).

Любой файл имеет три характерные особенности. Во-первых, у него есть имя, что дает возможность программе работать одновременно с несколькими файлами. Во-вторых, он содержит компоненты одного типа. Типом компонентов может быть любой тип Object Pascal, кроме файлов. Иными словами, нельзя создать “файл файлов”. В-третьих, длина вновь создаваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти.

Файловый тип можно задать одним из трех способов:

<имя> = File of <тип>;

<имя> = TextFile;

<имя> = File;

Здесь <имя> - имя файлового типа (правильный идентификатор);

File, of - зарезервированные слова (файл, из); TextFile - имя стандартного типа текстовых файлов; <тип> - любой тип Object Pascal, кроме файлов. Например:

type

Product = record Name: String; Code : Word;

Cost: Comp

end;

TextSO = File of String[80];

var

Fl: File of Char;

F2: TextFile;

F3: File;

F4: TextSO;

F5: File of Product;

В зависимости от способа объявления можно выделить три вида файлов:

  • типизированные файлы (задаются предложением File of ...);
  • текстовые файлы (определяются типом TextFile);
  • нетипизированные файлы (определяются типом File).

В наших примерах f1, F4 и F5 - типизированные файлы, F2 - текстовый файл, F3 - нетипизированный файл. Вид файла, вообще говоря, определяет способ хранения в нем информации. Однако в Object Pascal нет средств контроля вида ранее созданных файлов. При объявлении уже существующих файлов программист должен сам следить за соответствием вида объявления характеру хранящихся в файле данных.

11.1. ДОСТУП К ФАЙЛАМ

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

Файловая переменная связывается с именем файла в результате обращения к стандартной процедуре AssignFile :

AssignFile (<ф.п.>, <имя файла>);

Здесь <ф.п.> - файловая переменная (правильный идентификатор, объявленный в программе как переменная файлового типа);

<имя файла > - текстовое выражение, содержащее имя файла и, если это необходимо, маршрут доступа к нему.

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

Для чтения файл инициируется с помощью стандартной процедуры Reset;

Reset (<ф.п.>);

Здесь <ф. п. > - файловая переменная, связанная ранее процедурой AssignFile с уже существующим файлом.

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

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

Чтобы проверить, существует ли дисковый файл, можно использовать стандартную функцию FileExists, которая возвращает Truе, если указанный при обращении этой функции файл существует, и False - если не существует.

Например

begin

if FileExists(FileName) then

..... // Файл существует

else ..... // Файл не существует

end;

В Object Pascal разрешается обращаться к типизированным файлам, открытым процедурой Reset (т. е. для чтения информации), с помощью процедуры write (т. е. для записи информации). Такая возможность позволяет легко обновлять ранее созданные типизированные файлы и при необходимости расширять их. Для текстовых файлов, открытых процедурой Reset, нельзя использовать процедуру Write ИЛИ WriteLn.

Стандартная процедура

Rewrite (<ф.п.>);

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

Стандартная процедура

Append (<ф.п.>)

инициирует запись в ранее существовавший текстовый файл для его расширения, при этом указатель файла устанавливается в его конец. Процедура Append применима только к текстовым файлам, т. е. их файловая переменная должна иметь тип TextFile (см. выше). Процедурой Append нельзя инициировать запись в типизированный или нетипизированный файл. Если текстовый файл ранее уже был открыт с помощью Reset или Rewrite, использование процедуры Арpend приведет к закрытию этого файла и открытию его вновь, но уже для добавления записей.

11.2. ПРОЦЕДУРЫ И ФУНКЦИИ ДЛЯ РАБОТЫ С ФАЙЛАМИ

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

Таблица 11.1. Подпрограммы для работы с файлами

Procedure AssignFile(var F; FileName: String) ; | Связывает файловую переменную f с именем файла FileName
function ChangeFileExt (const FileName, Ex tension: String): Strings; | Изменяет существующее расширение файла на расширение, заданное параметром Extension
Procedure ChDir(Path: String); | Изменяет текущий каталог: path - строковое выражение, содержащее путь к устанавливаемому по умолчаниюкаталогу
Procedure CloseFile (var F) ; | Закрывает файл, однако связь файловой переменной F с именем файла, установленная ранее процедурой Assign-File, сохраняется. При создании нового или расширении старого файла процедура обеспечивает сохранение в файле всех новых записей и регистрацию файла в каталоге. Функции процедуры CloseFile выполняются автоматически по отношению ко всем открытым файлам при нормальном завершении программы. Поскольку связь файла с файловой переменной сохраняется, файл можно повторно открыть без дополнительного использования Процедуры AssignFile
function DateTime-ToFileDate(DateTime: TDateTime): Integer; | Преобразует значение DateTime в системный формат времени создания (обновления) файла
Function DiskFree(D: Byte): Longint; | Возвращает объем в байтах свободного пространства на указанном диске: D - номер диска (0 - устройство по умолчанию, 1 - диск А ,2- диск В и т, д.). Функция возвращает значение -1, если указан номер несуществующего диска
function Delete- File(const FileName: String): Boolean; | Уничтожает файл с именем (и, возможно, маршрутом доступа) FileName. Возвращает True, если операция прошла успешно
Function DiskSize(D: Byte) : Longing; | Возвращает объем в байтах полного пространства на указанном диске: d - номер диска (0 - устройство по умолчанию, 1 - диск А ,2- диск Д и т. д.). Функция возвращает значение -1, если указан номер несуществующего диска
Function EOF (var F) : Boolean; | Тестирует конец файла и возвращает True, если файловый указатель стоит в конце файла. При записи это означает, что очередной компонент будет добавлен в конец файла, при чтении - что файл исчерпан

Procedure Erase(var

F);

Уничтожает файл f. Перед выполнением процедуры не

обходимо закрыть файл (см. замечание ниже)

function FileAge(const

FileName: String): In

teger;

Для файла FileName возвращает время его последнего обновления (в системном формате) или -1, если такого файла не существует

function ExcludeTrai-

lingBackslash(const S: String): Strings;

Исключает из строки s замыкающий символ “\” (если этот символ не замыкает строку, возвращает S без изменения)

function ExpandUNC-

FileName(const File

Name: String): String;

Дополняет имя файла текущим сетевым каталогом (и

диском)

function ExtractFile-

Dir(const FileName:

String): Strings;

Извлекает из полного имени файла маршрут доступа к

нему (без последнего символа “\”)

function ExtractFileExt(const FileName:

String): Strings;

Извлекает из полного имени файла его расширение (с ведущей точкой)

function ExtractFileName(cons t Fi1eName:

String): Strings;

Извлекает из полного имени файла его имя (с расширением)

function ExtractFilePath(const File

Name: String): Strings;

Извлекает из полного имени файла маршрут доступа к нему (с последним символом “\”)

function ExtractRela-

tivePath(const Bas eName, De s tName:

String): Strings;

Извлекает из полного имени файла имя маршрута относительно DestName (промежуточные каталоги заменяются символами “..\”)

function ExtractShort-

PathName(const File

Name : String): Strings;

Преобразует имя файла к короткому формату 8.3 для MSDOS и Windows 3-х

function FileDateTo-

DateTime(FileDate: Integer) : TDateTime;

Преобразует системный формат FileDate времени создания файла в формат дата-время

Function FileEx-

ists(const FileName: String): Boolean;

Возвращает True, если файл с именем (и, возможно, маршрутом доступа) FileName существует

function File Get Date

(Handle: Integer): Integers;

По заданному дескриптору файла Handle возвращает время и дату его создания в системном формате. Возвращает 0 в случае успеха или код ошибки

function FileSetDate

(Handle: Integer; Age:

Integer): Integers;

Для файла с дескриптором Handle устанавливает новое время и дату его создания Age в системном формате.

Возвращает 0 в случае успеха или код ошибки

Function FindFirst

(const Path: String;

Attr: Integer; var F:

TSearchRec): Integer;

Возвращает атрибуты первого из файлов, зарегистрированных в указанном каталоге: Path - маршрут поиска и маска выбора файлов; Attr - атрибуты

выбираемых файлов; f - переменная типа

TSesrchRec, в которой будет возвращено имя первого выбранного файла. При успешном поиске возвращает значение 0

Procedure Find- Close(var F: TSear- chRec); | Освобождает память, выделенную для поиска файлов функциями FindFirst/FindNext
Function FindNext(var F: TSearchRec): Integer; | Возвращает в переменой f имя следующего файла в каталоге. Переменная f должна предварительно инициироваться обращением к функции FindFirst. При успешном поиске возвращает значение 0
Procedure Flush(varF); | Очищает внутренний буфер файла и, таким образом, гарантирует сохранность всех последних изменений файла на диске
Procedure GetDir(D: Byte; var S: Strings- | Возвращает имя текущего каталога (каталога по умолчанию): d - номер устройства (0 - устройство по умолчанию, 1 - диск А, 2- диск В и т. д.); s - переменная типа String, в которой возвращается путь к текущему каталогу на указанном диске
function IncludeTrailingBackslash(const S:String): String; | Возвращает полный маршрут доступа к файлу с ведомым символом “\”
Function lOResult: In teger; | Возвращает условный признак последней операции ввода-вывода
function IsPathDelimiter(const S: String; Index: Integer): Boo lean; | Возвращает True, если в строке S символ index есть “\”.
function MatchesMask (const Filename, Mask: String): Boolean; | Возвращает True, если имя FileName соответствует групповому имени Mask
Procedure MkDir(Dir: String) ; | Создает новый каталог на указанном диске: Dir маршрут поиска каталога. Последним именем в маршруте, т.е. именем вновь создаваемого каталога, не может быть имя уже существующего каталога
procedure ProcessPath (const EditText: String; var Drive: Char; var DirPart: String; var FilePart: String) ; | Возвращает имя диска, маршрут поиска и имя файла в переменных Drive, DirPart и FilePart соответственно. EditText - полное имя файла
Procedure Rename(var F; NewName: String); | Переименовывает файл F; NewName -' строковое выражение, содержащее новое имя файла. Перед выполнением процедуры необходимо закрыть файл только для не типизированных файлов и указывает раз мер блока данных
Procedure Resetfvar F: File; RecSize: Word]); | Открывает существующий файл. RecSize имеет смысл
Procedure Rewrite(varFile [; Recsize: ,Word]) ; | Создает новый файл. Recsize имеет смысл только для не типизированных файлов и указывает размер блока данных
Procedure RmDir(Dir:String); | Удаляет каталог Dir. Удаляемый каталог должен быть пустым, т. е. не содержать файлов или имен каталогов нижнего уровня

С каждой файловой переменой в момент открытия файла связывается структура данных, которая в числе прочих содержит поле Handle - системный дескриптор файла. Это поле следует использовать При обращении К функциям FileGetTime и FileSetTime. Эти функции, а также функция FileAge и поле Time записи TsearchRec (см. ниже) оперируют системным форматом времени-даты, который можно перевести в стандартный тип дата-время с помощью функции FileDateToDateTime (функция DateTimeToFileDate Осуществляет обратное преобразование).

Подпрограммы FindFirst, FindNext И FindClose позволяют получить

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

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

* означает, что на месте этого символа может стоять сколько угодно (в том числе ноль) разрешенных символов имени или расширения файла;

? означает, что на месте этого символа может стоять один из разрешенных символов.

Например:

* . * выбирает все файлы из каталога;

с* . * выбирает все файлы с именами, начинающимися на с

fcl.pas, ccl2345, с.dat И Т.Д.);

а? . dat выбирает имена файлов типа ао. dat, az. dat и т. д.

Маске выбора может предшествовать маршрут поиска файлов. Например, команда

C:\Dir\SubDir\*.pas

означает выбирать все файлы с расширением .раз из каталога Sub-Dir, находящегося на диске с; каталог subDir зарегистрирован в каталоге верхнего уровня Dir, который, в свою очередь, входит в корневой каталог. Если маршрут не указан, файлы ищутся в текущем каталоге.

Параметр Attr при обращении к FindFirst содержит двоичные разряды (биты), уточняющие, к каким именно файлам разрешен доступ. Вот как объявляются файловые атрибуты в модуле SysUtils:

const

faReadOnly = $01; // Только чтение

faHidden = $02; // Скрытый файл

faSysFile = $04; // Системный файл

faVolumeID = $08; // Идентификатор тома

faDirectory = $10; // Имя подкаталога

faArchive = $20; // Архивный файл

faAnyFile = $3F; // Любой файл

Комбинацией бит в этом байте можно указывать самые разные варианты, например $06 - выбирать все скрытые и/или системные файлы.

Результат работы процедуры FindFirst возвращается в переменной типа TSearchRec. Этот тип определяется следующим образом:

type

TSearchRec = record

Time : Integer;

Size : Integer;

Attr : Integer;

Name : TFileName;

ExcludeAttr: Integer;

FindHandle : THandle;

FindDate : Twin32FindDate;

end;

Здесь Attr - атрибуты файла (см. выше); Time - время и дата создания или последнего обновления файла в системном формате; size - длина файла в байтах; Name - имя и расширение файла; FindDate -содержит дополнительную информацию о файле (время создания, время последнего доступа).

Результат обращения к процедуре FindFist возвращается в значении типа integer, которое равно 0, если нет ошибок.

Следующая простая программа иллюстрирует способ использования функций PindFirst И FindNext. Программа выводит в окно

многострочного редактора mmoutput список всех файлов, маска выбора которых (и, возможно, маршрут поиска) указана в окне edInput:

procedure TfmExample.bbRunClick(Sender: TObject);

var

Mask: String;

SR: TSearchRec;

begin

Mask := edInput.Text;

if Mask = '' then Mask := '*.*';

mmOutput.Lines.Clear;

if FindFirst(Mask,faAnyFile,SR)=0 then

repeat

mmOutput.Lines.Add(SR.Name);

until FindNext(SR)<>0

FindClose(SR);

end;

Любое обращение к файлу в Object Pascal осуществляется через некоторый буфер, что необходимо для согласования внутреннего представления файлового компонента (записи) с принятым в ОС форматом хранения данных на диске. В ходе выполнения процедуры Flush все новые записи будут действительно записаны на диск. Процедура игнорируется, если файл был инициирован для чтения процедурой Reset.

Функция IOResuit досталась Object Pascal в наследство от Турбо Паскаля. Она используется следующим образом: перед фрагментом программы, в котором может возникнуть ошибка ввода/вывода, ставится директива {$!-}, отключающая автоконтроль операций ввода/вывода. После выполнения опасного участка автоконтроль включается вновь директивой {$!+} и вызывается функция IOResuit. Если операция завершилась успешно, функция возвращает ноль. Следует помнить, что IOResuit становится доступной только при отключенном автоконтроле ошибок ввода/вывода. Если автоконтроль отключен, а операция ввода-вывода привела к возникновению ошибки, устанавливается флаг ошибки и все последующие обращения к вводу/выводу блокируются, пока не будет вызвана функция IOResuit. Вот как можно проверить существование файла с использованием функции IOResuit:

var

F: File;

begin

AssignFile(F,'MyFile') ;

{$!-} // Отключаем автоконтроль Reset(F);

// Пытаемся открыть файл

{$!+} // Включаем автоконтроль

if IOResult=0 then

// Файл существует

else

// Файл не существует

end;

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

var

Fi : TextFile; // Исходный файл

Fo : TextFile; // Отредактированный файл

Name : String;

Name_bak : String;

const

ВАК = '.bak';

begin

// Получаем в name_bak имя файла с расширением .ВАК:

Name_bak := Name - ExtractFileExt(Name) + ВАК;

// Проверяем существование исходного файла:

AssignFile(Fi,Name) ;

try

Reset(Fi) ;

except

Halt; // Завершаем программу: файла не существует

end;

CloseFile(Fi) ;

// Проверяем существование .ВАК-файла:

AssignFile(Fo,Name_bak) ;

try

Reset(Fo) ;

// Файл .ВАК существует:

CloseFile(Fo); // Закрываем его Erase(Fo)

// и уничтожаем

except

end;

// Проверки закончены, подготовка к работе:

Rename(Fi,Name__bak) ;

Reset-(Fi) ;

AssignFile(Fo,Name);

Rewrite(Fo);

end.

Обратите внимание: проверка на существование файла вак в данном примере необходима, так как обращение

Rename(Fi,Name_bak);

вызовет ошибку в случае, если такой файл существует.

11.3. ТЕКСТОВЫЕ ФАЙЛЫ

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

Текстовый файл трактуется в Object Pascal как совокупность строк переменной длины. Доступ к каждой строке возможен лишь последовательно, начиная с первой. При создании текстового файла в конце каждой строки ставится специальный признак eoln (End Of LiNe - конец строки), а в конце всего файла - признак eof (End Of File - конец файла). Эти признаки можно протестировать одноименными логическими функциями (см. ниже). При формировании текстовых файлов используются следующие системные соглашения:

eoln - последовательность кодов #13 (cr) и #10 (lf);

EOF -КОД #26.

В Delphi 6 при создании межплатформенных приложений признаком конца строки считается один символ LF(#10)

Для доступа к записям применяются процедуры Read, ReadLn, write, writebn. Они отличаются возможностью обращения к ним с переменным числом фактических параметров, в качестве которых могут использоваться символы, строки и числа. Первым параметром в любой из перечисленных процедур должна стоять файловая

переменная. Обращение осуществляется к дисковому файлу, связанному С Переменной Процедурой AssignFile.

Таблица 11.2. Подпрограммы для работы с текстовыми файлами

Function Eoln(var F: TextFile): Boolean; | Тестирует маркер конца строки и возвращает True, если конец строки достигнут
Procedure Read(var F: TextFile; V1 [, V2,...,Vn ]); | Читает из текстового файла последовательность символьных представлении переменных Vi типа char. String, а также любого целого или вещественного типа, игнорируя признаки EOLN
Procedure ReadLn (var F: TextFile; [VI [, V2, ...,Vn]]); | Читает из текстового файла последовательность символьных представлении переменных Vi типа char, String, а также любого целого или вещественного типа с учетом границ строк
Function SeekEof(var F: Text): Boolean; | Пропускает все пробелы, знаки табуляции и маркеры конца строки eoln до маркера конца файла eof или до первого значащего символа и возвращает True, если маркер eof обнаружен
Function SeekEoln (var F: TextFile): Boolean; | Пропускает все пробелы и знаки табуляции до маркера конца строки eoln или до первого значащего символа и возвращает True, если маркер обнаружен
Procedure Write(var F: Text; PI [, P2,..., Pn] ) ; | Записывает символьные представления параметров Pi в текстовый файл
Procedure WriteLn (var F: Text; [PI [, P2, ..., Pn]]); | Записывает символьные представления параметров Pi и при знак конца строки eoln в текстовый файл

Процедура Read предназначена для последовательного чтения из текстового файла символьных представлений переменных Vi. При чтении переменных типа char выполняется чтение одного символа и присваивание считанного значения переменной. Если перед выполнением чтения указатель файла достиг конца очередной строки, то результатом чтения будет символ cr (код #1з), а если достигнут конец файла, то символ eof (код #26). Процедуру Read не рекомендуется использовать для ввода переменных типа string, т. к. она не способна “перепрыгнуть” через разделитель строк eoln и читает только первую строку текстового файла. Для ввода последовательности строк нужно использовать процедуру ReadLn (см. ниже).

Следующая программа “зависнет”, т. к. никогда не будет прочитана вторая строка файла:

procedure TfmExample.bbRunClick(Sender: TObject);

var

F: TextFile;

S: String;

begin

AssignFile(F,'example.pas');

Reset(F);

while- not EOF(F) do

begin

Read(P,S); // Ошибка! Бесконечный цикл!

mmOutput.Lines.Add(S)

end;

CloseFile(F)

end;

При вводе численных переменных процедура Read вначале выделяет подстроку во входном потоке по следующему правилу: все ведущие пробелы, символы табуляции и маркеры конца строк eoln пропускаются; после выделения первого значащего символа, наоборот, любой из перечисленных символов или символ eof служат признаком конца подстроки. Выделенная таким образом подстрока затем рассматривается как символьное представление числовой константы соответствующего типа и преобразуется во внутреннее представление, а полученное значение присваивается переменной. Если в подстроке был нарушен требуемый формат представления числовой константы, возникает исключительная ситуация. Если при пропуске ведущих пробелов встретился символ eof, переменная получает значение 0. В Object Pascal не предусмотрен ввод шестнадцатеричных констант.

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

const

N = 1000; // Максимальная длина ввода

var

F : TextFile;

М : array [1..N] of Real;

i : Integer;

begin

AssignFile(F, 'prog.dat');

Reset(F);

i := 1;

while not EOF(f) and (i <= N) do

begin

Read(F, M[i]);

inc (i) end;

CloseFile(F) ;

end.

Процедура ReadLn идентична процедуре Read за исключением того, что после считывания последней переменной оставшаяся часть строки до маркера eoln пропускается, поэтому следующее обращение к ReadLn начинается с первого символа новой строки. Кроме того, эту процедуру можно вызвать без параметров vi„ что приведет к пропуску всех символов текущей строки вплоть до eoln. Если в обработчике bbRunClick (см. пример на предыдущей странице) заменить Read на ReadLn, программа выведет в окно компонента mmout-put все строки из текстового файла example . раs.

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

OutExpr [ : MinWidth [ : DecPlaces ] ]

Здесь OutExpr - выводимое выражение; MinWidth, DecPlaces - выражения типа integer (квадратные скобки означают возможность отсутствия заключенных в них параметров). Параметр MinWidth, если он присутствует, указывает минимальную ширину поля, в которое будет записываться символьное представление значения OutExpr. Если символьное представление имеет меньшую длину, чем MinWidth, оно будет дополнено слева пробелами, если большую длину, то MinWidth игнорируется и в файл помещается необходимое число символов. Параметр DecPlaces задает количество десятичных знаков в дробной части вещественного числа. Он может использоваться только совместно с MinWidth и только по отношению к выводимому выражению одного из вещественных типов.

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

При выводе логических выражений в зависимости от их значения выводятся слова True или False. (Ввод логических констант процедурами Read или ReadLn не предусмотрен.)

Вещественные числа выводятся в экспоненциальном формате, если не указан параметр Decpiaces, в противном случае выбирается формат представления числа с фиксированной точкой. Если подпараметр MinWidth опущен, принимается его значение по умолчанию (23). Если Minwidth меньше 10, считается, что он равен 10. Если подпараметр Decpiaces равен нулю, ни дробная часть числа, ни десятичная точка не выводятся. При отрицательном значении Decpiaces этот параметр игнорируется и число выводится в экспоненциальном формате с учетом Minwidth. Если значение Decpiaces больше 18, принимается значение 18. Следует учесть, что при указании подпараметра Decpiaces вещественное число всегда будет выводиться в формате с фиксированной точкой и требуемым количеством знаков в дробной части, даже если значение подпараметра Minwidth окажется недостаточным для размещения целой части: в этом случае значение Minwidth автоматически увеличивается.

Процедура writebn полностью идентична процедуре Write за исключением того, что выводимая последовательность символов автоматически завершается маркером eoln (свое название процедура получила от Write Line - писать строку). При вызове WriteLn можно опускать параметры Vi-в этом случае в файл передается пустая строка.

11.4. ТИПИЗИРОВАННЫЕ ФАЙЛЫ

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

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

Таблица 11.3. Подпрограммы для работы с типизированными файлами

Function FilePos (var F): Longint; | Возвращает текущую позицию в файле, т. е. номер компонента, который будет обрабатываться следующей операцией ввода-вывода
Function FileSize (var F): Longint; | Возвращает количество компонентов файла. Чтобы переместить указатель в конец типизированного файла, можно написать: seek (FileVar, FileSize(FileVar));
Procedure Seek(var F; N: Longint) ; | Смещает указатель файла F к требуемому компоненту: n - номер компонента файла (первый компонент файла имеет номер 0)
Procedure Read(var F, VI,..., Vn) ; | Читает данные из типизированного файла f: v< - переменные такого же типа, что и компоненты файла
Procedure Write(var F,P1, ...,Pn) | Записывает данные в типизированный файл р: Pi - выражения такого же типа, что и компоненты файла

11.5. НЕТИПИЗИРОВАННЫЕ ФАЙЛЫ

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

При инициации нетипизированного файла процедурами Reset или Rewrite можно указать длину записи нетипизированного файла в байтах. Например, так:

var

F: File;

begin

AssignFile(F,'myfile.dat');

Reset(f,512);

end.

Длина записи нетипизированного файла указывается вторым параметром при обращении к процедурам Reset или Rewrite, в качестве которого может использоваться выражение типа Longint. Если длина записи не указана, она принимается равной 128 байтам.

Object Pascal не накладывает каких-либо ограничений на длину записи нетипизированного файла за исключением требования положительности и ограничения максимальной длины 2 Гбайт (для Delphi 1 длина записи ограничивается 65535). Для обеспечения максимальной скорости обмена данными рекомендуется задавать длину, которая была бы кратна длине физического сектора дискового носителя информации (512 байт). Однако операции обмена данными с дисковыми устройствами в среде Windows кэшируются, т. е. осуществляются через промежуточный буфер памяти, поэтому обычно задают Recsize = 1, что позволяет обмениваться с файлом блоками любой длины начиная с одного байта.

При работе с нетипизированными файлами могут применяться все процедуры и функции, доступные типизированным файлам, за исключением Read и write, которые заменяются соответственно высокоскоростными Процедурами BlockRead И BlockWrite:

Procedure BlockRead(var F: File; var Buf; Count: Integer [;

var AmtTransferred: Integer]) ;

Procedure BlockWrite(var F: File; var Buf; Count: Integer [;

var AmtTransferred: Integer]);

Здесь Buf - буфер: имя переменной, которая будет участвовать в обмене данными с дисками; count - количество записей, которые должны быть прочитаны или записаны за одно обращение к диску;

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

За одно обращение к процедурам может быть передано до count*RecSize байт, где RecSize - длина записи нетипизированного файла. Передача идет начиная с первого байта переменной Buf. Программист должен позаботиться о том, чтобы длина внутреннего представления переменной Buf была достаточной для размещения всех count*Rec3ize байт при чтении информации с диска. Если при чтении указана переменная недостаточной длины или если в процессе записи на диск не окажется нужного свободного пространства, возникнет ошибка ввода-вывода, которую можно заблокировать, указав необязательный параметр AmtTransferred.

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

11.6. СРЕДСТВА WINDOWS ДЛЯ РАБОТЫ С ФАЙЛАМИ

Операционная система Windows имеет собственные средства работы с файлами, которые становятся доступны программе Delphi после ссылки на модуль Windows. Поскольку файловые средства Object Pascal реализуют подавляющее большинство программных запросов, в табл. 11.4 приводится лишь краткая информация о назначении соответствующих API-функций. За подробной информацией обращайтесь к справочной службе в файлах WIN32. hlp или WIN32SDK.HLp (для версии б

Эти файлы расположены В каталоге Programs Files | Common Files |

Borland Share | MSHelp).

Таблица 11.4. Средства Windows для работы с файлами

AreFileApisANSI | Определяет, будут ли файловые операции использовать кодовую страницу ansi
CopyFile | Копирует содержимое одного файла в другой
CreateDirectory | Создает новый каталог на диске Создает новый каталог на диске
CreateDirectoryEx | Создает новый или открывает существующий файл
CreateFile | Связывает асинхронный ввод/вывод с файлом, что дает возможность получить извещение о завершении асинхронной операции
CreateIoCompletionPort | Определяет, переопределяет или уничтожает определение логического устройства ms-dos
DefineDosDevice DeleteFile | Уничтожает файл (в табл. 8.1 указан более удобный интерфейс вызова этой API-функции) операций
FileIOCompletionROutine | Связывает асинхронный ввод/вывод с подпрограммой для слежения за окончанием асинхронных
FindClose | Освобождает память, выделенную функциям Find FirstFile - FindNextFile
FindCloseChangeNotification | Освобождает память, выделенную функциям Find-FirstChangeNotification FindNextChangeNotification
FindFirstChangeNotification | Требует от Windows известить программу об изменении состояния каталога
FindFirstFile | Ищет первый файл из группы файлов
FindNextChangeNotifi- cation | Требует от Windows известить программу об очередном изменении состояния каталога
FindNextFile | Ищет следующий файл
FlushFileBuffers | Очищает файловый буфер
GetBinaryType | Определяет, является ли файл исполняемым и, если является, возвращает его тип
GetCurrentDirectory | Возвращает умалчиваемый каталог
GetDiskFreeSpace | Возвращает свободное дисковое пространство в байтах
GetDriveType | Возвращает тип диска (сменный, удаленный и т. п.)
GetFileAttributes | Возвращает атрибуты файла
GetFileInformationBy Handle | Возвращает системную информацию о файле
GetFileSize | Возвращает размер файла в байтах
GetFileType | Возвращает тип файла
GetFullPathName | По короткому имени файла в формате ms-dos и windows 16 возвращает его полное имя в формате windows 32
GetLogicalDrives | Возвращает битовую 32-разрядную маску, определяющую текущий диск
GetLogicalDriveStrings | Возвращает список всех дисков, зарегистрированных в Windows
GetQueuedCompletion Status | Требует от Windows выполнения асинхронного ввода/вывода и приостанавливает работу программы до завершения операций
GetShortPathName | Возвращает короткое имя файла в формате MS-DOS^Windows 16)
GetSystemDirectory | Возвращает имя системного каталога Windows для размещения библиотек, драйверов, шрифтов и т. п.
GetTempFileName | Возвращает уникальное имя файла для временного хранения данных
GetTempPath | Возвращает маршрут поиска каталога, предназначенного для хранения временно используемых файлов
Ge tVolumeIn fo rmat ion | Возвращает информацию о файловой подсистеме в целом и об указанном каталоге
GetWindowsDirectory | Возвращает полное имя каталога Windows для размещения прикладных программ, файлов инициализации, файлов помощи и т. п.
LockFile | Защищает файл от доступа к нему из других программ
LockFileEx | Устанавливает способ использования файла другими программами
MoveFile | Переименовывает файл или каталог (с подкаталогами)
MoveFileEx | Переименовывает файл
OpenFile | Открывает существующий файл
QueryDosDevice | Получает информацию об именах дисков (устройств), используемых в MS-DOS
ReadFile | Читает данные из файла
ReadFileEx | Реализует асинхронное чтение данных из файла
RemoveDirectory | Удаляет пустой каталог
SearchPath | Ищет файл в заданном каталоге (каталогах)
SetCurrentDirectory | Устанавливает умалчиваемый каталог
SetEndOfFile | Перемещает файловый указатель в конец файла
SetFileApisToANSI | Предписывает Windows использовать кодовую страницу ANSI при файловых операциях
SetFileApisToOEM | Предписывает Windows использовать кодовую страни . MS-DOS при файловых операциях
SetFileAttributes | Устанавливает атрибуты файла
SetFilePointer | Перемещает файловый указатель на нужную позицию
SetHandleCount | Устанавливает количество файлов, одновременно используемых программой
SetVolumeLabel | Устанавливает новую метку тома (носителя информации)
UnlockFile | Снимает с файла защиту, установленную функцией LockFile
UnlockFileEx | Снимает с файла защиту, установленную функцией LockFileEx
WriteFile | Записывает данные в файл
WriteFileEx | Реализует асинхронную запись в файл

11.7. ОТОБРАЖЕНИЕ ФАЙЛОВ В ПАМЯТЬ

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

11.7.1. Создание/открытие файла

Вначале файл создается обращением к функции

function FileCreate(FileName: String): Integer;

или открывается с помощью

function FileOpen(const FileName:

String; Mode: LongWord): Integer;

В обеих функциях FileName - имя файла, возможно, с маршрутом доступа. Параметр Mode определяет режим доступа к файлу и может принимать одно из следующих значений: fmOpenRead - только чтение; fmOpenWrite - только Запись; fmOpenReadWrite - чтение и запись. с помощью операции or эти константы можно комбинировать с одной из следующих регулирующих совместный доступ к файлу нескольких Программ: fmShareExclusive - совместный доступ Запрещен; fmShareDenyWrite - Другим Программам запрещается запись; fmShareDenyRead - другим программам запрещается чтение; fmSchareDenyNone - совместный доступ неограничен. Обе функции возвращают дескриптор созданного (открытого) файла или 0, если операция оказалась неуспешной.

11.7.2. Создание объекта отображения

На втором этапе создается объект отображения в память. Для этого используется такая функция:

function CreateFileMapping(hFile: THandle; IpFileMapping-Attributes: PSecurityAttributes; flProtect, dwMaximumSize-High, dwMaximumSizeLow: DWord; IpName: PChar): THandle;

Здесь hFile - дескриптор файла; ipFileMappingAttributes - указа-тель на структуру, в которой определяется, может ли создаваемый объект порождать дочерние объекты (обычно не может - nil); flProtect -определяет тип защиты, применяемый к окну отображения файла (см.ниже); dwMaximumSizeHigh, dwMaximumSizeLow - соответственно Старшие и младшие 32 разряда размера файла; если вы будете отображать файлы длинной до 4 ГбаЙТ, поместите В dwMaximumSizeHigh 0, а В dwMaximumSizeLow - длину файла; если оба параметра равны 0, размер окна отображения равен размеру файла; ipName - имя объекта отображения или nil.

Параметр flProtect задает тип защиты, применяемый к окну просмотра файла, и может иметь одно из следующих значений: page_readonly - файл можно только читать (файл должен быть создан или открыт В режиме fmOpenRead); PAGE_READWRITE - файл можно читать и записывать в него новые данные (файл открывается в режиме fmOpenReadWrite); PAGE_WRITECOPY - файл открыт для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (отображенные файлы могут разделяться программами, в этом режиме каждая программа сохраняет изменения в отдельной области памяти или участке файла подкачки); файл открывается В режиме fmOpenReadWrite или fmOpenWrite; этот тип защиты нельзя использовать в Windows 95/98. С помощью операции or к параметру fiprotect можно присоединить такие атрибуты:

sec_commit - выделяет для отображения физическую память или участок файла подкачки; sec_image - информация об атрибутах отображения берется из образа файла; sec_nocashe - отображаемые данные не кэшируются и записываются непосредственно на диск;

sec_reserve - резервируются страницы раздела без выделения физической памяти. Функция возвращает дескриптор объекта отображения или 0, если обращение было неудачным.

11.7.3. Создание окна просмотра

Наконец, на третьем этапе создается окно просмотра, т. е. собственно отображение данных в адресное пространство программы:

function MapViewOfFile(hFileMappingObject: THandle; dwDesiresAccess: DWord; dwFileOffsetHigh, dwFileIffsetLow, dwNumberOfBytesToMap: DWord): Pointer;

Здесь hFileMappingObject -дескриптор объекта отображения; dwDesiresAccess - определяет способ доступа к данным и может иметь одно из следующих значений: file_map_write - разрешает чтение и запись, При ЭТОМ В функции CreateFileMapping должен использоваться атрибут page_readwrite; file_map_read - разрешает только чтение, в функции CreateFileMapping должен использоваться атрибут

PAGE_READONLY или PAGE_READWRITE; FILE_MAP_ALL_ACCESS - тоь же, что и

file_map_write; file_map_copy - данные доступны для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти; в функции CreateFileMapping должен использоваться атрибут page_writecopy; dwFileOffsetHigh, dwFileIffsetLow -определяют соответственно старшие и младшие разряды смещения от начала файла, начиная с которого осуществляется отображение;

dwNumberOfBytesToMap - определяет длину окна отображения (0 - длина равна длине файла). Функция возвращает указатель на первый байт отображенных данных или nil, если обращение к функции оказалось неуспешным.

11.7.4. Освобождение ресурсов отображения

После использования отображенных данных ресурсы окна отображения нужно освободить функцией function UnMapViewOfFile(IpBaseAddress: Pointer): BOOL; единственный параметр обращения к которой должен содержать адрес первого отображенного байта, т. е. адрес, возвращаемый функцией MapViewOfFile. Закрытие объекта отображения и самого файла осуществляется обращением к функции

function CloseHandle(hObject: THandle).

11.7.5. Пример использования

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

Программа создает дисковый файл, состоящий из 100000 случайных вещественных чисел (длину файла можно выбрать другой, если изменить значение редактора длина массива). Файл с именем test.dat создается путем отображения файла в память (кнопка память) и традиционным способом (кнопка Файл). В обоих случаях показывается время счета (процессор 400 МГц, память 64 Мбайт). Чем больше частота и память, тем больше будет разница во времени.

Рис. 11.1. Окно демонстрационнойпрограммы

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Spin;

type

TFormI = class(TForm)

btMem: TButton;

btFile: TButton;

se: TSpinEdit;

Labell: TLabel;

pb: TProgressBar;

Label2: TLabel;

IbMem: TLabel;

IbFile: TLabel;

procedure btMemClick(Sender: TObject);

procedure btFileClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.btMemClick(Sender: TObject);

// Создание файла методом его отображения

type

PReal = ^Real;

var

HFile, HMap: THandle;

AdrBase, AdrReal: PReal;

k: Integer;

FSize: Cardinal;

BegTime: TDateTime;

begin

BegTime := Time; // Засекаем время пуска

// Готовим ProgressBar:

pb.Max := se.Value;

pb.Position := 0;

pb.Show;

FSize := se.Value * SizeOf(Real); // Длина файла HFile := FileCreate('test.dat'); // Создаем файл

if HFile = 0 then // Ошибка: возбуждаем исключение

raise Exception.Create('Ошибка создания файла');

try

// Отображаем файл в память HMap := CreateFileMapping(

HFile, NIL, PAGE_READWRITE, 0, FSize, NIL);

if HMap = 0 then // Ошибка: возбуждаем исключение

raise Exception.Create('Ошибка отображения файла');

try

// Создаем окно просмотра:

AdrBase := MapViewOfFile(

HMap, FILE_MAP_WRITE, О, О, FSize) ;

if AdrBase = NIL then // Ошибка: возбуждаем исключение

raise Exception.Create('Невозможно просмотреть файл');

// Сохраняем начальный адрес для правильной ликвидации

// окна просмотра:

AdrReal := AdrBase;

for k := 1 to se.Value do

begin

AdrReal^ := Random; // Помещаем в файл новое число

// Перед наращиванием текущего адреса необходимо

// привести его к типу Integer или Cardinal:

AdrReal := Pointer(Integer(AdrReal) + SizeOf(Real));

IbMem.Caption := IntToStr(k);

pb.Position := k;

Application.ProcessMessages;

end;

// Освобождаем окно просмотра:

UnmapViewOfFile(AdrBase) finally

// Освобождаем отображение CloseHandle(HMap)

end

finally

// Закрываем файл CloseHandle(HFile)

end;

// Сообщаем время счета pb.Hide;

IbMem.Caption := TimeToStr(Time-BegTime)

end;

procedure TFormI.btFileClick(Sender: TObject) ;

// Создание файла обычным методом var

F: File of Real;

k: Integer;

BegTime: TDateTime;

R: Real; // Буферная переменная для обращение к Write

begin

BegTime := Time; // Засекаем начальное время счета // Готовим ProgressBar:

pb.Max := se.Value;

pb.Position := 0;

pb.Show;

// Создаем файл:

AssignFile(F, 'test.dat');

Rewrite(F);

for k := 1 to se.Value do

begin

R := Random; // Параметрами обращения к Write

Write(F, R); // могут быть только переменные

IbFile.Caption := IntToStr(k);

pb.Position := k;

Application.ProcessMessages;

end;

CloseFile(F) ;

pb.Hide;

IbFile.Caption :=TimeToStr(Time-BegTime)

end;

end.

11.8. ОБЪЕКТНАЯ МОДЕЛЬ РАБОТЫ С ФАЙЛАМИ

В Delphi используется абстрактный класс TStream (поток данных), который является основой для работы с файлами как с объектами. В случае объектов совершенно не важно, что именно является носителем информации - дисковый ли файл, ленточный носитель или оперативная память. В специализированных потомках TStream определены стандартные методы Read, write и seek, открывающие полный доступ к файловому объекту. Сам объект создается конструктором и уничтожается деструктором.

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

В следующем примере файл с именем, хранящимся в Editl.Text,

копируется В файл Edit2 . Text.

procedure TFormI.CopyFileClick(Sender:TObject) ;

var

Streami, Stream2: TStream;

begin

Streami:=TFileStream.Create(Editi.Text,

fmOpenRead or fmShareDenyWrite) ;

try

Stream2 :=TFileStream.Create(Edit2.Text,

fmOpenCreate or fmShareDenyRead);

try

Stream2.CopyFrom(Streami, Streami.Size);

finally

Stream2.Free;

finally

Stream1.Free

end;

Абстрактный класс т Stream лишь декларирует ключевые методы Read и write, которые перекрываются в его наследниках для специализации операций:

  • TFileStream - обмен данными с файлом;
  • TStringStream - обмен данными с длинной строкой (в памяти);
  • TMemoryStream - обмен данными с памятью;
  • TBLOBStream - чтение/запись полей данных типа BLOB (Binary Large Object - большой двоичный объект;
  • TWinsocketStream - обмен данными с сокетным подключением;
  • TOLEStream - обмен данными в формате OLE.

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

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

  • Seek - пропускает нужное количество байт от текущей позиции потока;
  • ReadBuffer - читает из буфера в поток нужное количество байт;
  • Readcomponent - читает свойства компонента из потока;
  • WriteBuffer - записывает в поток содержимое буфера заданной длины;
  • WriteComponent - записывает в поток свойства компонента.

Следующий обработчик bbRunciick выведет в редактор mmOutput свойства кнопки bbRun:

procedure TfmExample.bbRunClick(Sender: TObject);

var

MemSourceStream, MemDestStream: TMemoryStream;

begin

MemSourceStream := TMemoryStream.Create;

try

MemDestStream := TMemoryStream.Create;

try

MemSourceStream.WriteComponent(bbRun);

MemSourceStream.Seek(0, soFromBeginning) ;

ObjectBinaryToText(MemSourceStream, MemDestStream);

MemDestStream.Seek(0, soFromBeginning) ;

mmOutput.Lines.LoadFromStream(MemDestStream) finally

MemDestStream.Free

end;

finally

MemSourceStream.Free

end;

end;

Комментарий к программе

Вначале свойства компонента записываются в поток MеmSourceStream. Чтобы полученным таким образом двоичным данным придать “читабельный” вид, используется процедура ObjectBinaryToText, которая преобразует данные из MemSourceStream и помещает иx в MemDestStream. Содержимое этого второго потока и выводится

В окне mmOutput. Вид окна работающей программы представлен на рис. 11.2.

Рис. 11.2. Свойства компонента bbRun


Глава 12. МОДУЛИ

  • Глава 12. МОДУЛИ
    • СТРУКТУРА МОДУЛЕЙ
    • ЗАГОЛОВОК МОДУЛЯ И СВЯЗЬ МОДУЛЕЙ ДРУГ С ДРУГОМ
    • ИНТЕРФЕЙСНАЯ ЧАСТЬ
    • ИСПОЛНЯЕМАЯ ЧАСТЬ
    • ИНИЦИИРУЮЩАЯ И ЗАВЕРШАЮЩАЯ ЧАСТИ
    • ДОСТУП К ОБЪЯВЛЕННЫМ В МОДУЛЕ ОБЪЕКТАМ
    • ТИПЫ МОДУЛЕЙ В DELPHI

Глава 12

МОДУЛИ

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

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

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

Роль модулей в Delphi не исчерпывается только механизмом раздельной компиляции. Delphi связывает с каждым включаемым в программу окном собственный модуль и таким способом локализует все свойства окна в отдельной программной единице. Как мы уже выяснили (см. п. 5.2), основная программа (файл проекта dpr) весьма специфична: она содержит перечень всех используемых в программе модулей и несколько исполняемых операторов, обеспечивающих создание нужных окон и связь программы с Windows. Вся основная работа программы управляется кодом, содержащимся в модулях.

12.1. СТРУКТУРА МОДУЛЕЙ

Модуль имеет следующую структуру:

Unit <имя>;

interface

<интерфейсная часть> implementation

<исполняемая часть> initialization <нициирующая часть> finalization <завершающая часть>

end.

Здесь unit - зарезервированное слово (единица); начинает заголовок модуля; <имя> - имя модуля (правильный идентификатор); interface - зарезервированное слово (интерфейс); начинает интерфейсную часть модуля; implementation - зарезервированное слово (выполнение); начинает исполняемую часть; initialization -зарезервированное слово (инициация); начинает инициирующую часть модуля; finalization - зарезервированное слово (завершение);

начинает завершающую часть модуля; end - зарезервированное слово - признак конца модуля.

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

12.2. ЗАГОЛОВОК МОДУЛЯ И СВЯЗЬ МОДУЛЕЙ ДРУГ С ДРУГОМ

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

Unit Global;

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

Uses <сп.модулей>

Здесь uses - зарезервированное слово (использует); <сп.модулей> -список модулей, с которыми устанавливается связь; элементами списка являются имена модулей, отделяемые друг от друга запятыми, например:

Uses Windows, SysUtils, MyUnit;

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

зарезервированным СЛОВОМ interface, либо Сразу за словом implementation, либо, наконец, и там, и там (т. е. в модуле допускаются два предложения uses).

12.3. ИНТЕРФЕЙСНАЯ ЧАСТЬ

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

Unit Cmplx;

interface type

Complex = record

re,im: Real end;

Function AddC(x,y: Complex): Complex;

Function MulC(x,y: Complex): Complex;

Если теперь в другом модуле написать предложение Uses Cmplx; то в нем станут доступными тип Сomplex и две процедуры - AddС и МulС из модуля Cmplx.

12.4. ИСПОЛНЯЕМАЯ ЧАСТЬ

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

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

Unit Cmplx;

Interface type

Complex = record

re,im: real

end;

Function AddC(x,y: Complex): Complex;

Function MulC(x,y: Complex): Complex;

Implementation

Function AddC(x,y: Complex): Complex;

begin

end; Function MulC;// Вариант описания подпрограммы без

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

begin

end;

end.

Хотя допускается краткое объявление заголовка подпрограммы ( как в предшествующем примере—функции MulC), тем не менее использовать такую форму в серьезной программе не рекомендуется: перечень параметров непосредственно в заголовке подпрограммы облегчает чтения кода и понимания деталей реализации алгоритма.

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

Function AddC(x,z: Complex): Complex; begin

end;

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

12.5. ИНИЦИИРУЮЩАЯ И ЗАВЕРШАЮЩАЯ ЧАСТИ

Инициирующая и завершающая части чаще всего отсутствуют

Вместе с начинающим их словами initialization и finalization.

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

12.6. ДОСТУП К ОБЪЯВЛЕННЫМ В МОДУЛЕ ОБЪЕКТАМ

Пусть, например, мы создаем модуль, реализующий арифметику комплексных чисел (такая арифметика ни в стандартном Паскале, ни в Object Pascal не предусмотрена, но в Delphi 6 введен пользовательский вариант, который реализует действия над комплексными

Числами - см. п. 10.5 и модуль Source\Rtl\Common\VarCmplx.pas Каталога размещения Delphi). Арифметика комплексных чисел реализуется четырьмя функциями:

Unit Cmplx;

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

Interface

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

type

Complex = record

re,im: real end; Function AddC (x,y: Complex): Complex;

Function SubC (x,y: Complex): Complex;

Function MuiC (x,y: Complex): Complex;

Function DivC (x,y: Complex): Complex;

const

с : Complex = (re : 0.1; im : -1);

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

Implementation

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

Function AddC (x,y: Complex): Complex;

// Сложение комплексных чисел

begin

Result.re := x.re + y.re;

Result.im := x.im + y.im

end; //AddC

Function SubC (x,y: Complex): Complex;

// Вычитание комплексных чисел

begin

Result.re := x.re - y.re;

Result.im := x.im - y.im

end; //SubC

Function MulC (x,y: Complex): Complex;

// Умножение комплексных чисел

begin

Result.re := x.re * y.re - x.im * y.im;

Result.im := x.re * y.im + x.im * y.re

end; //MulC

Function DivC (x,y: Complex): Complex;

// Деление комплексных чисел

var

z: Real;

begin

z := sqr(y.re) + sqr(y.im);

// Защищаем программу от краха в случае, когда z=0:

try-Result, re := (x.re * у.re + x.im * y.im) / z;

Result.im := (x.re * y.im - x.im * y.re) / z;

except

Result.re := l.le309;

Result.im := l.le309;

end

end

{Div.C};

end.

Чтобы создать такой модуль, следует вызвать опцию File | New I unit[В Delphi 1 для этого используется опция File | New | Unit, в версиях 2...5 - опция File |

New и выбор пиктограммы на закладке New окна Репозитория.]. Текст модуля следует сохранить в файле cmplx.pas: имя файла должно совпадать с именем модуля - только в этом случае Delphi сможет автоматически найти модуль и следить за его обновлением.

После создания модуля его имя нужно упомянуть в предложении uses того модуля, в котором будут использоваться вновь созданные подпрограммы, типы, константы (в нашем модуле - тип complex, подпрограммы Addc, subc. Mule, Dive и константа с). Пусть, например, при каждом щелчке по кнопке bbRun учебной программы создается пара случайных комплексных чисел, над которыми осуществляются все четыре арифметических действия. Тогда обработчик bbRunClick мог бы быть таким:

implementation

uses Cmplx;

{$R *.DFM}

procedure Tform1.bbRunClick(Sender: TObject);

var

x,y,z: Complex;

procedure Output(Operation: Char);

//Осуществляет нужное действие и выводит результат в mmOutput

var

S: String;

begin

case Operation of

'+': z := AddC(x,y) ;

'-': z := SubC(x,y) ;

'*': z := MulC(x,y) ;

'/': z := DivC(x,y) ;

end;

S := '('+FormatFloat('+0.0000;-0.0000',x.re)+

FormatFloat('+O.OOOOj;-O.OOOOj',x.im)+')'

+0peration+
'('+
FormatFloat('+0.0000;-0.0000',у.re)+
FormatFloat('+0.OOOOj;-0.OOOOj',у.im)+'='+
FormatFloat('+0.0000;-0.0000',z.re)+
FormatFloat('+0.OOOOj;-0.OOOOj',x.im);

mmOutput.Lines.Add(S) ;

end; //Output

begin //bbRunClick

x.re := Random;

x.im := Random;

y.re := Random;

y.im := Random;

Output('+');

Output('-');

Output ('*');

Output ('/');

mmOutput.Lines.Add('');

end;

Обратите внимание на ссылку uses cmpix в самом начале исполняемой части - именно она делает доступными обработчику bbRunClick объекты модуля Cmplx. Эту ссылку можно вставить с помощью среды Delphi: выберите опцию File | Use Unit и в появившемся окне щелкните по имени модуля Сmplx.

12.7. ТИПЫ МОДУЛЕЙ В DELPHI

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

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

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

Модули динамических библиотек предназначены для создания широко используемых в Windows динамически связываемых библиотек DLL (Dynamic-Link Libraries). DLL служат универсальным средством согласования подпрограмм, написанных на разных языках программирования. В Windows содержится множество DLL, написанных на языке Си или на языке ассемблера, что ничуть не мешает Delphi-программам использовать их. Модули динамических библиотек предназначены для разработки DLL с помощью Object Pascal. Такие DLL затем смогут использовать программы, созданные с помощью других языков программирования.

Пакеты - это особым образом откомпилированные DLL, оптимизированные для совместного использования Delphi-программами, или средой Delphi, или и программами, и средой. В отличие от DLL пакеты могут хранить и передавать программе типы (включая классы) и данные. Они разработаны специально для хранения компонентов, разного рода экспертов, редакторов сложных свойств и т. п. Например, в пакете VCL60 .bpl содержатся основные компоненты Delphi.

Модули потоков предназначены для реализации так называемых потоков команд[ В Delphi существуют также потоки данных - см.п. 11.4.] - фрагментов программы, которые исполняются параллельно с другими фрагментами, разделяя с ними время процессора и остальные системные ресурсы. Механизм потоков используется в 32-разрядных Windows и не поддерживается в Delphi 1. К сожалению, в текущей реализации Delphi 32 потоки не могут связываться с собственными видимыми компонентами, так как библиотека визуальных компонентов VCL (Visual Component Library) не поддерживает работу с потоками. Вот почему модуль потока не имеет связанного с ним окна.


Глава 13. ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ (DLL)

  • Глава 13. ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ (DLL)
    • НАЗНАЧЕНИЕ
    • РЕАЛИЗАЦИЯ
    • ПРИМЕР
    • ИСПОЛЬЗОВАНИЕ
      • Статическая загрузка
      • Динамическая загрузка
      • Интерфейсный модуль
    • ВКЛЮЧЕНИЕ В БИБЛИОТЕКУ ФОРМ

Глава 13

ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ (DLL)

13.1. НАЗНАЧЕНИЕ

Динамически подключаемые библиотеки (далее DLL - Dynamic Link Library) представляют собой универсальный механизм интегрирования в вашу программу процедур и функций, написанных другими программистами и, в общем случае, на других, чем Object Pascal, языках программирования.

DLL реализуются в виде исполняемых модулей, содержащих готовые к работе процедуры, функции и/или ресурсы. С точки зрения программиста, есть много общего между DLL и обычными для Object Pascal модулями, т. к. в конечном счете и библиотеки, и модули поставляют подпрограммы, избавляющие программиста от написания собственного кода. Но есть и принципиальные отличия. Главным из них является то, что DLL не в состоянии поставлять в программу переменные, константы и типы, ведь создатели DLL могут использовать не типизированные языки программирования, например, язык ассемблера. В результате DLL не могут экспортировать в программу столь необходимые сегодня программисту классы - для этого используются пакеты.

Другим важным отличием является способ связывания экспортируемых подпрограмм с основной программой. Модули связываются с программой на этапе компоновки, т. е. статически. Если загружены две программы, использующие одни и те же модули, в памяти будут два экземпляра одинаковых фрагментов кода. В отличие от этого DLL подключаются к программе в момент ее исполнения, т. е. динамически. Если опять же две программы используют одну и ту же DLL, в памяти будет лишь один экземпляр разделяемого программами кода. Следует уточнить, что речь идет о физической памяти компьютера. Поскольку каждая программа получает в свое распоряжение виртуальное адресное пространство (подробнее о процессах и памяти говорится в гл. 14), в эти пространства будут отображаться столько образов DLL, сколько программ ее используют (см. в главе 11 раздел “Отображение файлов в память”).

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

13.2. РЕАЛИЗАЦИЯ

Для создания DLL в Object Pascal введено зарезервированное слово Library, которым должен начинаться текст библиотеки. За словом Library следует правильный идентификатор, но в отличие от объявления модуля он не обязан совпадать с именем файла: имя DLL определяется именем DLL-файла, а не идентификатором, следующим за Library.

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

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

Library MyLibrary;

Function MyFunc (...):...;

begin

end;

Procedure MyProc;

begin

end;

Exports

MyFunc, MyProc;

begin

end.

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

Помимо имени подпрограммы в заголовок DLL помещается также ее порядковый номер, точнее, присвоенный ей целочисленный индекс. Это позволяет вызывающей программе ссылаться не на имя, а на индекс подпрограммы и тем самым уменьшить затраты времени на установление с ней связи. Индекс присваивается подпрограмме по порядку ее появления в списках Exports: первая подпрограмма в первом списке получает индекс 0, следующая - 1 и т. д. Программист может изменить умалчиваемую индексацию и явно указать индекс подпрограммы, добавив за ее именем в списке Exports слово index и целое число без знака в диапазоне от 0 до 32767:

Expots

MyFunc index 1, MyProc index 2;

Программист может определить внешнее имя экспортируемой подпрограммы отличным от ее настоящего имени. Для этого в списке Exports добавляется слово name и внешнее имя в апострофах:

Exports

MyFunc index I name 'NEWFUNC';

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

Замечу, что в отличие от модулей Delphi не компилирует DLL автоматически в режимах make или build, т. к. справедливо рассматривает ее как другую программу, никак не связанную в момент компиляции с основной программой.

13.3. ПРИМЕР

Рассмотрим пример создания DLL, в котором иллюстрируются различные приемы объявления экспортируемых подпрограмм. Для примера выбран модуль cmpix, описанный в гл. 12. В его состав входят 4 процедуры, реализующие действия с комплексными числами. Вариант соответствующей DLL показан ниже.

Для создания заготовки библиотечного модуля выберите опцию меню File | New | Unit или в окне репозитория щелкните по пиктограмме Da. В ответ Delphi откроет специальное окно проекта с длинным комментарием, в котором указывается на необходимость вставить ссылку на модуль ShareMem, если библиотека экспортирует длинные строки в параметрах обращения к подпрограммам или как результат функций. Эта ссылка должна быть первой как в предложении uses библиотеки, так и в uses файла проекта программы, которая использует эту библиотеку. Если подпрограммы библиотеки экспортируют строки ShortString или PChar, ссылаются на ShareMem не обязательно. Сразу же сохраните проект под именем Сmpix, чтобы Delphi автоматически исправила имя библиотеки в предложении Library.

Library Cmpix;

uses

SysUtils, Classes;

{$R *.RES}

type

TComplex = record Re, Im: Real;

end; function AddC(x, y: TComplex): TComplex; stdcall;

begin

Result.Im := x.Im + y.Im;

Result.Re := x.Re + y.Re end;

function SubC(x, y: TComplex): TComplex;

stdcall;

begin

Result.Im := x.Im - y.Im;

Result.Re := x.Re - y.Re

end;

function MulC(x, у: TComplex): TComplex;

stdcall;

begin

Result.Re := x.Re * y.Re + x.Im * y.Im;

Result.Im := x.Re * y.Im - x.Im * y.Re

end;

function DivC(x, y: TComplex): TComplex;

stdcall;

var

z: Real;

begin

z := sqr(y.Re) + sqr(y.Im);

try

Result.Re := (x.Re * y.Re + x.Im * y.Im)/z;

Result.Im := (x.Re * y.Im - x.Im * y.Re)/z

except

Result.Re := le+309;

Result.Im := le+309

end

end;

Exports

AddC index 1 name 'ADDC' resident,

SubC index 2,

MulC index 3,

DivC index 4;

begin

end.

Обратите внимание: все функции нашей DLL используют соглашение stdcall, которое обеспечивает совместимость новых функций с функциями API Windows 32. Мы могли бы не указывать это соглашение; в этом случае компилятор использовал бы более эффективное соглашение register, но обращение к нашей DLL из программ, написанных на других языках программирования, в общем случае стало бы невозможным.

Если вы создали DLL для "внешнего" исользования (внеDelphi), объявляйте подпрограммы с директивой stdcall или safecall!

Для использования подпрограмм из DLL необходимо описать их как внешние, добавив за словом External имя библиотеки в апострофах:

Procedure MyProc; External 'MyDLL';

Как уже говорилось, подпрограмма вызывается по имени или по индексу. В нашем примере из библиотеки MyDLL вызывается подпрограмма с внешним именем 'мургос'. Если нужно сослаться на индекс подпрограммы, за именем библиотеки указывается слово index и индекс:

Procedure MyProc; External 'MyDLL' index 2;

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

Procedure MyProc; External 'MyDLL' Name 'ExtName';

В этом варианте предполагается, что экспортируется процедура с внешним именем ' ExtName '.

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

13.4. ИСПОЛЬЗОВАНИЕ

13.4.1. Статическая загрузка

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

type

TComplex = record Re, Im: Real;

end;

function ADDC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' ;

function SubC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' ;

function MulC(x, y: TComplex): TComplex; stdcall; External 'Cmplx' ;

function

 DivC(x, y: TComplex): TComplex;

  stdcall;

 External 'Cmplx';

procedure TfmExample.bbRunClick(Sender: TObject);

var

x,y,z: TComplex;

..... // Далее смотри текст обработчика bbRunClick в п.12.6 end; end.

Обратите внимание: библиотечная функция cmpixAdd имеет внешнее имя addc. Именно так (заглавными буквами) описана эта функция в приведенном выше примере. Если бы мы использовали function AddC(x, у: TComplex): TComplex; stdcall; External

'Cmplx';

компоновщик не смог бы ее идентифицировать.

13.4.2. Динамическая загрузка

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

Стандартных функций : LoadLibrary, GetProcAddress И FreeLibrary.

Следующий пример иллюстрирует технику такой загрузки DLL Cmplx:

type

TComplex = record Re, Im: Real;

end;

TComplexFunc = function (x, y: TComplex): TComplex;

stdcall;

procedure TfmExample.bbRunClick(Sender: TObject);

var

x,y,z: TComplex;

AddC,SubC,MulC,DivC: TComplexFunc;

Handle: LongWord;

procedure Output(Operation: Char);

....//Тело процедуры Output остается без изменений

end; //Output

begin //bbRunClick

// Загружаем библиотеку CMPLX.DLL Handle := LoadLibrary('Cmplx.dll');

if Handle = 0 then

begin

ShowMessage('He найдена библиотека CMPLX.DLL') ;

Halt end;

{Определяем адреса функций. Три первые вызываем по индексу, четвертую - по имени. При вызове по индексу младшее слово PChar должно содержать индекс, поэтому делаем приведение типов:} @AddC := GetProcAddress(Handle,PChar(Longint (1)));

PSubC := GetProcAddress(Handle,PChar(Longint(2)));

@MulC := GetProcAddress(Handle,PChar(Longint(3)));

@DivC := GetProcAddress(Handle,'DivC');

x.re := Random;

x.im := Random;

y.re := Random;

y.im := Random; Output('+');

Output('-');

Output ('*');

Output ('/');

mmOutput.Lines.Add('');

// Освобождаем библиотеку FreeLibrary(Handle)

end;

13.4.3. Интерфейсный модуль

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

Unit Complx;

Interface

type

TComplex = record Re, Im: Real;

end;

function AddC(x, y: TComplex): TComplex; stdcall;

External 'Cmplx' index 1;

function SubC(x, y: TComplex): TComplex; stdcall;

External 'Cmplx' index 2;

function MulC(x, y: TComplex): TComplex; stdcall;

External 'Cmplx' index 3;

function DivC(x, y: TComplex): TComplex; stdcall;

External 'Cmplx' index 4;

Implementation end.

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

При обращении к подпрограммам DLL, написанным на других языках программирования, может оказаться, что внешнее имя подпрограммы содержит символы, которые не могут содержаться в правильном идентификаторе Delphi. Например, язык C++ разрешает использовать в идентификаторах символ “@”. В этом случае (а также если вы хотите переименовать экспортируемую из DLL подпрограмму) именуйте подпрограмму любым правильным с точки зрения Delphi идентификатором и укажите истинное имя подпрограммы после слова name. Например:

function MyFunction: WordBool; stdcall;

external 'MyDLL' name '_MyFunction@12'

13.5. ВКЛЮЧЕНИЕ В БИБЛИОТЕКУ ФОРМ

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

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

Текст DLL

library DLLWithForm;

uses

SysUtils,

Classes,

DLLFormU in 'DLLFormU.pas' {DLLForm};

{$R *.RES}

exports

ShowModalForm, ShowForm, FreeForm;

begin

end.

Текст формы в DLL

unit DLLFormU;

interface

uses

Windows, Messages, SysUtils,Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;

type

TDLLForm = class (TForm) BitBtnl: TBitBtn;

BitBtn2: TBitBtn;

procedure FormClose(Sender: TObject; var Action: TCloseAction);

private

{ Private declarations }

CallForm: THandle; //Дескриптор вызывающей формы

public

{ Public declarations }

end;

// Объявление экспортируемых подпрограмм

function ShowModalForm: Integer;

procedure ShowForm(aHandle: THandle);

procedure FreeForm;

var

DLLForm: TDLLForm;

implementation

{$R *.DFM}

function ShowModalForm: Integer;

// Модальный вызов

begin

DllForm := TDllForm.Create(Application);

Result := DLLForm.ShowModal;

DLLForm.Free;

end;

procedure ShowForm(Appl, Form: THandle);

// Немодальный вызов

begin

Application.Handle := Appl; // Замена объекта

Application DllForm := TDllForm.Create(Application);

// Запоминаем дескриптор вызывающего окна для посылки

// ему сообщения о закрытии

CallForm := Form;

DLLForm.Show

end;

procedure FreeForm;

// Уничтожение формы

begin

DLLForm.Free

end; procedure TDLLForm.FormClose(Sender: TObject;

var Action: TCloseAction);

begin

if CallFormoO then

SendMessage(CallForm, wm_User, 0, 0)

end;

end.

Текст вызывающей программы

unit TestMainU;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

TTestMain = class (TForm)

Buttoni: TButton; // Открыть в модальном режиме

Button2: TButton; // Открыть в немодальном режиме

Button3: TButton; // Закрыть окно

Label I: TLabel;

procedure ButtonlClick(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure ButtonSClick(Sender: TObject);

private

{ Private declarations } public

{ Public declarations }

procedure WMUser(var Msg: TMessage);

message WM_USER;

end;

var

TestMain: TTestMain;

implementation

{$R *.DFM}

function ShowModalForm: Integer;

External 'DLLWithForm';

procedure ShowForm(Appl, Form: THandle);

External ' DLLWithForm' ;

procedure FreeForm;

External 'DLLWithForm';

procedure TTestMain.ButtonlClick(Sender: TObject);

// Модальный вызов

begin

Button2.Enabled := False;

labell.Caption := 'ModalResult = '+IntToStr(ShowModalForm);

labell.Show; // Показываем результат вызова

Button2.Enabled := True

end;

procedure TTestMain.Button2Click(Sender: TObject);

// Немодальный вызов

begin

Buttoni.Enabled :== False;

Button2.Enabled := False;

Buttons.Enabled := True; label 1.Hide;

ShowForm(Application.Handle, Self.Handle) ;

end;

procedure TTestMain.Button3Click(Sender: TObject);

// Закрыть форму

begin

FreeForm;

Button1.Enabled := True;

Button2.Enabled := True;

Button3.Enabled := False/end;

procedure TTestMain.WMUser(var Msg: TMessage) ;

// Сообщение из формы DLL о ее закрытии

begin

Buttons.Click

end;

end.

Модуль формы DLLForm, помещенной в DLL, ссылается на стандартный модуль Forms и таким образом получает свой глобальный объект Application, который ничего “не знает” о глобальном объекте вызывающей программы (см. гл. 21). В режиме модального вызова это не имеет особого значения, т. к. модальное окно блокирует работу вызывающей программы. В режиме немодального вызова следует синхронизовать действия объектов, в противном случае минимизация главного окна, например, не приведет к минимизации окна DLL. Синхронизация достигается тем, что дескриптор объекта Application DLL заменяется на соответствующий дескриптор вызывающей программы.

При показе формы в немодальном режиме она может быть закрыта щелчком по собственной системной кнопке закрыть. В этом случае она должна каким-то образом известить вызывающую программу об этом событии. Для этого используется стандартный механизм посылки вызывающей форме Windows-сообщения. Сообщение должно иметь адрес, в роли которого используется дескриптор окна, получающего это сообщение. Вот почему вторым параметром обращения к функции ShowForm в DLL передается и в поле CallForm: запоминается дескриптор вызывающего окна. Обработчик события enclose формы проверяет это поле и, если оно определено, посылает вызывающему окну сообщение с индексом wm_user. В вызывающей программе предусмотрен обработчик этого сообщения, в котором реализуются необходимые действия.


Глава 14. ОСОБЕННОСТИ РАЗРАБОТКИ МОДУЛЕЙ-ПОТОКОВ

  • Глава 14. ОСОБЕННОСТИ РАЗРАБОТКИ МОДУЛЕЙ-ПОТОКОВ

Глава 14

ОСОБЕННОСТИ РАЗРАБОТКИ МОДУЛЕЙ-ПОТОКОВ

Современные операционные системы Windows 32 обеспечивают не только многозадачность, т. е. возможность параллельной работы нескольких программ, но и многопоточность, когда в рамках одной программы организуется несколько параллельно выполняемых фрагментов (потоков), каждый из которых конкурирует с другими потоками за наиболее важный ресурс - время центрального процессора. В многопоточном режиме время ЦП выделяется для каждого процесса небольшими порциями (квантами), по истечении этого времени управление передается другому потоку и т. д. до тех пор, пока потоки не закончат свою работу. В любой работающей программе организуется как минимум один поток для команд программы. С помощью объектов класса TThread программа может создать дополнительные потоки для проведения некоторой фоновой работы (например, текстовый процессор Word создает дополнительные потоки для проверки правильности орфографии, разбивки на страницы, печати документа и т. п.).

Для создания дополнительного потока в программах Delphi предназначен специальный модуль потока в репозитории он обозначен пиктограммой Thread Obiecll). При выборе этого модуля Delphi запрашивает имя класса, который будет дочерним для основополагающего класса TThread. Необходимость наследования связана с тем, что класс TThread содержит абстрактный метод Execute, который, собственно, и должен исполняться в рамках нового потока и который, следовательно, обязан перекрываться в потомках.

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

Например (с соответствующим переводом):

unit Unit1;

interface

uses

Classes;

type

MyThread = class(TThread)

private

{ Private declarations } protected

procedure Execute; override;

end;

implementation

{ Важно: Методы и свойства объектов из библиотеки визуальных компонентов могут использоваться только в рамках вызова метода Synchronize, например:

Synchronize(UpdateCaption);

где метод UpdateCaption должен быть подобен такому

procedure MyThread.UpdateCaption;

begin

Formi.Caption := 'Новый текст метки';

end; }

( MyThread }

procedure MyThread.Execute;

begin

{ Пожалуйста, поместите код потока в этом месте }

end;

end.

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

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

Для ее создания сначала на пустую форму поместите панель TPpanel, очистите ее свойство caption и поместите в Align значение аlRight - эта панель предназначена для размещения редактора TSpinEdit, кнопки TButton и индикатора TGauge и всегда должна располагаться в правой части окна программы. Поместите на панель перечисленные компоненты так, как это показано на рисунке (компоненты TSpinEdit и TGuage находятся на странице samples палитры компонентов).

Установите в свойство SpinEditl.Value 3начение 2, присвойте свойству Gaugel. Kind значение gkPie, Gaugel. BorderStyle-bsNone и Button1.Caption — 'Квадрат'.

На свободное место формы положите компонент TMemo и установите для него в свойство Align значение alСlient, а свойство Name- 'mmOutput'.

Теперь создадим обработчик события Button1.Click: при нажатии на кнопку вначале содержимое редактора SpinEdit1 возводится в квадрат до тех пор, пока отображаемое в нем значение не слишком большим (больше 10+1233). В этот момент надпись на кнопке меняется на “корень”, а нажатие на нее вычисляет корень квадратный ИЗ величины SpinEdit1.

Дважды щелкните по кнопке Button1и напишите такой код:

procedure TFormI.ButtonlClick(Sender: TObject) ;

begin

if Tag=0 then

begin

SpinEditl.Text := Float-ToStr(sqr(StrToPloat(SpinEditl.Text))) ;

if StrToFloat(SpinEditl.Text) > 1el233 then

begin

Tag := 1;

Buttoni.Caption := 'Корень'

end

end

else

begin

SpinEditl.Text := FloatToStr(sqrt(StrToFloat(SpinEditl.Text))) ;

if StrToFloat(SpinEditl.Text) < 2 then

begin

SpinEditl.Value := 2;

Tag := 0;

Button1.Caption := 'Квадрат'

end

end

end;

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

SpinEditl.

Теперь создадим модуль потока, в методе Execute которого будем непрерывно формировать по 100 строк в редакторе mmOutput и показывать процент заполнения редактора с помощью индикатора Gaugel.

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

unit Unit2;

interface

uses

Classes;

type

ThreadDemo = class(TThread) private

{ Private declarations } protected

S: String;

N: Integer;

procedure UpdateMemo;

procedure UpdateGauge;

procedure Execute; override;

end;

var

TDemo: ThreadDemo;

implementation

uses Uniti,SysUtils;

Important: Methods and properties of objects in VCL can only re used .in a method called using Synchronize, for example,

Synchronize(UpdateCaption);

and UpdateCaption could look like,

procedure ThreadDemo.UpdateCaption;

begin

Formi.Caption := 'Updated in a thread';

end; }

ThreadDemo }

procedure ThreadDemo.Execute;

var

j, k: Integer;

begin

repeat

S:='';

Synchronize(UpdateMemo);

for k := 0 to 99 do

begin N := k;

S : = ' ' ;

for j := 1 to 20 do

S := S+FormatFloat('00',k), Synchronize(UpdateMemo) ;

Synchronize(UpdateGauge)

end;

until False

end;

Procedure ThreadDemo.UpdateMemo;

begin

with .Form1.mmOutput.Lines do

if S=' ' then

Clear else

Add(S)

end;

Procedure ThreadDemo.UpdateGauge;

begin

Form1.Gaugel.Progress := N

end;

end.

Если вы запустите таким способом подготовленную программу, то ничего не произойдет - ведь мы еще не запустили поток. Чтобы сделать это, добавьте в модуле Unit1 главной формы ссылку uses Unit1, раскройте в окне Инспектора объектов список компонентов, выберите компонент Form1 и на его странице Event дважды щелкните по свойству onActivate, чтобы создать такой обработчик этого события:

procedure TFormI.FormActivate(Sender: TObject);

begin

TDemo := ThreadDemo.Create (False),

end;

Вот так просто запускается дополнительный поток - мы инициируем объект TDemo, передавая в его Консруктор ThreadDemo.Create

единственный параметр False (этот параметр показывает, должен ли вновь созданный поток “спать” - True или он обязан немедленно начать работу - False). Программа в любой момент может приостановить работу потока, присвоив его свойству suspended значение True, и продолжить его выполнение, присвоив этому свойству значение False. Обратите внимание - метод Execute потока вынесен в секцию protected и поэтому недоступен из основного модуля. Выполнение этого метода начинается автоматически, как только свойство suspended примет значение False.

Для обращения к свойствам и методам визуальных компонентов формы Form1 предназначен специальный метод потока Synchronize. Единственным параметром обращения к этому методу должно быть имя любой потоковой процедуры без параметров. Внутри такой процедуры разрешается обращаться к методам и свойствам визуальных компонентов. В нашем потоке две такие процедуры - UрdateMemo и updateGuage. В первой строка s добавляется к содержимому редактора mmoutput, во втором - глобальная переменная n присваивается свойству progress индикатора Gauge1. Поскольку эти процедуры не могут иметь параметров, для управления их работой приходится использовать глобальные переменные S и N.


Глава 15. ТИПИЗИРОВАННЫЕ КОНСТАНТЫ И ИНИЦИИРОВАННЫЕ ПЕРЕМЕННЫЕ

  • Глава 15. ТИПИЗИРОВАННЫЕ КОНСТАНТЫ И ИНИЦИИРОВАННЫЕ ПЕРЕМЕННЫЕ
    • КОНСТАНТЫ ПРОСТЫХ ТИПОВ И ТИПА STRING
    • КОНСТАНТЫ-МАССИВЫ
    • КОНСТАНТЫ-ЗАПИСИ
    • КОНСТАНТЫ-МНОЖЕСТВА
    • КОНСТАНТЫ-УКАЗАТЕЛИ
    • ИНИЦИАЦИЯ ПЕРЕМЕННЫХ

Глава 15

ТИПИЗИРОВАННЫЕ КОНСТАНТЫ И ИНИЦИИРОВАННЫЕ ПЕРЕМЕННЫЕ

В Object Pascal допускается использование типизированных констант. Они задаются в разделе объявления констант следующим образом:

<идентификатор> : <тип> = <значение>

Здесь <идентификатор> - идентификатор константы; <тип> - тип константы; <значение> - значение константы.

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

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

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

Типизированную константу нельзя использовать в качестве значения при объявлении других констант или границ типа-диапазона.

15.1. КОНСТАНТЫ ПРОСТЫХ ТИПОВ И ТИПА STRING

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

Примеры объявлений:

type

colors = (white, red, black);

{ ----- Правильные объявления: ----- } const

CurrCol colors = red;

Name String = 'Вирт H.' ;

Year Word = 1989;

X Real = 0.1;

Min Integer =0;

Max Integer = 10;

Days 1..31 = 1;

Answer Char = 'Y';

{----- Неправильные объявления: ----- }

Mass : array [Min..Max] of Real;

{Нельзя использовать типизированные константы в качестве границ диапазона} a,b,c : Byte =0;

{Нельзя использовать список идентификаторов}

15.2. КОНСТАНТЫ-МАССИВЫ

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

type

colors = (white, red, black) ;

const

ColStr : array [colors] of String [5] =('white', 'red', 'blak');

Vector : array [1..5] of Byte = (0,0,0,0,0);

При объявлении массива символов можно использовать то обстоятельство, что все символьные массивы и строки в Object Pascal хранятся в упакованном формате, поэтому в качестве значения массива-константы типа char допускается задание символьной строки соответствующей длины. Два следующих объявления идентичны:

const

Digit : array [0..9] of Char =

('0', '1', '2', ' 3 ', ' 4'•, '5', '6', '7', '8', '9');

DigChr: array [0..9] of Char = '0123456789';

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

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

Procedure TfmExample.bbRunClick(Sender: TObject);

var

i, j, k, 1 : Byte;

S: String;

const

Matr : array [1..3, 1..5] of Byte =

((0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10,11,12,13,14));

Cube : array [0..1, 0..1, 0..2] of Integer =

(((О ,1 ,2 ),(3 ,4 ,5 )), ((6 ,7 ,8 ),(9 ,10,11)));

Mas4 : array [0..1, 0..1, 0..1, 0..1] of Word =

((((О ,1 ),(2 ,3 )),

((4 ,5 ), (6 ,7 ))), (((8 ,9 ), (10,11)), ((12,13), (14,15))));

begin

S:=' ';

for i := I to 3 do for j := 1 to 5 do

S := S + IntToStr(Matr[i,j])+' ';

mmOutput.Lines.Add(S);

S: = '';

for i := 0 to 1 do for j := 0 to 1 do

for k := 0 to 2 do

S := S + IntToStr(Matr[i,j,k]) +' ';

mmOutput.Lines.Add(S);

S:=''; for i := 0 to 1 do

for j := 0 to 1 do

for k := 0 to 1 do

for l := 0 to 1 do

S := S + IntToStr(mas4[i,j,k,l]:3);

mmOutput.Lines.Add(S) ;

end;

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

15.3. КОНСТАНТЫ-ЗАПИСИ

Определение константы-записи имеет следующий вид:

<идентификатор> : <тип> = (<сп.знач.полей>)

Здесь <идентификатор> - идентификатор константы; <тип> - тип записи; <сп. знач. полей> - список значений полей.

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

type

Point = record

X, Y : Real end;

Vect = array [0..1] of Point;

Month = (Jan, Feb, Mar, Apr, May, Jun, Jly, Aug, Sep, Oct, Nov, Dec) ;

Date = record

D : 1..31;

M : month;

Y : 1900..1999 end;

const

Origon : Point = (X : 0; Y : -1) ;

Line : Vector =

((X : -3.1; Y : 1.5), (X : 5.9; Y : 3.0));

SomeDay : Date = (D : 16; M : May; Y : 1997);

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

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

type

Forma = record

case Boolean of

True : (Birthplace : String [40]);

False : (Country : String [20];

EntryPort : String [20];

EntryDate : array [1..3] of Word;

Count : Word) end;

const

Percon1 : Forma = (Country : 'Норвегия';

EntryPort : 'Мурманск';

EntryDate : (16, 3, 89);

Count : 12);

Percon2 : Forma = (Birthplace : 'Москва');

15.4. КОНСТАНТЫ-МНОЖЕСТВА

Значение типизированной константы-множества задается в виде правильного конструктора множества, например:

type

Days = set of 1..31;

Digc = set of ' 0 ' . . ' 9 ';

Error = set of 1..24;

const

WorkDays : Days = [1..5, 8..12, 15..19, 22..26, 29, 30];

EvenDigits: Digc = ['0', '2', '4', '6', '8'];

ErrorFlag : Error= [];

15.5. КОНСТАНТЫ-УКАЗАТЕЛИ

Единственным значением типизированной константы-указателя может быть только nil, например:

const pR : ^Real = NIL;

15.6. ИНИЦИАЦИЯ ПЕРЕМЕННЫХ

Во всех 32-разрядных версиях Delphi при объявлении глобальных переменных разрешается одновременно присваивать им значения (инициировать). Речь идет о глобальных переменных, объявляемых в интерфейсной или исполняемой частях модулей (переменные, объявляемые в теле процедуры, называются локальными, их инициировать нельзя).

Инициированная переменная получает присвоенное ей значение один раз - в момент старта программы; в дальнейшем она хранит последнее присвоенное ей значение. Пример инициации переменной:

unit Unit1.

implementation var

MyVar: Integer = 10;

end.

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


Глава 16. КЛАССЫ ОБЩЕГО НАЗНАЧЕНИЯ

  • Глава 16. КЛАССЫ ОБЩЕГО НАЗНАЧЕНИЯ
    • КЛАСС EXCEPTION-ОБРАБОТКА ИСКЛЮЧЕНИЙ
      • Защищенные блоки
      • Класс Exception
      • Стандартные классы исключений
      • Вызов исключения
      • Создание собственного класса
    • КЛАСС TLIST-СПИСКИ
    • КЛАССЫ TSTRINGS И TSTRINGLIST - НАБОРЫ СТРОК И ОБЪЕКТОВ
      • TStrings
      • TStringList
    • ГРАФИЧЕСКИЙ ИНСТРУМЕНТАРИЙ
      • Класс TFont
      • Класс TPen
      • Класс TBrush
      • Класс TCanvas
      • Дополнительные возможности API-функций
      • Области
      • Классы TGraphic и TPicture

Глава 16

КЛАССЫ ОБЩЕГО НАЗНАЧЕНИЯ

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

16.1. КЛАСС EXCEPTION - ОБРАБОТКА ИСКЛЮЧЕНИЙ

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

При работе в среде Delphi эксперименты с исключениями плохо прослеживаются, т. к. при каждом исключении среда; перехватывает управление программой. В этом случае бывает полезно отменить такое поведение среды. Для этого вызовите опцию Tools. [.- -Debugger Options и на странице Languaqe Exception уберите флажок в переключателе Stop on delphi. Exceptions (для шэсдьиуш-лх версий Delphi выберите опцию меню Tools | Environent: Options и на странице Preference уберите флажок в персключателе Break on exception).

16.1.1. Защищенные блоки

Для обработки исключений в Object Pascal предусмотрен механизм защищенного блока:

except <обработчики исключений> else <операторы> end; | finally <операторы> end;
Try <операторы> | Try <операторы>

Защищенный блок начинается зарезервированным словом try (попытаться [выполнить]) и завершается словом end. Существуют два типа защищенных блоков - except (исключить) и finally (в завершение), отличающихся способом обработки исключения. В блоке except порядок выполнения операторов таков: сначала выполняются операторы секции try... except; если операторы выполнены без возникновения исключительной ситуации, работа защищенного блока на этом прекращается, и управление получает оператор, стоящий за end; если при выполнении части try возникло исключение, управление получает соответствующий обработчик в секции except, а если таковой не найден - первый из операторов, стоящих

За словом else. В блоке finally операторы В секции finally. . .end получают управление всегда, независимо от того, возникло ли исключение в секции try.. .finally или нет. Если исключение возникло, все операторы в секции try.. .finally, стоящие за “виновником” исключения, пропускаются, и управление получает первый оператор секции finally... end. Если исключения не было, этот оператор получает управление после выполнения последнего оператора секции try.. .finally.

Обработчики исключений в блоке except имеют такой синтаксис:

on <класс исключения> do <оператор>;

Здесь on, do - зарезервированные слова; <класс исключения> -класс обработки исключения; <оператор> - любой оператор Object Pascal, кроме оператора передачи управления goto на метку вне блока except.

Обратите внимание: имя класса служит своеобразным ключом выбора, а собственно обработка осуществляется оператором, стоящим за do (этот оператор может быть составным, так что обработка исключения может занимать произвольное количество операторов Object Pascal).

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

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

try

except

ShowMessage('Ощибка!') ;

end;

Защищенные блоки могут вкладываться друг в друга на неограниченную глубину, т. к. везде, где в предыдущих описаниях использовались <операторы> ИЛИ <оператор>, могут использоваться любые операторы Object Pascal, в том числе и try... except или try...finally:

try

try finally

end;

except

on EMatchError do begin

try try end;

end;

end

end;

16.1.2. Класс Exception

Класс Exception является родительским классом для всех классов-исключений. Этот класс объявляется в модуле sysUtils следующим образом:

type

Exception = class(TObject)

private

FMessage: String;

FHelpContext: Integer;

public

constructor Create(const Msg: Strings);

constructor CreateFmt(const Msg: String;

const Args: array of const);

constructor CreateRes(Ident: Integers);

constructor CreateResFmt(Ident: Integer;

const Args: array of const) ;

constructor CreateHelp(const Msg: String; aHelpContext:

Integer) ;

constructor CreateFmtHelp(const Msg: String;

const Args: array of const; aHelpContext: Integers);

constructor CreateResHelp(Ident: Integer; aHelpContext:

Integer) ;

constructor CreateResFmtHelp(Ident: Integer;

const Args: array of const;

aHelpContext: Integers);

property HelpCoatext: Integer read FHelpContext write FHeipContext;

property Message: String read FMessage write FMessage;

end;

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

16.1.3. Стандартные классы исключений

В Delphi определены стандартные классы исключений, перечисленные в табл. 16.1. Именно эти имена, а также имена пользовательских классов (см. п. 16.1.5) могут использоваться в обработчиках исключении.

Таблица 16.1

Класс | Родитель | Обрабатываемое исключение
EAbort | Exception | Реализует “тихую” (без какого-либо сообщения) обработку любого исключения
EAbstractErrpr | Exception | Программа пытается вызвать абстрактный метод
EAccessViolation | Exception | Программа пыталась обратиться к не принадлежащей ей области памяти или использует недействительный указатель
EAppletException | Exception | Ошибка связана с созданием управляющих панелей в апплет-приложениях
EArrayError | Exception | Возникает из-за различного рода ошибок при работе с массивами (неверный индекс, попытка вставить элемент в массив фиксированной длины или в отсортированный массив и т. п.)
EAssertionFaild | Exception | Возбуждается отладочной процедурой Assert, когда тстируемое ею логическое выражение имеет значение False.
EBitsError | Exception | Программа пыталась обратиться к свойству Bits объекта TBits с индексом меньше нуля или больше максимально допустимого значения
EBrokerException | Exception | Объект-брокер не может найти сервер
ECacheError | Exception | Ошибка в наборе данных для компонента Tde-cisionCube
EClassNotFound | EFilerError | Для компонента, читаемого из потока данных, не найден соответствующий класс. Обычно возникает"в случае, когда в форму вставлен нестандартный компонент, а в библиотеке компонентов Delphi нет связанного с ним класса
ECommonCalendar-Error | Exception | Возникает в объектах класса TCommonCalendar и его потомках, когда вводится неверная дата
EComponentError | Exception | Возникает при различных манипуляциях программы с компонентом (программа не может зарегистрировать компонент, переименовать его или когда для его работы требуется интерфейс СОМ, который компонентом не поддерживается)
EControlC | Exception | Возникает при нажатии Ctrl-C при работе приложения в режиме консолиЛ
EConvertError | Exception | Ошибка преобразования в функциях StrToint или StrToFloat
ECorbaDispatch | Exception | Возникает в программах, использующих технологию corba, при ошибках, связанных с несовпадением интерфейсов сервера и брокера данных
ECorbaException | Exception | Возникает в программах, использующих технологию CORBA
ECorbaUser-Exception | ECorbaException | Возникает как определяемая пользователем реакция на ошибки интерфейса
EDatabaseError | Exception | Возникает, когда компонент обнаруживает ошибку в базе данных
EDateTimeError | Exception | Возбуждается компонентом TDateTimePicker при попытке ввода неверной даты или времени
EDBClient | EDatabaseError | Ошибка связана с неправильной работой Tcli-entDataSet
EDBEditError | Exception | Возникает, когда компонент пытается использовать данные, несовместимые с заданной маской
EDBEngineError | EDatabaseError | Связана с ошибками BDE
EDimensionMar-Error | Exception | Возникает, когда используемый в кубе решений набор данных не имеет агрегатных полей
EDimIndexError | Exception | Связана с нарушением размерности массива данных для куба решений
EDivByZero | EIntError | Ошибка целочисленного деления на ноль
EDSWriter | Exception | Ошибка при подготовке провайдером пакета данных для набора данных
EExternal-Exception | EStream-Error | Возникла ошибка, код которой не является предопределенным в Delphi
EFCreateError | EStream-Error | Ошибка при создании файла. Например, попытка создать файл на устройстве, предназначенном только для чтения, или в несуществующем каталоге
EFilerError | EStream-Error | Программа пытается повторно зарегистрировать в потоке один и тот же класс
EFOpenError | EStream-Error | Ошибка открытия потока данных. Например, попытка открыть несуществующий файл
EHeapException | Exception | Ошибка связана с неправильными операциями над динамической памятью
ElBClientError | ElBError | Ошибка связана с функционированием IBX-клиента
ElBError | EDatabaseError | Общая ошибка технологии IBX
ElBInterbase-Error | ElBError | Ошибка связана с функционированием сервера в технологии IBX
EInOutError | Exception | Любая ошибка в файловых операциях. Поле ErrorCode объекта этого класса содержит код ошибки
EInterpreterError | Exception | Возникает, когда компонент класса TDataBlockinterpeter не может интерпретировать данные блока данных
EIntError | Exception | Любая ошибка в целочисленных вычислениях
EIntfCastError | Exception | Попытка недопустимого приведения типов в OLE-объектах
EIntOverflow | EIntError | Ошибка целочисленного переполнения: программа пытается присвоить целочисленной переменной значение, выходящее из 32-двоичных разрядов
EInva1i dArgument | EMatchError | Возбуждается математическими функциями при выходе аргумента из допустимого диапазона
EInvalidCast | Exception | Программа пытается осуществить недопустимое преобразование типов с помощью оператора as
EInvalidGraphic | Exception | Программа пытается загрузить в контейнер изображение из файла, который имеет недопустимый формат (допустимыми форматами являются растр, метафайл, курсор, пиктограмма)
EInvalidGraphic-Operation | Exception | Программа пытается выполнить недопустимую графическую операцию
EInvalidGrid-Operation | Exception | Программа пытается выполнить недопустимую операцию над таблицей (например, обратиться к несуществующему столбцу или РЯДУ)
EInvalidImage | EFilerError | Программа пытается прочитать ресурс изображения из файла, в котором этого ресурса нет
EInvalidOp | EMatchError | Ошибка в операциях с плавающей точкой (недопустимая инструкция, переполнение стека сопроцессора и т. п.)
EInvalidOpera-tion | Exception | Не имеющий окна компонент пытается выполнить операцию, требующую дескриптора окна
EInvalidPointer | EHeap-Exception | Попытка использовать недействительный указатель
EListError | Exception | Эта ошибка связана с неверными действиями программы по отношению к разного рода спискам. Например обращение к элементу списка с индексом меньше нуля или больше максимально допустимого
ELowCapacity-Error | Exception | Ошибка возникает при попытке выделения памяти на устройстве, у которого нет нужной свободной памяти.
EMatchError | Exception | Любая ошибка при выполнении вычислений с плавающей точкой.
EMenuError | Exception | Ошибка при работе программы с меню. Например, при добавлении элемента с идентификатором, который уже определен в меню
EMCIDiviceError | Exception | Ошибка возникла в медиаплейере
EMethodNotFound | EFilerError | Программа прочитала из потока данных объект, но не может найти связанный с классом объекта метод
EMonthCalError | ECommon-Calendar-Error | Возбуждается компонентом класса TMonthcal-endar при попытке ввода неправильной даты
EOleCtrlError | Exception | Программа не может установить связь с элементом ActiveX
EOleError | Exception | Низкоуровневая ошибка в технологии OLE
EOleException | EOleSysError | Программа использует неверный OLE-интерфейс
EOleRegistration Error | EOleError | Ошибка регистрации OLE-объекта в реестре Windows
EOleSysError | EOleError | Возникает при неправильном выполнении команды OLE автоматизации
EOutlineError | Exception | Возникает при ошибке доступа к компоненту класса ToutLine
EOutOfMemory | EHeap-Exception | Эта ошибка возникает, когда программа запрашивает слишком большой для данной конфигурации Windows объем памяти
EOutOfResource | EOutOfMemory | Программа требует от Windows дескриптор окна, но Windows исчерпала лимит дескрипторов
EOverflow | EMatchError | Результат операций с плавающей точкой слишком велик, чтобы уместиться в регистрах сопроцессора
EPackageError | Exception | Возникает при ошибке доступа к пакету
EParserError | Exception | Ошибка преобразования текста в двоичные данные при чтении из потока
EPrinter | Exception | Windows сообщила программе об ошибке принтера
EPrivilege | Exception | Программа пытается выполнить привилегированную операцию. Привилегированные операции могут выполняться только ядром Windows
EPropertyConvert Error | Exception | Ошибка при чтении или записи значения свойства
EPropertyError | Exception | Ошибка доступа к свойству при чтении или записи
EPropReadOnly | Exception | Программа пытается присвоить значение свойству, из которого можно только читать (при использовании технологии OLE)
EPropWriteOnly | Exception | Программа пытается прочитать свойство, предназначенное только для записи
ERangeError | EIntError | Целочисленный результат превышает емкость целого типа данных
EReadError | EFilerError | Программа не может прочитать из потока данных нужного количества байт
EReconcileError | EData-baseError | Ошибка обновления данных в TclientDataset
ERegistry-Exception | Exception | Ошибка, связанная с операцией над реестром Windows
EResNotFound | Exception | Программа не может найти указанный ресурс в файле ресурсов
ESocketConnec-tionError | Exception | Ошибка связана с работой с сокетами Windows
ESocketError | Exception | Ошибка связана с работой с сокетами Windows
EStackOverflow | Exception | Исчерпан объем выделенного программе стека
EStreamError | Exception | Любая ошибка при работе с потоком данных
EStringListError | Exception | Программа ссылается на строку, индекс которой выходит из диапазона возможных значений для списка строк
EThread | Exception | Ситуация борьбы за общий ресурс в программе с несколькими потоками команд
ETreeViewError | Exception | Указан неверный индекс при обращении к TtreeView
EUnderflow | EMatchError | Результат операций с плавающей точкой слишком мал, чтобы уместиться в регистрах сопроцессора (исчезновение порядка)
EUnsupportedTypeE rror | Exception | Выбран недопустимый тип поля в качестве измерения в кубе решений
EUpdateError | Exception | Ошибка обновления провайдерского набора данных
EVariantError | Exception | Ошибка при работе с типом Variant: недопустимое приведение типов; недопустимая операция; обращение -к скалярной переменной как к варианту-массиву; индекс варианта-массива выходит из допустимых значений
EWin32Error | Exception | Ошибочное обращение к API-функции Windows. Свойство Message содержит номер ошибки и связанное с ней сообщение
EWriteError | EFilerError | Ошибка записи в поток данных
EZeroDivide | EMatchError | Вещественное деление на ноль

Важно помнить, что ищется самый первый из, возможно, нескольких обработчиков, класс которого способен обрабатывать данное исключение. Если, например, в списке первым стоит EAbort, который может обработать любое исключение, ни один из стоящих за ним обработчиков никогда не получит управления. Точно так же, если указан обработчик для класса EintError, за ним бесполезно размещать Обработчики EDivByZero, ERangeError или EIntOverflow:

try

except

// He имеет смысла делать так:

on EintError do .....;

on ERangeError do .....;

on EDivByZero do .....;

// Надо так:

on ERangeError do .....;

on EDivByZero do .....;

on EintError do .....;

end;

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

on EObject: EClassName do .....;

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

Например:

Reset(F) ;

while not EOF(F) do begin

end;

CloseFile(F) ;

except

on E: EInOutError do

ShowMessage('При выполнении файловой операции возникла'+ ' ошибка №'+ IntToStr(E.ErrorCode));

end;

16.1.4. Вызов исключения

В некоторых ситуациях программисту бывает необходимо инициировать собственное исключение. Для этого он использует зарезервированное слово raise (возбудить). Если это слово встретилось в секции try.. .exception или try.. .finally, немедленно начинают свою работу секции соответственно except... end и finally... end. Если оно встретилось в except.. .end или finally.. .end, считается, что данный защищенный блок на текущем уровне вложенности (блоки могут быть вложенными) завершил свою работу и управление передается вышестоящему уровню.

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

raise EInOutError.Create('Ощибка!') ;

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

16.1.5. Создание собственного класса

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

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

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

type

EIntCheckError = class(EAbort)

end;

var

F: TextFile;

S: String;

k: Integer;

begin

try

// Готовимся к работе: открываем файл AssignFile(F, FileName);

Reset(F); // Здесь возможна ошибка EinOutError // Цикл ввода-контроля-преобразования while not EOF(F) do begin

// Вводим символы очередного числа

ReadLn(F,S);// Здесь возможна ошибка EinOutError

// Преобразуем символы в число

k := StrToInt(S); // Здесь возможна ошибка EConvertError

// Проверяем число

if k < 0 then

raise EIntCheckError.Create("Отрицательное число');

// Преобразуем число

..... // Здесь возможна ошибка EIntError

// Вновь проверяем число

if k <= 0 then

raise EIntCheckError.Create('He положительное число');

end;

except

on E: EIntCheckError do

ShowMessage(E.Message) ;

on EInOutError do

ShowMessage('Некорректная файловая операция');

on EConvertError do

ShowMessage('Ошибка в записи числа');

on EIntError do

ShowMessage('Ошибка преобразования');

end;

end;

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

raise EIntCheckError.Create('Отрицательное число') ;

и

raise EIntCheckError.Create('Ошибка преобразования');

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

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

16.2. КЛАСС TLIST - СПИСКИ

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

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

Свойства класса:

property List: pPointerList; | Возвращает указатель на массив элементов списка
property Capacity: Integers; | Содержит количество элементов массива указателей списка. Всегда больше Count. Если при добавлении очередного элемента Count стало равно Capacity, происходит автоматическое расширение списка на 16 элементов
property Count: Integer; | Количество элементов списка. Это свойство изменяется при добавлении или удалении элемента
property Items(Index: Integer): Pointers; | Возвращает указатель на элемент списка по его индексу. Самый первый элемент списка имеет индекс 0

Тип pPointerList определен следующим образом:

type

pPointerList = PPointerList;

TPointerList = array [0..MaxListSize] of Pointer;

Константа MaxListSize для Delphi 1 ограничена значением 16379 элементов. Для старших версий Delphi она ограничивается доступной памятью.

Следует учесть, что свойство count определяет количество помещенных в список элементов, в то время как capacity - текущую емкость списка. Если при добавлении очередного элемента обнаруживается, что емкость списка исчерпана, происходит наращивание емкости на фиксированную величину (для count < 5 - на 4 элемента, для 4 < count < 8 - на 8, для Count > 7 - на 16). При этом сначала резервируется память для размещения расширенного массива указателей, затем в нее копируется содержимое старого массива, после чего старый массив указателей уничтожается (занимаемая им память возвращается Windows).

Если вы заранее знаете, сколько элементов необходимо поместить :в список, установите в начале работы нужное значение в свойство Саpacity - это снизит непроизводительные затраты времени на расширение списка.

Методы класса:

function Add(Item: Pointer): Integer; procedure Clear; | Добавляет элемент item в конец ci; вращает его индекс Очищает список, удаляя из него вес Не освобождает память, связанную удаленным элементом. Устанавлив: ства Count и Capacity значение 0
procedure Delete(Index:Integer); | Удаляет из списка элемент с индекс все элементы, расположенные за удаляемым, смещаются на одну позицию вверх
class procedure Er ror(const Msg: Stringy; Data: Integer); virtual; | Возбуждает исключение ElistErr метрами Msg и Data
procedure Ex change (Indexl, Index2:Integer) ; | Меняет местами элементы с индексами index1 иI ndex2
function Expand: TList; function Extract(Item: Pointer): Pointer; | Расширяет массив, увеличивая Capacity Удаляет из списка элемент Item
function First: Pointer; | Возвращает указатель на самый первый элемент списка
function IndexOf(Item:Pointer): Integer; | Отыскивает в списке элемент Item и возвращает его индекс
procedure Insert(Index: Integer; Item: Pointer) ; | Вставляет элемент Item в позицию Index списка: новый элемент получает индекс Index, все элементы с индексами Index и больше увеличивают свой индекс на 1. При необходимости расширяет список
function Last: Pointer; | Возвращает указатель на последний элемент списка
procedure Move(Curlndex, Newlndex: Integers; | Перемещает элемент в списке с позиции CurIndex в позицию Newlndex. Все элементы старого списка с индексами от Curlndex-1 до Newlndex уменьшают свой индекс на 1
procedure Pack; | Упаковывает список: удаляет пустые элементы в конце массива индексов
function Remove(Item: Pointer): Integer; | Отыскивает в списке элемент Item и удаляет его
procedure Sort(Compare: rListSortCompare); | Сортирует коллекцию с помощью функции Compare

Методы Add и insert получают указатель на вставляемый элемент. Чтобы воспользоваться ими, программист должен сам разместить в куче данные и получить соответствующий указатель. Точно так же методы Delete, Remove и Сlear не уничтожают распределенные в памяти данные, которые программист должен, если это необходимо, уничтожить сам.

Например:

var

List: TList;

Item: Pointer;

Value: AnyType;

begin

List := TList.Create; // Создаем список

Item := New(Value); // Размещаем в куче данные

List.Add(Item); // Добавляем элемент к списку .....

List.Remove(Item); // Удаляем элемент из списка

Dispose(Item); // Удаляем его из кучи

List.Free; // Удаляем ненужный список

end;

Метод sort сортирует список по критерию, устанавливаемому функцией compary. Тип TListSortCompare определен следующим образом:

TListSortCompare = function(Iteml, Item2: Pointer): Integer;

Таким образом, функция compare получает указатели на два элемента списка. Результат сравнения:

  • любое отрицательное число, если Item1^ <Item.2^;
  • 0,если Item1^ = Item2^;
  • любое положительное число, если Item1^ > Item2^.

Критерий сравнения данных устанавливается программистом и реализуется в функции Compare.

В следующем примере в список List помещается 20 случайных вещественных чисел, равномерно распределенных в диапазоне 0...1.

Список сортируется по возрастанию чисел и отображается в компоненте mmOutput (многострочный редактор из учебной формы fmExample).

type

PDouble = ^Double;

Function Comp(Iteml, Item2:. Pointer): Integer;

// С помощью этой функции реализуется сортировка чисел

begin

if PDouble(Iteml)^ < PDouble(Item2)^ then

Result := -1 else

if PDouble(Iteml^ > PDouble (Item2) ^ then

Result := 1 else

Result := 0 end;

procedure TfmExample.bbRunClick(Sender: TObject);

// Обработчик нажатий кнопки bbRun выполняет основную работу

var

k: Integer;

List: TList;

pD: PDouble;

begin

List := TList.Create; // Создаем список

for k := 1 to 20 do // Наполняем его

begin

New(pD); // Резервируем память

pD^ := Random; // Помещаем в нее случайное число

List.Add(pD); // Добавляем к списку

end/List. Sort (Comp) ; // Сортируем список по возрастанию mmOutput.Lines.Clear;

{ Очищаем редактор mmOutput. В следующем цикле наполняем mmOutput и уничтожаем элементы List }

for k := 0 to List.Count-1 do

begin

pD := List[k]; // Очередное число из списка

mmOutput. Lines .Add (FloatToStr (pD^ );

{Помещаем в mmOutput}

Dispose(pD) // Уничтожаем число

end;

List.Free; // Уничтожаем список

end;

16.3. КЛАССЫ TSTRINGS И TSTRINGLIST -НАБОРЫ СТРОК И ОБЪЕКТОВ

16.3.1. TStrings

Абстрактный класс TStrings инкапсулирует поля и методы для работы с наборами строк. От него порождены многочисленные специализированные потомки, обслуживающие наборы строк в таких компонентах, как TComboBox, TListBox, TRichEdit и др. Эти классы

(TComboBoxStrings, TListBoxStrings, TRichEditStrings и др.) объявляются в разделах Implementation соответствующих модулей (Stdctris, Сomctris и др.) и поэтому скрыты от браузера Delphi и не включены в Help-службу. Единственным доступным наследником TStrings является TStringList - полнофункциональный класс общего назначения.

Замечательной особенностью TStrings и его потомков является то обстоятельство, что элементами наборов служат пары строка-объект, в которых строка - собственно строка символов, а объект -объект любого класса Delphi. Такая двойственность позволяет сохранять в TStrings объекты с текстовыми примечаниями, сортировать объекты, отыскивать нужный объект по его описанию и т. д. Кроме того, в качестве объекта может использоваться потомок от TStrings, что позволяет создавать многомерные наборы строк.

Свойства класса:

property Capacity: Integer; | Текущая емкость набора строк
property CommaText: String; | Служит для установки или получения всего набора строк в виде единой строки с кавычками и запятыми (см. ниже пояснения) Текущее количество строк в наборе.
property Count: Integer; | Интерпретирует все строки списка в виде одной длинной строки
property DelimitedText: String; | Каждая строка окружается символами QuoteChar и отделяется от соседней символом Delimiter
property Delimiter: Char; | Символ для выделения строк в свойстве DelimitedText
property Names[Index: Integer] : String; | Для строки с индексом Index возвращает часть Name, если это строка вида Name=Value, в противном случае возвращает пустую строку
property Objects[Index: Integer] : TObject; | Открывает доступ к объекту, связанному со строкой с индексом Index
property QuoteChar: Char; | Символ для разделения строк в свойстве DelimitedText
property Strings[Index: Integer] : String; | Открывает доступ к строке с индексом Index
property StringsAdapter: TStringsAdapter; | Это свойство используется только при разработке компонентов, отвечающих стандарту Act iv
property Text: String; | Интерпретирует набор строк в виде одной длинной строки с разделителями eoln между отдельными строками набо
property Values[const Name: String]: String; | По части Name отыскивает в наборе и возвращает часть Value для строк вида Name=Valu

Набор строк технически реализуется подобно TList - в виде массива указателей. Свойство Capacity показывает текущую длину этого массива, а свойство count - количество элементов, занятых в нем. Если при добавлении очередного элемента capacity окажется меньше count, происходит автоматическое расширение массива. При этом в динамической памяти резервируется место для размещения Capacity + 16 указателей, в новый массив переписывается содержимое старого массива, после чего старый массив уничтожается. Если вам известно количество элементов в создаваемом наборе строк, имеет смысл заранее нужным образом установить свойство capacity, чтобы сократить непроизводительные расходы на многократные расширения массива указателей.

Свойство commaText интерпретирует содержимое набора строк в виде одной длинной строки с элементами вида "первая строка", "вторая строка", "третья строка" и т. д. (каждая строка набора заключается в двойные кавычки и отделяется от соседней строки запятой; если в строке встречается символ “"”, он удваивается). Свойство Text интерпретирует содержимое набора в виде одной длинной строки с элементами, разделенными стандартным признаком eoln (#13#10).

Свойства Names И Values обрабатывают строки вида Name=Value.

Такие строки широко используются в различных файлах инициации, например, в файле win. ini. Методы класса:

function Add(const S: String) : Integer; function AddObject(const S:String; aObject: TObject):Integer; | Добавляет строку в набор данных и возвращает ее индекс добавляет строку и объект в набор данных
procedure AddStrings( Strings: TStrings); | Добавляет к текущему набору новый набор строк
procedure Append(const S:String) ; | То же, что Add, но не возвращает индекс вставленной строки
procedure Assign(Source: TPersistent) ; | Уничтожает прежний набор строк и загружает из Source новый набор. В случае не удачи возникает исключение EсоnvertError
procedure BeginUpdate; | Устанавливает флаг начала обновления. До вызова EndUpdate блокируется сортировка строк, что ускоряет процесс много кратного изменения данных
procedure Clear; | Очищает набор данных и освобождает связанную с ним память
procedure Delete(Index: Integer) ; | Уничтожает элемент набора с индексом Index и освобождает связанную с ним память
procedure EndUpdate; | Сбрасывает флаг изменения и при необходимости сортирует строки
function Equals(Strings:TStrings): Boolean; | Сравнивает построчно текущий набор данных с набором Strings и возвращает True, если наборы идентичны
procedure Exchange(Indexl, Index3: Integers; | Меняет местами строки с индексами Index1 и Index2. Если объект сортируется, вызывает исключение EstringListError
function GetObject(Index:Integer): TObject; virtual; | Возвращает объект, связанный со строкой Index
function GetText: PChar; | Загружает строки из единого массива, в котором они отделяются друг от друга признаком EOLN
function IndexOf(const S: String): Integer; | Для строки S возвращает ее индекс или -1, если такой строки в наборе нет
function IndexOfName(const Name: String): Integer; | Возвращает индекс первой строки вида Name=Value, в которой часть Name совпадает с параметром обращения
function IndexOfObject (aObject: TObject): Integer; | Для объекта aObj ect возвращает индекс строки или -1, если такого объекта в наборе нет
procedure Insert(Index: In teger; const S: Strings; | Вставляет строку в набор и присваивает ей индекс Index
procedure InsertObject (Index: Integer; const S: String; aObject: TObject) | Вставляет строку и объект в набор и присваивает им индекс Index
procedure LoadFromFile (const FileName: Strings; | Загружает набор из файла
procedure LoadFromStream (Stream: TStream) ; | Загружает набор из потока
procedure Move(Curlndex, Nwlndex: Integers; | Перемещает строку из положения Curlndex в положение Newlndex
procedure SaveToFile(const FileName: Strings; | Сохраняет набор в файле
procedure SaveToStream (Stream: TStream) ; | Сохраняет набор в потоке
procedure SetText(Text: PChar); | Выгружает строки в единый массив, в ко тором они отделяются друг от друга при знаком EOLN

Методы Add, Append, Insert, Clear И Т. П. В классе TStrings - абстрактные. Связано это с тем, что класс инкапсулирует их и таким

образом делает доступными во всех потомках, но он при этом не накладывает никаких ограничений на то, как располагаются в памяти строки и объекты. Каждый потомок решает эту задачу наиболее удобным для него способом. Например, потомок TStringList располагает строки и объекты в общей динамической памяти, для чего перекрывает все абстрактные методы своего родителя. Замечу, что? если вы создадите экземпляр класса TStrings с помощью его конструктора Create, компилятор предупредит вас о том, что этот экземпляр содержит абстрактные методы, так что пользоваться им нужно лишь в исключительных случаях.

16.3.2. TStringList

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

property Duplicates: TDu-plicates; | Свойство, позволяющее управлять возможностью размещения в наборе двух и более идентичных строк
property Sorted: Boolean; | Признак необходимости сортировки строк в алфавитном порядке
property OnChange: TNotifyEvent;; | Определяет реакцию на изменение набора строк. Возникает после последнего изменения
property OnChanging: TNotifyEvent | Определяет реакцию на изменение набора строк. Возникает до очередного изменения
function Find(const S:String; var Index: Integer) : Boolean; | Ищет в наборе строку S и в случае успеха в параметре index возвращает ее индекс

При sorted = True строки набора автоматически сортируются в алфавитном порядке. При этом свойство Duplicates разрешает коллизию, связанную с добавлением в набор строки, идентичной одной из ранее вставленных. Если Duplicates = dulgnore, идентичная строка отвергается и программе ничего об этом не сообщается; если Duplicates = duError, возбуждается исключение EListError; значение Duplicates = duAccept разрешает вставлять в набор сколько угодно идентичных строк.

Следует заметить, что сортировка строк в Windows 32 осуществляется не совсем так, как это происходит в MS-DOS или Windows 3-х. Чтобы убедиться в этом, советую прогнать следующий вариант учебной программы (см. рис. 16.1). В ней обработчик события OnClick кнопки bbRun создает два списка строк - List1и List2. Список List1 первоначально способен сортировать строки (в его свойство sorted устанавливается значение True). Затем в цикле от 32 до 255 (О...31 - это коды служебных символов) происходит наполнение обоих списков: в List1 помещается строка, содержащая символ и его код, а в List2 - наоборот, сначала код, а затем сам символ. Поскольку символьное представление кода дополняется до трех символов (в двузначных числах слева добавляется ведущий ноль), строки в списке List1 окажутся отсортированными по символу, а в списке List2 - по коду символа. Затем оба списка для наглядности объединяются и помещаются в редактор mmOutput.

procedure TfmExample.bbRunClick (Sender: TObject);

var

k: Byte;

Listi, List2: TStringList;

S: String;

begin

// Создаем два списка

List1 := TStringList.Create;

Listi.Sorted := True;

List2 := TStringList.Create;

// Цикл наполнения списков монотонно возрастающими кодами

for k := 32 to 255 do

begin

S := IntToStr(k); // Код символа

if k<100 then

S := '0' + S;// Двузначное число дополняем ведущим нулем

// Формируем строку Listi из символа + табуляция + код символа Listi.Add(Char(k) + #9 + S) ;

// Формируем строку List2 из кода + табуляция + символ

List2.Add(S + #9 + Char(k));

end;

// Объединяем оба списка

Listi.Sorted := False; // Отключаем сортировку List1

for k := 0 to List1.Count-1 do

List1[k] := List1[k] + #9 + List2[k];

// Переносим результат в mmOutput mmOutput.Lines.Assign(List1);

// Уничтожаем List1 и List2 Listi.Free;

List2.Free;

end;

Следует прокомментировать два момента. Во-первых, с помощью единственного оператора

mmOutput.Lines.Assign(List1);

все строки List1 помещаются в свойство Lines редактора mmOutput. Это возможно из-за того, что mmOutput. Lines и Listi имеют общего родителя TStrings, который умеет копировать родственные наборы строк с помощью своего метода Assign. Во-вторых, перед объединением строк мы отключили свойство Sorted списка Listi. если этого не сделать, любое изменение строк в отсортированном списке привело бы к исключентю EStringListError.

Рис. 16.1. Окно прогона программы сравнения сортировки строк для Windows 32 и Windows З.х

Как видим, строки в 32-разрядных Windows сортируются не с учетом внутреннего кода символа, а с учетом “смысла”: в начале располагаются все знаки препинания и разного рода “мусор”, затем идут цифры, буквы латинского алфавита и символы кириллицы. Буквы упорядочены парами - сначала заглавная, за ней строчная - и не идут сплошным массивом. “Виновником” такой сортировки является API-функция AnsiCompareText, К который обращается метод TStringList. Quicksort. Если вам понадобится отсортировать так, как это принято в MS-DOS или Windows З.х, т. е. по коду символа, проделайте следующее.

  1. Разыщите в каталоге source | Rtl | common исходный файл classes . pas и скопируйте его в ваш рабочий каталог (в предыдущих версиях этот файл располагался в каталоге Source | vci). Если вы не найдете этого файла, вы не сможете изменить сортировку, т. к. в классе TStringList метод Quicksort, отвечающий за сортировку, определен в секции private и, следовательно, недоступен наследникам.
  2. В раздел implementation этого модуля сразу после предложения Uses поместите такое описание функции AnsiCompareText:

Function AnsiCompareText(SI, S2: String): Integer;

begin

if S1 < S2 then Result := -1

else if SI == S2 then Result := 0

else Result := 1

end;

3. С помощью опции Project | Add To Project добавьте измененный модуль classes к вашему проекту и сделайте новый прогон программы. Теперь оба столбца будут идентичны.

16.4. ГРАФИЧЕСКИЙ ИНСТРУМЕНТАРИЙ

Богатство изобразительных возможностей Windows связано с так называемым дескриптором контекста графического устройства DC (Device Context) и тремя входящими в него инструментами - шрифтом, пером и кистью. В Delphi созданы специализированные классы-надстройки, существенно упрощающие использование графических инструментов Windows: для контекста - класс TCanvas, для шрифта - TFont, для пера - ТРеп и для кисти - TBrush.

Связанные с этими классами объекты автоматически создаются для всех видимых элементов и становятся доступны программе

через свойства Canvas, Font, Pen и Brush.

16.4.1. Класс TFont

С помощью класса TFont создается объект-шрифт для любого графического устройства (экрана, принтера, плоттера и т. п.).

Свойства класса:

property Color: TColor; | Цвет шрифта
property Charset: TFontCharSet; | Набор символов. Для русскоязычных программ это свойство обычно имеет значение DEFAULTCHARSET или russian charset. Используйте значение OEMCHARSET для отображения текста MS-DOS (альтернативная кодировка)
property FontAdapter: IChangeNotifier; | Поставляет информацию о шрифте в компоненты ActveX
property Handle:hFont; | Дескриптор шрифта. Используется при непосредственном обращении к API-функциям Windows
property Height: Integer; | Высота шрифта в пикселях экрана
property Name: TFontName; | Имя шрифта. По умолчанию имеет значение MS Sans Serif
property Pitch: TFontPitch; | Определяет способ расположения букв в тексте: значение fpFixed задает моноширинный текст, при котором каждая буква имеет одинаковую ширину; значение fpVariabel определяет ропорциональный текст, при котором ширина буквы зависит от ее начертания; fpDefault определяет ширину, принятую для текущего шрифта
property PixelPerInch: Integer; | Определяет количество пикселей экрана на один дюйм реальной длины. Это свойство не следует изменять, т. к. оно используется системой для обеспечения соответствия экранного шрифта шрифту принтера
property Size: Integer; | Высота шрифта в пунктах (1/72 дюйма). Изменение этого свойства автоматически изменяет свойство Height и наоборот.
property Style: TFontStyles; | Стиль шрифта. Может принимать значение как комбинация следующих признаков: fsBold (жирный), fsltalic (курсив), fsUnderline (подчеркнутый), fsStrikeOut (перечеркнутый)

Для некоторых случаев может оказаться полезным метод

procedure Assign(Source: TPersistent);

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

16.4.2. Класс ТРеп

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

property Color: TColor; | Цвет вычерчиваемых пером линий
property Handle: Inte ger; | Дескриптор пера. Используется при непосредственном обращении к API-функциям Windows
property Mode: TPenMode; | Определяет способ взаимодействия линий с фоном (см.ниже)
property Style: TPenStyle; | Определяет стиль линий (см. рис. 16.2). Учитывается только для толщины линий 1 пиксель. Для толстых линий стиль всегда ps Solid (сплошная)
property Width: Integer; | Толщина линий в пикселях экрана
Свойство Mode может принимать одно из следующих значений:
pmBlack | Линии всегда черные. Свойства Color и Style игнорируются
pmWhite | Линии всегда белые. Свойства Color и Style игнорируются
pmNop | Цвет фона не меняется (линии не видны)
pmNot | Инверсия цвета фона. Свойства Color и Style игнорируются
pmCopy | Цвет линий определяется свойством Color пера
pmNotCopy | Инверсия цвета пера. Свойство Style игнорируется
pmMergePenNot | Комбинация цвета пера и инверсионного цвета фона
pmMaskPenNot | Комбинация общих цветов для пера и инверсионного цвета фона. Свойство Style игнорируется
pmMergeNotPen | Комбинация инверсионного цвета пера и фона
pmMaskNotPen | Комбинация общих цветов для инверсионного цвета пера и фона. Свойство Style игнорируется
pmMerge | Комбинация цветов пера и фона
pmNotMerge | Инверсия цветов пера и фона. Свойство Style игнорируется
pmMask | Общие цвета пера и фона
pmNotMask | Инверсия общих цветов пера и фона
pmXor | Объединение цветов пера и фона операцией XOR
pinNotXor | Инверсия объединения цветов пера и фона операцией XOR

Стили линий показаны на рис. 16.2.

Рис. 16.2. Стили линий

16.4.3. Класс TBrush

Объекты класса TBrush (кисти) служат для заполнения внутреннего пространства замкнутых фигур.

Свойства класса:

property Bitmap: TBitmap; | Содержит растровое изображение, которое будет использоваться кистью для заполнения. Если это свойство определено, свойства Color и Style игнорируются
property Color: TColor; | Цвет кисти
property Handle: Integer; | Дескриптор кисти. Используется при не посредственном обращении к API- функциям Windows
property Style: TBrushStyle; | Стиль кисти (см. рис. 16.3)

16.4.4. Класс TCanvas

Этот класс создает “канву”, на которой можно рисовать чертежными инструментами - пером, кистью и шрифтом. Объекты класса ЕСаnvas автоматически создаются для всех видимых компонентов, которые должны уметь нарисовать себя. Они инкапсулируют объекты Font, pen, Brush, а также многочисленные методы, использующие эти объекты.

Свойства класса:

property Brush: TBrush; property ClipRect: TRect; | Объект-кисть Определяет текущие размеры области, нуждающейся в прорисовке
property CopyMode: TCopyMode; | Устанавливает способ взаимодействия растрового изображения с цветом фона
property Font: TFont; | Объект-шрифт
property Handle: Integer; | Дескриптор канвы. Используется при непосредственном обращении к API-функциям Windows
property LockCount: In tegers; | Счетчик блокировок канвы. Увеличивается на единицу при каждом обращении к методу Lock и уменьшается на единицу при обращении к Unlock
property Pen: TPen; | Объект-перо
property PenPos:TPoint; | Определяет текущее положение пера в пикселях относительно левого верхнего угла канвы
property Pixels[X,Y:Integer]: TColor; | Массив пикселей канвы

Свойство copyMode используется при копировании части одной канвы (источника) в другую (приемник) методом copyRect и может иметь одно из следующих значений:

cmBlackness | Заполняет область рисования черным цветом
cmDestInvert | Заполняет область рисования инверсным цветом фона
cmMergeCopy | Объединяет изображение на канве и копируемое изображение операцией and
cmMergePaint | Объединяет изображение на канве и копируемое изображение операцией OR
cmNotSrcCopy | Копирует на канву инверсное изображение источника
cmNotSrcErase | Объединяет изображение на канве и копируемое изображение операцией OR и инвертирует полученное
cmPatCopy | Копирует образец источника
cmPatInvert | Комбинирует образец источника с изображением на канве с помощью операции XOR
cmPatPaint | Комбинирует изображение источника с его образцом с помощью операции OR, затем полученное объединяется с изображением на канве также с помощью OR
cmSrcAnd | Объединяет изображение источника и канвы с помощью операции AND
cmSrcCopy | Копирует изображение источника на канву
cmSrcErase | Инвертирует изображение на канве и объединяет результат с изображением источника операцией AND
cmSrcInvert | Объединяет изображение на канве и источник операцией XOR
cmSrcPaint | Объединяет изображение на канве и источник операцией OR
cmWhitness | Заполняет область рисования белым цветом

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

Методы класса:

procedure Arc(X1, Y1, X2, Y2, ХЗ, Y3, Х4, Y4: Integer); | Чертит дугу эллипса в охватывающем прямоугольнике (Xl,Yl)-(X2,Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (ХЗ, Y3), а конец - на пересечении с лучом из центра в точку (Х4, Y4). Дуга чертится против часовой стрелки (см. рис. 16.4, а)
procedure BrushCopy (const Dest: TRect; Bitmap: TBitmap; const Source: TRect; Color: TColor); | Копирует часть изображения Source на участок канвы Dest. Color указывает цвет в Dest, который должен заменяться на цвет кисти канвы. Метод введен для совместимости с ранними версиями Delphi. Вместо него следует пользоваться классом Т Image List
procedure Chord(XI, Yl, X2, Y2, X3, Y3, | Чертит сегмент эллипса в охватывающем прямоуголь нике (XI, У1) - (X2, Y2). Начало дуги сегмента лежит на пересечении эллипса и луча, проведенного из его центра в точку (X3, Y3), а конец - на пересечении с лучом из центра в точку (X4, Y4). Дуга сегмента чертится против часовой стрелки, а начальная и конечная точки дуги соединяются прямой (см. рис. 16.4, б)
procedure CopyRect (Dest: TRect; Canvas: TCanvas;Source: TRect) ; | Копирует изображение Source канвы Canvas в участок Dest текущей канвы. При этом разнообразные специальные эффекты достигаются с помощью свойства CopyMode
procedure Draw(X, Y:Integer; Graphic:TGraphic) ; | Осуществляет прорисовку графического объекта Graphic так, чтобы левый верхний угол объекта расположился в точке (X, Y)
procedure DrawFocusRect (const Rect:TRect) ; | Прорисовывает прямоугольник с помощью операции XOR, поэтому повторная прорисовка уничтожает ранее вычерченный прямоугольник. Используется в основном для прорисовки нестандартных интерфейсных элементов при получении ими фокуса ввода и при потере его
procedure Ellipse(XI, Yl, X2, Y2: Integers; | Чертит эллипс в охватывающем прямоугольнике (XI, Yl) - (X2, Y2). Заполняет внутреннее пространство эллипса текущей кистью
procedure FillRect(const Rect: TRect); | Заполняет текущей кистью прямоугольную область Rect, включая ее левую и верхнюю границы, но не затрагивая правую и нижнюю границы
procedure Flood- Fill(X, Y: Integer; Color: TColor; Fill Style: TFillStyle); | Производит заливку канвы текущей кистью. Залив ка начинается с точки (X, Y) и распространяется вовсе стороны от нее. Если FillStyle=fsSurface, заливка распространяется на все соседние точки с цветом Color. Если FillStyle=fsBorder, наоборот, заливка прекращается на точках с этим цветом
procedure FrameRect(const Rect: TRect); | Очерчивает границы прямоугольника Rect текущей кистью толщиной в 1 пиксель без заполнения внутренней части прямоугольника
procedure LineTo(X, Y: Integer); | Чертит линию от текущего положения пера до точки (X,Y)
procedure Lock; | Блокирует канву в многопоточных приложениях для предотвращения использования канвы в других по токах команд
procedure MoveTo(X,Y: Integer); | Перемещает перо в положение (X, Y) без вычерчивания линий
procedure Pie(XI, Yl, X2, Y2, X3, Y3, X4, Y4: Longint); | Рисует сектор эллипса в охватывающем прямоугольнике (XI, Yl) - (X2, Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (X3, Y3), а конец - на пересечении случом из центра в точку (X4, Y4). Дуга чертится против часовой стрелки. Начало и конец дуги соединяются прямыми с ее центром (см. рис. 16.4, в)
procedure Polygon. (Points: array of TPoint) ; | Вычерчивает пером многоугольник по точкам, заданным в массиве Points. Конечная точка соединяется с начальной и многоугольник заполняется кистью. Без заполнения используйте - Polyline
procedure Polyline (Points: array of TPoint); | Вычерчивает пером ломаную прямую по точкам, заданным в массиве Points
procedure Rectan gle(XI, Yl, X2, Y2: Integer); | Вычерчивает и заполняет прямоугольник (XI, Yl) -S(X2, Y2). Для вычерчивания без заполнения используйте FrameRect или Polyline
procedure Refresh; | Устанавливает в канве умалчиваемые шрифт и кисть
procedure RoundRect(Xl, Yl, X2,Y2, X3, Y3: Inte ger) ; | Вычерчивает и заполняет прямоугольник (XI,Yl)- (X2, Y2) со скругленными углами. Прямоугольник (XI, Yl) - (X3, Y3) определяет дугу эллипса для округления углов (см. рис. 16.4, г)
procedure Stretch Draw (const Rect:TRect; Graphic: TGraphic ); | Вычерчивает и при необходимости масштабирует графический объект Graphic так, чтобы он полностью занял прямоугольник Rect
function TextExtent (const Text:String): TSize; | Возвращает ширину и высоту прямоугольника, охватывающего текстовую строку Text
function TextHeight (const Text: String): Integer; | Возвращает высоту прямоугольника, охватывающего текстовую строку Text
procedure TextOut(X, Y: Integer; const Text: String) ; | Выводит текстовую строку Text так, чтобы левый верхний угол прямоугольника, охватывающего текст, располагался в точке (X, Y)
procedure TextRect(Rect: TRect; X, Y: Integer; const Text: String) ; | Выводит текстовую строку Text так, чтобы левый верхний угол прямоугольника, охватывающего текст, располагался в точке (х, Y). Если при этом какая-либо часть надписи выходит из границ прямоугольника Rect, она отсекается и не будет видна
function TextWidth (const Text:String): Integer; | Возвращает ширину прямоугольника, охватывающего текстовую строку Text
function TryLock: Boolean; | Пытается заблокировать канву. Если она не заблокирована другим потоком команд, - True, в противном случае ничего не делает и возвращает False
procedure Unlock; | Уменьшает на 1 счетчик блокировок канвы

16.4.4.1. Дополнительные возможности API-функций

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

Некоторые текстовые функции API

type TLogFont = record If Height: Integer; IfWidth: Integer; IfEs- capment: Integer; IfOri- entation: Integer; IfWeight: Integer; If Italic: Byte; IfUnder- line: Byte; IfStrikeOut: Byte; IfCharSet: Byte; IfOutPrecision: Byte; IfClipPrecision: Byte; IfQuality: Byte; IfPitchAndFamily: Byte; 1fFaceName: PChar ; end; function CreateFont(Font: TLogFont): hFont; | Создает новый шрифт на основе данных в параметре Font. Назначение полей структуры TLogFont см. после таблицы
function DrawText(DC:hDC; pText: PChar; varRect:'TRect; Format:UINT): Integer; | В прямоугольнике Rect выводит многострочный текст, на который указывает pText. Параметр Format используется для форматирования (см.ниже)
function ExtTextOut(DC: hDC; X, Y: Integer; Op tions: Integer; Rect: TRect; pText: PChar; Count: Integer; PX: PInteger): Bool; | Выводит текст с нестандартными меж сим вольными расстояниями: X, Y - верхняя левая точка текста; Options - параметр, управляющий выводом (см. ниже); Rect - ограничивающий прямоугольник; pText - указатель на строку вывода; Count - количество выводимых символов; px - указатель на массивцелочисленных значений, определяющих межсимвольные расстояния: 1-й параметр - расстояние от 1-го до 2-го символа; 2-й параметр- расстояние от 2-го до 3-го символа и т. д.; если какой-то параметр равен 0, используется умалчиваемое межсимвольное расстояние
function GetBkColor(DC:hDC): TColor; | Возвращает цвет фона
function GetBkMode(DC:hDC) : Integer; | Возвращает режим прорисовки фона: Opaque - фон прорисовывается заново при выводе текста; Transparent - фон не прорисовывается
function GetTextAlign(DC: hDC) : Integer; | Возвращает выравнивание текста
function GetTextCharacterExtra(DC: hDC): Integer; | Возвращает межсимвольное расстояние
function SetBkColor(DC:hDC; Color: TColor): TColor; | Устанавливает новый цвет фона и возвращает старый, если обращение успешно
function SetBkMode(DC:hDC; Mode: Integer): Integer; | Устанавливает новый режим прорисовки фона и возвращает старый, если операция успешна
function SetTextAlign(DC: hDC; Flags: Integer): Integer; | Устанавливает новое выравнивание текста и возвращает старое, если вызов успешен
function SetTextCharacterExtra(DC: hDC; CharExtra: Integer): Integer; | Устанавливает новое межсимвольное расстояние и возвращает старое, если вызов успешен

Отдельные поля структуры TLogFont для функции CreateFontIndirect имеют следующий смысл:

if Height - высота шрифта в пунктах (1 пункт = 1/72 дюйма); если больше 0, определяет высоту “знакоместа” (с учетом выступающих над заглавным символом элементов в буквах Е, И), если меньше нуля - высоту “чистого” символа, если 0 - высоту выбирает Windows;

if width - средняя ширина символа; если 0, ширину устанавливает Windows;

ifEscapment - угол наклона базовой линии текста в десятых долях градуса относительно горизонтального направления; положительные значения - поворот по часовой стрелке; в Windows 95/98 совпадает СIfOrientation;

iforientation - угол наклона символов по отношению к базовой линии; в Windows NT для шрифтов True Type может отличаться от ifEscapment; для этого следует установить режим устройства отображения равным gm_Advanced (по умолчанию устанавливается gm_Compatible);

IfWeight - плотность шрифта (fm_DontCare=0 - плотность выбирает Windows; fm_Thin=100 - очень тонкий шрифт;

fm_ExtraLight=200 - очень светлый; fm_Light=300 - светлый;

fm_Normal=400 - нормальный; fm_Medium=500 - утолщенный;

fm_SemiBold=600 - полужирный; fm_Bcld=700 - жирный;

fm_ExtraBold=800 - усиленный; fm_Heavy=900 - тяжелый);

ifitalic, ifunderiine, ifStrikeOut - ненулевое значение означает соответственно наклонный, перечеркнутый и подчеркнутый шрифт;

IfCharSet - Набор символов (ANSI_CharSet=0, Default_CharSet=l;

Symbol_CharSet=2;ShiftJis_CharSet=128;OEM_CharSet=255);

ifoutprecision - точность представления шрифта; рекомендуется out__TT_prec (выбирает True Type и векторные шрифты, если есть несколько разновидностей одноименных шрифтов) или out_TT_oniy_prec (только True Type);

ifciipprecision - определяет точность отсечения надписи границами области прорисовки (Clip_Character_Precis, Clip_Embedded, Clip_Mask, Clip_TT_Always, Clip_Default_Precis - рекомендуется, Clip_LH_Angles, Clip_Stroke_Precis);

IfQuality - Определяет качество прорисовки (Default_Quality, Draft_Quality,Proof_Quality);

ifpitchAndFamily - в четырех младших разрядах указывается тип шрифта, в четырех старших ~ его семейство;

IfFaceName - имя гарнитуры шрифта.

Вот как реализован этот пример.

procedure TForm1.FormPaint(Sender:TObject) ;

var

X: Integer;

LF: TLogFont;

Fnt: HFont;

const

Text = 'Лучшая в мире система программирования';

begin

// Определяем параметры нового шрифта

FillChar(LF, SizeOf(LF), 0) ;

with LF do

begin

If Height := 20;

If Weight := fw_Normal;

If Underline := 1;

If Escapement := 450;

StrPCopy(lfFaceName, 'Courier New Cyr');

end;

with Forml.Canvas do

begin

// Создаем шрифт

Fnt := CreateFontIndirect(LF) ;

// Присваиваем его дескриптор шрифту канвы

Font.Handle := Fnt;

// Выводим текст под углом +45 градусов

Text0ut(0, 300, Text);

X := TextWidth(Text) ;

DeleteObject(Fnt); // Удаляем ненужный шрифт

// Изменяем параметры шрифта

with LF do

begin

If Height := 90;

IfEscapement := -900;

IfWeight := fw_Heavy;

StrPCopy(LF.lfFaceName, 'Arial Cyr');

end;

Fnt := CreateFontIndirect(LF); // Создаем новый шрифт

Font.Handle := Fnt;

Font.Color := clRed;

// Выводим с наклоном -90 градусов

TextOut(X-10, 10, 'Delphi 5');

DeleteObject(Fnt); // Удаляем ненужный шрифт

end;

end;

Параметр Format функции DrawText может содержать один или несколько следующих флагов.

Возможные значения параметра Format функции DrawText

dt Bottom | Текст прижимается к нижней части области Rect
dtCalcRect | Разрешает динамически изменять размеры области Rect
dtCenter | Текст центрируется по горизонтали
dtEditControl | Функция дублирует свойства отображения многострочного тестового редактора. В частности, таким же способом вычисляется средняя ширина символа и не показывается частично видимая последняя строка
dtExpandTabs | Символы табуляции заменяются пробелами
dt ExternalLeading | В высоту строки включается высота межстрочного интервала
dtLeft | Текст прижимается к левой части области Rect
dtNoClip | Текст не отсекается границами Rect
dtNoPrefix | Символы & не заменяются подчеркиванием
dtRight | Текст прижимается к правой части области Rect
dt SingleLine | Весь текст выводится единственной строкой, символы EOLN игнорируются
dt TabsStop | Символы табуляции не заменяются пробелами
dtTop | Текст прижимается к верхней части области R'ect
dtVCenter | Текст центрируется по вертикали
dtWordBreak | Разрешает переход на новую строку при достижении пра вой границы Rect; разрыв строки - на границе слова; символы EOLN также переводят вывод на следующую строку

Если установлен флаг dt_caicRect, функция изменяет высоту и ширину прямоугольника так, чтобы вывести весь текст, но сам текст не выводится. Если в тексте несколько строк, ширина вывода не меняется. Функция возвращает истинную высоту прямоугольника вывода.

Параметр options функции ExtTextOut может быть комбинацией следующих значений:

eto_Сlipped - текст будет отсекаться границами Rect;

eto_Gliph_index - блокирует обработку языковым драйвером;

eto_opaque - фон перерисовывается заново;

eto_RTLReading - вывод для чтения справа налево. При выводе текста стандартными методами rcanvas всегда заново прорисовывается фон символов (цвет фона возвращает функция GetBkColor). Если цвет фона символов отличается от фона канвы, вывод сопровождается неприятными побочными эффектами. Если в предыдущем примере для формы оставить стандартный цвет ciFaceBtn, окно вывода будет таким, как на рис. 16.6. Конечно, с помощью функции setBkcoior можно установить цвет формы совпадающим с цветом канвы, однако это не всегда возможно. Ясно, что установить переменный цвет функцией SetBkcoior невозможно. Более того, заглавные надписи программ Setup.exe также традиционно выводятся утолщенным наклонным шрифтом Times New Roman белыми буквами с черной тенью. Реализовать такой эффект достаточно просто: нужно вообще отказаться от прорисовки фона, установив с помощью функции SetBkMode режим Transparent, и вывести надпись дважды: первый раз черным цветом, а второй -белым, сместив вторую надпись немного влево и вверх относительно первой. Следующий листинг иллюстрирует сказанное.

procedure TFormI.FormPaint(Sender: TObject);

var

Y: Integer;

Blue: Byte;

const

Text = 'Фон для программы Setup.exe';

begin

with Forml.Canvas do

begin

for Y := 0 to Forml.Height-1 do

begin

// Уменьшаем интенсивность цвета с ростом ординаты Y

Blue := Round($FF*(Forml.Height-Y)/Forml.Height);

Pen.Color := RGB(0, 0, Blue); // Формируем цвет

MoveTo(0, Y); // Чертим линию

LineTo(Forml.Width-1, Y) ;

end; //for Y := 0 to Forml.Height-1 do Font.Size := 32;

Font.Style := [fsBold, fsltalic, fsUnderline];

Font.Name := 'Times New Roman";

// Это обращение накладывает текст на фон SetBkMode(Handle, Transparent);

// Сначала выводим тень надписи

Font.Color := clBlack;

Text0ut(40, 30, Text);

// Теперь саму надпись Font.Color := clWhite;

Text0ut(36, 26, Text)

end; //with Forml.Canvas do end;

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

SetBkMode(Handle, Transparent);

можно использовать

Brush.Style := bsClear;

16.4.4.2. Области

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

uses

Forms,

Unit1 in 'Unitl.pas' {Formi}, Windows; // Этот модуль экспортирует функции API

{$R *.RES} var

Rgn: Cardinal;

begin

// Сначала создаем обычное окно:

Application.CreateForm(Tform1, Form1);

with Formi.Canvas, Formi do

begin

// Теперь создаем в нем эллиптическую область

Rgn := CreateEllipticRgn(0, 0, Width, Height);

// и показываем ее вместо окна

SetWindowRgn(Handle, Rgn, True);

end;

Application.Run;

end.

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

Работа с областями

function CombineRgn(DestRgn, Rgnl, Rgn2: Cardinal; Com-bineMode: Ineger): Integer; | Объединяет две области в одну: DestRgn - дескриптор результирующей области; Rgnl, Rgn2 -дескрипторы объединяемых областей; CombineMode - параметр, указывающий способ объединения; этот параметр, а также возвращаемое значение описаны ниже
function CreateEllipticRgn(XI, Yl, X2, Y2: Integer) : Cardinal; | Создает эллиптическую область
function CreatePoly-gonRgn(var Points; Count, PolyFillMode: Integer): Cardinal; | Создает область в виде многоугольника: Points - массив координат всех вершин многоугольника; Count - количество вершин; PolyFillMode -режим заполнения многоугольника (см. ниже)
function CreateRectRgn(XI, Yl, X2, Y2: Integer): Cardi nal; | Создает прямоугольную область
function CreateRoundRectRgn(Xl, Yl, X2, Y2, X3, Y3: Integer): Cardinal; | Создает область со скругленными углами; параметры обращения иллюстрирует
function EqualRgn(Rgnl,Rgn2: Cardibal): Bool; | Проверяет эквивалентность двух областей
function FillRgn(DC, Rgn,Brush: Cardinal): Bool; | Закрашивает область кистью: DC - дескриптор канвы окна; Rgn - дескриптор области; Brush - дескриптор кисти
function FrameRgn(DC, Rgn,Brush: Cardinal; Width,Height: Integer): Bool; | Обводит (но не заполняет) область кистью высотой Height и шириной Width пикселей
function InvertRgn(DC, Rgn:Cardinal): Bool; | Инвертирует все пиксели внутри области
function OffsetRgn(DC, Rgn:Cardinal; dX, dY: Integer):Integer; | Смещает область на dX по горизонтали и на dY по вертикали
function PaintRgn(DC, Rgn:Cardinal): Bool; | Закрашивает область текущей кистью
function PtInRegion(Rgn:Cardinal; X, Y: Integer):Bool; | Проверяет принадлежность точки (X,Y) области Rgn
function RectInRegion(Rgn:Cardinal; Rect: TRect):Bool; | Проверяет принадлежность прямоугольника Rect области Rgn
function SetWindowRgn(DC,Rgn: Cardina; Redraw: Bool): Integer; | Показывает принадлежащую окну с канвой DC область Rgn вместо окна; Redraw- разрешает/запрещает перерисовку области; все не вошедшие в область части окна не показываются (см. ниже)

Параметр combineMode в функции combineRgn может значения:

rgn_and - включает в результат общие части областей; rgn_copy -копирует область Rgnl В DestRgn; RGN_DIFF - исключает из Rgnl часть, принадлежащую Rgn2; rgn_or - объединяет области; rgn_xor -включает в результат только те части областей, которые не принадлежат им одновременно. Область DestRgn уже должна существовать и иметь размеры, достаточные для размещения объединенной области. При обращении к функции createPoiygonRgn параметр Poly FillMode может иметь значения: Alternate И Winding. В первом случае заполнение многоугольника идет последовательно между двумя близлежащими его сторонами: сначала между 1-й и 2-й, затем между 2-й и 3-й и т. д. В режиме winding заполняется все внутреннее пространство области.

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

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls;

tуре

Tform1 = class(TForm)

PaintBoxl: TPaintBox;

PaintBox2: TPaintBox;

Button1: TButton;

procedure ButtonlClick(Sender: TObject);

private

{ Private declarations } public

{ Public declarations } end;

var

Form1: Tform1;

implernentation

$R *.DFM)

procedure TFormI.ButtonlClick(Sender: TObject);

var

Rgnl, Rgn2: Cardinal;

const

Points: array [1..5, 1..2] of Integer =

C(91, 0), (160, 183), (0, 70), (183, 70), (23, 183));

begin

Rgn1 := CreatePolygonRgn(Points, 5, Alternate);

Rgn2 := CreatePolygonRgn(Points, 5, Winding);

with PaintBoxl.Canvas do begin

Brush.Color := clBlack;

FillRgn(Handle, Rgnl, Brush.Handle);

end;

with PaintBox2.Canvas do begin

Brush.Color := clBlack;

FillRgn(Handle, Rgn2, Brush.Handle) ;

end;

end;

end.

Обратите внимание: прорисовка и объединение областей доступны, только если области видны в окне (для создания предыдущего рисунка нельзя, например, использовать обработчик формы OnActivate).

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

16.4.5. Классы TGraphic и TPicture

Важное место в графическом инструментарии Delphi занимают классы TGraphic и TPicture.

TGraphic - это абстрактный класс, инкапсулирующий общие свойства и методы трех своих потомков: пиктограммы (TIcon), метафайла (TMetafile) и растрового изображения (TBitmap). Общей особенностью потомков TGraphic является то, что обычно они сохраняются в файлах определенного формата. Пиктограммы представляют собой небольшие растровые изображения, снабженные специальными средствами, регулирующими их прозрачность. Для файлов пиктограмм обычно используется расширение ico. Метафайл - это изображение, построенное на графическом устройстве с помощью специальных команд, которые сохраняются в файле с расширением wmf или emf. Растровые изображения - это произвольные графические изображения в файлах со стандартным расширением bmp.

Свойства класса TGraphic:

property Empty: Boolean; | Содержит True, если с объектом не связано графическое изображение
property Height: Integer; | Содержит высоту изображения в пикселях
property Modified: Boolean; | Содержит True, если графический объект изменялся
property Palette: HPALETTE; | Содержит цветовую палитру графического объекта
property PaletteModified: Boolean; | Содержит True, если менялась цветовая палитра графического объекта
property Transparent: Boolean; | Содержит True, если объект прозрачен для фона, на котором он изображен
property Width: Integer; | Содержит ширину изображения в пикселях
Методы Класса TGraphic:
procedure LoadFromClipooardFormat (AFormat: Word;AData: THandle; APalette:HPALETTE) ; | Ищет в буфере межпрограммного обмена Clipboard зарегистрированный формат AFormat и, если формат найден, загружает из буфера изображение AData и его палитру Apalette
procedure LoadFromrile(const FileName:String) ; | Загружает изображение из файла FileName
procedure LoadFromStream(Stream: TStream); | Загружает изображение из потока данных Stream
procedure SaveToClipboardFormat (var AFormat: Word;var AData: THandle; varAPalette: HPALETTE); | Помещает графическое изображение Adata и его цветовую палитру APalette в буфер межпрограммного обмена в формате Af ormat
procedure SaveToFile(constFileName: Strings; | Сохраняет изображение в файле FileName
procedure SavePoStream(Stream: TStream); | Сохраняет изображение в потоке Stream

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

Свойства класса TPicture:

property Bitmap: TBitmap; | Интерпретирует графический объект как растровое изображение
property Graphic: TGraphic; | Содержит графический объект
property Height: Integer; | Содержит высоту изображения в пикселях
property Icon: TIcon; | Интерпретирует графический объект как пиктограмму
property Metafile: TMetafile; | Интерпретирует графический объект как метафайл
property width: Integer; | Содержит ширину изображения в пикселях

Методы класса TPicture:

procedure Assign(Source: TPersistent) ; | Связывает собственный графический объект Graphic с объектом Source
procedure LoadFromClipboardFormat (AFormat: Word;AData: THandle; APalette:HPALETTE) ; | Ищет в буфере межпрограммного обмена Clipboard зарегистрированный формат AFormat и, если формат найден, загружает из буфера изображение AData и его палитру APalette
procedure LoadFromFile(const FileName:String); | Загружает изображение из файла FileName
class procedure Register; ClipboardFormat(AFormat: Word; AGraphicClass: TGraphicClass) ; | Используется для регистрации в Clipboard нового формата изображения
class procedure Register; FileFormat (const AExtension, ADescription: String; AGraphicClass: Tgraphic Class) ; | Используется для регистрации нового файлового формата
class procedure RegisterFileFormatRes(const AExtension: String; ADescriptionResID: Integer; Agraphic Class: TGraphicClass); | Используется для регистрации нового формата ресурсного файла
procedure SaveToClipboardFormat (var AFormat: Word; var AData: THandle; varAPalette: HPALETTE); | Помещает графическое изображение AData и его цветовую палитру APalette в буфер межпрограммного обмена в формате AFormat
procedure SaveToFile(const FileName: Strings; | Сохраняет изображение в файле FileName
class function SupportsClipboardFormat(AFormat: Word): Boolean; | Возвращает True, если формат Aformat зарегистрирован в буфере межпрограммного обмена Clipboard
class procedure UnregisterGraphicClass(AClass:TGraphicClass); | Делает недоступными любые графические объекты класса Aclass


Глава 17. ОБЩИЕ СВОЙСТВА КОМПОНЕНТОВ

  • Глава 17. ОБЩИЕ СВОЙСТВА КОМПОНЕНТОВ
    • ИЕРАРХИЯ КОМПОНЕНТОВ
    • ИМЕНА И СОБСТВЕННИКИ КОМПОНЕНТОВ
    • РОДИТЕЛЬСКИЕ И ДОЧЕРНИЕ КОМПОНЕНТЫ
    • ПОЛОЖЕНИЕ, РАЗМЕРЫ И ОФОРМЛЕНИЕ КОМПОНЕНТОВ
    • УКАЗАТЕЛИ МЫШИ
    • РЕАКЦИЯ НА СОБЫТИЯ ОТ МЫШИ И КЛАВИАТУРЫ
      • События от мыши
      • События от клавиатуры
      • Клавиатура в MS-DOS и Windows
      • Фокус ввода
    • МЕХАНИЗМ ДЕЙСТВИЙ ACTION
    • ИНТЕРФЕЙС DRAG&DROP
    • ИНТЕРФЕЙС DRAG&DOCK
    • ПОДДЕРЖКА СПРАВОЧНОЙ СЛУЖБЫ
    • ПОДДЕРЖКА СОМ
    • СВОЙСТВА РАЗНОГО НАЗНАЧЕНИЯ

Глава 17

ОБЩИЕ СВОЙСТВА КОМПОНЕНТОВ

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

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

17.1. ИЕРАРХИЯ КОМПОНЕНТОВ

Все компоненты Delphi порождены от класса TComponent, в котором инкапсулированы самые общие свойства и методы компонентов. Предком TComponent является класс TPersistent, который произошел непосредственно от базового класса TObject.

Класс TPersistent передает своим потомкам важный виртуальный метод

procedure Assign(Source: TPersistent);

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

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

Класс TComponent служит базой для создания как видимых, так и невидимых компонентов.

Большинство видимых компонентов происходит от класса ТСоntrol. Два наследника этого класса - TWinControl И TGraphicControопределяют две группы компонентов: имеющие оконный ресурс

rwincontroi и его потомки) и не имеющие этого ресурса

TGraphicControl и его потомками).

Оконный ресурс - это специальный ресурс Windows, предназначенный для создания и обслуживания окон. Только оконные компоненты способны получать и обрабатывать сообщения Windows.

Оконный компонент в момент своего создания обращается к Windows с требованием выделения оконного ресурса и, если требование удовлетворено, получает так называемый дескриптор окна. TWinControl и его потомки хранят дескриптор окна в свойстве Handle.

Программист может использовать этот дескриптор для непосредственного обращения к API-функциям Windows. 1 Потомки TGraphicControl не требуют от Windows дефицитного оконного ресурса, но они и не могут получать и обрабатывать Win-jows-сообщения - управляет такими элементами оконный компонент-владелец (например, форма), который является посредником между Windows и не оконными компонентами.

17.2. ИМЕНА И СОБСТВЕННИКИ КОМПОНЕНТОВ

Класс TComponent включает в себя свойства и методы, общие для всех компонентов.

Свойство

property Name: TComponentName;

type TComponentName = String; // Delphi 32

type TComponentName = String[63]; // Delphi 1

определяет имя компонента. Имя компонента строится по тем же правилам, что и имена любых других объектов программирования - констант, переменных, подпрограмм и т. д.: оно представляет собой правильный идентификатор и должно быть уникальным в области своей видимости в программе. Так как компоненты помещаются на форму средой Delphi, каждый компонент автоматически получает создаваемое средой имя, совпадающее с именем своего класса (без начальной буквы Т) и дополненное числовым суффиксом: Form1, Labell2, RichEdit2 И Т. Д.

Впоследствии программист может переименовать компонент, чтобы сделать текст программы более “читабельным”. При разработке собственных имен полезно выработать свою систему двух- или трехбуквенных префиксов, кодирующих тип компонента.

Например fm - для формы TForm, ib - для метки TLabel, ed - для редактора TEdit и т. п. Комбинируя префикс с мнемоническим именем, можно существенно улучшить читаемость программы и усилить контроль за правильным использованием свойств и методов компонента.

Например, оператор

IbOutput.Text := 'Текст';

сразу вызовет подозрение, т. к. префикс ib определяет компонент TLabel, который не имеет свойства или поля с именем Text. Свойство

property Tag: Integer;

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

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

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

property Owner: TComponent;

указывает на владельца компонента (это свойство доступно только для чтения), а свойство

property Componentlndex: Integer;

на положение компонента в массиве components своего владельца. Этот массив определяется свойством

property Components[Index: Integer]: TComponent;

и содержит список всех компонентов, которыми владеет данный компонент. Количество зарегистрированных в списке компонентов возвращает свойство

property ComponentCount: Integer;

Конструктор TComponet. Create имеет единственный параметр обращения:

constructor Create(AOwner: TComponent);

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

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

procedure DestroyComponents; | Вызывает поочередно деструкторы всех компонентов из списка Components и удаляет все компоненты из списка
function FindComponent(constName: String): TComponent; | Отыскивает в списке компонент с именем AName и возвращает ссылку на него
procedure insertComponent AComponent: TComponent); | Вставляет компонент AComponent в конец списка Components
procedure RemoveComponent AComponent: TComponent); | Удаляет компонент AComponent из списка

17.3. РОДИТЕЛЬСКИЕ И ДОЧЕРНИЕ КОМПОНЕНТЫ

Класс TControl со своими наследниками образуют всю палитру видимых компонентов Delphi. Терминологически они называются элементами управления, так как на их основе прежде всего реализуются управляющие элементы Windows - кнопки, переключатели, списки и т. п. В тексте книги я часто буду употреблять слова компонент и элемент как синонимы.

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

Класс TControl определяет свойство parent, которое содержит ссылку на родительский компонент:

property Parent: TWinControl;

Это свойство не следует путать с собственником owner: owner создал компонент (не обязательно - видимый), a parent управляет видимым компонентом. поскольку конструктор TComponent. Create не

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

вставит надпись дочерний элемент в левый верхний угол формы:

procedure TFormI.FormCreate(Sender: TObject);

var

IbLabel: TLabel;

begin

IbLabel := TLabel.Create(Self);

IbLabel.Parent := Self;

IbLabel.Caption := 'Дочерний элемент';

end;

Если убрать оператор

IbLabel.Parent := Self;

метка никогда не узнает о том, что пришла пора прорисовать себя на экране, и ее текст не будет виден. Наоборот, изменение свойства parent подключает метку к списку дочерних элементов формы, и оконная функция формы обратится к нужному методу метки, чтобы заставить ее появиться на экране в момент появления самой формы. (Замечу, что приведенный выше пример является образчиком того, как не надо программировать: локальная переменная IbLabel будет уничтожена после выхода из процедуры FormCreate, содержащийся в ней указатель будет безвозвратно потерян для программы, и она никогда не сможет освободить связанные с меткой ресурсы памяти[ На самом деле ресурсы будут автоматически освобождены при завершении работы программы.]. Правильнее было бы расположить переменную IbLabel вне процедуры, например, в секции public класса TForm1, чтобы в обработчике события OnDestroy формы выполнить вызов деструктора метки.)

Помимо свойства components каждый оконный компонент получает от своего родителя TWinControl свойство property Controls[Index: Integer]: TControl;

содержащее список дочерних элементов. Свойство

property ControlCount: Integer;

возвращает количество дочерних элементов (длину массива controls). Работать со списком controls можно с помощью следующих методов:

function ContainsControl( Control: TControl): Boolean; | Возвращает True, если компонент Control является дочерним элементом
function ControlAtPos(constPos: TPoint; AllowDisabled: Boolean): TControl; | Возвращает ссылку на компонент, которому принадлежит точка с координатами Роз. Если AllowDisabied=True, поиск ведется также среди запрещенных для выбора компонентов
function FindNextControl ( Curcontrol: TWinControl; GoForward, CheckTabStop, CheckParent: Boolean): TWinControl; | Ищет новый элемент в списке Controls. Cur-control определяет начальную позицию поиска; GoForward=True, если поиск идет от начальной позиции к концу списка; CheckTab Stop=True, если поиск идет только среди компонентов со свойством TabStop=True; если checkParent=True, поиск идет только среди оконных элементов
procedure GetTabOrderListfList: TList) ; | Создает список List всех дочерних компонентов в порядке их выбора клавишей Tab
procedure InsertCon- trol(AControl: TControl); | Вставляет новый дочерний элемент в список Controls. Программа не должна обращаться непосредственно к этому методу: для вставки дочернего элемента следует установить нужное значение свойства Parent во вставляемом компоненте
procedure NotifyControls(Msg: Word) ; | Посылает сообщение Msg во все дочерние оконные компоненты
procedure PaintControls(DC: HDC; First: TControl); | Перерисовывает все дочерние компоненты начиная с компонента First. Каждый дочерний компонент получает дескриптор графического устройства DC, с помощью которого он сможет прорисовать себя
procedure Realign; | Перерисовывает все компоненты со свойством Align не равным noAlign. Вызывается после изменения размеров родительского компонента.
procedure Repaint; | Перерисовывает родительский компонент и все его дочерние компоненты
procedure ScaleControls(M, D:Integer) ; | Перерисовывает все дочерние элементы с раз мерами, измененными в M./D раз по сравнению с предыдущими размерами
procedure SelectFirst; | Выбирает первый в порядке обхода клавишей Tab дочерний компонент
procedure SelectNext(CurControl: TWinControl; GoForward, CheckTabStop: Boolean) ; | Выбирает очередной в порядке обхода клавишей Tab дочерний компонент. CurControl определяет начальную точку поиска; GoForward= True, если поиск идет к концу списка; CheckTabStop=True, если выбираются только компоненты со свойством TabStop=True
procedure SetChildOrder(Child:TComponent; Order: Integers- | Устанавливает новый порядок выбора клавишей Tab компонента Child
procedure ShowControl(AControl:TControl); | Показывает дочерний компонент Acontrol

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

function Perform(Msg, WParam: Word; LParam: Longint): Longint;

Параметры обращения к методу соответствуют параметрам вызова

оконной функции: Msg - код сообщения; WParam, LParam - уточняющие параметры.

17.4. ПОЛОЖЕНИЕ, РАЗМЕРЫ И ОФОРМЛЕНИЕ КОМПОНЕНТОВ

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

property Height: Integer; // Высота

property Left: Integer;// Положение левой кромки

property Top: Integer;// Положение верхней кромки

property Width: Integer;// Ширина

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

property BoundsRect: TRect;

type

TPoint = record X: Longint;

Y: Longint;

end;

TRect = record case Integer of

0: (Left, Top, Right, Bottom: Integer);

1: (TopLeft, BottomRight: TPoint);

end;

Это свойство удобно использовать при прорисовке компонента методом TCanvas . FrameRect.

В некоторых случаях бывает необходимо пересчитать относительные координаты точки внутри клиентской части в абсолютные координаты экрана и наоборот. Эта задача решается двумя методами Control:

function ClientToScreen(const Point: TPoint): TPoint;

function ScreenToClient(const Point: TPoint): TPoint;

Важную роль играет свойство Align, определяющее выравнивание положения компонента относительно границ своего родителя:

type TAlign = (aINone, alTop, alBottom, alLeft, alRight, alClient) ;

property Align: TAlign;

Если это свойство не равно aiNone, компонент прижимается к верхней (alTop), нижней (alBottom), левой (alLeft) или правой (alRight) границе своего родителя. При этом размеры компонента по соседним с границей измерениям игнорируются, и компонент “растекается” по границе. Например, если Align=alTop, значения свойств компонента Left и width игнорируются и его прямоугольник будет занимать всю верхнюю часть клиентской области родителя высотой Height пикселей; если Align=alLeft, свойства тор и Height игнорируются и прямоугольник занимает левую часть родителя шириной width пикселей и т. д. Если несколько компонентов имеют одинаковое выравнивание, они последовательно прижимаются друг к другу в порядке их перечисления в свойстве controls: первый прижимается к границе родителя, второй - к границе первого и т. д. Вся не заполненная другими компонентами клиентская область родителя заполняется компонентами со свойствами Align=alClient, которые в этом случае накладываются друг на друга. Замечательной особенностью свойства является его постоянство при изменении размеров клиентской части родителя. Если, например, компонент прижат к верхней границе формы, он будет неизменно занимать верхнюю часть клиентской области при любых изменениях размеров окна. Таким способом можно легко реализовать панели с инструментальными кнопками, панели статуса и т. п. Временное отключение и затем включение эффекта от свойства Align обеспечивается методами

procedure DisableAlign;

procedure EnableAlign;

Любой видимый компонент можно спрятать или показать с помощью свойства visible или методами Hide и show:

property Visible: Boolean; // True - показывает

procedure Hide; // Прячет компонент

procedure Show; // Показывает компонент

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

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

procedure BringToFront; // Сделать верхним

procedure SendToBack; // Сделать нижним

Свойство

property Enabled: Boolean;

определяет возможность активизации компонента. Если оно имеет значение False, компонент запрещен для выбора. Такие компоненты (точнее, надписи на них) обычно отображаются серым цветом.

Некоторые компоненты имеют плоское изображение (например, метка TLabel), другие - всегда объемное (например, кнопка TButton).

Для остальных элементов объемность изображения регулируется свойством

property Ctl3D: Boolean;

С каждым управляющим компонентом связывается текстовая строка, которая становится доступна либо через свойство Caption, либо через свойство Text (альтернативой свойству Text, которое имеет тип string, является свойство TControi.windowsText типа pchar). Независимо от того, какое свойство хранит эту строку, ее можно установить и получить соответственно методами setTextBuf и GetTextBuf, при этом метод GetTextLen возвращает длину строки:

procedure SetTextBuf(Buffer: PChar);

function GetTextBuf(Buffer: PChar; BufSize: Integer): Integers;

function GetTextLen: Integer;

Если эта строка выводится в компоненте, используется шрифт, задаваемый свойством Font:

property Font: TFont;

В этом случае свойство

type TAlignment = (taLeftJustify, taRightJustify, taCenter);

property Alignment: TAlignment;

регулирует расположение текста относительно границ компонента:

taLeftJustify - прижать к левой границе; taRightJustify - прижать к правой границе; taCenter - расположить по центру.

С помощью придания свойству

property DesktopFont: Boolean;

значения True можно потребовать от компонента, чтобы он выводил текст системным шрифтом Windows.

Видимая часть элемента заливается цветом Color:

property Color: TColor;

Обычно значение этого свойства выбирается из таблицы стандартных цветов Windows в виде константы clxxxx (перечень этих констант содержит раскрывающийся список свойства). В некоторых случаях может потребоваться залить компонент нестандартным цветом. В этом случае учтите, что свойство Color хранит четырехбайтное значение, каждый байт которого (слева направо, т. е. от старшего к младшему) имеет следующее назначение:

  • 1 - указатель формата цвета (см. ниже);
  • 2, 3, 4 - интенсивность соответственно синей, зеленой и красной

составляющих.

Например, значение $00000000 определяет черный цвет, $00ff0000 -чистый синий цвет, $00ffffff - белый цвет и т. д.

Старший байт указывает, как используются остальные байты значения. Если он равен нулю, они определяют RGB-цвет так, как это описано выше. Если старший байт равен 1, три оставшихся байта определяют номер одной из 65536 возможных логических палитр (второй байт в этом случае игнорируется). Наконец, если старший байт равен 2, младшие определяют относительный цвет: в этом случае Windows отыскивает в текущей логической палитре ближайший к указанному цвет и использует его для заливки компонента. Другие значения старшего байта игнорируются (точнее, игнорируются старшие 5 разрядов этого байта; самый старший бит, если он равен 1, определяет черный цвет независимо от значений остальных 31 разрядов).

Чтобы разнообразить унылые серые окна программы, соответствующие стандартным формам Delphi, я иногда использую значение $00AAffff, определяющее бледно-желтый цвет (в свойство color можно вводить значения в виде десятичных или шестнадцатеричных чисел). Получить шестнадцатеричное значение того или иного цвета вам поможет несложная программа, окно которой показано на рис. 17.1. С ее помощью можно выбрать цвет в окне компонента TColor-Dialog (см. дальше п. 17.5.4) и получить его шестнадцатеричное представление или ввести шестнадцатеричное число и увидеть соответствующий ему цвет.

Рис. 17.1. Окно програлшы для определения цветовых констант

  1. Положите на пустую форму две панели - они будут наглядно демонстрировать цвет, выбранный с помощью TCoiorDialog (левая панель), и цвет, заданный с помощью 16-ричной константы (правая панель). Установите в свойство Align панели Panel1. Значение alRight, а в такое же свойство Рапе12 - alClient.
  2. На левую панель положите кнопку TButtcni и диалог TCoiorDialog (этот компонент находится на странице Dialogs палитры компонентов). В значение caption кнопки поместите строку. Выбор цвета.
  3. На правую панель положите редактор TEdit и кнопку TButton. В свойство charCase редактора поместите значение ecupper-case - это заставит компонент преобразовывать вводимые в нем буквы к заглавным и упростит дальнейший их анализ. В Caption кнопки поместите Ввод константы.
  4. Напишите такой обработчик события OnClick кнопки Button1 (в нем открывается диалоговое окно компонента ColorDialog1, закрашивается выбранным цветом левая панель panel 1, в заголовок окна помещается десятичное представление цветовой константы, а в редактор Editi - ее 16-ричное представление:

procedure TFormI.ButtonlClick(Sender: TObject) ;

begin

{Вызываем диалог CoiorDiaiogi и контролируем его результат:

если его метод Execute возвращает True, пользователь выбрал нужный цвет:}

if CoiorDiaiogi.Execute then begin

//В заголовок окна помещаем iu-чное представление цвета:

Caption := IntToStr(CoiorDiaiogi.Color);

//Закрашиваем левую панель выбранным цветом:

Рапе12.Color := CoiorDiaiogi.Color;

//Помещаем в Edit1 и в заголовок левой панели 16-ричное

// представление цвета:

Editl.Text := IntToHex(ColorDialogl.Color,8);

Panel2.Caption := Editl.Text

end

end;

5. В обработчике события нажатия правой кнопки нужно преобразовать введенное в редакторе 16-ричное представление цветовой константы в целое число и поместить его в свойство caption правой панели. Поскольку нет стандартной функции преобразования символьного представления 16-ричной константы в число, обработчик использует вспомогательную функцию HexStrToInt:

function HexStrToint(S: String): Integer;

(Функция преобразует 16-ричное представление S целого числа в тип Integer} function HDigitaKC: Char): Byte;

//Преобразует 16-ричную цифру С в число

begin

case С of 'О'..'9': Result := ord(C)-ord('0');

'A'..'F': Result := ord(C)-ord('A')+10;

else

{Если очередной символ не относится к допустимым для представления 16-ричной цифры, возбуждаем исключительную ситуацию, которая блокирует преобразование и сообщает пользователю об ошибке:}

raise EConvertError.Create

('Недопустимое представление 16-ричного числа');

end;

end; //HDigital

var

k: Integer;

begin //HexStrToInt

Result := 0;

//Удаляем возможный символ-указатель 16-ричного числа:

if S[1]='$' then delete(S,1,1) ;

//Цикл преобразования строки:

for k := 1 to Length (S) do

Result := Result*16+HDigital(S[k]);

end; //HexStrToInt

procedure TForml.Button2Click(Sender: TObject);

begin

Panell.Color := StrToHex(Editl.Text);

Panell.Caption := Edit1.Text

end;

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

property ParentColor: Boolean;

property ParentCtl3d: Boolean;

property ParentFont: Boolean;

property ParentHint: Boolean;

Если, например, цвет компонента не задан явно свойством color и его свойство ParentColor имеет значение True, компонент будет использовать при прорисовке цвет своего родителя, а изменение цвета родителя приведет к немедленному изменению цвета дочернего компонента. Явная установка значений свойств color, Cti3D, Font или Hint приводит к автоматической установке значений False в соответствующие свойства ParentXXX.

Некоторые компоненты очерчиваются двойной кромкой - внешней и внутренней. В класс TWinControl Delphi 4 введены специальные свойства, управляющие трехмерным эффектом, создаваемым такими кромками:

type TBevelEdge = (beLeft, beTop, oeRight, beBottom) ; TBevelEdges = set of TBevelEdge; property BevelEdges: TBevelEdges; | Определяет стороны компонента, которые будут содержать кромки: beLeft -левая; bеТор - верхняя; beRight - правая; beBot tom - нижняя
type TBevelCut = (bvNone, bvLow- ered, bvRaised, bvSpace) ; property Bevellnner: TBevelCut; | Определяет тип внутренней кромки: bvNone- нет рамки; bvLowered. - вдавленная кром ка; bvRaised - выпуклая кромка; bvSpace -кромки нет, но для нее отводится место толщиной 1 пиксель
type TBevelKind = (bkNone, okTile, bkSoft, bkFlat) ; property BevelStyle: TBevelKind; | Указывает тип кромок: bkNone - нет кромок; bkTile - есть внутренняя и внешняя кромка; bksoft - то же, что и bkTile, но менее контрастная внешняя кромка; bkFlat - есть только внешняя кромка толщиной 1пиксель
property BevelOuter: TBevelCut; | Определяет тип внешней кромки
type TBevelWidth = l..Max!nt; property BevelWidth: TBevelWidth; | Задает толщину линий кромки
type TBorderWidth = O..MaxInt; property BorderWidth: TBorder Width; | Определяет расстояние в пикселях от внешней кромки до внутренней

В версиях Delphi 4 ... 6 в класс TControl введено свойство

property Anchors: TAnchors;

type TAnchors = set of TAnchorKind;

type TAnchorKind = (akTop, akLeft, akRight, akBottom);

определяющее способ фиксирования дочернего компонента относительно границ контейнера, в котором он размещен. По умолчанию это свойство имеет значение [akTop, akLeft], что означает фиксацию компонента относительно левого верхнего угла контейнера. В результате возможные изменения размеров контейнера никак не скажутся на изменении положения и/или размеров компонента. Если установить значение [akRight, akBottom], правая и нижняя кромки компонента всегда будут располагаться на одном и том же расстоянии от соответствующих кромок контейнера. Так как положение левого верхнего угла компонента при этом не фиксируется, он будет “плавать” внутри контейнера, всегда располагаясь на одном и том же расстоянии от его правой и нижней кромки. Однако, если установлено значение [akTop, akLeft, akRight, akBottom], левый верхний угол будет зафиксирован, и компонент будет пытаться отслеживать расстояние до правого нижнего угла контейнера с помощью изменения своих размеров. Поместите на пустую форму редактор TEdit и поэксперименти-руйте с его свойством Anchors, каждый раз изменяя мышью размеры формы (не обязательно запускать программу - свойство Anchors работает и на этапе конструирования). В Delphi 4... 6 введено свойство

property Constraints: TSizeConstraints;

Оно используется для ограничения максимальных и минимальных размеров управляющих элементов по высоте и ширине. Объект класса TSizeConstraint имеет четыре следующих свойства: Max-Height, MaxWidth, MinHeight, MinWidth типа O..MaxInt, которые и определяют предельные значения указанных параметров. По умолчанию эти свойства имеют нулевые значения, что эквивалентно снятию каких бы то ни было ограничений.

Свойство

property AutoSize: Boolean;

также впервые введено в Delphi 4. Оно связано, в основном, с интерфейсом Drag&Dock (см.п. 17.8) и разрешает (True) или запрещает (False) оконному компоненту автоматически изменять свои размеры в зависимости от количества и размеров содержащихся в нем неоконных компонентов.

17.5. УКАЗАТЕЛИ МЫШИ

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

property Cursor: TCursor;

type TCursor = -32768..+32767;

В Delphi предопределены стандартные указатели, показанные на рисунке 17.2.

Рис. 17.2. Стандартные курсоры Delphi

В практике программирования часто возникает необходимость изменения формы указателя для всех окон программы. Например, при выполнении достаточно длительного по времени процесса указатель мыши часто принимает вид crHourGlass, а после завершения процесса - восстанавливает свой первоначальный вид. Чтобы изменить форму указателя для всех окон программы одновременно, используется свойство cursor у глобального объекта screen, который автоматически создается для каждой программы:

Screen.Cursor := crHourGiass;

..... //Делаем длительную работу

Screen.Cursor := crDefault; // Восстанавливаем начальную

// форму указателя

Программист может создать и использовать нестандартный указатель. При этом он должен:

  • с помощью редактора изображений Delphi создать изображение указателя и разместить это изображение в ресурсном файле программы;
  • в работающей программе сначала загрузить указатель из ресурсного файла с помощью функции LoadCursor и зарегистрировать его в списке cursors объекта screen, а уже только после этого назначать нестандартный указатель свойствам cursor компонентов или экрана.

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

  • Выберите опцию меню Tools | image Editor, чтобы загрузить редактор изображений Delphi.
  • В окне редактора сначала выберите File | New | Resource File, а затем - Resource | New | Cursor. Окно редактора к этому моменту должно иметь вид, показанный на рис. 17.3.

Рис. 17.3. Окно редактора изображений с заготовкой для указателя мыши

  • С помощью опции Resource | Rename обязательно измените стандартное имя ресурса cursorl на любое другое, например, mycursor. Вы можете даже написать прежнее имя Сursor1, но изменить умалчиваемое имя ресурса нужно обязательно, иначе функция LoadCursor не сможет извлечь указатель из ресурсного файла (это имя должно состоять из заглавных букв).
  • Дважды щелкните по ресурсу в списке ресурсов или выберите опцию Resource | Edit - на экране появится окно редактора с двумя пустыми полями, обведенными красными рамками. Левое поле предназначено для показа создаваемого указателя в крупном масштабе, правое показывает его в натуральную величину.
  • Выберите инструмент в форме окружности в левой части окна редактора, нажмите и удерживайте клавишу Shift, чтобы получилась правильная окружность, подведите указатель мыши в виде крестика несколько правее и ниже верхнего угла левого поля редактора, нажмите левую кнопку мыши и, удерживая кнопку и клавишу Shift нажатыми, переместите указатель несколько левее и выше правого нижнего угла поля, после чего отпустите кнопку и клавишу (см. рис. 17.5: на этом рисунке выбранный инструмент имеет вид вдавленной кнопки).
  • Щелкните мышью по заголовку untitiedi.res окна со списком ресурсов (или закройте окно с изображениями указателя кнопкой в его правом верхнем углу) и с помощью File | save сохраните ресурсный файл под именем cursor, после чего закройте редактор изображений.
  • Создайте новый проект с пустой формой и напишите такой обработчик события OnCreate для формы Form1:

{$R *.DFM} ($R Cursor.res}

procedure TForm1.FormCreate(Sender: TObject);

begin

// Регистрируем указатель:

Screen.Cursors[1] := LoadCursor(HInstance,'MYCURSOR');

// Используем его для клиентской части формы:

Cursor := 1

end;

Не забудьте указать комментарий {$r cursor, res}, чтобы компоновщик пристыковал ресурсный файл cursor, res к ресурсному файлу программы. После запуска программы вы увидите в клиентской части пустой формы нестандартный указатель. Замечу, что при регистрации указателя в screen, cursors индекс может быть любым числом в диапазоне от -32768 до +32767. Однако следует учитывать,

что индексы в диапазоне от -22 до -1 включительно заняты стандартными указателями, показанными на рис. 17.3 (индексы -5 и -1 соответствуют указателю crNone), а индекс 0 всегда связан со стандартным указателем Windows ^ и не может переопределяться.

Рис. 17.4. Окно редактора с готовым изображением указателя

17.6. РЕАКЦИЯ НА СОБЫТИЯ ОТ МЫШИ И КЛАВИАТУРЫ

17.6.1. События от мыши

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

type

TMouseButton = (mbLeft, mbRight, mbMiddle) ;

TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft,

ssRight, ssMiddle, ssDouble);

TMouseEvent = procedure (Sender: TObject;

Button: TMouseButton;

Shift: TShiftState; X, Y: Integer) of object;

TMouseMoveEvent = procedure(Sender: TObject;

Shift: TShiftState; X, Y: Integer) of object;

TNotifyEvent = procedure (Sender: TObject) of object-property OnMouseDown: TMouseEvent;

property OnMouseUp: TMouseEvent;

property OnMouseMove: TMouseMoveEvent;

property OnClick: TNotifyEvent;

property OnDblClick: TNotifyEvent;

Тип TMouseButton определяет одну из трех кнопок мыши: левую

(mbLeft), правую (mbRigth) и среднюю (mbMiddle).

Тип TShiftState содержит признаки, уточняющие обстоятельства возникновения события: ssShift - нажата клавиша Shift; ssAit -нажата клавиша Alt; ssctri - нажата клавиша Ctrl; ssLeft - нажата левая кнопка мыши; ssRight - нажата правая кнопка; ssMiddie -нажата средняя кнопка; ssDoubie - нажаты одновременно левая и правая кнопки.

Обработчики OnMouseDown и OnMouseUp определяют реакцию программы на соответственно нажатие и отпускание кнопки мыши, оп-MouseMove - на перемещение указателя мыши над компонентом, оп-click и OnDblClick - соответственно на щелчок и двойной щелчок левой кнопки. Во всех обработчиках параметр sender содержит ссылку на компонент, над которым произошло событие, а х и y определяют координаты точки чувствительности указателя мыши в момент возникновения события в системе координат клиентской области родительского компонента. Замечу, что событие OnClick возникает после OnMouseDown, но перед OnMouseUp, а событие

OnDblClick Возникает после OnMouseUp.

17.6.2. События от клавиатуры

События от мыши получают любые потомки TControl. В отличие от этого события от клавиатуры получают только некоторые оконные компоненты (потомки TWinControi). Обработка событий связана со следующими свойствами этих компонентов:

type

TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft,

ssRight, ssMiddie, ssDoubie) ;

TKeyEvent = procedure (Sender: TObject; var Key: Word;

Shift: TShiftState) of object;

TKeyPressEvent = procedure (Sender: TObject;

var Key: Char) of object-property OnKeyDown: TKeyEvent;

property OnKeyUp: TKeyEvent;

property OnKeyPress: TKeyPressEvent;

Параметр Shift, как и в обработчиках событий от мыши, содержит уточняющие признаки. Параметр Key в обработчиках TKeyEvent содержит виртуальный код клавиши, а в обработчике TKeypressEv-ent - ASCII-символ. Обработчики OnKeyDown и опкеуор перехваты-вают нажатие большинства клавиш клавиатуры, в то время как обработчик QnKeypress - только нажатие алфавитно-цифровых клавиш. Получаемый им символ Key учитывает выбранный язык и нажатую клавишу Shift.

Виртуальные коды клавиш определены константами vk_xxx в файле source | rtl | win windows . pas каталога размещения Delphi. Фактически виртуальный код - это просто уникальный числовой идентификатор клавиши. Для буквенно-цифровых клавиш О...9 и A...Z виртуальный код совпадает с кодом, возвращаемым функцией ord(X), где х - соответствующий заглавный символ: ord('o'), ord( 'w') и т. д. К сожалению, уникальность кода не обеспечивается для клавиши Enter в зоне дополнительных числовых клавиш, которой присвоен код 13, - как и аналогичной клавише в основной зоне, а также для правых и левых сдвиговых клавиш Shift, Alt и Ctrl. Кроме того, клавиши О...9 и Del в зоне дополнительной клавиатуры сопровождаются уникальным кодом только при активном переключателе NumLock, в противном случае они повторяют коды соответствующих управляющих клавиш. Все остальные клавиши стандартной клавиатуры (за исключением Print Screen, клавиш смещения курсора и Tab, нажатие на которые не передается в обработчики TKeyEvent) имеют постоянно закрепленные за ними числовые коды, позволяющие легко установить факт нажатия или отпускания любой из них.

Поскольку параметр кеу в каждом обработчике определен как параметр-переменная, программист может изменить фактический код клавиши на нужный. Такая возможность может оказаться полезной для фильтрации нажатия каких-либо клавиш. При этом изменение кода происходит в обработчике формы, а в оконный элемент с фокусом ввода будет поступать уже измененный код. Чтобы форма получила событие до передачи его в элемент с фокусом ввода, следует поместить в свойство property KeyPreview: Boolean;

формы значение True.

17.6.3. Клавиатура в MS-DOS и Windows

Следует заметить, что Windows значительно “строже” относится к использованию клавиатуры, чем MS-DOS. Это может вызывать проблемы при переносе игровых приложений, а также приложений, созданных с помощью FoxPro или Clipper, в среду Delphi.

Если вы захотите сохранить устоявшиеся приемы использования клавиатуры в новой разработке (а я настоятельно рекомендую сделать это), вам, возможно, придется перехватывать сообщения Windows, так как только таким способом программа сможет опознать факт нажатия на системные клавиши Alt, Tab, Shift и т. п. Нажатие на остальные клавиши можно анализировать с помощью перехвата сообщений от клавиатуры в обработчиках Опкеуххх формы при установленном значении True в ее свойство Keypreview. Например, пусть акселератор Alt+X используется в существующей программе для закрытия модального диалогового окна. Чтобы сконструированное вами окно закрывалось по этой команде, напишите для него такой Обработчик события OnKeyDown:

procedure TForm2.FormKeyDown(Sender: TObject;

var Key: Word; Shift: TShiftState) ;

begin

if (Key = ord('X')) and (ssAlt in Shift) then Close

end;

Во многих случаях можно использовать собственные клавиши-акселераторы Windows. К сожалению, такими клавишами снабжаются лишь опции меню, но связанные с ними (опциями) обработчики OnClick выйолняются даже в том случае, когда опция меню не видна (ее свойство visible имеет значение False). Этим можно воспользоваться, чтобы вставить в главное меню окна фиктивные невидимые опции, связав их с нужными акселераторами. Пусть, например, клавиша Buttoni должна “нажиматься” при нажатии Ctrl+S. Поместите на форму главное меню (если его еще нет) и создайте в нем опцию-заголовок с произвольным именем, например, MyButton. В списке свойства Shortcut опции выберите Ctrl+S и установите False в ее свойство visible. Теперь можно связать ее обработчик события OnClick непосредственно с Button1Click или написать такой обработчик:

procedure TForm2.MyButtonClick(Sender: TObject);

begin

ButtonlClick(Self)

end;

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

Как уже отмечалось, обработчики Опкеуххх не реагируют на нажатие системных клавиш Windows, в том числе - клавиш смещения курсора. Происходит это из-за того, что умалчиваемая оконная функция программы осуществляет стандартную обработку соответствующих сообщений Windows. Оконная функция связывается с каждым программным окном. Ее назначение - осуществлять связь программы с Windows. В оконную функцию главного окна программы Windows помещает сообщения о наступлении того или иного события, в том числе - о нажатии на клавишу. Оконная функция главного окна передает это сообщение оконной функции окна с фокусом ввода (см. ниже п. 17.6.4), а та, в свою очередь - функции сфокусированного оконного компонента. Чтобы получать все адресованные программе сообщения Windows, необходимо использовать метод HookMainwindow глобального объекта-программы Application или его обработчик события OnMessage. Единственным параметром обращения к методу Application. HookMainwindow является имя функции типа

TWindowHook = function(var Message: TMessage): Boolean of object;

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

отличие от этого обработчик Application. OnMessage

type

TMsg = packed record

hwnd: HWND; message: UINT; wParam: WPARAM; IParam: LPARAM;

time: DWORD; pt: TPoint;

end;

TMessageEvent = procedure (var Ms.g: TMsg;

var Handled: Boolean) of object-property OnMessage: TMessageEvent;

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

В переменных типа TMsg ядро Windows передает программе так называемые сообщения - небольшие пакеты данных, оповещающие программу о наступлении того или иного события, будь то нажатие на клавишу, перемещение мыши или наступление нужного момента времени. Три параметра этого пакета используются следующим образом: Message - код события, wParam и lParam - уточняющие параметры. Для сообщения о нажатии клавиши код Message определен константой wm_KeyDown (256) или (при отпускании клавиши) wm_KeyUp (257), параметр wParam содержит виртуальный код нажатой клавиши, а IParam - дополнительные параметры, такие как количество нажатий клавиши с момента последнего обращения к функции перехвата, признак нажатой и удерживаемой Shift-клавиши и т. п.

Вот как, например, можно использовать клавиши курсора для смещения изображения (квадрата) в компоненте PaintBox:

type

TFormI = class(TForm)

PaintBoxl: TPaintBox;

procedure FormCreate(Sender: TObject);

procedure FormDestroy(Sender: TObject);

procedure PaintBoxIPaint(Sender: TObject);

private

{ Private declarations }

public .

{ Public declarations }

X, Y: Integer-procedure Hook(var Msg: TMsg; var Handled: Boolean);

end;

procedure TFormI.FormCreate(Sender: TObject);

{Регистрируем обработчик OnMessage в момент создания главного окна программы и устанавливаем начальное положение квадрата} begin

Application.OnMessage := Hook;

X := 100;

Y := 100;

end;

procedure TFormI.Hook(var Msg: TMsg; var Handled: Boolean);

begin

if Msg.Message<>wm_keyDown then //Сообщение от клавиатуры?

Exit; //Нет case Msg.wParam of

vk_Left: dec(X,10); //Смещение влево

vk_Right: inc(X,10);//Смещение вправо

vk Up: dec(Y,10); //Смещение вверх

vk_Down: inc(Y,10); //Смещение вниз end;

Paintboxl.Repaint;

Handled := True; //Блокируем дальнейшую обработку end;

procedure TFormI.PaintBoxIPaint(Sender: TObject);

(Вычерчиваем квадрат со стороной 20 пикселей и центром в точке X, Y} begin

with PaintBoxl.Canvas do

Rectangle(X-10,Y-10,X+10,Y+10)

end;

В Delphi 5 и 6 специальный компонент TApplicationEvents, который существенно упрощает обработку любых сообщений Windows до торр, как они поступят в оконную функцию активной

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

Код | Значение | Клавиша | Код | Значение | Клавиша
vk Back | 8 | Backspace | vk A..vk Z | 65..90 | A. .Z
vk Tab | 9 | Tab | vk LWin | 91 | Левая Windows
vk Clear | 12 | [5] | vk RWin | 92 | Правая Windows
vk Return | 13 | Enter | vk NumpadO.. vk Numpad9 | 96..105 | [О]..[9]
vk Shift | 16 | Shift | vk Multiply | 106 | [*]
vk Control | 17 | Ctti | vk Add | 107 | [+]
vk Menu | 18 | Alt | vk Subtract | 109 | [-]
vk Pause | 19 | Pause | vk Decimal | 110 | [Del]
vk Capital | 20 | Caps Lock | vk Divide | 111 | [/]
vk Escape | 27 | esc | vkFl..vkF12 | 112..123 | Fl..F12
vk Space | 32 | Пробел | vk Numlock | 144 | Num Lock
vk Prior | 33 | Page Up | vk Scroll | 145 | Scroll Lock
vk Next | 34 | Page Down |  | 186 |
vk End | 35 | End |  | 187 | +
vk Home | 36 | Home |  | 188 | <
vk Left | 37 | Курсор влево |  | 189 | -
vk Up | 38 | Курсор вверх |  | 190 | >
vk Right | 39 | Курсор вправо |  | 191 | •?
vk Down | 40 | Курсор вниз |  | 192 | ~
vk Insert | 45 | Insert |  | 219 | [
vk Delete | 46 | Delete |  | 220 | \
vk 0..vk 9 | 48..57 | 0..9 |  | 221 | ]

В квадратных скобках указаны клавиши из зоны дополнительных цифровых клавиш.

Учтите, что константы vk_A.. .vk_z и vk_o.. .vk_9 не определены в файле source | rtl | win | windows . pas и следовательно в модуле System, поэтому для них компилятор выдаст сообщение о неизвестном идентификаторе - в этом случае используйте собственные определения констант или их числовые эквиваленты. Замечу также, что с помощью анализа параметра мзд. lParam в обработчике OnMes-заде можно отличить нажатие левых клавиш Alt, Ctrl, Shift от нажатия одноименных правых и нажатие Enter в основной клавиатуре от нажатия Enter в дополнительной.

17.6.4. Фокус ввода

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

Компонент с фокусом ввода имеет значение True в своем свойстве property Focused: Boolean;

Если элемент запрещен к выбору или невидим, его свойство

property CanFocus: Boolean;

имеет значение False.

Фокус ввода передается элементу после щелчка на нем мышью или в порядке выбора его клавишей Tab. Чтобы элемент можно было выбирать этой клавишей, следует установить в его свойство

property TabStop: Boolean;

значение True. Порядок выбора элемента определяется свойством

property TabOrder: Integer;

Delphi следит за уникальностью и непрерывностью значений этого свойства у всех помещенных на форму не запрещенных и видимых в данный момент компонентов. С помощью метода procedure GetTabOrderList(List: TList) ;

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

17.7. МЕХАНИЗМ ДЕЙСТВИЙ ACTION

В Delphi 4...6 введен специальный механизм действий, дающий удобное средство централизованной реакции программы на те или иные действия пользователя. Для реализации действий в страницу Standard палитры компонентов включен специальный компонент

TActionList (CM. П. 18.1.15), а в объект TComponent - свойство Action:

property Action: TBasicAction;

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

Компонент TActionList содержит список названий действий и связанных с ними имен подпрограмм, которые эти действия реализуют. Чтобы связать компонент с нужным действием, достаточно раскрыть список его опции Action (этот список содержит имена всех действий во всех компонентах TActionList) и выбрать нужное название. При этом обычно меняется пиктографическое изображение и/или надпись на компоненте, а также связанная с ним оперативная подсказка (Hint). Свойство Action используется только после создания и наполнения списка (списков)

TActionList.

17.8. ИНТЕРФЕЙС DRAG&DROP

Операционная система Windows широко использует специальный прием связывания программ с данными, который называется Drag&Drop (перетащи и отпусти). Такой прием в Проводнике windows используется для копирования или перемещения файлов, а также для запуска обрабатывающей программы. Если, например, файл с расширением doc “перетащить” на пиктограмму WinWord, автоматически запустится текстовый редактор word for windows и в его окне появится текст из этого файла.

В Delphi реализован собственный интерфейс Drag&Drop, позволяющий компонентам обмениваться данными путем “перетаскивания” их мышью. Этот интерфейс определяется двумя свойствами и тремя событиями, доступными каждому видимому компоненту.[В Delphi б события могут дублировать сложные свойства и наоборот. Сложными в данном случае называются свойства, ссылающиеся на внутренние объекты.]

Свойство

TDragMode = (dmManual, dmAutomatic);

property DragMode: TDragMode;

определяет, как будет выполняться весь комплекс действий, связанных С Drag&Drop: dmManual - Вручную (программой); dmAutomatic -автоматически (свойствами и методами компонентов). Значение dmManual означает, что все необходимые для обслуживания интерфейса события генерируются программой, dmAutomatic - события инициируются свойствами и методами компонентов. Во всех случаях программист должен написать обработчики этих событий (см. ниже).

Свойство property DragCursor: TCursor;

определяет вид указателя мыши в момент, когда над компонентом “протаскиваются данные”. Если компонент готов принять данные, он устанавливает в это свойство значение crDrag l3, в противном случае - crNoDrag 0. Установка этих свойств осуществляется автоматически, если DragMode = dmAutomatic. Событие TDragState = (dsDragEnter, dsDragLeave, dsDragMove) ;

TDragOverEvent = procedure(Sender, Source: TObject;

X, Y: Integer; State: TDragState; var Accept: Boolean)

of object; property OnDragOver: TDragOverEvent;

возникает в момент перемещения указателя мыши “с грузом” над компонентом. Здесь sender - компонент, который возбудил событие (обычно это Self - сам компонент-получатель; при ручном управлении механизмом Drag&Drop это может быть не так); source - компонент-отправитель “груза”; х, y - текущие координаты указателя мыши в пикселях клиентской области компонента; State - состояние указателя (dsDragEnter - только что появился на компонентом; dsDragLeave -только что покинул компонент или бьша отпущена кнопка мыши;

dsDragMove - перемещается над компонентом). В параметре Accept обработчик сообщает, готов ли компонент принять данные (т rue - готов).

Событие

TDragDropEvent = procedure(Sender, Source: TObject;

X, Y: Integer) of object;

property OnDragDrop: TDragDropEvent;

означает, что пользователь “бросил” данные на компонент. Параметры обработчика совпадают по назначению с одноименными параметрами OnDragOver.

Наконец, при завершении перетаскивания (вне зависимости от того, приняты данные или нет) возникает событие TEndDragEveht = procedure(Sender, Target: TObject;X, Y: Integer) of objects; property OnEndDrag: TEndDragEvent;

где sender - отправитель данных; Target - получатель данных или nil, если никто не принял “посылку”; X, Y - координаты мыши в момент отпускания левой кнопки.

Чтобы проиллюстрировать использование механизма Drag&Drop, загрузите описанную в гл. 5 учебную программу, установите во всех ее компонентах в свойство DragMode значение dmAutomatic и создайте такой обработчик события OnDragOver для метки:

procedure TfmExample.IbOutputDragOver(Sender, Source: TObject;

X, Y: Integer; State: TDragState;

var Accept: Boolean);

begin

Accept := True;

lbOutput.Caption := (Source as TComponent).Name

end;

Теперь перетаскивание любого компонента на метку IbOutput заставит ее показать имя перетаскиваемого компонента (рис. 17.5).

Рис. 17.5. Метка IbOutput показывает имя перетаскиваемого компонента

Для ручного (программного) управления механизмом Drag&Drop используются следующие методы, доступные любому потомку TControl:

procedure BeginDrag (Immediate: Boolean);

Используется источником для инициализации процесса Drag&Drop. Если Immediate =True, процесс начинается немедленно, в противном случае - после смещения указателя мыши на 5 пикселей в любом направлении

procedure DragDrop (Source: TObject; X, Y: Integer);

Вызывает обработчик события OnDragDrop

procedure EndDrag(Drop: Boolean);

Вызывает обработчик события OnEndDrag и в параметре Drop сообщает о том, были ли приняты данные

17.9. ИНТЕРФЕЙС DRAG&DOCK

В Delphi введена поддержка специального интерфейса Drag&Dock (перетащи и причаль), с помощью которого можно перетаскивать мышью компоненты на новое место. В среде Delphi этот интерфейс используется для настройки кнопок инструментальных панелей (см. гл. 2).

В интерфейсе Drag&Dock участвуют два компонента: принимающий компонент (всегда - потомок TWinControl) и перетаскиваемый компонент (потомок TControl).

Принимающий компонент должен иметь значение True в своем свойстве Docksite. Поскольку Drag&Dock является разновидностью более общего интерфейса Drag&Drop, в TControi введено дополнительное свойство

property DragKind: TDragKind;

type TDragKind = (dkDrag, dkDock) ;

с помощью которого различаются способы использования мыши:

для Drag&Drop (dkDrag) или для Drag&Dock (dkDock). И у перетаскиваемого компонента, и у компонента-приемника эти свойства должны иметь значения dkDock.

Количество установленных на форме перетаскиваемых компонентов (Т. С. компонентов, у которых DragKind=dkDock И DockSite=False) Определяется свойством DockClientCount, а их список хранится в индексированном свойстве DockClients.

Для реализации Drag&Dock в класс TControl введены такие дополнительные свойства и события.

property AutoSize: Boolean;

Разрешает (True) или запрещает (False) оконному компоненту менять свои размеры автоматически в зависимости от количества и размеров содержащихся в нем дочерних компонентов. Свойство

property FloatingDockSiteClass: TWinControlClass;

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

property DockOrientation: TDockOrientation;

type TDockOrientation = (doNoOrient,doHorizontal,doVertical);

можно установить (или получить) ориентацию, которую будет иметь “причаливаемый” компонент в окне родителя: doNoOrient -сохраняется исходная ориентация перемещаемого компонента; do-Horizontal (dovertical) - компонент будет иметь горизонтальную (вертикальную) ориентацию. С помощью свойства

property LRDockWidth: Integer;

можно получить ширину последнего перемещенного компонента, который расположился горизонтально, а с помощью

property TBDockHeight: Integer;

- высоту последнего вертикально расположенного компонента.

Свойства

property UndockHeight: Integer; property UndockWidth: Integer;

определяют соответственно высоту и ширину последнего “отчалившего” компонента. Свойство

property Constraints: TSizeConstraints;

с помощью объекта класса TSizeConstraints накладывает ограничения на возможные размеры “причаливаемого” компонента (задает максимальные и минимальные величины для высоты и ширины).

События

property OnDockDrop: TDockDropEvent;

property OnDockOver: TDockOverEvent;

type TDockOverEvent = procedure(Sender: TObject; Source:

TDragDockObject; X, Y: Integer; State: TDragState;

var Accept:

Boolean) of object;

аналогичны событиями OnDragDrop и OnDragOver (CM. П.17.7). Co6ытие

property OnGetSiteInfo: TGetSiteInfoEvent;

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

property OnUnDock: TUnDockEvent;

type TUnDockEvent = procedure(Sender: TObject; Client: TControl;

var Allow: Boolean) of object;

возникает при “отчаливании” неоконного компонента от своего родителя. Обработчик этого события должен поместить в Allow значение True, если компонент client может покинуть границы своего владельца sender.

Все указанные события обрабатываются автоматически, если оба компонента (клиент и сервер) содержат значение dmAutomatic в своем свойстве DragMode.

Чтобы познакомиться в действии с технологией Drag&Dock, поместите на пустую форму панель трапе! и кнопку TButton, установите для панели значение True в свойство Docksite, установите для

обоих компонентов Значение dkDock в свойстваа DragKind И dmAutomatic в свойства DragMode. После запуска программы перетащите кнопку на панель, а затем стащите ее обратно.

17.10. ПОДДЕРЖКА СПРАВОЧНОЙ СЛУЖБЫ

Все видимые элементы имеют свойства

property Hint: String;

property ShowHint: Boolean;

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

Чтобы компонент смог показать ярлычок, нужно поместить текстовую строку в его свойство Hint и присвоить свойству showHint значение True. Обычно ярлычок содержит максимально лаконичный текст, чтобы не отнимать слишком большую площадь экрана. Вы можете дополнить этот текст развернутым сообщением в любом компоненте, который способен отображать текст. Как правило, это панель статуса, занимающая нижнюю часть формы. Чтобы отобразить <<длинное” сообщение, его нужно прежде всего поместить в строку Hint сразу после “короткого” и отделить обе части символом “[”.

Например:

MyControl.Hint := 'Подсказка!Развернутое сообщение';

Далее, необходимо в самом начале программы (обычно в обработчике события Oncreate главной формы) указать программе метод, который будет обрабатывать событие onHint. Это событие возникает в момент появления ярлычка и предназначено для отображения длинного сообщения, которое метод-обработчик может получить в свойстве Hint глобального объекта Application.

Например:

type

TFormI=class(TForm) pnStatus: TPanel;{Панель для отображения длинной части Hint}

public

// Объявляем метод-обработчик события OnHint:

procedure ShowLongHint(Sender: TObject);

end;

procedure TFormI.ShowLongHint(Sender: TObject);

{Этот обработчик помещает на панель pnStatus длинную часть Hint}

begin

pnStatus.Caption := Application.Hint;

end;

procedure TFormI.FormCreate(Sender: TObject) ;

// Метод определяет свойство OnHint объекта-программы

begin

Application.OnHint := ShowLongHint;

end;

Обратите внимание: обработчиком события OnHint должен быть метод класса, а не обычная процедура. Вот почему в предыдущем примере в секцию public класса TForm1 вставлено описание метода

ShowLongHint.

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

bbClose.Hint := 'Эта кнопка'#13'завершает работу '#13'программы';

Следующие свойства глобального объекта-программы Application регулируют цвет и время появления ярлычка:

property HintColor: TColor; | Определяет цвет фона ярлычка
property HintHidePause: Integer; | Определяет длительность показа ярлычка в миллисекундах. По умолчанию равно 2500 (2,5 секунды). Чтобы ярлычок вообще не убирался, установите значение -1
property HintPause: Integer; | Определяет паузу в миллисекундах после остановки указателя мыши и перед появлением ярлычка. По умолчанию равно 500
property HintShortPause: Integer; | Определяет паузу перед появлением ярлычка при переходе с одного компонента с заданным свойством Hint на другой такой же. По умолчанию равно 50

Все видимые компоненты содержат свойство

property HelpContext: Integer;

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

У многих видимых компонентов имеется свойство

property PopupMenu: TPopupMenu;

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

17.11. ПОДДЕРЖКА СОМ

Модель компонентных объектов СОМ (Component Object Model) представляет собой технологию обмена объектами между разными приложениями и даже между разными сетевыми машинами. Эта технология усиленно развивается Microsoft и в перспективе может привести к тому, что ваша программа сможет использовать объект (напомню, что объект - это фрагмент исполняемого кода), установленный на машине, находящейся в другой части света и не относящейся к тому же классу, что и ваша машина, т. е. написанный на другом языке программирования и реализованный другим набором машинных инструкций!

В основе СОМ лежат те же идеи, что и в OLE, с той разницей, что СОМ-сервер может выполнять обработку СОМ-клиента на машине сервера.

Разумеется, и сервер, и клиент в технологии СОМ могут быть установлены на одной и той же машине, однако в OLE это фактически является обязательным требованием. Если клиент и сервер выполняются на разных машинах, реализуется распределенный вариант СОМ - DCOM (Distributed Component Object Model).

В Delphi включены средства поддержки СОМ как в виде готовых компонентов (например, DCOMConnection), так и на уровне класса TComponent, т. е. относящиеся ко всем компонентам Delphi.

Свойство этого класса

property ComObject:IUnknown;

возвращает интерфейс СОМ-объекта, если данный компонент поддерживает технологию СОМ. Если это не так, обращение к свойству

возбудит исключительную ситуацию EComponentError.

Свойство

property VCLComObject: Pointer;

предназначено для внешнего использования (т. е. для экспорта СОМ-объекта) и поставляет ссылку на СОМ-объект Delphi.

17.12. СВОЙСТВА РАЗНОГО НАЗНАЧЕНИЯ

Узнать текущее состояние любого управляющего элемента на этапе прогона программы можно с помощью свойства

type TControlState = set of (csLButtonDown, csClicked, csPalette, csReadingState, csAlignmentNeeded, csFocusing, csCreat-ing, csCustomPaint, ceDestroyingHandle);

property ControlState: TControlState;

класса TControl.

Элементы множества TControlState имеют следующий смысл:

csLButtonDow | Над элементом была нажата и еще не отпущена левая кнопка мыши
n csClicked | То же, что csLButtonDown, но устанавливается, если стиль компонента (см. ниже) содержит флаг csClickEvents и означает, что нажатие кнопки интерпретируется как щелчок
csPalette | Элемент или его родитель получил сообщение WMPALETTCHANGED
csReadingState | Элемент читает данные из потока
csAlignmentNeeded | Элемент нуждается в перерисовке, т. к. изменилось его свойство Alignment
csFocusing | Программа пытается передать элементу фокус ввода
csCreating | Элемент и/или его родительские и дочерние элементы находятся в стадии создания. Флаг очищается после завершения процесса
csCustomPaint | Элемент перерисовывается
csDestroyingHandle | Разрушается Windows-дескриптор элемента

Свойство

type TControlStyle = set of (csAcceptsControls, csCaptureMouse, csDesignInteractive, csFramed, csClickEvents, csSetCaption, csOpaque, csDoubleClicks, csFixedWidth, csFixedHeight, csNoDesignVisible, csReplicatable, csNoStdEvents, csDisplayDraglmage, csReflector, csActionClient, csMenuEvents) ;

property ControlStyle: TControlStyle;

содержит стилевые флаги элемента, имеющие такой смысл:

csAcceptsControls | Элемент может стать владельцем дочернего элемента в технологии Drag&Dock
csActionClient | Элемент может участвовать в централизованном действии
csCaptureMouse | Элемент будет получать все сообщения от мыши, даже если ее указатель выйдет из его границ
csClickEvents | Элемент может воспринимать щелчок мыши
csDesignInteractive | Элемент воспринимает щелчок правой кнопкой мыши на этапе конструирования формы
csDisplayDragImage | Элемент способен менять свое изображение при протаскива-нии над ним “груза” в технологии Drags Drop
csDoubleClicks | Элемент может воспринимать двойной щелчок мыши
csPixedHeight | Элемент имеет фиксированную высоту
csFixedWidth | Элемент имеет фиксированную ширину
csFramed | Элемент имеет трехмерную рамку
csNoDesignVisible | Элемент не виден на этапе конструирования формы
csNoStdEvents | Элемент не реагирует на стандартные события от мыши или клавиатуры
csOpaque | Элемент закрашивает всю свою клиентскую область
csReflector | Элемент способен получать диалоговые сообщения windows, сообщения, связанные с получением/потерей фокуса ввода и изменением размеров.
csReplicatable | Используется для компонентов АсtiveX
csSetCaption | Элемент способен отобразить свой образ с помощь метода PaintTo Элемент будет изменять свой заголовок при изменении своего свойства Name

Пара свойств класса Twincontrol

type TImeMode = (imDisable, imClose, imOpen, imDontCare, im-

SAlpha, imAlpha, imHira, imSKata, imKata, imChinese, imSHanguel, imHanguel);

property ImeMode: TImeMode;

И

type TImeName = String;

property ImeName: TImeName;

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

type TBiDiMode = (bdLeftToRight, bdRightToLeft, bdRightToLeft-NoAlign, bdRightToLeftReadingOnly);

property BiDiMode: TBiDiMode;

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


Глава 18. ИСПОЛЬЗОВАНИЕ КОМПОНЕНТОВ ОБЩЕГО НАЗНАЧЕНИЯ

  • Глава 18. ИСПОЛЬЗОВАНИЕ КОМПОНЕНТОВ ОБЩЕГО НАЗНАЧЕНИЯ
    • КОМПОНЕНТЫ СТРАНИЦЫ STANDARD
      • TFrame-рама и шаблоны компонентов
      • TMainMenu - главное меню формы (программы)
      • TPopupMenu- вспомогательное (локальное) меню
      • TLabel -метка для отображения текста
      • TEdit- ввод и отображение строки
      • TMemo- ввод и отображение текста
      • TButton - кнопка
      • TCheckBox - независимый переключатель
      • TRadioButton -зависимые переключатели
      • TListBox-список выбора
      • TComboBox - раскрывающийся список выбора
      • TScrollBar - управление значением величины
      • TGroupBox - панель группирования
      • TRadioGroup - группа зависимых переключателей
      • TPanel -панель
      • TActionList- механизм действий
    • КОМПОНЕНТЫ СТРАНИЦЫ ADDITIONAL
      • TBitBtn - кнопка с изображением
      • TSpeedButton - кнопка для инструментальных панелей
      • TMaskEdit - специальный редактор
      • TStrmgGrid - таблица строк
      • TDrawGrid-произвольная таблица
      • TImage - отображение картинок
      • TShape - стандартная фигура
      • TBevel - кромка
      • TScrollBox-панель с прокруткой
      • TcheckListBox-группа независимых переключателей
      • TSplitter - компонент для изменения размеров
      • TStaticText- метка для отображения текста
      • TControlBar- инструментальная панель
      • TApplicationEvents - обработчик сообщений Windows
      • TValueListEditor - специализированный редактор строк
      • TLabelEdit - однострочный редактор с меткой
      • TColOrBox- список выбора цвета
      • TChart- построитель графиков
      • TActionManager - менеджер действий
      • TActionMainMenuBar - полоса меню для действий
      • TActionTollBar - полоса кнопок для действий
      • TCustomizeDIg - диалог настройки интерфейса
    • КОМПОНЕНТЫ СТРАНИЦЫ WIN 32
      • TTabControl - набор закладок
      • TPageControl - набор страниц с закладками
      • TImageList - хранилище изображений
      • TRichEdit - ввод и отображение RTF-текста
      • TTrackBar- регулятор величины
      • TProgressBar - индикатор прогресса
      • TUpDown - спаренная кнопка
      • THotKey-ввод акселератора
      • TAnimate - отображение анимации
      • TDateTimePicker - ввод и отображение даты/времени
      • TMonthCalendar - календарь
      • TTreeView - дерево иерархии
      • TList View - список просмотра
      • THeaderControl - управляющий заголовок
      • TStatusBar - информационная панель
      • TToolBar и ToolButton - инструментальная панель и кнопки для нее
      • TCoolBar и TCoolBand - инструментальная панель и полосы для нее
      • TPageScroller - панельс прокруткой
      • TComboBoxEx - список выбора с расширенными возможностями
    • КОМПОНЕНТЫ СТРАНИЦЫ SYSTEM
      • TTimer- таймер
      • TPaintBox - окно для рисования
      • TMediaPlayer - медиаплейер
      • TOleContainer - контейнер объектов OLE
    • КОМПОНЕНТЫ СТРАНИЦЫ DIALOGS
      • TOpenDialog и TSaveDialog - диалоги открытия и сохранения файлов
      • TOpenPictureDialog и TSavePictureDialog - диалоги открытия и сохранения изображений
      • TFontDialog -диалог выбора шрифта
      • TColorDialog - диалог выбора цвета
      • TPrintDialog - диалог настройки параметров печати
      • TPrinterSetupDialog - диалог настройки параметров принтера
      • TFindDialog- диалог поиска
      • TReplaceDialog - диалог поиска и замены
    • КОМПОНЕНТЫ СТРАНИЦЫ WIN31
      • TTabSet- набор закладок
      • TNotebook - набор страниц
      • TTabbedNotebook - набор страниц с закладками
      • TOutLine - просмотр иерархических структур
      • THeader - управляющий заголовок
      • TFileListBox - панель с именами файлов
      • TDirectoryListBox - панель с именами устройств
      • TDriveComboBox - список выбора устройства
      • TFilterComboBox - список выбора с расширениями файлов
    • КОМПОНЕНТЫ СТРАНИЦЫ SAMPLES
      • TGauge - индикатор величины
      • TColorGrid - выбор или отображение цвета
      • TSpinButton - спаренная кнопка
      • TSpinEdit- редактор для ввода целого числа
      • TDirectoryOutLine - отображение структуры каталогов
      • TCalendar - ввод и отображение даты

Глава 18

ИСПОЛЬЗОВАНИЕ КОМПОНЕНТОВ ОБЩЕГО НАЗНАЧЕНИЯ

В этой главе рассматривается работа с некоторыми наиболее часто используемыми компонентами библиотеки визуальных компонентов Delphi (VCL - Visual Component Library). Компоненты описываются так, как они по умолчанию (в момент поставки) сгруппированы на страницах палитры компонентов. Это сделано сознательно, чтобы облегчить поиск в книге нужного компонента, т. к. эта глава в значительной степени основана на справочном материале Delphi.

Некоторые компоненты дублируют приблизительно одинаковые функции. Например, есть разного рода контейнеры, способные служить заготовками для создания инструментальных панелей, различные индикаторы, кнопки, два сорта меток, два редактора для ввода текста и т. д. Такая избыточность связана с тем, что часть компонентов является на самом деле “дельфийской” оболочкой для стандартных компонентов Windows (к таковым, в частности, относятся многие компоненты страниц Standard, Win32, win3.1 и Dialogs), а другая часть разработана сотрудниками Borland и сторонними организациям в связи с тем, что стандартные компоненты не всегда способны выполнять некоторые специфичные функции (например, маскируемый ввод текста или стандартные функции закрытия диалоговых окон). С другой стороны, Delphi развивается по принципу преемственности при переходе от младших версий к старшим, именно поэтому, например, имеется страница win3.1, хотя все функции ее компонентов с лихвой перекрываются компонентами страниц Win32 и Dialogs.

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

В некоторых примерах главы используются файлы из каталога Images, содержащего разного рода изображения. Для версий 4.,.6 этот каталог устанавливается только в режиме инсталляции Custom и по-умолчанию располагается в папке: Program Files | CommonFiles |Borland Shared.

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

18.1. КОМПОНЕНТЫ СТРАНИЦЫ STANDARD

18.1.1. TFrame - рама и шаблоны компонентов

Этот компонент впервые введен в Delphi 5. Он определяет раму -контейнер для размещения других компонентов. В функциональном отношении компонент почти повторяет свойства формы и отличается от нее в основном лишь тем, что его можно помещать на формы или в другие рамы. Фактически рама представляет собой удобное средство создания шаблонов - произвольных наборов компонентов, максимально приспособленных для нужд конкретного пользователя. Раз созданный шаблон может подобно любому другому компоненту размещаться на форме или другой раме (допускается неограниченная вложенность рам). Замечу, что любые изменения в базовой раме (т. е. в раме, сохраненной в палитре) тут же отображаются во всех проектах, использующих данную раму.

Создание шаблонов - отнюдь не исключительная особенность Delphi 5 и б - соответствующие механизмы были включены и в две предыдущие версии 3 и 4. Рамы лишь упрощают процесс создания шаблонов. В конце параграфа приводится простая методика создания и использования шаблонов без рам, т. е. так, как это возможно сделать в версиях 3 и 4.

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

No frames in project.

To create a frame select File New Frame.

(В проекте нет рам. Чтобы создать раму выберите File | New Frame.)

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

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

  • Создайте новый Проект (File | New Application). если он все еще связан с учебной формой, определенной в гл. 5, предварительно вызовите окно редактора Репозитория (Tools | Repository), в окне pages щелкните по Forms, а в появившемся в правом окне списке objects - по fmExamipe, после чего уберите флажок в переключателе Main Form и закройте окно кнопкой ок.
  • Создайте новую раму-выберите File | New | Frame.
  • Поместите на раму две кнопки TBitBtn (рис. 18.1, а) и установите следующие свойства для кнопок и рамы: BitBtnl: Left=0, Тор=0, Kind=bkOK, Name=bbOK;

BitBtn2: Left=90, Top=0, Kind=bkCancel, Name=bbCancel;

Framel : Height=25, Width=165, Anchors=[akRight,akBottom]

а) [image] б)

Рис. 18.1. а) вид рамы на этапе конструирования: 6) окно регистрации рамы в палитре компонентов

Установленные параметры создадут минимальную по размерам раму с двумя именованными кнопками. Так как свойство Anchors рамы содержит значения [akRight, akBottoms], рама будет все время отслеживать свое положение относительно правого нижнего угла контейнера, в который она будет помещена.
  • Щелкните по раме правой кнопкой мыши и выберите в локальном меню Add To palette - на экране появится диалоговое окно с предложением сохранить модуль шаблона в дисковом файле.
  • Сохраните шаблон в файле с именем, DigBtnsF в папке для ваших программ. На экране появится окно регистрации шаблона в палитре компонентов, показанное на рис. 18.1, б. В окне предлагается установить для нового компонента имя класса и страницу палитры компонентов, в которую он помещен. Обратите внимание на автоматически созданное умалчиваемое имя Заготовки - TFramelTamplate. Т. к. это имя будет появляться в ярлычке Hint при высвечивании компонента мышью, следует дать ему более осмысленное имя, например, TDiaiogButtons. И еще одно замечание - в качестве страницы палитры компонентов в окне предлагается страница Templates (шаблоны), которая отсутствует в начальном наборе страниц палитры. Если вы сохраните это имя, в Delphi будет создана новая страница компонентов.
  • Задайте имя класса шаблона и страницу его размещения, после чего щелкните по кнопке ок - шаблон готов для использования.
  • После закрытия окна регистрации сам шаблон (рис. 18.1, а) останется на экране. Подобно дополнительной форме его модуль стал составной частью проекта, поэтому размещение шаблона на форме не вызовет проблем.
  • Щелкните по пиктограмме Ю на странице Standard палитры компонентов и затем по пустому месту в форме Formi - на экране появится окно с предложением выбрать нужный шаблон (рис. 18.2, а). Нажмите Enter, и шаблон появится в форме так, как если бы это был любой другой компонент (рис. 18.2, б). Следует отметить, что, если вы откроете новый проект и попытаетесь разместить на форме раму со страницы Standard, на экране вновь появится сообщение о том, что в проекте нет рам. Однако, если вы присоедините модуль DigBtnsF к проекту клавишами Shift-F11 или кнопкой И, установка шаблона на любую форму проекта приведет к появлению окна 18.2, а. Если в новом проекте попробовать установить на пустую форму компонент-шаблон со страницы Templates (или с любой другой страницы, куда он был ранее помещен на этапе регистрации), на экране появится окно с такой надписью:

The following unit: DigBtnsF, is needed in your project to create the template. Do you wish to add it?

(Модуль DigBtnsF необходим в вашем проекте, чтобы создать шаблон. Хотите его добавить?)

а) [image] б)

Рис. 18.2. а) окно выбора доступного шаблона; б) форма с размещенным на ней шаблоном

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

Компонент TFrame является потомкам TScrollingWinControl, от которого ему достались три описываемых ниже специфичных свойства (остальные свойства, события и методы унаследованы от rwin-controi и описаны в гл. 17):

property AutoScroll: Boolean;property HorzScrollBar: TControlScrollBar; | Определяет свойства горизонтальной полосы прокрутки
property VertScrollBar: TControlScrollBar; | Определяет свойства вертикальной полосы прокрутки

Класс TControlScrollBar, к которому принадлежат свойства HorzScrollBar и VertScrollBar, устанавливает все необходимые свойства и методы для полос прокрутки.

Свойства TControlScrollBar:

property Margin: Word; | Определяет минимальное расстояние от полосы до края компонента, в котором она расположена
property ButtonSize: Integer; | Определяет размер кнопок полосы прокрутки
property Color: TColor; | Определяет цвет полосы
type=(sbHorizontal,sbVertical) ; property Kind: ScrollBarKind; | Указывает ориентацию полосы (это свойство - только для чтения)
type TScrollBarInc = 1..32767; property Increment: TScrollBarInc; | Указывает перемещение бегунка при щелчке мышью на концевой кнопке полосы
property ParentColor: Boolean; | Если содержит True, цвет полосы определяется системными установками Windows. Установка значения в свойство color при водит к автоматической установке значения False в свойство ParentColor
property Position: Integer; | Определяет положение бегунка на полосе прокрутки
property Range: Integer; | Определяет размер скроллируемой области
property ScrollPos: Integers; | Определяет положение скроллируемой области
property Size: Integer; | Определяет ширину полосы в пикселях
property Smooth: Boolean; | Если содержит True, прокрутка осуществляется стандартным образом: щелчок по концевой кнопке вызывает смещение приблизительно на 1/10 части всей прокручиваемой области. Если содержит False, смещение определяется свойством Increment
TScrollBarStyle = (ssRegular, ssFlat, ssHotTrack); | Определяет стиль полосы: ssRegular обычная полоса; ssFlat - плоская полоса;
property Style: TScrollBarStyle; | ssHotTrack - плоская полоса, ее компоненты выделяются цветом при перемещении над ней указателя мыши
property ThumbSize: Integer; | Определяет ширину бегунка в пикселях
property Tracking: Boolean; | Разрешает/запрещает динамическую прокрутку при перемещении ползунка
property Visible: Boolean; | Определяетвидимость полосы

18.1.1. Создание шаблонов без рам

При желании вы может создать шаблоны компонентов и без рам. Описываемый ниже способ можно с одинаковым успехом использовать в версиях от 3-й до 6-й.

Поместите на форму компоненты, которые войдут в шаблон, и нужным образом настройте их свойства. Затем нажмите клавишу Shift и, не отпуская ее, щелкните по каждому шаблонному компоненту - в результате будет выделена группа компонентов. Теперь раскройте пункт Сomponent главного меню Delphi и выберите create component Template (если на форме не будет выделен ни один компонент, этот пункт меню будет недоступен). В ответ на это Delphi развернет окно, показанное на рис. 18.1, б, - единственным отличием будет то, что в качестве пиктограммы шаблона будет предложена пиктограмма последнего выделенного компонента. Дальнейшие действия ничем не отличаются от описанных выше.

18.1.2. TMainMenu - главное меню формы (программы)

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

После установки компонента на форму необходимо создать его опции. Для этого следует дважды щелкнуть по компоненту левой кнопкой мыши, либо нажать на нем правую кнопку[ Жаргонизмы “дважды щелкнуть по компоненту левой кнопкой мыши” и “нажать на нем правую кнопку” используются вполне сознательно в целях экономии вашего и моего времени. Разумеется, следовало бы писать что-нибудь вроде “в момент, когда указатель мыши находится над видимым изображением компонента, дважды подряд без заметной паузы нажать левую кнопку мыши” и “расположить указатель мыши так, чтобы острие указателя находилось в пределах видимого изображения компонента, и нажать правую кнопку мыши”. В дальнейшем я иногда буду прибегать к такого рода сокращениям, а посему -заранее приношу свои извинения, если они вам покажутся не очень литературными.] и выбрать продолжение Menu Designer в появившемся вспомогательном меню, либо, наконец, щелкнуть по кнопке в правой половине строки items Инспектора объектов. На рис. 18.3 показан вид окна конструктора меню, которое появится на экране после выполнения любого из перечисленных действий.

Рис. 18.3. Окно конструктора меню

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

В названиях опций можно указать символ “&” перед тем символом, который определит клавишу быстрого выбора опции (в терминологии Windows такие клавиши называются акселераторами). Например, на рис. 18.3 показана опция Файл, которую можно выбрать сочетанием клавиш Alt+Ф. При создании меню эта опция в строке caption Инспектора объектов содержала текст &Файл.

Если вы захотите вставить разделительную черту, отделяющую группы подопций, назовите очередной элемент меню именем “-”. Так, например, создана черта, отделяющая опции сохранить как и выход в примере меню на рис. 18.3: свойство caption, пятого по счету элемента списка опций Файл, содержит единственный символ “-”.

Для создания разветвленных меню, т. е. таких, у которых подопций вызывают новые списки подопций, щелкните по подопций и нажмите Ctrl+вправо, где вправо - клавиша смещения курсора вправо. Такого же эффекта можно добиться после щелчка правой кнопкой мыши на подопций и выборе продолжения create Submenu. На рис. 18.4 показан пример разветвленной структуры меню.

Свойства компонента:

property AutoMerge: Boolean; | Разрешает/запрещает слияние главного меню вторичной формы с главным меню основной формы в не MDI-приложениях
type TMenuItemAutoFlag = (maAutomatic, maManual, maPar-ent) ; property AutoHotkeys: TMenuAuto-Flag; | Определяет, будет ли следить программа за уникальностью клавиш быстрого выбора для всех опций меню: maAutomatic - автоматически при создании опции; maManual -с помощью вызова специального метода; maparent - по правилу, установленному для опции верхнего уровня
property AutoLineReduction: TMenuItemAutoFlag; | Определяет, будет ли программа автомати чески удалять лишние разделители между опциями многоколончатого меню. Тип TMenuItemAutoFlag описан в свойстве AutoHotkeys
property Images: TCustomImageList; | Содержит ссылку на объект - хранилище изображении, которые могут появляться около индивидуальных элементов (пунктов) в меню
property Items: TMenuItem; default; | Открывает индексированный доступ к опциям меню
property OwnerDraw: Boolean; | Определяет, будет ли программа прорисовывать опции меню автоматически или с помощью обработчика onDrawltem

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

  • Начните новое приложение и на форму положите компонент TMainMenu.
  • Поместите в свойство Сaption единственной опции этого компонента значение открыть вторичное окно и напишите такой обработчик его события OnClick:

procedure TFormI.NIClick(Sender: TObject);

begin

Form2.MainMenul.AutoMerge := True;

Form2.Show

end;

  • Присоедините к проекту вторую форму, щелкнув по инструментальной кнопке Н или с помощью опции File | New|

Form.
  • Положите на эту форму TMainMenu, создайте в нем единственную опцию с Caption закрыть вторичное окно и таким обработчиком:

procedure TForm2.NIClick(Sender: TObject);

begin

Close;

Formi.MainMenul.Unmerge(MainMenul);

end;

Перед трансляцией свяжите модули окон предложениями Uses.

Обратите внимание: если при закрытии вторичного окна не восстановить опции главного меню методом unmerge, эти опции останутся прежними (измененными).

Свойство AutoMerge вторичного окна игнорируется, если оно вызывается в модальном режиме, а также если оно является дочерним окном MDI.

Наиболее важные методы компонента:

function DispatchCommand (ACommand: Word): Boolean; dynamic; | Имитирует щелчок по опции, свойство которой command имеет значение ACommand. Если такой опции нет или она не является исполняемой, возвращает False
procedure Merge(Menu: TMainMenu); | Объединяет пункты главного меню с пунктами меню Menu
procedure Unmerge(Menu: TMainMenu) ; | Удаляет из главного меню пункты меню Menu

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

property AutoCheck: Boo lean; | Определяет, будет или нет автоматически изменяться свойство Сhecked при выборе опции меню
type TMenuItemAutoFlag = (maAutomatic, maManual, maParent); | Определяет, будет ли следить программа за уникальностью клавиш быстрого выбора для подоп ций: maAutomatic - автоматически при создании
property AutoHotkeys: TMenuItemAutoFlag; | подопции; maManual - с помощью вызова специального метода; maParent - по правилу, установленному для опции верхнего уровня
property AutoLineReduction: TMenuItemAutoFlag; | Определяет, будет ли программа автоматически удалять лишние разделители между опциями многоколончатого меню второго уровня. Тип TMenuItemAutoFlag описан в свойстве AutoHotkeys
property Bitmap: TBitmap; | Содержит ссылку на связанное с опцией изображение. Это изображение (если оно есть) появляется слева от опции. Свойство игнорируется, если установлено свойство Imagelndex (см. ниже)
property Break: TMenuBreak; | Позволяет создать многоколончатый список подменю (см.ниже)
property Checked: Boolean; | Если True, рядом с опцией появляется галочка
property Command: Word; | Используется при разработке приложений, обращающихся непосредственно к API-функциям Windows
property Count: Integer; | Содержит количество опций в подчиненном меню, связанном с данным элементом (только для чтения)
property Default: Boolean; | Определяет, является ли данная опция подменю умалчиваемой (см. ниже)
property Grouplndex: Byte; | Определяет групповой индекс для зависимых опций (см. свойство Radioltem)
property Imagelndex: Integer; | Содержит индекс связанного с опцией изображения из компонента TimageList (см. п. 18.3.3). Это изображение (если оно есть) появляется слева от опции. Если свойство имеет значение -1, с опцией не связано никакого изображения из TimageList
property Items[Index: Integer] : TMenuItem; | Позволяет обратиться к любой опции подчиненного меню по ее индексу
property Menulndex: Integer; | Определяет индекс опции в списке items родительской опции
property Radio Itern: Boolean; | Определяет, зависит ли данная опция от выбора других опций в той же группе Grouplndex. Только одна опция группы может иметь True в свойстве checked. Рядом с такой опцией вместо галочкиизображается круг (см. ниже рис. 18.6)
property Shortcut: TshortCut; | Задает клавиши-акселераторы для быстрого выбора данной опции
property SubMenuImages: TCustomImageList; | Содержит ссылку на объект - хранилище изображений, которые могут появляться около индивидуальных элементов (пунктов) в подменю этого пункта меню

Рис. 18.4. Разветвленная структура меню

В Delphi 4 (5 и 6) появилась возможность связывать с опциями меню небольшие изображения. Эти изображения можно задать либо свойством BitMap, либо свойством imageindex. В последнем случае предполагается, что для меню в целом определено свойство Images либо (только для версии 6) свойство SubMenuImages опции верхнего уровня. Изображение показывается слева от опции. Например, на рис. 18.5 показан фрагмент главного меню Delphi с раскрытой опцией File.

Рис. 18.5. Подменю File среды Delphi

Как видим, опции Open, open Ptoject, Save и т. д. снабжены небольшими пиктограммами. Введение пиктограмм в меню, с одной стороны, повышает наглядность меню, а с другой - способствует унификации пиктограмм в рамках механизма действий (см. п. 17.7). Если опция меню связана с каким-то действием своим свойством Action, а компонент TActionList, в котором это действие описано, в свою очередь, связан с хранилищем пиктограмм TimageList (см. 18.5.3), индекс нужной пиктограммы можно задать в свойстве ImageIndex. В этом случае пиктограмма, указанная в свойстве BitMap (если она указана в нем), игнорируется.

Тип TMenuBreak определен следующим образом:

type TMenuBreak = (mbNone, mbBarBreak, mbBreak);

Свойство Break по умолчанию имеет значение mbNone. Два других возможных значения этого свойства используются для создания многоколончатых списков подменю (см. рис. 18.6). Значение mbBarBreak заканчивает предыдущую колонку и начинает новую. Эта опция меню будет открывать новую колонку, которая отделяется от предыдущей вертикальной чертой. Значение mbBreak также создает новую колонку, но не вставляет разделительную черту. Эти значения игнорируются в опциях самого первого уровня. В окне конструктора многоколончатое меню отображается как обычное.

Если в опции Default установлено значение True, такая опция выделяется цветом и выбирается двойным щелчком мыши на родительской опции (см. рис. 18.6). Только одна опция в подменю может быть умалчиваемой.

Рис. 18.6. Пример многоколенчатого меню

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

Для элемента меню определено единственное событие Onciick, которое возникает при щелчке на опции или при нажатии Enter, если в этот момент данная опция была выбрана (подсвечена). Обработчик события становится доступен после двойного щелчка на опции в окне конструктора меню.

18.1.3. TPopupMenu - вспомогательное (локальное) меню

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

Вспомогательное меню создается с помощью конструктора меню и содержит элементы описанного в п. 18.1.2 класса TMenuItem, поэтому процесс создания и свойства вспомогательного меню ничем не Отличаются ОТ TMainMenu.

18.1.4. TLabel - метка для отображения текста

Компоненты класса TLabel (метка) предназначены для размещения на форме различного рода текстовых надписей. Для этого служит центральное свойство компонента - Caption. С помощью свойства Font можно разнообразить вид надписи (оба эти свойства достались метке от предка TGraphicControl и поэтому не отображаются в таблице ниже). С компонентом может быть связан оконный управляющий элемент, который выбирается при нажатии Alt+Буква, где Буква - выделенная подчеркиванием буква в тексте метки. Такие символы в терминологии Windows называются акселераторами.

Свойства компонента (кроме унаследованных от TGraphicControl):

type TAlignment = (taLeftJustify, taRightJustify, taCenter) ; property lignment: TAlignment; | Определяет выравнивание текста по горизонтали относительно границ компонента (для AutoSize=False): taLeftJustify - Прижат К левой границе; taRightJustify - прижат к правой границе; taCenter - выровнен по центру
property AutoSize: Boolean; | Указывает, будет ли метка изменять свои размеры в зависимости от помещенного в ее свойство Caption текста: True - будет
property FocusControl: TWinControl; TTextLayout = (tITop, tICenter, tiBottom) ; property Layout: TTextLayout; | Содержит имя оконного компонента, который связан с меткой-акселератором Определяет выравнивание текста по вертикали относительно границ метки: tiTop - текст располагается вверху; ticenter - текст центрируется по вертикали; tiBottom - текст располагается внизу
property ShowAccelChar: Boolean; | Если содержит True, символ & в тексте метки предшествует символу-акселератору
property Transparent: Boolean; | Определяет прозрачность фона метки. Если False, фон закрашивается собственным цветом Color, в противном случае используется фон родительского компонента
property Wordwrap: Boolean; | Разрешает/запрещает разрыв строки на границе слова. Для вывода многострочных надписей задайтеAuroSize=False, wordwrap=True и установите подходящие размеры метки

Метка Label может отображать длинную текстовую строку своего свойства Caption в виде нескольких строк: для этого установите в AutoSize значение False, задайте достаточно большие размеры метки и поместите в Wordwrap значение True. Для отображения действительно многострочного текста используйте компоненты TMemo (см.п. 18.1.7)HTRichEdit(cM.n. 18.3.4).

18.1.5. TEdit - ввод и отображение строки

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

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

var

OldText: String;

procedure TFormI.EditlChange(Sender: TObject);

begin

if Editl.Text<>'' then

try

StrToFloat(Editi.Text) ;

OldText := Editl.Text except

// Ошибка преобразования: восстанавливаем прежний текст

Editl.Text := OldText;

// и позиционируем текстовый указатель в конец текста:

Editl.SelStart := Length(Editi.Text);

Editi.SelText := ''

end

end;

При повторении примера подготовьте глобальную переменную OldText типа String, в которой запоминается последний правильно введенный текст. Лучше всего ее поместить в секцию Private класса формы - тогда ее можно не обнулять в момент начала работы.

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

Свойства компонента:

property AutoSelect: Boolean; | Указывает, будет ли выделяться весь текст в момент получения компонентом фокуса ввода
property AutoSize: Boolean; | Если содержит True и BorderStyle=bsSingle, высота компонента автоматически меняется при изменении свойства Font. Size
TBorderStyle = bsNone..bsSingle; property BorderStyle: TBorderStyle; | Определяет стиль обрамления компонента: bsNone - нет обрамления; bsSingle - компонент обрамляется одной линией
property CanUndo: Boolean; | Содержит True, если сделанные пользователем изменения в тексте Text можно убрать методом Undo
TEditCharCase = (ecNormal, ecUpperCase, ecLowerCase) ; | Определяет автоматическое преобразование высоты букв: ecNormal - нет преобразования; ecUpper
property CharCase: TEditChar Case; | Case - все буквы заглавные; ecLowerCase - все буквы строчные. Правильно работает с кириллицей
property HideSelection: Boolean; | Если содержит False, выделение текста сохраняется при потере компонентом фокуса ввода
property MaxLength: Integers; | Определяет максимальную длину текстовой строки. Если имеет значение 0, длина строки не ограничена
property Modified: Boolean; | Содержит True, если текст был изменен
property OnChange: TNotifyEvent; | Определяет обработчик события OnChange, которое возникает после любого изменения текста
property OEMConvert: Boolean; | Содержит True, если необходимо перекодировать текст из кодировки MS-DOS в кодировку Windows и обратно
property PasswordChar: Char; | Если символ PasswordChar определен, он заменяет собой любой символ текста при отображении в окне. Используется для ввода паролей
property Readonly: Boolean; | Если содержит True, текст не может изменяться
property SelLength: Integers; | Содержит длину выделенной части текста
property SelStart: Integers; | Содержит номер первого символа выделенной части текста
property SelText: Strings- | Содержит выделенную часть текста
property Text: String; | Содержит весь текст
Методы компонента:
procedure Сlears; | Удаляет весь текст
procedure ClearSelection; | Удаляет выделенный текст
procedure ClearUndo; | Очищает буфер метода Undo
procedure CopyToClipboard; | Копирует выделенный текст в clipboard
procedure CutToClipboard; | Копирует выделенный текст в clipboard, после чего удаляет выделенный текст из компонента
function GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integers; | Копирует не более BufSize символов выделенного текста в буфер Buffer
procedure PasteFromClipboard; | Заменяет выделенный текст содержимым Clipboard, а если нет выделенного текста, копирует содержимое Clipboard в позицию текстового курсора
procedure SelectAll; | Выделяет весь текст
procedure SetSelText-Buf(Buffer: PChar); | Заменяет выделенный текст содержимым Buffer, а если нет выделенного текста, копирует содержимое Buffer в позицию текстового курсора
procedure Undo; | Восстанавливает текст в том виде, в каком он был перед последним получением компонентом фокуса ввода

Свойство CanUndo и методы СlearUndo и undo впервые введены в версии Delphi 4.

18.1.6. TMemo - ввод и отображение текста

Компоненты класса тмето предназначены для ввода, редактирования и/или отображения достаточно длинного текста. Текст хранится в свойстве Lines класса TStrings и, таким образом, представляет собой пронумерованный набор строк (нумерация начинается с нуля). С помощью свойств И методов этого класса (Count, Add, Delete, Clear и т. д. - см. п. 16.3.1) можно динамически формировать содержимое компонента.

Свойства BorderStyle, CanUndo, HideSelection, MaxLentgh, Modified, OEMConvert, OnChange, Readonly, SelLength, SelStart и SelText аналогичны соответствующим свойствам класса TEdit. Свойство WordWrap аналогично свойству TLabel. WordWrap. Другие специфичные свойства представлены ниже:

property CaretPos: TPoint; | Содержит координаты мигающего текстового курсора относительно границ клиентской области компонента (только для Delphi 4...6)
property Lines: TStrings; TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth) ; property ScrollBars: Tscroll-Style; | Содержит строки текста Определяет наличие в окне редактора полос прокрутки: ssNone - нет полос;ssHorizontal -есть горизонтальная полоса; ssVertical - есть вертикальная полоса; ssBoth - есть обе полосы
property WantReturns: Boolean; | Если содержит True, нажатие Enter вызывает переход на новую строку, в противном случае обрабатывается системой. Для перехода на новую строку в этом случае следует нажать Ctrl+Enter
property WantTabs: Boolean; | Если содержит True, нажатие Tab вызывает ввод в текст символа табуляции, в противном случае - обрабатывается системой. Для ввода символа табуляции в этом случае следует нажать Ctrl+Tab

Замечу, что, если свойство ScrollBars содержит ssHorizontal или ssBoth, свойство wordwrap игнорируется, и длинные строки будут отсекаться границами компонента без переноса текста на следующую строку.

Специфичные методы класса аналогичны методам класса TEdit.

Поскольку компонент является потомком TControl, он имеет также свойство Text, которое содержит отображаемый компонентом текст в виде одной длинной строки. В этой цепочке символов границы строк многострочного текста выделяются символами #13#10 (признак eoln - конец строки). В отличие от этого свойство Lines содержит пронумерованный список строк: первая строка в этом списке имеет индекс 0, вторая - 1, а общее количество строк можно узнать с помощью Lines .count.

Свойство Text удобно использовать для поиска в тексте нужного фрагмента. Чтобы, например, найти и выделить в тексте фрагмент,. содержащийся В компоненте edSearch типа TEdit, можно использовать такой обработчик события onciick кнопки btSearch:

procedure TFormI.btSearchClick(Sender: TObject);

var

k: Integer;

begin

with Memo1do

begin

k: := pos(edSearch.Text,Text);

if k>0 then

begin

SelStart := k-1;

SelLength := Length(edSearch.Text)

end

end;

end;

Если вы захотите, чтобы найденный в тексте фрагмент после установки SelStart и SelLength сразу бы выделился цветом, установите False в свойство HideSelection.

Для загрузки в компонент текста из файла и для сохранения текста в файле удобно использовать методы LoadFromFile И SaveToFile класса TStrings (см. п. 16.3.1). Например, следующий обработчик события OnCreat формы Form1 загружает в Memo1 текст проектного файла программы:

procedure TFormI.FormCreate(Sender: TObject);

begin

Memol.Lines.LoadFromFile(

ChangeFileExt(Application.ExeName,'.dpr'))

end;

18.1.7. TButton - кнопка

Кнопки TButton широко используются для управления программами. Связанный с кнопкой алгоритм управления реализуется в обработчике события OnClick.

Свойства компонента:

property Cancel: Boolean; | Если имеет значение True, событие OnClick кнопки возникает при нажатии клавиши Esc
property Default: Boolean; | Если имеет значение True, событие OnClick кнопки возникает при нажатии клавиши Enter
type TModaiResult = Low(Integer)..High(Integer) ; property ModalResult: TModaiResult; | Определяет результат, с которым было закрыто модальное окно (см. ниже пояснение)

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

mrNone Модальное окно не закрывается

mrlgnore Была нажата кнопка Ignore

mrOk Была нажата кнопка Oк

mrYes Была нажата кнопка Yes

mrCancel Была нажата кнопка Cancel

mrNo Была нажата кнопка No

mrAbort Была нажата кнопка Abort

mrAll Была нажата кнопка All

mrRetry Была нажата кнопка Retry

В отличие от большинства других видимых компонентов кнопка TButton является компонентом самой Windows и поэтому не может изменять свой цвет произвольным образом - она его меняет вместе с изменением палитры Windows. Кнопка всегда имеет системный цвет clBtnFace и не имеет свойства Color. Шрифт надписи на кнопке может менять свой стиль и размер, но компонент игнорирует изменение его цвета.

18.1.8. TCheckBox - независимый переключатель

Независимый переключатель TCheckBox используется для того, чтобы пользователь мог указать свое решение типа Да/Нет или Да/Нет/Не совсем (в последнем случае в окошке компонента устанавливается флаг выбора, но само окошко закрашивается серым цветом). Это решение отражается в свойстве State компонента, доступном как для чтения, так и для записи. В составе диалогового окна может быть несколько компонентов TCheckBox. Состояние любого из них не зависит от состояния остальных, поэтому такие переключатели называются независимыми.

Типичное использование компонента:

if CheckBoxl.Checked then

else

Или:

case CheckBoxl.State of cbChecked :... ;

cbUnchecked:...;

cbGrayed :...;

end;

Свойства компонента:

type TLeftRight = (taLeftJustify, taRightJustify) ; property Alignment: TLeftRight; | Определяет положение текста: taLeftJustify - с левой стороны компонента; taRightJustify - С Правой стороны
property AllowGrayed: Boolean; | Разрешает/запрещает использование состояния cbGrayed (Не совсем)
ptoperty Caption: Strings | Содержит связанный с компонентом текст
property Checked: Boolean; | Содержит выбор пользователя типа Да/Нет. Состояния cbUnchecked и cbGrayed отражаются как False
type TCheckBoxState = (cbUnchecked, cbChecked, cbGrayed) ; property State: TCheckBoxState | Содержит состояние компонента: cbUnchecked - нет; cbChecked - да; cbGrayed - не совсем

Свойство color компонента фактически игнорируется, а свойства Height и width определяют размеры прямоугольника, в котором выводится связанный с переключателем текст, и не влияют на размеры прямоугольного окошка. Сам текст указывается в свойстве Caption.

18.1.9. TRadioButton - зависимые переключатели

В отличие от TCheckBox компоненты TradioButton представляют собой зависимые переключатели, предназначенные для выбора одного из нескольких взаимоисключающих решений. На форму (точнее, в компонент-контейнер) помещается по меньшей мере два таких компонента. Они могут иметь только два состояния, определяемых свойством Сhecked. Если в одном компоненте это свойство принимает значение True, во всех других компонентах, расположенных в том же контейнере, свойства Сhecked принимают значения False.

Помимо свойства checked компонент TRadioButton имеет еще одно специфичное свойство - Alignment, аналогичное такому же свойству TCheckBox. Как и в TCheckBox, программист не может изменять размеры и цвет круглого окошка компонента.

18.1.10. TListBox - список выбора

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

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

Свойства компонента:

property AutoComplete: Boolean; | Определяет, будет ли компонент автоматически получать фокус ввода при нажатии пользователем на клавиатуру
type TBorderStyle = bsNone..-bsSingle; property Border-Style: TBorderStyle; | Определяет стиль рамки: bsNone - нет рамки; bssingle - рамка толщиной 1 пиксель
property Canvas: TCanvas; property Columns: Longing; | Канва для программной прорисовки элементов Определяет количество колонок элементов в списке
property Count: Integer; | Содержит количество строк в компоненте
property ExtendedSelect: Boolean; | Если ExtendedSelect =True и MultiSelect==True, выбор элемента без одновременного нажатия Crtl или Alt отменяет предыдущий выбор
property IntegralHeight:Boolean; | Если IntegralHeight=True и Style<>lb0wner-DrawVariabe, в списке показывается целое число элементов
property ItemHeight: Integer; | Определяет высоту элемента в пикселях для Style=lbOwnerDrawFixed
property Itemlndex: Integer; | Содержит индекс сфокусированного элемента. Если MultiSelect=False, совпадает с индексом выделенного элемента
property Items: TStrings; | Содержит набор строк, показываемых в компоненте
property MultiSelect: Boolean; | Разрешает/отменяет выбор нескольких элементов
property SelCount: Integer; | Содержит количество выбранных элементов
property Selected[X: Integer] : Boolean; | Содержит признак выбора для элемента с индексом х (первый элемент имеет индекс 0)
property Sorted: Boolean; | Разрешает/отменяет сортировку строк в алфавитном порядке
type TListBoxStyle = (IbStandard, IbOwnerDrawFixed, IbOwnerDrawVariable) ; property Style: TListBoxStyle; | Определяет способ прорисовки элементов: IbStandard - элементы рисует Windows; ibOwnerDrawFixed - рисует программа, все элементы имеют одинаковую высоту, определяемую свойством ItemHeight;IbOwnerDrawVariable -рисует программа, элементы имеют разную высоту
property TabWidth: Integer; | Задает ширину табуляционного пробела
property Toplndex: Integer; | Индекс первого видимого в окне элемента

Создание элементов (опций) списка компонента реализуется с помощью методов его свойства Items - Add, Append, Insert млм LoadFromFile (CM. П. 16.3.1).

Для компонента определены два события, связанные с программной прорисовкой элементов списка:

type

TOwnerDrawState = set of (odSelecred, odGrayed, odDisabied, odChecked, odFocused) ;

TDrawItemEvent = procedure(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState) of object-property OnDrawItem: TDrawItemEvent;

type

TMeasureItemEvent = procedure(Control: TWinControl; Integer;

var Height: Integer) of object/property OnMeasureItem: TMeasureItemEvent;

Первое событие возникает в момент, когда программа должна нарисовать очередной элемент. Обработчик события получает ссылку на список выбора control, индекс изображаемого элемента index, границы элемента Rect и его состояние State. Прорисовка ведется с помощью свойства Canvas.

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

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

procedure TFormI.ListBoxIDrawItem(Control: TWinControl;

Index: Integer; Rect: TRect; State: TOwnerDrawState);

var

Bitmap: TBitmap;// Временная переменная для растра

Offset: Integer;

// Расстояние от растра до текста

begin

{Свойство Canvas имеет не только список ListBox, но и форма, на которую он помещен! Поэтому нужно явно указать канву. Преобразуем тип и указываем канву:}

with Control as TListBox, Canvas do

begin

FillRect(Rect);

// Очищаем прямоугольник

// Получаем из списка растр:

Bitmap := TBitmap(Items.Objects[Index]);

if Bitmap <> nil then

begin

// Вычерчиваем растр:

BrushCopy(Bounds(Rect.Left + 2, Rect.Top, Bitmap.Width, Bitmap.Height), Bitmap, Bounds(0, 0, Bitmap.Width, Bitmap.Height), cIRed);

// Вычисляем смещение текста (4 пикселя от растра):

Offset := Bitmap.width + 6;

end

else Offset := 2;

// Если нет растра, 2 пикселя слева

// Выводим текст:

TextOut(Rect.Left + Offset, Rect.Top, Items[Index]);

end;

end;

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

PillRect(Rect); // Очищаем прямоугольник в предыдущем примере вставить

if odSelected in State then

begin

Brush.Color := clYellow;

Font.Color := clBlack;

Font.Style := [fsBold];

end;

выбранные элементы будут подсвечены желтым фоном, а текст в них будет выведен черным утолщенным шрифтом.

Событие OnMeasureItem возникает только для Style=lbOwnerDrawVariabie. Оно предшествует событию onDrawItem, и в ходе его обработки программа должна установить нужную высоту очередного элемента. Обработчику передается индекс элемента Index и переменная Height, в которой он должен вернуть высоту элемента. Например:

procedure TFormI.ListBoxIMeasureItem(Control: TWinControl;

Index: Integer;

var Height: Integer);

var

Bitmap: TBitmap;

begin

Bitmap := TBitmap((Control as TListBox).Items.Objects[Index]) ;

if Bitmap <> nil then

Height := Bitmap.Height

else

Height := abs(ListBoxl.Font.Height)

end;

18.1.11. TComboBox - раскрывающийся список выбора

Комбинированный список TComboBox представляет собой комбинацию списка TListBox и редактора TEdit, и поэтому большинство его свойств и методов заимствованы у этих компонентов. Существуют пять модификаций компонента, определяемые его свойством style:

csSimple, csDropDown, csDropDownList, csOwnerDrawFixed И csOwner DrawVariabie. В первом случае список всегда раскрыт, в остальных он раскрывается после нажатия кнопки справа от редактора (рис. 18.7). В модификации csDropDownList редактор работает в режиме отображения выбора и его нельзя использовать для ввода новой строки (в других модификациях это возможно). Модификации csOwnerDrawFixed и csOwnerDrawVariable используются программной прорисовки элементов списка. Используемые для этого свойства и методы полностью совпадают со свойствами и методами TListBox аналогичного назначения.

Фактически “своими” у компонента являются лишь свойства и события, связанные с раскрытием списка:

property DropDownCount: Integer;

property DroppedDown: Boolean;

property OnDropDown: TNotifyEvent;

Рис. 18.7. Примеры комбинированных списков

Свойство DropDownCount определяет количество элементов списка, появление которых еще не приводит к необходимости прокрутки списка. По умолчанию это свойство имеет значение 8: если в списке указано 9 и более элементов (т. е. больше, чем содержит DropDownCount), при его раскрытии к окну будет добавлена полоса прокрутки. Свойство DroppedDown определяет, раскрыт ли в данный момент список. Это свойство доступно также для записи, что позволяет программно управлять состоянием списка. Событие OnDropDown происходит при изменении состояния списка.

Наполнение списка ведется методами Add, Append, Insert и т. п. его свойства Items класса TStrings (см. п. 16.3.1). По поводу использования свойства items и программной прорисовки элементов списка см. замечания в п. 18.1.10.

18.1.12. TScrollBar - управление значением величины

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

Свойства компонента:

TSrollBarKind = (sbHorizontal, sbVertical) ; property Kind: TScrollBarKind; property LargeChange: TScroll-Barinc; | Определяет ориентацию компонента: sbHorizontal - бегунок перемещается по горизонтали; sbVertical - бегунок перемещается по вертикали “Большой” сдвиг бегунка (при щелчке мышью рядом с концевой кнопкой)
property Max: Integer; | Максимальное значение диапазона изменения числовой величины
property Min: Integers; | Минимальное значение диапазона изменения числовой величины
property PageSize: Integers; | Определяет размер позиции табуляции бегунка
property Position: Integers;property SmallChange: TScroll-Barinc; | Текущее значение числовой величины “Малый” сдвиг бегунка (при щелчке мышью по концевой кнопке)

С помощью метода

procedure SetParams(APosition, AMax, AMin: Integer);

можно сразу установить свойства Position, мах и Min.

С компонентом связано два события:

type TScrollEvent = procedure(Sender: TObject;

ScrollCode: TScrollCode; var ScrollPos: Integer) of objects;

property OnScroll: TScrollEvent;

property OnChange: TNotifyEvent;

Первое возникает при любом изменении свойства position, второе -при изменении параметров методом Setparams.

Подобно TButton компонентом полностью управляет Windows поэтому у него нет свойства Color.

18.1.13. TGroupBox - панель группирования

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

18.1.14. TRadioGroup - группа зависимых переключателей

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

Свойства компонента:

property Columns: Integers; | Определяет количество столбцов переключателей
property Itemlndex: Integer; | Содержит индекс выбранного переключателя
property Items: TStrings; | Содержит список строк с заголовками элементов. Добавление/удаление элементов достигается добавлением/удалением строк списка items (см. п. 16.3.1)

После размещения компонента на форме он пуст. Чтобы создать в нем хотя бы один переключатель, следует раскрыть редактор списка Items и ввести хотя бы одну строку: строки Items используются как поясняющие надписи справа от переключателей, а их количество определяет количество переключателей в группе. Замечу также, что после создания компонента его свойство Itemlndex по умолчанию имеет значение -1, это означает, что ни один переключатель в группе не выбран. Если в момент появления компонента на экране в каком-то переключателе выбор уже должен быть установлен, необходимо на этапе конструирования с помощью окна Инспектора объектов или программно (например, в обработчике OnActivate формы) установить в свойство ItemIndex номер соответствующего переключателя (нумерация начинается с 0). Это же свойство позволяет программе проанализировать выбор пользователя, например:

case RadioGroupl.Itemlndex of

0: ...; //Выбран 1-й переключатель

1: ...; //Выбран 2-й переключатель

else

..... //Не выбран ни один переключатель

end;

18.1.15. TPanel - панель

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

Компонент имеет развитые средства создания различных эффектов трехмерности за счет использующихся в нем двух кромок -внешней и внутренней (см. п. 17.4).

Свойства компонента:

type TAlignment = (taLeftJustify, taRightJustify, taCenter) ; property Alignment: TAlignment; | Определяет выравнивание текста относительно границ компонента
type TBevelCut = (bvNone, bvLow-ered, bvRaised, bvSpace) ; | Определяет вид внутренней кромки: bvNone - кромки нет; bvLowered - вдавленная кромка; bvRaised - приподнятая кромка; bvSpace - то же, что и bvRaised
property BevelInner: TPanelBevel; | Определяет вид внешней кромки
property BevelOuter: TPanelBevel; type TBevelWidth = l..MaxInt; | Определяет толщину кромок
property BevelWidth: TBevelWidth; TBorderStyle = bsNone..bsSingle; property BorderStyle: TBorderStyle; | Определяет стиль рамки: bsNone - нет рамки; bsSingle - компонент по периметру обводится линией толщиной в 1 пиксель
property FullRepaint: Boolean; | Разрешает/запрещает перерисовку панели и всех ее дочерних элементов при изменении ее размеров
property Locked: Boolean; | Используется при работе с объектами OLE

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

18.1.16. TActionList - механизм действий

Этот компонент впервые введен в Delphi 4. Он не имеет видимого изображения и служит для поддержки механизма действий (см. п. 17.7).

Основная схема его использования такова. Вначале с помощью его редактора создается действие - объект класса TAction (редактор вызывается двойным щелчком на компоненте либо с помощью опции ActionList Editor его вспомогательного меню, которое раскрывается после щелчка на нем правой кнопкой мыши - рис. 18.8).

Этот объект (на рисунке он имеет умалчиваемое имя Action1) имеет ряд свойств и событий, с помощью которых уточняется характер действия. Доступ к этим свойствам и событиям можно получить с помощью окна Инспектора объектов. С действием можно связать группу свойств (Caption, Checked, Enabled, Shortcut И Т. Д.), которыне будут помещаться в одноименные свойства компонентов, реализующих общее действие. Если с компонентом связан контейнер пиктограмм типа TImageList (свойство images - не действия, а самого компонента TActionList), при реализации действия можно использовать одну из хранящихся в нем пиктограмм (ImageIndex). Чтобы действие подкреплялось программным кодом, для него обязательно следует определить обработчик события OnExecute.

В свойства Action тех компонентов, активизация которых должна сопровождаться одним и тем же действием (например, в свойства Action опции меню и кнопки инструментальной панели), помещается имя только что определенного действия (Actioni). В результате выбор опции или нажатие кнопки вызовут один и тот же метод (OnExecute), в меню появится пиктограмма (imageindex), такая же пиктограмма будет у кнопки, у них будет одинаковые названия (caption), оперативные подсказки (Hint) И Т. Д.

Свойства компонента:

property ActionCount: Integer; | Содержит количество определенных в компоненте действии (только для чтения)
property Actions[Index: Integer]: TContainedAction; | Позволяет программе обратиться к нужному действию (объекту класса TContainedAction) по его индексу index
property Images: TCustomImageList; | Содержит имя компонента класса TimageList
type TActionListState = (asNormal, asSuspended, asSus-pendedEnabled) ; property State: TActionListState; | Состояние действий: asNormal - нормальное; asSuspended - механизм действий отключен, их свойства Enabled не изменены; asSuspendedEnabled - механизм действий отключен, но их свойства Enabled имеют значения True

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

Свойства TAction:

property Caption: String; | Содержит строку, которая будет устанавливаться в свойствах caption всех компонентов, связанных данным действием
property Checked: Boolean; | Содержит значение, которое будет устанавливаться в свойствах Checked
property DisableIfNoHandler: Boolean; | Указывает, будут ли запрещены для выбора связанные компоненты, если для действия не определен обработчик OnExecute
property Enabled: Boolean; | Содержит значение, которое будет устанавливаться в свойствах Enabled
property HelpContext: THelpContext; | Содержит значение, которое будет устанавливаться в свойствах HelpContext
property Hint: String; | Содержит строку, которая будет устанавливаться в свойствах Hint
property Imagelndex: Integer; | Содержит индекс изображения в хранилище, указанном в свойстве images компонента. Это изображение будет связано с компонентами данного действия
property Shortcut: TShortCut; | Содержит значение, которое будет устанавливаться в свойствах shortcut
property Visible: Boolean; | Содержит значение, которое будет устанавливаться в свойствах visible

Рис. 18.8. Редактор компонента TActionList

События TAction:

property OnExecute: TNotifyEvent; | Возникает при щелчке мышью на одном из компонентов, связанных общим действием. Обработчик этого события должен реализовать нужное действие
THintEvent = procedure (var HintStr: String; var CanShow: Boolean) of object; property OnHint: THintEvent; | Возникает при перемещении указателя мыши над одним из связанных общим действием компонентов. Его умалчиваемый обработчик создает окно оперативной подсказки и показывает в нем строку HintStr
property OnUpdate: TNotifyEvent; | Возникает, когда очередь сообщений для приложения пуста или когда обновляется содержимое списка действий

В версии Delph6 введена группа компонентов (См. ниже пп. 18.2.23), обеспечивающая более мощную Поддержку механизма действий, в том числе создание настраиваемого пользовательского интерфейса-,, Таким образом, компонент TActionList используется лйщБ;яД&Рвместимо(дхс более разними версиями,, . .

18.2. КОМПОНЕНТЫ СТРАНИЦЫ ADDITIONAL

18.2.1. TBitBtn - кнопка с изображением

Пиктографическая кнопка TBitBtn представляет собой популярную разновидность стандартной кнопки TButton. Ее отличительная особенность - свойство Glyph, с помощью которого определяется растровое изображение, рисуемое на поверхности кнопки. В состав поставки Delphi входит множество рисунков, разработанных специально для размещения в этих кнопках (по умолчанию для старших версий Delphi эти рисунки размещаются в папке program Files |

Common Files | Borland Shared | Images | Buttons, для остальных версий - в папке images l Buttons каталога размещения Delphi).

Свойство Kind определяет одну из 11 стандартных разновидностей кнопки, показанных на рис. 18.9.

Рис. 18.9. Разновидности кнопок TBitBtn

Нажатие любой из них, кроме bkCustom и bkHelp, закрывает модальное окно и возвращает в программу результат mrxxx: bkok - mrОk, bkCancel - mrCancel И Т. Д. Кнопка bkClose для модального окна возвращает mrCancel, а для главного окна программы - закрывает его и завершает работу программы. Кнопка bkHelp автоматически вызывает раздел справочной службы, связанный с Heipcontext формы, на которую она помещена. Если у кнопки была изменена пиктограмма Glyph, Delphi автоматически присвоит ей Kind=bkCustom. Это произойдет также в случае, когда кнопка указана как умалчиваемая (Defauit=True), но Kind не содержит bkok или bkYes, а также если ее свойство Cancel содержит True, a Kind нe содержит bkCancel или bkNo.

Свойства Cancel, Default И ModalResult кнопка TBitBtn унасле-довала у своего родительского класса TButton, остальные специфичные свойства указаны ниже:

property Glyph: TBitmap; TBitBtnKind = (bkCustom, bkOK, bkCancel, bkHelp, bkYes, bkNo, bkClose, bkAbort, bkRetry, bklg-nore, bkAll) ; property Kind: TBitBtnKind; | Определяет от 1 до 4 связанных с кнопкой растровых изображения (см. ниже) Определяет разновидность кнопки (рис. 18.9)
TButtonLayout = (bIGlyphLeft, bIGlyphRight, bIGlyphTop, bIGlyphBottom) ; property Layout: TButtonLayout; | Определяет край кнопки, к которому прижимается пиктограмма: bIGlyphLeft - к левому; bIGlyphRight - к правому; bIGlyphTop - к верхнему;biGlyphBottom - к нижнему)
property Margin: Integer; | Определяет расстояние в пикселях от края кнопки до пиктограммы
TNumGlyphs: 1..4 ; property NumGlyphs: TNumGlyphs; | Определяет количество растровых изображений (см.ниже)
property Spacing: Integer; | Определяет расстояние в пикселях от пиктограммы до надписи на кнопке
TByttonStyle = (bsAutoDetect, bsWinSI, bsNew) ; property Style: TButtonStyle; | Определяет стиль оформления кнопки, зависящий от операционной системы. Стиль bsNew соответствует 32-разрядным версиям Windows. Стиль bsAutoDetect изменяет оформление кнопки в зависимости от ОС, под управлением которой работает программа в данный момент

При разработке собственных растровых изображений для использования в кнопках следует учесть, что изображения должны меняться? при изменении состояния кнопки. Таких состояний может быть четыре: нормальное, запрещенное, нажатое и утопленное (последнее используется только в кнопках TSpeedButton). В соответствии с этим! разрабатывается до 4 пиктограмм, расположенных по горизонтали ввиде одного длинного растра. Например, стандартный размер пиктограммы для размещения на кнопке равен 16х16 пикселей. Если создаются 3 пиктограммы, размер растра должен составлять 48х16. Количество пиктограмм в растре задается свойством NumGlyphs. Если каждая; пиктограмма - квадратная и длина растра делится без остатка; на его высоту, Delphi автоматически распознает количество пиктограмм. Если задана только одна пиктограмма, ее изображение, меняется автоматически: в состоянии “нажатая” пиктограмма смещается! на один пиксель вправо и, вниз, а в состоянии “запрещенная” все цвета, кроме черного, меняются на светло-серый, а черный на белый, что обеспечивает эффект "вдавленности" изображения. Следует так же учесть, что самый левый нижний пиксель растра определяет цвет прозрачности: на кнопке этот цвет будет заменяться цветом поверхности кнопки.

Как и в кнопках TButton, программист не может управлять цветом поверхности кнопки, но в отличие от TButton может менять цвет надписи на ней. С помощью свойства Default кнопку можно сделать умалчиваемой - в этом случае нажатие Enter автоматически вызывает обработчик ее события OnClick. Однако умалчиваемыми можно сделать только кнопки bkYes и bkNo, остальные значения свойства Kind будут заменяться на bkcustom при размещении в Default значения True и наоборот - в Default автоматически помещается False при установке в Kind любого значения, кроме bkYes, bkNo или bkcus-tom.

18.2.2. TSpeedButton - кнопка для инструментальных панелей

Кнопки TSpeedButton отличаются от TBitBtn Тремя обстоятельствами: во-первых, они могут фиксироваться в утопленном состоянии, во-вторых, они не могут закрыть модальное окно в третьих, они не могут быть умалчиваемыми. Во всем остальном они повторяют свойства и методы TBitBtn.

Для фиксации кнопка должна быть отнесена к какой-либо группе кнопок (эта группа может состоять из нее одной - вариант одиночной фиксируемой кнопки). Для этого используется свойство GroupIndex: Integer, которое не должно быть равно 0. Поведение кнопки определяется логическим свойством AllowAllup: если это свойство имеет значение True, утопленная кнопка отпускается только при нажатии любой другой кнопки, входящей в ту же группу; если AllowAllup=False, кнопку можно освободить повторным щелчком. Индикатором состояния кнопки служит логическое свойство Down, которое имеет значение True, если кнопка утоплена. Свойство доступно для записи, что позволяет изменять состояние кнопки программно.

Для кнопки TSpeedButton предусмотрено событие onDbiciick, которое возникает при двойном щелчке на утопленной кнопке.

18.2.3. TMaskEdit - специальный редактор

Специализированный редактор TMaskEdit предназначен для ввода текста, соответствующего некоторому шаблону, задаваемому свойством EditMask: String. Если это свойство не задано, TMaskEdit работает как обычный редактор TEdit. Свойство IsMasked: Boolean доступно только для чтения и содержит True, если строка шаблона задана. Свойство EditText: string содержит текст до наложения на него маски шаблона (т. е. то, что ввел пользователь), а свойство Text: String может (в зависимости от шаблона см. ниже) содержать либо исходный текст, либо результат наложения на него маски шаблона.

Шаблон состоит из трех частей, отделенных друг от друга символами “;”. Первая часть задает маску ввода, вторая - это символ “О” или “I”, определяющий, записывается ли в Text результат наложения маски или исходный текст (“О” - исходный текст). В третьей части указывается символ, который в окне редактора будет стоять в полях, предназначенных для ввода символов (текст в редакторе может содержать символы маски; например, для ввода семизначного номера телефона текст в начале ввода может выглядеть так: (095)ХХХ-ХХ-ХХ где доступные для ввода пользователя поля указаны символом “X” -последним символом в шаблоне).

Маска состоит из описателей полей ввода, специальных символов и литералов. Описатель указывает, какой именно символ может ввести пользователь в данное поле (описатель всегда описывает поле единичной длины, т. е. рассчитанное на ввод одного символа). Литерал вставляется в текст, показываемый в окне редактора, но при вводе курсор перескакивает через литерал и не дает пользователю возможности изменить его. Литералами считаются любые символы, кроме описателей полей и специальных символов, а также любой символ, которому предшествует символ “\”. Специальные символы формируют дополнительные указания редактору.

Описатели полей ввода представлены в следующей таблице:

Символ

Поле...

L должно содержать букву

с может содержать любой символ

1 может содержать букву

0 должно содержать цифру

А должно содержать букву или цифру

9 может содержать цифру

а может содержать букву или цифру

# может содержать цифру, “+”, “-”

С должно содержать любой символ

Специальные символы:

\

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

:

На это место вставляется символ-разделитель Windows для часов, минут, секунд

/

На это место вставляется символ-разделитель Windows для полей даты

;

Разделитель частей шаблона

!

Подавляет все ведущие пробелы

>

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

<

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

<>

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

Примеры:

Маска

Вид в редакторе

Свойство EditText

Свойство Text

(095)000-0000;0;х

(095)ххх-хххх

123456

1234567

(095)0000000;1;х

(095)ххх-хххх

7 12345

(095)1234567

(095)\0\00-0000;1; .

(095)00.-....

1234567

(095)0012345

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

Остальные свойства компонента повторяют соответствующие свойства TEdit. В частности, установка в свойство passwordchar любого символа, кроме #0, обеспечивает секретность ввода: в этом случае все вводимые в компоненте символы заменяются символом PasswordChar.

18.2.4. TStringGrid - таблица строк

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

Таблица делится на две части - фиксированную и рабочую. Фиксированная служит для показа заголовков столбцов/рядов и для ручного управления их размерами. Обычно фиксированная часть занимает крайний левый столбец и самый верхний ряд таблицы, однако с помощью свойств FixedCols и FixedRows можно задать другое количество фиксированных столбцов и рядов (если эти свойства имеют 0, таблица не содержит фиксированной зоны). Рабочая часть - это остальная часть таблицы. Она может содержать произвольное количество столбцов и рядов, более того, эти величины могут изменяться программно. Рабочая часть может не умещаться целиком в пределах окна компонента, в этом случае в него автоматически помещаются нужные полосы прокрутки. При прокрутке рабочей области фиксированная область не исчезает, но меняется ее содержимое - заголовки строк и рядов.

Центральным свойством компонента является Cells - двухмерный массив ячеек, каждая из которых может содержать произвольный текст. Конкретная ячейка определяется парой чисел - номером столбца и номером ряда, на пересечении которых она находится (нумерация начинается с нуля). Свойство cells имеет тип String, поэтому программа может легко прочитать или записать содержимое нужной ячейки. Например:

Cells [1,1] := 'Левая верхняя ячейка рабочей зоны';

Количество ячеек по каждому измерению хранит пара свойств Colcount (количество столбцов) и RowCount (количество рядов). Значения-этих свойств и, следовательно, размеры таблицы могут меняться как на этапе разработки программы, так и в ходе ее работы, однако их значения должны быть как минимум на единицу больше соответственно значений в свойства FixedCois и FixedRows, определяющих размеры фиксированной зоны.

Содержимое ячеек можно редактировать. Для этого в таблице используется специализированный потомок редактора TMaskEdit.

Свойства компонента:

property BorderStyle: TBorderStyle; | Определяет рамку компонента: bsNone нет рамки; bsSingle - рамка толщиной 1 пиксель
property Cells[ACol, ARow: Integer] :String; | Определяет содержимое ячейки с табличными координатами (ACol, ARow)
property Col: Longint; | Содержит номер столбца сфокусированной ячейки
property ColCount: Longing- | Содержит количество столбцов таблицы
property Cols[Index: Integer]: TStrings; | Содержит все строки колонки с индексом Index
property ColWidths[Index: Longint] : Integer; | Содержит ширину столбца с индексом Index
property DefaultColWidth: Integers; | Содержит умалчиваемое значение ширины столбца
property DefaultDrawing: Boolean; | Разрешает/запрещает автоматическую прорисовку служебных элементов таблицы - фиксированной зоны, фона и прямоугольника сфокусированной ячейки и т. п.
property DefaultRowHeight: Integers; | Содержит умалчиваемую высоту рядов
property EditorMode: Boolean; | Разрешает/запрещает редактирование ячеек. гнорируется, если свойство Options включает goAlwayseShowEditor или не включает goEditing
property FixedColor: TColor; | Определяет цвет фиксированной зоны
property FixedCois: Integers; | Определяет количество столбцов фиксированной зоны
property FixedRows: Integers; | Определяет количество рядов фиксированной зоны
property GridHeight: Integers; | Содержит высоту таблицы
property GridLineWidth: Integers; | Определяет толщину линий, расчерчивающих таблицу
property GridWidth: Integers; | Содержит ширину таблицы
property LeftCol: Longint; | Содержит номер самого левого столбца, видимого в зоне прокрутки
property Objects [ACols ARow: Integer] : TObject; | Обеспечивает доступ к объекту, связанному с ячейкой (ACol, ARow)
property Options: TGridOptions; | Содержит параметры таблицы (см. ниже)
property Row: Longing; | Содержит номер ряда сфокусированной ячейки
property RowCount: Longint; | Содержит количество рядов таблицы
property RowHeights[Index: Long int] : Integer; | Содержит высоту ряда с индексом index
property Rows[Index: Integer]: TStrings; | Содержит все строки ряда с индексом Index
type TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth) ; | Определяет полосы прокрутки: ssNone -нет полос; ssHorizontal - в таблицу вставляется горизонтальная полоса;
property ScrollBars: TScroll Style; | ssVertical - вставляется вертикальная полоса; ssBoth - вставляются обе полосы
TGridRect = record case Integer of 0: (Left,Top,Right,Bottom: Long int) ; 1: (TopLeft, BottomRight: TGridCoord) ; end; | Определяет группу выделенных ячеек в координатах левая верхняя и правая нижняя ячейки(нумерация столбцов и рядов идет от нуля, включая столбцы и ряды фиксированной зоны). После выделения сфокусированной окажется правая нижняя ячейка
property Selection: TGridRect; property TabStops[Index: Long int] : Boolean; | Разрешает/запрещает выбирать столбец с индексом index при обходе ячеек клавишей Tab. Игнорируется, если Options не содержит goTabs
property TopRow: Longint; | Содержит номер самого верхнего ряда, видимого в прокручиваемой зоне ячеек
property VisibleColCount: Integer; | Содержит количество столбцов, полностью видимых в зоне прокрутки
property VisibleRowCount: Integer; | Содержит количество рядов, полностью видимых в зоне прокрутки

Для компонента определен тип TGridOptions:

type

TGridOption = (goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goDrawFocusSelected, goRowSizing, goColSizing, goRowMoving, goColMoving, goEditing, goTabs, goRowSelect, goAlwaysShowEditor, goThumbTracking);

TGridOptions = set of TGridOptions;

Элементы множества TGridOptions имеют следующий смысл:

goFixedVertLine | Столбцы фиксированной зоны разделяются вертикальными линиями
goFixedHorzLine | Ряды фиксированной зоны разделяются горизонтальными линиями
goVertLine | Столбцы рабочей зоны разделяются вертикальными линиями
goHorzLine | Ряды рабочей зоны разделяются горизонтальными линиями
goRangeSelect | Разрешено выделение нескольких ячеек. Игнорируется, если включен элемент goEdit
goDrawFocus Selected | Разрешено выделять сфокусированную ячейку так же, как выделенные
goRowSizing goColSizing goRowMoviog | Разрешено ручное (мышью) изменение высоты строк Разрешено ручное изменение ширины рядов Разрешено ручное перемещение рядов (нажать левую кнопку мыши на фиксированной ячейке перемещаемого ряда и, удерживая кнопку нажатой, переместить ряд на новое место)
goColMoving goEditing | Разрешено ручное перемещение столбца Разрешено редактирование ячейки. Игнорируется, если включен элемент goRowSelect. Редактирование начинается после щелчка мыши или нажатия клавиши F2 и завершается при щелчке по другой ячейке или нажатии клавиши Enter
goTabs goRowSelect | Разрешено обходить ячейки клавишей Tab (Shift+Tab) Обязывает выделять сразу все ячейки ряда и запрещает редактирование ячеек Разрешено редактировать сфокусированную ячейку: редактирование возможно после выбора ячейки клавишей Tab (Shift+Tab). Игнорируется, если не включен элемент goEditing
goAlwaysShowEditorgoThumbTracking | Разрешено обновление при прокрутке. Если этот элемент отсутствует, обновление ячеек произойдет только после окончания прокрутки

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

function CellRect(ACol, ARow: TRect; Longint): | Возвращает прямоугольник ячейки по номерам столбца ACol и ряда Arow
procedure MouseToCell(X, Y: Integer; var ACol, ARow: Longint); | Возвращает табличные координаты ячейки ACol и arow по экранным координатам(X,У)точки

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

TMovedEvent = procedure (Sender: TObject; Fromlndex, Tolndex: Longint) of object; property OnColumnMoved: TMovedEvent; TDrawCellEvent; | Возникает при перемещении столбца с индексом Fromlndex в положение, определяемое индексом ToIndex
TGridDrawState = set of (gdSelected, gdFocused, gdFixed) ; TDrawCellEvent = procedure (Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState) of object-property OnDrawCell: | Возникает при необходимости перерисовать ячейку с табличными координатами (Col, Row): Rect - прямоугольник прорисовки; State - состояние ячейки (gdSelected -ячейка выделена; gdFocused - ячейка сфокусирована; gdFixed - ячейка принадлежит фиксированной зоне таблицы). Для прорисовки используется табличное свойство Canvas
GetEditEvent = procedure (Sender: TObject; ACol, ARow: | Возникает при редактировании текста в ячейке с табличными координатами
Longint; var Value: String) ofobject; property OnGetEditMask: TGetE ditEvent; | (ACol.ARow). В параметре value обработчик должен вернуть шаблон для редактора TeditMask
property OnGetEditText: TGetE ditEvent; | Возникает при редактировании текста в ячейке с табличными координатами (ACol.ARow). В параметре value обработчик должен вернуть текст для редактора TEditMask (см. событие OmGetEditMask)
property OnRowMoved: TMovedEvent; | Возникает при перемещении ряда с индексом Fromindex в положение, определяемое индексом ToIndex (см. событие onColMoved)
SelectCellEvent = procedure (Sender: TObject; Col, Row: Long int; var CanSelect: Boolean) of object; property OnSelectCell: TSe lectCellEvent; | Возникает при попытке выделить ячейку с табличными координатами (col.Row). В параметре CanSelect обработчик сообщает о возможности выделения ячейки
TSetEditEvent = procedure (Sender: TObject; ACol, ARow: Longint; const Value: String) of object; property OnSetEditText: TSetEditEvent; | Возникает при завершении редактирования ячейки (ACol.ARow). В параметре value обработчик получает результат ввода или редактирования текста
property OnTopLeftChanged: TNotifyEvent; | Возникает после изменения значения ТоpRow или LeftCol в результате прокрутки рабочей зоны

В следующем примере компонент TStringGrid используется для показа всех слов из произвольного текстового файла (рис. 18.10).

  • Назовите пустую форму именем fmStGrid и положите на нее панель TPanel.
  • Установите в свойство панели Align значение alBottom, чтобы панель всегда занимала самую нижнюю часть окна, и удалите текст в ее свойстве caption.
  • Поместите на панель кнопку TBitBtn, установите в ее свойство

Caption строку Открыть файл..., загрузите в свойство Glyph файл images | buttons | FILE OPEN.BMP и растяните кнопку по горизонтали так, чтобы надпись и пиктограмма полностью умещались на ее поверхности.

Рис. 18.10. Пример использования TStringList

  • Поместите на панель еще одну кнопку TBitBtn и установите в ее свойство Kind значение bkClose.
  • Поместите на панель компонент OpenDialog из страницы компонентов Dialogs. Этот компонент создает и обсуживает стандартное для Windows окно открытия файла. С его помощью при прогоне программы вы сможете выбрать текстовый файл (например, файл с текстом программы), чтобы программа показала в таблице все слова из этого файла.
  • На форму (а не на панель panel1) положите компонент TStringGrid.
  • Поместите в его свойства ColCount и RowCount значения 2 (количество колонок и рядов таблицы должно быть больше, чем количество фиксированных колонок FixedCois и количество фиксированных рядов FixedRows). Установите в свойство Align компонента значение alClient, чтобы он занял все свободное место формы.

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

Создайте такой обработчик события OnClick для кнопки BitBtn1:

procedure TfmStGrid.BitBtnIClick(Sender: TObject) ;

Function GetWord(var S: String): String;

{ Вспомогательная функция для выделения очередного слова из

строки }

const // Множество символов слова:

Letters: set of Char = ['a'..'z', 'A'..'Z', 'A'..'я'];

begin

Result := '' ;

{ Уничтожаем в начале строки все символы, не относящиеся к слову }

while (S о '') and not (S[1] in Letters) do

Delete (S, 1, 1);

// Формируем очередное слово

while (S<> '') and (S[1] in Letters) do

begin

Result := Result + S[1];

Delete (S, 1, 1)

end;

end; //Get Word

var

F: TextFile; // Файл с текстом S,

Word: String; // Вспомогательные строки

NCol, NRow: Integer;// Номер текущей колонки и текущего ряда Words: TStringList; // Список отсортированных слов из файла

begin

// С помощью стандартного диалога получаем имя файла

if not OpenDialogI.Execute then

Exit; // Пользователь отказался выбрать файл

// Пытаемся открыть файл

AssignFile(F, OpenDialogI.FileName) ;

try

Reset(F) ;

except

// Файл нельзя открыть:

ShowMessage('Невозможно открыть файл ' +OpenDialogI.FileName) ;

Exit;

end;

// Готовим список Words:

Words := TStringList.Create;

Words.Sorted := True; // Сортируем строки Words.Duplicates := duplgnore; // Отвергаем дубликаты

// Изменяем курсор перед длительной работой

Screen.Cursor := crHourGlass;

// Читаем файл по строкам

while not EOF(F) do

begin

ReadLn(F, S); // Читаем очередную строку

// Выделяем из строки слова и заносим их в список Words

while S о '' do

begin

Word := GetWord(S) ;

if Word о '' then

Words.Add(Word) // He вставляем пустые строки

end

end;

Screen.Cursor := crDefault; // Восстанавливаем курсор

CloseFile(F); // Закрываем файл

if Words.Count=0 then

Exit; // Пустой файл - выходим

with sgWords do

begin

NCol := 1; // Номер первого столбца слов

// Цикл формирования таблицы

while Words.Count > 0 do

begin

{Формируем заголовок столбца и начальное значение номера ряда}

Cells [NCol, 0] := Words[0][1];

NRow := О

// Цикл заполнения очередного столбца

while (Words.Count > 0) and

(Words[0][1] = Cells[NCol, 0]) do

begin

inc(NRow); // Номер текущего ряда

if NRow = RowCount then

begin // Расширяем длину таблицы

RowCount := RowCount + 1;

{Для свойства RowCount нельзя, использовать функцию инкремента inc!} Cells [0, NRow] := IntToStr(NRow);

end;

Cells[NCol, NRow] :=Words[0];

Words.Delete(0) ;

end;

// Переходим к следующему столбцу

if Words.Count=0 then

Break; // Кончаем работу, если слов больше нет

inc(NCol); // Переходим к следующей колонке

ColCount := ColCount+1 // Расширяем таблицу справа

//на 1 колонку

end;

end;

end;

18.2.5. TDrawGrid - произвольная таблица

Компонент TDrawGrid предоставляет программисту мощные возможности создания и обслуживания табличных структур данных. Он обеспечивает двухмерное представление данных, упорядоченных по столбцам и рядам, и избавляет программиста от многих рутинных аспектов, связанных с представлением и обслуживанием таблиц.

Чтобы таблица была работоспособной, в ней как минимум следует определить обработчик события OnDrawCell, которое возникает при необходимости прорисовать ту или иную ячейку. Для прорисовки используется табличное свойство Canvas.

Компонент TDrawGrid является непосредственным родителем строковой таблицы TStringGrid, поэтому передает ей все свои свойства, методы и события за исключением специфичных для строк свойств Cells, Cols, Objects И Rows. Поскольку порядок описания компонентов в этой главе соответствует умалчиваемому порядку их расположения на страницах палитры компонентов, TStringGrid описан раньше своего родителя TDrawGrid, и вы сможете найти информацию о свойствах, методах и событиях последнего в предыдущем параграфе.

В следующем примере компонент TDrawGrid используется для показа текста и картинок одновременно, причем текст можно редактировать. Картинки взяты из каталога images l splash | igcolor (рис. 18.11).

Рис. 18.11. Пример использования таблицы TDrawGrid для отображения картинок и текста

Если вы захотите повторить пример, следует сначала подготовить пустую форму главного окна. Дайте ей имя fmDrawGrid и вставьте в описание класса TfmDrawGrid такое поле:

public

sIBitMap: TSringList;

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

procedure TfmDrawGrid.FormCreate(Sender: TObject);

// Создает и наполняет список sIBitMap

const

FNames: array [0..3] of String = {Имена файлов с картинками} ('athena.bmp', 'construe.bmp', 'earth.bmp', 'technlgy.bmp');

var

k: Integer;

begin

// Создаем список строк:

sIBitMap := TStringList.Create;

// Вставляем в него названия файлов и картинки:

with sIBitMap do for k := 0 to 3 do

begin

Add(FNames[k]) ;

Objects[k] := TBitMap.Create;

(Objects[k] as TBitMap).LoadFromFile(FNames[k])

end;

end;

procedure TfmDrawGrid.FormDestroy(Sender: TObject);

// Уничтожает список строк

begin

sIBitMap.Free

end;

Обратите внимание: в именах файлов нет маршрута поиска, поэтому следует скопировать файлы, перечисленные в константе FNames, из каталога images | splash | 16 color в каталог для размещения ваших учебных программ и в этом же каталоге сохранить форму fmDrawGrid и проект под именем DrawGrid. После этого полезно осуществить прогон программы, чтобы убедиться в доступности файлов. Если при прогоне появится сообщение cannot open file, еще раз убедитесь в том, что файл drawgrid. exe создан в том же каталоге, в котором находятся вмр-файлы.

Поместите на пустую форму компонент TDrawGrid и установите для него следующие значения свойств:

Свойство | Значение | Комментарий
Align | alClient | Таблица занимает всю клиентскую часть окна
ColCount | 2 | В таблице 2 колонки
FixedCols | 0 | Нет фиксированных колонок
FixedRows | 0 | Нет фиксированных рядов
Name | dgDraw | Имя таблицы
Options.goEditing | True | Разрешено редактирование текста
RowCount | 2 | В таблице 2 строки
ScrollBars | sbNone | He надо вставлять полосы прокрутки

Теперь необходимо создать обработчики событий OnGetEditText, OnSetEditText иOnDrawCell. Два первых используются для редактирования текста, а обработчик OnDrawCell обязательно создается для любого компонента TDrawGrid, т. к. без него компонент не сможет заполнить пустые клетки каким-либо изображением или текстом.

procedure TfmDrawGrid.DrawGridlGetEditText(Sender: TObject;

ACol, ARow: Integer;

var Value: String);

// Передает редактору текст из ячейки

begin

Value := slBitMap[2 * ACol + Arow]

end;

procedure TfmDrawGrid.DrawGridlSetEditText(Sentier: TObject;

ACol, ARow: Integer; const Value: String);

// Получает текст из редактора и сохраняет его в sIBitMap

begin

slBitMap[2 * ACol + ARow] := Value

end;

procedure TfmDrawGrid.dgDrawDrawCell(Sender: TObject;

Col, Row: Integer; Rect: TRect; State: TGridDrawState) ;

// Рисует картинку из sIBitMap.Objects и текст из sIBitMap

var

k: Integer;

begin

// Пересчитываем координаты ячейки в индекс списка:

k := 2 * Col + Row;

with dgDraw.Canvas, Rect, sIBitMap do

begin

// Уменьшаем высоту картинки для вывода под ней текста:

Bottom := Bottom - TextHeight('1') - 2;

// Рисуем картинку:

StretchDraw(Rect, (Objects[k] as TBitMap)) ;

// Центрируем текст по горизонтали и выводим его:

TextOut(Left+(Right-Left-TextWidth(sIBitMap[k])) div 2,

Bottom + 1, slBitMap[k])

end

end;

Осталось добиться того, чтобы размеры ячеек таблицы согласовывались с размерами окна программы. Для этого создайте для формы fmDrawGrid такой обработчик события onResize (это событие возникает при каждом изменении размеров окна):

procedure TfmDrawGrid.FormResize(Sender: TObject);

// Изменяет размеры ячеек при изменении размеров окна.

// Запас в один пиксель необходим для предотвращения

// автопрокрутки

begin

with dgDraw do

begin

DefaultColWidth := ClientRect. Right div 2-1;

DefaultRowHeight := ClientRect. Bottom div 2-1

end;

end;

18.2.6. TImage - отображение картинок

Этот компонент служит для размещения на форме одного из трех поддерживаемых Delphi типов изображений: растровой картинки, пиктограммы или метафайла. Любой из этих типов изображения содержится в центральном свойстве компонента - picture. В свойстве canvas содержится канва, с помощью которой при необходимости программа может отредактировать растровое изображение (но не пиктограмму или метафайл!).

Вот как можно создать несложный просмотрщик изображений, окно которого показано на рис. 18.12:

  • Поместите на форму панель TPanei, очистите ее свойство caption и поместите значение alBottom в свойство Alignment - панель займет нижнюю часть формы.
  • Разместите на панели кнопку TButton с надписью загрузить.
  • На любое свободное место формы (над панелью) поместите компонент Timage и установите в его свойство Align значение alClient.
  • На любое место (Timage, TPanei или даже TButton) поместите cтандартный диалог TOpenPictureDialog (страница Dialogs палитры компонентов) - с его помощью пользователь вашей программы сможете разыскать нужный файл с изображением.

Рис. 18.12. Компонент Tfmage воспроизводит изображение

  • Напишите такой обработчик события onciick для кнопки Button1:

procedure TFormI.ButtonlClick(Sender: TObject) ;

begin

if OpenPictureDialogI.Execute then

begin

Imagel.Picture.LoadFromFile
(OpenPictureDialogI.FileName);

Imagel.Stretch := True

end

end;

Замечу, что оператор

Imagel.Stretch := True

масштабирует изображение так, чтобы оно занимало всю рабочую область TImage.

Свойства компонента:

property Center: Boolean; | Указывает, надо ли центрировать изображение в границах компонента. Игнорируется, если Autosize=True или если Stretch =True и изображение не является пиктограммой (ICO)
property Canvas : TCanvas; | Содержит канву для прорисовки изображения
property Transparent: Boolean; | Запрещает/разрешает накладывать собственный фон изображения на фон компонента
property Proportional: Boolean;property Stretch: Boolean; | Разрешает/запрещает изменять размер изображения так, чтобы оно целиком заполнило клиентскую область компонента
property Picture: TPicture; | Разрешает/запрещает пропорционально уменьшать высоту и ширину изображения, если оно не может целиком уместиться в рабочей зоне компонента
property IncrementalDisplay: Boolean; | Разрешает/запрещает показ большого изображения по мере его загрузки Центральное свойство класса. Служит контейнером изображения

С помощью методов класса TPicture (см. п. 16.5.5), к которому относится свойство picture, программист может загрузить изображение из файла или буфера межпрограммного обмена clipboard и сохранить его в файле или буфере.

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

type TProgressStage = (psStarting, psRunning, psEnding) ;

TProgressEvent = procedure (Sender: TObject;

Stage: TProgressStage; PercentDone: Byte;

RedrawNow: Boolean;

const R:

TRect; const Msg: String) of object;

property OnProgress: TProgressEvent

Параметр stage содержит состояние процесса загрузки (psStarting - начало, psRunning - идет загрузка, psEnding - процесс завершен). Параметр PercentDone приблизительно указывает процент выполненной работы. С помощью параметра RedrawNow Windows сообщает, нужно ли сейчас выполнить прорисовку части изображения. Этот параметр имеет смысл, только если свойство incrementaiDis-play компонента содержит True. r - прямоугольник, нуждающийся в прорисовке. Msg содержит одно или более слов, уточняющих состояние процесса. Обычно в обработчике события по сигналу psStarting создается индикатор процесса типа TprogressBar, по сигналам psRunning изменяется позиция индикатора, а в момент psEnding индикатор уничтожается. Следует учесть, что событие onprogress создается только при загрузке некоторых типов изображений, например, подготовленных в формате JPEG (Joint Photographic Expert Group - объединенная группа фотографических экспертов) [Для поддержки JPEG в Delphi включен специальный класс TJPEGImage. Пример использования этого класса, а также события OnProgress вы найдете в программе help\examples\jpeg\jpegproj.dpr каталога размещения Delphi.].

18.2.7. TShape - стандартная фигура

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

type TShapeType = (stRectangle, stSquare, stRoundRect, stRoundSquare, stEllipse, stCircle) ;

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

Помимо стандартных чертежных инструментов Brush и pen (шрифт для компонента не нужен) в компоненте определено свойство shape.' TShapeType, которое и задает вид геометрической фигуры. Изменение этого свойства приводит к немедленной перерисовке изображения.

18.2.8. TBevel - кромка

Компонент класса TBevel носит оформительский характер и предназначен для выделения группы элементов или отделения их друг от друга. Свойство

type TBevelShape = (bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine);

property Shape: TBevelShape;

определяет вид компонента (прямоугольник, рамка, верхняя линия, нижняя линия, левая линия, правая линия).

Свойство

type TBevelStyle = (bsLowered, bsRaised) ;

property Style: TBevelStyle;

задает стиль компонента (вдавленный или выпуклый).

18.2.9. TScrollBox - панель с прокруткой

Компонент класса TScrollBox служит контейнером для размещения других компонентов. Его отличительная особенность - возможность прокрутки и, следовательно, экономия пространства формы при необходимости размещения на ней большого количества управляющих элементов. Замечу, что современные программные продукты относительно редко используют такого рода компоненты, отдавая предпочтение многолистным блокнотам с закладками. Наиболее походящей областью применения компонентов является размещение на них относительно длинных TEdit, TComboBox, TMemo И Т. П.

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

Однако если свойство AutoScroll компонента содержит False, полосы прокрутки не появятся, и компонент будет отсекать “лишние” части своих дочерних компонентов. Еще одним важным свойством компонента является AutoSize: если это свойство имеет значение True, размеры компонента будут автоматически изменяться так, чтобы все части дочерних компонентов были в его рабочей зоне, даже если для этого придется увеличить размеры внешнего контейнера (формы). С помощью свойств HorzScrollBar и VertscrollBar программист может управлять свойствами каждой из полос прокрутки в отдельности (свойства класса TControlScrolBar, к которому они принадлежат, описаны в п. 18.1.1).

Рис. 18.13. Размещение компонентой ни TScrollBox

18.2.10. TcheckListBox - группа независимых переключателей

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

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

Свойства компонента:

property AllowGrayed: Boolean; | Разрешает/запрещает использовать в переключателях третье состояние cbGrayed
property BorderStyle: TBorderStyle; | Определяет тип рамки, очерчивающей компонет: bsNone - нет рамки; bsSingie - рамка толщиной в 1 пиксель
property Canvas: TCanvas; | Это свойство используется для программной прорисовки опций
property Checked[Index: Integer]: Boolean; | Содержит выбор пользователя типа Да/Нет для переключателя с индексом index. Состояния cbUnchecked И cbGrayed отражаются как False
property Columns: Integers; | Определяет количество колонок опций
property Flat: Boolean; | Разрешает/запрещает трехмерную прорисовку окошек выбора
property Header[Index: Integer] : Boolean; | Запрещает прорисовку окошка выбора для опции с индексом index. Таким образом вставляются заголовки отдельных секций
property HeaderBackgroundColor: TColor; | Цвет фона для заголовка (см. свойство Header)
property HeaderColor:TColor; | Цвет текста для заголовка (см. свойство Header)
property IntegralHeight: Boolean; | Если имеет значение True, компонент автоматически изменяет высоту так, чтобы в нем полностью отображались опции, в противном случае последняя опция может не полностью отображаться в окне компонента
property ItemEnabled[Index: Integer]: Boolean; | Разрешает/запрещает выбор опции с индексом Index
property ItemHeight: Integer; | Если свойство Style компонента имеет значение IsOwnerDrawFixed, значение ItemHeight Определяет высоту каждой опции компонента. При других значениях Style это свойство игнорируется
property Itemlndex: Integer; | Содержит индекс выбранной опции (индексация опций начинается с нуля)
property Items: TStrings; | Содержит опции компонента
property MultiSelect: Boolean; | Если содержит True, пользователь может выбирать несколько опций
property SelCount: Integer; | Содержит количество выбранных опций компонента, если свойство MultiSelect содержит True
property Selected[Index: Integer]: Boolean; | Содержит True, если опция с индексом index выбрана
property Sorted: Boolean; | Сортирует по алфавиту надписи на переключателях
property State[Index: Integer] : TCheckBoxState; | Содержит состояние переключателя с индексом Index: cbUnchecked - нет; cbChecked ~ да; cbGrayed - не совсем
type TListBoxStyle = (IbStandard, IbOwnerDraw- Fixed, IbOwnerDrawVariable); | Определяет способ прорисовки опций компонента: IbStandard - опции прорисовывает Windows; ibOwnerDrawFixed - компонент использует программный метод прорисовки опций, каждый из
property Style: TListBox Style; | которых имеет одинаковую высоту ItemHeight; IbOwnerDrawVariable - опции прорисовываются программно и могут иметь разную высоту
property TabWidth: Integer; | Определят ширину табулостопов
property Toplndex: Integer; | Содержит индекс самой первой отображаемой опции

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

property OnClickCheck: TNotifyEvent;

которое наступает при изменении состояния любого переключателя.

18.2.11. TSplitter - компонент для изменения размеров

Компонент TSplitter предназначен для ручного (с помощью мыши) управления размерами контейнеров TPanel1, TGroupBox или подобных им во время прогона программы. Визуально он представляет собой небольшую вертикальную или горизонтальную полосу, располагающуюся между двумя соседними контейнерами или на “свободной” стороне одного их них.

Непременным условием правильной работы компонента является выравнивание контейнера (контейнеров), размерами которого (которых) он управляет, вдоль одной из сторон окна или охватывающего контейнера. Для примера на рис. 18.14 показаны две панели, разделенные компонентом TSpiitter. Нижняя панель имеет Align = alBottom, верхняя в этом случае может иметь выравнивание alBottom или alciient. Между ними помещен TSplitter с выравниванием alBottom.

Свойства компонента:

property Beveled: Boolean;

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

NaturalNumber = 1..High(Integer) ; | Содержит минимальный размер любого из компонентов, которые разделяет TSplitter.
property MinSize: Natural-Number; | Если выравнивание aiLeft или aiRight - минимальная ширина компонента слева и справа от TSplitter, еСЛИ alTop или alBottom -минимальная высота компонента выше или ниже отнего

Для компонента определено событие onMoved, которое вызывается при любом перемещении компонента мышью.

Рис. 18.14. Пример использования компоненты TSplitter (помечен черными квадратиками)

18.2.12. TStaticText - метка для отображения текста

Этот компонент во всем подобен своему “двоюродному брату” TLabel за исключением двух обстоятельств. Во-первых, он порожден от TwinControl и, таким образом, имеет Windows-окно. Это обстоятельство может быть необходимым условием правильного взаимодействия со связанным свойством FocusControl управляющим элементом, если это внешний для Delphi элемент ActiveX. Во-вторых, в его свойстве

type TStaticBorderStyle = (sbsNone, sbsSingle, sbsSunken) ;

property BorderStyle: TStaticBorderStyle;

добавлено значение sbsSunken, которое создает иллюзию “вдавлен-ности” компонента. Все остальные свойства и методы компонента совпадают со свойствами и методами TLabel.

18.2.13. TControlBar - инструментальная панель

Компонент служит удобным контейнером для размещения инструментальных панелей TTооlBаr (см. п. 18.3.16). Он активно использует технологию Drag&Dock для управления положением панелей. На рис. 18.15 показан компонент TControlBar с двумя помещенными на него панелями TTооlваг.

Если вы захотите повторить пример, проделайте следующее.

  • Создайте новое приложение с пустой формой и положите на нее компонент ControlBar.

Рис 18.15. Пример использования компонента TControlBar

  • Установите в его свойство Align значение alTip, в AutoSize- True, в DradKind-dkDragMode. Оставьте в свойстве DragMode значение dmManual.
  • Положите на свободное место формы два компонента ImageList (страница Win 32 палитры компонентов) и наполните кождый из них двумя тремя изображениями.
  • В появившемся окне редактора (рис. 18.16) нажмите кнопку Add и выберите любой вмр-файл из каталога images\Buttons[См. замечание в начале главы.]. Файлы в этом каталоге имеют по два изображения, в то время как компонент ImageList способен хранить только одиночные изображения одинакового размера. Поэтому редактор предложит вам разделить два изображения - согласитесь с ним (кнопка ок). Щелкните по правому изображению в окне редактора (оно предназначено для отображения кнопки в запрещенном состоянии) и нажмите кнопку Delete, чтобы уничтожить его.
  • Повторите п. 4 для загрузки нескольких изображений и затем закройте редактор.
  • Поместите на компонент ControlBar1 компонент TooiBar (страница Win32). Компонент TooiBari, попав на панель соп-troiBari, для версии Delphi 4.0 сразу сожмется по горизонтали (для более поздних версий 4.5, 5 и 6 этого не произойдет). В этом случае установите в его свойство width значение 20, чтобы несколько растянуть его, иначе вы не сможете щелкнуть по нему правой кнопкой мыши.

Рис. 18.16. Окно редактора ImageList

  • Установите в свойство images компонента TooiBar1 значение ImageList1 и поместите в него столько кнопок, сколько изображений вы подготовили в imageList1. Для вставки кнопки щелкните по TooiBar1 правой кнопкой мыши и выберите опцию NewButton.
  • Поместите на свободное место формы еще один компонент ToolBar. Он автоматически прижмется к нижнему краю СоntrolBar1.Измените в его свойстве Align значение alTop на а1None и мышью сместите компонент несколько вниз - на этапе прогона программы этот компонент будет “причаливать” к ControlBar1. Свяжите компонент С ImageList2 (свойство Images) и создайте в нем кнопки (см. п. 7).
  • Для обоих компонентов поместите в свойства DragKind значения dkDock, В DragMode - dmAutomatic И В AutoSize — True.
  • Запустите программу и перетащите нижнюю панель кнопок на ControlBar1.

Свойства компонента:

property AutoDrag: Boolean; | Разрешает/запрещает компонентам ToolBar покидать границы controlBar
property Picture: TPicture; | Содержит изображение, которое, периодически повторяясь, создает фон компонента
property RowSize: TRowSize; | Задает высоту одного ряда инструментальных панелей. Умалчиваемое значение 25 в точности соответствует высоте одной панели и в этом случае между рядами нет зазора
property RowSnap: Boolean; | Если содержит True, “причаливаемая” панель будет выравниваться по высоте ряда

Метод

procedure StickControls; virtual;

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

События компонента:

TBandInfoEvent = procedure(Sender: TObject; Control: TControl; var Insets: TRect; var PreferredSize, RowCount: Integer) of object-property OnBandInfo: TBandInfoEvent; | Возникает при перемещении панели внутри компонента: Sender - компонент; control - панель; insets - зазор между сторонами компонента и панели; Рге-ferredsize - предпочтительная ширина панели; RowCount - количество рядов панелей
TBandMoveEvent = procedure (Sender: TObject; Control: TControl; ARect: TRect) of object/property OnBandMove: TBandMoveEvent; | Возникает при перемещении панели " внутри компонента: Sender - компонент; Control - панель; ARect - прямоугольник для размещения панели
TBandPaintEvent = procedure (Sender: TObject; Control: TCon-tro,l; Canvas: TCanvas; var ARect: TRect; var Options: TBandPaintOp-tions) of object; property OnPaint: TNotifyEvent; | Возникает при необходимости прорисовки панели: sender - компонент; Control - панель; Canvas - канва для прорисовки; ARect - прямоугольник прорисовки; Options - определяет, какая часть панели нуждается в прорисовке Возникает при необходимости прорисовки компонента

18.2.14. TApplicationEvents - обработчик сообщений Windows

Компонент TApplicationEvents впервые введен в версии Delphi 5. В отличие от остальных компонентов страницы Additional он не имеет видимого эквивалента в работающей программе. Его основное и единственное назначение - служить приемником многочисленных сообщений, которые windows посылает работающей программе.

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

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

  • На пустую форму поместите TEdit И TApplicationEvents.
  • Напишите такой обработчик события OnMessage компонента TApplicationEvents:

procedure TForm1.ApplicationEventslMessage(var Msg: tagMSG;

var Handled: Boolean);

begin

Handled := Editi.Focused and (Msg.Message=wm_Char)

and (Msg.wParam>=48) and (Msg.wParam<=57) end;

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

Любая форма может иметь сколько угодно компонентов TAppicationEvents. Одно и то же сообщение поступает последовательно в каждый из них до тех пор, пока это сообщение не будет обработано или пока оно не поступит всем. Порядок получения сообщений обратен порядку размещения компонентов на форме: вначале сообщение получит последний размещенный на форме компонент, затем предпоследний и т. д. Этот порядок можно изменить с помощью метода Activate. Запретить получение остальным компонентам уже обработанного сообщения можно с помощью метода CancelDispatch. если в проекте имеется несколько форм, расположенные на них компоненты TAplicationEvents начинают получать сообщения только после того, как соответствующая форма станет активной. Работа всех компонентов TApplicationEvents будет блокирована, если в объекте Application определен обработчик события onMessage.

В Инспекторе объектов опубликованы только два свойства компонента: Name И Tag. Методы компонента:

procedure Activate; | Делает компонент первым получателем сообщения
procedure CancelDispatch; | Запрещает получение текущего сообщения остальным компонентам
События:
type TActionEvent = procedure (Action: TBasicAction; var Han dled: Boolean) of objects; property OnActionExecute: TActionEvent; | Это событие возникает тогда, когда пользователь активизирует управляющий элемент, приписанный определенному действию, в котором не определен обработчик OnExecute
type TActionEvent = procedure (Action: TBasicAction; var Han dled: Boolean) of object; property OnActionUpdate: TAction Event; | Это событие возникает тогда, когда пользователь активизирует управляющий элемент, приписанный определенному действию, в котором не определен обработчик OnUpdate
property OnActivate: TNotifyEvent; | Возникает при активизации приложения
property OnDeactivate: TNoti- fyEvent; | Возникает при детивизации приложения
type TExceptionEvent = procedure (Sender: TObject; E: Exception) of object; property OnException: TException Event; | Возникает перед умалчиваемой обработкой исключения, для которого нет соответствующего программного обработчика.
type THelpEvent = function (Command: Word; Data: Longint; var CallHelp: Boolean): Boolean of object/ property OnHelp: THelpEvent; | Возникает перед вызовом встроенной справочной службы. Параметру CallHelp следует придать значение True, если программа должна после завершения обработчика вызвать справочную службу
property OnHint: TNotifyEvent; | Возникает перед выдачей оперативной справки для компонента, свойство Hint которого не пустая строка
type TIdleEvent = procedure (Sender: TObject; var Done: Boo lean) of object; | Возникает, когда программа переходит, в состояние ожидания. Если в Done установлено значение False, после выполнения
property Orrldle: TIdleEvent; | других работ Windows вновь вызывает приостановленную программу, в противно случае вызов программы происходит только при поступлении в ее адрес очередного сообщения
type TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object; property OnMessage: TMessageEvent; | Возникает при поступлении любого сообщения от Windows. Msg - сообщение (его тип описан в п. 16.6.3), Handled=True, если обработчик завершил обработку сообщени
property OnMinimize: TNotifyEvent; | Возникает при минимизации главного окна программы
property OnRestore: TNotifyEvent; | Возникает при восстановлении размеров главного окна программы
ShortCutEvent = procedure (var Msg: TWMKey; var Handled: Boo lean) of object; property OnShortCut: TShortCutEvent; | Возникает, когда пользователь нажал клавишу на клавиатуре (перед событиями ОnKeyDown или OnKeyPress). Позволяет программе распознать и интерпретировать нажатие клавиш-акселераторов
type TShowHintEvent = procedure (var HintStr: String; var CanShow: Boolean; var Hintlnfo: THintInfo) of object- property OnShowHint: TShowHintEvent; | Возникает перед появлением оперативной справки Hint. HintStr - строка для справки; Hintlnfo - информация об окне справки. Установите в CanShow значение True, если нужно отобразить справку (остальные параметры к этому времени могут быть изменены)

18.2.15. TValueListEditor - специализированный редактор строк

Этот компонент введен в версии 6 и предназначен для редактирования списков строк вида имя=значение. Такие списки широко используются в реестре Windows и файлах инициализации. Наконец. начиная с версии 5 Delphi по умолчанию в таком формате хранит файлы описания форм dfm. Если на пустую форму положить компонент TValueListEditor, диалог TOpenDialog и кнопку TBitBtn, то после загрузки в редактор файла Example. dfm (для учебной формы, описанной в гл. 5) получим экран, показанный на рис. 18.17. Обработчик нажатия кнопки имеет такой вид:

procedure TFormI.BitBtnIClick(Sender: TObject);

begin

if OpenDialogI.Execute then

ValueListEditorl.Strings.LoadFromFile(

OpenDialogI.FileName)

end;

Непосредственным предком компонента является класс TCustomDrawGrid, поэтому многие его свойства, методы и события совпадают с описанными в п. 18.2.4 свойствами, методами и событиями компонента TStringGrid.

Специфичные свойства компонента:

property Cells[ACol, ARow: Integer]: String; | Открывает доступ к содержимому ячейки, лежащей на пересечении столбца ACol со строкой arow (нумерация столбцов и строк начинается с нуля)
property ColCount: Integer; | Содержит количество столбцов
type TDisplayOption = (doColumnTitles, doAutoColResize, doKeyColFixed) ; TDisplayOptions = set of TDisplayOption; property DisplayOptions: TDisplayOptions; | Свойство может включать следующие значения: doColumnTitles - первая строка содержит заголовки столбцов, определенные свойством TitleCaption, И не скроллируется; doAutoColResize - предписывает автоматически изменять ширину столбцов при изменении размеров компонента; doKeyColFixed - если указано, ширина первого столбца, содержащего имена, не меняется при изменении размеров

Рис. 18.17. Демонстрация компонента TValueListEditor

property DropDownRows: Integer; | Указывает максимальное количество строк в списке возможных значении, показ которых не сопровождается появлением полосы прокрутки
property ItemProps[const KeyOrIndex: Variant]: TItemProp; | Сложное свойство, управляющее различными аспектами отображения значений (см. пояснения ниже)
type TKeyOption = (keyEdit, keyAdd, keyDelete, keyUnique) ; TKeyOptions = set of TDisplayOption; property KeyOptions: TKeyOptions; | Управляет возможным изменением списка: keyEdit - пользователь может изменять имя параметра (в первом столбце); keyAdd - пользователь может добавлять новую пару имя-значение нажатием клавиши Insert (требует включения keyEdit); keyDelete -пользователь может уничтожить выделен ную пару нажатием клавиши Delete; keyUnique - названия вводимых пользователем имен должны быть уникальными
property Keys[Index: Integer]:String; | Открывает доступ к названиям значений по их индексам
property RowCount: Integer; | Содержит количество строк
property Strings: TStrings; | Содержит строки списка
property TitleCaptions: TStrings; | Содержит заголовки столбцов
property Values[const Key:String]: String; | Открывает доступ к значениям по их индексам

Свойство itemprops класса Titemprop имеет собственные свойства:

property EditMask: String; | Содержит маску для формирования значения. Правила создания маски описаны в п. 18.2.3
type TEditStyle = (esSimple, esEllipsis, esPickList); property EditStyle: TEditStyle; | Определяет правила редактирования значения: esSimple - значение редактируется в обычном однострочном редакторе; esEllipsis - в строку значения вставляется кнопка для вызова диалогового окна редактора значения (возбуждается событие OEditButtonclick); esPickList - в строку значения вставляется кнопка для раскрытия списка возможных значений (возбуждается событие OnGetPickList)
property MaxLength: Integer; | Определяет максимальную длину (в символах) значения. Если 0, нет ограничений на длину
property PickList: TStrings; | Содержит выпадающий список возможных значений
property Readonly: Boolean; | Разрешает/запрещает редактирование значения

Свойство PickList не предназначено для программного доступа. Чтобы создать выпадающий список возможных значений, нужно свойству ItemProp.EditStyle присвоить значение esPickList и написать обработчик события onGetpickList. Пусть, например, первое значение должно иметь выпадающий список. Тогда необходим такой код:

procedure TFormI.FormActivate(Sender: TObject);

begin

ValueListEditorl.ItemProps[1].EditStyle := esPickList;

end;

procedure TFormI.ValueListEditorlGetPickList(Sender: TObject;

const KeyName: String; Values: TStrings);

begin

// Содержимое списка доступно через параметр Values:

Values.Add('1') ;

Values.Add('2') ;

Values.Add ('3') ;

Values.Add('4') ;

end;

Следующие свойства компонента аналогичны одноименным

Свойствам компонента TStringGrid (cm. П. 18.2.4): BorderStyle, Col, ColWidths, DefaultColWidth, DefaultDrawing, DefaultRowHeight, EditorMode, FixedColor, FixedCols, GridHeight, GridLineWidth, GridWidth, LeftCol, Options, Row, RowHeights, ScrollBars, Selection, TabStops, TopRow, VisibleColCount,VisibleRowCount. function InsertRow(const KeyName,. "."alue: String; Append: Boolean) : Integer-procedure Refresh; function RestoreCurrentRow: Boolean;

Методы CellRect, MouseToCell И MouseCoord аналогичны одноименным методам компонента TStringGrid (см. п. 18.2.4). События компонента:

property OnEditButtonClick: TNo- tifyEvent; | Возникает при щелчке по кнопке раскрытия диалогового окна редактирования значения. Свойство ItemProps .EditStyle должно содержать значение esEllipse
type TGetPickListEvent = proce dure (Sender: TObject; const KeyName: String; Values: TStrings) of object; property OnGetPickList: TGetPick ListEvent; | Возникает при щелчке по кнопке раскрытия списка возможных значений. Параметр KeyName содержит имя строки, в которой произведен щелчок. Обработчик должен заполнить возможными значениями список Values. Свойство ItemProps. EditStyle должно содержать значение esPickList
property OnStringsChange: TNotifyEvent; | Возникает сразу после изменения имени или значения текущей строки
property OnStringsChanging: TNotifyEvent; | Возникает непосредственно перед изменением имени или значения текущей строки
type TOnValidateEvent = procedure (Sender: TObjectACol, ARow: Longint; const KeyName, KeyValue:String) of object; property OnValidate: TOnValidateEvent; | Возникает перед тем как фокус ввода покинет ячейку ACol, ARow. Обработчик может проверить новые значения KeyName и KeyValue и возбудить исключение, если исправления в строке были ошибочными

Следующие события аналогичны одноименным событиям компонента TStringGrid: OnDrawCell, OnGetEditMask, OnGetEditText OnRowMoved, OnSelectCell, OnSetEditText, OnTopLeftChanged.

18.2.16. TLabelEdit - однострочный редактор с меткой

Представляет собой удобную комбинацию однострочного редактора с меткой. Надпись в метке определяет свойство EditLabei. Свойство

type TLabelPosition = (IpAbove, ipBelow, IpLeft, IpRight);

property LabelPosition: TLabelPosition;

определяет положение метки относительно редактора, а

property LabelSpacing: Integer;

расстояние от метки до редактора (в пикселях). Впервые введен в версии 6.

18.2.17. TColorBox - список выбора цвета

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

TColorGrid (страница Samples) и TColorDialog (страница Dialogs) состав отображаемых им цветов может меняться.

Многие свойства, методы и события компонента совпадают с одноименными свойствами, методами и событиями его ближайшего родственника - компонента TCоmbоВох (см. п. 18.1.10).

Специфичные свойства компонента:

property ColorNames[Index: Inte ger]: Strings; | Возвращает имя цвета по его индексу в списке выбора
property Colors[Index: Integer]: TColor; | Содержит список цветов
property DefaultColorColor: TColor; | Указывает умалчиваемый цвет. Свойство Style должно включать флаги cbSystem Colors И cbIncludeDefault
property NoneColorColor: TColor; | Указывает цвет “нет цвета”. Свойство Style должно включать флаги cbSystem Colors и cbIncludeNone
property Selected: TColor; | Указывает текущий выбранный цвет
type TColorBoxStyles = (cbStandardColors, cbExtendedColors, cbSystemColors, cblnclude None, cbIncludeDefault, cbCustomColor, cbPrettyNames); TColorBoxStyle = set of TColor BoxStyles; property Style: TColorBoxStyle | Определяет отображаемые компонентом цвета: cbStandardColors - список содержит 16 основных цветов; cbExtendedColors -список содержит дополнительные цвета, определенные в модуле Graghics; cbSystemColors - в списке отображаются цвета cIMoneyGreen,cISkyBlue,cICream и cIMedGray; cbIncludeNone -список содержит опцию cINone;cbIncludeDefault -список содержит опцию cIDefault; cbCus tomcolor - первая опция списка открывает стандартный диалог TColorDialog; cbPrettyNames - имена цветов отображаются без префикса cl

18.2.18. TChart - построитель графиков

Этот компонент предназначен для графического представления числовых данных. На рис. 18.18 показан пример использования компонента.

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

Общая схема его использования такова.

Вначале компонент помещается на форму. После щелчка по нему правой кнопкой мыши вызывается его вспомогательное меню, которое в числе прочих содержит опцию Edit chart - с помощью этой опции вызывается многолистное окно редактора компонента. Закладка Series этого окна открывает доступ к так называемым сериям - объектам класса TChartSeries, которые, собственно, и будут отображать наборы чисел в графическом виде. Чтобы отобразить данные, нужно создать как минимум одну серию - для этого следует в окне редактора нажать кнопку Add и выбрать подходящий тип графика (рис. 18.19).

После закрытия редактора компонент будет содержать примерный вид графика. Однако его реальный вид зависит от фактических данных, которые создаются в работающей программе и добавляются к серии с помощью методов AddX, AddY или AddXY объекта TChartSeries (получить доступ к нужной серии можно с помощью индексированного свойстваSeriesList - первая созданная серия имеет индекс 0, вторая 1 и т. д.). Например, такой обработчик события OnActivate формы создал график, показанный на рис. 18.18:

procedure TForm1.FormActivate(Sender: TObject);

var

k: Integer;

begin

for k := 0 to 20 do

Chartl.SeriesList[0].AddXY(k,sin(k*pi/10),' ',clRed) ;

end;

Рис. 18.18. Пример использования компонента Tchart

Рис. 18.19. Возможные виды графиков компонента TChart

Компонент впервые появился в версии 3.

18.2.19. TActionManager - менеджер действий

Вместе с тремя другими компонентами страницы Additional -

TActionMainMenuBar, TActionToolBar И TCustomDIg (все перечисленные компоненты впервые введены в версии 6) позволяет создавать настраиваемый пользовательский интерфейс.

Таким интерфейсом обладает, например, среда Delphi: вы можете добавить/удалить любую инструментальную кнопку, запретить/разрешить выбор кнопок и опций меню и т. п.

Компонент TActionManager централизует однотипные действия, производимые схожими в функциональном отношении элементами интерфейса. Каждое такое действие компонент сохраняет в дисковом файле, что позволяет программе “запоминать” последние настройки, произведенные пользователем. Таким образом, компонент значительно расширяет функциональность компонента TActionList (см. п. 18.1.16) и по существу делает его ненужным.

Компонент оперирует категориями однотипных действий (Edit, File, Format и т. п.) и может размещать категории в контейнере тасtionMainMenuBar (в виде опций меню), а входящие в них действия - в контейнере TActionToolBar (в виде инструментальных кнопок). В

Delphi предусмотрены 12 стандартных категорий, многие действия которых уже заранее предопределены. Например, в категорию File входит предопределенное действие TFileExit. Выбор этого действия в меню или инструментальной кнопкой приводит к завершению работы программы.

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

  • Поместите на пустую форму компоненты TActionManager, ТАсtionMainMenuBar, TActionToolBar И TCustomizeDlg. по умолча-нию контейнеры TActionMainMenuBar И TActionToolBar займут верхнюю часть формы, положение двух других компонентов не имеет значения.
  • Дважды щелкните по компоненту или вызовите его локальное меню и выберите опцию customize - на экране появится окно редактора компонента (рис. 18.20).

Рис. 18.20. Редактор компонента TActionManager

  • Щелкните по окну правой кнопкой и выберите New Standard Action или просто нажмите Ctrl+Ins - на экране появится дополнительное окно с перечнем всех определенных в Delphi стандартных классов действий (рис. 18.21).
  • С помощью полосы скроллирования сместите список вверх и выберите в нем все классы, связанные с категорией File (для выбора нескольких классов одновременно щелкайте по каждому, удерживая нажатой клавишу Ctrl).
  • “Схватите” мышью категорию File и перетащите ее на полосу TActionMainMenuBar. Раскройте категорию и перетащите на полосу TActionToolBar опцию Exit.
  • Поместите в свойство/событие ActionManager компонента TCustomizeDIg ссылку на компонент ActionManagerl.
  • Напишите такой обработчик onciick для полосы TActionToolBar:

procedure TFormI.ActionToolBarlClick(Sender: TObject);

begin

CustomizeDlgl.Show

end;

Рис. 18.21. Окно со списком стандартных действий

Если вы запустите программу, то увидите пустое окно с меню File и инструментальной кнопкой Exit, причем опции меню (кроме недоступной опции Run) и инструментальная кнопка выполняют ожидаемые от них действия. Щелчок по инструментальной панели активизирует уже знакомое нам (рис. 18.20) окно редактора Atcion-Manageri, и вы можете изменять состав меню и инструментальной панели.

Свойства компонента:

property ActionBars: TAction Bars; | Содержит коллекцию всех инструменталь ных полос, определенных в приложении. Коллекция автоматически пополняется, когда на очередную полосу впервые кладется элемент меню (категория) или кнопка (действие)
property ActionCount: Integer; | Содержит общее количество определенных в программе действий
property Actions[Index: Integer]: TContainedAction; | Открывает индексированный доступ к конкретному действию
property FileName: TFileName; | Имя файла, в котором компонент сохраняет текущую конфигурацию действий
property LinkedActionLists: TActionListCollection; | Содержит коллекцию всех связанных с компонентом действий
property Images: TCustomImageList; | Ссылка на связанное с компонентом хранилище изображений
property PrioritySchedule: TStringList; | Содержит для каждого действия номера сессий (запусков программы), когда связанная с действием кнопка или опция меню меняла свою видимость. Если какой-то элемент интерфейса должен постоянно присутствовать на экране, его строка PrioritySchedule должна хранить (-1)
type TActionListState = (asNormal, asSuspended, asSuspendedEnabled) ; | Включает или отключает действия: asNormal - все действия включены; asSuspended - все действия выключены, их свойства
property State: TActionListState; | Enabled не изменяются; asSuspendedE nabled - все действия выключены, их свойства Enabled получают значения True

Если свойство FileName не определено, компонент не сможет запоминать изменения, сделанные пользователем в интерфейсе программы, и представит элементы интерфейса так, как они были определены при создании программы. Для файла нет умалчиваемого расширения, но сама среда Delphi запоминает сделанные вами изменения в файле BIN\DELPHI32 . DMT.

Методы компонента:

procedure LoadFromFile(const FileName: String); | Читает файл, указанный в параметре File-Name и загружает информацию, необходимую для восстановления полос действий
procedure LoadFromStream(Stream: TStream); | Загружает информацию, необходимую для восстановления полос действий, из потока Stream Восстанавливает вид полосы таким, как сохраняет ее свойство DefaultActionBar Уничтожает файл, указанный в свойстве FileName
procedure ResetActionBar(Index: Integer) ; procedure ResetUseageData; | Сохраняет текущие настройки интерфейса в файле FileName
procedure SaveToFile(const FileName: String) ; procedure SaveToStream(Stream: TStream); | Сохраняет текущие настройки интерфейса в потоке Stream
function ExecuteAction(Action: TBasicAction): Boolean; override; | Создает событие OnExecute для выполнения действия Action

18.2.20. TActionMainMenuBar - полоса меню для действий

Компонент служит настраиваемым контейнером для размещения связанных с действиями опций меню. Опции-заголовки размещаются “перетаскиванием” из окна менеджера действий (рис. 18.20) категорий.

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

property ActionClient: TActionClient;

Свойство visible класса TActionClient управляет видимостью полосы. Для компонента TActionMainMenuBar (в отличае от TActionToolBar) это свойство не может иметь значение False (независимо от количества компонентов на форме). Иными словами, программа не может “спрятать” полосы главного меню.

18.2.21. TActionTollBar - полоса кнопок для действий

Компонент служит настраиваемым контейнером для связанных с действиями инструментальных кнопок. Кнопки реализуются техникой “перетаскивания” действий из окна менеджера действий (рис. 18.20).

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

18.2.22. TCustomizeDIg - диалог настройки интерфейса

Этот компонент служит для активизации окна менеджера действий (рис. 18.20). Для этого у него имеется единственный метод

procedure Show;

Свойство

property ActionManager: TCustomActionManager;

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

property StayOnTop: Boolean;

установить True, окно менеджера действий всегда будет располагаться поверх других окон.

Для вызова метода show проще всего использовать дополнительное меню контейнера опций или кнопок. Например:

procedure TFormI.NIClick(Sender: TObject);

begin

CustomizeDlgl.Show

end;

18.3. КОМПОНЕНТЫ СТРАНИЦЫ WIN32

18.3.1. TTabControl - набор закладок

Компонент TTabControl представляет собой контейнер с закладками. Свойство Tabs определяет названия и количество закладок. Событие onchange. возникает при выборе новой закладки и позволяет управлять содержимым окна компонента. Для примера на рис. 18.22 показано использование компонента для управления календарем (с помощью TTabControl созданы закладки с названиями месяцев).

Свойства компонента:

property DisplayRect: TRect; | Определяет рабочую зону компонента, предназначенную для размещения других компонентов. Клиентская часть компонента содержит зону закладок и рабочую зону
property HotTrack: Boolean; | Если содержит True, название закладки автоматически выделяется цветом при перемещении над ней указателя мыши
property Images: TCustomImageList; | Определяет объект - хранилище изображений, которые будут прорисовываться слева от текста
property Multi-Line: Boolean; | Разрешает расположение закладок в несколько рядов. Если содержит False и закладки не умещаются в границах компонента, в зону закладок автоматически вставляются кнопки прокрутки
property OwnerDraw: Boolean; | Разрешает программную прорисовку закладок
property ScrollOpposite: Boolean; | Разрешает/запрещает перемещение неактивных рядов закладок на противоположную сторону компонента. Учитывается, если количество рядов больше 2
type TTabStyle = (tsTabs, tsButtons, tsFlatButtons) ; property Style: TTabStyle; | Определяет стиль закладок: tsTabs - обычные надписи; tsButtons -трехмерные кнопки; tsFlat Buttons - плоские кнопки
property TabHeight: Smallint; | Определяет высоту каждой закладки в пикселях. Если содержит 0, высота закладок выбирается автоматически в зависимости от выбранного шрифта
property Tablndex: Integer; | Определяет индекс выбранной закладки или содержит -1, если ни одна закладка не выбрана. Индексация начинается с 0
TTabPosition = (tpTop, tpBottom) ; property TabPosition: TTab Position; | Определяет положение зоны закладок относительно рабочей зоны компонента (tpTop - вверху, tpBottom - ВНИЗУ)
property Tabs: TStrings; | Определяет надписи на закладках и их количество. Чтобы добавить или удалить закладку, нужно добавить ее надпись к списку Tabs или удалить надпись из списка
property TabWidth: Smallint; | Определяет ширину каждой закладки в пикселях. Если содержит 0, ширина каждой закладки выбирается индивидуально в зависимости от длины ее надписи

Рис. 18.22. Управление календарем с помощью TTabControl

Помимо события OnChange, возникающего после выбора новой закладки, для компонента определено также событие OnChanging, которое возникает перед сменой закладки:

type TTabChangingEvent =

procedure (Sender: TObject;

var AllowChange: Boolean) of object;

property OnChanging: TTabChangingEvent;

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

18.3.2. TPageControl - набор страниц с закладками

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

Чтобы на этапе конструирования добавить новую панель или выбрать ранее вставленную, щелкните по компоненту правой кнопкой мыши и выберите New page (новая панель), Next page (следующая панель) или previous page (предыдущая панель). Смена панелей идет циклически, т. е. после показа последней показывается первая и наоборот.

Помимо свойств HotTrack, MultiLine, ScrollOpposite, TabHeight, TabPosition и TabWidth, которые аналогичны одноименным свойствам TTabcontrol, компонент имеет следующие специфичные свойства:

property ActivePage: TTab-Sheet; | Содержит ссылку на активную панель. Установка нового значения ActivePage размещает соответствующую панель поверх остальных. Для выбора новой панели следует использовать методы SelectNextPage и FindNextPage
property ActivePageIndex: Integer; | Содержит индекс активной панели
property PageCount: Integers; | Содержит количество панелей (только для чтения)
property Pages[Index: Integer] : TTabSheet; | Возвращает ссылку на панель по ее индексу (только для чтения)

Методы компонента:

function FindNextPage (CurPage: TTabSheet; GoFor-ward, CheckTabVisible: Boolean) : TTabSheet; | Ищет следующую панель: CurPage - текущая панель; GoForward - содержит True, если поиск идет от первой панели к последней; CheckTabVisible -содержит True, если из поиска исключаются панели с признаком Tabvisible=False. Возвращает ссылку на найденную панель. Если CurPage не принадлежит компоненту, возвращает ссылку на первую или последнюю панель в зависимости от параметра GoForward
procedure SelectNextPage (GoForward: Boolean); | Делает активной следующую панель. Если GoFor-ward=True, активизируется следующая панели, в противном случае - предыдущая

18.3.3. TImageList - хранилище изображений

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

Компонент не имеет собственной канвы и поэтому не может самостоятельно отображать хранимые в нем картинки, которые могут быть растрами (bmp) или пиктограммами (ico). Его метод Draw получает канву от другого компонента и рисует изображение в клиентской области этого компонента. В компоненте есть множество специальных свойств и методов, упрощающих обслуживание набора картинок. Для получения дополнительной информации обратитесь к встроенной справочной службе. Пример использования компонента см. в п. 18.2.13.

18.3.4. TRichEdit - ввод и отображение RTF-текста

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

Рис. 18.23. Окно демонстрационной программы DEMOS | RICHEDIT \ RICHED1T.EXE

На рис. 18.23 показан пример использования возможностей компонента. Полный текст программы, с помощью которой создан рисунок, приводится в папке demos | RICHEDIT | RICHEDIT.DPR, котораянаходится в каталоге размещения Delphi.

Компонент использует вспомогательные объекты класса TTextAt-tributes для хранения атрибутов шрифта. Эти атрибуты распространяются на весь текст через свойство редактора DefAttributes или на выделенную часть текста - через его свойство SeiAttributes.

Помимо обычных шрифтовых свойств CharSet, Color, Height, Name, Pitch, Size И Style (см. класс TFont) объект TTextAttributes содержит также свойства consistentAttributes и protected. Первое доступно только для чтения и содержит набор текстовых характеристик, общих как для всего текста, так и для его выделенной части. Свойство protected защищает весь текст или его части от редактирования. Попытка изменить текст, имеющий атрибут protected, вызывает обработчик события OnProtectChange, который может разрешить или запретить изменения. По умолчанию изменения запрещены.

Для каждого текстового абзаца создается объект класса трага-Attributes, в котором сохраняются атрибуты абзаца. Эти атрибуты доступны через следующие свойства класса TparaAttributes:

property Alignment: TAlignment; | Определяет горизонтальное выравнивание текста абзаца относительно границ компонента
property Firstlndent: Longint; | Указывает отступ текста абзаца в пикселях от предыдущего абзаца
property Leftlndent: Longint; | Указывает отступ текста абзаца в пикселях от левого края компонента
TNumberingStyle = (nsNone, nsBullet) ; property Numbering: TNum beringStyle; | Указывает, надо ли вставлять слева от абзаца символы списка. Если содержит nsBullet, символы списка вставляются
property Rightlndent: Longint; | Указывает отступ текста абзаца в пикселях от правого края компонента
property Tab[Index: Byte] : Longing; | Для табулостопа с индексом index содержит его позицию в пикселях от левого края компонента
property TabCount: Irteger; | Определяет количество табулостопов в строке абзаца

Свойства класса TRichEdit

property DefAttributes: TTextAttributes; | Определяет шрифтовые атрибуты всего текста
TConversionClass = class of TConversion; property DefaultConverter: TConversionClass; | Свойство указывает класс конвертора, использующегося для преобразования текстовых форматов причтении или записи текста. По умолчанию нет преобразования
property HideScroliBars: Boolean; | Определяет, будет ли редактор автоматически вставлять полосы прокрутки, если текст отсекается границами компонента. Игнорируется, если scrollBars содержит ssNone
property HideSelection: Boolean; | Указывает, будет ли убираться выделение текста, если компонент потеряет фокус ввода
property Lines: TStrings; | Содержит набор строк текста. С помощью его методов LoadFromFile и SaveToFile компонент может читать текст из файла или записывать в него текст
property PageRect: TRect; | Указывает размеры страницы при печати на принтере
property Paragraph: TParaAttributes; | Содержит атрибуты текущего абзаца, т. е. абзаца с выделением или с текстовым курсором. Программа не может изменить свойство paragraph, но может изменить свойства связанного с ним абзаца
property PlainText: Boolean; | Запрещает/разрешает записывать в файл или читать из него служебную информацию формата RTF (True -запрещает)
property SelAttributes: TTextAttributes; | Определяет шрифтовые атрибуты выделенного текста
property SelLength: Integer; | Задает длину в символах выделенной части текста
property SelStart: Integer; | Определяет номер первого символа выделенной части текста от начала текста (нумерация символов начинается с 0). Если нет выделения, указывает символ, перед которым располагается текстовый курсор
property SelText: String; | Содержит выделенный текст. Установка нового значения SelText заменяет выделенный текст на новый, а если нет выделения - вставляет его в позицию курсора

Методы компонента:

procedure Clear; TSearchType = (stWholeWord, stMatchCase) ; TSearchTypes = set of TSear chType ; function FindText(const SearchStr: String; StartPos, Length: Integer; Options: TSear chTypes) :Integer; | Удаляет весь текст Ищет в тексте строку SearchStr и возвращает индекс первого ее символа при удачном поиске: StartPos - начало поиска: Length-длина строки, options указывает, будет ли поиск идти по целым словам и надо ли учитывать высоту букв
function GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer; | Копирует не более BufSize символов выделенного текста в буфер Buffer и возвращает количество скопированных символов
procedure Print(const Caption: String) ; | Форматирует текст по границам листа бумаги и печатает его на умалчиваемом принтере, caption определяет заголовок печати
TConversionClass = class of TConversion; class procedure RegisterConver- sionFormat(const AExtension: String; AConversionClass: TConversionClass) ; | Устанавливает соответствие между расширением файла AExtension и конвертором текста

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

TRichEditProtectChange = procedure(Sender: TObject; Start Pos, EndPos: Integer; var AllowChange: Boolean) of object property OnProtectChange: TRichE ditProtectChange; | Возникает при попытке изменить текст, имеющий атрибут Protected: StartPos, EndPos - соответственно начальная и конечная позиции изменяемого текста. В параметре AllowChange обработчик возвращает True, если можно изменять текст
TRichE'ditResizeEvent = proce dure (Sender: TObject; Rect: TRect) of object property OnResizeRequest: TRichEditResizeEvent; | Событие связано с перерисовкой текста из-за изменения размеров шрифта. Rect содержит прямоугольник, который будет перерисован
TRichEditSaveClipboard = procedure(Sender: TObject; NumObjects, NumChars: Integer; var SaveClipboard: Boolean) of object; property OnSaveClipboard: TRichEditSaveClipboard; | Возникает в момент уничтожения компонента и извещает его о том, что в clipboard осталась информация, которая помещена им. Если обработчик вернет False в параметре Save clipboard, буфер обмена будет очищен
property OnSelectionChange: TNotifyEvent; | Возникает при изменении выделенного текста

18.3.5. TTrackBar-регулятор величины

Компонент класса TTrackBar предназначен для визуального управления числовой величиной. Он во многом схож со стандартным элементом TScroll-Ваг и отличается от него в основном оформлением.

Свойства компонента:

property Frequency: Integer; | Определяет частоту нанесения меток: 1 - каждое значение диапазона изменения имеет метку; 2 - каждое 2-е значение и т. д.
property LineSize: Integer; | Определяет смещение ползунка при нажатии клавиш курсора или при единичном перемещении мышью
property Max: Integer; | Определяет максимальное значение диапазона изменения
property Min: Integer; | Определяет минимальное значение диапазона измене ния
property Orientation: TTrackBarOrientation; | Определяет ориентацию компонента: trHorizontal -горизонтальная; trVertical -вертикальная
property PageSize: Integer; | Определяет смещение ползунка при нажатии клавиш Page Up, Page Down или при щелчке мышью на концах шкалы
property Position: Integer; | Определяет текущее положение ползунка
property SelEnd: Integer- | Задает конечную позицию выделения
property SelStart: Inte | Задает начальную позицию выделения
ger-property SliderVisible: Boolean-property ThumbLength: Integer; TTickMark = (tmBottomRight, tmTopLeft, tmBoth) ; property TickMarks: TTickMark; TTickStyle = (tsNone, tsAuto, tsManual) ; property TickStyle: TTickStyle; | Определяет, будет ли видимым указатель компонента Определяет длину указателя Определяет способ нанесения меток: tmBottomRight -внизу или справа; tmTopLeft - вверху или слева; tmBottom - по обеим сторонам Определяет стиль нанесения меток: tsNone - нет меток; tsAuto - метки наносятся с частотой Frequence; tsManual - наносятся начальная и конечная метки, остальные наносит программа с помощью метода setTick

С помощью метода

procedure SetTick(Value: Integer);

устанавливается метка в позицию, определяемую значением value.

18.3.6. TProgressBar - индикатор прогресса

Компонент TProgressBar предназначен для отображения хода выполнения длительного по времени процесса. Он имеет много общего с TSrollBar и TTrackBar, но в отличие от них у него нет ползунка, т. е. с его помощью можно только отображать числовую величину, но пользователь не может ее изменять.

Свойства компонента:

property Max: Integer; property Min: Integer-property Position: Integers- | Определяет максимальное значение диапазона изменения свойства position Определяет минимальное значение диапазона изменения свойства position Содержит текущее значение отображаемой величины
type TProgressBarOrienta-tion == (pbHorizontal, pbVertical) ; property Orientation: TProgressBarOrientation; | Определяет ориентацию компонента: pbHorizontal - слева направо; pbVertical - снизу вверх
property Smooth: Boolean; | Определяет, будет ли полоса сплошной (True) или сегментированной
property Step: Integer; | Шаг наращивания свойства position методом Stepit

Методы компонента:

procedure StepBy(Delta: Integer); procedure Stepit;

Наращивает значение свойства Position на величину Delta Наращивает значение свойства Position на величину, определяемую свойством step

18.3.7. TUpDown - спаренная кнопка

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

Свойства компонента:

TUDAlignButton = (udLeft, udRight) ; property AlignButton: TUDAlignButton; | Определяет положение компонента относительно компаньона: udLeft - TUpDown располагается слева от него; udRight - справа от него
property Associate: TWinuontrol; | Определяет связанный компонент. TupDown автоматически располагается с нужной стороны компаньона и выравнивает свою высоту с его высотой
property Increment: Smallint; | Определяет шаг наращивания/уменьшения регулируемой величины
property Max: Smallint; | Определяет максимальное значение диапазона изменения регулируемой величины
property Min: Smallint; | Определяет минимальное значение диапазона изменения регулируемой величины
TUDOrientation = (udHorizontal, udVertical) ; property Orientation: TUDOrientation; | Определяет ориентацию компонента: udHorizontal - по горизонтали; udVertical - по вертикали
property Positior-.: Sallint; | Содержит текущее значение регулируемой величины
property Thousands: Boolean; | Если содержит True, в отображение числовой величины в компаньоне вставляются разделители тысяч
property Wrap: Boolean; | Запрещает/разрешает выход position из диапазона Max...Min (True - запрещает)

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

type TUDChangingEvent = procedure (Sender: TObject;

var AllowChange: Boolean) of object;

property OnChar.ging: TUDChanginEvent;

И

type TUDBtnType = (btNext, btPrev) ;

type TUDClickEvent = procedure (Sender: TObject; Button:

TUDBtnType) ;

property OnClick: TUDClickEvent;

Первое возникает при любом изменении регулируемой величины. Обработчик события в параметре AllowChange сообщает, может ли величина измениться. Второе - при щелчке по кнопкам элемента. Параметр Button определяет нажатую кнопку: btprev - вниз или влево; btNext - вверх или вправо.

18.3.8. THotKey - ввод акселератора

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

как специализированный однострочный редактор, который распознает нажатие сдвиговых клавиш Shift, Ctrl и Alt и преобразует их в текст <<shift+x”, “ctrl+x” или “Alt+x”, где х - символ совместно нажатой клавиши. Допускается одновременное нажатие двух или

Трех сдвиговых клавиш: Alt+Shift+X, Ctrl+Shift+Alt+X И Т. П.

Свойства компонента:

property AutoSize: Boolean; | Разрешает/запрещает автоматическое изме нение размеров компонента при изменении шрифта, которым отображается текст в нем
type TShortCut = Low(Word)..High(Word) ; property HotKey: TShortCut; | Содержит код клавиши быстрого выбора
type THKInvalidKey = (hcNone, hcShift, hcCtrl, hcAlt, hcCtrlAlt, hcShiftCtrlAlt) ; THKInvalidKeys = set of THKIn validKey; property InvalidKeys: THKInvalid Keys; | Указывает запрещенные комбинации сдвиговых клавиш: hcNone - запрещено отсутствие сдвиговых клавиш; hcShift - запрещена клавиша Shift; hcCtrlAlt -запрещена комбинация Ctrl + Alt и т. д. По умолчанию содержит [hcNone, hcShift]. При вводе запрещенной комбинации она заменяется комбинацией, содержащейся в свойстве Modifier
type THKModifier = (hkShift, hkCtrl, hkAlt, hkExt); THKModifiers = set of THKModi fier; property Modifiers: THKModifiers; | Содержит комбинацию сдвиговых клавиш, которая заменяет собой запрещенную комбинацию. По умолчанию содержит hkAlt, и поэтому нажатие, например, одиночной алфавитно-цифровой клавиши заменяется ее комбинацией с клавишей Alt

18.3.9. TAnimate - отображение анимации

Компонент TAnimate представляет собой проигрыватель видеоклипов формата AVI (Audio Video Interleaved - чередование аудио и видео). Компонент воспроизводит видеочасть файла AVI и игнорирует его звуковое сопровождение. Он способен показывать лишь несжатое изображение или изображение, сжатое по методу RLE (Run-Length Encoding). Изображение воспроизводится в отдельном потоке команд, что освобождает ресурсы программы для выполнения необходимой работы на фоне демонстрации клипа.

Если вы захотите посмотреть компонент в действии (рис. 18.24), выполните следующее:

Рис. 18.24. Демонстрация компонента TAnimate

  • Скопируйте файл demos | cool: cool. avi в ваш рабочий каталог (каталог demos распологался в каталоге размещения Delphi).
  • На пустую форму положите компоненты Animate, ProgressBar, Timer (страница System) и кнопку BitBtn. Таймер необходим для имитации достаточно длительного по времени процесса, который будет отображаться в ProgressBar на фоне вращающейся надписи “COOL” в компоненте Animate.
  • Установите в свойство Enabled компонента Timer1 значение False, а в его же свойство interval - значение 10. Поместите в свойство Visible компонента ProgressBarl значение False и сохраните форму в том же каталоге, куда вы скопировали файл COOL.AVI.
  • Создайте такой обработчик события OnClick кнопки BitBtn1:

procedure TForml.BitBtnlClick(Sender: TObject);

begin

Animatel.FileName := 'cool.avi'; // Указываем файл с клипом Animatel.Active := True; // Запускаем клип

ProgressBarl.Show; // Показываем ProgressBar Timeri.Enabled := True; // Включаем таймер

end;

  • Создайте такой обработчик события onTimer компонента

Timer1:

procedure TFormI.TimerlTimer(Sender: TObject) ;

begin

ProgressBarl.Position := ProgressBarl.Position + 1;

if ProgressBarl.Position >= ProgressBarl.Max then

begin

Timeri.Enabled := False; // Выключаем,таймер ProgressBarl.Positinr := о; // Готовим новый запуск ProgressBarl.Hide; // Прячем ProgressBar

Animatel.Stop // Останавливаем клип

end;

end;

Свойства компонента:

property Active: Boolean; | Разрешает/запрещает демонстрацию клипа. Во время демонстрации содержит True
property AutoSize: Boolean; | Если содержит True, компонент автоматически устанавливает свои размеры так, чтобы полностью разместить изображение кадра
property Center: Boolean; | Центрирует изображение кадра в границах компонента по горизонтали и по вертикали
TCommonAVI = (aviNone, aviFind- Folder, aviFindFile, aviFindCom- puter, aviCopyFiles, aviCopyFile, aviRecycleFile, aviEmptyRecycle, aviDeleteFile) ; property ComrnonAVI: TCommonAVI; | Задает один из стандартных видеоклипов, входящтх в библиотеку SHELL32 . DLL (СМ. рис. 18.25)
property FileName: TFileName; | Связывает компонент с AVI-файлом
property FrameCount: Integer; | Содержит количество кадров, показанных с начала демонстрации клипа
property FrameHeight: Integers; | Высота в пикселях одного кадра клипа
property FrameWidth: Integers; | Ширина кадра
property Open: Boolean; | Содержит True, если компонент связан с AVI-клипом и готов к работе
property Repetitions: Integers; | Определяет количество повторений клипа. Если 0, клип повторяется до тех пор, пока свойство Active содержит True
property ResHandle: THandle; | Указывает дескриптор ресурсного файла, содержащего AVI-клип
property ResHandle: THandle; | Указывает идентификатор ресурса с клипом в ресурсном файле
property ResName: Strings; | Указывает имя ресурса с клипом в ресурсном файле
property StartFrame: Smallint; | Содержит номер начального кадра демонстрации (нумерация кадров начинается с 1)
property StopFrame: Smallint; | Содержит номер конечного кадра демонстрации (нумерация кадров начинается с 1)

property Timers: Boolean; property Transparent: Boolean;

Разрешает/запрещает синхронизацию по таймеру. Если True, демонстрация синхронизируется сигналами таймера. Если False, для демонстрации используется независимый поток команд Если содержит True, фон клипа не накладывается на фон компонента

С помощью свойства commonAvi можно заставить компонент показывать один из стандартных видеоклипов, показанных на рис. 18.25.

Рис. 18.25. Видеоклипы свойства СоттопА VI

Методы компонента:

procedure Play(FromFrame, ToFrame: Word; Count: Integer); | Демонстрирует Count раз подряд фрагмент клипа, начиная с кадра FromFrame по ToFrame включительно (нумерация кадров начинается с 1)
procedure Reset; procedure Seek(Frame: Smallint) ; procedure Stop; | Восстанавливает исходное состояние компонента. Свойство Open вновь становится True, но свойство Active = False Пропускает и не показывает кадр с номером Frame (нумерация кадров начинается с 1) Прекращает показ клипа
Для компонента определены следующие события:
property OnClose: TNotifyEvent; property OnOpen: TNoti-fyEvent; property OnStart: TNoti-fyEvent; property OnStop: TNotifyEvent; | Возникает при установке значения False в свойство Open. Например, когда компонент демонстрирует несколько видеоклипов подряд Возникает при установке значения True в свойство Open Возникает в момент начала демонстрации Возникает в момент прекращения демонстрации

18.3.10. TDateTimePicker - ввод и отображение даты/времени

Компонент предназначен для ввода и/или отображения даты или времени. На рис. 18.26 показаны различные варианты использования компонента.

Рис. 18.26. Примеры использования компонента TDateTimePicker

При показе даты в режиме DateMode=dmComboBox календарь можно не раскрывать и установить дату вручную в верхнем окне. Это удобно, если нужно установить сразу и день, и месяц, и год. На раскрытом календаре изменить месяц можно небольшими кнопками вверхней части компонента. Календарь закрывается после выбора даты или при щелчке по раскрывающей кнопке. Установив в свойство Kind значение dtkTime, можно заставить компонент отображать время (справа на рисунке). Это время в момент установки компонента на форму соответствует системному времени, и в дальнейшем его можно изменить, задав новое значение в свойство Time.

Свойства компонента:

TDTCalAlignment = (dtaLeft, dtaRight) ; | Определяет положение раскрывающегося календаря: dtaLeft - слева от компонента;
property CalAlignment: TDTCalAlignment; | dtaRight - справа от компонента. Учитывается только ДЛЯ Kind = dtkDate и Date-Mode = dmComboBox
property CalColors: TDateTimeColors; | С календарем связан объект класса TDateTimeColors, свойства которого определяют цвета календаря
property Checked: Boolean; | Если Checked=True И ShowCheckBox=True, независимый переключатель рядом с датой (временем) будет иметь состояние Выбрано
property Date: TDate;TDTDateFormat = (dfShort,dfLong) ; property DateFormat: TDTDateFormat; | Содержит введенную дату Определяет формат показа даты: df short -08. 09. 99; dfLong-8 Сентябрь 1999г.
TDTDateMode = (dmComboBox, dmUpDown) ; property DateMode: TDTDateMode; | Определяет способ выбора даты: dmComboBox - с помощью раскрывающегося ка лендаря; dmUpDown - с помощью встроенного объекта TupDown
property Format: String; | Определяет формат вывода даты и времени (см. табл. 7.8 в гл. 7)
TDateTimeKind = (dtkDate, dtkTime); property Kind: TDateTimeKind; | Определяет содержимое компонента: dtkDate -дата; dtkTime - время
property MaxDate: TDate; | Определяет максимальную дату, которую может выбрать иди ввести пользователь
property MinDate: TDate; | Определяет минимальную дату, которую может выбрать или ввести пользователь
property Parselnput: Boolean; | Если True, возникает событие OnUserlnput при каждом вводе пользователя в окне редактора
property ShowCheckbox: Boolean; | Если True, рядом с датой (временем) вставляется независимый переключатель
property Time: TTime; | Содержит введенное пользователем время
Для компонента определены следующие события:
property OnChange: TNotifyEvent; | Возникает при любом изменении содержимого компонента
property OnCloseUp: TNotifyEvent; | Возникает при закрытии календаря
property OnDropDown: TNotifyEvent; | Возникает при открытии календаря
TParseInputEvent = procedure (Sender: TObject; const User-String: String” var DateAndTime: TDateTime; var AllowChange: Boolean) of objects-property OnUserInput: TDTParseIn-putEvent; | Возникает при ручном вводе в окне редактора, если ParseInput=True.UserString -введенная пользователем строка; DateAndTime - значения свойств Date и Time. В параметре AllowChange обработчик разрешает или запрещает изменение даты или времени

18.3.11. TMonthCalendar - календарь

Как видно из рисунка слева, компонент предназначен для выбора или отображения даты. Он очень похож на календарь, который появляется В компоненте TDateTimePicker (при значении dmComboBox В свойстве DateMode), но в отличие от последнего может отображать одновременно несколько смежных месяцев - в зависимости от своих размеров.

Свойства компонента:

property CalColors: TMonthCalColors; | С компонентом связан объект класса TMonthCalColors, имеющий такие свойства:BackColor - определяет цвет фона, разделяющего смежные месяцы; MonthBackColor- цвет фона дат; TextColor - цвет дат; TitIeBackColor - цвет фона заголовка месяца: TitleTextColor - цвет текста заголовка; TrailingTextColor - цвет текста ведущих и ведомых дат
property Date: TDate; | Определяет выбранную дату
property EndDate: TDate; | Определяет конечную дату диапазона вы бранных дат
type TCalDayOfWeek = (dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday, dowSunday, dowLocaleDefault) ; property FirstDayOfWeek: TCalDay OfWeek; | Определяет первый день недели
property MaxDate: TDate; | Содержит максимальную дату, месяц которой еще будет доступен для отображения в компоненте. Если свойство содержит пустое значение, отображается любой следующий месяц
property MaxSelectRange: Integer; | Содержит максимальное количество дат в выбранном диапазоне
property MinDate: TDate; property MultiSelect: Boolean; property ShowToday: Boolean; property ShowTodayCircle: Boolean; property WeekNumbers: Boolean; | Содержит минимальную дату, месяц которой еще будет доступен для отображения в компоненте. Если свойство содержит пустое значение, отображается любой предыдущий месяц Разрешает/запрещает выбор диапазона дат Разрешает/запрещает показ текущей даты (по показаниям системных часов) внизу календаря Разрешает/запрещает обводить кружком текущую дату Разрешает/запрещает показ порядковых номеров недель от начала года в левой колонке

18.3.12. TTreeView - дерево иерархии

Компонент TTreeView служит для показа ветвящихся иерархических структур, таких как дерево наследования объектов или файловая структура диска. Он содержит связанные узлы, каждый из которых может содержать пиктограмму, текст и произвольный объект (подобно спискам TStringList - см. п. 14.3.2). Любой узел может иметь собственный список подузлов (рис. 18.27), которые можно раскрывать или закрывать щелчком мыши на пиктограмме узла.

Рис. 18.27. Пример использования компонента TTreeView

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

Рис. 18.28. Редактор компонента

Чтобы начать наполнение, щелкните по кнопке New item и введите связанный с узлом текст в поле Text. С помощью image index панели item properties устанавливается индекс связанной с узлом пиктограммы, с помощью selected index - индекс пиктограммы для выбранного узла. Для ввода подузла любого уровня сначала нужно щелкнуть в окошке Items по узлу, который должен стать родительским, и лишь затем - на кнопке New subitem.

Для ввода списка в режиме прогона программы широко используется центральное свойство компонента - items типа TTreeNodes, открывающее индексированный доступ ко всем узлам списка. Каждый узел описывается классом TTreeNode, имеющим свои собственные методы и свойства. В частности, его свойство item содержит список всех подузлов данного узла; с помощью многочисленных методов свойства TTreeview. items к этому списку можно добавить новый подузел, а с помощью метода TTreeNode. MoveTo - переместить узел в любую позицию дерева иерархии.

Следующий обработчик события oncreate формы создал рис. 18.29:

procedure TFormI.PormCreate(Sender: TObject) ;

var

k: Integer;

begin with TreeViewl do

begin

// Добавляем корневой узел

Items.Add(NIL,'Корень') ;

//Добавляем 10 вложенных узлов

for k := 1 to 10 do

Items.AddChild(Items[k-1],IntToStr(k))

end

end;

Puc. 18.29. Создание подузлов на этапе прогона программы

Свойства компонента TTreeview:

lean;TBorderStyle = bsNone..bsSingle; property BorderStyle: TBorderStyle; | Определяет стиль рамки, охватывающей компонент: bsNone - нет рамки; bsSingle - рамка толщиной в 1 пиксель
property ChangeDelay: Integer; | Указывает задержку (в миллисекундах) перед раскрытием узла
property DropTarget: TTreeNode; | Указывает узел, который может служить приемником операций Drag&Drop
property HideSelection:Boolean; | Указывает, будет ли убираться выделение узлов, когда компонент теряет фокус ввода
property Images: TImageList; | Содержит набор изображений, которые будут использоваться при прорисовке узлов
property Indent: Integer; | Определяет отступ в пикселях от левого угла узла для всех его подузлов
property Items: TTreeNodes; | Открывает доступ к любому узлу по его индексу. Индексация начинается с нуля и соответствует просмотру всех узлов полностью раскрытого списка
property MultiSelect: Boo lean; | Разрешает/запрещает одновременный выбор несколь ких узлов
type TMultiSelectStyles = (msControlSel-ect, msShift Select, msVisibleOnly, msSiblingOnly) ; TMultiSelectStyle = set of TMultiSelectStyles; property MultiSelectStyle: TMultiSelect-Style default [msControlSelect]; | Определяет способ выбора нескольких узлов: msControiselect - с нажатой и удерживаемой клавишей Ctrl для выбора узлов в произвольном порядке; msShiftSelect - с нажатой и удерживаемой клавишей Shift для выбора сплошного диапазона; msVisibleOnly - подобно msShiftSelect, но в диапазон не включаются дочерние нераскрытые узлы; msSiblingOnly - подобно msShiftSelect, но в диапазон включаются только узлы одного уровня
property Readonly: Boolean; | Запрещает/разрешает редактирование надписей в узлах
property RightClickSelect: Boolean; | Разрешает выбор узлов правой кнопкой мыши
property Selected: TTreeNode; | Содержит список всех выбранных узлов или nil, если таких нет
property SelectionCount: Cardinal; | Содержит количество выбранных узлов
property Selections[Index: Integer]: TTreeNode; | Обеспечивает индексированный доступ к выбранным узлам
property RowSelect: Boolean; | Разрешает цветовыделение линий выбранных узлов. Игнорируется, если showLinesFalse
property ShowButfcons: Boolean; | Разрешает/запрещает показ стандартных кнопок раскрытия подузлов. По умолчанию содержит True. Если False, узел раскрывается двойным щелчком мыши
property ShowLines: Boolean; | Разрешает/запрещает показ линий
property ShowRoot: Boolean; | Разрешает/запрещает показ линий, идущих от самого верхнего уровня иерархии. Игнорируется, если ShowLines=False
TSortType = (stNone, stData, stText, stBoth) ; property SortType: TSort Type; | Указывает способ сортировки узлов: stNone - нет сортировки; stData - сортировка по данным; stText - сортировка по тексту надписей; stBoth - сортировка по тексту и по данным. См. также событие OnCompare
property Topitern: , TTreeNode; | Определяет корневой узел
Методы компонента:
function AlphaSort: Boolean; TTVCompare = function (IParamI, lParam2, IParamSort: Longint) Integer stdcall; tfunction CustomSort(SortProc: TTVCompare; Data: Longint): Boolean; | Сортирует узлы по тексту и возвращает True, если сортировка прошла успешно Определяет нестандартную сортировку с : помощью функции SortProc. Эта функция должна рассматривать IParamI и lParam2 : как объекты TTreeNode и возвращает отрицательное число, если lParaml<lParam2; ноль, если IParamel^lParamS; положитель ное число, если lParaml>lParam2
procedure FullCollapse; | Прячет все узлы, кроме узлов самого верх него уровня иерархии
Procedure FullExpand; | Показывает все узлы дерева иерархии
function GetNodeAt(X, Y: Integer) : TTreeNode; | Возвращает узел, располагающийся в указанной точке, или nil, если точка не принадлежит ни одному узлу
function IsEditing: Boolea' | Возвращает True, если пользователь редактирует какой-либо узел
procedure LoadPromFile(const FileName: String); | Загружает дерево иерархии из файла
procedure SaveToFile(const File Name: String) ; | Сохраняет в файле дерево иерархии
procedure SaveToStream(Stream: TStream) ; | Сохраняет в потоке данных дерево иерархии события: С компонентом связаны такие
TTVChangedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnChange: TTVChangedE vent; | Возникает при смене состояния выбора у одного из узлов. Node - узел, который изменил состояние
TTVChangingEvent = procedure( Sender: TObject; Node: TTreeNode; var AllowChange: Boolean) of objectproperty OnChangir.g: TTVChang ingEvent; | Возникает перед сменой состояния выбора у одного из узлов. Node - узел, который будет выбран. Обработчик в параметре AllowChange разрешает или запрещает выбор узла
TTVExpandedEvent = procedure( Sender: TObject; Node: TTreeNode) of object;property OnCollapsed: TTVExpand edEvent; | Возникает при закрытии списка подузлов узла Node
TTVCollapsingEvent = procedure( Sender: TObject; Node: TTreeNode; var AllowCollapse: Boolean) of object; property OnCollapsing: TTVCollapsingEvent; | Возникает перед закрытием списка подуз лов узла Node. В параметре AllowCollapse обработчик разрешает или запрещает закрыть список
TTVCompareEvent = procedure( Sender: TObject; Nodel, Node2: TTreeNode; Data: Integer; var Compare: Integer) of object; property OnCompare: TTVCom pareEvent; | Возникает при сравнении двух узлов Nodel И Node2. В параметре Compare обработчик должен вернуть отрицательное число, если Nodel<Node2; ноль, если Model=Node2; положительное число, если Nodel>Node2
TTVExpandedEvent = procedure( Sender: TObject; Node: TTreeNode) of object; property OnDeletion: TTVExpand edEvent; | Возникает при удалении узла Node из дерева иерархии
TTVEditedEvent = procedure(Sender: TObject; Node: TTreeNode; var S: String) of object; property OnEdited: TTVEditedEvent; | Возникает при завершении редактирования надписи в узле Node: S - новая надпись
TTVExpandedEvent = procedure( Sender: TObject; Node: TreeNode) of object; property OnExpanded: TTVExpand edEvent; | Возникает при распахивании списка подузлов узла Node
TTVExpandingEvent = procedure( Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean) of object; property OnExpanding: TTVExpand ingEvent; | Возникает перед открытием списка подузлов узла Node. В параметре AllowExpansion обработчик разрешает или запрещает открыть список
TTVExpandedEvent = procedure( Sender: TObject; Node: TreeNode) of object;property OnGetImageIndex: TTVEx pandedEvent; | Возникает при необходимости получения индекса изображения для прорисовки узла Node в обычном состоянии
TVExpandedEvent == procedure( Sender: TObject; Node: TTreeNode) of object; property OnGetSelectedIndex: TTVExpandedEvent; | Возникает при необходимости получения индекса изображения для прорисовки узла Node в выбранном состоянии

При программном заполнении списка следует пользоваться свойством TTreeView. Items класса TTreeNodes.

Свойства класса TTreeNodes:

property Count: Integer; property Item[Index: Integer]: TTreeNode; default-property Owner: TCustomTreeView; | Количество узлов, входящих в items Открывает индексированный доступ к узлам Содержит ссылку на родительский список
Методы класса TTreeNodes:
function Add(Node: TTreeNode; const S: String): TTreeNode; function AddChild(Node: TTreeNode; const S: String): TTreeNode; function AddChildFirst(Node: TTreeNode; const S: String): TTreeNode; function AddChildObject(Node: TTreeNode; const S: String; Ptr: Pointer): TTreeNode; function AddChildObjectFirst( Node: TTreeNode; const S: String; Ptr: Pointer): TTreeNode; | Добавляет узел в конец того списка, в котором зарегистрирован узел Node. Если Node=NlL, добавляется корневой узел для всего компонента Добавляет узел в конец списка item дочерних узлов узла Node Добавляет узел в начало списка Item дочерних узлов узла Node Добавляет узел и данные в конец списка item дочерних узлов узла Node. На данные ссылается указатель Ptr. Связанная с данными область памяти не освобождается автоматически при уничтожении списка Добавляет узел и данные в начало списка item дочерних узлов узла Node. На данные ссылается указатель Ptr. Связанная с данными область памяти не освобождается автоматически при уничтожении списка
function AddFirst(Node: TTreeNode; const S: String): TTreeNode; | Добавляет узел в начало того списка, в котором зарегистрирован узел Node
function AddObject(Node: TTreeNode; const S: String; Ptr: Pointer): TTreeNode; | Добавляет узел и данные в конец того же списка, в котором зарегистрирован узел Node. На данные ссылается указатель Ptr. Связанная с данными область памяти не освобождается автоматически при уничтожении списка
function AddObjectFirst(Node: TTreeNode; const S: String; Ptr: Pointer): TTreeNode; | Добавляет узел и данные в начало того же списка, в котором зарегистрирован узел Node. На данные ссылается указатель Ptr. Связанная с данными область памяти не освобождается автоматически при уничтожении списка
procedure Assign(Source: TPersistent) ; | Связывает список текущего компонента со списком компонента Source
procedure BeginUpdate; | Блокирует обновление экрана до тех пор, пока не будет выполнен метод EndUpdate. Используется при одновременной вставке нескольких элементов списка для предотвращения мерцания экрана
procedure Clear; | Очищает список всех узлов и подузлов компонента
procedure Delete(Node: TTreeNode); | Уничтожает узел Nods
procedure EndUpdate; | Отменяет действие метода BeginUdate
function GetFirstNode: TTreeNode; | Возвращает самый первый узел в списке Items[0]
function GetNode(Itemid: HTreeItem): TTreeNode; | Возвращает узел по его идентификатору Itemid
function Insert(Node: TTreeNode; const S: String): TTreeNode; | Вставляет узел непосредственно перед узлом Node
function InsertObj ect(Node: TTreeNode; const S: String; Ptr: Pointer): TTreeNode; | Вставляет узел и данные непосредственно перед узлом Node

Как уже отмечалось, каждый узел класса TTreeNode имеет свой набор методов, свойств и событий.

Свойства TTreeNode:

property Absolutelndex: Integer; | Возвращает абсолютный индекс узла (с учетом всех подузлов)
property Count: Integer; | Содержит количество подузлов в списке Item
property Cut: Boolean; | Вырезает узел и помещает его в clipboard
property Data: Pointer; | Указывает на связанные с узлом данные
property Deleting: Boolean; | Содержит True, если для узла вызван De-story
property DropTarget: Boolean; | Содержит True, если узел может служить приемником операции Drag&Drop
property Expanded: Boolean; | Содержит True, если узел распахнут
property Focused: Boolean; | Содержит True, если узел сфокусирован
property HasChildren: Boolean; | Содержит True, если узел имеет дочерние узлы
property Imagelndex: TImageIndex; | Содержит индекс связанной с узлом пиктограммы
property Index: Longint; | Содержит индекс узла в списке дочерних узлов его родительского узла
property IsVisible: Boolean; | Содержит True, если узел виден
property Item[Index: Integer]: TTreeNode; | Открывает индексированный доступ ко всем дочерним узлам
property Itemid: HTreeItem; | Содержит уникальный Windows-дескриптор узла
property Level: Integer; | Содержит иерархический уровень узла
property Overlaylndex: Integers- | Содержит индекс оверлейной пиктограммы. Оверлейная пиктограмма вычерчивается поверх основной, чтобы, например, указать, что узел стал недоступен
property Owner: TTreeNodes; | Содержит ссылку на владельца данного узла
property Parent: TTreeNode; | Содержит ссылку на родительский узел
property Selected: Boolean; | Содержит True, если узел выделен цветом
property Selectedlndex: Integer; | Содержит номер пиктограммы для выделенного узла
property Text: Strings- | Содержит текст узла
property TreeView: TCustomTree- View; | Содержит ссылку на компонент TreeView, к которому принадлежит узел

Методы класса TTreeNode:

function AlphaSort: Boolean; | Сортирует узлы по алфавиту свойств Text и возвращает True в случае успеха
procedure Assign(Source: TPersis tent); override; | Связывает список подузлов с источником Source
procedure Collapse(Recurse: Boolean) ; | Закрывает все узлы (Recource=True) или только распахнутые(Resource=False)
type TTVCompare = function( IParamI, lParam2, IParamSort: Longint): Integer stdcall; func tion CustomSort(SortProc: TTVCom pare; Data: Longint): Boolean; | Реализует нестандартную сортировку узлов
procedure Delete; | Удаляет текущий узел
procedure DeleteChildren; | Удаляет дочерние узлы
function DisplayRect(TextOnly: Boolean): TRect; | Возвращает очерчивающий прямоугольник узла. Если TextOnly=True - возвращает очерчивающий прямоугольник текста
function EditText: Boolean; | Переводит текст узла в режим редактирования
procedure EndEdit(Cancel Boo lean) ; | Заканчивает редактирование текста и со храняет его изменения, если Cancel=False
procedure Expand(Recurse: Boo lean) ; | Открывает узел (и его подузлы, если Recurce=True)
function GetFirstChild: TTreeNode; | Возвращает ссылку на первый подузел или nil, если нет подузлов
function GetLastChild: TTreeNode; | Возвращает ссылку на последний подузел или nil, если нет подузлов
function GetNext: TTreeNode; function GetNextChild(Value: TTreeNode): TTreeNode; | Возвращает ссылку на очередной подузел dозвращает ссылку на подузел после value (или nil, если такового нет).
function GetNextSibling: TTreeNode; | Возвращает ссылку на очередной узел в том же списке.
function GetNextVisible: TTreeNode; | Возвращает ссылку на очередной видимый узел (для которого раскрыты все дочерние узлы)
function GetPrev: TTreeNode; | Возвращает ссылку на предыдущий узел в том же списке независимо от его видимости
function GetPrevChild(Value: TTreeNode): TTreeNode; | Возвращает ссылку на предыдущий по отношению к value дочерний узел
function GetPrevSibling: TTreeNode; | Возвращает ссылку на предыдущий узел того же уровня
function GetPrevVisible: TTreeNode; | Возвращает ссылку на видимый узел того же уровня
function HasAsParent(Value: TTreeNode): Boolean; | Возвращает True, если value - родительский узел
function IndexOf(Value: TTreeNode): Integer; | Возвращает идентификатор узла value
procedure MakeVisible; | Если родительский узел видимый, делает видимыми все дочерние узлы
type TNodeAttachMode = (naAdd, naAddFirst, naAddChild, naAddChildFirst, nalnsert) ; procedure MoveTo(Destination: TTreeNode; Mode: TNodeAttach Mode) ; | Перемещает текущий узел в позицию относительно Destination в зависимости от параметра Mode: naAdd - добавляет в конец списка узла того же уровня; naAddFirst - делает первым в списке узлов того же уровня; naAddchild - добавляет в конец списка дочерних узлов; naAddChildFirst -делает первым в списке дочерних узлов; nalnsert - вставляет непосредственно перед узлом

18.3.13. TListView - список просмотра

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

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

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

На Пустую форму положите компоненты TListView, TImageList, TComboBox И TCheckBox.

  • Компонент ImageList1 будет служить контейнером для нескольких пиктограмм, отображаемых компонентом Listview. Для наполнения контейнера дважды щелкните по нему мышью или нажмите на нем правую кнопку и выберите продолжение ImageList Editor.
  • В появившемся окне редактора imageList нажмите кнопку Add и выберите файл images | ICONS | CHEMICAL[См. замечание в начале главы.].
  • Повторите п. 3 для выбора нескольких пиктограмм. Обратите внимание, что каждой помещаемой в imageListi пиктограмме редактор присваивает уникальный индекс.
  • Напишите такой обработчик события oncreate для формы:

procedure TFormI.FormCreate(Sender: TObject);

var

k: Integer;

Listltem: TListItem;

begin

with ListViewl do

begin

// Указываем источник изображений:

Smalllmages := ImageListI;

Largelmages := ImageListI;

// Наполняем ListViewl пиктограммами и текстом:

for k := 0 to ImageListI.Count - 1 do

begin

// Добавляем элемент и получаем ссылку на него

Listltem := Items.Add;

// Указываем индекс связанного изображения

Listltem.Imagelndex := k;

// Вставляем надпись ImageN

Listltem.Caption := 'Image' + IntToStr(k);

end;

// Наполняем список ComboBoxl доступными стилями ListView ComboBoxl.Items.AddObject('vslcon', TObject(vslcon)) ;

ComboBoxl.Items.AddObject('vsLisf, TObject (vsList)) ;

ComboBoxl.Items.AddObject('vsReporf, TObject(vsReport)) ;

ComboBoxl.Items.AddObject('vsSmall Icon',

TObject(vsSmallIcon)) ;

// Показываем первый стиль в Combo Box

ComboBoxl.Itemlndex := 0;

// Создаем колонку для режима vsReport:

Columns.Add; Columns[0].Caption := 'Колонка T;

Columns [.0 ] .Width := 80;

end;

end;

  • Напишите такие Обработчики событий ComboBoxl.OnChange И A. CheckBoxl.Click:

procedure TFormI.ComboBoxIChange(Sender: TObject);

begin

with ComboBoxl do

ListViewl.ViewStyle := TViewStyle(

I terns.Objects[Itemlndex]) ;

end;

procedure TFormI.CheckBoxIClick(Sender: TObject);

begin

ListViewl.CheckBoxes := CheckBoxl.Checked

end;

Некоторые важные свойства компонента:

property AllocBy: Integer; | Используется перед вставкой большого количества элементов: для экономии времени и памяти перед добавлением установите в это свойство количество добавляемых элементов
property Checkboxes: Boolean; | Разрешает/запрещает показ независимых переключателей рядом с элементами
property Column[Index: Integer]: TListColumn; | Открывает доступ к колонкам элементов по их индексам
property ColumnClick: Boolean; | Разрешает/запрещает генерацию события OnColumnClick
property Columns: TListCoiumns; | Содержит объект-колонки. Используйте это свойство для удаления или добавления колонок, а также для изменения их свойств. Колонки видны только для режима View-Style = vsReport. И наоборот: элементы в этом режиме видны, только если определена хотя бы одна колонка
property GridLines: Boolean; | Разрешает/запрещает показ линий в режиме ViewStyle = vsReport
property HideSelection: Boolean; | Запрещает/разрешает сохранять выбор элементов при потере компонентом фокуса ввода
property HotTrack: Boolean; | Разрешает/запрещает подсветку элементов, над которыми проходит указатель мыши
property IconOptions: TIconOptions; | С помощью объекта класса TiconOptions задаются дополнительные условия отображения (см.ниже)
property Items: TListItems; | Содержит список всех элементов
property Largelmages: TImageList; | Указывает источник крупных пиктограмм
property MultiSelect: Boolean; | Разрешает/запрещает множественный выбор
property Readonly: Boolean; | Запрещает/разрешает редактирование надписей
property ShowColumnHeaders: Boolean; | Разрешает/запрещает показ заголовков колонок в режиме ViewStyle = vsReport
property Smalllmages: TImageList; TSortType = (stNone, stData, stText, stBoth) ; property SortType: TSortType; | Указывает источник мелких пиктограмм Указывает способ сортировки элементов
property Statelmages: TImageList; | Указывает источник пиктограмм для вы бранных элементов
TViewStyle = (vslcon, vsSmallI- con, vsList, vsReport) ; property ViewStyle: TViewStyle; | Определяет стиль показа элементов: vslcon - крупные пиктограммы; vsSmalllcon - мелкие пиктограммы; vsList - список пиктограмм; vsReport - таблица пиктограмм

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

Column Editor. Пример программного создания колонки показан на стр.483.

Класс TIconOption определяет дополнительные условия отображения узлов и имеет такие свойства:

type TIconArrangement = (iaTop, iaLeft) ; property Arrangement: TIconArrangement; property AutoArrange: Boolean;

property WrapText: Boolean;

Показывает способ расположения изображения: iaTop - слева направо (вверху колонки); iaLeft - сверху вниз слева от ряда колонок Если содержит True, элементы будут автоматически располагаться правильными рядами при изменении их количества Если содержит True, текст элемента может переноситься на границах слов на несколько строк, если он выходит за границы пиктограммы

При программном заполнении компонента центральную роль играет свойство Items класса TListItems.

Свойства класса TListItems:

property Count: Integers-property Item[Index: Integer]: TListItem; | Содержит количество элементов в item Открывает индексный доступ к элементам списка
Методы класса TListItems:
function Add: TListItem; procedure Assign(Source: TPersis-tent); override; procedure BeginUpdate; procedure Clear; procedure Delete(Index: Integers-procedure EndUpdate;” function IndexOf(Value: TListItem) : Integers-function Insert(Index: Integer): TListItem; procedure SetCount(Value: Integer) ; | Добавляет очередной элемент к списку Связывает список компонента со списком компонента source Блокирует обновление экрана до тех пор, пока не будет выполнен метод EndUpdate. Используется при одновременной вставке нескольких элементов списка для предотвращения мерцания экрана Очищает список Удаляет элемент списка с индексом index Отменяет действие BeginUpdate Возвращает индекс узла value Вставляет новый элемент после элемента, заданного индексом index Устанавливает новое количество компонентов списка

Свойство item типа TListItem открывает индексированный доступ к подэлементам конкретного элемента верхнего уровня (допускается только два уровня иерархии элементов). За информацией по этому классу обратитесь к справочной службе Delphi.

18.3.14. THeaderControl - управляющий заголовок

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

В программе, окно которой показано на рис. 18.30, компонент THeaderControl используется для управления положением и линейными размерами трех других компонентов. Для реализации программы выполните следующее:

  • Положите на пустую форму компоненты THeaderControl, ТЕ-dit, TMemo и TButton. По умолчанию HeaderControll .Align =alTop - убедитесь в этом и установите свойство, если это не так. Положение и размеры других компонентов не имеют значения.
  • Создайте такие обработчики событий OnCreate для формы Form1, а также OnResize и OnSectionResize для компонента HeaderControll:

Рис. 18.30. Пример использования компонента THeaderControl для управления положением и размерами других компонентов

const

Delta = 10;// Зазор между границами заголовка и компонентами

procedure TFormI.FormCreate(Sender: TObject);

var

HSection: THeaderSection;

k: Integer;

begin

// Создаем три секции заголовка:

with HeaderControll do

for k := 0 to 2 do

begin

HSection := Sections.Add;

HSection.Text := 'Секция №' + IntToStr(k);

HSection.Width := Forml.Width div 3;

HSection.MinWidth := 3 * Delta;

end;

end;

procedure Tform1.HeaderControllResize(Sender: TObject);

// Устанавливает положение и размеры компонентов

begin

with HeaderControll do

begin

Editl.Left := Delta;

Editl.Top := HeaderControll.Height + 1;

Editl.Width := Sections.Items[0].Width - 2 * Delta;

Memol.Left := Sections.Items[1].Left + Delta;

Memol.Top := HeaderControll.Height + 1;

Memol.Width := Sections.Items[1].Width - 2 * Delta;

Buttonl.Left := Sections.Items[2].Left + Delta;

Buttonl.Top := HeaderControll.Height + 1;

Buttonl.Width := Sections.Items[2].Width - 2 * Delta;

end

end;

procedure TFormI.HeaderControllSectionResize(

HeaderControl: THeaderControl; Section: THeaderSection);

begin

HeaderControllResize(Self)

end;

He забудьте определить в области видимости обработчиков FormCreate И HeaderControllResize глобальную константу Delta.

Для заголовка создается объект класса THeadersections, определяющий секции заголовка. Его свойство

property Items[Index: Integer]: THeaderSection;

открывает доступ к индексированным объектам-секциям класса THeaderSection. Свойства класса THeaderSection:

property Alignment: TAlignment; | Определяет выравнивание текста: taLeftJustify - прижат влево; taCenter - центрирован по горизонтали; taRight Justify - прижат вправо
property AllowClick:.Boolean; | Разрешает/запрещает генерацию события OnSectionClick при щелчке по секции
property Left: Integer; | Определяет положение левой границы секции относительно границ компонента (в пикселях)
property MaxWidth: Integer; | Определяет максимальную ширину секции в пикселях
property MinWidth: Integer; | Определяет минимальную ширину секции в пикселях
property Right: Integer; | Определяет положение правой границы секции относительно границ компонента (в пикселях)
THeaderSectionStyle = (hsText, hsOwnerDraw) ; property Style: THeaderSectionStyle; | Определяет способ формирования секции: hsText - секция содержит только текст и изображается автоматически; hsOwnerDraw - секция прорисовывается программой
Property Width: Integer; | Содержит текущую ширину секции в пикселях

Метод function THeaderSections.Add: THeaderSection;

добавляет очередную секцию заголовка. Некоторые свойства класса

THeaderSection:

property AllowClick: Boolean-property DisplayName: String; property Imagelndex: TImageIndex; | Если содержит True, разрешает щелкнуть по секции заголовка как по кнопке. В этом случае управление передается в обработчик события OnSectionClick Имя заголовка секции Указывает индекс изображения из хранилища images, которое будет появляться следом за текстом заголовка
property MaxWidth: Integer; property MinWidth: Integer; type THeaderSectionStyle == (hsText, hsOwnerDraw) ; | Определяет максимальную и минимальную ширину секции Определяет тип секции: hsText - секция содержит только текст; hsOwnerDraw - секция нуждается в программной прорисовке
property Style: THeaderSectionStyle; property Text: String; | Содержит текст секции

Свойства Компонента THeaderControl:

property Canvas: TCanvas; | С помощью этого свойства можно изображать графику в секциях заголовка
property HotTrack: Boolean; | Разрешает/запрещает цвето выделение секции, над которой располагается указатель мыши
property Sections: THeaderSections; | Содержит секции заголовка

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

TDrawSectionEvent = procedure( Header-Control: THeaderControl; Section: THeaderSection; const Rect: TRect; Pressed: Boolean) of object; property OnDrawSection: TDrawSectionEvent; | Возникает при необходимости прорисовать секцию, для которой установлен СТИЛЬ hsOwnerDraw: Section - объект-секция; Rect - прямоугольник прорисовки;Pressed -признак нажатой кнопки мыши
property OnResize: TNotifyEvent; TSectionNotifyEvent = procedure( Head- erControl: THeaderControl; Section: THeaderSection) of object; property OnSectionCli.ck: TSectionNoti fyEvent; | Возникает при изменении размеров компонента возникает при щелчке мышью по секции Section
TSectionNotifyEvent = procedure( Head- erControl: THeaderControl; Section: THeaderSection) of object; property OnSectionResize: TSectionNo tifyEvent; | Возникает при изменении размеров секции Section
TSectionTrackState = (tsTrackBegin, tsTrackMove, tsTrackEnd) ; TSectionTrackEvent = procedure( Head- erControl: THeaderControl; Section: THeaderSection; Width: Integer; State: TSectionTrackState) of objects- property OnSectionTrack: TSection TrackEvent; | Возникает при изменении размеров секции и позволяет обработать три возможных состояния: tsTrackBegin - начало переме щения границы;tsTrackMove - идет перемещение границы; tsTrackEnd - конец перемещения границы

18.3.15. TStatusBar - информационная панель

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

Рис. 18.31. Пример компонента с тремя панелями и кнопкой изменения размеров окна

На рис. 18.31 показан компонент TStatusBar с тремя панелями и кнопкой изменения размеров окна, созданный таким обработчиком события OnCreate для формы Form1(предварительно на пустую форму положите компонент TSatusBar):

procedure TFormI.FormCreate(Sender: TObject);

var

Panel: TStatusPanel;

k: Integer;

begin

with StatusBarl do

for k := 0 to 2 do

begin

Panel := Panels.Add;

Panel.Text := 'Панель №' + IntToStr(k);

Panel.. Width := Forml. Width div 3

end;

end;

С компонентом связывается объект класса TStatusPanels, который определяет панели компонента. Каждая панель относится к классу TstatusPanel и имеет такие свойства:

property Alignment: TAlignment; | Определяет выравнивание текста относительно границ секции; taLeftJustify: прижимается влево; taCenter - центрируется по горизонтали; taRightJustify - прижимается вправо
property Bevel: TStatusPanel-Bevel; | Определяет стиль рамки секции: pbNone -нет рамки; pbLowered - вдавленная рамка; pbRaised - приподнятая рамка
type TStatusPanelStyle = (psText, psOwnerDraw); property Style: TStatusPanelStyle; property Text: String; property Width: Integer; | Определяет способ формирования изображения панели: psText - панель содержит только текст и изображается автоматически; psOwnerDraw - панель прорисовывается программой Определяет текст надписи в секции Определяет ширину секции в пикселях
Свойства компонента TStatusBar:
property Canvas: TCanvas; | Канва для прорисовки панелей
property Panels: TStatusPanels; | Содержит объекты-панели. Свойство TStatusPanels.items [index: Integer]: TStatusPanei открывает доступ к панели по ее индексу
property SimplePanel: Boolean; | Запрещает/разрешает создание нескольких панелей. Если содержит True, компонент имеет единственную панель
property SimpleText: String; | Содержит текст панели для sim-plePanel=True
property SizeGrip: Boolean; | Разрешает/запрещает вставку кнопки изменения размеров окна. Игнорируется, если AlignoalBottom
Для компонента определены такие события:
TDrawPanelEvent = procedure( StatusBar: TStatusBar; Panel: TStatusPanei; const Rect: TRect) of object-property OnDrawPanel: TDrawPanelEvent; | Возникает при необходимости прорисовки панели Panel, если ее свойство Style определено как psOwnerDraw: Rect - прямоугольник прорисовки
Property OnResize: TNo.tifyEvent; | Возникает при изменении размеров компонента

18.3.16. TToolBar и ToolButton - инструментальная панель и кнопки для нее

Компонент TToolBar представляет собой специальный контейнер для создания инструментальных панелей. Главная отличительная черта TToolBar - его способность гибкого управления дочерними элементами, которые он может группировать, выравнивать по размерам, располагать в несколько рядов. Компонент может манипулировать любыми вставленными в него дочерними элементами, но все его возможности в полной мере проявляются только со специально для него разработанным компонентом TToolButton (инструментальная кнопка). Этот компонент похож на кнопку TSpeedButton, но не ищите его в палитре компонентов - его там нет. Поскольку он разработан специально для TToolBar, вставить его в инструментальную панель можно только после щелчка правой кнопкой на компоненте TToolBar и выборе NewButton или NewSeparator (сепараторы предназначены для функционального выделения на инструментальной панели групп элементов и представляют собой разновидности кнопок TTooiButton). Компонент TTooiButton не имеет свойства, предназначенного для хранения картинки, однако TToolBar умеет использовать контейнер TimageList, чтобы извлечь из него нужную картинку и поместить ее на инструментальную кнопку.

Методику использования TToolBar совместно с TToolButton рассмотрим на примере программы, окно которой показано на рис. 18.32.

Рис. 18.32. Пример TToolBar с кнопками TTooiButton

  • Поместите на пустую форму компонент TimageList, щелкните по нему правой кнопкой мыши и выберите продолжение ImageList Editor.
  • Для заполнения контейнера ImageList1 подойдут любые небольшие по размеру изображения. К сожалению, растры из каталога images | BUTTONS[См. замечание в начале главы.] рассчитаны на использование в кнопках TSpeedButton и содержат по два изображения каждый -обычное и для запрещенной кнопки. Кнопки TToolButton также могут иметь два или три изображения, но эти изображения необходимо размещать в разных контейнерах rimageList, поэтому использование заготовок из каталога images | buttons в кнопках TToolButton приведет к появлению на них по два изображения одновременно[Точнее, при вставке двойной картинки редактор компонента спросит у вас, надо ли ее разделять на две отдельные картинки.]. Чтобы этого не произошло, загрузите пиктограммы из каталога images | icons: щелкните по кнопке Add окна редактора и выберите в этом каталоге файл chemical.
  • Повторите П. 2 для фацлов Chip, Construe, Earth, Finance, Handchek, Shipping, Skyline И Technlgy, после чего закройте редактор ImageList.
  • Поместите на форму компонент TToolBar, в окне Инспектора объектов разыщите свойство images, раскройте список в правой колонке строки images и выберите imageListi. Мы указали компоненту на источник изображений и можем теперь вставлять в него кнопки.
  • Щелкните по ToolBar1 правой кнопкой мыши и выберите New Button.
  • Вставьте еще две кнопки, после чего вставьте разделитель NewSeparator.
  • Разместите на TooiBarl остальные кнопки и сепараторы (см. рис. 18.33).
  • Если вы захотите, чтобы кнопки имели модный “плоский” вид, установите в свойство Flat компонента ToolBarl значение True.

Свойство Buttons компонента TToolBar позволяет обратиться к каждому дочернему компоненту как к объекту класса TToolButton, имеющему такие свойства:

property AllowAlIUp: Boolean;. | Если содержит True, синхронизирует свое состояние с состоянием других кнопок в той же группе: в любой момент может быть нажата только одна кнопка группы. Игнорируется, если Grouped=False
property Caption: String; | Содержит связанный с кнопкой текст, который будет показан, если свойство ShowCaptions компонента TTooiBar имеет значение True
property Down: Boolean; | Определяет состояние кнопки: если содержит True, кнопка утоплена
property DropdownMenu: TPopupMenu; | Связывает вспомогательное меню с нажатой кнопкой
property Grouped: Boolean; | Разрешает/запрещает учитывать свойство ailOwAlIUp
property Imagelndex: Integer; | Определяет индекс связанного с кнопкой изображения
property Indeterminate: Boolean; | Запрещает/разрешает выбор кнопки.
TToolButtonStyle = (tbsButton, tbsCheck, tbsDropDown, tbsSeparator, tbsDivider) ; property Style: TToolButton Style; | Определяет стиль кнопки: tbsButton - обычная кнопка; tbsCheck - западающая кнопка (остается в нажатом положении; для ее освобождения нужно щелкнуть по ней еще раз); tbsDropDown - кнопка с символом раскрывающегося списка; tbsSeparator - разделитель (на месте этой кнопки будет пустое место); tbsDivider - разделитель (в работающей программе на местеэтой кнопки будет вертикальная черта)
property Wrap: Boolean; | Если имеет значение True, кнопка завершает текущий ряд кнопок. Игнорируется, если свойство wrapable компонента TToolBar имеет значение True

Свойства компонента TToolBar:

property AutoSize: Boolean; | Если содержит True, высота компонента будет автоматически согласовываться с высотой кнопок
property ButtonCount: Integer; | Содержит количество вставленных в инструментальную панель дочерних компонентов (не только кнопок TToolButton)
property ButtonHeight: Integer; | Определяет высоту кнопок TtoolButton
property Disabledlmages: TImageList; | Определяет контейнер для изображений запрещенных кнопок
property Flat: Boolean; | Если содержит True, панель и кнопки на ней прозрачны для фона и вокруг кнопки появляется граница, только когда над ней располагается указатель мыши
property Hotlmages: TImageList; | Определяет контейнер изображений для кнопок в момент, когда над кнопкой располагается указатель мыши
property Images: TImageList; | Определяет контейнер для изображений кнопок в обычном состоянии
property Indent: Integer; | Определяет отступ в пикселях от левого края компонента для первого дочернего элемента
property List: Boolean; | Если имеет значение True, изображение прижимается к левой границе кнопки, а текст - к правой, в противном случае текст выводится под изображением. Игнорируется, если showCaptions = False
property RowCount: Integer; | Содержит количество рядов кнопок
property ShowCaptions: Boolean; | Разрешает/запрещает показ на кнопках текста
property Wrapable: Boolean; | Запрещает/разрешает располагать кнопки в нескольких рядах

Для компонента определено событие OnResize, возникающее при изменении размеров компонента.

18.3.17. TCoolBar и TCoolBand - инструментальная панель и полосы для нее

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

Следует сразу же заметить, что в отличие от TcontrolBar (страница Additional палитры компонентов) компонент TCoolBar предназначен для примерно таких же задач, но и в версии 2, где он появился впервые, и в версии б он все еще кажется “сырым”, плохо отлаженным.

Центральным свойством компонента является Bands - массив созданных в компоненте полос TCoolBand. Каждая полоса может иметь текст, пиктограмму и произвольный управляющий элемент. В отличие от ттоо1ваг или TControiBar полоса в TCoolBar всегда содержит только один интерфейсный элемент, но ничто не мешает этому элементу быть элементом-контейнером для размещения нескольких компонентов. Размещенный на полосе компонент окружен специальным окном TCoolBar и всегда стремится занять левый верхний угол полосы, при этом остальные размеры полосы изменяются так, чтобы полностью охватить элемент.

Поясним сказанное несложным примером.

  • Поместите на пустую форму компонент TCoolBar - он тотчас же займет верхнюю часть формы, так как по умолчанию его свойства Alignment содержит alTop.
  • Щелкните по компоненту левой кнопкой и выберите Bands Editor (редактор полос).
  • Окно редактора похоже на многие другие используемые в Delphi окна-редакторы: щелкните по кнопке, чтобы вставить в компонент новую полосу.
  • В окне Инспектора объектов можете задать текст для полосы и связанную с ней пиктограмму свойствами соответственно Text и imageindex (предварительно компонент в свойстве images должен уже содержать ссылку на связанный с ним контейнер TimageList). Свойство BitMap используется для заполнения полосы чередующимся узором. Для простоты ограничьтесь указанием текста полоса i и закройте редактор полос.
  • Поместите ниже полосы, но в тот же контейнер CoollBar1 любой управляющий элемент, например, редактор TEdit. Подобно полосе он растянется вдоль верхнего края и будет окружен специальным окном с полосой-кнопкой для реализации механизма Drag&Dock.
  • Вновь раскройте редактор полос, щелкните по ранее установленной полосе и в Инспекторе объектов раскройте список в свойстве Control - в нем окажется только что вставленный в форму компонент TEdit. Щелкните по нему - и он “перепрыгнет” на первую полосу, оставив ранее занимавшую им полосу TCoolBar пустой. Таким способом происходит связывание полосы TCooiBar с компонентом.
  • Освободившуюся полосу можно связать с новым управляющим элементом (например, ComboBox) и т. д.
  • Вид работающей программы показан на рис. 18.33.

Рис. 18.33. Вид окна CollBar при рачых тчтпччких пользователя

Свойства TCoolBand:

property Bitmap: TBitmap; | Определяет изображение, которое будет циклически повторяться по всему пространству полосы
property BorderStyle: TBorder-Style; | Определяет наличие рамки у полосы: bsNone - нет рамки; bssingle - рамка толщиной в 1 пиксель
property Break: Boolean; | Если содержит True, полоса располагается в новой строке, в противном случае - в той же строке, что и предыдущая полоса
property Control: TWinControl; | Указывает элемент, который содержит полоса
property FixedBackground: Boolean; | Запрещает/разрешает периодическое повто рение изображения Bitmap по всей поверхности полосы
property FixedSize: Boolean; | Запрещает/разрешает изменение размеров полосы
property HorizontalOnly: Boolean; | Предписывает показывать полосу только для значения False свойства Vertical компонента-владельца TCoolBar
property Imagelndex: Integers; | Содержит индекс связанного с полосой изображения
property MinHeight: Integers; | Определяет минимальное значение высоты полосы при изменении ее размеров
property MinWidth: Integers; | Определяет минимальное значение ширины полосы при изменении ее размеров
property ParentBitmap: Boolean; | Разрешает/запрещает использовать значение свойства Bitmap компонента-владельца TCoolBar вместо собственного свойства Bitmap
property Text: String; | Содержит текст полосы
property Visible: Boolean; | Если содержит False, полоса не видна на этапе прогона программы
property Width: Boolean; | Определяет ширину полосы в пикселях

Свойства компонента TCoolBar:

property AutoSize: Boolean; | Если содержит True, высота компонента будет автоматически согласовываться с высотой полос
property BandBorderStyle: TBorderStyle; | Определяет наличие рамок: bsNone - нет рамок; bssingle - каждая полоса имеет рамку толщиной в 1 пиксель
property Bands: TCoolBands; | Содержит список всех полос. Свойство items этого объекта открывает доступ к полосе по ее индексу
property Bitmap: TBitmap; | Определяет изображение, которое будут использовать все полосы
property FixedOrder: Boolean; | Запрещает/разрешает перемещение полос
property FixedHeight: Boolean; | Запрещает/разрешает изменение размера полос
property Images; TImageList; | Указывает контейнер для изображений, связанных с каждой полосой
property ShowText: Boolean; | Если содержит True, на полосе показывается связанный с ней текст
property Vertical: Boolean; | Если содержит True, полосы располагаются по вертикали компонента
Для компонента определены такие события:
property OnChange: TNotifyEvent; | Возникает при изменении свойств Break, Index или Width у любой полосы
property OnResize: TNotifyEvent; | Возникает при изменении размеров компонента

18.3.18. TPageScroller - панель с прокруткой

Компонент служит контейнером для размещения длинных узких компонентов наподобие TToolBar. Его отличительная черта - наличие небольших стрелок прокрутки по сторонам контейнера в случае, если ширина (высота) дочернего окна превышает соответствующий размер компонента. Чтобы увидеть компонент в действии, щелкните по странице Win32 галереи компонентов Delphi (сам компонент TPageScroller на этой странице обычно не виден[Для разрешения экрана 800х600.]), и, чтобы получить к нему доступ, следует щелкнуть по кнопке с небольшой стрелкой в правой части страницы.

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

Свойства компонента:

property AutoScroll: Boolean; | Разрешает/запрещает автоматическую прокрутку при перемещении указателя мыши над кнопкой прокрутки
property ButtonSize: Integer; | Определяет размер в пикселях кнопок про крутки: для горизонтальной ориентации - ширину, для вертикальной - высоту
property Control: TWinControl; | Содержит ссылку на прокручиваемое окно
property DragScroll: Boolean; | Указывает, будет ли компонент приемни ком в операциях Drag&Drop. Если содержит True, компонент будет прокручивать дочернее окно при перемещении над ним указателя мыши с “грузом”
property Margin: Integer; | Указывает расстояние в пикселях от границы компонента до соответствующей стороны дочернего окна
type TPageScrollerOs-xentation = (soHorizontal, soVertical) ; property Orientation: TPageS crollerOrientation; | Определяет ориентацию компонента: soHorizontal -по горизонтали; soVertical -по вертикали
property Position: Integer; | Определяет текущее положение прокручиваемого окна относительно границ компонента

18.3.19. TComboBoxEx - список выбора с расширенными возможностями

Этот компонент впервые введен в версии 6. По сравнению с TCоmboBох (см. п. 18.1.11) он способен рисовать слева от элемента списка небольшое изображение и выводить элемент с заданным отступом от левого края списка (рис. 18.34). Кроме того, с каждым элементом можно связать указатель на произвольные данные, размещенные в динамической памяти.

Рис. 18.34. Пример использования компонента ТСотbоВохЕх

Специфичные свойства компонента:

property Images: TCustomImageList; | Определяет объект-хранилище изображении
property ItemsEx: TComboExItems; | Определяет строки списка выбора
property MaxLength: Integer; | Определяет максимальное количество символов, которые пользователь может ввести в окне редактора
type TComboBoxExStyle = (csExDropDown, csExSimple, csEx-DropDownList) ; property Style: TComboBoxExStyle; | Определяет стиль компонента: csExDrop-Down с кнопкой раскрытия списка и возможностью ручного ввода текста; csExSim-ple - без кнопки (список всегда раскрыт), высота списка определяется свойством Height; csExDropDownList -с кнопкой раскрытия списка, но пользователь не может ввести текст выбора вручную

Строки ItemsEx списка выбора определяет класс TComboExItems, имеющий такие свойства:

property Comboltems[const Index: Integer]: TComboExItem; property Items[const Index: Integer] : TListControlItem; default; type TLi'stItemsSortType = (stNone, stData, stText, stBoth); property SortType: TSortType;

Класс TListControlItem имеет три специфичных свойства: caption (String), Data (Pointer) И Imagelndex (integer), с помощью которых описываются текст элемента, а также связанные с ним данные и индекс изображения. Для установки значений этих свойств используются методы SetCaption, SetData И Setlmagelndex класса TListControlItem.

Класс TComboExitem, с помощью которого описываются строки списка в целом, имеет такие свойства:

property Caption: Strings-property Data: Pointed; | Текст элемента выбора Указатель на связанные с элементом данные
property Imagelndex: Timagelndex; | Индекс изображения для невыбранного элемента
property Indent: Integer; | Отступ элемента от левого края списка
property Overlaylmagelndex: Timagelndex; | Индекс изображения-маски
property SelectedlmageIndex: Timagelndex; | Индекс изображения для выбранного элемента

Нетрудно заметить, что свойства Caption, Data и Imagelndex дублируют одоименные свойства класса TListControlltem. Свойство Overlaylmagelndex ссылается на черно-белый растр, который накладывается на изображения Imagelndex И Selectedlmagelndex. Этопозволяет реализовать разного рода анимационные эффекты при выводе изображений.

18.4. КОМПОНЕНТЫ СТРАНИЦЫ SYSTEM

В этом разделе не описаны расположенные на этой странице компоненты TDDEClient, TDDEClientItem, TDDEServer И TDDEServerItem.

Они рассчитаны на поддержку устаревшей технологии межпрограммного обмена данными DDE - Dynamic Data Exchange (динамический обмен данными), которая в современных программах полностью вытеснена технологией OLE.

18.4.1. TTimer - таймер

Компонент TTimer (таймер) служит для отсчета интервалов реального времени. Его свойство interval определяет интервал времени в миллисекундах, который должен пройти от включения таймера до наступления события onTimer. Таймер включается при установке значения True в его свойство Enabled. Раз включенный таймер все время будет возбуждать события onTimer до тех пор, пока его свойство Enabled не примет значения False.

Следует учесть, что в силу специфики реализации аппаратного таймера персонального IBM-совместимого компьютера минимальный реально достижимый интервал отсчета времени не может быть меньше 55 миллисекунд (этот интервал называется тиком). Более того, любой интервал времени, отсчитываемый с помощью таймера, всегда кратен 55 миллисекундам. Чтобы убедиться в этом, проделайте следующий эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера:

  • Положите на пустую форму компонент TTimer. Установите в его свойство interval любое число в диапазоне от 1 до 55 включительно.
  • Напишите такой обработчик события onTimer:

var

BegTime: TDateTime;

procedure TFormI.TimerlTimer(Sender: TObject);

var

h, m, s, ms: Word;// Переменные для декодирования времени

const

MaxTick = 10; // Количество интервалов таймера

begin

if Tag = 0 then

BegTime := Time; // Запоминаем начальное время

Tag := Tag + 1; // Наращиваем счетчик тиков

if Tag = MaxTick then

begin

Timeri.Enabled := False; // Выключаем таймер

// Декодируем значение 1 тика

DecodeTime((Time-BegTime)/MaxTick, h, m, s, ms);

// Выводим результат в заголовке окна:

Caption := IntToStr(s) + ' s ' + IntToStr(ms) + ' ms'

end

end;

Обратите внимание на определение глобальной переменной BegTime в области видимости процедуры TimerlTimer. Необходимость нескольких (MaxTick) срабатываний для точного усреднения результата связана с тем, что системные часы также обновляются каждые 55 мс. После запуска программы в заголовке окна вы увидите строку

О s 55 ms,

в которой указывается, сколько реального времени разделяет два соседних события OnTimer. Этот результат повторится для любых значений константы MaxTick. Если вы установите Interval таймера в диапазоне от 56 до 110 миллисекунд, в заголовке будет указано 110 ms И Т. Д.

18.4.1.1. Мультимедийный таймер

В ряде практически важных применений (при разработке игр, в системах реального времени для управления внешними устройствам и т. п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше. Однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции API.

Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с таким заголовком:

procedure TimeProc(uID, uMsg: UINT; dwUser, dwi, dw2: DWORD);

stdcall;

Здесь uID - идентификатор события таймера (см. ниже); uMsg - не используется; dwuser - произвольное число, передаваемое процедуре в момент запуска таймера; dwi, dw2 - не используются. Запуск таймера реализуется функцией

function timeSetEnet(uDelay, uResolution: UINT;

IpTimeProc:Pointer;

dwUser: DWORD;

fuEvent: UINT): UINT;

stdcall;

external 'winmm.dll';

Здесь uDelay - требуемый период срабатывания таймера (в мс);

uResolution - разрешение таймера; значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение; IpTimeProc - адрес процедуры обратного вызова; dwUser - произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuEvent - параметр, управляющий периодичностью возникновения события таймера: time_oneshot (0) - событие возникает только один раз через uDelay мс; time_periodic (1) - события возникают периодически каждые uDelay мс. При успешном обращении функция возвращает идентификатор события таймера или 0, если обращение было ошибочным.

Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией

function timeKillEvent(uID: UINT): UINT;

stdcall; external 'winmm.dll';

Здесь uID - идентификатор события таймера, полученный с помощью timeSetEvent.

В следующем примере иллюстрируется особенность использования мультимедийного таймера.

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panell: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

procedure bbRunClick(Sender: TObject);

procedure FormActivate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations

end;

var

fmExample: TfmExample;

implementation

{$R *.DFM} // Объявление экспортируемых функций:

function timeSetEvent(uDelay, uReolution: UINT; IpTimeProc:

Pointer;

dwUser: DWORD; fuEvent: UINT): Integer;

stdcall;

external

'winmm';

function timeKillEvent (uiD: UINT): Integer;

stdcall;

external 'winmm' ;

// Объявление глобальных переменных

var

uEventID: UINT; // Идентификатор события таймера

BegTime: TDateTime; // Засечка времени

Counter: Integer; // Счетчик повторений

Delay: Word; // Период срабатывания

procedure ProcTime(uID, msg:. UINT; dwUse, dwi, dw2: DWORD);

stdcall;

// Реакция на срабатывание таймера (процедура обратного вызова)

var

h, m, s, ms: Word; // Переменные для декодирования времени

const

MaxCount = 55; // Количество повторений

begin

timeKillEvent(uEventID); // Останавливаем таймер

Counter := Counter+1; // Наращиваем счетчик

if Counter=MaxCount then // Конец цикла?

begin // - Да: декодируем время

DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms);

fmExample.mmOutput.Lines.Add(

// Сообщаем результат

Format('Задано %s ms. Получено %d ms', [fmExample.edinput.Text, ms ] ) ) ;

fmExample.edinput.Text := ''; // Готовим повторение fmExample.edinput.SetFocus

end

else // - Нет: вновь пускаем таймер

uEventID := timeSetEvent(Delay,0,@ProcTime,0,1);

end;

procedure TfmExample.bbRunClick(Sender: TObject);

// Запускает таймер, edinput содержит требуемый период.

begin

// Проверяем задание периода

if edinput.Text='' then Exit;

try

Delay := StrToInt(edinput.Text)

except

ShowMessage('Ошибка ввода числа');

edinput.SelectAll;

edinput.SetFocus ;

Exit

end;

Counter := 0;

// Сбрасываем счетчик

BegTime := Time;

// Засекаем время

// Запускаем таймер:

uEventID := timeSetEvent(Delay,0,@ProcTime,О,1);

if uEventID=0 then

ShowMessage('Ошибка запуска таймера')

end;

procedure TfmExample.FormActivate(Sender: TObject);

begin

edinput.SetFocus

end;

end.

18.4.2. TPaintBox - окно для рисования

Назначение компонента TPaintBox - дать вам простое окно с канвой для рисования произвольных изображений. Канва содержится в свойстве Canvas компонента, графические инструменты - в свойствах Font, pen и Brush, а собственно рисование осуществляется в обработчике события OnPaint. Особенности использования этих инструментов см. в п. 16.4. Например, следующий обработчик создаст окно, показанное на рис. 18.35:

Рис. 18.35. Пример использования TPaintBox

procedure TFormI.PaintBoxIPaint(Sender: TObject);

var

X, Y: Integer;

begin

with PaintBoxl, Canvas do

begin

Brush.Color := cIRed;

Ellipse (0, 0, Width, Height);

Font.Name := 'Arial';

Font.Size := Height div 5;

Font.Style := [fsBold, fsltalic];

Font.Color := clWhite;

X := (Width - TextWidth('Delphi')) div 2;

Y := (Height - TextHeight('D')) div 2;

TextOut(X, Y, 'Delphi')

end

end;

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

TPaintBox И установите его размеры: Height = 100/ Width = 300.

18.4.3. TMediaPlayer - медиаплейер

Компонент TMediaPlayer представляет собой набор кнопок (рис. 18.36), предназначенных для управления различными мультимедийными устройствами (компакт-дисками, звуковыми картами и т. п.).

Рис. 18.36. Кнопки компонента TMediaPlayer

Если ваш компьютер оснащен звуковой картой, вставьте этот компонент в пустую форму, в его свойство FileName поместите название любого файла с расширением WAV (из каталога с: | winsows | media), установите в свойство Autoopen компонента значение True и запустите программу - после щелчка мышью по кнопке [image] вы услышите звучание выбранного музыкального фрагмента. Как видите, использование компонента предельно просто. Он автоматически распознает тип мультимедийного устройства по расширению файла и берет на себя управление этим устройством. Разумеется, в каждый момент времени компонент может управлять лишь одним устройством, однако с помощью изменения содержимого FileName или явно с помощью свойства DeviceType программа может менять устройство, связанное с компонентом.

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

btStop | Останавливает запись или воспроизведение
btNext | Позиционирует устройство на следующую дорожку или в конец носителя информации, если устройство не имеет дорожек
btPrev | Позиционирует устройство на предыдущую дорожку или в начало носителя информации, если устройство не имеет до
btStep | рожек Позиционирует устройство на один блок кадров ближе к концу носителя информации. Количество кадров в блоке
btBack | содержится в свойстве Frames компонента Позиционирует устройство на один блок кадров ближе к началу носителя информации
btRecord | Начинает запись в носитель информации
btEject | Освобождает устройство от носителя информации
btPlay | Запуск
btPause | Пауза

Свойства компонента:

property AutoEnable: Boolean; | Разрешает/запрещает автоматическое определение доступных и запрещенных кнопок
property AutoOpen: Boolean; | Разрешает/запрещает автоматический старт записи/воспроизведения в момент создания формы, в которую вставлен компонент
property AutoRewind: Boolean; | Разрешает/запрещает автоматическую “перемотку” носителя информации в момент начала записи или воспроизведения. Игнорируется, если устройство имеет дорожки или если установлены значения в свойства startpos и EndPos
TMPDevCaps = (mpCanStep, mpCanEject, mpCanPlay, mpCanRecord, mpUsesWindows); TMPDevCapsSet = set of TMPDevCaps; | Содержит возможности устройства, связанного с компонентом mpCanStep, - может смещать носитель на кадр вперед или назад (устройство типа Animation, AVI Video, Digital Video, Overlay, или VCR); mpCanEject - может автоматически удалять носитель из устройства; mpCanPlay - может воспроизводить информацию; mpCanRecord - может записывать информацию; mpUsesWindows - может использовать окно для отображения процесса
property Capabilities: TMPDevCap sSet; TMPBtnType = (btPlay, btPause, btStop, btNext, btPrev, btStep, btBack, btRecord, btEject); | Кнопки, перечисленные в этом свойстве, будут использовать умалчиваемые цвета, не перечисленные кнопки будут черно-белыми.
TButtonSet = set of TMPBtnType;property ColoredButtons: Tbutton Set; | По умолчанию множество ColoredButtons содержит все кнопки
property DeviceID: Word; | Содержит Windows-идентификатор устройства, связанного с компонентом. Доступно только для чтения
TMPDeviceTypes = (dtAutoSelect, dtAVIVideo, dtCDAudio, dtDAT, dtDigitalVideo, dtMMMovie, dtOther, dtOverlay, dtScanner, dtSequencer, dtVCR, dtVideodisc, dtWaveAudio); property DeviceType: TMPDevice Types; | Содержит тип устройства. Если Device- Type" dtAutoSelect, тип определяется автоматически по расширению файла в свойстве FileName. Каждое мультимедийное устройство в момент инсталляции указывает в файле system. ini, файлы с какими расширениями оно поддерживает
property Display: TWinContrn: | Содержит ссылку на окно для устройств, которые могут его использовать в процессе записи/воспроизведения
property DisplayRect: TRect; | Содержит область окна Display, которое устройство может использовать для отображения процесса записи/воспроизведения. Для улучшения процесса отображения рекомендуется в DisplayRect указывать умалчиваемые размеры(задаются координатами 0,0 для правого нижнего угла)
property EnabledButtons: TButtonSet; | Содержит множество доступных кнопок (тип TButtonSet описан выше для свойства ColoredButtons)
property EndPos: Longint; | Содержит положение носителя информации в момент прекращения записи или воспроизведения. Переустановка значения приводит к повторению записи или воспроизведения
property Error: Longint; | Содержит код ошибки. Доступно только для чтения
property ErrorMessage: String; | Содержит сообщение об ошибке. Доступно только для чтения
property FileName: String; | Содержит имя читаемого или записываемого файла
property Frames: Longing; | Определяет количество кадров, пропускаемых при выполнении методов step или Back
property Length: Longint; | Содержит текущую длину носителя информации. Доступно только для чтения
TMPModes = (mpNotReady,-ipStopped, mpPlaying, mpRecord- ing, mpSeeking, mpPaused, -npOpen) ; property Mode: TMPModes; | Показывает текущее состояние мультимедийного устройства: mpNotReady - не готово; mpStopped - остановлено; mpPlaying - воспроизводит информацию; mpRecording -записывает информацию; mpSeeking - перемещает носитель информации; mpPaused - Приостановлено; mpOpen - от крыто. Доступно только для чтения
property Notify: Boolean; PNotifyValues = (nvSuccessful, Superseded, nvAborted, nvFailre); | Содержит True, если методы Back, Close, Eject, Next, Open, Pause, PauseOnly, Play, Previous, StartRecording, Resume, Rewind, Step и Stop будут создавать событие OnNotify Содержит результат последней команды, создающей событие OnNotify: nvSuccessful - выполнена успешно; nvSuperseded
property NotifyValue: TMPNotify; Values; | заменена другой командой; nvAborted досрочно прервана; nvFailure - завершилась ошибкой. Доступно только для чтения
property Position: Longint; | Содержит текущую позицию носителя информации
property Shareable: Boolean; | Разрешает/запрещает другим компонентам и программам использовать устройство, связанное с данным компонентом
property Start: Longing; | Содержит позицию от начала носителя, с которой начинается запись или воспроизведение. Определяется в момент открытия устройства и доступно только для чтения
property StartPos: Longint; | Содержит текущую позицию от начала носителя, с которого начинается запись или воспроизведение
TMPTimeFormats = (tfMilliseconds, tfHMS, tfMSF, tfFrames,tfSMPTE24, tfSMPTE25, tfSMPTESO,tfSMPTE30Drop, tfBytes, tfSam ples, tfTMSF);property TimeFormat: TMPTimeFor mats; | Определяет формат представления данных в свойствах StartPos, Length, Position, Start и Endpos (см. пояснения ниже)
property TrackLength[TrackNum: Integer]: Longint; | Возвращает длину (в формате TimeFormat) дорожки с индексом TrackNum. Доступно только для чтения
property TrackPosition[TrackNum: Integer]: Longint | Возвращает стартовую позицию (в формате TimeFormat) дорожки С индексом TrackNum. Доступно только для чтения
property Tracks: Longint; | Содержит количество дорожек в открытом устройстве. Доступно только для чтения
property VisibleButtons: TButtonSet; | Содержит множество видимых кнопок (тип TButtonSet описан выше для свойства ColoredButtons)
property Wait: Boolean; | Если содержит True, управление возвращается в программу только после завершения перехода устройства в другое состояние, связанное с методами Back, Close, Eject, Next, Open, Pause, PauseOnly, Play, Previous, StartRecording, Resume, Rewind, Step или Stop

Если свойство AutoEnabie имеет значение True, компонент автоматически по типу устройства или расширению файла и значению свойства Mode определяет, какие кнопки будут доступны пользователю. Если AutoEnabie=Faise, доступными будут только те кнопки, которые входят в множество EnabiedButtons. Множество Enabied-

Buttons игнорируется, если AutoEnable=True.

Свойство AutoRewind игнорируется, если устройство имеет дорожки или если процесс записи/воспроизведения вызван изменением значения в свойстве StartPos или Endpos.

Положение носителя В свойствах StartPos, Length, Position, Start и EndPos В Зависимости от значения свойства TimeFormat должно интерпретироваться следующим образом (отсчет ведется от начала носителя информации):

tfMilliseconds | Миллисекунды в диапазоне от 0 до 4 294 967 295 Часы, минуты, секунды.
tfHMS | Занимают в порядке перечисления по одному байту начиная с младшего. Старшин байт Longint не используется
tfMSF | Минуты, секунды и кадры. Занимают в порядке перечисления по одному байту начиная с младшего. Старший байт Longint не используется
tfFrames | Кадры в диапазоне от 0 до 4 294 967 295
tfSMPTE24 | Часы, минуты. секунды и количество блоков по 24 кадра в секунду. Занимают в порядке перечисления по одному байту начиная с младшего
tfSMPTE25 | Часы, минуты, секунды и количество блоков по 25 кадров в секунду
tfSMPTE30 | Часы, минуты, секунды и количество блоков по 30 кадров в секунду
tfSMPTE30Drop | Часы, минуты, секунды и количество пропущенных блоков по 30 кадров в секунду
tfBytes | Байты в диапазоне от 0 до 4 294 967 295
tfSamples | Количество условных блоков информации в диапазоне от 0 до 4294967295
tfTMSF | Дорожки, минуты, секунды и кадры. Занимают в порядке перечисления по одному байту начиная с младшего

Свойство TimeFormat недоступно из окна Инспектора объектов и устанавливается автоматически при открытии мультимедийного устройства методом open. В связи с этим значения в свойствах StartPos, Position и EndPos могут устанавливаться, а в свойствах Length, Start, TrackLength И TrackPosition Становятся действительными только после вызова СPеn.

Методы компонента:

procedure AutoButtonSet(Btn: TMPBtnType); dynamic; | Реализует разрешение/запрещение кнопок компонента при установке в свойство AutoEnable значения True Перемещает текущую позицию в носителе информации назад на количество кадров, содержащихся В свойстве Frames
procedure Back; | По умолчанию вызывает обработчик события OnClick.
procedure Click(Button: TMPBtnType; var DoDefault: eoolean); dynamic ; | Предназначен для перекрытия в потомках
procedure Close; | Закрывает связанное с компонентом устройство
procedure DoNotify; dynamic; | По умолчанию вызывает обработчик события OnNotify. Предназначен для перекрытия в потомках
procedure Eject; | Выгружает носитель информации из устройства
procedure MMNotify(var Message: TMessage); message :MCINOTIFY; | Реализует отклик на сообщение Windows мм mcinotify. По умолчанию вызывает метод DoNotify
procedure Next; | Перемещает текущую позицию носителя на начало следующей дорожки, а если устройство не имеет дорожек - в конец носителя
procedure Notification ( AComponent: TComponent; Operation: TOperation); override; | Извещает компонент AComponent о том, что завершилась операция Operation
procedure Open; | Открывает мультимедийное устройство
procedure Pause; | Приостанавливает процесс воспроизведения или записи информации. Если устройство уже было приостановлено, восстанавливает процесс с помощью вызова метода Resume
procedure PauseOnly; | Приостанавливает процесс воспроизведения или записи информации. Если устройство уже было приостановлено, ничего не делает
procedure Play; | Начинает воспроизведение с позиции StartPos, а если это свойство не установлено - с текущей позиции position
procedure PostClick (Button: TMPBtnType); dynamic; | Реализует отклик на событие OnPostClick. По умолчанию вызывает соответствующий обработчик события
procedure Previous; | Перемещает текущую позицию носителя на начало предыдущей дорожки, а если устройство не имеет дорожек - в начало носителя
procedure Resume; | Восстанавливает процесс записи или воспроизведения. Вызывается при повторном щелчке по кнопке btPause
procedure Rewind; | Перемещает текущую позицию носителя к положению, определяемому свойством Start
procedure Save; | Сохраняет информацию в файле с именем FileName
procedure StartRecording; | Начинает запись информации с текущей позиции или позиции StartPos
procedure Step; | Перемещает текущую позицию в носителе информации вперед на количество кадров, содержащихся В свойстве Frames
procedure Stop; | Прекращает запись или воспроизведение информации
Для компонента определены следующие события:
EMPNotify = procedure (Sender: TObject; Button: TMPBtnType; var DoDefault: Boolean) of object; property OnClick: EMPNotify; | Возникает после щелчка по кнопке Button компонента. Если DoDefault=True, вызывается метод, связанный с этой кнопкой
property OnNotify: TNotifу Event; | Возникает после завершения вызова одного из методов Back, Close, Eject, Next, Open, Pause, PauseOnly, Play, Previous, Resume, Rewind, StartRecording, Step или Stop,если свойство Notify имеет значение True
EMPPostNotify = procedure (Sender: TObject; Button: TMPBtnType) of object; property OnPostClick: EMPPost-Notify; | Если wait=True, событие OnPostClick возникает только после завершения соответствующей операции, вызванной событием OnClick, в противном случае - немедленно после OnClick

Иногда требуется воспроизвести короткий звуковой сигнал для привлечения внимания пользователя программы - например, после завершения длительного , по времени процесса- обновления данных. Стандартная для Delphi процедура без параметров Веер извлекает сигнал из системного динамика только в том случае, если ПК не оснащен звуковой картой. С другой стороны, мощные возможности компонента TMediaPlayer кажутся излишними для решения этой задачи на ПК, оснащенных звуковой картой. В этом случае может пригодиться API-функция MessageBeep, с помощью которой Windows 32 озвучивает стандартниге диалоговые окна. Единственным параметром обращения к ней является один из следующих идентификаторов стандартного звука: mb_IconAsfcerisk, mb_IconExcla-mation, mb_IconHand, mb IconQuestion, mb Ok. Обращение с параметром $ffffffff игнорирует звуковую карту и извлекает “бип” из системного динамика. Сразу после обращения функция возвращает управление программе и воспроизводит звук асинхронно.

Значительно более богатыми возможностями в этом отношении имеет, API-функция PlaySound, которая способна воспроизводить любые звуковые клипы.

Заголовок функции:

function PlaySound(pszSound: PChar; hmod: HINST;

fdw.Sound: Cardinal): .Boolean;

3десь pszSound - имя воспроизводимого файла; hmod - указывает Экземпляр программы, если звук берется из ресурсного файла ( в противном случае содержит 0); fdwSound - параметр, уточняющий воспроизведение. Его значениями могут быть: snd_async звук воспроизводится асинхронно (функция возвращает управление сразу после обращения к ней); end loop звук воспроизводится постоянно до тех пор, пока не будет .вызвана та же функция с параметром pszSound=0;

SND_NOSTOP функция пытается воспроизвести звук, если устройство не занято; в противном случае не воспроизводит ничего; если устройство занято, но этот параметр не указан, функция прервет воспроизведение старого звука и начнет воспроизведение нового;

SND_NOWAIT если устройство занято, функция не будет воспроизводить новый звук;

SND_PORGE. прекращает, воспроизведение всех звуков для данной задачи;

SND SYNC воспроизводит звук синхронно и возвращает управление только после полного его воспроизведения.

18.4.4. TOleContainer - контейнер объектов OLE

Компонент является удобным контейнером для размещения связанного или внедренного OLE-объекта. Такие объекты (таблицы, картинки, фрагменты текста и т. п.) на форме Delphi-программы выглядят как обычно или заменяются пиктограммами. Замечательной особенностью OLE-объекта является то, что его активизация (обычно с помощью двойного щелчка мышью) приводит к активизации связанной с объектом программы, которая называется OLE-сервером и которая после загрузки показывает на экране свое окно и предоставляет пользователю средства редактирования объекта.

Типичными OLE-серверами являются такие системные утилиты, как Paint и Notepad, текстовый процессор Word, табличный процессор Excel и др.

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

  • Создайте новое приложение с пустой формой и вставьте в нее компонент TOleContainer. Установите в его свойство Align значение alСlient.
  • Сохраните проект вместе с формой в каком-либо каталоге и скопируйте средствами Windows в этот же каталог файл Athena, bmp, который находится В каталоге Images | Splash | 16color[См. замечание в начале главы.].
  • Напишите такой обработчик события OnCreate для формы Form1:

procedure TFormI.FormCreate(Sender: TObject);

begin

{Поскольку в следующем операторе имя файла указано без маршрута поиска, файл 'athena.bmp' должен находиться в каталоге запуска программы!}

OleContainerI.CreateObjectFromFile('athena.bmp',False)

end;

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

Рис. 18.37. Пример связывания OLE-обьекта с сервером Paint

Теперь вы можете использовать средства графического редактора Paint для редактирования изображения. Чтобы завершить редактирование, нажмите Esc - окно примет первоначальный вид, но изображение в нем сохранит все внесенные в него изменения.

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

Компонент может создавать как связанные, так и внедренные объекты и открывать окно сервера как в отдельном окне, так и в окне содержащей его программы. В последнем случае при запуске сервера происходит автоматическая замена опций главного меню программы с групповыми индексами 1, 3 и 5 (если они есть) на опции главного меню сервера, а также вставка в окно программы инструментальных панелей сервера. Если компонент открывается в окне программы, его свойство Align (или такое же

свойство панели, на которую он помещен) должно иметь значение alClient.

Свойства компонента:

property AllowActiveDoc: Boolean; | Разрешает/запрещает компоненту использовать специализированный интерфейс для документов loleDocumentSite
property AllowInPlace: Boolean; | Определяет способ размещения OLE- сервера. Если имеет значение True и свойство Icon=False, окно сервера с редактируемым объектом размещается в пределах размера компонента, в противном случае создается отдельное окно для сервера (независимо от значения icon)
TAutoActivate = (aaManual, aaGet- Focus, aaDoubleClick) ; property AutoActivate: TAutoActivate; | Определяет способ активизации OLE- объекта: aaManual - активизируется путем программного вызова метода doVerb(ovShow);aaGetFocus - активизирует ся при получении фокуса ввода; aaDouble Click - активизируется при двойном щелчке мышью
property AutoVerbMenu: Boolean; | Если имеет значение True, для компонента автоматически создается вспомогательное меню, содержащее доступные команды OLE-сервера
TBorderStyle = bsNone..bsSingle; property BorderStyle: Tborder Style; | Определяет стиль рамки: bsNone - компонент не имеет рамки; bsSingle - компонент имеет рамку толщиной 1 пиксель
property CanPaste: Boolean; | Содержит True, если буфер clipboard содержит OLE-объект (или связь с ним), который(ую) можно вставить в компонент. Доступно только для чтения
property CopyOnSave: Boolean; | Если содержит True, перед сохранением объекта в файле или потоке он предварительно сжимается для экономии размеров файла (потока). Если в компоненте размещен очень большой объект и динамической памяти может не хватить для создания временной копии сжатого объекта, в свойство следует поместить значение False
property Iconic: Boolean; | Содержит True, если объект будет заменяться пиктограммой OLE-сервера, и False если объект изображается так, как он будет виден в окне сервера
property Linked: Boolean; | Содержит True, если объект связан с программой, и False - если является ее частью (внедрен в нее). Доступно только для чтения
property Modified: Boolean- | Содержит True, если объект был изменен (в том числе заменен другим или уничтожен)
Property Newlnserted: Boolean; | Содержит True, если объект был заново создан с помощью метода insertOb jectDialog. В этом случае вызывайте DoVerb (ovShow), чтобы показать объект в окне сервера. Доступно только для чтения
property ObjectVerbs: TStrings; | Содержит список команд, которые программа может посылать серверу. Доступно только для чтения
property OldStreamFormat: Boolean; | В это свойство следует поместить True перед чтением объекта из файла или потока, если объект был создан версией Delphi 1
property OleClassName: String; | Содержит имя класса, под которым был зарегистрирован OLE-сервер в реестре Windows. Доступно только для чтения
property OleObject: Variant; | Содержит ссылку на OLE-объект
property OleObjectInterface: IQleObject; | Предоставляет доступ к OLE-объекту через интерфейс сloleObject
property PrimaryVerb: Integer; TSizeMode = (smClip, smCenter, smScale, smStretch, smAutoSize) ; property SizeMode: TSizeMode; | Содержит индекс умалчиваемой команды для OLE-объектаОпределяет способ размещения OLEобъекта в контейнере: smClip - отсекаются любые части объекта, выходящие за границы контейнера; smCenter - центрируется в контейнере; smScale - масштабируется так, чтобы целиком заполнить границы контейнера; smStretch - масштабируется так, чтобы не нарушились пропорции объекта; smAutoSize - изменяются границы контейнера, чтобы полностью отобразить объект без искажений
property SourceDoc: String; | Содержит имя файла для связанного объекта
type TObjectState = (osEmpty, osLoaded, osRunning, osOpen, os- InPlaceActive, osUIActive); property State: TObjectState; | Определяет состояние OLE-объекта: osEmpty - контейнер не содержит объект; osLoaded - объект загружен, но его сервер не запущен; osRuftning - запущен OLE-сервер; osOpen - объект открыт в отдельном окне; osinPlaceActive - объект открыт в окне контейнера, но сервер еще не заменил опции меню и инструментальные панели программы на свои (промежуточное состояние перед osOIActive); osUIActive -объект открыт в окне контейнера
property Storagelnterface: IStorage; | Открывает доступ к интерфейсу низкого уровня istorage, использующемуся для обращения к API OLE
Методы компонента:
function ChangeIconDialog: Boolean; | Создает и показывает диалоговое окно смены пиктограммы, заменяющей изображение объекта. Возвращает True, если пользователь закрыл окно кнопкой ОК
procedure Close; | Закрывает сервер. Все сделанные к этому моменту изменения в объекте автоматически сохраняются
procedure Copy;rocedure CreateLinkToFile(const FileName: String; Iconic: Boolean) ; | Создает связанный объект по имени файла-FileName. Параметр Iconic содержит True, если изображение объекта заменяется пиктограммой
procedure CreateObject(const OleClassName: String; Iconic: Boolean) ; | Создает внедренный объект по имени класса сервера oleClassName. Параметр iconic содержит True, если изображение объекта заменяется пиктограммой
procedure CreateObjectFromFile(const FileName: String;Iconic: Boolean) ; | Создает внедренный объект по имени файла FileName. Параметр iconic содержит True, если изображение объекта заменяется пиктограммой
procedure CreateObjectFromInfo (const Createlnfo: TCreateInfo); | Создает объект по информации, хранящейся в Createlnfo (см.ниже)
procedure DestroyObject; | Уничтожает объект без сохранения сделанных в нем изменений
procedure DoVerb(Verb: Integer); | Требует от объекта выполнить действие с индексом verb из списка objectVerbs
function GetIconMetaPict: HGlobal; | Возвращает указатель на распределенный в куче метафайл для прорисовки пиктограммы объекта на другой канве
function InsertObjectDialog: Boolean; | Создает и показывает диалоговое окно создания объекта. Возвращает True, если пользователь закрыл окно кнопкой OK
procedure LoadFromFile (constFileName: String); | Загружает объект из файла FileName. Если OldStreamFormat содержит True, загружает объект, созданный с помощью Delphi 1
procedure LoadFromStream(Stream:TStream) ; | Загружает объект из потока Stream. Если OldStreamFormat содержит True, загружает объект, созданный с помощью Delphi 1
function ObjectPropertiesDialog:Boolean; | Создает и показывает диалоговое окно изменения свойств объекта. Возвращает True, если пользователь закрыл окно кнопкой OK
procedure Paste; | Создает объект по данным, хранящимся в буфере Сlipboard
function PasteSpecialDialog: Boolean; | Создает и показывает диалоговое окно специальной вставки объекта. Возвращает True, если пользователь закрыл окно кнопкой ОК
procedure Run; | Запускает сервер, но не активизирует объект. Активизация объекта при запущенном сервере происходит значительно быстрее
procedure SaveAsDocument(const FileName: Strings- | Сохраняет объект в файле FileName с ис пользованием формата OLE Document
procedure SaveToFile(const FileName: String) ; | Сохраняет объект в файле FileName. Если OldStreamFormat содержит True, объект может быть загружен программой, созданной с помощью Delphi 1
procedure SaveToStream(Stream:TStream); | Сохраняет объект в потоке Stream. Если OldStreamFormat содержит True, объект может быть загружен программой, созданной с помощью Delphi 1
procedure UpdateObject; | Обновляет объект, вновь загружая его из источника
procedure UpdateVerbs; | Обновляет список Objectverbs (некоторые команды могут его изменить, например, команда Play может заменить Play на Stop)

Метод СreateObjectFrominfo использует запись типа

type

TCreateInfo = record

CreateType: TCreateType;

ShowAsIcon: Boolean;

IconMetaPict: HGlobal;

ClassID: TCLSID;

FileName: String;

DataObject: IDataObject;

end;

type TCreateType = (ctNewObject, ctFromFile, ctLinkToFile, ctFromData, ctLinkFromData) ;

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

CreateType | Тип объекта и его источник: ctNewObject - внедренный объект, заданный идентификатором класса ClassID; ctFromFile - внедренный из файла FileName; ctLinkToFile - связанный С файлом FileName; ctFromData - внедренный по данным DataObject; CtLinkFromData -связанный сданными DataObject
ShowAsIcon | Если True, объект изображается пиктограммой
IconMetaPict | Дескриптор глобальной памяти, в которой распределен метафайл для пиктограммы. Если содержит 0 и showAslcon= True, используется стандартная пиктограмма Идентификатор класса сервера
ClassID FileName | Имя файла
DataObject | Данные для объекта

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

property OnActivate: TNoti-fyEvent; | Возникает сразу после активизации объекта
property OnDeactivate: TNotifyEvent; | Возникает сразу после перехода объекта в неактивное состояние
type TObjectMoveEvent = procedure( OleContainer: TOleCon-tainer; const Bounds: TRect) of object; | Возникает при перемещении или изменении размеров объекта. Параметр Bounds содержит границы объекта в координатах клиентской части контейнера
property On.ObjectMoye: TObjectMoveEvent; property OnResize: TNotifyEvent; | Возникает при изменении размеров окна контейнера

18.5. КОМПОНЕНТЫ СТРАНИЦЫ DIALOGS

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

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

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

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

Открыть файл может вызвать метод Execute Диалога TOpenDialog, обработчик нажатия инструментальной кнопки сохранить может вызвать такой же метод у компонента TSaveDialog и т. д. Только после обращения к Execute на экране появляется соответствующее диалоговое окно. Окно диалога является модальным окном, поэтому сразу после обращения к Execute дальнейшее выполнение программы приостанавливается до тех пор, пока пользователь не закроет окно. Поскольку Execute - логическая функция, она возвращает в программу True, если результат диалога с пользователем был успешным.

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

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

procedure TForml.ButtonlClick(Sender: TObject);

var

S: String;

F: TextFile;

begin

// Настраиваем диалог на отбор текстовых файлов:

OpenDialogI.Filter :== 'Текстовые файлы|*.txt|'+ 'Файлы Паскаля|*.pas';

// Выполняем диалог и анализируем его результат if OpenDialogI.Execute and FileExists(OpenDialogI.FileName) then

begin

// Результат успешный - пользователь выбрал файл

// Открываем файл

AssignFile(F, OpenDialogI.FileName) ;

Reset(F);

Memol.Lines.Clear; // Очищаем редактор

while not EOF(F) do // и наполняем его

begin

ReadLn(F, S) ;

Memol.Lines.Add(S)

end;

CloseFile(F)

end

end;

18.5.1. TOpenDialog и TSaveDialog - диалоги открытия и сохранения файлов

Эти компоненты имеют идентичные свойства и поэтому рассматриваются вместе. Пример окна TOpenDialog показан на рис. 18.38.

Свойство FileName: string содержит маршрут поиска и выбранный файл при успешном завершении диалога. Программа может использовать это свойство для доступа к файлу с целью читать из него данные (TOpenDialog) или записывать в него (TSaveDialog). Замечу, что пользователь может ввести произвольное имя и, следовательно, указать несуществующий файл. Для записи это не имеет значения, но при чтении отсутствие файла может привести к краху программы. Чтобы избежать этого, можно проверить существование файла глобальной функцией FileExists, как это сделано в предыдущем примере, или использовать механизм обработки исключительных ситуаций.

Рис. 18.38. Стандартное окно TOpenDialog

Свойство Filter: string используется для фильтрации (отбора) файлов, показываемых в диалоговом окне. Это свойство можно устанавливать с помощью специального редактора на этапе конструирования формы или программно, как это сделано в предыдущем примере. Для доступа к редактору достаточно щелкнуть по кнопке в строке Filter окна Инспектора объектов. При программном вводе фильтры задаются одной длинной строкой, в которой символы “|” служат для разделения фильтров друг от друга, а также для разделения описания фильтруемых файлов от соответствующей маски выбора. Например, оператор

OpenDialogI.Filter := 'Текстовые файлы|*.txt| Файлы Паскаля|*.раs';

задает две маски - для отбора файлов с расширениями раs и TXT.

Установить начальный каталог позволяет свойство InitialDir string. Например:

OpenDialogI.InitiaiDir := 'c:\program files\borland\delphi5\source';

С помощью свойства DefaultExt: String [ 3 ] формируется полное имя файла, если при ручном вводе пользователь не указал расширение. В этом случае к имени файла прибавляется разделительная точка и содержимое этого свойства.

В диалоговом окне для ручного ввода предусмотрен элемент TEdit, который при желании можно заменить на TCоmbовох. Для этого необходимо свойству FileEditStyle придать значение fsComboBox вместо умалчиваемого fsEdit. Если выбран комбинированный список, с ним можно связать протокол выбора имен. Для этого используется свойство HistoryList: TStrings, содержимое которого будет появляться в выпадающем списке. Этот список не пополняется автоматически, поэтому за его содержимым должна следить программа. Например:

if OpenDialogI.Execute then

begin

HistoryList.Add(OpenDialogI.FileName);

end;

Настройка диалога может варьироваться с помощью свойства

TOpenOption = (ofReadOnly, ofOverwritePrompt, ofHideReadOnly, ofNoChangeDir, ofShowHelp, ofNoValidate, ofAllowMultiSelect, ofExtensionDifferent, ofPathMustExist, ofFileMustExist, ofCre-atePrompt, ofShareAware, ofNoReadOnlyReturn, ofNoTestFileCre-ate, ofNoNetworkButton, ofNoLongNames, ofOldStyleDialog, of-NoDereferenceLinks) ;

TOpenOptions = set of TOpenOption;

property Options: TOpenOptions;

Значения этого свойства имеют следующий смысл:

ofReadOnly | Устанавливает переключатель Только для чтения
ofOverwritePrompt | Требует согласия пользователя при записи в существующий файл
ofHideReadOnly | Прячет переключатель Только для чтения
OfNoChangeDir | Запрещает смену каталога
ofShowHelp | Включает в окно кнопку Help
ofNoValidate | Запрещает автоматическую проверку правильности набираемых в имени файла символов
OfAllowMultiSelect | Разрешает множественный выбор файлов
ofExtensionDifferent | При завершении диалога наличие этого значения в свойстве options говорит о том, что пользователь ввел расширение, отличающееся от умалчиваемого
ofPathMustExist | Разрешает указывать файлы только из существующих каталогов
ofFileMustExist | Разрешает указывать только существующие файлы
ofCreatePrompt | Требует подтверждения для создания несуществующего файла
ofShareAware | Разрешает выбирать файлы, используемые другими параллельно выполняемыми программами
OfNoReadOnlyReturn | Запрещает выбор файлов, имеющих атрибут Только для чтения
ofNoTestFileCreate | Запрещает проверку доступности сетевого или локального диска
OfNoNetworkButton | Запрещает вставку кнопки для создания сетевого диска
ofNoLongNames | Запрещает использование длинных имен файлов
ofOldStyleDialog | Создает диалог в стиле Windows 3-х

Если разрешен множественный выбор, доступ к выбранным именам можно получить в свойстве Files: strings.

18.5.2. TOpenPictureDialog и TSavePictureDialog - диалоги открытия и сохранения изображений

Специализированные диалоги для открытия и сохранения графических файлов TOpenPictureDialog И TSavePictureDialog отличаются от

TOpenDialog и TSaveDialog двумя обстоятельствами. Во-первых, в них предусмотрены стандартные фильтры для выбора графических файлов (с расширениями bmp, ico, wmf и emf) . Во-вторых, в окна диалога включены панели для предварительного просмотра выбираемого файла.

На рис. 18.39 показано стандартное окно компонента TOpenPictureDialog.

18.5.3. TFontDialog - диалог выбора шрифта

Компонент TFontDialog создает и обслуживает стандартное окно выбора шрифта (рис. 18.41). Свойство

TFontDialogDevice = (fdScreen, fdPrinter, fdBoth) ;

property Device: TFontDialogDevice;

определяет тип устройства, для которого выбирается шрифт:

fdScreen - экран; fdPrinter - принтер; fdBoth - шрифты, поддерживаемые и экраном, и принтером.

(Левая часть окна)

(Правая часть окно)

Рис. 18.39. Стандартное окно TopenPictureDialog

Рис. 18.40. Стандартное окно TFontDialog

Диапазон возможных значений размеров шрифтов определяется свойствами MinFontSize и MaxFontSize. Значения этих свойств задаются в пунктах (1 пункт равен1/72 дюйма, что приблизительно равно 0,36 мм). Если свойства содержат 0, ограничения на размер шрифта отсутствуют.

Свойство

TFontDialogOption = (fdAnsiOnly, fdTrueTypeOnly, fdEffects, fdFixedPitchOnly, fdForceFontExist, fdNoFaceSel, fdNoOEMFonts, fdNoSimulations, fdNoSizeSel, fdNoStyleSel, fdNoVectorFonts, fdShowHelp, fdWysiwyg, fdLimitSize, fdScalableOnly, fdApply-Button);

TFontDialogOptions = set of TFontDialogOption;

property Options: TFontDialogOptions;

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

fdAnsiOnly | Показывает только шрифты с набором символов Windows
fdTrueTypeOnly | Показывает только TrueType-шрифты
fdEffects | Включает в окно переключатели Подчеркнутый и Зачеркнутый, а также список выбора цвета шрифта только моноширинные шрифты
fdFixedPitchOnly | Включает только моноширинные шрифты
fdForceFontExist | Предупреждает о выборе несуществующего шрифта
fdNoFaceSel | Запрещает выделение имени шрифта в момент открытия окна
fdNoOEMFonts | Запрещает выбор MS-DOS-шрифтов
fdNoSimulations | Исключает шрифты, которые синтезируются графическим интерфейсом Windows
fdNoSizeSel | Запрещает выделение размера шрифта в момент открытия окна
fdNoStyleSel | Запрещает выделение стиля шрифта в момент открытия окна
fdNoVectorFonts | Исключает векторные шрифты (шрифты для Windows версии 1.0; используются в плоттерах)
fdShowHelp | Включает в диалоговое окно кнопку Help шрифты, которые поддерживаются и экраном, и принтером
fdWysiwyg | Включает шрифты, которые поддерживаются и экраном, и принтером
fdLimitSize | Включает ограничения на размер шрифта, заданные свойствами MaxFontSize И MinFontSize
fdScalableOnly | Включает только масштабируемые шрифты (векторные и TrueType)
fdApplyButton | Включает в окно кнопку применить

Результат выбора шрифта содержит свойство Font.

18.5.4. TColorDialog - диалог выбора цвета

Компонент создает и обслуживает стандартное диалоговое окно выбора цвета, показанное на рис. 18.42.

Свойства компонента:

property Color: TColor; | Содержит выбранный цвет
property CustomColors: TStrings; | Содержит до 16 цветов, определенных пользователем. Каждая строка имеет такой формат: Со1огх=нннннн, где х - буква от а до р, определяющая номер цвета, нннннн ~ шестнадцатеричное представление цвета в формате rgb
TColorDialogOption = (cdFullOpen, cdPreventFullOpen, cdShowHelp, cdSolidColor, cdAnyColor) ; TColorDialogOptions = set of TColorDialogOption; property Options: TColorDialogOptions; | Определяет настройку окна: cdFullOpen -показывать с развернутым окном выбора цвета пользователя; cdPreventFullOpen -запретить показ окна выбора цвета пользователя; cdShowHelp - включить в окно кнопку Help; cdSolidColor - выбирать ближайший сплошной цвет; cdAnyColor -разрешить выбор не сплошных цветов

Рис. 18.41. Стандартное окно компонента TColorDialog

18.5.5. TPrintDialog - диалог настройки параметров печати

Компонент TPrintDialog создает стандартное диалоговое окно для выбора параметров печати, показанное на рис. 18.42.

Рис. 18.42. Стандартное окно компонента TPrintDialog

Свойства компонента:

property Collate: Boolean; | Если имеет значение True, окно показывается с выбранным переключателем разобрать (collate). Если этот переключатель выбран, печать нескольких копии документа будет идти по копиям: сначала первая копия, затем вторая и т. д., в противном случае - по страницам: сначала все копии первой страницы, затем второй и т. д
property Copies: Integer; | Определяет количество копии (0 - одна копия)
property FromPage: Integer | Определяет начальную страницу печати
property MaxPage: Integers; | Определяет верхнюю границу диапазона страниц для свойств FromPage, ToPage
property MinPage: Integer; | Определяет нижнюю границу диапазона страниц для свойств FromPage, ToPage
TPrintDialogOption = (poPrintToFile, poPageNums, poSe- lection, poWarning, poHelp, poDisablePrintToFile) ; TPrintDialogOptions = set of TPrintDialogOption; property Options: TPrintDialogOptions; | Определяет настройку окна: poPrintToFile - печатать в файл; poPrintToFile - разрешает выбор диапазона страниц; poSelection - разрешает печать выбранного текста; poWarning - предупреждать пользователя о неустановленном принтере; poHelp вставить в окно кнопку Help; poDisablePrintToFile - запрещает печать в файл
TPrintRange = (prAllPages, prSelection, prPageNums) ; property PrintRange: TPrintRange; | Определяет диапазон печатаемых страниц: prAllPages - все страницы; preelection - выделенный фрагмент текста; prPageNums - страницы по номерам
property PrintToFile: Boolean; | Содержит True, если пользователь выбрал печать в файл
property ToPage: Integer; | Определяет конечную страницу печати

18.5.6. TPrinterSetupDialog - диалог настройки параметров принтера

Компонент создает окно настройки параметров принтера, вид которого зависит от типа принтера. Этот диалог взаимодействует с драйвером принтера и не возвращает в программу никакой информации, поэтому его метод Execute - процедура, а не функция.

18.5.7. TFindDialog - диалог поиска

Стандартное диалоговое окно компонента TFindDialog используется для поиска фрагмента текста (рис. 18.43).

Свойства компонента:

property FindText: String; | Указывает образец для поиска
property Left: Integer; | Содержит горизонтальную позицию левого верхнего угла места появления окна
property Options: TFindOptions; | Определяет настройку диалога (см. ниже)
property Position: TPoint; | Содержит горизонтальную и вертикальную позицию левого верхнего угла места появления окна
property Top: Integer; | Содержит вертикальную позицию левого верхнего угла места появления окна

Для компонента определен следующий тип, использующийся в свойстве options:

TFindOption = (frDown, frFindNext, frHideMatchCase, frHideWholeWord, frHideUpDown, frMatchCase, frDisableMatchCase, frDisableUpDown, frDisableWholeWord, frReplace, frReplaceAll, frWholeWord, frShowHelp) ;

TFindOptions = set of TFindOption;

Рис. 18.43. Стандартное окно компонента TFindDialog

Его значения имеют следующий смысл:

frDown frFindNext | Устанавливает поиск вперед по тексту
frHideMatchCase | Сообщает программе, что пользователь нажал кнопку Найти далее Убирает выбор в переключателе с учетом регистра
frHideWholeWord | Убирает выбор в переключателе только слово целиком
frHideUpDown | Прячет кнопки выбора направления поиска
frMatchCase | Устанавливает выбор в переключателе с учетом регистра
frDisableMatchCase | Запрещает выбор С учетом регистра
frDisableUpDown | Запрещает выбор направления поиска
frDisableWholeWord | Запрещает выбор только слово целиком
frReplace | Используется в компоненте TReplacteDialog и указывает на необходимость замены текущего выбора
frReplaceAll | Используется в компоненте TreplaceDialog и указывает на необходимость замены всех вхождений образца поиска
frWholeWord | Устанавливает выбор в переключателе Только слово целиком.
frShowHelp | Включает в окно кнопку Help

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

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

procedure TFormI.FindDialoglFind(Sender: TObject);

var

SelPos: Integer;

begin

with TFindDialog(Sender) do

begin

// Ищем фрагмент в тексте

SelPos := Pos(FindText, Memol-Lines.Text);

if SelPos > 0 then

begin // Фрагмент найден - выделяем его

Memol.SelStart := SelPos - 1;

Memol.SelLength := Length(FindText);

end

else

ShowMessage ('Текст "' + FindText + '"не найден')

end;

end;

18.5.8. TReplaceDialog - диалог поиска и замены

Компонент TReplaceDialog создает и обслуживает окно поиска и замены текстового фрагмента (см. рис. 18.44).

Рис. 18.44. Стандартное окно компонента TReplaceDialog

Класс TReplaceDialog является прямым потомком класса TFindDialog и наследует от него большинство свойств. Дополнительно в компоненте определено свойство RepiaceText: string, в котором содержится текст замены, и событие OnRepiace, которое возникает

При нажатии кнопки заменить или заменить все.

18.6. КОМПОНЕНТЫ СТРАНИЦЫ WIN3.1

Многие компоненты страницы win3.1 имеют более совершенные аналоги в виде компонентов страницы Win32. Если вы работаете с windows 32, рекомендуется использовать вместо компонентов Win 3.1 компоненты Win32.

18.6.1. TTabSet - набор закладок

Компонент TTabSet представляет собой набор закладок. В отличие от компонента TTabControl он не может служить контейнером для размещения других компонентов и поэтому обычно работает совместно с каким-либо компонентом-контейнером (чаще всего - с TNotebook).

Свойства компонента:

property AutoScroll: Boolean; | Разрешает/запрещает появление стрелок прокрутки, если закладки не умещаются целиком в границах компонента
property BackgroundColor: TColor; | Определяет цвет фона закладок
property DitherBackground: Boolean; | Если содержит True, уменьшается насыщенность цвета BackgroundColor
property EndMargin: Integer; | Отступ закладок от правой границы компонента
property Firstlndex: Integer; | Содержит индекс самой левой закладки, целиком видимой на экране
property SelectedColor: TColor; | Содержит цвет выбранной закладки (обычно совпадает с цветом связанного компонента-контейнера)
property StartMargin: Integer; TTabStyle = (tsStandard, tsOwnerDraw); | Отступ закладок от левой границы компонента
property Style: TTabStyle; | Определяет способ прорисовки закладок: tsStandard - автоматический; tsOwnerDraw - программный
property TabHeight: Integer, | Определяет высоту закладок в пикселях
property Tablndex: Integer; | Содержит индекс выбранной закладки (индексация начинается с 0)
property UnselectedColor: TColor; | Определяет цвет невыбранных закладок
property VisibleTabs: Integer; | Содержит количество закладок, целиком видимых в границах компонента
Методы компонента:
function ItemAtPos(Pos: TPoint): Integer; | Возвращает индекс закладки, которой принадлежит точка Pos
function ItemRect(Item: Integer): TRect; | Возвращает прямоугольник прорисовки закладки с индексом item
procedure SelectNext(Direction: Boolean) ; | Смещает выбор к соседней закладке: Direction - направление смещения (True - вправо)
Для компонента определены следующие события:
TTabChangEvent = procedure (Sender: TObject; NewTab: Integer; var AllowChange: Boolean) of object; | Возникает перед сменой выбранной закладки: NewTab - индекс закладки, которая будет выбрана. В параметре AllowChange обработчик должен разрешить или запретить выбор закладки
property OnChange: TTabChangingEvent; TDrawTabEvent = procedure(Sender: TObject; TabCanvas: TCanvas; R: TRect; Index: Integer; Selected: Boolean) of object; | Возникает при необходимости прорисовать закладку с индексом index, если она имеет стиль tsOwnerDraw: TabCanvas - канва для прорисовки; Selected - содержит True для выбранной закладки
property OnDrawTab: TDrawTabEvent; TMeasureTabEvent = procedure(Sender: TObject; Index: Integer; var TabWidth: Integer) of object-property OnMeasureTab: TMeasureTabEvent; | Возникает перед прорисовкой закладки с индексом index, если она имеет стиль tsOwnerDraw. В параметре TabWidth обработчик должен вернуть ширину закладки

Как уже говорилось, часто компонент TTabSet связывается с компонентом TNotebook для организации многостраничного блокнота с закладками. В этом случае в обработчике событий OnChange или

OnClick компонента следует предусмотреть изменение текущей страницы блокнота. Например:

procedure TFormI.TabSetlChange(Sender: TObject;

NewTab: Integer;

var AllowChange: Boolean);

begin

AllowChange := True; // Разрешаем смену закладки

Notebookl.Pagelndex := NewTab // Меняем страницу блокнота

end;

18.6.2. TNotebook - набор страниц

Многостраничный контейнер TNotebook обычно работает в паре с TTabSet и предназначен для размещения других компонентов на нескольких перекрывающих друг друга страницах (панелях).

Свойства компонента:

property ActivePage: Strings; | Определяет имя верхней страницы блокнота
property Pagelndex: Integers; | Содержит индекс верхней страницы блокнота
property Pages: TStrings; | Содержит набор строк с именами страниц

Ключевым свойством является Pages. С помощью его методов Add, Delete, Find и т. д. можно добавлять и удалять страницы, отыскивать Нужную и т. д. (см. класс TStrings).

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

property OnChange: TNotifyEvent;

которое возникает при смене страницы.

18.6.3. TTabbedNotebook - набор страниц с закладками

Компонент TTabbedNotebook представляет собой объединение TTabSet и TNotebook в готовый многостраничный блокнот с заклад-ками. В отличие от TTabSet закладки в TabbedNotebook могут располагаться в несколько рядов.

Свойства ActivePage, Pagelndex И Pages компонента совпадают c одноименными свойствами компонента TNotebook. Свойство property TabsPerRow: Integer; определяет количество закладок в одном ряду.

С помощью метода function GetIndexForPage(const PageName: String): Integer; программа может получить индекс страницы по ее имени PageName.

18.6.4. TOutLine - просмотр иерархических структур

Подобно компоненту TTreeview страницы Win32 компонент Tout-Line предназначен для отображения иерархических структур данных (каталогов, например). Элементом отображения является узел. Каждый узел имеет родительский узел и дочерние узлы. Исключение составляет узел самого верхнего уровня (он не имеет родительского узла) и узлы самых нижних уровней (они не имеют дочерних узлов),

Для каждого узла компонент создает объект класса TOutLineNode. Его свойства:

property Data: Pointers; | Указатель на связанные с узлом данные
property Expanded: Boolean; | Если содержит True, с узлом связан раскрытый список дочерних узлов
property FullPath: String; | Содержит полное имя узла со списком имен всех родительских узлов, разделенных символом ItemSeparator объекта ToutLine
property Hasltems: Boolean; | Содержит True, если с узлом связаны дочерние узлы
property Index: Longint; | Содержит уникальный идентификатор узла в компоненте ToutLine
property IsVisible: Boolean; | Содержит True, если узел виден в окне компонента ToutLine
property Level: Integer; | Определяет уровень иерархии узла. Узел самого верхнего уровня имеет уровень 0, его дочерние узлы - уровень 1, их дочерние узлы - уровень 2 и т. д.
property Text: String; | Определяет имя узла
property Topltem: Longint; | Содержит индекс своего родительского узла первого уровня (родительский узел нулевого уровня не виден в компоненте TOutLine)

Методы класса TOutLineNode:

procedure ChangeLevelBy(Value: TChangeRange) ; | Перемещает уровень узла на один вверх (Value=-1) или вниз(Value=+l) Закрывает список дочерних узлов
procedure Collapse; procedure FullExpand; | Раскрывает список всех своих дочерних узлов на всех уровнях
function GetFirstChild: TTreeNode; | Возвращает ссылку на первый дочерний узел или nil, если нет дочерних узлов
function GetPrevChild(Value: Longint): Longint; | Возвращает индекс узла, предшествующего узлу Value
TAttachMode = (oaAdd, oaAddChild, oalnsert) ; procedure MoveTo(Destination: Longint; AttachMode: TAttachMode) ; | Перемещает узел со всеми потомками в положение, определяемое индексом Destination и режимом AttachMode: oaAdd -добавить последним на том же уровне, что и Destination; oaAddChild -добавить последним к потомкам узла Destination; oalnsert - вставить на место Destination, который смещается ниже него на том же уровне

Свойства компонента TOutLine:

property Canvas: TCanvas; | Определяет канву для прорисовки компонента
property itemHeight: Integer; | Определяет высоту узлов
property Items[Index: Longint]: TOutlineNode; | Открывает доступ к узлу по его индексу
property ItemCount: Longing; | Содержит общее количество узлов
property Lines: TStrings; | Содержит список всех узлов
TOutlineOption = (ooDrawTreeRoot, ooDrawFocusRect, ooStretchBitmaps) ; TOutlineOptions = set of Tout lineOption; property Options: TOutlineOptions; | Определяет атрибуты прорисовки: ooDraw TreeRoot - соединять линии узлов верхнего уровня; ooDrawFocusRect - выделять пунктиром сфокусированный узел; oostretch Bitmaps - масштабировать пиктограммы
TOutlineStyle = (osText, osPlus- MinusText, osPictureText, osPlus- MinusPictureText, osTreeText, osTreePictureText) ; property OutlineStyle: TOutlineStyle; | Задает стиль прорисовки: osText - выводится только текст(без линий); osPlusMinusText - текст и пиктограммы PicturePlus/PictureMinus;osPictureText -текст и пиктограммы PictureOpen/PictureClosed/PictureLeaf;osPlusMinus-PictureText - объединяет два предыдущих стиля; osTreeText - текст и линии связи между узлами; osTreePictureText - объединяет osTreeText и osPictureText
property PictureClosed: TBitmap; | Содержит пиктограмму узла с закрытым списком дочерних узлов. По умолчанию -закрытая папка
property PictureMinus: TBitmap; | Содержит пиктограмму узла с закрытым списком дочерних узлов. По умолчанию - пиктограмма со знаком минус
property PictureOpen: TBitmap; | Содержит пиктограмму узла с открытым списком дочерних узлов. По умолчанию - открытая папка
property PictureLeaf: TBitmap; | Содержит пиктограмму узла, не имеющего дочерних узлов. По умолчанию - пиктограмма документа
property PicturePlus: TBitmap; | Содержит пиктограмму узла с открытым списком дочерних узлов. По умолчанию - пиктограмма со знаком плюс
property Row: Longint; | Содержит индекс сфокусированного узла
property ScrollBars: TScroll-Style; | Определяет наличие полос прокрутки: ssNone - нет полос; ssHorizontal - горизонтальная полоса; ssVertical - вертикальная полоса; ssBoth - и горизонтальная, и вертикальная полоса

Методы компонента:

function Add(Index: Longint; const Text: String): Longint; | Добавляет узел с именем Text как последний на тот же уровень иерархии, что и узел index, и возвращает его индекс (режим oaAdd)
function AddChild(Index: Longint; const Text: String): Longing; | Добавляет дочерний узел с именем Text к узлу index и возвращает его индекс (режим oaAddChild)
function AddChildObject(Index: Longint; const Text: String; const Data: Pointer): Longint; | Добавляет дочерний узел с именем Text и данными Data к узлу index и возвращает его индекс
function AddObject(Index: Long int; const Text: String; const Data: Pointer): Longint; | Добавляет узел с именем Text и с данными Data как последний на тот же уровень иерархии, что и узел index, и возвращает его индекс
procedure BeginUpdate ; | Блокирует перерисовку компонента перед вставкой/удалением нескольких узлов
procedure Clear; | Удаляет все узлы компонента
procedure EndUpdate; | Разрешает перерисовку компонента после вставки/удаления нескольких узлов
procedure FullCollapse; | Закрывает списки всех дочерних узлов на всех уровнях
procedure FullExpand; | Раскрывает списки всех дочерних узлов на всех уровнях
function GetDataItem(Value: Pointer): Longint; | Возвращает индекс узла по его данным
function GetItem(X, Y: Integer): Longint; | Возвращает индекс узла по его координатам
function GetTextItem(Value: String) : Longing; | Возвращает индекс узла по его имени
function Insert(Index: Longint; const Text: String): Longing; | Вставляет узел с именем Text на место узла Index(режим oalnsert)
function InsertObj ect(Index: Longint; const Text: String; const Data: Pointer): Longint; | Вставляет узел с именем Text и данными Data на место узла index (режим oalnsert)
procedure LoadFromFile(const FileName: String); | Загружает содержимое компонента из файла с именем FileName
procedure LoadFromFile(const FileName: Strings; | Сохраняет содержимое компонента в файле
procedure SetUpdateState(Value: Boolean); | С именем FileName если Value=True, соответствует BeginUp date, в противном случае - EndUpdate

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

EOutlineChange = procedure (Sender: TObject; Index: Longint) of object; property OnCollapse: EOut lineChange;Возникает при закрытии списка дочерних узлов узда Index |
TOwnerDrawState = set of (odSelected, odGrayed, odDisabled, odChecked, odFocused) ; TListBox; Index: Integer; Rect:TRect; State: TOwnerDrawState) of Object; property OnDrawItem: TDrawItemEvent; | Возникает при необходимости прорисовки узла index: Rect - прямоугольник прори совки; State - состояние узла (odSelected - узел выделен; odDisabled - узел запрещен для выбора; odFocused - узел сфокусирован)
EOutlineChange = procedure (Sender: TObject; Index: Longint) of object; property OnExpand: EOut lineChange; | Возникает при открытии списка дочерних узлов узла Index

18.6.5. THeader - управляющий заголовок

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

Свойства Компонента THeader:

property Sections: TStrings; | Содержит набор заголовков секций. Методами Add, Delete, Insert объекта Sections можно добавлять, вставлять и удалять секции заголовка
property SectionWidth[X: Integer] : Integer; | Содержит ширину секции с индексом index

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

TSectionEvent = procedure(Sender: TObject; ASection, AWidth: Integer) of object; | Возникает при завершении изменения размера секции с индексом ASection. AWidth -текущая ширина секции
property OnSized: TSectionEvent; TSectionEvent = procedure(Sender: TObject; ASection, AWidth: Integer) of object-property OnSizing: TSectionEvent; | Возникает в процессе изменения размера секции с индексом ASection. AWidth - текущая ширина секции

18.6.6. TFileListBox - панель с именами файлов

Компонент TFileListBox представляет собой панель с именами файлов. Совместно С компонентами TDriveComboBox, TFilterComboBox и TDirectoryListBox может использоваться для создания диалоговых окон доступа к файлам.

Свойства компонента:

property Directory: String; | Определяет каталог размещения файлов
property Drive: Char; | Определяет диск размещения файлов
property FileEdit: TEdit; property FileName: String; | Объект-редактор для ввода имени файла вручную Введенное или выбранное имя файла
TFileAttr = (ftReadOnly, ftHidden, ftSystem, ftVolumeID, ftDirectory, ftArchive, ftNormal) ; TFileType = set of TFileAttr; property FileType: TFileType; | Определяет типы элементов, показываемых в окне компонента: ftReadOnly - файлы только для чтения; ftHidden - скрытые файлы; ftSystem -системные файлы; ftVolumeID - метки носителей; ftDirectory - каталоги; ftArchive - архивные файлы; ftNormal - обычные файлы
property Mask: String; | Определяет маску выбора файлов
property MultiSelect: Boolean; | Разрешает/запрещает множественный выбор файлов
property ShowGlyphs: Boolean; | Если содержит True, рядом с именами файлов показываются пиктограммы типа файла

Методы компонента:

procedure ApplyFilePath(const EditText: Strings; | Заменяет значения свойств Drive, Directory, FileName и Mask на те, что содержатся в строке EditText
procedure Update; | Обновляет содержимое окна

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

property OnChange: TNotifyEvent;

возникающее при любом изменении выбора в окне компонента.

18.6.7. TDirectoryListBox - панель с именами устройств

Компонент TDirectoryListBox представляет собой панель с именами каталогов. Совместно с компонентами TDriveComboBox, tfuterComboBox И TFileListBox может использоваться для создания диалоговых окон доступа к файлам. Свойства компонента:

property CaseSensitive: Boolean; | Содержит True, если файловая система чувствительна к высоте букв
property Directory: String; | Содержит имя каталога
property DirLabel: TLabel; | Указывает связанный с компонентом объект-метку, в котором отображается полный путь к текущему каталогу
property Drive: Char; | Содержит имя диска
property FileList: TFileListBox; | Указывает связанный с компонентом объект TFileListBox, в котором отображается содержимое каталога
property PreserveCase: Boolean; | Содержит True, если файловая система нечувствительна к высоте букв

Методы компонента:

function DisplayCase(const S: String): Strings; | Преобразует буквы строки S к строчным с учетом языкового драйвера, если ни свойство CaseSensitive, ни свойство PreserveCase не содержит True
function FileCompareText(const A, B: String): Integers; | Сравнивает строки а и в с учетом свойства CaseSensitive
function GetItemPath(Index : Integer) : Strings; | Возвращает путь к дочернему каталогу по его индексу
procedure OpenCurrent; | Открывает текущий каталог
procedure Update; | Обновляет текущий каталог

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

property OnChange: TNotifyEvent;

возникающее при любом изменении выбора в окне компонента.

18.6.8. TDriveComboBox - список выбора устройства

Компонент TDriveComboBox представляет собой список выбора с именами доступных дисков. Совместно с компонентами TDirectoryListBox, TFilterComboBox И TFileListBox может использоваться для создания диалоговых окон доступа к файлам.

Свойства компонента:

property DirList: TDirectoryList-Box; | Содержит ссылку на объект TDirectoryListBox, в котором отображается структура файлов на диске
property Drive: Char; TTextCase = (tcLowerCase, tcUp-perCase); property TextCase: TTextCase; | Содержит имя текущего диска Определяет отображение буквы диска: tcLowerCase - отображать строчной буквой; tcuppercase - отображать заглавной буквой

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

property OnChange: TNotifyEvent;

возникающее при любом изменении выбора в окне компонента, а также событие

property OnDropDown: TNotifyEvent;

связанное с раскрытием списка.

18.6.9. TFilterComboBox - список выбора с расширениями файлов

Компонент TFilterComboBox представляет собой список выбора с расширениями файлов. Совместно с компонентами TDirectoryListBox, TDriveComboBox и TFiieListBox может использоваться для создания диалоговых окон доступа к файлам.

Свойства компонента:

property FileList: TFiieListBox; | Указывает связанный с компонентом объект для просмотра файлов.
property Filter: String; | Содержит фильтр в виде поясняющего текста и маски.
property Mask: String; property Text: TCaption; | Содержит текущую маску выбора файлов. Содержит поясняющий текст фильтра.

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

property OnChange: TNotifyEvent;

возникающее при любом изменении выбора в окне компонента, а также событие

property OnDropDown: TNotifyEvent;

связанное с раскрытием списка.

18.7. КОМПОНЕНТЫ СТРАНИЦЫ SAMPLES

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

18.7.1. TGauge - индикатор величины

Компонент TGauge предназначен для отображения некоторой изменяющейся числовой величины. Он отличается от TProgress разнообразием форм (см. рис. 18.45).

Рис. 18.45. Различные формы компонента TGauge

Свойства компонента:

property BackColor: TColor; property ForeColor: TColor; TGaugeKind = (gkText, gkHorizon-talBar, gkVerticalBar, gkPie, gkNeedle) ; | Цвет незакрашенной части индикатора Цвет закрашенной части индикатора Определяет форму индикатора (см. рис. 18.44)
property Kind: TGaugeKind; property MaxValue: Longint; | Определяет максимальное значение диапазона изменения свойства progress
property MinValue: Longint; | Определяет минимальное значение диапазона изменения свойства progress
property PercentDone: Longint; | Содержит текущее значение progress в процентах от его диапазона изменения
property Progress: Longing; | Содержит текущее значение изменяющейся числовой величины
property ShowText: Boolean; | Если содержит True, в центре компонента выводится строковое представление значения PercentDone

С помощью метода

procedure AddProgress(Value: Longint);

к текущему значению progress можно добавить величину value.

18.7.2. TColorGrid - выбор или отображение цвета

Компонент предназначен для выбора и/или отображения цветов из 16-цветной палитры. Выбирать/отображать можно два цвета: основной и фоновый. Основной выбирается щелчком левой кнопки мыши и отображается символами fg, фоновый выбирается правой кнопкой и отображается символами bg (если оба цвета совпадают, соответствующая клетка таблицы помечается символами fb).

Свойства компонента:

property BackgroundColor: TColor; | Содержит выбранный фоновый цвет
property BackgroundEnabled: Boolean; | Разрешает/запрещает выбор фонового цвета. Игнорируется, если clickEnablesColor имеет значение True
property Backgroundlndex: Integer; | Содержит индекс выбранного фонового цвета
property ClickEnablesColor: Boolean; | Разрешает/запрещает выбор цветов
type TGridOrdering = (gol6xl,go8x2, go4x4, go2x8, golxl6); property GridOrdering: TGridOrdering; | Определяет конфигурацию таблицы: первая цифра - количество столбцов, вторая - количество рядов
property ForegroundColor: TColor; | Содержит выбранный основной цвет
property ForegroundEnabled: Boolean; | Разрешает/запрещает выбор основного цвета. Игнорируется, если ClickEnablesColor имеет значение True
property Foregroundlndex: Integer; | Содержит индекс выбранного основного цвета
property Selection: Integer; | Содержит индекс последнего выбранного цвета

Индекс цвета - это число в диапазоне О... 15, указывающее номер соответствующей ячейки в порядке перехода от самых темных цветов к самым светлым: 0 - ячейка с черным цветом, 1 - с коричневым, ..., 15 - с белым цветом.

Пары свойств BackgroundEnabled - ClickEnablesColor И ForegroundEnabled - ClickEnablesColor запрещают выбор и отображение соответствующего цвета только в том случае, когда каждое из них имеет значение False. Однако даже в этом случае свойство selection хранит номер ячейки таблицы, по которой последний раз был произведен щелчок левой или правой кнопкой мыши. Это свойство изменяется до возникновения события от мыши и при необходимости может использоваться для выбора цвета без его отображения. С помощью метода

function ColorToIndex(AColor: TColor): Integer;

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

property OnChange: TNotifyEvent;

возникает только при разрешенном выборе/отображении соответствующего цвета.

18.7.3. TSpinButton - спаренная кнопка

В отличие от компонента TUpDown (см. п. 18.3.7) компонент TSpinButton не связан с регулированием числовой величины и может использоваться просто как пара кнопок: верхняя (up) и нижняя (Down). Для компонента следует написать обработчики его событий onup-click и onDownciick, в которых реализуется реакция программы на щелчок по той или иной кнопке.

Рисунки на кнопках определяются свойствами upGlyph и DownGiyph. Каждый рисунок может содержать 1, 2 или 3 изображения: 1-е - для нормальной кнопки, 2-е - для запрещенной, 3-е - для нажатой.

18.7.4. TSpinEdit - редактор для ввода целого числа

Компонент TSpinEdit предназначен для ввода/отображения числа. Он представляет собой комбинацию специализированного потомка однострочного редактора rcustomEdit и кнопок TSpinButton. Редактор способен вводить только числа (в этом и заключается его специализация), а кнопки используются для изменения числа на некоторую фиксированную величину.

Свойства компонента:

property Button: TSpinButton; | Содержит ссылку на встроенный объект TSpinButton
property EditorEnabled: Boolean; | Разрешает/запрещает использовать редактор для ручного ввода числа
property Increment: Longint; | Содержит число, на которое будет изменяться свойство value при нажатии кнопок TSpinButton
property MaxValue: Longint; | Содержит верхнюю границу диапазона возможных значений value
property MinValue: Longint; | Содержит нижнюю границу диапазона возможных значений value
property Value: Longint; | Содержит текущее значение числовой величины

18.7.5. TDirectoryOutLine - отображение структуры каталогов

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

Рис. 18.46. Окно компонента TDirectoryOutLine

Компонент является наследником класса TCustomOutLine, и поэтому многие его свойства (itemHeight, Optins, PictureXXX, ShowBars и Style) совпадают с аналогичными свойствами другого наследника этого класса - компонента TOutLine. Свойство TTextCase = (tcLowerCase, tcUpperCase, tcAsIs) ;

property TextCase: TTextCase;

определяет высоту букв в именах каталогов: tcLowerCase - строчные буквы; tcUpperCase - заглавные буквы; tcAsis - строчные и заглавные буквы.

Следующие два свойства доступны только на этапе прогона программы:

property Drive: Char;

property Directory: TFileName;

Первое определяет выбранный диск, второе - выбранный каталог.

18.7.6. TCalendar - ввод и отображение даты

Компонент TCalendar отображает календарь на выбранный месяц и год. Его свойства Day, Month и Year могут содержать любую дату от 1 до 9999 года от Рождества Христова.

Свойства компонента:

property CalendarDate: ~DateTime; property CellText[ACol, ARow: Integer]: String; property Day: Integer; property GridLineWidth: Integer; | Содержит выбранную дату Содержит текст в ячейке календаря на пересечении столбца ACol с рядом ARow (только для чтения) Определяет выбранный день месяца
property Month: Integer; property Readonly: Boolean; | Устанавливает толщину линий таблицы календаря Определяет выбранный месяц Если содержит True, пользователь не может выбрать другую дату
type TDayOfWeek = 0..6;property StartOfWeek: TSayOfWeek; | Определяет день недели, который будет отображаться в самом левом столбце таблицы
property UseCurrentDate: Boolean; | Если содержит True, компонент будет показывать текущую системную дату
property Year: Integer; | Определяет выбранный год
Методы компонента:
procedure NextMonth; procedure NextYear; procedure PrevMonth; | Показывает календарь на следующий месяц Показывает календарь на следующий год Показывает календарь на предыдущий месяц
procedure PrevYear; | Показывает календарь на предыдущий год


Глава 19. ФОРМА

  • Глава 19. ФОРМА
    • РАЗНОВИДНОСТИ ФОРМ
    • КОМПОНЕНТ TFORM
    • СОЗДАНИЕ И ИСПОЛЬЗОВАНИЕ ФОРМ
    • ПРОГРАММЫ С МНОГИМИ ФОРМАМИ

Глава 19

ФОРМА

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

19.1. РАЗНОВИДНОСТИ ФОРМ

Разновидности форм определяются значениями их свойств FormStyle, а также разнообразием форм-заготовок, хранящихся в репозитории Delphi.

Стиль формы задается одним из значений свойства

TFormStyle = (fsNormal, fsMDIChild, fsMDIForm, fsStayOnTop) ;

property FormStyle: TFormStyle;

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

Стили fsMDIChild и fsMDIForm используются при создании так называемых многодокументных приложений в стиле MDI (MDI -Multi Document Interface). Этот немодный сегодня стиль предполагает создание главного окна MDI (его обычно называют рамочным), внутри которого по мере надобности появляются дочерние окна.' Дочерние окна, подобно дочерним элементам контейнера, не могут выходить за границы своего владельца - рамочного окна. В MDI-приложениях есть специальные средства управления взаимодействием рамочного окна с дочерними окнами. Например, каждое дочернее окно в момент активизации может нужным образом настроить главное меню рамочного окна (дочерние MDI-окна не имеют собственного главного меню). В Delphi для создания рамочного окна используется стиль fsMDIForm, а для создания дочернего MDI-окна - стиль fsMDIChild.

Стиль fsStayOnTop предназначен для окон, которые всегда должны располагаться над всеми другими окнами программы[ В терминологии Windows такие окна называются Popup Window, что буквально переводится как окно-поплавок.]. В момент активизации окна оно обычно становится видимым на экране, даже если перед этим его загораживали другие раскрытые окна. Стиль fsStayOnTop препятствует перекрытию окна другими окнами, даже если оно становится неактивным и теряет фокус ввода (так сказано в документации, однако на самом деле это не так, см. ниже). Понятно, что этот стиль используется в исключительных случаях, когда окно содержит что-то, требующее повышенного внимания пользователя.

Как показывает практика, объявление окна со стилевым признаком fsStayOnTop еще не решает проблему создания окна-поплавка, т. е. окна, которое невозможно перекрыть другими окнами. Более того, несложные эксперименты убедят вас, что этот признак вообще не играет никакой роли! Чтобы создать окно-поплавок, нужно обратиться к API-функции SetWindowPos.

Например:

SetWindowPos(fmAlarmForm.Handle, hwnd_TopMost, 300, 300, 250, 70, swp_noActvate)

Полное описание этой функции (как и всех других API-функций Windows) вы найдете в Help-файлах WIN32.HLP или WIN32SDK.HLP, которые поставляются вместе с Delphi и по умолчанию располагаются В папке Program Files | Common Files | Borland shared | MSHelp. Здесь я лишь кратко поясню назначение параметров вызова.

Первым параметром обращения к функции является дескриптор окна, которое должно стать “поплавком”. Напомню, что дескриптор формы содержится в ее свойстве Handle, поэтому для формы fmAlarmForm параметр вызова имеет вид fmAlarmForm. Handle. вторым параметром определяется расположение окна относительно других окон в так называемом Z-порядке их расположения. Константа nwnd_TopMost указывает, что окно должно стать самым верхним и впредь до его закрытия не может перекрываться другими (обычными) окнами. Четыре следующие параметра определяют координаты левого верхнего угла окна, его ширину и высоту. Все параметры указываются в пикселях, координаты угла задаются относительно, левого верхнего угла экрана. Последним указывается один или несколько битовых флагов, уточняющих поведение окна. В нашем примере использован флаг swp_noActvate, означающий, что окно не получает фокуса ввода в момент своего появления на экране. Поясню, что на практике я часто использую окна-поплавки, чтобы сообщить пользователю, например, о необходимости обновить набор данных, которые он видит в настоящий момент, так как эти данные были изменены другими пользователями клиент-серверной БД. Такое окно не должно отнимать активность (фокус ввода) у окна, с которым работает пользователь.

И последнее замечание. “Поплавком” может стать и обычное окно (со значением FormStyle=fsNormai). Однако “поплавок” действительно появится на экране, только если в его свойстве visible содержится значение True.

Иными словами, если пользователь закроет такое окно, оно не появится в результате вызова функции setwindowpos - его предварительно нужно сделать видимым методом show. Поскольку окно-поплавок может вызываться в разных местах программы, удобно поместить вызов функции setwindowspos в его обработчик события onActivate. Однако если вы попытаетесь создать такой обработчик для окна со стилем FormStyle=fsStayonTop, то компилятор выдаст сообщение об ошибке и поместит заготовку метода в тексте модуля после ограничителя “.”. В этом случае определяйте окно со стилем fsNormal.

Современные многооконные приложения чаще всего строятся в стиле SDI (SDI - Single Document Interface), который в противоположность MDI не накладывает ограничений на положение и размеры вспомогательных форм, каждая из которых при необходимости может иметь свое главное меню (в стиле SDI реализована, например, среда Delphi). Для создания форм в этом случае используется стиль fsNormal.

Замечу, что в рамках SDI-приложений могут использоваться рамочные MDI-формы со своими дочерними окнами, так что термин SDI носит весьма условный характер и применяется в основном для противопоставления давно используемому термину MDI.

В репозитории Delphi хранится множество стандартных форм-заготовок, предназначенных для решения конкретных задач (доступ к репозиторию открывает опция меню File | New | other).

Помимо универсальной пустой формы Form (страница New peno-зитория) репозитории содержит следующие специализированные формы:

Название | Страница | Назначение
About box | Forms | Окно 0 программе
Dual list box | Forms | Диалоговое окно с двумя компонентами ListBox. Используется для гибкого управления списками, в том числе для перемещения элементов из одного списка в другой

Quick Report Labels

Forms

Используется в приложениях баз данных для печати этикеток

Quick Report List | Forms | Используется в приложениях баз данных для создания обычных отчетов
Quick Report Master/Detail | Forms | Используется в приложениях баз данных для создания отчетов типа главный/детальный
Tabbed Pages | Forms | Заготовка для многостраничного диалогового окна с закладками, кнопками ok, cancel и Help
Dialog with Help | Dialogs | Заготовка для диалогового окна с кнопками ok. Cancel, Help. Имеются варианты с вертикальным асположением кнопок и с горизонтальным расположением
Password Dialog | Dialogs | Диалоговое окно с редактором TEdit, кнопками ok и Cancel для ввода паролей
Reconcile Error Dialog | Dialogs | Используется в приложениях баз данных для пояснения обнаруженной ошибки при изменении таблицы
Standard Dialog | Dialogs | Заготовка для диалогового окна с кнопками ok, cancel. Имеются варианты с вертикальным расположение кнопок и с горизонтальным расположением
Dialog Wizard | Dialogs | Мастер создания диалоговых окон
Decision Cube Sample | Business | Заготовка для использование компонентов страницы Decision Cube
Database Form Wizard | Business | Мастер создания форм для доступа к базам данных

Quick Report Wizard

Business

Мастер создания отчетов для баз данных

TeeChart Wizard

Business

Мастер форм для доступа к компоненту chart

19.2. КОМПОНЕНТ TFORM

Свойства формы:

property Active: Boolean-property ActiveControl: TWinControl; | Содержит True, если окно активно (имеет фокус ввода) Определяет дочерний элемент, содержащий фокус ввода
property ActiveMDIChild: TForm; | Определяет дочернее midi oкнo с фокусом ввода
TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp) ; TBorderIcons = set of TBorderIcon; property Borderlcons: TBorderIcons; | Определяет наличие кнопок в заголовке окна: biSystemMenu - имеется кнопка вызова системного меню; biMinimize - имеется кнопка минимизации; biMaximize - имеется кнопка максимизации; biHelp - имеется кнопка вызова справочной службы
property Canvas: TCanvas; | Канва для прорисовки фона окна. Это свойство могут использовать не оконные дочерние элемента
property ClientHeight: Integer; | Высота клиентской части окна
property ClientRect: TRect; | Прямоугольник клиентской части окна
property ClientWidth: Integer; | Ширина клиентской части окна
property HelpFile: Strings; | Каждая форма может иметь индивидуальный Help- файл, имя которого содержит это свойство. Если имя не указано, используется Hdp-файп приложения
property Icon: TIcon; | Содержит пиктограмму окна. Для главной формы это свойство определяет также пиктограмму программы
property KeyPreview: Boolean; | Если имеет значение True, форма получает события от клавиатуры, перед тем как они поступят в элемент с фокусом ввода
property MDIChildCount: Integer; | В рамочном MDI-окне указывает количество связанных с ним дочерних midi-oкнo
property MDIChildren[I: Integer]: Tforms; | В рамочном MDI-окне открывает доступ к I-му дочернему окну
property Menu: TMainMenu; | Содержит главное меню окна
TModalResuit = Low(Integer)..High(Integer) ; property ModalResult: TModalResuit; | Для модального окна содержит результат диалога
property PixelsPerInch: Integer; | Определяет разрешающую способность окна в пикселях на один линейный дюйм для этапа конструирования формы
TPosition = (poDesigned, poDe- fault, poDefaultPosOnly, poDe- faultSizeOnly, poScreenCenter) ; property Position: TPosition; | Определяет положение и размеры окна в момент его появления на экране: poDesigned - такие же, как на этапе конструирования окна; poDefault - положение и размеры определяет Windows; poDefaultposOnly - положение, как на этапе конструирования, размеры определяет Windows; л poDefaultSizeOnly - размеры, как на этапе конструирования, положение определяет Windows; poScreenCenter - в центре экрана с размерами, как на этапе конструирования
TPrintScale = (poNone, poProportional, poPrintToFit); property PrintScale: TPrintScale; | Определяет масштабирование окна при его печати на принтере: poNone - нет масштабирования; каждый пиксель окна воспроизводится одной точкой на бумаге; рорrороrtional - форма масштабируется так, чтобы ее образ на бумаге был максимально похож на ее изображение на экране; poPrintToFit- форма печатается с такими же пропорциями, как на экране, но с размерами, за полняющими лист бумаги
property Scaled: Boolean; | Разрешает/запрещает масштабировать форму, если значение ее свойства PixelPerinch отличается от текущего разрешения экрана
TileMode = (tbHorizonial, tbVertical); property TileMode: TTileMode; | Определяет стиль расположения дочерних окон MDI-приложения при их упорядочении мозаикой
property WindowMenu: TMenuItem; | Определяет пункт главного меню рамочного MDI-окна, к которому добавляются опции дочернего окна
TWindowState = (wsNormal, wsMinimized, wsMaximized) ; | Определяет состояние окна в момент его появления на экране: wsNormal - бычное
property WindowState: TWindow State; | Окно; wsMinimized - минимизировано до пиктограммы; wsMaximized - распахнуто на весь экран

Если в множестве свойства BоrderIcon убрать кнопки biMinimize: biMaximize, а в свойство WindowState поместить значение wsMaximized, форма займет все пространство экрана, включая нижнюю панель задач.

Методы формы:

procedure Arrangelcons; | Упорядочивает пиктограммы закрытых дочерних окон MDI-приложения
procedure Cascade; | Располагает дочерние MDI-окна каскадом
procedure Close; | Закрывает окно. Для главного окна завершает работу программы
function CloseQuery: Boolean | Возвращает True, если можно закрыть окно
procedure DefocusControl(Control: TWinControl; Removing: Boolean); | Отбирает фокус ввода у дочернего элемента Control. Если при этом Removing=True, фокус ввода получает форма
procedure FocusControl(Control: TWinControl); | Передает фокус ввода дочернему элементу Control
function GetFormImage: TBitmap; | Содержит текущее изображение окна формы
procedure Next; | Делает активным следующее mdi-okнo
procedure Previous; | Делает активным предыдущее mdi-okнo
procedure Tile; | Располагает дочерние MDI-окна мозаикой
procedure Print; | Печатает окно на принтере
procedure Release; | Ожидает окончания обработки всех событий формы и ее дочерних элементов, после чего уничтожает окно и освобождает всю связанную с ним память
procedure SendCancel- Mode(Sender: TControl); | Восстанавливает начальное состояние окна: освобождает мышь, прекращает прокрутку и закрывает меню
procedure SetFocus; | Передает фокус ввода форме. Форма при этом должна быть активной и видимой
procedure Show; | Показывает форму в немодальном режиме
function ShowModal: Integer; | Показывает форму в модальном режиме и возвращает результат диалога

События формы:

property OnActivate: TNoti fyEvent; | Возникает в момент активизации окна (при полученииею фокуса ввода)
TCloseAction = (caNone, caHide, caFree, caMinimize); TCloseEvent = procedure(Sender: TObject; var Action: TCloseAction) of object; property OnClose: TCloseEvent; | Возникает перед закрытием окна. Параметр Action уточняет необходимые действия: саNоnе - не закрывать окно; caHide - спрятать окно; caFree - уничтожить окно; caMimimize - минимизировать окно
TCloseQueryEvent = proce dure(Sender: TObject; var CanClose: Boolean) of object; property OnCloseQuery: TCloseQueryEvent; | Возникает перед закрытием окна. В параметре canclose обработчик сообщает о возможности закрытия окна
property OnCreate: TNotifyEvent; | Возникает при создании окна, но до его появления на экране
property OnDeactivate: TNoti fyEvent; | Возникает при передаче активности другому окну той же программы
property OnDestroy: TNotifyEvent; | Возникает перед разрушением окна
THelpEvent = function (Command: Word; Data: Longint; var CallHelp: Boolean): Boolean of object; property OnHelp: THelpEvent; | Возникает при обращении к справочной службе. В параметре callHelp обработчик сообщает о возможности получения помощи
property OnHide: TNotifyEvent; | Возникает перед исчезновением окна
property OnPaint: TNotifyEvent; | Возникает при необходимости прорисовки окна
property OnResize: TNotifyEvent; | Возникает при изменении размеров окна
property OnShow: TNotifyEvent; | Возникает при появлении окна на экране

19.3. СОЗДАНИЕ И ИСПОЛЬЗОВАНИЕ ФОРМ

Для подключения новой формы к проекту достаточно обратиться к репозиторию и выбрать нужную разновидность формы. Менеджер проекта автоматически подключает новую форму к списку используемых форм и обеспечивает все необходимые действия по ее инициации. Самая первая подключенная к проекту форма (стандартное имя формы - Form1) становится главным окном программы. Окно этой формы автоматически появляется на экране в момент старта программы. Впрочем, программист может указать любую форму, окно которой станет главным. Для этого нужно обратиться к опции

Project | Options И, раскрыв список Main form, выбрать нужную

форму (рис. 19.1).

Pис 19.1. Окно управления проектом

Каждое следующее окно становится видно только после обращения к его методу show или showModal. Чтобы обратиться к этим методам, нужно сослаться на объект-окно, который автоматически объявляется в интерфейсном разделе связанного с окном модуля. Для этого, в свою очередь, главное окно должно знать о существовании другого окна, что достигается ссылкой на модуль окна в предложении uses. Если, например, в ходе выполнения одного из методов главного окна программист захочет вызвать окно с именем fmForm, связанное с модулем Formunit, он должен сослаться на этот модуль в предложении uses главного окна:

implementation Uses FormUnit;

после чего вызвать окно на экран:

fmForm.Show;

или

fmForm.ShowModai;

Delphi автоматизирует вставку ссылки на модуль в предложение uses. Для этого на этапе конструирования нужно активизировать главное окно, щелкнув по нему мышью, после чего обратиться к опции File | uses unit. В появившемся диалоговом окне (рис. 19.2) нужно выбрать модуль и нажать ок. Вставляется ссылка в предложение, стоящее за зарезервированным словом Implementation, т. к. обычно главное окно в своей интерфейсной части не ссылается на элементы интерфейсной части второго окна. Точно так же можно при необходимости сослаться в модуле второго окна на модуль главного окна: активизируйте второе окно и вновь вызовите опцию File | uses Unit. Замечу, что, если программист забыл сослаться на модуль, который подключен к проекту, Delphi при первой же трансляции программы сообщит об этом и предложит вставить недостающую ссылку.

Рис. 19.2. Вставка ссылки на модуль

При вызове метода show второе окно появляется на экране и работает одновременно с первым, поэтому управление сразу передается оператору, стоящему за обращением к этому методу. Такие окна называются немодальными, они всегда открываются в одном методе, а закрываются в другом. В отличие от этого обращение к show-Modal создает модальное окно, которое полностью берет на себя дальнейшее управление программой, поэтому оператор за обращением к showModal в вызывающей части программы получит управление только после закрытия модального окна.

Модальные окна всегда требуют от пользователя принятия какого-либо решения. С их помощью реализуется диалог с пользователем или создается информационное окно, которое пользователь должен закрыть после ознакомления с содержащейся в нем информацией. Если от пользователя требуется принятие решения, в модальное окно вставляются зависимые или независимые переключатели, кнопки и другие интерфейсные элементы, с помощью которых пользователь сможет сообщить программе о принятом решении. В момент закрытия диалога модальное окно должно поместить число, соответствующее решению пользователя, в свое свойство ModalResuit. Некоторые стандартные кнопки (ok, Yes, No, cancel и т. п.) автоматически выполняют эти действия: помещают нужное число в ModalResuit и закрывают окно. В других случаях об этом должен позаботиться программист. Вызывающая программа получает значение ModaiResuit как значение функции showModal и может тут же его проанализировать:

if Form2.ShowModai = mrXXX then ....

Возможен и такой вариант:

Form2.ShowModal ;

if Form2.ModaiResuit = mrXXX then .....

Для закрытия окна (модального или немодального) используются методы Hide или close. Следует учесть, что метод Close всегда помещает в ModalResult значение 2 (mrCancel), в то время как Hide неt меняет значения этого свойства, поэтому, если программист хочет передать в вызывающую программу нестандартный модальный результат, следует писать:

ModaiResuit := MyResult;

Hide; // Но ни в коем случае Close!

Подключение нового окна к проекту приводит к тому, что менеджер;

проекта размещает его имя в списке автоматически создаваемых окон на рис. 19.1 -список Auto-Create forms). В этом случае про-Дкшмист может не заботиться об инициации, соответствующего объекта окна. Однако если в программе используется множество окон, автоматическое их создание в момент старта программы может существенно затянуть во времени процесс загрузки программы; В то же Демя немедленное создание всех окон вовсе не обязательно, так как вряд ли вам понадобится одновременно показывать их на экране в немодальном режиме. Профессиональные программисты никогда не создают: все оконные объекты в момент старта программы, но используют их по мере надобности. Дчя .этого в окне менеджера проекта ссылки на эти формы переносятся в панель Available forms (рис. 19.1), а обращение к окну .

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

Кстати, полезно соответствующим образом настроить среду Delphi:

вызовите Tools I Environment Options и на странице Designer уберите флажок в преключателе Autоcreate forms & data modules.

19.4. ПРОГРАММЫ С МНОГИМИ ФОРМАМИ

Сложные программы подчас требуют создания множества форм, каждая из которых решает ту или иную конкретную задачу. Например, при создании систем управления базами данных типичным для проекта будет разработка от 50 до 100 и более форм. Процесс создания такого проекта обычно растягивается на несколько месяцев, а над его реализацией трудятся одновременно несколько программистов. Все это затрудняет ориентацию программиста, его способность быстро вспомнить назначение той или иной формы. В этом случае существенную помощь может оказать файл проекта. Как уже говорилось, рядом с описанием включенного в проекта модуля содержится строка комментария, в которой Delphi указывает имя файла формы. Этот комментарий появляется в диалоговом окне после щелчка по инструментальной кнопке выбора формы Я или выбора опции view | Forms главного меню. Если вы поместите в этом комментарии произвольный текст, он также будет виден в окне и поможет вам вспомнить назначение конкретной формы. На рис. 19.3 показана копия экрана с окном проекта, содержащего несколько десятков форм, а на рис. 19.4 - соответствующее диалоговое окно выбора форм.

Если вы захотите воспользоваться моим советом, учтите, что имя файла должно отделяться от вашего комментария только пробелами. Не пользуйтесь для разделения клавишей табуляции, так как в этом случае Delphi откажется включить комментарий в список форм проекта и вы просто не увидите его в диалоговом окне. (В умалчи-ваемой настройке Delphi нажатие клавиши табуляции имитируется вставкой нужного количества пробелов - опция Tools | Environment, закладка Editor, флажок use tab character не отмечен; после его отметки вместо пробелов вставляется символ табуляции #9, что и приводит к отказу Delphi включать форму в список.)

Рис. 19.3 Файл проекта с многими формами

Рис. 19.4. Диалоговое окно выбора формы


Глава 21. СОЗДАНИЕ ВСТРОЕННОЙ СПРАВОЧНОЙ СИСТЕМЫ

  • Глава 21. СОЗДАНИЕ ВСТРОЕННОЙ СПРАВОЧНОЙ СИСТЕМЫ
    • ЭТАПЫ РАЗРАБОТКИ
    • ПЛАНИРОВАНИЕ СИСТЕМЫ СПРАВОК
    • СОЗДАНИЕ ТЕКСТОВЫХ ФАЙЛОВ
      • Задание идентифицирующей строки и организация перекрестных ссылок
      • Задание названия раздела
      • Определение ключевых слов
      • Определение условий компиляции
      • Указание порядка просмотра связанных разделов
      • Вставка графики
      • Выполнение макрокоманд
      • Отображение текста раздела в дополнительном окне
    • РАЗРАБОТКА ПРОЕКТНОГО ФАЙЛА
      • Секция OPTIONS
      • Секция FILES
      • Секция WINDOWS
      • Секция BITMAPS
      • Секция MAP
      • Секция ALIASES
      • Секция Conftg
      • Секция BAGGAGE
    • ФАЙЛ СОДЕРЖАНИЯ СПРАВОЧНОЙ СЛУЖБЫ
    • КОМПИЛЯЦИЯ, ТЕСТИРОВАНИЕ И СВЯЗЬ С ПРОГРАММОЙ
    • МАКРОКОМАНДЫ WINHELP

Глава 21

СОЗДАНИЕ ВСТРОЕННОЙ СПРАВОЧНОЙ СИСТЕМЫ

Ни одна серьезная программная разработка не обходится без создания контекстно-чувствительной справочной службы (далее - Help-службы).

В этом разделе описан процесс создания Help-службы и настройка программы на работу с ней. Для реализации процесса вам понадобятся как минимум две утилиты: текстовый RTF-редактор и компилятор Help-файлов. В качестве текстового редактора обычно используется Microsoft Word (далее просто Word), поддерживающий расширенный текстовый формат RTF (стандартный для Windows 32 текстовый редактор WordPad также поддерживает формат RTF, но в нем нет удобных средств вставки специальных управляющих символов). В ранних версиях Delphi в качестве компилятора Help-файлов поставлялся стандартный компилятор hcsi.exe. С последними версиями 4 и 5 поставляется более удобная утилита Microsoft Help Workshop (файл hcw.exe из каталога help | tools каталога размещения Delphi).

21.1. ЭТАПЫ РАЗРАБОТКИ

Разработка Help-службы требует решения следующих основных задач.

  • Планирование системы справок. На этом этапе составляется перечень разделов справочной службы и необходимых перекрестных ссылок.
  • Создание текстовых файлов, содержащих описания справочных разделов. Текстовые файлы готовятся с помощью любого текстового редактора, поддерживающего расширенный текстовый формат RTF. В них включаются специальные управляющие символы для создания перекрестных ссылок и подключения растровых изображений.
  • Разработка проектного файла, содержащего специальные команды для Help-компилятора. Проектный файл описывает структуру справочной службы в целом, в нем каждому разделу присваивается уникальный целочисленный идентификатор.
  • Разработка файла содержания. Содержание активизируется при запуске HLP-файла, а также после щелчка по кнопке содержание в окне справочной службы.
  • Компиляция Help-файлов.
  • Тестирование и отладка Help-службы.
  • Связь программы с разделами Help-службы.

21.2. ПЛАНИРОВАНИЕ СИСТЕМЫ СПРАВОК

На этапе планирования необходимо составить перечень разделов справочной службы и нужных перекрестных ссылок. Структура разделов и количество перекрестных ссылок зависит от сложности программы, для которой создается Help-служба, и опыта (и вкуса) разработчика. Полезно учесть следующие рекомендации.
  • Любой раздел может содержать список подчиненных или связанных разделов, т. е. разрешается произвольная иерархия справочной службы.
  • Полезно структурировать разделы по роду предоставляемой ими информации. Например, предусмотреть разделы для начинающего пользователя с пошаговыми инструкциями, выделить примеры использования программы, предоставить информацию опытным пользователям с точным и детальным описанием каждой программной функции, а также информацию о “продвинутых” возможностях программы и т. п.
  • Каждый раздел должен, по возможности, отображаться в одном распахнутом окне. Избегайте слишком длинных пояснений, лучше разбейте такой комментарий на несколько разделов. Учтите, что согласно исследованиям лишь менее 30% людей могут читать текст на экране так же легко и быстро, как на бумаге.
  • Старайтесь описывать справочную информацию простым и ясным языком. Избегайте использования профессионального жар- гонизма, а также фраз типа “как вам это известно”. Юмор, безусловно, увеличит эффективность справки, однако его чрезмерное количество может навредить главному - помощи пользователю.
  • Структурируйте текст, вставляйте в него больше рисунков, выделяйте важную информацию шрифтами и цветом. Помните, что монотонный текст (как и чрезмерно “раскрашенный”) резко увеличивает утомляемость пользователя и затрудняет усвоение информации.

21.3. СОЗДАНИЕ ТЕКСТОВЫХ ФАЙЛОВ

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

При создании текстовых файлов учтите следующие особенности их подготовки:

  • Количество RTF-файлов может быть произвольным. Вы можете поместить всю справочную информацию в один файл или предусмотреть для каждого раздела отдельный файл - все зависит от объема и структуризации Help-службы и ваших личных пристрастий.
  • В пределах одного файла каждый раздел отделяется от другого служебным символом конца страницы (с помощью Word этот символ вставляется клавишами Ctrl+Enter).
  • Любой раздел, доступный с помощью перекрестных ссылок или индексных указателей, должен иметь связанный с ним идентификатор - уникальную текстовую строку.
  • Раздел может иметь название и связанный с ним список ключевых слов.

В текстовый файл включаются такие управляющие символы:

Сноска “#” Перечеркнутый или дважды подчеркнутый текст Подчеркнутый текст | Указывает идентификатор раздела Определяет отображение связанного с текстом раздела (перекрестной ссылки) в стандартном справочном окне Определяет отображение раздела перекрестной ссылки в окне пояснений (“всплывающем” окне)
Скрытый текст | Определяет идентификатор раздела, связанного с перекрестной ссылкой
Сноска “$” | Задает название раздела
Сноска “К” | Указывает список ключевых слов для поиска раздела
Сноска “+” | Задает порядковый номер раздела в списке просмотра связанных разделов
Сноска “/\” | Определяет условие компиляции раздела
Сноска “>” | Определяет тип дополнительного окна, в котором будет отображаться раздел
Сноска “!” | Указывает макрокоманду, которая будет выполняться при открытии окна с разделом

21.3.1. Задание идентифицирующей строки и организация перекрестных ссылок

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

Идентифицирующая строка может содержать любые символы, кроме #, @, !, *, =, >, % и пробелов. Разница в высоте латинских букв (но не кириллицы!) игнорируется. Длина строки - до 255 символов.

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

Идентификатор задается с помощью сноски “#” в самом начале раздела.

Например:

Назначение_программы

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

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

На рис. 21.1 показано окно редактора Word с фрагментом текста Help-службы.

Раздел перекрестные ссылки связан с идентификатором hypertext, который задан в виде текста сноски “#”, предшествующей первому символу заголовка (для задания сноски выберите в главном меню Word'a Вставка | Сноска, в окне Сноски выберите Другая и введите символ “#” в строке 1, 2, 3).

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

Поскольку идентификатор выделяется скрытым текстом” в обычном режиме он скрыт и становится виден только в режиме отображения служебных символов после щелчка по кнопке V на инструментальной панели Word.

Рис. 21.1. Пример задания перекрестных ссылок

Для задания скрытого текста в редакторе Word необходимо выделить текст и применить к нему команду Hidden. Для этого можно воспользоваться опцией Формат | Шрифт и в разделе Эффекты щелкнуть по опции скрытый.

Поскольку эту последовательность действий; при написании текста, справочной службы придется выполнять очень часто, полезно предварительно настроить Word: выберите Сервис | Настройка, на панели.

Команды в окне категории выберите все команды, а в окне Команды - Hidden и “перетащите” мышью (нажав и не отпуская, левую, кнопку) эту команду на инструментальную, панельку. Теперь, чтобы выбрать скрытый текст, достаточно выделить его и щелкнуть по новой кнопке. Для Задания перечеркнутого или дважды подчеркнутого текста нет специальной команды, поэтому автоматизировать соответствующие действия нужно с помощью макроса. Чтобы создать Макрос сначала выделите текст, затем выберите Сервис | Макрос | Начать запись, в окне Запись макроса укажите подходящее имя (например, Зачеркнутый) и щелкните по кнопке ''Панели, затем из окна Настройка перетащите имя макроса на панель инструментов и накройте окно. Теперь указатель мыши будет иметь вид кассеты с лентой, а на экране появится соответствующая панелька управления. Выберите Формат | Шрифт [Зачеркнутый и остановите запись макроса щелчком по левой кнопке панельки управления или с помощью Сервис | Макрос |Остановить запись.

Любой вызываемый раздел может отображаться в основном окне справки или в окне пояснений. Это окно появляется поверх основного окна (такие окна иногда называют “всплывающими”) и обычно используется для пояснений (рис. 21.2).

Рис. 21.2. Пример использования окна пояснений

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

Для проверки АSI-шиныASI и связанных с ней реле и датчиков используется опция Сервис | Контроль шины главного меню.

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

* отменяет выделение текста ссылки цветом;

% отменяет выделение текста ссылки цветом и подчеркиванием;

@ указывает HLP-файл, в котором расположена нужная тема;> указывает тип окна для отображения раздела.[Методика работы с Microsoft Word дается для версии 97. Для других версий могут быть незначительные отличия.]

Символы “*” и “%” вставляются непосредственно перед идентификатором раздела и, так же как и он, оформляются скрытым текстом. Например:

ГИПЕРТЕКСТ%НУРЕРТЕХТ

Теперь в справочном окне слово гипертекст ничем, кроме формы расположенного над ним указателя мыши, не будет отличаться от обычного текста справки, тем не менее щелчок по нему вызовет переход к теме hypertext. В ссылке можно указать только один из символов отмены выделения - “*” или “%”.

Символы “@” и “>”, наоборот, вставляются в конце скрытого текста и за ними должны следовать:

  • маршрут доступа и имя HLP-файла - для символа “@”;
  • имя типа окна, в котором следует отобразить раздел - для символа “>”; это окно должно определяться в файле проекта (см. п. 21.4.3).

Например, если раздел hypertext расположен в файле c:\proba\proba.hlp и должен показываться в дополнительном окне типа wind, соответствующая ссылка будет такой:

ГИПЕPTEKCT HYPERTEXT@C:\PRQBA\PROBA.HLP>WIND

Обратите внимание: символы “@” и “>”, как и следующие за ними символы маршрута и имени типа окна, должны оформляться скрытым текстом. Если, как в приведенном примере, в ссылке одновременно указываются и маршрут, и тип окна, порядок их следования безразличен, однако в одной ссылке можно указать только один символ “@” и (или) один символ “>”. Замечу, что имя типа окна нужно указывать заглавными буквами, даже если в файле проекта оно определено строчными.

21.3.2. Задание названия раздела

Название раздела используется в Help-службе следующим образом:

• оно появляется после активизации опции закладка в главном меню Help-службы;

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

На рис. 21.3 показано окно поиск справочной службы Delphi.

Рис. 21.3. Окно Поиск со списком названий найденных разделов (“З. Выберите нужный раздел ...”)

Название раздела задается с помощью сноски “$”, которая должна предшествовать первому символу текста раздела. На рис. 21.4 показан пример окна Word с названием Режим|0тладка для раздела Отладочный режим. Название раздела записывается в тексте сноски и отделяется от символа “$” одним пробелом. В тексте названия можно указывать любые символы, в том числе и пробелы. Максимальная длина названия - 255 символов. Обычно название раздела совпадает с его заголовком.

Рис. 21.4. Вставки названия раздела

21.3.3. Определение ключевых слов

Help-служба позволяет искать разделы по связанным с ними ключевым словам. В диалоговом окне Поиск (см. выше рис. 21.3) предусмотрены строка (1) для ввода ключевых слов и возможность выбора их из списка (2). Для любого раздела можно назначить сколько угодно ключевых слов и наоборот - с любым ключевым словом можно связать сколько угодно разделов.

Для определения ключевого слова в начале раздела (до первого символа текста раздела) ставится сноска, помеченная латинской буквой “К” или “k”. Например:

к открыть;текст файл;ASCII;текст

Все связанные с разделом ключевые слова помещаются в текст сноски и отделяются от “К” одним пробелом, а друг от друга символом “;”. Группы связанных по смыслу ключевых слов объединяются во фразы, которые отделяются друг от друга пробелами. Help-служба ищет и отображает в списке выбора названия всех разделов, ключевые слова которых перечислены в одной фразе. Например, если для приведенной выше сноски пользователь ввел слово файл, будут представлены названия разделов, связанных со словами файл, ASCII и текст.

Помимо основной таблицы ключевых слов в Help-службе может быть определена дополнительная таблица. Слова из дополнительной таблицы не показываются в окне Поиск. Их использование возможно только вмакрокомандах ALink и TestALink (СМ. П. 21.7). Для

вставки ключевых слов в дополнительную таблицу используется сноска, помеченная латинской буквой “А”:

А таблица/слово

21.3.4. Определение условий компиляции

Подобно директивам условной компиляции Delphi в RTF-файл можно вставлять указания Help-компилятору помещать или не помещать в результирующий файл тот или иной раздел. Такие указания могут понадобиться на этапе отладки справочной службы, а также при создании нескольких версий приложения (например, справочная служба демонстрационной версии может не содержать некоторые разделы, определенные для основной версии). Они вставляются в текст с помощью сноски, помеченной символом “*”. Чтобы включить условный раздел в Help-файл или исключить его из файла, ключевое слово, определяемое сноской “*”, должно указываться в секции соответственно include или Exclude файла проекта справочной службы (см. п. 21.4.1).

В тексте сноски “*” можно указать одно или несколько управляющих слов (следующие друг за другом слова в тексте сноски разделяются символом “;”). Раздел будет включен в Help-файл, если хотя бы одно из связанных с ним управляющих слов указано в секции include файла проекта (если раздел не имеет сноски “*”, он всегда включается в результирующий файл). Управляющие слова могут содержать любые символы, кроме “;” и пробелов. Например:

Test_Build; AppVersionI; DebuggVer

21.3.5. Указание порядка просмотра связанных разделов

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

Файл | гл меню:005
Новый | меню файл:005
Открыть | меню файл:010
Сохранить | меню файл:015
Сохранить как | меню файл:020
Печатать | меню файл:025
Выход | меню файл:030
Редактировать | гл меню:010
Очистить | меню редак:005
Вырезать | меню редак:010
Копировать | меню редак:015
Вставить | меню редак:020
Отменить | меню редак:025
Окно | гл меню:015
Каскад | меню окно:005
Мозаика | меню окно:010
Закрыть все | меню окно:015

В этом списке каждой опции меню поставлена в соответствие комбинация символов (гл_меню: 010, меню_файл:025 и т. п.), определяющая порядковый номер просмотра раздела. Все разделы, помеченные соответствующими комбинациями, сортируются в обычном ASCII-порядке следования символов этих комбинаций. Таким образом, раздел Редактировать, помеченный гл_меню:010, будет следовать за Файл (гл_меню: 005), но предшествовать Окно (гл_меню: 015).

Чтобы связать с разделом код для указания порядка просмотра раздела, необходимо вставить сноску “+”:

*гл_меню:010

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

Замечу, что кнопки просмотра связанных разделов появляются в справочном окне автоматически, только от одного факта указания сноски “+” в текущем разделе. Чтобы вставить их независимо от этого, нужно выполнить макрокоманду BrowsButtons. Если макрокоманда выполнена, а текущий раздел не связан ссылкой “+” с другими разделами, кнопки появятся, но будут запрещены для выбора.

21.3.6. Вставка графики

Если вы создаете RTF-файл с помощью редактора Word, вы можете вставлять в текст графические врезки средствами редактора. Единственное ограничение: файлы с графикой должны соответствовать формату bmp. Однако Help-компилятор имеет и собственные средства вставки графических врезок с помощью внешних вмр-файлов. Ниже описывается, как это сделать.

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

(bmc filename.bmp}

{bml filename.bmp}

{bmr filename.bmp}

Здесь filename - имя вмр-файла; bmc, bml, bmr - команды, управляющие положением картинки относительно текста.

Команда bmc (от BitMap Char) предписывает рассматривать графический образ как символ. В этом случае он располагается точно на том месте в тексте справки, где указана директива вставки. Оставшаяся часть текста слева и справа от директивы разместится соответственно слева и справа от врезки, а высота строки с врезкой будет автоматически выбрана так, чтобы текст не накладывался на картинку. Команды bml (BitMap Left) и bmr (BitMapRight) заставят картинку прижаться соответственно к левому или правому краю окна справки. Все перечисленные в директивах ДМР-файлы должны располагаться в каталоге, указанном в опции bmroot секции [Options] проектного файла (см. п. 21.4.1).

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

Этот графический образ [bmc flower.bmp]Bitmap Cross вставлен непосредственно в текст и служит перекрестной ссылкой. {bml hardware.bmp}Bitmap Comment Если щелкнуть мышью по расположенной слева картинке, появится дополнительное окно с пояснениями. Такие окна удобно использовать для пояснения второстепенных деталей справки.

{bmr legal.bmp} Правая картинка не является перекрестной ссылкой. Она просто иллюстрирует одно из многочисленных изображений, поставляемых для текстового редактора MS Word.

На рис. 21.5 показан экран Help-службы с графическими вставками. Для его создания использовался приведенный выше фрагмент RTF-файла.

Рис. 21.5. Использование графических врезок

21.3.7. Выполнение макрокоманд

При открытии того или иного раздела можно выполнить одну или несколько макрокоманд. С помощью макрокоманд можно гибко воздействовать на состояние окна справочной службы: его положение, размеры, цвет, содержимое меню и инструментальных кнопок, отображать другие Help-файлы, выполнять внешние программы и т. д. (см. п. 21.7). Для указания макрокоманды, исполняющейся в момент открытия раздела, ее имя задается в тексте сноски “!”. Сноска указывается вместе с другими сносками в самом начале раздела (до первого символа текста). Например, для вставки в инструментальную панель кнопок просмотра связанных разделов сноска имеет такой вид:

!BrowseButtons ( )

Если нужно указать несколько макрокоманд, они отделяются в тексте сноски символами “;”.

21.3.8. Отображение текста раздела в дополнительном окне

Помимо основного окна в справочной службе можно определить множество дополнительных окон, каждое из которых в общем случае будет иметь свой цвет, заголовок, размеры и т. д. Дополнительные окна описываются в секции [windows] проектного файла (см. п. 21.4.). Чтобы указать, что раздел справки должен по умолчанию отображаться в дополнительном окне, используется ссылка “>” (с помощью модификации гиперссылки можно заменить умалчиваемый тип окна на другой, см. ниже). В тексте ссылки указывается тип окна так, как он определен в проектном файле, например:

> wind

В ссылке “>” может указываться только одно умалчиваемое окно. Другим способом указания дополнительного окна является модификация перекрестной ссылки (см. п. 21.3.1). Если, например, в

ссылке

ГИПЕРТЕКСТ НУРЕКТЕХТ

раздел hypertext должен отображаться в окне windi, ссылка оформляется следующим образом:

ГИПЕРТЕКСТНУРЕКТЕХТ>WIND1

Окно, указанное в тексте ссылки, имеет приоритет перед умалчиваемым окном, если оно определено сноской">" для раздела. Для нашего примера это означает, что окно windi будет использоваться для отображения ссылки гипертекст даже в том случае, если раздел hypertext имеет сноску “>”, в которой указано другое окно.

21.4. РАЗРАБОТКА ПРОЕКТНОГО ФАЙЛА

Проектный файл служит основным управляющим документом для Help-компилятора. В Windows 32 он создается с помощью утилиты Microsoft Help Workshop (MS HW) и представляет собой AS-CII-текстовый файл, содержащий несколько секций. Секция - это фрагмент текста, состоящий из заголовка и одной или нескольких следующих за ним строк (опций) вида

ИМЯ_ПАРАМЕТРА = ЗНАЧЕНИЕ,

подобно тому как организованы файлы инициации ini.

Для запуска MS HW следует загрузить файл help | tools | hcw.exe из папки размещения Delphi. Если файл проекта уже был ранее создан, для работы с ним используется опция File | open или соответствующая кнопка инструментальной панели MS HW. Для создания нового проектного файла выберите File | New и в дополнительном диалоговом окне - Help project; после указания имени создаваемого Help-файла в стандартном диалоговом окне save File окно MS HW приобретет вид, показанный на рис. 21.6.

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

21.4.1. Секция OPTIONS

Секция [Options] создается или изменяется с помощью кнопки options. Оно содержит общие для всей справочной службы установки и, в частности, определяет умалчиваемую тему, показываемую при открытии HELP-службы, заголовок основного окна, используемый в текстах язык и т. п.

После нажатия кнопки options появляется многостраничное окно, показанное на рис. 21.7.

Рис. 21.6. Окно утилиты Microsoft Help Workshop

Страница General

Default topic - идентификатор умалчиваемого раздела;

Help title - заголовок основного окна справочной службы; замечу, что мне не удалось получить заметных результатов, связанных с изменением этой строки, так что для изменения заголовка основного окна пришлось использовать дополнительное окно main (см. ниже п. 21.4.3); заголовок можно также изменить при создании файла содержания (см. п. 21.5);

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

Progress - если переключатель выбран, компилятор сообщает о транслируемом -RTF-файле;

Errors - открывает окно для указания кодов ошибок, о которых компилятор не будет сообщать;

Display this text... - определяет текст, который будет помещен

В окно Справка | Версия справочной службы;

Рис. 21.7. Окно установки параметров секции Options

if users paste... - определяет текст, который будет добавляться в конце копируемой через Clipboard или печатаемой справочной информации.

Страница Compession

Эта страница управляет сжатием результирующего файла.

None - результирующий файл не сжимается;

Maximum - результирующий файл максимально сжимается;

Custom - выбирает алгоритм сжатия результирующего файла.

Страница Sorting

Задает язык и способ сортировки ключевых слов.

Language of Help file - определяет используемый для сортировки ключевых слов язык;

Other - определяет язык сортировки по его индексу;

Non-spacing marks - если переключатель выбран, при сортировке игнорируются кавычки и апострофы; Symbols - если переключатель выбран, при сортировке игнорируются пробелы и знаки препинания; separate index... - содержит перечень символов, которые используются для указания нижних уровней сортировки.

Страница Files

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

Help File - имя результирующего нlр-файла; это имя должно быть указано обязательно;

Log File - имя ASCII-текстового файла, в который компилятор будет помещать свою информацию (может не указываться);

RTF-files - определяет имена исходных файлов с текстами справок; замечу, что изменение имен файлов фактически приводит к изменению секции [Files] (см. ниже п. 21.4.2);

contents file - указывает имя файла содержания, если такой файл создан (см. п. 21.5);

тмр folder - определяет папку для хранения временных файлов компилятора;

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

Страница FTS

Управляет созданием индекса для быстрого поиска по отдельным словам или фразам справочного текста. Этот индекс создается при первом обращении к окну поиск.

Generate full text search index - Создавать индекс по всем словам справочных фалов;

Include untitled topics in index - включает в индекс разделы без названия;

Enable search for word only - разрешить поиск только по словам;

Enable search for phrase - разрешить поиск по фразам;

Enable display of matching phrase - разрешить поиск по списку выделенных фраз;

Enable search for similar topics - разрешить поиск по альтернативным словам.

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

Страница Macros

Определяет макрокоманды, автоматически выполняемые при выборе того или иного ключевого слова.

Keywords - содержит список ключевых слов, для которых определены макрокоманды;

Title that appears... - указывает заголовок окна Найденные разделы при выборе соответствующего слова;

Macro (s) associated with... - связанная со словом макрокоманда;

Add - позволяет добавить слово к списку Keywords;

Remove - удаляет слово из списка Keywords;

Edit - изменяет определение макрокоманды или заголовка для указанного слова.

Страница Build Tags

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

Страница Fonts

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

character set - определяет используемый в результирующем файле набор символов (он может отличаться от набора символов на инструментальной машине);

Font in winHelp Dialog boxes - определяет шрифт, используемый в диалоговых окнах справочной службы;

substitute this fonts.. - содержит перечень подстановок, указывающих, какой шрифт в исходном RTF-файле будет заменен при отображении в окне справки и на какой шрифт он будет заменен.

21.4.2. Секция FILES

С помощью этой секции определяются RTF-файлы, содержащие текст, справочной системы. Для нормальной работы компилятора

необходимо определить хотя бы один файл в этой секции. Замечу, что содержимое этой секции автоматически изменяется при изменении списка RTF-files на странице Files окна options (см. п. 21.4.1).

Справочная служба может состоять из множества RTF-файлов. В этом случае все они должны быть перечислены в секции [Files]. Компилятор разрешает задать в этом списке один или несколько ссылочных ASCII-текстовых файлов, каждая строка которых указывает нужный RTF-файл .(т. е. содержит маршрут доступа и имя файла).

21.4.3. Секция WINDOWS

Секция [windows] определяет используемый в справочной службе набор дополнительных окон. Каждое указанное в секции окно характеризуется своим типом, цветом, размерами, заголовком. Только имена перечисленных в секции окон могут указываться в ссылках и сносках “>” RTF-файлов.

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

Рис. 21.8. Диалоговое окно определения имени и типа дополнительного окна

В строке create a window named следует задать имя окна. Замечу, что, если указать имя main, установки, производимые далее, будут относиться к основному окну справочной службы (именно таким способом мне, например, удалось получить нужный заголовок основного окна). С помощью списка Based on this standard window можно выбрать один из трех возможных стандартных типов окна:

procedure - окно предназначено для вывода текстов процедур; оно позиционируется в правом верхнем углу экрана и при значительной ширине может своим правым краем выйти за пределы экрана;

Reference - обычное справочное окно; позиционируется в левом верхнем углу, занимает приблизительно 2/3 экрана по высоте и ширине;

Error message - окно предназначено для вывода сообщений об ошибках, позиционируется по центру экрана, занимает почти всю высоту экрана и 3/4 его ширины.

Замечу, что все три типа создают стандартные Windows-окна с “толстой” рамкой и заголовком, т. е. их положение и размеры пользователь может изменять по своему усмотрению, так что выбор типа не носит решающего характера. Кроме того, с помощью кнопки Auto sizer на странице position (см. ниже) можно визуально настроить положение и размеры окна.

Если хотя бы одно окно в секции windows уже определено или если диалоговое окно определения типа и имени окна (рис. 21.8) закрыто кнопкой ок, на экране появляется многостраничное окно, показанное на рис. 21.9.

Рис. 21.9. Диалоговое окно определения параметров дополнительных окон

Страница General

window type - позволяет выбрать одно из ранее определенных окон; все дальнейшие установки на странице General будут осуществляться для указанного в этой строке окна; подобные строки есть на всех других страницах окна window Properties;

Add - вызывает окно рис. 21.8 для добавления нового окна к списку окон;

Remove - удаляет окно из списка;

include - включает ASCII-текстовьгй файл со списком окон;

Title bar text - определяет заголовок окна;

comment - содержит произвольный комментарий; здесь, например, можно указать функциональность окна; текст комментария не включается в результирующий файл;

Auto-size height - если переключатель выбран, окно автоматически изменяет высоту в зависимости от разрешения экрана;

Maximize window - окно максимизируется по размеру экрана;

Keep Help window on top - окно всегда появляется поверх остальных окон.

Страница Position определяет положение и размеры окна в момент его появления на экране. тор - отступ левого верхнего угла от верхнего края экрана;

Left - отступ левого верхнего угла от левого края экрана;

Height - высота окна;

width - ширина окна;

Adjust for user screen resolution - если переключатель выбран, координаты задаются относительно виртуального экрана с разрешением 1024х1024; если не выбран, координаты указываются в пикселях;

Auto-sizer - после щелчка по этой кнопке появляется окно с кнопками ОК и Cancel для визуальной настройки его размеров и положения;

Default position - после щелчка по этой кнопке окну задаются умалчиваемые размеры.

Страница Buttons

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

Contents - кнопка Содержание;

Index - кнопка Индекс;

Find - кнопка Поиск;

Help Topics - кнопка Разделы;

Print - кнопка Печать;

Back - кнопка Назад;

Options — кнопка Параметры;

Brows - кнопки просмотра связанных тем.

Страница Color

Позволяет изменять цвет фиксированной (Nonscrolling area) и основной (Topic area) зоны окна. Изменения становятся доступны после щелчка по соответствующей кнопке Add.

Страница Macros

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

21.4.4. Секция BITMAPS

Эта секция определяет список папок, в которых компилятор будет искать графические файлы, встроенные в текст справки директивами bmc, bmi, bmr (см. п. 21.3.6). Если ни одна папка не определена, компилятор сможет подключить только те файлы, которые располагаются в том же каталоге, где создается нlр-файл.

21.4.5. Секция MAP

В этой секции следует определить числовые идентификаторы разделов, которые будут автоматически вызываться кнопками Help и клавишей F1 в работающей программе. Напомню, что все видимые компоненты Delphi имеют специальное свойство HeipContext, в которое можно поместить числовой идентификатор раздела справочной службы. Если соответствующий компонент имеет фокус ввода и пользователь нажимает кнопку F1, автоматически включается справочная система Windows, которая отыскивает в ньр-файле и показывает в справочном окне соответствующий раздел. Только разделы, указанные в секции [map] , могут быть контекстно-доступными из работающей программы. Остальные разделы можно просмотреть после вызова справки с помощью выбора ключевых слов, кнопками просмотра связанных разделов или по содержанию справочной службы.

После нажатия кнопки Map в окне MS HW появляется окно, показанное на рис. 21.10.

Рис. 21.10. Окно определения числовых идентификаторов разделов

В списке Map topics IDs to numeric values указываются уже определенные разделы в формате

идентификатор_темы=Неlр Сontext

Здесь идентификатор_темы - идентификатор, определенный сноской # в RTF-файле; HelpContext -присвоенный разделу числовой идентификатор, который можно помещать в свойство HelpContext визуального компонента.

Для задания идентификатора нового раздела или удаления раздела из списка Map используются соответственно кнопки Add и Remove. С помощью кнопки include можно указать ASCII-файл с перечнем идентификаторов разделов и присвоенными им числовыми идентификаторами. Кнопка Edit позволяет отредактировать элемент списка.

При разработке файлов справочной службы, включающих множество разделов, приходится запоминать (или распечатывать) многочисленные идентификаторы разделов, чтобы поставить им в соответствие числовые идентификаторы. Эту проблему можно упростить, если вы назначили идентификаторы такими же, как и названия разделов, заменив в них пробелы символами подчеркивания. В этом случае можно вызвать опцию File | Report, в появившемся диалоговом окне указать имя нlр-файла и имя файла для отчета, установить режим вывода названий (отметить переключатель Titles) и щелкнуть по кнопке Report - MS HW создаст текстовый файл с перечнем названий всех тем. Если этот файл не слишком велик, он будет тут же показан в окне MS HW, и вы сможете без труда вспомнить идентификаторы. Для примера на рис. 21.11 показано, как можно организовать окна MS HW для упрощения создания списка MAP.

Рис. 21.11. Использование отчета по названиям разделов для оргачишции списка MAP

21.4.6. Секция ALIASES

В этой секции можно указать псевдонимы идентификаторов разделов, с помощью которых можно модифицировать секцию Map без изменения RTF-файлов. Назначение кнопок Add, Remove, include и Edit такое же, как и в секции мар. После назначения псевдонима он может использоваться в секции мар вместо идентификатора раздела. Эта возможность облегчает модификацию справочной службы.

21.4.7. Секция Config

Секция [Config] предназначена для указания макрокоманд, которые будут выполняться в момент открытия справочной службы. С ее помощью можно также зарегистрировать подпрограммы из библиотеки) DLL, которые после этого могут использоваться наравне с макрокомандами.

21.4.8. Секция BAGGAGE

Кнопкой Data Files окна MS HW открывается диалоговое окно определения файлов, которые будет использовать справочная служба. Список этих файлов содержит секция [Baggage] проектного файла. Помимо RTF-файлов, которые обычно вставляются в секции [Files] (см. п. 21.4.2), здесь можно также указать файлы библиотек DLL, подпрограммы которых после этого могут использоваться как макрокоманды.

21.5. ФАЙЛ СОДЕРЖАНИЯ СПРАВОЧНОЙ СЛУЖБЫ

Содержание справочной службы оформляется в файле с расширением сыт и становится доступным после нажатия кнопки содержание в справочном окне. Для создания/редактирования содержания используется утилита MS HW.

Чтобы создать файл содержания, запустите MS HW, выберите File i New и затем Help Contents - на экране появится окно, изображенное на рис. 21.12.

В строках Default filename (and window) И Default title В верхней части окна нужно указать соответственно имя ньр-файла, для которого создается содержание, и тип окна для отображения оглавлений, а также заголовок основного окна справочной службы. Замечу, что заголовок Default title, если он задан, переопределяет заголовок, указанный в секции [Options] файла проекта (см. п. 21.4.1).

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

вводе в строке Default filename (and window) имя файла отделяется от имени окна символом “>”.

Например:

HelpTest.hlp>WIND

Рис. 21.12. Окно MS H W в режиме создания/редактирования содержания

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

Для вставки в файл элемента содержания нужно щелкнуть по кнопке Add Above или Add Below. В первом случае элемент вставляется перед текущим элементом, во втором - после него. После щелчка по любой из этих кнопок появляется окно, показанное на рис. 21.13.

Переключатели в верхней части окна определяют тип вставляемого элемента:

Heading - вставляется тема; при выборе этого переключателя все расположенные ниже строки, кроме Title, становятся недоступными;

Topic - вставляется раздел;

Macro - вставляется макрокоманда; эта макрокоманда будет выполняться при выборе элемента в окне содержания;

include - вставляется ASCII-текстовый файл с элементами содержания.

Рис. 21.13. Окно определения элемента оглавления

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

В строке Title вводится произвольный текст длиной до 127 символов, который будет представлять элемент в окне содержания. В этой строке также указывается имя включаемого файла, если выбран переключатель Include. Во второй строке указывается идентификатор раздела или имя макрокоманды. В строке Help file нужно указать ньр-файл, если справочная система использует несколько файлов, а в строке window type - тип окна для отображения раздела.

Для смещения элемента оглавления на один уровень иерархии вниз используется клавиша Move Right, а на один уровень вверх -Move Left. Элемент сдвигается вместе со всеми другими элементами, расположенными ниже него. Максимальное количество уровней иерархии - 9.

Следует заметить, что файл содержания представляет собой AS-СН-текстовый файл, каждая строка которого соответствует элементу содержания. В начале строки указывается цифра, определяющая уровень иерархии, на котором располагается элемент. На рис. 21.14 для примера показано содержание справочной службы What's New in Delphi и соответствующий ей сnт-файл.

Рис. 21.14. Содержание справочной службы Delphi:

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

21.6. КОМПИЛЯЦИЯ, ТЕСТИРОВАНИЕ И СВЯЗЬ С ПРОГРАММОЙ

Компиляция и тестирование справочной службы не составляют проблемы: с помощью MS HW создайте (или загрузите) проектный файл и щелкните по кнопке save and compile или по инструментальной кнопке. После этого появляется окно, показанное на рис. 21.15.

При компиляции MS HW сворачивает свое окно и вновь раскрывает его после завершения работы компилятора, если отмечен переключатель Minimize window while compiling. После окончания компиляции в нем будут показаны сообщения компилятора. Если компилятор обнаружил ошибки, он сообщает о них, причем некритические ошибки сопровождаются предупреждениями (warnings) и замечаниями (notes), а критические - прерывают компиляцию.

Рис. 21.15. Окно запуска компилятора

Для тестирования скомпилированного справочного файла используется одна из опций меню Test:

contents File - тестирует файл содержания;

close All Help - закрывает все ранее открытые нlр-файлы;

send a macro - посылает в winHelp нужную макрокоманду;

WinHelp api - вызывает нужный раздел справочной службы по присвоенному ему в секции map числовому идентификатору.

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

Связь с программой реализуется с помощью свойств HelpContext видимых компонентов, в которые следует поместить числовые идентификаторы нужных разделов справочной службы так, как они определены. В секции MAP. Кроме того, в свойство Application. HelpFile нужно поместить имя нlр-файла. Обычно эта связь устанавливается в обработчике события ОnCreat главной формы программы. После такой настройки пользователь программы сможет с помощью клавиши F1 получить контекстно-чувствительную справку, т. к. при нажатии F1 автоматически вызывается раздел, числовой идентификатор которого помещен в свойство HeipContext компонента с фокусом ввода. Если Heipcontext компонента с фокусом ввода содержит 0, вызывается раздел, указанный в HelpContext его владельца, а если и у того это свойство не определено, используется HelpContext активной формы (если во всей цепочке владельцев, включая активную форму, свойство HelpContext не определено, нажатие F1 игнорируется).

Рис. 21.16. Сообщение об ошибке вызова раздела

21.7. МАКРОКОМАНДЫ WINHELP

Справочная служба Windows 32 - WinHelp имеет встроенные макрокоманды, которые доступны из справочной службы программы. Макрокоманды могут выполняться в следующих случаях:

  • при открытии справочного файла (определяются в секции Соnfig);
  • при открытии окна с разделом (определяются сноской “!”);
  • при выборе ключевого слова (определяются на странице Macros окна options).

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

Макрокоманда | Назначение
Управление инструментальными кнопками
Back() | Соответствует нажатию кнопки назад. Идентификатор кнопки (id) - btn back
BackFlush О | Очищает список предыдущих разделов кнопки Назад
BrowsButtons () ChangeButtonBuilding(ID,macro) ChangeEnable(ID,macro) CreateButton(ID,title,macro) | Вставляет кнопки последовательного просмотра связанных разделов Для кнопки id определяет макрокоманду macro то же, что ChangeButtonBuilding, дополниттельно отменяет запрет кнопки Создает кнопку с идентификатором id и надписью title; назначает кнопке макрокоманду macro
DestroyButton (ID) DisableButton(ID) EnableButton(ID) Contents ( ) | Удаляет кнопку с идентификатором id Запрещает выбор кнопки с идентификатором id Разрешает выбор кнопки с идентификатором id Соответствует нажатию кнопки содержание. Id кнопки-btn contents
FindO | Соответствует нажатию кнопки Поиск. id кнопки-btn find
Finder () | Соответствует нажатию кнопки Разделы. id
Menu () | кнопки - btntopics Соответствует нажатию кнопки параметры Id кнопки - btn menu
Next() | Соответствует нажатию кнопки >.. id кнопки -btn next
Prev() | Соответствует нажатию кнопки <. id кнопки -btn previous
Print () | Соответствует нажатию кнопки Печать. id
Search () | кнопки-btn print Соответствует нажатию кнопки Указатель. id кнопки-btn search

Макрокоманды переходов

ALink(keyword[,type[,ID[.win- name] ] ] ) | Переход по ключевому слову keyword из дополнительной таблицы слов на раздел с идентифи катором id. winname - тип окна для показа раздела. Параметр type определяет дополнительное действие (см. ниже примечание 1)
JumpContents(filename) | Переход на умалчиваемый раздел, определенный для файла filename
JumpContext([[filename>]winname, ]NID) | Переход на раздел с числовым идентификатором nid, который указан в секции map
JumpHash([[filename>]winname,] HID) | Переход на раздел с внутренним числовым идентификатором hid (см. примечание 2)
JumpID([[filename>]winname,]ID) | Переход на раздел с идентификатором id
JumpKeyword([filename,]keyword) | Переход на раздел по ключевому слову keyword В файле filename
KLink(keyword[,type[,ID[,winname] ] ]) | Переход по ключевому слову keyword на раздел с идентификатором id. winname - тип окна для показа раздела. Параметр type определяет дополнительное действие (см. ниже примечание 1)
PopupContext([filename,]NID) | Переход на раздел с числовым идентификатором nid, который указан в секции map. Показывает раздел в окне пояснений
PopupHash([filename,]HID) | Переход на раздел с внутренним числовым идентификатором hid (см. примечание 2). Показывает раздел в окне пояснений
PopupID([filename,]ID) SetContents(filename,ID) TestALink("keyword") TestKLink("keyword") UpdateWindow([filename>]win-name, ID) | Переход на раздел с идентификатором id. Показывает раздел в окне пояснений Определяет раздел с идентификатором id как содержание справочной службы Выполняет ALink для ключевого слова keyword и возвращает 1, если переход возможен, или 0, если ни один раздел не найден Выполняет KLink для ключевого слова keyword и возвращает 1, если переход возможен, или 0, если ни один раздел не найден То же, что JumpiD, но активным остается прежнее окно

Работа с меню

About () | Эквивалент выбору опции Справка | версия
AppendItem(menuID,itemID, Title,macro) | Вставляет опцию с надписью Title в подменю menuiD и связывает с ней макрокоманду macro itemID - идентификатор новой опции
Annotate () | Эквивалент выбору опции правка Заметки.
BookmarkDefine () | Эквивалент выбору опции Закладка | Определить
ChangeItemBinding(itemIDmacro) | Назначает опции с идентификатором itemID макрокоманду macro
Checkltem(itemID) | Помещает значок отметки рядом с опцией itemID
CopyTopic () | Эквивалент выбору опции Правка Копировать
Deleteltem(itemID) | Удаляет опцию itemID
Disableltem(itemID) | Запрещает выбор опции itemid
Enableltem(itemID) | Разрешает выбор опции itemid
Exit() | Эквивалент выбору опции Файл | выход
FileOpen () | Эквивалент выбору опции Файл | Открыть
FloatingMenu() | Вывод вспомогательного меню (эквивалент щелчку правой кнопкой мыши)
HelpOnTopO | Эквивалент выбору опции Параметры | Размещение окна | Поверх остальных
Histopy() | Эквивалент выбору опции параметры | Показать окно хронологии
InitMPrint() InsertItem(menuID,itemID, Title,macro,position) | Вставляет элемент меню itemID в меню menuiD
ResetMenu () | Устанавливает умалчиваемое меню

Команды условного перехода

IFThen(macro,macrol) | Если макрокоманда выполняет macrol иначе ничего не делает
IFThenElse(macro,macrol,macro2) | Если макрокоманда macro возвращает 1, выполняет macrol иначе выполняет macro2
Not(macro) macro возвращает 1, | Инвертирует результат, возвращаемый macro

Примечание 1. Параметр type в командах поиска ALink, KLink может иметь несколько следующих возможных значений, разделенных пробелом:

0 | Выводится окно с названиями наиденных разделов
1 | Если найден только один раздел, он показывается в справочном окне
2 | Если разделы находятся в разных файлах, в окне поиска указываются имена файлов вместе с названиями разделов
4 | Если найден хотя бы один раздел, макрокоманда возвращает 1

Примечание 2. Внутренний числовой идентификатор создается компилятором по идентификатору раздела. Его можно увидеть в окне File | Report после щелчка по кнопке Report при установленном переключателе Hash number.


Приложение 1. СРЕДА РАЗРАБОТЧИКА

  • Приложение 1. СРЕДА РАЗРАБОТЧИКА
    • ГЛАВНОЕ МЕНЮ
      • Опция File
      • Опция Edit
      • Опция Search
      • Опция View
      • Опция Project
      • Опция Run
      • Опция Component
      • Опция Database
      • Опция Tools
      • Опция Help
    • РАБОТА С РЕДАКТОРОМ
      • Команды перемещения курсора
      • Команды удаления/вставки
      • Команды работы с блоками
      • Прочие команды
      • Интеллектуальные возможности редактора
    • ОТЛАДКА ПРОГРАММ
      • Точки контрольного останова
      • Окно наблюдения
      • Принудительное прерывание работы программы
      • Трассировка программы
      • Действия в точках прерывания
      • Группировка точек прерывания
      • Вычисление выражений и изменение значений
      • Ведение протокола работы программы

Приложение 1

СРЕДА РАЗРАБОТЧИКА

П 1.1. ГЛАВНОЕ МЕНЮ

П 1.1.1. Опция File

New | Опция-заголовок. При ее выборе раскрывается подменю со следующими опциями
Application | Создает новую программу для Windows
CLX Application | Создает новую программу для Windows и Linux
Data Module | Создает новый модуль данных
Form | Создает новую форму и подключает ее к проекту
Frame | Создает новую раму
Other | Открывает окно репозитория (рис. П1.1).
Exit | Вставляет в текущую форму ссылку на другой модуль Печатает активную форму или модуль Прекращает работу Delphi
Print | Закрывает все открытые файлы
Unit | Закрывает текущую форму
Open | Открывает ранее созданную форму
Open Project | Открывает ранее созданный проект
Reopen | Вызывает список ранее загружавшихся проектов и форм для выбора и повторной загрузки
Save Save As | Сохраняет активную форму
Save Project As | Сохраняет активную форму под другим именем
Save All Close | Сохраняет файл проекта под другим именем
Close All Use | Сохраняет файл проекта и все открытые модули

П1.1.1.1. Репозиторий Delphi

Окно репозитория Delphi открывается при выборе File | New | Other и имеет вид, показанный на рис. П1.1.

На его страницах расположены пиктограммы для выбора прототипов форм, модулей, проектов и экспертов построения форм и проектов. Зависимые переключатели Copy, Inherit и Use определяют режим связи между хранящимся в репозитории прототипом и его копией в проекте: Copy - выбранный элемент копируется в текущий каталог и автоматически подключается к проекту; между образцом и его копией нет никакой связи; inherit - в проекте создаются наследники выбранного элемента и всех его родителей; любые изменения образца проявляются во всех проектах, которые его унаследовали; изменения наследника не влияют на образец; Use - выбранный элемент становится частью проекта; любые его изменения в проекте приводят к изменениям образца и сказываются во всех других проектах, которые его унаследовали или используют.

Ниже кратко описывается методика размещения в репозитории разработанной программистом формы.

Рис.П 1.l. Окно репозитория Delphi

  1. Разработайте форму, которую вы часто будете использовать в разных проектах[Если вы хотите тиражировать какую-то форму текущего проекта, ее нет нужды размещать в репозитории - она там уже есть на закладке с именем вашего проекта (такая закладка репозитория автоматически создается для каждого открываемого проекта).]. Поместите на ней все необходимые интерфейсные компоненты и, если это необходимо, закодируйте обработчики их событий.
  2. Сохраните форму в любой папке жесткого диска. Для упорядочения поиска прототипов можно использовать для этих целей папку Objrepos каталога размещения Delphi.
  3. Щелкните по форме правой кнопкой мыши и в локальном меню выберите Add to Repository. На экране появится диалоговое окно регистрации формы в репозитории (рис. П1.2).
  4. В строке Title напишите имя, под которым форма будет зарегистрирована в репозитории, например, WorkForm, в строке Description - описание формы, например. Основная форма для проекта Bibliophil, в строке Author - свое имя. Раскройте список Page и выберите закладку страницы репозитория, на которой следует разместить форму, после чего щелкните по кнопке ок - форма помещена в репозитории. Замечу, что, если вы не укажете страницу, форма не будет размещена в репозитории, однако вы сможете ее найти на странице, связанной с закладкой с именем вашего проекта. Тиражирование такой формы, как и любой другой формы этой страницы, возможно только в рамках вашего проекта.

Рис. П1.2. Окно регистрации формы в репозитории

Вновь размещенную форму можно сделать главной. Такая форма создается при старте нового проекта (опция главного меню File | New Application). Для определения главной формы выберите опцию Tools [Repository, укажите страницу размещения в левом списке Радез диалогового окна и форму в правом списке Objects, после чего установите флажок в переключателе Main Form. Если установить флажок в переключателе New Form, форма будет создаваться при щелчке по пиктографической кнопке New Form или при выборе опции File | New Form

П 1.1.2. Опция Edit

Undo | Отменяет последнее изменение проекта
Redo | Восстанавливает последнее изменение проекта
Cut | Вырезает выбранный компонент формы или фрагмент текста и помещает его в буфер Clipboard
Copy | Копирует в Clipboard выделенные компоненты формы или фрагмент текста модуля
Paste | Извлекает из буфера и переносит компоненты на форму или копирует текст в модуль (в позицию, указываемую текущим положением текстового курсора)
Delete | Удаляет выделенные компоненты или фрагмент текста
Select All | Выделяет все компоненты формы или весь текст модуля
Align To Grid | Привязывает выделенные компоненты к масштабной сетке так, чтобы их левые верхние углы располагались в ближайших точках сетки
Bring To Front | Перемещает выделенные компоненты на передний план
Send To Back | Перемещает выделенные компоненты на задний план
Align | Вызывает окно выравнивания выделенных компонентов (рис.П1.3)
Size | Вызывает окно изменения размеров выделенных компонентов(рис. П 1.4)
Scale | Масштабирует выделенные компоненты (рис. П1.5)
Tab Order | Изменяет порядок обхода компонентов клавишей Tab
Creation Order | Изменяет порядок создания невизуальных компонентов
Flip Children Lock Controls Add to interface | Изменяет свойство BiDiMode для всех или только для выделенных компонентов Блокирует возможность перемещения компонентов на форме Определяет новые свойства, методы и события для компонентов ActiveX

П1.1.2.1. Управление группой компонентов

Показанные ниже окна (рис. П1.3, П1.4 и П1.5) обрабатывают группу выделенных на форме компонентов по образу (или относительно) первого выделенного. Чтобы выделить группу компонентов, нужно нажать и удерживать клавишу Shift, после чего щелкать по компонентам левой кнопкой мыши. Каждый отмеченный таким образом компонент выделяется серыми квадратиками по углам его видимых границ. Затем вызываются опции Align, size или Scale для соответственно выравнивания, изменения размеров или масштабирования компонентов. Другой вариант - не отпуская Shift, щелкнуть правой кнопкой мыши по любому свободному месту формы и выбрать нужную опцию в локальном меню.

Рис. П1.3. Окно выравнивания компонентов

Переключатели этого окна определяют выравнивание всех выделенных компонентов относительно самого первого выделенного компонента (эталона):

  • Left sides - компоненты выравниваются по левой границе;
  • Centers - компоненты центрируются относительно границ эталона;
  • Right sides-компоненты выравниваются по правой границе эталона;
  • Space equally - между всеми компонентами устанавливается равное расстояние по горизонтали или вертикали;
  • Center in window-все компоненты центрируются относительно границ окна;
  • Tops - компоненты выравниваются по верхнему краю;
  • Bottoms - компоненты выравниваются по нижнему краю.

Группа переключателей width регулирует ширину выделенных компонентов, а группа Height - их высоту:

  • Shrink to smallest-уменьшает размеры компонентов до размеров наименьшего из-них;
  • Grow to largest-увеличивает размеры компонентов до размеров наибольшего из них;
  • Width - указывает ширину компонентов;
  • Height - указывает высоту компонентов.

Рис. П1.4. Окно изменения размеров компонентов

С помощью окна Scaling factor вводится коэффициент масштабирования в процентах от текущих размеров.

Рис. П 1.5. Окно масштабирования компонентов

П 1.1.3. Опция Search

Find | Ищет фрагмент текста и подсвечивает его, если он найден
Find In Files | Ищет фрагмент текста во всех файлах проекта, или только в открытых файлах, или, наконец,
Replace | Ищет и заменяет фрагмент текста во всех файлах текущего каталога
Search Again | Повторяет поиск или поиск и замену
Incremental Search | Ищет текст по мере его ввода - сначала первую букву, затем две первые буквы и т. д.
Go to Line Number | Перемещает курсор на строку с указанным номером от начала файла
Browse Symbol | Показывает место возникновением определения символа программы (опция доступна только после успешного прогона программы). Символом считается любой глобальный идентификатор вашего проекта
Find Error | По адресу ошибки периода прогона программы отыскивает фрагмент кода, связанный с ее

Go to Line Number Find Error Browse Symbol

П 1.1.4. Опция View

Project Manager

Показывает окно Менеджера проекта

Translation Manager

Открывает доступ к Менеджеру трансляций Показывает окно Инспектора объектов

Object Inspector

Показывает окно

Object TreeView

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

To_Do List

Открывает доступ к списку To-Do

Alignment Palette

Показывает окно палитры выравнивания компонентов

Browser

Показывает окно браузера объектов

Code Explorer

Показывает спрятанное ранее окно Навигатора кода

Component List

Показывает окно для выбора компонентов

Window List

Показывает окно открытых окон проекта

Debug Windows

Отладочные окна. Эта опция-заголовок открывает доступ к подменю со следующими опциями

Breakpoints

Показывает окно точек останова

Call Stack

Показывает окно стека

Watching expressions

Показывает окно наблюдения за переменными/выражениями

Local Variables

Позволяет наблюдать за изменениями локальных переменных в отладочном режиме

Threads

Показывает окно статуса потоков команд

Modules

Показывает окно модулей проекта

Event Log

Показывает журнал событии

CPU

Показывает состояние регистров центрального процессора

FPU

Показывает состояние регистров арифметического сопроцессора

Desktops

Управляет конфигурациями основных окон. Эта опция-заголовок открывает доступ к подменю со следующими опциями

Save Desktop

Сохраняет текущую конфигурацию

Delete

Удаляет ранее сохраненную конфигурацию

Save Debug Desktop

Определяет текущую конфигурацию как отладочную

Toggle Form/Unit

Переключает активность из окна формы в окно кода программы и обратно

Units

Показывает окно модулей

Forms

Показывает окно форм

Type Library

Показывает окно библиотеки типов

New Edit Window

Открывает новое окно с кодом текущего модуля

Toolbars

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

View as Form/Text

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

Next Window

Показывает следующий присоединенный к проекту модуль

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

П 1.1.4.1. Менеджер проекта

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

С помощью менеджера проектов осуществляется компиляция проекта на тот или иной язык локализации (см. ниже П 1.1.4.2). Если для проекта определены языки локализации, в группе проектов помимо исполняемого файла хххх.ехе будут дополнительные проекты с тем же именем, но разными расширениями (по одному проекту на каждый язык локализации). Если, например, язык локализации английский (Великобритания), в группе будет проект xxxx.eng. Сделайте активным нужный проект и вызовите опцию Project | Build хххх (хххх - имя вашего проекта). В результате будет откомпилирован нужный ресурсный файл. После, этого вновь активизируйте проект хххх.ехе, с помощью опции proj-ect | Language | Set Active укажите язык локализации и вновь откомпилируйте проект. К нему будет подключен нужный ресурсный файл, и ваша программа станет локализованной.

Рис. ПI.6. Окно менеджера проекта

П1.1.4.2. Менеджер трансляции

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

Для выбора языка (языков) локализации используется опция меню Project [Language (см. с. 619). Главным языком программы по умолчанию считается язык локализации Windows, так что если Delphi работает под управлением русскоязычной Windows, главным языком будет русский.

Окно менеджера трансляций показано на рис. П1.7. Для представленного на нем примера главным языком программы является русский, а языком локализации - английский (Великобритания). Переводится надпись Введите пароль (Enter password).

Рис. П1.7. Окно менеджера трансляций

П 1.1.4.3. Список ТО-DO

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

Этот список содержит все комментарии проекта, которые начинаются символами //todo: (рис. П1.8).

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

После вызова списка то-do переход к нужному комментарию осуществляется двойным щелчком мыщи на соответствующей строке списка.

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

Рис. П 1.8. Пример списка ТО-DO

В списке можно указать приоритет сообщения (колонка “! ”), его собственника (Owner) и категорию.

Эти параметры можно задать с помощью соответствующих ключей непосредственно в комментарии (см. рис. П1.8) или после вызова редактора сообщения в списке (он вызывается после активизации сообщения и нажатия клавиши F2).

П 1.1.4.4. Браузер объектов

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

П 1.1.5. Опция Project

Lanages | Позволяет добавить новый, удалить или сделать главным один из существующих языков локализации
View Source | Показывает окно с кодом проекта Опция-заголовок.
Add To Repository | Помещает проект в репозиторий
Import Type Library | Импортирует в проект библиотеку типов элементов ActiveX
Remove From Project | Удаляет файл из проекта
Add To Project | Добавляет файл к проекту
Add Remove Set Active Update Resources DLL | Добавляет новый язык локализации Удаляет существующий язык локализации Делает активным язык локализации Создает заново ресурсные DLL, управляющие локализацией программы
Add New Project | Добавляет программу, DLL или пакет к текущей проектной группе
Add Exists Project | Открывает проект и добавляет его к текущей проектной группе
Compile Project! | Компилирует модули, которые изменились с момента предыдущей компиляции проекта
Build Projecti | Компилирует все модули проекта и создает исполняемую программу
Syntax Check Project1 | Проверяет синтаксическую правильность программы
Information | Показывает информацию о вашей программе
Compile All Projects | Компилирует все файлы данной проектной группы, которые изменились с момента предыдущей компиляции
Build All Projects | Компилирует все файлы данной проектной группы независимо от того, изменялись ли они или нет с момента последней компиляции
Web Deployment Options | Устанавливает ActiveX компонент или ActiveForm на вашем Web-сервере. Вызывается перед компиляцией проекта
Web Deploy | Устанавливает ActiveX компонент или ActiveForm на вашем Web-сервере. Вызывается после компиляцией проекта
Options | Показывает диалоговое окно установки параметров проекта

П 1.1.4.1. Выбор языков локализации

После выбора Project [Languages I Add вызывается окно эксперта выбора языков локализации проекта (рис. П1.9).

Рис. П 1.9. Окно выбора языков локализации

В нем указывается один или несколько проектов, для которых осуществляется локализация, и главный язык проекта (код $419 соответствует русскому языку). В следующем окне (после щелчка по кнопке Next) вы сможете выбрать языки локализации, отметив их флажками. На следующем шаге эксперт предложит вам указать каталоги размещения библиотек, которые будут содержать локализованные ресурсы. Все библиотеки имеют одинаковое название хххх. drc (хххх - имя проекта), поэтому они должны размещаться в разных каталогах. Для компиляции программы с тем или иным языком локализации используются менеджер проекта и опция Project | Language I Set Active.

П 1.1.4.2. Управление опциями проекта

Управление опциями проекта осуществляется с помощью диалогового окна, вызываемого опцией Project | Options.

На странице Forms окна опций проекта (рис. ГЛ. 11) указывается главная форма проекта, а также автоматически создаваемые формы (Auto-create forms) и доступные проекту формы (Available forms). Кнопки между панелями этого окна позволяют переносить формы из одной панели в другую. Переключатель Default разрешает/запрещает использовать текущие установки страницы как умалчиваемые для других проектов.

Рис. П1.10. Окно опций проекта

На странице Application указывается подпись под пиктограммой свернутой программы (Title), сама пиктограмма (Icon) и имя Help-файла (Help file).

На странице Compiler собраны переключатели, управляющие параметрами процесса компиляции. В том числе (в фигурных скобках указана соответствующая директива компилятора): Optimizations - включает режим оптимизации {$0}, Aligned record fields - размещает данные, выравнивая их на границу 32-разрядного слова {$а}; Stack frames - заставляет компилятор создавать стековые рамы для всех процедур и функций {$W}. Pentium-Safe fdiv - вырабатывает код, предохраняющий от ошибок в вещественных вычислениях на процессорах Pentium ранних выпусков {$U}; Range Checking - создает код проверки выхода за границы массивов {$R}; l/o Checking создает код проверки корректности выполнения операций ввода/вывода {$I}; Overflow checking - вырабатывает код проверки переполнения при выполнении целочисленных операций ($Q}. Strict var-Strings - определяет строгую проверку соответствия строковых типов при обращении к подпрограммам {$V}. Complete Boolean Eva1 - определяет полное вычисление логических выражений {$B}; Extended Syntax - включает расширенный синтаксис Object Pascal {$xf; Typed @ Operator - контролирует типы указателей в операции @ {$Т}; open Parameters - разрешает использование открытых параметров в подпрограммах {$р}. Huge Strings - связывает зарезервированное слово String с длинными строками {$Н}; Assignable Typed Constants - разрешает присваивание типизированным константам {$J}; Debug information - помещает в DCU-файл отладочную информацию {$D}; Local Symbols - создает отладочную информацию о локальных символах программы {$l};

Symbol information - создает отладочную информацию о символах программы {$y}, Show Hints - заставляет компилятор выдавать рекомендации; Show Warnings - заставляет компилятор выдавать предупреждающие сообщения; Assertions - заставляет вырабатывать код для отладочных процедур Assertion {$C}.

Страница Linker определяет параметры компоновщика, в том числе: Off - запрещает создавать карту распределения памяти; Segments - карта содержит список модулей и адреса точек входа всех подпрограмм; Publics - дополняет Segments отсортированным списком символов секций public; Detailed - дополняет Public детальной информацией о модулях; Generate dcus - создает стандартные для Delphi DCU-фаЙлы; Generate С Object files - создает файлы в формате объектов языка С; Generate C++ Object files - создает файлы для связывания с программой, созданной С помощью C++ Builder; Generate Console Application - создает консольную программу; include TD32 Debug info - помещает в исполняемый файл информацию для внешнего отладчика; include remote debug symbols-используется для удаленного вызова отладчика; Min Stack Size-устанавливает минимальный размер стека; мах Stack size - устанавливает максимальный размер стека; image Base - указывает начальный адрес для загрузки изображений (для dll); exe Descriptor - информационная строка длиной до 255 символов, которая включается в исполняемый файл (например, для объявления авторских прав).

Страница Directories/Conditionals задает каталоги размещения и условные символы: Output Directory - указывает каталог размещения исполняемого файла; Unit Output Directory - указывает каталог размещения Dcu-файлов;

Search Path - каталог размещения файловых с исходными текстами программы, если они не обнаружены в текущем каталоге; при необходимости указать несколько каталогов в любом из описанных выше окон они разделяются точкой с запятой; Debug source path - определяет каталог размещения внешнего отладчика; bpl output directory - указывает папку размещения файлов компиляции пакетов; dcp output directory - определяет каталог размещения файлов для локализации программ; Conditional Defines - определяет символы для условной компиляции; Unit Aliases-определяет псевдонимы модулей.

На странице Versioninfo сосредоточены параметры управления информацией о версии программы: include version information in project - если переключатель выбран, в проект включается информация о версии программы, которую можно прочитать после щелчка правой кнопкой мыши на пиктограмме программы и выборе Properties; Module Version Number - поля Major, Minor, Release, Build определяют составной номер версии; Auto-increment build number - если переключатель активен, номер версии автоматически наращивается при каждой компиляции программы; Debug Build - указывает на создание отладочной версии программы; Pre-Release - указывает на создание некоммерческой версии программы; Special Build - казывает на специальную версию программы; Private Build - указывает на версию, не предназначенную для широкого распространения; dll - создается динамическая библиотека; Language id - идентификатор языка, на который рассчитана программа.

П 1.1.5. Опция Run

Run | Компилирует программу и делает ее прогон
Attach to Process | Позволяет присоединиться в режиме отладки к одному из уже запущенных процессов на другой сетевой машине
Parameters | Указывает командную строку запуска вашей программы
Register ActiveX Servers | Регистрирует ваш проект в реестре Windows. Опция доступна для ActiveX-проектов
Unregister ActiveX Servers | Удаляет ваш проект из реестра Windows. Опция доступна для ActiveX-проектов
Install MTS Objects | Регистрирует в вашем проекте объект MTS
Step Over | В отладочном режиме выполняет текущую строку кода и не прослеживает работу вызываемых подпрограмм
Trace Into | В отладочном режиме выполняет текущую строку кода и прослеживает работу вызываемых подпрограмм
Trace To Next Source Line | Программа выполняется до ближайшего от текущего положения курсора исполняемого оператора
Run To Cursor | В отладочном режиме выполняет программу и останавливается перед выполнением кода в строке с текстовым курсором
Run Until Return | В отладочном режиме выполняет текущую подпрограмму и останавливается
Show Execution Point | Отображает в окне кода оператор, на котором было прервано выполнение программы
Program Pause | Приостанавливает прогон отлаживаемой программы
Program Reset | Прекращает прогон программы и восстанавливает ре жим конструирования программы
Inspect | Открывает окно проверки текущего значения
Evaluate/Modify | Открывает окно проверки/изменения переменных
Add Watch | Добавляет переменную или выражение в окно наблюдения
Add Breakpoint | Добавляет точку останова

П 1.1.6. Опция Component

New Component | Открывает окно эксперта компонентов
Install Component | Помещает компонент в существующий или новый пакет
Import ActiveX Control | Добавляет к проекту библиотеку типов ActiveX-компонентов
Create Component Template | Помещает шаблон в палитру компонентов
Install Packages | Указывает пакеты, необходимые на этапе конструирования и прогона программы
Configure Palette | Вызывает диалоговое окно настройки палитры компонентов

П 1.1.7. Опция Database

Explore | Вызывает инструмент исследования баз данных - Database Explorer или SQL Explorer (в зависимости от версии Delphi)
SQL Monitor | Вызывает инструмент запросов к БД - SQL Monitor
Form Wizard | Вызывает окно эксперта форм для создания формы, отображающей наборы данных из удаленных или локальных БД

П 1.1.8. Опция Tools

Environment Options | Вызывает окно настройки параметров среды Delphi и ее инструментов
Editor Options | Вызывает окно настройки параметров редактора Delphi
Debugger Options | Вызывает окно настройки параметров отладчика Delphi

П 1.1.8.1. Настройка параметров среды

Диалоговое окно настройки параметров среды вызывается опцией

Tools I Environment Options (рис. П1.11).

Рис. П 1.11. Окно настройки параметров среды

Закладка Preferences открывает доступ к параметрам среды Delphi: Editor files - перед прогоном автоматически сохраняются все измененные файлы; Project Desktop - перед прогоном автоматически сохраняется информация о состоянии экрана; Desktop Only - при выходе из программы сохраняется информация о состоянии экрана, Desktop and Symbols - при выходе из программы сохраняется информация о состоянии экрана и символах программы на момент последней удачной компиляции; Auto drag docking - разрешает причаливать одно инструментальное окно к другому; Show compiler progress - показывать окно отображения процесса компиляции; Warn on package rebuild - предупреждать о перекомпиляции пакетов; Minimize On Run - минимизировать окна Delphi в момент старта программы; Hide Designers On Run - прятать вспомогательные окна (окно Инспектора объектов и окна форм) в момент старта программы; Directory -содержит путь к окну расположения файла репозитория DELPHI32.DRO; если путь не указан, используется каталог bin каталога размещения Delphi.

Закладка Designer содержит настройки для процесса конструирования форм:

Display Grid - показывать сетку на пустой форме; Snap to Grid - привязывать расположение компонентов к узлам сетки; Show component captions - показывать имена компонентов на этапе конструирования программы; show designer hints - показывать оперативную подсказку об именах компонентов и их типах на этапе конструирования; show extended control hints - показывать ярлычки оперативной подсказки с расширенной информацией; New Forms as Text - сохранять файлы описания форм в текстовом формате; Auto create forms & data modules - при выборе переключателя каждая добавляемая к проекту форма или модуль данных помещается в список автоматически создаваемых форм, в противном случае - в список доступных форм; Grid size x - горизонтальный шаг сетки; Grid size Y - вертикальный шаг сетки.

Закладка Object Inspector открывает окно настройки параметров Инспектора объектов: SpeedSettings - позволяет выбрать один из следующих вариантов настройки: Custom colors & settings (настраиваемые цвета и установки). Default colors & settings (умалчиваемые цвета и установки), Delphi 5 emulation (эмуляция Delphi 5), Visual Studio (TM) emulation (эмуляция Visual Studio); Colors - позволяет настроить цветовыделение опций Инспектора объектов; Expand inline - раскрывать свойства и методы внешнего объекта; Show on events page -показывать внешний объект на странице событий.

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

Закладка Library определяет каталоги размещения библиотек Delphi: Library path - маршрут поиска библиотечный файлов; bpl output library - выходной каталог для размещения BPL-файлов; dcp output library - выходной каталог для размещения DCP-файлов; Browsing Path - каталоги для, браузера.

Закладка Explorer позволяет настроить свойства кодового браузера, окно которого обычно “причалено” к окну редактора кода. Automatically show Explorer - если этот переключатель отмечен, окно кодового браузера появляется при создании каждого нового проекта; Highlight incomplete class items - выделяет цветом незавершенные определения классов; show declaration syntax - позволяет помимо имен элементов показывать свойства и методы; Explorer sorting- определяет способ сортировки элементов (по алфавиту или по порядку объявления). Finish incomplete properties - после отметки завершает определения не только классов, но и свойств. Переключатели initial browser view позволяют выбрать приоритет показа классов, модулей или глобальных определений.

Закладка Type Library служит для управления свойствами редактора библиотеки типов (используется при разработке многозвенных приложений баз данных).

Закладка Environment variables определяет т. н. переменные среды разработки, в том числе тип ОС, положение основных файлов ОС и т. п.

С помощью небольшой странички Delphi Direct можно управлять автоматической связью с сайтом Delphi для обновления информации об этом программном продукте.

Закладка internet управляет форматами создаваемых файлов для передачи по Интернет (интранет).

Закладка General окна кодового редактора (вызывается Tools I Editor Options) позволяет настроить общие свойства кодового редактора: Auto indent Mode- реализовать автоотступ (при нажатии Enter курсор устанавливается на начало предыдущей строки); insert Mode - определяет умалчиваемым режим вставки (переключается клавишей Insert); Use Tab Character - при нажатии клавиши Tab в текст вставляется символ Tab (если переключатель не установлен, вставляются символы пробела); Smart Tab - при нажатии клавиши Tab смещает курсор к первому не пробелу в предыдущей строке; Optimal Fill - начинает каждый автоотступ с минимального количества символов Tab и/или пробела; Backspace Unindents - разрешает удалять автоотступ клавишей Backspace; Cursor Through Tabs - разрешает курсору перескакивать через пустые символы табуляции; Group Undo - разрешает удалять группу последних изменений текста при нажатии Alt+Backspace или выборе опции Edit/Undo; Cursor Beyond eof - позиция курсора включает символы конца строки; undo After Save - восстанавливать изменения, бывшие до последнего сохранения файла; Keep Trailing Blanks - сохранять ведомые символы пробела; Brief Regular Expressions - использовать шаблоны при поиске и поиске-замене; шаблоны включают в себя следующие специальные символы:

Символ | Назначение
^ или % | В начале строки означает, что искомый образец должен располагаться с начала строки
& или > | В конце строки означает, что искомый образец должен располагаться в конце строки
? | На этом месте может стоять любой символ
@ | После символа указывает на любое число этих символов в этом месте: bо@ означает boo, bo, bot
+ | После символа указывает на любое число символов, которые следуют дальше: bо+ означает boo, bonus, bot, но не b или bo
| | Выбор одного из выражений - до или после черты: bar | car выбирает bar или car На этом месте не должен стоять следующий символ
~ | На этом месте должен стоять любой из указанных в скобках символов: [bot ] означает b, о или t
[^] | На этом месте не должен стоять любой из указанных в скобках символов: [bot ] означает любой символ, кроме b, о или t
[-] | Задает диапазон символов. Например [bot] -любой символ в диапазоне от b до о включительно
( ) | Определяет вложенный шаблон. Среда допускает до 10 уровней вложенности
\ | Отменяет действие стоящего за ним специального символа

Persistent Blocks - указывает, что выделенный блок остается выделенным, даже если его покидает текстовый курсор (выделение сохраняется до нового выделения);

Overwrite Blocks - заменяет выделенный блок текстом из буфера; если при этом установлен переключатель persistent Blocks, блок вставляется сразу за выделенным; Double Click Line - выделяет цветом всю строку при двойном щелчке на любом ее символе; Find Text At cursor - если переключатель установлен, в образце поиска при поиске или поиске/замене будет появляться слово, рядом с которым располагается текстовый курсор; Force Cut And Copy Enabled - разрешает операции Edit/Cut и Edit/Copy, даже если текст не выделен; Use syntax Highlighting - разрешает использование синтаксического цветовыделения; Block indent - определяет величину автоотступа для выделенного блока; undo Limit - определят размер буфера для операций Undo; Tab stops - размер табулостопа в символах шрифта экрана; Syntax Extensions - определяет расширения файлов, для которых будет использоваться синтаксическое цветовыделение.

Закладка Display определяет настройку экрана: Brief Cursor Shapes - использовать формы курсора, подобные использующимся в редакторе Brief; Create Backup File - создавать страховочный файл с расширением, начинающимся символом “~” (тильда); Preserve Line Ends - исключать остановку текстового курсора в конце строки; Zoom To Full Screen - разрешить распахивание кодового окна на весь экран; visible Right Margin - разрешает показывать правую границу текста в виде вертикальной пунктирной линии; visible gutter - разрешает показывать служебную зону в левой части окна редактора; Right Margin - определяет правую границу текста; Gutter width - определяет ширину левой служебной зоны; Editor Font - определяет используемый в редакторе шрифт; size - задает высоту шрифта.

Закладка Color позволяет выбрать цвета для отдельных синтаксических элементов. Эти цвета будут использованы для синтаксического цветовыделения.

Закладка Key Mappings позволяет настроить “горячие” клавиши, используемые в кодовом редакторе для ускорения решения типовых задач.

Закладка Code insight определяет используемые интеллектуальные возможности кодового редактора: Code Completion - разрешает использовать подсказку в виде списка свойств, методов и событий, появляющуюся после ввода имени класса (или имени объекта) и следующей за ним точки; Code Parameters - разрешает появление подсказки с перечислением формальных параметров при определении вызовов методов класса; Tooltip Expression Evaluation - разрешает показывать подсказку, в которой на этапе останова программы в контрольной точке будет отображаться текущее содержимое переменной или выражения при указании на них мышью; Tooltip Symbol insight - разрешает показывать информацию об объявленном типе и местонахождении этого объявления; Code Completion Delay - определяет задержку в секундах до включения интеллектуальных возможностей редактора; Templates - определяет кодовые слова, по которым редактор подготавливает стандартные заготовки; для использования уже определенных кодовых слов просто напечатайте нужное слово и нажмите Ctrl+Shift+J; например, если вы напечатаете arrayd и нажмете Ctrl+Shift+j, редактор вставит

array [0..I] of ;

(вертикальная черта обозначает место появления текстового курсора); кнопки Add, Edit и Delete используются соответственно для добавления нового, редактирования и удаления старого кодового слова; Code - показывает код, который связан с указанным кодовым словом.

П 1.1.9. Опция Help

Delphi Help | Основная справочная служба Delphi
Delphi Tools | Справочная служба по инструментам Delphi
Windows SDK | Справочная служба по Windows API
Borland Home Page | Домашняя страничка Borland
Delphi Home Page | Домашняя страничка Delphi
Borland Developer Support | Страничка поддержки разработчиков
Delphi Direct | Окно Интернет-поддержки разработчика
Customize | Вызов службы OpenHelp
About | Окно с краткой информацией о Delphi

Рис. П 1.12. Окна справочной службы

П 1.1.9.1. Служба OpenHelp

Служба OpenHelp предназначена для модификации справочной службы Delphi: с ее помощью можно удалять ненужные разделы и, что наиболее важно, вставлять новые. Последнее связано с тем, что существует множество относительно небольших фирм, занимающихся разработкой и продажей пакетов компонентов для Delphi и C++ Builder (пакеты компонентов для этих двух инструментов идентичны), а также компонентов ActiveX. Сама Delphi имеет развитые средства создания новых компонентов (см. главы части 8). Поставщики компонентов обычно поставляют вместе с ними нужные файлы справочной помощи, которые OpenHelp может сделать частью справочной службы Delphi.

Служба OpenHelp вызывается опциями Help | Customize главного меню. Ее окно показано на рис. П 1.13.

Четыре закладки этого окна управляют содержанием справочной службы (закладка Content), набором файлов помощи (index), контекстно-чувствительной помощью (Link) и проектами (Project). Чтобы удалить раздел содержания, файл справки или проект, нужно на соответствующей закладке щелкнуть по удаляемому компоненту и выбрать в меню Edit | Remove Files или щелкнуть по инструментальной кнопке.

Для добавления к содержанию нового раздела требуется предварительно создать два специальных файла: файл с таблицей содержания (с расширением .toe) и файл содержания (.cnt). toe-файл, как следует из документации, есть стандартный файл содержания cnt, создаваемый утилитой hcw.exe (детальное описание процесса создания справочных файлов и файлов содержания вы найдете в гл. 21), за тем отличием, что в нем нельзя использовать секции include. Файл cnt, в свою очередь, отличается от стандартного тем, что в нем должны быть только две директивы: base и title, причем содержимое title у обоих файлов должно быть одинаковым и они должны располагаться в одной папке. После создания файлов вызывается OpenHelp, и на закладке Content щелкается кнопка или вызывается опция Edit |Add Files. В появляющемся вслед за этим диалоговом окне нужно указать положение файла toe.

Рис. П1.13. Окно службы OpenHelp

Для добавления справочного файла (.hlp) выбирается закладка index, щелкается та же кнопка и указывается положение hlp-файла.

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

П 1.2. РАБОТА С РЕДАКТОРОМ

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

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

вл

вп

курсор влево;

курсор вправо;

вв

вн

курсор вверх;

курсор вниз.

П 1.2.1. Команды перемещения курсора

вл | на символ влево | HOME | в начало строки
вп | на символ вправо | END | в конец строки
Ctrl-ВЛ | на слово влево | Ctrl+PgUp | в начало экрана
Ctrl-ВП | на слово вправо | Ctrl+PgDn | в конец экрана
вв | на строку вверх | Ctrl+HOME | в начало файла
вн | на строку вниз | Ctrl+END | в конец файла
PgUp | на страницу вверх | Ctrl+Q+B | в начало блока
PgDn | на страницу вниз | Ctrl+Q+K | в конец блока

П 1.2.2. Команды удаления/вставки

INS | включить/отключить режим вставки | Delete | стереть символ справа от курсора
Enter | вставить строку | Ctrl+T | стереть слово справа от курсора
Qrl+Y | удалить строку | Ctrl+Q+Y | стереть остаток строки справа от курсора
Backspace | стереть символ слева от курсора | Ctrl+Z | отменить последнее изменение текста

П 1.2.3. Команды работы с блоками

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

Ctrl+K+T | пометить в качестве блока слово слева от курсора
Ctrl+K+P | напечатать блок
Ctrl+K+H | убрать выделение блока цветом; повторное использование Ctrl+K+H вновь выделит блок
Ctrl+K+Y | удалить блок
Ctrl+K+R | читать блок из дискового файла в позицию, определяемую текстовым курсором
Ctrl+K+W | записать блок на диск
Ctrl+K+I | сместить блок вправо на два символа
Ctrl+K+U | сместить блок влево на два символа
Shift+Delete | вырезать блок и поместить его в Clipboard
Ctrl+Insert | копировать блок в буфер Clipboard
Shift+Insert | вставить содержимое Clipboard в позицию, указываемую текстовым курсором

П 1.2.4. Прочие команды

Ctrl+F F3 | искать по образцу продолжить поиск
Ctrl+R | искать по образцу и заменить
Ctrl+K+n | установить маркер; п = 0..9 (см. ниже)
Ctrl+Q+n | искать маркер
Ctrl+Q+] | искать парную скобку (см. ниже)
Ctrl+O+O | вставить настройку компилятора в начало файла (см. ниже)
Ctri+Shift+R | начинает и заканчивает определение макроса
Ctrl+Shift+P | выполняет ранее определенный макрос

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

Ctrl+K+n. Устанавливает в текущую позицию курсора маркер с номером п = 0..9. Маркер на экране появляется в виде небольшого окошка с номером маркера в служебном поле слева от текста. Он никак не влияет на исполнение программы. Команда используется совместно с командой Ctrl+Q+n (искать маркер с номером и) для ускорения поиска нужных фрагментов текста при разработке крупных программ. Раз установленный маркер нельзя удалить, но можно его поместить в другое место файла. При записи на диск маркеры не запоминаются, т. е. после чтения файла с диска в нем нет маркеров.

Ctrl+Q+]. Эта команда используется для поиска ближайшей парной скобки. Она позволяет отыскивать пары скобок ( и ), { и }, [ и ]. Подведите курсор так, чтобы он расположился непосредственно перед одной из скобок, и дайте команду - редактор отыщет нужную парную скобку.

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

($A+,B-,C+,D+,E-,F-,G+,H+,

I+,J+,K-,L+,M-,N+,0+,P+,

Q-,R-,S-,T-,U-,V+,W-,X+,

Y-}

($MIKSTACKSIZE $00004000)

($MAXSTACKSIZE $00100000)

{$IMAGEBASE $00400000}

{$APPTYPE GUI)

В окне кода можно запрограммировать часто повторяющиеся манипуляции с клавишами в виде макроса. Для начала указания макроса используется команда Ctri+Shift+R. После этого любые действия программиста с клавиатурой запоминаются вплоть до повторения команды Ctrl+Shift+R. Запомненный макрос исполняется командой Ctrl+Shift+P.

П1.2.5. Интеллектуальные возможности редактора

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

П 1.2.5.1. Поиск объявлений

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

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

  1. Project | Options | Directories | Conditionals | Search path;
  2. Project | Options|Directories | ConditionalsI Debug source path;
  3. Tools | Environment Options[Library|Browsing path
  4. Tools | Environment Options | Library | Library path.

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

Если мышь перемещается над идентификатором без нажатой и удерживаемой клавиши Shift, рядом с указателем мыши появляется небольшое окно, в котором сообщается, к какому элементу языка относится идентификатор (к процедуре, функции, переменной и т. д.), а также имя модуля и номер строки в нем, где этот идентификатор впервые описан.

П 1.2.5.2. Создание стандартных заготовок для новых свойств и методов

При объявлении новых свойств класса в интерфейсной секции вы можете написать лишь имя свойства и его тип.

После нажатия Ctrl+Shift+C или щелчка правой кнопкой мыши и выбора продолжения Complete Class at Cursor редактор добавит необходимые элементы Read и Write в описание свойства и внесет другие изменения в текст программы.

Пусть, например, вы написали

type TMyButton = class(TButton) property Size: Integer;

procedure DoSomething;

end;

и нажали Ctrl+Shift+C (текстовый курсор при этом должен находиться в любом месте внутри описания класса). Редактор изменит описание класса следующим образом:

type TMyButton = class(TButton)

property Size:

Integer read FSize write SetSize;

procedure DoSomething;

private

FSize: Integer;

procedure SetSize(const Value: Integers;

end;

и добавит в исполняемую секцию описание двух методов:

{ TMyButton }

procedure TMyButton.DoSomething;

begin

end;

procedure TMyButton.SetSize(const Value: Integer);

begin

FSize := Value;

end;

Вы можете также вставить в раздел implementation новый метод класса и нажать Ctrl+Shift+C - редактор вставит прототип метода в объявление класса в секции interface.

П 1.2.5.3. Навигация внутри модуля

Клавиши курсора Вверх (Up) и Вниз (Down) в сочетании с нажатыми и удерживаемыми клавишами Ctrl и Shift осуществляют переключение между секциями interface и implementation текущего модуля. Если в секции interface вас заинтересовала реализация того или иного метода, щелкните по нему мышью и нажмите Ctrl+Shift+Down - редактор отыщет реализацию и покажет ее вам. Наоборот, чтобы из секции implementation переместиться к заголовку метода в секции interface, нажмите Ctrl+Shift+Up.

П 1.2.5.4. Вставка текстовых заготовок и окно Code Insight

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

Просмотреть имеющиеся текстовые заготовки и при необходимости добавить к ним собственные можно с помощью Tools | Editor Options | Code Insight.

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

С помощью кнопки Add можно добавить новый образец, с помощью Edit — изменить его имя и краткое описание, а с помощью Delete - удалить его.

При редактировании имеющейся или вставки новой заготовки учтите, что символ “|” определяет позицию текстового курсора после вставки заготовки в текст программы.

Для вставки заготовки напечатайте ее имя в окне кода и нажмите Ctrl+J - имя заменится на полный текст заготовки.

Если вы не помните всех имен заготовок, их можно выбрать из списка.

Для этого установите текстовый курсор в то место, где вы хотите вставить заготовку, и нажмите Ctrl+J - на экране появится окно с именами и краткими описаниями всех заготовок.

Рис. П1.14. Окно настройки параметров Coda Insight

Остальные элементы окна Code insight:

Code Completion - разрешает/запрещает появление окна с именами свойств и методов класса после ввода имени объекта и точки (рис. П 1.15).

Code Parameters - разрешает/запрещает появление окна с именами формальных параметров обращения к подпрограмме после ввода имени подпрограммы и открывающей скобки (рис. П 1.15).

Tooltip Expression Evaluations - разрешает/запрещает появление окна с указанием текущего значения переменной, над именем которой располагается указатель мыши в режиме отладки

Tooltip Symbol Insight - разрешает/запрещает появление окна с указанием параметров идентификатора, над которым располагается указатель мыши.

Delay - определяет задержку в секундах включения механизма показа окон Code Insight.

Рис. П1.15. Окна Code Insight

П1.3. ОТЛАДКА ПРОГРАММ

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

П 1.3.1. Точки контрольного останова

Точка контрольного останова определяет оператор в программе, перед выполнением которого программа прервет свою работу и управление будет передано среде Delphi. Точка останова задается с помощью опции view | Debug windows | Breakponts.

Окно точек останова (рис. П 1.16) содержит список всех установленных в проекте точек, перед выполнением которых происходит прекращение работы программы и управление получает среда Delphi.

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

Line number - номер строки от начала файла (в момент появления окна оно содержит файл и строку с текстовым курсором). В строке Condition можно указать условие останова в виде логического выражения (например, MyValue = Мах-Value-12), а в строке Pass count - количество проходов программы через контрольную точку без прерывания вычислений.

Рис. П1.16. Окно точек останова (слева) и окно добавления новой точки (справа)

П 1.3.2. Окно наблюдения

Наблюдать за состоянием переменной или выражения можно с помощью специального окна, вызываемого опцией View | Debug windows | Watches (cm. там же).

Окно наблюдения (рис. П 1.17) используется в отладочном режиме для наблюдения за изменением значений выражений, помещенных в это окно. Для добавления нового выражения щелкните по окну правой кнопкой мыши и выберите опцию New Watch. В строке Expression введите выражение. Окно Repeat count определяет количество показываемых элементов массивов данных; окно Digits указывает количество значащих цифр для отображения вещественных данных;

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

Рис. П1.17. Окно наблюдения и окно добавления в него нового выражения

П 1.3.3. Принудительное прерывание работы программы

Если программа запущена из среды Delphi, ее работу можно прервать в любой

момент G помощью клавиш Ctrl+F2, кнопки, опцией Run | program pause или, наконец, установив точку контрольного останова в той части программы, которая выполняется в данный момент или будет выполнена.

П 1.3.4. Трассировка программы

Перед исполнением оператора, в котором установлена точка контрольного останова, работа программы будет прервана, управление получит среда Delphi, a в окне наблюдения отразится текущее значение наблюдаемых переменных и/или выражений. Теперь программист может прослеживать работу программы по шагам с помощью клавиш F7 и F8 или инструментальных кнопок. При нажатии F8 будут выполнены запрограммированные в текущей строке действия, и работа программы прервется перед выполнением следующей строки текста программы. Замечу, что контрольная точка останова выделяется по умолчанию красным цветом, а текущая прослеживаемая строка - синим. Если программа остановлена в контрольной точке, т.е. когда текущая строка совпадает со строкой останова, строка выделяется красным цветом, Признаком текущей строки является особое выделение строки в служебной зоне слева в окне редактора (см. рис. П1.18).

Рис. П1.18. Фрагмент окна редактора в режиме отладки

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

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

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

П 1.3.5. Действия в точках прерывания

В Delphi 5 и 6 с любой точкой можно связать одно или несколько действий. Для этого нужно активизировать окно точек останова, вызвать его локальное меню (щелчок правой кнопкой) и выбрать продолжение Properties. В появившемся окне свойств щелкнуть по кнопке Advanced (рис. П1.19).

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

Break - простой останов перед выполнением помеченного оператора.

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

Handle subsequent exceptions - после установки этого переключателя отменяется действие предыдущего переключателя и возобновляется обработка возможных исключений.

С помощью Log message вы можете указать произвольное сообщение, связанное с точкой останова, а с помощью Eval expression - вычислить некоторое выражение и поместить его результат в это сообщение.

П 1.3.6. Группировка точек прерывания

В Delphi 5 и б имеется возможность объединения точек останова в группы. Для этого используется все то же окно рис. П1.20: в строке Group следует указать имя группы, к которой принадлежит точка, а в строках Enable Group и Disable Group соответственно разрешить или запретить действие всех точек останова, относящихся к соответствующей группе.

Рис.П.1.19.Окно связывания точки псптнови с действием

П 1.3.7. Вычисление выражений и изменение значений

С помощью окна Evaluate/Modify (рис. П1.20) можно узнать значение любого выражения или установить в переменную другое значение. Это окно вызывается в режиме отладки после нажатия Ctrl+F7.

Рис 1.20. Окно прослеживания изменения значении

Это окно - модальное, т. е. оно прерывает отладку программы до тех пор, пока не будет закрыто. В строке Expression можно написать имя переменной или интересующее вас выражение. После щелчка по кнопке Evaluate в поле Result появится текущее значение переменной (выражения). Если в Expression содержится имя переменной, одновременно становится доступной кнопка Modify, а в строке New value повторяется текущее значение переменной. Если изменить эту строку и нажать Modify, в переменную будет помещено новое значение, которое и будет использоваться при дальнейшем прогоне программы (если определяется значение выражения, кнопка Modify и строка New value будут недоступны).

В Delphi 5 и 6 используются также дополнительные кнопки этого окна Watch и inspect. Если вы щелкните по первой из них, выражение (переменная) из окна Evaluate будет перенесено в окно наблюдений watch, щелчок по второй отображает выражение (переменную) в специальном окне Inspect.

П 1.3.8 Ведение протокола работы программы

В ряде случаев бывает неудобно или невозможно пользоваться пошаговой отладкой программ. Если вы, например, установите точку останова в подпрограмме прорисовки сетки TDBGrid, программа после останова не сможет нормально продолжить свою работу, т. к. в этом случае она будет пытаться восстановить экран и вновь будет остановлена и т. д. В таких ситуациях вам могут помочь контрольные точки, которые не прерывают работу программы, а лишь помещают некоторую информацию в специальный файл трассировки. Для реализации такой точки раскройте окно RunlAdd Breakpoint | Source Breakpoint (см. рис. П1.19), уберите флажок в переключателе Break и напишите сообщение в строке Log message. Вы можете также в строке Eval expression указать некоторое выражение, которое будет вычислено и вместе с сообщением помещено в протокол работы программы. Этот протокол можно просмотреть в любой момент (в том числе и после завершения прогона программы) с помощью опции View] Debug Windows! Event Log.


Приложение 2. ПЕРЕМЕННЫЕ, ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ SYSTEM

  • Приложение 2. ПЕРЕМЕННЫЕ, ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ SYSTEM

Приложение 2

ПЕРЕМЕННЫЕ, ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ SYSTEM

Модуль SYSTEM автоматически связывается с любой программой, поэтому объявленные в его интерфейсной части типы, константы, переменные и подпрограммы доступны программисту в любой момент. В этом приложении приводится (в алфавитном порядке) список всех объявлений модуля в его интерфейсной секции. Следует заметить, что большинство объявлений осталось неизменным еще со времен давнего предшественника Delphi - системы программирования Turbo Pascal. Кроме того, многие подпрограммы описаны в других разделах книги (работа с файлами, с вариантами, преобразование строк и т. п.).

function Abs(X) ;

Возвращает абсолютную величину выражения x (целого или вещественного типа)

function Addr(X): Pointer; | Возвращает адрес переменной или подпрограммы х
var AllocMemCount: Integer; | Возвращает полное количество блоков памяти, выделенных программе
var A11 о cMemS ize: Integer; | Возвращает полное количество памяти (в байтах), выделенное программе
procedure Append (var F: Text); | Открывает текстовый файл f для добавления новых строк
function ArcTan(X: Extended): Extended; | Возвращает Арктангенс (в радианах) x
procedure Assert (expr : Boolean [;const msg: Strings; | Проверяет условное выражение ехрг. Выдает сообщение msg и останавливает программу, если выражение имеет значение False, в противном случае ничего не делает
procedure AssignFile (var F; File Name: String) ; | Связывает файловую переменную F с файлом, указанным в FileName
function Assigned (var P) : Boolean; | False, если указатель р связан с nil (т. е. не указывает на область памяти)

procedure BlockRead(var F: File;

var Buf; Count: Integer [; var

AmtTransferred: Integer]);

Читает из файла, связанного с f, в буфер Buf не более count блоков и возвращает в AmtTransfered истинное количество прочитанных блоков

procedure BlockWrite (var f: File;

var Buf; Count: Integer [; var pa Buf не более Count блоков и возвращает AmtTransferred: Integer]);procedure Break;

Записывает в файл, связанный с F, в AmtTransfered истинное количество записанных блоковНемедленно прекращает выполнение циклов for, while или repeat

procedure ChDir(S: Strings; | Изменяет умалчиваемый каталог на каталог, заданный выражением s
function Chr(X: Byte): Char; | Преобразует байт х в символ
procedure Close(var F) ; | Закрывает файл F
procedure CloseFile(var F); | Закрывает файл f
var CmdLine: PChar; | Содержит параметры запуска программы
var CmdShow: Integer; | Содержит статус окна, открытого API- функцией showwindow
function CompToCurrency(acomp: Comp): Currency; cdecl; | Преобразует тип Comp к типу currency
function CompToDouble(acomp: Comp): Double; cdecl; | Преобразует тип Comp к типу Double
function Concat(sl [, s2,..., sn]: String): String; | Объединяет строки Si в единую строку
procedure Continue; | Прекращает очередную итерацию цикла for, while или repeat
function Copy(S; Index, Count: Integer): Stringy; function Copy(S; Index, Count: Integer): array; | Возвращает подстроку или сегмент динамического массива
function Cos(X: Extended): Ex tended/ | Возвращает косинус аргумента X, заданного в радианах
procedure CurrencyToComp (acurrency: Currency; var result: Comp); cdecl; | Преобразует тип currency к типу Comp
var DLLProc: Pointer; | Указывает точку входа в DLL-процедуру, которая выполняется в данный момент
procedure Dec(var X [ ; N: Long- Int]); | Уменьшает х на n, а если n опущено - на 1. х, n - любые порядковые типы, в том числе Int64
var Default8087CW: Word; | Содержит 0, если в ПК нет арифметического сопроцессора, в противном случае разряды слова указывают некоторые технические параметры сопроцессора
procedure Delete(var S: String; Index, Count: Integer); | Удаляет из строки s count символов начи ная с символа с номером index
procedure Dispose(var P: Pointer) ; | Освобождает память, связанную с указателем p
function DoubleToComp(adouble: Double; var result: Comp); cdecl; | Преобразует тип Double к типу Comp
var EmptyParam: OleVariant; | Указывает, что умалчиваемый параметр дуального интерфейса не используется
procedure EnumModules(Func: TEnumModuleFunc; Data: Pointers- overload; procedure EnumModules(Func: TEnumModuleFuncLW; Data: Pointer); overload; | Реализует обратный вызов (callback) для всех модулей и всех пакетов программы
procedure EnumResourceModules(Func: TEnumModuleFunc; Data: Pointer); | Реализует обратный вызов (callback) для всех ресурсных модулей программы
procedure EnumResourceMod- ules(Func: TEnumModuleFuncLW; Data: Pointer) ; |
function Eof(var F): Boolean; | Возвращает True, если достигнут конец файла f (любого типа)
function Eoln [(var F: Text) ]: Boolean- | Возвращает True, если достигнут конец строки или конец текстового файла
procedure Erase(var F) ; | Уничтожает файл, связанный с переменной F
var ErrorAddr: Pointer; | Содержит адрес исполняемого оператора, вызвавшего ошибку при прогоне программы
var ErrorProc: Pointer; | Содержит адрес входа в умалчиваемый обработчик ошибок
var ExceptProc: Pointed; | Содержит адрес входа в низкоуровневый обработчик исключений
procedure Exclude(var S: set of T;I:T) ; | Исключает элемент i из множества s
procedure Exit; | Завершает работу подпрограммы
var ExitCode: Integer; | Возвращает код завершения программы. В современных разработках используйте для этих целей секцию finalization
var ExitProc: Pointer; | Содержит адрес входа в процедуру завер шения программы
function Exp(X: Real): Real; | Возвращает х°, где е - основание натурального логарифма
var FileMode: Byte; | Указывает режим файла, открытого процедурой Reset: 0 - только чтение; 1 - только запись; 2 - чтение и запись
function FilePos(var F): Longint; | Возвращает текущую позицию в файле F
function FileSize(var F): Integer; | Возвращает размер нетекстового файла
procedure FillChar(var X; Count: Integer; Value: Byte); | Заполняет переменную х не более Count байтами со значением value
procedure Finalize( var V [; Count: Integer] ) ; | Освобождает динамически распределенную память, связанную с F
function FindClassHIn- stance(ClassType: TClass): Long Word; | Возвращает дескриптор модуля, в котором определен экземпляр класса ClassType
function FindHInstance( Address: Pointer): LongWord; | Возвращает дескриптор модуля, в котором определен адрес Address
function FindResourceHInstance (Instance: LongHord): LongWord; | Возвращает дескриптор ресурсного файла для экземпляра instance
procedure Flush(var F: Text); | Записывает буфер файла на диск
function Frac(X: Extended): Ex tended; | Возвращает дробную часть х
procedure FreeMem(var P: Pointer[; Size: Integer]); | Освобождает динамически распределенную память, связанную с P
procedure GetDir(D: Byte; var S: String) ; | Возвращает имя диска по его номеру
procedure GetMem(var P: Pointer;Size: Integer); | Связывает с указателем p size байт динамической памяти
procedure GetMemoryManager(var MemMgr: TMemoryManager); | Возвращает точку входа в менеджер динамической памяти
procedure Halt [ ( Exitcode: Integer) ] ; | Прекращает выполнение программы с кодом завершения ExitCode
var HeapAllocFlags: Word = 2; | Указывает, какую память получает менеджер динамической памяти от операционной системы (по умолчанию - gmemmoveable)
function Hi(X): Byte; | Возвращает старший байт 16-разрядного числа
function High(X); | Возвращает наивысшее значение диапазона порядкового типа, строки или открытого массива, к которому принадлежит х
function lOResult: Integer; | Возвращает статус последней операции ввода-вывода
procedure Inc(var X [ ; N: LongInt ] ); | Наращивает х на n, а если n отсутствует - на единицу
procedure Include(var S: set ofT; I:T); | Включает элемент i в множество S
procedure Initialize(var V [ ; Count: Integer ] ); | Инициализирует динамически распределенную переменную v, если она была создана не процедурой New
var Input: Text; | Системный файл ввода
procedure Insert(Source: String; var S: String; Index: Integers; | Вставляет подстроку source в строку s начиная с символа index
function Int(X: Extended): Ex tended; | Возвращает целую часть вещественной переменной
var IsConsole: Boolean; | Содержит True, если выполняется приложение консольного типа
var IsLibrary: Boolean; | Содержит True, если выполняется подпрограмма из DLL
function IsMemoryManagerSet: Boolean; | Возвращает True, если менеджер динамической памяти был изменен процедурой Set-MemoryManager
var IsMultiThread: Boolean; | Содержит True, если в программе инициировано несколько потоков
var JITEnable: Byte; | Указывает тип обработки исключения, возникающего в процессе отладки
function Length (S): Integer; | Возвращает длину строки
function Ln(X: Real): Real; | Возвращает натуральный логарифм х
function Lo(X): Byte; | Возвращает младший байт аргумента
function Low(X); | Возвращает наименьшее значение диапазона порядкового типа, строки или открытого массива, к которому принадлежит х
var Mainlnstance: LongWord; | Содержит дескриптор экземпляра главного модуля программы
var MainThreadID: LongWord; | Содержит дескриптор главного потока для текущего модуля
const Maxint = High(Integer); | Содержит максимальное значение типа integer (2 147 483 647)
const MaxLongint = High(Longint); | Содержит максимальное значение типа Longint(2 147 483 647)
procedure MkDir(S: String); | Создает новый каталог
procedure Move(const Source; var Dest; Count: Integers; | Переносит не более count байт из источника Source в приемник Dest
procedure New(var P: Pointer); | Создает новую динамическую переменную и связывает ее с p
var NoErrMsg: Boolean=False; | Указывает, будут ли ошибки периода исполнения программы сопровождаться сообщениями (False - будут)
var Null: Variant; | Используется для указания значения Null переменной вариантного типа
function Odd(X: Longint): Boolean; | Возвращает True, если аргумент - нечетное число
procedure OleStrToStrVar (Source: PWideChar; var Dest: String); | Копирует “широкую” (двухбайтную) строку в обычную строку Object Pascal
function Ord(X): Longint; | Возвращает порядковое значение перемен ной порядкового типа (в том числе и Int64)
var Output: Text; | Файл системного вывода для консольных приложений
function ParamCount: Integer; | Возвращает количество параметров запуска программы
function ParamStr(Index: Integer) : String; | Возвращает параметр запуска под номером index (для index=0 - имя исполняемого файла)
function Pi: Extended; | Возвращает число it=3,141592653589793
function Po3(Substr: String; S:String): Integer; | Возвращает номер символа, начиная с которого в строке s располагается подстрока Substr
function Pred(X) ; | Возвращает предыдущее значение для выражения х порядкового типа
function Ptr(Address: Integer): Pointer; | Преобразует Address в указатель
var RandSeed: Longint; | Определяет стартовое значение для генератора псевдослучайных последовательностей
function Random [ ( Range: Integer) ] ; | Возвращает очередное псевдослучайное число.
procedure Randomize; | Инициирует генератор псевдослучайных последовательностей.
procedure Read(var F / VI [/ V2,...,Vn ] ); | Читает из файла f заданное количество значений и помещает их в переменные vi
procedure Readin([ var F: Text; ] VI [, V2, . . ., Vn ] ) ; | Читает из файла f заданное количество строк и помещает их в переменные vi
procedure ReallocMem(var P: Pointer; Size: Integers; | Изменяет размер динамической переменной
procedure Rename(var F; Newname:String) ; procedure Rename(var F; Newname:PChar) ; | Переименовывает файл, связанный с файловой переменной f
procedure Reset(var F [: File; RecSize: Word ] ) ; | Открывает существующий файл для чтения и/или записи
procedure Rewrite(var F: File [; Recsize: Word ] ) ; | Создает новый файл и открывает его для записи
procedure RmDir(S: Strings; | Удаляет пустой каталог s
function Round(X: Extended): Int64; | Округляет вещественное число до ближайшего целого
procedure RunError [ ( Errorcode: Byte ) ]; | Останавливает работу программы и сообщает код ошибки
procedure Seek(var F; N: Long- Int) ; | Пропускает n байт от начала файла
function SeekEof [ (var F: Text) ]: Boolean; | Пропускает все байты до конца файла
function SeekEoln [ (var F: Text) ]: Boolean; | Пропускает все символы текстового файла до конца текущей строки
procedure Set8087CW(NewCW: Word); | Временно устанавливает новый режим работы арифметического сопроцессора
procedure SetLength(var S; NewLength: Integer); | Устанавливает новую длину строки или динамического массива
procedure SetMemoryManager (const MemMgr: TMemoryManager); | Устанавливает новый менеджер динамической памяти
procedure SetString(var s: String; buffer: PChar; len: Integer) ; | Копирует Len символов буфера Buffer в строку s
procedure SetTextBuf(var F: Text; var Buf [ ; Size: Integer] ); | Устанавливает внешний буфер Buf длиной size символов для файловой переменной f
function Sin(X: Extended): Ex tended; | Возвращает синус аргумента (в радианах)
function SizeOf(X): Integer; | Возвращает длину переменной х в байтах
function Slice(var A: array; Count: Integer): array; | Возвращает открытый массив, содержащий первые count элементов массива а
function Sqr(X: Extended): Extended; | Возвращает квадрат аргумента
function Sqrt(X: Extended): Extended; | Возвращает корень квадратный из аргумента
procedure Str(X [: Width [: Decimals ] ]; var S) ; | Преобразует х целого или вещественного типа в строку s с учетом длины width и количества знаков после запятой Decimals
function StringOfChar(Ch: CharCount: Integer): String; | Создает строку, состоящую из Count раз повторенного символа Ch
function StringToOleStr(const Source: String): PWideChar; | Копирует обычную строку в двухбайтную
function StringToWideChar(const Source: String; Dest: PWideChar; DestSize: Integer): PWideChar; | Преобразует обычную строку в строку с символами UNICODE
function Succ(X) ; | Возвращает следующее значение для порядкового аргумента х
function Swap(X) ; | Меняет местами байты в 16-разрядном слове
function Trunc(X: Extended): Int64; | Преобразует вещественное число к целому путем отбрасывания дробной части
procedure Truncate(var F) ; | Отсекает оставшуюся часть файла до его конца
const Unassigned: Variant; | Используется для указания варианта с не связанным (пустым) значением
function UpCase(Ch: Char): Char; | Преобразует строчный символ Ch в заглавный
procedure Val(S: String; var V;var Code: Integer); | Преобразует строковое значение в целую или вещественную величину
function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; | Создает вариантный массив
function VarArrayDimCount(consfc A: Variant): Integer; | Возвращает количество измерении вариантного массива
function VarArrayHighBound (const A: Variant; Dim: Integer): Integers; | Возвращает верхнюю границу измерения вариантного массива
function VarArrayLock(var A: Variant): Pointers; | Блокирует вариантный массив и возвращает указатель на его данные
function VarArrayLowBound(const A: Variant; Dim: Integer): Integer; | Возвращает нижнюю границу измерения вариантного массива
procedure VarArrayRedim(var A: Variant; HighBound: Integer) ; | Перестраивает вариантный массив
function VarArrayRef(const A: Variant): Variants- | Преобразует вариантный массив к форме, необходимой при обращении к API- функциям
procedure VarArrayUnlock(var A: Variant) ; | Отменяет действие функции VarArrayLock
function VarAsType(const V: Variant; VarType: Integer): Variants; | Преобразует вариант к заданному типу
procedure VarCast(var Dest: Variant; const Source: Variant; VarType: Integer); | Преобразует вариант к заданному типу и сохраняет результат в Dest
procedure VarClear(var V : Variant) ; | Помещает в вариант пустое значение
procedure VarCopy(var Dest: Vari ant; const Source: Variants; | Копирует вариант-источник Source в вариант-приемник Dest
function VarFromDateTime (DateTime: TDateTime): Variants; | Преобразует дату-время в значение варианта
function VarIsArray(const V: Variant): Boolean; | Возвращает True, если v - вариантный массив
function VarIsEmpty(const V: Variant): Boolean; | Возвращает True, если вариант имеет пустое значение
function VarIsNull(const V: Variant) : Boolean; | Возвращает True, если вариант имеет значение Null
function VarToDateTime(const V: Variant): TDateTime) ; | Преобразует вариант в значение дата-время
function VarToStr(const V: Variant) : String; | Преобразует вариант в строку.
function VarType(const V: Vari ant) : Integers; | Возвращает тип хранящегося в варианте результата
procedure WideCharLenToStrVar (Source: PWideChar; SourceLen: Integer; var Dest: String); | Преобразует не более SourceLen символов строки UNICODE к обычной строке
function WideCharLenToString (Source: PWideChar; SourceLen: Integer): Strings; | Преобразует не более SourceLen символов строки UNICODE к обычной строке
procedure WideCharToStrVar (Source: PWideChar; var Dest: String); | Преобразует строку UNICODE к обычной строке


Приложение 3. ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ MATCH

  • Приложение 3. ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ MATCH

Приложение 3

ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ MATCH

Тригонометрические подпрограммы
function ArcCos(X: Extended): Extended; | Арккосинус
function ArcSin(X: Extended): Extended; | Арксинус
function ArcTan2(Y, X: Extended): Ex tended; | Вычисляет арктангенс Y/X и возвращает угол в правильном квадранте (функция АгсТап модуля System не учитывает квадрант)
function Cotan(X: Extended): Extended; | Котангенс
function Hypot (X, Y: Extended): Extended; | Корень квадратный из (X** 2 + Y** 2)-гипотенуза прямоугольного треугольника по двум катетам
procedure SinCos (Theta: Extended; va: '"Sin, Cos: Extended); | Возвращает одновременно синус и косинус угла Theta (почти в 2 раза быстрее, чем раздельное получение синуса и косинуса)
function Tan(X: Extended): Extended; | Тангенс
Функции преобразования углов
function CycleToRad(Cycles: Extended) : Extended; | Radians := Cycles * 2PI
function DegToRad(Degrees: Extended) Extended; | Radians := Degrees * PI / 18
function GradToRad(Grads: Extended): Extended; | Radians := Grads * PI / 200
function RadToDeg(Radians: Extended) Extended; | Degrees := Radians * 180 / PI
function RadToGrad(Radians: Extended) : Extended; | Grads := Radians * 200 / PI
function RadToCycle(Radians: Extended) : Extended; | Cycles := Radians / 2PI
Гиперболические функции

function ArcCosh(X: Extended): Extended;

Гиперболический арккосинус

function ArcSinh(X: Extended): Extended;

Гиперболический арксинус

function ArcTanh(X: Extended): Extended;

Гиперболический арктангенс

function Cosh(X: Extended): Extended;

Гиперболический косинус

function Sinh(X: Extended): Extended;

Гиперболический синус

function Tanh(X: Extended): Extended;

Гиперболический тангенс

Логарифмические функции

Function LnXP1 (X: Extended) : Exteaded;

Логарифм натуральный от (Х+1). Используется, когда Х близок к нулю

functios Extended): Extended;

Десятичный логарифм

functior extended): Extended;

Двоичный логарифм

function LogN(Base, X: Extended): Extended;

Логарифм от X при основании Base

Экспоненциальные функции

function IntPower(Base: Extended; Expopient: Integer) : Extended;

Возведение Base в целочисленную степень Exponent

function Power(Base, Exponent: Extended) : Extended;

Возведение Base в вещественную степень Exponent

Подпрограммы разного назначения

function Ceil(X: Extended): Integer;

Ближайшее меньшее целое

function Floor (X: Extended): Integer;

Ближайшее большее целое

procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer);

Возвращает мантиссу и степень вещественного числа

function Ldexp(X: Extended; P: Integer) : Extended;

Возвращает Х*Р*P

Статические программы

function Мах(А,В: Int64): Int64; overload;

function Мах (А, В: Integer): Integer; overloads;

Возвращает максимальное из двух чисел

function Poly(X: Extended; const Coefficients: array of Double): Extended;

Значение полинома A*X"+B*X" '+.. .+Z. Коэффициенты задаются в порядке возрастания степени

function Мах(А,В: Single): Single/overload;

function Max(А,В: Double): Double/overload;

function Max(А,В: Extended): Extended; overload;

function MaxIntValue(const Data: array of Integer): Integer;

Возвращает максимальное из набора целых чисел

function MaxValue(const Data: array of Double): Double;

Возвращает максимальное из Тнабора вещественных чисел

function Mean(const Data: array of

Double): Extended;

Вычисляет арифметическое среднее для набора вещественных чисел

procedure MeanAndStdDev(const Data: array of Double; var Mean, StdDev: Extended) ;

Вычисляет арифметическое среднее и стандартное отклонение для набора вещественных чисел

function Min(А/В: Integer): Integer/overload/function Min(А,В: Int64): Int64; overload;

Возвращает минимальное из двух чисел

function Min(A,B: Single): Single/overload;

function Min(A,B: Double): Double; overload/function Min(A,B: Extended): Extended; overload/

function MinIntValue(const Data: array of Integer): Integer;

Возвращает минимальное из набора целых чисел

function MinValue(const Data: array of Double): Double;

Возвращает минимальное из набора вещественных чисел

procedure MomentSkewKurtosis (const Data: array of Double;

var Ml, M2, МЗ, М4, Skew, Kurtosis: Extended);

Вычисляет статистические моменты порядков с первого по четвертый, а также асимметрию Skew и эксцесс Kurtosis для набора чисел

function Norm(const Data: array of Double): Extended;

Возвращает норму (квадратный корень из суммы квадратов) вещественных чисел

function PopnStdDev(const Data: array of Double): Extended;

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

function PopnVariance(const Data: array of Double): Extended;

Выборочная дисперсия. Использует “смещенную” формулу TotalVariance/N (см. ниже TotalVariance)

function RandG(Mean, StdDev: Extended) : Extended;

Генерирует нормально псевдораспределенную последовательность чисел с заданным средним значением Mean и стандартным отклонением StdDev

function StdDev(const Data: array of Double): Extended;

Вычисляет среднеквадратическое отклонение для набора чисел

function Sum(const Data: array"orDouble): Extended register;

Вычисляет сумму чисел

procedure SumsAndSquares(const Data: array of Double;

Одновременное вычисление суммы и суммы квадратов для набора чисел

function Sumint(const Data: array of Integer): Integer register;

Сумма наоора целых чисел

function SumOfSquares(const Data: array of Double): Extended;

Сумма квадратов чисел

function TotalVariance(const Data: array of Double): Extended;

Сумма квадратов расстояний всех величин от их среднего арифметического

function Variance(const Data: array

of Double): Extended;

Выборочная дисперсия для набора чисел. Использует ^несмещенную” формулу TotalVariance/(N-1)

Финансовые функции

type TPaymentTime = (ptEndOfPeriod,

ptStartOfPeriod) ;

Перечисляемый тип, используемый в финансовых функциях

function DoubleDecliningBalance (Cost, Salvage: Extended; Life, Period: Integer): Extended;

Вычисление амортизации методом двойного баланса

function FutureValue(Rate: Extended; NPeriods: Integer; Payment, Pre-sentValue: Extended; PaymentTime: TPaymentTime): Extended;

Будущее значение вложения

- function InterestPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;

Вычисление процентов по ссуде

function InterestRate(NPeriods: Integer; Payment, PresentValue, Future-Value: Extended; PaymentTime: TPaymentTime) : Extended;

Норма прибыли, необходимая для получения заданной суммы

function InternalRateOfReturn (Guess: Extended} const CashFlows: array of Double): Extended;

Вычисление внутренней скорости оборота вложения для ряда последовательных выплат

function NetPresentValue(Rate: Extended; const CashFlows: array of Double; PaymentTime: TPaymentTime): Extended;

Вычисление чистой текущей стоимости вложения для ряда последовательных выплат с учетом процентной ставки

function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended/

Количество периодов, за которые вложение достигнет заданной величины

function Payment(Rate: Extended; NPeriods: Integer; PresentValue, Future-Value: Extended; PaymentTime: TPaymentTime) : Extended/

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

function PeriodPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue, FutureValu'e: Extended; PaymentTime: TPaymentTime): Extended;

Платежи по процентам за заданный период

function PresentValue(Rate: Extended; NPeriods: Integer; Payment, Future-Value: Extended; PaymentTime: TPaymentTime) : Extended;

Текущее значение вложения

function SLNDepreciation (Cost, Salvage: Extended; Life: Integer): Extended;

Вычисление амортизации методом постоянной нормы

function SYDDepreciation (Cost, Salvage: Extended; Life, Period: Integer) : Extended;

Вычисление амортизации методом весовых коэффициентов

В отличие от функций табл. 7.5 перечисленные выше подпрограммы станут доступны программе только после ссылки на модуль Match в предложении Uses (подробнее об использовании модулей см. в главе 12).


Глава 20. ГЛОБАЛЬНЫЕ ОБЪЕКТЫ И НАСТРОЙКА ПРОГРАММ

  • Глава 20. ГЛОБАЛЬНЫЕ ОБЪЕКТЫ И НАСТРОЙКА ПРОГРАММ
    • APPLICATION
    • SCREEN
    • PRINTER
    • CLIPBOARD
    • НАСТРОЙКА ПРОГРАММ
      • Файлы инициации
      • Системный реестр Windows

Глава 20

ГЛОБАЛЬНЫЕ ОБЪЕКТЫ И НАСТРОЙКА ПРОГРАММ

С любой запущенной программой автоматически связываются четыре глобальных объекта: Application (программа), screen (экран), Printer (принтер) и Clipboard (буфер межпрограммного обмена). В этой главе рассматриваются свойства и методы этих объектов. Отдельный раздел посвящен индивидуальной настройке программ.

20.1. APPLICATION

Объект Application относится к классу TAppiication и инкапсулирует важнейшие свойства и методы программы как таковой. Он создается автоматически и доступен любой программе. Объект не помещен в палитру компонентов, поэтому его свойства недоступны на этапе конструирования, но становятся доступны при прогоне программы[ Некоторые свойства программы (в том числе ее пиктограмма, сведения о версии и используемом языке) могут устанавливаться с помощью менеджера проекта (опция project | Options и страница Application).].

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

Ниже описываются некоторые свойства Application для приложения Windows (для приложения Linux объект Application несколько отличается). Опущенные свойства предназначены в основном для прямого обращения к API-функциям Windows.

property Active: Boolean; | Содержит True, если любое окно программы имеет фокус ввода
property BiDiMode: TBiDiMode; | Определяет свойство bidiMode (см. п. 17.12) для программы в целом
property CurrentHelpFile: Strings; | Определяет имя текущего справочного файла
property ExeName: Strings; | Содержит полное имя (с маршрутом доступа) исполняемого файла программы
property HelpFile: String; | Определяет имя файла справочной службы
property Hint: String; | Содержит длинную часть оперативной подсказки
property HintColor: TColor; | Определяет цвет фона ярлычка оперативной подсказки
property HintHidePause: Integers; | Определяет паузу (в миллисекундах) от момента появления ярлычка до его исчезновения
property HintPause: Integer; | Определяет паузу (в миллисекундах) от момента остановки указателя мыши до появления ярлычка
property HintShortPause: Integers; | Определяет паузу (в миллисекундах) появления ярлычка при переходе с одного компонента на другой
property Icon: TIcon; | Определяет пиктограмму программы
property MainForm: TForm; | Определяет главное окно программы
property ShowHint: Boolean; | Разрешает/запрещает показ ярлычков для всех окон программы
property ShowMainForm: Boolean; | Разрешает/запрещает показ главного окна в момент старта программы
property Terminated: Boolean; | Содержит True, если программа должна прекратить работу. Используется в приложениях с интенсивным счетом для контроля необходимости прекращения дальнейшей работы
property Title: String; | Определяет текст под пиктограммой минимизированной программы
property UpdateFormatSettings: Boolean; | Если содержит True, программа автоматически будет учитывать новые форматные установки Windows (например, новый разделитель целой и дробной частей числа) при их изменении в момент работы программы.
property UpdateMetricSet;Boolean; | Если содержит True, программа автоматически будет учитывать новые метрические установки Windows (например, новый системный шрифт) при их изменении в момент работы программы

Методы Application:

procedure BringToFront; | Помещает окно, которое было активным в последний раз, поверх остальных окон
procedure HandleException(Sender: TObject) ; | Осуществляет умалчиваемую обработку исключительных ситуаций. Используется при разработке новых компонентов
function HelpCommand(Command: Word; Data: Longint): Boolean; | Посылает справочной службе команду Command и параметр Data
procedure HandleMessage; | Приостанавливает работу программы до тех пор, пока не будут обработаны все сообщения, после чего создает событие Onidle
procedure CreateForm(FormClass: TFormClass; var Reference) ; | Создает объект-окно: вызывает конструктор класса TFormClass и связывает его значение с переменной Reference
procedure CancelHint; | Запрещает показ ярлычков оперативной подсказки
function HelpContext(Context: THelpContext): Boolean; | Вызывает раздел context справочной службы и возвращает True, если такой раздел существует
function HelpJump(const JumpID: String): Boolean; | Вызывает раздел справочной службы по его имени JumpID и возвращает True, если такой раздел существует
procedure Hide Hint; | Убирает текущий ярлычок оперативной подсказки
TWindowHook = function(var Mes sage: TMessage): Boolean of object procedure HookMainWindow(Hook: TWindowHook); | Регистрирует функцию для перехвата сообщений Windows
function MessageBox(Text, Cap tion: PChar; Flags: Longint): Integer; | Создает и показывает диалоговое окно с текстом и кнопками: Text - сообщение в окне; Caption - заголовок окна. Назначение Flags и возвращаемое значение см. ниже
procedure Minimize; | Минимизирует программу до пиктограммы
procedure NormalizeTopMosts; | Приостанавливает действие стиля fsTopMost для всех окон программы
procedure NormalizeAllTopMosts; | Распространяет действие NormalizeTop Mosts на главное окно программы
procedure ProcessMessages; | То же, что HandleMessage, но не создает событие OnIdle
procedure Restore; | Восстанавливает первоначальные размеры всех окон программы до ее минимизации
procedure RestoreTopMosts; | Восстанавливает действие стиля fsTopMost для всех окон программы
procedure Run; | Запускает цикл получения и обработки Windows-сообщений
procedure ShowException(EiException) ; | Реализует умалчиваемую обработку исключительных ситуаций
procedure Terminate; | Завершает работу программы
procedure UnhookMainWindow(Hook:TWindowHook); | Удаляет перехватчик сообщений, ранее установленный методом ookMainWindow. Параметр Hook - имя той же функции перехвата, что и при обращении к HookMainWindow

С помощью методов HandleException и ProcessMessages программа может временно приостановить свою работу до тех пор, пока не будут обработаны все предназначенные ей сообщения Windows. Это бывает необходимо для обновления видимых компонентов в процессе отображения длительного по времени цикла работы. Если, например, программа изменит текст метки или состояние компонента progressBar, сообщение об этом поступит в очередь сообщений и останется в ней, пока не закончится текущая работа. Вызов HandleException или ProcessMessages в этом случае позволит метке или ProgressBar действительно обновить свой вид и отобразить новую фазу процесса. Разница между методами HandieException И ProcessMessages состоит в том, что первый после очистки очереди сообщений вызывает обработчик фонового события OnIdie, а второй не делает этого. Назначение события onidie см. после описания событий объекта Application.

С помощью метода HookMainWindow можно зарегистрировать функцию Hook, которая будет получать все адресованные программе сообщения windows. По моим наблюдениям, эта функция получает

Сообщение Message параллельно c главной оконной функцией Application. ProcessMessage или после нее, т. к. ни изменение сообщения, ни возвращаемый функцией Hook результат никак не влияют на поведение программы. Как следует из встроенной справочной службы, в качестве функции Hook должна регистрироваться оконная функция диалогового окна, созданного не с помощью Delphi. Если вы решите использовать HookMainWindow для наблюдения за посылаемыми программе сообщениями, не забудьте перед завершением программы отключить регистрацию Hook методом unHookMainwin-dow, в противном случае завершение работы программы будет сопровождаться сообщением об ошибке. Для целей перехвата сообщений и их возможного изменения следует использовать обработчик Application. OnMessage, который вызывается из оконной функции Программы Application. ProcessMessage до вызова методов Trans-lateMessage И DispatchMessage, реализующих стандартную обработку сообщений. В Delphi 5 (и 6) появилось дополнительное удобное средство перехвата сообщений - компонент TApplicationEvents (см. п. 18.2.14).

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

mbA bort Retry Ignore | В окно вставляются кнопки Abort, Retry, Ignore
mbApplModal | Пользователь должен закрыть это окно перед тем, как продолжить работу с окном, его породившим; однако он может переходить в другие окна и работать с ними. Этот признак берется по умолчанию, если не определены ни mbSystemModal, ни inbTaskModal
mbDefButtonl | Кнопкой по умолчанию является кнопка 1
mbDefButton2 | Кнопкой по умолчанию является кнопка 2
mbDefButton3 | Кнопкой по умолчанию является кнопка 3
mbDefButton4 | Кнопкой по умолчанию является кнопка 4
mbHelp | Вставляется кнопка Help
mbIconAftensk | В окно вставляется пиктограмма в виде строчной буквы i в кружке
mblconExclcimation | В окно вставляется пиктограмма в виде знака восклицания
mbIconHand | В окно вставляется пиктограмма в виде знака
mblconlnformation | Стоп То же,что и mbIconAsterisk
mb IconQuestion | В окно вставляется пиктограмма в виде знака вопроса
mbIconStop | To же, ЧТО И mbIconHand
mb0k | В окно вставляется кнопка Ok
mbOkCancel | В окно вставляются кнопки Ok и Cancel
mbRetry Cancel | В окно вставляются кнопки Retry и Cancel
mbSy stem Modal | Все программы приостанавливают свою работу, пока пользователь не закроет это окно
mbTaskModal | Приостанавливается работа во всех окнах данной программы до тех пор, пока пользователь не закроет это окно
mbYesNo mb Yes No Cancel | В окно вставляются кнопки Yes, ыо В окно вставляются кнопки Yes, No, Cancel

Например:

Application.MessageBox('Текст в окне', 'Заголовок окна', mb_IconQuestion+mb_YesNo) ;

Функция MessageBox возвращает такие значения:

idAbort idCancel

idlgnore idOk

Была нажата кнопка Abort Была нажата кнопка cancel

Была нажата кнопка ignore Была нажата кнопка ok

idNo idRetry

idYes

Была нажата кнопка No Была нажата кнопка Retry Была нажата кнопка Yes

Альтернативой MessageBox является глобальная функция MessageDIg, объявленная в модуле Dialogs:

function MessageDIg(const.Msg: String;AType: TMsgDIgType; AButtons: TMsgDIgButtons;HelpCtx:- Longint): Word;

Эта? функция имеет шараметр; HelpCtx, с помощью которого создаваемое ею окно можно легко связать с Help-службой. В отличие от MessageBox заголовок окна MessageDIg не может изменяться программистом - он опредёляётся типом окна, задаваемым параметром AType (возможные значения: mtWarning - пиктограмма в виде знака: Восклицания, заголовок Warning; mtError - пиктограмма в виде знака Стоп заголовок Error; mtlnformation:-- пиктограмма в виде:

Буквы i, заголовок information; mtConfirmation - пиктограмма в виде знака вопроса, заголовок Confirm; mtCustom - нет пиктограммы, заголовок содержит имя исполняемого файла программы) Набор кнопок задаётся в виде конструктора множества и может включать: mbYes-—кнопка Хез (результат, возвращаемый функцией при нажатии на эту кнопку - nu-Yes); mbNo -кнопка. No (mrNo); mbOk - кнопка OK (mrOk); mbCancel - кнопка ; Cancel (mrCancel);

mbHelp -кнопка Help (после нажатия на эту кнопку окно не закрывается); mbAbort - кнопка Abort (mrAbort); mbIgnore- кнопка Ignore (mrlgnore); mbAll —кнопка All .(mbА11). В отличие от MessageBox раскрытие окна MessageDIg не сопровождается системным звуком.

Для объекта Application определены следующие события:

property OnActivate: TNotifyEvent; | Возникает, когда программа получает фокус ввода
property OnDeactivate: TNotifyEvent; | Возникает, когда программа теряет фокус ввода
TExceptionEvent = procedure (Sender: TObject; E: Exception) of object; property OnException: TException Event; | Обработчик этого события реализует умалчиваемую обработку исключительных ситуаций
THelpEvent = function (Command: Word; Data: Longint; var CallHelp: Boolean): Boolean of ob ject; property OnHeip: THelpEvent; | Возникает при каждом обращении к справочной службе
property OnHint: TNotifyEvent; | Возникает при необходимости отобразить длинную часть оперативной подсказки
TIdleEvent = procedure (Sender: TObject; var Done: Boolean) of object; property Onldle: TIdleEvent; | Обработчик этого события реализует фоновую работу (см. ниже пояснения)
TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object; property OnMessage: TMessageEvent; | Возникает при получении программой сообщения Windows. С помощью обработчика этого события можно обработать не сколько или все сообщения до того, как их получит активная форма
property OnMinimize: TNotifyEvent; | Возникает при минимизации программы
property OnRestore: TNotifyEvent; | Возникает при восстановлении программой первоначальных размеров после ее минимизации
property OnShowHint: TShowHintE- vent; | Возникает при необходимости отобразить окно с ярлычком оперативной подсказки

Событие OnIdie возникает всякий раз, когда программа обнаруживает, что ее очередь сообщений пуста. Обработчик этого события может незаметно для пользователя выполнить вспомогательную работу, например, проверить орфографическую правильность только что введенного слова или собрать и обработать статистические данные. Важно помнить, что выполняемая в обработчике работа не может занимать много времени, иначе программа перестанет реагировать на запросы пользователя. Если фоновая работа отнимает много времени, ее следует разбить на сравнительно небольшие фрагменты или периодически вызывать в ней метод HandieMessage. Обработчик в параметре Done сообщает о необходимости продолжить фоновую работу (Done = False) или 0 ее завершении (Done = True). В первом случае программа просмотрит очередь сообщений и после ее очистки вернет управление обработчику OnIdie. Во втором случае программа будет ожидать поступления новых сообщений.

Событие Onidie введено в Delphi 1 для совместимости с Windows З.х в которой не было многопоточности; Если вы работаете с Windows 32 и вам действительно нужно сделать некоторую фоновую работу, имеет смысл поручить ее выполнение отдельному потоку команд(см.га. 14)

Для события ОnShowHint определены такие типы:

type

THintInfo = record

HintControl: TControl;

HintPos: TPoint;

HintMaxWidth: Integer;

HintColor: TColor;

CursorRect: TRect;

CursorPos: TPoint;

ReshowTimeout: Integer;

HideTimeout: Integer;

HintStr: String;

end;

TShowHintEvent =

procedure (var HintStr: String;

var CanShow: Boolean;

var Hintlnfo: THintInfo)

of object;

В параметре HintStr обработчик получает текст, который должен содержать ярлычок, а в параметре Hintlnfo - дополнительную информацию, в том числе: HintControl - элемент, для которого выводится ярлычок; HintPos - координаты верхнего левого угла ярлычка; HintMaxWidth - максимальная ширина окна; HintColor - цвет окна; CursorRect - прямоугольник, в котором должен оставаться указатель мыши, чтобы появился ярлычок; CursorPos - текущее положение указателя мыши; ReshowTimeout - задержка в миллисекундах от момента возникновения события до появления ярлычка;

HideTimeout - время показа ярлычка в миллисекундах.

20.2. SCREEN

Объект screen класса TScrеen инкапсулирует свойства и методы, упрощающие работу с дисплеем ПК, в том числе с его помощью оперативно изменяется вид используемого программой указателя мыши. Как правило, программа устанавливает указатель в виде песочных часов перед началом длинной по времени работы и восстанавливает умалчиваемую форму после ее завершения. Для этого используется свойство Cursor объекта Screen:

Screen.Cursor := crHourGlass;

// Выполняем длительную работу:

….

// Восстанавливаем умалчиваемую срорму:

Screen.Cursor :== crDefault;

Свойства объекта Screen:

property ActiveControl: TWinCon- trol; | Содержит ссылку на элемент с фокусом ввода
property ActiveForm: TForm; | Содержит ссылку на окно с фокусом ввода
property Cursor: TCursor; | Определяет текущий указатель мыши для окон программы
property Cursors[Index: Integer]: HCursor; | Открывает доступ к одному из зарегистрированных указателей мыши по его индексу
property CustomFormCount: Integer; | Содержит количество окон или страниц свойств программы, открытых в данный момент
property CustomForms[Index: Integer] : TCustomForm; | Содержит список объектов класса TCustom Form - открытых в данный момент окон программы
property DataModuleCount: Inte ger; | Содержит количество модулей данных в программе
property DataModules[Index: Integer] : TDataModule; | Содержит список модулей данных - объектов класса TdataModule
property Fonts: TStrings; | Содержит список имен всех экранных шрифтов
property FormCount: Integer; | Содержит общее количество показываемых на экране окон программы
property Forms[Index: Integer]: TForm; | Открывает доступ к окну программы по его индексу
property Height; Integer; | Содержит высоту экрана в пикселях
property IconFont: TFont; | Определяет шрифт для надписи ниже пиктограммы минимизированной программы
property PixelsPerInch: Integer; | Содержит разрешающую способность экрана в пикселях на линейный дюйм
property Width: Integer; С объектом screen связаны два | Содержит ширину экрана в пикселях события:
property OnActiveControlChange: TNotifyEvent; | Возникает при переходе фокуса ввода от одного элемента к другому
property OnActiveFormChange: TNotifyEvent; | Возникает при переходе фокуса ввода от одной формы к другой

20.3. PRINTER

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

Вывод на принтер в Windows ничем не отличается от вывода на экран: в распоряжение программиста предоставляется свойство сап-vas объекта printer, содержащее набор чертежных инструментов и методы, свойственные классу TCanvas. Размер листа бумаги в пикселях принтера определяют свойства Height и Width, а набор принтерных шрифтов - свойство Fonts.

Свойства объекта printer:

property Aborted: Boolean; | Содержит True, если выполнение предыдущего задания на печать было досрочно прекращено
property Canvas: TCanvas; | Канва принтера - основной инструмент создания изображения на листе бумаги
type TPrinterCapability = (pcCopies, pcOrientation, pcCollation) ; TPrinterCapabilities = set of TPrinterCapability; property Capabilities: TPrinter Capabilities; | Содержит возможности текущей настройки драйвера принтера: pcCopies - может печататься несколько копий документа; pcOrien tation -драйвер поддерживает разную ориентацию листа бумаги; pcCollation -документ печатается в подбор по экземплярам
property Copies: Integer; | Указывает требуемое количество копий документа
property Fonts: TStrings; | Содержит список всех доступных принтеру шрифтов
TPrinterOrientation = (poPortrait, poLandscape) ; property Orientation: TPrinterOrientation; | Определяет ориентацию листа бумаги: poPortrait вертикальная; poLandscape - горизонтальная
property PageHeight: Integer; | Содержит высоту листа бумаги в пикселях принтера
property PageNumber: Integers; | Содержит номер печатаемой страницы документа (начинается с 1)
property PageWidth: Integer; | Содержит ширину листа бумаги в пикселях принтера
property Printerlndex: Integer; | Содержит индекс умалчиваемого принтера в списке Printers
property Printers: TStrings; | Содержит список всех доступных принтеров
property Printing: Boolean; | Содержит True, если принтер занят печатью документа
property Title: String; | Содержит имя задания на печать
Методы объекта printer:
procedure Abort; | Досрочно прекращает печать документа
procedure BeginDoc; | Начинает печать документа
procedure EndDoc; | Завершает печать документа
procedure NewPage; | Начинает печать новой страницы документа

Вот как, например, можно напечатать текст, содержащийся в редакторе Memo 1:

procedure TFormI.ButtonlClick(Sender: TObject);

var

Y,dY,k: Integer;

begin

if Memol.Lines.Count=0 then Exit;

Screen.Cursor := crHourGlass;

with Printer do

begin

BeginDoc;

with Printer.Canvas do

begin

Font := Memol.Font;

dY := TextHeight('1'); // Определяем высоту строки

Y := 2*dY; // Отступ от верхнего края листа

for k := 0 to Memol.Lines.Count-1 do

begin

//Выводим очередную строку TextOut(О,Y,Memol.Lines[k]);

//Смещаемся на следующую строку листа

inc(Y,dY) ;

if PageHeight-Y<2*dY then

// Нижний край листа?

begin //Да

NewPage; //Переход на новый лист

// Выводим номер страницы

TextOut(0,0,'с.'+IntToStr(PageNumber)) ;

Y := 2*dY

end;

end;

end;

EndDoc;

end;

Screen.Cursor := crDefault;

end;

Замечу, что до обращения к BeginDoc программа не имеет никаких сведений о принтере, и поэтому широко распространенной ошибкой будет объединение такого рода:

with Printer, Printer.Canvas do

begin

BeginDoc;

Font.Size := 12;

end;

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

Printer.BeginDoc; //Обращаемся к драйверу принтера

//и настраиваем нужным образом канву

with Printer.Canvas do

begin

Font.Size := 12;

end;

Во многих случаях для печати документа гораздо проще использовать специальные компоненты, расположенные на странице QRe-port галереи компонентов Delphi. Эти компоненты разработаны для создания отчетов по материалам, хранящимся в базах данных, но могут с успехом использоваться и для печати обычных документов. Центральный компонент QuickRep имеет метод preview, с помощью которого можно просмотреть образ печатного документа и при желании напечатать документ.

Методику использования компонентов QReport поясню на следующем примере. Пусть на форме Form1 имеется текстовый редактор Memol, просмотр содержимого которого в виде образа печатного документа открывает нажатие кнопки Button1. Создайте в проекте новую пустую форму Form2 и положите на нее компонент QuickRepl.

На этот компонент положите QRBand1, а на него - QRMemo1. Напишите такой обработчик нажатия кнопки Buttoni:

procedure TFormI.ButtonlClick(Sender: TObject);

begin

with Form2 do

begin

//Копируем содержимое Memol в QRMemo1:

QRMemoi.Lines.Assign(Memol.Lines);

//Используем установленный в Memol шрифт:

QRMemoi.Font := Memol.Font;

//Вызываем окно просмотра и печати документа:

QuickRepl.Preview

end;

end;

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

Рис. 20.1. Окно просмотра печатного документа

20.4. CLIPBOARD

В Windows широко используется буфер межпрограммного обмена данными Clipboard. Если программа Delphi сослалась на модуль Сlipbrd, для нее становится доступным автоматически созданный объект clipboard, инкапсулирующий методы и свойства для работы с буфером.

Свойства объекта clipboard:

property AsTex-t: Strings; | Рассматривает содержимое буфера как текстовую строку длиной до 255 символов. Для обмена с буфером более длинными строками следует использовать его методы SetTextBuf и GetTextBuf
property FormatCount: Integers; | Возвращает общее количество форматов хранящихся в буфере данных
property Formats[Index: Integer]: Word; | Открывает доступ к данным по формату, указанному индексом Index
Методы объекта clipboard:
procedure Assign(Source: TPersistent) ; | Копирут в буфер изображение
procedure Clear; procedure Closes; | Очищает буфер Уменьшает на 1 счетчик блокировок буфера и закрывает его, если счетчик содержит 0
function GetTextBuf(Buffer: PChar; BufSize: Integer): Integers; | Копирует из clipboard не более BufSize символов в текстовый буфер Buffer и возвращает действительное количество скопированных символов
procedure HasFormat(Format: Word): Boolean; procedure Open; | Возвращает True, если буфер содержит данные в формате Format Увеличивает на 1 счетчик блокировок буфера и открывает его, если он еще не был открыт
procedure SetTextBuf(Buffer: PChar); | Помещает в clipboard содержимое текстового буфера Buffer

Чтобы поместить в буфер изображение, хранящееся, например, в объекте BitMap класса TBitMap, следует обратиться к методу Assign буфера:

Clipboard.Assign(BitMap);

а для копирования изображения из буфера - к методу Assign объекта-приемника: BitMap.Assign(Clipboard) ;

20.5. НАСТРОЙКА ПРОГРАММ

Удобным средством хранения индивидуальных параметров настройки программы являются широко используемые в Windows З.х файлы инициации (файлы с расширением ini). Для Windows 32 индивидуальная настройка программ реализуется с помощью системного реестра. В этом разделе рассматриваются оба способа, т. к. даже если вы работаете с Windows 32, перенос программ на другие ПК проще реализовать с помощью ini-файлов.

20.5.1. Файлы инициации

Файлы инициации в Delphi связаны с объектами класса TIniFile. Эти объекты не являются глобальными и создаются программно по мере надобности. С каждой программой можно связать сколько угодно файлов инициации.

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

<ИМЯ_ПАРАМЕТРА>=<ЗНАЧЕНИЕ>

Например:

[Location]

DataBase=C:\mybase Graphics=C:\BITMAP

В этом примере в секции Location файла инициации представлены два параметра с именами DataBase и Graphics. Объекты класса TiniFiie предоставляют удобные средства чтения/записи параметров по именам параметров и секций.

В объекте имеется единственное свойство Filename: String, содержащее имя файла инициации с возможным маршрутом поиска.

Методы класса TIniFile:

constructor Create(const FileNameString) ;

Создает объект-файл с именем FileName. Это имя автоматически переносится в свойство FileName. Если физический файл существовал, он открывается, если нет создается

procedure DeleteKey(const Section,

Ident: String) ;

Уничтожает значение параметра Ident в секции Section. Если указана несуществующая секция или имя несуществующего параметра, метод создаст секцию и/или установит в ней параметр с пустым значением

procedure EraseSection(const Section: String) ;

Удаляет секцию со всеми ее параметрами.

Если секции не существует, возникает исключительная ситуация

function ReadBool (const Section,

Ident:.String; Default: Boolean): Boolean;

Возвращает значение логического параметра Ident в секции section. Возвращает Default, если не существует указанная

секция, или в ней нет нужного параметра, или, наконец, параметр имеет не логическое значение

function Readlnteger(const Section, Ident: String; Default:Longint): Longint;

Возвращает значение целого параметра Ident в секции Section, Возвращает Default, если не существует указанная секция, или в ней нет нужного параметра, или, наконец, параметр имеет не целое значение

procedure ReadSection (const Section: String; Strings: TStrings);

Помещает в список Strings имена всех параметров из секции Section

procedure ReadSections (Strings:

TStrings);

Помещает в список Strings имена всех

секций файла

procedure ReadSectionValues(const Section: String; Strings: TStrings) ;

Помещает в список strings имена всех

значений из секции Section

function ReadString(const Section,

Ident, Default: String): String;

Возвращает значение строкового параметра Ident в секции section. Возвращает Default, если не существует указанная секция, или в ней нет нужного параметра,

или, наконец, параметр не имеет значения

procedure WriteBool(const Section,

Ident: String; Value: Boolean);

Записывает в параметр Ident секции section логическое значение value.

procedure Writelnteger(const Sec

tion, Ident: String; Value: Longint) ;

Записывает в параметр Ident секции Section целое значение Value

procedure WriteString(const Section, Ident, Value: String);

Записывает в параметр Ident секции Section строковое значение value

20.5.2. Системный реестр Windows

Системный реестр Windows - это общедоступная база данных, хранящая индивидуальную для каждого компьютера настроечную информацию, используемую программным обеспечением. Данные в реестре упорядочены в древовидные структуры, состоящие из узлов, которые в терминологии Windows называются ключами. Каждый ключ имеет один родительский ключ, один или несколько дочерних ключей и ноль или несколько параметров типа <имя_парамет-ра>=<значение>, Исключение представляют, корневые ключи (они не имеют родителей) и дочерние ключи низшего уровня (они не имеют дочерних ключей). По умолчанию несистемное программное обеспечение регистрирует свою информацию в корневом ключе с именем hkey_current_user. Для просмотра/коррекции системного реестра используется системная утилита regedit.exe. Системному реестру Windows соответствует объект класса TRegistry.

Методы класса TRegistry:

procedure CioseKey; | Записывает информацию в ключ и закрывает его
constructor Create; | Создает объект класса TRegistry. Устанавливает в RootKey значение hkeycurrent - user и в LazyWrite значение True
function CreateKey(const Key: String): Boolean; | Создает ключ с именем Key. Если имя начинается символом “\”, ключ является подключом корневого ключа, в противном случае - подключом текущего ключа
function DeleteKey(const Key: String): Boolean; | Удаляет ключ Key и возвращает True, если операция прошла успешно
function DeleteValue(const Name: String): Boolean; | В текущем узле удаляет значение параметра с именем Name
destructor Destroy; | Уничтожает объект TRegistry
TRegDataType == (rdUnknown, rdString, rdExpandString, rdlnteger, rdBinary) ; TRegDataInfo = record RegData: TRegDataType; DataSize: Integers- end; | Возвращает в параметре value значение, определяющее тип данных для параметра сименем valueName текущего ключа: rdunknown - неизвестный тип; rdString - стро ковый тип;rdExpandString - значением является строка, описывающая переменную окружения Windows, например “%РАТН%”;
function GetDataInfo(const ValueName: String; var Value: TRegDataInfo) : Boolean; | rdlnteger - целочисленный тип; rdBinary - двоичный тип (набор битов)
function GetDataSize(const Valu- eName: String): Integer; | Возвращает длину значения параметра с именем valueName в текущем узле
TRegDataType = (rdUnknown, rdString, rdExpandString, rdlnteger, rdBinary) ; function GetDataType(const ValueName: String): TRegDataType; | Возвращает значение, определяющее тип данных для параметра с именем ValueMame текущего ключа (см. метод GetDataInfo)
TRegKeyInfo = record NumSubKeys: Integer; MaxSubKeyLen: Integer; NumValues: Integer; MaxValueLen: Integer; MaxDataLen: Integer; FileTime: TFileTime; end; function GetKeyInfo(var Value: TRegKeyInfo): Boolean; | Возвращает в параметре Value информацию о текущем узле: - NumSubKeys количество дочерних узлов;MaxSubKeyLen - максимальная длина имени дочернего узла; NumValues - количество параметров; Мах-valueben - максимальная длина имени параметра; MaxDataLen - максимальна длина параметра; FileTime - время последнего обновления ключа
procedure GetKeyNames(Strings:TStrings) ; | Возвращает в параметре strings имена всех дочерних ключей
procedure GetValueNames(Strings:TStrings) ; | Возвращает в параметре Strings имена всех параметров
function HasSubKeys: Boolean; | Возвращает True, если ключ имеет дочерние ключи
function KeyExists(const Key: String): Boolean; | Возвращает True, если существует ключ с именем Key
function LoadKey(const Key, File- Name: String): Boolean; | Создает ключ с именем Key как дочерний ключ корневого ключа и загружает из файла FileName его содержимое (параметры и все подключи)
procedure MoveKey(const OldName, NewName: String; Delete: Boolean) ; | Копирует или перемещает ключ OldName в ключ NewName: Delete -содержит True для перемещения
function OpenKey(const Key: String; CanCreate: Boolean): Boolean; | Открывает существующий или создает и открывает новый ключ с именем Key и воз вращает True, если операция прошла успешно. CanCreate разрешает/запрещает создание ключа, если он не существует
function ReadBinaryData(const Name: String; var Buffer; BufSize: Integer): Integer; | Копирует не более Bufsize байт в переменную Buffer из параметра с именем Name. Возвращает количество действительно скопированных байтов
function ReadXXXX(const Name: String): YYYY; | Возвращает значение параметра типа yyyy с именем Name
function RegistryConnect(const UNCName: String): Boolean- | Разрешает удаленному компьютеру ONCName доступ к системному реестру вашего компьютера
procedure RenameValue(const Old Name, NewName: Strings; | Переименовывает параметр OldName
function ReplaceKey(const Key FileName, BackUpFileName: String): Boolean; | Заменяет содержимое ключа Key содержимым файла FileName.BackUpFileName -имя файла, в котором будет сохраняться старое содержимое ключа
function RestoreKey(const Key, FileName: String): Boolean; | Считывает содержимое ключа Key из файла FileName
function SaveKey(const Key, File Name: String): Boolean; | Сохраняет содержимое ключа Key в файле FileName
function ValueExists(const Name: String): Boolean; | Возвращает True, если параметр с именем Name имеет значение
procedure WriteBinaryData(const Name: String; var Buffer; Buf- Size: Integer) ; | Копирует не более Bufsize байтов из буфера Buffer в параметр с именем Name
procedure WriteXXXX(const Name: String; Value: YYYY); | Записывает в параметр с именем Name данные Типа YYYY

Delphi создает надстройку над классом TRegistry для того, чтобы манипуляция с системным реестром Windows была подобна манипуляциям с файлами инициации. Эта надстройка инкапсулируется в классе TRegIniFile.

Класс TRegIniFile имеет свойство FileName: String, Хранящее имя ключа, для которого будут осуществляться нужные действия. Эти действия реализуются следующими методами класса:

constructor Create(const FileName : String) ; | Создает объект класса TRegIniFile и связывает его с ключом FileName
procedure DeleteKey(const Section, Ident: String); | Удаляет параметр с именем Ident в ключе Section
procedure EraseSection(const Section : String) ; | Удаляет ключ section со всеми его параметрами и под ключами
function ReadBool(const Section, Ident: String; Default: Boolean): Boolean; | Возвращает значение логического параметра с именем Ident в ключе section
function Readlnteger(const Sec tion, Ident: String; Default: Longint): Longint; | Возвращает значение целочисленного параметра с именем Ident в ключе Section
procedure ReadSection(const Section: String; Strings: TString); | Возвращает в Strings имена всех параметров ключа Section
procedure ReadSections(Strings:TStrings); | Возвращает в Strings имена всех ключей, связанных с текущим ключом
procedure ReadSectionValues(const Section: String; Strings: TStrings) ; | Возвращает в Strings значения всех пара метров ключа section
function ReadString(const Sec tion, Ident, Default: String):String; | Возвращает значение строкового параметра с именем Ident в ключе section
procedure WriteBool(const Sec tion, Ident: String; Value: Boolean) ; | Записывает логическое значение value в параметр Ident ключа section
procedure Writelnteger(const Sec tion, Ident: String; Value: Longint) ; | Записывает целочисленное значение Value в параметр Ident ключа Section
procedure WriteString(const Sec tion, Ident, Value: String); | Записывает строковое значение value в параметр Ident ключа Section