Валерий Фаронов
DELPHI 2005 ЯЗЫК, СРЕДА, РАЗРАБОТКА ПРИЛОЖЕНИЙ
ПИТЕР М о с к в а • Санкт-Петербург • Н и ж н и й Н о в г о р о д • В о р о н е ж Ростов-на-Дону • Е к а т е р и н б у р г • Самара • Н о в о с и б и р с к Киев • Харьков • М и н с к
2005
ББК 32.973-018.1 УДК 004.43
Краткое содержание
Ф24
От автора Введение Ф24
Фаронов В. В. Delphi 2005. Язык, среда, разработка приложений. — СПб.: Питер, 2005. — 560 с: ил. ISBN 5-469-00826-6 Книга содержит описание системы Delphi 2005 — среды разработчика и языка программирования, — с точки зрения программиста Delphi, а также достаточно полное руководство по созданию разнообразных программ общего назначения. Рассмотрены основные особенности языка Delphi и интегрированной среды разработчика по сравнению с предыдущими версиями. Описаны методики создания программ общего назначения, использующие технологию .NET. Приведены справочный материал по некоторым процедурам и функциям VCL и краткое руководство по языку С#. Издание в первую очередь ориентировано на программистов, работаю щих в версиях Delphi с первой по седьмую, но будет полезно и тем, кто освоил работу в Delphi 8.
ББК 32.973-018.1 УДК 004.43
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав. Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги.
ISBN 5-469-00826-6
© ЗАО Издательский дом «Питер», 2005
ЧАСТЬ Глава Глава Глава Глава Глава Глава Глава Глава Глава ЧАСТЬ Глава Глава Глава Глава Глава Глава Глава Глава Глава
16 18 I. Язык Delphi 1. Основы Delphi 2. Элементы языка 3. Типы данных 4. Процедуры и функции 5. Классы 6. Интерфейсы 7. Варианты 8. Файлы 9. Модули и пространства имен
И. 10. 11. 12. 13. 14. 15. 16. 17. 18.
ЧАСТЬ I I I . Глава 19. Глава 20. Глава 21. Глава 22. Глава 23.
43 44 73 81 119 137 160 166 174 189
Использование компонентов VCL Классы общего назначения Общие свойства компонентов Компоненты категории Standard Компоненты категории Additional Компоненты категории Win32 Компоненты категории System Компоненты категории Dialogs Формы Программа
199 200 224 248 268 298 338 350 362 373
Создание WinForms-приложений Классы общего назначения Общие свойства, методы и события компонентов Компоненты категории Windows Forms Компоненты категорий Components и Dialogs Форма и WinForms-программа
415 416 450 460 481 497
Приложения , Приложение А. Некоторые стандартные подпрограммы, переменные, классы Приложение Б. Краткая справка по языку С#
509 510 527
Литература Об авторе Алфавитный указатель
543 544 548
Содержание
Содержание
Операторы языка Оператор присваивания Составной оператор и пустой оператор Условный оператор Операторы повторений Оператор выбора Метки и операторы перехода Массивы Процедуры и функции Правила кодирования программ
Глава 2. Элементы языка От автора От издательства
Введение Знакомство стехнологией .NET Суть технологии Общеязыковая инфраструктура Компилирование в промежуточный язык CIL Компилирование CIL в машинные инструкции Исполнение кода Сборки Домены приложений Домены и сборки Знакомство с Delphi 2005 Устаревшие и новые средства Delphi Две модели Windows-приложений Работа сбазами данных Работа с Интернетом Некоторые возможности среды разработчика Основные инструментальные панели среды
16 17
18 18 18 19 22 22 23 24 26 27 27 27 32 33 34 35 36
ЧАСТЬ I. Язык Delphi
43
Глава 1. Основы Delphi
44
Структура программы Delphi Специальные типы модулей Другие файлы проекта Взаимодействие программных модулей Элементы программы Типы Строковый и символьный типы Целые типы
44 45 46 46 47 50 51 52
*
,
,
Алфавит Идентификаторы Константы Выражения Операции
Указатели и динамическая память Динамическая память Указатели Псевдонимы типов
Глава 4. Процедуры и функции Локализация имен Описание подпрограммы Заголовок и стандартные директивы Параметры Процедурные типы РеК\/ПГИЯ И П П Р П Р Ж Д Ю Ш Р Р ПП1/1ГЯМ1/1Р
54 54 55 55 57 62 63 64 66 69
73 73 75 76 78 78
Глава 3. Типы данных Общая система типов Простые типы Порядковые типы Вещественные типы Тип дата-время Особенности реализации простых типов Структурированные типы Массивы Записи Особенности записей втехнологии .NET Множества Строки
,
7
81
,
82 84 84 90 93 94 95 96 100 101 104 108
115 115 115 118
Н9 119 123 123 124 132
8
Содержание
Содержание
Глава 5. Классы
•
137
Основные понятия Инкапсуляция Наследование Полиморфизм Члены класса Поля Методы Свойства События Объявление класса , Приведение типов классов Изменения в модели классов Delphi 2005 (8) Области видимости strict Статические члены класса Атрибуты sealed и final Вложенные типы События с несколькими слушателями Специальные атрибуты Помощники класса
137 137 138 139 139 139 140 146 147 148 151 151 151 152 153 154 155 155 157
Глава 6. Интерфейсы Объявление интерфейсов Компонентные классы Использование интерфейсов Глобально-уникальные идентификаторы Делегирование интерфейсов
Глава 7. Варианты Преобразование вариантов к данным других типов Использование вариантов в выражениях Подпрограммы для работы с вариантами Вариантные массивы , Пользовательские варианты
Глава 8. Файлы Особенности файлов Текстовые файлы Двоичные файлы Подпрограммы для работы с файлами и файловой системой
Глава 9. Модули и пространства имен Пространства имен Структура модулей
160
'.
160 161 162 164 165
166 168 169 170 171 172
174 174 175 180 184
189 190 190
Заголовок модуля и связь модулей друг сдругом Интерфейсная часть Исполняемая часть Инициализирующая и завершающая части Доступ к объявленным в модуле объектам Типы модулей в Delphi Модули динамических библиотек Пакеты
9 191 192 192 193 194 197 197 197
Модули потоков команд
198
ЧАСТЬ I I . Использование компонентов VCL
199
Глава 10. Классы общего назначения
200
Класс Exception — обработка исключений Защищенные блоки Класс Exception Стандартные классы исключений Вызов исключения Создание собственного класса Класс TList — списки Классы TCollection и TCollectionltem — коллекции Класс TCollection Класс TCollectionltem Классы TStrings и TStringList— наборы строк и объектов Класс TStrings Класс TStringList Графический инструментарий Класс TFont Класс ТРеп Класс TBrush Класс TCanvas Классы TGraphic nTPicture
Глава 1 1 . Общие свойства компонентов Иерархия компонентов Имена и владельцы компонентов Родительские и дочерние компоненты Положение, размеры и оформление компонентов Указатели мыши Реакция на события мыши и клавиатуры События мыши События клавиатуры Клавиатура в Windows Фокус ввода Механизм действий
,
200 200 202 203 204 205 207 211 212 213 213 213 214 215 215 216 217 218 221
224 224 225 227 229 234 235 235 236 237 240 240
10
Содержание
Содержание
Механизм перетаскивания Механизм причаливания Поддержка справочной службы
241 243 245
Глава 12. Компоненты категории Standard TMainMenu— главное меню формы TPopupMenu— контекстное меню TLabel— надпись TEdit—однострочное редактируемое текстовое поле ТМето — многострочное редактируемое текстовое поле TButton— кнопка TCheckBox— флажок TRadioButton — переключатель TListBox— список TComboBox— комбинированный список TScrollBar— ползунок TGroupBox— панель группирования TRadioGroup— группа переключателей TPanel— панель TActionList— список действий
248 248 250 250 251 254 256 257 258 258 262 263 263 264 264 265
,
Глава 13. Компоненты категории Additional TBitBtn— кнопка с изображением TSpeedButton — кнопка панели инструментов TMaskEdit— поле с маской ввода TStringGrid — текстовая таблица TDrawGrid— произвольная таблица TImage— изображение TShape— стандартная фигура TBevel— кромка TScrollBox— панель сполосами прокрутки TCheckListBox — группа флажков TSplitter— вешка разбивки TStaticText—текстовая метка TControlBar— контейнер для панели инструментов TApplicationEvents— обработчик сообщений Windows TValueListEditor— специализированный редактор списков TLabeiEdit— однострочное поле сметкой TColorBox— список выбора цвета TColorListBox — список выбора цвета TrabSet— набор вкладок
Глава 14. Компоненты категории Win32 TTabControl — набор вкладок TPageControl — набор страниц со вкладками
268
,
268 270 271 272 279 284 286 286 287 287 289 290 290 291 292 295 296 296 297
TImageList— хранилище изображений TRichEdit— поле формата RTF TTrackBar— ползунок TProgressBar— индикатор процесса TUpDown — счетчик TAnimate— анимация TDateTimePicker— ввод и отображение даты/времени TMonthCalendar— календарь TTreeView— иерархическое дерево TListView— иерархический список THeaderControl — управляющий заголовок TStatusBar— строка состояния TToolBar и TToolButton — панель инструментов и кнопки для нее TCoolBar и TCoolBand— панель инструментов и полосы для нее TPageScroller— панель с кнопками прокрутки TComboBoxEx— комбинированный список с расширенными возможностями
Глава 15. Компоненты категории System TTimer — таймер TPaintBox— окно для рисования TMediaPlayer— медиа-плеер
Глава 16. Компоненты категории Dialogs Работа со стандартными диалоговыми окнами TOpenDialog и TSaveDialog — окна открытия и сохранения файлов TOpenPictureDialog и TSavePictureDialog — окна открытия и сохранения изображений TFontDialog — окно выбора шрифта TColorDialog — окно выбора цвета TPrintDialog — окно настройки параметров печати TPrinterSetupDialog — окно настройки параметров принтера TFindDialog — окно поиска TReplaceDialog — окно поиска и замены TPageSetupDialog — окно установки параметров печатаемой страницы
Глава 17. Формы Разновидности форм Компонент TForm Создание и использование форм
11 300 ЗОО ЗОЗ 304 305 306 308 310 311 318 322 326 328 330 333 333
338 338 341 342
350 350 351 354 354 357 357 358 359 360 361
362 362 364 370
298
Глава 18. Программа
373
298 299
Программные файлы Файл проекта
373 374
12
Содержание Файлы модулей Файлы ресурсов Файлы настройки Файлы резервных копий Глобальные объекты Объект Application Объект Screen Объект Printer Объект Clipboard Настройка программы Файлы инициализации Системный реестр Windows Программа и Windows Процессы и потоки Использование памяти Windows-сообщения
Содержание 378 378 378 379 379 379 384 386 391 392 393 397 402 402 409 410
ЧАСТЬ I I I . Создание WinForms-приложений
415
Глава 19. Классы общего назначения
416
Класс Exception — обработка исключений Класс Delegate— обработка событий Класс ArrayList — наборы объектов Класс String и преобразование строк Класс String Преобразования числовых значений Преобразования перечислений Преобразование значений даты-времени Графический инструментарий Класс Graphics Класс Region
Глава 20. Общие свойства, методы и события компонентов Свойства компонентов Категория Accessibility Категория Appearance Категория Behavior Категория Data Категория Design Категория Layout Свойства класса Control Методы компонентов События компонентов
416 417 419 423 424 425 427 427 429 430 444
450 450 450 451 451 451 451 452 452 454 457
Глава 2 1 . Компоненты категории Windows Forms Label— надпись LinkLabel — надпись с гиперссылкой Button — кнопка TextBox — текстовое поле Panel— панель CheckBox— флажок RadioButton — переключатель ComboBox— комбинированный список ListBox— список CheckedListBox— группа флажков TreeView— иерархическое дерево ListView — список со значками TabControl— набор вкладок PictureBox— изображение Splitter— вешка разбивки Toolbar— панель инструментальных кнопок MonthCalendar— календарь DateTimePicker— ввод и отображение даты/времени TrackBar— ползунок HScrollBar и VScrollBar— полосы прокрутки NumericUpDown — поле со счетчиком DomainUpDown — текстовое поле с памятью GroupBox— панель группирования PropertyGrid — браузер свойств StatusBar— строка состояния RichTextBox — поле формата RTF ProgressBar— индикатор процесса ТооГПр — управление оперативной справкой
13
460 460 461 462 462 463 463 464 464 464 465 466 470 472 473 473 474 475 475 475 475 476 477 477 478 479 479 479 480
.'
,
Глава 22. Компоненты категорий Components и Dialogs MainMenu— главное меню ContextMenu — контекстное меню Notifylcon — извещающий значок ImageList— хранилище изображений Timer—таймер ErrorProvider — сигнализатор ошибок HelpProvider— поставщик справочной информации OpenFileDialog и SaveFileDialog — диалоговые окна открытия и сохранения файлов Перекодировка русскоязычных текстов FontDialog — диалоговое окно выбора шрифта PrintPreviewDialog — предварительный просмотр и печать файла ColorDialog — диалоговое окно выбора цвета
481 ,
481 482 482 483 483 484 485 486 489 491 492 495
14
Содержание
Содержание
Глава 23. Форма и WinForms-программа
497
Оператор for Оператор foreach Операторы while и do/while Перечисления Структуры Классы Методы и свойства Наследование События и делегаты
15 534 534 535 536 536 537 538 540 541
Класс Form Свойства формы Методы формы События формы Интерфейс MDI Класс Application Свойства Application Методы Application
498 499 504 505 505 506 507 507
События Application
508
Список литературы
543
Приложения
509
Об авторе
544
Приложение А. Некоторые стандартные подпрограммы, переменные, классы
5Ю
Подпрограммы модуля Match Подпрограммы модуля DateUtils Спецификаторы формата даты/времени Системные переменные, управляющие стандартным отображением данных Правила использования параметров функции FloatToStrF Спецификаторы форматирования вещественных чисел Подпрограммы для работы сфайлами Стандартные классы исключений
Приложение Б. Краткая справка по языку С# Начальные сведения Элементы языка Идентификаторы Операции Переменные и константы Блоки Комментарии Типы данных Явное и неявное преобразование типов Оператор присваивания Массивы Операторы управления Оператор if Оператор switch Операторы break и continue Оператор goto Исключения Циклические операторы
510 514 517 518 519 520 520 522
527 527 528 528 528 528 529 529 529 530 530 531 532 532 532 533 533 534 534
Алфавитный указатель
,
548
От автора
От издательства
17
заны основные особенности языка программирования Delphi и интегрированной среды разработчика Borland Developer Studio for Windows. В первой части изучается язык программирования Delphi. По сравнению с пре дыдущими версиями, язык Delphi 2005 практически не изменился, однако неко торые его конструкции устарели, были добавлены новые. Эти изменения следу ет учитывать только при обращении к технологии .NET.
Значительный по объему материал, связанный с Delphi 2005, определил необхо димость его разбиения на две книги: книга, которую вы держите в руках, содер жит описание языка программирования и среды разработчика, а также достаточно полное руководство по созданию разнообразных программ общего назначения. Во 1 второй книге рассматривается программирование для баз данных и Интернета . ПРИМЕЧАНИЕ — — Замечу, что Delphi 2005 поддерживает целых три языка программирования: Delphi, C# и Visual Basic .NET (VB.NET). Эта книга написана с позиций программиста Delphi.
К моменту создания Delphi 2005 были выпущены 8 версий этой системы про граммирования. Все они (кроме версии 8) построены по принципу преемствен ности: все, что может первая версия, может и последняя, но не наоборот. Версия 8 в этом смысле являла собой исключение: полная поддержка технологии .NET потребовала серьезной переделки, связанной с переводом всех типов на общую систему типов (Common Type System, CTS). В результате версия 8 практически несовместима с версией 7 и более ранними. Версия 2005 может поддерживать технологию .NET, но может ее и не поддержи вать. В первом случае следует говорить лишь о той или иной степени совмести мости с Delphi 8, во втором случае можно достичь совместимости с любой из первых версий, включая версию 1. Версия 8 вышла в декабре 2004 г. Она полностью ориентирована на технологию .NET Ее язык программирования и среда разработчика очень похожи на язык и среду версии 2005, во всяком случае, в той ее части, которая относится к под держке этой технологии. Таким образом, эта книга может оказаться полезной тем из вас, кто приобрел версию 8 и по каким-либо причинам не торопится за менить ее версией 2005. Книга состоит из введения, трех частей и приложений. Во введении описываются суть технологии .NET, ее достоинства и недостатки. Поскольку многие возможности Delphi 2005 основаны на этой технологии, сове тую внимательно изучить эту небольшую вводную часть к книге. В ней же ука1
Эта книга планируется к выпуску в издательстве «Питер» в апреле-мае 2005 г. Ее ориентировочное название: «Delphi 2005. Создание приложений для работы с базами данных и Интернетом».
Во второй и третьей частях рассматриваются методики создания программ об щего назначения, использующих технологию .NET: на основе VCL-форм (часть II) и на основе WinForms (часть III). Благодаря оригинальной технологии по мощников классов (см. главу 5) разработчикам Borland удалось адаптировать су ществовавшую в предыдущих версиях библиотеку компонентов VCL (Visual Component Library) к новой технологии. В результате создание приложений на основе VCL-форм практически не отличается от аналогичной работы в предыду щих версиях, так как существовавшие в этой библиотеке типы и классы неяв ным образом интерпретируются типами и классами CTS. Тем из вас, кто работал с предыдущими версиями, часть II покажется хорошо известной. Приложения на основе WinForms используют «чистую» систему CTS, и поэтому способы их создания существенно различаются. Часть III может быть относительно знако мой (с учетом различий в языках программирования) тем, кто имеет опыт рабо ты с Visual Studio™ .NET 2003 корпорации Microsoft®. В приложении А приводится справочный материал по некоторым процедурам и функциям VCL, в приложении Б — краткое справочное руководство по языку С#. Это руководство, возможно, поможет вам в освоении материала встроенной справочной службы, значительная часть которого ориентирована на этот язык программирования. Москва, 14 декабря 2004 г.
От издательства Ваши замечания, предложения и вопросы отправляйте по адресу электронной почты:
[email protected] (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! Подробную информацию о наших книгах вы найдете на веб-сайте издательства: http://www.piter.com.
Знакомство с технологией .NET
Введение
Система программирования Borland® Delphi™ 2005 For Microsoft® .NET Frame work - сложный программный продукт, дающий программисту все необходи мые средства для создания программ любых сложности и назначения. Характер ной особенностью системы является полная поддержка новой технологии .NET. Во введении к книге приводится краткий обзор технологии .NET, а также языка программирования и среды разработчика Delphi 2005.
Знакомство с технологией .NET Технология .NET - это сравнительно недавнее изобретение программистов кор порации Microsoft. Ее разработчики поставили перед собой задачу создания^ еди ной универсальной платформы (базы) программирования, равно подходящей для разработки любых программ - будь то обычные Windows-приложения, прило жения для работы с базами данных, веб- и Windows-службы, приложения для мобильных и переносных устройств и т. д.
Суть технологии В основе технологии .NET лежит идея использования некоторого промежуточно го машинно-независимого языка. При обычном программировании (с использова нием любых существующих вне технологии .NET языков, от ассемблера до ран них версий Delphi) программа, написанная в понятной для программиста нотации, компилировалась в последовательность машинных инструкций, «понятных» про цессору. В новой технологии программа компилируется в термины машинно-неза висимого языка CIL (Common Intermediate Language - общий промежуточный язык) и сопровождается метаданными - подробными инструкциями как о самой программе, так и обо всем необходимом для ее успешного выполнения. В момент, когда коды промежуточной программы (они называются управляемыми кодами) ставятся на исполнение, в дело вступает среда CLR (Common Language Runtime общеязыковая среда исполнения), которая с помощью встроенного JIT-компилятора (JIT - just-in-time - вовремя, по мере надобности)1 переводит управляемые 1
JIT-компилятор - компилятор, вызываемый по мере надобности. В отличие от обычных компиля торов, JIT-компилятор может вызываться в процессе выполнения программы неоднократно.
]
коды в набор исполняемых машинных инструкций и обеспечивает разнообразш вспомогательные действия для исполняемой программы. Идея использования машинно-независимого промежуточного языка не нов Впервые она была высказана еще в 1958 г. американским программистом Me. вином Е. Конвеем (Conway) в журнальной статье «Proposal For An UNCOI («Предложение по универсальному компьютерно-ориентированному языку» Двухфазное кодирование имеет два существенных преимущества. Во-первы предельно упрощается распространение программ. Переведенная на CIL npi грамма может выполняться на любом компьютере, имеющем соответствующу среду исполнения. Причем в управляемый код включается вся системная т формация, необходимая для нормального функционирования программы, ТЕ что отпадает необходимость в регистрации отдельных частей программы (объе] тов, модулей, динамических библиотек) в системном реестре. Собственно, по аналогичной схеме работают Java-машины. Существенная ра: ница заключается в значительно большей роли, которую играет CLR по сравш нию с Java-машинами. Например, CLR может не вызывать JIT-компилятор дл фрагментов CIL, которые никогда не будут исполняться. Она (среда) осущест] ляет контроль текущего состояния программы и освобождает все выделенные е ресурсы, если программа завершила свою работу, и т. п. ПРИМЕЧАНИЕ
;
В настоящее время технология .NET реализована для ОС семейства Windows. Существуют пр< екты переноса технологии в ОС FreeBSD, Mac ОС X 10.2 и Linux. Однако распространение .NE на другие платформы затруднено в основном проблемами воспроизведения пользовательског интерфейса: экраны настольного компьютера, блокнотного компьютера или мобильного тел< фона существенно различаются.
Во-вторых, повышается защищенность программ и файлов: в управляемых кода нет информации о файловой системе компьютера, на котором исполняется прс грамма, или способах запуска программ, а среда исполнения сконструирована та] чтобы всемерно уберечь программно-аппаратные средства компьютера от атак ви русов, других злонамеренных программ, а таклсе от программных ошибок.
Общеязыковая инфраструктура Общеязыковая инфраструктура (Common Language Infrastructure, CLI) — эт набор перечисленных далее спецификаций, определяющих различные аспект! технологии .NET. •
1
Common Type System (CTS) — общая система типов. Определяет возможност: взаимодействия программ и их частей, написанных на разных языках про граммирования. Каждый компилятор, вырабатывающий CIL-инструкции, дол жен частично или полностью использовать CTS, и никакие другие типы дан ных, кроме указанных в CTS. Набор перечисленных в CTS типов значительш превышает количество типов в реально существующих языках программиро вания 1 .
Для сравнения: в Delphi 7 имеется около 1000 типов (с учетом классов), а в CLR их более 4000.
20 •
Common Intermediate Language (CIL) - общий промежуточный язык програм 1 мирования . Это язык инструкций абстрактного процессора. В этом отноше нии CIL — аналог байт-кода Java. В отличие от байт-кода, в CIL вставляются специальные метаданные. • Extensible Metadata — расширяемые метаданные. В технологии подразумевается, что CIL-инструкции помещаются в единицу распространения — сборку (assembly) — и сопровождаются метаданными, которые делают сборку полно стью самоописываемым объектом. В метаданные помещаются имя и версия сборки, сведения о локализации, данные о типах, включенных в сборку, спи сок внешних файлов (сборок), от которых зависит данная сборка, и т. п. • Framework Class Library (сокращенно .NET Framework) — это библиотека клас сов, которые должна использовать любая программа в рамках технологии. Библиотека VCL Delphi в чем-то подобна .NET Framework. Разница между ними, прежде всего, состоит в том, что библиотеку .NET Framework можно использовать при создании программ на любом поддерживающем техноло гию .NET языке программирования. 'Более того, в Delphi (или в С#, J# и т. д.) вы можете расширять эту библиотеку новыми классами, которые за тем могут использоваться в программах на других языках программирования. • Platform Invocation Service (сокращенно P/Invoke) - служба согласования плат форм. Программы, исполняемые в .NET, предельно изолированы друг от друга и от средств операционной системы. Однако вне этих средств .NET Framework не может реально работать. P/Invoke реализует взаимодействие .NET Framework и операционной системы. • Extended Portable Executable (РЕ) File Format - стандартный формат исполня емых файлов в Win32, используемый для хранения объектов технологии. Он загружается обычным загрузчиком точно так же, как и любой другой испол няемый файл. Однако в его заголовке имеется бит, указывающий на то, что файл относится к технологии .NET. Обнаружив бит, загрузчик вызывает сре ду CLR, которая и выполняет обработку файла.
Библиотека классов и пространства имен Замечательной особенностью новой технологии является ее объектная ориента ция. Вместе с CLR в рамках программного продукта .NET Framework (этот про дукт свободно доступен на сайте www.microsoft.com) поставляется обширная биб лиотека классов, которую должны использовать новая операционная система с рабочим названием Longhorn (выпуск намечен на 2005-2006 гг.) и все работа ющие под ее управлением программы. Классы в .NET Framework организованы в ветвящиеся иерархические структу ры, которые называются пространствами имен. До появления технологии .NET практически все промышленные операционные системы (ОС) строились из кро шечных «кирпичиков», которые назывались функциями API (Application Program Interface — интерфейс прикладных программ). Эти функции выполняли неболь шие локальные задачи как для нужд самой ОС, так и для любой работающей 1
Знакомство с технологией .NET
Введение
До стандартизации технологии в декабре 2001 г. этот язык назывался Microsoft Intermediate Language (промежуточный язык корпорации Microsoft).
21
под ее управлением программы. В системах с .NET Framework роль функций API играют объекты (экземпляры классов). Отказ от API фактически делает все языки программирования не процедурными, а объектно-ориентированными. Например, Delphi 2005 — процедурный язык программирования, но порождаемая им в ко нечном счете программа обращается к объектам, а потому ничуть не отличается от той, что порождается полностью объектно-ориентированным языком С#.
Общеязыковая среда исполнения Общеязыковая среда исполнения (CLR) играет ключевую роль во всей техноло гии. Она поддерживает строгую систему правил и соглашений, которой должен следовать промежуточный язык. Этот язык представляет собой код, который на зывается управляемым. Важнейшим свойством CLR является то обстоятельство, что входной поток данных может представлять собой и неуправляемый код, а так же управляемый и неуправляемый одновременно! Неуправляемые фрагменты входных данных являются обычными машинными инструкциями, которые без изменений поступают в процессор. Указателем на то, что входной поток содер жит управляемый код, является специальный бит в заголовке файла. Управляемый код в общем случае порождает объекты с управляемым временем жизни. Такие объекты автоматически уничтожаются, когда надобность в них ис чезает (например, завершает работу создавшая их программа). Таким образом, одной их замечательных способностей CLR является встроенный в нее механизм борьбы с утечками памяти1. Для создания объектов с управляемым временем жизни в управляемый код по мещаются также метаданные, которые содержат подробные инструкции о по рождаемых объектах, их свойствах, методах и событиях. Управляемый код перед запуском должен пройти процесс верификации (если только администратор не разрешил его пропустить). Процесс верификации определяет, будет ли код пы таться получить доступ к неправильным адресам памяти или выполнять другие неверные действия. Код, успешно прошедший верификацию, называется надеж ным (safe). Возможность верификации позволяет CLR обеспечивать высокий уро вень изолированности объектов при минимальном снижении производительнос ти. CLR использует метаданные, чтобы найти и загрузить классы, поместить их экземпляры в память, разрешить вызов программ, генерировать машинные инст рукции, усиливать безопасность и устанавливать динамические контекстные гра ницы. Более того, наличие метаданных позволяет создавать модели программ и на их основе реализовывать приложения, большая часть кода которых генерирует ся автоматически (так работает технология ECO Framework, входящая в постав ку Delphi 2005). CLR облегчает разработку компонентов и программ, объекты которых взаимо действуют посредством языка CLI. Объекты, написанные на разных языках, мо гут взаимодействовать друг с другом, и их поведение может быть тесно связан ным. Например, вы можете определить класс и его потомка на разных языках или вызвать метод класса, написанного на другом языке. Межъязыковое взаимоУтечкой памяти обычно называют зарезервированную программой оперативную память, которая не освобождается после завершения работы программ.
22
Введение
действие возможно потому, что языковые компиляторы и инструменты целевой машины используют общую систему типов, определенную в CTS, и они следуют общим правилам для определения новых типов, а также их создания, использо вания, снабжения данными и связывания.
Компилирование в промежуточный язык CIL При создании управляемого кода компилятор языка программирования, поддер живающего .NET (Visual Basic .NET, C#, J # , а с появлением Delphi 2005 еще 1 и Delphi ), транслирует исходный код в набор машинно-независимых инструк ций языка CIL. ПРИМЕЧАНИЕ Синтаксис того или иного языка никак не влияет на CLR. Однако некоторые языки (С#, Delphi) не имеют существенных синтаксических ограничений и позволяют использовать практически все возможности CLR. Эти инструкции могут затем легко переводиться в машинно-зависимые. CIL включает инструкции для загрузки, сохранения, инициализации объектов, вызо ва их методов, а также для логических и арифметических операций, управления потоками, прямого доступа к памяти, поддержки исключений и др. Перед вы полнением программы CIL-инструкции преобразуются JIT-компилятором CLR в машинно-зависимые инструкции процессора. Одновременно с CIL-инструкциями производятся также метаданные. Метадан ные описывают типы вашего кода, в том числе содержат описание каждого типа, сигнатуры вызова методов объектов, ссылки на членов вашего кода и другие не обходимые при выполнении данные. CIL и метаданные содержатся в выполняе мом файле формата РЕ, который основан на расширенной версии опубликован ного формата MS РЕ и общего объектного файлового формата, использующегося исторически для выполняемых программ. Этот файловый формат, который объе диняет CIL-код и метаданные, предоставляет операционной системе целевого компьютера всю необходимую информацию для создания CLR-объектов. При сутствие в CIL-кодах метаданных позволяет коду описывать самого себя и, та ким образом, отказаться от библиотек типов и языка IDL (Interface Definition Language — язык описания интерфейсов). CLR находит и извлекает метаданные из РЕ-файла по мере надобности в ходе прогона.
Компилирование CIL в машинные инструкции Перед выполнением CIL-кода он должен быть преобразован с помощью JIT-KOMпилятора в машинные инструкции. В процессе компиляции J IT-компилятор под считывает также те фрагменты кода, которые могут никогда не вызываться. Ком пилятор преобразует CIL-код по мере надобности и вставляет заглушку на место вызова любого метода. При первом же вызове кода заглушка передается JIT1
В ранних версиях Delphi (6 и ниже) этот язык программирования назывался Object Pascal.
Знакомство с технологией .NET
23
компилятору, который заменяет заглушку реальным кодом. Такого рода обраще ния к компилятору выполняются непосредственно из реальных машинных инст рукций, ранее сгенерированных компилятором, что повышает скорость исполне ния программ. В ходе компиляции CIL-кода он передается верификационному процессу. При верификации CIL-код и метаданные проверяются на предмет их надежности. Надежность типов объектов есть надежность их изолирования от других объек тов и надежность их защиты от ошибочного или злонамеренного разрушения. Во время верификации код проверяется на доступ к разрешенной памяти и вы зов только правильно определенных методов. Например, не допускается обра щение к полям, которые выходят за отведенные им границы. Дополнительно при верификации проверяется правильность генерации машинного кода. Процесс верификации открывает доступ к правильно определенному надежному коду. Если встречается ненадежный код, возбуждается исключение.
Исполнение кода CLR предоставляет, во-первых, инфраструктуру, позволяющую управлять про цессом исполнения машинного кода, во-вторых — различные службы, которые могут быть использованы во время исполнения. Перед вызовом метода он дол жен быть скомпилирован в машинные инструкции. Каждый метод, для которого есть CIL-код, должен вначале с помощью JIT-компилятора генерироваться в ма шинный и затем выполняться. Компилятор не вызывается каждый раз, но ис пользуется созданный им код. Этот процесс повторяется до конца прогона. ПРИМЕЧАНИЕ — Компиляция CIL, безусловно, снижает общую производительность программ. По моим оценкам, на 10-15 %. Во время выполнения управляемый код получает дополнительное обслужива ние, такое как сборка мусора, повышенная защита, взаимодействие с неуправля емым кодом, поддержка межъязыковой отладки, улучшение распространения программ и контроля версий.
Автоматическое управление памятью Автоматическое управление памятью — это одна из услуг, которые CLR предос тавляет во время управляемого исполнения. Механизм сборки мусора управляет распределением и освобождением памяти. Это избавляет разработчика от необ ходимости писать соответствующий код. Автоматическое управление памятью позволяет решать типичные проблемы, такие как утечки памяти или попытки освободить уже уничтоженный объект. Когда инициируется новый процесс, для него резервируется непрерывное адрес ное пространство, называемое управляемой кучей. Управляемая куча поддержи вает указатель на следующий распределяемый в памяти объект. Первоначально он указывает на базовый адрес управляемой кучи. Все типы указателей распре деляются в управляемой куче. Когда создается первый указатель, память для ас-
24
Введение
социированного с ним типа начинается с базового адреса кучи. При создании следующего указателя память выделяется непосредственно за первым. Пока ад ресное пространство доступно, процесс продолжается описанным образом. Распределение в управляемой куче идет быстрее, чем в неуправляемой. CLR про сто наращивает значение указателя кучи, что происходит почти так же быстро, как «заталкивание» данных в стек. Кроме того, так как новые объекты распреде ляются в памяти последовательно, приложение обращается к ним быстрее. Оптимизирующая машина сборщика мусора определяет наилучшее время для удаления мусора. В процессе очистки сборщик мусора удаляет из памяти объек ты, которые более не используются приложением. Для этого он исследует корни приложений. Каждое приложение имеет набор корней. Каждый корень либо ссы лается на объект в управляемой куче, либо содержит NIL. Корни включают ука затели на глобальные и статические объекты, локальные переменные и ссылки на объектные параметры в стеке потока и регистрах процессора. Сборщик мусо ра имеет доступ к списку активных корней, которые управляются компилятором времени исполнения и CLR. Используя этот список, он проверяет корни и стро ит граф, который содержит все объекты, порожденные от корня. Объекты, которые не содержит граф, не порождены от корней приложения. Сбор щик мусора удаляет эти объекты из памяти. При этом он оптимизирует состоя ние управляемой кучи и нужным образом корректирует указатели.
Знакомство с технологией .NET
загружаться по требованию. Это упрощает приложения и уменьшает их объе] что облегчает загрузку из Интернета.
• Является единицей, для которой поддерживается параллельное выполнен! (см. далее).
Если вы уже имели опыт работы с Delphi или Turbo Pascal, то заметите, что мн< гие свойства сборок соответствуют свойствам модулей этих систем программ! рования.
Сборки могут быть статическими и динамическими. Статические сборки вклк чают типы .NET Framework (интерфейсы и классы), а также нужные ресурсг Статические сборки сохраняются на диске в виде РЕ-файлов. Вы можете испол! зовать .NET Framework для создания динамических сборок, которые не сохрани ются на диске и создаются (и запускаются) непосредственно в памяти. Поел выполнения динамическую сборку можно сохранить на диске. Сборки созданы, чтобы упростить распространение программ и решить пробле мы контроля версий, которые возникают в приложениях, основанных на компс нентах. На платформах Win32 возникают две проблемы совместимости версий: •
Невозможно выразить правила контроля версий между частями приложени и обеспечить их реализацию силами ОС. Текущий подход руководствуете правилом обратной совместимости, которое часто трудно гарантировать. Он ределения интерфейсов должны быть статическими, раз и навсегда опубли кованными, и фрагменты кода должны поддерживать обратную совместимое^ Более того, код обычно разрабатывается так, что в каждый момент времен] на данном компьютере может быть установлена и запущена единственная вер сия объекта или DLL.
•
Нет путей согласования версий между наборами компонентов, которые со браны совместно, и набором, представленным в момент запуска.
Сборки Сборки — фундаментальные части программ .NET Framework. Сборка выполня ет перечисленные далее функции: • Содержит код, который выполняет CLR. CIL-код в файле формата РЕ не бу дет исполняться, если не имеет связанного с ним манифеста сборки. Учтите, что сборка может иметь только одну точку входа. • •
•
•
•
Формирует границы защиты. Сборка есть блок, в котором запрашиваются и выдаются разрешения. Формирует границы типа. Каждый идентификатор типа включает имя сбор ки, в которой он расположен. Тип МуТуре, загруженный в пределах одной сборки, в общем случае может отличаться от одноименного, но загруженного в другую сборку. Определяет границы видимости ссылок. Сборочный манифест содержит ме таданные, которые используются для разрешения ссылок и удовлетворения ресурсных требований. Манифест указывает экспортируемые типы и ресурсы и перечисляет другие сборки, от которых зависит данная сборка. Определяет границы контроля версий. Сборка представляет собой минималь ный блок в CLR, все типы и ресурсы которого имеют ту же версию, что и вер сия блока. Определяет единицу распространения. В момент старта должны быть загру жены только первоначально вызванные сборки. Другие сборки, такие как ре сурсы локализации или сборки, содержащие вспомогательные классы, могут
2
Объединение этих двух проблем порождает конфликты DLL, когда установк; одного приложения может нарушить функциональность другого из-за того, чт< вновь установленный компонент (или DLL) не совместим с предыдущей вереи ей. При возникновении такой ситуации у ОС нет средств обнаружения и устра нения проблемы. Windows 2000/ХР отчасти решает проблему с помощью двух приемов:
• Windows 2000/ХР разрешает размещать вспомогательные библиотеки DLI в той же папке, что и исполняемый файл. Такие компоненты отыскиваются первыми, и поэтому другие их версии игнорируются. •
Windows 2000/ХР блокирует файлы, которые помещаются в системную пап ку System32 при установке ОС, и не позволяет другим приложениям заме щать их.
Для решения проблемы зависимости сборки от версии делают следующее: •
Разрешают разработчику указывать правила согласования версий между раз личными компонентами.
•
Реализуют инфраструктуру поддержки контроля версий.
26
•
Введение
Реализуют параллельное (side-by-side) выполнение компонентов разных вер сий.
Параллельное выполнение объектов и приложений означает, что CLR дает при ложению средства вызова той или иной версии DLL (объекта) для использова ния ее специфических возможностей. CLR разрешает параллельное исполнение произвольного количества разных версий одного объекта, причем одновременно и в рамках одной сборки.
Домены приложений Исторически сложилось так, что параллельно запускаемые на одном компьюте ре программы всемерно изолированы друг от друга. Приложения изолируются прежде всего из-за того, что адресные указатели зави сят от процесса. Указатель, переданный из одного приложения другому, никак не может использоваться в нем. Более того, вы не можете из одного процесса обра титься непосредственно к другому. Вместо этого вы должны задействовать меха низм представителей (proxy), которые реализуют косвенные вызовы, как это дела ется в модели COM (Component Object Model — компонентная модель объектов). Домены приложений реализуют безопасный процесс, который позволяет CLR изо лировать приложения. Вы можете запускать различные домены в рамках един ственного процесса с уровнем изоляции, который существует между отдельны ми процессами, и не заботиться о перекрестных обращениях и переключениях. Возможность запуска множества приложений в едином процессе существенно повышает степень масштабируемости серверов. Изоляция приложений важна также для безопасности программ. Например, вы можете запускать программы из веб-приложения в единственном процессе брау зера так, что они не будут иметь доступа к другим данным и ресурсам. Изоляция, обеспечиваемая доменами приложений, имеет следующие преиму щества: • Ошибка в одном приложении не может повлиять на другое приложение. •
Неправильно функционирующее приложение может быть выгружено без выг рузки других приложений.
ПРИМЕЧАНИЕ Нельзя выгрузить сборку или тип, но можно домен.
• Код, исполняемый в одном приложении, не имеет непосредственного досту па к коду или ресурсу другого приложения. CLR обеспечивает изоляцию путем предотвращения вызовов между объектами в разных доменах прило жений. Объекты, которыми обмениваются домены, могут быть копиями или получены через представителей. Если объект — копия, его вызов локаль ный. Это означает, что как получатель объекта, так и сам объект находятся в одном домене. Если объект получен через представителя, этот объект уда ленный. В этом случае получатель и объект находятся в разных доменах. Как следствие, доступ к метаданным объекта должны иметь оба домена, чтобы
Знакомство с Delphi 2005
1
обеспечить правильную работу встроенному компилятору для вызова мето дов (в противном случае произойдет ошибка).
Домены и сборки Перед запуском приложения вы должны загрузить сборку в домен. Запуск ти пичного приложения вызывает загрузку различных сборок в программный до мен. По умолчанию CLR загружает сборку в домен, который содержит ссылку на нее. Таким образом, код и данные сборки изолируются от использующего егс приложения. Если одна и та же сборка используется несколькими доменами, ее код (но не данные) могут разделяться доменами, что уменьшает затраты памяти. Этот ме тод подобен разделению DLL. Сборка называется доменно-нейтральной, если ее код могут разделяться другими доменами в одном процессе. CLR решает, будет ли сборка доменно-нейтральной. Сборка не разделяется между доменами, если предоставляемые ею возможности нужны лишь одному домену.
Знакомство с Delphi 2005 Ограниченная поддержка технологии .NET была реализована еще в предыдущей версии, Delphi 7 Studio. Однако в этой версии технология .NET могла и не ис пользоваться, что достигалось отказом от загрузки соответствующих модулей в момент установки системы. Версия Delphi 8, напротив, не может не применять эту технологию — создаваемые ею программы представляют собой надежный CILкод, который интерпретируется платформой .NET Framework. Наконец, Delphi 2005 также может использовать или не использовать .NET, но уже с помощью соответствующих изменений в создаваемых программах. В хранилище объектов Delphi 2005 имеются заготовки программ самых разных типов. На рис. В.1, а, показан фрагмент хранилища объектов Delphi 2005, а на рис. В.1, б, — хранили ща объектов Delphi 8. Выбор нужного типа находящегося в хранилище объекта определяет тот или иной фрагмент кода, вставляемого в проект, а следовательно, тот или иной тип проекта.
Устаревшие и новые средства Delphi Для того чтобы язык Delphi соответствовал требованиям к языкам, вырабатываю щим CIL-код, была проведена его модификация. В ходе модификации из языка убраны средства, которые не поддерживаются средой CLR, и добавлены новые. ПРИМЕЧАНИЕ Устаревшими считаются средства, которые не могут использоваться в программах, рассчитан ных на технологию .NET. Однако эти же средства могут применяться в традиционных програм мах, то есть не рассчитанных на средства .NET (это поимечанир UP n-mnrw-rra i/ o^™» rv=ir,hr Q\
28
Введение
Знакомство с Delphi 2005
2!
рещены любая арифметика указателей, а также обращение к функциям и проце дурам New, D i s p o s e , GetMem, FreeMem HReallocMem. Вместо концепции ука зателей программы должны использовать два класса из CTS: i n t P t r и M a r s h a l Первый — это зависящий от платформы указатель, открывающий доступ к меха низму межплатформенного взаимодействия P/Invoke. Второй осуществляет мар шалит данных, то есть низкоуровневое взаимодействие процессов, включая упа ковку/распаковку передаваемых данных. В следующем примере создается и используется указатель для размещения в йел целого числа: uses S y s t e m . R u n t i m e . I n t e r o p S e r v i c e s ; var X: I n t P t r ; begin X := Marshal.AllocHGlobal(SizeOf(Integer) ) ; try Marshal.Writelnt32(X, 123456); Console.WriteLn(Marshal.Readlnt32(X) finally X.Free; end end;
* 2),
// Создаем указатель // на 4 байта // Наполняем его // Используем // Освобождаем
Запрещены типизированные и нетипизированные файлы. Безопасный код может использовать только текстовые файлы типа F i l e V a r : T e x t F i l e . Для работы с нетекстовыми файлами рекомендуются объекты класса T F i l e S t r e a m . Напри мер, следующая программа создаст файл, содержащий 5 случайных веществен ных чисел: procedure TForm3.ButtonlClick(Sender: TObject); var A: Real; k: I n t e g e r ; F: TFileStream; begin F := TFileStream.CreateCdata.dat', fmCreate); try
Рис. В.1. Фрагменты хранилища объектов Delphi: а— версии 2005; б— версии 8
Устаревшие типы Устаревшие типы, пожалуй, самая болезненная проблема в плане совместимости с ранними версиями. Прежде всего, речь идет об указателях. Указатели считаются небезопасным ти пом, так как код, содержащий указатели, нельзя проверить на безопасность. Зап-
for k := 1 to 5 do begin A := Random; F.Write(A, SizeOf (Real)); end; finally F.. Free end end;
30
Введение
Записи не могут содержать вариантную часть, но могут — методы (см. далее). В .NET Framework используются «широкие» символы (2 байта на символ). В связи с этим небезопасным считается тип PChar, применяемый как ссылка на массив однобайтных символов. В то же время формат типа S t r i n g в Delphi и CTS со впадает. Поскольку тип PChar в программах Delphi используется в основном при обра щении в функциям API, вместо PChar следует применять класс S t i n g B u i l d e r . Следующая подпрограмма прочитает заголовок активного окна: function GetText(Window: HWND; BufSize: I n t e g e r = 1024): String; var Buffer: StringBuilder; begin Buffer := S t r i n g B u i l d e r . C r e a t e ( B u f S i z e ) ; GetWindowText(Window, Buffer, B u f f e r . C a p a c i t y ) ; Result := B u f f e r . T o S t r i n g ; end; Вы уже, наверняка, заметили, что локальные переменные и связанные с ними объекты (в нашем случае B u f f e r ) не уничтожаются после выхода из области видимости. Они будут уничтожены автоматически во время сборки мусора. Для явного освобождения ресурсов можно сделать так: function GetText(Window: HWND; BufSize: I n t e g e r = 1024) : String; var Buffer: StringBuilder; begin Buffer := S t r i n g B u i l d e r . C r e a t e ( B u f S i z e ) ; GetWindowText(Window, Buffer, B u f f e r . C a p a c i t y ) ; Result := B u f f e r . T o S t r i n g ; Buffer.Finalize end; Однако замечу, что во фрагментах на языке С# явное освобождение памяти не рекомендуется, так как метод F i n a l i z e вызывается автоматически методами Close и Dispose.
Устаревшие возможности кода Компилятор Delphi накладывает значительные ограничения на директивы i n l i n e . Не поддерживаются директивы asm. Запрещено использовать функции прямого доступа к памяти, такие как BlockRead, BlockWrite, GetMem, FreeMem, ReallocMem, а также директиву a b s o l u t e и функцию a d d r . Поддерживается операция @ (получение адреса). В связи с изменениями типа Char запрещены функции ord, s u c c и p r e d . Функттия ппрпбпяяовяния типов c h r ппименима только для значений от 0 до 127.
Знакомство с Delphi 2005
31
Новые возможности Delphi
Как уже отмечалось, записи не могут иметь вариантных частей, но могут — про цедуры и функции. Это в какой-то степени сближает их с классами. Вот пример объявления: type MyRec = record a: Integer; procedure aProc; end; procedure MyRec.aProc; begin end; Несмотря на схожесть синтаксиса, записи, конечно, не классы — в них нет меха низмов наследования и полиморфизма. .NET позволяет интегрировать в единое целое код, написанный на разных язы ках, в которых используются в общем случае разные ключевые слова. Как быть, если в CTS определен класс, совпадающий с ключевым словом? В Delphi для это го можно использовать стандартное средство — составное имя, например: var Т:
System.Type;
Однако «путь» к классу может быть довольно длинным, тогда составное имя ока жется громоздким. В этом случае Delphi разрешает перед именем класса ставить символ амперсанда (&): var Т: SType; Встретив такое описание, компилятор станет просматривать список доступных модулей в поисках типа Туре и найдет его в модуле System. Существенным изменениям подверглось объявление класса. Дело в том, что Delphi и CLR по-разному трактуют области видимости секций p r i v a t e и p r o t e c t e d : в Delphi члены, объявленные в этих секциях, видны всюду в пределах данного мо дуля. В CLR в секции p r i v a t e объявляются члены, доступные только внутри ме тодов класса, а в секции p r o t e c t e d — члены, доступные потомкам класса. В свя зи с этим в Delphi перед названиями секций следует ставить спецификатор c l a s s — в этом случае области видимости в Delphi и CLR совпадут: type MyClass = class class private a: Integer; class protected
// Поле видно только в методах // класса MyClass
32
Знакомство с Delphi 2005
Введение // класса и самому классу
end; Классы можно лишить потомков, а виртуальный метод — возможности перекры тия. Для этого объявление класса сопровождается директивой s e a l e d , а объяв ление метода — директивой f i n a l : type Nolnst = class public // Метод нельзя перекрыть procedure NoOverride; dynamic; final; end sealed; // Класс не может иметь потомков Приведенный пример лишь иллюстрирует синтаксис объявлений и по существу бессмыслен: если класс не имеет потомков, то ни один его метод не может быть перекрыт. Замечу, что следующее объявление ошибочно: procedure NoOverride; final; Можно лишить возможности перекрывать только виртуальные методы, то есть объявленные с директивами dynamic, v i r t u a l или o v e r r i d e . В CTS широко используются коллекции, под которыми понимают наборы одно типных элементов. Специально для удобства в Delphi 2005 введен цикл просмотра элементов коллекции, который имеет такую структуру:
33
С этой целью они создали пространства имен Borland.VCL.XXXX, почти пол ностью имитирующие библиотеку VCL (Visual Component Library — библиотека визуальных компонентов) предыдущих версий. В эти пространства имен вошли VCL-классы, хорошо известные по предыдущим версиям: T A p p l i c a t i o n , TForm, T B u t t o n , TCheckBox и т. д. Символы ХХХХ в названиях пространств имен со впадают с именами соответствующих модулей VCL. Например, пространство имен B o r l a n d . VCL. DB содержит классы, определенные в модуле DB библиотеки VCL (классы для работы с базами данных). Класс TForm определен в пространстве имен B o r l a n d . V C L . Forms, в этом же пространстве определен класс T A p p l i c a t i o n , класс T B u t t o n — в пространстве имен B o r l a n d . VCL. S t d C t r l s , в этом пространстве объявлен также класс TCheckBox и т. д. Пространства имен Borland.VCL.ХХХХ являются прозрачными надстройка ми над пространствами имен классов, входящих в .NET Framework, но не зак рывают их. Это означает, что вам также доступны классы .NET Framework. Что бы создать Windows-приложение, базирующееся на классах .NET Framework, вы выбираете в меню команду File • New • Windows Forms Application - Delphi for .NET, а для создания VCL-подобного приложения — команду File • New • VCL Forms Application — Delphi for .NET. ПРИМЕЧАНИЕ
—
В рамках этой книги классы .NET Framework объединены в библиотеку с названием WinForms. Это неофициальное название введено только для противопоставления названию VCL.
for Element in CollName do Stmt; Здесь E l e m e n t — итерационный элемент, открывающий доступ ко всем членам коллекции CollName; Stmt — некоторое допустимое выражение. Пусть, напри мер, нужно подсчитать сумму всех членов одномерного массива: var MyArray: array [0..10] of integer; К, Sum: Integer; begin Sum := 0; for К in MyArray do Sum
:=
Sum + К;
// //
// Внутри цикла
Работа с базами данных Отличительной особенностью системы программирования Delphi была и остает ся встроенная в нее возможность работы с различными промышленными базами данных (БД). Добрая треть компонентов VCL в Delphi 7 в той или иной степени связана с созданием приложений для БД. В .NET Framework встроена архитектура ADO.NET, решающая аналогичные за дачи. Упрощенная схема этой архитектуры показана на рис. В.2. ADO.NET
параметр К представляет
собой не индекс массива, а его (массива)очередной
член!
end;
Две модели Windows-приложений Хотя Delphi теперь всего лишь один из языков, поддерживающих .NET, сама система программирования Delphi имеет богатую историю, и миллионы програм мистов до сих пор с удовольствием работают с ней. Учитывая это, разработчики Delphi обеспечили максимально возможную совместимость (пусть — мнимую, см. ранее) последней версии с предыдущими.
Рис. В. 2. Архитектура ADO.NET
На этой схеме источник данных — физическая БД или XML-файл с данными. Провайдер данных обеспечивает связь с источником данных и передает ему команды. Набор данных предназначен для отображения данных. С любым ис точником данных (ИД) могут быть связаны один или несколько наборов дан ных (НД), и наоборот, единственный НД может отображать данные из не скольких ИД.
34
Введение
Знакомство с Delphi 2005
Провайдер данных, входящий в архитектуру ADO.NET, обеспечивает взаимодей ствие наборов данных с такими ИД, как MS SQL Server, OLE DB и Oracle. В Delphi 2005 можно использовать также провайдер BDP.NET (Borland Database Provider for .NET — провайдер баз данных корпорации Borland для .NET), кото рый обеспечивает взаимодействие с серверами MS SQL, Sybase, DB2, Oracle и InterBase. Дублирование в BDP.NET связи с промышленным сервером Oracle не случайно: корпорации Borland и Oracle связывает многолетнее плодотворное сотрудничество1. По оценкам разработчиков Delphi, управление сервером Oracle с помощью BDP.NET дает определенные преимущества по сравнению с управле нием через провайдер ADO.NET.
35
же время в Microsoft разработали технологию ASP (Active Server Pages — актив ные страницы сервера), которая во многом упрощала актуальную ныне задачу создания интерактивных веб-сайтов (например, для электронной торговли това рами и услугами) 1 . Технология ASP вошла в .NET Framework в виде ASP.NET и в полной мере доступна в Delphi 2005. Для использования технологии ASP.NET на хосте (то есть на машине, на которой развернут сайт) должен функциониро вать сервер Microsoft IIS (Internet Information Server — информационный сер вер для Интернета корпорации Microsoft), а сам хост должен работать под уп равлением Windows 2000/XP. Основой технологии являются компоненты веб-страниц, на которых размеща ются серверные управляющие компоненты и HTML-текст. Страницы содержат внутренний код, обеспечивающий логику работы, и поддерживаются скомпили рованными библиотеками DLL. Когда пользователь впервые обращается к ак тивной странице, ASP.NET автоматически создает и компилирует библиотеку DLL, которая в конечном счете передает пользователю HTML-код. Этот код спо собен исполняться в браузере клиента. В результате значительная часть работы по взаимодействию клиента с сервером выполняется на машине клиента, что по вышает пропускную способность хоста.
Наличие BDP.NET не ограничивает новаторского подхода разработчиков Delphi к интеграции с технологией .NET Framework. Связано это с тем, что изначально система Delphi тяготела к приложениям для работы с БД в значительно боль шей степени, чем разработанная в Microsoft технология ADO (ActiveX Data Object — объекты данных, построенные по технологии ActiveX), которая и легла в основу ADO.NET. Среда Delphi 7, например, поддерживала такие технологии, как BDE (Borland Database Engine — машина баз данных корпорации Borland; обеспечивала доступ к файл-серверным БД на таблицах Paradox, dBASE и т. п.), dbExpress (набор скоростных драйверов для непосредственного доступа к неко торым промышленным серверам, в том числе — к MySQL), IBX (доступ к серве ру InterBase на уровне его функций API), DataSnap (разработка многозвенных СУБД), и ряд других.
Активные страницы помимо HTML-текста могут содержать различные сервер ные элементы управления, например, обеспечивающие необходимую идентифи кацию пользователя, а также специализированные компоненты, такие как кален дарь, сетка с данными, навигатор, список и т. п.
Delphi 2005 обеспечивает естественную миграцию этих технологий в новую сре ду. Для этого в ее состав включены такие дополнительные технологии:
Внутри .NET Framework активные страницы получают доступ к данным через ADO.NET с «родным» провайдером или с провайдером BDP.NET
•
TADONETConnector — универсальный класс (наследник T D a t a S e t ) , кото рый разработчики рекомендуют как наиболее простой и эффективный меха низм миграции существующих приложений с НД (например, TADODataSet);
• dbExpress.NET — миграция технологии dbExpress; • DataSnap .NET Client — поддержка многозвенных СУБД; • IBX.NET — миграция технологии IBX; •
BDE.NET — миграция технологии BDE.
Поддержка этих технологий доступна только в режиме разработки VCL Forms Application.
Некоторые возможности среды разработчика С выходом Delphi 2005 разработчики Borland сделали очередной шаг к сближе нию своего продукта с популярнейшим инструментом Visual Studio корпорации Microsoft: теперь среда разработчика поддерживает три языка программирова ния (Delphi, C#, Visual Basic .NET) и называется Borland Developer Studio for Windows. Кроме того, она способна создавать программы, рассчитанные на обыч ные возможности Windows (без CLR). В этом разделе описывается среда разра ботчика Delphi 2005 с позиции программиста Delphi. ВНИМАНИЕ
Работа с Интернетом Работа с Интернетом никогда не была в Delphi столь же эффективной, как рабо та с БД. И хотя уже в Delphi 2 была вкладка Internet (компоненты FTP, HTML, POP и т. д.), поддержке Интернета в Delphi всегда не хватало некоторой систем ности. Даже многочисленные компоненты Indy в Delphi 7 годятся лишь на то, чтобы создать «самопальный» Outlook Express или скромный веб-браузер. В то 1
По распространенной версии, свое название среда Delphi получила в честь древнегреческого города, прославившегося своим оракулом (дельфийский оракул).
;
Если вы работаете с русифицированной версией Windows, имена папок маршрута доступа к про ектам Delphi 2005 не должны содержать символы кириллицы, в противном случае станет не возможной работа со встроенным отладчиком. Если проект Delphi поместить в предлагаемую по умолчанию папку ...\Мои документы\Вог1апс! Studio Projects, при запуске приложения из сре ды Delphi появится сообщение Unable to create process и работа приложения будет блокирова на. В этом случае нужно переустановить проект, например, в папку C:\Delphi Projects или сбросить флажок Integrated Debugging на странице, связанной с узлом Debugger Options окна Options (вызывается командой Tools • Options). Как показывает опыт, такое же требование выд вигает Visual Studio .NET 2003, так что это не ошибка проектировщиков Delphi, а свойство CLR.
1
Справедливости ради отмечу, что поддержка ASP зведена в Delphi 5, 6 и 7.
36
Введение
Знакомство с Delphi 2005
ПРИМЕЧАНИЕ
Среда Delphi 8 очень похожа на описываемую, но она не поддерживает другие языки програм мирования и создает программы, рассчитанные на работу в CL.R.
В целях сокращения объема книги описываются лишь некоторые отличия среды 2005 от аналогичных инструментов предыдущих версий.
Основные инструментальные панели среды После запуска программы она создает окно, показанное на рис. В.З. Панель формы/кода
Панель структуры
Управляющая панель
37
ющей панели можно выбрать схему Classic Undocked, которая представляет все панели, кроме управляющей, в отдельных свободно перемещающихся окнах. Управляющая панель содержит главное меню и управляющие кнопки. Как и в не которых предыдущих версиях, в правой части панели находятся две кнопки и рас крывающийся список. Они предназначены для выбора схемы расположения ос тальных панелей и запоминания текущего их положения. Панель формы/кода первоначально отображает так называемую страницу при ветствия (Welcom Page), с помощью которой можно задействовать как доступные через Интернет, так и встроенные вспомогательные справочные службы. Щелчком на кнопке New Item открывается одноименное окно, с помощью кото рого можно выбрать различные проекты, в том числе и создаваемые с использо ванием языка программирования Visual Basic .NET. В режиме отображения страницы приветствия тип проекта можно выбрать так же с помощью списка на инструментальной панели. В этом списке перечислены указанные далее проекты, упорядоченные по категориям: •
В категории Delphi Projects собраны средства для создания обычных Windowsпроектов (не рассчитанных на работу под управлением CLR): о
Package — заготовка для группы новых компонентов;
о
DLL Wizard — заготовка для одного нового компонента;
о Console Application — заготовка консольного приложения; о VCL Forms Application — заготовка приложения VCL Forms. •
В категории Delphi for .NET Projects представлены средства создания разнооб разных проектов, использующих технологию .NET Framework: о DBWeb Control Library — заготовка компонентов DBWeb; о Web Control Library — заготовка для компонентов Интернета; о ASP.NET Web Service Application — заготовка для приложений ASP.NET Web Service; о ECO ASP.NET Web Service Application — заготовка для приложений ECO ASP.NET Web Service;
ПРИМЕЧАНИЕ Панель инспектора объектов
Панель менеджера проектов
Инструментальная панель
Здесь и далее символами ЕСО обозначается технология ECO Farmework (Enterprise Core Objects — базовый набор объектов для предприятия). Эта технология позволяет автоматизировать про цесс создания программ.
Рис. В.З. Окно среды разработчика после запуска
ПРИМЕЧАНИЕ Как и в предыдущих версиях Delphi, здесь окно среды можно настраивать, приспосабливая к особенностям создаваемого проекта или этапа его создания, так что показанное окно может иметь и иной вид.
На рисунке показано стандартное расположение панелей, соответствующее схе ме Default Layout. С помощью раскрывающегося списка в средней части управля-
о ЕСО ASP.NET Web Application — заготовка для интернет-приложений ЕСО ' ASP.NET; о ASP.NET Web Application — заготовка для интернет-приложений; о
Lybrary — заготовка для DLL;
о Windows Forms Application — заготовка WinForms-приложения; о VCL Forms Application — заготовка VCL-приложения; о
Console Application — заготовка консольного приложения;
38
Введение о
ECO WimForms Application — заготовка WinForms-приложения на основе тех нологии ECO Frameworks; о WinForms Controls Package — заготовка для пакета компонентов WinForms.
•
В категории С# Projects собрано все необходимое для разработки проектов на языке С#: о
Знакомство с Delphi 2005 •
о Unit — заготовка модуля. •
В категории Crystal Reports имеется единственный пункт Report, предназначен ный для создания отчетов по базам данных.
•
Категория Other Files помогает создавать программы на языке Visual Basic .NET: о VB.NET Cobtrol Library — заготовка для компонентов VB.NET;
о ЕСО ASP.NET Web Application — заготовка для интернет-приложения;
о VB.NET Library — заготовка для библиотеки VB.NET;
о ASP.NET Web Service Application — заготовка для приложения ASP.NET Web Service;
о VB.NЕТ.Application — заготовка для приложения VB.NET; о VB.NET Console Application — заготовка для консольного приложения VB.NET;
о ASP.NET Web Applicaion — заготовка для интернет-приложения;
о Text — файл с произвольным текстом;
о Class Library — заготовка библиотеки классов; о Control Library — заготовка для библиотеки компонентов; о Console Application — заготовка для консольного приложения;
•
Категория Delphi for .NET Projects • New Files включает средства для создания компонентов и модулей: о VCL Component — заготовка компонента VCL;
DBWebControl Lybrary — заготовка для компонентов DBWeb;
о ЕСО ASP.NET Web Service Application — заготовка для приложения ЕСО ASP.NET Web Service;
39
о
Project Group — группа проектов;
о
Pull project wizard — вызов мастера создания проекта.
о ЕСО Package in DLL — заготовка для пакета DLL на основе ЕСО;
•
о ЕСО WinForms Application — заготовка для WinForms-приложения, разраба тываемого средствами ЕСО Framework.
В категории Delphi Projects • ActiveX имеется единственный пункт ActiveX Library, который помогает создать заготовку компонента ActiveX.
•
Категория Unit Test содержит компоненты, облегчающие тестирование проек тов.
В категории Delphi Projects • Delphi Files собраны дополнительные средства раз работки обычных Windows-программ:
•
В категории Web Documents представлены средства создания разнообразных интернет-документов:
о Thread Object — заготовка модуля потока команд;
о XML Document — заготовка для XML-документа;
о Component — заготовка модуля компонента;
о XSL Stylesheet — заготовка для XSL;
о
Unit — заготовка модуля;
о Java Script — фрагмент Java-сценария;
о Win2000 Logo Application — заготовка приложения, удовлетворяющего тре бованиям Win2000;
о XML Shema — схема X ML-доку мента;
о Win95/98 Logo Application —заготовка приложения, удовлетворяющего тре бованиям Win95/98;
о XSS Stylesheet — таблица стилей;
о
SDI Application — заготовка SDI-приложения;
о
MDI Applicator) — заготовка MDI-приложения;
о
Dual List Box — форма с двойным списком;
о
Password Dialog — диалоговое окно для ввода пароля;
о XML File - XML-файл; о •
HTML Page - HTML-страница.
В категории С# Projects • New Files имеются дополнительные средства созда ния проектов на языке С#: о Assemblylnfo File — файл сборки;
о Tabbed Pages — окно с вкладками;
о
о About Box — окно «О программе»;
Q Class — класс;
о
Dialog with Help (vertical) — диалоговое окно;
о
Standard Dialog (vertical) — диалоговое окно;
о
Dialog with Help (horizontal) — диалоговое окно;
о
Standard Dialog (horizontal) — диалоговое окно;
о
ReconcicleError Dialog — окно реакции на ошибку.
о
Component — компонент; User Control — управляющий компонент;
о Windos Form — форма Windows. •
Категория С# Proects • ECO/New File подключает технологию ЕСО Framework для создания проектов на языке С#: о ЕСО Persistans Mappaer Provider — ЕСО-провайдер;
40
Знакомство с Delphi 2005
Введение о ECO Enabled Winidows Form — ЕСО-окно; о
ECO UML Package - ЕСО-пакет;
о ECO Space — ЕСО-пространство имен. Выбор проекта того или иного типа обычно запускает соответствующий мас тер, который вставляет в файл модуля нужный текст и, при необходимости, несколько невизуальных компонентов в файл формы. Если, например, выбран простейший тип консольного приложения, в окне кода будет сгенерирован та кой текст: program Projectl; {$APPTYPE
CONSOLE)
uses SysUtils; begin (
TODO
-oUser
-cConsole
Main
Insert
code
here
}
end.
41
При этом с проектом не будет связана какая-либо форма. А вот если выбран тип ECO Windows Forms Application (автоматизированное создание приложения WinForms с помощью технологии ЕСО), в модуль формы будут добавлены множество про странств имен, специальный класс и некоторые его члены, а на форме появятся 5 невизуальных компонентов. После открытия проекта панель формы/кода работает в режиме отображения формы или кода. В ее нижней части появляются три ярлычка — Code, Design и History. Первые два обеспечивают смену режима отображения кода/формы, яр лычок History вызывает окно контроля версий (этого средства нет в Delphi 8). Для смены режима отображения можно использовать также клавишу F12. На инструментальной панели после открытия проекта находится палитра ком понентов, доступных в данном проекте. Эта панель, а также панель инспектора объектов позволяют создавать формы в режиме визуального программирования: вначале очередной компонент перетаскивается на форму из палитры компонен тов, затем с помощью инспектора объектов устанавливаются нужные значения его параметров и создаются обработчики событий. Панель структуры отобража ет структуру текущей формы в режиме конструирования или структуру кода в ре жиме кодирования программы, а панель менеджера проектов — структуру про екта в целом. Для примера на рис. В.4 показаны все перечисленные панели после загрузки проекта. В режиме отображения кода появляется дополнительное окно с кратким описа нием элемента кода, на который показывает указатель мыши (рис. В.5). Имею щиеся в этом окне ссылки позволяют оперативно перейти к нужному разделу справочной службы. lbOutpu TBitBtn Type procedu procedu private
T B l t B t n
. ;.ar!ar:d.Vcl.>?uttnr:f^ri-,i*>?tn re.,,,....
I
private
Рис. В.5. Сведения об элементе кода
Этот режим управляется элементами окна, связанного с узлом Edit Options • Code Insight в режиме Tools • Options. На палитре компонентов имеется кнопка у , упрощающая поиск нужного компо нента. Щелкните сначала на ней, а затем на любом компоненте и начните печатать имя нужного компонента. Среда будет показывать компоненты в режиме инкрементного поиска (по мере ввода имени). С помощью команды Tools • Options ста новятся доступными узлы Tool Palette и Colors. Связанные с ними страницы содер жат информацию, влияющую на вид палитры компонентов. Новое подменю Refactor позволяет перестраивать или модифицировать уже на писанный код с помощью команд Extract, Rename, Find Unit и др. Чтобы воспользо ваться новыми возможностями, выделите фрагмент кода и выберите соответству ющую команду в подменю Refactor. Для примера на рис. В.6 показана строка кода, к которой применяется метод Extract. Этот метод оформляет выделенный код в виде дополнительной процедуры (рис. В.7). Рис. В.4. Окно среды разработчика после открытия прое:гга
42
Введение
Новая возможность позволяет синхронизировать множественные изменения кода. Выделите блок кода, содержащий несколько упоминаний переменной, метода и т. п., после чего щелкните на кнопке Sync Edit рядом с выделенным блоком. Все вхождения этого блока окажутся выделенными, и курсор установится рядом с пер вым вхождением (рис. В.8). Любые изменения первого блока приводят к авто матическому изменению всех других.
Язык Delphi
С точки зрения программиста, любая система программирования (СП) предос тавляет ему те или иные средства создания, тестирования и отладки программ. В рамках Delphi такими средствами являются язык программирования (ЯП) Delphi и интегрированная среда разработчика (ИСР). Рис. В.6. Окно метода Extract
p r o c e d u r e TfniExample. ExtractedMethod; begin mmOutput.Lines.Add(l ' ) ; end; Рис. В.7. Результат применения метода
. » i pu/tput}('-'); I * i |Output)(' *' ) ; i » \ ^'utp'uBc/ 1 ) ; Рис. В.8. Синхронизация множественных изменений
Язык Delphi является формой представления любых программ в этой СП 1 . Он следует строгим синтаксическим правилам, позволяющим компилятору языка преобразовать программу, написанную на почти естественном английском язы ке, в инструкции промежуточного машинно-независимого языка CIL. За неукос нительным соответствием описания программы необходимым синтаксическим правилам следит компилятор языка. ИСР предоставляет программисту множество вспомогательных инструментов, облегчающих создание конкретной программы. В частности, она подключает ком пилятор по мере ввода конструкций Delphi, что обеспечивает проверку соответ ствия введенного текста синтаксическим правилам языка еще на этапе написа ния программ. В главах этой части описывается язык программирования Delphi.
Если необходимо закомментировать несколько строк кода подряд, выделите их и нажмите клавиши Ctrl+/. Повторное нажатие этих клавиш удаляет символы комментария.
Напомню, что ИСР Delphi 2005 поддерживает также языки С# и VB.NET, так что программа может состоять из фрагментов, написанных на разных ЯП.
Структура программы Delphi
45
тью доступен программисту, который, таким образом, может активно влиять на процесс создания компонентов.
Специальные типы модулей
Основы Delphi
Специальные типы модулей играют связующую роль для других модулей. К этим специальным модулям относятся программы, библиотеки и пакеты. Программа — это наиболее широко распространенный продукт системы програм мирования. Заголовок программы начинается зарезервированным словом prog ram, за которым следуют правильный идентификатор (имя программы) и завер шающая точка с запятой, например: program MyProgram; Файл программы должен иметь такое же имя и расширение dpr. Для предыду щего примера — это файл MyProgram.dpr.
Назначение этой главы — предоставить вам возможность неформального знаком ства с основными свойствами языка программирования без точного описания и углубленного изучения его конструкций.
Структура программы Delphi Любая программа Delphi состоит из одного и более текстовых файлов, которые называются модулями. Программный модуль начинается заголовком и заканчи вается терминатором программной единицы, в качестве которого выступает за резервированное слово end со следующей за ним точкой: Оаголовок модуля>
Программа представляет собой функционально законченный программный про дукт, решающий конкретную прикладную задачу. Например, это может быть игро вая программа, программа для работы с базами данных, программа для расчета инженерных формул, программа математического моделирования и т. п. Харак терно, что сам файл программы обычно содержит объявления других программ ных модулей и 2-3 оператора, инициирующие работу программы. Дело в том, что контейнером программных модулей в рамках .NET является сборка. Сборка создается средой CLR с помощью манифеста сборки — специальных метаданных, описывающих некоторые важные свойства сборки. Основное тело dpr-файла про граммы как раз и занимает манифест сборки. Библиотеки содержат программные заготовки довольно широкого назначения, которые могут использоваться в разных программах. Заголовок библиотеки на чинается зарезервированным словом l i b r a r y , за которым следуют правильный идентификатор (имя программы) и завершающая точка с запятой, например: library Complex;
end. Некоторые (консольные) программы содержат единственный файл с расшире нием dpr. Такие программы не нуждаются в развитых средствах пользователь ского интерфейса. Однако в большинстве программ применяются дополни тельные окна (например, диалоговые), существенно обогащающие интерфейс пользователя. Каждое дополнительное окно увеличивает объем программы на один или несколько файлов. В одном из них (с расширением pas) содержится связанный с окном код. Этот код может дополняться файлом формы с расшире нием nfm, если проект представляет собой VCL-приложение. В этом случае файл формы содержит информацию, необходимую для создания принадлежащих окну компонентов (кнопок, надписей и т. п.). В WinForms-приложениях нет отдель ного файла формы, а вся необходимая информация содержится в файле окна. Эта информация используется методом I n i t i a l i z e C o m p o n e n e t , который вы зывается конструктором окна C r e a t e . Метод I n i t i a l i z e C o m p o n e n t полное -
Исходный текст библиотеки содержится в одноименном файле с расширением pas, а после компиляции создается одноименный файл с расширением dll (Dynamic Link Library — динамически связываемая библиотека). Библиотека предназначе на для упрощения повторного использования однажды созданного кода. В отли чие от других модулей, программист может загрузить нужную библиотеку дина мически, а после использования — выгрузить ее из памяти. Пакеты — это специфичные для СП Delphi библиотеки, которые содержат так на зываемые компоненты. Компоненты, так же как и библиотеки, предназначены для упрощения повторного использования однажды созданного кода (и фактически представляют собой лишь разновидность библиотеки). Характерными отличиями компонента являются его самодостаточность и возможность его применения в про цессе визуального создания программ. Пакеты начинаются зарезервированным словом package, за которым следует имя пакета, например: package MyComponenets;
46
Элементы программы
Глава 1 • Основы Delphi
Файл пакета должен имеет такое же имя и расширение dpk (в данном случае — MyComponenets.dpk).
Другие файлы проекта Помимо описанных, к проекту относятся еще несколько файлов. Все они имеют то же.название, что и файл проекта. Это, прежде всего, файл, содержащий XMLсхему проекта (расширение bdsproj). Содержимое этого файла участвует в созда нии манифеста сборки. Проект снабжается файлами ресурсов (расширение res), конфигурации (cfg), а также запоминания состояния файлов и окон среды (dsk). Все они играют вспомогательную роль и могут не использоваться компилято ром. Исключение представляют собой файлы ресурсов формы (resx). Таким образом, для нормальной компиляции проекта необходимы следующие файлы: • файл проекта (dpr); • модули форм (pas); • ресурсы форм (resx); • файлы форм (nfm), если создается VCL-приложение.
Взаимодействие программных модулей Каждый программный модуль размещается в отдельном файле с расширением pas, причем имя модуля и имя файла, в котором он размещается, должны быть идентичны. Модуль начинается зарезервированным словом u n i t , за которым через пробел следует имя модуля, например: unit MyUnit; Такой модуль должен располагаться в файле MyUnit.pas. После компиляции мо дуля создается одноименный файл с расширением dcuil. Именование модулей позволяет им ссылаться друг на друга. Для ссылки исполь зуется зарезервированное слово u s e s , за которым следует перечень имен моду лей, разделенных запятыми, например:
в нем и которые он экспортирует другим модулям. Зарезервированным словом i m p l e m e n t a t i o n начинается раздел реализации тех сущностей, которые объяв лены в разделе u n t e r f асе. В ходе реализации модуль может использовать те программные возможности, которые он импортирует из других модулей, пере численных в предложении u s e s . ПРИМЕЧАНИЕ • Любой модуль получает доступ только к тем сущностям другого модуля, которые тот объявил в своем разделе i n t e r f a c e . Любые сущности, объявленные в разделе i m p l e m e n t a t i o n , доступны только внутри модуля-владельца.
Помимо рассмотренных разделов i n t e r f a c e и i m p l e m e n t a t i o n в модуле мо гут объявляться также разделы i n i t i a l i z a t i o n (инициирующий) и f i n a l i z a t i o n (завершающий). В инициирующем разделе размещаются операторы, которые исполняются до пе редачи управления основной программе и обычно используются для подготовки ее работы. Например, в них могут инициализироваться переменные, открывать ся нужные файлы и т. д. В завершающем разделе указываются операторы, вы полняющиеся после завершения работы основной программы (в них освобожда ются выделенные программе ресурсы, закрываются файлы и т. д.). Если несколько модулей содержат инициирующие части, эти части выполняются последователь но друг за другом в порядке перечисления модулей в предложении u s e s глав ной программы. Если несколько модулей содержат завершающие части, эти час ти выполняются последовательно друг за другом в порядке, обратном порядку перечисления модулей в предложении u s e s главной программы.
Элементы программы Элементы программы — это минимальные неделимые ее части, имеющие опре деленную значимость для компилятора. К элементам относятся: • зарезервированные слова; • идентификаторы; • типы;
unit MyUnit;
• •
переменные;
uses Windows, StdCtrls, Messages;
•
метки;
Встретив такие строки, компилятор перед компиляцией модуля MyUnit найдет и откомпилирует модули, от которых он зависит (в нашем случае — модули Windows, S t d C t r l s , Messages). Если хотя бы одного из перечисленных в списке u s e s модуля нет, компилятор сообщит об ошибке. Любой программный модуль состоит как минимум из двух разделов: раздела внешних объявлений и внутреннего раздела. Раздел внешних объявлений начи нается зарезервированным словом i n t e r f a c e . В нем перечисляются те сущнос ти модуля (типы, классы, переменные, объекты и т. п.), которые реализованы
47
константы;
•
подпрограммы;
•
комментарии;
• директивы компилятора. Зарезервированные слова — это английские слова, указывающие компилятору на необходимость выполнения определенных действий. Зарезервированные слова не могут использоваться в программе ни для каких иных целей, помимо тех, для которых они предназначены. Например, зарезервированное слово b e g i n означа-
48
Глава 1 • Основы Delphi
ет для компилятора начало составного оператора. Программист не может создать в программе переменную с именем b e g i n , константу b e g i n , метку b e g i n и во обще какой бы то ни было другой элемент программы с именем b e g i n . Идентификаторы — это слова, которыми программист обозначает любой другой элемент программы, кроме зарезервированного слова или комментария. Иденти фикаторы в Delphi могут состоять из латинских букв, арабских цифр и знака под черкивания. Никакие другие символы или специальные знаки не могут вхо дить в идентификатор. Из этого простого правила следует, что идентификаторы не могут состоять из нескольких слов (нельзя использовать пробел) или вклю чать в себя точки, запятые или другие специальные символы. В Delphi 2005 и 8 используются 2-байтные символы Unicode. В связи с этим в этих версиях Delphi идентификаторы могут содержать символы национального (русского) алфавита. Максимальная длина идентификатора не ограничена, но значащими для компи лятора являются первые 255 символов. Типы — это специальные конструкции языка, которые рассматриваются компи лятором как образцы для создания других элементов программы, таких как пе ременные, константы и функции. Любой тип определяет две важные для компи лятора вещи: объем памяти, выделяемый для размещения элемента (константы, переменной или возвращаемого функцией результата), и набор допустимых дей ствий, которые программист может совершать над элементами данного типа. За мечу, что любой определяемый программистом идентификатор должен быть опи сан в разделе описаний (перед началом исполняемых операторов). Это означает, что компилятор должен знать тот тип (образец), по которому создается опреде ляемый идентификатором элемент. Исключением являются так называемые стан дартные типы — эти типы были определены при создании Delphi и не нуждают ся в повторном описании. Константы определяют области памяти, которые не могут изменять своего значе ния в ходе работы программы. Как и любые другие элементы программы, константы могут иметь собственные имена. Объявлению имен констант должно предшество вать зарезервированное слово c o n s t (от англ. constants — константы). Например, чтобы вместо длинных чисел 1048576 (1024x1024) и 1073741824 (1024x1024x1024) писать Mbyte и Gbyte соответственно, мы можем определить такие константы: const Kbyte = 1024; Mbyte = K b y t e * K b y t e ; Gbyte = 1024*Mbyte;
Тип константы определяется способом ее записи и легко распознается компиля тором в тексте программы, поэтому программист может не использовать имено ванные константы (то есть не объявлять их в программе явно). Переменные связаны с изменяемыми областями памяти, то есть с такими ее уча стками, содержимое которых может меняться в ходе работы программы. В отли чие от констант, переменные всегда объявляются в программе. Для этого после идентификатора переменной ставятся двоеточие и имя типа, по образу которого должна строиться переменная. Объявлению переменной (переменных) должно предшествовать слово v a r (от англ. variable — переменная), например:
Элементы программы
49
var inValue: Integer; byValue: Byte; Здесь идентификатор inValue объявляется как переменная типа I n t e g e r , а иден тификатор byValue — как переменная типа Byte. Стандартный (то есть заранее заданный в Delphi) тип I n t e g e r определяет четырехбайтный участок памяти, со держимое которого рассматривается как целое число в диапазоне от -2 147 483 648 до +2 147 483 647, а стандартный тип Byte - участок памяти длиной 1 байт, в ко тором размещается беззнаковое целое число в диапазоне от 0 до 255. Метки — это имена операторов программы. Метки используются очень редко и только для того, чтобы программист смог указать компилятору, какой оператор программы должен выполняться следующим. Метки, как и переменные, всегда объявляются в программе. Объявлению метки (меток) предшествует зарезервиро ванное слово l a b e l (метка). В программе метка помещается непосредственно пе ред именуемым (помечаемым) оператором и отделяется от него двоеточием, на пример:
// Программист требует передать управление // оператору, помеченному меткой Loop. // Эти операторы будут пропущены // Оператору, идущему за этой меткой, // будет передано управление
Подпрограммы — это специальным образом оформленные фрагменты программы. Замечательной особенностью подпрограмм является их значительная независимость от остальной программы. Говорят, что свойства подпрограммы локализуются в ее теле. Это означает, что если программист что-либо изменит в подпрограмме, ему, как правило, не понадобится в связи с этим изменять что-либо вне подпрограммы. Таким образом, подпрограммы являются средством структурирования программ, то есть расчленения программ на ряд во многом независимых фрагментов. Структури рование неизбежно для крупных программных проектов, поэтому подпрограммы используются в программах Delphi очень часто. В Delphi есть два вида подпрограмм: процедуры и функции. Функция отличается от процедуры только тем, что ее идентификатор можно наряду с константами и пе ременными использовать в выражениях, так как функция имеет выходной резуль тат определенного типа. Пусть, например, определены функция MyFunction и пе ременная X: function MyFunction: Integer; var X: Integer;
50 Глава 1 • Основы Delphi
Типы
В этом случае допустим такой оператор присваивания: X := 2*MyFunction-l; Имя процедуры нельзя использовать в выражении, так как процедура не имеет связанного с нею результата: procedure MyProcedure; X := 2 * M y P r o c e d u r e - l ;
//
Ошибка!
Комментарии — это произвольные фрагменты текста, размещаемые в программе и поясняющие ее. Комментарии никак не влияют на исполнение программы. Они могут помещаться в любое место программы, где можно вставить пробел. В Delphi используются три типа ограничителей комментария: {ЭТО — многострочный комментарий} (*Это — тоже многострочный комментарий*) // Все символы до конца текущей строки — комментарий Комментарии с различными ограничителями не могут пересекаться, но могут вкладываться: / Гак (* нельзя } *) { Так (* можно *) } Директивы компилятора — это особым образом оформленные комментарии, ко торые не поясняют те или иные фрагменты кода, как это делают обычные ком ментарии, но задают управляющую информацию для компилятора. Формат директивы: {$<ключевое_слово>} Как видим, директива обрамляется фигурными скобками, причем за первой из них без каких-либо пробелов следует знак денежной единицы $. Ключевое сло во — это набор символов, которые компилятор трактует как предписание выпол нить какое-либо действие. Например, директива {$WARNINGS OFF} предписы вает компилятору отказаться от генерации предупреждений (список директив компилятора см. в разделе Compiler directives файла справочной службы).
Типы Типы в Delphi играют огромную роль. Связано это с тем, что лежащий в осно ве Delphi язык Pascal был специально придуман как средство обучения студен тов программированию. Поскольку начинающий программист легко может до пустить ошибку или неточно описать свои действия, компилятор Pascal должен был иметь средства контроля действий программиста, чтобы вовремя предос теречь его от последствий неверных действий. Первоначально типы как раз и предназначались для того, чтобы программист явно указывал компилятору, какого размера память нужна ему в программе и что он собирается делать с этой памятью. Практика применения типов показала их высокую эффективность для
защиты программы от случайных ошибок, так что практически во всех совре менных языках программирования в той или иной степени реализован меха низм типов.
В этой главе приводится беглое описание нескольких простых типов, которые понадобятся для иллюстрации операторов языка.
Строковый и символьный типы Свое знакомство с типами мы начнем с типа Char. В Delphi 2005 (8) этот тип предназначен для размещения символа Unicode и занимает два соседних байта.
ПРИМЕЧАНИЕ —
.
В предыдущих версиях Delphi тип Char занимал один байт и хранил один символ в кодировке ANSI. Переход на двухбайтную кодировку связан с тем, что технология .NET Framework работа ет с этой кодировкой символов. Символ Unicode в памяти занимает одно слово (2 байта), которое имеет 65 536 возможных значений. Специальная международная комиссия по Unicode выра ботала соглашение, позволяющее с помощью этого кода представить все симво лы всех языков мира. Тип S t r i n g определяет цепочку символов Char. Этот тип занимает участок па мяти переменной длины, каждые 2 байта которого содержат один символ Unicode. Каждый символ в S t r i n g пронумерован, причем первый символ имеет номер 1. Программист может обращаться к любому символу строки, указывая его поряд ковый номер в квадратных скобках сразу за именем переменной: var // Начало раздела описания переменных S: S t r i n g ; // Объявление переменной строкового типа begin // Начало раздела исполняемых операторов S := 'Строка с и м в о л о в ' ; // Переменная S содержит S[6] end;
:=
// значение "Строка символов" ' и ' ; // Теперь переменная содержит значение // "Строки символов" // Конец раздела исполняемых операторов
Наличие комментариев избавляет меня от необходимости подробно описывать назначение каждой строки текста. Напомню, что программист обязан объявить любой вводимый им идентификатор. Идентификатор S неизвестен Delphi — он введен нами для переменной строкового типа. После его объявления в разделе переменных Delphi выделит для него начальную область памяти минимальной длины и будет контролировать использование этого идентификатора в разделе исполняемых операторов. Например, если в этом разделе встретится выражение 2 * S - 1, компилятор сразу «забьет тревогу», так как строка не может быть участником математических вычислений — она предназначена для хранения сим волов, а не чисел. В первом операторе присваивания в переменную S помещается значение строко вой константы 'ГфПгаСЛ ПМЧОЛП^и ' Гтпга/тчл,,™.
52
Глава 1 • Основы Delphi
Типы
53
ные символы, заключенные в обрамляющие апострофы, причем сами апострофы не входят в значение константы, поэтому после присваивания переменная при мет значение Строка символов без апострофов. Если понадобится включить в текстовую константу апостроф, он удваивается: ' Символ ' ' - а п о с т р о ф ' . После первого присваивания S будет занимать участок памяти длиной 30 байт — по два байта на каждый символ. Переменность размера области памяти, выделя емой для размещения строки символов, — характерная особенность типа S t r i n g . Пусть, например, во втором операторе мы обратились не к 6-му по счету симво лу, а ко всей строке:
щий в памяти 4 смежных байта и предназначенный для хранения целых чисел в диапазоне от -2 147 483 648 до +2 147 483 647 (более полные сведения о це лых типах вы найдете в главе 3).
S : = ' и' ;
• получение остатка от деления (mod).
Тогда эта переменная стала бы занимать 2 байта, а следующие за ними 28 байтов оказались бы свободными. Длина строковой переменной в программе меняется автоматически при каждом присваивании переменной нового значения и может составлять от 0 до 2 Гбайт.
Спецификой деления является то обстоятельство, что результат может быть дроб ным: У2, 2 3 / 5 и т. п. Для хранения дробных чисел в Delphi используются веще ственные типы (см. подраздел «Вещественные типы» в разделе «Простые типы» главы 3), вот почему в языке имеется целых две операции деления ( d i v и mod): var X,Y: Integer; begin
Над строковым типом определена операция сцепления (+): S
:=
'Delphi'
+
'
for'
+
'
.NET;
// S содержит "Delphi for .NET"
Кроме того, строки можно сравнивать с помощью следующих операций отноше ния: • равно (=); • не равно (о); • больше (>); • больше или равно (>=); • меньше (<); • меньше или равно (<=). ВНИМАНИЕ Символы о, <= и >= пишутся слитно, их нельзя разделять пробелами или комментариями.
Результат применения операции сравнения к двум строкам имеет логический тип, который характеризуется двумя возможными значениям: True (читается тру), или истина, и F a l s e (читается фоле), или ложь. Строки сравниваются побайтно, слева направо; каждая пара символов сравнивается в соответствии с их внутрен ней кодировкой. Все остальные действия над строками осуществляются с помощью нескольких стандартных для Delphi подпрограмм, описанных в главе 3.
Над целыми числами определены следующие математические операции: • сложение (+); • вычитание (-); • умножение (*); • деление с отбрасыванием остатка (div);
X := 5 div 2;
// X содержит 2
Y := 5 mod 2;
// Y содержит 1
end; Смысл остальных операций совпадает с общепринятым математическим, за тем исключением, что результат не должен выходить из диапазона возможных зна чений целого типа. Следующая простая программа проиллюстрирует искажение результата при пе реполнении целого типа. Это — консольная программа. Такого рода программы имеют предельно скромные возможности в отношении ввода и вывода (проце дуры R e a d l n и W r i t e l n соответственно) и выполняются в DOS-подобном окне. ВНИМАНИЕ В Delphi 2005 имеется возможность создания консольных приложений двух типов: использую щих и не использующих технологию NET. Заготовка для программ первого типа хранится в раз деле Delphi for .NET Projects хранилища объектов и связана с таким значком:
Console Application
Программы второго типа создаются с помощью заготовки из раздела Projects хранилища объек тов, которая связана с таким значком:
Целые типы Целые типы используются для хранения и преобразования целых чисел. В Delphi предусмотрено несколько целочисленных типов, различающихся диапазоном воз можных значений. В этой главе мы будем использовать тип I n t e g e r , занимаю-
Console Application
В дальнейшем мы будем создавать только консольные программы первого типа (использующие технологию .NET). В Delphi 8 программы второго типа (не использующие технологию .NET) соз дать нельзя.
54
Глава 1 • Основы Delphi
Выберите в разделе Delphi for .NET Projects хранилища объектов Delphi значок кон сольной программы и дополните ее код следующим образом: program Overview; {$APPTYPE CONSOLE} var X: I n t e g e r ; begin X := 2 1 4 7 4 8 3 6 4 7 ; X := X + 1; // // Writeln(X); // Readln //
Операторы языка
55
Составной оператор и пустой оператор Составной оператор — это последовательность произвольных операторов про граммы, заключенная в операторные скобки - зарезервированные слова b e g i n ... end. Составные операторы - важный инструмент Delphi, дающий возможность пи сать программы по современной технологии структурного программирования (без операторов перехода g o t o ) .
Ошибка! Результат +2 147 483 648 выходит из допустимого диапазона. Будет выведено -2147483648 Задержка окна до нажатия Enter
end;
Delphi не накладывает никаких ограничений на характер операторов, входящих в составной оператор. Среди них могут быть и другие составные операторы язык Delphi допускает произвольную глубину их вложенности: begin begin
Как и к строкам, к целым числам применимы операции сравнения.
begin
Операторы языка Операторами языка программирования называются языковые конструкции, в ко торых описываются команды, передаваемые программистом процессору. Все они (кроме оператора присваивания) сопровождаются зарезервированными словами. Каждый оператор может занимать произвольное количество строк и должен за канчиваться точкой с запятой. На одной строке могут располагаться несколько операторов. В этом разделе рассматриваются операторы ЯП Delphi.
Оператор присваивания Оператор присваивания — один из наиболее часто используемых операторов язы ка Delphi. Он состоит из двух частей. Левая часть содержит приемник значения. Этим приемником могут быть переменная или свойство некоторого типа. Пра вая часть содержит выражение того же типа. Обе части связываются следующи ми символами, которые пишутся слитно и читаются «присвоить значение»:
end ; end; end;
Поскольку зарезервированное слово end является закрывающей операторной скобкой, оно одновременно указывает и конец предыдущего оператора, поэтому ставить перед ним символ точки с запятой (;) необязательно. Наличие точки с запятой перед end в некоторых предыдущих примерах означало, что между пос ледним оператором и операторной скобкой end располагается пустой оператор. Пустой оператор не содержит никаких действий, просто в программу добавляет ся лишняя точка с запятой. В основном пустой оператор используется для пере дачи управления в конец составного оператора: как и любой другой, пустой опе ратор может быть помечен, и ему можно передать управление.
Условный оператор
bbRun.Enabled := False;
Условный оператор позволяет проверить некоторое условие и в зависимости от результатов проверки выполнить то или иное действие. Таким образом, услов ный -оператор — это средство ветвления вычислительного процесса. Структура условного оператора имеет следующий вид: if <условие> then <оператор1> e l s e <оператор2>;
В ходе выполнения оператора присваивания вначале вычисляется стоящее спра ва выражение, затем значение этого выражения помещается в приемник значе ния (переменную или свойство).
Здесь if, then, e l s e — зарезервированные слова {если, то, иначе); <условие> — произвольное выражение логического типа; <оператор1>, <оператор2> — лю бые операторы языка Delphi.
Примеры операторов присваивания: S := 'Строка символов'; N := N + 1;
56
Глава 1 • Основы Delphi
Операторы языка
ПРИМЕЧАНИЕ • — После оператора < о п е р а т о р ! > ставить признак конца оператора (символ точки сзапятой) нельзя, если следом за ним идет зарезервированное слово e l s e .
Условный оператор работает по следующему алгоритму. Вначале вычисляется условное выражение <условие>. Если результат равен True (истина), то вы полняется <оператор1>, а < о п е р а т о р 2 > пропускается; если результат равен F a l s e (ложь), наоборот, <оператор1> пропускается, а выполняется <оператор2>, например: var
X, Y, Max: Integer; begin
57
begin if X > Max t h e n Мах : = X; Y : = X; end;
В этом примере переменная Y всегда будет иметь значение переменной х, а в Мах запоминается максимальное значение X. Поскольку любой из операторов < о п е р а т о р 1 > и < о п е р а т о Р 2 > может иметь любой тип, в том числе быть условным, а в то же время не каждый из «вложен ных» условных операторов может иметь часть e l s e <оператор2>, то возни кает неоднозначность трактовки условий. Эта неоднозначность в Delphi разре шается следующим образом: любая встретившаяся часть e l s e соответствует ближайшей к ней сверху по тексту программы части t h e n условного операто ра, например:
if X > Max then Y := Max else Y := X;
var
end; При выполнении этого фрагмента переменная Y получит значение переменной X, если только это значение не превышает Мах, в противном случае Y станет равно Мах. Условными называются выражения, имеющие одно из двух возможных значе ний: истина или ложь. Такие выражения чаще всего получаются при сравнении переменных с помощью операций отношения =, о, >, >=, < и <=. Сложные ло гические выражения составляются с использованием логических операций and (логическое И), or (логическое ИЛИ) и n o t (логическое НЕ), например: if
(a > b)
and
(b о 0)
a,b,c,d : Integer; begin = 1 = 2, = 3, = 4, a < b then if с < d then if с < 0 then с := 0 else a := b; if a < b then if с < d then if с < 0 then с := 0 else else else a := b;
a b с d if
then . . .
ВНИМАНИЕ В отличие от большинства других языков программирования, в Delphi приоритет операций от ношения ниже, чем у логических операций, поэтому отдельные составные части сложного ло гического выражения заключаются в скобки. Например, такая запись предыдущего оператора будет неверной: if a > b and b <> 0 t h e n . . .
// Ошибка!
так как фактически (с учетом приоритета операций) компилятор будет транслировать такую строку: if a >
(b and b)
о 0 then
...
end;
// Да // Да // Нет //
Этот
// а равно // //
Да
//
Нет
выполняется
не
выполняется
2
// с равно
О
У/ if с < О // if с
< d
// if a
< Ъ
//
Этот
Операторы повторений
var X,
запрограммировать
Integer;
не
Да
Часть e l s e <оператор2> условного оператора может быть опущена. Тогда при значении T r u e условного выражения выполняется <оператор1>, в противном случае этот оператор пропускается: Y, Max:
оператор
оператор
В языке Delphi имеется три различных оператора, с помощью которых можно повтоояюптиегя
ГЬПЯГМРНТТЛ
п^
58
Глава 1 • Основы Delphi
Операторы языка
Счетный оператор цикла f o r имеет такую структуру: f o r <параметр_цикла>
:=
<нач_знач>
to <кон_знач>
do <оператор>;
Здесь for, to, do — зарезервированные слова (для, до, выполнить); <параметр цикла> — переменная типа I n t e g e r , точнее, любого порядкового типа (см. гла ву 3); < н а ч _ з н а ч > — начальное значение (выражение того же типа); <кон знач> — конечное значение (выражение того же типа); <оператор> — произ^ вольный оператор Delphi. При выполнении оператора f o r вначале вычисляется выражение <нач_знач> и осуществляется присваивание <параметр_цикла> : = <нач_знач>. После этого циклически повторяется следующая последовательность шагов. 1. Проверяется условие <параметр_цикла> <= <кон_знач>. Если условие не выполняется, оператор f o r завершает свою работу. 2. Выполняется оператор <оператор>. 3. Значение переменной <параметр_цикла> наращивается на единицу. В качестве иллюстрации применения оператора f o r рассмотрим программу, осу ществляющую ввод произвольного целого числа N и вычисление суммы всех це лых чисел от 1 до JV. program
Отметим два обстоятельства. Во-первых, условие, управляющее работой опера тора for, проверяется перед выполнением оператора <оператор>: если условие не выполняется в самом начале работы оператора for, исполняемый оператор не будет выполнен ни разу. В этом отношении показанную ниже проверку мож но опустить без малейших последствий: if N >=
1
then
Во-вторых, шаг наращивания параметра цикла строго постоянен и равен +1. Су ществует другая форма оператора: for <пар_цик>:
= <нач_знач> downto <кон_знач> do <оператор>;
Замена зарезервированного слова to словом downto означает, что шаг наращи вания параметра цикла равен - 1 , а управляющее условие приобретает вид <параметр_цикла> >= <кон знач> Предыдущий пример можно модифицировать так, чтобы сделать его пригодным для подсчета любых сумм — положительных и отрицательных: Sum : = 0 ; if N >= 0
Создайте такое консольное приложение:
59
then
f o r k := 1 t o N do
Summa;
Sum : = Sum + i else
{$APPTYPE
CONSOLE}
for k
:= -1 d o w n t o N do
Sum : = Sum + i ;
uses SysUtils; var k, Sum, N: Integer; begin Write('N = ' ) ;
Два других оператора лишь проверяют условие выполнения или повторения цик ла, но не связаны с изменением счетчика цикла. Оператор цикла w h i l e с предпроверкой условия: //
ВВОДИМ
границу
цикла
Readln(N); if N >= 1 then begin Sum := 0; for k
:= 1
w h i l e <условие> do <оператор>;
Здесь w h i l e , do - зарезервированные слова (пока [выполняется условие], де лать); <условие> - выражение логического типа; <оператор> - произволь ный оператор Delphi. to N do
// Цикл подсчета суммы
Sum := Sum + k end; Writeln('Сумма от 1 до ', N, ' = ',Sum); Readln end. Прежде всего, заметим, что оператор W r i t e l n в круглых скобках может содер жать несколько выражений, разделенных запятыми. Перед выводом выражения вычисляются и переводятся в символьный вид.
Если выражение <условие> имеет значение True, то выполняется <оператор>, после чего вычисление выражения <условие> и его проверка повторяются. Если <условие> имеет значение F a l s e , оператор w h i l e прекращает свою работу. Следующая программа отыскивает так называемое «машинное эпсилон» - такое минимальное не равное нулю вещественное число, которое после прибавления его к 1,0 еще дает результат, отличный от 1,0. Заметим, что для хранения и преобразо вания дробных чисел в Delphi предназначены так называемые вещественные типы (см. главу 3). В программе используется один из этих типов - Real, занимающий » смежных байтов и представляющий дробные (вещественные) числа в диапазоне от 10-J24 до 10+308 с точностью 14...16 значащих цифр.
60
Операторы языка
Глава 1 • Основы Delphi
61
У читателя, привыкшего к непрерывной вещественной арифметике, может выз вать недоумение утверждение о том, что в дискретной машинной арифметике всегда существуют такие числа 0 < X < eps, что 1,0 + X = 1,0. Дело в том, что 19 внутреннее представление типа R e a l может дать «лишь» приблизительно 10 возможных комбинаций значащих разрядов в отведенных для него восьми бай тах. Конечно же, это очень большое число, но оно несопоставимо с бесконечным множеством вещественных чисел. Аппроксимация бесконечного непрерывного множества вещественных чисел конечным (пусть даже и очень большим) мно жеством их внутреннего машинного представления и приводит к появлению «ма шинного эпсилон».
Обратите внимание: пара r e p e a t . . . u n t i l подобна операторным скобкам Ьеgin...end, поэтому перед u n t i l ставить точку с запятой необязательно. Заметим, что для правильного выхода из цикла условие выхода должно менять ся внутри операторов, составляющих тело цикла w h i l e или repeat...until. Сле дующие циклы никогда не завершатся «естественным» способом: while True do begin
Создайте такое консольное приложение:
repeat
ends-
program EpsilonDet;
until False; !$APPTYPE
CONSOLE}
var Epsilon: Real; begin Epsilon := 1; while l+Epsilon/2>l do Epsilon := Epsilon/2; Writeln('Машинное эпсилон = ', Epsilon); Readln end; Для вещественных чисел можно использовать операцию деления без отбрасыва ния дробной части (обозначаемую знаком / ) . После применения этой операции результат всегда имеет вещественный тип, поэтому такой фрагмент программы ошибочен: var X:
Integers-
begin X := 4 / 2 ;
/ / Ошибка! Вещественный результат нельзя // присвоить целой переменной
end;
Оператор цикла r e p e a t . . . u n t i l с постпроверкой условия: repeat <тело_цикла> u n t i l <условие>; Здесь r e p e a t , u n t i l — зарезервированные слова (повторять [до тех пор], пока [не будет выполнено условие]); <тело_цикла> — произвольная последователь ность операторов Delphi; <условие> — выражение логического типа. Операторы <тело_цикла> выполняются хотя бы один раз, после чего вычисля ется выражение <условие>: если его значение есть F a l s e , операторы < т е л о _ цикла> повторяются, в противном случае оператор r e p e a t . . . u n t i l завершает свою работу.
Для гибкого управления циклическими операторами for, w h i l e и r e p e a t в со став Delphi включены две процедуры без параметров: • break — реализует немедленный выход из цикла, передавая управление опе ратору, стоящему сразу за концом циклического оператора; •
c o n t i n u e — обеспечивает досрочное завершение очередного прохода цикла, что эквивалентно передаче управления в самый конец циклического оператора.
Введение в язык этих процедур практически исключает необходимость исполь зования операторов безусловного перехода g o t o (см. далее). В Delphi 2005 появился еще один итерационный оператор цикла for, имеющий такую структуру: for <итератор> in <коллекция> do Something; Здесь for, in, do — зарезервированные слова (для, в, делать); < и т е р а т о р > — итератор (элемент коллекции); <коллекция> — коллекция; S o m e t h i n g — про извольный оператор Delphi. В общей системе типов (CTS) широко использу ются так называемые коллекции, предназначенные для хранения разного рода объектов. Коллекции во многом подобны массивам, но в них нет индексного доступа к элементам. Итерационный цикл устраняет это неудобство. С его по мощью можно просмотреть все элементы коллекции и выполнить над ними не которые действия. Для примера в следующей консольной программе создается целочисленный массив и с помощью итерационного цикла находится сумма всех его элементов: program For__in_do; {$АРРТУРЕ
CONSOLE}
uses SysUtils; var Arr: array [1..10]
62
Глава 1 • Основы Delphi
Операторы языка
of Integer = (1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ) ; X, Sum: Integer; begin Sum := 0; for X in Arr do Sum := Sum + X; Writeln (Sum); Readln end.
case k of 1: Writeln('к = 1'); 2: Writeln('к = 2 ') ; else Writeln('k не 1 и не 2'); end end; В результате выполнения этой программы появится сообщение: к не 1 и не 2
В приведенном примере X — не параметр цикла, а текущий элемент коллекции. После итерационного прохода им становится другой элемент, и так продолжает ся до тех пор, пока не будут просмотрены все элементы коллекции. Заметим, что в Delphi 8 (и более ранних версиях) итерационного цикла нет.
Оператор выбора Оператор выбора позволяет выбрать одно из нескольких возможных продолже ний программы. Параметром, по которому осуществляется выбор, служит ключ выбора — выражение любого порядкового типа (из уже рассмотренных к поряд ковым относятся типы I n t e g e r и Char; описание остальных порядковых типов см. в главе 3). Структура оператора выбора такова: case <ключ_выбора> of <список_выбора>
[ e l s e <операторы>]
end;
Здест> c a s e , of, e l s e , end — зарезервированные слова (случай, из, иначе, конец); <ключ_выбора> — ключ выбора (выражение порядкового типа); <список_выбора> — одна или более конструкций вида <константа_выбора>: <оператор>; Здесь < к о н с т а н т а _ в ы б о р а > — константа того же типа, что и выражение <ключ_выбора>; <оператор> — произвольный оператор Delphi. Оператор выбора работает следующим образом. Вначале вычисляется значение выражения <ключ_выбора>, а затем в последовательности операторов < с п и сок_выбора> отыскивается такой, которому предшествует константа, равная вы численному значению. Найденный оператор выполняется, после чего оператор выбора завершает свою работу. Если в списке выбора не будет найдена констан та, соответствующая вычисленному значению ключа выбора, управление пере дается операторам, стоящим за словом e l s e . Часть e l s e <операторы> можно опускать. Тогда при отсутствии в списке выбора нужной константы ничего не произойдет, и оператор выбора просто завершит свою работу, например: var k: Integer; begin к := 3;
63
Любому из операторов списка выбора может предшествовать не одна, а несколь ко констант выбора, разделенных запятыми, например: var ch : Char; begin case ch of 'n'/N'/H'j'H': Writeln ( ' Нет ' ) ; ' у ' / У ' / д ' / Д ' : Writeln('Да'); end end;
Метки и операторы перехода Можно теоретически показать, что рассмотренных операторов вполне достаточ но для написания программ любой сложности. В этом отношении наличие в языке операторов перехода кажется излишним. Более того, современная технология структурного программирования основана на принципе «программировать без goto»: считается, что злоупотребление операторами перехода затрудняет пони мание программы, делает ее запутанной и сложной в отладке. Тем не менее в некоторых случаях использование операторов перехода может упростить программу. Оператор перехода имеет вид goto <метка>; Здесь g o t o — зарезервированное слово (перейти [на метку]); <метка> — мет ка. Метка в Delphi — это произвольный идентификатор, позволяющий имено вать некоторый оператор программы и, таким образом, ссылаться на него. В це лях -совместимости со стандартным языком Pascal в Delphi в качестве меток допускается использование целых чисел без знака. Метка располагается непо средственно перед помечаемым оператором и отделяется от него двоеточием. Опе ратор можно помечать несколькими метками, которые в этом случае отделяются друг от друга двоеточием. Перед тем как появиться в программе, метка должна быть описана. Описание меток состоит из зарезервированного слова l a b e l (мет ка), за которым следует список меток:
64
Глава 1 • Основы Delphi
label loop, begin
lbl,
1Ь2;
goto l b l ; loop:
...
1Ы:1Ь2:
...
goto lb2; end; Действие оператора g o t o состоит в передаче управления соответствующему по меченному оператору. При использовании меток необходимо руководствоваться следующими прави лами: • •
метка, на которую ссылается оператор g o t o , должна быть описана в разделе описаний и обязательно встретиться где-нибудь в теле программы; метки, описанные в подпрограмме, локализуются в ней, поэтому передача уп равления извне подпрограммы на метку внутри нее невозможна.
Массивы Рассмотренные простые типы данных позволяют применять в программе оди ночные объекты — числа, символы, строки и т. п. В Delphi могут использоваться также объекты, содержащие множество однотипных элементов. Это массивы — формальное объединение нескольких однотипных объектов (чисел, символов, строк и т. п.), рассматриваемое как единое целое. К необходимости применения массивов мы приходим всякий раз, когда требуется связать и использовать це лый ряд родственных величин. Например, результаты многократных замеров тем пературы воздуха в течение года удобно рассматривать как совокупность веще ственных чисел, объединенных в один сложный объект — массив измерений. При описании массива необходимо указать общее количество входящих в мас сив элементов и тип этих элементов, например: var a: array [1.'.10] of Real; b: array [0..50] of Char; c: array [-3..4] of String; Как видим, при описании массива используются зарезервированные слова a r r a y 1 и of (массив , из). За словом a r r a y в квадратных скобках указывается тип-диа1
Буквальный перевод слова «array» — боевой порядок, упорядоченная масса (войск). В компьютерной терминологии слово «array» переводится как массив.
Массивы
65
пазон, с помощью которого компилятор определяет общее количество элементов массива. Тип-диапазон (подробнее см. в главе 3) задается левой и правой грани цами изменения индекса массива, так что массив А состоит из 10, массив В — из 51, а массив С — из 8 элементов. За словом of указывается тип элементов, обра зующих массив. Доступ к каждому элементу массива в программе осуществляется с помощью индекса — целого числа (точнее, выражения порядкового типа, см. главу 3), слу жащего своеобразным именем элемента в массиве (если левая граница типа-диа пазона равна 1, индекс элемента совпадает с его порядковым номером). При упо минании в программе любого элемента массива сразу за именем массива должен следовать индекс элемента в квадратных скобках, например: а: array [ 1. . 10] of Integer; b: array [0..40] of Char; с: array [-2..2] of Boolean; k: Integer; begin b[17] := 'F'; с[-2] := a[l] > 2; for k := 1 to 10 do a[k] := 0; end; В правильно составленной программе индекс не должен выходить за пределы, определенные типом-диапазоном. Например, можно использовать элементы А [ 1 ], В [ 3 8 ] , С [ 0 ] , н о нельзя А [ 0 ] или С [ 3 8 ] (определение массивов см. ранее). Ком пилятор Delphi может контролировать индексы в программе на этапе как компи ляции, так и прогона программы. Для иллюстрации приемов работы с массивами составим программу, которая соз дает массив случайных целых чисел, подсчитывает их среднее арифметическое, а также определяет и выводит на экран минимальное и максимальное из этих чисел: program Array^demo; {$APPTYPE
CONSOLE}
uses SysUtils; {Программа создает массив из N случайных целых чисел, равномерно распределенных в диапазоне от 0 до MAX_VALUE-1, подсчитывает среднее арифметическое этих чисел, а также минимальное и максимальное из них.}
66
Процедуры и функции
Глава 1 • Основы Delphi
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 begin m[i] := Random(MAX_VALUE); Write(m[i]:5); // Выводим число в строку if i mod 10=0 then // Выведено 10 чисел в строке? Writeln // -Да- Переходим на новую строку end; // Задаем
начальные
значения
переменных:
sum := m [ l ] ; max := m [ 1 ] ; min := m [ l ] ; // Цикл вычисления суммы всех случайных // минимального и максимального: f o r i : = 2 t o N do begin
чисел и поиска
67
Процедуры и функции Процедуры и функции (я часто буду использовать их общее название — подпрог раммы) представляют собой важный инструмент Delphi, позволяющий писать хо рошо структурированные программы. В структурированных программах обычно легко прослеживается основной алгоритм, их проще понять любому читателю, они удобнее в отладке и менее подвержены ошибкам программирования. Все эти свой ства являются следствием важной особенности подпрограмм, каждая из которых представляет собой во многом самостоятельный фрагмент программы, связанный с основной программой лишь несколькими параметрами. Самостоятельность под программ позволяет локализовать в них все детали программной реализации того или иного алгоритмического действия, и поэтому изменение этих деталей, напри мер в процессе отладки, обычно не приводит к изменениям основной программы. Многие примеры в этой книге невелики по размерам (не более 30-40 строк), поэтому в таких программах можно обойтись и без подпрограмм. Иное дело — создание больших программ в сотни, тысячи и десятки тысяч строк. Писать та кие программы как единое целое, без расчленения на относительно самостоя тельные фрагменты, то есть без структурирования, просто невозможно. Практи чески во всех языках программирования имеются средства структурирования. Языки, в которых предусмотрены такие механизмы, называются процедурноориентированными. К их числу принадлежит и Delphi. Процедурой в Delphi называется особым образом оформленный фрагмент про граммы, имеющий собственное имя. Упоминание этого имени в тексте програм мы приводит к активизации процедуры и называется ее вызовом. Сразу после активизации процедуры начинают выполняться входящие в нее операторы, пос ле выполнения последнего из них управление возвращается обратно в основную программу и выполняются операторы, стоящие непосредственно за оператором вызова процедуры (рис. 1.1).
sum : = sum + m [ i ] ; if m [ i ] < min t h e n min : = m [ i ] e l s e i f m [ i ] > max t h e n max := m [ i ] end; // Вычисляем среднее значение и выводим результат: W r i t e l n ( ' М и н = ', m i n , ' Макс = \ r n a x , ' Среднее = ' , s u m / N ) ; Readln end. Для создания массива используется встроенная функция Random (Max), кото рая возвращает случайное целое число, равномерно распределенное в диапазоне от 0 до Мах-1 (Мах - параметр обращения). Каждое случайное число перево дится в строковый формат и добавляется к буферной строке следующим опера тором (символы : 5 указывают ширину каждого выводимого фрагмента): Write(m[i]:5); // Выводим число в строку
Рис. 1.1. Взаимодействие вызывающей программы и процедуры Для обмена информацией между основной программой и процедурой использу ется один или несколько параметров вызова. Как мы увидим далее (см. главу 4), процедуры могут иметь и другой механизм обмена данными с вызывающей про граммой, так что параметры вызова могут и не использоваться. Если они есть, то они перечисляются в круглых скобках за именем процедуры и вместе с ним об разуют оператор вызова процедуры. Функция отличается от процедуры тем, что результат ее работы возвращается в виде значения этой функции, и, следовательно, вызов функции может исполь зоваться наряду с другими операндами в выражениях.
68 Глава 1 • Основы Delphi С примерами процедур и функций мы уже сталкивались — это стандартные про цедуры Read, W r i t e , функция Random, математические функции и др. Стандар тными они называются потому, что созданы одновременно с системой Delphi и яв ляются ее неотъемлемой частью. В Delphi имеется много стандартных процедур и функций. Наличие богатой библиотеки таких программных заготовок суще ственно облегчает разработку прикладных программ. Однако в большинстве случаев некоторые специфичные для данной прикладной программы действия не находят прямых аналогов в библиотеках Delphi, и тогда программисту прихо дится разрабатывать свои, нестандартные процедуры и функции. Нестандартную подпрограмму необходимо описать, чтобы компилятор смог ус тановить связь между оператором вызова и теми действиями, которые предус мотрены в подпрограмме. Описание подпрограммы помещается в разделе описа ний (до начала исполняемых операторов). Составим процедуру для решения квадратного уравнения: procedure SqRoot(const a, b, c: Real; var xl, x2: Real; var OK: Boolean); var D: Real; begin D := Sqr(b) - 4 * a * с; // Дискриминант OK := (D >= 0 ) ; // Признак решения if OK then // Решение существует? begin // -Да. Находим его xl := (b + Sqrt(D)) / (2 * a) ; x2 := (b - Sqrt(D)) / (2 * a) end end; Как видим, описание процедуры начинается зарезервированным словом p r o cedure, за которым следуют имя процедуры и список формальных параметров. Список параметров заключается в круглые скобки и содержит перечень парамет ров с указанием их типа. Заметим, что перед параметром ОК, с помощью которого в вызывающую программу возвращается результат преобразования, стоит зарезер вированное слово var. Именно таким способом компилятору указываются те па раметры, в которых процедура возвращает вызвавшей ее программе результат сво ей работы (подробнее см. главу 4). Зарезервированное слово procedure, имя процедуры и список ее параметров образуют заголовок процедуры. За заголовком следует тело процедуры, содержащее новый раздел описаний (в этом разделе опи сана переменная D, предназначенная для вычисления дискриминанта уравнения) и раздел исполняемых операторов. Вызов этой процедуры мог бы быть таким: var SolvExists: Boolean; А, В, С, XI, Х2 Real; begin SqRoot(А, В, С, XI, X2, SolvExists);
Правила кодирования программ if SolvExists then begin // Использование end;
найденных
69
корней
end; Реализуем этот же алгоритм в виде функции: function SqRoot(const a, b, с: Real; var x l , x 2 : R e a l ) : B o o l e a n ) ; var D: Real; begin D := Sqr(b) - 4 * a * с; // Дискриминант Result := (D >= 0) and (a <> 0 ) ; // Признак решения if Result then // Решение существует? begin // -да. Находим его xl := (b + Sqrt(D)) / (2 * a ) ; x2 := (b - Sqrt (D) ) / (2 * a) end end; При описании функции необходимо за списком используемых для ее вызова па раметров указать тип возвращаемого ею результата. Именно поэтому за закры вающей круглой скобкой в заголовке функции стоят двоеточие и тип B o o l e a n : вместо переменной ОК для указания на факт существования решения использу ется значение, возвращаемое функцией. В теле любой функции определена стан дартная переменная R e s u l t , которая трактуется как результат, возвращаемый функцией. Возможный вариант вызова функции: var А, В, С, XI, Х2: Real; begin if SqRoot(А, В, С, XI, X2) then begin // Использование найденных корней end; end;
Правила кодирования программ Подавляющая часть кода программы располагается в многочисленных подпрог раммах, поэтому далее приводятся правила и рекомендации, относящиеся преж де всего к подпрограммам. Особенности кодирования модулей, библиотек и соб ственно программы тассматоиваются в соответствующих пазлелах книги.
70
Правила кодирования программ
Глава 1 • Основы Delphi
Любая подпрограмма имеет заголовок, раздел объявлений и раздел операторов: procedure //
MyProcedure;
Раздел
//
Заголовок
объявлений
begin //
Раздел
операторов
end;
В разделе объявлений описываются переменные, константы, подпрограммы и мет ки, которые будут использованы в разделе операторов. Описания группируются в секции, открываемые зарезервированными словами v a r (секция переменных), c o n s t (секция констант), l a b e l (секция меток). По рядок следования секций и их количество не имеют значения. Описание переменной заключается в указании ее типа, описание константы — в указании ее значения; метка описывается простым упоминанием ее в секции label:
В отличие от нетипизированных, типизированные константы можно изменять в ходе работы программы, то есть они ведут себя как инициализированные пере менные. Операторы располагаются в одной или нескольких строках и завершаются сим волом точки с запятой. Если оператор занимает несколько строк, он должен за канчиваться на каждой строке в том месте, где можно вставить пробел. Порядок следования операторов определяет порядок их исполнения, если не учитывать операторы переходов, которые нарушают этот порядок. В одной строке может размещаться несколько операторов. В этом случае они выполняются слева на право. В любом месте, где можно вставить пробел, можно разместить комментарий. В Delphi в качестве ограничителей комментария используются символы {...}, (*...*) и / / : { Это комментарий } (* Это тоже комментарий *) //
var I: Integer; a, b: Real; List: TStringList; const s = 'Delphi'; Max = 1234567; label 11, 12, 13;
конца
текущей
строки
образуют
комментарий
•
Не размещайте на одной строке больше одного оператора. Этот совет связан с тем, что при пошаговой трассировке программ (а это — основной способ отладки) компилятор останавливается перед выполнением очередной строки кода. Если на строке размещены несколько операторов, вы не сможете про следить работу каждого оператора в этой строке отдельно от других.
•
Выделяйте тела составных, условных, циклических операторов отступами. Если вы не станете этого делать, вам будет трудно определить, какому слову b e g i n соответствует то или иное слово end. Delphi провоцирует широкое использо вание составных операторов, причем часто они бывают вложенными. Если пре небречь этим и двумя следующими советами, вы, возможно, потратите много времени на устранение ошибки, вызывающей сообщение "; " e x p e c t e d b u t " . " found (нехватка слов end) или D e c l a r a t i o n e x p e c t e d b u t i d e n t i f i e r found (избыток слов end).
•
Во вложенных составных операторах старайтесь помечать замыкающие слова end небольшими комментариями:
var Name: String = 'Delphi'; Локальные (то есть объявляемые в теле подпрограммы) переменные таким спо собом инициализировать нельзя. В программе разрешается использовать типизированные константы: const
ВНИМАНИЕ
символы до
Излагаемые далее советы «выстраданы» мною за 25 лет программирования на языке Pascal. Однако если вы думаете, что они носят сугубо личный характер, то глубоко ошибаетесь: посмотрите исходные тексты реализации любого модуля Delphi, и вы увидите их на практике!
При описании глобальных переменных (то есть переменных, объявленных вне тела подпрограммы) их можно инициализировать, указав после названия типа знак равенства и начальное значение:
String =
Все
Если комментарий ограничивается символами {$...} или (*$...*), такой коммен тарий считается директивой компилятора. С его помощью программист может динамически изменять настройку компилятора и осуществлять условную ком пиляцию.
В одной строке может размещаться произвольное количество описаний. Одно типные переменные можно группировать в списки (см. описание переменных а и b в приведенном примере).
Name:
71
'Delphi';
—
Изменять значения типизированных констант можно во всех версиях Delphi, однако в вер сии 2005 (8) это сгановится возможным только при установленном флажке Assignable typed constants на вкладке Compiler окна Options (вызывается командой Project • Options).
if a>b then begin if not IsNew then
72
Глава 1 • Основы Delphi begin end; end;
// //if
if not
IsNew
a>b
•
Структурируйте алгоритм! Если тело вашей подпрограммы содержит более 20-30 строк, проанализируйте алгоритм и относительно самостоятельные его части вынесите во вложенные подпрограммы или реализуйте с помощью вспо могательных классов. Помните, что уже через 2-3 месяца вы будете с трудом припоминать детали реализации подпрограммы, насчитывающей сотни строк кода.
•
Не скупитесь на комментарии. Небольшие затраты времени на вставку ком ментария по ходу составления программы с лихвой окупятся, когда вам по надобится ее модернизировать. Не откладывайте вставку комментария «на потом» — у меня, например, никогда «потом» на это не хватает времени.
•
Старайтесь использовать осмысленные идентификаторы. Если ваших знаний английского языка для этого недостаточно, используйте кириллицу (только в Delphi 2005 (8)).
Элементы языка
Элементы языка кратко были рассмотрены в главе 1. В этой главе приводятся более полные сведения.
Алфавит Алфавит языка Delphi включает буквы, цифры, шестнадцатеричные цифры, спе циальные символы, пробелы и зарезервированные слова. Буквы— это буквы латинского алфавита от а до z и от А до Z, а также знак подчеркивания (_). В языке нет различия между прописными и строчными бук вами алфавита, если только они не входят в символьные и строковые выраже ния. Поскольку в Delphi используются символы Unicode, буквами могут быть также любые буквы национального алфавита. Например, следующая программа впол не работоспособна: program P r o j e c t ; {$APPTYPE CONSOLE} uses SysUtils; type Месяцы = (июнь, июль, а в г у с т , с е н т я б р ь ) ; var Месяц: Месяцы; begin Месяц := а в г у с т ; if Месяц = а в г у с т then
Идентификаторы
74 Глава 2 • Элементы языка
75
WriteLn('К морю!'); ReadLn end.
рективами — в остальных случаях. Слова at и on имеют специальный смысл и также относятся к зарезервированным.
Цифры — арабские цифры от 0 до 9.
ПРИМЕЧАНИЕ
Зарезервированные слова не могут использоваться в качестве идентификаторов.
Каждая шестнадцатеричная цифра имеет значение от 0 до 15. Первые 10 значе ний обозначаются арабскими цифрами 0 . . . 9, остальные шесть — латинскими буквами А. . . F или а. . . f. Специальные символы Delphi — это символы +
- * /
=
,
'
.
:
;
<
>
[
]
( ) { }
Л
@ $ #
К специальным символам относятся также следующие пары символов: О
<=
>=
:=
(*
*)
(.
.)
//
В программе эти пары символов нельзя разделять пробелами, если они исполь зуются как знаки операций отношения или ограничители комментария. Пары (. и .) могут употребляться вместо символов [ и ] соответственно. Особое место в алфавите языка занимают пробелы, к которым относятся любые символы в диапазоне кодов от 0 до 32. Эти символы рассматриваются как огра ничители идентификаторов, констант, чисел, зарезервированных слов. Несколь ко следующих друг за другом пробелов считаются одним пробелом (последнее не относится к строковым константам). В Delphi имеются следующие зарезервированные слова: and array as asm begin case class const constructor destructor dispinterface div do downto else end except exports
file finalization final finally for function goto if implementation in inherited initialization inline interface is label library mod
nil not object of or out packed procedure program property raise record repeat resourcestring sealed set shl shr
static string then threadvar to try type unit unsafe until uses var while with xor
В дополнение к перечисленным слова p r i v a t e , p r o t e c t e d , p u b l i c , p u b l i s h e d и automated считаются зарезервированными в пределах объявления класса и ди-
В режиме создания WinForms-приложений программа обращается к классам CTS, названия ко торых (или их членов) могут совпадать с зарезервированными словами Delphi. В этом случае для отмены действия зарезервированного слова непосредственно перед ним следует ставить символ амперсанта (&), например &Туре.
Стандартные директивы первоначально связаны с некоторыми стандартными объявлениями в программе. К ним относятся: absolute abstract assembler automated cdecl contains default deprecated dispid
dynamic export external far forward implements index library local
message name near nodefault overload override package pascal platform
private protected public published read readonly register reintroduce requires
resident safecall stdcall stored varargs virtual write writeonly
Как и зарезервированные слова, стандартные директивы в окне кода Delphi вы деляются жирным шрифтом, тем не менее вы можете переопределить любую стандартную директиву, то есть объявить одноименный идентификатор.
Идентификаторы Идентификаторы в Delphi — это имена констант, переменных, меток, типов, объек тов, классов, свойств, процедур, функций, модулей, программ и полей в записях. Идентификаторы могут иметь произвольную длину, но для компилятора знача щими являются первые 255 символов. Идентификатор всегда начинается буквой, за которой могут следовать буквы и цифры. Напомню, что буквой считается также символ подчеркивания, поэто му идентификатор может начинаться этим символом и даже состоять только из одного или нескольких символов подчеркивания. Пробелы и специальные сим волы алфавита не могут входить в идентификатор. Примеры правильных идентификаторов: MyProgramlsBestProgram external ALPHA date_27_sep_3 9 beta
76
Глава 2 • Элементы языка
Константы
77
Примеры неправильных идентификаторов:
Если необходимо записать собственно символ апострофа, он удваивается:
lProgram blocktl My P r o g mod
' ' — символ «'» (апостроф). Допускается использование записи символа путем указания его внутреннего кода, которому предшествует символ # (код 35), например:
// // // //
начинается цифрой содержит специальный символ содержит пробел зарезервированное слово
Объявление идентификатора можно дополнять так называемыми предупреждаю щими- стандартными директивами platform, l i b r a r y , d e p r e c a t e d . Эти дирек тивы никак не влияют на программу, но при компиляции с установленными ди рективами компилятора {$HINTS ON} и {$NARNINGS ON} компилятор будет сообщать о том, что описываемый идентификатором объект зависит от платфор мы (platform), особенностей библиотечной архитектуры ( l i b r a r y ) или уста рел и введен для обратной совместимости (deprecated).
•
#97 — символ «а»;
•
#90 — символ «Z»;
•
#39 — символ «'»;
•
#13 — символ «CR» (возврат каретки).
Строковая константа — любая последовательность символов (кроме символа CR), заключенная в апострофы. Если в строке нужно указать сам символ апост рофа, он, как уже упоминалось, удваивается, например:
Константы
'Это — строка символов';
В качестве констант в Delphi могут использоваться целые, вещественные и шестнадцатеричные числа, логические константы, символы, строки символов, конст рукторы множеств и признак неопределенного указателя NIL.
Строка символов может быть пустой, то есть не иметь никаких символов в об рамляющих ее апострофах. Строку можно составлять из кодов нужных симво лов с предшествующими каждому коду символами #. Например, следующая стро ка эквивалентна строке Symbol:
Целые числа записываются со знаком или без него по обычным правилам и мо гут иметь значение в диапазоне от - 2 6 3 до +2 63 -1.Следует учесть, что, если цело численная константа выходит за указанные границы, компилятор дает сообще ние об ошибке. Такие константы должны записываться с десятичной точкой, то есть определяться как вещественные числа. Вещественные числа записываются со знаком или без него с использованием де сятичной точки и/или экспоненциальной части. Экспоненциальная часть начи нается символом е или Е, за которым могут следовать знаки + (плюс) или (минус) и десятичный порядок. Символ е (Е) означает десятичный порядок и имеет смысл «умножить на 10 в степени», например: •
3 .14Е5 — 3,14 умножить на 10 в степени 5;
•
- 1 7 е - 2 — минус 17 умножить на 10 в степени минус 2.
Если в записи вещественного числа присутствует десятичная точка, перед точ кой и за ней должно быть хотя бы по одной цифре. Если используется символ экспоненциальной части е (Е), за ним должна следовать хотя бы одна цифра десятичного порядка. Шестнадцатеричное число состоит из шестнадцатеричных цифр, которым пред шествует знак доллара $ (код символа 36). Диапазон шестнадцатеричных чи сел - от $FFFFFFFFFFFFFFFF ДО $7FFFFFFFFFFFFFFF.
'That''s string'.
#83#121#109#98#11#108 Наконец, в строке можно чередовать части, записанные в обрамляющих апост рофах, с частями, записанными кодами. Таким способом можно вставлять в строки любые управляющие символы, в том числе и символ CR (код 13), например: #7'Ошибка!'#13'Нажмите любую клавишу ...'#7 Конструктор множества — это список элементов множества, обрамленный квад ратными скобками, например: [1,2,4..7,12]
[blue,
red]
[]
[true] В Delphi разрешается в объявлении констант использовать произвольные выра жения, операндами которых могут быть ранее объявленные нетипизированные константы, имена типов и объектов, а также следующие функции от них:
Логическая константа — это либо слово FALSE (ложь), либо слово TRUE (ис тина).
ab chrhi length
Символьная константа — это любой символ, заключенный в апострофы:
Например:
•
' z' — символ «z»;
•
'Ф' — символ «Ф».
const MaxReal
lo odd ord pred
ptr round sizeof succ
swap trunc
= Maxlnt div SizeOf(real);
78
Операции
Глава 2 • Элементы языка
NumChars = ord('Z') - ord('a') + 1; LnlO = 2.302585092994; LnlOR = 1 / LnlO;
Выражения Основными элементами, из которых конструируется исполняемая часть програм мы, являются константы, переменные и обращения к функциям. Каждый из этих элементов характеризуется своим значением и принадлежит к какому-либо типу данных. С помощью знаков операций и скобок из элементов можно составлять выражения, которые фактически представляют собой правила получения новых значений. Частным случаем выражения может быть просто одиночный элемент, то есть константа, переменная или обращение к функции. Значение такого выражения имеет, естественно, тот же тип, что и сам элемент. В общем случае выражение состоит из нескольких элементов (операндов) и знаков операций, а тип его зна чения определяется типом операндов и видом примененных к ним операций. Примеры выражений:
тором из условия оптимизации кода программы и не обязательно слева направо. При исчислении логических выражений операции равного приоритета всегда вы числяются слева направо, причем будут вычисляться все или только достаточ ные операции в зависимости от установленного в среде Delphi на вкладке Compiler окна Options (открывается командой Project • Options) флажка Complete Boolean eval: при установленном флажке вычисляются все операции отношения, при неуста новленном — только те, что необходимы для однозначного определения резуль тата исчисления. Правила использования операций с операндами различного типа приводятся в табл. 2.1. Таблица 2.1. Правила использования операций Операция not not 8
* •к •к
у 21 (а + Ь) * с sin(t) а > 2 n o t F l a g and (a = b) NIL [1, 3..7] * s e t l
Операции В Delphi определены следующие операции: •
унарные: not, @;
•
мультипликативные: *, /, div, mod, and, shl, shr;
•
аддитивные: +, —, or, xor;
• отношения: =, о , <, >, <=, >=, in. Приоритет операций убывает в указанном порядке, то есть наивысшим приори тетом обладают унарные операции, низшим — операции отношения. Порядок вы полнения нескольких операций равного приоритета устанавливается компиля-
79
/ div mod and and shl shr
+ + + + or or
= <> < <= > , >=
Действие
Тип операндов
Тип результата
Отрицание
Логический Любой целый Любой Любой целый Любой вещественный Множественный Любой вещественный Любой целый То же Логический Любой целый То же То же То же Любой вещественный Множественный Строковый Любой целый Любой вещественный Логический Любой целый Любой простой или строковый То же Логический То же То же То же
Логический Тип операнда Указатель Наименьший целый Extended Множественный Extended Наименьший целый То же Логический Наименьший целый Тоже То же То же Extended Множественный Строковый Наименьший целый Extended Логический Наименьший целый Логический
To же Адрес Умножение То же Пересечение множеств Деление Целочисленное деление Остаток от деления Логическое И То же Левый сдвиг Правый сдвиг Сложение То же Объединение множеств Сцепление строк Вычитание То же Логическое ИЛИ То же Равно Не равно Меньше Меньше или равно Больше Больше или равно
То же Логический То же То же То же
Унарная операция @ применяется к операнду любого типа и возвращает резуль тат типа P o i n t e r (см. главу 3), в котором содержится адрес операнда. Если опе рация @ применяется к процедуре, функции или методу в объекте, ее результа том является адрес точки входа в эту процедуру (функцию, метод).
80
Глава 2 • Элементы языка
В Delphi определены следующие логические операции: • n o t — логическое НЕ; • and — логическое И; • or — логическое ИЛИ; • х о г — исключающее ИЛИ. Логические операции применимы к операндам целого и логического типов. Если операнды — целые числа, то результат логической операции есть тоже целое чис ло, биты которого (двоичные разряды) формируются из битов операндов по пра вилам, указанным в табл. 2.2.
Типы данных
Таблица 2.2. Логические операции над данными целого типа (поразрядно) Операнд 1
Операнд 2
not
and
or
хог
1
—
—
—
—
0 0 0 1 1
0 1
0 1 0 1
0 0 0 1
0 1 1 1
0 1 1 0
— — —
К логическим в Delphi обычно относят еще две сдвиговые операции над целыми числами: •
i s h l j — сдвиг содержимого i на j разрядов влево (освободившиеся млад шие разряды заполняются нулями);
•
i s h r j — сдвиг содержимого i на j разрядов вправо (освободившиеся стар шие разряды заполняются нулями).
Любые данные, то есть константы, переменные, свойства, значения функций или выражения, в Delphi характеризуются своими типами. Тип определяет множество допустимых значений, которые может иметь тот или иной объект, а также мно жество допустимых операций, применимых к нему. Кроме того, тип определяет формат внутреннего представления данных в памяти ПК. Delphi характеризуется разветвленной структурой типов данных (рис. 3.1). В язы ке предусмотрен механизм создания новых типов, благодаря чему общее количест во используемых в программе типов может быть сколь угодно большим.
В этих операциях i и j — выражения любого целого типа. Логические операции над логическими данными дают результат логического типа по правилам, указанным в табл. 2.3. Таблица 2.3. Логические операции над данными логического типа Операнд 1
Операнд 2
not
and
or
xor
True False False False True True
— —
False True
— —
— —
— —
False True False True
— — — —
False False False True
False True True True
False True True False
Операция отношения IN применяется к двум операндам. Первым (левым) опе рандом должно быть выражение любого порядкового типа, вторым — множе ство, состоящее из элементов того же типа, или идентификатор множественно го типа. Результатом операции является True, если левый операнд принадлежит множеству.
Рис. 3 . 1 . Типы данных в Delphi
Характерной особенностью среды Delphi является возможность использования в ней не только собственных типов, но и типов из общей системы типов (CTS). Фактически все собственные типы Delphi проецируются на. соответствующие
82
Глава 3 • Типы данных
типы CTS, потому что их и только их способна обрабатывать общеязыковая сре да исполнения (CLR). Вот почему знакомство с типами мы начнем с краткого обзора CTS.
Общая система типов Все типы CTS делятся на две группы: значимые и ссылочные. Значимые типы являются контейнерами двоичных значений. Например, к зна чимым относится логический тип, имеющий два возможных значения — ноль и не ноль. Значимым является и тип Char, имеющий значения в диапазоне от О до 65 535. Этот тип кодирует символы Unicode. Подавляющее большинство зна чимых типов предопределено в конкретном языке программирования (в том числе в Delphi). К значимым относятся также и некоторые типы, определенные про граммистом, — например, перечисления. Значения значимых типов распределя ются в локальной памяти программы, в том числе в стеке. Ссылочные типы содержат указатели (см. далее в этой главе) на данные, разме щенные в памяти компьютера. Они распределяются в общей памяти (в куче). Ссылочные типы бывают самоописываемыми (self-describing), указателями и ин терфейсами. Самоописываемые типы, в свою очередь, делятся на классы и мас сивы (рис. 3.2).
Общая система типов type Int = class I: Integerend;
83
// Ссылочный тип
var Intl,
Int2:
Integer;
// Переменные значимого типа
Refl, begin
Ref2:
Int;
// Переменные ссылочного типа
Intl := 0; Int2 := 123; Refl : = Int.Create; Ref2
// Обе ссылочные переменные
:= Refl; //
Refl.I
:=
234;
указывают
на
// Изменение
один
объект
одного изменяет другой
Writeln(Format('Intl=%d
Int2=%d\
[Intl,
Writeln(Format('Refl=%d
Ref2=%d',
[Refl.I, Ref2.I]));
Int2]));
Readln end.
На экран будут выведены следующие строки: I n t l = 0 I n t 2 = 123 R e f l = 234 Ref2 = 234
Замечу, что для форматирования вывода используется функция Format, опи санная в разделе «Строки». Каждое значение относится к определенному типу. Значения самоописываемых типов называются объектами. В предыдущих версиях Delphi объектами обычно назывались экземпляры (значения) классов. В Delphi 2005 (8) к объектам отно сятся также и значения массивов. В то время как по значению самоописываемо го типа можно точно определить сам тип, этого нельзя сделать в отношении зна чений значимых типов и указателей. Рис. 3.2. Схема общей системы типов
Переменные значимых типов имеют собственную копию данных, и действия над одной переменной относятся только к этой копии. Действия над переменными ссылочных типов относятся к некоторым объектам и могут влиять сразу на не сколько ссылочных переменных. Пусть, например, имеются две переменные зна чимого типа ( l n t l и l n t 2 ) и две — ссылочного (ReflnRef2). Следующий при мер иллюстрирует изменение их значений: p r o g r a m CTS; {$APPTYPE
CONSOLE}
uses SysUtils;
Контейнером, определяющим границы видимости типов, в .NET Framework яв ляются сборки (assembly). В Delphi роль сборок играют модули. Таким образом, два одноименных типа, описанные в разных сборках (модулях), — это разные типы. Имя типа в CLR имеет две логические части: имя сборки (модуля) и имя типа, разделенные точкой. Базовым классом в CTS и Delphi является класс Object, определенный в про странстве имен System. Этот класс не имеет полей. В нем инкапсулированы ме тоды, некоторые из которых перечислены в табл. 3.1. Они могут перекрываться в потомках. ПРИМЕЧАНИЕ Все типы, описываемыа далее в этой части книги, относятся к типам VCL, то есть они доступны в режиме создания VCL-приложений (см. часть II). Некоторые из них недоступны в режиме со здания WinForm-приложений (см. часть III). В главе 20 описаны некоторые альтернативные и из мененные типы WinForm.
84
Глава 3 • Типы данных
Простые типы
Т а б л и ц а 3 . 1 . Некоторые методы класса Object Мет0д
Описание
f u n c t i o n Equals (Objl, Obj2: TObject) : Bool procedure F i n a l i z e
Возвращает True, если объекты Obj 1 и Obj2 имеют одинаковые значения Освобождает занятые объектом ресурсы перед его удалением из кучи
f u n c t i o n GetHashCode: I n t e g e r f u n c t i o n GetType: Type f u n c t i o n ToString: S t r i n g
Возвращает хэш-код объекта Возвращает связанный с текущим объектом объект класса Туре Возвращает строковое представление значения объекта (обычно — полное имя класса)
Поскольку класс O b j e c t — прародитель всех других классов CTS и Delphi, ме тоды, перечисленные в табл. 3.1, могут применяться к любому экземпляру любо го класса.
Простые типы К простым типам относятся порядковые типы, вещественные типы и тип датавремя. Все они в иерархии CTS относятся к значимым. Порядковые типы отличаются тем, что каждый из них имеет конечное количе ство возможных значений. Эти значения можно определенным образом упоря дочить (отсюда — название типов), и, следовательно, каждому из них можно со поставить некоторое целое число — порядковый номер значения. Вещественные типы, строго говоря, тоже имеют конечное количество значений, которое определяется форматом внутреннего представления вещественного чис ла. Однако это количество настолько велико, что сопоставить каждому из них целое число (его номер) не представляется возможным. Тип дата-время предназначен для хранения даты и времени. Фактически для этих целей в нем используется вещественный формат.
Порядковые типы К порядковым типам относятся целые типы, логические типы, символьный тип, перечисленный тип и тип-диапазон (см. рис. 3.1). ПРИМЕЧАНИЕ В предыдущих версиях Delphi к любому порядковому типу можно было применить функции o r d (возвращала порядковый номер значения, заданного параметром обращения), p r e d (возвра щала предыдущее значение типа) и s u c c (возвращала следующее значение типа). В Delphi 8 эти функции не определены. В Delphi 2005 они могут использоваться только в приложениях, не основанных на .NET.
Целые типы Диапазон возможных значений целых типов зависит от их внутреннего пред ставления, которое может занимать 1, 2, 4 или 8 байт. В табл. 3.2 приводятся
85
названия целых типов, длина их внутреннего представления в байтах и диапазон возможных значений. Т а б л и ц а 3 . 2 . Целые типы Название
Длина, байт
Диапазон значений
Byte Shortlnt Smalllnt Word Integer Longlnt LongWord Int64 Cardinal
1 1 2 2 4 4 4 8 4
0...255 -128...+127 -32 768...+32 767 0...65 535 -2 147 483 648...+2 147 483 647 -2 147 483 648...+2 147 483 647 0...4 294 967 295 -9-1018...+9-1018 0... 2 147 483 647
При использовании процедур и функций с целочисленными параметрами следует руководствоваться «вложенностью» типов, то есть везде, где может быть задейство ван тип Word, допускается использование типа Byte (но не наоборот), в L o n g l n t «входит» тип S m a l l l n t , который, в свою очередь, включает в себя S h o r t l n t . Перечень процедур и функций, применимых к целочисленным типам, приведен в табл. 3.3. Буквами b, s, w, i, 1 обозначены выражения типа Byte, S h o r t l n t , Word, I n t e g e r и L o n g l n t соответственно, буквой х — выражение любого из этих типов; буквы vb, vs, vw, v i , v l , vx обозначают переменные соответствую щих типов. В квадратных скобках указывается необязательный параметр. Таблица 3.3. Стандартные процедуры и функции, применимые к целым типам Обращение
abs(x) chr(b) dec(vx[,I]) inc(vx[,I]) Hi (w) Hi(I) Lo(i) Lo (w) odd(l) Random(w) sqr(x) swap (i) swap(w)
Тип результата
Действие
IK
Возвращает модуль х Возвращает символ по его коду1
Char
Byte To же To же To же Boolean Как у параметра
Integer Word
Уменьшает значение vx на i, а при отсутствии i — на 1 Увеличивает значение vx на i, а при отсутствии i — на 1 Возвращает старший байт аргумента Возвращает третий по счету байт Возвращает младший байт аргумента То же Возвращает T r u e , если аргумент— нечетное число Возвращает псевдослучайное число, равномерно распределенное в диапазоне 0...(w-l) Возвращает квадрат аргумента Меняет местами байты в слове То же
При действиях с целыми числами тип результата соответствует типу операндов, а если операнды относятся к различным целым типам — общему типу, который В Delphi 8 функция chr применима только к значениям от 0 до 127.
86
Глава 3 • Типы данных
Простые типы
включает в себя оба операнда. Например, при действиях с типами S h o r t l n t и Word общим является тип I n t e g e r . При стандартной настройке компилятор Delphi не вырабатывает код, осуществляющий контроль за возможным выходом значения из допустимого диапазона, что может привести к недоразумениям. На пример, при прогоне следующей программы на экране появится значение 0: procedure
TfmExample.bbRunClick(Sender:
TObject);
var
k
:=
65535;
//
Максимальное
//
По
k+1;
Writeln(k);
//
На
значение
типа
правилам математики
самом
деле
Word
k=65536
k=0!
Readln
Таблица 3.5. Кодировка символов в соответствии со стандартом ANSI
end;
Если на вкладке Compiler диалогового окна Options (открывается командой Pro ject • Options) установить флажок Range checking и повторить компиляцию коман дой Project • Build All, компилятор вставит в программу код проверки переполне ния и при прогоне программы возникнет исключительная ситуация, которую при желании можно соответствующим образом обработать. Замечу, что можно изме нить программу следующим образом: к
:= 6 5 5 3 5 ;
Writelnfk
+
// 1);
Максимальное //
Будет
значение
выведено
типа
Word
65536
Тогда переполнения не произойдет, так как 32-разрядный компилятор Delphi автоматически преобразует операнды выражения к + 1 к 4-байтным величи нам. При обращении к функциям API Windows могут использоваться целые типы, определенные в модуле1 Windows (табл. 3.4). Таблица 3.4. Целые типы для связи с ядром Windows Базовый тип
Тип Windows
Базовый тип
DWORD
LongWord
UCHAR
Byte
LANGID
Word
UINT
LongWord
LCID
LongWord
ULONG
Cardinal
SHORT
Smalllnt
Тип Windows
Логические типы К логическим относятся типы Boolean, ByteBool, Bool, WordBool и LongBool. В стандартном языке Pascal определен только тип B o o l e a n , остальные логичес кие типы введены в Delphi для совместимости с Windows: типы B o o l e a n и By t e B o o l занимают по одному байту каждый, Bool и WordBool — по 2 байта, LongBool — 4 байта. Значениями логического типа может быть одна из предва рительно объявленных констант: F a l s e (ложь) или T r u e (истина). 1
Значениями символьного типа Char является множество всех символов кодUnicode. Этот код разработан международным комитетом Unicode Technica' Committee и предназначен для отображения символов всех языков мира Дл* каждого символа в этой кодировке отводятся два смежных байта. Таким обпазом, следующий оператор выведет сообщение «2»:
Для кириллицы, например, в соответствии с последней версией Unicode 3 0 1 от водится диапазон значений с $0400 по $04FF. Внутри этого диапазона каждый код закрепляется за единственной буквой. Например, буквы с «А» по «Я» (без «Ё») имеют коды C$0410 по $042 F, а буквы с «а» по «я» (без «ё») - с $0430 по $04 4F.
begin :=
Символьный тип
ShowMessage (IntToStr(SizeOf(Char)));
k:'Word; k
8i
На самом деле — в пространстве имен Borland.VCL.Windows. Связь между пространствами имен и модулями обсуждается в главе 9.
Код
Символ
Код
Символ
Код
Символ
Код
0
NUL
32
BL
1
SOH
@
2 3
STX ЕТХ
33 34 35
ii
64 65 66 67
4
EOT
96 97 98 99
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
ENQ АСК BEL BS НТ LF VT FF CR SO SI DEL DC1 DC2 DC3 DC 4
NAK SYN ETB CAN EM SUB ESC FS GS RS US
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
5a
60 61 62 63
i
# $ 6
& •
( ) * + f
1 0 1 2 3 4 5 6 7 8 9
; < = > •p
A В
72 73 74 75
С D E F G H I J К
76 77
L M
78 79 80 81 82 83 84
N О P
68 69 70 71
85 86 87 88 89 90 91 92 93 94 95
Q R S T U V W X Y Z [
\ ]
_
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
Символ
a b с d e f g h i
J
k 1 m n о P q r s t u V
w X
У z { 1 }
•
88
Глава 3 • Типы данных
Если говорить об алфавитных языках (европейские, арабский, иврит и т. п.), то символы внутри кодовых диапазонов сортируются в основном по национальному алфавиту, причем прописные буквы предшествуют строчным. Такой подход упро щает сортировку и поиск символов и строк. Подробную информацию о Unicode можно получить на сайте www.unicode.org. К типу Char применимы операции отношения, а также следующие функции: •
•
Chr (W) — функция типа Char, преобразующая выражение W типа Word в сим вол и возвращающая его своим значением, причем значение В должно нахо диться в диапазоне от 0 до 127 (см. табл. 3.5) — для указания одного из симво лов ANSI (American National Standard Institute — Американский национальный институт стандартов) либо определять символ Unicode; UpCase (CH) — функция типа Char, возвращающая прописную букву, если СН — строчная латинская буква, в противном случае возвращающая сам сим вол СН (для кириллицы возвращает исходный символ).
Следующий оператор выведет на экран символ «Б»: Writeln(Chr ($411));
Перечисленный тип
Простые типы
Использование перечисленных типов повышает надежность программ благода ря возможности контроля тех значений, которые получают соответствующие пе ременные. Пусть, например, заданы такие перечисленные типы: type colors = (black, red, w h i t e ) ; o r d e n a l = ( o n e , two, t h r e e ) ; d a y s = (Monday, T u e s d a y , Wednesday) ;
С точки зрения мощности и внутреннего представления все три типа эквивален тны. Однако пусть определены такие переменные: var
col : colors; num : ordenal; day : days; Тогда допустимы следующие операторы: col := black; num := two; day := Tuesday;
Перечисленный тип задается перечислением тех значений, которые он может получать. Каждое значение именуется некоторым идентификатором и распола гается в списке, обрамленном круглыми скобками, например:
В то же время следующие операторы недопустимы:
type colors = (red, white, blue);
day : = b l a c k ;
Применение перечисленных типов делает программы нагляднее. Если, напри мер, в программе используются данные, связанные с месяцами года, то такой фрагмент программы был бы, согласитесь, весьма наглядным: type ТипМесяц=(янв,фев,мар,апр,май,июн,июл,авг,сен, окт,ноя, дек); var месяц : ТипМесяц; begin ... if месяц = авг then lbOutput.Caption := 'Хорошо бы поехать к морю!'; end. Соответствие между значениями перечисленного типа и порядковыми номера ми этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер 0, второе — 1 и т. д. Максимальная мощ ность перечисленного типа составляет 65 536 значений, поэтому фактически пе речисленный тип задает некоторое подмножество целого типа Word и может рас сматриваться как компактное объявление группы целочисленных констант со значениями О, 1 и т. д.
81
c o l := o n e ;
Как уже упоминалось, между значениями перечисленного типа и множеством целых чисел существует однозначное соответствие. В Delphi допускается и об ратное преобразование: любое выражение типа Word можно преобразовать в зна чение перечисленного типа, если только значение целочисленного выражения не превышает мощности этого типа. Такое преобразование достигается примене нием автоматически объявленной функции с именем перечисленного типа. На пример, для рассмотренного ранее объявления типов эквивалентны следующие присваивания: col := black; col := colors (0); Разумеется, показанное далее присваивание недопустимо, так как перечисленно му типу нельзя присвоить целое значение: c o l : = 0;
Переменные любого перечисленного типа можно объявлять без предварительно го описания этого типа, например: var col : (black, white, green);
Тип-диапазон Тип-диапазон есть подмножество своего базового типа, в качестве которого мо-
90
Глава 3 • Типы данных
Простые типы
Тип-диапазон задается границами своих значений внутри базового типа:
91
Длина, байт
Название
Количество значащих цифр
Диапазон значений
Например:
8 10 8 8
Double Extended Comp Currency
15...16 19...20 19...20 19...20
type digit = ' 0 ' . . ' 9 ' ; dig2 = 4 8 . . 57;
5,0-10- ...1,7х10 51 4932 3,4 •10^ ...1,1х10 63 63 -2 ...+2 -1 ±922 337 203 685 477,5807
Как видно из табл. 3.6, вещественное число в Delphi занимает от 4 до 10 смеж ных байтов и имеет следующую структуру в памяти компьютера:
<мин.знач.>..<макс.знач.> Здесь <мин. з н а ч . > — минимальное значение типа-диапазона; < м а к с . з н а ч . > — максимальное его значение.
Тип-диапазон необязательно описывать в разделе type, его можно указывать не посредственно при объявлении переменной, например: var date : 1..31; month : 1. . 12; lchr : 'A'..' Z';. При определении типа-диапазона нужно руководствоваться следующими прави лами: • два символа точки (. .) рассматриваются как один символ, поэтому между ними недопустимы пробелы; • левая граница диапазона не должна превышать его правую границу. Тип-диапазон наследует все свойства своего базового типа, но с ограничениями, связанными с его меньшей мощностью. В стандартную библиотеку Delphi включены две функции, поддерживающие ра боту с типами-диапазонами: •
High (X) — возвращает максимальное значение типа-диапазона, к которому принадлежит переменная X; • Low(X) — возвращает минимальное значение типа-диапазона.
Вещественные типы В отличие от порядковых типов, значения которых всегда сопоставляются с ря дом целых чисел и, следовательно, представляются в компьютере абсолютно точ но, значения вещественных типов определяют произвольное число лишь с неко торой конечной точностью, зависящей от внутреннего формата вещественного числа (табл. 3.6).
323
308
Здесь s — знаковый разряд числа; е — экспоненциальная часть (содержит двоич ный порядок); m — мантисса числа. Мантисса m имеет длину от 23 (для S i n g l e ) до 63 (для E x t e n d e d ) двоичных разрядов, что для S i n g l e обеспечивает точность 3...8, а для E x t e n d e d — 19...20 десятичных цифр. Десятичная точка (запятая) подразумевается перед левым (старшим) разрядом мантиссы, но при действиях с числом ее положение сдвига ется влево или вправо в соответствии с двоичным порядком числа, хранящимся в экспоненциальной части, поэтому действия над вещественными числами назы вают арифметикой с плавающей точкой (запятой). Отметим, что арифметический сопроцессор всегда обрабатывает числа в форма те E x t e n d e d , а три других вещественных типа в этом случае получаются про стым усечением результатов до нужных размеров и применяются в основном для экономии памяти. Например, пусть «машинное эпсилон» (см. пример в главе 1) вычисляется с по мощью такой программы: type RealType = Real; var Epsilon : RealType; begin Epsilon := 1; while l+Epsilon/2 > 1 do Epsilon := Epsilon/2; Writenl(Epsilon) end; Тогда независимо от объявления типа R e a l T y p e (он может быть S i n g l e , R e a l , Double или E x t e n d e d ) на печать будет выдан следующий результат: 2.2204 4 604 9250031Е-0016
Таблица 3.6. Вещественные типы Длина, байт
Название
Количество значащих цифр
Диапазон значений
8
Real
15...16
5,0-10-323...1,7-10308
4
Single
3...8
1,5-10^5...3,4-1038
Этот результат соответствует типу E x t e n d e d . Происходит это по той причине, что все операнды вещественного выражения 1 + E p s i l o n / 2 в операторе w h i l e перед вычислением автоматически преобразуются к типу E x t e n d e d . Чтобы по лучить правильный результат, программу необходимо изменить следующим об разом:
92
Простые типы
Глава 3 • Типы данных
93
В Delphi включен модуль Match, который существенно расширяет представлен ный в табл. 3.7 набор встроенных математических функций. Особенностью реали зации содержащихся в нем более 70 функций и процедур является их оптимиза ция для работы с арифметическим сопроцессором класса Pentium, так что все они выполняют необходимые вычисления за рекордно малое время. Исходный текст модуля содержится в файле Source\Rtl\Borland.VCL.Match.pas каталога размещения Delphi. В приложении А перечисляются некоторые подпрограммы модуля Match.
type RealType = Real; var Epsilon, Epsl : RealType; begin Epsilon := 1; repeat Epsilon := Epsilon/2; Epsl := 1 + Epsilon until Epsl = 1; Writeln(2*Epsilon) end.
ПРИМЕЧАНИЕ
Особое положение в Delphi занимают типы Comp и Currency, которые трактуются как вещественные числа с дробными частями фиксированной длины: в Comp дроб ная часть имеет длину 0 разрядов, то есть просто отсутствует, в Currency длина дробной части составляет 4 десятичных разряда. Фактически оба типа определяют большое целое число со знаком, сохраняющее 19...20 значащих десятичных цифр (во внутреннем представлении они занимают 8 смежных байтов). В то же время в выражениях Comp и Currency полностью совместимы с любыми другими веще ственными типами: над ними определены все вещественные операции, они могут использоваться как аргументы математических функций и т. д. Наиболее подходя щей областью применения этих типов являются бухгалтерские расчеты. Для работы с вещественными данными могут использоваться стандартные мате матические функции, представленные в табл. 3.7. Бэтой таблице Real означает любой вещественный тип, I n t e g e r — любой целый тип. Таблица 3.7. Стандартные математические функции Delphi Обращение
Тип параметра
Тип результата
Примечание Модуль аргумента Арктангенс (значение в радианах)
abs(x)
Real, I n t e g e r
Тип аргумента
ArcTan(x)
Real
Real
cos(х)
To же
To же
Косинус, угол в радианах
ехр(х)
Тоже
To же
Экспонента
fгас(х)
To же
To же
Дробная часть числа
int(x)
Тоже
To же
1п(х)
To же
To же
Целая часть числа Логарифм натуральный
Pi Random
— —.
To же
Random(x)
Integer
Integer
Randomize
—
—
sin(x) sqr(x)
Real To же
Real To же
Синус, угол в радианах Квадрат аргумента
Tn wp
T n ЖР
КООРНЬ квалпатиый
To же
я = 3,141592653 Псевдослучайное число, равномерно распределенное в диапазоне 0...[1] Псевдослучайное целое число, равномерно распределенное в диапазоне 0...(х-1) Инициация генератора псевдослучайных чисел
;
—
Генератор псевдослучайных чисел представляет собой функцию, которая использует некоторое предварительно заданное целое число, называемое базовым, изменяет его разряды по опреде ленному алгоритму и выдает новое число как результат. Одновременно с этим новое число ста новится базовым при следующем обращении к функции и т. д. (Так как алгоритм функции не меняется в ходе ее работы, числа называются псевдослучайными.) В системном модуле System, который автоматически доступен любой программе, базовое число хранится в переменной с име нем RandSeek и всегда имеет начальное значение 0. Это означает, что при последовательном обращении к функции Random в разных программах (или при нескольких прогонах одной про граммы) будет всегда выдана одна и та же последовательность псевдослучайных чисел. С помо щью процедуры Randomize в переменную RandSeek можно поместить численное значение системного времени, что приведет к генерации другой последовательности (обращение к этой процедуре должно предшествовать первому обращению к функции Random).
Тип дата-время Тип дата-время определяется стандартным идентификатором TDateTime и пред назначен для одновременного хранения и даты, и времени. Во внутреннем представ лении он занимает 8 байт и, подобно Currency, представляет собой вещественное число с фиксированной дробной частью: в целой части числа хранится дата, в дроб ной — время. Дата определяется как количество суток, прошедших с 30 декабря 1899 г., а время — как часть суток, прошедших с 0 часов, так что значение 36 677,8157 соответствует дате 31.05.2004 и времени 19:43'. Количество суток может быть и от рицательным, однако значения, меньшие -693 594 (соответствует дате 00.00.0000 от Рождества Христова), игнорируются функциями преобразования даты к строково му типу. Над данными типа TDateTime определены те же операции, что и над веществен ными числами, а в выражениях этого типа могут участвовать константы и пере менные целого и вещественного типов. Для работы с датой и временем исполь зуются подпрограммы, часть из которых перечислена в табл. 3.8. Т а б л и ц а 3 . 8 . Подпрограммы для работы с датой и временем Программа
Описание
f u n c t i o n Date : TDateTime;
Возвращает текущую дату
f u n c t i o n D a t e T o S t r ( D : TDateTime) : S t r i n g ;
Преобразует дату в строку символов
function DateTimeToStr (D: T D a t e T i m e ) : S t r i n g ;
Преобразует дату и время в строку символов продолжение &
Смотри подраздел «Преобразование значений даты-времени» раздела «Класс String и преобразова ние строк» в главе 19.
94 Глава 3 • Типы данных
Простые типы
Таблица 3.8 (продолжение) Программа
Описание
function FormatDateTime Преобразует дату и время из (Format: String; Value: TDateTime) : String; параметра Value в строку символов в соответствии со спецификаторами параметра F o r m a t (см. приложение А)
function Now: TDateTime; function Time : TDateTime; function TimeToStr (T: TDateTime) : String;
Возвращает текущую дату и время Возвращает текущее время Преобразует время в строку
Более полный перечень функций для работы с датой-временем можно найти в приложении А. Поскольку тип TDateTime совместим с форматом вещественных чисел, можно без труда определить дату, отстоящую от заданной на сколько-то дней вперед или назад, — для этого достаточно, соответственно, прибавить к заданной дате или отнять от нее нужное целое число. Например, следующий оператор помес тит в метку l b O u t p u t дату, соответствующую текущей дате, плюс 3 недели: lbOutput.Caption := DateToStr(Date + 21); Чуть сложнее с исчислением времени. Например, чтобы добавить к текущему времени полтора часа, можно использовать любое из выражений: Time + StrToTime('1:30') Time + 1.5/24 Замечу, что специализированные функции IncXXXX (см. приложение А) меня ют указанные дату и время на нужное количество лет, месяцев, недель, дней, часов, минут, секунд, миллисекунд: lbOutput.Caption := DateToStr(IncDay(Date, 21)); IncHour(IncMinute(Time, 30), 1 ) ;
Особенности реализации простых типов Все сказанное относительно простых типов действительно только в режиме со здания VCL-приложений, который введен для совместимости с предыдущими версиями Windows. При разработке WinForms-приложений становится значимым тот факт, что программисту приходится иметь дело не с имитацией VCL, а с ре альными типами CTS, которые, собственно, и интерпретируются CLR. Простые типы VCL проецируются на простые типы CTS. Типы Byte, Boolean, Char, I n t e g e r , I n t 6 4 , S i n g l e , Double и TDateTime реализуются в виде запи сей, исполняющих интерфейсы IComparable, I F o r m a t t a b l e и I C o n v e r t i b l e . Эти интерфейсы предназначены для сравнения, форматирования и преобразова ния значений соответственно. Все другие простые типы ( S h o r t i n t , Word, R e a l и т. п.) реализуются на основе перечисленных типов как их подмножества.
95
Тот факт, что большинство простых типов VCL реализуется в виде интерфейсных записей, гарантирует, что эти типы имеют методы соответствующих интерфейсов, что существенно расширяет их возможности в части сравнения, форматирования и преобразования данных. Далее описываются соответствующие интерфейсы (под робнее об интерфейсах см. главу 6): type IComparable = interface function CompareTo(const Objl, Obj2: TObject): Integer; end; Этот простой интерфейс объявляет единственную функцию, которая принимает два объекта и, сравнивая их, выдает отрицательное число, ноль или положитель ное число в зависимости от того, меньше, равен или больше первый объект отно сительно второго. type IFormattable = interface function ToString(const Format: String; const FormatProvider: IFormatProvider): String; end; IFormatProvider = interface function GetFormat(const FormatType: &Type): TObject; end; Этот интерфейс объявляет единственную функцию T o S t r i n g , осуществляющую форматное преобразование значения простого типа в строку. Для каждого про стого типа объявляется также функция P a r s e , которая принимает строковый эквивалент значения и возвращает значение простого типа, например: procedure TForml.ButtonlClick(Sender: TObject); var L: Double; begin L := L.Parse('3,141593'); Caption := L.ToString('N') end; В результате в заголовке формы появится строка 3,13. Более подробно о преобразовании значения простого типа в строку и обратно методами CTS см. раздел «Класс String и преобразование строк» в главе 19. Наконец, интерфейс I C o n v e r t i b l e объявляет множество функций, осуществ ляющих преобразование текущего значения простого типа к соответствующему типу CTS, поддерживаемому CLR. Такими типами являются B o o l e a n , SByte, Byte, I n t l 6 , U I n t l 6 , I n t 3 2 , UInt32, I n t 6 4 , UInt64, S i n g l e , Double, Decimal, DateTime, Char и S t r i n g .
96
Глава 3 • Типы данных
Структурированные типы Любой из структурированных типов (а в Delphi их четыре: массивы, записи, мно жества и файлы) характеризуется множественностью образующих этот тип эле ментов. Каждый элемент, в свою очередь, может принадлежать структурирован ному типу, что позволяет говорить о возможной вложенности типов. В Delphi допускается произвольная глубина вложенности типов, однако суммарная дли на любого из них во внутреннем представлении не должна превышать 2 Гбайт. В целях совместимости со стандартным языком Pascal в Delphi разрешается перед описанием структурированного типа ставить зарезервированное слово packed, предписывающее компилятору по возможности экономить память, отводимую под объекты структурированного типа.
Массивы Массивы в Delphi во многом схожи с аналогичными типами данных в других язы ках программирования. Отличительная особенность массивов заключается в том, что все их компоненты — суть данные одного типа (возможно, структурирован ного). Можно легко упорядочить эти компоненты и обеспечить доступ к любому из них простым указанием его порядкового номера, например: type digit = array [0..9] of Char; matrix = array [byte] of Single; var m: matrix; d: digit; i: integer; begin ... m[17]
:= o r d ( d [ i - l ] ) / 1 0 ;
Структурированные типы
97
Обычно в качестве индексного типа используется тип-диапазон, в котором зада ются границы изменения индексов. Так как тип <тип>, идущий в описании мас сива за словом of, - любой тип Delphi, то он может быть, в частности, и другим массивом, например: type mat = a r r a y [ 0 . . 5 ] of a r r a y [ - 2 . . 2] of a r r a y [Char] of B y t e ; Такую запись можно заменить более компактной: type mat = array [0..5, - 2 . . 2, char] of Byte; Глубина вложенности структурированных типов вообще, а следовательно, и мас сивов — произвольная, поэтому количество элементов в списке индексных ти пов (размерность массива) не ограничено, однако суммарная длина внутреннего представления любого массива не может быть больше 2 Гбайт. В памяти ПК эле менты массива следуют друг за другом так, что при переходе от младших адре сов к старшим наиболее быстро меняется самый правый индекс массива. Пусть, например, var
а: array begin a [ l , l ] := a[2,l] := a [ l , 2 ] := a[2,2] :=
[ 11 ..
.2,
1 . .2] o f B y t e ;
1; 2; 3; 4;
end.
Тогда в памяти последовательно друг за другом будут расположены байты со значениями 1, 3, 2, 4. В Delphi можно одним оператором присваивания передать все элементы одного массива другому массиву того же типа, например: var
Описание типа массива задается следующим образом:
a, b: array [1..5] of Single; begin
<имя типа> = array [ < с п . и н д . т и п о в > ] of <тип>; Здесь <имя типа> — правильный идентификатор; array, of — зарезервирован ные слова {массив, из); < с п . и н д . т и п о в > — список из одного или нескольких индексных типов, разделенных запятыми; квадратные скобки, обрамляющие спи сок, — требование синтаксиса; <тип> — любой тип Delphi. В качестве индексных типов в Delphi можно использовать любые порядковые типы, имеющие мощность не более 2 Гбайт (то есть кроме LongWord и I n t 6 4 ) . Определить переменную как массив можно и непосредственно при описании этой переменной, без предварительного описания типа массива, например: var a, b: array [1..10] of Real;
a := b; end. После этого присваивания все пять элементов массива А получат те же значе ния, что и в массиве В. Замечу, что следующее объявление создаст разные типы массивов: var
a: array [1..5] of Single; b: array [1..5] of Single;
98
Глава 3 • Типы данных
Поэтому представленный далее оператор вызовет сообщение об ошибке: а : = 'Ь; Над массивами не определены операции отношения. Нельзя, например, записать if a = 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: a r r a y of I n t e g e r ; В: a r r a y of a r r a y of Char; C: a r r a y of a r r a y of a r r a y of Real; В этом примере динамический массив А имеет одно измерение, массив В — два, массив С — три измерения. Распределение памяти и указание границ индексов по каждому измерению динамических массивов осуществляется в ходе выполнения программы путем инициализации массива с помощью процедуры S e t L e n g t h . В хо де выполнения следующего оператора одномерный динамический массив А будет инициализирован, то есть получит память, достаточную для размещения трех це лочисленных значений: SetLength(А,3); Нижняя граница индексов по любому измерению динамического массива всегда равна 0, поэтому верхней границей индексов для А является 2. Фактически идентификатор динамического массива ссылается на указатель (см. раздел «Указатели и динамическая память»), содержащий адрес первого байта памяти, выделенной для размещения массива. Поэтому для освобождения этой памяти достаточно присвоить идентификатору значение NIL (другим способом является использование процедуры F i n a l i z e ) :
Структурированные типы
99
var А, В: array of Integer; begin //' Распределяем память: SetLength(A,10); SetLength(В,20); // Используем массивы: // Освобождаем память: А := NIL; Finalize(В); end; При изменении длины уже инициализированного динамического массива по ка кому-либо его измерению сначала резервируется нужная для размещения ново го массива память, затем элементы старого массива переносятся в новый, после чего освобождается память, выделенная прежнему массиву. Чтобы сократить до полнительные затраты времени, связанные с изменением границ большого дина мического массива, следует сразу создать массив максимальной длины. В многомерных массивах сначала устанавливается длина его первого измерения, затем второго, третьего и т. д. Например: var A: array of array of Integer; // Двухмерный динамический массив begin // Устанавливаем длину первого измерения (количество столбцов): SetLength(А,3); // Задаем длину каждого столбца: SetLength(А[0],3); SetLength(А[1],3); SetLength(А[2],3); ... end; Обратите внимание: в отличие от обычных массивов стандартного языка Pascal (и Delphi), динамические массивы могут иметь разную длину по второму и сле дующим измерениям. В предыдущем примере определен квадратный массив 3x3. Однако ничто не мешает нам создать, например, треугольный массив: SetLength(А,3); // Задаем длину каждого столбца: SetLength(А[0],3); SetLength(А[1],4); SetLength(А[2],5); В многомерных динамических массивах каждый элемент любого из ЛМ измере ний (N— количество измерений) представляет собой динамический массив и, следовательно, нуждается в инициализации. Вот как, например, можно ини циализировать вещественный кубический массив 3x3x3:
100
Глава 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;
Как и в массиве, значения переменных типа записи можно присваивать другим переменным того же типа, например: а := Ь; К каждому из компонентов записи можно получить доступ, если использовать составное имя, то есть указать и м я переменной, затем точку и и м я поля: a . d a y : = 27; b . y e a r : = 2002; Д л я вложенных полей приходится продолжать уточнения:
end;
Записи Запись — это структура данных, состоящая из фиксированного количества ком понентов, называемых полями записи. В отличие от массива, компоненты ( п о л я ) записи могут быть разного типа. Чтобы можно было ссылаться на тот или иной компонент записи, поля именуются. Структура объявления типа записи такова: <имя типа> = r e c o r d < с п . п о л е й > [ < с п . с в о й с т в > ] [ < с п . м е т о д о в > ]
end;
Здесь <имя т и п а > — правильный идентификатор; r e c o r d , e n d — зарезервиро ванные слова {запись, конец); < с п . п о л е й > — список полей; представляет собой последовательность разделов записи, между которыми ставится точка с запятой; < с п . с в о й с т в > и < с п . м е т о д о в > — необязательные списки свойств и методов (см. далее).
ПРИМЕЧАНИЕ
:
101
:
Во многих других языках программирования (в С#, например, — см. приложение Б) аналогом записи является структура (structure). Каждый раздел записи состоит из одного или нескольких идентификаторов по лей, отделенных друг от друга запятыми. За идентификатором (идентификато рами) ставится двоеточие и описание типа поля (полей), например:
type BirthDay = record Day,Month : Byte; Year : Word end; var с : record Name : String; Bd : BirthDay end; begin if c.Bd.Year = 1989 then ... end. Чтобы упростить доступ к полям записи, используется оператор присоединения w i t h : w i t h <переменная> do < о п е р а т о р > ; Здесь w i t h , do — зарезервированные слова (с, делать); < п е р е м е н н а я > — и м я переменной типа записи, за которым, возможно, следует список вложенных по лей; < о п е р а т о р > — любой оператор Delphi. Например: с . B d . M o n t h := 9; Этот оператор эквивалентен любому из трех таких операторов: w i t h c.Bd do Month := 9;
type BirthDay = record Day, Month : Byte; Year : Word end; var a,b : Birthday;
w i t h с do w i t h Bd do Month := 9;
В этом примере тип B i r t h D a y {деньрождения) есть запись с полями Day, M o n t h и Y e a r {день, месяц и год); переменные а и b содержат записи типа B i r t h D a y .
В Delphi 2005 (8) записи помимо полей могут содержать подпрограммы (мето ды) и свойства. Синтаксис объявления ИНКЯПГ.УЛИПОНЯННКТХ МРТПЯГЖ и тпйстп
w i t h c, Bd do Month := 9; Имена полей должны быть уникальными в пределах той записи, где они объяв лены, однако, если записи содержат поля-записи, то есть вложены одна в дру гую, имена могут повторяться на разных уровнях вложения.
Особенности записей в технологии .NET
1 0 2 Глава 3 • Типы данных очень напоминает синтаксис объявления соответствующих членов класса (см. да лее главу 5). ПРИМЕЧАНИЕ Как и классы, записи с инкапсулированными методами (свойствами) не могут объявляться в раз деле описаний подпрограммы.
Рассмотрим такой пример. Пусть запись TComplex реализует сложение комп лексных чисел. Введем такой тип: type TComplex = record Re, Im: Real; procedure Create(const R, I: Real); procedure Add(const Rel, Iml: Real); function CTS: String; property ComplToStr: String read CTS; end; procedure TComplex.Create(const R, I: Real); begin Re := R; Im := I end; procedure TComplex.Add(const Rel, Iml: Real); // Сложение комплексных чисел begin Re := Re + Rel; Im := Im + Iml end; function TComplex.CTS: String; // Символьное представление комплексных чисел begin Result := FloatToStr(Re); if Im >= 0 then Result := Result + '+'; Result := Result + FloatToStr(Im) +'i' end; А вот как можно этот тип использовать: procedure TfmExample.bbRunClick(Sender: TObject); var A: TComplex; begin A. Created, 2) ; A.AddUO, -3) ;
Структурированные типы
103
IbOutput.Caption := A.ComplToStr; end; Методы и свойства записи могут объявляться только после объявления всех ее полей. Поскольку записи могут содержать методы, они могут исполнять интерфейсы (см. главу 6). В этом случае имена исполняемых в записи интерфейсов указывают ся в круглых скобках сразу после зарезервированного слова record. Вот как, на пример, объявляется запись типа TDateTime (модуль B o r l a n d . D e l p h i . System): type TDateTime = packed record(IFormattable,
IComparable,
IConvertible)
Вообще записи в Delphi 2005 (8) имеют много общего с классами (см. главу 5). В частности, как и классы, записи могут иметь секции, определяющие области видимости членов записи. По умолчанию все члены записи относятся к секции p u b l i c и доступны в любой части программы. Однако некоторые члены (толь ко поля) могут объявляться в секции s t r i c t p r i v a t e . Такие поля доступны только в методах записи и нигде больше. Некоторые методы могут быть статическими. Такие методы объявляются с заре зервированным словом s t a t i c . Доступ к статическим методам возможен без со здания экземпляра записи, то есть без наполнения ее полей, поэтому статичес кие методы не могут обращаться к обычным полям — только к статическим. Статические поля объявляются с зарезервированным словом c l a s s и инициа лизируются (наполняются значениями) специальным статическим конструкто ром без параметров. CLR автоматически обращается к статическому конструк тору при первом обращении к статическому члену записи. Для примера рассмотрим фрагмент объявления типа TDateTime: type TDateTime = packed record(IFormattable, s t r i c t private var
IComparable,
IConvertible)
FValue: System.DateTime; c l a s s var FMinValue, FMaxValue: System.DateTime; public c l a s s constructor C r e a t e ; // Статический конструктор c l a s s function MinValue: TDateTime; s t a t i c ; { 01/01/0100 12:00:00.000 AM } c l a s s function MaxValue: TDateTime; s t a t i c ; { 12/31/9999 11:59:59.999 PM } end; c l a s s constructor TDateTime.Create;
Структурированные типы
1 0 4 Глава 3 • Типы данных begin FMinValue := System.DateTime.Create(100, 1, 1, 0, 0, 0, 0 ) ; FMaxValue := System.DateTime.Create(9999, 12, 31, 23, 59, 59, 999); end; Следует заметить, что расширенные возможности записей введены исключитель но для совместимости с CTS. ПРИМЕЧАНИЕ В отличие от предыдущих версий, в Delphi 2005 (8) записи не могут иметь вариантную часть.
Множества Множества — это наборы логически связанных друг с другом объектов. Харак тер связей между объектами лишь подразумевается программистом и никак не контролируется Delphi. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256 (множество, не содержащее элементов, называ ется пустьш). Именно непостоянством количества своих элементов множества отличаются от массивов и записей. Два множества считаются эквивалентными тогда и только тогда, когда все их эле менты одинаковы, причем порядок следования элементов в множестве безразличен. Если все элементы одного множества входят также и в другое, говорят о включении первого множества во второе. Пустое множество включается в любое другое. Пример определения и задания множеств: type digitChar = set of ' 0'..'9'; digit = set of 0..9; var sl,s2,s3 : digitChar; s4,s5,s6 : digit;begin
s2
= t'l', '2', = t'3', '2',
s3
=
['2',
'3']
s4
=
[0..3,
6];
si
s5
=
[4,
s6
=
[3..9];
Здесь <имя типа> — правильный идентификатор; s e t , of — зарезервирован ные слова (множество, из); <базовый тип> — базовый тип элементов множе ства, в качестве которого может использоваться любой порядковый тип, кроме Word, I n t e g e r , L o n g l n t , I n t 6 3 . Для задания множества используется так называемый конструктор множества: список спецификаций элементов множества, отделенных друг от друга запяты ми; список обрамляется квадратными скобками. Спецификациями элементов могут быть константы или выражения базового типа, а также тип-диапазон того же базового типа. Над множествами определены перечисленные далее операции: •
Пересечение множеств (*). Результат содержит элементы, общие для обоих мно жеств; например, S4*S6 содержит [ 3 ] , S4*S5 — пустое множество (см. ранее).
•
Объединение множеств (+). Результат содержит элементы первого множества, дополненные недостающими элементами из второго множества:
•
Описание типа множества имеет вид <имя типа> = s e t of <базовый тип>;
о
S5+S6 содержит [ 3 , 4 , 5 , б , 7 , 8 , 9 ] .
Разность множеств (-). Результат содержит элементы из первого множества, которые не принадлежат второму: S4-S5 содержит [ 0 , 1 , 2 , 3 , 6 ] .
•
Проверка эквивалентности (=). Возвращает True, если оба множества экви валентны.
•
Проверка неэквивалентности ( о ) . Возвращает True, если оба множества не эквивалентны.
•
Проверка вхождения (<=). Возвращает True, если первое множество вклю чено во второе.
•
Проверка вхождения (>=). Возвращает True, если второе множество включе но в первое.
•
Проверка принадлежности ( i n ) . В этой бинарной операции первый элемент — выражение, а второй — множество одного и того же типа. Возвращает True, если выражение имеет значение, принадлежащее множеству: о
3 in s6 — возвращает True;
о
2*2
in
si — возвращает F a l s e .
Дополнительно к этим операциям можно использовать две процедуры: •
I n c l u d e — включает новый элемент в множество. Обращение к процедуре: Include(S,I)
end. В этом примере множества SI и S2 эквивалентны, а множество S3 включено в S2, но не эквивалентно ему.
S4 + S5 содержит [ 0 , 1 , 2 , 3 , 4 , 5 , 6 ] ;
о
'3' ] ;
5];
о
о S6-S5 содержит [ 3 , 6, 7, 8, 9 ] ;
• l' ];
•
105
Здесь S — множество, состоящее из элементов базового типа T S e t B a s e ; I — эле мент типа TSetBase, который необходимо включить в множество. •
E x c l u d e — исключает элемент из множества. Обращение: Exclude(S,I)
1 0 6 Глава 3 • Типы данных
Структурированные типы
Параметры обращения — такие же, как у процедуры I n c l u d e . В отличие от операций + и -, реализующих аналогичные действия над двумя множествами, процедуры оптимизированы для работы с одиночными элемента ми множеств и поэтому отличаются высокой скоростью выполнения. В следующем примере, иллюстрирующем приемы работы с множествами, реали зован алгоритм выделения из первой сотни натуральных чисел всех простых чи сел1. В его основе лежит прием, известный под названием «решето Эратосфена». В соответствии с этим алгоритмом вначале формируется множество B e g i n S e t , состоящее из всех целых чисел в диапазоне от 2 до N. В множество P r i m e r S e t (оно будет содержать искомые простые числа) помещается 1. Затем циклически повторяются следующие действия. 1. Взять из множества B e g i n S e t первое входящее в него число Next и помес тить его в P r i m e r S e t . 2. Удалить из множества B e g i n S e t число Next и все другие числа, кратные ему, то есть 2*Next, 3*Next и т. д. Цикл повторяется до тех пор, пока множество B e g i n S e t не станет пустым. Эту программу нельзя использовать для произвольного значения N, так как в лю бом множестве не может быть больше 256 элементов.
удаления
из
исходного
множества
непростых
чисел:
while nl <= N do begin Exclude(BeginSet, nl); nl
:=
nl
+
Next //
Include(PrimerSet, //
Получаем
//
число,
следующее не
Следующее
//
end;
Конец
кратное
цикла
удаления
next);
простое,
вычеркнутое
из
которое исходного
есть
первое
множества
repeat inc (Next) until (Next in BeginSet) or (Next > N) end; //
Выводим
//
Конец
основного
цикла
результат:
for i := 2 to N do if i in PrimerSet then Write (i); Readln end. Окно программы показано на рис. 3.3.
program PrimerSet_; {$APPTYPE
Цикл
//
107
CONSOLE}
uses SysUtils; Выделение
//
всех
простых
чисел
из
первых
N
целых
const N = 255;
//
Количество
элементов
исходного
множе
ства
type SetOfNumber = set of 1..N; var nl,Next,i: Word; BeginSet,
// //
Вспомогательные переменные Исходное множество
PrimerSet: SetOfNumber; // Множество простых чисел begin BeginSet := [ 2 . . N ] ; PrimerSet :» [1]; Next := 2; while BeginSet о []
// Создаем исходное множество // Первое простое число // Следующее простое число do // Начало основного цикла
begin nl
:= Next;
// //
nl - число, кратное очередному простому
Рис. 3.3. Окно программы PrimerSet Перед тем как закончить рассмотрение множеств, полезно провести небольшой эксперимент. Измените описание типа SetOfNumber следующим образом: type
SetOfNumber = set of 1..1; Еще раз запустите программу из предыдущего примера. На экран будет выведено
(Next)
Простыми называются целые числа, которые не делятся без остатка на любые другие целые числа, кроме 1 и самого себя. К простым относятся числа 1, 2, 3, 5, 7, 11, 13 и т. д.
1, 3, 5, 7 Хотя множества B e g i n S e t и P r i m e r S e t состоят теперь из одного элемента, программа сумела поместить в них не менее семи!
1 0 8 Глава 3 • Типы данных
Строки
109
Секрет этого прост: внутреннее устройство множества таково, что каждому его элементу ставится в соответствие один двоичный разряд (один бит); если эле мент включен в множество, соответствующий разряд имеет значение 1, в про тивном случае — 0. В то же время минимальной единицей памяти является 1 байт, содержащий 8 бит, поэтому компилятор выделил множествам по 1 байту, и в ре зультате мощность каждого из них стала равна 8 элементам. Максимальная мощ ность множества — 256 элементов. Для таких множеств компилятор выделяет по 16 смежных байтов.
чие от символов коротких строк, символы длинной строки кодируются кодиров кой Unicode и занимают в памяти по 2 байта каждый.
И еще один эксперимент: измените диапазон базового типа на 1..256. Хотя мощ ность этого типа составляет 256 элементов, при попытке компиляции програм мы компилятор сообщит об ошибке (множества могут иметь не более 256 эле ментов): Sets may have at most 256 elements
var ssS: String[250]; ssMax: S h o r t S t r i n g ; s t S : String; swS: W i d e S t r i n g ;
Причина проста — нумерация элементов множества начинается с нуля, независи мо от объявленной в программе нижней границы. Компилятор разрешает исполь зовать в качестве базового типа целочисленный тип-диапазон с минимальной гра ницей 0 и максимальной 255 или любой перечисленный тип не более чем с 256 элементами (максимальная мощность перечисленного типа — 65 536 элементов).
Несмотря на разницу во внутреннем представлении, короткие строки S h o r t S t r i n g и длинные строки S t r i n g ( W i d e S t r i n g ) имеют для программиста оди наковые свойства. 1 Текущую длину строки можно получить с помощью функции L e n g t h . Напри мер, следующий оператор уничтожает все ведомые (хвостовые) пробелы:
Строки Для обработки текстов в Delphi используются следующие типы: •
короткая строка S h o r t S t r i n g или S t r i n g [N], где N < 255;
• длинная строка S t r i n g ; •
широкая строка W i d e S t r i n g ;
•
нуль-терминальная строка PChar.
Общим для этих типов является то, что каждая строка трактуется как одномер ный массив символов, количество элементов в котором может меняться в рабо тающей программе: для S t r i n g [N] длина строки меняется от 0 до N, для S t r i n g , W i d e S t r i n g и PChar — от 0 до 2 Гбайт. В стандартном языке Pascal используются только короткие строки S t r i n g [ N ] . В памяти такой строке выделяется N + 1 байт, первый байт содержит текущую длину строки, а сами символы располагаются начиная со 2-го по счету байта. Поскольку для длины строки в этом случае отводится 1 байт, максимальная длина короткой строки не может превышать 255 символов. Для объявления короткой строки максимальной длины предназначен стандартный тип S h o r t S t r i n g (эк вивалент S t r i n g [ 2 5 5 ] ) . В Windows широко используются нуль-терминальные строки, представляющие собой цепочки символов, ограниченные символом #0. Максимальная длина такой строки лимитируется только доступной памятью и может быть очень большой. В Delphi введен тип S t r i n g , сочетающий в себе достоинства обоих типов. При работе с этим типом память выделяется по мере надобности (динамически) и ог раничена доступной памятью, имеющейся в распоряжении программы. В отли-
Для совместимости с компонентами, основывающимися на технологии OLE, в 32-разрядных версиях Delphi введены также широкие строки, объявляемые стан дартным типом W i d e S t r i n g . В Delphi 2005 (8) по своим свойствам они иден тичны длинным строкам S t r i n g . Примеры объявлений строковых типов: // Короткая строка // Короткая строка / / Длинная строка // Широкая строка
длиной до длиной до
w h i l e ( L e n g t h ( s t S ) <> 0) and ( s t S [ L e n g t h ( s t S ) ] = ' SetLentgh(stS, Length(stS) - 1);
250 255
символов символов
') do
В этом примере стандартная процедура S e t L e n g t h устанавливает новую длину строки. К строкам можно применять операцию сцепления (+), например: stS := 'а' + 'b'; // stS содержит "ab" stS := stS + 'с'; // stS содержит "abc"
Если длина строки превысит максимально допустимую длину N короткой стро ки, то «лишние» символы отбрасываются. Следующая программа, например, вы ведет символ «1»: var
ssS: String[l]; begin ssS := 4 2 3 ' ; Writeln(ssS); end; Операции отношения =, о, >, <, >=, <= выполняются над двумя строками по символьно слева направо с учетом внутренней кодировки символов. Если одна строка меньше другой по длине, недостающие символы короткой строки заменя ются значением #0. Следующие операции отношения дадут значение True: " " <
' . '
'А'
Ч '
>
Длина короткой строки определяется содержимым 1-го байта, который в Delphi трактуется как тип Shortlnt: при последовательном наращивании строки ее длина, возвращаемая функцией Length, ме няется от 0 до 128, затем -127, -126 и т. д.
110
Строки
Глава 3 • Типы данных
'Object' < ' Delphi' 'Пас' > 'Pascal' Все остальные действия над строками и символами реализуются с помощью стандартных процедур и функций, перечисленных в табл. 3.9, а также в прило жении А.
Подпрограмма
Описание
function StrToDate (St: String): TDateTime;
Преобразует символы строки St в дату. Строка должна содержать два или три числа, разделенных правильным для Windows разделителем даты (в русифицированной версии таким разделителем является точка). Первое число— правильный день, второе — правильный месяц. Если указано третье число, оно должно задавать год в формате XX или х х х х . Если символы года отсутствуют, дата дополняется текущим годом. Например, обращение D a t e T o S t r ( S t r T o D a t e ( ' 2 8 . 0 6 ' ) ) даостроку ' 2 8 . 0 6 . 0 4 ' (см. далее пояснения) Преобразует символы строки St в дату и время. Строка должна содержать правильную дату (см. описание функции S t r T o D a t e ) и правильное время (см. описание функции S t r T o T i m e ) , разделенные пробелом, например: StrToDateTime('28.06 18:23')
Т а б л и ц а 3.9. Подпрограммы для работы со строками Подпрограмма
Описание
function AnsiLowerCase (const S: String) : String;
Возвращает исходную строку S, в которой все прописные буквы заменены строчными в соответствии с кодировкой Unicode (то есть с учетом кириллицы)
function AnsiUpperCase (const S: String) : String;
Возвращает исходную строку S, в которой все строчные буквы заменены прописными в соответствии с кодировкой Unicode
function Concat (SI [, S2, . . . , SN] : String) String;
Возвращает строку, представляющую собой сцепление строк-параметров S I , S2, ..., SN
f u n c t i o n Copy ( S t : S t r i n g ; I n d e x , Count: I n t e g e r ) : String; procedure D e l e t e ( S t : S t r i n g ; Index, Count: I n t e g e r ) ;
Копирует C o u n t символов из строки S t , начиная с символа с номером I n d e x
Procedure I n s e r t ( S u b S t : S t r i n g ; St, Index: I n t e g e r ) ;
Вставляет подстроку SubSt в строку S t , начиная с символа с номером I n d e x
function Length (St: String) : Integer;
Возвращает текущую длину строки St
function Lowercase ( c o n s t S: S t r i n g ) : S t r i n g ;
Возвращает исходную строку S, в которой все латинские прописные буквы заменены строчными
f u n c t i o n Pos (SubSt, S t : S t r i n g ) : I n t e g e r ;
Отыскивает в строке St первое вхождение подстроки SubSt и возвращает номер позиции, с которой она начинается. Если подстрока не найдена, возвращается ноль
procedure S e t L e n g t h ( S t : S t r i n g ; NewLength: Integer);
Устанавливает новую (меньшую) длину N e w L e n g t h строки S t . Если N e w L e n g t h больше текущей длины строки, обращение к S e t L e n g t h игнорируется
f u n c t i o n S t r i n g O f C h a r (Ch: C h a r ; Count: I n t e g e r ) : String;
Создает строку, состоящую из C o u n t раз повторенного символа Ch
function Uppercase ( c o n s t S: S t r i n g ) : S t r i n g ;
Возвращает исходную строку S, в которой' все строчные латинские буквы заменены прописными
Удаляет C o u n t символов из строки S t , начиная с символа с номером I n d e x
Подпрограммы преобразования строк в другие типы function StrToCurr (St: String): Currency;
Преобразует символы строки St в целое число типа C u r r e n c y . Строка не должна содержать ведущих или ведомых пробелов
111
function StrToDateTime (St: String): TDateTime;
Преобразует символы строки St в вещественное число. Строка не должна содержать ведущих или ведомых пробелов Преобразует символы строки St в целое число. function StrToInt Строка не должна содержать ведущих или ведомых (St: String) : Integer; пробелов function StrToIntDef(St: String Преобразует символы строки St в целое число. Если строка не содержит правильного Default: Integer) : Integer; представления целого числа, возвращается значение D e f a u l t Преобразует символы строки St в целое число function StrToIntRange (St: String;Min, Max: Longlnt) и возбуждает исключение E R a n g e E r r o r , Lomglnt; если число выходит из заданного диапазона Min...Мах Преобразует символы строки St во время. function StrToTime Строка должна содержать два или три числа, (St: String): TDateTime; разделенных правильным для Windows разделителем времени (для русифицированной версии таким разделителем является двоеточие). Числа задают часы, минуты и, возможно, секунды. За последним числом через пробел могут следовать символы «am» или «рт», указывающие на 12-часовой формат времени function StrToFloat (St: String):Extended;
procedure Val (St: String; var X; Code: Inteaer);
Преобразует строку символов St во внутреннее представление целой или вещественной переменной X, которое определяется типом этой переменной. Параметр Code содержит ноль, если преобразование прошло успешно, и тогда в X помещается результат преобразования, в противном случае он содержит номер позиции в строке S t , где обнаружен ошибочный символ, и в этом случае содержимое X не меняется. В строке St могут быть ведущие и/или ведомые пробелы. Если St содержит символьное представление вещественного числа, разделителем целой и дробной частей должна быть точка независимо от того, каким символом этот разделитель указан в Windows продолжение &
112
Глава 3 • Типы данных
Строки
Т а б л и ц а 3.9 (продолжение) Подпрограмма
Описание
Подпрограммы обратного преобразования
function DateTimeToStr (Value: TDateTime): String; procedure DateTimeToString (varSt: String; Format: String; Value: TDataTime); function DateToStr (Value: TDateTime): String; function FloatToStr (Value: Extended): String; function FloatToStrF (Value: Extended; Format: TFloatFormat; Precision, Digits: Integer) : String; function Format (const Format: String; const Args: array of const) : String; function FormatDateTime (Format: String; Value: TDateTime): String;
Преобразует дату и время из параметра V a l u e в строку символов Преобразует дату и время из параметра V a l u e в строку St в соответствии со спецификаторами параметра F o r m a t (см. приложение А) Преобразует дату из параметра V a l u e в строку символов Преобразует вещественное значение V a l u e в строку символов Преобразует вещественное значение V a l u e в строку символов с учетом параметра F o r m a t , а также параметров P r e c i s i o n и D i g i t s (см. приложение А) Преобразует произвольное количество аргументов открытого массива A r g s в строку в соответствии с параметром F o r m a t (см. приложение А) Преобразует дату и время из параметра V a l u e в строку символов в соответствии со спецификаторами параметра F o r m a t (см. приложение А)
Преобразует вещественное значение V a l u e function FormatFloat(Format: String;Value: Extended): String, в строку символов с учетом спецификаторов формата F o r m a t (см. приложение А)
function IntToHex(Value: Integer;Digits:Integer): String;
Преобразует целое число V a l u e в строку символьного представления шестнадцатеричного формата: D i g i t s — минимальное количество символов в строке
function IntToStr(Value: Integer):String; procedure Str(X [: Width [:Decimals]];var St: String);
Преобразует целое значение V a l u e в строку символов Преобразует число X любого вещественного или целого типа в строку символов S t ; параметры W i d t h и D e c i m a l s , если они присутствуют, задают формат преобразования: W i d t h определяет общую ширину поля, выделенного под соответствующее символьное представление вещественного или целого числа X, D e c i m a l s — количество символов в дробной части (этот параметр имеет смысл только в том случае, если X — вещественное число) Преобразует время из параметра V a l u e в строку символов
function TimeToStr (Value: TDateTime): String;
При преобразовании с помощью функций StrToXXX строка может содержать недопустимые для типа XXX символы. В этом случае возбуждается исключитель ная ситуация E C o n v e r t E r r o r . При обратных преобразованиях XXXToStr фор мат получаемой строки зависит от установленных в Windows системных пара метров: разделителей даты, времени, целой и дробной частей вещественного числа.
113
Используемый в некоторых процедурах и функциях строковый параметр F o r m a t может содержать символы-спецификаторы, перечисленные в приложении А. За мечу, что любые другие символы, указанные в строке Format, а также заключен ные в апострофы или кавычки специальные символы-спецификаторы помеща ются в выходную строку без преобразования, поэтому спецификаторы ' h ч а с п мин' дадут строку 19 ч а с 45 мин, а ' п ч а с " п " мин' — 19час п мин. При форматном преобразовании времени-даты или других типов в строку и об ратно могут пригодиться системные переменные, приведенные в приложении А. Для форматного преобразования вещественных чисел предназначены функции F l o a t T o S t r F и F o r m a t F l o a t . Первая использует значение F o r m a t перечис ленного типа T F l o a t F o r m a t и два дополнительных параметра— P r e c i s i o n и D i g i t s . Правила применения параметров функции F l o a t T o S t r F и специфи каторы параметра F o r m a t в функции F o r m a t F l o a t приведены в приложении А. Как и в случае даты/времени, любые другие символы строки Format, а также заключенные в апострофы или кавычки специальные символы-спецификаторы помещаются в выходную строку без преобразования: для V a l u e = л*1000 спе цификаторы ' # , р у б л я ' дадут строку 3 142 рубля. Мощная функция преобразования F o r m a t перешла в Delphi из языка Си (в нем она называется p r i n t f ) . Она позволяет преобразовать сразу несколько элемен тов открытого массива аргументов в соответствии с указаниями форматирующей строки. Рассмотрим, например, следующее выражение: F o r m a t ( ' С т р о к а "%s" содержит %d с и м в о л о в ' , [ ' П а с к а л ь ' , 7 ] )
Это выражение даст такой результат: Строка " П а с к а л ь " содержит 7 с и м в о л о в .
Элементами массива аргументов могут быть константы и/или переменные цело го и вещественного типа, строки и указатели. Форматирующая строка — это произвольная строка, в которую в любом месте можно вставить форматирующий спецификатор. Количество форматирующих спецификаторов должно быть не больше количества элементов массива аргу ментов — в противном случае возникнет исключительная ситуация. Каждому элементу массива аргументов по порядку их перечисления в конструкторе мас сива функция ставит в соответствие форматирующий спецификатор по порядку его следования в форматирующей строке: первому аргументу — первый специ фикатор, второму — второй и т. д. Если количество спецификаторов меньше ко личества аргументов, «лишние» аргументы игнорируются. Форматирующий спецификатор всегда начинается символом процента и в общем случае имеет такую структуру (в квадратных скобках указываются необязатель ные элементы): "%"
[index
":"]
["-"]
[width]
["."
prec]
type
Здесь i n d e x " : " — индекс открытого массива (с помощью этого элемента можно явно указать аргумент, который будет обрабатывать спецификатор); " - " указыва ет на необходимость прижать отформатированный спецификатором текст к ле вой границе отведенного для него пространства; w i d t h — число, определяющее
114
Глава 3 • Типы данных
количество символов для обработанного спецификатором текста; если это число меньше требуемого, этот элемент спецификатора игнорируется, если больше — дополняется справа (если есть элемент " - " ) или слева (если он отсутствует) нуж ным количеством пробелов; " . " p r e с — точность представления целых и веще ственных типов; t y p e — символ, определяющий тип форматирования (табл. 3.10). Т а б л и ц а 3 . 1 0 . Форматирующие спецификаторы для функции Format Спецификатор
Описание
d
Целое десятичное число. Если задан параметр р г е с , то символьное представление должно содержать по меньшей мере р г е с десятичных цифр: если символьное представление содержит меньше цифр, оно дополняется слева символами 0, если больше— параметр р г е с игнорируется. Если аргумент не является целым числом, возникает исключительная ситуация
и
Беззнаковое целое число. Используется подобно типу d, но аргумент должен быть положительным числом. Если аргумент —отрицательное целое, результат форматирования непредсказуем
е
Вещественное число в экспоненциальном представлении: символьное представление имеет вид - d , dddddddddddE+ddd. Если число положительное, ведущий минус опускается, а если его модуль меньше 1, знак плюс меняется на минус. Всегда содержит одну цифру целой части, по меньшей мере одну цифру дробной части и не меньше трех цифр десятичного порядка. Если задан параметр точности, он определяет общее количество цифр до символа Е, но не меньше двух: если параметр точности содержит 1 или 0, он заменяется на 2. Если символьное представление содержит больше символов, чем р г е с , оно округляется по первой отбрасываемой цифре. Умалчиваемое значение параметра точности равно 15
f
Вещественное число в виде - d d d , ddd (фиксированное представление). Параметр точности (по умолчанию 2) указывает количество цифр в дробной части. Если он 0, выводится только целая часть числа
g
Вещественное число в максимально коротком представлении (экспоненциальном или фиксированном). Параметр точности (по умолчанию 15) определяет максимальное количество значащих разрядов. Если число имеет меньшее количество цифр, оно не дополняется до р г е с , а если число не имеет дробной части, оно выводится как целое (без запятой)
п
Соответствует фиксированному представлению, но использует символразделитель тысяч
m
Денежное представление вещественного числа. Подобно типу п, но справа ставится знак денежной единицы
Р
Указатель. Выводит содержимое указателя в виде 8 шестнадцатеричных цифр Аргумент должен быть символом, строкой или строкой с терминальным нулем. Параметр точности, если указан, определяет максимальную длину строки: если строка больше, она усекается, если меньше— параметр точности игнорируется
s
х
Шестнадцатеричное представление целого числа. Параметр точности определяет минимальное количество шестнадцатеричных цифр (если число меньше, оно дополняется ведущими нулями)
Указатели и динамическая память
115
Функция не чувствительна к регистру букв, указывающих тип преобразования. Параметры index, р г е с и w i d t h задаются явно (числами в форматирующей стро ке) или неявно — с помощью символа * (звездочка). В этом случае в качестве параметра берется значение очередного аргумента в списке аргументов (он дол жен быть целым числом). Например, два следующих выражения дадут одинако вый результат: Format (' %*.*f, F o r m a t ( ' %8.2f\
[8, 2, 1 2 3 . 4 5 6 ] ) ; [123.456]);
Несколько слов о строках PChar. Этот тип объявляет указатель на цепочку од нобайтных символов в кодировке ANSI, заканчивающуюся терминальным нулем. Тип явно не соответствует безопасному коду, так как является указателем (см. следующий раздел) и не использует Unicode. С другой стороны, этот тип широ ко применяется в функциях API. Для передачи этим функциям строк PChar и по лучения от них таких строк используются типы I n t P t r , S t r i n g B u i l d e r и Mar s h a l общей системы типов (CTS).
Указатели и динамическая память Как отмечалось во введении, работа с указателями в Delphi версии 2005 (8) су щественно отличается от работы с ними в предыдущих версиях. Даже если вы уже имеете опыт использования ранних версий, настоятельно рекомендую озна комиться с этим разделом.
Динамическая память Динамическая память — это оперативная память ПК, предоставленная програм ме при ее работе. Динамическое размещение данных означает обращение к дина мической памяти непосредственно при работе программы. В отличие от этого статическое размещение осуществляется компилятором Delphi в процессе ком пиляции программы. При динамическом размещении заранее не известны ни тип, ни количество размещаемых данных.
Указатели Оперативная память ПК представляет собой совокупность ячеек для хранения информации — байтов, каждый из которых имеет собственный номер. Эти номе ра называются адресами, они позволяют обращаться к любому байту памяти. Delphi предоставляет в распоряжение программиста гибкое средство управления динамической памятью — так называемые указатели. Указатель — это перемен ная, которая в качестве своего значения содержит адрес байта памяти. С помо щью указателей можно размещать в динамической памяти любой из известных в Delphi типов данных. Операции с указателями порождают небезопасный код, так как содержимое ука зателей может меняться динамически и свела CLR не сможет ппоконтполипо-
Указатели и динамическая память
Глава 3 • Типы данных
116
вать правильность их использования. В связи с этим в Delphi используются за писи и классы, реализующие корректную работу с указателями. Запись (структура) i n t P t r представляет собой указатель, зависимый от плат формы и предназначенный для размещения указателей и дескрипторов, которы ми CLR обменивается с базовой операционной системой. Размер указателя за висит от базовой операционной системы и составляет 32 или 64 бита. Все действия с записями типа I n t P t r реализуются методами класса M a r s h a l , которые осуществляют низкоуровневый обмен между управляемой (принадле жащей CLR) и неуправляемой (принадлежащей операционной системе) памя тью. Большинство методов класса M a r s h a l — классные, то есть обращение к ним возможно без создания объекта класса. Оба класса определены в пространстве имен System. R u n t i m e . I n t e r o p S e r v i c e s , ссылку на которое нужно включить в предложение u s e s . В следующем примере выделяется неуправляемая память для размещения цело го числа. Эта память наполняется, используется и освобождается. Для повторения примера создайте новое VCL-приложение, поместите на фор му метку T L a b e l и кнопку T B u t t o n . Напишите такой обработчик щелчка по кнопке: program Project;
117
Т а б л и ц а 3 . 1 1 . Наиболее важные свойства и методы записи IntPtr Свойство или метод
Описание
property Size: Integer; function Equals (const P t r : O b j e c t ) : B o o l e a n ; function ToInt32 : Integer; function Tolnt64 : Int64; procedure F i n a l i z e ;
Возвращает размер указателя в байтах (4 или 8) Возвращает T r u e , если текущий указатель ссылается на ту же область памяти, что и указатель P t r Преобразует указатель в целое 32-разрядное число Преобразует указатель в целое 64-разрядное число Освобождает связанную с указателем память
Таблица 3 . 1 2 . Наиболее важные члены класса Marshal Член класса Marshal
Описание
SystemDefaultCharSize:
Integer;
function AllocHGlobal (Size: I n t e g e r ) : I n t P t r ;
Возвращает умалчиваемый размер символа (2 для Unicode и 1 для ANSI). Это поле доступно только для чтения Резервирует S i z e байт неуправляемой памяти и связывает ее со структурой I n t P t r
procedure Copy(var Source, Dest; Копирует C o u n t элементов одномерного массива const S t a r t , Count: I n t e g e r ) ; в неуправляемую память или обратно, начиная с индекса S t a r t . Для копирования в неуправляемую память S o u r c e — одномерный массив, D e s t —структура I n t P t r . Для копирования из неуправляемой памяти Source — структура IntPtr, Dest — массив
($APPTYPE CONSOLE}
procedure FreeHGlobal (const Mem: I n t P t r ) ;
Освобождает память, выделенную структуре Mem
uses
function PtrToStringANSI (const Mem: I n t P t r ) : String;
Получает из неуправляемой памяти строку ANSI и перекодирует ее в Unicode
function PtrToStringUni (const Mem: IntPtr)": String;
Получает из неуправляемой памяти строку Unicode
SysUtils, System.Runtime.InteropServices; var Ptr: IntPtr; begin Ptr := Marshal.AllocHGlobal(SizeOf(Integer)) ; // Создаем указатель
try Marshal.Writelnt32(Ptr,
1234567);
// Наполняем его
WriteLn(2 * Marshal.Readlnt32(Ptr)); //
Используем
finally Marshal.FreeHGlobal(Ptr)
// Освобождаем
end; ReadLn end; На экран будет выведено 2569134 (то есть 2 * 1234567). В табл. 3.11 перечислены некоторые наиболее важные свойства и методы записи IntPtr. Некоторые наиболее важные члены класса Marshal представлены в табл. 3.12.
function ReadByte (const Mem: I n t P t r ) : Byte; function Readlntl6 (const Mem: I n t P t r ) : Smalllnt; function Readlnt32 (const Mem: I n t P t r ) : Integer;
Читает из памяти 32-разрядное знаковое число
function Readlnt64 (const Mem: I n t P t r ) : Int64;
Читает из памяти 64-разрядное знаковое число
function SizeOf(T: Integer;
Возвращает размер памяти в байтах, нужный для размещения типа Т
aType):
Читает из памяти один байт Читает из памяти 16-разрядное знаковое число
function StringToHGlobalAnsi (const S: String): I n t P t r ;
Распределяет в памяти строку S и преобразует ее в кодировку ANSI
fubction StringToHGlobalUni (const S: String): I n t P t r ;
Распределяет в памяти строку S
procedure WriteByte (Mem: I n t P t r ; Val: Byte);
Записывает в память 1 байт
procedure Writelntl6 (Mem: I n t P t r ; Val: Smalllnt);
Записывает в память 16-разрядное знаковое целое число продолжение
118
Глава 3 • Типы данных
Таблица
3.12
(продолжение)
Член класса Marshal
Описание
procedure Writelnt32 (Mem: I n t P t r ; Val: I n t e g e r ) ;
Записывает в память 32-разрядное знаковое целое число
procedure Writelnt64 (Mem: I n t P t r ; Val: Int64) ;
Записывает в память 64-разрядное знаковое целое число
Псевдонимы типов
Процедуры и функции
Для любого типа можно объявить сколько угодно псевдонимов, например: type TMylnteger = Integer;
В дальнейшем псевдоним можно использовать так же, как и базовый тип: var Mylnt: TMylnteger; begin Mylnt := 2*Round(pi); end;
Такого рода псевдонимы обычно используются для повышения наглядности кода программы. Однако в Delphi добавлением зарезервированного слова type перед именем базового типа можно объявлять строго типизированные псевдонимы: type TMylntegerType = type Integer;
Процедуры и функции представляют собой относительно самостоятельные фрагменты программы, оформленные особым образом и снабженные именем. Упоминание этого имени в тексте программы называется вызовом процедуры (функции). Отличие функции от процедуры заключается в том, что результа том исполнения операторов, образующих тело функции, всегда является неко торое значение, поэтому обращение к функции можно использовать в соответ ствующих выражениях наряду с переменными и константами. Условимся далее называть процедуру или функцию общим именем «подпрограмма», если толь ко для излагаемого материала указанное различие не имеет значения.
var MylntVar: TMylntegerType;
С точки зрения компилятора, типизированные псевдонимы совместимы с базо вым типом в различного рода выражениях, но фактически они объявляют новый тип данных, поэтому их нельзя использовать в качестве формальных парамет ров обращения к подпрограммам вместо базового типа. Пусть, например, объяв лена такая функция: function MylntFunc(APar: Integer): Integer; begin end;
Тогда следующее обращение к ней будет расценено компилятором как ошибочное: MylntFunc(MylntVar);
Строго типизированные псевдонимы заставляют компилятор вырабатывать ин формацию о типе для этапа прогона программы (Run-Time Type Information, RTTI). Эта информация обычно используется средой Delphi, чтобы обеспечить функционирование разного рода редакторов свойств и программ-экспертов.
Локализация имен Напомню, что вызов подпрограммы осуществляется простым упоминанием име ни процедуры в операторе вызова процедуры или имени функции в выраже нии. В Delphi функцию можно вызывать точно так же, как и процедуру, то есть оез использования возвращаемого ею значения. Для этого следует задейство вать так называемый расширенный синтаксис языка, который вводится уста новкой флажка Extended Syntax на вкладке Compiler окна Options (вызывается командой Project • Options)1 или заданием директивы компилятора {$х+}. Как уже отмечалось, любое имя в программе должно быть обязательно описано перед тем, как оно появится среди исполняемых операторов. Не делается исклю чения и в отношении подпрограмм: каждую свою процедуру и функцию програм мисту необходимо описать в разделе описаний. Описать подпрограмму — значит указать ее заголовок и тело. В заголовке объяв ляются имя подпрограммы и формальные параметры, если они есть. Для функ1
Этот флажок установлен по умолчанию.
120
Глава 4 • Процедуры и функции
Локализация имен
11
ции, кроме того, указывается тип возвращаемого ею результата. За заголовком следует тело подпрограммы, которое, подобно программе, состоит из раздела опи саний и раздела исполняемых операторов. В разделе описаний подпрограммы могут встретиться описания подпрограмм низшего уровня, а в них — описания других подпрограмм и т. д. ПРИМЕЧАНИЕ
Заголовок подпрограммы часто называют прототипом подпрограммы, или ее сигнатурой.
Вот какую иерархию описаний получим, например, для программы, структура которой представлена на рис. 4.1 (для простоты считается, что все подпрограм мы представляют собой процедуры без параметров): procedure А; procedure A1; begin end
{Al};
procedure A2; begin end
{A2};
begin {A} end
{A};
procedure B; procedure Bl;
Сказанное относится не только к именам самих подпрограмм, но и вообще к Л1 бым объявленным в них именам — типам, константам, переменным и меткам. В имена в пределах подпрограммы, в которой они объявлены, должны быть ун кальными и не могут совпадать с именем самой подпрограммы.
При входе в подпрограмму низшего уровня не только становятся доступнь» объявленные в ней имена, но и сохраняется доступ ко всем именам верхнего уро ня. Образно говоря, любая подпрограмма как бы окружена полупрозрачнь» стенками: снаружи подпрограммы мы не видим ее внутренности, но, попав в по программу, можем наблюдать все, что делается снаружи. Так, например, из по программы В21 мы можем вызвать подпрограмму А, использовать имена, объя ленные в основной программе, в подпрограммах в и В2, и даже обратиться к ни Любая подпрограмма может, наконец, вызвать саму себя — такой способ вызо: называется рекурсией. Пусть имеется такое описание: var vi :
begin end
Рис. 4 . 1 . Пример структуры программы
{Bl};
procedure B2; procedure B21; Подпрограмма любого уровня обычно содержит множество имен констант, пере менных, типов и вложенных в нее подпрограмм низшего уровня. Считается, что все имена, описанные внутри подпрограммы, локализуются в ней, то есть они как бы «невидимы» снаружи подпрограммы. Таким образом, для операторов, об ращающихся к подпрограмме, она представляется как «черный ящик», в кото ром реализуется тот или иной алгоритм. Все детали этой реализации скрыты от глаз пользователя подпрограммы и потому недоступны ему. Например, в рассмот ренном ранее примере из основной программы можно обратиться к процедурам А и В, но нельзя вызвать ни одну из вложенных в них процедур А1, А2, В1 и т. д.
procedure A; var V2 : end
{A};
procedure B; var V3 : procedure Bl; var V4 : procedure Bll; var V5;
Из процедуры B l l доступны все пять переменных VI, ..., V5, из процедуры Е доступны переменные VI, ..., V4, из центральной программы — только переме] ная VI.
1 2 2 Глава 4 • Процедуры и функции При взаимодействии подпрограмм одного уровня иерархии вступает в силу ос новное правило Delphi: любая подпрограмма перед ее использованием должна быть описана. Поэтому из подпрограммы в можно вызвать подпрограмму А, но из А вызвать В невозможно, точнее, такая возможность появляется только с примене нием опережающего описания (см. раздел «Рекурсия и опережающее описание»). Продолжая образное сравнение, подпрограмму можно уподобить ящику с непроз рачными стенками и дном, но полупрозрачной крышкой: из подпрограммы можно смотреть только «вверх» и нельзя «вниз», то есть подпрограмме доступны только те объекты верхнего уровня, которые описаны до описания данной подпрограм мы. Эти объекты называются глобальными по отношению к подпрограмме. В Delphi допускается произвольная последовательность описания констант, пе ременных, типов, меток и подпрограмм. Например, раздел v a r описания пере менных может появляться в пределах раздела описаний одной и той же подпрог раммы много раз и перемежаться объявлениями других объектов и подпрограмм. Для Delphi совершенно безразличен порядок следования и количество разделов var, type, c o n s t и l a b e l , но при определении области действия этих описа ний следует помнить, что имена, описанные ниже по тексту программы, недо ступны из ранее описанных подпрограмм, например: v a r VI
Описание подпрограммы
123
Что выведет эта программа на экран? Все что угодно: значение внутренней пере менной i при входе в процедуру Р не определено, хотя одноименная глобальная переменная имеет значение 1. Локальная переменная «закроет» глобальную, и на экран будет выведено произвольное значение, содержащееся в неинициализиро ванной внутренней переменной. Если же убрать из процедуры Р показанное да лее описание, то на экран будет выведено значение глобальной переменной i, то есть 1: var i
:
integer;
Таким образом, одноименные глобальные и локальные переменные — это раз ные переменные. Любое обращение к таким переменным в теле подпрограммы трактуется как обращение к локальным переменным, то есть глобальные пере менные в этом случае попросту недоступны.
Описание подпрограммы Описание подпрограммы состоит из заголовка и тела подпрограммы.
:
procedure S; var V2 :
Заголовок и стандартные директивы Заголовок процедуры имеет вид
end (S); var V3 : Из процедуры S можно обратиться к переменным VI H V 2 , НО нельзя использо вать V3, так как описание этой переменной следует в программе за описанием процедуры S. Локализованные в подпрограмме имена могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя «закрывает» глобальное и делает его недоступным, например: var
i : Integer; procedure P; var i : Integer; begin lbOutput.Caption := IntToStr(i); end {P}; begin i := 1; P end;
procedure <имя>
[(<сп.ф.п.>)];
Заголовок функции: f u n c t i o n <имя> [ (<сп.ф.п.>) ]
: <тип>;
Здесь <имя> — имя подпрограммы (правильный идентификатор); < с п . ф. п. > — список формальных параметров; <тип> — тип возвращаемого функцией резуль тата. Сразу после заголовка подпрограммы может следовать одна из стандартных ди ректив: e x t e r n a l или forward. Эти директивы уточняют действия компилято ра и распространяются на всю подпрограмму, и только на нее, то есть если за подпрограммой следует другая подпрограмма, стандартная директива, указанная после заголовка первой, не распространяется на вторую: • e x t e r n a l — с помощью этой директивы объявляется внешняя подпрограмма; •
forward — используется при опережающем описании (см. раздел «Рекурсия и опережающее описание») для сообщения компилятору того, что описание подпрограммы следует где-то дальше по тексту программы (но в пределах те кущего программного модуля).
Помимо директив e x t e r n a l и forward в Delphi можно задействовать стан дартные директивы, регламентирующие способ передачи параметров через стек и применение регистров для их передачи, — такие директивы используются при работе с ядром Windows (табл. 4.1). Столбец «Порядок» определяет порядок
124
Глава 4 • Процедуры и функции
Описание подпрограммы
размещения параметров в стеке: «слева направо» означает размещение в стеке по порядку описания — сначала первый параметр, затем второй и т. д.; «справа налево» означает размещение с конца перечисления параметров — сначала пос ледний, затем предпоследний и т. д. Столбец «Очистка» определяет, кто будет очищать стек: подпрограмма перед передачей управления в вызывающую про грамму или программа после получения управления. В столбце «Регистры» ука зывается «Да», если для передачи параметров помимо стека используются так же регистры центрального процессора, и «Нет» — в противном случае. Таблица 4 . 1 . Стандартные директивы, регламентирующие способ передачи параметров через стек и использование регистров для их передачи
Рассмотрим такой полезный пример. В Delphi не предусмотрена операция во ведения вещественного числа в произвольную степень1. Тем не менее эту зaдa^ можно решить с использованием стандартных математических функций Ехр и L по следующему алгоритму: XY= e
Порядок
Очистка
Регистры
program P o w e r _ f u n c ;
register pascal cdecl stdcall safecall
Слева направо Слева направо Справа налево Справа налево Справа налево
Подпрограмма Подпрограмма Программа Подпрограмма Подпрограмма
Да Нет Нет Нет Нет
{$APPTYPE CONSOLE} var X, Y: R e a l ;
Параметры Список формальных параметров необязателен и может отсутствовать. Если же он есть, то в нем должны быть перечислены имена формальных параметров и их типы, например: procedure SB(a: Real; b: Integer; с: Char); Как видно из примера, параметры в списке отделяются друг от друга точкой с за пятой. Несколько следующих подряд однотипных параметров можно объединять в подсписки; например, следующие два описания эквивалентны: function F(a: Real; b: Real): Real; function F(a, b: Real): Real; Для операторов тела подпрограммы список формальных параметров являет ся своеобразным расширением раздела описаний: все переменные из этого списка могут использоваться в любых выражениях внутри подпрограммы. Таким способом осуществляется настройка алгоритма подпрограммы на кон кретную задачу. ПРИМЕЧАНИЕ В отличие от процедуры, любая функция имеет внутреннюю переменную R e s u l t того же типа, что и тип функции. Присваивание этой переменной значения трактуется как указание результата, возвращаемого функцией.
(rLn(X
».
Создадим функцию с именем Power и двумя вещественными параметрами А В, которая будет возвращать результат возведения А в степень в. Следующая KOI сольная программа читает два числа, X и Y, затем обращается к функции Powe дважды: сначала возводит первое число X в степень Y, затем X возводится в ст< пень -У:
Директива
ВНИМАНИЕ Для поддержки всех функций API ядра Windows подпрограмма должна компилироваться ссдиди рективой поддержки функций функций СОМ СОМ— —с с директивой директивой ssaaffeecc aa ll ll.. рективои s t dd cc aa ll ll ,, а для поддержки
12
function Power(A, B: Real): Real; { Функция возводит число А в степень В. Поскольку логарифм отрица тельного числа не существует, реализуется проверка значения Аотрицательное значение заменяется положительным, для нулевого, числа результат равен нулю. Кроме того, любое число в нулевой степени дае< единицу.} " begin if А > 0 then Result := Ехр(В * Ln(A)) else if A < 0 then Result := Exp(В * Ln(Abs(A))) else if В = 0 then Result := 1 else Result := 0; end; // Power begin Write('X = ' ) ; Readln(X); Write('Y = ' ) ; Readln(Y) ; Writeln('X в степени Y = ', Power(X, Y)); Writeln('Y в степени -X = ', Power(Y, -X)) ; Readln end. Начиная с версии 2, с Delphi поставляется модуль M a t c h , в котором соответствующая функция поддерживается (см. Сгм приложение ппвлг.™,,,» KI Б)
1 2 6 Глава 4 • Процедуры и функции Для вызова функции Power мы просто указали ее в качестве параметра при об ращении к стандартной функции вывода W r i t e l n . Параметры X иУ в момент обращения к функции Power — это фактические параметры. Они подставляют ся вместо формальных параметров А и В в заголовке функции, и затем над ними осуществляются нужные действия. Полученный результат присваивается специ альной переменной с именем R e s u l t , которая в теле любой функции интерпре тируется как значение, которое вернет функция после окончания своей работы. В программе функция Power вызывается дважды: сначала с параметрами X и Y, затем с параметрами У и -X, поэтому будут получены два разных результата. Механизм замены формальных параметров фактическими позволяет нужным образом настроить алгоритм, реализованный в подпрограмме. Delphi следит за тем, чтобы количество и типы формальных параметров строго соответствовали количеству и типам фактических параметров в момент обращения к подпрограм ме. Смысл используемых фактических параметров зависит от того, в каком по рядке они перечислены при вызове подпрограммы. В нашем примере первый по порядку фактический параметр возводится в степень, задаваемую вторым пара метром, а не наоборот. Программист должен сам следить за правильным поряд ком перечисления фактических параметров при обращении к подпрограмме. Любой из формальных параметров подпрограммы может быть либо параметромзначением, либо параметром-переменной, либо, наконец, параметром-константой. В предыдущем примере параметры А и В определены как параметры-значения. Если параметры определяются как параметры-переменные, перед ними необхо димо ставить зарезервированное слово var, а если это параметры-константы — слово c o n s t , например: p r o c e d u r e Myprocedure(var A: Real; В: Real; c o n s t C: S t r i n g ) ; Здесь А — параметр-переменная, в — параметр-значение, С — параметр-константа. Определение формального параметра тем или иным способом существенно в ос новном только для вызывающей программы: если формальный параметр объяв лен как параметр-переменная, то при вызове подпрограммы ему должен соответ ствовать фактический параметр в виде переменной нужного типа; если формальный параметр объявлен как параметр-значение.или параметр-константа, то при вызове ему может соответствовать произвольное выражение. Контроль за неукоснитель ным соблюдением этого правила осуществляется компилятором Delphi. Предпо ложим, для предыдущего примера был использован такой заголовок функции: f u n c t i o n Power
(A: Real; v a r В: R e a l ) : Real;
Тогда при втором обращении к функции компилятор указал бы на несоответ ствие типов фактических и формальных параметров (параметр обращения -X есть выражение, в то время как соответствующий ему формальный параметр В опи сан как параметр-переменная).
Описание подпрограммы
127
память (стек) и передается подпрограмме. Важно учесть, что даже если в каче стве фактического параметра указано простейшее выражение в виде переменной или константы, все равно подпрограмме будет передана лишь копия переменной (константы). Любые возможные изменения в подпрограмме параметра-значения никак не воспринимаются вызывающей программой, так как в этом случае изме няется копия фактического параметра. Если параметр определен как параметр-переменная, то при вызове подпрограм мы передается сама переменная, а не ее копия (фактически в этом случае под программе передается адрес переменной). Изменение параметра-переменной при водит к изменению фактического параметра в вызывающей программе. В случае параметра-константы в подпрограмму также передается адрес области памяти, в которой располагается переменная или вычисленное значение. Одна ко компилятор блокирует любые присваивания параметру-константе нового зна чения в теле подпрограммы. Итак, параметры-переменные используются как средство связи алгоритма, реа лизованного в подпрограмме, с внешним миром: с помощью этих параметров под программа может передавать результаты своей работы вызывающей программе. Разумеется, в распоряжении программиста всегда есть и другой способ передачи результатов — через глобальные переменные. Однако злоупотребление глобаль ными связями, как правило, делает программу запутанной, трудной в понима нии и сложной в отладке. В соответствии с требованиями хорошего стиля про граммирования рекомендуется там, где это возможно, передавать результаты через фактические параметры-переменные. В то же время описание всех формальных параметров как параметров-перемен ных нежелательно по двум причинам. Во-первых, это исключает возможность вызова подпрограммы с фактическими параметрами в виде выражений, что де лает программу менее компактной. Во-вторых, и это главное, в подпрограмме возможно случайное использование формального параметра, например, для вре менного хранения промежуточного результата, то есть всегда существует опас ность непреднамеренно испортить фактическую переменную. Вот почему пара метрами-переменными следует объявлять только те параметры, через которые подпрограмма в действительности передает результаты вызывающей программе. Чем меньше параметров объявлено параметрами-переменными и чем меньше в подпрограмме используется глобальных переменных, тем меньше опасность по лучения непредусмотренных программистом побочных эффектов, связанных с вы зовом подпрограммы, и тем проще программа в понимании и отладке. По той же причине не рекомендуется использовать параметры-переменные в заголовке функ ции: если результатом работы функции не может быть единственное значение, то логичнее задействовать процедуру или нужным образом декомпозировать ал горитм на несколько подпрограмм. Для передачи результатов вызывающей программе можно использовать парамет ры-переменные, которым предшествует зарезервированное слово out, например:
Для того чтобы понять, в каких случаях использовать тот или иной тип парамет ра, рассмотрим, как осуществляется замена формальных параметров фактичес кими в момент обращения к подпрограмме.
procedure AProc(out A: Real; В: Integer);
Если параметр определен как параметр-значение, то перед вызовом подпрограм мы это значение вычисляется, полученный результат копируется во временную
В этом примере параметр А есть параметр-переменная, поэтому вызов процеду ры может быть, например, таким:
1 2 8 Глава 4 • Процедуры и функции
var X: Real; begin AProc(X, 123); end;
Параметры, описанные с ключевым словом out, нельзя использовать в подпрог рамме как входные, они предназначены исключительно для передачи результата вызывающей программе. Существует еще одно обстоятельство, которое следует учитывать при выборе вида формальных параметров. Как уже говорилось, при объявлении параметра-значе ния осуществляется копирование фактического параметра во временную память. Если этим параметром является массив большой размерности, то существенные затраты времени и памяти на копирование при многократных обращениях к под программе можно минимизировать, объявив этот параметр параметром-констан той. Параметр-константа не копируется во временную область памяти, что сокра щает затраты времени на вызов подпрограммы, а любые его изменения в теле подпрограммы невозможны — за этим строго следит компилятор.
Описание подпрограммы
12!
р([1,2,3], " ) ; Р([1,2,3]);
Если в подпрограмме используются два и более умалчиваемых параметра, тс в случае переопределения одного из них при обращении к подпрограмме следует указывать все параметры вплоть до последнего переопределяемого (то ест! нельзя заменять запятыми те умалчиваемые параметры, которые не переопреде ляются), например: procedure P ( a : I n t e g e r ; S: String = ' ' ;
В: I n t e g e r = 0) ;
Допустимые обращения: Р(123); Р(123, ' С т р о к а ' ) ; Р(123,
",
1)
Умалчиваемые параметры должны следовать в конце списка параметров. Они могут быть только простых типов и передаваться по значению или с модифика тором const. Параметры-массивы и параметры-строки
Еще одно свойство Delphi — возможность использования нетипизированных па раметров. Параметр считается нетипизированным, если тип формального пара метра-переменной в заголовке подпрограммы не указан, при этом соответствую щий ему фактический параметр может быть переменной любого типа. Заметим, что нетипизированными могут быть только параметры-переменные:
Может сложиться впечатление, что объявление переменных в списке формаль ных параметров подпрограммы ничем не отличается от их объявления в разделе описания переменных. Действительно, в обоих случаях много общего, но есть одно существенное различие: типом любого параметра в списке формальных пара метров может быть только стандартный или ранее объявленный тип. Поэтому нельзя, например, объявить следующую процедуру:
procedure MyProc(var aParametr);
procedure S (a: array [ 1 . . 1 0 ] of r e a l ) ;
Нетипизированные параметры обычно используются в случае, когда тип данных несущественен. Как уже говорилось, список параметров подпрограммы может отсутствовать. В этом случае, по правилам стандартного языка Pascal, опускаются и обрамляю щие список круглые скобки. Однако в Delphi скобки можно оставить:
Причина в том, что в списке формальных параметров фактически объявляется тип-диапазон, указывающий границы индексов массива. Если мы хотим передать какой-то элемент массива, то проблем, как правило, не возникает, но если в подпрограмму передается весь массив, то следует первона чально описать его тип, например:
procedure MyProc();
type
Это сделано для упрощения перехода на Delphi программистов, активно исполь зующих языки С#, C++, Java.
аТуре = array [1..10] of Real; procedure S(var a: aType);
Умалчиваемые параметры В Delphi можно использовать так называемые умалчиваемые параметры, то есть параметры, которые могут опускаться при обращении к подпрограмме. Умалчи ваемые параметры замыкают список формальных параметров и имеют вид <имя>: <тип> = <значение>
Например:
Поскольку короткая строка является фактически своеобразным массивом, ее пе редача в подпрограмму осуществляется аналогичным образом: type InputType = String [15]; OutputType = String [ 3 0 ] ; function S t ( S : InputType): OutputType;
procedure P(a: array of I n t e g e r ; S: String = ' ' ) ;
В этом случае два следующих обращения идентичны:
Требование описать любые тип-массив или тип-строку перед объявлением под программы на первый взгляд кажется несущественным. Действительно, в рам-
1 3 0 Глава 4 • Процедуры и функции
Описание подпрограммы
131
ках простейших вычислительных задач обычно заранее известна структура всех используемых в программе данных, поэтому статическое описание массивов не вызывает проблем. Однако разработка программных средств универсального на значения связана со значительными трудностями.
Как видно из этого примера, фактические границы массивов А и в, передаваемых в качестве параметров вызова процедуре PrintOf Array, не имеют значения. Од нако размерность открытых массивов (количество индексов) всегда равна 1 — за этим следит компилятор. Например, добавим в программу двухмерный массив С:
Открытые массивы Delphi поддерживает так называемые открытые массивы, легко решающие про блему передачи подпрограмме одномерных массивов переменной длины. Открытый массив представляет собой формальный параметр подпрограммы, опи сывающий базовый тип элементов массива, но не определяющий его размернос ти и границы:
var С: array [ 1 . . 3 , 1 . . 5 ] of I n t e g e r ;
procedure MyProc(OpenArray: array of I n t e g e r ) ;
Внутри подпрограммы такой параметр трактуется как одномерный массив с нуле вой нижней границей. Верхняя граница открытого массива возвращается стандарт ной функцией High. Используя 0 как минимальный индекс и значение, возвраща емое функцией High, как максимальный индекс, подпрограмма может обрабатывать одномерные массивы произвольной длины: program ArrayPrint; {$APPTYPE CONSOLE} uses SysUtils; { Иллюстрация использования открытых массивов: программа выводит на экран содержимое двух одномерных массивов разной длины с помощью одной процедуры PrintOfArray} procedure PrintOfArray(aArray: array of Integer); var k: Integer; begin for k := 0 to High(aArray) do Write(aArray[k]:5); end; const A: array [-1..2] of Integer = (0,1,2,3) B: array [4.. 6] of Integer = (4,5,6); begin PrintOfArray(A); Writeln; PrintOfArray(B); Readln end.
Тогда следующее обращение вызовет сообщение об ошибке: PrintOfArray (С)
Delphi поддерживает также открытый массив констант, который задается клю чевыми словами array of const. Такой массив может содержать данные раз личных типов, например: procedure MyProc(array of c o n s t ) ; MyProc(['',
123,
@MyProc,
3.14, F a l s e ] ) ;
Конструктор массива При обращении к подпрограмме на месте формального параметра в виде откры того массива можно указать так называемый конструктор массива. Конструктор массива представляет собой список разделенных запятыми значений элементов массива, обрамленный квадратными скобками. Например, вспомним предыду щий пример: const A: array [-1..2] of Integer = (0,1,2,3); В: array [4..6] of Integer = (4,5,6); begin PrintOfArray (A); PrintOfArray (B); end.
Обращения к массивам в этом примере мы могли бы написать так: begin PrintOfArray ([0,1,2,3]); PrintOfArray ([4,5,6]); end;
Вариантные массивы-параметры В Delphi при передаче подпрограмме массивов переменной длины и размернос ти удобно использовать вариантные массивы (подробнее о вариантах см. главу 7). В следующем примере с помощью функции GetArrayAverage определяется среднее арифметическое значение всех элементов вариантного массива произ вольной длины и размерности не выше 5: function GetArrayAverage(const V: Variant): Double; {Возвращает среднее арифметическое значение массива произвольной длины и размерности или очень маленькую отрицательную величину, если
1 3 2 Глава 4 • Процедуры и функции
Процедурные типы
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+VarArrayHighBound(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,l) 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,l) 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 l:=VarArrayLowBound(V,4) to VarArrayHighBound(V,4) do for m:=VarArrayLowBound(V,5) to VarArrayHighBound(V,5) do Sum := Sum+V[i,j,k,l,m]; end; Result := Sum/NItem end; Как нетрудно догадаться, функция VarArrayDimCount () ность вариантного массива.
возвращает размер
133
Процедурные типы
Основное назначение процедурных типов — предоставить программисту гибкие средства передачи функций и процедур в качестве фактических параметров об ращения к другим процедурам и функциям.
Для объявления процедурного типа используется заголовок процедуры (функ ции), в котором опускается ее имя, например: type Procl Proc2 РгосЗ Fund Func2
= procedure (a, b, с: Real; var d: Real); = procedure (var a, b ) ; = procedure(); = function: String; = function (var s: String): Real;
Как видно из приведенных примеров, существует два процедурных типа: типпроцедура и тип-функция. В следующий программе иллюстрируется механизм передачи процедур в каче стве фактических параметров вызова. Программа выводит на экран таблицу зна чений двух функций: sinl(x) = (sin(x) + 1) • ехр(-дг) cosl(.r) = (cos(x) + 1) • ехр(-х) Вычисление и печать значений этих функций реализуются в процедуре P r i n t Func, которой в качестве параметров передаются количество NP вычислений функции в диапазоне X от 0 до 2я и имя нужной функции: program ProcType; {$АРРТУРЕ
CONSOLE}
uses SysUtils; function Sinl(X: Real): Real; begin Result := (Sin(X) + 1) * Exp(-X) end; // sinl function Cosl(X: Real): Real; begin e n
Result := (Cos(X) + 1) * Exp(-X) d; // cosl
type Func = function (X: Real): Real; // Процедурный тип
1 3 4 Глава 4 • Процедуры и функции procedure PrintFunc(NP: Integer; F: Func); var k: Integer; X: Real; begin for k := 0 to NP do begin • X : = k * 2 * pi / NP; Writeln(FloatToStrF(X, ffExponent, 10, 2) + #9#9 + FloatToStrF(F(X), ffExponent, 10, 2)); end; end; // PrintFunc
Рекурсия и опережающее описание
135
При выполнении правильно организованной рекурсивной подпрограммы осуще ствляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не бу дет получено тривиальное решение поставленной задачи. В нашем случае реше ние при N = 0 тривиально (факториал 0 равен 1) и используется для остановки рекурсии: program Factorial_demo; f$APPTYPE
CONSOLE}
uses SysUtils;
begin Writeln(#9'Функция SIN1:'); PrintFunc (10, Sinl); Writeln(#9'Функция C0S1:'); PrintFunc (10, Cosl); Readln end.
function Factorial(N: Word): Extended; begin if N = 0 then Result := 1 else Result := N * Factorial(N-l) end; // Factorial
Обратите внимание на то, что передаваемые подпрограммы не могут быть локаль ными, то есть процедурами или функциями, объявленными внутри другой подпрог раммы. Вот почему описания подпрограмм S i n l H C O S I размещаются вне основ ной программы, но выше по тексту. Замечу, что символ # 9 — это символ табуляции, который вставляется в формируемые строки для разделения колонок с цифрами. В программе могут быть объявлены переменные процедурных типов, например, так:
var N: Integer; begin Readln(N); Writeln(N, '! = ', Factorial(N)); Readln end.
var pi : Procl; fl, f2 : Func2; ар : array [1..N] of Procl;
Рекурсивная форма организации алгоритма обычно выглядит изящнее итераци онной и дает более компактный текст программы, но при выполнении, как пра вило, медленнее и может вызвать переполнение стека (при каждом входе в под программу ее локальные переменные размещаются в организованной особым образом области памяти, называемой программным стеком). Рекурсивный вызов может быть косвенным. В этом случае подпрограмма обра щается к себе опосредованно, вызывая другую подпрограмму, в которой содер жится обращение к первой, например:
Переменным процедурных типов допускается присваивать в качестве значений имена соответствующих подпрограмм. После такого присваивания имя перемен ной становится синонимом имени подпрограммы.
Рекурсия и опережающее описание Рекурсия — это такой способ организации вычислительного процесса, при кото ром подпрограмма в ходе выполнения составляющих ее операторов обращается сама к себе. Рассмотрим классический пример — вычисление факториала. Программа читает целое число N и выводит на экран значение N!, которое вычисляется с помощью рекурсивной функции F a c t o r i a l .
procedure A (i begin
:
Byte);
В (i) ; end; procedure B (j : Byte); begin
1 3 6 Глава 4 • Процедуры и функции
A(j); end;
Если строго следовать правилу, согласно которому каждый идентификатор пе ред употреблением должен быть описан, то такую программную конструкцию использовать нельзя. Чтобы такого рода вызовы стали возможны, вводится опе режающее описание: procedure В (j : Byte); forward; procedure- A (i : Byte);
Классы
begin В (ibend; procedure B; begin A(j); end;
Как видим, опережающее описание заключается в том, что объявляется лишь за головок процедуры В, а ее тело заменяется стандартной директивой forward. После этого в процедуре А можно обращаться к процедуре в — ведь она уже описана, точнее, известны ее формальные параметры, и компилятор может правильным об разом организовать ее вызов. Обратите внимание на то, что тело процедуры В начинается заголовком, в котором уже не указываются описанные ранее формаль ные параметры.
Классами в Delphi называются специальные типы, которые содержат поля, мето ды и свойства. Как и любой другой тип, класс служит лишь образцом для созда ния конкретных экземпляров реализации, которые называются объектами. Важ ным отличием классов от других типов является то, что объекты класса всегда распределяются в куче, поэтому объект-переменная фактически представляет собой лишь указатель на динамическую область памяти.
Основные понятия Классы — это особое «изобретение» программистов для упрощения разработки сложных программ и улучшения их качества. В основе классов лежат три фунда ментальных принципа: инкапсуляция, наследование и полиморфизм.
Инкапсуляция Класс представляет собой единство трех сущностей (членов класса) — полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от ос тальных частей программы, сделать его «самодостаточным» для решения конк ретной задачи. В результате класс всегда несет в себе некоторую функциональ ность. Например, класс TForm содержит (инкапсулирует в себе) все необходимое для создания окна Windows-программы, класс ТМето представляет собой полно функциональный многострочный текстовый редактор, класс ТTimer обеспечи вает работу программы с таймером и т. д. Инкапсуляция является мощным средством обмена готовыми к работе про граммными заготовками. Библиотека классов Delphi — это фактически набор
138
Члены класса
Глава 5 • Классы
«кирпичиков», созданных программистами Borland для построения ваших про грамм.
Наследование Класс может быть порожден от другого класса. Для этого при его объявлении указывается имя класса-родителя: TChildClass = class (TParentClass) Порожденный класс автоматически наследует поля, методы и свойства своего родителя и может добавлять новые. Таким образом, принцип наследования обес печивает поэтапное создание сложных классов и разработку собственных биб лиотек классов. Все классы Delphi порождены от единственного родителя— класса T O b j e c t . Этот класс не имеет полей и свойств, но включает в себя методы самого общего назначения, обеспечивающие весь жизненный цикл любых объектов — от созда ния до уничтожения. Программист не может создать класс, который не был бы дочерним классом T O b j e c t . Следующие два объявления идентичны: TaClass TaClass
class(TObject) class
Принцип наследования приводит к созданию ветвящегося дерева классов, по степенно разрастающегося в направлении от класса TObj e c t к его потомкам. Каж дый потомок дополняет возможности своего родителя новыми и передает их сво им потомкам. Замечу, что дочерний класс не может удалить какую-либо сущность родительского класса. В отличие от C++, в Delphi дочерний класс не может иметь два и более родительских класса, то есть запрещено множественное наследова ние.
139
лать и все его потомки. Класс TComponent, в свою очередь, умеет взаимодей ствовать со средой разработчика и передает это умение своим потомкам. Класс T C o n t r o l n e только способен работать с файлами и средой разработчика, он еще умеет создавать и обслуживать видимые на экране изображения, а его потомок TWinControl может создавать окна Windows-программ и т. д.
Полиморфизм Полиморфизм — это свойство классов решать схожие по смыслу проблемы раз ными способами. В рамках Delphi поведенческие свойства класса определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в по томках класса, программист может придавать этим потомкам отсутствующие у ро дителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, то есть объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут дей ствовать два одноименных метода, имеющие разную алгоритмическую основу и, следовательно, придающие объектам разные свойства. Это и называется поли морфизмом объектов. В Delphi полиморфизм достигается не только описанным механизмом наследо вания и перекрытия методов родителя, но и их виртуализацией (см. далее), по зволяющей родительским методам обращаться к методам своих потомков.
Члены класса Поля Полями называются инкапсулированные в классе данные. Поля могут быть лю бого типа, в том числе — классами, например: type TMyClass = class alntField: Integer; aStrField: String; aObjField: TObject; end;
Рис. 5.1. Фрагмент дерева классов Delphi
Для примера на рис. 5.1 показан небольшой фрагмент дерева классов Delphi. Класс T P e r s i s t e n t обогащает возможности своего родителя T O b j e c t : он «уме ет» сохранять данные в файле и получать их из него, в результате это умеют де-
Каждый объект получает уникальный набор полей, но общий для всех объектов данного класса набор методов и свойств. Фундаментальный принцип инкапсу ляции требует обращаться к полям только с помощью методов и свойств класса. Однако в Delphi разрешается обращаться к полям и напрямую: type TMyClass = class FIntField: Integer; FStrField: String;
1 4 0 Глава 5 • Классы end; var aObject: TMyClass; begin aObject.FIntField := 0; aObject.FStrField := 'Строка символов'; end;
Класс-потомок получает все поля всех своих предков и может дополнять их сво ими, но он не может переопределять их или удалять. Таким образом, чем ниже в дереве иерархии располагается класс, тем больше данных получают в свое рас поряжение его объекты. Большинство полей класса предназначены для хранения информации, которая изменяется динамически, в ходе выполнения программы. В Delphi могут использоваться также статические поля. Эти поля наполняются специальным конструктором в момент создания экземпляра класса. Им предше ствуют зарезервированные слова c l a s s и var, например: type MyClass = class private class var X: I n t e g e r ; end;
Методы Инкапсулированные в классе процедуры и функции называются методами. Они объявляются так же, как и обычные подпрограммы: type TMyClass = class function MyFunc(aPar: Integer): Integer; procedure MyProc; end;
Доступ к методам класса, как и к его полям, возможен с помощью составных имен: var aObject: TMyClass; begin aObject.MyProc; end;
Члены класса
141
ПРИМЕЧАНИЕ — Любому методу класса доступна автоматически объявляемая переменная S e l f , которая трак туется как текущий экземпляр данного класса. Если, например, в классе объявлено целочис ленное поле i n t D a t a , то два следующих присваивания в каком-либо методе класса идентичны: IntData := 123; Self.IntData := 123;
Как уже говорилось, методы класса могут перекрываться в потомках, например: type TParentClass = class procedure DoWork; end; TChildClass = class(TParentClass) procedure DoWork; end;
Потомки обоих классов могут выполнять процедуру DoWork, но в общем случае будут это делать по-разному. Такое перекрытие методов называется статичес ким, так как реализуется компилятором. В Delphi гораздо чаще имеет место динамическое перекрытие методов на этапе прогона программы. Для реализации этого метод, перекрываемый в родительс ком классе, должен объявляться как динамический (с директивой dynamic) или виртуальный (virtual). Встретив такое объявление, компилятор создает две таблицы - DMT (Dynamic Method Table) и VMT (Virtual Method Table) - и по мещает в них адреса точек входа динамических и виртуальных методов соответ ственно. При каждом обращении к перекрываемому методу компилятор встав ляет код, позволяющий извлечь из той или иной таблицы адрес точки входа в подпрограмму. В классе-потомке перекрывающий метод объявляется с дирек тивой override (перекрыть). Получив это указание, компилятор создает код, который на этапе прогона программы помещает в родительскую таблицу точку входа метода класса-потомка, что позволяет родителю выполнить нужное дей ствие с помощью нового метода. Пусть, например, родительский класс с помощью методов Show и Hide соответ ственно показывает что-то на экране или прячет изображение. Для создания изоб ражения он использует метод Draw с логическим параметром: type TVisualObject = c l a s s (TWinControl) procedure Hide; procedure Show; procedure Draw(IsShow: Boolean); v i r t u a l ; and; TVisualChildObject = class(TVisualObject) procedure Draw(IsShow: Boolean); override; end;
Члены класса
1 4 2 Глава 5 • Классы Реализация методов Show и Hide очень проста: procedure TVisualObject.Show; begin Draw(True); end; procedure TVisualObject.Hide; begin Draw (False); end; Методы Draw у родителя и потомка имеют разную реализацию и создают раз ные изображения. В результате родительские методы Show и H i d e будут пря тать или показывать те или иные изображения в зависимости от конкретной ре ализации метода Draw у любого из своих потомков. Динамическое связывание в полной мере реализует полиморфизм классов. Разница между динамическими и виртуальными методами состоит в том, что таб лица динамических методов (DMT) содержит адреса только тех методов, которые объявлены с директивой dynamic в данном классе, в то время как таблица вирту альных методов (VMT) содержит адреса виртуальных методов не только данного класса, но и всех его родителей. Значительно большая по размеру таблица VMT обеспечивает более быстрый поиск, в то время как при обращении к динамическо му методу программа сначала просматривает таблицу DMT у объекта, затем — у его родительского класса и т. д., пока не будет найдена нужная точка входа. Динамически перекрываемые методы часто могут вообще ничего не делать. Та кие методы называются абстрактными, они обязаны перекрываться в потомках. Программист может запретить вызов абстрактного метода, объявив его с дирек тивой a b s t r a c t , например: type TVisualObject = class(TWinControl) procedure Draw(IsShow: B o o l e a n ) ; v i r t u a l ; a b s t r a c t end; TVisualChildObject = class(TWinControl) p r o c e d u r e Draw(IsShow: B o o l e a n ) ; o v e r r i d e ; end; var aVisualObject: TVisualObject; aVisualChild: TVisualChildObject; begin a V i s u a l O b j e c t . S h o w ; { Ошибка! Обращение к абстрактному методу } a V i s u a l C h i l d . S h o w ; // Нормальное обращение.
//
TVisualChildObject
143
перекрыт.
end; Обращение к неперекрытому абстрактному методу вызывает ошибку периода исполнения. Разумеется, в грамотно составленной программе абстрактные мето ды никогда не вызываются. Классы, содержащие абстрактные методы, называ ются абстрактными. Такие классы инкапсулируют общие свойства своих неабст рактных потомков, но объекты абстрактных классов никогда не создаются и не используются. Для эксплуатации абстрактных классов в библиотеку классов Delphi включаются классы-потомки, в которых перекрываются абстрактные ме тоды родителя. В состав любого класса входят два специальных метода — конструктор и дест руктор. У класса T O b j e c t эти методы называются C r e a t e и D e s t r o y , так же они называются в подавляющем большинстве его потомков. Конструктор рас пределяет объект в динамической памяти (размещаются лишь поля объекта, его методы являются общими для всех объектов данного класса и в кучу не перено сятся) и помещает адрес этой памяти в переменную Self, которая автоматичес ки объявляется в классе. Деструктор удаляет объект из кучи. Обращение к кон структору должно предварять любое обращение к полям и некоторым методам объекта. По своей форме конструкторы и деструкторы являются процедурами, но объявляются с помощью зарезервированных слов Constructor
и Destructor: type TMyClass = class IntField: Integer; Constructor Create (Value: Integer); Destructor Destroy; end; Динамические поля объекта, а также методы класса, оперирующие с динамичес кими полями, могут вызываться только после создания объекта путем вызова конструктора, так как конструкторы распределяют объект в динамической памя ти и делают действительными содержащийся в объекте указатель и автоматически объявляемую переменную S e l f : var MyObject: TMyClass; begin MyObject.IntField := 0; тором! }
{ Ошибка! Объект не создан конструк '
- MyObject := TMyClass.Create; // Надо так: создаем объект MyObject.IntField := 0; //и обращаемся к его полю MyObect.Free; end;
// Уничтожаем ненужный объект
144
Глава 5 • Классы
В базовом классе T O b j e c t определен метод F r e e , который сначала проверяет действительность адреса объекта и лишь затем вызывает деструктор D e s t r o y . Обращение к деструктору объекта будет ошибочным, если объект не создан кон структором, поэтому для уничтожения ненужного объекта следует вызывать ме тод F r e e , как это сделано в предыдущем примере. Специально для уничтожения объектов в модуле System определена процедура F r e e A n d N i l , которая не только уничтожает объект, но и помещает в его указа тель (напомню, что им является идентификатор объекта) значение NIL: FreeAndNil(MyObject) Большинство конструкторов реализуют некоторые действия, необходимые для правильной работы объекта. Поэтому в конструкторе класса-потомка следует сначала вызвать конструктор своего родителя, а уже затем осуществлять допол нительные действия. Вызов любого метода родительского класса достигается с по мощью зарезервированного слова I n h e r i t e d (унаследованный): Constructor TMyClass.Create(Value: Integer); // Возможная реализация конструктора begin Inherited Create; // Вызываем унаследованный конструктор IntField := Value; // Реализуем дополнительные действия end; Некоторые методы могут вызываться без создания и инициализации объекта. Такие методы называются методами класса, они объявляются с помощью заре зервированного слова c l a s s : type TMyClass = class (TObject) class function GetClassName: String; end; var S: String; begin S := TMyClass.GetClassName; end; Методы класса не могут обращаться к динамическим полям (но могут — к ста тическим), так как в общем случае вызываются без создания объекта, а следова тельно, в момент вызова полей просто не существуют. Обычно они возвращают служебную информацию о классе — имя класса, имя его родительского класса, адрес метода и т. п. В Delphi имеется возможность в рамках одного класса объявлять несколько од ноименных {перегруженных) методов. Описанный механизм перекрытия роди тельского метода одноименным методом потомка приводит к тому, что потомок «не видит» перекрытый родительский метод и может обращаться к нему лишь с помощью зарезервированного слова I n h e r i t e d . В Delphi введено зарезерви-
Члены класса
145
рованное слово o v e r l o a d (перезагрузить), с помощью которого становятся вид ны одноименные (перегруженные) методы как родителя, так и потомка. ВНИМАНИЕ — Чтобы перегруженные методы можно было отличить друг от друга, каждый из них должен иметь уникальный набор параметров. В ходе выполнения программы при обращении к одному из перегруженных методов программа проверяет тип и количество фактических параметров обращения и выбирает нужный метод. При обнаружении перегруженного метода компилятор Delphi предупреждает о том, что у класса уже есть аналогичный метод с другими параметрами. Для по давления сообщений объявление одноименного метода можно сопровождать зарезервированным словом r e i n t r o d u c e (вновь ввести). В следующем примере в классе TForml используются целых 4 одноименных ме тода C l o s e . Лишь один из них (унаследованный метод без параметра) выполня ет свои основные функции — закрывает окно. Три других различаются набором параметров и выводят сообщение в заголовке окна. Поместите на пустую форму четыре кнопки T B u t t o n и напишите такие обра ботчики их событий O n c l i c k : procedure TForml.ButtonlClick(Sender: TObject); begin Close ('Строка символов') end; procedure TForml.Button2Click(Sender: TObject); begin Close (123) end; procedure TForml.Button3Click(Sender: TObject); begin Close(20,300); end; procedure TForml.Button4Click (Sender: TObject); begin Close end; Теперь в раздел p r i v a t e класса TForml вставьте три объявления методов C l o s e : private { Private declarations } procedure C l o s e ( S : S t r i n g ) ; reintroduce; overload; procedure C l o s e d : I n t e g e r ) ; reintroduce; overload; procedure C l o s e ( I , J : I n t e g e r ) ; reintroduce; overload;
1 4 6 Глава 5 • Классы И наконец, в разделе implementation поместите описания объявленных методов: procedure TForml.Close(S: String); begin Caption := S end;
Члены класса
147
aClass.IntegerValue := 0; Value := aClass.IntegerValue; FreeAndNil(aClass); // Удаление ненужного объекта end;
procedure TForml.Close(I: Integer); begin Caption := IntToStr(I) end; procedure TForml.close(I,J: Integer); begin Caption := IntToStr(i*j) end; Теперь после запуска программы три первые кнопки будут вызывать методы C l o s e класса TForml и менять заголовок окна, в то время как кнопка B u t t o n 4 обратится к методу C l o s e родительского класса TForm и закроет окно.
Свойства Свойства реализуют специальный механизм классов, регулирующий доступ к по лям. Свойства объявляются с помощью зарезервированных слов p r o p e r t y , r e a d и w r i t e (слова r e a d и w r i t e считаются зарезервированными только в контек сте объявления свойства). Обычно свойство связано с некоторым полем и ука зывает те методы класса, которые должны использоваться при записи в это поле или при чтении из него, например: type TaClass = class IntField: Integer; function GetField: Integer; procedure SetField(Value: Integer); property IntegerValue: Integer read GetField write SetField; end; В контексте программы свойство ведет себя как обычное поле. Например, мы могли бы написать такие операторы: var aClass: TaClass; Value: Integer; begin aClass := TaClass.Create; { Обязательное обращение к конструктору перед обращением к полю или свойству! }
Более того, возможен и такой оператор присваивания: aClass.IntField := NewValue; Разница между этим оператором и оператором, показанным далее, заключается в том, что при обращении к свойству автоматически подключается метод S e t F i e l d , в котором могут реализовываться специфичные действия: aClass.IntegerValue := NewValue; Если нет необходимости в специальных действиях при чтении или записи свой ства, вместо имени соответствующего метода можно указывать имя поля: type TaClass = class IntFiled: Integer; procedure SetField(Value: Integer); property IntegerValue: Integer read IntFiled write SetField; end; Если необходимо, чтобы свойство было доступно только для чтения или только для записи, следует опустить часть w r i t e или r e a d соответственно. Вообще говоря, свойство может и не связываться с полем. Фактически оно описывает один или два метода, которые осуществляют некоторые действия над данными того же типа, что и свойство.
События Событие представляет собой связь между некоторым происшествием, вызван ным действиями пользователя, вмешательством операционной системы или ло гикой программы, и некоторым программным кодом, отвечающим за это проис шествие. Совокупность события и кода, выполняющегося в ответ на это событие, называется свойством-событием и реализуется как указатель на процедуру. Эта процедура называется обработчиком события. Свойства-события позволяют ре ализовать различные поведенческие аспекты одного и того же класса без необ ходимости его переделки простым изменением кода обработчиков событий. Delphi трактует событие как указатель на метод. Указатель на метод — это осо бый тип указателя, которым реализуется ссылка на метод экземпляра компонен та. Указатели на методы во всем подобны обычным указателям, но они еще под держивают скрытый указатель на класс конкретного объекта. Этот указатель — не что иное, как S e l f (указатель на экземпляр). Когда возникает событие, этот указатель передается неявно вместе с другими параметрами обработчику собы-
148
Глава 5 • Классы
тия. В результате объект как бы делегирует свойство-событие другой программе, объявляя, что именно он осуществил вызов обработчика. Указатель на метод представляет собой процедурный тип, объявленный с помо щью зарезервированных слов of o b j e c t . Например, в модуле B o r l a n d . V C L . C l a s s e s объявляется такой указатель на метод: type TNotifyEvent = procedure (Sender: TObject) of o b j e c t ; Важной особенностью события является возможность связать его с некоторым программным кодом — обработчиком события. Чтобы возможность это сделать была на этапе конструирования программы, события должны отражаться в окне инспектора объектов. С этой целью формально они объявляются в виде свойств. Поскольку типом свойства-события является указатель на метод, свойства-со бытия имеют информацию RTTI (см. раздел «Приведение типов классов») с при знаком tkMethod, что позволяет инспектору объектов отделить их от других опубликованных свойств и поместить на вкладку Events своего окна. Подобно другим свойствам, свойства-события имеют внутренние поля, в кото рых хранятся указатели на обработчики событий. В отличие от других свойств, свойства-события не используют косвенные способы доступа к этим полям с по мощью процедур WriteXXXX и функций GetXXXX. Вместо этого они просто ссы лаются на эти поля, например: type TMyControl = class(TGraphicControl) private FOnClik: TNotifyEvent; // Поле для указателя на метод published // Объявление свойства-события: property OnClick: TNotifyEvent read FOnClick write FOnClick; end; В технической документации строго рекомендуется оформлять обработчики со бытий в виде процедур, но не функций. Дело в том, что в обработчике програм мист может ошибочно не указать результат функции, который, таким образом, ока жется неопределенным. Если обработчик должен вернуть компоненту результат, он передает его в виде значения параметра-переменной. Перед вызовом обработ чика компонент присваивает этому параметру некоторое умалчиваемое значение, которое программист может изменить по своему усмотрению. Таким образом, даже если программист не изменит значение параметра-переменной, оно останется впол не определенным и заранее оговоренным.
Объявление класса Любой вновь создаваемый класс может содержать секции (разделы), определяе мые зарезервированными словами p u b l i s h e d (опубликованные), p r i v a t e (за крытые), p r o t e c t e d (защищенные) и p u b l i c (открытые). Внутри каждой сек ции вначале определяются поля, а затем — методы и свойства.
Объявление класса
149
ПРИМЕЧАНИЕ В предыдущих версиях Delphi в объявлении класса могла использоваться секция automated, в которой собирались члены для поддержки технологии OLE Automation. В Delphi 2005 (8) сек ция a u t o m a t e d не используется. Секции определяют области видимости элементов описания класса. Секция p u b l i c не накладывает ограничений на область видимости перечисляемых в ней полей, методов и свойств — их можно вызывать в любом другом модуле программы. Секция p u b l i s h e d также не ограничивает область видимости, од нако в ней перечисляются свойства, которые должны быть доступны не только на этапе исполнения, но и на этапе конструирования программы (то есть в ок не инспектора объектов). Секция p u b l i s h e d используется только при разра ботке нестандартных компонентов. Замечу, что среда Delphi помещает описа ния компонентов, вставленных в форму, в специальную секцию без названия, которая располагается сразу после заголовка класса и продолжается до первой объявленной секции. Эта секция — p u b l i s h e d . Программисту не следует по мещать в нее собственные элементы описания класса или удалять из нее эле менты, вставленные средой. Секция p r i v a t e сужает область видимости до минимума: закрытые элементы описания доступны только внутри методов дан ного класса и в подпрограммах, находящихся в том же модуле, в котором опи сан класс. Элемент, объявленный в секции p r i v a t e , становится недоступным даже ближайшим потомкам класса, если они размещаются в других модулях. Секция p r o t e c t e d доступна только методам самого класса, а также любым его потомкам, независимо от того, находятся они в том же модуле или нет. В Delphi разрешается сколько угодно раз объявлять любую секцию, причем по рядок следования секций не имеет значения. Любая секция может быть пустой. Следующий фрагмент кода поясняет различные области видимости: Unit U n i t l ; Interface uses Controls, Forms; type TForml = class(TForm) Buttonl: TButton; // Эта секция обслуживается Delphi // Ее элементы доступны всем private // Эта секция доступна в модуле Unitl FIntField: Integer; procedure SetValue(Value: Integer); function GetValue: Integer; published // Эта секция доступна в любом модуле property IntField: read GetValue write SetValue; protected // Эта секция доступна классам-потомкам procedure Procl; public // Эта секция доступна в любом модуле procedure Ргос2; end;
Изменения в модели классов Delphi 2005 (8) 1 5 1
1 5 0 Глава 5 • Классы
При объявлении класса-потомка разрешается перемещать элементы класса из одной области видимости в другую. Для предыдущего примера допустимо такое объявление:
var Forml: TForml; Implementation procedure TForml.Procl; Buttonl.Color := clBtnFace; FIntField := 0; IntField := 0; Procl; Proc2; end; begin Forml.Buttonl.Color := clBtnFace; Forml.FIntField := 0; Forml.IntField := 0; Forml.Procl; Forml.Proc2; end.
// // // // //
Так Так Так Так Так
можно можно можно можно можно
public procedure Procl; end;
// // // // //
Так можно Так можно Так можно Так нельзя! Так можно
Unit Unit2; Interface uses Controls, Unitl; type TForm2 = class(TForml) Button2: TButton; procedure Button2Click(Sender: TObject) end; var Form2: TForm2; Implementation procedure TForm2 Button2Click(Sender: TObject); begin // Так можно Buttonl.Color := clBtnFace; // Так нельзя! FIntField := 0; // Так можно IntField := 0; // Так можно Procl; // Так можно Proc2; end; begin Forml.Buttonl.Color : Forml.FIntField := 0; Forml.IntField := 0; Forml.Procl; Forml.Proc2; end.
type TForm2 = class(TForml)
clBtnFace; // Так можно // Так нельзя! // Так можно // Так нельзя! // Так можно
После этого в модуле Unit2 возможно следующее обращение: Form2.Procl;
После перемещения в секцию p r i v a t e элемент объявления становится неви дим потомкам (если потомок, как это обычно бывает, объявляется в другом мо дуле),, и, следовательно, его уже нельзя переместить в другую секцию. Класс может объявляться только в интерфейсной области модуля или в самом начале области реализации. Нельзя определять классы в разделе описаний под программ.
Приведение типов классов Любой тип-класс относится к так называемым типам с информацией RTTI (Run Time Type Information — информация о типе во время исполнения). Экземпляры таких типов компилятор снабжает дополнительной информацией, позволяющей, в частности, на этапе прогона программы проверить принадлежность экземпляра нужному классу и осуществить его приведение. Для этого используются операто ры i s и as: if (AOblect is TMyClass) then (AObject as TMyClass).IntData := 12345;
Изменения в модели классов Delphi 2005 (8) Для согласования с CLR разработчики Delphi 2005 (8) внесли некоторые изме нения в описанную модель классов.
Области видимости strict CLR трактует области видимости секций private и protected не так, как было описано в разделе «Объявление класса»: члены p r i v a t e доступны только внут ри класса, а в секции protected — внутри класса и его потомков. Напомню, что в описанной модели эти члены видны также внутри модуля с описанием класса. Для согласования областей видимости введено зарезервированное слово s t r i c t (строгий), которое должно предварять объявление секции, наппимеп:
1 5 2 Глава 5 • Классы
Изменения в модели классов Delphi 2005 (8) 1 5 3
unit MyUnit; uses ... ; type MyClass = class(TObject) strict private A: Integer; // Поле доступно только внутри класса end; // MyClass procedure MyProc; // Это не член класса MyClass! begin // Ошибка! Процедура не видит поля А A := 0 end; // Unit MyUnit end.
Статические члены класса Ранее говорилось о методах класса, которые применяются не к конкретному эк земпляру, а к классу в целом. В технологии .NET Framework эта идея расширена за счет статических членов класса. К ним, так же как и к методам класса, можно обращаться, не имея объекта, то есть до обращения к конструктору класса. Ста тическими могут быть любые члены класса. Для объявления статического члена используется зарезервированное слово c l a s s . В следующем примере консольное приложение использует статическое свой ство S t a t типа TStatDemo. Этот тип для удобства объявлен в отдельном мо дуле Class Unit. Обратите внимание: обращение к статическому конструкто ру C o n s t a t происходит автоматически, как только программе понадобится значение свойства S t a t : program Static_demo;
uses SysUtils, ClassUnit in 'ClassUnit.pas';
Unit ClassUnit; interface type
implementation class constuctor TStatDemo.Constat; begin FStat := 123 end; class function TStatDemo.GetStat: integer; begin Result := FStat end; end.
При прогоне программа выведет на экран значение статического поля FStat (123). Статический конструктор всегда объявляется в секции s t r i c t private. Он не имеет параметров и не должен обращаться к унаследованному статическому кон структору — об этом позаботится CLR. Вызов этого конструктора осуществляет ся автоматически в момент первого обращения к любому статическому члену класса. При последующих обращениях этого не происходит. ПРИМЕЧАНИЕ — Ни один статический метод (в том числе конструктор) не может быть перекрываемым.
($APPTYPE CONSOLE}
begin Writeln(TStatDemo.Stat); Readln end.
TStatDemo = class strict private class var FStat: Integer; // Статическое поле class constructor Constat; // Статический конструктор strict protected class function GetStat: integer; static; // Статический метод public class property Stat: Integer read GetStat;// Статическое свойство end;
//
Атрибуты sealed и final
Автоматический вызов
конструктора!
В некоторых случаях требуется регулировать наследование классов и перекры тие методов. Для этого в Delphi введены атрибуты sealed и final. Класс, объяв ленный с атрибутом sealed, не может иметь потомков: type TNonParentClass = class sealed (...) end;
С помощью атрибута f i n a l можно запретить перекрытие метода в потомках: type TMyClass = class
1 5 4 Глава 5 • Классы
Изменения в модели классов Delphi 2005 (8) 1 5 5
public procedure NonOver; override; final; end;
implementation procedure TOuterClass.OuterProc;
Введение этих атрибутов помогает CLR генерировать более эффективный код.
begin
Вложенные типы
end;
Вложенные типы определяются внутри описания класса, который в этом случае используется в качестве пространства имен для вложенных типов:
procedure TOuterClass.TInnerClass.InnerProc; begin
Writeln('OuterProc')
Writeln('InnerProc ' )
program NestedTypes;
end; {$APPTYPE
CONSOLE}
procedure TOuterClass.TInnerClass.TInnerlnnerClass.InnerlnnerProc; begin Writeln('InnerlnnerProc' ) end;
uses SysUtils, ClassUnit in 'ClassUnit.pas'; var Outer: TOuterClass; Inner: TOuterClass.TInnerClass; InnerInner: TOuterClass.TInnerClass.TInnerlnnerClass; begin Outer := TOuterClass.Create; Inner := TOuterClass.TInnerClass.Create; Innerlnner := TOuterClass.TInnerClass.TInnerlnnerClass.Create; Outer.OuterProc; Inner.InnerProc; Innerlnner.InnerlnnerProc; Readln end.
Delphi позволят создать несколько обработчиков одного и того же события. Для этого используются методы Include и Exclude. Синтаксис объявления:
unit ClassUnit;
Include (bbRun.OnClick, bbRunClickl); Include (bbRun.Onclick, bbRunClick2);
После прогона на экране появятся три строки: OuterProc InnerProc InnerlnnerProc
События с несколькими слушателями
В рамках VCL-приложений обработчики событий не вызываются последовательно в порядке их перечисления, как следовало бы ожидать. В нашем примере при щелчке на кнопке bbRun сработает лишь обработчик bbRunClick2. Но если далее в коде добавить следующий оператор, то обрабатывать щелчок на кнопке станет обработчик bbRunClickl:
interface type TOuterClass = class procedure OuterProc; type TInnerClass = class type TInnerlnnerClass = class procedure InnerlnnerProc; end; procedure InnerProc; end; end;
end.
// Вложенный // Еще один
класс
Exclude(bbRun.OnClick,
bbRunClick2);
В режиме создания WinForms-приложений для динамической смены обработчикалтельзя использовать присваивания. Назначение/смена обработчиков воз можны только с помощью указанных процедур, создающих список обработчи ков, которые могут исполняться последовательно. Подробнее об обработке событий в WinForms-приложениях см. раздел «Класс Delegate — обработка со бытий» в главе 19.
156
Глава 5 • Классы
Специальные атрибуты Как уже говорилось во введении, результат работы компилятора Delphi (РЕ-файл) содержит не только управляемый код, но и метаданные, которые помогают CLR оптимизировать процесс исполнения программы. Метаданные делают сборку са моописываемым объектом. В состав Delphi входит утилита Reflection (запуска ется командой Tools • Reflection), позволяющая увидеть метаданные (рис. 5.2).
Изменения в модели классов Delphi 2005 (8) constructor TMyCustomAttribute Create(aVal: begin inherited
Create; //
157
Integer);
// Унаследованный конструктор не имеет параметров обращения!
FAttr := aVal end;
Для связывания атрибута с классом или его членом используется конструкция в стиле языка С: type [TMyCustomAttribute(10)] TClassWithAttr = class
// Инициализация атрибута // Класс с дополнительным атрибутом
end;
Для инициализации атрибута имя класса атрибута в квадратных скобках указы вается непосредственно перед классом (или членом класса), к которому отно сится атрибут. В круглых скобках за именем класса атрибута указываются все параметры, необходимые для конструктора класса. Обратите внимание: конст руктор T C u s t o m A t t r i b u t e s . C r e a t e не имеет параметров обращения, поэто му следующая реализация ошибочна: constructor begin
TMyCustomAttribute.Create(aVal: // Ошибка!
inherited; inherited Create;
// Нужно
так
Integer);
Конструктору будет передан параметр (без передачи параметра)
end; Рис. 5.2, Метаданные в окне утилиты Reflection
Программист может расширять метаданные за счет специальных атрибутов, при меняемых к агрегатам, классам, методам и пр. Установленные атрибуты можно проанализировать методами классов System.Attribute и System.Reflection. Специальный атрибут инкапсулируется в виде члена класса, являющегося на следником класса TCustomAttributes: type TMyCustomAttribute = class(TCustomAttributes) private FAttr: Integer,public constructor Create(aVal: Integer); property Attr: Integer read FAttr write FAttr; end;
Помощники класса Чтобы упростить отображение классов VCL на базовые классы .NET, разработ чики Delphi 2005 (8) создали так называемые помощники класса. Помощник класса представляет собой расширение класса, в котором объявляются члены, по тем или иным причинам не включенные в описание базового класса. Рассмотрим такой пример: type TForml = class(TForm) Buttonl: TButton; procedure private { Private public (' end;
Public
// //
ButtonlClick(Sender: declarations declarations
} }
TObject);
основной К н о п к а
на
//Обработчик
класс-форма ф о р м е
1 5 8 Глава 5 • Классы
Изменения в модели классов Delphi 2005 (8) 1 5 9
THelpClass = class Helper for TForml public procedure Hellow; end;
// Класс-помощник
var Forml: TForml;
implementation {$R *.nfm} procedure THelpClass.Hellow; begin Forml.Caption := Self.ClassType.ClassName end;
implementation {$R *.nfm} procedure THelpClass.Hellow; begin // Переменная Self относится к базовому классу TForml Caption := Self.ClassType.ClassName end; procedure TForml.ButtonlClick(Sender: TObject); begin Forml.Hellow // Обращения к методу класса-помощника end;
В результате выполнения этой программы в заголовок формы будет выведено имя класса (TForml). Расширения класса, как известно, можно достичь простым наследованием: type TForml = class(TForm) Buttonl: TButton; procedure ButtonlClick(Sender: TObject); private { Private declarations } public I Public declarations } end; THelpClass = class(TForm) public procedure Hellow; end; var Forml: TForml; HClass: THelpClass;
// Класс-наследник
procedure TForml.ButtonlClick(Sender: TObject); begin HClass := THelpClass.Create(Self); HClass.Hellow end;
Однако теперь в заголовке формы появится имя класса-наследника (THelpClass). Классы-помощники существенно облегчили адаптацию классов VCL к условиям инфраструктуры .NET. Например, в файле $(DELPHI)\Source\rtl\Borland.Delphi.System содержится такое объявление: type TObject = System.Object;
Таким образом, базовым классом VCL становится базовый класс .NET. Однако существовавший в предыдущих версиях Delphi класс TObject имел большую функциональность. Чтобы восстановить ее, в том же модуле объявляется такой класс-помощник: type TObjectHelper = class helper for TObject procedure Free; function ClassType: TClass; class function ClassName: String; class function ClassNamels(const Name: String): Boolean; class function ClassParent: TClass; class function Classlnfo: TObject; class function InheritsFrom(AClass: TClass): Boolean; class function MethodAddress(const Name: String): TObject; class function SystemType: System.Type; function FieldAddress(const Name: String): TObject; procedure Dispatch(var Message); end;
Компонентные классы
161
end; IMaylnterf асе = i n t e r f a c e ( I l n t e r f a c e ) end;
Интерфейсы
Интерфейсы играют главную роль в технологии COM (Component Object Mo del — модель компонентных объектов) и связанных с ней технологиях удаленно го доступа, то есть технологиях доступа к объектам, расположенным (и выпол няющимся) на другой машине. Их основная задача — описать свойства, методы и события удаленного объекта в терминах машины клиента, то есть на использу емом при разработке клиентского приложения языке программирования. С по мощью интерфейсов программа клиента обращается к удаленному объекту так, как если бы он был ее собственным объектом.
Объявление интерфейсов Интерфейсы представляют собой частный случай классов. Они объявляются с по мощью зарезервированного слова interface. Структура объявления такова: type <имя_интерфейса> = interface {<имя_родителя>} {['
']} <члены_интерфейса> end;
В фигурных скобках указаны необязательные части объявления. К ним относят ся имя родительского интерфейса и так называемый глобально-уникальный иден тификатор (Globally Unique Identifiers, GUID). Если опущено имя родительского интерфейса, им автоматически становится ин терфейс I l n t e r f a c e . Два следующих объявления идентичны: type IMylnterface = 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). Ин терфейсы не могут иметь конструкторы и деструкторы, так как описываемые в них методы реализуются только в рамках поддерживающих их классов, которые назы ваются компонентными (co-class).
Компонентные классы Если компонентный класс поддерживает интерфейс, имя этого интерфейса ука зывается при объявлении класса в списке его родителей: 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, IEdit, IMylnterface)
1 6 2 Глава 6 • Интерфейсы procedure Copy; stdcall; procedure Cut; stdcall; procedure Paste; stdcall; function Undo: Boolean; stdcall; procedure Delete; stdcall; end; В любом случае в разделе реализации компонентного класса необходимо опи сать соответствующие интерфейсные методы. Подобно тому, как все классы в Delphi порождены от единственного родителя TObject, все компонентные классы порождены от общего предка T l n t e r f a c e d O b j e c t . Этот предок умеет распределять память для компонентных объектов и использует глобальный интерфейс IUnknow: type TInterfacedObject = class(TObject, IUnknown) 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; Поле FRefCount этого класса служит счетчиком вызовов интерфейсного объек та и используется по принятой в Windows схеме: при каждом обращении к мето ду Add интерфейса IUnknow счетчик наращивается на единицу, при каждом об ращении к методу R e l e a s e — на единицу сбрасывается. Когда значение этого поля становится равным 0, интерфейсный объект удаляется и занимаемая им память освобождается. ПРИМЕЧАНИЕ — Если интерфейс предполагается использовать в технологиях COM/DCOM или CORBA, его мето ды должны описываться с директивой s t d c a l l .
Использование интерфейсов Пусть, например, объявлен следующий интерфейс: IPaint = interface procedure CirclePaint (Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; XI, Yl, X2, Y2: Integer); end; Пусть также объявлен поддерживающий его компонентный класс:
Использование интерфейсов
163
TPainter = class (TInterfacedObject, IPaint) procedure CirclePaint(Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; XI, Yl, X2, Y2: Integer); end; Тогда в разделе implementation следует указать реализацию методов: procedure TPainter.CirclePaint(Canva: TCanvas; X, Y, R: Integer); begin with Canva do Ellipse (X, Y, X+2*R, Y+2*R) ; end; procedure TPainter.RectPaint(Canva: TCanvas; XI, Yl, X2, Y2: Integer); begin with Canva do Rectangle(XI, Yl, X2, Y2) end; Теперь можно объявить интерфейсный объект класса T P a i n t e r , чтобы с его по мощью нарисовать окружность и квадрат: procedure T F o r m l . P a i n t B o x l P a i n t ( S e n d e r: TObject); var Painter: IPaint; begin P a i n t e r := T P a i n t e r . C r e a t e ; P a i n t e r . C i r c l e P a i n t ( P a i n t B o x l . C a n v a s , 10, 0, 10); P a i n t e r . R e c t P a i n t ( P a i n t B o x l . C a n v a s , 40, 0, 60, 20); end; Несмотря на то что интерфейс всегда объявляется до объявления поддерживаю щего его компонентного класса и, следовательно, известен компилятору, его мето ды обязательно должны быть перечислены в объявлении класса. В нашем случае простое указание классов, как это показано далее, было бы ошибкой: компилятор потребовал бы вставить описание методов C i r c l e P a i n t и R e c t P a i n t : type TPainter = class(TInterfacedObject, end;
IPaint)
Методы компонентного класса могут исполняться под именами, отличными от имен методов интерфейса. Для этого при описании компонентного класса после имени интерфейсного метода ставятся знак равенства и имя метода, который будет вызываться вместо него: TPainter = class(TInterfacedObject, IPaint) procedure IPaint.CirclePaint = CPaint;
164
Глава б • Интерфейсы
Делегирование интерфейсов
procedure IPaint.RectPaint = RPaint; procedure CPaint(Canva: TCanvas; X, Y, R: Integer); procedure RPaint(Canva: TCanvas; XI, Yl, X2, Y2: Integer); end;
type TGUID = System.GUID
Программист может объявлять типизированные константы типа TGUID, напри мер:
К интерфейсному объекту можно применить оператор приведения типов as, что бы использовать нужный интерфейс: procedure PaintObjects(P: TInterfacedObject) var X: IPaint; begin try X := P as IPaint; X.CirclePaint(PaintBoxl.Canvas,0,0,20) except ShowMessage('Объект не поддерживает интерфейс end end;
165
c o n s t I I D _ I P a i n t : TGUID = [' {A4AFEB61-7705-11D2-8B41-444553540000} ' ] ;
Константы типа GUID могут использоваться вместо имен интерфейсов при вы зове подпрограмм. Например, два следующих обращения идентичны: procedure Paint(const IID:
TGUID);
Paint (IPaint); Paint(IID_Paint);
Делегирование интерфейсов IPaint')
Встретив такое присваивание, компилятор создаст код, с помощью которого вы зывается метод Q u e r y l n t e r f a c e интерфейса IUnknow с требованием вернуть ссылку на интерфейс I P a i n t . Если объект не поддерживает указанный интер фейс, возникает исключительная ситуация.
Глобально-уникальные идентификаторы Интерфейсы, рассчитанные на использование в удаленных объектах, должны снабжаться глобально-уникальным идентификатором (GUID), например: IPaint = interface ['{A4AFEB60-7705-11D2-8B41-444553540000} ' ] procedure CirclePaint(Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; XI, Yl, X2, Y2: Integer); end;
Глобально-уникальные идентификаторы представляют собой 16-байтные двоич ные величины, которые создаются по специальной технологии, гарантирующей ничтожно малую вероятность того, что два идентификатора совпадут. В Delphi GUID записывается в виде групп шестнадцатеричных цифр: хххххххх-хххх-хххх-хххх-хххххххххххх Здесь X — цифра от 0 до 9 или буква от А (а) до F (f). Технология создания GUID включена в 32-разрядные версии Windows: чтобы по лучить GUID для вновь созданного интерфейса в среде Delphi, достаточно нажать клавиши Ctrl+Shift+G. Для работы с GUID в модуле Borland. Delphi . System объявлен следующий тип:
С помощью зарезервированного слова implements программист может делеги ровать какому-либо свойству некоторого класса полномочия интерфейса, сделав свойство уполномоченным. Это свойство должно иметь тип интерфейса или клас са. Если свойство имеет тип интерфейса, имя этого интерфейса должно указы ваться в списке родителей класса, как если бы это был интерфейсный класс: type IMylnterface = interface procedure PI; procedure P2; end; TMyClass = c l a s s ( T O b j e c t , I M y l n t e r f a c e ) FMylnterface: IMylnterface; property M y l n t e r f a c e : IMylnterface read F M y l n t e r f a c e implements I M y l n t e r f a c e ; end;
Обратите внимание: в этом примере класс TMyClass не является компонентным классом, то есть классом, в котором исполняются методы Р1 и Р2. Однако если из него убрать определение уполномоченного свойства Mylnterface, он станет ком понентным и в нем должны быть описаны методы интерфейса IMylnterface. Уполномоченное свойство обязательно должно иметь часть read. Если уполно моченное свойство имеет тип класса, класс, в котором оно объявлено, не может иметь других уполномоченных свойств.
Варианты
167
Таблица 7 . 1 . Типы возможных значений варианта
Варианты
Вариант — это тип V a r i a n t , разработанный специально для тех случаев, когда на этапе компиляции программист не может сказать, какого типа данные будут ис пользоваться в выражении или в качестве параметров вызова подпрограмм. Пере менная-вариант занимает в памяти дополнительные 2 байта, в которые помещает ся информация о действительном типе переменной. Эта информация позволяет компилятору создать код, который будет осуществлять необходимое преобразова ние типов на этапе прогона программы. В переменную-вариант можно поместить: •
целое или вещественное число;
• логическое значение; •
строку;
•
время и/или дату;
•
OLE-объект;
•
массив произвольных размерности и длины, содержащий элементы одного из перечисленных ранее типов.
Варианты при условии корректности соответствующих преобразований могут участвовать в целочисленных, вещественных и логических выражениях, а также в выражениях с временем и датой. Например, если варианту V присвоена строка ' 1 . 0 ' , то выражение 1 + V будет правильным вещественным значением 2,0. Однако, если V содержит строку ' Текст ', выражение 1 + V вызовет исключе ние E V a r i a n t E r r o r . В Delphi определены перечисленные в табл. 7.1 константы, отражающие тип по мещенных в вариант данных.
Имя
Константа
Описание
varEmpty varNull varSmalllnt varlnteger varSingle varDouble varCurrency varDate varOleStr varDispatch varError varBoolean varVar.iant varUnknow varByte varString varArray varByRef
$0000 $0001 $0002 $0003 $0004 $0005 $0006 $0007 $0008 $0009 $000A $000B $000C $0011 $0100 $0100 $2000 $4000
Нет данных Вариант содержит N u l l Целый тип S m a l l l n t Целый тип I n t e g e r Вещественный тип S i n g l e Вещественный тип D o u b l e Вещественный тип C u r r e n c y Тип дата-время OLE-строка в кодировке Unicode Указатель на OLE-объект Код ошибки Тип WordBool Тип V a r i a n t (только для вариантных массивов) Неизвестный OLE-объект Целый тип B y t e Строковый тип Вариантный массив Указатель на данные
Структура вариантного типа описывается следующим образом: TVarData = packed record VType: Word; Reservedl, Reserved2, Reserved3: Word; case Integer of varSmalllnt: (VSmalllnt: Smalllnt); varlnteger: (VInteger: Integer); (VSingle: Single); varSingle: (VDouble: Double); varDouble: varCurrency: (VCurrency: Currency) varDate: (VDate: Double); varOleStr: (VOleStr: PWideChar) varDispatch: (VDispatch: Pointer); varError: (VError: WordBool); Pointer); varString: (VString: PVarArray) varArray: (VArray: varByRef: (VPointer: Pointer); end; Как нетрудно убедиться, любая переменная вариантного типа представляет собой 16-байтную запись, содержащую 8-байтную вариантную часть, которая хранит либо собственно данные, либо их адрес (то есть указатель на динамически размещен-
168
Глава 7 • Варианты
Использование вариантов в выражениях
ные данные). В поле VType в момент создания варианта компилятор помещает признак отсутствия данных varEmpty. В работающей программе значение этого поля меняется в соответствии с текущим типом данных, размещенных в вариант ной части. Замечу, что программа не может получить прямого доступа к полям вариантной записи. Получить тип вариантных данных можно с помощью функ ции VarType (см. далее), а изменить тип — присваиванием варианту нового зна чения.
Использование вариантов в выражениях Подобно данным большинства других типов, вариантные данные можно исполь зовать в выражениях при условии корректности автоматического приведения ти пов. Если, например, варианты VI и V2 содержат целые числа, то выражение VI + V2 будет целого типа, а выражение VI /V2 — вещественного. Однако что будет, если операнды-варианты содержат различные типы данных? Однозначно ответить на это трудно — все зависит от структуры выражения. Рассмотрим такой пример:
Преобразование вариантов к данным других типов
program VarJTest;
При участии вариантов в выражениях, а также при присваивании их значений переменным других типов тип размещенных в варианте данных преобразуется по правилам, приведенным в табл. 7.2.
uses SysUtils,
{$APPTYPE CONSOLE}
Тип данных в варианте varEmpty
К целым
дата-
К строковым
/(логическим
// Целый тип
v2 := 2 0;
// Целый тип
Датавремя
Строковые Логические
Преобразование в соответствующий тип
Округление до ближайшего целого
Округление до ближайшего целого
Преобразование в целый тип
0 для F a l s e , иначе -1 (255 для B y t e )
V3 : = '30'; // Строковый тип Writeln(Vl+V2+V3); Readln end;
Преобразование в соответствующий тип
Преобразование в соответствующий тип
ПреобраПреобразование зование в соответст- в веществующий венный тип тип
0 для F a l s e , -1 для T r u e
Здесь сначала вычисляется выражение VI + V2 = 30, а затем к полученному целочисленному значению прибавляется строка, которая компилятором предва рительно приводится к целому типу. В результате получаем 60.
30.12.1899 Преобра- Преобра00:00:00 зование зование в Double в Double
Без преобразования
Преобразование в дату
Преобразование в Double
Пустая строка
Преобразование всимвольный вид
Без преобразования
' 0' для False, '-1'для True
К вещественными^
К типу время
VI := 10;
Вещественные
0
False
Целые
Borland.VCL.Variants;
var VI, V2, V3: Variant; begin
Таблица 7.2. Преобразование типов для вариантов Приводится
169
Преобразование всимвольный вид
Преобразование всимвольный вид
Изменим пример следующим образом:
False False False False Без для 0, для 0, для 0, для ' F a l s e ' п р е о б р а иначе T r u e иначе T r u e иначе T r u e и для ' 0 ' , зования иначе True
Здесь •
к целым отнесены типы v a r B y t e , v a r S m a l l l n t , v a r l n t e g e r , v a r E r r o r ;
•
к вещественным отнесены типы — v a r S i n g l e , v a r D o u b l e , v a r C u r r e n c y ;
•
к строковым отнесены типы — v a r S t r i n g , v a r O l e S t r .
var VI, V2, V3: Variant; begin VI : = '10'; // Строковый тип v2 : = '20'; // Строковый тип V3 := 3 0;
// Целый тип
Writeln(V1+V2+V3) ; Readln end; Теперь сначала будет получена строка ' 1 0 2 0 ' , к которой следует прибавить це лое число 30. Компилятор преобразует строку в целое число, в результате полу чаем 1050. Если бы вычислялось следующее выражение, компилятор тут же при вел бы каждый операнд к целому типу, и мы опять получили бы 60: Integer(VI)+V2+V3
170
Глава 7 • Варианты
Вариантные массивы
Об этих тонкостях применения вариантов не следует забывать: если вы исполь зуете выражения с операндами, везде, где это возможно, явно укажите тип ре зультата. Однако, к сожалению, это противоречит самой природе варианта, и по этому не всегда возможно. Как уже отмечалось, вариант может иметь два особых значения: v a r E m p t y и v a r N u l l (см. табл. 7.1). Первое он получает в момент создания. Оно гово рит о том, что вариант не имеет никакого значения. Второе же говорит о том, что значением варианта является ничто ( N u l l ) . Это значение широко исполь зуется в приложениях для работы с базами данных. Для проверки, содержит ли вариант значение v a r E m t y или v a r N u l l , можно использовать две опреде ленные в модуле S y s t e m константы: U n a s s i g n e d и N u l l . Если хотя бы один операнд выражения содержит значение v a r N u l l , все выражение приобретает такое же значение. Если какой-то операнд содержит значение v a r E m p t y , вы числение выражения приведет к исключительной ситуации.
Подпрограммы для работы с вариантами Для работы с вариантами можно использовать подпрограммы, перечисленные в табл. 7.3. Таблица 7.3. Подпрограммы для работы с вариантами Подпрограмма
Описание
function VarAsType (const V: Variant; const VarType : Integer) : Variant;
Преобразует данные варианта V к типу, определяемому параметром V a r T y p e (см. табл. 7.1)
procedure VarCast (var Dest: Variant;constSource: Variant; VarType: Integer);
Преобразует данные варианта S o u r c e ктипу, определяемому параметром V a r T y p e , и помещает результат в переменную D e s t
procedure VarCiear (var V: Variant)
Освобождает динамическую память, если она была связана с вариантом, и присваивает ему тип v a r E m p t y
procedure VarCopy (var Dest: Variant;const Source: Variant); function VarFromDateTirne (DateTime: TDateTime): Variant; function VarlsEmpty (const V: Variant) : Boolean; function VarlsNull (const V: Variant) : Boolean; function VarToDateTime (const V: Variant) : TDateTime); function VarToStr (const V: Variant) : String; function VarType (const V: Variant) : Integer;
Копирует параметр S o u r c e в вариант Dest Возвращает вариант, содержащий данные DateTime типа дата-время Возвращает T r u e , если вариант V не содержит данных Возвращает T r u e , если вариант V содержит данные неопределенного типа ( v a r N u l l ) Преобразует данные варианта V ктипу дата-время Преобразует данные варианта V к строке Возвращает тип хранящихся в варианте данных
171
Вариантные массивы Значением варианта может быть массив данных; такие варианты называются ва риантными массивами. (Не путайте с обычным или динамическим массивом, эле ментами которого являются варианты!) Значениями элементов вариантного мас сива могут быть любые допустимые для варианта значения. Значениями элементов вариантного массива могут быть и варианты, а это значит, что в таком массиве мо гут одновременно храниться данные разных типов, например: var V: Variant; begin // Создаем одномерный вариантный массив // с 5 элементами: V := VarArrayCreate([0, 4], varVariant); // Наполняем его: V[0] := 1; // Целый тип V[l] := 1234.5678; // Вещественный тип V[2] := 'Hello world'; // Строковый тип V [ 3] := True; // Логический тип // Пятым элементом исходного массива // сделаем еще один массив: V[4] := VarArrayOf([1, 10, 100, 1000]); Writeln(V[2]); // Hello world Writeln(V[4] [2]); // 100 end; Все действия с вариантными массивами осуществляются с помощью процедур и функций, перечисленных в табл. 7.4. Таблица 7.4. Подпрограммы для работы с вариантными массивами Подпрограмма
Описание
function VarArrayCreate (const Bounds : array of Integer;
Создает вариантный массив из элементов типа VarType с количеством и границами измерений, определяемыми параметром Bounds Возвращает количество измерений вариантного массива А или 0, если А— не массив Создает новый вариантный массив со строками WideString из аргумента S t r i n g s Возвращает единственное значение из многомерного вариантного массива: c o n s t A V a l u e — многомерный вариантный массив; A I n d i c e s — индексы ячейки (по одному на каждое измерение) Возвращает верхнюю границу индекса вариантного массива А по измерению Dim
VarType: Integer) : Variant; function VarArrayDimCount (const A: Variant) : Integer; function VarArrayFromStrings (Strings: TStrings) : Variant; function VarArrayGet (const AValue : Variant, AIndices:array of Integer) : Variant; function VarArrayHighBound (const A: Variant; Dim: Integer) : Integer;
ППП ЛП П Wf^ULIP
172
Пользовательские варианты
Глава 7 • Варианты
Таблица 7.4 (продолжение) Подпрограмма
Описание
function VarArrayLowBound (const A: Variant; Dim: Integer) : Integer; function VarArrayOf (const Values array of Variant) : Variant;
Возвращает нижнюю границу индекса вариантного массива А по измерению Dim
procedure VarArrayPut (var AValue : Variant, const AData: Variant, const AIndices : array of Integer) ;
Создает одномерный вариантный массив по перечню значений, содержащихся в открытом массиве V a l u e s . Нижняя граница индексов вариантного массива в этом случае равна О Устанавливает значение AData единственной ячейки многомерного вариантного массива A V a l u e ; A I n d i c e s — индексы ячейки (по одному на каждое измерение)
Пользовательские варианты В Delphi можно создавать так называемые пользовательские варианты, которые фактически снимают ограничения на характер значений варианта. Чтобы познакомиться со свойствами новых вариантов, воспользуемся одним из них — вариантом, способным хранить комплексные числа, преобразовывать их в другие типы и осуществлять над ними нужные действия. Как мы увидим, создание пользовательского варианта может быть весьма трудоемким делом — все зависит от сложности хранимых в нем данных. Мы воспользуемся вариан том, созданным разработчиками Delphi и включенным в модуль Borland. VCL. VarCmplx, — этот вариант способен хранить как стандартные для других вари антов значения (числа, строки, OLE-объекты и т. д.), так и комплексные числа. Создайте такую консольную программу: program Var_Complex; {$APPTYPE CONSOLE} uses SysUtils, Borland.VCL.Variants, Borland.VCL.VarCmplx;
// Эта
ссылка обязательна!
var VI, V2: Variant; begin // Создаем два случайных комплексных числа: VI := VarComplexCreate(Trunc(Random * 1000) / 100, Trunc(Random * 1000) / 100); V2 := VarComplexCreate(Trunc(Random * 1000) / 100, Trunc(Random * 1000) / 100); Writeln(4-e число: '#9 + VI);
173
Writeln('2-e число: '#9 + V2); Writeln('Сложение'#9 + (VI + V2)); Writeln('Вычитание'#9 + (VI - V2)); Writeln('Умножение'#9 + (VI * V2) ) ; Writeln('Деление'#9#9 + (VI / V2)); end;
Сложная конструкция Trunc (Random* 1000) /100 понадобилась только для того, чтобы реальные и мнимые части комплексных чисел содержали по три значащех цифры. Как видим, новый вариант легко справляется с поддержкой комплексных чисел: функция VarComplexCreate создает вариант, содержащий комплексное число, а дальнейшее поведение варианта стандартное (он поддерживает математичес кие операции и преобразование к строковому типу). Однако эта легкость обман чива: исходный текст модуля Borland.VCL.VarCmplx, который, собственно, и придал варианту дополнительные свойства (по умолчанию располагается в фай ле Source\Rtl\Borland.VCLVarCmplx.pas каталога размещения Delphi), занимает бо лее 12 000 байт.
Текстовые файлы
Файлы
Под файлом понимается именованная область внешней памяти компьютера (же сткого диска, дискеты, компакт-диска и т. п.). Методика работы с файлами, использовавшаяся в ранних версиях Delphi, не ме нялась еще со времен Turbo Pascal и явно устарела. При подготовке версии Delphi 8 разработчики Borland переделали ее, взяв за основу классы из библио теки CTS. Однако некоторые возможности предыдущих версий сохранились, так что перед программистом открывается одновременно несколько путей решения одной и той же задачи1. В этой главе я не ставил перед собой задачу описать все возможности работы Delphi с файлами и файловой системой. Тем не менее излагаемый в ней матери ал достаточен для осуществления любых действий с файлами.
Особенности файлов Любой файл имеет две характерные особенности. Во-первых, у него есть имя, что дает возможность программе работать одновременно с несколькими файла ми. Во-вторых, размер вновь создаваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти. В Delphi различают файлы текстовые и двоичные. Это различие проявляется в том, что помещать информацию в текстовый файл и извлекать ее из него мож но лишь последовательно — от первой строки к последней. Такие файлы называ ются файлами с последовательным доступом. В двоичные файлы информация помещается (и извлекается из них) произвольно. Такие файлы называются фай лами с произвольным доступом. 1
Напомню, что среда Delphi 2005 сохранила все возможности предыдущих версий, в том числе под держку старых приемов работы с файлами.
175
Различие в способах доступа диктуется особенностями хранения информации во внешней памяти. Эта информация распределяется по участкам фиксирован ной емкости, которые называются секторами. За одно обращение к внешней па мяти можно прочитать или записать не менее одного сектора (стандартная ем кость сектора обычно составляет 512 байт). Таким образом, возникает проблема согласования размера единичной записи файла (строки, числа, объекта и т. п.) с размером сектора. В текстовых файлах последовательного доступа эта задача решается так: конец записи помечается специальным признаком — комбинацией байтов, которая заведомо не может встретиться в самой записи. Эта комбинация называется EOL (End Of Line — конец строки). Очевидно, что двоичные файлы могут иметь в содержательной части записи любые комбинации файлов, в том числе и EOL. В этом случае программа должна сама подсчитывать длину запи си. Если, например, в файл записывается последовательность целых чисел, дли на единичной записи составляет 4 байта, а если последовательность веществен ных — 4, 8 или 12, в зависимости от того, записываются числа типов S i n g l e , R e a l или E x t e n d e d . В старой технологии работы с файлами (версий Delphi до 7-й включительно) существовали так называемые типизированные файлы. Они описывались как фай лы того или иного типа ( f i l e of aType, например f i l e of I n t e g e r ) . Фикси рованная длина типа позволяла автоматизировать согласование размера единич ной записи с размером сектора носителя информации. В технологии .NET типизированные файлы запрещены. Для работы с произвольными файлами ис пользуется техника потоков данных (stream). Далее в этой главе рассматриваются текстовые и двоичные файлы, а также при емы работы с файловой системой. Изложение строится так, что вначале описы ваются возможности устаревшей (но все еще разрешенной) технологии, а затем — приемы работы с классами .NET Framework. Такой подход, с одной стороны, ос тавляет программисту право выбора, а с другой — облегчает освоение новой тех нологии. Еще раз замечу, что в .NET Framework существует множество классов, способных решать одну и ту же задачу. Выбор описываемых в главе конкретных классов — всего лишь дело вкуса автора.
Текстовые файлы Как уже отмечалось, характерной особенностью текстовых файлов является воз можность использования в них специальной метки конца очередной строки — так называемого признака EOL. В Windows в качестве такового используется комбинация байтов $0D0A (13 и 10 в десятичном исчислении); первый байт на зывается CR (Carriage Return — возврат каретки), второй — LF (Line Feed — пе ревод строки). Эта комбинация не может вставляться в записываемую строку. ПРИМЕЧАНИЕ В силу того, что в Delphi 2005 (8) используется двухбайтная кодировка символов Unicode, опи сываемые в этом разделе средства рассчитаны именно на строки, составленные из двухбайт ных символов.
176
Глава 8 • Файлы
Текстовые файлы 1 7 7
Текстовый файл можно задать следующим образом:
Reset(F);
<имя ф.п.>: TextFile; Здесь <имя ф. п. > — имя так называемой файловой переменой (правильный иден тификатор); T e x t F i l e — имя стандартного типа текстовых файлов. Например: var F: TextFile; Перед использованием файловая переменная должна инициализироваться. Эта процедура заключается в связывании файловой переменной с именем существу ющего или вновь создаваемого файла, а также в указании направления обмена информацией: чтение из файла или запись в него. Файловая переменная связывается с именем файла в результате обращения к стан дартной процедуре A s s i g n F i l e :
while not EOF(F) do begin ReadLn(F, S ) ; Writeln(S) end; CloseFile(F); Readln end;
// Открываем файл для чтения // Читаем до конца файла// //
Читаем Выводим
//
Закрываем
очередную ее
строку
файл
ПРИМЕЧАНИЕ Процедуры R e a d l n и W r i t e l n могут читать информацию с клавиатуры компьютера и выво дить ее на экран. Однако если первым параметром обращения к любой из них указана файло вая переменная, осуществляется обмен информацией с файлом. Например: R e a d l n (S) читает информацию с клавиатуры; R e a d l n ( F , S) читает очередную строку из текстового файла связанного с переменной F. '
AssignFile (<ф.п.>, <имя файла>); Здесь < ф . п . > — файловая переменная; <имя файла> — текстовое выражение, содержащее имя файла и, если это необходимо, маршрут доступа к нему. Для чтения файл инициализируется с помощью стандартной процедуры R e s e t : Reset
(<ф.п.>);
Здесь <ф. п. > — файловая переменная, связанная ранее процедурой A s s i g n F i l e с уже существующим файлом. При выполнении этой процедуры дисковый файл подготавливается к чтению информации. В результате специальная переменная-указатель, связанная с этим файлом, будет указывать на начало файла, то есть на строку с порядковым номе ром 0. После инициализации существующего файла прочитать из него первую и после дующие строки можно процедурой ReadLn. Следующая программа читает стро ки из модуля проекта:
{$APPTYPE CONSOLE}
Для определения конца файла проверяется логическая функция EOF (End Of File — конец файла), которая тестирует связанный с файловой переменной файл и возвращает True, если файл исчерпан. Следующая стандартная процедура инициирует запись информации в текстовый файл, связанный с файловой переменной <ф. п. >: Rewrite
•
var F: TextFile; S: String; begin
Для записи информации используется процедура WriteLn. ПРИМЕЧАНИЕ
Append
Получаем имя
(<ф.п.>);
Процедурой R e w r i t e нельзя инициировать запись информации в ранее существо вавший файл: при выполнении этой процедуры старый файл (если он был) унич тожается и никаких сообщений об этом в программу не передается. Новый файл подготавливается к приему информации, и его указатель принимает значение 0. Следующая стандартная процедура Append инициирует запись в ранее существо вавший текстовый файл для его расширения, при этом указатель файла устанав ливается в его конец:
uses SysUtils;
файла
проекта:
Readln(S); A s s i g n F i l e (F,
Если делается попытка инициировать чтение из несуществующего файла, возбуждается ис ключительная ситуация. Чтобы проверить, существует ли дисковый файл, можно использовать стандартную функцию F i l e E x i s t s , которая возвращает T r u e , если указанный при обраще нии к этой функции файл существует, и F a l s e — если не существует.
Если текстовый файл открыт для чтения информации, к нему нельзя применять процедуру W r i t e L n , и наоборот, при записи файла нельзя использовать процедуру ReadLn.
program TextFile_Demo;
//
СОВЕТ -
S);
//
Связываем
файловую
переменную
(<ф.п.>)
Если текстовый файл ранее уже был открыт с помощью процедуры R e s e t или H e w r i t e , использование процедуры Append приведет к закрытию этого файла и открытию его вновь, но уже для добавления записей.
178
Глава 8 • Файлы
В технологии .NET не делается принципиального различия между текстовым и двоичными файлами. Вместо этого используются две группы классов — для чтения информации (XXXXReader) и для ее записи (XXXXWriter). В каждом из подобного рода классов есть методы для чтения/записи отдельных символов, строк, блоков данных. Рассмотренный ранее пример чтения информации из проектного файла силами только классов .NET Framework можно решить так: uses System.10; procedure TForml.ButtonlClick(Sender: TObject); var F: StreamReader; S: Variant; begin S := ChangeFileExt(Application.ExeName, 'dpr'); F := StreamReader.Create(S); // Экземляр потока для чтения repeat // Бесконечный цикл чтения S := F.ReadLine;. if VarType(S) = VarEmpty then Break; // Файл исчерпан! Memol.Lines.Add(S) ; until True; F.Close; // Закрываем файл F.Free // Уничтожаем поток end;
ПРИМЕЧАНИЕ Представленный обработчик написан в VCL-программе (команда File • New • VCL Forms App lication - Delpni for .NET). На форме размещены многострочное текстовое поле Memol и кнопка B u t t o n l . Объект F класса StreamReader при обращении к его методу R e a d L i n e возвра щает очередную строку или признак N o t h i n g , если файл исчерпан. К сожалению, в Delphi нет возможности проверить этот признак, если приемником информации служит строка. Но если читать информацию в переменную типа V a r i a n t , этот признак проверяется таким опе ратором: if VarType(S) = varEmpty then ... Как видите, обе программы, несмотря на внешнее сходство, различаются прин ципиально: в первой используются более или менее независимые подпрограм мы, которые координируются с помощью файловой переменной. Такой подход типичен для Win32 API. Во второй создается объект определенного класса, ко торый содержит все необходимые поля, методы и события. Для смены направле ния перемещаемых данных в первой программе нужно поменять одну процеду ру, а во второй — класс объекта. В следующем примере приводятся два варианта VCL-программы для записи и повторной записи текстового файла: uses System.10;
Текстовые файлы
179
procedure TForml.bbRunClick(Sender: TObject); // Использование классов .NET Framework var F: StreamWriter; S: String; begin S := 'Pasternack'; // Название файла F := StreamWriter.Create(S); // Объект для записи строк F.WriteLine('В занавесках кружевных'); // Первая строка F.WriteLine('Воронье.'); // Вторая строка F.Close; // Закрываем файл F.Free; // Разрушаем объект S := 'Pasternack'; // Название файла F := &File.AppendText(S); // Добавляем строки! F.WriteLine('Ужас стужи уж и в них'); F.WriteLine('Заронен.'); F.Close; F.Free; end; Любопытная деталь: название класса F i l e пространства имен S y s t e m . 10 со впадает с зарезервированным словом Delphi. Чтобы компилятор на считал это слово зарезервированным, ставим перед ним символ амперсанта. Теперь вариант с файловой переменной: procedure TForml.bbRunClick(Sender: TObject); // Использование файловой переменной var F: TextFile; S: String; begin S := 'Pasternack'; // Название файла AssignFile (F, S ) ; // Связывание переменной с файлом Rewrite(F); // Создаем новый файл WriteLn(F, 'Это кружится Октябрь,'); WriteLn(F, 'Это жуть'); CloseFile(F); // Закрываем файл Append(F); // Открываем файл для расширения WriteLn(F, 'Подступила на когтях'); WriteLn(F, 'К этажу.'); // Добавляем строки CloseFile(F) // Закрываем файл end; Оба варианта равноценны с точки зрения конечного результата, но вариант с фай ловой переменной кажется чуть проще.
180
Глава 8 • Файлы
Двоичные файлы
Двоичные файлы Д л я работы с двоичными файлами можно использовать VCL-класс T F i l e S t r e a m или CTS-классы. Сначала рассмотрим работу с объектами T F i l e S t r e a m (они определены во всех версиях Delphi,.кроме версии 1). Класс T F i l e S t r e a m является наследником класса T S t r e a m и рассчитан на работу с файлами. Основные свойства и методы класса перечислены в табл. 8.1. Таблица 8 . 1 . Свойства и методы класса TFileStream Член класса
Назначение
constructor C r e a t e (const FileName: s t r i n g ; Mode: Word) ;
Создает объект и связывает его с именем файла FileName. Параметр Mode определяет характер overload; работы с файлом (см. далее) f u n c t i o n CopyFrom ( S o u r c e : T S t r e a m ; Копирует не более Count байтов из потока Count: Int64) : I n t 6 4 ; S o u r c e в текущую позицию текущего файла property P o s i t i o n : I n t 6 4 ; Определяет текущую позицию файла f u n c t i o n Read (var B u f f e r ; C o u n t : Читает из файла не более Count байтов Longint) :Longint; override; в буфер B u f f e r f u n c t i o n Seek ( O f f s e t : L o n g i n t ; Смещает текущую позицию файла на O f f s e t O r i g i n : Word) : L o n g i n t ; o v e r l o a d ; байтов. Параметр O r i g i n определяет начало virtual; смещения: s o F r o m B e g i n n i n g — о т начала файла; s o F r o m C u r r e n t — от текущей позиции; soFromEnd— от конца файла property S i z e : I n t 6 4 ; Текущий размер файла в байтах function Write (const Buffer; Записывает в файл Count байтов Count: Longint) : Longint; override; и з буфера B u f f e r _^ Методы C o p y F r o m , R e a d и W r i t e осуществляют нужную операцию, возвраща ют количество действительно переданных байтов и смещают текущую позицию файла на эту величину. При создании объекта параметр Mode должен иметь одно из следующих значе ний: •
f m C r e a t e — создается файл с указанным именем; если файл уже существует, он открывается в режиме f m O p e n W r i t e ;
•
f m O p e n R e a d — файл открывается только для чтения;
•
f m O p e n W r i t e — файл открывается только для записи;
•
f m O p e n R e a d W r i t e — файл открывается для модификации (для чтения и за писи одновременно).
При необходимости значение Mode можно объединять операцией or с одним из следующих значений, определяющих доступность файла другим процессам: •
f m S h a r e C o m p a t — разрешен полный доступ;
•
f m S h a r e E x c l u s j - v e — доступ запрещен;
•
f m S h a r e D e n y W r i t e — доступ запрещен для записи;
•
f m S h a r e D e n y R e a d — доступ запрещен для чтения;
•
f m S h a r e D e n y N o n — доступ запрещен.
181
В методах R e a d и W r i t e в Delphi разрешается опускать параметр C o u n t , если B u f f e r принадлежит одному и з стандартных простых типов ( C h a r , I n t e g e r , R e a l и т. п.). В следующем примере (листинг 8.1) создается файл, содержащий 15 случайных целых чисел. Затем ф а й л модифицируется: в нем осуществляется восходящая сортировка чисел методом «всплывающего пузырька». Исходный и отсортиро ванный массивы выводятся на экран. Листинг 8 . 1 . Восходящая сортировка чисел методом «всплывающего пузырька» program S o r t ; {$APPTYPE CONSOLE} uses SysUtils, Borland.VCL.Classes; var IsSorted: Boolean; N1, N2: Integer; Offl, Off2: Integer; k: Integer; F: TFileStream; const ~~ •'•-'' FileName = 'sort.dat'; begin
// //
Признак окончания сортировки Два соседних числа // Смещение к ним от начала файла
// Количество // Имя файла
чисел
в
файле
F := TFileStream.Create(FileName, fmCreate); // Создаем файл Writeln('Исходный файл:'); for к := 1 to N do begin
//
Наполняем
файл
N1 := Random(100); F.Write(Nl); Write (N1:4); end;
// // //
Очередное число записываем его в файл выводим на экран и
// Закрываем файл // и открываем для изменения: F := TFileStream.Create(FileName, fmOpenReadWrite); repeat // ц и к л сортировки IsSorted := True; Offl := 0; Off2 := SizeOf(Integer); Free;
ППП ЛП П Wf=4-1L4C>
182 Глава 8 • Файлы
Двоичные файлы
Листинг 8.1 (продолжение) repeat // Проход по всей парам чисел F.Seek(Off1, soFromBeginning); F.Read(Nl); // Первое число F.Read(N2); // Соседнее число if N2 <• N1 then // Следующее число меньше? begin // Да. Меняем числа местами F.Seek(Offl, soFromBeginning); F.Write(N2); F.Write (N1); IsSorted := False; // Сбрасываем признак сортировки end; Offl := 0ff2; // Новые смещения 0ff2 := 0ff2 + SizeOf (Integer); until Off2 >= F.Size // Пока не исчерпаем файл until IsSorted; // Пока не будет ни одной перестановки F.Free; // Переоткрываем файл для чтения F := TFileStream.Create(FileName, fmOpenRead); Writeln; Writeln('Отсортированный файл:'); for к := 1 to N do // Выводим begin F.Read(Nl); Write (N1:4); end; F.Free; Readln end.
результат
Для работы с двоичными файлами средствами .NET Framework нужно подгото вить как минимум два объекта: собственно поток и механизм доступа к нему. Поток представляет экземпляр класса F i l e S t r e a m , а механизм доступа к нему — экземпляры типов B i n a r y R e a d e r и/или B i n a r y W r i t e r . В листинге 8.2 показан предыдущий пример, реализованный средствами .NET Framework. Листинг 8.2. Восходящая сортировка чисел методом «всплывающего пузырька», реализованная средствами .NET Framework program S o r t 2 ; {$APPTYPE CONSOLE} uses SysUtils, System.10;
183
var FS: FileStream; // Файловый поток BW: BinaryWriter; // Объект для записи в него BR: BinaryReader; // Объект для чтения из потока k: Integer; N1, N2: Integer; // Два соседних числа Off: Integer; // Смещение к первому IsSorted: Boolean; // Признак завершения сортировки const N = 15; // Количество чисел в файле FileName = 'sort.dat'; // Имя файла begin // Создаем поток: FS := FileStream.Create(FileName, FileMode.Create) ; BW := BinaryWriter.Create(FS); // Объект для записи for к := 1 to N do // Наполняем файл begin N1 := Random(lOO); BW.Write(N1); Write(N1:4); end; BW.Close; // Закрываем файл Writeln; // и вновь открываем для чтения и записи: FS := FileStream.Create(FileName, FileMode.Open); BR := BinaryReader.Create (FS); // Объект чтения BW := BinaryWriter.Create (FS); // Объект записи repeat // Цикл сортировки Off := 0; IsSorted := True;
// // //
Смещение Признак
к
завершения
числу сортировки
repeat FS.Seek(Off, SeekOrigin.begin); N1 := BR.Readlnt32; // Читаем два соседних числа N2 := BR.ReadInt32; if N1 > N2 then // Второе меньше? begin // -да. Меняем числа местами FS.Seek(Off, SeekOrigin.Begin); BW.Write(N2); BW.Write(N1); IsSorted := False; end; Off := Off + SizeOf(Integer); // Новое смещение продолжение &
184
Глава 8 • Файлы
Подпрограммы для работы с файлами и файловой системой
Л и с т и н г 8.2 (продолжение) until Off >= (N - 1)*SizeOf(Integer) until IsSorted; // Выводим результат FS.Seek(0, SeekOrigin.begin); for к := 1 to N do begin N1":= BR.ReadInt32; Write(N1:4); end; BR.Close; BR.Free; BW.Close; BW.Free; FS.Free; Readln end.
Подпрограммы для работы с файлами и файловой системой В табл. 8.2 описываются процедуры и функции, которые часто используются с файлами любого вида. В таблице указаны только «родные» подпрограммы сре ды Delphi, которые подходят для любой ее версии (и для версий 2005(8) в том числе). Замечу, что .NET Framework имеет набор классов и подпрограмм в про странствах имен System и System. 10, предназначенных для управления фай лами и файловой системой. Таблица 8.2. Подпрограммы для работы с файлами и файловой системой Подпрограмма
Описание
function ChangeFileExt (const FileName,Extension: String): String;Extension procedure ChDir (Path : String);
Заменяет существующее расширение файла расширением, заданным параметром
function DateTimeToFileDate (DateTime: TDateTime) : Integer; function DeleteFile (lpFileName: String) :'LongBool; function DiskFree (D: Byte) : Longlnt;
Изменяет текущий каталог: P a t h — строковое выражение, содержащее путь к устанавливаемому по умолчанию каталогу Преобразует значение D a t e T i m e в системный формат времени создания (обновления) файла Удаляет файл l p F i l e N a m e Возвращает объем в байтах свободного пространства на указанном диске: D — номер диска (0 — устройство по умолчанию, 1 — диск А, 2 — диск В и т . д.). Функция возвращает значение - 1 , если указан номер несуществующего диска
185
Подпрограмма
Описание
f u n c t i o n D i s k S i z e (D: B y t e ) : Longlnt;
Возвращает объем в байтах полного пространства на указанном диске: D — номер диска (0 — устройство по умолчанию, 1 — диск А, 2 — диск В и т . д.). Функция возвращает значение - 1 , если указан номер несуществующего диска Тестирует конец текстового файла и возвращает T r u e , если файловый указатель стоит в конце файла. При записи это означает, что очередная строка будет добавлена в конец файла, при чтении — что файл исчерпан Уничтожает текстовый файл F. Перед выполнением процедуры необходимо закрыть файл. В ряде случаев вместо процедуры E r a s e удобнее использовать функцию D e l e t e F i l e , которая не требует предварительного связывания имени файла с файловой переменной
f u n c t i o n EOF (var F: T e x t F i l e ) : Boolean;
p r o c e d u r e E r a s e (var F : T e x t F i l e ) ;
function E x c l u d e T r a i l i n g B a c k s l a s h ( c o n s t S: S t r i n g ) : S t r i n g ;
Исключает из строки s замыкающий символ \ (если этот символ не замыкает строку, возвращает S без изменения)
function ExpandFileName Дополняет имя файла текущим каталогом (constFileName: String): String; (и диском) function E x t r a c t F i l e D i r Извлекает из полного имени файла маршрут (const FileName: S t r i n g ) : String; доступа к нему (без последнего символа \) function E x t r a c t F i l e D r i v e Извлекает из полного имени файла имя диска ' (const FileName: S t r i n g ) : String; function E x t r a c t F i l e E x t Извлекает из полного имени файла (const FileName: S t r i n g ) : String; его расширение (с ведущей точкой) function ExtractFileName Извлекает из полного имени файла его имя (const FileName: S t r i n g ) : String; (с расширением) function E x t r a c t F i l e P a t h Извлекает из полного имени файла маршрут (const FileName: String) : String; доступа к нему (с последним символом \) function E x t r a c t R e l a t i v e P a t h Извлекает из полного имени файла маршрут ( c o n s t BaseName, DestName: S t r i n g ) : относительно DestName (промежуточные String; каталоги заменяются символами . . \) functionExtractShortPathName Преобразует имя файла к короткому формату 8.3 (const FileName: String) : String; для MS-DOS и Windows 3.x function FileAge Для файла F i l e N a m e возвращает время его ( c o n s t F i l e N a m e : S t r i n g ) : I n t e g e r ; последнего обновления (в системном формате) или - 1 , если такого файла не существует function F i l e E x i s t s Возвращает T r u e , если файл с именем ( c o n s t F i l e N a m e : S t r i n g ) : B o o l e a n ; (и, возможно, маршрутом доступа) F i l e N a m e существует function FileDateToDateTime Преобразует системный формат F i l e D a t e ( F i l e D a t e : I n t e g e r ) : TDateTime; времени создания файла в формат дата-время function FileGetDate По заданному дескриптору файла H a n d l e (Handle: I n t e g e r ) : I n t e g e r ; возвращает время и дату его создания в системном формате. Возвращает 0 в случае успеха или код ошибки продолжение &
186
Глава 8 • Файлы
Подпрограммы для работы с файлами и файловой системой
Т а б л и ц а 8 . 2 (продолжение) Описание
Подпрограмма
function F i l e S e t D a t e
(Handle:
I n t e g e r ; Age: I n t e g e r ) : I n t e g e r ; function F i n d F i r s t (const Path: String;Attr: I n t e g e r ; varF: TSearchRec) : I n t e g e r ;
procedure FindClose (var F: TSearchRec); function FindNext (varF: TSearchRec) : I n t e g e i
function I n c l u d e T r a i l i n g B a c k s l a s h ( c o n s t S : S t r i n g ) : String; function I s P a t h D e l i m i t e r (const S: String; Index: I n t e g e r ) : Boolean; function MatchesMask (const Filename, Mask: S t r i n g ) : Boolean; procedure MkDir (Dir : String) ;
procedure P r o c e s s P a t h (const E d i t T e x t : String; var Drive: Char; v a r D i r P a r t : String; v a r F i l e P a r t : String); procedure Rename (varF: T e x t F i l e ; NewName: String)
procedure RmDir (Dir: String) ;
Для файла с дескриптором H a n d l e устанавливает новое время и дату его создания Age в системном формате. Возвращает О в случае успеха или код ошибки Возвращает атрибуты первого из файлов, зарегистрированных в указанном каталоге: P a t h — маршрут поиска и маска выбора файлов; A t t r — атрибуты выбираемых файлов; F — переменная типа T S e s r c h R e c , в которой будет возвращено имя первого выбранного файла. При успешном поиске возвращает значение О Освобождает память, выделенную для поиска файлов функциями F i n d F i r s t и F i n d N e x t Возвращает в переменой F имя следующего файла в каталоге. Переменная F должна предварительно инициализироваться обращением к функции F i n d F i r s t . При успешном поиске возвращает значение О Возвращает полный маршрут доступа к файлу с ведомым символом \ Возвращает T r u e , если символ I n d e x в строке S является символом \ Возвращает T r u e , если имя F i l e N a m e соответствует групповому имени Mask Создает новый каталог на указанном диске: D i r — маршрут поиска каталога. Последним именем в маршруте, то есть именем вновь создаваемого каталога, не может быть имя уже существующего каталога Возвращает имя диска, маршрут поиска и имя файла в перемененных D r i v e , D i r P a r t и F i l e P a r t соответственно; E d i t T e x t — полное имя файла Переименовывает текстовый файл F; NewName — строковое выражение, содержащее новое имя файла. Перед выполнением процедуры необходимо закрыть файл Удаляет каталог D i r . Удаляемый каталог должен быть пустым, то есть не содержать файлов или имен каталогов нижнего уровня
Подпрограммы F i n d F i r s t , F i n d N e x t и F i n d C l o s e позволяют получить дос туп к группе файлов, объединенных общими признаками. Эти признаки при об ращении к функции F i n d F i r s t указываются маской выбора файлов и их атри бутами.
187
При формировании маски выбора файлов могут использоваться следующие сим волы-заменители: • звездочка (*) означает, что на месте этого символа может стоять сколько угод но (в том числе ноль) разрешенных символов имени или расширения файла; • знак вопроса (?) означает, что на месте этого символа может стоять один из разрешенных символов. Например; • •
* . * — все файлы из каталога; с* .* — все файлы с именами, начинающимися на с (cl.pas, ccl2345, c.dat и т. д.);
• а? . d a t — имена файлов типа a0.dat, az.dat и т. д. Маске выбора может предшествовать маршрут поиска файлов. Например, ко манда может выглядеть так: С: \Dir\SubDir\*.pas Эта команда означает выбор всех файлов с расширением .pas из каталога SubDir, находящегося на диске С; каталог SubDir зарегистрирован в каталоге верхнего уров ня Dir, который, в свою очередь, входит в корневой каталог. Если маршрут не указан, файлы ищутся в текущем каталоге. Параметр A t t r при обращении к F i n d F i r s t содержит двоичные разряды (биты), уточняющие, к каким именно файлам разрешен доступ. Вот как объявляются файловые атрибуты в модуле S y s U t i l s : const faReadOnly = $01 faHidden = $02 faSysFile = $04 faVolumelD = $08 f a D i r e c t o r y = $10 faArchive = $20 faAnyFiie = $3F
// Только чтение // Скрытый файл // Системный файл // Идентификатор тома // Имя вложенного каталога // Архивный файл // Любой файл
Комбинацией битов в этом байте можно указывать самые разные варианты, на пример, $0 6 - выбор всех скрытых и/или системных файлов. Результат работы процедуры F i n d F i r s t возвращается в переменной типа TSearch Rec. Этот тип определяется следующим образом: type TSearchRec = record ' Time : Integer; Size : Integer; Attr : Integer; Name : TFileName; ExcludeAttr: Integer; FindHandle : THandle;
1 8 8 Глава 8 • Файлы FindDate : Twin32FindDate; end; Здесь A t t r — атрибуты файла (см. ранее); Time — время и дата создания или последнего обновления файла в системном формате; S i z e — длина файла в бай тах; Name — имя и расширение файла; F i n d D a t e — переменная с дополнитель ной информацией о файле (время создания, время последнего доступа). Результат обращения к процедуре F i n d F i s t возвращается в значении типа I n t e ger, которое равно 0, если нет ошибок. Следующая простая VCL-программа иллюстрирует способ использования функ ций F i n d F i r s t и FindNext. Программа выводит в окно многострочного тек стового поля mmOutput список всех файлов, маска выбора которых (и, возмож но, маршрут поиска) указана в поле e d l n p u t : procedure TfmExample.bbRunClick(Sender: TObject); var Mask: String; SR: TSearchRec; begin Mask := edlnput.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;
Модули и пространства имен
Стандартный язык Pascal не предусматривает механизмов раздельной компиля ции частей программы с последующей их сборкой перед выполнением. Более того, последовательное проведение в жизнь принципа обязательного описания любого объекта перед его использованием делает практически невозможной разработку разнообразных библиотек прикладных программ. Точнее, такие библиотеки в рам ках стандартного языка Pascal могут существовать только в виде исходных тек стов, и программист должен сам включать в программу подчас весьма обширные тексты различных поддерживающих процедур, таких как процедуры матричной алгебры, численного интегрирования, математической статистики и т. п. Вполне понятно поэтому стремление разработчиков коммерческих компилято ров языка Pascal включать в язык средства, повышающие его модульность. Чаще всего таким средством является разрешение использовать внешние процедуры и функции, тело которых заменяется стандартной директивой external. Разра ботчики Delphi пошли в этом направлении еще дальше, включив в язык меха низм так называемых модулей. Модуль — это автономно компилируемая программная единица, включающая в себя различные компоненты интерфейсного раздела (типы, константы, пере менные, процедуры и функции) и, возможно, некоторые исполняемые операто ры инициализирующего раздела. Появление объектов в интерфейсной части делает их доступными для других модулей и основной программы. Тела проце дур и функций располагаются в исполняемой части модуля, которая может быть скрыта от пользователя. Роль модулей в Delphi не исчерпывается только механизмом раздельной компи ляции. В Delphi модуль является контейнером пространства имен. В .NET Frame work контейнером пространства имен является сборка (assembly). Таким обра зом, в Delphi роль сборки выполняет модуль.
190
Глава 9 • Модули и пространства имен
Заголовок модуля и связь модулей друг с другом
Пространства имен Пространства имен — это иерархические конструкции, содержащие классы, ин терфейсы, записи (структуры), перечисления. Иерархия достигается указанием точки между двумя соседними именами, например: S y s t e m . 10, S y s t e m . W i n dows. Forms. Корневым пространством имен в .NET Framework является про странство System. С другой стороны, каждый проект порождает собственное пространство имен. Если создается проект M y P r o j e c t , автоматически создается пространство имен M y P r o j e c t . Если к проекту добавляется модуль Myunit, появляется пространство имен M y P r o j e c t .Myunit и т. д. При ссылке на член пространства имен можно указывать полное имя члена, на пример S y s t e m . 1 0 . F i l e — класс F i l e в пространстве имен 10, которое, в свою очередь, входит в пространство имен System. Если составное имя пространства слишком велико, можно использовать псевдо ним имени: uses MyCompany.AVeryLongNamespaceDesignation.unitName
as
aunit;
Псевдоним — обычный идентификатор Delphi. Он не может содержать точек. В программе псевдоним локализуется в модуле, в котором он объявлен, и может использоваться вместо длинного составного идентификатора пространства имен: WriteLn(aunit.SomeString) Это эквивалентно такому оператору: WriteLn(MyCompany.AVeryLongNamespaceDesignation.unitName.SomeString)
Структура модулей Модуль имеет следующую структуру: unit <имя>; interface интерфейсная часть> implementation <исполняемая часть> initialization инициализирующая часть> finalization Оавершающая часть> end. Здесь u n i t — зарезервированное слово (единица), начинающее заголовок моду ля; <имя> — имя модуля (правильный идентификатор) или имя объявляемого им пространства имен; i n t e r f a c e — зарезервированное слово (интерфейс), на чинающее интерфейсную часть модуля; i m p l e m e n t a t i o n — зарезервированное слово (выполнение), начинающее исполняемую часть; i n i t i a l i z a t i o n — за резервированное слово (инициализация), начинающее инициализирующую часть
191
модуля; f i n a l i z a t i o n — зарезервированное слово (завершение), начинающее завершающую часть модуля; end — зарезервированное слово, являющееся при знаком конца модуля. Таким образом, модуль состоит из заголовка и четырех составных частей, любая из которых может быть пустой.
Заголовок модуля и связь модулей друг с другом Заголовок модуля состоит из зарезервированного слова u n i t и следующего за ним имени модуля или пространства имен. Для правильной работы среды Delphi и возможности подключения средств, облегчающих разработку крупных про грамм, это имя должно совпадать с именем дискового файла, в который помеща ется исходный текст модуля. Например, имеем заголовок unit Global; Тогда исходный текст соответствующего модуля должен размещаться в диско вом файле GLOBAL.PAS. Если объявляется пространство имен, имя файла повто ряет имя пространства имен со всеми разделяющими точками: unit Myunit.Complex.Match Соответствующий файл будет называться Myunit.Complex.Match.pas. ПРИМЕЧАНИЕ По существующему для 32-разрядных версий Windows соглашению имена файлов могут содер жать сколько угодно точек, но лишь самая последняя отделяет собственно имя файла от его расширения. Если в окне менеджера проектов изменить имя модуля на Myunit.Complex.Match, этот модуль будет помещен в файл Myunit.Complex с расширением Match и не будет восприни маться компилятором как часть проекта. Чтобы достичь нужного эффекта, нужно явно допол нять составное имя модуля расширением .pas.
Имя модуля служит для его связи с другими модулями и основной программой. Эта связь устанавливается специальным предложением uses <сп.модулей> Здесь u s e s — зарезервированное слово (использует); <сп.модулей> — список модулей, с которыми устанавливается связь; элементами списка являются имена модулей, отделяемые друг от друга запятыми, например: uses Windows, S y s U t i l s , Myunit.Complex.Match; Предложение u s e s в модулях может следовать либо сразу за зарезервирован ным словом i n t e r f a c e , либо сразу за словом implementation, либо, нако нец, и там, и там (то есть в модуле допускаются два предложения u s e s ) : unit . . . interface uses . . . / / Модули, используемые в объявлениях интерфейсной части implementation
192
uses
Глава 9 • Модули и пространства имен
...// Модули,
используемые в исполняемой части
end.
Интерфейсная часть Интерфейсная часть открывается зарезервированным словом i n t e r f a c e . В этой части содержатся объявления всех глобальных объектов модуля (типов, кон стант, переменных и подпрограмм), которые должны стать доступными основ ной программе и/или другим модулям. При объявлении глобальных подпрог рамм или методов класса в интерфейсной части указывается только их заголовок, например: unit Complex.Match; interface type Complex = record r e , i m : Real end; function AddC(x,y: Complex): Complex; function MulC(x,y: Complex): Complex; Если теперь в другом модуле написать показанное далее предложение, то в нем станут доступными тип Complex и две процедуры— AddC H M U I C из модуля Complex.Match: uses Complex.Match;
Исполняемая часть Исполняемая часть начинается зарезервированным словом i m p l e m e n t a t i o n и содержит описания подпрограмм, объявленных в интерфейсной части. В ней могут объявляться локальные для модуля объекты — вспомогательные типы, кон станты, переменные и подпрограммы, а также метки, если они используются в инициализирующей части. Описанию подпрограммы, объявленной в интерфейсной части модуля, в испол няемой части должен предшествовать заголовок, в котором можно опускать спи сок формальных переменных (и тип результата для функции), так как они уже описаны в интерфейсной части. Но если заголовок подпрограммы приводится в полном виде, то есть со списком формальных параметров и объявлением ре зультата, он должен совпадать с заголовком, объявленным в интерфейсной час ти, например: unit Cmplx; Interface type Complex = record
Инициализирующая и завершающая части
193
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), использовать такую форму в серьезной программе не рекомендуется: перечень параметров непосредственно в заголовке подпрограммы облегчает чтение кода и понимание де талей реализации алгоритма. Для точного указания заголовка подпрограммы в исполняемом раз деле используйте возможности редактора кода: установите курсор в любое место заголовка подпрограммы в интерфейсной части и нажмите клавиши Ctrl+Shift+C — кодовый редактор соз даст заготовку подпрограммы в исполняемой части модуля.
Повторение заголовка в исполняемой части должно быть полным и точным. Если бы мы использовали следующий заголовок, компилятор немедленно известил бы нас о несовпадении заголовка с объявлением функции в интерфейсной части (второй параметр должен иметь имя у): function AddC(x, z: Complex): Complex; begin end;
Инициализирующая и завершающая части Инициализирующая и завершающая части чаще всего отсутствуют вместе с на чинающими их словами i n i t i a l i z a t i o n и f i n a l i z a t i o n . В инициализирующей части размещаются операторы, которые исполняются до передачи управления основной программе и обычно используются для подго товки ее работы. Например, в них могут инициализироваться переменные, от крываться нужные файлы и т. д. В завершающей части указываются операторы, выполняющиеся после завершения работы основной программы (в них освобож-
1 9 4 Глава 9 • Модули и пространства имен
даются выделенные программе ресурсы, закрываются файлы и т. д.). Если не сколько модулей содержат инициализирующие части, эти части выполняются последовательно друг за другом в порядке перечисления модулей в предложе нии uses главной программы. Если несколько модулей содержат завершающие части, эти части выполняются последовательно друг за другом в порядке, обрат ном порядку перечисления модулей в предложении 'uses главной программы.
Доступ к объявленным в модуле объектам Пусть, например, мы создаем модуль, реализующий арифметику комплексных чисел (такая арифметика ни в стандартном языке Pascal, ни в Delphi не предус мотрена, но в Delphi введен пользовательский вариант, который реализует дей ствия над комплексными числами — см. модуль Source\Rtl\Borland.VCL.VarCmplx.pas каталога размещения Delphi). Арифметика комплексных чисел реализуется че тырьмя функциями: unit Cmplx; // // type Complex = record re,im: real end; AddC SubC MulC DivC
(x,y: (x,y: (x,y: (x,y:
Complex): Complex): Complex): Complex):
195
// Вычитание комплексных чисел 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 * y.re + x.im * y.im) / z; Result.im := (x.re * y.im - x.im * y.re) / z; except
Interface
function function function function
Доступ к объявленным в модуле объектам
Complex; Complex; Complex; Complex;
Result.re := l.le309; Result.im := l.le309; end end {DivC}; end.
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;
Чтобы создать такой модуль, следует выбрать команду File • Other и в категории Delphi for .NET Projects • New Files хранилища объектов выбрать значок unit. Текст модуля следует сохранить в файле CMPLX.PAS: имя файла должно совпадать с име нем модуля — только в этом случае Delphi сможет автоматически найти модуль и следить за его обновлением. После создания модуля его имя нужно упомянуть в предложении uses того мо дуля, в котором будут использоваться вновь созданные подпрограммы, типы, кон станты (в нашем модуле — тип Complex, подпрограммы AddC, SubC, MulC, DivC и константа С). Пусть, например, при каждом щелчке на кнопке bbRun создает ся пара случайных комплексных чисел, над которыми осуществляются все четы ре арифметических действия (помимо кнопки на форме имеется также много строчное текстовое поле mmOutput). Тогда обработчик события bbRunClick мог бы быть таким, как показано в листинге 9.1.
1 9 6 Глава 9 • Модули и пространства имен Листинг 9.1. Создание пары случайных комплексных чисел implementation uses Cmplx; ($R *.DFM} procedure TForml.bbRunClick(Sender: TObject); var x,y,z: Complex; procedure Output(Operation: Char); // Осуществляет нужное действие и выводит резуль 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('+0.0000j;-0.0000j',x.im)+')' +Operation+ '('+FormatFloat('+0.0000;-0.0000',y.re)+ FormatFloat(,+0.00O0j;-O.00O0j',y.im)+'='+ FormatFloat('+0.0000;-0.0000',z.re)+ FormatFloat('+0.0000j;-0.0000j',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;
Типы модулей в Delphi
197
Обратите внимание на ссылку u s e s Cmplx в самом начале исполняемой части — именно она делает объекты модуля Cmplx доступными обработчику bbRunClick. Эту ссылку можно вставить с помощью среды Delphi: выберите команду File • Use unit в главном меню и в появившемся окне щелкните на имени модуля Cmplx.
Типы модулей в Delphi Наиболее распространенным типом модуля в Delphi является форма — модуль со связанным с ним окном. Интерфейсная часть такого модуля обычно содер жит объявление нового класса и автоматически обновляется Delphi в ходе кон струирования окна. В интерфейсной части модуля-формы содержится также объявление объекта для соответствующего оконного класса. Например, стандарт ный модуль содержит объявление класса TForml и объекта Forml. Большин ство типовых модулей в хранилище объектов содержат заготовки для создания диалоговых окон. Помимо форм в хранилище содержатся также не связанные с видимыми окнами модули. Помимо уже рассмотренного модуля общего назначения к ним относят ся модули динамических библиотек, пакеты и модули потоков.
Модули динамических библиотек Модули динамических библиотек предназначены для создания широко исполь зуемых в Windows динамически связываемых библиотек (Dynamic-Link Libraries, DLL). Библиотеки DLL служат универсальным средством согласования подпрог рамм, написанных на разных языках программирования. В Windows содержится множество библиотек DLL, написанных на языке С или на языке ассемблера, что ничуть не мешает программам Delphi их использовать. Модули динамичес ких библиотек предназначены для разработки DLL средствами Delphi. Такие биб лиотеки DLL в дальнейшем смогут использовать программы, созданные с помо щью других языков программирования.
Пакеты Пакеты — это особым образом откомпилированные библиотеки DLL, оптимизи рованные для совместного использования программами Delphi, или средой Delphi, или и программами и средой. В отличие от DLL, пакеты могут хранить и переда вать программе типы (включая классы) и данные. Они разработаны специально для хранения компонентов, разного рода экспертов, редакторов сложных свойств и т. п.
Модули потоков команд Модули потоков предназначены для реализации так называемых потоков ко манд — фрагментов программы, которые исполняются параллельно с другими
1 9 8 Глава 9 • Модули и пространства имен
фрагментами, разделяя с ними время процессора и остальные системные ресур сы. Механизм потоков команд используется в 32-разрядных версиях Windows и не поддерживается в Delphi 1. К сожалению, в текущей реализации 32-разряд ной версии Delphi потоки команд не могут связываться с собственными види мыми компонентами, так как библиотека визуальных компонентов (Visual Com ponent Library, VCL) не поддерживает работу с потоками. Вот почему модуль потока команд не имеет связанного с ним окна.
Использование компонентов VCL
Как уже говорилось, в Delphi есть возможность использования двух библиотек компонентов: VCL и WinForms. Компоненты VCL — «родные» компоненты сре ды Delphi, они применялись еще в ее ранних версиях. Компоненты WinForms разработаны корпорацией Microsoft специально для .NET Framework и во мно гом повторяют основные свойства компонентов VCL. В главах этой части рассматриваются некоторые из множества компонентов об щего назначения, входящих в VCL. Компоненты WinForms описываются в сле дующей части.
Класс Exception — обработка исключений
Классы общего назначения
В состав VCL входит около 400 различных стандартных классов, простое пере числение которых заняло бы несколько страниц книги. В этой главе рассматри ваются лишь некоторые самые важные классы общего назначения из библиоте ки VCL. Классы общего назначения из библиотеки WinForms рассматриваются в главе 19.
Класс Exception — обработка исключений Класс E x c e p t i o n является прямым потомком базового класса T O b j e c t . Вмес те со своими потомками он предназначен для обработки исключительных ситуа ций (исключений), возникающих при некорректных действиях программы, на пример, в случае деления на ноль, при попытке открыть несуществующий файл, при выходе за пределы выделенной области динамической памяти и т. п. В этом разделе рассматриваются основные свойства исключений и их использование для повышения надежности программ. СОВЕТ Во время работы в среде Delphi эксперименты с исключениями плохо прослеживаются, так как при каждом исключении среда перехватывает управление программой. В этом случае бывает полезно отменить такое поведение среды. Для этого откройте диалоговое окно Options, выб рав команду Tools • Options, и на странице, связанной с узлом Debugger Options • Borland De buggers • Language Exceptions, сбросьте флажок Notify on Language Exceptions.
Защищенные блоки Для обработки исключений в Delphi предусмотрены два типа защищенных бло ков:
201
try <операторы> except <обработчики исключений> else <операторы> end; try <операторы> finally <операторы> end; Защищенный блок начинается зарезервированным словом t r y (попытаться [выпол нить]) и завершается словом end. Два типа защищенных блоков, e x c e p t (исклю чить) и f i n a l l y (в завершение), различаются способом обработки исключения. В блоке e x c e p t порядок выполнения операторов таков: сначала выполняются опе раторы секции try...except; если операторы выполнены без возникновения исклю чительной ситуации, работа защищенного блока на этом прекращается и управле ние получает оператор, стоящий за словом end; если при выполнении части t r y возникло исключение, управление получает соответствующий обработчик в секции except, а если таковой не найден — первый из операторов, стоящих за словом e l s e . В блоке f i n a l l y операторы в секции f inally...end получают управление всегда, независимо от того, возникло исключение в секции try...f i n a l l y или нет. Если исключение возникло, все операторы в секции try...finally, стоящие за «виновни ком» исключения, пропускаются и управление получает первый оператор секции f inally...end Если исключения не было, этот оператор получает управление пос ле выполнения последнего оператора секции try...f i n a l l y . Обработчики исключений в блоке e x c e p t имеют такой синтаксис: on <класс исключения> do < о п е р а т о р > ;
Здесь on, do — зарезервированные слова; <класс исключения> — класс обра ботки исключения; <оператор> — любой оператор Delphi, кроме оператора пе редачи управления g o t o на метку вне блока e x c e p t . Обратите внимание: имя класса служит своеобразным ключом выбора, а собствен но обработка осуществляется оператором, стоящим за do (этот оператор бывает составным, так что обработка исключения может выполняться произвольным количеством операторов Delphi). Поиск нужного обработчика осуществляется с начала списка вниз до тех пор, пока не встретится класс, способный обрабатывать исключение данного типа. Если подходящего класса не обнаружено, управление передается операторам, стоящим за словом e l s e , а если таковых нет (часть e l s e <операторы> может опускаться), выполняется обработка исключения по умолчанию. Если для программиста важен лишь сам факт возникновения исключения и несу щественен тип связанной с ним ошибки, он может опустить в секции except...end
2 0 2 Глава 10 • Классы общего назначения обработчики вместе со словом e l s e , оставив в ней лишь необходимый код реак ции на любую ошибку: try except ShowMessage('Ошибка! ') ; end; Защищенные блоки могут вкладываться друг в друга на неограниченную глуби ну, так как везде, где в предыдущих описаниях указывался параметр < о п е р а тор>, могут использоваться любые операторы Delphi, в том числе try...except
или try...finally: try try
finally end; except on EMatchError do begin try try end; end; end; end;
Класс Exception Класс E x c e p t i o n (пространство имен B o r l a n d . D e l p h i . System) является ро дительским классом для всех классов-исключений. Этот класс объявляется в мо дуле S y s U t i l s следующим образом: type Exception = class(TObject) private
Класс Exception — обработка исключений
203
FMessage: String; FHelpContext: Integer; public constructor Create(const Msg: String); constructor CreateFmt(const Msg: String; const Args: array of const); constructor CreateRes(Ident: Integer); 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: Integer); constructor CreateResHelp (Ident: Integer; aHelpContext: Integer); constructor CreateResFmtHelp(Ident: Integer; const Args: array of const; aHelpContext: Integer); property HelpContext: Integer read FHelpContext write FHelpContext; property Message: String read FMessage write FMessage; end; В классе определены целых 8 конструкторов для создания объекта. С их помощью можно прочитать текстовое сообщение из ресурса, отформатировать его, связать исключение с контекстной справочной службой. Свойство Message делает дос тупным закрытое поле FMessage, в котором содержится текстовое сообщение.
Стандартные классы исключений В Delphi определены стандартные классы исключений (см. приложение А). Имен но эти имена, а также имена пользовательских классов (см. далее) могут приме няться в обработчиках исключений. Важно помнить, что управление передается самому первому обработчику (воз можно существование нескольких обработчиков), класс которого способен об рабатывать данное исключение. Если, например, в списке первым стоит класс EAbort, который может обработать любое исключение, ни один из стоящих за ним обработчиков никогда не получит управления. Точно так же, если указан обработчик для класса E I n t E r r o r , за ним бесполезно размещать обработчики EDivByZero, E R a n g e E r r o r или E I n t O v e r f l o w : try except // He имеет смысла делать так: on EIntError do ...;
2 0 4 Глава 10 • Классы общего назначения on ERangeError do ...; on EDivByZero do ...; // Надо так: on ERangeError do . . . ; on EDivByZero do on EIntError do . . . ; end; При возникновении исключительной ситуации объекты классов-обработчиков создаются и уничтожаются автоматически. Если программист пожелает исполь зовать поля или методы класса-обработчика явно, он должен поименовать авто матически создаваемый объект. Для этого перед именем класса ставятся иденти фикатор и двоеточие: on EObject: EClassName do ...; Для стандартных классов такой прием фактически позволяет использовать един ственное строковое свойство Message со стандартным сообщением об ошибке, которое получают все наследники класса E x c e p t i o n . Исключение составляет класс E I n O u t E r r o r , в котором для программиста может представлять интерес целочисленное свойство E r r o r C o d e с кодом ошибки ввода-вывода, например: try Reset (F); while not EOF(F) do begin end; CloseFile(F); except on E: EInOutError do ShowMessage('При выполнении файловой операции возникла'+ ' ошибка №'+ IntToStr (E.ErrorCode)); end;
Класс Exception — обработка исключений
момент объекта путем вызова его конструктора. Например, следующий оператор возбудит ошибку ввода-вывода: raise EInOutError.Create('Ошибка! ' ) ; Такой прием — единственная возможность возбудить нестандартное исключение, обрабатываемое пользовательским классом.
Создание собственного класса Программист может создать собственный класс обработки исключений, объявив его потомком E x c e p t i o n или любого другого стандартного класса (этим дру гим чаще всего бывает класс EAbort). Объявление нестандартного класса имеет смысл только тогда, когда вам необходимо научить программу распознавать не корректные наборы данных и соответствующим образом на них реагировать. Пусть, например, в программе используется цикл ввода целочисленных значе ний из текстового файла, их проверки и преобразования. Проверка заключается в простом контроле неотрицательности очередного числа после ввода и его по ложительности после преобразования. Перед проверкой необходимо получить строку из файла (здесь может возникнуть ошибка E I n O u t E r r o r ) и преобразо вать ее в целую величину (здесь возможна ошибка E C o n v e r t E r r o r ) ; после про верки осуществляется обработка величины, в процессе которой может возник нуть ошибка E I n t E r r o r . Создадим новый класс E I n t C h e c k E r r o r и будем возбуждать исключение этого класса при обнаружении ошибки в данных: type EIntCheckError = class(EAbort) end; var F: TextFile; S: String; 1
Вызов исключения В некоторых ситуациях программисту бывает необходимо инициировать собствен ное исключение. Для этого он использует зарезервированное слово r a i s e (возбу дить). Если это слово встретилось в секции try...exception или try...finally, немедленно начинают свою работу секция except...end или finally...end соот ветственно. Если оно встретилось в секции except...end или finally...end, счи тается, что данный защищенный блок на текущем уровне вложенности (блоки могут быть вложенными) завершил свою работу, и управление передается выше стоящему уровню. Слово r a i s e возбуждает исключение самого общего класса E x c e p t i o n . Если программист желает возбудить исключение конкретного типа (неважно, стан дартного или собственного), он должен явно указать класс создаваемого в этот
205
T
^
к: Integer; begin try // Готовимся к работе: открываем файл AssignFile(F, FileName); Reset(F); // Здесь возможна // ошибка EInOutError // Цикл ввода-контроля-преобразования while not EOF(F) do begin // Вводим символы очередного числа ReadLn(F,S); // Здесь возможна // ошибка EInOutError
2 0 6 Глава 10 • Классы общего назначения // Преобразуем символы в число к := StrToInt (S); // Здесь возможна // ошибка EConvertError // Проверяем число if к < 0 then raise EIntCheckError.Create( 'Отрицательное число'); // Преобразуем число ••• // Здесь возможна // ошибка EIntError // Вновь проверяем число if к <= 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; В этом примере создается класс E I n t C h e c k E r r o r , который ничем, кроме назва ния, не отличается от своего родителя EAbort. В реальной программе потомок обычно расширяет набор полей (свойств) своего родителя или перекрывает его методы; приведенный пример лишь иллюстрирует, что это делать не обязатель но. При неудачной проверке следующими операторами возбуждается исключе ние нового класса: raise EIntCheckError.Create ('Отрицательное число'); raise EIntCheckError.Create('Ошибка преобразования'); При этом с помощью унаследованного конструктора C r e a t e создается новый безымянный объект, а строковый параметр обращения к конструктору запоми нается в поле FMessage и становится доступным с помощью свойства Message объекта. Обработчик исключения E I n t C h e c k E r r o r именует объект идентифи катором Е ис помощью стандартной процедуры ShowMessage показывает его в небольшом окне на экране. Пример наглядно иллюстрирует выгоды от использования исключений. В прин ципе, весь этот фрагмент можно было бы написать с многочисленными провер-
Класс TList — списки
207
ками if ...then, но в этом случае логика программы стала бы запутанной, а сама программа — сложной для отладки.
Класс TList—списки Класс T L i s t (пространство имен B o r l a n d . V C L . C l a s s e s ) позволяет создать набор из произвольного количества элементов и организовать индексный способ доступа к ним, как это делается при работе с массивом. Списки отличаются от массивов двумя важными особенностями. Во-первых, их размер может динами чески меняться в ходе работы программы, фактически ограничиваясь лишь дос тупной памятью. Во-вторых, в списках могут храниться элементы разных типов. Технически списки представляют собой массивы указателей на размещенные в ди намической памяти элементы. Сами элементы должны быть экземплярами клас са O b j e c t , являющегося родоначальником всех классов в CTS. Таким образом, в списки можно поместить только экземпляры любого из классов CTS. Напри мер, строка является классом, и ее можно поместить в список. Целое число не является классом. Чтобы поместить целое число в список, его нужно сделать чле ном какого-либо класса. В следующем примере создается список из двух эле ментов — строки и целого числа: program Project!.; {$APPTYPE CONSOLE} uses SysUtils, Borland.VCL.Classes; type Int = class I: Integer; end;
// Класс-оболочка для целого числа
var L: TList; X: Int; begin L := TList.Create; // Создаем список L.Capacity := 2; // Его емкость - 2 элемента L.Add('Строка символов'); // Строка - класс X := Int.Create; // Создаем экземпляр класса Int X.I := 12345; L.Add(X); // Вставляем в список Writeln(L[0].ToString); Writeln (Int (L[l]) .I);
208
Глава 10 • Классы общего назначения
L[0].Free; L[l].Free; L.Free; Readln end;
// // //
Освобождаем Освобождаем Освобождаем
Класс TList — списки строку контейнер список
целого
На экран будут выведены такие строки: Строка 12345
символов
В табл. Ю.2 перечислены некоторые методы класса T L i s t . Таблица 10.2. Методы класса TList Метод
Описание
function Addfltem: Integer;
Добавляет элемент I t e m в конец списка и возвращает его индекс
Object)
procedure Clear;
Очищает список, удаляя из него все элементы. Не освобождает память, связанную с каждым удаленным элементом. Устанавливает в свойства C o u n t и C a p a c i t y значение О
procedure Delete(Index: Integer);
Удаляет из списка элемент с индексом I n d e x . Все элементы, расположенные за удаляемым, смещаются на одну позицию вверх
procedure Exchange (Indexl, Index2: Integer) function Expand: TList; function First: Object; function IndexOf(Item: Object):Integer; procedure Insert(Index: Integer; Item: Object);
Меняет местами элементы с индексами i n d e x l иIndex2
В табл. 10.1 указаны наиболее важные свойства класса T L i s t . Т а б л и ц а 1 0 . 1 . Свойства класса TList Свойство
Описание
property Capacity:
p r o p e r t y Count:
Integer;
Integer;
Содержит количество элементов массива указателей списка. Всегда больше C o u n t . Если при добавлении очередного элемента значение C o u n t стало равно C a p a c i t y , происходит автоматическое расширение списка на 16 элементов Количество элементов списка. Это свойство изменяется при добавлении или удалении элемента
p r o p e r t y Items (Index: I n t e g e r ) : Object;
Возвращает указатель на элемент списка по его индексу. Самый первый элемент списка имеет индекс О
property List:
Возвращает указатель на массив элементэв списка
ArrayList;
Свойство I t e m s объявлено умалчиваемым, поэтому два следующих оператора эквивалентны: Caption Caption
:= L.Items[0].ToString; := L[0].ToString;
В связи со спецификой работы с указателями (см. главу 3) свойство L i s t явля ется объектом класса A r r a y L i s t , который принадлежит пространству имен S y s t e m . C o l l e c t i o n . Этот класс CTS имеет многочисленные свойства и методы, упрощающие размещение в памяти элементов списка (см. справочную службу). Следует учесть, что свойство Count определяет количество элементов списка, в то время как C a p a c i t y — текущую емкость списка. Если при добавлении очередно го элемента обнаруживается, что емкость списка исчерпана, происходит наращи вание емкости на фиксированную величину. При этом сначала резервируется па мять для размещения расширенного массива указателей, затем в нее копируется содержимое старого массива, после чего старый массив указателей уничтожается (занимаемая им память возвращается Windows). СОВЕТ Если вы заранее знаете, сколько элементов необходимо поместить в список, установите в на чале работы нужное значение в свойство C a p a c i t y — это снизит непроизводительные затра ты времени на расширение списка.
209
function Last: Object; procedure Move(Curlndex, Newlndex: Integer); procedure
Pack;
function Remove(Item: Object): Integer; procedure Sort(Compare: TListSortCompare);
Расширяет массив элементов, увеличивая C a p a c i t y Возвращает самый первый элемент списка Отыскивает в списке элемент I t e m и возвращает его индекс Вставляет элемент I t e m в позицию I n d e x списка: новый элемент получает индекс I n d e x , все элементы с индексами I n d e x и больше увеличивают свой индекс на 1. При необходимости расширяет список Возвращает последний элемент списка Перемещает элемент в списке с позиции C u r l n d e x в позицию N e w l n d e x . Все элементы старого списка с индексами от C u r l n d e x - 1 до N e w l n d e x уменьшают свой индекс на 1 Упаковывает список: удаляет пустые элементы в конце массива индексов Отыскивает в списке элемент i t e m и удаляет его Сортирует коллекцию с помощью функции Compare
Методы Add и I n s e r t получают вставляемый элемент. Чтобы воспользоваться ими, программист должен создать экземпляр класса, то есть разместить в памя ти его поля. Точно так же методы D e l e t e , Remove и C l e a r не уничтожают рас пределенные в памяти данные, которые при необходимости программист дол жен уничтожить сам, например 1 : type
Int = class I: Integer; end; Напомню, что по окончании работы программы сборщик мусора CLR автоматически очистит выде ленную ей память. Тем не менее приведенная рекомендация не является лишней, так как класс T L i s t может работать и без CLR.
2 1 0 Глава 10 • Классы общего назначения
Классы TCollection и TCollectionltem — коллекции
var List:
TList;
Item:
Int;
Result := 0; if Int(XT.I < Int(Y).I then Result := -1; end;
begin List
:= T L i s t . C r e a t e ;
Item := I n t . C r e a t e ;
/ / Создаем список // Размещаем в куче данные
List.Add(item);
// Добавляем элемент
List.Remove(Item);
// Удаляем элемент из
Item.Free;
// Удаляем его из
List.Free;
// Удаляем ненужный список
к
списку списка
кучи
Метод S o r t сортирует список по критерию, устанавливаемому функцией Сотрагу. Тип T L i s t S o r t C o m p a r e определен следующим образом: TListSortCompare = f u n c t i o n ( I t e m l ,
Item2:
Object):
Integer;
Таким образом, функция Compare получает два элемента списка. Результат срав нения: • любое отрицательное число, если I t e m l < I t e m 2 ; 0, если I t e m l = I t e m 2 ;
• любое положительное число, если I t e m l > I t e m 2 . Критерий сравнения данных устанавливается программистом и реализуется в функ ции Compare. В следующем примере в список L помещается 10 случайных целых чисел. Спи сок сортируется по возрастанию чисел и выводится на экран: program ListSort; {$APPTYPE CONSOLE}
uses SysUtils, Borland.VCL.Classes; type Int = class I: Integerend; function Comp(X, Y: TObject): Integer; begin if Int(X).I > Int(Y).I then Result := 1; i f I n t ( X ) . I = I n t ( Y ) . I then
var L: TList; X: array [0..9] of Int; k: Integer; begin L := TList.Create;
// Контейнеры для чисел
// Создаем список // Его емкость - 10 элементов // Цикл наполнения списка
L.Capacity := 10; for k := 0 to 9 do
end;
•
211
begin X[k]
:=
Int.Create;
//
Создаем
очередной
объект-контейнер
X[k].I := Random(lOO); // Наполняем его L.Add(X[k]);
/ / и помещаем в список
end; L.Sort(Comp);
/ / Сортируем список
for k := 0 to L.Count - 1 do begin Writeln(IntToStr(Int(L[K]).1)); Int(L[k]).Free
//
Уничтожаем контейнер
end; L.Free;
/ / Уничтожаем список
Readln end;
Классы TCollection и TCollectionltem — коллекции Класс T C o l l e c t i o n во многом подобен классу T L i s t , но, в отличие от после днего, может хранить только элементы, являющиеся объектами класса T C o l l e c t i o n l t e m (элементы T L i s t могут быть любого типа). Существует несколько наследников T C o l l e c t i o n , для каждого разработан свой наследник T C o l l e c t i o n l t e m . В табл. 10.3 показаны наследники T C o l l e c t i o n , соответствую щие наследники T C o l l e c t i o n l t e m и компонентные классы, в которых исполь зуются нужные пары. Таблица 10.3. Наследники TCollection и TCollectionltem Наследник TCollection
Наследник TCollectionltem
Используются в компоненте
TAggregates
TAggregate
TCookieCollection
TCookie
TClientDataSet TWebResponse
TCoolBands TDBGridColumns
TCoolBand TColumn
TCoolBar TDBGrid
продолжение &
212
Глава 10 • Классы общего назначения
Классы TStrings и TStringList — наборы строк и объектов
Т а б л и ц а 1 0 . 3 (продолжение) Наследник TCollection
Наследник TCollectionltem
Используются в компоненте
TDependencies IDisplayDims TFieldDefs • THeaderSections TIndexDefs TListColumns TParams TStatusPanels TWorkAreas
TDependencie TDisplayDim TFieldDef THeaderSection TIndexDef TListColumn TParam TStatusPanel TWorkArea
TService TDecisionGrid TDataSet THeaderControl TTable TListView Многие наборы данных TStatusBar TListView
Указанные классы разработаны д л я специальных целей и не я в л я ю т с я универ сальными. Тем не менее они рассматриваются в этой главе, так как ничто не ме шает вам разработать свою пару классов для использования в специфичных це лях.
Класс TCollection В табл. 10.4 и 10.5 описываются важнейшие свойства и методы класса T C o l l e c t i o n (пространство имен B o r l a n d . VCL. C l a s s e s ) . Таблица 1 0 , 4 . Свойства TCollection Свойство
Назначение
Содержит количество элементов коллекции property Count: I n t e g e r ; property I t e m C l a s s : Указывает тип элементов коллекции TCo1lectionItemClass; p r o p e r t y I t e m s [ I n d e x : I n t e g e r ] : Открывает индексированный доступ к элементам TCollectionltem; коллекции Свойство I t e m s у класса умалчиваемое, поэтому два следующих обращения иден тичны: Collection.Items[0] Collection[0] Таблица 1 0 . 5 . Методы TCollection Метод f u n c t i o n Add:
Назначение TCollectionltem;
procedure BeginUpdate; procedure C l e a r ;
Создает новый элемент коллекции и добавляет его к списку I t e m s Блокирует отрисовку компонента вплоть до вызова метода EndUpdate. Ускоряет процесс наполнения коллекции и уменьшает мерцание экрана Удаляет все элементы из коллекции и уничтожает каждый из них
Назначение
Метод procedure D e l e t e ( I n d e x : procedure E n d U p d a t e ;
213
Integer);
v
function GetAttr (Index: I n t e g e r ) : string; function GetAttrCount: I n t e g e r ; function GetltemAttr (Index: Integer, Itemlndex: I n t e g e r ) : string; function I n s e r t (Index: I n t e g e r ) : TCollectionltem;
Уничтожает указанный элемент коллекции Прекращает действие метода B e g i n U p d a t e и отрисовывает компонент Возвращает имя атрибута, связанного с указанным элементом Возвращает общее количество атрибутов Для элемента I n d e x возвращает атрибут с номером I t e m l n d e x Создает новый элемент и вставляет его в коллекцию на указанное место
Класс TCollectionltem Свойства и методы класса T C o l l e c t i o n l t e m (пространство имен B o r l a n d . V C L . C l a s s e s ) носят преимущественно служебный характер и не представляют осо бого интереса для программиста.
Классы TStrings и TStringList— наборы строк и объектов Характерной особенностью классов T S t r i n g s и T S t r i n g L i s t является возмож ность сохранения в них не только строк, но и связанных с ними произвольных объектов.
Класс TStrings Абстрактный класс T S t r i n g s ( п р о с т р а н с т в о имен B o r l a n d . VCL . C l a s s e s ) инкапсулирует п о л я и методы д л я работы с наборами строк. От него порож дены м н о г о ч и с л е н н ы е с п е ц и а л и з и р о в а н н ы е п о т о м к и , о б с л у ж и в а ю щ и е набо ры строк в т а к и х компонентах, как TComboBox, T L i s t B o x , T R i c h E d i t и др. Эти классы ( T C o m b o B o x S t r i n g s , T L i s t B o x S t r i n g s , T R i c h E d i t S t r i n g s и др.) о б ъ я в л я ю т с я в разделах I m p l e m e n t a t i o n с о о т в е т с т в у ю щ и х м о д у л е й ( S t d C t r l s , C o m C t r l s и др.) и поэтому с к р ы т ы от браузера Delphi и не вклю чены в справочную службу. Е д и н с т в е н н ы м доступным н а с л е д н и к о м T S t r i n g s я в л я е т с я T S t r i n g L i s t — п о л н о ф у н к ц и о н а л ь н ы й класс общего н а з н а ч е н и я . Замечательной особенностью класса T S t r i n g s и его потомков является то обсто ятельство, что элементами наборов служат пары строка—объект, в которых стро ка — собственно строка символов, а объект — объект любого класса Delphi. Такая двойственность позволяет сохранять в T S t r i n g s объекты с текстовыми примеча ниями, сортировать объекты, отыскивать нужный объект по его описанию и т. д. Кроме того, в качестве объекта может использоваться потомок T S t r i n g s , что по зволяет создавать многомерные наборы строк.
214
Глава 10 • Классы общего назначения
Набор строк технически реализуется подобно T L i s t — в виде массива указате лей. Свойство C a p a c i t y показывает текущую длину этого массива, а свойство Count — количество элементов, занятых в нем. Если при добавлении очередно го элемента C a p a c i t y окажется меньше Count, происходит автоматическое рас ширение массива. При этом в динамической памяти резервируется место для размещения C a p a c i t y + 16 указателей, в новый массив переписывается содержимое старого массива, после чего старый массив уничтожается. Если вам известно количество элементов в создаваемом наборе строк, имеет смысл зара нее нужным образом установить свойство C a p a c i t y , чтобы сократить непроиз водительные расходы на многократные расширения массива указателей. Свойство CommaText интерпретирует содержимое набора строк в виде одной длинной строки с элементами вида " П е р в а я с т р о к а " , " В т о р а я с т р о к а " , " Т р е т ь я с т р о к а " и т. д. (каждая строка набора заключается в двойные кавычки и отделяется от соседней строки запятой; если в строке встречается символ двой ной кавычки, он удваивается). Свойство T e x t интерпретирует содержимое на бора в виде одной длинной строки с элементами, разделенными стандартным признаком EOLN ( # 1 3 # 1 0 ) . Свойства Names и V a l u e s обрабатывают строки вида Name = V a l u e . Такие строки широко используются в различных файлах инициализации, например в файле WIN.INI. Методы Add, Append, I n s e r t , C l e a r и т. п. в классе T S t r i n g s абстрактные. Связано это с тем, что класс инкапсулирует их и таким образом делает доступ ными во всех потомках, но он при этом не накладывает никаких ограничений на то, как располагаются в памяти строки и объекты. Каждый потомок решает эту задачу наиболее удобным для него способом. Например, потомок T S t r i n g L i s t располагает строки и объекты в общей динамической памяти, для чего перекры вает все абстрактные методы своего родителя. Замечу: если вы создадите экзем пляр класса T S t r i n g s с помощью его конструктора C r e a t e , компилятор пре дупредит вас о том, что этот экземпляр содержит абстрактные методы, поэтому пользоваться им нужно лишь в исключительных случаях.
Метод или свойство
T S t r i n g L i s t (пространство имен B o r l a n d . V C L . C l a s s e s ) представляет со бой полнофункциональный класс общего назначения и является прямым потом ком T S t r i n g s . Помимо перекрытых абстрактных методов своего родителя, класс включает в себя дополнительные методы и свойства, перечисленные в табл. 10.6. Т а б л и ц а 1 0 . 6 . Некоторые свойства и методы класса TStringList
property OnChange: TNotifyEvent;
Описание
p r o p e r t y O n C h a n g i n g : T N o t i f y E v e n t ; Определяет реакцию на изменение набора строк. Возникает до очередного изменения
v
f u n c t i o n F i n d ( c o n s t S: S t r i n g ; var Index: I n t e g e r ) : Boolean;
Ищет в наборе строку S и в случае успеха в параметре I n d e x возвращает ее индекс
При S o r t e d = True строки набора автоматически сортируются в алфавитном по рядке. При этом свойство D u p l i c a t e s разрешает коллизию, связанную с добав лением в набор строки, идентичной одной из ранее вставленных. Если D u p l i c a t e s = d u l g n o r e , идентичная строка отвергается и программе ничего об этом не сооб щается; если D u p l i c a t e s = d u E r r o r , возбуждается исключение E L i s t E r r o r ; значение D u p l i c a t e s = duAccept разрешает вставлять в набор сколько угодно идентичных строк.
Графический инструментарий Богатство изобразительных возможностей Windows связано с так называемым дес криптором контекста графического устройства (Device Context, DC) и тремя вхо дящими в него инструментами — шрифтом, пером и кистью. Все они связаны с про странством имен B o r l a n d . V C L . C l a s s e s . В Delphi созданы специализированные классы-надстройки, существенно упрощающие использование графических инст рументов Windows: для канвы — класс TCanvas, для шрифта — TFont, для пера — ТРеп и для кисти— TBrush. Связанные с этими классами объекты автоматичес ки создаются для всех видимых элементов и становятся доступными программе через свойства Canvas, Font, Pen и Brush.
Класс TFont Наиболее важные свойства класса перечислены в табл. 10.7. Т а б л и ц а 1 0 . 7 . Свойства класса TFont Свойство
Описание
property Charset: TFontCharSet;
Набор символов. Для русскоязычных программ это свойство обычно имеет значение DEFAULT_CHARSET ИЛИ RUSSIAN_CHARSET. Используйте значение OEM_CHARSET для отображения текста MS-DOS (альтернативная кодировка) Цвет шрифта Высота шрифта в пикселах экрана Имя шрифта. По умолчанию имеет значение MS Sans Serif
Описание
property Duplicates: TDuplicates; Свойство, позволяющее управлять property Sorted: Boolean;
215
С помощью класса TFont создается объект-шрифт для любого графического ус тройства (экрана, принтера, плоттера и т. п.).
Класс TStringList
Метод или свойство
Графический инструментарий
возможностью размещения в наборе двух и более идентичных строк Признак необходимости сортировки строк в алфавитном порядке Определяет реакцию на изменение набора строк. Возникает после последнего изменения
property Color: TColor; property' Height: Integer; p r o p e r t y Name: T F o n t N a m e ;
продолжение •&
2 1 6 Глава 10 • Классы общего назначения Таблица 10.7 (продолжение) Свойство property S i z e : property S t y l e :
Описание Integer; TFontStyles;
"
Высота шрифта в пунктах (1/72 дюйма). Изменение этого свойства автоматически изменяет свойство Height и наоборот Стиль шрифта. Может принимать значение как комбинацию следующих признаков: f sBold (жирный), f s l t a l i c (курсив), fsUnderline (подчеркнутый), f s S t r i k e O u t (перечеркнутый)
Графический инструментарий • • • • • •
217
pmMerge — комбинация цветов пера и фона; pmNotMerge — инверсия цветов пера и фона, свойство S t y l e игнорируется; pmMask — общие цвета пера и фона; pmNotMask — инверсия общих цветов пера и фона; pmXor — объединение цветов пера и фона операцией XOR; pmNotXor — инверсия объединения цветов пера и фона операцией XOR.
Для некоторых случаев может оказаться полезным метод
Стиль линии определяется свойством S t y l e и может иметь одно из следующих значений:
procedure A s s i g n ( S o u r c e: T P e r s i s t e n t ) ;
• p s S o l i d — сплошная линия;
С помощью этого метода значения свойств шрифтового объекта S o u r c e присва иваются свойствам текущего шрифта. Метод можно использовать для создания шрифта принтера по экранному шрифту и наоборот.
• psDash — пунктирная линия;
• p s C l e a r — нет линии (для очистки контура фигуры).
С помощью класса ТРеп создается объект-перо, служащий для вычерчивания линий. В табл. 10.8 перечислены свойства класса. Таблица 10.8. Свойства класса ТРеп Свойство
Описание
property Color: TColor; property Mode : TPenMode;
Цвет вычерчиваемых пером линий Определяет способ взаимодействия линий с фоном (см. далее) Определяет стиль линий (см. далее). Учитывается только для толщины линий 1 пиксел. Для толстых линий стиль всегда p s S o l i d (сплошная) Толщина линий в пикселах экрана
property S t y l e : TPenStyle; Integer;
Свойство Mode может принимать одно из следующих значений: • • • • • • • •
• psDashDot — штрих-пунктирная линия; • p s D a s h D o t D o t — комбинация пунктира и двух точек;
Класс ТРеп
property Width:
• p s D o t — точечная линия;
pmBlack — линии всегда черные, свойства C o l o r и S t y l e игнорируются; pmWhite — линии всегда белые, свойства C o l o r и S t y l e игнорируются; pmNop — цвет фона не меняется (линии не видны); pmNot — инверсия цвета фона, свойства C o l o r и S t y l e игнорируются;
ртСору — цвет линий определяется свойством C o l o r пера; pmNotCopy — инверсия цвета пера, свойство S t y l e игнорируется; pmMergePenNot — комбинация цвета пера и инверсионного цвета фона; pmMaskPenNot — комбинация общих цветов для пера и инверсионного цве та фона, свойство S t y l e игнорируется; • pmMergeNotPen — комбинация инверсионного цвета пера и фона; • pmMaskNotPen — комбинация общих цветов для инверсионного цвета пера и фона, свойство S t y l e игнорируется;
Класс TBrush Объекты класса TBrush (кисти) служат для заполнения внутреннего простран ства замкнутых фигур. В табл. 10.9 представлены свойства класса. Таблица 1С.9. Свойства класса TBrush Свойство
Описание
property Bitmap: TBitmap;
Содержит растровое изображение, которое будет использоваться кистью. Если это свойство определено, свойства Color и S t y l e игнорируются Цвет кисти Стиль кисти (см. далее)
property Color: TColor; property S t y l e : TBrushStyle;
Замечу, что свойство Bitmap содержит ссылку на объект класса TBitmap. Пе ред обращением к свойству этот объект нужно создать. Свойство C o l o r опреде ляет цвет рисунка (узора) кисти, но не цвет фона: цвет фона выбирается автома тически так, чтобы линии узора контрастировали с фоном. Обратите внимание: при назначении кисти нового стиля автоматически очища ется свойство C o l o r , которое следует установить заново, если только стиль не bsClear.
Класс TCanvas Этот класс создает «канву», на которой можно рисовать чертежными инстру ментами — Пером. КИСТЬЮ И Шрифтом. Объекты класса TCanvas автоматически
Графический инструментарий
2 1 8 Глава 10 • Классы общего назначения
создаются для всех видимых компонентов, которые должны уметь нарисовать себя. Они инкапсулируют объекты Font, Pen, Brush, а также многочисленные методы, использующие эти объекты. Свойства класса перечислены в табл. 10.10. Т а б л и ц а 1 0 . 1 0 . Свойства класса TCanvas Свойство
Описание
p r o p e r t y Brush: TBrush; p r o p e r t y C l i p R e c t : TRect;
Объект-кисть Определяет текущие размеры области, нуждающейся а отрисовке Устанавливает способ взаимодействия растрового изображения с цветом фона (см. далее) Объект-шрифт Объект-перо Определяет текущее положение пера в пикселах относительно левого верхнего угла канвы Массив пикселов канвы
p r o p e r t y CopyMode:
TCopyMode;
p r o p e r t y Font: TFont; p r o p e r t y P e n : TPen; p r o p e r t y PenPos: T P o i n t ; property Pixels[X,Y: I n t e g e r ] : TColor;
Свойство CopyMode используется при копировании части одной канвы (источни ка) в другую (приемник) методом CopyRect и может иметь одно из следующих значений: •
c m B l a c k n e s s — заполняет область рисования черным цветом;
•
c m D e s t l n v e r t — заполняет область рисования инверсным цветом' фона;
•
cmMergeCopy— объединяет изображение на канве и копируемое изображе ние операцией AND;
•
cmMe где P a i n t — объединяет изображение на канве и копируемое изображе ние операцией OR; • cmNotSrcCopy — копирует на канву инверсное изображение источника; •
cmPatCopy — копирует образец источника; cmPat I n v e r t — комбинирует образец источника с изображением на канве с помощью операции XOR; • c m P a t P a i n t — комбинирует изображение источника сего образцом с помо щью операции OR, затем результат объединяется с изображением на канве так же с помощью операции OR;
• •
•
cmWhitness
объединяет изображение на канве и источник операцией OR; заполняет область рисования белым цветом.
С помощью свойства P i x e l s все пикселы канвы представляются в виде двух мерного массива точек с нулевой нижней границей по каждому измерению; точ ка P i x e l s [0, 0] соответствует левому верхнему пикселу канвы. Изменяя цвет пикселов, можно отрисовывать изображение по отдельным точкам. Методы класса перечислены в табл. 10.11. Т а б л и ц а 1 0 . 1 1 . Методы класса TCanvas Метод
Описание
p r o c e d u r e A r c ( X I , Yl, X2, Y2, ХЗ, Y3, Х4, Y4: I n t e g e r )
Чертит дугу эллипса в охватывающем прямоугольнике ( X I , Y 1 ) - ( X 2 , Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (ХЗ, Y3), а конец — на пересечении с лучом, проведенным из центра в точку (Х4, Y4). Дуга чертится против часовой стрелки (см. далее)
procedure C h o r d ( X I , Yl, X2, Y2, ХЗ, Y3, X4, Y4 : Integer);
Чертит сегмент эллипса в охватывающем прямоугольнике ( X I , Y 1 ) - ( X 2 , Y2). Начало дуги сегмента лежит на пересечении эллипса и луча, проведенного из его центра в точку (ХЗ, Y3), а конец— на пересечении с лучом, проведенным из центра в точку (Х4, Y4). Дуга сегмента чертится против часовой стрелки, а начальная и конечная точки дуги соединяются прямой (см. далее)
procedure CopyRect ( D e s t : TRect; Canvas: TCanvas; .Source: TRect); p r o c e d u r e Draw(X, Y: I n t e g e r ; Graphic: TGraphic); procedure DrawFocusRect (const Rect: TRect);
c m N o t S r c E r a s e — объединяет изображение на канве и копируемое изобра жение операцией OR и инвертирует результат;
• •
•
• cmSrcPaint
cmSrcAnd— объединяет изображение источника и канвы с помощью опера ции AND;
cmSrcCopy — копирует изображение источника на канву; c m S r c E r a s e — инвертирует изображение на канве и объединяет результат с изображением источника операцией AND; • c m S r c I n v e r t — объединяет изображение на канве и источник операцией XOR;
219
procedure E l l i p s e (XI, Yl, X2, Y2: I n t e g e r ) procedure F i l l R e c t (const Rect: TRect); p r o c e d u r e F l o o d F i l l ( X , Y: I n t e g e r ; C o l o r : TColor; FillStyle: TFillStyle); F i l l S t y l e = fsSurface,
Копирует изображение S o u r c e канвы Canvas в участок D e s t текущей канвы Осуществляет отрисовку графического объекта G r a p h i c так, чтобы левый верхний угол объекта расположился в точке (X, Y) Отрисовывает прямоугольник с помощью операции XOR, поэтому повторная отрисовка уничтожает ранее вычерченный прямоугольник. Используется в основном для отрисовки нестандартных интерфейсных элементов при получении ими фокуса ввода и при потере его Чертит эллипс в охватывающем прямоугольнике ( X I , Y 1 ) - ( X 2 , Y2). Заполняет внутреннее пространство эллипса текущей кистью Заполняет текущей кистью прямоугольную область R e c t , включая ее левую и верхнюю границы, но не затрагивая правую и нижнюю границы Производит заливку канвы текущей кистью. Заливка начинается сточки (X, Y) и распространяется во все стороны от нее, если заливка распространяется на все соседние точки с цветом C o l o r . Если F i l l S t y l e = f s B o r d e r , наоборот, заливка прекращается на точках с этим цветом продолжение &
220
Глава 10 • Классы общего назначения
Т а б л и ц а 1 0 . 1 1 (продолжение) Метод
Описание
procedure FrameRect (const Rect: TRect);
Очерчивает границы прямоугольника R e c t текущей кистью толщиной 1 пиксел без заполнения внутренней части прямоугольника
procedure LineTo(X, Y: Integer); procedure MoveTo(X, Y: Integer); procedure Pie (XI, Yl, X2, Y2, X3, Y3, X4, Y4: Longint)
Чертит линию от текущего положения пера до точки (X, Y)
procedure Polygon (Points: array of TPoint);
Вычерчивает пером многоугольник по точкам, заданным в массиве P o i n t s . Конечная точка соединяется с начальной, и многоугольник заполняется кистью. Для вычерчивания без заполнения используйте метод P o l y l i n e
На рис. 10.1 показаны параметры обращения к методам Arc, Chord, P i e и Round Rect.
Рисует сектор эллипса в охватывающем прямоугольнике ( X I , Y 1 ) - ( X 2 , Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (ХЗ, Y3), а конец — на пересечении с лучом, проведенным из центра в точку (Х4, Y4). Дуга чертится против часовой стрелки. Начало и конец дуги соединяются прямыми с ее центром (см. далее)
Вычерчивает пером ломаную прямую по точкам, заданным в массиве P o i n t s
procedure Refresh;
Устанавливает в канве принятый по умолчанию шрифт, перо и кисть Вычерчивает и заполняет прямоугольник ( X I , Y1) (Х2, Y2) со скругленными углами. Прямоугольник ( X I , Y l ) - ( X 3 , Y3) определяет дугу эллипса для скругления углов (см. далее)
procedure StretchDraw (const Rect: TRect; Graphic: TGraphic ); function TextExtent (const Text: String): TSize; function TextHeight (const Text: String): Integer; procedure TextOut(X, Y: Integer; const Text: String);
221
Перемещает перо в положение (X, Y) без вычерчивания линий
procedure Polyline (Points: array of TPoint); procedure Rectangle (XI, Yl, X2, Y2: Integer);
procedure RoundRect(XI, Yl, X2, Y2, X3, Y3: Integer);
Графический инструментарий
Вычерчивает и заполняет прямоугольник ( X I , Y1) (Х2, Y2). Для вычерчивания без заполнения используйте метод FrameRect или P o l y l i n e
Вычерчивает и при необходимости масштабирует графический объект G r a p h i c так, чтобы он полностью занял прямоугольник R e c t Возвращает ширину и высоту прямоугольника, охватывающего текстовую строку T e x t
Возвращает высоту прямоугольника, охватывающего текстовую строку T e x t Выводит текстовую строку T e x t так, чтобы левый верхний угол прямоугольника, охватывающего текст, располагался в точке (X, Y) Выводит текстовую строку T e x t так, чтобы левый procedure TextRect (Rect: верхний угол прямоугольника, охватывающего TRect; X, Y: Integer; const текст, располагался в точке (X, Y). Если при этом Text: String); какая-либо часть надписи выходит за границы прямоугольника R e c t , она отсекается и не будет видна Возвращает ширину прямоугольника, function TextWidth (const Text: String): Integer; охватывающего текстовую строку T e x t
в
з
Рис. 10.1. Параметры обращения кметодам: a—Arc; б — Chord; в — Pie; г —RoundRect
Следует отметить два момента. Во-первых, создаваемые на канве изображения безвозвратно теряются при перемещении окна программы, его перекрытии дру гими окнами, при изменении размеров окна или канвы. Если вам необходимо восстановить изображение, следует вынести его отрисовку в отдельную под программу и вызывать ее в обработчиках событий O n A c t i v a t e H O n R e s i z e . Во-вторых, нельзя рисовать на невидимой канве. Это означает, что свойство V i s i b l e компонента, содержащего канву, игнорируется, как только на канве появляется изображение. Это также означает, что нельзя отрисовывать изобра жение в обработчике O n C r e a t e формы, так как в момент создания окна с кан вой оно еще не появилось на экране.
Классы TGraphic и TPicture Важное место в графическом инструментарии Delphi занимают классы T G r a p h i c иTPicture. TGraphic — это абстрактный класс, инкапсулирующий общие свойства и методы трех своих потомков: значка (TIcon), метафайла (TMetaf i l e ) и растрового изоб ражения (TBitmap). Общей особенностью потомков T G r a p h i c является то, что обычно они сохраняются в файлах определенного формата. Значки представляют собой небольшие растровые изображения, снабженные специальными средства ми, регулирующими их прозрачность. Для файлов значков обычно используется расширение ICO. Метафайл — это изображение, построенное на графическом уст ройстве с помощью специальных команд, которые сохраняются в файле с расши рением WMF или EMF. Растровые изображения — это произвольные графические рисунки в файлах со стандартным расширением BMP. В табл. 10.12 перечислены свойства класса TGraphic, а в табл. 10.13 — его методы.
222
Глава 10 • Классы общего назначения
Таблица 10.12. Свойства класса TGraphic Свойство
Описание
property Empty: Boolean;
Содержит T r u e , если с объектом не связано графическое изображение Содержит высоту изображения в пикселах Содержит T r u e , если графический объект изменялся Содержит цветовую палитру графического объекта Содержит T r u e , если менялась цветовая палитра графического объекта Содержит T r u e , если объект прозрачен для фона, на котором он изображен Содержит ширину изображения в пикселах
property Height: Integer; property Modified: Boolean; property Palette: HPALETTE; property PaletteModified: Boolean; property Transparent: Boolec property Width: Integer;
Таблица 1 0 . 1 3 . Методы класса TGraphic Метод
Описание
procedure LoadFromClipboardFormat (AFormat: Word; AData: THandle; APalette: HPALETTE);
Ищет в буфере межпрограммного обмена (clipboard) зарегистрированный формат A F o r m a t и, если формат найден, загружает из буфера изображение AData и его палитру A P a l e t t e
procedure LoadFromFile (const FileName: String); procedure LoadFromStream (Stream: TStream);
Загружает изображение из файла F i l e N a m e
procedure SaveToClipboardFormat (var AFormat: Word; var AData: THandle; var APalette: HPALETTE)
Помещает графическое изображение AData и его цветовую палитру A P a l e t t e в буфер межпрограммного обмена в формате A F o r m a t
procedure SaveToFile (const FileName: String); procedure SaveToStream (Stream: TStream);
Сохраняет изображение в файле F i l e N a m e
Загружает изображение из потока данных Stream
Сохраняет изображение в потоке данных Stream
Полнофункциональный класс T P i c t u r e инкапсулирует в себе все необходимое для работы с готовыми графическими изображениями — значком, растром или метафайлом. Его свойство Graphic может содержать объект любого из этих ти пов, обеспечивая нужный полиморфизм методов класса. В табл. 10.14 и 10.15 перечислены свойства и методы класса T P i c t u r e . Таблица 10.14. Свойства класса TPicture Свойство property Bitmap:
Описание TBitmap;
property G r a p h i c : TGraphic; property Height: I n t e g e r ; property I c o n : TIcon;
Интерпретирует графический объект как растровое изображение Содержит графический объект Содержит высоту изображения в пикселах Интерпретирует графический объект как значок
Графический инструментарий
223
Свойство
Описание
property M e t a f i l e : T M e t a f i l e ; property Width: I n t e g e r ;
Интерпретирует графический объект как метафайл Содержит ширину изображения в пикселах
Таблица 1 0 . 1 5 . Методы класса TPicture Метод
Описание
procedure Assign(Source: TPersistent); procedure LoadFromClipboardFormat (AFormat: Word; AData: THandle; APalette: HPALETTE);
Связывает собственный графический объект G r a p h i c с объектом S o u r c e Ищет в буфере межпрограммного обмена (clipboard) зарегистрированный формат AFormat и, если формат найден, загружает из буфера изображение AData и его палитру APalette Загружает изображение из файла F i l e N a m e
procedure LoadFromFile (const FileName: String); procedure SaveToClipboardFormat (var AFormat: Word; var AData: THandle;var APalette: HPALETTE); procedure SaveToFile (const FileName: String);
Помещает графическое изображение AData и его цветовую палитру A P a l e t t e в буфер межпрограммного обмена в формате A F o r m a t Сохраняет изображение в файле F i l e N a m e
Имена и владельцы компонентов
Общие свойства компонентов
225
t r o l и его потомки) и не имеющие этого ресурса (класс T G r a p h i c C o n t r o l и его потомки). Оконный ресурс — это специальный ресурс Windows, предназначен ный для создания и обслуживания окон. Только оконные компоненты способны получать и обрабатывать сообщения Windows.
Рис. 11.1. Фрагмент иерархии компонентов Delphi
Компонентами в Delphi называются потомки класса TComponent. В этой главе опи сываются общие свойства, методы и события компонентов. В остальных главах книги при описании конкретного компонента эти свойства уже не упоминаются. Следует учесть, что они относятся в основном к классам-родоначальникам — TObject, T P e r s i s t e n t , TComponent и T C o n t r o l . Некоторые из них описаны в секциях p r o t e c t e d этих классов и могут быть недоступны своим- потомкам. Поэтому для получения полного набора методов, свойств и событий конкретного компонента необходимо обращаться к встроенной справочной службе Delphi.
Иерархия компонентов Все компоненты Delphi порождены от класса TComponent, в котором инкапсу лированы их самые общие свойства и методы. Предком TComponent является класс T P e r s i s t e n t , который происходит непосредственно от базового класса T O b j e c t (рис. 11.1). Класс T P e r s i s t e n t передает своим потомкам важный виртуальный метод: procedure Assign(Source: T P e r s i s t e n t ) ; С помощью этого метода поля и свойства объекта S o u r c e копируются в объект, вызвавший метод A s s i g n . Замечу, что обмениваться данными могут все наслед ники класса T P e r s i s t e n t независимо от того, находятся они в непосредствен ном родстве по отношению друг к другу или имеют единственного общего пред ка — класс T P e r s i s t e n t . Класс TComponent служит базой для создания как видимых, так и невидимых компонентов. Большая часть видимых компонентов происходит от класса TCont r o l . Два наследника этого класса— TWinControl и T G r a p h i c C o n t r o l — оп ределяют две группы компонентов: имеющие оконный ресурс (класс TWinCon-
Оконный компонент в момент своего создания обращается к Windows с требова нием выделения оконного ресурса и, если требование удовлетворено, получает так называемый дескриптор окна. Класс TWinControl и его потомки хранят дес криптор окна в свойстве H a n d l e . Программист может использовать этот деск риптор для непосредственного обращения к API-функциям Windows. Потомки T G r a p h i c C o n t r o l не требуют от Windows дефицитного оконного ресурса, но они и не могут получать и обрабатывать Windows-сообщения — управляет таки ми элементами оконный компонент-владелец (например, форма), который явля ется посредником между Windows и неоконными компонентами.
Имена и владельцы компонентов Класс TComponent включает в себя свойства и методы, общие для всех компо нентов. Свойство Name определяет имя компонента: p r o p e r t y Name: TComponentName; t y p e TComponentName = S t r i n g ;
Имя компонента строится по тем же правилам, что и имена любых других объек тов программирования — констант, переменных, подпрограмм и т. д. Оно пред ставляет собой правильный идентификатор и должно быть уникальным в облас ти своей видимости в программе. Так как компоненты помещаются на форму средой Delphi, каждый компонент автоматически получает создаваемое средой имя, совпадающее с именем своего класса (без начальной буквы т) и дополнен ное числовым суффиксом: Forml, L a b e l 1, E d i t 2 и т. д. Впоследствии програм мист может переименовать компонент, чтобы сделать текст программы более 8 Зак. 126
2 2 6 Глава 11 • Общие свойства компонентов читабельным. При разработке собственных имен полезно выработать свою сис тему двух- или трехбуквенных префиксов, кодирующих тип компонента. Напри мер, fm — для формы TForm, l b — для метки TLabel, ed — для поля T E d i t и т. п. Комбинируя префикс с мнемоническим именем, можно существенно улуч шить читабельность программы и усилить контроль за правильным использова нием свойств и методов компонента. Например, показанный далее оператор сра зу вызывает подозрение: lbOutput .Text :'= 'Текст'; Причина в том, что префикс lb определяет компонент TLabel, который не име ет свойства или поля с именем T e x t . Следующее свойство определяет произвольный целочисленный параметр, кото рый не используется Delphi и которым программист может распоряжаться по своему усмотрению:
Родительские и дочерние компоненты
227
Для управления списком Components предназначены методы, перечисленные в табл. 11-1Таблица 11.1. Методы управления списком Components Метод
Описание
procedure DestroyComponents;
Вызывает поочередно деструкторы всех компонентов из списка Components и удаляет все компоненты из списка Отыскивает в списке компонент с именем AName и возвращает ссылку на него
function FindComponent (const AName: String) : TComponent; procedure InsertComponent (AComponent: TComponent); procedure RemoveComponent (AComponent: TComponent) ;
Вставляет компонент AComponent в конец списка Components Удаляет компонент AComponent из списка
property Tag: TObject; Любой компонент Delphi (кроме форм и модулей данных) является собственно стью другого компонента и, в свою очередь, может быть владельцем одного или нескольких компонентов. Такая зависимость компонентов друг от друга позво ляет существенно упростить процесс управления ими. Например, чтобы удалить окно с включенными в него компонентами, достаточно вызвать деструктор окна: он поочередно вызовет деструкторы всех других компонентов, владельцем кото рых является компонент-окно, и таким образом полностью освободит выделен ные окну ресурсы. Указывает на владельца компонента следующее свойство (это свойство доступно только для чтения): property Owner: TComponent; Положение компонента в массиве Components своего владельца определяет свой ство C o m p o n e n t l n d e x : property Componentlndex: Integer; Сам массив содержит список всех компонентов, которыми владеет данный ком понент, и определяется следующим свойством: property Components[Index:
Integer]:
TComponent;
Количество зарегистрированных в списке компонентов возвращает свойство СотponentCount: property ComponentCount: Integer; Конструктор TComponet. C r e a t e имеет единственный параметр обращения, в ко тором компоненту передается ссылка на его владельца: constructor Create(AOwner: TComponent); В ходе выполнения конструктора компонент вставляет ссылку на себя в список C o m p o n e n t s своего владельца и изменяет содержимое собственного свойства Owner.
Родительские и дочерние компоненты Наследники класса T C o n t r o l образуют всю палитру видимых компонентов Delphi. В терминах Windows они называются элементами управления, так как на их основе прежде всего реализуются элементы интерфейса Windows — кнопки, флажки, переключатели, списки и т. п. В тексте книги я часто буду употреблять слова «компонент» и «элемент управления» как синонимы. Как уже отмечалось, некоторые из наследников T C o n t r o l обладают дескрипто рами окон и способны получать и обрабатывать Windows-сообщения, другие окон не имеют, но обязательно включаются в состав оконных компонентов, которые управляют ими, согласуясь с требованиями (сообщениями) Windows. Оконные элементы управления обладают специальной оконной процедурой, в которую Windows посылает управляющие сообщения (например, извещения о манипуля ции пользователя мышью или о нажатии клавиш). В терминах Windows такие элементы называются родительскими, а связанные с ними неоконные элементы — дочерними. Замечу, что оконный компонент может выступать как родительский по отношению не только к неоконным, но и к оконным компонентам. В этом слу чае он просто транслирует управляющие Windows-сообщения в оконные проце дуры дочерних компонентов. Обязательным требованием Windows является ви зуальная синхронизация дочерних элементов: они не могут выходить за границы своего родителя и появляются и исчезают вместе с ним. Иными словами, роди тель с дочерними элементами рассматривается Windows как единое целое. Класс T C o n t r o l определяет свойство P a r e n t , которое содержит ссылку на родитель ский компонент: property Parent: TWinControl; Это свойство не следует путать со свойством владения (Owner): владелец создает компонент (не обязательно видимый), а родитель ( P a r e n t ) управляет видимым компонентом. Поскольку конструктор TComponent. C r e a t e не изменяет свойства P a r e n t (в родительском классе TComponent такого свойства нет), при создании
228
Глава 11 • Общие свойства компонентов
видимых компонентов на этапе прогона программы это свойство необходимо из менять программно. Например, следующий обработчик события O n C r e a t e фор мы Forml вставит надпись Дочерний элемент в левый верхний угол формы: procedure T F o r m l . F o r m C r e a t e ( S e n d e r : T O b j e c t ) ; . var lbLabel: TLabel; begin lbLabel := T L a b e l . C r e a t e ( S e l f ) ; l b L a b e l . P a r e n t := Self; l b L a b e l . C a p t i o n : = 'Дочерний э л е м е н т ' ; end; Обратите внимание на следующий оператор: lbLabel.Parent := Self; Если убрать этот оператор из приведенного фрагмента, метка никогда не «узна ет» о том, что пришла пора отрисовать себя на экране, и ее текст не появится. Наоборот, изменение свойства P a r e n t подключает метку к списку дочерних эле ментов формы, и оконная процедура формы обратится к нужному методу метки, чтобы заставить ее появиться на экране в момент появления самой формы. Помимо свойства Components каждый оконный компонент получает от своего родителя T W i n C o n t r o l свойство C o n t r o l s , содержащее список дочерних эле ментов: property C o n t r o l s [ I n d e x :
Integer]:
TControl;
Свойство C o n t r o l C o u n t возвращает количество дочерних элементов (длину мас сива C o n t r o l s ) : property ControlCount: Integer; Работать со списком C o n t r o l s можно с помощью методов, перечисленных в табл. 11.2. Таблица 11.2. Методы управления списком Controls
Положение, размеры и оформление компонентов
229
Метод
Описание
procedure G e t T a b O r d e r L i s t (List: TList) ;
Создает список L i s t всех дочерних компонентов в порядке их обхода, реализуемого нажатием клавиши Tab Вставляет новый дочерний элемент в список C o n t r o l s . Программа не должна обращаться непосредственно к этому методу: для вставки дочернего элемента следует установить нужное значение свойства P a r e n t во вставляемом компоненте
procedure I n s e r t C o n t r o l (AControl: T C o n t r o l ) ;
Посылает сообщение Msg во все дочерние оконные компоненты Перерисовывает родительский компонент и все его дочерние компоненты Перерисовывает все дочерние элементы procedure S c a l e C o n t r o l s с размерами, измененными в M/D раз по сравнению (M, D: I n t e g e r ) ; с предыдущими размерами Выбирает первый в порядке обхода, реализуемого procedure S e l e c t F i r s t ; нажатием клавиши Tab, дочерний компонент procedure S e l e c t N e x t ( C u r C o n t r o l : Выбирает очередной в порядке обхода, реализуемого нажатием клавиши Tab, дочерний TWinControl; GoForward, компонент. C u r C o n t r o l определяет начальную CheckTabStop: B o o l e a n ) ; точку поиска; G o F o r w a r d = T r u e , если поиск идет к концу списка; C h e c k T a b S t o p = T r u e , если выбираются только компоненты со свойством TabStop = True
procedure N o t i f y C o n t r o l s (Msg: Word); procedure R e p a i n t ;
procedure S e t C h i l d O r d e r ( C h i l d : TComponent; O r d e r : Integer);
Устанавливает новый порядок обхода, реализуемого нажатием клавиши Tab, компонента C h i l d
procedure S h o w C o n t r o l (AControl: T C o n t r o l ) ;
Показывает дочерний компонент A C o n t r o l
Положение, размеры и оформление компонентов Положение и размеры компонента (в пикселах) определяются четырьмя его свой ствами:
Метод
Описание
function ContainsControl ( C o n t r o l : TControl) : Boolean;
Возвращает значение T r u e , если компонент C o n t r o l является дочерним элементом
property H e i g h t :
function ControlAtPos (const Pos: TPoint; AllowDisabled: Boolean): TControl;
Возвращает ссылку на компонент, которому принадлежит точка с координатами Pos. Если A l l o w D i s a b l e d = T r u e , поиск ведется также среди запрещенных для выбора компонентов
property L e f t : I n t e g e r ; property Top: I n t e g e r ; property Width: I n t e g e r ;
function FindNextControl ( C u r C o n t r o l : TWinControl; GoForward, C h e c k T a b S t o p , C h e c k P a r e n t : Boolean) : TWinControl;
Ищет новый элемент в списке C o n t r o l s . C u r C o n t r o l определяет начальную позицию поиска; G o F o r w a r d = T r u e , если поиск идет от начальной позиции к концу списка; C h e c k T a b S t o p = T r u e , если поиск идет только среди компонентов со свойством T a b S t o p = T r u e ; если C h e c k P a r e n t = T r u e , поиск идет только среди оконных элементов
Для всех компонентов, кроме форм, эти свойства задаются в координатах клиентс кой части родительского компонента, для форм — в координатах экрана. Клиентс кая часть компонента — это его внутренняя область за исключением заголовка, рам ки и меню. Свойства обычно определяются на стадии конструирования формы, но они доступны также и на этапе прогона программы. Изменение любого из них при водит к немедленному изменению положения или размера компонента как на этапе конструирования, так и при прогоне программы. Как показано далее, все четыре чис ловые величины содержатся также в единственном свойстве BoundsRect:
Integer;
/ / Высота // Положение // Положение / / Ширина
левой кромки верхней кромки
Положение, размеры и оформление компонентов
2 3 0 Глава 11 • Общие свойства компонентов type TPoint = record X: Longlnt; У: Longlnt; end; property BoundsRect: TRect; TRect = record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end; Это свойство удобно использовать при отрисовке компонента методом ТСапvas.FrameRect. В некоторых случаях бывает необходимо пересчитать относительные координа ты точки внутри клиентской части в абсолютные координаты экрана и наоборот. Эта задача решается двумя методами класса T C o n t r o l : function ClientToScreen(const Point: TPoint): TPoint; function ScreenToClient(const Point: TPoint): TPoint; Важную роль играет свойство A l i g n , определяющее положение компонента от носительно границ своего родителя: type TAlign = (alNone, alTop, alBottom, alLeft, alRight, alClient); property Align: TAlign; Если это свойство не равно alNone, компонент прижимается к верхней (alTop), нижней ( a l B o t t o m ) , левой ( a l L e f t ) или правой ( a l R i g h t ) границе своего ро дителя. При этом размеры компонента по соседним с границей измерениям игно рируются и компонент «растекается» по границе. Например, если A l i g n = alTop, значения свойств L e f t и w i d t h компонента игнорируются и его прямоугольник будет занимать всю верхнюю часть клиентской области родителя высотой H e i g h t пикселов; если A l i g n = a l L e f t , свойства Тор и H e i g h t игнорируются и прямо угольник занимает левую часть родителя шириной Width пикселов и т. д. Если несколько компонентов имеют одинаковое выравнивание, они последовательно прижимаются друг к другу в порядке их перечисления в свойстве C o n t r o l s : пер вый прижимается к границе родителя, второй — к границе первого и т. д. Вся не заполненная другими компонентами клиентская область родителя заполняется компонентами со свойствами A l i g n = a l C l i e n t , которые в этом случае (то есть если их несколько) накладываются друг на друга. Замечательной особенностью свойства является его постоянство при изменении размеров клиентской части ро дителя. Если, например, компонент прижат к верхней границе формы, он будет неизменно занимать верхнюю часть клиентской области при любых изменениях размеров окна. Таким способом можно легко создавать панели инструментов, строки состояния и т. п. Временное отключение и затем включение эффекта от свойства A l i g n обеспечивается следующими методами:
231
procedure DisableAlign; procedure EnableAlign; Любой видимый компонент можно скрыть или показать с помощью свойства V i s i b l e или методами Hide и Show: p r o p e r t y V i s i b l e : B o o l e a n ; // True - показывает компонент procedure H i d e ; // Скрывает компонент procedure Show; // Показывает компонент Скрытый компонент не реагирует на события мыши и клавиатуры, он не уча ствует в «дележе» клиентской области родителя, ему нельзя передать фокус вво да нажатием клавиши Tab. Если компонент частично или полностью перекрывается другими компонента ми, его можно расположить над всеми компонентами и убрать под них с помо щью следующих методов: procedure B r i n g T o F r o n t ; // Сделать верхним procedure SendToBack; // Сделать нижним Свойство E n a b l e d определяет возможность активизации компонента: property Enabled: Boolean; Если это свойство имеет значение F a l s e , компонент недоступен. Такие компо ненты, точнее, надписи на них, обычно отображаются серым цветом. Некоторые компоненты выглядят плоскими (например, метка TLabel), другие — всегда объемными (например, кнопка T B u t t o n ) . Для остальных элементов объем ность представления регулируется свойством Ctl3D: property Ctl3D: Boolean; Все потомки класса TWinControl получают от своего родителя 4 свойства, которые позволяют очерчивать оконные компоненты внутренней и внешней рамками: type TBevelEdge = (beLeft, beTop, beRight, beBottom); TBevelEdges = set of TBevelEdge; property BevelEdges: TBevelEdges; // Стороны с рамками type TBevelKind = (bkNone, bkTile, bkSoft, bkFlat) property BevelKind: TBevelKind; // Стиль рамок type TBevelCut = (bvNone, bvLowered, bvRaised, bvSpace); property Bevellnner: TBevelCut; // Внутренняя рамка property BevelOuter; TBevelCut; // Внешняя рамка Очерчиваются только те стороны компонента, которые содержатся в свойстве BevelEdges. Свойство BevelKind определяет стиль рамок: •
b k N o n e — рамок нет;
•
b k T i l e — рамки контрастные (черные);
2 3 2 Глава 11 • Общие свойства компонентов •
bkSof t — рамки пониженной контрастности (серые);
Положение, размеры и оформление компонентов
Придав этому свойству значение True, можно потребовать от компонента, что
• b k F l a t — рамки не контрастные (белые).
бы он выводил текст системным шрифтом Windows.
Тип TBevelCut определяет эффект объемности для соответствующей рамки:
Видимая часть элемента заливается цветом C o l o r :
• bvNone — нет объемности; • bvLowered — вдавленная рамка; • b v R a i s e d — выпуклая рамка; • bvSpace — рамка белая, если свойство BevelKind не содержит b k T i l e , в про тивном случае — выпуклая. Замечу, что наличие описанных свойств у класса T W i n C o n t r o l еще не означа ет, что они будут у каждого его потомка. Например, свойства B e v e l E d g e s H B e v e l K i n d при объявлении класса T P a n e i не переобъявляются и потому у панели отсутствуют, зато объявляется дополнительное свойство B e v e l W i d t h , определяющее толщину рамок. С каждым управляющим компонентом связывается текстовая строка, которая становится доступной либо через свойство C a p t i o n , либо через свойство T e x t (альтернативой свойству Text, которое имеет тип S t r i n g , является свойство WindowsText типа PChar класса T C o n t r o l ) . Независимо от того, какое свой ство хранит эту строку, ее можно установить и получить методами S e t T e x t B u f и G e t T e x t B u f соответственно, при этом метод G e t T e x t L e n возвращает длину строки: procedure SetTextBuf(Buffer: PChar); function GetTextBuf(Buffer: PChar; BufSize: Integer): Integer; function GetTextLen: Integer; Если эта строка отрисовывается в компоненте, используется шрифт, задаваемый свойством F o n t : property Font: TFont; В этом случае свойство TAlignment регулирует расположение текста относи тельно границ компонента: type TAlignment = (taLeftJustify, taRightJustify, taCenter); property Alignment: TAlignment; Допустимые значения свойства: •
t a L e f t J u s t i f y — прижать клевой границе;
•
t a R i g h t J u s t i f у — прижать к правой границе;
•
t a C e n t e r — расположить по центру.
Рассмотрим следующее свойство: property DesktopFont: Boolean;
233
property C o l o r : TColor; Обычно значение этого свойства выбирается в таблице стандартных цветов Win dows в виде константы clXXXX (перечень этих констант содержит раскрываю щийся список свойства). В некоторых случаях может потребоваться залить ком понент нестандартным цветом. В этом случае учтите, что свойство C o l o r хранит четырехбайтное значение, каждый байт которого (слева направо, то есть от стар шего к младшему) имеет следующее назначение: • •
1 — указатель формата цвета (см. далее); 2, 3, 4 — интенсивность синей, зеленой и красной составляющих соответст венно.
Например, значение $00000000 определяет черный цвет, $00FF0000 — чистый синий цвет, $00FFFFFF — белый цвет и т. д. Старший байт указывает, как используются остальные байты значения. Если он равен нулю, они определяют RGB-цвет так, как это описано ранее. Если стар ший байт равен 1, три оставшихся байта определяют номер одной из 65 536 воз можных логических палитр (второй байт в этом случае игнорируется). Наконец, если старший байт равен 2, младшие определяют относительный цвет: в этом случае Windows отыскивает в текущей логической палитре ближайший к ука занному цвет и использует его для заливки компонента. Другие значения стар шего байта игнорируются (точнее, игнорируются старшие 5 разрядов этого бай та; самый старший бит, если он равен 1, определяет черный цвет независимо от значений остальных 31 разрядов). Чтобы разнообразить унылые серые окна программы, соответствующие стандарт ным формам Delphi (в Windows 98), я иногда использую значение $00AAFFFF, определяющее бледно-желтый цвет (в свойство C o l o r можно вводить значения в виде десятичных или шестнадцатеричных чисел). Для упрощения разработки интерфейсных элементов, выдержанных в едином стиле, в состав видимых компонентов включены свойства, связывающие ту или иную особенность оформления или поведения компонента с соответствующей особенностью родителя. Таких свойств четыре: property P a r e n t C o l o r : Boolean; property P a r e n t C t l 3 d : Boolean; property P a r e n t F o n t : Boolean; property P a r e n t H i n t : Boolean; Если, например, цвет компонента не задан явно свойством C o l o r и его свойство P a r e n t C o l o r имеет значение True, компонент будет использовать при отрисовке цвет своего родителя, а изменение цвета родителя приведет к немедленно му изменению цвета дочернего компонента. Явная установка значений свойств C o l o r , Ctl3D, F o n t или H i n t приводит к автоматической установке значений F a l s e в соответствующие свойства ParentXXX.
Реакция'на события мыши и клавиатуры
2 3 4 Глава 11 • Общие свойства компонентов В классе T C o n t r o l имеется свойство Anchors, определяющее способ фиксации дочернего компонента относительно границ контейнера, в котором он размещен: property Anchors: TAnchors; type TAnchors = set of TAnchorKind; type TAnchorKind = (akTop, akLeft, akRight, akBottom); По умолчанию это свойство имеет значение [akTop, a k L e f t ] , что означает фик сацию компонента относительно левого верхнего угла контейнера. В результате возможные изменения размеров контейнера никак не скажутся на изменении по ложения и/или размеров компонента. Если установить значение [ a k R i g h t , a k B o t t o m ] , правая и нижняя кромки компонента всегда будут располагаться на одном и том же расстоянии от соответствующих кромок контейнера. Так как по ложение левого верхнего угла компонента при этом не фиксируется, он будет «пла вать» внутри контейнера, всегда располагаясь на одном и том же расстоянии от его правой и нижней кромок. Однако, если установлено значение [akTop, a k L e f t , a k R i g h t , a k B o t t o m ] , левый верхний угол фиксируется и компонент будет пы таться отслеживать расстояние до правого нижнего угла контейнера за счет изме нения своих размеров. Поместите на пустую форму поле T E d i t и поэксперимен тируйте с его свойством Anchors, каждый раз изменяя мышью размеры формы (не обязательно запускать программу — свойство A n c h o r s работает и на этапе конструирования). В класс T C o n t r o l введено также свойство C o n s t r a i n t s : property Constraints: TSizeConstraints; Это свойство используется для ограничения максимальных и минимальных раз меров элементов управления по высоте и ширине. Объект класса T S i z e C o n s t r a i n t s имеет четыре свойства: MaxHeight, MaxWidth, MinHeight, MinWidth типа 0. . M a x l n t , которые и определяют предельные значения указанных пара метров. По умолчанию эти свойства имеют нулевые значения, что эквивалентно снятию каких бы то ни было ограничений. Свойство A u t o S i z e впервые введено в Delphi 4: property AutoSize: Boolean; Это свойство связано в основном с интерфейсом «причаливания» (Drag&Dock) и разрешает ( T r u e ) или запрещает ( F a l s e ) оконному компоненту автоматичес ки изменять свои размеры в зависимости от количества и размеров содержащих ся в нем неоконных компонентов.
Указатели мыши При перемещении указателя мыши по экрану он может менять свою форму в за висимости от значения свойства C u r s o r компонента, на котором он расположен в данный момент: property Cursor: TCursor; type TCursor = -32768..+32767;
235
В Delphi предопределены стандартные указатели, показанные на рис. 11.2. В практике программирования часто возникает необходимость изменения фор мы указателя для всех окон программы. Например, при выполнении достаточно длительного по времени процесса указатель мыши часто принимает вид песоч ных часов ( c r H o u r G l a s s ) , а после завершения процесса восстанавливает свой первоначальный вид. Чтобы изменить форму указателя для всех окон програм мы одновременно, используется свойство C u r s o r глобального объекта S c r e e n , который автоматически создается для каждой программы: crNone crArrow crCross crIBeam crSize crSizeNESW crSizeNS
Рис. 11.2. Стандартные указатели Delphi Screen.Cursor := crHourGlass; ... // Делаем длительную работу Screen.Cursor := crDefault; // Восстанавливаем начальную // форму указателя
Реакция на события мыши и клавиатуры События мыши Для большинства видимых компонентов определен набор обработчиков собы тий, связанных с мышью: 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;
2 3 6 Глава 11 • Общие свойства компонентов property OnMouseMove: TMouseMoveEvent; property OnClick: TNotifyEvent; property OnDblClick: TNotifyEvent; Тип TMouseButton определяет одну из трех кнопок мыши: левую (mbLeft), правую (mbRigth) или среднюю (mbMiddle). Тип T S h i f t S t a t e содержит признаки, уточняющие обстоятельства возникно вения события: •
s s S h i f t — нажата клавиша Shift;
•
s s A l t — нажата клавиша Alt;
•
s s C t r l — нажата клавиша Ctrl;
•
s s L e f t — нажата левая кнопка мыши;
•
s s R i g h t — нажата правая кнопка мыши;
•
s s M i d d l e — нажата средняя кнопка мыши;
•
s s D o u b l e — нажаты одновременно левая и правая кнопки мыши.
Обработчики событий OnMouseDown и OnMouseUp определяют реакцию програм мы на нажатие и отпускание кнопки мыши соответственно, OnMouseMove — на перемещение указателя мыши на компоненте, O n C l i c k и O n D b l C l i c k — соот ветственно, на щелчок и двойной щелчок левой кнопкой мыши. Во всех обработ чиках параметр S e n d e r содержит ссылку на компонент, на котором находился указатель мыши в момент наступления события, а X и Y определяют координаты точки чувствительности указателя мыши в этот момент в системе координат кли ентской области родительского компонента. Замечу, что событие OnClick возни кает после события OnMouseDown, но перед событием OnMouseUp, а событие OnDblClick возникает после события OnMouseUp.
События клавиатуры События мыши возникают в любом потомке класса T C o n t r o l . В отличие от этого события клавиатуры возникают только в некоторых оконных компонентах (в потомках класса TWinControl). Обработка событий связана со следующими свойствами этих компонентов: type T S h i f t S t a t e = s e t of ( s s S h i f t , ssAlt, s s C t r l , s s L e f t , s s R i g h t , ssMiddle, ssDouble); TKeyEvent = procedure (Sender: TObject; var Key: Word; S h i f t : T S h i f t S t a t e ) of object; TKeyPressEvent = procedure (Sender: TObject; var Key: Char) of object; property OnKeyDown: TKeyEvent; property OnKeyUp: TKeyEvent; property OnKeyPress: TKeyPressEvent; Параметр S h i f t , как и в обработчиках событий от мыши, содержит уточняющие признаки. Параметр Key в обработчиках TKeyEvent содержит виртуальный код
Реакция на события мыши и клавиатуры
237
клавиши, а в обработчике T K e y P r e s s E v e n t — ASCII-символ. Обработчики On KeyDown и OnKeyUp перехватывают нажатие большинства клавиш клавиатуры, в то время как обработчик OnKeyPress — только нажатие алфавитно-цифровых клавиш. Получаемый им символ Key учитывает выбранный язык и нажатую кла вишу Shift. Виртуальные коды клавиш определены константами vk_XXX в файле SOURCE\RTL\ Borland.VCL.Windows.PAS каталога размещения Delphi. Фактически виртуальный к о д _ эхо просто уникальный числовой идентификатор клавиши. Для буквенноцифровых клавиш 0-9 и A-Z виртуальный код совпадает с кодом, возвращаемым функцией o r d (' X ' ) , где X — соответствующий прописной символ: o r d (' 0 ' ) . o r d (' W') и т. д. К сожалению, в зоне дополнительных числовых клавиш уникаль ность кода не обеспечивается для клавиши Enter, которой присвоен код 13, как и аналогичной клавише в основной зоне, а также для правых и левых клавиш Shift, Alt и Ctrl. Кроме того, клавиши 0-9 и Del в зоне дополнительной клавиатуры сопро вождаются уникальным кодом только при активном переключателе NumLock, в про тивном случае они повторяют коды соответствующих основных клавиш. Все ос тальные клавиши стандартной клавиатуры (за исключением клавиши Print Screen, клавиш перемещения курсора и клавиши Tab, нажатие которых не передается в об работчики TKeyEvent) имеют постоянно закрепленные за ними числовые коды, позволяющие легко установить факт нажатия или отпускания любой из них. Поскольку параметр Key в каждом обработчике определен как параметр-пере менная, программист может изменить фактический код клавиши. Такая возмож ность может оказаться полезной для фильтрации нажатия каких-либо клавиш. При этом изменение кода происходит в обработчике формы, а в оконный эле мент с фокусом ввода будет поступать уже измененный код. Чтобы форма полу чила сообщение от клавиатуры до передачи его в элемент с фокусом ввода, сле дует поместить в следующее свойство формы значение True: p r o p e r t y KeyPreview: Boolean;
Клавиатура в Windows Следует заметить, что Windows значительно «строже» относится к использова нию клавиатуры, чем MS-DOS. Это может вызывать проблемы при переносе в среду Delphi игровых приложений, а также приложений, созданных с помощью системы FoxPro или Clipper. Если вы захотите сохранить устоявшиеся приемы использования клавиатуры в новой разработке, вам, возможно, придется перехватывать сообщения Windows, так как только таким способом программа сможет опознать факт нажатия сис темных клавиш Alt, Tab, Shift и т. п. Нажатие остальных клавиш можно анализи ровать путем перехвата сообщений от клавиатуры в обработчиках ОпКеуХХХ фор мы при значении True ее свойства KeyPreview. Например, пусть сочетание клавиш Alt+X используется в существующей програм ме для закрытия модального диалогового окна. Чтобы разработанное вами окно закрывалось при нажатии этого сочетания, напишите для него следующий обра ботчик события OnKeyDown:
238
Реакция на события мыши и клавиатуры
Глава 11 • Общие свойства компонентов
p r o c e d u r e TForm2.FormKeyDown(Sender : T O b j e c t ; v a r Key: Word; S h i f t : T S h i f t S t a t e ) ; begin i f (Key = o r d ( ' X ' ) ) and ( s s A l t i n S h i f t ) t h e n C l o s e end;
Во многих случаях можно использовать собственные клавиши быстрого вызова Windows. К сожалению, такими клавишами снабжаются лишь команды меню, но связанные сними (командами) обработчики O n c l i c k выполняются даже в том случае, когда команда меню не видна (ее свойство V i s i b l e имеет значение F a l s e ) . Этим можно воспользоваться, чтобы вставлять в главное меню окна не видимые команды, связывая их с нужными клавишами. Пусть, например, кноп ка B u t t o n l должна «нажиматься» при нажатии клавиш Ctrl+S. Поместите на форму главное меню (если его еще нет) и создайте в нем пункт с произвольным именем, например MyButton. В списке свойства S h o r t c u t пункта меню выберите вариант C t r l + S и установите значение F a l s e в его свойство V i s i b l e . Теперь можно связать обработчик события O n C l i c k пункта меню непосредственно с со бытием B u t t o n l C l i c k или написать такой обработчик: procedure TForm2.MyButtonClick(Sender: TObject); begin ButtonlClick(Self) end; И хотя пункт MyButton главного меню не виден, нажатие связанных с ним кла виш Ctrl+S вызовет срабатывание нужного обработчика. В этом случае форма может не перехватывать клавиатурный ввод. Как уже отмечалось, обработчики ОпКеуХХХ не реагируют на нажатие систем ных клавиш Windows, в том числе клавиш перемещения курсора. Происходит это из-за того, что оконная процедура программы по умолчанию осуществляет стандартную обработку соответствующих сообщений Windows. ПРИМЕЧАНИЕ
;
В Delphi введен специальный компонент T A p p l i c a t i o n E v e n t s , который существенно упро щает обработку любых сообщений Windows до того, как они поступят в оконную функцию ак тивной формы.
Для клавиш определены виртуальные коды, перечисленные в табл. 11.3. ПРИМЕЧАНИЕ В квадратных скобках указаны клавиши из зоны дополнительных цифровых клавиш. Т а б л и ц а 1 1 . 3 . Виртуальные коды клавиш Код
Значение
Клавиша
vk_Back vk_Tab vkjClear
8 9 12
Backspace Tab [5]
Код
Значение
13 eturn vk__R 16 vk_Shift 17 vk__Control 18 vk_Menu 19 vkJPause 20 vkjCapital 27 vk_Escape 32 vk_Space 33 vk__Prior 34 vk_Next 35 vk_End 36 vk_Home 37 vk_Left 38 vkJJp 39 vk_Right 40 vk_Down 45 vk_Insert 46 vk_Delete 48-57 vk_0-vk_9 65-90 vk_A-vk_Z 91 vk_LWin 92 vk_RWin vk_NumpadO-vk Numpad9 vk_Multiply 106 vk_Add 107 vk_Subtract 109 vk_Decimal 110 vk_Divide 111 vkFl-vkF12 112-123 vk_Numlock 144 vk S c r o l l 145 186 187 188 189 190 191 192 219 220 221
239
Клавиша Enter Shift Ctrl Alt Pause Caps Lock Esc Пробел Page Up Page Down End Home
<— T —>
i Insert Delete 0-9 A-Z Windows (левая) Windows (правая) 96-105 [0]-[9] [*] [+] [-] [Del] [/] F1-F12 Num Lock Scroll Lock
+ < > ? [
\ ]
Учтите, что константы vk_A-vk_z n v k _ 0 - v k _ 9 не определены в файле SOURCE\RTL\ Boland.VCL.WINDOWS.PAS, поэтому для них компилятор выдаст сообщение
2 4 0 Глава 11 • Общие свойства компонентов о неизвестном идентификаторе — в этом случае используйте собственные опре деления констант или их числовые эквиваленты. Замечу также, что путем ана лиза параметра M s g . l P a r a m в обработчике A p p l i c a t i o n . O n M e s s a g e можно отличить нажатие левых клавиш Alt, Ctrl, Shift от нажатия одноименных правых клавиш, а также нажатие клавиши Enter на основной клавиатуре от нажатия кла виши Enter на дополнительной клавиатуре.
Фокус ввода Поскольку клавиатура одна, а элементов, могущих ее использовать, может быть много, необходимо каким-то способом выделять элемент, которому в данный момент передается клавиатурный ввод. Это выделение достигается передачей элементу фокуса ввода с помощью его метода: procedure SetFocus; Компонент с фокусом ввода имеет значение True в своем свойстве property Focused: Boolean; Если элемент недоступен или невидим, его следующее свойство имеет значение False: property CanFocus: Boolean; Фокус ввода передается элементу после щелчка на нем мышью или в порядке выбора его нажатием клавиши Tab. Чтобы элемент можно было выбирать с по мощью этой клавиши, следует установить в следующее его свойство значение True: property TabStop: Boolean; Порядок выбора элемента определяется другим свойством: property TabOrder: Integer; Delphi следит за уникальностью и непрерывностью значений этого свойства у всех помещенных на форму доступных и видимых в данный момент компонентов. С помощью следующего метода можно получить список всех дочерних элемен тов, выбираемых нажатием клавиши Tab: procedure G e t T a b O r d e r L i s t ( L i s t : T L i s t ) ;
Механизм действий В Delphi имеется специальный механизм, обеспечивающий удобное средство цен трализованной реакции программы на те или иные действия пользователя. Для реализации механизма действий в категории Standard палитры компонентов поме щен специальный компонент T A c t i o n L i s t (см. главу 12), а в класс TComponent введено свойство A c t i o n : property Action: TBasicAction;
Механизм перетаскивания
241
Централизация необходима потому, что подавляющее большинство промыш ленных программ для Windows содержат два или более интерфейсных элемен та, реализующих одно и то же действие. Например, в среде Delphi некоторые команды главного меню дублируются соответствующими кнопками панели ин струментов (в принципе, любая команда меню Delphi может иметь кнопочный эквивалент). Компонент T A c t i o n L i s t содержит список имен действий и связанных сними имен подпрограмм, которые эти действия реализуют. Чтобы связать компонент с нужным действием, достаточно раскрыть список Action (этот список содержит имена всех действий во всех компонентах T A c t i o n L i s t ) и выбрать нужное на звание. При этом обычно меняется графическое изображение и/или надпись на компоненте, а также связанная с ним оперативная подсказка ( H i n t ) . Свойство A c t i o n используется только после создания и заполнения списка (списков) TActionList. Учтите, что если для действия не определен обработчик OnExecute, все связан ные с этим действием элементы управления на этапе прогона программы будут недоступными.
Механизм перетаскивания Операционная система Windows широко использует специальный прием связыва ния программ с данными, который называется Drag&Drop (перетащи и отпусти). Такой прием в Проводнике Windows применяется для копирования или переме щения файлов, а также для запуска обрабатывающей программы. Если, например, файл с расширением DOC перетащить на значок программы Word, автоматически запустится текстовый редактор Word и в его окне появится текст из этого файла. В Delphi реализован собственный механизм Drag&Drop, позволяющий компо нентам обмениваться данными путем перетаскивания их мышью. Этот механизм определяется двумя свойствами и тремя событиями, доступными каждому види мому компоненту. Первое свойство: TDragMode = (dmManual, d m A u t o m a t i c ) ; p r o p e r t y DragMode: TDragMode;
Это свойство определяет, как будет выполняться весь комплекс действий, свя занных с перетаскиванием: •
dmManual — вручную (то есть все необходимые для обслуживания механиз ма события генерируются программой);
•
dmAutomatic — автоматически (события инициируются свойствами и мето дами компонентов).
Во всех случаях программист должен написать обработчики этих событий (см. далее). о второе свойство:
2 4 2 Глава 11 • Общие свойства компонентов
Механизм причаливания
property DragCursor: TCursor;
property OnEndDrag: TEndDragEvent;
Это свойство определяет вид указателя мыши в момент, когда на компоненте ока зываются перетаскиваемые данные. Если компонент готов принять данные, он ус танавливает в это свойство значение c r D r a g fei , в противном случае — crNoDrag ф). Установка этих свойств осуществляется автоматически, если выполняется ус ловие DragMode = dmAutomatic Следующее событие возникает, когда указатель мыши «с грузом» оказывается на компоненте TDragState = (dsDragEnter, dsDragLeave, dsDragMove); TDragOverEvent = procedure(Sender, Source: TObject; X, Y: I n t e g e r ; S t a t e : TDragState; var Accept: Boolean) of object; property OnDragOver: TDragOverEvent; Параметры здесь следующие: •
• • •
243
S e n d e r — компонент, который генерирует событие (обычно имеет значение S e l f — сам компонент-получатель, хотя при программном управлении меха низмом перетаскивания это может быть не так); S o u r c e — компонент-отправитель «груза»; X, Y — текущие координаты указателя мыши в пикселах клиентской области компонента; S t a t e — состояние указателя: о d s D r a g E n t e r — только что появился на компоненте;
Здесь S e n d e r — отправитель данных; T a r g e t — получатель данных или NIL, если никто не принял «посылку»; X, Y — координаты мыши в момент отпуска ния левой кнопки. Для ручного (программного) управления механизмом перетаскивания использу ются специальные методы, доступные любому потомку класса T C o n t r o l и пе речисленные в табл. 11.4. Таблица 11.4. Методы управления механизмом Drag&Drop Метод procedure BeginDrag (Immediate: Boolean) ;
procedure DragDrop (Source : TObject; X, Y: I n t e g e r ) ; procedure EndDrag (Drop: Boolean) ;
Описание Используется источником для инициирования процесса перетаскивания. Если Immediate = True, процесс начинается немедленно, в противном случае— после смещения указателя мыши на 5 пикселов в любом направлении Вызывает обработчик события OnDragDrop Вызывает обработчик события OnEndDrag и в параметре Drop сообщает о том, были ли приняты данные
Механизм причаливания В Delphi введена поддержка специального механизма Drag&Dock (перетащи и причаль), посредством которого можно «причалить» мышью компонент в но вое место.
• A c c e p t — в этом параметре обработчик сообщает, готов ли компонент при нять данные ( T r u e — готов).
В механизме Drag&Dock участвуют два компонента: принимающий компонент (всегда потомок класса T W i n C o n t r o l ) и причаливаемый компонент (потомок класса T C o n t r o l ) . Принимающий компонент должен иметь значение True в сво ем свойстве Docks it е. Поскольку причаливание является разновидностью бо лее общего механизма перетаскивания, в класс T C o n t r o l введено дополнитель ное свойство, благодаря которому различаются способы использования мыши:
Следующее событие означает, что пользователь «бросил» данные на компонент:
property DragKind: TDragKind;
TDragDropEvent = procedure(Sender, Source: TObject;
type TDragKind = (dkDrag, dkDock);
о d s D r a g L e a v e — только что покинул компонент или была отпущена кнопка мыши; о dsDragMove — перемещается на компоненте;
X, Y: Integer) of object; property OnDragDrop: TDragDropEvent; Параметры обработчика аналогичны одноименным параметрам обработчика On DragOver. Наконец, при завершении перетаскивания (вне зависимости от того, приняты данные или нет) возникает еще одно событие: TEndDragEvent = procedure(Sender, X, Y: I n t e g e r ) of object;
Target:
TObject;
Значение этого свойства для перетаскивания равно dkDrag, а для причалива ния — dkDock (и у причаливаемого, и у принимающего компонентов). Количество установленных на форме причаливаемых компонентов (то есть ком понентов, для которых выполняются условия DragKind = dkDock и D o c k S i t e = F a l s e ) определяется свойством D o c k C l i e n t C o u n t , а их список хранится в ин дексированном свойстве D o c k C l i e n t s . Для реализации причаливания в класс T C o n t r o l введены некоторые свойства и события, которые перечислены далее. Property AutoSize: Boolean;
2 4 4 Глава 11 • Общие свойства компонентов Это свойство разрешает ( T r u e ) или запрещает ( F a l s e ) оконному компоненту менять свои размеры автоматически в зависимости от количества и размеров содержащихся в нем дочерних компонентов. property F l o a t i n g D o c k S i t e C l a s s : TWinControlClass; Это свойство определяет класс окна, в которое будет погружен дочерний эле мент вне границ оконного элемента. Неоконные компоненты могут не только причаливаться к оконным компонентам, но и покидать их. После «отчалива ния» от компонента неоконные элементы автоматически оказываются в окне, тип которого содержит свойство F l o a t i n g D o c k S i t e C l a s s . По умолчанию это окно имеет уменьшенный по высоте заголовок и системную кнопку закры тия. property D o c k O r i e n t a t i o n : TDockOrientation; type TDockOrientation = (doNoOrient, d o H o r i z o n t a l , d o V e r t i c a l ) ; В этом свойстве можно установить (или получить) ориентацию, которую будет иметь «причаливаемый» компонент в окне родителя: о d o N o O r i e n t — сохраняется исходная ориентация перемещаемого компо нента; о d o H o r i z o n t a l — компонент будет иметь горизонтальную ориентацию; о
d o V e r t i c a l — компонент будет иметь вертикальную ориентацию.
property LRDockWidth: Integer; С помощью этого свойства можно получить ширину последнего перемещенно го компонента, расположившегося горизонтально. property TBDockHeight: Integer; С помощью этого свойства можно получить высоту последнего вертикально расположенного компонента. property UndockHeight: Integer; property UndockWidth: Integer; Эти свойства определяют, соответственно, высоту и ширину последнего «отчалив шего» компонента. property C o n s t r a i n t s : T S i z e C o n s t r a i n t s ; Это свойство с помощью объекта класса T S i z e C o n s t r a i n t s накладывает ог раничения на возможные размеры причаливаемого компонента (задает мак симальные и минимальные значения высоты и ширины). Следующие события аналогичны событиям OnDragDrop и OnDragOver (см. ра нее): property OnDockDrop: TDockDropEvent; property OnDockOver: TDockOverEvent; type TDockOverEvent = procedure(Sender: TObject; Source: TDragDockObject; X, Y: I n t e g e r ; S t a t e : TDragState; var Accept: Boolean) of object;
Поддержка справочной службы
245
Следующее событие возникает перед событием OnDragDrop: property O n G e t S i t e l n f o : TGetSitelnfoEvent; Обработчик этого события должен сообщить объекту TDragDockObject, кото рый автоматически связывается с причаливаемым объектом, некоторую допол нительную информацию (размеры, которые будет иметь этот объект, окажется ли он в «плавающем» окне и т. п.). Осталось еще одно событие: property OnUnDock: TUnDockEvent; type TUnDockEvent = procedure(Sender: TObject; C l i e n t : TControl; var Allow: Boolean) of object; Это событие возникает при «отчаливании» неоконного компонента. Обработчик этого события должен поместить в свойство Allow значение True, если компо нент C l i e n t может покинуть границы своего владельца S e n d e r . Все указанные события обрабатываются автоматически, если оба компонента (клиент и сервер) содержат значение dmAutomatic в своем свойстве DragMode. Чтобы познакомиться с технологией причаливания, поместите на пустую форму панель T P a n e l и кнопку T B u t t o n , установите для панели значение T r u e в свой ство D o c k S i t e , установите для обоих компонентов значение dkDock в свойстве DragKind и dmAutomatic в свойстве DragMode. После запуска программы пе ретащите кнопку на панель, а затем стащите ее обратно.
Поддержка справочной службы Все видимые элементы имеют следующие свойства: property Hint: String; property ShowHint: Boolean; Эти свойства регулируют появление и содержание оперативной, или всплываю щей, подсказки — небольшого справочного окна возле элемента, на котором оста навливается указатель мыши. Оперативные подсказки существенно облегчают начинающему пользователю знакомство с программным продуктом. Введение механизма оперативных подсказок во все видимые компоненты легко решает проблему создания дружественного программного интерфейса в современном стиле. Чтобы компонент смог показать оперативную подсказку, нужно поместить тек стовую строку в его свойство H i n t и присвоить свойству ShowHint значение True. Обычно оперативная подсказка содержит максимально лаконичный текст, чтобы не занимать слишком большую площадь экрана. Вы можете дополнить этот текст развернутым сообщением в любом компоненте, который способен ото бражать текст. Как правило, это строка состояния, занимающая нижнюю часть формы. Чтобы отобразить «длинное» сообщение, его нужно, прежде всего, поме стить в свойство H i n t сразу после «короткого» и разделить обе части символом вертикальной черты ( | ) , например: MyControl.Hint := 'Подсказка|Развернутое сообщение';
2 4 6 Глава 11 • Общие свойства компонентов
Далее необходимо в самом начале программы (обычно в обработчике события OnCreate главной формы) указать программе метод, который будет обрабаты вать событие OnHint. Это событие возникает в момент появления оперативной подсказки и предназначено для отображения длинного сообщения, которое методобработчик может получить в свойстве Hint глобального объекта Application, например: type TForml = class(TForm) pnStatus: TPanel; // Панель для отображения длинной // части свойства Hint public // Объявляем метод-обработчик события OnHint: procedure ShowLongHint(Sender: TObject); end; procedure TForml.ShowLongHint(Sender: TObject); // Этот обработчик помещает на панель pnStatus // длинную часть свойства Hint begin pnStatus.Caption := Application.Hint; end; procedure TForml.FormCreate(Sender: TObject); // Метод определяет свойство OnHint объекта-программы begin Application.OnHint := ShowLongHint; end;
Обратите внимание: обработчик события OnHint должен быть методом класса, а не обычной процедурой. Вот почему в предыдущем примере в секцию public класса TForml вставлено описание метода ShowLongHint. И еще одно замеча ние. Обычно свойство Hint компонента устанавливается на этапе конструиро вания формы. В этом случае вы можете создать только однострочную оператив ную подсказку. Если же установить свойство Hint на этапе прогона, вы сможете вставить в символьную строку разделители EOLN и таким способом создать мно гострочную оперативную подсказку: bbClose.Hint := 'Эта кнопка'#13'завершает работу'#13'программы 1 ;
Свойства глобального объекта-программы Application, перечисленные в табл. 11.5, регулируют цвет и время появления оперативной подсказки. Все видимые компоненты содержат следующее свойство, с помощью которого компонент привязывается к контекстной справочной службе: property HelpContext: Integer;
Поддержка справочной службы
247
Таблица 11.5. Свойства объекта Application, регулирующие цвет и время появления оперативной подсказки Свойство
Описание
property HintColor : TColor; property HintHidePause: Integer;
Определяет цвет фона окна оперативной подсказки Определяет длительность показа оперативной подсказки в миллисекундах. По умолчанию равно 2500 (2,5 с). Чтобы оперативная подсказка вообще не исчезала, установите значение -1 Определяет паузу в миллисекундах после остановки указателя мыши и перед появлением оперативной подсказки. По умолчанию равно 500 Определяет паузу перед появлением оперативной подсказки при переходе с одного компонента с заданным свойством H i n t на другой такой же. По умолчанию равно 50
property H i n t P a u s e : I n t e g e r ; property HintShortPause : Integer;
В это свойство следует поместить идентификатор раздела файла помощи, кото рый будет автоматически появляться в окне справки после нажатия клавиши F1. У многих видимых компонентов имеется свойство PopupMenu: property PopupMenu: TPopupMenu;
Это свойство определяет контекстное меню, которое вызывается щелчком на ком поненте правой кнопки мыши.
TMainMenu — главное меню формы
249
Компоненты категории Standard Рис. 12.1. Окно конструктора меню Чтобы связать с выбором пункта меню нужное действие, следует определить об работчик его события OnClick.
Описываемые в этой главе компоненты имеют самое общее назначение. Они широко используются при создании любых окон. Многие их них представляют собой компонентную оболочку для популярных объектов интерфейса Windows.
TMainMenu — главное меню формы Компонент класса TMainMenu определяет главное меню формы (программы). На форму можно поместить сколько угодно объектов этого класса, но отобра жаться в строке меню в верхней части формы будет только тот из них, который указан в свойстве Menu формы (это обстоятельство можно использовать для про граммного изменения меню формы). После установки компонента на форму необходимо создать пункты меню. Для этого следует либо дважды щелкнуть на компоненте левой кнопкой мыши, либо щелкнуть на нем правой кнопкой и выбрать в контекстном меню команду Menu Designer, либо, наконец, щелкнуть в правой половине строки свойства I t e m s в ок не инспектора объектов. На рис. 12.1 показано окно конструктора меню, которое появится на экране после выполнения любого из перечисленных действий. Создание пунктов меню не вызывает проблем. Перейдите в окно инспектора объек тов и введите название пункта меню в строке свойства C a p t i o n , после чего на жмите клавишу Enter — пункт меню готов, и можно переходить к следующему. Каж дый пункт главного меню может раскрываться в подменю или являться конечной командой. Для создания пунктов подменю щелкните мышью ниже пункта меню и введите имя первого пункта подменю. Продолжайте ввод, пока не будут созда ны все пункты подменю, после чего щелкните на пустом прямоугольнике справа от первого пункта и введите имя следующего. Процесс гораздо сложнее описать, чем выполнить.
В названиях пунктов можно указать символ амперсанта (&) перед тем символом, который определит клавишу быстрого вызова (в терминологии Windows) этого пункта. Например, на рис. 12.1 показано, что меню Файл можно раскрыть нажа тием сочетания клавиш Alt+Ф. При создании меню этот пункт в строке свойства C a p t i o n инспектора объектов содержал текст &Файл. Если вы захотите вставить разделительную черту, разделяющую пункты в под меню, введите в качестве имени очередного пункта меню дефис (-). Так, напри мер, создана черта, разделяющая команды Сохранить как и Выход в меню Файл (см. рис. 12.1): свойство C a p t i o n пятого по счету пункта меню Файл содержит единственный символ дефиса (-). Для создания многоуровневых меню, то есть таких, у которых пункты подменю раскрывают новые подменю, щелкните на пункте подменю и нажмите клавиши Ctrl+->. Такого же эффекта можно добиться щелчком правой кнопки мыши на пунк те подменю и выбором в контекстном меню команды Create Submenu. На рис. 12.2 показан пример многоуровневого меню.
Рис. 12.2. Многоуровневое меню
2 5 0 Глава 12 • Компоненты категории Standard
В Delphi имеется возможность связывать с пунктами меню небольшие изобра жения. Эти изображения можно задать либо свойством BitMap, либо свойством Image I n d e x . Изображение (если оно есть) появляется слева от пункта меню. Например, на рис. 12.3 показан фрагмент главного меню Delphi с раскрытым меню File. Как видно, команды Open, Open Project, Save и т. д. снабжены небольшими значками. Введение значков в меню, с одной стороны, повышает его наглядность, а с другой — способствует унификации значков в рамках механизма действий (см. раздел «Механизм действий» в главе 11). Если команда меню связана с какимто действием своим свойством A c t i o n , а компонент T A c t i o n L i s t , в котором это действие описано, в свою очередь, связан с хранилищем значков T I m a g e L i s t , индекс нужного значка можно задать в свойстве Image I n d e x . В этом случае зна чок, указанный в свойстве BitMap (если он указан), игнорируется.
TEdit— однострочное редактируемое текстовое поле 2 5 1
понентом может быть связан оконный элемент управления, который выбирается при нажатии клавиш Alt+Буква, где Буква — выделенная подчеркиванием буква в тексте метки. Такие клавиши в терминологии Windows называются клавишами быстрого вызова. С помощью логического свойства A u t o S i z e можно разрешить компоненту ав томатически изменять свои размеры так, чтобы полностью отобразить текст C a p t i o n с учетом установленного шрифта. Если свойство имеет значение F a l s e , высота и ширина компонента определяются текущими значениями свойств H e i g h t и Width. Если при этом ширина компонента больше минимально необ ходимой для отображения текста, свойством A l i g n m e n t можно указать гори зонтальное выравнивание текста: ta L e f t J u s t i f y — прижать текст влево; t a C e n t e r — выровнять по центру; t a R i g h t J u s t i f y — прижать текст вправо. Свойство L a y o u t управляет положением текста по вертикали: t l T o p — при жать вверх; t l C e n t e r — выровнять по центру; t l B o t t o m — прижать вниз. Другие свойства надписи перечислены в табл. 12.1. Таблица 1 2 . 1 . Свойства класса TLabel Свойство property FocusControl: TWinControl; p r o p e r t y ShowAccelChar : Boolean; propertyTransparent: Boolean;
Рис. 12.3. Подменю File среды Delphi
TPopupMenu — контекстное меню
p r o p e r t y Wordwrap: B o o l e a n ;
Компоненты класса TPopupMenu используются для создания контекстных меню, появляющихся при щелчке на компоненте правой кнопкой мыши. В отличие от главного меню, контекстное меню может быть создано для любого оконного ком понента. Чтобы связать щелчок правой кнопкой мыши на компоненте с раскры тием контекстного меню, в свойство PopupMenu компонента необходимо помес тить имя компонента-меню.
ПРИМЕЧАНИЕ
Контекстное меню создается с помощью конструктора меню, процесс создания контекстного меню ничем не отличаются от создания меню класса TMainMenu.
TLabel — надпись Компоненты класса TLabei предназначены для размещения на форме различного рода текстовых надписей. Для этого служит центральное свойство компонента — C a p t i o n . С помощью свойства F o n t можно разнообразить вид надписи. С ком-
Описание Содержит имя оконного компонента, который связан с надписью клавишами быстрого вызова Если содержит значение T r u e , символ & в тексте надписи предшествует символу клавиши быстрого вызова Определяет прозрачность надписи: если содержит значение F a l s e , пространство надписи закрашивается собственным цветом C o l o r , в противном случае этого не происходит, и сквозь надпись будут видны расположенные за ней компоненты (так, например, можно наложить текст на графику) Разрешает/запрещает разрыв строки на границе слова. Для вывода многострочных надписей задайте условия A u r o S i z e = F a l s e , W o r d w r a p = T r u e и установите подходящие размеры компонента
Надпись L a b e l может отображать длинную текстовую строку, указанную в свойстве C a p t i o n , в виде нескольких строк. Для этого установите в свойство A u t o S i z e значение F a l s e , задайте достаточно большие размеры компонента и поместите в свойство Wordwrap значение T r u e . Для отображения действительно многострочного текста используйте компоненты ТМето и T R i c h E d i t .
TEdit — однострочное редактируемое текстовое поле Компонент класса T E d i t представляет собой однострочное редактируемое тек стовое поле. С его помощью можно вводить и/или отображать довольно длин ные текстовые строки.
252
Глава 12 • Компоненты категории Standard
TEdit— однострочное редактируемое текстовое поле
Центральным свойством компонента является свойство Text, которое содержит отображаемую компонентом строку. С помощью обработчика события OnChange программа может контролировать вводимый пользователем текст и при необходи мости фильтровать его, игнорируя недопустимые символы. В следующем приме ре компонент фильтрует все символы, которые не соответствуют правильному представлению вещественного числа: var OldText: String;
Свойство
Описание
TEditCharCase= (ecNormal, ecUpperCase,ecLowerCase); property CharCase : TEditCharCase;
Определяет способ автоматического преобразования регистра букв: e c N o r m a l — нет преобразования; e c U p p e r C a s e — все буквы прописные; ecLowerCase — все буквы строчные. Правильно работает с кириллицей Если содержит значение F a l s e , выделение текста сохраняется при потере компонентом фокуса ввода Определяет максимальную длину текстовой строки. Если имеет значение 0, длина строки не ограничена Содержит значение T r u e , если текст был изменен Определяет обработчик события OnChange, которое возникает после любого изменения текста Содержит значение T r u e , если необходимо перекодировать текст из кодировки MS-DOS в кодировку Windows и обратно
property H i d e S e l e c t i o n : Boolean; p r o p e r t y MaxLength : I n t e g e r -
procedure TForml.EditlChange(Sender: TObject);
property Modified: Boolean;
begin
propertyOnChange: TNotifyEvent
if Editl.Texto' ' then
p r o p e r t y OEMConvert: B o o l e a n ;
try StrToFloat(Editl.Text) ; OldText := Editl.Text
property PasswordChar: Char;
except Ошибка
//
преобразования:
восстанавливаем
прежний
текст
Editl.Text := OldText; //
и
позиционируем
текстовый
курсор
в
конец
текста:
property R e a d o n l y : B o o l e a n ; property S e l L e n g t h : I n t e g e r -
Editl.SelStart := Length(Editl.Text); property S e l S t a r t : I n t e g e r ;
Editl.SelText := ''
end end;
property S e l T e x t : S t r i n g ; property T e x t : S t r i n g ;
При воспроизведении примера подготовьте глобальную переменную OldText типа String, в которой запоминается последний правильно введенный текст. Лучше всего поместить ее в секцию p r i v a t e класса формы — тогда ее можно не обнулять в момент начала работы. СОВЕТ ; Позиционировать текстовый курсор на любой символ строки можно с помощью свойств S e l S t a r t и S e l T e x t : в первое нужно поместить порядковый номер символа от начала текста, после ко торого должен стоять курсор ввода, во второе — пустую строку. ^
Свойства компонента TEdit представлены в табл. 12.2. Таблица 1 2 . 2 . Свойства класса TEdit Свойство
Описание
property A u t o S e l e c t : Boolean;
Определяет, будет ли выделяться весь текст в момент получения компонентом фокуса ввода Если содержит значение T r u e и B o r d e r S t y l e = b s S i n g l e , высота компонента автоматически меняется при изменении свойства F o n t . S i z e Содержит значение T r u e , если сделанные пользователем изменения в тексте T e x t можно убрать методом Undo
property AutoSize: Boolean; p r o p e r t y CanUndo: B o o l e a n ;
253
Если символ Char определен, он заменяет собой любой символ текста при отображении в окне. Используется для ввода паролей Если содержит значение T r u e , текст не может изменяться Содержит длину в символах выделенной части текста Содержит номер первого символа выделенной части текста Содержит выделенную часть текста Содержит весь текст
Замечу, что компонент TEdit, как и практически все остальные компоненты вкладки Standard, является «надстройкой» над стандартным объектом Windows. Поэтому, хотя его свойство Text объявлено как длинная строка String, макси мальная емкость этого свойства лимитируется Windows 98 и составляет 32 766 байт (в Windows NT/2000/XP такого ограничения нет). Методы компонента TEdit перечислены в табл. 12.3. Таблица 1 2 . 3 . Методы класса TEdit Метод
Описание
procedure C l e a r ;
Удаляет весь текст Удаляет выделенный текст Очищает буфер метода Undo Копирует выделенный текст в буфер обмена Копирует выделенный текст в буфер обмена, после чего удаляет выделенный текст из компонента Копирует не более B u f S i z e символов выделенного текста в буфер B u f f e r
procedure C l e a r S e l e c t i o n ; procedure C l e a r U n d o ; procedure C o p y T o C l i p b o a r d ; procedure C u t T o C l i p b o a r d ;
function GetSelTextBuf (Buffer: p Char; BufSize: I n t e g e r ) : Integer;
продолжение •&
2 5 4 Глава 12 • Компоненты категории Standard
Т М е т о — многострочное редактируемое текстовое поле 2 5 5
Таблица 1 2 . 3 (продолжение) Метод
Описание
procedure P a s t e F r o m C l i p b o a r d ;
Заменяет выделенный текст содержимым буфера обмена, а если нет выделенного текста, копирует содержимое буфера обмена в позицию текстового курсора
procedure S e i e c t A l l ; procedure SetSelTextBuf (Buffer: PChar);
Выделяет весь текст
p r o c e d u r e Undo ;
Заменяет выделенный текст содержимым буфера B u f f e r , а если нет выделенного текста, копирует содержимое буфера B u f f e r в позицию текстового курсора Восстанавливает текст в том виде, в котором он был перед последним получением компонентом фокуса ввода
ТМемо — многострочное редактируемое текстовое поле Компоненты класса ТМемо предназначены для ввода, редактирования и/или отображения достаточно длинного многострочного текста. Текст хранится в свой стве L i n e s класса T S t r i n g s и, таким образом, представляет собой пронумеро ванный набор строк (нумерация начинается с нуля). С помощью свойств и мето дов этого класса (Count, Add, D e l e t e , C l e a r и т. д. — см. раздел «Классы TStrings и TStringList — наборы строк и объектов» в главе 10) можно динамически фор мировать содержимое компонента. Свойства B o r d e r S t y l e , CanUndo, H i d e S e l e c t i o n , MaxLentgh, M o d i f i e d , OEMConvert, OnChange, Readonly, S e l L e n g t h , S e l S t a r t и S e l T e x t анало гичны соответствующим свойствам класса T E d i t . Свойство Wordwrap анало гично свойству TLabel .Wordwrap. Другие специфические свойства представ лены в табл. 12.4. Т а б л и ц а 1 2 . 4 . Свойства класса Т М е т о СВОЙСТВО
Описание
property C a r e t P o s : TPoint;
Содержит координаты мигающего текстового курсора относительно границ клиентской области компонента
property Lines : T S t r i n g s ;
Содержит строки текста
TScrollStyle= (ssNone, ssHorizontal,ssVertical, ssBoth);property ScrollBars: TScrollStyle;
Определяет наличие в многострочном поле полос прокрутки: ssNone — нет полос; s s H o r i z o n t a l — есть горизонтальная полоса; s s V e r t i c a l — есть вертикальная полоса; s s B o t h — есть обе полосы
property Text: String;
Отображает содержимое свойства L i n e s в виде одной длинной строки, в которой границы отдельных строк определяются символами EOL (CR+LF)
Свойство o c e r t y WantReturns : Boolean; r p P
o r o p e r t y WantTabs : B o o l e a n ; v "
Описание Если содержит значение T r u e , нажатие клавиши Enter вызывает переход на новую строку, в противном случае — обрабатывается системой. Для перехода на новую строку в этом случае следует нажать клавиши Ctrl+Enter Если содержит значение T r u e , нажатие клавиши Tab вызывает ввод в текст символа табуляции, в противном случае — обрабатывается системой. Для ввода символа табуляции в этом случае следует нажать клавиши Ctrl+Tab
Замечу, что, если свойство S c r o l l B a r s содержит значение s s H o r i z o n t a l или ssBoth, свойство Wordwrap игнорируется и длинные строки будут отсекаться границами компонента без переноса текста на следующую строку. Специфические методы класса аналогичны методам класса T E d i t . Поскольку компонент является потомком класса T C o n t r o l , он имеет также свой ство Text, которое содержит отображаемый компонентом текст в виде одной длинной строки. В этой цепочке символов границы строк многострочного текста выделяются символами с кодом #13#10 (признак EOLN — конец строки). В от личие от этого свойство L i n e s содержит пронумерованный список строк: пер вая строка в этом списке имеет индекс 0, вторая— 1, а общее количество строк можно узнать с помощью метода L i n e s .Count. Свойство T e x t удобно исполь зовать для поиска в тексте нужного фрагмента. Чтобы, например, найти и выде лить в тексте фрагмент, содержащийся в компоненте e d S e a r c h типа T E d i t , мож но использовать такой обработчик события O n C l i c k кнопки b t S e a r c h : procedure T F o r m l . b t S e a r c h C l i c k ( S e n d e r : T O b j e c t ) ; var k: I n t e g e r ; begin w i t h Memol do begin k := p o s ( e d S e a r c h . T e x t , T e x t ) ; if k > 0 t h e n begin S e l S t a r t := k - 1; SelLength := Length(edSearch.Text) end end; end;
Если вы хотите, чтобы найденный в тексте фрагмент после установки свойств S e l S t a r t и S e l L e n g t h сразу оказался выделенным, установите значение F a l s e в свойство H i d e S e l e c t i o n . Как и в компоненте T E d i t , максимальная длина свойства T e x t ограничивается Windows и составляет 40 976 байт1. Речь идет о Windows 98. Для Windows 2000/XP такого ограничения нет.
2 5 6 Глава 12 • Компоненты категории Standard TCheckBox — флажок
Для загрузки в компонент текста из файла и для сохранения текста в файле удоб но использовать методы L o a d F r o m F i l e и S a v e T o F i l e класса T S t r i n g s . На пример, следующий обработчик события OnCreat формы Forml загружает в ком понент Memol текст проектного файла программы: procedure TForml.FormCreate(Sender: TObject); begin Memol.Lines.LoadFromFile( C h a n g e F i l e E x t ( A p p l i c a t i o n . E x e N a m e ,' d p r ' ) ) end;
257
• m r R e t r y — был щелчок на кнопке Retry; • mrYes — был щелчок на кнопке Yes. В отличие от большинства других видимых компонентов, кнопка T B u t t o n яв ляется элементом операционной системы Windows и поэтому не может изменять свой цвет произвольным образом — кнопка меняет его вместе с изменением па литры Windows. Кнопка всегда имеет системный цвет c l B t n F a c e и не имеет свойства C o l o r . Шрифт надписи на кнопке может менять свои стиль и размер, но компонент игнорирует изменение его цвета.
TButton — кнопка
TCheckBox — флажок
Компоненты TButton широко используются для управления программами. Свя занный с кнопкой алгоритм управления реализуется в обработчике события OnClick.
Компонент TCheckBox используется для того, чтобы пользователь мог указать свое решение типа да/нет или да/нет/не уверен (в последнем случае флажок устанавливается, но выглядит блеклым). Это решение отражается в свойстве S t a t e компонента, доступном как для чтения, так и для записи. В составе диа логового окна может быть несколько компонентов TCheckBox, причем состоя ние любого из них никак не зависит от состояния остальных. Типичное использование компонента: if CheckBoxl.Checked then
Свойства компонента представлены в табл. 12.5. Таблица 1 2 . 5 . Свойства класса TButton Свойство
Описание
property Cancel: Boolean;
Если имеет значение True, событие OnClick
property D e f a u l t : Boolean;
Если имеет значение True, событие OnClick
кнопки возникает при нажатии клавиши Esc
else
кнопки возникает при нажатии клавиши Enter
type TModalResult = Low ( I n t e g e r ) . . High ( I n t e g e r ) ; property ModalResult: TModalResult;
Определяет результат, с которым было закрыто модальное окно (см. далее пояснение) _____
.
В терминологии Windows модальными окнами называются такие специаль ные окна, которые, появившись на экране, блокируют работу пользователя с другими окнами вплоть до своего закрытия. Обычно с их помощью реали зуется диалог, требующий от пользователя принятия какого-то решения. Для этого в состав модального окна включается несколько кнопок. Если у кнопки определено свойство M o d a l R e s u l t , щелчок на ней приводит к закрытию мо дального окна и возвращению в программу значения M o d a l R e s u l t как ре зультата диалога с пользователем. В Delphi определены следующие стандарт ные значения M o d a l R e s u l t : • mrAbort — был щелчок на кнопке Abort;
Или: case CheckBoxl.State of cbChecked cbUnchecked: cbGrayed end; Свойства класса TCheckBox представлены в табл. 12.6. Таблица 1 2 . 6 . Свойства класса TCheckBox ^!_*51_____ 6 T L e f t R i
t^R 9 - (taLeft J u s t i f y , dKigntJustify) ; property Alignment: TLef tRight;
• mrAll — был щелчок на кнопке АН;
operty AllowGrayed: Boolean;
• m r C a n c e l — был щелчок на кнопке Cancel; • m r l g n o r e — был щелчок на кнопке Ignore; •
h t
Per у C a p t i o n : S t r i n g ; Per у Checked: B o o l e a n ;
mrNo — был щелчок на кнопке No;
• mrNone — модальное окно не закрывается; • mrOk — был щелчок на кнопке ОК;
Описание
Определяет положение текста: t a L e f t J u s t i f y - е л е в о й стороны компонента; t a R i g h t J u s t i f y c правой стороны Разрешает/запрещает использование состояния c b G r a y e d (не уверен) Содержит связанный с компонентом текст Содержит значение, соответствующее выбору пользователя. Состоянию c b C h e c k e d соответствует значение T r u e . Состояния c b U n c h e c k e d и c b G r a y e d отражаются как F a l s e продолжение &
9
Зак. 126
258
Глава 12 • Компоненты категории Standard
Таблица 1 2 . 6 (продолжение) Свойство
Описание
type TCheckBoxState = (cbUnchecked, cbChecked, cbGrayed) ; property S t a t e : TCheckBoxState;
Содержит состояние компонента: cbUnchecked—не установлен; cbChecked — установлен; cbGrayed—не уверен
Свойство C o l o r компонента в Windows 98 игнорируется. Свойства H e i g h t и Width определяют размеры прямоугольника, в котором выводится связанный с флажком текст, и никак не влияют на размеры самого флажка. Текст указыва ется в свойстве C a p t i o n .
TRadioButton — переключатель В отличие от компонента TCheckBox, компоненты T R a d i o B u t t o n представля ют собой переключатели, предназначенные для выбора одного из нескольких взаимоисключающих решений. На форму (точнее, в компонент-контейнер) по мещается по меньшей мере два таких компонента. Они могут иметь только два состояния, определяемые свойством Checked. Если в одном компоненте это свой ство принимает значение True, во всех других компонентах, расположенных в том же контейнере, свойства Checked принимают значения F a l s e . Помимо свойства Checked компонент T R a d i o B u t t o n имеет еще одно специфич ное свойство— A l i g n m e n t , аналогичное такому же свойству компонента TCheckBox. Как и в случае с флажком, программист может изменять размеры и цвет текста, указанного в свойстве C a p t i o n , но не может изменять размеры и цвет самого переключателя (в Windows 98).
TListBox — список Компонент класса T L i s t B o x представляет собой стандартный для Windows спи сок, с помощью которого пользователь может выбрать один или несколько пунк тов. В компоненте предусмотрена возможность программной отрисовки пунктов, поэтому список может содержать не только текстовые строки, но и произволь ные изображения. Свойства класса T L i s t B o x перечислены в табл. 12.7. Т а б л и ц а 1 2 . 7 . Свойства класса TListBox Свойство
Описание
property AutoComplete : Boolean;
Если содержит значение True, выбранный элемент
type TborderStyle = bsNone . . bsSingle/property BorderStyle: TborderStyle;
Определяет стиль рамки: bsNone — нет рамки; b s S i n g l e — рамка толщиной 1 пиксел
в списке заменяется тем, начальные буквы которого вводит пользователь
TListBox — список
Свойство property Canvas: TCanvas; property Columns : Longlnt; property ExtendedSelect: Boolean;
259
Описание Канва для программной отрисовки пунктов списка Количество колонок в списке
Если ExtendedSelect = True и M u l t i S e l e c t = True, выбор пункта без
одновременного нажатия клавиши Crtl или Alt отменяет предыдущий выбор
property IntegralHeight: Boolean;
Если I n t e g r a l H e i g h t = True и S t y l e <> lbOwnerDrawVariabe, в списке отображается целое количество пунктов
property ItemHeight: Integer;
Определяет высоту элемента в пикселах для
property Itemlndex: Integer;
Содержит индекс элемента, имеющего фокус ввода. Если M u l t i S e l e c t = F a l s e , совпадает с индексом выделенного пункта
property Items : TStrings; property MultiSelect: Boolean;
Содержит набор строк, показываемых в компоненте Разрешает/отменяет возможность выбора нескольких пунктов
propertySelCount: Integerproperty Selected [X: Integer]: Boolean; property Sorted: Boolean;
Содержит количество выбранных пунктов
S t y l e = lbOwnerDrawFixed
Содержит признак выбора для элемента с индексом X (первый элемент имеет индекс 0) Разрешает/отменяет сортировку строк в алфавитном порядке
type TListBoxStyle = (lbStandard, lbOwnerDrawFixed, lbOwnerDrawVariable); property Style: TListBoxStyle;
Определяет способ отрисовки элементов: l b S t a n d a r d — элементы рисует Windows; l b O w n e r D r a w F i x e d — элементы рисует программа, и они имеют одинаковую высоту, определяемую свойством I t e m H e i g h t ; l b O w n e r D r a w V a r i a b l e — элементы рисует программа, и они имеют разную высоту
property TabWidth: Integer; property Toplndex: Integer;
Задает ширину табуляционного пробела Индекс первого видимого в окне пункта списка
Создание элементов (пунктов) списка компонента реализуется с помощью мето дов его свойства I t e m s — Add, Append, I n s e r t или L o a d F r o m F i l e . Следует учесть, что свойство I t e m l n d e x в компонентах-списках T L i s t B o x и TComboBox (см. далее) содержит индекс элемента списка, имеющего фокус ввода, и не меняется при изменении списка методами Add, Append, I n s e r t . Сле дующий цикл, в котором каждому элементу списка I t e m s ставится в соответ ствие свой список T S t r i n g L i s t ( i t e m s является специализированным потом ком класса T S t r i n g s и, следовательно, может содержать не только строки, но и объекты), некорректен: while ( . . . ) do begin Items. Add ( ' . . . ' ) ; Objects[Itemlndex] := TStringList.Create; end;
260
Глава 12 • Компоненты категории Standard
Так как при вставке очередной строки значение I t e m l n d e x не меняется, объект и связанный с ним список T S t r i n g L i s t будут созданы только для одной стро ки компонента. Исправленный вариант: к := 0; while (...) do begin Items.Add('...'); Objects[к] := TStringList.Create; inc(k); end; Учтите также, что значение свойства I t e m l n d e x не может быть больше значе ния I t e m s . C o u n t или равно ему, поэтому, если не будет найден искомый эле мент списка, такой цикл окажется бесконечным: while
(Itemlndex < Items.Count)
// Ошибка! Значение Itemlndex // не может быть равно Items.Count
and (Items[Itemlndex] <> ...) do Itemlndex := Itemlndex+l; Н у ж н о исключить переход в цикле за границы списка: while (Itemlndex < Items.Count-i) // Теперь все правильно and (Items[Itemlndex] <> ...) do Itemlndex := Itemlndex+l; if Items[Itemlndex] <> ... // Однако приходится дополнительно then Itemlndex := - 1 ; // проверять последнюю строку Для компонента определены два события, связанные с программной отрисовкой элементов списка: type TOwnerDrawState = s e t of (odSelected, odGrayed, odDisabled, odChecked, odFocused); TDrawItemEvent = procedure(Control: TWinControl; Index: Integer; Rect: TRect; S t a t e : TOwnerDrawState) of object; property OnDrawItem: TDrawItemEvent; type TMeasureltemEvent = procedure(Control: TWinControl; Integer; var Height: Integer) of object; property OnMeasureltem: TMeasureltemEvent; Первое событие возникает в момент, когда программа должна нарисовать оче редной элемент. Обработчик события получает ссылку на список C o n t r o l , ин декс изображаемого элемента I n d e x , границы элемента R e c t и его состояние S t a t e . Отрисовка ведется с помощью свойства Canvas.
TListBox — список
261
ПРИМЕЧАНИЕ —— Поскольку программная отрисовка обычно связана с выводом на экран рисунков, параметр C o n t r o l задан как абстрактный объект класса T W i n C o n t r o l , и необходимое преобразова ние типов должно выполняться в обработчике. В следующем примере в каждом элементе рисуются растровое изображение и текст: procedure TForml.ListBoxlDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; S t a t e : 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), clRed); // Вычисляем смещение текста (4 пиксела от растра): Offset := Bitmap.width + 6; end e l s e Offset := 2; // Если нет растра, 2 пиксела слева // Выводим текст: TextOut(Rect.Left + Offset, Rect.Top, Items[Index]); end; end; Обратите внимание: обработчик не учитывает состояния элемента. Окантовка сфокусированного элемента пунктиром, установка нужного цвета кисти и шрифта для выбранного элемента осуществляются автоматически. Если вас не устраива ют стандартные цвета, следует проверять параметр S t a t e . Например, в преды дущем примере обратите внимание на следующий оператор: F i l l R e c t ( R e c t ) ; // Очищаем прямоугольник Ьсли перед этим оператором вставить показанные далее строки, выбранные эле менты будут подсвечены желтым фоном, а текст в них будет выведен черным полужирным шрифтом: if odSelected in State then begin
2 6 2 Глава 12 • Компоненты категории Standard Brush.Color := clYellow; Font.Color := clBlack; Font.Style := [fsBold]; end; Событие OnMeasureltem возникает только при выполнении условия S t y l e = lbOwnerDrawVariable. Оно предшествует событию OnDrawItem, и в ходе его обработки программа должна установить нужную высоту очередного элемента. Об работчику передаются индекс элемента I n d e x и переменная H e i g h t , в которой он должен вернуть высоту элемента, например: procedure TForml.ListBoxlMeasureltem(Control: TWinControl; Index: I n t e g e r ; var Height: I n t e g e r ) ; var Bitmap: TBitmap; begin Bitmap := TBitmap((Control as T L i s t B o x ) . I t e m s . O b j e c t s [ I n d e x ] ) ; if Bitmap <> n i l then Height := Bitmap.Height else Height := abs (ListBoxl.Font.Height) end;
TComboBox— комбинированный список Комбинированный, или раскрывающийся, список TComboBox представляет собой комбинацию списка T L i s t B o x и текстового поля T E d i t , поэтому большая часть его свойств и методов заимствована у этих компонентов. Существует пять модифи каций компонента, определяемых его свойством S t y l e : c s S i m p l e , csDropDown, csDropDownList, csOwnerDrawFixed и csOwnerDrawVariable. В первом случае список всегда имеет тот размер, который назначил ему программист в режиме кон струирования формы или программа свойством H e i g h t . Если этого размера недо статочно для отображения всех элементов списка, на компоненте ниже поля ввода появляются две небольшие стрелки, позволяющие прокручивать содержимое спис ка. Если пространства ниже поля нет вообще, для смены элемента списка, демон стрируемого в поле, следует активизировать компонент и использовать клавиши перемещения курсора I и Т. В модификации csDropDownList поле списка рабо тает в режиме отображения выбранного элемента и в это поле нельзя ввести но вый элемент (в других модификациях это возможно). Модификации csOwnerDrawFixed и csOwnerDrawVariable используются для программной отрисовки элементов списка. Требуемые для этого свойства и ме тоды полностью совпадают со свойствами и методами компонента T L i s t B o x ана логичного назначения. Фактически «своими» для компонента являются лишь свойства и события, свя занные с раскрытием списка:
TScrollBar— ползунок
263
property DropDownCount: I n t e g e r ; oroperty DroppedDown: Boolean; property OnDropDown: TNotifyEvent; Свойство DropDownCount определяет количество элементов списка, появление которых еще не приводит к необходимости прокрутки списка. По умолчанию это свойство имеет значение 8: если в списке указано 9 и более элементов (то есть больше, чем содержит свойство DropDownCount), при его раскрытии к ок ну будет добавлена полоса прокрутки. Свойство DroppedDown определяет, рас крыт ли в данный момент список. Это свойство доступно также для записи, что позволяет программно управлять состоянием списка. Событие OnDropDown про исходит при изменении состояния списка. Наполнение списка ведется методами Add, Append, I n s e r t и т. п. его свойства I t e m s класса T S t r i n g s (см. раздел «Классы TStrings и TStringList — наборы строк и объектов» в главе 10).
TScrollBar— ползунок С помощью компонента T S c r o l l B a r пользователи создают ползунки— стан дартные для Windows элементы управления, похожие на полосы прокрутки окна. Обычно такие элементы управления используются для плавного изменения чис лового значения (см. также раздел «TTrackBar — ползунок» в главе 14). Свойства класса T S c r o l l B a r представлены в табл. 12.8. Таблица 12.8. Свойства класса TScrollBar Свойство
Описание
TSrollBarKind= ( s b H o r i z o n t a l , sbVertical) ; property Kind: TScrollBarKind;
Определяет ориентацию компонента: s b H o r i z o n t a l — ползунок перемещается по горизонтали; s b V e r t i c a l — ползунок перемещается по вертикали
property LargeChange: TScrollBarlnc; property Max: I n t e g e r ;
«Большой» сдвиг ползунка (при щелчке мышью рядом с концевой кнопкой)
property Min: I n t e g e r ;
Минимальное значение диапазона изменения числового значения
property P o s i t i o n : I n t e g e r ; Property SmallChange: TScrollBarlnc;
Текущее значение числовой величины «Малый» сдвиг ползунка (при щелчке мышью на концевой кнопке)
Максимальное значение диапазона изменения числового значения
*- помощью следующего метода можно сразу установить свойства Position, Мах и Min: Procedure SetParams(APosition, AMax, AMin: Integer); ^ компонентом связаны два события: fc
ype TScrollEvent = procedure(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer) of object;
TActionList—список действий
2 6 4 Глава 12 • Компоненты категории Standard property OnScroll: TScrollEvent; property OnChange: TNotifyEvent; Первое возникает при любом изменении свойства P o s i t i o n , второе— при из менении параметров методом S e t P a r a m s . Подобно компоненту T B u t t o n , компонентом T S c r o l l B a r полностью управля ет Windows, поэтому у него нет свойства C o l o r .
TGroupBox— панель группирования
265
RadioGroupl.Itemlndex of case // Установлен 1-й переключатель 0: • Г. _ ; // Установлен 2-й переключатель else // Не установлен ни один из переключателей end;
TPanel — панель
Компонент TGroupBox служит контейнером для размещения дочерних компо нентов и представляет собой прямоугольную рамку и текст в разрыве рамки. Обычно с его помощью выделяется группа элементов управления, объединен ных по функциональному назначению. Свойства и методы этого класса целиком унаследованы им от своих предков T C u s t o m C o n t r o l и T W i n C o n t r o l и описа ны в главе 11.
Компонент T P a n e l представляет собой контейнер общего назначения. В отли чие от компонента TGroupBox, он не имеет заголовка и поэтому менее удобен для функционального группирования элементов. С другой стороны, его свойство C a p t i o n отображается в виде текстовой строки и может использоваться для вы вода сообщений. Компоненты этого класса часто помещаются на форму для того, чтобы располагать вставленные в них дочерние компоненты вдоль одной из сто рон окна независимо от изменения размеров этого окна.
TRadioGroup — группа переключателей
Компонент имеет развитые средства создания различных эффектов объемности за счет использующихся в нем двух кромок — внешней и внутренней. Свойства класса T P a n e l представлены в табл. 12.10.
Компонент класса T R a d i o G r o u p представляет собой специальный контейнер, предназначенный для размещения переключателей класса T R a d i o B u t t o n . Каж дый размещаемый в нем переключатель помещается в специальный список I t e m s и доступен по индексу, что упрощает обслуживание группы. Свойства класса TRadioGroup перечислены в табл. 12.9.
Таблица 12.10. Свойства класса TPanel Свойство
Описание
Свойство
Описание
TBorderStyle = bsNone. . b s S i n g l e ; property B o r d e r S t y l e : TBorderStyle; p r o p e r t y F u l l R e p a i n t : Boolean;
property Columns : I n t e g e r ; property Itemlndex: I n t e g e r ; property Items : T S t r i n g s ;
Определяет количество столбцов переключателей Содержит индекс установленного переключателя Содержит список строк с заголовками элементов. Добавление/удаление элементов достигается добавлением/удалением строк списка Items
Для компонента объявлено событие OnResize, в обработчике которого програм мист может предусмотреть необходимую реакцию на изменение размеров ком понента. \0
Таблица 12.9. Свойства класса TRadioGroup
После размещения компонента на форме он пуст. Чтобы создать в нем пере ключатель, следует раскрыть редактор списка I t e m s и ввести хотя бы одну строку: строки I t e m s используются как поясняющие надписи справа от пере ключателей, а их количество определяет количество переключателей в группе. Замечу также, что после создания компонента его свойство I t e m l n d e x по умол чанию имеет значение -1 — это означает, что ни один переключатель в группе не установлен. Чтобы в момент появления компонента на экране какой-то переключатель уже был установлен, необходимо на этапе конструирования с по мощью окна инспектора объектов или программно (например, в обработчике O n A c t i v a t e формы) установить в свойство I t e m l n d e x номер соответствую щего переключателя (нумерация начинается с 0). Это же свойство позволяет программе проанализировать выбор пользователя, например:
Определяет стиль рамки: bsNone — нет рамки; b s S i n g l e — компонент по периметру обводится линией толщиной 1 пиксел Разрешает/запрещает перерисовку панели и всех ее дочерних элементов при изменении ее размеров
TActionList— список действий Компонент T A c t i o n L i s t не имеет видимого изображения и служит для под держки механизма действий (см. раздел «Механизм действий» в главе 11). исновная схема его использования такова. Вначале с помощью его редактора со здается действие — объект класса TAction. Редактор (рис. 12.4) вызывается двой ным щелчком на компоненте или выбором команды Action List Editor в его контекст ном меню, которое раскрывается после щелчка на нем правой кнопкой мыши, •этот объект имеет ряд свойств и событий, с помощью которых уточняется ха рактер действия. Доступ к этим свойствам и событиям можно получить из окна
TActionList—список действий 2 6 7
2 6 6 Глава 12 • Компоненты категории Standard
инспектора объектов. С действием можно связать группу свойств ( C a p t i o n , Checked, E n a b l e d , S h o r t c u t и т. д.), которые будут помещаться в одноимен ные свойства компонентов, реализующих общее действие. Если с компонентом связан контейнер значков типа T I m a g e L i s t (свойство Images — это свойство не действия, а самого компонента T A c t i o n L i s t ) , при реализации действия мож но использовать один из хранящихся в нем значков ( I m a g e l n d e x ) . Чтобы дей ствие подкреплялось программным кодом, для него обязательно следует опреде лить обработчик события OnExecute.
Рис. 12.4. Редактор компонента TActionList
В свойства A c t i o n тех компонентов, активизация которых должна сопровож даться одним и тем же действием (например, в свойства A c t i o n команды меню и кнопки панели инструментов), помещается имя только что определенного дей ствия. В результате выбор команды в меню или щелчок на кнопке панели инст рументов вызовут один и тот же метод (OnExecute). Кроме того, возле коман ды меню и на кнопке панели инструментов будет присутствовать один и тот же значок ( I m a g e l n d e x ) , у них будут одинаковые названия ( C a p t i o n ) , оператив ные подсказки ( H i n t ) и т. д. Свойства класса T A c t i o n L i s t представлены в табл. 12.11. Таблица 12.11. Свойства класса TActionList Свойство
Описание
property ActionCount: I n t e g e r ;
Содержит количество определенных в компоненте действий (только для чтения) Позволяет программе обратиться к нужному действию (объекту класса T C o n t a i n e d A c t i o n ) по его индексу I n d e x Содержит имя компонента класса T i m a g e L i s t
property Actions [Index: I n t e g e r ] : TContainedAction; property Images: TCustomlmageList;
Редактор компонента создает объекты класса TAction. Свойства класса T A c t i o n в основном определяют те значения, которые будут иметь одноименные свой-
ства всех компонентов или команд меню, связанных общим действием (если, разумеется, компонент или команда меню имеет данное свойство). Свойства класса T A c t i o n перечислены в табл. 12.12. Т а б л и ц а 1 2 . 1 2 . Свойства класса TAction Свойство
Описание
property C a p t i o n : String;
Содержит строку, которая будет устанавливаться в свойствах C a p t i o n всех компонентов, связанных данным действием
property Checked: Boolean;
Содержит значение, которое будет устанавливаться в свойствах C h e c k e d
property DisablelfNoHandler: Boolean;
Указывает, будут ли недоступны связанные компоненты, если для действия не определен обработчик O n E x e c u t e
property Enabled: Boolean;
Содержит значение, которое будет устанавливаться в свойствах E n a b l e d
property HelpContext: THelpContext; property H i n t : String;
Содержит значение, которое будет устанавливаться в свойствах H e l p C o n t e x t
property Imagelndex: I n t e g e r ;
Содержит индекс изображения в хранилище, указанном в свойстве Images компонента. Это изображение будет связано с компонентами данного действия
property S h o r t c u t : TShortCut;
Содержит значение, которое будет устанавливаться в свойствах S h o r t c u t Содержит значение, которое будет устанавливаться в свойствах V i s i b l e
property V i s i b l e : Boolean;
Содержит строку, которая будет устанавливаться в свойствах H i n t
События класса TAction перечислены в табл. 12.13. Таблица 12.13. События класса TAction Событие
Описание
property OnExecute : TNotifyEvent;
Возникает при щелчке мышью на одном из компонентов, связанных общим действием. Обработчик этого события должен реализовать нужное действие Возникает при наведении указателя мыши на один из связанных общим действием компонентов. Его обработчик по умолчанию выводит оперативную подсказку со строкой H i n t S t r Возникает, когда очередь сообщений для приложения пуста или когда обновляется содержимое списка действий
THintEvent =procedure (varHintStr: String; var CanShow: Boolean)of object; property OnHint: THintEvent; property OnUpdate: TNotifyEvent;
TBitBtn — кнопка с изображением
Компоненты категории Additional
269
bkCustom. Это произойдет также в случае, когда кнопка указана задаваемой по умолчанию ( D e f a u l t = True), но при этом свойство Kind не содержит значе ние bkOk или bkYes, и, кроме того, когда ее свойство C a n c e l содержит значе ние True, а свойство Kind не содержит значения b k C a n c e l или bkNo.
Рис. 13.1. Разновидности кнопок TBitBtn
На странице палитры объектов, связанной с категорией Additional, расположены компоненты общего назначения. Некоторые из них в функциональном плане дуб лируют компоненты категории Standard (см. главу 12), но их большая часть име ют уникальную функциональность.
Свойства C a n c e l , D e f a u l t и M o d a l R e s u l t кнопка T B i t B t n унаследовала у своего родительского класса TButton, остальные специфические свойства пе речислены в табл. 13.1. Таблица 1 3 . 1 . Свойства класса TBitBtn
TBitBtn — кнопка с изображением Графическая кнопка T B i t B t n представляет собой популярную разновидность стандартной кнопки T B u t t o n . Ее отличительная особенность — свойство Glyph, с помощью которого определяется растровое изображение на поверхности кноп ки. В комплект Delphi входит множество рисунков, разработанных специально для размещения на этих кнопках. ПРИМЕЧАНИЕ По умолчанию для версий Delphi 2005 (8) рисунки для кнопок размещаются в папке Program Files\Common Files\Borland Shared\Images\GlyFX\ Small.
Свойство Kind определяет одну из 11 стандартных разновидностей кнопки, по казанных на рис. 13.1. Щелчок на любой из показанных на рисунке кнопок, кроме кнопок bkCustom и bkHelp, закрывает модальное окно и возвращает в программу результат вида mrXXX (bkOk — mrOk, b k C a n c e l — m r C a n c e l и т. д.). Кнопка b k C l o s e для мо дального окна возвращает значение m r C a n c e l , а для главного окна программы закрывает его и завершает работу программы. Кнопка b k H e l p автоматически вызывает раздел справочной службы, связанный со свойством H e l p C o n t e x t формы, на которую кнопка помещена. Если у кнопки был изменен рисунок (свой ство Glyph), Delphi автоматически присвоит свойству Kind кнопки значение
Свойство
Описание
property Glyph: TBitmap;
Определяет от 1 до 4 связанных с кнопкой растровых изображений (см. далее) Определяет разновидность кнопки
T B i t B t n K i n d = (bkCustom, bkOK, b k C a n c e l , b k H e l p , bkYes, bkNo, bkClose, bkAbort, bkRetry, b k l g n o r e , bkAll) ; property Kind: TBitBtnKind; TButtonLayout= (blGlyphLeft, blGlyphRight, blGlyphTop, blGlyphBottom);property Layout: TButtonLayout; property Margin: I n t e g e r ; TNumGlyphs: 1. . 4; p r o p e r t y NumGlyphs: TNumGlyphs; property Spacing: I n t e g e r ; TButtonStyle = (bsAutoDetect, bsWin31,bsNew) ; p r o p e r t y S t y l e : TButtonStyle;
Определяет край кнопки, к которому прижимается рисунок: b l G l y p h L e f t — клевому; b l G l y p h R i g h t — к правому; b l G l y p h T o p — к верхнему; b l G l y p h B o t t o m — к нижнему Определяет расстояние в пикселах от края кнопки до рисунка Определяет количество растровых изображений (см. далее) Определяет расстояние в пикселах от рисунка до надписи на кнопке Определяет стиль оформления кнопки, зависящий от операционной системы. Стиль bsNew соответствует 32-разрядным версиям Windows. Стиль b s A u t o D e t e c t изменяет оформление кнопки в зависимости от операционной системы, под управлением которой работает программа в данный момент
270
TMaskEdit— поле с маской ввода
Глава 13 • Компоненты категории Additional
При разработке собственных растровых изображений для кнопок следует учесть, что изображения должны меняться при изменении состояния кнопки. Таких состо яний может быть четыре: нормальное состояние, кнопка недоступна, кнопка нажа та и кнопка утоплена (последнее используется только в кнопках T S p e e d B u t t o n ) . В соответствии с этим разрабатывается до 4 изображений, расположенных по го ризонтали в виде одного длинного растра. Например, стандартный размер изобра жения для размещения на кнопке равен 16x16 пикселов. Если создаются 3 изоб ражения, размер растра должен составлять 16x48. Количество изображений в растре задается свойством NumGlyphs. Если каждое изображение квадратное и длина растра делится без остатка на его высоту, Delphi автоматически распознает количе ство изображений. Если задано только одно изображение, оно меняется автомати чески: в состоянии «кнопка нажата» изображение смещается на один пиксел впра во и вниз, а в состоянии «кнопка недоступна» все цвета, кроме черного, меняются на светло-серый, а черный — на белый, что обеспечивает эффект «вдавленности» изображения. Следует также учесть, что самый левый нижний пиксел растра оп ределяет цвет прозрачности: на кнопке этот цвет будет заменяться цветом поверх ности кнопки. Как и в кнопках T B u t t o n , программист не может управлять цветом поверхнос ти кнопки, но, в отличие от TButton, может менять цвет надписи на ней. С по мощью свойства D e f a u l t кнопку можно сделать задаваемой по умолчанию — в этом случае нажатие клавиши Enter автоматически вызывает обработчик ее со бытия OnClick. Однако кнопками по умолчанию можно сделать только кнопки bkYes и bkNo, остальные значения свойства Kind будут заменяться на bkCustom при вводе в свойство D e f a u l t значения True, и наоборот — в свойство D e f a u l t автоматически помещается значение F a l s e при установке в свойстве Kind лю бого значения, кроме bkYes, bkNo или bkCustom.
TSpeedButton — кнопка панели инструментов Кнопки T S p e e d B u t t o n отличаются от кнопок T B i t B t n тремя аспектами: вопервых, они могут фиксироваться в утопленном состоянии, во-вторых, они не могут закрыть модальное окно, в-третьих, они не могут задаваться по умолча нию. Во всем остальном они повторяют свойства и методы кнопок T B i t B t n . Для фиксации кнопка должна быть отнесена к какой-либо группе кнопок (эта группа может состоять из одной фиксируемой кнопки). Для этого используется свойство G r o u p l n d e x : I n t e g e r , которое не должно быть равно 0. Поведение кнопки определяется логическим свойством AllowAllUp: если это свойство имеет значение True, утопленная кнопка отпускается при фиксации любой другой кноп ки, входящей в ту же группу; если A l l o w A l l U p = F a l s e , кнопку можно освобо дить только повторным щелчком. Индикатором состояния кнопки служит логи ческое свойство Down, которое имеет значение T r u e , если кнопка утоплена. Свойство доступно для записи, что позволяет изменять состояние кнопки про граммно. Для кнопки TSpeedButton предусмотрено событие OnDblClick, которое возника ет при двойном щелчке на утопленной кнопке.
271
TMaskEdit — поле с маской ввода Поле TMaskEdit предназначено для ввода текста, соответствующего некоторо му шаблону, задаваемому свойством E d i t M a s k : S t r i n g . Если это свойство не задано, компонент TMaskEdit работает как обычное текстовое поле T E d i t . Свой ство I s M a s k e d : B o o l e a n доступно только для чтения и содержит значение True, если строка шаблона задана. Свойство E d i t T e x t : S t r i n g содержит текст до наложения на него маски ввода (то есть то, что ввел пользователь), а свойство T e x t : S t r i n g может (в зависимости от шаблона — см. далее) содержать либо исходный текст, либо результат наложения на него маски. Шаблон состоит из трех частей, отделенных друг от друга символами точки с за пятой (;). Первая часть задает маску ввода, вторая — это символ 0 или 1, опре деляющий, нужно ли записывать в свойство T e x t результат наложения маски (1) или требуется оставить исходный текст (0). В третьей части указывается сим вол, который в поле с маской ввода будет присутствовать в местах, предназна ченных для ввода символов (текст в поле может содержать символы маски). На пример, для ввода семизначного номера телефона текст перед началом ввода может выглядеть так: (095)ХХХ-ХХ-ХХ
Здесь доступные для ввода пользователя места обозначены символом X — послед ним символом в шаблоне. Маска состоит из описателей мест ввода (табл. 13.2), специальных символов (табл. 13.3) и литералов. Описатель указывает, какой именно символ может вве сти пользователь в данное место (описатель всегда указывает место для одного символа). Литерал вставляется в текст, показываемый в окне редактора, но при вводе курсор перескакивает через литерал и не дает пользователю возможнос ти изменить его. Литералами считаются любые символы, кроме описателей по лей и специальных символов, а также любые символы, которым предшествует символ \. Специальные символы формируют дополнительные указания редак тору. Примеры применения маски представлены в табл. 13.4. Таблица 1 3 . 2 . Описатели мест ввода маски Символ
Место ввода
L
Должно содержать букву
1
Может содержать букву
А
Должно содержать букву или цифру
а
Может содержать букву или цифру
С
Должно содержать любой символ
с
Может содержать любой символ
0
Должно содержать цифру
9
Может содержать цифру
#
Может содержать цифру, а также знаки + и -
272
Глава 13 • Компоненты категории Additional
TStringGrid — текстовая таблица
Таблица 1 3 . 3 . Специальные символы маски Символ
Описание
\
Следующий символ — литерал. Позволяет вставлять в маску символы, относящиеся к описателям мест ввода и специальным символам На это место вставляется символ-разделитель Windows для часов, минут, секунд На это место вставляется символ-разделитель Windows для полей даты Разделитель частей шаблона Подавляет все начальные пробелы Все следующие за этим символом места ввода преобразуют буквы в прописные Все следующие за этим символом места ввода преобразуют буквы в строчные Отменяет преобразование букв
: / ;
! > < о
Таблица 13.4. Примеры применения маски ввода Шаблон с маской
Вид в редакторе
(095) 0 0 0 - 0 0 0 0 ; 0;х (095) 0 0 0 - 0 0 0 0 ; 1;х (095)\0\00-0000;1;
(095) ххх-хххх (095) ххх-хххх (095)00.-
Свойство EditText 1234567 1234567 12345
Свойство Text 1234567 (095)123-4567 (095)001-2345
Если компонент лишается фокуса ввода, когда не все требуемые места ввода заполнены (или заполнены неправильно), возникает исключительная ситуация E D B E d i t E r r o r : на экран выводится окно с сообщением и после закрытия окна курсор устанавливается в то место поля, на котором закончился правильный ввод. Остальные свойства компонента TMaskEdit повторяют соответствующие свой ства компонента T E d i t . В частности, установка в свойство P a s s w o r d C h a r лю бого символа, кроме символа с кодом #0, обеспечивает секретность ввода: в этом случае все вводимые в компоненте символы заменяются символом, указанным в свойстве P a s s w o r d C h a r .
TStringGrid — текстовая таблица Компонент T S t r i n g G r i d предназначен для создания таблиц, в ячейках кото рых располагаются произвольные текстовые строки. Он является прямым по томком класса TDrawGrid (см. далее), от которого им унаследовано большин ство свойств и методов. Таблица делится на две части: фиксированную и рабочую. Фиксированная часть служит для показа заголовков колонок и рядов, а также для ручного управления их размерами. Обычно фиксированная часть занимает левую колонку и верхний ряд таблицы, однако с помощью свойств F i x e d C o l s и FixedRows можно за дать другое количество фиксированных колонок и рядов (если эти свойства име ют значение 0, таблица не содержит фиксированной зоны). Рабочая часть — эта остальная часть таблицы. Она может содержать произвольное количество коло нок и рядов, более того, эти величины могут изменяться программно. Рабочая часть может не умещаться целиком в пределах окна компонента, в этом случае
273
в него автоматически помещаются нужные полосы прокрутки. При прокрутке рабочей области фиксированная область не исчезает, но меняется ее содержи мое — заголовки колонок и рядов. Центральным свойством компонента является свойство C e l l s — двухмерный массив ячеек, каждая из которых может содержать произвольный текст. Конк ретная ячейка определяется парой чисел — номером колонки и номером ряда, на пересечении которых она находится (нумерация начинается с нуля). Свойство C e l l s имеет тип S t r i n g , поэтому программа может легко прочитать или запи сать содержимое нужной ячейки, например: Cells[1,1]
:= 'Левая верхняя ячейка рабочей зоны';
Количество ячеек по каждому измерению хранит пара свойств: C o l C o u n t (коли чество колонок) и RowCount (количество рядов). Значения этих свойств и, следовательно, размеры таблицы могут меняться как на этапе разработки про граммы, так и в ходе ее работы, однако их значения должны быть как минимум на единицу больше, соответственно, значений в свойствах F i x e d C o l s и F i x e d Rows, определяющих размеры фиксированной зоны. Содержимое ячеек можно редактировать. Для этого в таблице используется спе циализированный потомок поля с маской ввода TMaskEdit. Свойства класса T S t r i n g G r i d перечислены в табл. 13.5. Таблица 13.5. Свойства класса TStringGrid Свойство
Описание
property B o r d e r S t y l e : TBorderStyle; property C e l l s [ACol, ARow: I n t e g e r ] : String; property Col: Longlnt;
Определяет рамку компонента: bsNone — нет рамки; b s S i n g l e — рамка толщиной 1 пиксел Определяет содержимое ячейки с табличными координатами (ACol, ARow) Содержит номер колонки с ячейкой, имеющей фокус ввода Содержит количество колонок таблицы Содержит все строки колонки с индексом I n d e x
property ColCount: Longlnt; property C o l s [ I n d e x : I n t e g e r ] TStrings; property ColWidths [ Index : Longlnt]: Integer; property DefaultColWidth: Integer; property DefaultDrawing: Boolean; property Def aultRowHeight: Integer; propertyEditorMode: Boolean;
property FixedCoIor: TColor;
Содержит ширину колонки с индексом I n d e x Содержит значение ширины колонки, заданное по умолчанию Разрешает/запрещает автоматическую отрисовку служебных элементов таблицы — фиксированной зоны, фона и прямоугольника ячейки, имеющей фокус ввода, и т. п. Содержит значение высоты рядов, заданное по умолчанию Разрешает/запрещает редактирование ячеек. Игнорируется, если свойство O p t i o n s включает значение g o A l w a y s e S h o w E d i t o r или не включает значение g o E d i t i n g Определяет цвет фиксированной зоны продолжение &
274
Глава 13 • Компоненты категории Additional
Таблица 1 3 . 5 (продолжение) Свойство property F i x e d C o l s : I n t e g e r ;
Определяет количество колонок фиксированной зоны Определяет количество рядов фиксированной зоны Содержит значение высоты таблицы Определяет толщину линий, расчерчивающих таблицу Содержит значение ширины таблицы Содержит номер самого левого столбца, видимого в зоне прокрутки Обеспечивает доступ к объекту, связанному с ячейкой ( A C o l , ARow) Содержит параметры таблицы (см. далее) Содержит номер ряда ячейки, имеющей фокус ввода Содержит количество рядов таблицы Содержит значение высоты ряда с индексом I n d e x
property Objects [ACol, ARow: Integer]: TObject; property Options : TGridOptions; property Row: Longlnt; property RowCount: Longlnt; property RowHeights [Index: Longlnt] : Integer; property Rows [Index: Integer]: TStrings; type TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth); property ScrollBars: TScrollStyle; TGridRect = record case Integer of 0: (Left,Top,Right, Bottom: Longlnt); 1: (TopLeft, BottomRight: TGridCoord); end; property Selection: TGridRect; property TabStops [ Index: Lonalnt]: Boolean;
property TopRow: Longlnt; property VisibleColCount: Integer; property VisibleRowCount: Integer;
Содержит все текстовые строки ряда с индексом I n d e x Определяет полосы прокрутки: ssNone — нет полос; s s H o r i z o n t a l — в таблицу вставляется горизонтальная полоса; s s V e r t i c a l — вставляется вертикальная полоса; s s B o t h — вставляются обе полосы Определяет группу выделенных ячеек в координатах левой верхней и правой нижней ячеек (нумерация колонок и рядов начинается с нуля, включая колонки и ряды фиксированной зоны). После выделения фокус ввода окажется в правой нижней ячейке Разрешает/запрещает выбирать колонку с индексом I n d e x при обходе ячеек с помощью клавиши Tab. Игнорируется, если свойство O p t i o n s не содержит значения goTabs Содержит номер самого верхнего ряда, видимого в прокручиваемой зоне ячеек Содержит количество колонок, полностью видимых в зоне прокрутки Содержит количество рядов, полностью видимых в зоне прокрутки
Для компонента определен тип TGridOptions: type TGridOption goHorzLine, goColSizing, goRowSelect, TGridOptions
275
Назначение элементов множества TGridOptions представлено в табл. 13.6. Описание
property FixedRows: I n t e g e r ; property G r i d H e i g h t : I n t e g e r ; property GridLineWidth: Integer; property GridWidth: Integer; property LeftCol: Longlnt;
TStringGrid — текстовая таблица
= (goFixedVertLine, goFixedHorzLine, goVertLine, g o R a n g e S e l e c t , goDrawFocusSelected, goRowSizing, goRowMoving, goColMoving, g o E d i t i n g , g o T a b s , goAlwaysShowEditor, goThumbTracking); = s e t of TGridOptions;
Таблица 13.6. Назначение элементов множества TGridOptions Элемент
Описание
goFixedVertLine
Колонки фиксированной зоны разделяются вертикальными линиями
goFixedHorzLine goVertLine goHorzLine goRangeSelect
Ряды фиксированной зоны разделяются горизонтальными линиями Колонки рабочей зоны разделяются вертикальными линиями Колонки рабочей зоны разделяются горизонтальными линиями Разрешено выделение нескольких ячеек. Игнорируется, если во множество включен элемент g o E d i t i n g
goDrawFocusSelected
Разрешено показывать ячейку, имеющую фокус ввода, так же, как выделенную ячейку
goRowSizing goColSizing goRowMoving
Разрешено ручное (мышью) изменение высоты рядов Разрешено ручное изменение ширины колонок Разрешено ручное перемещение рядов (нажать левую кнопку мыши на фиксированной ячейке перемещаемого ряда и, удерживая кнопку нажатой, перетащить ряд на новое место)
goColMoving
Разрешено ручное перемещение колонок
goEditing
Разрешено редактирование ячейки. Игнорируется, если включен элемент g o R o w S e l e c t . Редактирование начинается после щелчка мышью или нажатия клавиши F2 и завершается при щелчке на другой ячейке или нажатии клавиши Enter
goTabs
Разрешено обходить ячейки клавишей Tab (Shift+Tab)
goRowSelect
Обязывает выделять сразу все ячейки ряда и запрещает редактирование ячеек
goAlwaysShowEditor
goThumbTracking
Разрешено редактировать ячейку, имеющую фокус ввода: редактирование возможно после выделения ячейки клавишей Tab (Shift+Tab). Игнорируется, если не включен элемент g o E d i t i n g Разрешено обновление при прокрутке. Если этот элемент отсутствует, обновление ячеек произойдет только после окончания прокрутки
Несколько методов класса могут оказаться полезными для процедуры отрисовки (табл. 13.7). Таблица 1 3 . 7 . Методы класса TStringGrid Метод
Описание
function CellRect (ACol, ARow: Longlnt) : TRect;
Возвращает прямоугольник ячейки по номерам колонки A C o l и ряда ARow Возвращает табличные координаты ячейки A C o l и ARow по экранным координатам (X, Y) точки
procedure MouseToCell (X, Y: Integer; varACol, ARow: Longlnt); type TGridCoord = record X: Integer; Y: Integer; end; function MouseCoord (X: Integer, Y: Integer): TGridCoord;
Возвращает номера столбца и ряда ячейки, которая содержит точку с заданными экранными координатами
276
Глава 13 • Компоненты категории Additional
TStringGrid — текстовая таблица
277
Остальные методы рассчитаны на перекрытие в потомках и представляют инте рес только для разработчиков новых компонентов. Для программистов-пользо вателей важны доступные компоненту события, перечисленные в табл. 13.8.
3. Поместите на панель кнопку TBitBtn, установите в ее свойство C a p t i o n стро ку Открыть файл. . ., загрузите в свойство Glyph файл FOLDER_OPEN16.BMP1 и растяните кнопку по горизонтали так, чтобы надпись и изображение полно стью умещались на ее поверхности.
Таблица 1 3 . 8 . События класса TStringGrid
4. Поместите на панель компонент O p e n D i a l o g из категории Dialogs палитры компонентов. Этот компонент создает и обслуживает стандартное для Windows окно открытия файла. С его помощью при прогоне программы вы сможете выбрать текстовый файл (например, файл с текстом программы), чтобы про грамма показала в таблице все слова из этого файла.
Событие
Описание
TMovedEvent=procedure (Sender: TObject; Fromlndex, Tolndex: Longlnt) of object; property OnColumnMoved: TMovedEvent; TGridDrawState = s e t of (gdSelected, : gdFocused, gdFixed); TDrawCellEvent procedure (Sender: TObject; Col, Row: Longlnt; Rect: TRect; S t a t e : TGridDrawState) of object;property OnDrawCell: TDrawCellEvent;
Возникает при перемещении колонки с индексом F r o m l n d e x в положение, определяемое индексом T o l n d e x
TGetEditEvent=procedure (Sender: TObject; ACol, ARow: Longlnt; var Value: String) of object; property OnGetEditMask: TGetEditEvent; property OnGetEditText: TGetEditEvent;
Возникает при необходимости перерисовать ячейку с табличными координатами ( C o l , Row): R e c t —прямоугольник отрисовки; S t a t e — состояние ячейки ( g d S e l e c t e d —ячейка выделена; g d F o c u s e d — ячейка имеет фокус ввода; g d F i x e d — ячейка принадлежит фиксированной зоне таблицы). Для отрисовки используется табличное свойство Canvas Возникает при редактировании текста в ячейке с табличными координатами ( A C o l , ARow). В параметре V a l u e обработчик должен вернуть шаблон для компонента T E d i t M a s k Возникает при редактировании текста в ячейке с табличными координатами (ACol,ARow). В параметре V a l u e обработчик должен вернуть текст для компонента T E d i t M a s k (см. событие
5. На форму (а не на панель P a n e l l ) поместите компонент T S t r i n g G r i d . 6. Поместите в свойства ColCount и RowCount компонента T S t r i n g G r i d зна чения 2 (количество колонок и рядов таблицы должно быть больше, чем коли чество фиксированных колонок F i x e d C o l s и количество фиксированных рядов FixedRows). Установите в свойство A l i g n компонента значение a l C l i e n t , чтобы он занял все свободное место формы. На этом закончим этап разработки формы. Вся основная работа будет связана с обработкой щелчка на кнопке B i t B t n l : программа сначала предложит вам выб рать текстовый файл, затем прочитает его и создаст список отсортированных в ал фавитном порядке слов из каждой строки файла, после чего наполнит словами таблицу. Наполнение будет происходить так, чтобы каждая колонка строки со держала все слова, начинающиеся на одну и ту же букву (рис. 13.2).
OnGetEditMask)
property OnRowMoved: TMovedEvent;
Возникает при перемещении ряда с индексом F r o m l n d e x в положение, определяемое индексом T o l n d e x (см. событие
OnColMoved) S e l e c t C e l l E v e n t = procedure (Sender: TObject; Col, Row: Longlnt; var C a n S e l e c t : Boolean)of object; property O n S e l e c t C e l l : TSelectCellEvent; TSetEditEvent=procedure (Sender: TObject; ACol, ARow: Longlnt; const Value: S t r i n g ) o f object; property OnSetEditText: TSetEditEvent; property OnTopLeftChanged: TNotifyEvent;
Возникает при попытке выделить ячейку с табличными координатами ( C o l , Row). В параметре C a n S e l e c t обработчик сообщает о возможности выделения ячейки Возникает при завершении редактирования ячейки ( A C o l , ARow). В параметре V a l u e обработчик получает результат ввода или редактирования текста Возникает после изменения значения TopRow или L e f t C o l в результате прокрутки рабочей зоны
В следующем примере компонент T S t r i n g G r i d используется для отображения всех слов из произвольного текстового файла: 1. Назовите пустую форму именем fmStGrid и поместите на нее панель T P a n e l . Установите в свойство A l i g n панели значение a l B o t t o m , чтобы панель всегда занимала самую нижнюю часть окна, и удалите текст в ее свойстве C a p t i o n .
Рис. 13.2. Пример использования компонента TStringList
Обработчик события O n c l i c k для кнопки B i t B t n l показан в листинге 13.1. Л и с т и н г 1 3 . 1 . Обработчик события OnClick
procedure T f m S t G r i d . B i t B t n l C l i c k ( S e n d e r: TObject); Function GetWord(var S: S t r i n g ) : String; { Вспомогательная функция для выделения очередного слова из строки } продолжение ^ ' Напомню, что поставляемые с Delphi изображения размещены в папках Program Files\Common Files\Borland Shared\Images\GlyFX\Small и ...\GlyFX\Large.
2 7 8 Глава 13 • Компоненты категории Additional Листинг 13.1 (продолжение) const // Множество символов слова: L e t t e r s : s e t o f Char = [ ' a ' . - ' z ' , ' A ' . . ' Z ' ,
TStringGrid — текстовая таблица
'А'..'я'];
begin Result := ' ' ; // Удаляем в начале строки все символы, не относящиеся while (S о '•) and not (S[l] in Letters) do Delete (S, 1, 1) ; // Формируем очередное слово while ( S O '') and (S [1] in Letters) do begin Result := Result + S[l]; Delete (S, 1, 1) end; end; // Get Word
к
слову
var F: TextFile; // Файл с текстом S, Word: String; // Вспомогательные строки NCol, NRow: Integer; // Номер текущей колонки и текущего ряда Words: TStringList; // Список отсортированных слов из файла begin // С помощью стандартного диалогового окна получаем имя файла if not OpenDialogl.Execute then Exit; // Пользователь отказался выбрать файл // Пытаемся открыть файл AssignFile(F, OpenDialogl.FileName); try Reset (F); except // Файл нельзя открыть: ShowMessage('Невозможно открыть файл ' + OpenDialogl.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 ) ; // Читаем очередную строку
279
// Выделяем из строки слова и заносим их в список Words while S o ' ' d o begin Word := GetWord(S); if Word <> '' then Words.Add(Word) // He вставляем пустые строки end end; // while not EOF(F) do Screen.Cursor := crDefault; // Восстанавливаем указатель CloseFile(F); // Закрываем файл if Words.Count=0 then Exit; // Пустой файл — выходим with StringGridl do begin NCol := 1; // Номер первой колонки слов // Цикл формирования таблицы while Words.Count > 0 do begin // Формируем заголовок колонки // и начальное значение номера ряда Cells[NCol, 0] := AnsiUpperCase(Words[0][1]); NRow := 0; // Цикл заполнения очередной колонки while (Words.Count > 0) and (AnsiUpperCase(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 [ObWords. Delete (0) ; end; // while (Words.Count > 0) // Переходим к следующей колонке if Words.Count=0 then Break; // Кончаем работу, если слов больше нет inc(NCol); // Переходим к следующей колонке ColCount := ColCount+1 // Расширяем таблицу вправо // на 1 колонку end; // while Words.Count > 0 do end; // with StringGridl do end; // procedure TfmStGrid.BitBtnlClick
280
Глава 13 • Компоненты категории Additional
'
~ TDrawGrid — произвольная таблица
TDrawGrid — произвольная таблица Компонент TDrawGrid предоставляет программисту мощные возможности созда ния и обслуживания табличных структур данных. Он обеспечивает двухмерное представление данных, упорядоченных по колонкам и рядам, и избавляет програм миста от многих рутинных операций, связанных с представлением и обслужива нием таблиц. Чтобы таблица была работоспособной, в ней, как минимум, следует определить обработчик события OnDrawCell, которое возникает при необходимости отри совать ту или иную ячейку. Для отрисовки используется табличное свойство Canvas. Компонент TDrawGrid является непосредственным родителем строковой таб лицы T S t r i n g G r i d , поэтому передает ей все свои свойства, методы и события за исключением специфичных для строк свойств C e l l s , C o l s , O b j e c t s и Rows' Поскольку порядок описания компонентов в этой главе соответствует порядку их расположения на страницах палитры компонентов, компонент T S t r i n g G r i d описан раньше своего родителя TDrawGrid, и в ы сможете найти информацию о свойствах, методах и событиях последнего в предыдущем разделе. В следующем примере компонент TDrawGrid используется для одновременно го отображения текста и картинок, причем текст можно редактировать (рис. 13.3).
281
Для получения окна, показанного на рисунке, расположите на пустой форме fmDrawGrxd компонент TDrawGrrd и задайте для него значения свойств п Т е численные в табл. 13.9. ""г" 1 , 1 .?' ^ р е Таблица 13.9. Значения свойств компонента TDrawGrid Свойство
Значение
Комментарии
Align ColCount FixedCols FixedRows Name Options.goEditing RowCount ScrollBars
alClient 2 0 0 dgDraw True 2 sbNone
Таблица занимает всю клиентскую часть о к н 7 ~ В таблице 2 колонки Нет фиксированных колонок Нет фиксированных рядов Имя таблицы Разрешено редактирование текста В таблице 2 строки Не надо вставлять полосы прокрутки
В папку с текстом примера поместите четыре произвольных растровых изображе ния форматов J P G или BMP и назовите их 01.bmp, 02.bmp и т. д. П о д г о т о в Ь « ж е показанные в листинге 13.2 обработчики событий OnCreate, OnResize и Т п " о у формы и событии O n G e t E d i t T e x t , O n S e t E d i t T e x t , OnCellDraw компонента Листинг 13.2. Модуль окна для демонстрации компонента TDrawGrid unit DrawGridMain; interface
Windows, Messages, SysUtils, Variants, Classes, Graphics Controls, Forms, Dialogs, System.ComponentModel Borland.Vcl.Grids; type TfmDrawGrid = class(TForm) dgDraw: TDrawGrid;
Рис. 13.3. Пример использования таблицы TDrawGrid для отображения картинок и
текста
procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure dgDrawGetEditText(Sender: TObject; ACol, ARow: Integer; var Value: String); procedure dgDrawSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String); procedure dgDrawDrawCell(Sender: TObject; ACol, ARow:-Integer; Rect: TRect; State: TGridDrawState); procedure FormResize(Sender: TObject)private
\
продолжение ё>
284
Глава 13 • Компоненты категории Additional
Чтобы отобразить текст подписи в окне редактора (оно появляется автоматически после щелчка на подписи), необходимо создать обработчик события O n G e t E d i t Text. В нем строка списка s l B i t M a p , индекс которой вычисляется на основе те кущих колонки и ряда табличной ячейки, передается встроенному в компонент редактору. Поскольку в программе разрешается редактировать текст подписи, об работчик O n S e t E d i t T e x t осуществляет обратные действия и сохраняет в списке s l B i t M a p отредактированную подпись. Обработчик OnDrawCell обязательно со здается для любого компонента TDrawGrid, так как без него компонент не смо жет заполнить пустые ячейки каким-либо изображением и/или текстом. Чтобы согласовать размеры ячеек таблицы с размерами окна программы, исполь зуется обработчик события O n R e s i z e (это событие возникает при каждом изме нении размеров окна). Высота (ширина) каждой ячейки устанавливается равной половине высоты (ширины) клиентской области формы минус 1 пиксел. Этот запас предотвращает автоматическую вставку в таблицу полос прокрутки.
Tlmage — изображение Компонент Tlmage служит для размещения на форме одного из трех поддержи ваемых Delphi типов изображений: растрового рисунка, значка или метафайла. Любой из этих типов изображений содержится в центральном свойстве компо нента — P i c t u r e . Свойство Canvas открывает доступ к канве, с помощью кото рой при необходимости программа может отредактировать растровое изображе ние (но не значок или метафайл!). Несложная программа просмотра изображений, создает окно, которое показано на рис. 13.4.
Tlmage — изображение
1. Поместите на форму панель T P a n e l , очистите ее свойство C a p t i o n и введи те значение a l B o t t o m в свойство A l i g n m e n t — панель займет нижнюю часть формы. 2. Разместите на панели кнопку T B u t t o n с надписью З а г р у з и т ь . 3. На любое свободное место формы (над панелью) поместите компонент Tlmage и установите в его свойство A l i g n значение a l C l i e n t . 4. На любое место (Tlmage, T P a n e l или даже T B u t t o n ) поместите компонент T O p e n P i c t u r e D i a l o g (вкладка Dialogs палитры компонентов) — с его помо щью пользователь вашей программы сможет разыскать нужный файл с изоб ражением. 5. Для кнопки B u t t o n l напишите следующий обработчик события OnClick: procedure TForml.ButtonlClick(Sender: TObject); begin if OpenPictureDialogl.Execute then begin Image1.Picture. LoadFromFile(OpenPictureDialogl.FileName); Imagel.Stretch := True end end; Замечу, что показанный далее оператор масштабирует изображение так, чтобы оно занимало всю рабочую область Tlmage: Imagel.Stretch := True Свойства класса Tlmage перечислены в табл. 13.10. Таблица 13.10. Свойства класса Tlmage Свойство
Описание
property AutoSize : Boolean;
Разрешает/запрещает изменять размеры компонента так, чтобы в них полностью загрузилось изображение (см. свойство S t r e t c h ) Содержит канву для отрисовки изображения Указывает, надо ли центрировать изображение в границах компонента. Игнорируется, если A u t o S i z e = T r u e или если S t r e t c h = T r u e и изображение не является значком (ICO) Центральное свойство класса. Служит контейнером изображения Разрешает/запрещает изменять размер изображения так, чтобы оно целиком заполнило клиентскую область компонента (см. свойство A u t o S i z e ) Запрещает/разрешает накладывать собственный фон изображения на фон компонента
p r o p e r t y Canvas : T C a n v a s ; property Center: Boolean;
property Picture : TPicture; property S t r e t c h : Boolean;
property Transparent: Boolean;
Рис. 13.4. Компонент Tlmage воспроизводит изображение
285
С помощью методов класса T P i c t u r e (см. подраздел «Классы TGraphic и TPicture» раздела «Графический инструментарий» в главе 10), к которому относится свой ство P i c t u r e , программист может загрузить изображение из файла или буфера обмена (clipboard) и сохранить его в файле или буфере.
2 8 6 Глава 13 • Компоненты категории Additional
TCheckListBox — группа флажков
287
TShape — стандартная фигура
TScrollBox— панель с полосами прокрутки
Компонент класса TShape рисует одну из простейших геометрических фигур, определяемых следующим перечисленным типом (прямоугольник, квадрат, скруг ленный прямоугольник, скругленный квадрат, эллипс, окружность):
Компонент класса T S c r o l l B o x служит контейнером для размещения других ком понентов. Его отличительная особенность — возможность прокрутки и, следова тельно, экономия пространства формы при необходимости размещения на ней боль шого количества элементов управления. Замечу, что в современных программных продуктах такого рода компоненты используются относительно редко, предпоч тение отдается многостраничным элементам управления с вкладками. Наиболее подходящей областью применения компонентов является размещение на них от носительно больших по размерам компонентов TEdit, TComboBox, TMemo и т. п. Использование компонента не отличается сложностью: поместите его на форму и размещайте затем на нем другие компоненты. Если очередной компонент вый дет за пределы рабочей области компонента T S c r o l l B o x , по сторонам контей нера возникнут полосы прокрутки (рис. 13.6).
type TShapeType = ( s t R e c t a n g l e , s t S q u a r e , stRoundRect, stRoundSquare, s t E l l i p s e , s t C i r c l e ) ; Фигура полностью занимает все пространство компонента (рис. 13.5). Если за дан квадрат или круг, а размеры элемента по горизонтали и вертикали различа ются, размер фигуры равен меньшему измерению.
Рис. 13.5. Стандартные фигуры
Помимо стандартных графических инструментов B r u s h и Реп (шрифт для ком понента не нужен), в компоненте определено свойство S h a p e : TShapeType, ко торое и задает вид геометрической фигуры. Изменение этого свойства приводит к немедленной отрисовке изображения.
TBevel — кромка Компонент класса TBevel носит оформительский характер и предназначен для визуального выделения группы элементов или отделения их друг от друга. Вид компонента (прямоугольник, рамка, верхняя линия, нижняя линия, левая ли ния, правая линия) определяет следующее свойство: type TBevelShape = (bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, b s R i g h t L i n e ) ; property Shape: TBevelShape; Стиль компонента (вдавленный или выпуклый) определяет другое свойство: type TBevelStyle = (bsLowered, b s R a i s e d ) ; property S t y l e : TBevelStyle;
Рис. 13.6. Размещение компонентов на компоненте TScrollBox Однако, если свойство A u t o S c r o l l компонента содержит значение F a l s e , полосы прокрутки не появятся и компонент будет отсекать «лишние» части своих дочерних компонентов. Еще одним важным свойством компонента является A u t o S i z e : если это свойство имеет значение True, размеры компонента будут автоматически изме няться так, чтобы все части дочерних компонентов оставались в его рабочей зоне, даже если для этого придется увеличить размеры внешнего контейнера (формы). С помощью свойств H o r z S c r o l l B a r и V e r t S c r o l l B a r программист может уп равлять свойствами каждой из полос прокрутки в отдельности.
TCheckListBox— группа флажков Подобно тому, как компонент TRadioGroup группирует переключатели, компо нент T C h e c k L i s t B o x группирует флажки, позволяя обратиться к любому из них по индексу. Чтобы вставить в компонент группу флажков, следует раскрыть редактор его свойства I t e m s и ввести одну или несколько текстовых строк— каждая строка будет соответствовать поясняющей надписи справа от флажка.
288
Глава 13 • Компоненты категории Additional
Свойства класса TCheckListBox перечислены в табл. 13.11. Таблица 13.11. Свойства класса TCheckListBox Свойство
Описание
property AllowGrayed: Boolean;
Разрешает/запрещает использовать третье состояние флажка c b G r a y e d (не уверен) Определяет тип рамки, очерчивающей компонент: bsNone — нет рамки; b s S i n g l e — рамка толщиной 1 пиксел
property BorderStyle: TBorderStyle; property Canvas : TCanvas;
Это свойство используется для программной отрисовки флажков
property Checked [Index: Integer] : Boolean;
Содержит для флажка с индексом I n d e x значение, идентифицирующее выбор пользователя. Для состояний c b U n c h e c k e d и c b G r a y e d свойство имеет значение F a l s e
property Columns : Integer; property IntegralHeight: Boolean;
Определяет количество колонок флажков
property ItemHeight: Integer;
Если свойство S t y l e компонента имеет значение I s O w n e r D r a w F i x e d , значение I t e m H e i g h t определяет высоту каждого флажка компонента. При других значениях S t y l e это свойство игнорируется
property Itemlndex: Integer;
Содержит индекс флажка, состояние которого изменилось (индексация флажков начинается с нуля)
property I terns : TStrings; property MultiSelect: Boolean;
Содержит подписи флажков компонента
property SelCount: Integer;
Содержит количество установленных флажков, если свойство M u l t i S e l e c t содержит значение T r u e Содержит значение T r u e , если установлен флажок с индексом I n d e x
Если имеет значение T r u e , компонент автоматически изменяет высоту так, чтобы в нем полностью отображались все флажки
289
Для компонента определено событие, которое наступает при изменении состоя ния любого флажка: property OnClickCheck: TNotifyEvent;
TSplitter— вешка разбивки Компонент T S p l i t t e r предназначен для ручного (с помощью мыши) управле ния размерами контейнеров TPanel, TGroupBox и им подобных во время про гона программы. Визуально он представляет собой небольшую вертикальную или горизонтальную полосу, располагающуюся между двумя соседними контейнера ми или на «свободной» стороне одного их них. Непременным условием правильной работы компонента является выравнивание контейнера (контейнеров), размерами которого (которых) он управляет, вдоль од ной из сторон окна или охватывающего контейнера. Для примера на рис. 13.7 по казаны две панели, разделенные компонентом T S p l i t t e r . Для нижней панели выполняется условие A l i g n = a l B o t t o m , верхняя панель в этом случае может иметь выравнивание a l B o t t o m или a l C l i e n t . Между панелями помещен ком понент T S p l i t t e r с выравниванием a l B o t t o m .
Если содержит значение T r u e , пользователь может устанавливать несколько флажков
property Selected[Index: Integer] : Boolean; property Sorted: Boolean; Сортирует по алфавиту подписи к флажкам property State [Index : Integer]: Содержит состояние флажка с индексом I n d e x : TCheckBoxState; cbUnchecked— не установлен; cbChecked — установлен; c b G r a y e d — не уверен Определяет способ отрисовки флажков: type TListBoxStyle = (IbStandard, lbOwnerDrawFixed, I b S t a n d a r d — флажки отрисовывает Windows; lbOwnerDrawVariable); property l b O w n e r D r a w F i x e d — компонент использует программный метод отрисовки флажков, каждый Style: TListBoxStyle;
из которых имеет одинаковую высоту ItemHeight; lbOwnerDrawVariable — флажки отрисовываются программно и могут иметь разную высоту
property TabWidth: Integer; property Toplndex: Integer;
TSplitter— вешка разбивки
Определяет ширину табуляционного пробела Содержит индекс самого первого отображаемого флажка
Рис. 13.7. Пример использования компонента TSplitter
Свойства класса T S p l i t t e r представлены в табл. 13.12. Таблица 13.12. Свойства класса TSplitter Свойство
Описание
property Beveled: Boolean; Управляет трехмерным изображением компонента. NaturalNumber =1..High (Integer);property MinSize: NaturalNumber;
Ю Зак. 126
Если имеет значение F a l s e , компонент виден как узкая полоска фона между разделяемыми им компонентами Содержит минимальный размер любого из компонентов, которые разделяет компонент T S p l i t t e r . Если выравнивание имеет значение a l L e f t или a l R i g h t — минимальная ширина компонента слева и справа от вешки разбивки, если a l T o p или a l B o t t o m минимальная высота компонента выше или ниже от нее
290
Глава 13 • Компоненты категории Additional
Замечу, что, в отличие от других видимых компонентов, T S p l i t t e r не имеет свой ства ShowHint, поэтому он не может отображать оперативную подсказку в неболь шом окне. Однако у него есть свойство Hint, содержимое которого (до возможного разделителя в виде вертикальной черты |) может отображать обработчик события A p p l i c a t i o n . O n H i n t (см. раздел «Поддержка справочной службы» в главе 11). Для компонента определено событие OnMoved, которое вызывается при любом перемещении компонента мышью.
TStaticText — текстовая метка Компонент T S t a t i c T e x t подобен своему «двоюродному брату» TLabel во всем, за исключением двух аспектов. Во-первых, он порожден от класса TWinControl и, таким образом, имеет Windows-окно. Это обстоятельство может быть необхо димым условием правильного взаимодействия с внешним для Delphi элементом ActiveX, с которым текстовая метка связана своим свойством F o c u s C o n t r o l . Во-вторых, в его свойстве B o r d e r S t y l e добавлено значение s b s S u n k e n , кото рое создает иллюзию рельефности компонента: type T S t a t i c B o r d e r S t y l e = (sbsNone, s b s S i n g l e , sbsSunken); property B o r d e r S t y l e : T S t a t i c B o r d e r S t y l e ; Все остальные свойства и методы компонента совпадают со свойствами и мето дами TLabel. Отмечу, что компонент обладает внешней и внутренней рамками, так что его вид можно разнообразить.
TControlBar— контейнер для панели инструментов Компонент T C o n t r o l B a r служит удобным контейнером для размещения пане лей инструментов TToolBar (см. раздел «TToolBar и TToolButton — панель ин струментов и кнопки для нее» в главе 14). Он активно использует технологию «причаливания» (Drag&Dock) для управления положением панелей. При размещении на компоненте контейнера TToolBar на левой кромке после днего появляется вешка, которая используется для перемещения компонента мы шью в пределах T C o n t r o l B a r . Если свойство AutoDrag компонента имеет зна чение T r u e , то контейнеры T T o o l B a r могут покидать пределы компонента T C o n t r o l B a r и «плавать» в самостоятельном окне. Свойства класса T C o n t r o l B a r представлены в табл. 13.13. Таблица 13.13. Свойства класса TControlBar Свойство
Описание
property AutoDrag: Boolean; property Picture : TPicture;
Разрешает/запрещает компонентам T T o o l B a r покидать границы компонента T C o n t r o l B a r Содержит изображение, которое, периодически повторяясь, создает фон компонента
TApplicationEVents — обработчик сообщений Windows 2 9 1 Свойство p r o p e r t y RowSize : TRowSize; p r o p e r t y RowSnap: Boolean;
Описание Задает высоту одного ряда панелей инструментов. Значение по умолчанию 25 вточности соответствует высоте одной панели, и в этом случае между рядами нет зазора Если содержит T r u e , «причаливаемая» панель будет выравниваться по высоте ряда
Следующий метод устанавливает все дочерние компоненты на их окончатель ные позиции после «причаливания» или «отчаливания» очередной панели инст рументов: procedure StickControls; virtual; Этот метод нужно перекрыть, если стандартное размещение панелей по какимлибо причинам вас не устраивает.
TApplication Events — обработчик сообщений Windows В отличие от остальных компонентов вкладки Additional, этот компонент не име ет видимого эквивалента в работающей программе. Его основное и единственное назначение — служить приемником многочисленных сообщений, которые Win dows посылает работающей программе. Лишь очень немногие программы нуждаются в специальной (отличной от при нятой по умолчанию) обработке сообщений — примером могут служить програм мы, эмулирующие клавиатуру DOS или фильтрующие ввод пользователя (в учеб ных целях, например). Следующая простая программа познакомит вас с примером использования ком понента. В ней фильтруется ввод программы в компонент T E d i t так, чтобы за претить появление в нем любых цифр: 1. На пустую форму поместите компоненты T E d i t и T A p p l i c a t i o n E v e n t s . 2. Напишите следующий обработчик события OnMessage компонента T A p p l i c a tionEvents: procedure TForml.ApplicationEventslMessage(var Msg: tagMSG; var Handled: Boolean); begin Handled := E d i t l . F o c u s e d and (Msg.Message = wm_Char) and (Msg.wParam >= 48) and (Msg.wParam <= 57) end; Если теперь запустить программу, ввод цифр в компонент E d i t l будет блокирован. В нашей простой программе есть единственный компонент, способный получать фокус ввода, поэтому проверка свойства E d i t l . F o c u s e d в операторе присваива ния является излишней. Я ввел ее только для того, чтобы продемонстрировать, как программа может определить получателя клавиатурного ввода.
292
Глава 13 • Компоненты категории Additional
Любая форма может иметь сколько угодно компонентов T A p p l i c a t i o n E v e n t s . Одно и то же сообщение поступает последовательно в каждый из них до тех пор, пока это сообщение не будет обработано или пока оно не поступит всем. Порядок получения сообщений обратен порядку размещения компонентов на форме: вна чале сообщение получит последний размещенный на форме компонент, затем предпоследний и т. д. Этот порядок можно изменить с помощью метода A c t i v a t e . Запретить остальным компонентам получение уже обработанного сообщения мож но с помощью метода C a n c e i D i s p a t c h . Если в проекте имеется несколько форм, расположенные на них компоненты T A p p l i c a t i o n E v e n t s начинают получать сообщения только после того, как соответствующая форма становится активной. Работа всех компонентов T A p p l i c a t i o n E v e n t s будет блокирована, если в объ екте A p p l i c a t i o n определен обработчик события OnMessage. В инспекторе объектов представлены только два свойства компонента: Name и Tag. Методы и свойства класса T A p p l i c a t i o n E v e n t s перечислены в табл. 13.14. Т а б л и ц а 1 3 . 1 4 , Методы и свойства класса TAplicationEvents Метод (свойство)
Описание
procedure Activate; procedure CanceiDispatch;
Делает компонент первым получателем сообщений Запрещает получение текущего сообщения остальным компонентам Возникает при активизации приложения
property OnActivate: TNotifyEvent; property OnDeactivate: TNotifyEvent; property OnException: TNotifyEvent; property OnHelp: TNotifyEvent; property OnHint: TNotifyEvent; property Onldle: TNotifyEvent; type TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object; property OnMessage: TMessageEvent; property OnMinimize: TNotifyEvent; property OnRestore: TNotifyEvent; type TSettingChangeEvent = procedure (Sender: TObject; Flag: Integer; const Section: string; var Result: Longint) of object;property OnSettingChange: TSettingChangeEvent ; TShortCutEvent = procedure (var Msg: TWMKey; var Handled: Boolean) of object; property OnShortCut: TShortCutEvent;
TValueListEditor — специализированный редактор списков
293
Метод (свойство)
Описание
type TShowHintEvent = procedure (var HintStr: string, var CanShow:Boolean; var Hintlnfo: THintInfo)of object; property OnShowHint:
Возникает непосредственно перед появлением окна оперативной подсказки. Обработчик может изменить текст подсказки и свойства окна TShowHintEvent;
TValueListEditor — специализированный редактор списков Компонент T V a l u e L i s t E d i t o r предназначен для редактирования списков, стро ки которых состоят из пар Имя = Значение. Такие списки широко используются в реестре Windows и файлах инициализации. Наконец, начиная с версии 5, Delphi по умолчанию в таком формате хранит файлы описания форм с расширением DFM (NFM). Если на пустую форму поместить компонент T V a l u e L i s t E d i t o r , диало говый ресурс TOpenDialog и кнопку TButton, то после загрузки в редактор файла формы получим экран, показанный на рис. 13.8.
Возникает при потере активности Возникает при создании исключительной ситуации Возникает, когда пользователь обращается к справочной службе Возникает перед появлением окна оперативной подсказки Возникает при наступлении паузы (очередь сообщений исчерпана) Возникает при получении любого сообщения Windows. Если обработчик поместит в параметр H a n d l e d значение T r u e , дальнейшая обработка сообщения прекращается Возникает перед минимизацией окна приложения Возникает перед восстановлением нормальных размеров окна приложения Возникает при изменении какого-либо параметра Windows: S e c t i o n — указывает секцию настройки Windows (ключ в реестре или в файле инициализации); R e s u l t — ответ программы (О — сообщение принято и учтено); F l a g — целочисленная величина, конкретизирующая изменение Возникает после нажатия клавиши быстрого вызова и до активации соответствующего действия. Если обработчик поместит в параметр H a n d l e d значение T r u e , это действие блокируется
Рис. 13.8. Демонстрация компонента TValueListEditor Обработчик щелчка на кнопке имеет такой вид: procedure T F o r m l . B i t B t n l C l i c k ( S e n d e r : TObject); begin i f OpenDialogl.Execute then ValueListEditorl.Strings.LoadFromFile(OpenDialogl.FileName) end; Непосредственным предком компонента T V a l u e L i s t E d i t o r является класс TCus tomDrawGrid, поэтому многие его свойства, методы и события совпада ют с описанными в этой главе свойствами, методами и событиями компонента TStringGrid. Специфичные свойства класса T V a l u e L i s t E d i t o r перечислены в табл. 13.15.
294
Глава 13 • Компоненты категории Additional
Таблица 1 3 . 1 5 . Некоторые свойства класса TValueListEditor Свойство
Описание
Открывает доступ к содержимому ячейки, лежащей на пересечении столбца A C o l со строкой ARow (нумерация столбцов и строк начинается с нуля) Содержит количество столбцов property ColCount: I n t e g e r ; Свойство может включать следующие значения: type TDisplayOption = d o C o l u m n T i t l e s — первая строка содержит (doColumnTitles, заголовки столбцов, определенные свойством doAutoColResize, T i t l e C a p t i o n , и не прокручивается; doKeyColFixed); d o A u t o C o l R e s i z e — предписывает TDisplayOptions = s e t автоматически изменять ширину столбцов of TDisplayOption; при изменении размеров компонента; property d o K e y C o l F i x e d — е с л и указано, ширина первого DisplayOptions : столбца, содержащего имена, не меняется TDisplayOptions; при изменении размеров Сложное свойство, управляющее различными property ItemProps [const KeyOrlndex: Variant] : TItemProp, аспектами отображения значений (см. далее) Управляет возможным изменением списка: type TKeyOption = (keyEdit, k e y E d i t — пользователь может изменять имя keyAdd, keyDelete, keyttnique) ; параметра (в первом столбце); k e y A d d — TKeyOptions = set of пользователь может добавлять новую пару TDisplayOption; property имя-значение нажатием клавиши Insert KeyOptions: TKeyOptions; (требует включения k e y E d i t ) ; k e y D e l e t e — пользователь может удалить выделенную пару нажатием клавиши Delete; k e y U n i q u e — названия вводимых пользователем имен должны быть уникальными Открывает доступ к названиям значений property Keys [Index : Integer] по их индексам String; Содержит количество строк property RowCount: Integer; property Strings: TStrings; Содержит строки списка Содержит заголовки столбцов property TitleCaptions : TStrings; property Values [const Key: Открывает доступ к значениям по их индексам String]: String; p r o p e r t y C e l l s [ACol, ARow: I n t e g e r ] : String;
Свойство I t e m P r o p s класса T I t e m P r o p имеет собственные свойства (табл. 13.16). Таблица 13.16. Свойства, относящиеся к свойству ItemProps класса TItemProp Свойство
Описание
property EditMask: String;
Содержит маску для формирования значения. Правила создания маски описаны в разделе «TMaskEdit — поле с маской ввода» этой главы
type T E d i t S t y l e = (esSimple, Определяет правила редактирования значения (см. далее) esEllipsis,esPickList); property E d i t S t y l e : TEditStyle, property MaxLength: I n t e g e r ; Определяет максимальную длину (в символах) значения. Если 0, нет ограничений на длину Содержит раскрывающийся список возможных propertyPickList: TStrings; значений property Readonly: Boolean; Разрешает/запрещает редактирование значения
TValueListEditor— специализированный редактор списков
295
Значения свойства E d i t S t y l e : •
e s S i m p l e — значение редактируется гфямо в строке как в обычном одностроч ном текстовом поле;
•
e s E l l i p s i s — в строку вставляется кнопка для вызова диалогового окна ре дактора значения (возбуждается событие O n E d i t B u t t o n C l i c k ) ;
•
e s P i c k L i s t — в строку вставляется кнопка для раскрытия списка возмож ных значений (возбуждается событие O n G e t P i c k L i s t ) .
Свойство P i c k L i s t не предназначено для программного доступа. Чтобы создать раскрывающийся список возможных значений, нужно свойству I t e m P r o p s . E d i t S t y l e присвоить значение e s P i c k L i s t и написать обработчик события O n G e t P i c k L i s t . Пусть, например, первая отображаемая в компоненте строка должна иметь раскрывающийся список. Тогда необходим такой код: procedure T F o r m l . F o r m A c t i v a t e ( S e n d e r : T O b j e c t ) ; begin ValueListEditorl.ItemProps[1].EditStyle := esPickList; end; procedure T F o r m l . V a l u e L i s t E d i t o r l G e t P i c k L i s t ( S e n d e r : TObject, c o n s t KeyName: S t r i n g ; V a l u e s : T S t r i n g s ) ; begin // Содержимое списка Values.Add('!') V a l u e s . A d d ( ' 2' ) Values.Add('3') Values.Add('4') end;
доступно
через
параметр
Values:
Методы класса T V a l u e L i s t E d i t o r перечислены в табл. 13.17. Таблица 13.17. Методы класса TValueListEditor Метод
Описание
p r o c e d u r e D e l e t e R o w (ARow: I n t e g e r ) ; override; f u n c t i o n FindRow ( c o n s t KeyName: S t r i n g ; v a r Row: I n t e g e r ) : Boolean;
Удаляет из списка строку с индексом ARow. Свойство K e y O p t i o n s должно содержать флаг k e y D e l e t e Ищет в списке строку с именем KeyName и возвращает в параметре Row индекс найденной строки. Возвращает значение T r u e в случае успеха поиска
function InsertRow(const KeyName,Value: S t r i n g ; Append: B o o l e a n ) : I n t e g e r ;
Вставляет в список строку вида KeyName = V a l u e . Если текущая строка пуста, вставляемая строка занимает ее место, в противном случае она занимает место перед текущей (если Append = F a l s e ) или за ней (если Append = T r u e ) . Возвращает индекс вставленной строки. Свойство K e y O p t i o n s должно содержать флаг k e y A d d Перерисовывает компонент Отменяет все изменения текущей строки и возвращает значение T r u e в случае успеха
procedure R e f r e s h ; function RestoreCurrentRow: Boolean;
2 9 6 Глава 13 • Компоненты категории Additional
TTabSet—набор вкладок
297
Методы C e l l R e c t , MouseToCell и MouseCoord компонента T V a l u e L i s t E d i t o r аналогичны одноименным методам компонента T S t r i n g G r i d .
Многие свойства, методы и события компонента совпадают с одноименными свой ствами, методами и событиями его ближайшего «родственника» — компонента TComboBox.
TLabelEdit— однострочное поле с меткой
Специфичные свойства класса TColorBox перечислены в табл. 13.18.
Компонент T L a b e l E d i t представляет собой удобную комбинацию однострочно го текстового поля с меткой. Надпись в метке указывается в свойстве E d i t L a b e l . Следующее свойство определяет положение метки относительно текстового поля:
TColorListBox — список выбора цвета
type T L a b e l P o s i t i o n = (lpAbove, lpBelow, l p L e f t , l p R i g h t ) ; property L a b e l P o s i t i o n : T L a b e l P o s i t i o n ; Расстояние от метки до текстового поля (в пикселах) определяется свойством LabelSpacing property LabelSpacing: I n t e g e r ;
TColorBox — список выбора цвета Компонент TColorBox представляет собой комбинированный список, пунктами которого являются цвета, то есть этот компонент предназначен для отображения и выбора цвета. В отличие от компонентов T C o l o r G r i d (категория Samples) и ТСоl o r D i a l o g (категория Dialogs), состав отображаемых им цветов может меняться. Таблица 13.18. Некоторые свойства класса TColorBox Свойство
Описание
property ColorNames [Index: I n t e g e r ] : String; property Colors [Index : I n t e g e r ] : TColor; property Def a u l t C o l o r C o l o r : TColor;
Возвращает имя цвета по его индексу в списке Содержит список цветов
Содержит цвет, заданный по умолчанию. Свойство S t y l e должно включать флаги cbSystemColors и cblncludeDefault property NoneColorColor: Определяет пункт без цвета. Свойство S t y l e TColor; должно включать флаги cbSystemColors и cblncludeNone property S e l e c t e d : TColor; Определяет текущий выбранный цвет type TColorBoxStyles = Определяет отображаемые компонентом цвета: (cbStandardCoIors, cbStandardColors — список содержит 16 cbExtendedColors, основных цветов; cbExtendedColors — список cbSystemColors, cblncludeNone, содержит дополнительные цвета, определенные cblncludeDef ault, в модуле Graghics; cbSystemColors — cbCustomColor, cbPrettyNames) ; в списке отображаются цвета clMoneyGreen, TColorBoxStyle = s e t of clSkyBlue,clCreamиclMedGray; TColorBoxStyles; cblncludeNone — список содержит пункт property S t y l e : TColorBoxStyle; clNone; cblncludeDef a u l t — список содержит пункт c l D e f a u l t ; cbCustomColor—первый пункт списка открывает стандартное диалоговое окно TColorDialog; cbPrettyNames — имена цветов отображаются без префикса cl
Компонент T C o l o r L i s t B o x имеет такую же функциональность, как и TCo lorBox, но отличается оформлением: он представляет собой постоянно раскры тый список, элементами которого являются цвета. Основные свойства компо нента аналогичны свойствам TColorBox.
TTabSet— набор вкладок Компонент TTabSet представляет собой набор вкладок и отличается от компо нента T T a b C o n t r o l (категория Win32) отсутствием каких бы то ни было компо нентов (страниц), связанных с каждой вкладкой. Таким образом, компонент мож но использовать для осуществления определенных действий, происходящих при выборе очередной вкладки. В этом отношении он предоставляет ту же функцио нальность, что и переключатели TRadioGroup. Свойство Tabs : T S t r i n g s представляет собой набор строк, определяющих на звания вкладок. Чтобы в компоненте появилась хотя бы одна вкладка, в это свой ство нужно поместить строку (количество строк в этом свойстве определяет коли чество вкладок). Свойство Tab I n d e x — индекс выбранной вкладки (индексация начинается с 0). Таким образом, с помощью следующего обработчика события O n C l i c k компонента можно выяснить, какая вкладка выбрана, и выполнить свя занные с этой вкладкой действия: procedure TForml.TabSetlClick(Sender: TObject); begin case TabSetl.Tablndex of 0: DoWorkO; 1: DoWorkl; end end; Свойство T a b P o s i t i o n позволяет располагать вкладки вверху, внизу, вдоль ле вой или правой стороны компонента.
TPageControl — набор страниц со вкладками 2 9 9
Компоненты категории Win32
Компоненты категории Win32 предназначены для разработки программ с услож ненным интерфейсом — предоставляемые ими элементы управления в традици онных программах обычно не требуются.
TTabControl — набор вкладок Компонент T T a b C o n t r o l представляет собой контейнер с вкладками. Свойство Tabs определяет названия и количество вкладок. Событие OnChange возникает при выборе новой вкладки и позволяет управлять содержимым окна компонента. ПРИМЕЧАНИЕ Изменение выбранной вкладки не влияет автоматически на содержимое окна компонента. Для этого используется обработчик OnChange (см. раздел «TPageControl — набор страниц со вкладками»).
Свойства класса T T a b C o n t r o l представлены в табл. 14.1. Таблица 1 4 . 1 . Свойства класса TTabControl Свойство
Описание
property D i s p l a y R e c t : TRect;
Определяет рабочую зону компонента, предназначенную для размещения других компонентов. Клиентская часть компонента содержит зону вкладок и рабочую зону Если содержит значение T r u e , название вкладки автоматически выделяется цветом, когда на ней находится указатель мыши Разрешает располагать вкладки в несколько рядов. Если содержит значение F a l s e и вкладки не умещаются в границах компонента, в зону вкладок автоматически вставляются кнопки прокрутки
property HotTrack: Boolean;
p r o p e r t y M u l t i L i n e : Boolean;
Свойство
Описание
property S c r o l l O p p o s i t e : Boolean;
Разрешает/запрещает перемещение неактивных рядов вкладок на противоположную сторону компонента. Учитывается, если количество рядов больше 2
property TabHeight: S m a l l i n t ;
Определяет высоту каждой вкладки в пикселах. Если содержит 0, высота вкладок выбирается автоматически в зависимости от выбранного шрифта Определяет индекс выбранной вкладки или содержит - 1 , если ни одна из вкладок не выбрана. Индексация начинается с О
property Tablndex: I n t e g e r ; TTabPosition=(tpTop, tpBottom) property T a b P o s i t i o n : TTabPosition; property Tabs : T S t r i n g s ;
Определяет положение зоны вкладок относительно рабочей зоны компонента ( t p T o p — вверху, t p B o t t o m — внизу)
property TabWidth: S m a l l i n t ;
Определяет ширину каждой вкладки в пикселах. Если содержит 0, ширина каждой вкладки выбирается индивидуально в зависимости от длины ее надписи
Определяет надписи на вкладках и их количество. Чтобы добавить или удалить вкладку, нужно добавить ее надпись в список Tabs или удалить надпись из списка
Помимо события OnChange, возникающего после перехода к новой вкладке, для компонента определено также событие OnChanging, которое возникает перед сменой вкладки: type TTabChangingEvent = procedure (Sender: TObject; var AllowChange: Boolean) of object; property OnChanging: TTabChangingEvent; Обработчик события может запретить выбор вкладки, вернув в параметре Allow Change значение F a l s e .
TPageControl — набор страниц со вкладками Компонент T P a g e C o n t r o l , в отличие от компонента T T a b C o n t r o l , может со держать несколько перекрывающих друг друга страниц (панелей) класса ТТаЬS h e e t . Каждая страница выбирается щелчком на связанной с ней вкладке и мо жет содержать свой набор помещенных на нее компонентов. Чтобы на этапе конструирования добавить новую страницу или выбрать ранее вставленную, щелкните на компоненте правой кнопкой мыши и выберите в кон текстном меню команду New Page (новая страница), Next Page (следующая стра ница) или Previous Page (предыдущая страница). Смена страниц идет цикличес ки, то есть после показа последней отображается первая и наоборот. Помимо свойств HotTrack, M u l t i L i n e , S c r o l l O p p o s i t e , TabHeight, TabPo s i t i o n и TabWidth, аналогичных одноименным свойствам компонента TTab C o n t r o l , компонент T P a g e C o n t r o l имеет специфические свойства, перечис ленные в табл. 14.2.
TRichEdit— поле формата RTF
3 0 0 Глава 14 • Компоненты категории Win32
Методы класса T P a g e C o n t r o l перечислены в табл. 14.3. Таблица 14.2. Свойства класса TPageControl Свойство
Описание
property ActivePage : TTabSheet;
Содержит ссылку на активную страницу. Установка нового значения A c t i v e P a g e размещает соответствующую страницу поверх остальных. Для выбора новой страницы следует использовать методы S e l e c t N e x t P a g e ' и F i n d N e x t P a g e Содержит количество страниц (только для чтения) Возвращает ссылку на страницу по ее индексу
property PageCount: I n t e g e r ; property Pages [Index : I n t e g e r ] TTabSheet;
(только для чтения)
Таблица 14.3. Методы класса TPageControl Метод
Описание
f u n c t i o n FindNextPage (CurPage: TTabSheet; GoForward, CheckTabVisible: Boolean): TTabSheet;
Ищет следующую страницу: CurPage — текущая страница; G o F o r w a r d —содержит значение T r u e , если поиск идет от первой страницы к последней; C h e c k T a b V i s i b l e — содержит значение T r u e , если из поиска исключаются страницы с признаком T a b V i s i b l e = F a l s e . Возвращает ссылку на найденную страницу. Если страница CurPage не принадлежит компоненту, возвращает ссылку на первую или последнюю страницу в зависимости от параметра G o F o r w a r d Делает активной другую страницу: если G o F o r w a r d = T r u e , активизируется следующая страница, в противном случае— предыдущая
procedure SelectNextPage (GoForward: Boolean);
TImageList — хранилище изображений Компонент класса T I m a g e L i s t представляет собой контейнер для хранения мно жества рисунков одинакового размера. Он может быть полезен при программ ном создании набора кнопок для панели инструментов, секций управляющего заголовка, анимационных эффектов и вообще там, где требуется индексирован ный доступ к изображениям. Компонент не имеет собственной канвы и поэтому не может самостоятельно ото бражать хранимые в нем изображения, которые могут быть растрами (BMP) или значками (ICO). Его метод Draw получает канву от другого компонента и рисует изображение в клиентской области этого компонента. У компонента есть множе ство специальных свойств и методов, упрощающих обслуживание набора изоб ражений. Для получения дополнительной информации обратитесь к встроенной справочной службе.
TRichEdit— поле формата RTF Компонент T R i c h E d i t представляет собой многострочное редактируемое тексто вое поле, работающее с форматом RTF (Rich Text Format — расширенный тексто-
301
вый формат). Текст формата RTF хранит дополнительную служебную информа цию, управляющую свойствами каждого абзаца и сменой шрифта по ходу текста. Замечу, что формат RTF не использует кодировку Unicode и имеет свои сред ства сохранения информации о языке. Следующая программа выведет в заголо вок окна пустую строку, так как свойство C a p t i o n умеет работать с Unicode, но не умеет — с RTF: procedure T F o r m l . B u t t o n l C l i c k ( S e n d e r : TObject); begin RichEditl.Lines.Add(Editl.Text); Caption := R i c h E d i t l . L i n e s [ 1 ] end; Правильный вариант ( T R i c h E d i t умеет работать с Unicode): procedure T F o r m l . B u t t o n l C l i c k ( S e n d e r : TObject); begin Caption := E d i t l . T e x t ; RichEditl.Lines.Add(Editl.Text) ; end; Компонент использует вспомогательные объекты класса T T e x t A t t r i b u t e s для хранения атрибутов шрифта. Эти атрибуты распространяются на весь текст через свойство компонента D e f A t t r i b u t e s или на выделенную часть текста— через его свойство S e l A t t r i b u t e s . Помимо обычных шрифтовых свойств C h a r S e t , Color, H e i g h t , Name, P i t c h , S i z e и S t y l e (см. описание класса TFont) объект T T e x t A t t r i b u t e s имеет также свойства C o n s i s t e n t A t t r i b u t e s и P r o t e c t e d . Первое доступно только для чтения и содержит набор текстовых характеристик, общих как для всего текста, так и для его выделенной части. Свойство P r o t e c t e d защищает весь текст или его части от редактирования. Попытка изменить текст, имеющий атрибут P r o t e c t e d , вызывает обработчик события O n P r o t e c t C h a n g e , который может разрешить или запретить изменения. По умолчанию изменения запрещены. Для каждого текстового абзаца создается объект класса T P a r a A t t r i b u t e s , в ко тором сохраняются атрибуты абзаца. Эти атрибуты доступны через свойства клас са T P a r a A t t r i b u t e s , перечисленные в табл. 14.4. Т а б л и ц а 1 4 . 4 . Свойства класса TParaAttributes Свойство
Описание
property Alignment: TAlignment;
Определяет горизонтальное выравнивание текста
property F i r s t l n d e n t : Longlnt;
Определяет отступ текста первой строки абзаца
абзаца относительно границ компонента в пикселах от левого края рабочей зоны компонента
property
Lef
tlndent:
Longlnt;
TNumberingStyle = (nsNone, n s B u l l e t ) ; property Numbering: TNumberingStyle;
Определяет отступ остальных строк текста абзаца
в пикселах от границы, устанавливаемой свойством
Firstlndent Определяет, надо ли вставлять слева от абзаца маркеры списка. Если содержит значение nsBullet, маркеры списка вставляются
продолжение &
302
ТТгаскВаг—ползунок
Глава 14 • Компоненты категории Win32
Таблица
14.4
303
Т а б л и ц а 1 4 . 6 . Методы класса TRichEdit
(продолжение)
Свойство
Описание
Метод
Описание
property R i g h t l n d e n t : Longlnt;
Определяет отступ текста абзаца в пикселах от правого края компонента
Удаляет весь текст
property Tab [ I n d e x : Byte] : Longlnt; property TabCount: Integer;
Для табулостопа с индексом I n d e x содержит его позицию в пикселах от левого края компонента Определяет количество табулостопов в строке абзаца
procedure C l e a r ; TSearchType= (stWholeWord, stMatchCase); TSearchTypes= s e t of TSearchType; function FindText (const S e a r c h S t r : String; S t a r t P o s , Length: Integer; Options: TSearchTypes) : I n t e g e r ; function GetSelTextBuf (Buffer: PChar; B u f S i z e : I n t e g e r ) : Integer; procedure P r i n t (const Caption: String);
Копирует не более B u f S i z e символов выделенного текста в буфер B u f f e r и возвращает количество скопированных символов Форматирует текст по границам листа бумаги и печатает его на принтере, заданном по умолчанию. Параметр C a p t i o n определяет заголовок печати
В табл. 14.5 представлены свойства класса T R i c h E d i t . Таблица 14.5. Свойства класса TR chEdit Свойство
Описание
property D e f A t t r i b u t e s : TTextAttributes; property H i d e S c r o l l B a r s : Boolean;
Определяет шрифтовые атрибуты всего текста Определяет, будут ли автоматически появляться полосы прокрутки, если текст отсекается границами компонента. Игнорируется, если свойство S c r o l l B a r s содержит значение ssNone
property H i d e S e l e c t i o n : Boolean, Определяет, будет ли убираться выделение текста, если компонент потеряет фокус ввода property Lines : T S t r i n g s ;
Содержит набор строк текста. С помощью его методов L o a d F r o m F i l e и S a v e T o F i l e компонент может читать текст из файла или записывать в него текст
property PageRect: TRect;
Определяет размеры страницы при печати на принтере
property P a r a g r a p h : TParaAttributes;
Содержит атрибуты текущего абзаца, то есть абзаца с выделением или с текстовым курсором. Программа не может изменить свойство P a r a g r a p h , но может изменить свойства связанного с ним абзаца
property P l a i n T e x t : Boolean;
Запрещает/разрешает записывать в файл или читать из него служебную информацию формата RTF ( T r u e — запрещает)
property S e l A t t r i b u t e s : TTextAttributes; propertySelLength: Integer;
Определяет шрифтовые атрибуты выделенного текста
property S e l S t a r t : I n t e g e r ;
Определяет номер первого символа выделенной части текста от начала текста (нумерация символов начинается с 0). Если нет выделения, определяет символ, перед которым располагается текстовый курсор
property S e l T e x t : S t r i n g ;
Содержит выделенный текст. Установка нового значения S e l T e x t заменяет выделенный текст новым, а если нет выделения — вставляет его в позицию курсора
Задает длину в символах выделенной части текста
Методы класса T R i c h E d i t перечислены в табл. 14.6.
Ищет в тексте строку S e a r c h S t r и возвращает индекс первого ее символа при удачном поиске: S t a r t P o s — начало поиска: L e n g t h — длина строки. O p t i o n s указывает, будет ли поиск идти по целым словам и надо ли учитывать регистр букв
События класса TRichEdit представлены в табл. 14.7. Таблица 14.7. События класса TRichEdit Событие
Описание
TRichEditProtectChange = procedure(Sender: TObject; StartPos, EndPos: Integer; var AllowChange: Boolean) of object;property OnProtectChange: TRichEditProtectChange;
Возникает при попытке изменить текст, имеющий атрибут P r o t e c t e d : S t a r t P o s , EndPos — начальная и конечная позиции изменяемого текста соответственно. В параметре A l l o w C h a n g e обработчик возвращает значение T r u e , если можно изменять текстСобытие связано с отрисовкой текста из-за изменения размеров шрифта. Параметр R e c t содержит прямоугольник, который будет отрисован
TRichEditResizeEvent = procedure(Sender: TObject; Rect: TRect) of object; property OnResizeRequest: TRichEditResizeEvent; TRichEditSaveClipboard = procedure(Sender: TObject; NumObjects, NumChars: Integer; varSaveClipboard: Boolean) of object; property OnSaveClipboard: TRichEditSaveClipboard; property OnSelectionChange: TeKcraTNotifyEvent;
Возникает в момент удаления компонента и извещает его о том, что в буфере обмена осталась помещенная компонентом информация. Если обработчик вернет значение F a l s e в параметре SaveClipboard, буфер обмена будет очищен Возникает при изменении выделенного
ТТгаскВаг — ползунок Компонент класса Т Т г а с к В а г предназначен для плавного изменения числовой величины. Он во многом схож с элементом управления T S c r o l l B a r (см. раздел «TScrollBar — полоса прокрутки» в главе 12) и отличается от него в основном оформлением. Свойства класса Т Т г а с к В а г представлены в табл. 14.8.
3 0 4 Глава 14 • Компоненты категории Win32 Таблица 14.8. Свойства класса ТТгаскВаг Свойство
Описание
property Frequency: Integer;
Определяет частоту нанесения меток: 1 — каждое значение диапазона изменения имеет метку; 2 — каждое 2-е значение имеет метку и т. д. Определяет минимальное смещение ползунка при нажатии клавиш перемещения курсора или перетаскивании мышью Определяет максимальное значение диапазона изменения
property LineSize: Integer; property Max: Integer; property Min: Integer;
Определяет минимальное значение диапазона изменения
property Orientation: TTrackBarOrientation;
Определяет ориентацию компонента: t r H o r i z o n t a l — горизонтальная; t r V e r t i c a l — вертикальная Определяет смещение ползунка при нажатии клавиш Page Up, Page Down или при щелчке мышью на концах шкалы Определяет текущее положение ползунка Задает конечную позицию выделения Задает начальную позицию выделения Определяет способ нанесения меток: t m B o t t o m R i g h t — внизу или справа; t m T o p L e f t — вверху или слева; t m B o t t o m — по обеим сторонам
property PageSize : Integerproperty Position : Integer; property SelEnd: Integer; property SelStart: Integer; TTickMark= (tmBottomRight, tmTopLeft, tmBoth); property TickMarks: TTickMark; TTickStyle = (tsNone, tsAuto, tsManual) ; property TickStyle: TTickStyle;
Определяет стиль нанесения меток: t s N o n e — нет меток; t s A u t o — метки наносятся с частотой F r e q u e n c e ; t s M a n u a l — наносятся начальная и конечная метки, остальные наносит программа с помощью метода S e t T i c k
С помощью следующего метода метка устанавливается в позицию, определяе мую значением V a l u e : procedure SetTick(Value: I n t e g e r ) ;
TProgressBar— индикатор процесса Компонент T P r o g r e s j B a r предназначен для отображения хода выполнения дли тельного по времени процесса. Он имеет много общего с компонентами T S r o l l B a r и ТТгаскВаг, но в отличие от них у него нет регулятора, то есть с его помощью можно только отображать числовую величину, но пользователь не может ее изме нять. В табл. 14.9 представлены свойства, а в табл. 14.10 — методы класса T P r o g r e s s B a r . Таблица 14.9. Свойства класса TProgressBar Свойство
Описание
p r o p e r t y Max: I n t e g e r ;
Определяет максимальное значение диапазона изменения свойства P o s i t i o n
TUpDown — счетчик
305
Свойство
Описание
property Min: I n t e g e r ;
Определяет минимальное значение диапазона изменения свойства P o s i t i o n
property P o s i t i o n : Integer; property Step: I n t e g e r ;
Содержит текущее значение отображаемой величины Шаг наращивания свойства P o s i t i o n методом S t e p l t
Таблица 1 4 . 1 0 . Методы класса TProgressBar Метод
Описание
procedure StepBy ( D e l t a : Integer); procedure Steplt;
Наращивает значение свойства P o s i t i o n на величину Delta Наращивает значение свойства P o s i t i o n на величину, определяемую свойством S t e p
TUpDown — счетчик Компонент TUpDown предназначен для пошагового регулирования числовой ве личины. Он имеет пару кнопок, с помощью которых величина наращивается или уменьшается. Обычно компонент TUpDown связан с компонентом класса TEdit (текстовым полем), который отображает регулируемую величину и при необхо димости обеспечивает возможность ее непосредственного редактирования. Свя занный компонент называется компаньоном. Свойства класса TUpDown перечислены в табл. 14.11. Таблица 14.11. Свойства класса TUpDown Свойство
Описание
TUDAlignButton= (udLeft, udRight);property AlignButton: TUDAlignButton; property ArrowKeys : Boolean;
Определяет положение счетчика относительно компаньона: u d L e f t — счетчик располагается слева от него; u d R i g h t — справа от него Разрешает/запрещает интерпретировать нажатие клавиш Т и 4 как щелчки, соответственно, на направленной вверх и направленной вниз кнопках счетчика. Игнорируется, если компонент не связан свойством A s s o c i a t e с компаньоном
property Associate : TWinControl;
Определяет связанный компонент (компаньон). Кнопки автоматически располагаются с нужной стороны компаньона и выравнивают свою высоту по его высоте property Increment: Определяет шаг наращивания/уменьшения регулируемой величины Smalllnt; property Max: Smalllnt; Определяет максимальное значение диапазона изменения регулируемой величины property Min: Smalllnt; Определяет минимальное значение диапазона изменения регулируемой величины TUDOrientation = ( Определяет ориентацию компонента: u d H o r i z o n t a l — udHorizontal, udVertical) по горизонтали; u d V e r t i c a l — по вертикали
property Orientation: TUDOrientation;
продолжение #
306
Глава 14 • Компоненты категории Win32
Таблица 1 4 . 1 1 (продолжение)
Свойство
Свойство
Описание
property Position : Smalllnt; p r o p e r t y Thousands : Boolean;
Содержит текущее значение регулируемой величины
property Wrap : Boolean;
TAnimate—анимация
Если содержит значение T r u e , при отображении числовой величины в компаньоне втекст вставляются разделители тысяч Запрещает/разрешает выход значения Position из диапазона Max...Min (True — запрещает)
Для компонента определены два события. Первое событие: type TUDChangingEvent = procedure (Sender: TObject; var AllowChange: Boolean) of object; property QnChanging: TUDChanginEvent; Это событие возникает при любом изменении регулируемой величины. Обра ботчик события в параметре AllowChange сообщает, может ли величина изме ниться. Второе событие: type TUDBtnType = (btNext,, btPrev); type TUDClickEvent = procedure (Sender: TObject; Button: TUDBtnType); property OnClick: TUDClickEvent;
Это событие возникает при щелчке на кнопках элемента. Параметр Button опре деляет нажатую кнопку: btPrev — вниз или влево; btNext — вверх или вправо.
Описание
TCommonAVT = ( a v i N o n e , Задает один из стандартных видеоклипов, входящих aviFindFolder, aviFindFile, в библиотеку SHELL32.DLL (рис. 14,1) aviFindComputer, aviCopyFiles, aviCopyFile, aviRecycleFile, aviEmptyRecycle, a v i D e l e t e F i l e ) p r o p e r t y CommonAVT : TCommonAVI ; property FileName: TFileName; Связывает компонент cAVI-файлом property FrameCount: I n t e g e r ; Содержит количество кадров, показанных с начала демонстрации клипа property FrameHeight: I n t e g e r ; Высота в пикселах одного кадра клипа property FrameWidth: I n t e g e r ;
Ширина кадра
p r o p e r t y Open : B o o l e a n -
Содержит значение T r u e , если компонент связан с AVI-клипом и готов к работе
property R e p e t i t i o n s : I n t e g e r ;
Определяет количество повторений клипа. Если О, клип повторяется до тех пор, пока свойство A c t i v e содержит значение T r u e
property ResHandle: THandle;
Определяет дескриптор ресурсного файла, содержащего AVI-клип
property ResHandle : THandle; p r o p e r t y ResName: S t r i n g ; property S t a r t F r a m e : S m a l l l n t ;
TAnimate— анимация
307
propertyStopFrame:
Smalllnt;
Определяет идентификатор ресурса с клипом в ресурсном файле Определяет имя ресурса с клипом в ресурсном файле Содержит номер начального кадра демонстрации (нумерация кадров начинается с 1) Содержит номер конечного кадра демонстрации (нумерация кадров начинается с 1) Разрешает/запрещает синхронизацию по таймеру. Если содержит значение T r u e , демонстрация синхронизируется сигналами таймера. Если содержит значение F a l s e , для демонстрации используется независимый поток команд
Компонент TAnimate представляет собой проигрыватель видеоклипов формата AVI (Audio Video Interleaved — чередование аудио и видео). Компонент воспро изводит визуальную часть AVI-файла и игнорирует его звуковое сопровождение. Он способен показывать лишь несжатое изображение или изображение, сжатое по методу RLE (Run-Length Encoding). Изображение воспроизводится в отдель ном потоке команд, что освобождает ресурсы программы для выполнения необ ходимой работы на фоне воспроизведения клипа. Свойства класса TAnimate представлены в табл. 14.12.
Таблица 1 4 . 1 3 . Методы класса TAnimate
Таблица 1 4 . 1 2 . Свойства класса TAnimate
Метод
Описание
Свойство
Описание
p r o p e r t y A c t i v e : Boolean;
Разрешает/запрещает демонстрацию клипа.
procedure Play (FromFrame, ToFrame: Word; Count: Integer)
Демонстрирует C o u n t раз подряд фрагмент клипа, с кадра FromFrame no ToFrame включительно (нумерация кадров начинается с 1) Восстанавливает исходное состояние компонента. Свойство Open вновь становится равным значению T r u e , но свойство A c t i v e = F a l s e Пропускает (не показывает) кадр с номером Frame (нумерация кадров начинается с 1) Прекращает показ клипа
property AutoSize: Boolean;
p r o p e r t y Center: Boolean;
Во время демонстрации содержит значение T r u e Если содержит значение T r u e , компонент автоматически устанавливает свои размеры так, чтобы полностью вместить изображение кадра Центрирует изображение кадра в границах компонента по горизонтали и по вертикали
property Timers : Booleanproperty T r a n s p a r e n t : Boolean;
Если содержит значение T r u e , фон клипа не накладывается на фон компонента
Методы класса TAnimate перечислены в табл. 14.13.
procedure Reset;
procedure Seek (Frame: Smalllnt) procedure Stop;
308
Глава 14 • Компоненты категории Win32
TDateTimePicker— ввод и отображение даты/времени
309
та. Календарь закрывается после выбора даты или при щелчке на кнопке рас крытия. Установив в свойство Kind значение d t k T i m e , можно заставить компо нент отображать время. Это время в момент установки компонента на форму соответствует системному времени, и в дальнейшем его можно изменить, задав новое значение свойству Time.
Рис. 14.2. Примеры использования компонента TDateTimePicker Свойства класса T D a t e T i m e P i c k e r перечислены в табл. 14.15. Таблица 1 4 . 1 5 . Свойства класса TDateTimePicker Рис. 14.1. Стандартные видеоклипы, определяемые свойством CommonAVI События класса TAnimate перечислены в табл. 14.14.
Свойство
Описание
TDTCalAlignment= ( d t a L e f t , d t a R i g h t ) ; property C a l A l i g n m e n t : TDTCalAlignment;
Определяет положение раскрывающегося календаря: d t a L e f t — слева от компонента; d t a R i g h t — справа от компонента. Учитывается только для K i n d = d t k D a t e И DateMode=dmComboBox
property CalColors : TDateTimeColors; property Checked: Boolean;
Объект класса T D a t e T i m e C o l o r s , свойства которого определяют цвета календаря Если выполняются условия C h e c k e d = T r u e и ShowCheckBox=True, флажок рядом с полем даты (времени) будет показан и установлен. Наличие флажка или его отсутствие разрешает или запрещает ручной ввод даты или времени Содержит введенную дату Определяет формат показа даты: df S h o r t — 14.09.99; d f L o n g — 15 Сентября 1999 г.
Таблица 1 4 . 1 4 . События класса TAnimate Событие
Описание
p r o p e r t y OnClose: T N o t i f yEvent;
Возникает при установке значения F a l s e в свойство Open. Например, когда компонент демонстрирует несколько видеоклипов подряд Возникает при установке значения T r u e в свойство Open Возникает в момент начала демонстрации Возникает в момент прекращения демонстрации
p r o p e r t y OnOpen : T N o t i f y E v e n t ; property O n S t a r t : TNotif yEvent property OnStop: TNotif yEvent;
TDateTimePicker— ввод и отображение даты/времени Компонент T D a t e T i m e P i c k e r предназначен для ввода и/или отображения даты и времени. На рис. 14.2 показаны варианты использования компонента. При показе даты в режиме DateMode=dmComboBox календарь можно не рас крывать и установить дату вручную в верхнем окне. Это удобно, если нужно ус тановить сразу и день, и месяц, и год. На раскрытом календаре изменить месяц можно с помощью небольших кнопок, размещенных в верхней части компонен-
property Date: TDate; TDTDateFormat = ( d f S h o r t , dfLong) ; p r o p e r t y D a t e F o r m a t : TDTDateFormat; TDTDateMode= (dmComboBox, dmUpDown) ; p r o p e r t y DateMode : TDTDateMode; TDateTimeKind= ( d t k D a t e , dtkTime) ; property Kind: TDateTimeKind; p r o p e r t y MaxDate: T D a t e ; property MinDate: TDate;
Определяет способ выбора даты: dmComboBox — с помощью раскрывающегося календаря; dmUpDown — с помощью встроенного компонента TUpDown Определяет содержимое компонента: d t k D a t e — дата; d t k T i m e — время Определяет максимальную дату, которую может выбрать или ввести пользователь Определяет минимальную дату, которую может выбрать или ввести пользователь продолжение &
310
Глава 14 • Компоненты категории Win32
Таблица
14.15
(продолжение)
Свойство
Описание
property P a r s e l n p u t : Boolean;
Если содержит значение T r u e , возникает событие OnUser I n p u t при каждом вводе в текстовое поле Если содержит значение T r u e , рядом с полем даты (времени) вставляется флажок, с помощью которого пользователь может запретить ручное изменение даты (времени)
propertyShowCheckbox:
Boolean;
p r o p e r t y Time : TTime;
Таблица 14.16. События класса TDateTimePicker
Возникает при любом изменении содержимого компонента property OnCloseUp: TNotifyEvent; Возникает при закрытии календаря property OnDropDown: TNotifyEvent; Возникает при открытии календаря Возникает при ручном вводе в текстовое поле, DTParselnputEvent = procedure (Sender: TObject; constUserString: если P a r s e l n p u t = T r u e . String; var DateAndTime: TDateTime; U s e r S t r i n g — введенная var AllowChange: Boolean) of object; пользователем строка; DateAndTime — значения свойств Date и Time. В параметре property OnUserlnput: A l l o w C h a n g e обработчик разрешает или TDTParselnputEvent; запрещает изменение даты или времени
TMonthCalendar— календарь Компонент предназначен для выбора или отображения даты. Он очень похож на календарь, который появляется в компоненте T D a t e T i m e P i c k e r (при значении dmComboBox в свойстве DateMode), но в отличие от последнего может отображать одновременно несколько смежных месяцев — в зависимости от своих размеров. Свойства класса T M o n t h C a l e n d a r представлены в табл. 14.17. Т а б л и ц а 1 4 . 1 7 . Свойства класса TMonthCalendar Свойство
Описание
С компонентом связан объект класса T M o n t h C a l C o l o r s (см. далее) Определяет выбранную дату property Date: TDate; Определяет конечную дату диапазона property EndDate: TDate; выбранных дат t y p e TCalDayOfWeek = (dowMonday, Определяет первый день недели dowTuesday, dowWednesday, dowThursday, dowFriday, d o w S a t u r d a y , dowSunday, dowLocaleDefault);property FirstDayOfWeek: TCalDayOfWeek;
property C a l C o l o r s : TMonthCalColors;
Описание
p r o p e r t y MaxDate : T D a t e ;
Содержит максимальную дату, месяц которой еще будет доступен для отображения в компоненте. Если свойство содержит пустое значение, отображается любой следующий месяц Содержит максимальное количество дат в выбранном диапазоне
property MaxSelectRange: Integer;
property MinDate: TDate;
property MultiSelect: Boolean, property ShowToday: Boolean;
Описание
property OnChange : TNotifyEvent;
311
Свойство
Содержит введенное пользователем время
События класса TDateTimePicker представлены в табл. 14.16.
Событие
TTreeView—иерархическое дерево
property ShowTodayCircle: Boolean; property WeekNumbers : Boolean;
Содержит минимальную дату, месяц которой еще будет доступен для отображения в компоненте. Если свойство содержит пустое значение, отображается любой предыдущий месяц Разрешает/запрещает возможность выделения диапазона дат Разрешает/запрещает показ текущей даты (по показаниям системных часов) внизу календаря Разрешает/запрещает обводить кружком текущую дату Разрешает/запрещает показ порядковых номеров недель от начала года в левой колонке
Свойства объекта класса T M o n t h C a l C o l o r s : •
B a c k C o l o r — цвет фона, разделяющего смежные месяцы;
• M o n t h B a c k C o l o r — цвет фона дат; •
T e x t C o l o r — цвет дат;
•
T i t l e B a c k C o l o r — цвет фона заголовка месяца: о о
T i t l e T e x t C o l o r — цвет текста заголовка; T r a i l i n g T e x t C o l o r — цвет текста ведущих и ведомых дат.
TTreeView — иерархическое дерево Компонент TTreeView служит для показа ветвящихся иерархических структур, таких как дерево наследования объектов или файловая структура диска. Он со держит связанные узлы, каждый из которых может содержать значок, текст и про извольный объект. Любой узел может иметь собственный список дочерних уз лов, которые можно раскрывать или закрывать щелчком мыши на значке узла (рис. 14.3). Для наполнения списка на этапе разработки программы нужно щелкнуть на ком поненте правой кнопкой мыши и выбрать в контекстном меню команду Items Editor, либо щелкнуть на нем дважды, либо, наконец, щелкнуть на кнопке с мно готочием в строке свойства I t e m s — во всех случаях на экране появится окно редактора компонента (рис. 14.4). Чтобы начать заполнение дерева, щелкните на кнопке New Item и введите свя занный с узлом текст в поле Text. В поле Image Index раздела Item Properties вво дится индекс связанного с узлом значка, в поле Selected Index — индекс значка для выбранного узла. Для ввода дочернего узла любого уровня сначала нужно
3 1 2 Глава 14 • Компоненты категории Win32
выделить (щелчком) в поле Items узел, который должен стать родительским, и лишь затем щелкнуть на кнопке New Subltem.
TTreeView—иерархическое дерево
313
Для заполнения дерева в режиме прогона программы широко используется цен тральное свойство компонента — I t e m s типа TTreeNodes, открывающее индек сированный доступ ко всем узлам списка. Каждый узел описывается классом TTreeNode, имеющим собственные методы и свойства. В частности, его свой ство I t e m содержит список всех дочерних узлов данного узла; с помощью много численных методов свойства TTreeView. I t e m s к этому списку можно добавить новый дочерний узел, ас помощью метода TTreeNode .MoveTo — переместить узел в любую позицию иерархического дерева. Процесс создания дочерних узлов на этапе прогона программы иллюстрирует рис. 14.5. Показанную на рисунке конструкцию создал следующий обработчик события O n C r e a t e формы:
Рис. 14.3. Пример использования компонента TTreeView
Рис. 14.4. Редактор компонента TTreeView
procedure TForml.FormCreate(Sender: TObject); var k: Integer; begin with TreeViewl do begin Items.Cliear; // Добавляем корневой узел Items.Add(NIL,'Корень'); // Добавляем 10 дочерних узлов for k := 1 t o 10 do if Odd(k) then I t e m s . A d d C h i l d d t e m s [k-1] , I n t T o S t r (k) ) Else Items.Add(Items[k-1], IntToStr(k); // Раскрываем все узлы FullExpand end end; Свойства класса TTreeView представлены в табл. 14.18. Таблица 1 4 . 1 8 . Свойства класса TTreeView Свойство
TBorderStyle = bsNone . . b s S i n g l e ; property BorderStyle : TBorderStyle; property DropTarget: TTreeNode;
_^
Описание
Определяет стиль рамки, охватывающей компонент: bsNone — нет рамки; b s S i n g l e — рамка толщиной в 1 пиксел Определяет узел, который может служить приемником для операций перетаскивания (Drag&Drop)
property H i d e S e l e c t i o n : Boolean; Указывает, будет ли убираться выделение узлов, p r o p e r t y images : T I m a g e L i s t ; Рис. 14.5. Создание дочерних узлов на этапе прогона программы
когда компонент теряет фокус ввода Содержит набор изображений, которые будут использоваться при отрисовке узлов продолжение &
314
Глава 14 • Компоненты категории Win32
Таблица 14.18 (продолжение) Свойство
Описание
property Indent: Integer;
Определяет отступ в пикселах от левого угла узла для всех его дочерних узлов Открывает доступ к любому узлу по его индексу. Индексация начинается с нуля и соответствует просмотру всех узлов полностью развернутого дерева Запрещает/разрешает редактирование надписей в узлах
property Items: TTreeNodes;
property Readonly: Booleanproperty Selected: TTreeNode; property ShowButtons : Boolean;
Содержит список всех выбранных узлов или N I L , если таких нет Разрешает/запрещает показ стандартных кнопок раскрытия дочерних узлов. По умолчанию содержит значение T r u e . Если содержит значение F a l s e , узел раскрывается двойным щелчком мыши
property ShowLines: Boolean; property ShowRoot: Boolean;
Разрешает/запрещает показ линий Разрешает/запрещает показ линий, идущих от самого верхнего уровня иерархии. Игнорируется, если S h o w L i n e s = F a l s e
TSortType = (stNone, stData, stText, stBoth); property SortType: TSortType;
Определяет способ сортировки узлов: s t N o n e — нет сортировки; stData—сортировка по данным; s t T e x t — сортировка по тексту надписей; s t B o t h — сортировка по тексту и по данным (см. также описание события OnCompare далее)
Методы класса TTreeView перечислены в табл. 14.19.
Таблица 14.19. Методы класса TTreeView Метод
Описание
function A l p h a S o r t : Boolean;
Сортирует узлы по тексту и возвращает значение T r u e , если сортировка прошла успешно Определяет нестандартную сортировку с помощью функции S o r t P r o c . Эта функция должна рассматривать параметры l P a r a m l и 1 Р а г а т 2 как объекты TTreeNode и возвращает отрицательное число, если l P a r a m l < 1 Р а г а т 2 ; ноль, если l P a r a m e l = 1 Р а г а т 2 ; положительное число, если l P a r a m l > 1 Р а г а т 2
TTVCompare = f u n c t i o n ( l P a r a m l , lParam2, l P a r a m S o r t : L o n g l n t ) : I n t e g e r s t d c a l l ; tfunction CustomSort(SortProc: TTVCompare; Data: Longlnt) : Boolean; procedure F u l l C o l l a p s e ; Procedure FullExpand; function GetNodeAt(X, Y: I n t e g e r ) : TTreeNode; function I s E d i t i n g : Boolean; procedure LoadFromFile (const FileName: S t r i n g ) ;
Прячет все узлы, кроме узлов самого верхнего уровня иерархии Показывает все узлы дерева иерархии Возвращает узел, располагающийся в указанной точке, или N I L , если точка не принадлежит ни одному узлу Возвращает значение T r u e , если пользователь редактирует какой-либо узел Загружает иерархическое дерево из файла
TTreeView — иерархическое дерево Метод
Описание
procedure SaveToFile (const FileName: String) ; procedure SaveToStream (Stream: TStream);
Сохраняет иерархическое дерево в файле
315
Сохраняет иерархическое дерево в потоке данных
События класса TTreeView перечислены в табл. 14.20. Таблица 14.20. События класса TTreeView Событие
Описание
TTVChangedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnChange: TTVChangedEvent; TTVChangingEvent =procedure(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean) of object; property OnChanging: TTVChangingEvent;
Возникает при смене состояния выбора у одного из узлов. Node — узел, который изменил состояние
TTVExpandedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnCollapsed: TTVExpandedEvent; TTVCollapsingEvent = procedure(Sender: TObject; Node: TTreeNode; var AllowCollapse: Boolean) of object; property OnCollapsing: TTVCollapsingEvent;
Возникает при свертывании ветви дочерних узлов узла Node
TTVCompareEvent =procedure (Sender: TObject; Nodel, Mode2: TTreeNode; Data: Integer; var Compare : Integer) of object; property OnCompare: TTVCompareEvent ;
Возникает при сравнении двух узлов N o d e l nNode2. В параметре Compare обработчик должен вернуть отрицательное число, если N o d e l < Node2; ноль, если N o d e l « Node2; положительное число, если N o d e l > Node2
TTVExpandedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnDeletion: TTVExpandedEvent; TTVEditedEvent =procedure(Sender: TObject; Node: TTreeNode; var S: String) of object; property OnEdited: TTVEditedEvent;
Возникает при удалении узла Node из иерархического дерева
TTVExpandedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnExpanded: TTVExpandedEvent; TTVExpandingEvent = procedure(Sender: iObject; Node: TTreeNode; var AllowExpansion: Boolean) of object; Property OnExpanding: TTVExpandingEvent TTVExpandedEvent= procedure(Sender: iObiect; Node: TTreeNode) of object; Property OnGetlmagelndex: TTVExpandedEvent;
Возникает перед сменой состояния выбора у одного из узлов. Node — новый выбираемый узел. Обработчик в параметре A l l o w C h a n g e разрешает или запрещает возможность выделения (выбора) узла
Возникает перед свертыванием ветви дочерних узлов узла Node. В параметре A l l o w C o l l a p s e обработчик разрешает или запрещает свертывание
Возникает при завершении редактирования надписи в узле Node: S — новая надпись Возникает при развертывании ветви дочерних узлов узла Node Возникает перед развертыванием ветви дочерних узлов узла Node. В параметре A l l o w E x p a n s i o n обработчик разрешает или запрещает развертывание Возникает при необходимости получения индекса изображения для отрисовки узла Node в обычном состоянии продолжение &
316
Глава 14 • Компоненты категории Win32
Таблица
14.20
TTreeView— иерархическое дерево
(продолжение)
Событие
Описание
TVExpandedEvent = procedure(Sender: TObject; Node: TTreeNode) of object; property OnGetSelectedlndex: TTVExpandedEvent ;
Возникает при необходимости получения индекса изображения для отрисовки узла Node в выбранном состоянии
При программном заполнении дерева следует пользоваться свойством TTreeView. I t e m s класса T T r e e N o d e s . Свойства и методы этого класса представлены в табл. 14.21 и 14.22. Т а б л и ц а 1 4 . 2 1 . Свойства класса TTreeNodes Свойство
Описание
Количество узлов, входящих в I t e m s property Count: Integer; Открывает индексированный доступ к узлам property Item [Index : Integer] : TTreeNode; default; property Owner: TCustomTreeView; Содержит ссылку на родительское дерево
Таблица 14.22. Методы класса TTreeNodes Метод
Описание
function Add (Node: TTreeNode; const S: String) : TTreeNode;
Добавляет узел в конец того дерева, в котором зарегистрирован узел Node. Если Node = N I L , добавляется корневой узел для всего компонента Добавляет узел в конец ветви I t e m дочерних узлов узла Node
function AddChild (Node: TTreeNode; constS : String) : TTreeNode; function AddChildFirst (Node: TTreeNode; const S : String) : TTreeNode; function AddChildObj ect (Node : TTreeNode; constS : String; Ptr: Pointer): TTreeNode; function AddChildObjectFirst ( Node: TTreeNode; constS: String; Ptr: Pointer): TTreeNode; function AddFirst (Node: TTreeNode; const S: String) : TTreeNode; function AddOb j ect (Node: TTreeNode; const S: String; Ptr: Pointer) : TTreeNode;
Добавляет узел в начало ветви I t e m дочерних узлов узла Node Добавляет узел и данные в конец ветви I t e m дочерних узлов узла Node. На данные ссылается указатель P t r . Связанная сданными область памяти не освобождается автоматически при удалении дерева Добавляет узел и данные в начало ветви I t e m дочерних узлов узла Node. На данные ссылается указатель P t r . Связанная с данными область памяти не освобождается автоматически при удалении дерева Добавляет узел в начало той ветви, в которой зарегистрирован узел Node
Добавляет узел и данные в конец той же ветви, в которой зарегистрирован узел Node. На данные ссылается указатель P t r . Связанная с данными область памяти не освобождается автоматически при удалении дерева Добавляет узел и данные в начало той же ветви, function AddOb jectFirst (Node : TTreeNode; const S: String; Ptr: в которой зарегистрирован узел Node. На данные ссылается указатель P t r . Связанная сданными Pointer):TTreeNode; область памяти не освобождается автоматически при удалении дерева
317
Метод
Описание
procedure Assign (Source: TPersistent); procedure BeginUpdate;
Связывает дерево текущего компонента с деревом компонента S o u r c e Блокирует обновление экрана до тех пор, пока не будет выполнен метод E n d U p d a t e . Используется при одновременной вставке нескольких элементов дерева для предотвращения мерцания экрана
procedure Clear;
Очищает дерево от всех узлов и дочерних узлов компонента Удаляет узел Node
procedure Delete (Node: TTreeNode); procedure EndUpdate; function GetFirstNode : TTreeNode; function GetNode (Itemld: HTreeltem) : TTreeNode; function Insert (Node : TTreeNode, const S: String) : TTreeNode; function InsertObj ect(Node: TTreeNode; const S : String; Ptr: Pointer):TTreeNode;
Отменяет действие метода B e g i n U d a t e Возвращает самый первый узел в дереве I t e m s [ 0 ] Возвращает узел по его идентификатору I t e m l d Вставляет узел непосредственно перед узлом Node Вставляет узел и данные непосредственно перед узлом Node
Как уже отмечалось, каждый узел класса TTreeNode имеет свой набор свойств (табл. 14.23) и методов (табл. 14.24). Т а б л и ц а 1 4 . 2 3 . Свойства класса TTreeNode Свойство
Описание
p r o p e r t y A b s o l u t e l n d e x : I n t e g e r ; Возвращает абсолютный индекс узла (с учетом всех дочерних узлов) property Count: I n t e g e r ; Содержит количество дочерних узлов в ветви I t e m property Cut: Boolean; Вырезает узел и помещает его в буфер обмена property Data: P o i n t e r Указывает на связанные с узлом данные Содержит значение T r u e , если для узла вызван property D e l e t i n g : Boolean; метод D e s t r o y property DropTarget: Boolean; Содержит значение T r u e , если узел может служить приемником операции перетаскивания (Drag&Drop) property Expanded: Boolean; Содержит значение T r u e , если узел развернут property Focused: Boolean; Содержит значение T r u e , если узел свернут property HasChildren: Boolean, Содержит значение T r u e , если узел имеет дочерние узлы p r o p e r t y I m a g e l n d e x : T I m a g e l n d e x ; Содержит индекс связанного с узлом значка
property Index: Longint; property I s V i s i b l e : Boolean; property Item [Index: I n t e g e r ] : TTreeNode; property I t e m l d : HTreeltem;
Содержит индекс узла в списке дочерних узлов его родительского узла Содержит значение T r u e , если узел виден Открывает индексированный доступ ко всем дочерним узлам Содержит уникальный Windows-дескриптор узла продолжение &
318
Глава 14 • Компоненты категории Win32
Т а б л и ц а 1 4 . 2 3 (продолжение) Свойство
Описание
property L e v e l : I n t e g e r ; property O v e r l a y I n d e x : I n t e g e r ;
Содержит иерархический уровень узла Содержит индекс оверлейного значка. Оверлейный значок вычерчивается поверх основного, чтобы, например, указать, что узел стал недоступен Содержит ссылку на владельца данного узла Содержит ссылку на родительский узел Содержит значение T r u e , если узел выделен цветом
property Owner: TTreeNodes; property P a r e n t : TTreeNode; property S e l e c t e d : Boolean; property S e l e c t e d l n d e x : I n t e g e r , property T e x t : s t r i n g ; property TreeView: TCustomTreeView;
Содержит номер значка для выделенного узла Содержит текст узла Содержит ссылку на компонент T r e e V i e w , к которому принадлежит узел
TListView — иерархический список Метод
Описание
function G e t N e x t V i s i b l e : TTreeNode; function GetPrev: TTreeNode;
Возвращает ссылку на очередной видимый узел (для которого развернуты все дочерние узлы) Возвращает ссылку на предыдущий узел в том же дереве независимо от его видимости
function G e t P r e v C h i l d (Value: TTreeNode) : TTreeNode; function G e t P r e v S i b l i n g : TTreeNode; function GetPrevVisible : TTreeNode; function HasAsParent (Value: TTreeNode): Boolean; function IndexOf (Value : TTreeNode): Integer;
Возвращает ссылку на предыдущий по отношению к узлу V a l u e дочерний узел
Описание
function A l p h a S o r t : Boolean;
Сортирует узлы по алфавиту свойства T e x t и возвращает значение T r u e в случае успеха
procedure A s s i g n ( S o u r c e : T P e r s i s t e n t ) ; override; procedure C o l l a p s e ( R e c u r s e : Boolean);
Связывает список дочерних узлов с источником Source Закрывает все узлы ( R e c o u r c e = T r u e ) или только развернутые ( R e s o u r c e = F a l s e ) Реализует нестандартную сортировку узлов
type TTVCompare = function (lParaml, !Param2, l P a r a m S o r t : Longint) : I n t e g e r s t d c a l l ; function CustomSort ( S o r t P r o c : TTVCompare; Data: L o n g i n t ) : Boolean; procedure D e l e t e ; procedure DeleteChildren; function D i s p l a y R e c t (TextOnly: Boolean) : TRect; function E d i t T e x t : Boolean; procedure EndEdit (Cancel Boolean); procedure Expand (Recurse: Boolean); function G e t F i r s t C h i l d : TTreeNode; function G e t L a s t C h i l d : TTreeNode; function GetNext: TTreeNode; function GetNextChild (Value : TTreeNode) : TTreeNode; function G e t N e x t S i b l i n g : TTreeNode;
Возвращает ссылку на предыдущий узел того же уровня Возвращает ссылку на видимый узел того же уровня Возвращает значение T r u e , если V a l u e — родительский узел Возвращает идентификатор узла V a l u e
procedure MakeVisible;
Если родительский узел видимый, делает видимыми все дочерние узлы
type TNodeAttachMode = (naAdd, naAddFirst, naAddChild, naAddChildFirst, nalnsert) ; procedure MoveTo (Destination: TTreeNode; Mode: TNodeAttachMode);
Перемещает текущий узел в позицию относительно узла D e s t i n a t i o n в зависимости от параметра Mode: naAdd — добавляет в конец списка узлов того же уровня; n a A d d F i r s t — делает первым в ветви узлов того же уровня; n a A d d C h i l d — добавляет в конец списка дочерних узлов; n a A d d C h i l d F i r s t — делает первым в списке дочерних узлов; n a l n s e r t — вставляет непосредственно перед узлом
Таблица 1 4 . 2 4 . Методы класса TTreeNode Метод
319
TListView — иерархический список Удаляет текущий узел Удаляет дочерние узлы Возвращает очерчивающий прямоугольник узла. Если T e x t O n l y = T r u e , возвращает очерчивающий прямоугольник текста Переводит текст узла в режим редактирования Заканчивает редактирование текста и сохраняет его изменения, если C a n c e l = F a l s e Развертывает узел (и его дочерние узлы, если R e c u r c e = T r u e ) Возвращает ссылку на первый дочерний узел или N I L , если нет дочерних узлов Возвращает ссылку на последний дочерний узел или NIL, если нет дочерних узлов Возвращает ссылку на очередной дочерний узел Возвращает ссылку на дочерний узел, следующий за узлом V a l u e (или N I L , если такового нет) Возвращает ссылку на очередной узел в том же дереве
Компонент TListView предназначен для отображения и выбора нескольких эле ментов. Каждый элемент может содержать значок и текст и подобно компоненту TTreeView иметь ветвь связанных с ним вложенных (дочерних) элементов. В от личие от компонентов TTreeView, BTListView допускается не более одного уровня вложенности элементов. Компонент TListView показывает свои элемен ты в одной или нескольких колонках с крупными или мелкими значками. Рядом с элементами могут присутствовать флажки, упрощающие множественный вы бор элементов. Компонент может наполняться как на этапе конструирования, так и на этапе про гона программы. Выполните следующую последовательность шагов, которая продемонстрирует вам основные особенности использования компонента: 1. На пустую форму поместите компоненты TListView, TImageList, TComЬоВох и TCheckBox. 2. Компонент I m a g e L i s t l будет служить контейнером для нескольких знач ков, отображаемых компонентом Listview. Для наполнения контейнера дваж ды щелкните на нем мышью или щелкните на нем правой кнопкой мыши и выберите в контекстном меню команду ImageList Editor.
320
Глава 14 • Компоненты категории Win32
3. В появившемся окне редактора изображений щелкните на кнопке Add и выбе рите любой графический ВМР-файл. 4. Повторите предыдущий шаг для выбора нескольких значков. Обратите вни мание на то, что каждому помещаемому в компонент I m a g e L i s t l значку ре дактор присваивает уникальный индекс. 5. Напишите обработчики событий, представленные в листинге 14.1. Листинг 14.1. Обработчики событий списка procedure TForml.FormCreate(Sender: TObject); var k: I n t e g e r ; Listltem: TListltem; begin with ListViewl do begin // Указываем источник изображений: Smalllmages : = I m a g e L i s t l ; Largelmages := I m a g e L i s t l ; // Наполняем ListViewl значками и текстом: for k := 0 to ImageListl.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('vsList', TObject(vsList)); ComboBoxl.Items.AddObject('vsReport', TObject(vsReport)); ComboBoxl.Items.AddObject('vsSmalllcon' , TObject(vsSmalllcon)); // Показываем первый стиль в Combo Box ComboBoxl.Itemlndex := 0; // Создаем колонку для режима vsReport: Columns.Add; Columns[0].Caption := 'Колонка 1'; Columns[0].Width := 80; end; end; procedure TForml.ComboBoxlChange(Sender: TObject); begin with ComboBoxl do
TListView — иерархический список
321
ListViewl.ViewStyle := TViewStyle(Items[Itemlndex]); end; procedure TForml.CheckBoxlClick (Sender: TObject); begin ListViewl.CheckBoxes := CheckBoxl.Checked end; Некоторые важные свойства класса TListView представлены в табл. 14.25. Таблица 14.25. Свойства класса TListView Свойство property AllocBy: Integer-
Описание
Используется перед вставкой большого количества элементов: для экономии времени и памяти перед добавлением элементов установите в это свойство их количество Разрешает/запрещает показ флажков рядом property Checkboxes : Boolean; с элементами Открывает доступ к колонкам элементов по их property Column [Index': индексам Integer]: TListColumn; Разрешает/запрещает генерацию события property ColumnClick: Boolean; OnColumnClick property Columns: TListColumns; Содержит объекты-колонки. Используйте это свойство для удаления или добавления колонок, а также для изменения их свойств. Колонки видны только в режиме V i e w S t y l e = v s R e p o r t . И наоборот, элементы в этом режиме видны, только если определена хотя бы одна колонка Разрешает/запрещает показ линий в режиме property GridLines : Boolean; ViewStyle = vsReport property HideSelection: Запрещает/разрешает сохранять элементы Boolean; выделенными при потере компонентом фокуса ввода property HotTrack: Boolean; Разрешает/запрещает подсветку элементов, на которых проходит указатель мыши property IconOptions: С помощью объекта класса T I c o n O p t i o n s TIconOptions; задаются дополнительные условия отображения (см. далее) property Items: TListltems; Содержит список всех элементов property Largelmages: TImageList, Указывает источник крупных значков Разрешает/запрещает множественный выбор property MultiSelect: Boolean; Запрещает/разрешает редактирование надписей property Readonly: Boolean; Разрешает/запрещает показ заголовков колонок property ShowColumnHeaders : Boolean; в режимеViewStyle = v s R e p o r t property Smalllmages: TImageList, Определяет источник мелких значков TSortType = (stNone, stData, Определяет способ сортировки элементов stText, stBoth);property SortType: TSortType; property Statelmages : Определяет источник значков для выбранных TImageList; элементов продолжение & П Зак. 126
322
THeaderControl — управляющий заголовок
Глава 14 • Компоненты категории Win32
Таблица 1 4 . 2 5 (продолжение) Свойство
Описание
TViewStyle = (vslcon, Определяет стиль показа элементов: vslcon — vsSmalllcon, v s L i s t , vsReport) ; крупные значки; vsSmalllcon — мелкие значки; property ViewStyle : TViewStyle; v s L i s t — список значков; vsReport — таблица значков
ВНИМАНИЕ
Чтобы компонент работал в режиме v s R e p o r t , необходимо создать хотя бы одну колонку. На этапе конструирования программы колонки создаются и изменяются с помощью редактора ко лонок, окно которого открывается после щелчка на компоненте правой кнопкой мыши и выбо ре в контекстном меню команды Column Editor. Пример программного создания колонки был показан в листинге 14.1.
Класс T I c o n O p t i o n определяет дополнительные условия отображения узлов; свойства этого класса представлены в табл. 14.26. Таблица 1 4 . 2 6 . Свойства класса TIconOption Свойство
Описание
type TIconArrangement = (iaTop, iaLeft) ; property Arrangement: TIconArrangement; property AutoArrange: Boolean;
Определяет способ расположения изображений: i a T o p — слева направо (вверху колонки); i a L e f t — сверху вниз слева от ряда колонок
property WrapText: Boolean;
Если содержит значение T r u e , текст элемента может переноситься по словам на несколько строк, если он выходит за границы значка
Если содержит значение T r u e , элементы будут автоматически располагаться правильными рядами при изменении их количества
При программном заполнении компонента центральную роль играет свойство I t e m s класса T L i s t l t e m s (табл. 14.27). Таблица 1 4 . 2 7 . Свойства класса TListltems Свойство
Описание
property Count: I n t e g e r ; property Item [Index: I n t e g e r ] : TListltem;
Содержит количество элементов в списке I t e m Открывает индексный доступ к элементам списка
Метод
Описание
procedure C l e a r ; procedure Delete (Index: I n t e g e r ) procedure EndUpdate; function IndexOf (Value: TListltem) : I n t e g e r function I n s e r t (Index : I n t e g e r ) : TListltem; procedure SetCount (Value: Integer);
Очищает список
Удаляет элемент списка с индексом Index Отменяет действие BeginUpdate Возвращает индекс узла Value Вставляет новый элемент после элемента, заданного индексом I n d e x Устанавливает новое количество элементов списка
THeaderControl — управляющий заголовок Компонент T H e a d e r C o n t r o l представляет собой многоколончатый заголовок с регулируемыми размерами колонок (секций). Каждая секция заголовка может содержать текст и/или графику. Компонент способен обрабатывать событие OnResize, которое возникает при каждом изменении размеров любой секции. В ходе обработки этого события программа обычно соответствующим образом изменяет линейные размеры столбцов таблицы или подобной структуры, с кото рой связан компонент. В следующей программе, окно которой показано на рис. 14.6, компонент THea d e r C o n t r o l используется для управления положением и линейными размера ми трех других компонентов. Для реализации программы выполните следующую последовательность шагов: 1. Поместите на пустую форму компоненты T H e a d e r C o n t r o l , T E d i t , TMemo и TButton. По умолчанию должно выполняться условие H e a d e r C o n t r o l l . A l i g n = a l T o p — убедитесь в этом и установите свойство Align, если это не так. Положение и размеры других компонентов не имеют значения. 2. Создайте обработчики событий OnShow для формы Forml, а также O n R e s i z e и O n S e c t i o n R e s i z e для компонента H e a d e r C o n t r o l l (листинг 14.2).
Методы класса T L i s t l t e m s представлены в табл. 14.28. Т а б л и ц а 1 4 . 2 8 . Методы класса TListltems Метод
Описание
function Add: T L i s t l t e m ; procedure Assign (Source : T P e r s i s t e n t ) ; override; procedure BeginUpdate;
Добавляет очередной элемент к списку Связывает список компонента со списком
компонента Source Блокирует обновление экрана до тех пор, пока
не будет выполнен метод EndUpdate.
Используется при одновременной вставке нескольких элементов списка для предотвращения мерцания экрана
323
Рис. 14.6. Пример использования компонента THeaderControl для управления положением и размерами других компонентов
324
Глава 14 • Компоненты категории Win32
Листинг 14.2. Обработчики событий для формы и для заголовка const Delta = 10; // Зазор между границами заголовка и компонентами procedure TForml.FormShow(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 TForml.HeaderControllResize(Sender: TObject); // Устанавливает положение и размеры компонентов begin with HeaderControll do begin Editl.Left := Delta; Editl.Top := HeaderControll.Height + 1; Editl.Width := Sections.Items[0].Width - 2 * Delta;
THeaderControl — управляющий заголовок
325
Для заголовка создается объект класса T H e a d e r S e c t i o n s , определяющий сек ции заголовка. Следующее свойство этого объекта открывает доступ к индекси рованным объектам-секциям класса T H e a d e r S e c t i o n : property I t e m s [ I n d e x : I n t e g e r ] : THeaderSection; Свойства класса T H e a d e r S e c t i o n перечислены в табл. 14.29. Таблица 14.29. Свойства класса THeaderSection Свойство
Описание
property Alignment: TAlignment; Определяет выравнивание текста: t a L e f t J u s t i f y — выравнивание влево; t a C e n t e r — текст центрирован по горизонтали; t a R i g h t J u s t i f y — выравнивание вправо Разрешает/запрещает генерацию события property AllowClick: Boolean; O n S e c t i o n C l i c k при щелчке на секции Определяет положение левой границы секции property Left: Integer; относительно границ компонента (в пикселах) Определяет максимальную ширину секции property MaxWidth: Integer; в пикселах property MinWidth: Integer; Определяет минимальную ширину секции в пикселах property Right: Integer; Определяет положение правой границы секции относительно границ компонента (в пикселах) Определяет способ формирования секции: THeaderSectionStyle = (hsText, h s T e x t — секция содержит только текст hsOwnerDraw) ; property Style: и изображается автоматически; hsOwnerDraw — THeaderSectionStyle; секция отрисовывается программой property Width: Integer; Содержит текущую ширину секции в пикселах Следующий метод добавляет очередную секцию заголовка:
Memol.Left := Sections.Items[1].Left + Delta; Memol.Top := HeaderControll.Height + 1; Memol.Width := Sections.Items[1].Width - 2 * Delta;
function THeaderSections.Add: THeaderSection; Некоторые свойства класса THeaderSections представлены в табл. 14.30. Таблица 14.30. Свойства класса THeaderSections
Buttonl.Left := Sections.Items[2].Left + Delta; Buttonl.Top := HeaderControll.Height + 1; Buttonl.Width := Sections.Items[2].Width - 2 * Delta; end end; procedure TForml.HeaderControllSectionResize( HeaderControl: THeaderControl; Section: THeaderSection); begin HeaderControllResize(Self) end; He забудьте определить в области видимости обработчиков F o r m C r e a t e и Hea d e r C o n t r o l l R e s i z e глобальную константу D e l t a .
Свойство
Описание
Если содержит значение T r u e , разрешает щелкнуть на секции заголовка, как на кнопке. В этом случае управление передается обработчику события OnSectionClick property DisplayName: String; Имя заголовка секции property Imagelndex: Определяет индекс изображения из хранилища TImagelndex; Images, которое будет появляться следом за текстом заголовка property MaxWidth: Integer; Определяет максимальную и минимальную ширину property MinWidth: Integer; секции type THeaderSectionStyle = Определяет тип секции: h s T e x t — секция (hsText, hsOwnerDraw);property содержит только текст; hsOwnerDraw — секция нуждается в программной отрисовке Style: THeaderSectionStyle; property Text: String; Содержит текст секции property AllowClick: Boolean;
326
Глава 14 • Компоненты категории Win32
TStatusBar—строка состояния
327
Свойства класса T H e a d e r C o n t r o l представлены в табл. 14.31. Таблица 14.31. Свойства класса THeaderControl Свойство
Описание
property Canvas : TCanvas;
С помощью этого свойства можно изображать графику в секциях заголовка
property HotTrack: Boolean;
Разрешает/запрещает цветовое выделение секции, на которой располагается указатель мыши Содержит секции заголовка
property S e c t i o n s : THeaderSections;
События класса THeaderControl перечислены в табл. 14.32.
Рис. 14.7. Пример компонента TStatusBar с тремя панелями и кнопкой изменения размеров окна
Таблица 14.32. События класса THeaderControl
Событие
Описание
TDrawSectionEvent = procedure (HeaderControl: THeaderControl; Section: THeaderSection; const Rect: TRect; Pressed: Boolean) of object; property OnDrawSection: TDrawSectionEvent; property OnResize: TNotif yEvent;
Возникает при необходимости отрисовать секцию, для которой установлен стиль hsOwnerDraw: S e c t i o n — объектсекция; R e c t — прямоугольник отрисовки; P r e s s e d — признак нажатой кнопки мыши
TSectionNotifyEvent = procedure (HeaderControl: THeaderControl; Section: THeaderSection) of object; property OnSectionClick: TSectionNotifyEvent; TSectionNotifyEvent = procedure (HeaderControl: THeaderControl; Section: THeaderSection) of object; property OnSectionResize: TSectionNotifyEvent; TSectionTrackState = (tsTrackBegin, tsTrackMove, tsTrackEnd); TSectionTrackEvent = procedure (HeaderControl: THeaderControl; Section: THeaderSection; Width: Integer; State: TSectionTrackState) of object; property OnSectionTrack: TSectionTrackEvent;
Возникает при щелчке мышью на секции
Возникает при изменении размеров компонента
Section
Возникает при изменении размеров секции
Section
Возникает при изменении размеров секции и позволяет обработать три возможных состояния; t s T r a c k B e g i n —начало перемещения границы; t s T r a c k M o v e — идет перемещение границы; t s T r a c k E n d — о к о н ч а н и е перемещения границы
TStatusBar — строка состояния Компонент T S t a t u s B a r предназначен для создания строки состояния, которая обычно располагается в нижней части основной формы. Компонент может иметь несколько панелей (секций), а также кнопку изменения размеров окна, в кото рое он помещен (рис. 14.7). Показанный на рисунке компонент T S t a t u s B a r стремя панелями и кнопкой изменения размеров окна создан следующим обработчиком события OnShow фор мы Forml (предварительно на пустую форму поместите компонент T S a t u s B a r ) :
procedure TForml.FormShow(Sender: TObject); var Panel: TStatusPanel; k: I n t e g e r ; begin with S t a t u s B a r l do for k := 0 to 2 do begin Panel := Panels.Add; Panel.Text := Format ('Панель №%d', [k]) ; Panel.Width := Forml.Width div 3 end; end; С компонентом связывается объект класса T S t a t u s P a n e l s , который определя ет панели компонента. Каждая панель относится к классу T S t a t u s P a n e l и имеет свойства, перечисленные в табл. 14.33. Таблица 14.33. Свойства класса TStatusPanel Свойство
Описание
property Alignment: TAlignment;
Определяет выравнивание текста относительно границ панели; t a L e f t J u s t i f y — выравнивается влево; taCenter—центрируется по горизонтали; t a R i g h t J u s t i f y — выравнивается вправо
property Bevel: TStatusPanelBevel;
Определяет стиль рамки панели: pbNone — нет рамки; p b L o w e r e d — вдавленная рамка; p b R a i s e d — выпуклая рамка Определяет способ формирования изображения панели: p s T e x t — панель содержит только текст и отрисовывается автоматически; psOwnerDraw — панель отрисовывается программой Определяет текст надписи в панели Определяет ширину панели в пикселах
property S t y l e : TStatusPanelStyle; property Text: String; property Width: I n t e g e r ;
Свойства класса T S t a t u s B a r перечислены в табл. 14.34.
328
Глава 14 • Компоненты категории Win32
Т а б л и ц а 1 4 . 3 4 . Свойства класса TStatusBar Свойство
Описание
p r o p e r t y Canvas : TCanvas; p r o p e r t y Panels : T S t a t u s P a n e l s ;
Канва для отрисовки панелей Содержит объекты-панели. Свойство TStatusPanels.Items[Index:Integer]: T s t a t u s P a n e l открывает доступ к панели по ее индексу Запрещает/разрешает создание нескольких панелей.
p r o p e r t y SimplePanel: Boolean;
TToolBar и TToolButton — панель инструментов и кнопки для нее
329
оажения, однако компонент TToolBar позволяет извлечь нужное изображение из контейнера T l m a g e L i s t и поместить это изображение на кнопку. Методику использования компонентов TToolBar и T T o o l B u t t o n рассмотрим на примере программы, окно которой показано на рис. 14.8:
Если содержит значение T r u e , компонент имеет единственную панель
p r o p e r t y SimpleText: S t r i n g ;
Содержит текст панели для SimplePanel = True
p r o p e r t y SizeGrip: Boolean;
Разрешает/запрещает вставку кнопки изменения размеров окна. Игнорируется, если A l i g n о alBottom Рис. 14.8. Пример панелей TToolBar с кнопками TToolButton
События класса T S t a t u s B a r представлены в табл. 14.35. Таблица 14.35. События класса TStatusBar Событие
Описание
TDrawPanelEvent = procedure (StatusBar: TStatusBar; Panel: T S t a t u s P a n e l ; c o n s t R e c t : TRect) of o b j e c t ; p r o p e r t y OnDrawPanel: TDrawPanelEvent; P r o p e r t y OnResize: TNotif yEvent;
Возникает при необходимости отрисовки панели Panel, если ее свойство S t y l e определено как psOwnerDraw: Rect — прямоугольник отрисовки Возникает при изменении размеров компонента
TToolBar и TToolButton — панель инструментов и кнопки для нее Компонент TToolBar представляет собой специальный контейнер для создания панелей инструментов. Главная отличительная черта компонента TToolBar — его способность гибко управлять дочерними элементами, которые он может груп пировать, выравнивать по размерам, располагать в несколько рядов. Компонент может манипулировать любыми вставленными в него дочерними элементами, но все его возможности в полной мере проявляются только со специально для него разработанным компонентом T T o o l B u t t o n (кнопка панели инструментов), по хожим на кнопку T S p e e d B u t t o n , но не ищите ее в палитре компонентов — ее там нет. Поскольку компонент T T o o l B u t t o n разработан специально для компо нента TToolBar, вставить его в панель инструментов можно только после щелчка правой кнопкой мыши на компоненте TToolBar и выбора в контекстном меню команды New Button (новая кнопка) или New Separator (новый разделитель) — раз делители предназначены для функционального выделения на панели инструмен тов групп элементов и представляют собой разновидности кнопок T T o o l B u t t o n . Компонент T T o o l B u t t o n не имеет свойства, предназначенного для хранения изоб-
1. Поместите на пустую форму компонент T l m a g e L i s t , щелкните на нем пра вой кнопкой мыши и выберите команду ImageList Editor. Для заполнения контейнера I m a g e L i s t l подойдут любые небольшие по раз меру изображения. 2. Поместите на форму компонент TToolBar, в окне инспектора объектов ра зыщите свойство Images, раскройте список в правой колонке свойства Images и выберите пункт I m a g e L i s t l . Таким образом мы указываем компоненту на источник изображений и можем теперь вставлять в него кнопки. 3. Щелкните на компоненте T o o l B a r l правой кнопкой мыши и выберите ко манду New Button. 4. Вставьте еще две кнопки, после чего вставьте разделитель командой New Separator. 5. Разместите на компоненте T o o l B a r l остальные кнопки и разделители (см. рис. 14.8). 6. Если хотите, чтобы кнопки имели модный «плоский» вид, установите в свой ство F l a t компонента T o o l B a r l значение True. Свойство B u t t o n s компонента TToolBar позволяет обращаться к каждому до чернему компоненту как к объекту класса T T o o l B u t t o n , свойства которого пред ставлены в табл. 14.36. Т а б л и ц а 1 4 . 3 6 . Свойства класса TToolButton Свойство
Описание
p r o p e r t y AllowAllUp: Boolean;
Если содержит значение T r u e , синхронизирует свое состояние с состоянием других кнопок в той же группе: в любой момент может быть нажата только одна кнопка группы. Игнорируется, если
Grouped = False
продолжение &
3 3 0 Глава 14 • Компоненты категории Win32 Таблица 14.36 (продолжение)
Свойство
Описание
property Caption: String;
Содержит связанный с кнопкой текст, который будет показан, если свойство S h o w C a p t i o n s компонента T T o o l B a r имеет значение T r u e Определяет состояние кнопки: если содержит значение T r u e , кнопка утоплена
property Down : Boolean; property DropdownMenu: TPopupMenu; property Grouped: Boolean; property Image Index: Integerproperty Indeterminate : Boolean TToolButtonStyle= (tbsButton, tbsCheck, tbsDropDown, tbsSeparator, tbsDivider) ; property Style : TToolButtonStyle;
property Wrap: Boolean;
Связывает с нажатой кнопкой контекстное меню Разрешает/запрещает учитывать свойство
AllowAllUp Определяет индекс связанного с кнопкой изображения Запрещает/разрешает выбор кнопки Определяет стиль кнопки: t b s B u t t o n — обычная кнопка; t b s C h e c k —фиксируемая кнопка (остается в нажатом положении, для ее освобождения нужно щелкнуть на ней еще раз); t b s D r o p D o w n — кнопка с символом раскрывающегося списка; t b s S e p a r a t o r — разделитель (на месте этой кнопки будет пустое место); t b s D i v i d e r — разделитель (в работающей программе на месте этой кнопки будет вертикальная черта) Если имеет значение T r u e , кнопка завершает текущий ряд кнопок. Игнорируется, если свойство W r a p a b i e компонента T T o o l B a r имеет значение T r u e
Свойства класса TToolBar перечислены в табл. 14.37. Таблица 14.37. Свойства класса TToolBar Свойство
Описание
property AutoSize: Boolean;
Если содержит значение T r u e , высота компонента будет автоматически согласовываться с высотой кнопок
property ButtonCount: I n t e g e r ;
Содержит количество вставленных в панель инструментов дочерних компонентов (не только кнопок T T o o l B u t t o n ) Определяет высоту кнопок T T o o l B u t t o n
property B u t t o n H e i g h t : I n t e g e r ; property Disabledlmages: TImageList; property F l a t : Boolean;
property Hotlmages : TImageList; property Images: TImageList;
Определяет контейнер для изображений недоступных кнопок Если содержит значение T r u e , панель и кнопки на ней прозрачны для фона и вокруг кнопки появляется граница, только когда на ней располагается указатель мыши Определяет контейнер изображений для кнопок в момент, когда на кнопке располагается указатель мыши Определяет контейнер для изображений кнопок в обычном состоянии
TCoolBar и TCoolBand — панель инструментов и полосы для нее
331
Г^йство
Описание
property Indent: I n t e g e r ;
Определяет отступ в пикселах от левого края компонента для первого дочернего элемента Если имеет значение T r u e , изображение выравнивается по левой границе кнопки, а текст — по правой, в противном случае текст выводится под изображением. Игнорируется, если S h o w C a p t i o n s = F a l s e Содержит количество рядов кнопок Разрешает/запрещает показ на кнопках текста Запрещает/разрешает располагать кнопки в нескольких рядах
property L i s t : Boolean;
p r o p e r t y RowCount: I n t e g e r ; property ShowCaptions : B o o l e a n ; property Wrapabie : Boolean;
"
Для компонента определено событие O n R e s i z e , возникающее при изменении размеров компонента.
TCoolBar и TCoolBand — панель инструментов и полосы для нее Компонент TCoolBar предназначен для создания настраиваемых панелей инст рументов. Для каждого размещаемого на компоненте TCoolBar элемента созда ется объект класса TCoolBand (полоса), который может изменять свои размеры и положение в пределах границ компонента. Центральным свойством компонента является свойство Bands — массив создан ных в компоненте полос TCoolBand. Каждая полоса может иметь текст, значок и произвольный элемент управления. В отличие от компонента TToolBar или T C o n t r o l B a r , полоса в компоненте TCoolBar всегда содержит только один ин терфейсный элемент, но ничто не мешает этому элементу быть элементом-кон тейнером для размещения нескольких компонентов. Размещенный на полосе ком понент всегда стремится занять левый верхний угол полосы, при этом размеры полосы изменяются так, чтобы полностью охватить элемент. Поясним сказанное несложным примером: 1. Поместите на пустую форму компонент TCoolBar — он тотчас же займет верхнюю часть формы, так как по умолчанию его свойство A l i g n m e n t со держит значение a l T o p . 2. Щелкните на компоненте правой кнопкой мыши и выберите в контекстном меню команду Bands Editor (Редактор полос). 3. Окно редактора похоже на многие другие используемые в Delphi окна редакто ров: щелкните на кнопке, чтобы вставить в компонент новую полосу. 4. В окне инспектора объектов можете задать текст для полосы и связанный с ней значок свойствами T e x t и I m a g e l n d e x соответственно (предварительно ком понент в свойстве Images должен уже содержать ссылку на связанный с ним контейнер T I m a g e L i s t ) . Свойство BitMap используется для заполнения по лосы чередующимся узором. Для простоты ограничьтесь текстом Полоса 1 и закройте редактор полос.
332
TPageScroller— панель с кнопками прокрутки
Глава 14 • Компоненты категории Win32
333
Свойства класса TCoolBar перечислены в табл. 14.39.
5. Поместите ниже полосы, но в тот же контейнер C o o l B a r l любой оконный элемент управления, например текстовое поле T E d i t . Подобно полосе поле растянется вдоль верхнего края контейнера, и рядом появится вешка переме щения для реализации механизма перетаскивания (Drag&Dock).
Таблица 14.39. Свойства класса TCoolBar Свойство
Описание
6. Перетащите поле T E d i t на контейнер C o o l B a r l , «ухватившись» за вешку.
p r o p e r t y AutoSize : Boolean;
Если содержит значение True, высота компонента
Освободившуюся полосу можно связать с новым элементом управления (напри мер, ComboBox) и т. д. Два окна работающей программы показаны на рис. 14.9.
p r o p e r t y Bands : TCoolBands;
будет автоматически согласовываться с высотой полос
Содержит список всех полос. Свойство Items этого объекта открывает доступ к полосе по ее индексу
p r o p e r t y Bitmap: TBitmap;
Определяет изображение, которое будут использовать все полосы
p r o p e r t y FixedOrder : Boolean; Запрещает/разрешает перемещение полос p r o p e r t y FixedHeight: Boolean; Запрещает/разрешает изменение размера полос p r o p e r t y Images : TImageList; Указывает контейнер для изображений, связанных с каждой полосой
p r o p e r t y ShowText: Boolean;
Если содержит значение True, на полосе отображается связанный с ней текст
p r o p e r t y V e r t i c a l : Boolean;
Если содержит значение True, полосы располагаются вдоль вертикальной границы компонента
События компонента TCoolBar перечислены в табл. 14.40. Рис. 14.9. Вид компонента TCoolBar при разных настройках пользователя Свойства класса TCoolBand представлены в табл. 14.38. Таблица 14.38. Свойства класса TCoolBand Свойство
Описание
p r o p e r t y Bitmap: TBitmap;
Определяет изображение, которое будет циклически повторяться по всему пространству полосы
p r o p e r t y Break: Boolean;
Если содержит значение T r u e , полоса располагается в новой строке, в противном случае — в той же строке, что и предыдущая полоса Связанный с полосой элемент управления
Property property Boolean; property property Boolean; property
C o n t r o l : TWinControl; FixedBackground: Запрещает/разрешает периодическое повторение
изображения B i t m a p по всей поверхности полосы
FixedSize: Boolean; HorizontalOnly:
Запрещает/разрешает изменение размеров полосы
Imagelndex: I n t e g e r ;
Содержит индекс связанного с полосой изображения
Предписывает показывать полосу только горизонтально
Т а б л и ц а 1 4 . 4 0 . События компонента TCoolBar Событие
Описание
p r o p e r t y OnChange: TNotif yEvent; Возникает при изменении свойств Break, Index или Width у любой полосы p r o p e r t y OnResize : TNotif yEvent; Возникает при изменении размеров компонента
TPageScroller— панель с кнопками прокрутки Компонент служит контейнером для размещения длинных узких компонентов наподобие компонента TToolBar. Его отличительная черта— наличие неболь ших стрелок прокрутки по обеим сторонам контейнера в случае, если ширина (высота) дочернего окна превышает соответствующий размер компонента. По назначению компонент весьма напоминает уже рассмотренный компонент T S c r o l l B o x (см. раздел «TScrollBox — панель с полосами прокрутки» в главе 13) и отличается от него двумя обстоятельствами: компонент не содержит полос про крутки, а прокрутка осуществляется так, чтобы полностью показать ранее неви димый дочерний компонент.
p r o p e r t y MinHeight: I n t e g e r ;
Определяет минимальное значение высоты полосы при изменении ее размеров
Свойства класса T P a g e S c r o l l e r представлены в табл. 14.41.
p r o p e r t y MinWidth: I n t e g e r ;
Определяет минимальное значение ширины полосы при изменении ее размеров
Таблица 1 4 . 4 1 . Свойства класса TPageScroller
p r o p e r t y ParentBitmap: Boolean;
Разрешает/запрещает использовать значение свойства B i t m a p компонента-владельца
TCoolBar вместо собственного свойства Bitmap
p r o p e r t y Text: S t r i n g ;
Содержит текст полосы
Свойство
Описание
p r o p e r t y A u t o S c r o l l : Boolean;
Разрешает/запрещает автоматическую прокрутку при остановке указателя мыши на кнопке прокрутки продолжение •&
334
Глава 14 • Компоненты категории Win32
Т а б л и ц а 1 4 . 4 1 (продолжение) Свойство
Описание
property ButtonSize : I n t e g e r ;
Определяет размер в пикселах кнопок прокрутки: для горизонтальной ориентации — ширину, для вертикальной — высоту Содержит ссылку на прокручиваемое окно Указывает, будет ли компонент приемником в операциях перетаскивания (Drag&Drop). Если содержит значение T r u e , компонент будет прокручивать дочернее окно при перемещении на нем указателя мыши с «грузом» Указывает расстояние в пикселах от границы компонента до соответствующей стороны дочернего окна Определяет ориентацию компонента: s o H o r i z o n t a l — по горизонтали; s o V e r t i c a l — по вертикали
property C o n t r o l : TWinControl; property D r a g S c r o l l : Boolean;
property Margin: I n t e g e r ; type T P a g e S c r o l l e r O r i e n t a t i o n (soHorizontal, soVertical); property O r i e n t a t i o n : TPageScrollerOrientation; property P o s i t i o n : I n t e g e r ;
Определяет текущее положение прокручиваемого окна относительно границ компонента
TComboBoxEx— комбинированный список с расширенными возможностями По сравнению с рассмотренным компонентом TComboBox (см. раздел «TComboВох — комбинированный список» в главе 12), компонент TComboBoxEx позво ляет снабжать пункты (элементы) списка небольшими изображениями, а также регулирует отступ каждого элемента от левого края списка. Пример компонента показан на рис. 14.10. Чтобы воспроизвести пример, проделайте следующие шаги. 1. Поместите на пустую форму компоненты TComboBoxEx и T I m a g e L i s t . В свой ство TComboBoxEx. Images поместите ссылку на I m a g e L i s t l — этот компо нент будет хранилищем изображений для ComboBoxExl.
TComboBoxEx— комбинированный список с расширенными возможностями 3 3 5
2. Наполните компонент I m a g e L i s t l несколькими изображениями. 3. В окне инспектора объектов щелкните на кнопке с многоточием в строке слож ного свойства ComboBoxExl. ItemsEx, чтобы раскрыть редактор этого свой ства. 4. Добавьте с помощью редактора столько строк в список, сколько изображений хранится в компоненте I m a g e L i s t l (если, разумеется, в каждую строку вы хотите поместить собственное изображение). Для каждой строки в окне инс пектора объектов задайте текст (свойство C a p t i o n ) , индекс изображения ( I m a g e l n d e x ) , смещение от левого края ( I n d e n t ) и индекс изображения для выбранной строки ( S e l e c t l m a g e l n d e x ) . Отрицательное или нулевое зна чение I n d e n t означает отсутствие отступа, любое положительное число оп ределяет отступ горизонтального размера изображений. Если значения свойств S e l e c t l m a g e l n d e x и I m a g e l n d e x не совпадают, выбранная строка полу чит не то изображение, которое было в раскрытом списке в момент выбора. Это же изображение она получит в момент повторного раскрытия списка, но при выборе другой строки вернет начальное изображение. Следует заметить, что функциональность компонента TComboBoxEx не полностью соответствует функциональности компонента TComboBox, так как в нем отсутству ют некоторые возможности компонента TComboBox (например, программная отрисовка строк). Большая часть свойств, методов и событий компонентов TComboBoxEx и TComboBox совпадают. Специфические свойства описаны в табл. 14.42. Таблица 1 4 . 4 2 . Свойства класса TComboBoxEx Свойство
Описание
property Images : TCustomlmageList; propertyItemsEx: TComboExItems;
Содержит ссылку на объект-хранилище изображений Содержит ссылку на объект класса TComboExItems
Объект класса TComboExItems содержит всю необходимую информацию о списке в целом. Свойства этого класса представлены в табл. 14.43. Таблица 1 4 . 4 3 . Свойства класса TComboExItems
Рис. 14.10. Демонстрация компонента TComboBoxEx
Свойство
Описание
property C a s e S e n s i t i v e : Boolean; property Count: I n t e g e r ; property Comboltems [ c o n s t Index: I n t e g e r ] : TComboExItem; type TListltemsSortType = (stNone, s t D a t a , s t T e x t , stBoth) ; property SortType: TSortType;
Определяет, будет ли учитываться регистр букв при сортировке строк Содержит количество строк в списке Открывает индексированный доступ к объектам класса TComboExItem, в которых хранится информация о каждом элементе списка Определяет способ сортировки элементов: s t N o n e — нет сортировки; s t D a t a — по значениям свойства D a t a ; s t T e x t — по значениям свойства C a p t i o n ; s t B o t h — объединенная сортировка (по значениям обоих свойств). Свойства D a t a и C a p t i o n представлены далее в описании класса
TComboExItem
336
Глава 14 • Компоненты категории Win32
TComboBoxEx— комбинированный список с расширенными возможностями
Если для списка указан тип сортировки s t T e x t , строки списка сортируются не медленно. Если указан тип s t D a t a или s t B o t h , сортировка осуществляется в об работчике события TComboExItems.OnCompare.
337
В объектах класса TComboExItem хранится вся информация о каждом элементе списка. Этот класс имеет собственные свойства (табл. 14.44).
Это событие возникает, если в качестве типа сортировки выбрано значение s t D a t a или s t B o t h . Обработчик должен проанализировать два элемента и вернуть - 1 , О или +1, если первый элемент меньше второго, равен ему или больше соответ ственно. Методы класса TComboBoxEx представлены в табл. 14.46.
Таблица 1 4 . 4 4 . Свойства класса TComboExItem
Таблица 14.46. Методы класса TComboBoxEx
Свойство
Описание
Метод
Описание
property C a p t i o n : String; property Data: P o i n t e r ;
Текст элемента списка Указатель на связанный с элементом произвольный объект Индекс связанного с элементом изображения в раскрытом списке 1 Смещение элемента от правого края листа Определяет индекс маски, накладываемой на изображения I m a g e l n d e x и Selectedlmagelndex Индекс изображения для выбранного элемента списка
procedure Addltem ( I t e m : String; AObject: TObject) ; procedure C l e a r ; o v e r r i d e ; procedure C l e a r S e l e c t i o n ; override;
Добавляет новый элемент и связанные с ним данные
type TImagelndex = type I n t e g e r ; property Imagelndex: TImagelndex; property I n d e n t : I n t e g e r ; property Overlaylmagelndex: TImagelndex; property S e l e c t e d l m a g e l n d e x : TImagelndex;
У класса TComboExItems есть собственные методы (табл. 14.45) и события, уп равляющие списком в целом. Таблица 1 4 . 4 5 . Методы класса TComboExItems Метод
Описание
f u n c t i o n Add: TComboExItem; function Addltem (const C a p t i o n : S t r i n g ; const Imagelndex, Selectedlmagelndex, Overlaylmagelndex, Indent: Integer; Data: P o i n t e r ) : TComboExItem;
Добавляет к списку новый элемент Добавляет к списку новый элемент и инициализирует его свойства
function I n s e r t (Index: I n t e g e r ) : TComboExItem; type TListltemsCompare = function ( L i s t : TListControlItems; Indexl, Index2: I n t e g e r ) : I n t e g e r ; procedure CustomSort (Compare: T L i s t l t e m s C o m p a r e ) ; procedure S o r t ;
Вставляет в указанную позицию списка новый элемент Сортирует элементы с помощью функции Compare
Очищает список Проверяет, выделен ли какой-то элемент списка, и, если это так, снимает выделение (помещает -1 в свойство I t e m l n d e x и очищает свойство T e x t ) Проверяет, выделен ли какой-то элемент списка, и, если это так, копирует его в список D e s t i n a t i o n
procedure C o p y S e l e c t i o n (Destination: TCustomListControl) ; override; procedure D e l e t e S e l e c t e d ; Проверяет, выделен ли какой-то элемент списка, override; и, если это так, удаляет его из списка function Focused: Boolean; Возвращает значение T r u e , если компонент имеет override; фокус ввода procedure M o v e S e l e c t i o n Проверяет, выделен ли какой-то элемент списка, и, (Destination: если это так, копирует его в список D e s t i n a t i o n T C u s t o m L i s t C o n t r o l ) ; v i r t u a l и затем удаляет procedure S e l e c t A l l ; Выделяет весь текст выбранного элемента. override; Чтобы выделить часть текста, используйте свойства S e l S t a r t и SelLength События класса TComboBoxEx перечислены в табл. 14.47.
Сортирует элементы с помощью обработчика события OnCompare
Помимо перечисленных в табл. 14.45, в классе определены также типичные для любой коллекции методы: A s s i g n , C l e a r , D e l e t e и т. д. Для класса определено единственное событие type TListCompareEvent = f u n c t i o n ( L i s t : T L i s t C o n t r o l I t e m s ; teml, Item2: T L i s t C o n t r o l I t e m ) : I n t e g e r of object; p r o p e r t y OnCompare: T L i s t C o m p a r e E v e n t ; В документации сказано, что отступ определяется в пикселах экрана. На самом деле это не так — он приблизительно равен ширине изображений: 1 — отступ на одно изображение 2 — на два и т. д.
Таблица 1 4 . 4 7 . События класса TComboBoxEx Событие
Описание
property OnBeginEdit: TNotifyEvent; p r o p e r t y OnChange : TNotifyEvent; p r o p e r t y OnDropDown: TNotifyEvent; property OnEndEdit: TNotifyEvent; property OnSelect: TNotifyEvent;
Возникает, когда пользователь начинает изменять текст в поле списка Возникает при каждом изменении пользователем (но не программой!) свойства T e x t Возникает в момент раскрытия списка Возникает, когда пользователь заканчивает изменять текст в поле списка Возникает, когда пользователь выделяет элемент списка
TTimer —таймер
Компоненты категории System
В этой главе описаны компоненты, представленные в категории System палитры компонентов.
TTimer —таймер Компонент TTimer (таймер) служит для отсчета интервалов реального време ни. Его свойство I n t e r v a l определяет интервал времени в миллисекундах, ко торый должен пройти от включения таймера до наступления события OnTimer. Таймер включается при установке значения T r u e в его свойство E n a b l e d . Од нажды включенный таймер все время будет возбуждать события OnTimer до тех пор, пока его свойство E n a b l e d не примет значения F a l s e . Следует учесть, что в силу специфики реализации стандартного аппаратного таймера IBM-совместимого компьютера минимальный реально достижимый ин тервал отсчета времени (этот интервал называется тиком) в операционных си стемах MS-DOS, Windows 3.x и Windows 95/98/ME не может быть меньше 55 мс. Более того, любой интервал времени, отсчитываемый с помощью тайме ра, в этих операционных системах всегда кратен 55 мс. Замечу, что для опера ционных систем Windows NT Server (Workstation), Windows 2000, Windows XP длительность тика составляет 10 мс. Чтобы убедиться в этом, проделайте сле дующий эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера. 1. Начните новый проект и поместите на форму компоненты TTimer, T E d i t , TMemo и T B u t t o n . 2. Установите в свойство E n a b l e d таймера значение F a l s e . 3. Напишите модуль главной формы, представленный в листинге 15.1.
339
Листинг 15.1. Модуль главной формы программы, иллюстрирующей работу таймера unit U n i t l ; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Borland.Vcl.StdCtrls, Borland.Vcl.Buttons, Borland.Vcl.ExtCtrIs, System.ComponentModel; type TForml = class(TForm) mmOutput: TMemo; Panell: TPanel; edlnput: TEdit; SpeedButtonl: TSpeedButton; Labell: TLabel; Timerl: TTimer; procedure SpeedButtonlClick(Sender: TObject); procedure TimerlTimer(Sender: TObject); procedure FormActivate(Sender: TObject); private { Private declarations } Counter: Integer; BegTime: TDateTime; public { Public declarations } end; var Forml: TForml; implementation {$R
*.nfm}
procedure TForml.SpeedButtonlClick(Sender: TObject); // Запускает таймер. // Свойство edlnput содержит период его срабатывания var Delay: Word; продолжение &
3 4 0 Глава 15 • Компоненты категории System Листинг 15.1 (продолжение) begin // Проверяем задание интервала if e d I n p u t . T e x t = ' ' then Exit; try Delay := StrToInt(edlnput.Text); except ShowMessage('Ошибка в записи числа'); edlnput.SelectAll; edlnput.SetFocus; Exit end; Counter := 0; // Сбрасываем счетчик Timerl.Interval := Delay; // Устанавливаем интервал BegTime := Time; // Засекаем время Timerl.Enabled := True; // Пускаем таймер Screen.Cursor := crHourGlass end; procedure TForml.TimerlTimer(Sender: TObject); // Обработчик таймера var h, m, s, ms: Word; // Переменные для декодирования времени const MaxCount = 55; // Количество срабатываний таймера begin Counter := Counter + 1; // Наращиваем счетчик срабатываний if Counter=MaxCount then // Конец цикла? begin // - Да Timerl.Enabled := False; // Останавливаем таймер // Находим среднее время срабатывания: DecodeTime((Time-3egTime)/MaxCount, h, m, s, m s ) ; mmOutput.Lines.Add( // Выводим результат Format('Задано %s ms. Получено %d ms.', [edlnput.Text, ms])); edlnput.Text := ''; // Готовим следующий запуск edlnput.SetFocus; Screen.Cursor := crDefault endend// Обработчик таймера var h, m, s, ms: Word; // Переменные для декодирования времени const MaxCount = 55; // Количество срабатываний таймера begin Counter := Counter + 1; // Наращиваем счетчик срабатываний
TPaintBox— окно для рисования 3 4 1 if Counter=MaxCount then // Конец цикла? begin // — Да Timerl.Enabled := False; // Останавливаем таймер // Находим среднее время срабатывания: DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms); mmOutput.Lines.Add( // Выводим результат Format('Задано %s ms. Получено %d ms.' , [edlnput.Text, ms])); edlnput.Text := "; // Готовим следующий запуск edlnput.SetFocus; Screen.Cursor := crDefault end; end; procedure TForml.FormActivate(Sender: TObject); begin edlnput.SetFocus end; end. Необходимость нескольких (MaxCount) срабатываний для точного усреднения результата связана с тем, что системные часы обновляются каждые 55 мс (10 — для операционных систем на основе ядра NT). Вид окна работающей программы показан на рис. 15.1.
Рис. 15.1. Окно программы проверки таймера
TPaintBox— окно для рисования Назначение компонента TPaintBox - предоставить пользователю простое окно с канвой для рисования произвольных изображений. Канва содержится в свойстве Canvas компонента, графические инструменты - в свойствах Font, Pen и Brush,
3 4 2 Глава 15 • Компоненты категории System а собственно рисование осуществляется в обработчике события O n P a i n t . Осо бенности использования этих инструментов описаны в разделе «Графический ин струментарий» главы 10. Например, представленный далее обработчик создаст окно, показанное на рис. 15.2.
TMediaPlayer—медиа-плеер
343
3. Поместите на нижнюю панель компонент T M e d i a P l a y e r , назовите его MP и введите в его свойство D i s p l a y ссылку на панель p n D i s p l a y . Раскройте список свойства FileName и выберите в нем любой файл с расширением avi. Поместите в его свойство AutoOpen значение True. 4. Напишите такой обработчик события O n R e s i z e для формы: implementation uses Borland.Vcl.Types;
Рис. 15.2. Пример использования компонента TPaintBox procedure TForml.PaintBoxlPaint(Sender: TObject); var X, Y: Integer; begin with PaintBoxl, Canvas do begin Brush.Color := clRed; 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;
procedure TForml.DisplayResize(Sender: TObject); begin // Устанавливаем границы изображения MP.DisplayRect := Rect(0, 0, Display.Width, Display.Height); end; Если ваш компьютер оснащен звуковой картой, включите звуковые колонки и за пустите программу. После щелчка мышью на кнопке воспроизведения вы увиди те видеоролик. Причем вы можете изменять размеры окна программы и таким способом получить полноэкранное видео (рис. 15.3).
TMedia Player— медиа-плеер Компонент T M e d i a P l a y e r представляет собой полнофункциональное мульти медийное устройство, способное, в частности, воспроизводить одновременно и изображение, и звук AVI-файлов. Чтобы увидеть компонент в действии, выпол ните следующую последовательность шагов: 1. Поместите на пустую форму две панели. Удалите содержимое их свойств C a p t o i n . Для нижней панели установите A l i g n = a l B o t t o m , для верхней — Align = a l C l i e n t . 2. Верхняя панель будет экраном для отображения изображения. Установите для нее Name = p n D i s p l a y .
Рис. 15.3. Демонстрация компонента TMediaPlayer Как видите, использование компонента предельно просто. Он автоматически рас познает тип мультимедийного устройства по расширению файла и берет на себя управление этим устройством. Разумеется, в каждый момент времени компонент может управлять лишь одним устройством, однако опосредовано путем измене ния содержимого свойства FileName или явно с помощью свойства DeviceType программа может менять устройство, связанное с компонентом. Каждая кнопка компонента имеет собственное имя, позволяющее программисту сделать какую-либо кнопку невидимой или недоступной. В табл. 15.1 приводят-
344
Глава 15 • Компоненты категории System
ся имя и назначение каждой кнопки (под носителем информации подразумева ются файлы и физические устройства, которые могут служить источником или приемником информации). Таблица 1 5 . 1 . Назначение кнопок компонента TMediaPlayer Кнопка Имя btPlay btPause btstop btNext btPrev btstep
«:
btBack btRecord btE j ect
Описание Инициирует воспроизведение носителя информации Прерывает процесс записи или воспроизведения информации. Повторный щелчок на этой кнопке возобновляет прерванный процесс Останавливает запись или воспроизведение Позиционирует устройство на следующую дорожку или в конец носителя информации, если устройство не имеет дорожек Позиционирует устройство на предыдущую дорожку или в начало носителя информации, если устройство не имеет дорожек Позиционирует устройство на один блок кадров ближе к концу носителя информации. Количество кадров в блоке содержится в свойстве Frames компонента Позиционирует устройство на один блок кадров ближе к началу носителя информации Начинает запись в носитель информации Освобождает устройство от носителя информации
Свойства класса TMediaPlayer представлены в табл. 15.2.
TMediaPlayer—медиа-плеер Свойство
Описание
p r o p e r t y D e v i c e l D : Word;
Содержит Windows-идентификатор устройства, связанного с компонентом. Доступно только для чтения Содержит тип устройства. Если D e v i c e T y p e = d t A u t o S e l e c t , тип определяется автоматически по расширению файла в свойстве F i l e N a m e . В момент установки любого мультимедийного устройства в файл SYSTEM.INI заносятся расширения файлов, которые поддерживаются этим устройством
TMPDeviceTypes = ( d t A u t o S e l e c t , dtAVIVideo, dtCDAudio, dtDAT, d t D i g i t a l V i d e o , dtMMMovie, dtOther, dtOverlay, dtScanner, dtSequencer, dtVCR, d t V i d e o d i s c , dtWaveAudio) ; p r o p e r t y D e v i c e T y p e : TMPDeviceTypes; property D i s p l a y : TWinControl; property D i s p l a y R e c t: TRect;
property EnabledButtons : TButtonSet; p r o p e r t y EndPos : L o n g i n g -
Таблица 15.2. Свойства класса TMediaPlayer Свойство
Описание
p r o p e r t y A u t o E n a b l e : B o o l e a n ; Разрешает/запрещает автоматическое определение доступных и недоступных кнопок Разрешает/запрещает автоматический старт записи/ p r o p e r t y AutoOpen: B o o l e a n ; воспроизведения в момент создания формы, в которую вставлен компонент p r o p e r t y A u t o R e w i n d : B o o l e a n ; Разрешает/запрещает автоматическую «перемотку» носителя информации в момент начала записи или воспроизведения. Игнорируется, если устройство имеет дорожки или если установлены значения в свойства S t a r t P o s HEndPos Содержит параметры устройства, связанного с компонентом: mpCanStep — может смещать носитель на кадр вперед или назад (устройство типа Animation, AVI Video, Digital Video, Overlay или VCR); m p C a n E j e c t — может автоматически удалять носитель из устройства; m p C a n P l a y — может воспроизводить информацию; mpCanRecord— может записывать информацию; mpUsesWindows — может использовать окно для отображения процесса TMPBtnType= ( b t P l a y , b t P a u s e , Кнопки, перечисленные в этом свойстве, будут использовать цвета, заданные по умолчанию btStop,btNext, btPrev, (не перечисленные кнопки будут черно-белыми). b t s t e p , btBack,btRecord, b t E j e c t ) ; T B u t t o n S e t = s e t o f По умолчанию множество C o l o r e d B u t t o n s содержит TMPBtnType;property все кнопки ColoredButtons: TButtonSet;
TMPDevCaps = (mpCanStep, m p C a n E j e c t , mpCanPlay, mpCanRecord, mpUsesWindows) ; TMPDevCapsSet = s e t of TMPDevCaps; p r o p e r t y C a p a b i l i t i e s : TMPDevCapsSet;
property E r r o r : Longlnt; property ErrorMessage: String; property FileName : S t r i n g ; p r o p e r t y Frames : L o n g l n t ; property Length: Longlnt; TMPModes = (mpNotReady, mpStopped, mpPlaying, mpRecording, mpSeeking, mpPaused, mpOpen) ; p r o p e r t y Mode: TMPModes;
345
Содержит ссылку на окно для устройств, которые могут его использовать в процессе записи/воспроизведения Содержит область окна, указанного в свойстве D i s p l a y , которое устройство может использовать для отображения процесса записи/воспроизведения. Для улучшения процесса отображения рекомендуется в свойстве D i s p l a y R e c t указывать размеры, заданные по умолчанию (координаты (0, 0) для правого нижнего угла) Содержит множество доступных кнопок (тип T B u t t o n S e t описан ранее для свойства ColoredButtons) Содержит положение носителя информации в момент прекращения записи или воспроизведения. Переустановка значения приводит к повторению записи или воспроизведения Содержит код ошибки. Доступно только для чтения Содержит сообщение об ошибке. Доступно только для чтения Содержит имя читаемого или записываемого файла Определяет количество кадров, пропускаемых при выполнении методов S t e p или Back Содержит текущую длину носителя информации. Доступно только для чтения
Показывает текущее состояние мультимедийного устройства: mpNotReady— не готово; m p S t o p p e d — остановлено; m p P l a y i n g — воспроизводит информацию; m p R e c o r d i n g — записывает информацию; m p S e e k i n g — перемещает носитель информации; mpPaused— приостановлено; mpOpen — открыто. Доступно только для чтения property N o t i f y : Boolean; Содержит значение T r u e , если методы Back, C l o s e , E j e c t , N e x t , Open, Pause, P a u s e O n l y , P l a y , P r e v i o u s , S t a r t R e c o r d i n g , Resume, R e w i n d , S t e p и S t o p будут генерировать событие O n N o t i f y TMPNotifyValues = Содержит результат последней команды, генерирующей (nvSuccessful, n v S u p e r s e d e d , событие O n N o t i f y : n v S u c c e s s f u l — выполнена nvAborted, n v F a i l u r e ) ; успешно; n v S u p e r s e d e d —заменена другой p r o p e r t y N o t i f yValue : командой; nvAborted—досрочно прервана; TMPNotifyValues; n v F a i l u r e — завершилась ошибкой. Доступно только для чтения продолжение &
346
Глава 15 • Компоненты категории System
Таблица 15.2 (продолжение) Свойство
Описание
property Position: Longlnt; property Shareable: Boolean;
Содержит текущую позицию носителя информации Разрешает/запрещает другим компонентам и программам использовать устройство, связанное с данным компонентом Содержит позицию от начала носителя, с которой начинается запись или воспроизведение. Определяется в момент открытия устройства и доступно только для чтения Содержит текущую позицию от начала носителя, с которой начинается запись или воспроизведение Определяет формат представления данных в свойствах
property Start: Longlnt;
property StartPos: Longlnt; TMPTimeFormats = (tfMilliseconds, tfHMS, tfMSF, tfFrames, tfSMPTE24, tfSMPTE25, tfSMPTE30, tfSMPTE30Drop, tfBytes, tfSamples, tfTMSF); property TimeFormat: TMPTimeFormats; property TrackLength [TrackNum: Integer]: Longlnt; property TrackPosition [TrackNum: Integer]: Longlnt property Tracks : Longlnt; property VisibleButtons: TButtonSet; property Wait: Boolean;
S t a r t P o s , Length, P o s i t i o n , S t a r t и EndPos
(см. пояснения далее)
Возвращает длину (в формате T i m e F o r m a t ) дорожки с индексом TrackNum. Доступно только для чтения Возвращает стартовую позицию (в формате
TimeFormat) дорожки с индексом TrackNum.
Доступно только для чтения Содержит количество дорожек в открытом устройстве. Доступно только для чтения Содержит множество видимых кнопок (тип T B u t t o n S e t описан ранее для свойства
ColoredButtons)
Если содержит значение T r u e , управление возвращается в программу только после завершения перехода устройства в другое состояние, связанное
с методом Back, Close, E j ect, Next, Open, Pause, PauseOnly, Play, Previous, StartRecording, Resume, Rewind, Step или Stop Если свойство A u t o E n a b l e имеет значение True, компонент автоматически по типу устройства или расширению файла и значению свойства Mode определяет, какие кнопки будут доступны пользователю. Если свойство A u t o E n a b l e имеет значение F a l s e , доступными будут только те кнопки, которые входят во множе ство E n a b l e d B u t t o n s . Множество E n a b l e d B u t t o n s игнорируется, если свой ство A u t o E n a b l e имеет значение True. Свойство AutoRewind игнорируется, если устройство имеет дорожки или если процесс записи/воспроизведения вызван изменением значения в свойстве S t a r t Pos или EndPos. Положение носителя в свойствах S t a r t P o s , Length, P o s i t i o n , S t a r t и EndPos в зависимости от значения свойства TimeFormat должно интерпретироваться следующим образом (отсчет ведется от начала носителя информации): • •
t f M i l l i s e c o n d s — миллисекунды в диапазоне от 0 до 4 294 967 295; tfHMS — часы, минуты, секунды (занимают в порядке перечисления по одно му байту, начиная с младшего; старший байт L o n g l n t не используется);
TMediaPlayer—медиа-плеер 3 4 7
•
tfMSF — минуты, секунды и кадры (занимают в порядке перечисления по од ному байту, начиная с младшего; старший байт L o n g l n t не используется); • t f F r a m e s — кадры в диапазоне от 0 до 4 294 967 295; •
tfSMPTE24 — часы, минуты, секунды и количество блоков по 24 кадра в се кунду (занимают в порядке перечисления по одному байту, начиная с млад шего);
•
tf SMPTE25 — часы, минуты, секунды и количество блоков по 25 кадров в се кунду;
•
tf SMPTE30 — часы, минуты, секунды и количество блоков по 30 кадров в се кунду;
•
tfSMPTE30Drop — часы, минуты, секунды и количество пропущенных бло ков по 30 кадров в секунду;
•
t f B y t e s — байты в диапазоне от 0 до 4 294 967 295;
•
tf Samples — количество условных блоков информации в диапазоне от 0 до 4 294 967 295;
•
tfTMSF — дорожки, минуты, секунды и кадры (занимают в порядке перечис ления по одному байту, начиная с младшего).
Свойство TimeFormat в окне инспектора объектов недоступно и устанавливается автоматически при открытии мультимедийного устройства методом Open. В связи с этим значения в свойства S t a r t P o s , P o s i t i o n и EndPos могут устанавливать ся, а в свойствах L e n g t h , S t a r t , T r a c k L e n g t h и T r a c k P o s i t i o n становятся действительными только после вызова метода Open. Методы класса T M e d i a P l a y e r перечислены в табл. 15.3. Таблица 15.3. Методы класса TMediaPlayer Метод Описание procedure AutoButtonSet (Btn: TMPBtnType) ; dynamic; procedure Back;
Реализует доступность/недоступность кнопок компонента при установке в свойство AutoEnable значения True Перемещает текущую позицию в носителе информации назад на количество кадров,
procedure Click (Button: TMPBtnType; v a r DoDef a u l t : Boolean) ; dynamicprocedure Close; procedure DoNotify; dynamic;
содержащихся в свойстве Frames По умолчанию вызывает обработчик события OnClick. Метод должен перекрываться в потомках
Закрывает связанное с компонентом устройство По умолчанию вызывает обработчик события OnNotify. Метод должен перекрываться в потомках procedure E j ect; Выгружает носитель информации из устройства procedure MMNotif у (var Message : Реализует отклик на Windows-сообщение TMessage) ; message ММ_МС I NOT I FY. По умолчанию вызывает MM_MCINOTIFY; метод DoNotify procedure Next; Перемещает текущую позицию носителя на начало следующей дорожки, а если устройство не имеет дорожек — в конец носителя продолжение •&
3 4 8 Глава 15 • Компоненты категории System Таблица 15.3 (продолжение) Метод
TMediaPlayer—медиа-плеер 3 4 9 Событие
Описание Извещает компонент AComponent о том, что завершилась операция O p e r a t i o n
procedure N o t i f i c a t i o n (AComponent:TComponent; Operation: TOperation);override; Открывает мультимедийное устройство p r o c e d u r e Open; Приостанавливает процесс воспроизведения или procedure Pause; записи информации. Если устройство уже было приостановлено, восстанавливает процесс, вызывая метод Resume Приостанавливает процесс воспроизведения procedure PauseOnly; или записи информации. Если устройство уже было приостановлено, ничего не делает Начинает воспроизведение с позиции S t a r t P o s , procedure P l a y ; а если это свойство не установлено — с текущей позиции P o s i t i o n Реализует отклик на событие O n P o s t C l i c k . procedure P o s t C l i c k ( B u t t o n : По умолчанию вызывает соответствующий TMPBtnType) ; dynamicобработчик события Перемещает текущую позицию носителя на начало procedure P r e v i o u s ; предыдущей дорожки, а если устройство не имеет дорожек — в начало носителя Восстанавливает процесс записи p r o c e d u r e Resume; или воспроизведения. Вызывается при повторном щелчке на кнопке b t P a u s e Перемещает текущую позицию носителя p r o c e d u r e Rewind; к положению, определяемому свойством S t a r t Сохраняет информацию в файле procedure Save; с именем FileName Начинает запись информации с текущей позиции procedure S t a r t R e c o r d i n g ; или позиции S t a r t P o s Перемещает текущую позицию в носителе procedure S t e p ; информации вперед на количество кадров, содержащихся в свойстве Frames Прекращает запись или воспроизведение procedure S t o p ; информации
События класса T M e d i a P l a y e r представлены в табл. 15.4. Таблица 15.4. События класса TMediaPlayer Событие
Описание
EMPNotifу=procedure (Sender: T O b j e c t ; B u t t o n : TMPBtnType; var DoDefault:Boolean) of object; property O n C l i c k : EMPNotify; property OnNotifу: TNotifyEvent
Возникает после щелчка на кнопке B u t t o n компонента. Если D o D e f a u l t = T r u e , вызывается метод, связанный с этой кнопкой
Возникает после завершения вызова одного из методов Back, C l o s e , E j e c t , Next, Open, Pause, PauseOnly, P l a y , P r e v i o u s , Resume, Rewind, S t a r t R e c o r d i n g , S t e p или S t o p , если свойство N o t i f y имеет значение T r u e
Описание
EMPPostNotify = procedure (Sender: TObject; Button: TMPBtnType) of o b j e c t ; p r o p e r t y OnPostClick: EMPPostNotify;
Если Wait = T r u e , событие O n P o s t C l i c k возникает только после завершения соответствующей операции, вызванной событием OnClick, в противном случае — немедленно после события OnClick
Иногда требуется воспроизвести короткий звуковой сигнал для привлечения вни мания пользователя программы, например после завершения длительного по време ни процесса обновления данных. Стандартная для Delphi процедура без параметров Веер извлекает сигнал из системного динамика только в том случае, если компью тер не оснащен звуковой картой. С другой стороны, мощные возможности компо нента TMediaPlayer кажутся излишними для решения этой задачи на компьюте рах, оснащенных звуковой картой. В этом случае может пригодиться API-функция MessageBeep, с помощью которой в 32-разрядных версиях Windows озвучиваются стандартные диалоговые окна. Единственным параметром обращения к этой функ ции является один из следующих идентификаторов стандартного звука: mb_IconA s t e r i s k , mb_IconExclamation, mb_IconHand, mb_IconQuestion или mb_OK. Обращение с параметром $FFFFFFFF игнорирует звуковую карту и извлекает ко роткий звуковой сигнал из системного динамика. Сразу после обращения функция возвращает управление программе и воспроизводит звук асинхронно. Значительно более богатые возможности в этом отношении имеет API-функция PlaySound, которая способна воспроизводить любые звуковые клипы. Объявление функции: function
PlaySound(pszSound:
fdwSound:Cardinal):
PChar;
Boolean;
hmod:
HINST;
stdcall; external
'winmm.dll';
Параметры функции перечислены далее: • p s z S o u n d — имя воспроизводимого файла; • hmod — дескриптор программы, если звук берется из ресурсного файла (в про тивном случае содержит 0); •
fdwSound — параметр, уточняющий воспроизведение; его значениями могут быть: о SND_ASYNC — звук воспроизводится асинхронно (функция возвращает уп равление сразу после обращения к ней); о SND_LOOP — звук воспроизводится постоянно до тех пор, пока не будет вызвана та же функция с параметром p s z S o u n d = 0; о SND_NOSTOP — функция пытается воспроизвести звук, если устройство не занято, в противном случае не воспроизводит ничего (если устройство за нято, но этот параметр не указан, функция прервет воспроизведение пре дыдущего звука и начнет воспроизведение следующего); о SND_NOWAIT — если устройство занято, функция не будет воспроизводить новый звук; о SND_PURGE — прекращает воспроизведение всех звуков для данной задачи; о SND_SYNC — воспроизводит звук синхронно и возвращает управление толь ко после окончания его воспроизведения.
TOpenDialog и TSaveDialog — окна открытия и сохранения файлов
Компоненты категории Dialogs
В состав Windows входит ряд типовых диалоговых окон, таких как окно выбора загружаемого файла, выбора шрифта, настройки принтера и т. д. В Delphi реали зованы классы, объекты которых дают программисту удобные способы создания и использования таких окон.
Работа со стандартными диалоговыми окнами Работа со стандартными диалоговыми окнами осуществляется в три этапа: 1. Вначале на форму помещается соответствующий компонент и осуществляет ся настройка его свойств (следует заметить, что собственно компонент не ви ден в момент работы программы, видно лишь создаваемое им стандартное окно). Настройка свойств может проходить как на этапе конструирования, так и в ходе прогона программы. Как и для любых других компонентов, про граммист не должен заботиться о вызове конструктора и деструктора класса диалогового окна — эти вызовы реализуются автоматически в момент старта и завершения программы. 2. На втором этапе осуществляется вызов стандартного для классов диалоговых окон метода E x e c u t e , который создает и показывает на экране диалоговое окно. Вызов этого метода обычно располагается внутри обработчика какоголибо события. Например, обработчик выбора в меню команды Открыть файл может вызвать метод E x e c u t e компонента TOpenDialog, обработчик щелч ка на кнопке Сохранить панели инструментов может вызвать такой же метод компонента T S a v e D i a l o g и т. д. Только после обращения к методу E x e c u t e на экране появляется соответствующее диалоговое окно. Это окно является модальным, поэтому сразу после обращения к методу E x e c u t e дальнейшее выполнение программы приостанавливается до тех пор, пока пользователь не
351
закроет окно. Поскольку E x e c u t e — логическая функция, она возвращает в программу значение True, если результат диалога с пользователем был ус пешным. 3 Проанализировав результат вызова метода E x e c u t e , программа может вы полнить третий этап — использовать введенные с помощью диалогового окна данные: имя файла, параметры принтера, выбранный шрифт и т. д. Проиллюстрируем сказанное следующим примером. Создадим простую програм му для просмотра содержимого текстового файла. Для этого на пустую форму поместите компонент TOpenDialog, а также кнопку T B u t t o n и многострочное поле ТМето. При работе программы щелчок на кнопке будет сигналом о необхо димости загрузить в поле новый файл. Создайте представленный далее обработ чик события O n C l i c k этой кнопки: procedure T F o r m l . B u t t o n l C l i c k ( S e n d e r : T O b j e c t ) ; begin // Настраиваем компонент на отбор текстовых файлов: O p e n D i a l o g l . F i l t e r : = 'Текстовые ф а й л ы ) * . t x t | ' + 1 Файлы П а с к а л я | * . p a s ' ; // Реализуем диалог и анализируем его результат i f O p e n D i a l o g l . E x e c u t e and F i l e E x i s t s ( O p e n D i a l o g l . F i l e N a m e ) then // Результат успешный - пользователь выбрал файл. // Загружаем файл в поле Memol: Memol.Lines.LoadFromFile(OpenDialogl.FileName); end;
TOpenDialog и TSaveDialog — окна открытия и сохранения файлов Компоненты TOpenDialog и T S a v e D i a l o g имеют идентичные свойства и поэто му рассматриваются вместе. Пример диалогового окна TOpenDialog показан на рис. 16.1. Свойство FileName : S t r i n g содержит путь поиска и выбранный файл при ус пешном завершении диалога с пользователем. Программа может применять это свойство для доступа к файлу с целью читать из него данные (TOpenDialog) или записывать в него ( T S a v e D i a l o g ) . Замечу, что пользователь может ввести произ вольное имя и, следовательно, указать несуществующий файл. Для записи это не имеет значения, но при чтении отсутствие файла может привести к краху про граммы. Чтобы избежать этого, можно проверить существование файла глобаль ной функцией F i l e E x i s t s , как это сделано в предыдущем примере, или исполь зовать механизм обработки исключительных ситуаций. Свойство F i l t e r : S t r i n g используется для фильтрации (отбора) файлов, пока зываемых в диалоговом окне. Это свойство можно устанавливать с помощью спе циального редактора на этапе конструирования формы или программно, как это сделано в предыдущем примере. Для доступа к редактору достаточно щелкнуть на
352
Глава 16 • Компоненты категории Dialogs
кнопке с многоточием в строке свойства F i l t e r окна инспектора объектов. При программном вводе фильтры задаются одной длинной строкой, в которой симво лы вертикальной черты ( | ) служат для разделения фильтров, а также для отделе ния описания фильтруемых файлов от соответствующей маски выбора. Напри мер, следующий оператор задает две маски для отбора файлов с расширениями PAS и ТХТ:
TOpenDialog и TSaveDialog — окна открытия и сохранения файлов ofEnableSizing, ofDontAddToRecent, ofForceShowHidden); TOpenOptions = set of TOpenOption; property Options: TOpenOptions; Значения этого свойства имеют следующий смысл: • ofReadOnly — устанавливает флажок Только чтение; •
o f O v e r w r i t e P r o m p t — требует согласия пользователя при записи в суще ствующий файл;
•
of HideReadOnly — скрывает флажок Только чтение;
•
ofNoChangeDir — запрещает смену каталога;
•
of ShowHelp — включает в окно кнопку Справка;
• •
Рис. 16.1. Стандартное окно TOpenDialog
O p e n D i a l o g l . F i l t e r := 'Текстовые файлы|*.txt|Файлы П а с к а л я | * . p a s ' ;
353
o f N o V a l i d a t e — запрещает автоматическую проверку правильности наби раемых в имени файла символов; of A l l o w M u l t i S e l e c t — разрешает множественный выбор файлов;
•
o f E x t e n s i o n D i f f e r e n t — при завершении диалога наличие этого значе ния в свойстве O p t i o n s говорит о том, что пользователь ввел расширение, отличающееся от заданного по умолчанию;
•
of P a t h M u s t E x i s t — разрешает указывать файлы только из существующих каталогов;
•
o f F i l e M u s t E x i s t — разрешает указывать только существующие файлы;
•
o f C r e a t e P r o m p t — требует подтверждения для создания несуществующего файла;
•
of ShareAware — разрешает выбирать файлы, используемые другими парал лельно выполняемыми программами;
•
Установить начальный каталог позволяет свойство I n i t i a l D i r : S t r i n g , на пример:
o f N o R e a d O n l y R e t u r n — запрещает выбор файлов, имеющих атрибут толь ко для чтения;
•
OpenDialogl.InitialDir := 'с:\program files\borland\bds\3.0\source';
o f N o T e s t F i l e C r e a t e — запрещает проверку доступности сетевого или ло кального диска;
•
o f N o N e t w o r k B u t t o n — запрещает вставку кнопки создания сетевого диска;
С помощью свойства D e f a u l t E x t : S t r i n g формируется полное имя файла, если при ручном вводе пользователь не указал расширение. В этом случае к имени файла прибавляются разделительная точка и содержимое этого свойства. Настройка диалогового окна может варьироваться с помощью следующего свой ства: type TOpenOption = (ofReadOnly, ofOverwritePrompt, ofHideReadOnly, ofNoChangeDir, ofShowHelp, ofNoValidate, ofAllowMultiSelect, ofExtensionDifferent, ofPathMustExist, ofFileMustExist, ofCreatePrompt, ofShareAware, ofNoReadOnlyReturn, ofNoTestFileCreate, ofNoNetworkButton, ofNoLongNames, ofOldStyleDialog, ofNoDereferenceLinks, ofEnablelncludeNotify,
•
of NoLongNames — запрещает использование длинных имен файлов;
•
o f O l d S t y l e D i a l o g — создает диалоговое окно в стиле Windows 3.x;
•
o f N o D e r e f e r e n c e L i n k s — запрещает разыменование ярлыков Windows (если пользователь выбирает ярлык, в свойство FileName помещются путь доступа и имя файла ярлыка с расширением LNK, а не файла, на который указывает ярлык);
•
o f E n a b l e l n c l u d e N o t i f у (Windows 2000 и выше) - разрешает посылку уведомляющих сообщений диалоговому окну, если пользователь открывает папку (сообщения посылаются для каждого файла папки, что позволяет уп равлять отображением содержимого папки; см. также описание события Onlncludeltem);
•
o f E n a b l e S i z i n g (Windows 98 и выше) — позволяет окну в стиле Провод ника Windows изменять размеры (диалоговые окна в старом стиле изменения размеров не допускают) с помощью мыши или клавиатуры (по умолчанию 12 Зак. 126
3 5 4 Глава 16 • Компоненты категории Dialogs
такое изменение размеров допускается независимо от этого флага, который учитывается только в пользовательских диалоговых окнах); •
ofDontAddToRecent — запрещает присоединять файл к списку недавно от крывавшихся файлов;
•
of ShowHidden — показывает скрытые файлы.
TFontDialog — окно выбора шрифта
355
На рис. 16.2 показано стандартное окно компонента T O p e n P i c t u r e D i a l o g .
Если разрешен множественный выбор, доступ к выбранным именам можно по лучить в свойстве F i l e s : S t r i n g s . События классов TOpenDialog и T S a v e D i a l o g перечислены в табл. 16.1. Таблица 16.1. События классов TOpenDialog и TSaveDialog Событие
Описание
type TCloseQueryEvent = procedure(Sender: TObject; v a r C a n C l o s e : Boolean) of o b j e c t ; property OnCanClose: TCloseQueryEvent;
Возникает непосредственно перед закрытием диалогового окна. Используйте обработчик этого события для окончательной проверки правильности выбранного файла, например, его существования, возможности перезаписи содержимого и т. п. Если обработчик установит значение F a l s e в параметр C a n C l o s e , окно не будет закрыто Возникает при закрытии диалогового окна
property OnClose: TNotifyEvent; property OnFolderChange: TNotifyEvent; t y p e _OFNOTIFYEXA = packed r e c o r d h d r : TNMHdr; lpOFN: POpenFilename; psf: IShellFolder; pidl: Pointer; end; T I n c l u d e l t e m E v e n t = p r o c e d u r e ( c o n s t OFN: TOFNotifyEx; v a r I n c l u d e : Boolean)of object; property OnIncludeItem: TIncludeltemEvent; property OnSelectionChange: TNotifyEvent; p r o p e r t y OnShow: TNotifyEvent; property OnSelectionChange: TNotifyEvent;
Возникает при изменении отображаемой папки Возникает для каждого элемента вновь открываемой папки, если свойство O p t i o n s диалогового окна содержит элемент o f E n a b l e l n c l u d e N o t i f у. Обработчик события с помощью параметра I n c l u d e указывает, следует ли отображать данный элемент папки
Рис. 16.2. Стандартное окно TOpenPictureDialog
TFontDialog — окно выбора шрифта Компонент T F o n t D i a l o g создает и обслуживает стандартное окно выбора шриф та (рис. 16.3).
Возникает при изменении содержимого диалогового окна: при изменении папки, выбранного файла, фильтра отбора и т. п. Возникает при открытии диалогового окна Возникает при изменении фильтра отбора
TOpenPictureDialog и TSavePictureDialog — окна открытия и сохранения изображений Специализированные диалоговые окна для открытия и сохранения графических файлов T O p e n P i c t u r e D i a l o g и T S a v e P i c t u r e D i a l o g отличаются от компо нентов TOpenDialog и T S a v e D i a l o g двумя аспектами. Во-первых, в них пре дусмотрены стандартные фильтры для выбора графических файлов (с расширени ями BMP, ICO, WMF и EMF). Во-вгорых, в диалоговые окна включены панели для предварительного просмотра выбираемого файла.
Р и с . 1 6 . 3 . Стандартное о к н о TFontDialog
3 5 6 Глава 16 • Компоненты категории Dialogs Следующее свойство определяет тип устройства, для которого выбирается шрифт: TFontDialogDevice = (fdScreen, fdPrinter, fdBoth) ; property Device: TFontDialogDevice; Здесь fdScreen — экран; fdPrinter — принтер; fdBoth — шрифты, поддер живаемые и экраном, и принтером. Диапазон возможных значений размеров шрифтов определяется свойствами MinFontSize HMaxFontSize. Значения этих свойств задаются ^пунктах (1 пункт равен 1/72 дюйма, что приблизительно равно 0,36 мм). Если свойства содержат 0, ограничения на размер шрифта отсутствуют. Представленное далее свойство используется для настройки диалогового окна:
TColorDialog — окно выбора цвета
357
• f d S c a l a b l e O n l y — включает только масштабируемые шрифты (векторные и TrueType-шрифты); • f d A p p l y B u t t o n — включает в окно кнопку Применить. Результат выбора шрифта содержит свойство Font.
TColorDialog — окно выбора цвета Компонент T C o l o r D i a l o g создает и обслуживает стандартное диалоговое окно выбора цвета, показанное на рис. 16.4.
type TFontDialogOption = (fdAnsiOnly, fdTrueTypeOnly, fdEffects, fdFixedPitchOnly, fdForceFontExist, fdNoFaceSel, fdNoOEMFonts, fdNoSimulations, fdNoSizeSel, fdNoStyleSel, fdNoVectorFonts, fdShowHelp, fdWysiwyg, fdLimitSize, fdScalableOnly, fdApplyButton); TFontDialogOptions = set of TFontDialogOption; property Options: TFontDialogOptions; Значения этого свойства имеют следующий смысл: •
f d A n s i O n l y — показывает только шрифты с набором символов Windows;
•
f d T r u e T y p e O n l y — показывает только TrueType-шрифты;
•
f d E f f e c t s — включает в окно флажки Подчеркнутый и Зачеркнутый, а также список выбора цвета шрифта;
•
f d F i x e d P i t c h O n l y — включает только моноширинные шрифты;
•
f d F o r c e F o n t E x i s t — предупреждает о выборе несуществующего шрифта;
•
f dNoFaceSel — запрещает выделение имени шрифта в момент открытия окна;
Свойства компонента T C o l o r D i a l o g представлены в табл. 16.2.
•
fdNoOEMFonts — запрещает выбор шрифтов MS-DOS;
Таблица 16.2. Свойства компонента TColorDialog
• • • • • • •
f d N o S i m u l a t i o n s — исключает шрифты, которые синтезируются графичес ким интерфейсом Windows; f d N o S i z e S e l —запрещает выделение размера шрифта в момент открытия окна; f d N o S t y l e S e l — запрещает выделение стиля шрифта в момент открытия окна; f d N o V e c t o r F o n t s — исключает векторные шрифты (шрифты для Windows версии 1.0; используются в плоттерах); f dShowHelp — включает в диалоговое окно кнопку Справка; fdWysiwyg — включает шрифты, которые поддерживаются и экраном, и прин тером; f d L i m i t S i z e — включает ограничения на размер шрифта, заданные свой ствами M a x F o n t S i z e и M i n F o n t S i z e ;
Рис. 16.4. Стандартное окно компонента TColorDialog
Свойство
Описание
Содержит выбранный цвет Содержит до 16 цветов, определенных пользователем. Каждая строка имеет формат C o l o r X = НННННН, где X — буква от А до Р, определяющая номер цвета, НННННН — шестнадцатеричное представление цвета в модели RGB (красный, зеленый, голубой) TCoIorDialogOption = Определяет вид окна: c d F u l l O p e n — показывать (cdFullOpen, с развернутой палитрой выбора цвета; cdPreventFullOpen, cdShowHelp, c d P r e v e n t F u l l O p e n — запретить показ палитры выбора цвета; cdShowHelp— включить в окно cdSolidColor, cdAnyColor) ; кнопку Справка; c d S o l i d C o l o r — выбирать TColorDialogOptions = s e t ближайший чистый цвет; c d A n y C o l o r — of TCoIorDialogOption; разрешить выбор цветов, не являющихся чистыми Property Options (не относящихся к цветам из основного набора) TColorDialogOptions;
property Color : TColor; property CustomColors : TStrings;
358
Глава 16 • Компоненты категории Dialogs
TPrintDialog — окно настройки параметров печати Компонент T P r i n t D i a l o g создает стандартное диалоговое окно для выбора пара метров печати, показанное на рис. 16.5. Свойства класса T P r i n t D i a l o g перечислены в табл. 16.3.
TFindDialog — окно поиска Свойство
Описание
property P r i n t T o F i l e : Boolean; roperty ToPage: I n t e g e r ;
Содержит значение T r u e , если пользователь выбрал печать в файл
359
Определяет конечную страницу печати
TPrinterSetupDialog — окно настройки параметров принтера Компонент T P r i n t e r S e t u p D i a l o g создает диалоговое окно настройки пара метров принтера, вид которого зависит от типа принтера. Это окно взаимодей ствует с драйвером принтера и не возвращает в программу никакой информации, поэтому его метод E x e c u t e - процедура, а не функция.
TFindDialog — окно поиска Стандартное диалоговое окно компонента T F i n d D i a l o g используется для по иска фрагмента текста (рис. 16.6).
Рис. 16.5. Стандартное окно компонента TPrintDialog
Т а б л и ц а 16.3. Свойства класса TPrintDialog Свойство
Описание
property Collate : Boolean; Если имеет значение T r u e , окно отображается с установленным флажком Разобрать по копиям. Если этот флажок установлен, печать нескольких копий документа будет идти по копиям: сначала первая копия, затем вторая и т. д., в противном случае— по страницам: сначала все копии первой страницы, затем второй и т. д. property Copies : Integer; Определяет количество копий (0 — одна копия) property FromPage : Integer Определяет начальную страницу печати property MaxPage: Integer; Определяет верхнюю границу диапазона страниц
для свойств FromPage и ТоРаде property MinPage: Integer; Определяет нижнюю границу диапазона страниц для свойств FromPage и ТоРаде Определяет настройку окна: p o P r i n t T o F i l e — печатать TPrintDialogOption = (poPrintToFile, в файл; p o P r i n t T o F i l e — разрешает выбор диапазона poPageNums, poSelection, страниц; p o S e l e c t i o n — разрешает печать выбранного poWarning, poHelp, текста; p o W a r n i n g — предупреждать пользователя о не poDisablePrintToFile) ; установленном принтере; p o H e l p — вставить в окно TPrintDialogOptions = кнопку Справка; p o D i s a b l e P r i n t T o F i l e — set of TPrintDialogOption; запрещает печать в файл property Options : TPrintDialogOptions; TPrintRange = (prAHPages, Определяет диапазон печатаемых страниц: prSelection, prPageNums); p r A l l P a g e s — все страницы; p r S e l e c t i o n — выделенный фрагмент текста; property PrintRange: prPageNums — страницы по номерам TPrintRange;
Рис. 16.6. Стандартное окно компонента TFindDialog Свойства класса T F i n d D i a l o g перечислены в табл. 16.4. Таблица 1 6 . 4 . Свойства класса TFindDialog Свойство
Описание
property FindText: String; Определяет образец поиска Содержит горизонтальную позицию левого верхнего угла property L e f t : I n t e g e r ; property Options : TFindOptions; property P o s i t i o n : TPoint; p r o p e r t y Top: I n t e g e r ;
места появления окна Определяет настройку диалогового окна (см. далее)
Содержит горизонтальную и вертикальную позиции левого верхнего угла места появления окна Содержит вертикальную позицию левого верхнего угла места появления окна
Для компонента определен следующий тип, использующийся в свойстве O p t i o n s : type TFindOption = (frDown, frFindNext, frHideMatchCase, frHideWholeWord, frHideUpDown, frMatchCase, frDisableMatchCase, frDisableUpDown, frDisableWholeWord, frReplace, frReplaceAll, frWholeWord, frShowHelp); TFindOptions = s e t of TFindOption;
360
Глава 16 • Компоненты категории Dialogs
Его значения имеют следующий смысл: • f r Down — устанавливает поиск вперед по тексту; • f r F i n d N e x t — сообщает программе, что пользователь щелкнул на кнопке Найти далее; • f r H i d e M a t c h C a s e — снимает флажок С учетом регистра; • f rHideWholeWord — снимает флажок Только слово целиком; • f rHideUpDown — скрывает переключатели выбора направления поиска; • f r M a t c h C a s e — устанавливает флажок С учетом регистра; • f r D i s a b l e M a t c h C a s e — делает недоступным флажок С учетом регистра; • frDisableUpDown — запрещает выбор направления поиска; • f r D i s a b l e W h o l e W o r d — делает недоступным флажок Только слово целиком; • f r R e p l a c e — используется в компоненте T R e p l a c e D i a l o g и указывает на необходимость замены найденного фрагмента; • f r R e p l a c e A l l — используется в компоненте T R e p l a c e D i a l o g и указывает на необходимость замены всех вхождений образца поиска; • f rWholeWord — устанавливает флажок Только слово целиком; • f rShowHelp — включает в окно кнопку Справка. Специфичной для диалогового окна является реализуемая им возможность про смотра найденного фрагмента и при необходимости продолжение поиска. С этой целью для компонента определено событие O n F i n d : T N o t i f y E v e n t , которое возникает всякий раз, когда пользователь щелкает на кнопке Найти далее. Обра ботчик события должен найти образец в тексте и показать его пользователю. Пусть, например, компонент Memo 1 содержит отыскиваемый фрагмент и поиск идет с учетом регистра. Тогда обработчик может иметь такой вид: procedure TForml.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;
TReplaceDialog — окно поиска и замены Компонент T R e p l a c e D i a l o g создает и обслуживает окно поиска и замены тек стового фрагмента (рис. 16.7).
TPageSetupDialog — окно установки параметров печатаемой страницы
361
Класс T R e p l a c e D i a l o g является прямым потомком класса T F i n d D i a l o g и нас ледует от него большую часть свойств. Дополнительно в компоненте определены свойство R e p l a c e T e x t : S t r i n g , в котором содержится текст замены, и событие OnReplace, которое возникает при щелчке на кнопке Заменить или Заменить все.
Рис. 16.7. Стандартное окно компонента TReplaceDialog
TPageSetupDialog — окно установки параметров печатаемой страницы Компонент T P a g e S e t u p D i a l o g создает и обслуживает окно настройки парамет ров печатаемой страницы (рис. 16.8).
Рис. 16.8. Стандартное окно компонента TPageSetupDialog Свойства и методы компонента предоставляют пользователю гибкие средства управления параметрами печатаемой страницы (ее ориентацией, размерами по лей и т. п.). С помощью обработчиков событий программист может предоставить пользователю возможность изменения указанных параметров в различных ситу ациях — перед началом печати, перед печатью очередной страницы и т. д. Под робнее о компоненте см. в справочной службе Delphi.
Разновидности форм
Формы
Форма является основным «строительным блоком» в Delphi. Любая программа (за исключением так называемой консольной) имеет как минимум одну связанную с ней форму, которая называется главной, — эта форма появляется на экране в мо мент старта программы. Однако программа может иметь сколько угодно форм, каждая из которых может решать ту или иную локальную задачу и появляться на экране по мере надобности. В этой главе мы познакомимся с назначением и спосо бами использования различных форм, а также изучим их свойства и методы.
Разновидности форм Разновидности форм определяются значениями их свойства F o r m S t y l e , а так же разнообразием форм-заготовок в хранилище объектов Delphi. Стиль формы задается одним из значений следующего свойства: T F o r m S t y l e = ( f s N o r m a l , fsMDIChild, fsMDIForm, f s S t a y O n T o p ) ; property FormStyle: TFormStyle;
Стиль f s N o r m a l определяет обычную форму, использующуюся для решения са мых разных задач, в том числе для общего управления всей программой (глав ная форма). Стили fsMDIChild и fsMDIForm используются при создании так называемых многодокументных приложений в стиле MDI (Multi Document Interface). Этот стиль предполагает создание главного окна MDI (его обычно называют рамоч ным), внутри которого по мере надобности появляются дочерние окна. Дочер ние окна, подобно дочерним элементам контейнера, не могут выходить за грани цы своего владельца — рамочного окна. В MDI-приложениях есть специальные средства управления взаимодействием рамочного окна с дочерними окнами. На пример, каждое дочернее окно в момент активизации может нужным образом
363
настроить главное меню рамочного окна (дочерние MDI-окна не имеют собствен ного главного меню). В Delphi для создания рамочного окна используется стиль fsMDIForm, а для создания дочернего MDI-окна — стиль fsMDIChild. Стиль f s S t a y O n T o p предназначен для окон, которые всегда должны распола 1 гаться над всеми другими окнами программы . В момент активизации окна оно обычно становится видимым на экране, даже если перед этим его загораживали другие раскрытые окна. Стиль f s S t a y O n T o p препятствует перекрытию окна другими окнами, даже если оно становится неактивным и теряет фокус ввода (так сказано в документации, однако, как показано далее, на самом деле это не так). Понятно, что этот стиль используется в исключительных случаях, когда окно содержит что-то, требующее повышенного внимания пользователя. Как показывает практика, объявление окна со стилевым признаком fsStayOnTop еще не решает проблему создания всплывающего окна, то есть окна, которое не возможно перекрыть другими окнами. Более того, несложные эксперименты убе дят вас, что этот признак вообще не играет никакой роли! Чтобы создать всплыва ющее окно, нужно обратиться к API-функции SetWindowPos, например: SetWindowPos(fmAlarmForm.Handle, hwnd__TopMost, 300, 300, 250, 70, swp__noActivate) Полное описание этой функции (как и всех других API-функций Windows) вы найдете в файле помощи WIN32.HLP или WIN32SDK.HLP, которые поставляются вме сте с Delphi и по умолчанию располагаются в папке Program Files\Common Files\Borland Shared\MSHelp. Здесь я лишь кратко поясню назначение параметров вызова. Первым параметром обращения к функции является дескриптор окна, которое должно стать всплывающим. Напомню, что дескриптор формы содержится в ее свойстве H a n d l e , поэтому для формы fmAlarmForm параметр вызова имеет вид fmAlarmForm.Handle. Второй параметр определяет расположение окна отно сительно других окон в стопке, то есть так называемый Z-порядок окон. Кон станта hwnd_TopMost указывает, что окно должно стать самым верхним и впредь до его закрытия не может перекрываться другими (обычными) окнами. Четыре следующих параметра определяют координаты левого верхнего угла окна, его ширину и высоту. Все параметры указываются в пикселах, координаты угла задаются относительно левого верхнего угла экрана. Последним указывается один или несколько битовых флагов, уточняющих поведение окна. В нашем примере использован флаг s w p _ n o A c t i v a t e , означающий, что окно не получает фокуса ввода в момент своего появления на экране. На практике я часто применяю всплывающие окна, чтобы сообщить пользователю, например, о необходимости обновить набор данных, которые он видит в настоящий момент, так как эти дан ные были изменены другими пользователями клиент-серверной базы данных. Такое окно, в отличие от модального окна, не должно отнимать активность (фо кус ввода) у окна, с которым работает пользователь. И последнее замечание. Всплывающим окном может стать и обычное окно (со значе нием стиля F o r m S t y l e = fsNormal). Однако всплывающее окно действительно появится на экране, только если в его свойстве V i s i b l e содержится значение True. 1
В терминологии Windows такое окно называется «Popup Window», что буквально переводится как «окно-поплавок», а в локализованных программных продуктах именуется всплывающим окном.
364
Глава 17 • Формы
Компонент TForm
Иными словами, если пользователь закроет такое окно, оно не появится в результа те вызова функции SetWindowPos — его предварительно нужно сделать видимым методом Show. Поскольку всплывающее окно может вызываться в разных местах программы, удобно поместить вызов функции SetWindowsPos в его обработчик события O n A c t i v a t e . Однако если вы попытаетесь создать такой обработчик для окна со стилем F o r m S t y l e = f sStayOnTop, то компилятор выдаст сообщение об ошибке и поместит заготовку метода в тексте модуля после ограничителя-точки ( . ) . В этом случае определяйте окно со стилем f sNormal. Современные многооконные приложения чаще всего строятся в стиле SDI (Single Document Interface). Этот стиль в противоположность интерфейсу MDI не накла дывает ограничений на положение и размеры вспомогательных форм, каждая из которых при необходимости может иметь свое главное меню. Для создания форм в этом случае используется стиль f sNormal'. Замечу, что в рамках SDI-приложений могут задействоваться рамочные MDI-формы со своими дочерними окнами, так что термин «однооконный» носит весьма условный характер и применяется в основном для противопоставления термину «многооконный». В хранилище объектов Delphi хранится множество стандартных форм-заготовок, предназначенных для решения конкретных задач (доступ к хранилищу объектов открывает команда File • New • Other). Помимо универсальной пустой формы Form (категория New Files • VCL Form хранилища объектов) хранилище содержит специа лизированные формы и классы, некоторые из них перечислены в подразделе «Ос новные инструментальные панели среды» раздела «Знакомство с Delphi 2005» вве дения.
Компонент TForm В этом разделе мы рассмотрим свойства, методы и события формы, то есть ком понента TForm. Свойства формы представлены в табл. 17.1. Таблица 17.1. Свойства формы Свойство property Active : Boolean;
Описание
Содержит значение True, если окно активно (имеет фокус ввода) property A c t i v e C o n t r o l : Определяет дочерний элемент, содержащий фокус TWinControl; ввода p r o p e r t y A c t i v e M D l C h i l d : TForm; Определяет дочернее MDI-окно с фокусом ввода p r o p e r t y AlphaBTend: B o o l e a n ; Если содержит значение True, форма определяет полупрозрачное окно (только для Windows 2000 и выше) property AlphaBlendValue : Byte; Указывает степень прозрачности окна T B o r d e r l c o n = (biSystemMenu, Определяет наличие кнопок в заголовке окна: b i M i n i m i z e , b i M a x i m i z e , b i H e l p ) ; biSystemMenu — кнопка вызова системного T B o r d e r l c o n s = s e t of , меню; b i M i n i m i z e — кнопка свертывания; TBorderlcon; property b i M a x i m i z e — кнопка развертывания; b i H e l p — Borderlcons: TBorderlcons; кнопка вызова справочной службы
365
Свойство
Описание
TFormBorderStyle = (bsNone, b s S i n g l e , b s S i z e a b l e , bsDialog, bsToolWindow, bsSizeToolWin); property B o r d e r S t y l e : TFormBorderStyle;
Определяет стиль рамки окна: bsNone — окно не имеет рамки и заголовка и не может перемещаться и изменять свои размеры; b s S i n g l e — рамка толщиной 1 пиксел, такое окно не может изменять свои размеры; b s S i z e a b l e — обычная рамка; b s D i a l o g — рамка диалогового окна, окно не может изменять свои размеры; b s T o o l W i n d o w — подобно b s S i n g l e , но с уменьшенным по высоте заголовком; b s S i z e T o o l W i n — подобно b s S i z e a b l e , но с уменьшенным по высоте заголовком
Канва для прорисовки фона окна. Это свойство могут использовать неоконные дочерние элементы property C l i e n t H e i g h t : I n t e g e r ; Высота клиентской части окна Прямоугольник клиентской части окна property C l i e n t R e c t : TRect; Ширина клиентской части окна property C l i e n t W i d t h : I n t e g e r ; Определяет монитор, на котором появляется форма: type TDefaultMonitor = dmDesktop — для формы не указан монитор; (dmDesktop, dmPrimary, d m P r i m a r y —форма появляется на первом dmMainForm, dmActiveForm) ; мониторе списка M o n i t o r s глобального объекта property Def a u l t M o n i t o r : TDefaultMonitor; S c r e e n ; dmMainForm— форма появляется на том же мониторе, что и главное окно программы; d m A c t i v e F o r m — форма появляется на том же мониторе, что и текущее активное окно Если содержит значение T r u e , форма property DropTarget: Booleanподдерживает перетаскивание (Drag&Drop) Если содержит значение F a l s e , окно «причалено» property F l o a t i n g : Boolean; к другому окну, в противном случае оно «плавает» Определяет текущее состояние формы: type TFormState = s e t of f s C r e a t i n g — окно создается; f s V i s i b l e — (fsCreating, fsVisible, окно видно на экране (это состояние используется fsShowing, fsModal, fsCreatedMDIChild, fsActivated) ; для обновления свойства V i s i b l e ) ; property FormState : TFormState; f s S h o w i n g — свойство F o r m S t a t e изменяется; f s M o d a l — создано модальное окно; f s C r e a t e d M D I C h i l d — форма представляет собой рамочное MDI-окно (устанавливается в момент создания дочернего окна); f s A c t i v a t e d — форма получила сообщение CM_ACTIVATE, но еще не возникло событие OnActivate Определяет стиль окна: f s N o r m a l — обычное type TFormStyle = (f sNormal, окно; f s M D I C h i l d —дочернее MDI-окно; fsMDIChild, fsMDIForm, f s M D I F o r m — рамочное MDI-окно; fsStayOnTop); property FormStyle: TFormStyle; f s S t a y O n T o p — всплывающее окно Каждая форма может иметь индивидуальный файл property H e l p F i l e : String; помощи, имя которого содержит это свойство. Если имя не указано, используется файл помощи приложения Содержит значок окна. Для главной формы property I c o n : TIcon; это свойство определяет также значок программы Если имеет значение T r u e , форма получает property KeyPreview: Boolean; события от клавиатуры, перед тем как они поступят в элемент с фокусом ввода
property Canvas : TCanvas ;
продолжение #
366 Глава 17 • Формы
Компонент TForm
Таблица 17.1 (продолжение)
Свойство property MDIChildCount:
Описание Integer; В рамочном MDI-окне определяет количество
property MDIChildren [N: Integer] : TForm; property Menu: TMainMenu; TModalResult = Low(Integer) . . High (Integer); property ModalResult:TModalResult; property Monitor: TMonitor;
связанных с ним дочерних MDI-OKOH
В рамочном MDI-окне открывает доступ к дочернему окну под номером N Содержит главное меню окна Для модального окна содержит результат диалога с пользователем Содержит ссылку на монитор, на котором отображается окно
propertyPixelsPerlnch:Integer; Определяет разрешающую способность окна
в пикселах на один линейный дюйм для этапа конструирования формы
TPosition = (poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly, poScreenCenter) ; property Position: TPosition;
Определяет положение и размеры окна в момент его появления на экране: p o D e s i g n e d — такие же, как на этапе конструирования окна; p o D e f a u l t — положение и размеры определяет Windows; p o D e f a u l t P o s O n l y — положение, как на этапе конструирования, размеры определяет Windows; p o D e f a u l t S i z e O n l y — размеры, как на этапе конструирования, положение определяет Windows; p o S c r e e n C e n t e r — в центре экрана с размерами, как на этапе конструирования
TPrintScale = (poNone, poProportional, poPrintToFit); property PrintScale : TPrintScale;
Определяет масштабирование окна при его печати на принтере: poNone — нет масштабирования; каждый пиксел окна воспроизводится одной точкой на бумаге; p o P r o p o r t i o n a l — форма масштабируется так, чтобы ее образ на бумаге был максимально похож на ее изображение на экране; p o P r i n t T o F i t — форма печатается с такими же пропорциями, как на экране, но с размерами, заполняющими лист бумаги
property Scaled: Boolean; TileMode= (tbHorizontal, tbVertical); property TileMode: TTileMode; property TransparentColor: Boolean;
Разрешает/запрещает масштабировать форму, если значение ее свойства P i x e l P e r l n c h отличается от текущего разрешения экрана Определяет стиль расположения дочерних окон MDI-приложения при их упорядочении мозаикой Если содержит значение T r u e , свойство T r a n s p a r e n t C o l o r V a l u e определяет цвет прозрачности формы, сквозь который будут видны нижележащие окна (только для Windows 2000 и выше)
property TransparentColorValue: Содержит цвет прозрачности формы (игнорируется, TColor; если T r a n s p a r e n t C o l o r = F a l s e ) property WindowMenu: TMenuItem; Определяет пункт главного меню рамочного TWindowState = (wsNormal, wsMinimized, wsMaximized); property WindowState: TWindowState;
MDI-окна, к которому добавляются пункты меню дочернего окна Определяет состояние окна в момент его появления на экране: w s N o r m a l — обычное окно; w s M i n i m i z e d — свернуто до кнопки; w s M a x i m i z e d — развернуто на весь экран
367
Два свойства, AlphaBlend и AlphaBlendValue, позволяют регулировать степень прозрачности окна формы. Если AlphaBlend = True, то значение свойства Alpha BlendValue задает степень прозрачности: 0 — окно полностью прозрачно, 255 — окно совершенно непрозрачно. Свойства T r a n s p a r e n t C o l o r V a l u e и T r a n s p a r e n t C o l o r определяют цвет прозрачности и возможность его использования. Однако указанные свойства работают только под управлением Windows 2000/XP и на процессорах Pentium с тактовой частотой не ниже 90 МГц. Замечу, что результат изменения значения AlphaBlendValue проявляется только в том случае, если видеокарта компьютера способна отображать больше 256 цве тов, то есть использует два (High Color) или более (True Color) байта для коди рования цвета одного пиксела. Свойства T r a n s p a r e n t C o l o r V a l u e и T r a n s p a r e n t C o l o r игнорируются, если цвет не относится к основной 16-цветной палитре или если свойство A l p h a B l e n d содержит значение True. Показываемое окно действительно прозрачно — сквозь него не только видны знач ки рабочего стола, но на них даже можно щелкать мышью и вызывать програм мы (если окно полностью прозрачно). ПРИМЕЧАНИЕ Если в множестве свойства B o r d e r l c o n убрать кнопки b i M i n i m i z e n b i M a x i m i z e , а в свойство W i n d o w S t a t e поместить значение w s M a x i m i z e d , форма займет все простран ство экрана, включая панель задач.
Методы формы перечислены в табл. 17.2. Таблица 17.2. Методы формы Метод
Описание
procedure A r r a n g e l c o n s ;
Упорядочивает значки закрытых дочерних окон MDI-приложения Располагает дочерние MDI-окна каскадом Закрывает окно. Для главного окна завершает работу программы Возвращает значение T r u e , если можно закрыть окно Отбирает фокус ввода у дочернего элемента C o n t r o l . Если при этом R e m o v i n g = T r u e , фокус ввода получает форма Создает структуру Params (см. далее) для указания
procedure C a s c a d e ; procedure C l o s e ; functionCloseQuery: Boolean procedure D e f o c u s C o n t r o l ( C o n t r o l : TWinControl; Removing: B o o l e a n ) ; p r o c e d u r e C r e a t e P a r a m s (var Params: TCreateParams); override; procedure F o c u s C o n t r o l (Control: TWinControl); function GetFormlmage: TBitmap; procedure Next; procedure Hide; procedure M a k e F u l l y V i s i b l e (AMonitor: TMonitor = n i l ) ;
стилевых признаков создаваемого окна Передает фокус ввода дочернему элементу C o n t r o l Содержит текущее изображение окна формы Делает активным следующее MDI-OKHO Скрывает окно Проверяет, полностью ли умещается форма на мониторе, и при необходимости изменяет ее положение так, чтобы у нее не было частей на других мониторах продолжение •&
Компонент TForm
368 Глава 17 • Формы Таблица 17.2 (продолжение) Метод
Описание
procedure MouseWheelHandler (var Message: TMessage); override ; procedure Previous; procedure Print; procedure Release;
Вызывается автоматически при получении сообщения от колесика мыши. По умолчанию ничего не делает
procedure SendCancelMode (Sender: TControl); procedure SetFocus; function SetFocusedControl (Control: TWinControl): Boolean; virtual; procedure Show; function ShowModal: Integer;
unit U n i t l ; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForml = class(TForm) private { Private declarations } protected // Перехват сообщения WM_NCHITTEST для перемещения // окна без заголовка: procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST; public { Public declarations } procedure CreateParams(var Params: TCreateParams); override; end;
Делает активным предыдущее MDI-OKHO Печатает окно на принтере Ожидает окончания обработки всех событий формы и ее дочерних элементов, после чего удаляет окно и освобождает всю связанную с ним память Восстанавливает начальное состояние окна: освобождает мышь, прекращает прокрутку и закрывает меню Передает фокус ввода форме. Форма при этом должна быть активной и видимой Передает фокус ввода указанному элементу
Показывает форму в немодальном режиме Показывает форму в модальном режиме и возвращает результат диалога с пользователем Располагает дочерние MDI-окна мозаикой procedure Tile; function WantChildKey (Child: Этот метод вызывается любым размещенным на форме компонентом в момент, когда он получает фокус ввода. TControl; var Message: TMessage): Boolean; virtual; Если функция возвращает значение T r u e , весь клавиатурный ввод переназначается форме. По умолчанию возвращает значение F a l s e Структура Params в методе C r e a t e P a r a m s описывается следующим типом: type TCreateParams record Caption: PChar; // Заголовок окна Style: DWORD; // Стилевые флаги ExStyle: DWORD; // Дополнительные стилевые флаги X, Y: Integer; // Координаты левого // верхнего угла Width, H e i g h t : I n t e g e r ; / / Размеры W n d P a r e n t : HWND; // Дескриптор владельца Param: P o i n t e r ; // Указатель на параметры // для сообщения WM_CREATE WindowClass: TWndClass; // Класс окна WinClassName: a r r a y [ 0 . . 6 3 ] of C h a r ; // Имя оконного класса end; Этот метод следует перекрыть, если вы хотите создать окно нестандартного типа. Например, набор возможных значений свойства B o r d e r S t y l e не предполагает создания окна с «толстой» рамкой, но без заголовка. Однако если в свойство P a rams . S t y l e поместить значение WSJTHICKFRAME or WS__POPUP, такое окно бу дет создано. В следующем примере создается окно без заголовка, которое тем не менее можно перетаскивать мышью:
369
var F o r m l : TForml; implementation I
{$R *.DFM) procedure TForml.WMNCHitTest(var Message: TWMNCHitTest); begin // Результат HTCAPTJON означает, что указатель // мыши находится на заголовке. Это заставляет // Windows перемещать окно: Message.Result := HTCAPTION; end; procedure TForml.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); // Окно с рамкой (WSJTHICKFRAME), // но без заголовка (WS_POPUP): Params.Style := WS_THICKFRAME or WS_POPUP; end; end. Сообщение WM_NCHITTEST посылает Windows в момент создания окна при любом изменении его нерабочей области (заголовка, рамки, полос прокрутки и т. п.), а так же при перемещении на нем указателя мыши. Обычно программы его не обрабаты вают, но в нашем случае обработчик этого события «обманывает» Windows и сооб щает операционной системе, что указатель перемещается на области заголовка.
Создание и использование форм
3 7 0 Глава 17 • Формы События формы перечислены в табл. 17.3. Таблица 17.3. События формы Событие
Описание
Возникает в момент активизации окна property OnActivate : (при получении им фокуса ввода) TNotifyEvent; type TCanResizeEvent =procedure Возникает при изменении размеров окна. (Sender: TObject; var NewWidth, Обработчик может указать новые размеры NewHeight: Integer; var Resize: или запретить их изменение, поместив в свойство R e s i z e значение F a l s e Boolean) of object; property OnCanResize: TCanResizeEvent; TCloseAction= (caNone, caHide, Возникает перед закрытием окна. Параметр A c t i o n уточняет необходимые действия: caFree,caMinimize); caNone — не закрывать окно; c a H i d e — TCloseEvent =procedure спрятать окно; c a F r e e — удалить окно; (Sender: TObject; var Action: c a M i m i m i z e — свернуть окно TCloseAction) of object; property OnClose: TCloseEvent; Возникает перед закрытием окна. В параметре TCloseQueryEvent = procedure (Sender: TObject; var CanClose: CanClose обработчик сообщает о возможности закрытия окна Boolean) of object; property OnCloseQuery: TCloseQueryEvent, Возникает при двойном щелчке на любом property OnDblClick: расположенном на форме компоненте TNotifyEvent; Возникает при передаче активности (фокуса ввода) property OnDeactivate : другому окну TNotifyEvent; Возникает перед удалением окна property OnDestroy: TNotifyEvent; Возникает перед исчезновением окна property OnHide : TNotifyEvent; property OnPaint: TNotifyEvent; Возникает при необходимости прорисовки окна property OnResize : TNotifyEvent, Возникает при изменении размеров окна Возникает до события OnKeyDown и предназначено TShortCutEvent =procedure для перекрытия стандартной обработки нажатия (varMsg: TWMKey; var Handled: клавиш быстрого вызова Boolean) of object; property OnShortCut: TShortCutEvent; property OnShow: TNotifyEvent; Возникает при появлении окна на экране
Создание и использование форм Для подключения новой формы к проекту достаточно обратиться к хранилищу объек тов и выбрать нужную разновидность формы. Менеджер проекта автоматически под ключает новую форму к списку используемых форм и обеспечивает все необходи мые действия по ее инициализации. Самая первая подключенная к проекту форма (стандартное имя формы— Forml) становится главным окном программы. Окно этой формы автоматически появляется на экране в момент старта программы. Впро чем, программист может указать любую форму, окно которой станет главным. Для этого нужно обратиться к команде Project • Options, в открывшемся окне выбрать ка тегорию Forms и в списке Main form указать нужную форму (рис. 17.1). Каждое следующее окно становится видимым только после обращения к его мето ду Show или ShowModal. Чтобы обратиться к этим методам, нужно сослаться на объект-окно, который автоматически объявляется в интерфейсном разделе связан-
371
ного с окном модуля. Для этого, в свою очередь, главное окно должно знать о су ществовании другого окна, что достигается ссылкой на модуль окна в предложе нии u s e s . Если, например, в ходе выполнения одного из методов главного окна программист захочет вызвать окно с именем fmForm, связанное с модулем FormUnit, он должен сослаться на этот модуль в предложении u s e s главного окна: implementation uses FormUnit;
Рис. 17.1. Окно управления проектом После этого можно вызвать окно на экран, добавив в программу один из следую щих операторов: fmForm.Show; fmForm.ShowModal; Delphi автоматизирует вставку ссылки на модуль в предложение u s e s . Для этого на этапе разработки нужно активизировать главное окно, щелкнув на нем мышью, после чего обратиться к команде File • Uses Unit и в появившемся диалоговом окне выбрать модуль и щелкнуть на кнопке ОК. Вставляется ссылка в предложение, сто ящее за зарезервированным словом Implementation, так как обычно главное окно в своей интерфейсной части не ссылается на элементы интерфейсной части второго окна. Точно так лее можно при необходимости сослаться в модуле второго окна на модуль главного окна: активизируйте второе окно и вновь выберите ко манду File • Uses Unit. Замечу, что, если программист забудет сослаться на модуль, подключенный к проекту, Delphi при первой же трансляции программы сообщит °б этом и предложит вставить недостающую ссылку. При вызове метода Show второе окно появляется на экране и работает вместе с первым, поэтому управление сразу передается оператору, стоящему за обраще нием к этому методу. Такие окна называются немодальными, они всегда откры ваются в одном методе, а закрываются в другом. В отличие от этого обращение к методу ShowModal создает модальное окно, которое полностью берет на себя
3 7 2 Глава 17 • Формы дальнейшее управление программой, поэтому оператор, расположенный следом за обращением к методу ShowModal в вызывающей части программы, получит управление только после закрытия модального окна. Модальные окна часто требуют от пользователя принятия какого-либо решения. С их помощью реализуется диалог с пользователем или создается информационное окно, которое пользователь должен закрыть после ознакомления с содержащейся в нем информацией. Если от пользователя требуется принятие решения, в модаль ное окно вставляются флажки, переключатели, кнопки и другие интерфейсные эле менты, с помощью которых пользователь может сообщить программе о принятом решении. В момент завершения диалога модальное окно должно поместить число, соответствующее решению пользователя, в свое свойство Modal R e s u l t . Некото рые стандартные кнопки (OK, Yes, No, Cancel и т. п.) автоматически выполняют эти действия: помещают нужное число в свойство M o d a l R e s u l t и закрывают окно. В других случаях об этом должен позаботиться программист. Вызывающая программа получает значение свойства M o d a l R e s u l t как возвращаемое значение функции ShowModal и может тут же его проанализировать:
Программа
if Form2.ShowModal = mrXXX then ... Возможен и такой вариант: Form2.ShowModal ; if Form2.ModalResult = mrXXX then ... Для закрытия окна (модального или немодального) используется метод Hide или C l o s e . Следует учесть, что метод C l o s e всегда помещает в свойство M o d a l R e s u l t значение 2 (mrCancel), в то время как метод Hide не меняет значения этого свой ства, поэтому, если программист хочет передать в вызывающую программу нестан дартный модальный результат, следует ввести в программу следующие операторы: ModalResult := MyResult; H i d e ; // Но ни в коем случае
Close!
По умолчанию среда Delphi настроена так, что при подключении нового окна к проекту менеджер проекта размещает его имя в списке автоматически создава емых окон (на рис. 17.1 это список Auto-Create forms). В этом случае программист может не заботиться об инициализации соответствующего объекта окна. Однако если в программе используется множество окон, их автоматическое создание в мо мент старта программы может существенно затянуть процесс ее загрузки. В то же время немедленное создание всех окон вовсе не обязательно, так как вряд ли вам понадобится одновременно показывать их на экране в немодальном режиме. Профессиональные программисты никогда не создают все оконные объекты в мо мент старта программы, а делают это по мере надобности. Для этого сбрасывает ся флажок Auto create forms & data modules на вкладке VCL Designer окна Options (открывается командой Tools • Options) или ссылки на эти формы из списка Autocreate Forms в окне менеджера проекта (см. рис. 17.1) переносятся в список Available forms, а обращение к окну реализуется так: if not Assigned(Form2) then
//
Проверяем,
//
оконный
создан ли
объект
Form2 := TForm2.Create(Self); // Нет — создаем его if Form2.ShowModal = mrXXX then ... // и используем
Конечной целью программиста, работающего в Delphi, является создание про граммы — исполняемого файла, который может загружаться и выполняться под управлением 32-разрядной версии Windows. На самом деле программа выполня ется под управлением общеязыковой среды выполнения CLR, которая взаимо действует с базовой операционной системой (не обязательно Windows)1. Посколь ку технология .NET сейчас шире всего применяется в Windows, в этой главе рассматриваются вопросы, относящиеся к Windows-программе в целом. В главе, в частности, описываются многочисленные вспомогательные файлы, связанные с программой, доступные программе глобальные объекты, вопросы ее динами ческой настройки с помощью файлов инициализации и/или системного реестра. В конце главы приводятся некоторые сведения о 32-разрядной версии Windows и механизмах взаимодействия программы с этой, операционной системой.
Программные файлы 2
Delphi создает множество файлов, связанных с одной программой . Перед тем как начать их рассмотрение, хочу порекомендовать следовать золотому правилу: для каждого проекта выделять отдельную папку и хранить в ней все относящие ся к проекту файлы; это значительно упростит перенос проекта в другую папку, например, на гибкий диск и затем с него на другую машину.
Это справедливо при использовании технологии .NET. Напомню, что среда Delphi 2005 способна создавать и «обычные» программы. Речь идет о полноценной Windows-программе; при разработке консольных приложений, библиотек DLL, собственных модулей и в некоторых других специальных случаях может быть создан един ственный файл.
374
Глава 18 • Программа
Программные файлы
Файл проекта Файл проекта имеет расширение DPR. Обычно он не виден в окне кода — чтобы его увидеть, следует воспользоваться командой Project • View source среды Delphi. Если закрыть файл проекта с помощью команды File • Close или клавиш Alt+F4, вместе с ним закроются и все остальные формы проекта. Чтобы этого не про изошло, используйте клавиши Ctrl+F4 или команду Close Page контекстного меню файла проекта. Файл проекта содержит код главной программы, с помощью которого создается объект главной формы программы и, возможно, объекты некоторых других форм, а также обеспечивается связь программы с ядром Windows. Далее показан ти пичный пример файла проекта, содержащего единственное окно формы: program P r o j e c t ! . ; /%DelphiDotNetAssemblyCompi1er ' $(SystemRoot) \microsoft.net\framework\vl.1.4322\System.dll'} {%DelphiDotNetAssemblyCompiler '$ (SystemRoot) \microsoft.net\framework\vl.1.4322\ ' + 'System.Data.dll'} {%DelphiDotNetAssemblyCompller ' $(SystemRoot) \microsoft.net\framework\vl.l.4322\ ' + ' System.Drawing.dll') (%DelphiDotNetAssemblyCompiler '$ (SystemRoot)\microsoft.net\framework\vl.1.4322\System.XML.dll
'}
В предложении u s e s перечисляются все (или лишь некоторые) связанные с про ектом формы. Помимо стандартного модуля Forms, необходимого для создания главного окна программы, в предложении u s e s указывается также модуль U n i t l , связанный собственно с главным окном. Обратите внимание на использование зарезервированного слова in для указания файла с текстом модуля (' U n i t l . p a s ') и комментария {{Forml}), который именует объект-окно, создаваемый модулем U n i t l . Такого рода объявления Delphi автоматически создает для каждого вклю ченного в проект модуля. Только перечисленные в этом предложении модули Delphi считает входящими в проект, и их алфавитный список появляется при выборе ко манды View • Units; а при выборе команды View • Forms отображается список всех перечисленных в комментариях оконных объектов.
ПРИМЕЧАНИЕ Настроечный DSK-файл, в котором среда сохраняет информацию о состоянии экрана в момент выхода из Delphi, также содержит полные пути доступа к открытым файлам. При переносе про екта этот файл не следует копировать. За предложением u s e s следует ссылка на автоматически создаваемый файл ре сурса: {$R
*.res}
Завершает подготовительную часть файла проекта обширный текст, который обычно сворачивается до единственной строки
*.res}
{$REGION 'Program/Assembly {$ENDREGION}
Сразу за объявлением программы перечисляются директивы компилятора, пред писывающие ему использовать важнейшие пространства имен технологии .NET Framework.
Следует учесть, что если вы хотите перенести проект в другую папку и при этом сохранить его работоспособность, нужно сначала с помощью команды File • Save Project As скопировать в эту папку файл проекта, а затем с помощью команды File • Save As перенести туда все связанные с проектом модули: только в этом слу чае Delphi сумеет внести необходимые корректировки в файл проекта. Но если все файлы хранятся в единственной папке, в предложении u s e s не указываются пути доступа и вы можете безболезненно разом скопировать все файлы в другую папку.
uses System.Reflection, System.Runtime.CompilerServices, SysUtils, Forms, Unitl in 'Unitl.pas' {Forml}; {$R
375
Information')
[STAThread] begin Application.Initialize; Application.CreateForm(TForml, Application.Run; end.
Forml);
Зарезервированное слово program открывает файл проекта; оно может встре титься в программе лишь один раз.
Program/Assembly Information В этом тексте указывается дополнительная информация о программе в виде ком ментариев и стандартных атрибутов такого рода: [assembly: [assembly: [assembly: [assembly: [assembly: [assembly: [assembly: [assembly:
AssemblyDescription('')] AssemblyConfiguration ( ' ')] AssemblyCompany('')] AssemblyProduct('')] AssemblyCopyright(")] AssemblyTrademark('')] AssemblyCulture('')] AssemblyVersion('1.0.*')]
3 7 6 Глава 18 • Программа
Программные файлы 3 7 7
С помощью этих атрибутов программист может указать описание сборки (про граммы), ее конфигурацию, имя компании-разработчика и имя самой программы, авторские права, торговую марку, сведения о локализации продукта и его версию. В разделе исполняемых операторов формируются как минимум три оператора: вызовы методов I n i t i a l i z e , C r e a t e F o r m и Run глобального объекта A p p l i cation.
В следующем примере (листинг 18.1) в проекте используются две формы: стан дартная форма I n p u t Q u e r y и обычная главная форма MainForm. Форма I n p u t Query создается при обращении к одноименной функции, определенной в модуле D i a l o g s . Она представляет собой небольшое диалоговое окно с однострочным текстовым полем T E d i t и двумя кнопками — ОК и Cancel. В окне пользователь дол жен ввести пароль ( D e l p h i ) и нажать клавишу Enter.
Метод I n i t i a l i z e предусмотрен на всякий случай и по умолчанию ничего не делает. Чтобы заставить его работать, следует поместить указатель на соответ ствующую процедуру без параметров в глобальную переменную I n i t P r o c .
Листинг 18.1. Защита программы паролем program Password;
ПРИМЕЧАНИЕ В рамках технологии .NET доступ к переменной I n i t P r o c закрыт, и поэтому использовать процедуру инициализации нельзя.
uses Forms, Dialogs, Unitl in
В подавляющем большинстве случаев оператор A p p l i c a t i o n . I n i t i a l i z e мож но удалить без каких-либо последствий для программы. Следует учесть, что до оператора A p p l i c a t i o n . R u n можно вставлять любые другие операторы Delphi. В приводимом далее примере в программе создается пять окон. В форме Form5 имеется компонент P r o g r e s s B a r l , с помощью кото рого визуализируется процесс загрузки программы, точнее — процесс создания остальных окон: begin with TForm5.Create(nil) do try ProgressBarl.Max := 100; Show; // Показываем форму Form5 // с индикатором ProgressBar Application.CreateForm(TForml, Forml); ProgressBarl.StepBy(25); Application.CreateForm(TForm2, Form2); ProgressBarl.StepBy(25); Application.CreateForm(TForm3, Form3); ProgressBarl.StepBy(25) ; Application.CreateForm(TForm4, Form4); ProgressBarl.StepBy(25); finally Free; // Удаляем ненужную форму FormS end; Application.Run; end. С помощью метода A p p l i c a t i o n . R u n программа подключается к циклу обра ботки сообщений Windows. Выход из метода возможен лишь после закрытия главного окна программы. Программист может показать диалоговое окно с запросом пароля и блокировать вызов метода A p p l i c a t i o n . R u n , если введенный пользователем пароль неверен.
// В этом модуле определена 'Unitl.pas' {MainForm};
функция
InputQuery
{$R *.RES} var Passwrd: String; begin // Запрашиваем пароль: if InputQuery('Окно ввода пароля', 'Введите пароль:',Passwrd) then // Проверяем его: if Passwrd='Delphi' then begin // Все в порядке, пароль верен Application.CreateForm(TMainForm, Application.Run; end else ShowMessage('Пароль не верен!'); end.
MainForm);
Таким же способом можно создавать пробные версии программ, которые будут функционировать только до определенной даты или до исчерпания заданного количества запусков. С проектом может быть не связано ни одного видимого окна. Такие программы называются консольными. Они используются для проведения работ, не требую щих диалога с пользователем и соответствующих средств ввода-вывода инфор мации. Например, они могут пассивно ждать наступления определенного часа (обычно ночью), чтобы провести резервное копирование базы данных. Консольные программы не вызывают методы C r e a t e F o r m и Run объекта A p p l i c a t i o n . Вместо этого в раздел исполняемых операторов файла проекта помеща ется код, который реализует необходимые действия. Для создания консольного приложения нужно выбрать значок Console Application на вкладке New окна хранилища объектов. В ответ Delphi создаст файл проекта следующего вида:
3 7 8 Глава 18 • Программа
Глобальные объекты
program Project!.; {$APPTYPE
нии в них неправильных изменений компиляция проекта может сопровождаться сообщением «Программа выполнила недопустимую операцию и будет закрыта», после чего Delphi выгружается из памяти. В такого рода случаях следует просто удалить все три настроечных файла и повторить компиляцию — Delphi создаст новые настроечные файлы с параметрами по умолчанию.
CONSOLE}
uses SysUtils; begin {
TODO
-oUser
379
-cConsole Main
:
Insert
code
here
jend.
Директива {$APPTYPE CONSOLE} дает в распоряжение программы «консоль ные» средства ввода-вывода. В этом случае программа автоматически исполня ется в окне MS-DOS, и с помощью операторов ReadLn и W r i t e L n программист может ввести что-то с клавиатуры и вывести на экран так, как если бы програм ма работала под управлением MS-DOS. Если программе не нужны даже такие средства общения с внешним миром, эту директиву следует удалить. Несмотря на полное отсутствие видимых окон или их замену окном MS-DOS, консольная программа остается полноценным 32-разрядным приложением, и пос ле ссылки на нужные модули ей доступны глобальные объекты, средства типа I n p u t Q u e r y , ShowMessage и т. п.
Файлы резервных копий При внесении в проект изменений Delphi обычно создает резервные копии из мененных файлов. Эти копии имеют расширения, начинающиеся символом тиль да (~). Они создаются на всякий случай и содержат состояние проекта и/или модулей до внесения изменений. От создания резервных копий можно отказать ся, если сбросить флажок Create backup file на вкладке Display диалогового окна Editor Options (открывается командой Tools • Editor Options).
Глобальные объекты
Файлы с текстами модулей имеют расширения PAS. После компиляции для каж дого такого файла создается одноименный файл машинного кода с расширением DCUIL. Если модуль содержит объект-окно, на этапе конструирования создается и автоматически изменяется файл описания формы с расширением NFM (файл будет реально создан после сохранения модуля на диске). PAS- и NFM-файлы жизненно важны для проекта: удаление любого из них приведет к невозможнос ти повторной компиляции проекта.
С любой запущенной программой автоматически связываются от двух до пяти глобальных объектов: A p p l i c a t i o n (программа), S c r e e n (экран), P r i n t e r (принтер), S e s s i o n (сеанс) и C l i p b o a r d (буфер обмена). Все они объявлены как глобальные переменные в наиболее часто используемых модулях: A p p l i c a t i o n и S c r e e n — в модуле B o r l a n d . VCL. Forms, P r i n t e r — в модуле B o r l a n d . VCL. P r i n t e r s , S e s s i o n — в модуле B o r l a n d . VCL. DBTables и, нако нец, C l i p b o a r d — в модуле B o r l a n d . VCL. C l i p b r d . Создание этих объектов реализуется в секциях инициализации соответствующих модулей, так что к мо менту начала исполнения первого оператора файла проекта они уже готовы к ра боте.
Файлы ресурсов
Объект Application
Файл проекта содержит директиву компилятора {$R *. RES}, с помощью кото рой на этапе компоновки к исполняемому файлу присоединяется файл ресурсов. Последний имеет имя, совпадающее с именем проекта, и расширение RES. Он создается автоматически для каждого проекта. В него Delphi помещает значок программы, номер ее версии и тому подобные данные. В этом файле не следует размещать никаких дополнительных ресурсов (указателей, строк, значков и т. п.), так как при каждой компиляции файл ресурсов создается заново. Если в прог рамму необходимо ввести дополнительные ресурсы, их нужно разместить в от дельном файле и вставить в файл проекта соответствующую директиву.
Объект A p p l i c a t i o n относится к классу T A p p l i c a t i o n и инкапсулирует важ нейшие свойства и методы программы как таковой. Он создается автоматически и доступен любой программе1. Объекта A p p l i c a t i o n нет в палитре компонен тов, поэтому его свойства недоступны на этапе конструирования, но становятся 2 доступными при прогоне программы .
Файлы модулей
Объект A p p l i c a t i o n является посредником между программой и операцион ной системой Windows. В частности, с его помощью осуществляется диспетче ризация сообщений Windows, реализуется контекстная справочная служба, он обрабатывает нажатие клавиш быстрого доступа, исключительные ситуации и т. д. Свойства класса T A p p l i c a t i o n перечислены в табл. 18.1.
Файлы настройки
Для обычных Windows-приложений файл проекта ссылается на модуль Forms, поэтому объекты Application и Screen действительно всегда доступны.
Для проекта создаются также три вспомогательных текстовых файла, в которых сохраняются параметры компилятора (CFG), проекта (DOF) и среды (DSK). Хотя все они текстовые, их ручное редактирование нежелательно, так как при внесе-
Некоторые атрибуты программы (в том числе ее значок, сведения о версии и используемом языке) могут устанавливаться с помощью менеджера проекта на вкладке Application окна Options (откры вается командой Project • Options).
380
Глобальные объекты
Глава 18 • Программа
Таблица 18.1. Свойства класса TApplication
381
Таблица 1 8 . 2 . Методы класса TApplication
Свойство
Описание
Метод
Описание
property Active : Boolean;
Содержит значение T r u e , если любое окно программы имеет фокус ввода Если содержит значение T r u e , процедура «причаливания» (Drag&Dock) реализуется автоматически, в противном случае — только при перемещении указателя мыши с нажатой клавишей Ctrl
procedure ActivateHint (CursorPos: TPoint) ; procedure BringToFront;
Показывает оперативную подсказку в заданной координатами C u r s o r P o s позиции экрана Помещает окно, которое было активным в последний раз, поверх остальных окон Запрещает показ оперативной подсказки Создает объект-окно: вызывает конструктор класса T F o r m C l a s s и связывает созданный с его помощью объект с переменной R e f e r e n c e
property AutoDragDocking: Boolean;
property CurrentHelpFile: String; property ExeName : String; property HelpFile: String; property HelpSystem: IHelpSystem; property Hint: String; property HintColor: TColor; property HintHidePause : Integer; property HintPause: Integer;
property HintShortCuts : Boolean; property HintShortPause: Integer; property Icon: TIcon; property Ma in Form: TForm; property ShowHint: Boolean; property ShowMainForm: Boolean; property Terminated: Boolean
property Title: String; property UpdateFormatSettings: Boolean;
property UpdateMetricSettings: Boolean;
Определяет имя текущего справочного файла Содержит полное имя (с маршрутом доступа) исполняемого файла программы Определяет имя файла справочной службы Открывает доступ к интерфейсу справочной системы для непосредственного взаимодействия с менеджером справочной службы Содержит длинную часть оперативной подсказки Определяет цвет фона окна оперативной подсказки Определяет паузу (в миллисекундах) от момента появления оперативной подсказки до ее исчезновения Определяет паузу (в миллисекундах) от момента остановки указателя мыши до появления оперативной подсказки Если содержит значение T r u e , текст оперативной подсказки автоматически дополняется информацией о клавишах быстрого доступа Определяет паузу (в миллисекундах) появления оперативной подсказки при переходе с одного компонента на другой Определяет значок программы Определяет главное окно программы Разрешает/запрещает показ оперативной подсказки для всех окон программы Разрешает/запрещает показ главного окна в момент старта программы Содержит значение T r u e , если программа должна прекратить работу. Используется в приложениях с интенсивным счетом для контроля необходимости прекращения дальнейшей работы Определяет текст на значке свернутой программы Если содержит значение T r u e , программа автоматически будет учитывать новые параметры формата, установленные в Windows ^например, новый разделитель целой и дробной частей числа) при их изменении в момент работы программы Если содержит значение T r u e , программа автоматически будет учитывать новые метрические установки Windows (например, новый системный шрифт) при их изменении в момент работы программы
Методы класса T A p p l i c a t i o n перечислены в табл. 18.2.
procedure CancelHint; procedure CreateForm (FormClass: TFormClass; var Reference) ; procedure HandleException ( Sender: TObject); procedure HandleMessage;
function HelpCommand (Command: Word; Data: Longlnt): Boolean; function HelpContext (Context: THelpContext): Boolean; function HelpJump(const JumpID: String): Boolean;
Осуществляет обработку исключительных ситуаций, заданную по умолчанию. Используется при разработке новых компонентов Приостанавливает работу программы до тех пор, пока не будут обработаны все сообщения, после чего генерирует событие On I d l e Посылает справочной службе команду Command и параметр D a t a Вызывает раздел Context справочной службы и возвращает значение T r u e , если такой раздел существует Вызывает раздел справочной службы по его идентификатору JumpID и возвращает значение T r u e , если такой раздел существует
procedure HideHint; function MessageBox(Text, Caption: PChar; Flags: Longlnt): Integer;
Убирает текущую оперативную подсказку
procedure Minimize; procedure NormalizeTopMosts;
Свертывает программу до кнопки на панели задач Приостанавливает действие стиля f s T o p M o s t (поместить окно поверх остальных) для всех окон программы, кроме главного
Создает и показывает диалоговое окно с текстом и кнопками: T e x t — сообщение в окне; C a p t i o n — заголовок окна. Назначение параметра F l a g s и возвращаемое значение описаны далее
procedure Распространяет действие метода N o r m a l i z e T o p M o s t s на главное окно программы NormalizeAllTopMosts; procedure ProcessMessages, То же, что метод HandleMessage, но событие Onldle не генерируется procedure Restore; Восстанавливает первоначальные размеры всех окон программы, открытые до ее свертывания procedure RestoreTopMosts, Восстанавливает действие стиля f s T o p M o s t для всех окон программы procedure Run ; Запускает цикл получения и обработки Windows-сообщений procedure ShowException Реализует обработку исключительных ситуаций, заданную (E: Exception); по умолчанию Procedure Terminate; Завершает работу программы помощью методов H a n d l e E x c e p t i o n и P r o c e s s M e s s a g e s программа может временно приостановить свою работу до тех пор, пока не будут обработаны все предназначенные ей сообщения Windows. Это бывает необходимо для обновления видимых компонентов в процессе отображения длительного по времени цикла ра-
3 8 2 Глава 18 • Программа
Глобальные объекты
боты. Если, например, программа изменит текст надписи (компонента TLabel) или состояние индикатора процесса (компонента T P r o g r e s s B a r ) , сообщение об этом поступит в очередь сообщений и останется в ней, пока не закончится теку щая работа. Вызов метода H a n d l e E x c e p t i o n или P r o c e s s M e s s a g e s в этом случае позволит надписи или индикатору немедленно обновить свой вид и отобразить новую фазу процесса. Разница между методами H a n d l e E x c e p t i o n и P r o c e s s Messages состоит в том, что первый после очистки очереди сообщений вызывает обработчик фонового события O n l d l e , а второй не делает этого (о назначении события O n l d l e см. далее). Функция MessageBox дает удобное средство вывода на экран небольшого со общения. Параметр F l a g s может быть произвольной комбинацией следующих значений: • m b _ A b o r t R e t r y l g n o r e — в окно вставляются кнопки Abort, Retry, Ignore; • rnb_ApplModal — пользователь должен закрыть это окно перед тем, как про должить работу с окном, его породившим; однако он может переходить в дру гие окна и работать с ними (этот признак устанавливается по умолчанию, если не определены ни mb_SystemModal, ни mb_TaskModal);
383
функция MessageBox возвращает следующие значения: • • •
i d A b o r t — был щелчок на кнопке Abopt; i d C a n c e l — был щелчок на кнопке Cancel; i d l g n o r e — был щелчок на кнопке Ignore;
•
idOk — был щелчок на кнопке ОК;
• • •
idNo — был щелчок на кнопке No; i d R e t r y — был щелчок на кнопке Retry; idYes — был щелчок на кнопке Yes.
Альтернативой функции MessageBox является глобальная функция M e s s a g e Dig, объявленная в модуле D i a l o g s : f u n c t i o n M e s s a g e D l g ( c o n s t Msg: S t r i n g ; AType: TMsgDlgType; A B u t t o n s : T M s g D l g B u t t o n s ; H e l p C t x : L o n g i n t ) : Word;
• mb_DefButton2 — кнопкой по умолчанию является кнопка 2;
Эта функция имеет параметр HelpCtx, с помощью которого создаваемое ею окно можно легко связать со справочной службой. В отличие от окна MessageBox, заголовок окна MessageDlg не может изменяться программистом — он опреде ляется типом окна, задаваемым параметром АТуре со следующими возможными значениями:
•
• mtWarning — значок в виде восклицательного знака, заголовок Warning;
• m b _ D e f B u t t o n l — кнопкой по умолчанию является кнопка 1; mb_DefButton3 — кнопкой по умолчанию является кнопка 3;
• mb_DefButton4 — кнопкой по умолчанию является кнопка 4;
• m t E r r o r — значок в виде знака Стоп, заголовок E r r o r ;
• mb_Help — в окно вставляется кнопка Help;
• m t l n f o r m a t i o n — значок в виде буквы i, заголовок I n f o r m a t i o n ;
• m b _ I c o n A s t e r i s k — в окно вставляется значок в виде строчной буквы г в кружке;
• mtConf i r m a t i o n — значок в виде вопросительного знака, заголовок Confirm;
• m b _ I c o n E x c l a m a t i o n — в окно вставляется значок в виде восклицательного знака;
• mtCustom— нет значка, заголовок содержит имя исполняемого файла про граммы.
• mb_lconHand — в окно вставляется значок в виде знака Стоп;
Набор кнопок задается в виде конструктора множества и может включать:
• m b _ I c o n I n f o r m a t i o n — то же, что и m b _ I c o n A s t e r i s k ; • m b _ I c o n Q u e s t i o n — в окно вставляется значок в виде знака вопроса;
• mbYes — кнопка Yes (результат, возвращаемый функцией при щелчке на этой кнопке— mrYes);
• m b _ I c o n S t o p — то же, что и mb_IconHand;
• mbNo — кнопка No (mrNo);
• mb_OK — в окно вставляется кнопка ОК;
• mbOk - кнопка OK (mrOk);
• mb_OkCancel — в окно вставляются кнопки ОК и Cancel;
• mbCancel — кнопка Cancel (mrCancel);
• m b _ R e t r y C a n c e l — в окно вставляются кнопки Retry и Cancel;
• mbHelp — кнопка Help (после щелчка на этой кнопке окно не закрывается);
• mb_SystemModal — все программы приостанавливают свою работу, пока пользователь не закроет окно;
• mbAbort — кнопка Abort (mrAbort);
• mb_TaskModal — приостанавливается работа во всех окнах данной програм мы до тех пор, пока пользователь не закроет окно;
• mbAll - кнопка All (mrAll).
• mb_YesNo — в окно вставляются кнопки Yes и No; •
mb_YesNoCancel — в окно вставляются кнопки Yes, No и Cancel.
Пример: Application.MessageBox('Текст в окне', mb I c o n Q u e s t i o n + mb YesNo);
'Заголовок окна',
• m b l g n o r e — кнопка Ignore ( m r l g n o r e ) ; В отличие от окна MessageBox, раскрытие окна MessageDlg не сопровождает ся системным звуком. События класса T A p p l i c a t i o n представлены в табл. 18.3. оытие 0 n I d l e возникает всякий раз, когда программа обнаруживает, что ее очередь сообщений пуста. Обработчик этого события может незаметно для пользователя выполнить вспомогательную работу, например проверить орфо-
3 8 4 Глава 18 • Программа
Глобальные объекты
графическую правильность только что введенного слова или собрать и обрабо тать статистические данные. Важно помнить, что выполняемая в обработчике работа не может занимать много времени, иначе программа перестанет реаги ровать на запросы пользователя. Если фоновая работа отнимает много време ни, ее следует разбить на сравнительно небольшие фрагменты или периодичес ки вызывать в ней метод H a n d l e M e s s a g e . Обработчик в параметре Done сообщает о необходимости продолжить фоновую работу (Done = F a l s e ) или о ее завершении (Done = T r u e ) . В первом случае программа просмотрит оче редь сообщений и после ее очистки вернет управление обработчику O n l d l e . Во втором случае программа будет ожидать поступления новых сообщений. Таблица 1 8 . 3 . События класса TApplication Событие
Описание
property OnActivate : TNotifyEvent; property OnDeactivate : TNotifyEvent; TExceptionEvent =procedure (Sender: TObject; E: Exception) of object; property OnException : TExceptionEvent ; THelpEvent= function (Command: Word; Data: Longlnt; var C a l l H e l p : Boolean): Boolean of object; property OnHelp : THelpEvent; property OnHint: TNotifyEvent;
Возникает, когда программа получает фокус ввода Возникает, когда программа теряет фокус ввода Обработчик этого события реализует обработку исключительных ситуаций, заданную по умолчанию Возникает при каждом обращении к справочной службе
TIdleEvent=procedure (Sender: TObject; var Done: Boolean) of object; property O n l d l e : TIdleEvent; TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object; property OnMessage: TMessageEvent; property OnMinimize : TNotifyEvent; property OnRestore : TNotifyEvent; property OnShowHint: TShowHintEvent;
тивно изменяется вид используемого программой указателя мыши. Как прави ло, программа устанавливает указатель в виде песочных часов перед началом длительной операции и восстанавливает заданную по умолчанию форму указа теля после ее завершения. Для этого используется свойство C u r s o r объекта Screen: Screen.Cursor := crHourGlass; // Выполняем длительную операцию: // Восстанавливаем форму, заданную по умолчанию: Screen.Cursor := c r D e f a u l t ; Наиболее важные свойства класса T S c r e e n перечислены в табл. 18.4. Таблица 18.4. Наиболее важные свойства класса TScreen Свойство
Возникает при получении программой сообщения Windows. С помощью обработчика этого события можно обработать несколько или все сообщения до того, как их получит активная форма Возникает при свертывании программы Возникает при восстановлении программы после ее свертывания Возникает при необходимости отобразить окно с оперативной подсказкой
СОВЕТ Событие O n l d l e введено в Delphi для совместимости с платформой Windows 3.x, в которой не поддерживается многопоточность. Если вам действительно нужно проделать некоторую фоно вую работу, имеет смысл поручить ее выполнение отдельному потоку команд (см. далее под раздел «Процессы и потоки» в разделе «Программа и Windows»).
Объект Screen Объект S c r e e n класса T S c r e e n инкапсулирует свойства и методы, упрощаю щие работу с дисплеем компьютера, в том числе с помощью этого объекта опера-
Описание
property ActiveControl: TWinControl;• Содержит ссылку на элемент с фокусом ввода Содержит ссылку на окно с фокусом ввода property ActiveForm: TForm; property Cursor: TCursor; Определяет текущий указатель мыши для окон программы
property Cursors [Index : I n t e g e r ] : HCursor; property DefaultKbLayout: HKL;
Возникает при необходимости отобразить длинную часть оперативной подсказки Обработчик этого события реализует фоновую работу (см. пояснения далее)
385
Открывает доступ к одному из зарегистри рованных указателей мыши по его индексу Содержит исходную раскладку клавиатуры (см. пояснения далее)
Содержит высоту экрана относительно высоты экрана первичного монитора Содержит горизонтальное положение левого property DesktopLeft: I n t e g e r ; верхнего угла экрана относительно такого же параметра экрана первичного монитора property DesktopTop: I n t e g e r ; Содержит вертикальное положение левого верхнего угла экрана относительно такого же параметра экрана первичного монитора property DesktopWidth: I n t e g e r ; Содержит ширину экрана относительно ширины экрана первичного монитора property F o n t s : T S t r i n g s ; Содержит список имен всех экранных шрифтов Содержит общее количество показываемых property FormCount: I n t e g e r на экране окон программы property Forms [Index : I n t e g e r ] : Открывает доступ к окну программы по его TForm; индексу property H e i g h t : I n t e g e r ; Содержит высоту экрана в пикселах property H i n t F o n t : TFont; Содержит шрифт для оперативной подсказки property IconFont: TFont; Определяет шрифт для надписи на кнопке свернутой программы property MenuFont: TFont; Содержит шрифт для команд меню property MonitorCount: I n t e g e r Содержит количество мониторов property Monitors [Index : I n t e g e r ] : Открывает индексированный доступ к монитору
property DesktopHeight: I n t e g e r ;
TMonitor;
property P i x e l s P e r l n c h : I n t e g e r ; property Width: I n t e g e r ; 13 Зак. 126
Содержит разрешающую способность экрана в пикселах на линейный дюйм Содержит ширину экрана в пикселах
386
Глава 18 • Программа
Глобальные объекты
Программа с помощью API-функции A c t i v a t e K e y b o a r d L a y o u t может изме нить раскладку клавиатуры. Свойство D e f a u l t K b L a y o u t позволяет восстано вить прежнюю раскладку. Следующая программа всегда запускается с русскоя зычной раскладкой клавиатуры: program
Projectl;
uses Forms, Unitl in 'Unitl.pas'
{$R
{Forml}, Windows;
*.resj
begin if Screen.DefaultKbLayout = 67699721 then ActivateKeyboardLayout(HKL_NEXT,KLF_REORDER); Application.CreateForm(TForml, Forml); Application.Run; ActivateKeyboardLayout (Screen.DefaultKbLayout,KLF_REORDER); end. При функционировании под управлением Windows 2000/ХР программа может одновременно работать с множеством мониторов, которые в совокупности обра зуют первичный монитор, составленный из остальных слева направо и сверху вниз. Ряд свойств DesktopXXX определяют положение и размеры текущего мо нитора в координатах первичного. Если программа работает с единственным мо нитором, значения этих свойств и свойств XXX ( H e i g h t , L e f t , Top, Width) со впадают. Свойство M o n i t o r C o u n t содержит общее количество обслуживаемых мониторов; с помощью свойства M o n i t o r s можно указать конкретный монитор в рамках первичного. Свойство F o n t s содержит перечень всех зарегистрированных в системе экранных шрифтов. Этот перечень можно связать с раскрывающимся списком, чтобы пользо ватель мог выбрать нужный шрифт:
для того, чтобы заставить программу выводить данные на один из подключен ных к компьютеру принтеров. Вывод на принтер в Windows ничем не отличается от вывода на экран: в распоря жение программиста предоставляется свойство Canvas объекта P r i n t e r , содер жащее набор графических инструментов и методы, свойственные классу TCanvas (подробнее см. раздел «Графический инструментарий» в главе 10). Размер листа бумаги в пикселах принтера определяют свойства H e i g h t и Width, а набор прин терных шрифтов — свойство F o n t s . Свойства класса T P r i n t e r перечислены в табл. 18.5. Таблица 18.5. Свойства класса TPrinter Свойство
Описание
property Aborted: Boolean;
Содержит значение T r u e , если выполнение предыдущего задания на печать было досрочно прекращено Канва принтера — основной инструмент создания изображения на листе бумаги Содержит текущие параметры драйвера принтера: p c C o p i e s — может печататься несколько копий документа; p c O r i e n t a t i o n — драйвер поддерживает разную ориентацию листа бумаги; p c C o l l a t i o n — документ печатается в подбор по экземплярам
property Canvas: TCanvas; type T P r i n t e r C a p a b i l i t y = (pcCopies, p c O r i e n t a t i o n , pcCollation); T P r i n t e r C a p a b i l i t i e s = s e t of T P r i n t e r C a p a b i l i t y ; property Capabilities: TPrinterCapabilities; • property Copies: I n t e g e r ; property Fonts : T S t r i n g s ; TPrinterOrientation = ( p o P o r t r a i t , poLandscape); property O r i e n t a t i o n : TPrinterOrientation; property PageHeight: I n t e g e r ; property PageNumber: I n t e g e r ; property PageWidth: I n t e g e r property P r i n t e r Index: I n t e g e r ;
ComboBoxl.Items.Assign(Screen.Fonts); С объектом S c r e e n связаны два события:
property P r i n t e r s : T S t r i n g s ; property P r i n t i n g : Boolean;
property OnActiveControlChange: TNotifyEvent; Это событие возникает при переходе фокуса ввода от одного элемента к дру гому.
property T i t l e : String;
property OnActiveFormChange: TNotifyEvent; Это событие возникает при переходе фокуса ввода от одной формы к другой.
Таблица 1 8 . 6 . Методы класса TPrinter
Объект Printer Объект P r i n t e r автоматически создается, если в программе указана ссылка на модуль P r i n t e r s . Этот объект предоставляет программисту все необходимое
387
Указывает требуемое количество копий документа Содержит список всех доступных принтеру шрифтов Определяет ориентацию листа бумаги: p o P o r t r a i t — вертикальная; p o L a n d s c a p e — горизонтальная Содержит высоту листа бумаги в пикселах принтера Содержит номер печатаемой страницы документа (начинается с 1) Содержит ширину листа бумаги в пикселах принтера Содержит индекс принтера, назначенного принтером по умолчанию в списке Printers окна параметров печати Содержит список всех доступных принтеров Содержит значение T r u e , если принтер занят печатью документа Содержит имя задания на печать
Методы класса T P r i n t e r перечислены в табл. 18.6.
Метод
Описание
procedure Abort; procedure BeginDoc; procedure EndDoc; procedure NewPage;
Досрочно прекращает печать документа Начинает печать документа Завершает печать документа Начинает печать новой страницы документа
3 8 8 Глава 18 • Программа
Глобальные объекты 3 8 9
Существует множество способов печати текста на принтере. Прежде всего сле дует упомянуть о глобальной процедуре A s s i g n P r n (она определена в модуле P r i n t e r s ) , позволяющей использовать принтер как текстовый файл и печатать текстовые строки с помощью процедуры WriteLn. В листинге 18.2 приведен пол ный текст модуля, на форме которого расположено многострочное текстовое поле Memol и 4 кнопки: для выбора текстового файла и ввода его содержимого в по ле, для выбора нужного шрифта отображения/печати документа, для иницииро вания процесса печати и для завершения работы программы. Листинг 18.2. Печать текста с помощью процедуры AssignPrn unit Unitl; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Borland.Vcl.StdCtrls, System.ComponentModel; type TForrnl = class(TForm) Memol: TMemo; Buttonl: TButton; Button2: TButton; OpenDialogl: TOpenDialog; BitBtnl: TBitBtn; Button3: TButton; FontDialogl: TFontDialog; procedure ButtonlClick(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var
procedure TForrnl.Button3Click(Sender: TObject); // Выбор шрифта и связывание его с текстовым полем begin if FontDialogl.Execute then Memol.Font := FontDialogl.Font end; procedure TForrnl.Button2Click(Sender: TObject); // Печать содержимого текстового поля // как вывод в текстовый файл var Prn: TextFile; k: Integer; begin AssignPrn(Prn); // Переназначаем вывод // с файла на принтер Rewrite(Рта); // Готовим принтер к печати // (аналог BeginDoc) { Для печати используем такой же шрифт, как и для показа поле: } Printer.Canvas.Font := Memol.Font; // Цикл печати: for к := 0 to Memol.Lines.Count-1 do WriteLn(Prn, Memol.Lines[k]); CloseFile(Prn); // Аналог EndDoc end;
в
текстовом
end. Такой способ печати — самый примитивный. С его помощью невозможно вывес ти линии, разделяющие столбцы или строки, трудно форматировать текст, встав лять заголовки, номера страниц и т. п. Значительно более гибкие средства обеспечивает свойство P r i n t e r . C a n v a s . листинге 18.3 показано, как с его помощью можно напечатать текст, содержа щийся в поле Memol.
Forml: TForrnl; implementation uses Borland.VCL.Printers;
begin if OpenDialogl.Execute then Memol.Lines.LoadFromFile(OpenDialogl.FileName) end;
// Эта ссылка обязательна!
{$R *.DFM}
Листинг 18.3. Печать текста с помощью свойства Printer.Canvas procedure TForrnl.Button2Click(Sender: TObject); Печать содержимого поля с помощью свойства Printer.Canvas var Y
procedure TForrnl.ButtonlClick(Sender: TObject); // Выбор файла с текстом и его загрузка в текстовое поле
Р=>
'dY,X,k: Integer; продолжение &
390
Глава 18 • Программа
Л и с т и н г 18.3 (продолжение) S: String; begin if Memol.Lines.Count=0 then Exit; Screen.Cursor := crHourGlass; with Printer do begin BeginDoc; with Canvas do begin Font := Memol.Font; dY := TextHeight ( '1'); // Определяем высоту строки Y := 3*dY; // Отступ от верхнего края листа X := PageWidth div 15; // Отступ от левого края for к := 0 to Memol.Lines.Count-1 do begin // Выводим очередную строку TextOut(X,Y,Memol.Lines[k]); // Смещаемся на следующую строку листа inc(Y,dY); if PageHeight-Y<2*dY then // Нижний край листа? begin // Да NewPage; // Переход на новый лист // Выводим номер страницы посередине листа: S := '- 'tlntToStr(PageNumber)+' - ' ; TextOut((PageWidth-TextWidth(S)) div 2, dy, S ) ; // и отчеркиваем его от текста: MoveTo(X, 3*dy div 2 ) ; LineTo(PageWidth-X, 9*dy div 4 ) ; // Ордината первой строки: Y := 3*dY end; // if PageHeight-Y<2*dY end; // for k := 0 to Memol.Lines.Count-1 do end; // with Canvas do EndDoc; end; // with Printer do Screen.Cursor := c r D e f a u l t ; end; Как видим, прямое обращение к графическим инструментам свойства Canvas требует от программиста значительно больших усилий, но зато предоставляет ему полный контроль над печатным изображением. Наконец, очень хороших результатов можно достичь, используя специализиро ванные средства просмотра/печати документов, такие как текстовый процессор MS Word.
Глобальные объекты
391
Печать изображений может показаться очень сложным делом, однако свойство P r i n t e r . C a n v a s содержит метод, который легко справляется с этой задачей: procedure StretchDraw(const Rect: TRect; Graphic: TGraphic ); При обращении к этому методу в качестве первого параметра указывается пря моугольная область, отводимая на поверхности листа для распечатки изображе ния, второго— объект класса T G r a p h i c , в котором хранится изображение, на пример: with P r i n t e r do begin BeginDoc; Canvas . StretchDraw(Canvas.ClipRect, Imagel.Picture.Graphic); EndDoc; end; He следует забывать также о том, что любая форма, являющаяся потомком базо вого класса TCustomForm, получает метод P r i n t , с помощью которого на прин тере печатается клиентская часть формы (без заголовка и рамки).
Объект Clipboard В Windows широко используется буфер обмена (clipboard). Если в программе Delphi сослаться на модуль C l i p b r d , для нее становится доступным автомати чески созданный объект C l i p b o a r d , инкапсулирующий свойства (табл. 18.7) и методы (табл. 18.8) для работы с буфером обмена. Таблица 18.7. Свойства объекта Clipboard Свойство
Описание
p r o p e r t y AsText: S t r i n g ;
Рассматривает содержимое буфера как текстовую строку неограниченной длины Возвращает общее количество форматов хранящихся в буфере данных Открывает доступ к данным по формату, указанному индексом Index
property FormatCount: I n t e g e r ; p r o p e r t y Formats [ I n d e x : I n t e g e r ] : Word;
Таблица 1 8 . 8 . Методы объекта Clipboard Метод
Описание
procedure Assign (Source: TPersistent); procedure C l e a r ; procedure Close;
Копирует изображение в буфер обмена
function GetTextBuf (Buffer : PChar; Buf S i z e : I n t e g e r ) Integer;
:
Очищает буфер обмена Уменьшает на 1 счетчик блокировок буфера обмена и закрывает его, если счетчик содержит О Копирует из буфера обмена не более Buf Size символов в текстовый буфер Buf fer и возвращает действительное количество скопированных символов продолжение &
392
Глава 18 • Программа
Настройка программы
Таблица 1 8 . 8 (продолжение) Метод p r o c e d u r e HasFormat ( F o r m a t : Word) : B o o l e a n ; p r o c e d u r e Open; procedure SetTextBuf (Buffer : PChar.) ;
Описание Возвращает значение T r u e , если буфер обмена содержит данные в формате F o r m a t Увеличивает на 1 счетчик блокировок буфера обмена и открывает его, если он еще не был открыт Помещает в буфер обмена содержимое текстового буфера B u f f e r _ ^ _ ^ _ _ _
Большая часть компонентов, предназначенных для хранения текста и/или изоб ражений, имеют собственные методы для работы с буфером обмена. Например, текстовые поля T E d i t , TMemo, T R i c h E d i t имеют методы C o p y T o C l i p b o a r d (копировать в буфер), C u t T o C l i p b o a r d (вырезать и поместить в буфер), P a s t e F r o m C l i p b o a r d (вставить из буфера); компонент TImage имеет свойство P i c r t u r e класса T P i c t u r e , которое инкапсулирует методы L o a d F r o m C l i p b o a r d F o r m a t , S a v e T o C l i p b o a r d F o r m a t и R e g i s t e r C l i p b o a r d F o r m a t — с по мощью этих методов компонент обменивается с буфером изображениями опре деленного формата и может зарегистрировать в нем новый формат изображения. Тем не менее эти и другие компоненты для работы с буфером обмена могут ис пользовать свойство AsText объекта C l i p b o a r d и его методы A s s i g n , G e t TextBuf f er, S e t T e x t B u f f e r . Например, чтобы поместить в буфер обмена изоб ражение, хранящееся в объекте I m a g e l , можно обратиться к методу A s s i g n буфера: Clipboard.Open; Clipboard.Assign(Imagel.Picture); Clipboard.Close; А для копирования изображения из буфера обмена можно обратиться к методу P i c t u r e . A s s i g n компонента I m a g e l : Clipboard.Open; Imagel.Picture.Assign(Clipboard); Clipboard.Close; Надпись не имеет средств работы с буфером обмена, но программа может про читать в нее текст из буфера: Clipboard.Open; Labell.Caption := Clipboard.AsText; Clipboard.Close;
рением INI). Для 32-разрядных версий Windows индивидуальная грамм чаще всего реализуется с помощью системного реестра. рассматриваются оба способа, так как даже если вы работаете версией Windows, перенос программ на другие компьютеры и их ще реализовать с помощью файлов инициализации.
Настройка программы Удобным средством хранения индивидуальных параметров программы являют ся широко используемые в Windows 3.x файлы инициализации (файлы с расши-
настройка про В этом разделе с 32-разрядной настройку про
Файлы инициализации Файлы инициализации в Delphi связаны с объектами класса T I n i F i l e . Эти объек ты не являются глобальными и создаются программно по мере надобности. С каж дой программой можно связать сколько угодно файлов инициализации. Физически файл инициализации представляет собой обычный текстовый файл, который сохраняется либо в каталоге запуска программы (локальный файл ини циализации), либо в каталоге запуска Windows (глобальный файл инициализации). Файл содержит ряд секций, каждая из которых имеет заголовок и следующие за ним параметры. Заголовок — это произвольный английский текст, заключенный в квадратные скобки. Каждый параметр представлен строкой вида имя_параметра=значение Пример: [Location] DataBase=C:\MYBASE Graphics=C:\BITMAP В этом примере в секции L o c a t i o n файла инициализации представлены два па раметра с именами D a t a B a s e и G r a p h i c s . Объекты класса T I n i F i l e предос тавляют удобные средства чтения/записи параметров по именам параметров и секций. В объекте имеется единственное свойство FileName : S t r i n g , содержащее имя файла инициализации с возможным путем поиска. Методы класса T I n i F i l e перечислены в табл. 18.9. Т а б л и ц а 1 8 . 9 . Методы класса TIniFile Метод c o n s t r u c t o r Create ( c o n s t FileName S t r i n g ) ;
Можно также поместить в буфер обмена содержимое свойства C a p t i o n надписи: Clipboard.Open; Clipboard.AsText := Labell.Caption; Clipboard.Close;
393
procedure DeleteKey (const Section, Ident : String); procedure EraseSection (const Section : String) ; f u n c t i o n ReadBool ( c o n s t Section, Ident: String; D e f a u l t : Boolean): Boolean;
Описание Создает объект-файл с именем F i l e N a m e . Это имя автоматически переносится в свойство F i l e N a m e объекта. Если физический файл существовал, он открывается, если нет — создается Удаляет значение параметра I d e n t в секции S e c t i o n . Если указаны несуществующая секция или имя несуществующего параметра, метод создаст секцию и/или установит в ней параметр с пустым значением Удаляет секцию со всеми ее параметрами. Если секции не существует, возникает исключительная ситуация Возвращает значение логического параметра I d e n t в секции S e c t i o n (см. далее первое примечание) продолжение #
394
Глава 18 • Программа
Настройка программы
ПРИМЕЧАНИЕ
Таблица 1 8 . 9 (продолжение) Метод
Описание
f u n c t i o n ReadDate ( c o n s t S e c t i o n , I d e n t : String; D e f a u l t : TDateTime): TDateTime; v i r t u a l ; f u n c t i o n ReadDateTime ( c o n s t S e c t i o n , I d e n t : String; D e f a u l t : TDateTime): TDateTime; v i r t u a l ; function ReadFloat (const Section, I d e n t : String; Default: Double) : D o u b l e ; v i r t u a l ;
Возвращает значение параметра I d e n t типа даты в секции S e c t i o n (см. далее первое примечание)
function Readlnteger (const Section, I d e n t : String; Default: Longlnt): Longlnt;
Возвращает значение целого параметра I d e n t в секции S e c t i o n (см. далее первое примечание)
procedure R e a d S e c t i o n (const S e c t i o n : String; S t r i n g s : T S t r i n g s ); procedure R e a d S e c t i o n s ( S t r i n g s : TStrings);
Помещает в список S t r i n g s имена всех параметров из секции S e c t i o n Помещает в список S t r i n g s имена всех секций файла Помещает в список S t r i n g s имена всех значений из секции S e c t i o n
procedure R e a d S e c t i o n V a l u e s (const Section: String; S t r i n g s : TStrings);
Возвращает значение параметра I d e n t типа даты-времени в секции S e c t i o n (см. далее первое примечание) Возвращает значение параметра I d e n t вещественного типа в секции S e c t i o n (см. далее первое примечание)
function ReadString (const S e c t i o n , Ident, Default: String): String;
Возвращает значение строкового параметра I d e n t в секции S e c t i o n (см. далее первое примечание)
f u n c t i o n ReadTime ( c o n s t S e c t i o n , I d e n t : String; D e f a u l t : TDateTime): TDateTime; v i r t u a l ;
Возвращает значение параметра I d e n t типа времени в секции S e c t i o n (см. далее первое примечание)
function S e c t i o n E x i s t s String) : Boolean;
Возвращает значение T r u e , если секция S e c t i o n существует
(const Section
function ValueExists (const Section, I d e n t : String): Boolean;
Возвращает значение T r u e , если у параметра I d e n t секции S e c t i o n существует значение
procedure WriteBool (const S e c t i o n , I d e n t : S t r i n g ; Value: Boolean) ;
Записывает в параметр I d e n t секции S e c t i o n логическое значение V a l u e (см. далее второе примечание)
procedure WriteDate (const S e c t i o n , I d e n t : String; Value: TDateTime); virtual;
Записывает в параметр I d e n t секции S e c t i o n значение V a l u e типа даты (см. далее второе примечание)
procedure WriteDateTime (const Section, I d e n t : String; Value: TDateTime); v i r t u a l ;
Записывает в параметр I d e n t секции S e c t i o n значение V a l u e типа датывремени (см. далее второе примечание)
Записывает в параметр I d e n t секции S e c t i o n вещественное значение V a l u e (см. далее второе примечание) p r o c e d u r e W r i t e l n t e g e r ( c o n s t S e c t i o n , Записывает в параметр I d e n t секции I d e n t : String; Value: L o n g l n t ); S e c t i o n целое значение V a l u e p r o c e d u r e W r i t e S t r i n g ( c o n s t S e c t i o n , Записывает в параметр I d e n t секции S e c t i o n строковое значение V a l u e I d e n t , Value: String) ; Записывает в параметр I d e n t секции procedure WriteTime (const S e c t i o n , S e c t i o n ь,;ачение V a l u e типа времени I d e n t : String; Value: TDateTime); (см. далее второе примечание) virtual ; procedure W r i t e F l o a t (const S e c t i o n , I d e n t : String; Value: Double); virtual;
395
В функциях вида ReadXXXX (ReadBool, ReadData и т. п.) используется параметр D e f a u l t того же типа, что и тип возвращаемого результата. Если функция не найдет указанной секции или нужного параметра, а также если значение параметра окажется не ожидаемого типа, она возвращает значение D e f a u l t . ПРИМЕЧАНИЕ Если при обращении к процедуре вида WriteXXXX ( W r i t e B o o l , W r i t e D a t a и т. п.) не су ществует указанной секции и/или параметра, они будут созданы.
В листинге 18.4 представлен модуль, иллюстрирующий приемы работы с фай лом инициализации. Л и с т и н г 18.4. Иллюстрация работы с файлом инициализации unit
Unitl;
interface uses Windows, Messages, Controls,
Forms,
SysUtils,
Dialogs,
Classes,
StdCtrls,
Graphics,
Spin;
type TForml = class(TForm) Labell: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; edSection: TEdit; edParaml: TEdit; edParam2: TEdit; cbParam4: TCheckBox; Buttonl: TButton; Button2: TButton; seParam3: TSpinEdit; procedure ButtonlClick(Sender: TObject); procedure Button2Click(Sender: TObject); procedure edParam2Exit(Sender: TObject); private { Private declarations } public { Public end;
declarations
}
продолжение •&
96 Глава 18 • Программа
Настройка программы 3 9 7
истинг 18.4 (продолжение) аг Forml: TForml;
'Param4', True); Ini.Destroy; end;
mplementation ses
Borland.VCL.IniFiles; //
// Обязательная ссылка для доступа к типу
onst SectionName = 'InitSection'; IniFileName = 'DemoIni.ini'; аг Ini: TIniFile; $R
TIniFile
// Название секции по умолчанию // Имя файла инициализации
*.DFM}
procedure TForml.ButtonlClick(Sender : TObject); '/ Создает или изменяет файл инициализации iegin if edSection.Text='' then edSection.Text := SectionName; Ini := TIniFile,Create(IniFileName); Ini.WriteString(edSection.Text, 'Parami', edParaml.Text); Ini.WriteFloat(edSection.Text, 'Param2', StrToFloat (edParam2.Text)); Ini.WriteInteger(edSection.Text, 'РагатЗ', seParaml8.Value); Ini.WriteBooi (edSection.Text, 'Param4', cbParam4.Checked); Ini.Destroy; snd; procedure TForml.Button2Click(Sender: TObject); V Читает параметры настройки jegin if edSection.Text='' then edSection.Text := SectionName; Ini := TIniFile.Create(IniFileName); edParaml.Text := Ini.ReadString(edSection.Text, 'Paraml', 'Delphi'); edParam2.Text := FloatToStr(Ini.ReadFloat( edSection.Text, 'Param2', 18.1415)); seParaml8.Value := Ini.Readlnteger(edSection.Text, 'РагатЗ', 100); cbParam4.Checked := Ini.ReadBool(edSection.Text,
procedure TForml.edParam2Exit(Sender: TObject); // Проверяет правильность символьного представления // вещественного числа begin try StrToFloat(edParam2.Text) except edParam2.SelectAll; edParam2.SetFocus ; end end; end.
Отмечу, что если путь доступа к файлу инициализации не указан (как в нашем примере), считается, что он располагается в системном каталоге (обычно C:\Windows).
Системный реестр Windows Системный реестр Windows — это общедоступная база данных, хранящая инди видуальную для каждого компьютера настроечную информацию, используемую программным обеспечением. Данные в реестре упорядочены в древовидные структуры, состоящие из узлов, которые в терминологии Windows называются ключами. Каждый ключ имеет один родительский ключ, один или несколько дочерних ключей и ноль или несколько пар типа имя_параметра = значе ние. Исключение составляют корневые ключи (они не имеют родителей) и до черние ключи низшего уровня (они не имеют дочерних ключей). По умолчанию несистемное программное обеспечение регистрирует свою информацию в корне вом ключе с именем HKEY_CURRENT_USER. Для просмотра/коррекции систем ного реестра используется утилита REGEDIT.EXE. Системному реестру Windows соответствует объект класса TRegistry, свойства которого представлены в табл. 18.10. Таблица 18.10. Свойства класса TRegistry Свойство
Описание
p r o p e r t y Access : LongWord;
Определяет уровень доступа к открытому ключу. Значение свойства может представлять собой некоторую комбинацию флагов (см. далее) Содержит текущий раскрытый ключ Содержит путь поиска для текущего ключа продолжение &
p r o p e r t y C u r r e n t K e y : HKEY; property CurrentPath: String;
98
I лава 18 • Программа
Настройка программы
аблица 1 8 . 1 0 (продолжение)
Метод
воиство
Описание
roperty LazyWrite : Boolean;
Если содержит значение T r u e , ключ записывается при выполнении процедуры C l o s e K y e , в противном случае— при любом изменении ключа Содержит имя корневого ключа
roperty RootKey:
HKEY;
1еречень флагов для свойства Access: KEY QUERY_VALUE — разрешает запрос значений дочерних ключей; KEY ENUMERATE_SUB_KEYS — разрешает получение списка всех дочерних ключей; KEY NOTIFY — разрешает получение извещения об изменении ключа; KEY_SET_VALUE — разрешает изменение значений дочерних ключей; KEY_CREATE_SUB_KEY — разрешает создание дочерних ключей; KEY CREATE_LINK — разрешает создание строковой ссылки; KEY_EXECUTE — определяет доступ к ключу в режиме «только для чтения»; KEY__ALL_ACCESS - комбинация флагов KEY_R EAD > KEY_WRITE И KEY_CREATE_LINK; KEY _READ - комбинация флагов KEY_QUERY__VALUE, KEY_ENUMERATE_SUB_ KEYS ИKEY_NOTIFY; KEY_WRITE - комбинация флагов KEY_SET_VALUE И KEY_CREATE_SUB_KEY,
Методы класса TRegistry перечислены в табл. 18.11. Габлица 18.11. Методы класса TRegistry Четод
Описание
procedure CloseKey; constructor Create;
Записывает информацию в ключ и закрывает его Создает объект класса T R e g i s t r y . Устанавливает В RootKey значение HKEY__CURRENT_USER и в L a z y W r i t e — значение T r u e Создает ключ с именем Key. Если имя начинается символом \, ключ является дочерним ключом корневого ключа, в противном случае — дочерним ключом текущего ключа Удаляет ключ Key и возвращает значение T r u e , если операция прошла успешно В текущем узле удаляет значение параметра с именем Name , Удаляет объект T R e g i s t r y Возвращает в параметре V a l u e значение, определяющее тип данных для параметра с именем ValueName текущего ключа: rdUnknown — неизвестный тип; r d S t r i n g — строковый тип; r d E x p a n d S t r i n g —значением является строка, описывающая переменную окружения Windows, например, "<<РАТН%"; r d l n t e g e r — целочисленный тип; r d B i n a r y — двоичный тип (набор битов)
function CreateKey (const Key: String): Boolean;
function DeleteKey (const Key: String): Boolean; function DeleteValue (const Name: String): Boolean; destructor Destroy; TRegDataType = (rdUnknown, rdString, rdExpandString, rdlnteger, rdBinary); TRegDatalnfo = record RegData : TRegDataType; DataSize: Integer; end; function GetDatalnf о (const ValueName : String; var Value: TRegDatalnfo) : Boolean;
399
Описание
function GetDataSize (const Возвращает длину значения параметра с именем ValueName: String): Integer; ValueName в текущем узле TRegDataType = (rdUnknown, Возвращает значение, определяющее тип данных rdString, rdExpandString, для параметра с именем ValueName текущего rdlnteger, rdBinary); ключа (см. метод G e t D a t a l n f о) function GetDataType (const ValueName: S t r i n g ) : TRegDataType TRegKeylnf о = r e c o r d NumSubKeys : Возвращает в параметре V a l u e информацию I n t e g e r ; M a x S u b K e y L e n : I n t e g e r ; о текущем узле: NumSubKeys — количество NumValues: I n t e g e r ; MaxValueLen: дочерних узлов; MaxSubKeyLen — I n t e g e r ; MaxDataLen: I n t e g e r ; максимальная длина имени дочернего узла; F i l e T i m e : T F i l e T i m e ; end; NumValues — количество параметров; f u n c t i o n G e t K e y l n f о (var V a l u e : M a x V a l u e L e n — максимальная длина имени TRegKeylnfo) : B o o l e a n ; параметра; MaxDataLen — максимальная длина параметра; F i l e T i m e — время последнего обновления ключа p r o c e d u r e GetKeyNames ( S t r i n g s : Возвращает в параметре S t r i n g s имена всех TStrings); дочерних ключей p r o c e d u r e GetValueNames Возвращает в параметре S t r i n g s имена всех (Strings: TStrings) ; параметров f u n c t i o n HasSubKeys: B o o l e a n ; Возвращает значение T r u e , если ключ имеет дочерние ключи f u n c t i o n K e y E x i s t s ( c o n s t Key: Возвращает значение T r u e , если существует ключ String) : Boolean; с именем Key f u n c t i o n LoadKey ( c o n s t Key, Создает ключ с именем Key как дочерний ключ FileName: S t r i n g ) : Boolean; корневого ключа и загружает из файла F i l e N a m e его содержимое (параметры и все дочерние ключи) p r o c e d u r e MoveKey ( c o n s t OldName Копирует или перемещает ключ OldName в ключ NewName: S t r i n g ; D e l e t e : NewName: D e l e t e — содержит значение T r u e Boolean); для перемещения f u n c t i o n OpenKey ( c o n s t Key: Открывает существующий или создает и открывает String; CanCreate : Boolean) : новый ключ с именем Key и возвращает значение Boolean; T r u e , если операция прошла успешно. Параметр C a n C r e a t e разрешает/запрещает создание ключа, если он не существует f u n c t i o n OpenKeyReadOnly ( c o n s t Открывает ключ в режиме «только для чтения» Key: S t r i n g ) : B o o l e a n ; function ReadBinaryData (const Копирует не более B u f S i z e байтов в переменную Name: String; var Buffer; B u f f e r из параметра с именем Name. Возвращает BufSize: I n t e g e r ) : Integer; количество действительно скопированных байтов f u n c t i o n ReadXXXX ( c o n s t Name : Возвращает значение параметра типа YYYY S t r i n g ) : YYYY; с именем Name f u n c t i o n R e g i s t r y C o n n e c t ( c o n s t Разрешает удаленному компьютеру UNCName UNCName: S t r i n g ) : B o o l e a n ; доступ к системному реестру вашего компьютера p r o c e d u r e RenameValue ( c o n s t Переименовывает параметр OldName OldName, NewName: S t r i n g ) ; f u n c t i o n R e p l a c e K e y ( c o n s t Key, Заменяет содержимое ключа Key содержимым F i l e N a m e , BackUpFileName: файла F i l e N a m e . B a c k U p F i l e N a m e — имя String): Boolean; файла, в котором будет сохраняться прежнее содержимое ключа f u n c t i o n R e s t o r e K e y ( c o n s t Key, Считывает содержимое ключа Key из файла FileName: S t r i n g ) : Boolean; FileName продолжение &
400
Глава 18 • Программа
Таблица
18.11
Настройка программы
(продолжение)
Метод
Описание
f u n c t i o n SaveKey ( c o n s t Key, Сохраняет содержимое ключа Key в файле FileName: S t r i n g ) : Boolean; FileName function ValueExists (const Возвращает значение True, если параметр Name: S t r i n g ) : B o o l e a n ; С именем Name имеет значение p r o c e d u r e W r i t e B i n a r y D a t a ( c o n s t Копирует не более Buf S i z e байтов из буфера Name: S t r i n g ; v a r B u f f e r ; Buf f e r в параметр с именем Name BufSize: I n t e g e r ) ; p r o c e d u r e WriteXXXX ( c o n s t Name : Записывает в параметр с именем Name данные типа S t r i n g ; V a l u e : YYYY) ; YYYY Системный реестр — удобное место для размещения некоторых «секретных» дан ных, которые не должны быть на виду у пользователей. Например, при первом запуске пробных версий программ в него помещается дата запуска или максималь ное количество запусков. Перед очередным прогоном эта информация проверят ся, в результате после некоторого количества запусков или по истечении установ ленного времени пробная версия программы перестает нормально работать. В листинге 18.5 представлен файл проекта, в котором пробная версия програм мы может запускаться не более 5 раз. Л и с т и н г 1 8 . 5 . Создание пробной версии программы program T r i a l ; uses Forms, U n i t l in ' U n i t l . p a s ' Borland.VCL.Registry,
401
W r i t e l n t e g e r ( ' M a x R u n ' , N) end; Free end; i f N>0 t h e n begin Application.CreateForm(TForml, Forml); Forml.Label2.Caption := I n t T o S t r ( N - l ) ; Application.Run; end e l s e S h o w M e s s a g e ( ' И с ч е р п а н о максимальное к о л и ч е с т в о з а п у с к о в ' + ' пробной в е р с и и программы') end. Небольшой комментарий. Д в у м я обращениями к ф у н к ц и и T R e g i s t r y . O p e n K e y создается и / и л и открывается узел HKEY_CURRENT_USER\Sof t w a r e \ T r i a l P r o g системного реестра. Ф у н к ц и я T R e g i s t r y . V a l u e E x i s t s возвращает значение T r u e , если в этом узле есть параметр с именем MaxRun и д л я него определено з н а ч е н и е . П р и п е р в о м з а п у с к е п р о г р а м м ы это не так, поэтому процедурой W r i t e l n t e g e r создается параметр и д л я него указывается начальное значе ние 5 (максимальное количество прогонов программы). П р и каждом следую щем запуске этот параметр уменьшается на 1, и в момент, когда он становится р а в н ы м нулю, программа блокирует создание и вызов главного окна. Delphi создает надстройку над классом T R e g i s t r y для того, чтобы манипуля ц и я с системным реестром Windows была подобна м а н и п у л я ц и я м с ф а й л а м и инициализации. Эта надстройка инкапсулируется в классе T R e g l n i F i l e .
{Forml), Dialogs;
Свойства класса T R e g l n i F i l e аналогичны свойствам класса T R e g i s t e r y , з а исключением дополнительного свойства F i l e N a m e : S t r i n g , хранящего имя клю ча, для которого будут осуществляться нужные действия. Эти действия реализу ются методами класса, перечисленными в табл. 18.12.
// Для TRegistry // и ShowMessage
{$R *.RES} var Reg: T R e g i s t r y ; N: I n t e g e r ; . begin Reg : = T R e g i s t r y . C r e a t e ; w i t h Reg do begin OpenKey('software' , True); OpenKey('TrialProg', True); if V a l u e E x i s t s ( ' M a x R u n ' ) then / / Первый запуск? begin / / Нет N := R e a d l n t e g e r ( ' M a x R u n ' ) - 1 ; if N>=0 t h e n W r i t e l n t e g e r ( ' M a x R u n ' , N) end e l s e b e g i n // Да, первый запуск N : = 5; ?
Т а б л и ц а 1 8 . 1 2 . Методы класса TReglniFile Метод
Описание
constructor Create(const FileName: String); procedure D e l e t e K e y ( c o n s t Section, Ident: String); procedure E r a s e S e c t i o n ( c o n s t Section: String); f u n c t i o n ReadBool ( c o n s t S e c t i o n , I d e n t : String; Default: Boolean): Boolean; function Readlnteger (const Section, I d e n t : String; Default: Longlnt): Longlnt; procedure R e a d S e c t i o n (const Section: String; S t r i n g s : TString)
Создает объект класса T R e g l n i F i l e и связывает его с ключом F i l e N a m e Удаляет параметр с именем I d e n t в ключе Section Удаляет ключ S e c t i o n со всеми его параметрами и дочерними ключами Возвращает значение логического параметра с именем I d e n t в ключе S e c t i o n Возвращает значение целочисленного параметра с именем I d e n t в ключе Section Возвращает в параметре S t r i n g s имена всех параметров ключа S e c t i o n продолжение &
4 0 2 Глава 18 • Программа
Программа и Windows 4 0 3
Таблица 18.12 (продолжение) Метод
Описание
procedure ReadSections ( S t r i n g s : TStrings) ; procedure ReadSectionValues (const S e c t i o n : String; S t r i n g s : TStrings) ; function ReadString (const Section, Ident, Default: S t r i n g ) : String; procedure WriteBool (const S e c t i o n , I d e n t : String; Value: Boolean); procedure W r i t e l n t e g e r (const S e c t i o n , I d e n t : String; Value: Longlnt); procedure W r i t e S t r i n g (const S e c t i o n , I d e n t , Value: S t r i n g ) ;
Возвращает в параметре S t r i n g s имена всех ключей, связанных с текущим ключом Возвращает в параметре S t r i n g s значения всех параметров ключа Section Возвращает значение строкового параметра с именем Ident в ключе Section Записывает логическое значение Value в параметр Identключа Section Записывает целочисленное значение Value в параметр I d e n t ключа Section
система обладает свойствами многозадачности и многопоточности. Создать про цесс можно с помощью функции C r e a t e P r o c e s s : function CreateProcess(lpApplicationName: PChar; lpCommandLine: PChar; lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; b l n h e r i t H a n d l e s : BOOL; dwCreationFlags: DWORD; IpEnvironment: P o i n t e r ; I p C u r r e n t D i r e c t o r y : PChar; const I p S t a r t u p I n f o : T S t a r t u p I n f o ; var l p P r o c e s s I n f o r m a t i o n : T P r o c e s s I n f o r m a t i o n ) : BOOL; Назначение параметров описано далее. •
l p A p p l i c a t i o n N a m e — имя исполняемого файла с возможным путем досту па. Если путь доступа опущен, имя дополняется текущим диском и текущим каталогом. Если параметр имеет значение NIL, имя исполняемого файла дол жно указываться первым в перечне параметров командной строки lpCommand L i n e и отделяться от других параметров пробелом. Если запускается 16-раз рядная программа под управлением Windows NT/2000/XP, параметр l p A p p l i c a t i o n N a m e должен быть равен NIL.
•
lpCommandLine — строка командных параметров. Если параметр имеет зна чение NIL, командные параметры можно передавать программе в параметре l p A p p l i c a t i o n N a m e . Если запускается 16-разрядная программа под управ лением Windows NT/2000/XP, первым в списке параметров должно стоять имя исполняемого файла с возможным путем доступа.
•
l p P r o c e s s A t t r i b u t e s — указатель на структуру, описывающую привиле гии доступа процесса. Имеет смысл только для Windows NT/2000/XP. Если имеет значение NIL, процессу присваиваются привилегии доступа, заданные по умолчанию.
•
l p T h r e a d A t t r i b u t e s — указатель на структуру, описывающую привилегии доступа главного потока команд программы. Имеет смысл только для Win dows NT/2000/XP. Если имеет значение NIL, потоку присваиваются приви легии доступа, заданные по умолчанию.
Записывает строковое значение Value в параметр Ident ключа Section
ПРИМЕЧАНИЕ В ключе HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run хранится перечень программ, которые будут автоматически запускаться в момент старта Windows.
Программа и Windows Delphi — это всего лишь одно из многочисленных приложений, написанных в рас чете на работу под управлением операционной системы Windows. В этом разде ле приводятся минимально необходимые сведения о Windows, позволяющие по нять и грамотно использовать соответствующие средства Delphi.
Процессы и потоки Процесс - это специальный объект Windows, который создается для каждой ис полняющейся под управлением этой операционной системы программы. Сам по себе процесс ничего не делает, просто в нем инкапсулируется вся необходимая информация о программе. Чтобы программа могла выполняться, помимо про цесса создается еще поток команд, который называется первичным, или глав ным. В программе можно создать сколько угодно потоков команд, однако в каж дый момент времени активен (то есть использует ресурсы процессора) только один из них. Потоки получают доступ к процессору небольшими квантами вре мени, поэтому у пользователя складывается впечатление, что все потоки команд работают параллельно. Один процесс может породить другой процесс. Это означает, что одна работаю щая программа может инициировать выполнение другой программы. В Windows может параллельно исполняться множество программ, то есть эта операционная
• b l n h e r i t H a n d l e s — указывает, будет ли новый процесс наследовать деск рипторы вызывающего процесса. Если имеет значение True, порождаемый процесс разделяет память, карту файлов и другие системные атрибуты со сво им родителем. •
d w C r e a t i o n F l a g s — один или несколько флагов, уточняющих способ со здания нового процесса и его приоритет: о
CREATE_DEFAULT_ERROR_MODE - новый процесс получает свой обработ чик ошибок;
о CREATE_NEW_CONSOLE — новый процесс получает свои консольные сред ства ввода-вывода; не совместим с флагом DETACHED_ PROCESS; о CREATE_NEW_PROCESS_GROUP — новый процесс будет корневым для груп пы процессов;
4 0 4 Глава 18 • Программа о
CREATE_SEPARATE_WOW_VDM — новый 16-разрядный процесс Windows бу дет запущен в отдельной виртуальной машине DOS; имеет смысл только для Windows NT/2000/XP;
о
CREATE_SHARED_WOW_VDM — проверяет параметр Def a u l t S e p a r a t e V D M в файле Win.ini и, если он имеет значение True, запускает новый 16-раз рядный процесс в отдельной виртуальной машине DOS, в противном слу чае — в общей виртуальной машине; имеет смысл только для Windows NT/
2000/ХР; о CREATE_SUSPENDED — после создания нового процесса его главный по ток команд не будет активным (его можно впоследствии активизировать обращением к функции ResumeThread); о CREATE_UNICODE_ENVlRONMENT — если указан, блок заканчивающихся нулем строк E n v i r o n m e n t имеет кодировку Unicode; о DEBUG_PROCESS — новый процесс будет отлаживаться вызывающим про цессом; о DEBUG_ONLY_THIS_PROCESS — если не установлен, каждый процесс, по рождаемый новым отлаживаемым процессом, становится еще одним отла живаемым процессом вызывающего процесса; о DETACHED_PROCESS — если установлен, новый консольный процесс не имеет собственных консольных средств ввода-вывода, но может их запросить об ращением к функции A l l o c C o n s o l e ; не совместим с флагом CREATE_ NEW_C0NS0LE; о HIGH_PRIORITY_CLASS — присваивает главному потоку команд нового процесса высокий приоритет; о
IDLE_PRIORITY_CLASS — главный поток нового процесса будет активи зироваться только тогда, когда нет ни одного активного потока;
о NORMAL_PRIORITY_CLASS — присваивает обычный приоритет; о REALTIME_PRIORITY_CLASS — присваивает наивысший приоритет. •
I p E n v i r o n m e n t — указатель на оканчивающийся нулем блок оканчивающих ся нулем строк, описывающих переменные окружения нового процесса (на пример, каталоги поиска по умолчанию). Каждая переменная задается стро кой вида п а р а м е т р = з н а ч е н и е . Если содержит NIL, процесс использует значения переменных окружения, заданные по умолчанию.
•
l p C u r r e n t D i r e c t o r y — указатель на оканчивающуюся нулем строку, уста навливающую для нового процесса диск и каталог, заданные по умолчанию.
•
I p S t a r t u p I n f o — указатель на структуру, описывающую положение и раз меры главного окна нового процесса, его средства ввода-вывода и т. п.
•
l p P r o c e s s I n f o r m a t i o n — указатель на структуру, создаваемую функцией C r e a t e P r o c e s s , в которой содержится информация о вновь созданном про цессе и его главном потоке команд.
Программа и Windows
405
Листинг 18.6. Пример вызова функции CreateProcess procedure T F o r m l . B u t t o n l C l i c k ( S e n d e r : T O b j e c t ) ; // Пример запуска дочернего процесса var StartUpInfo: TStartUpInfо; Proclnfo: TProcessInformation; const CommandLine = 'Child.exe'; begin { Готовим структуру StartUpInfo. Подробности о назначении ее полей см. в справочной службе Win32 SDK Reference} FillChar(StartUpInfo, SizeOf (StartUpInfo), 0 ) ; with StartUpInfo do begin cb := S i z e O f ( S t a r t U p I n f o ) ; // Указываем размер структуры { Флаг STARTF_USESHOWWINDOW заставляет учитывать параметр wShowNindow. Флаг STARTF_FORCEONFEEDBACK переводит указатель мыши в режим "обратной связи" - он ждет окончания создания дочернего про цесса . } dwFlags := STARTF_USESHOWWINDOW or STARTF__FORCEONFEEDBACK; // Окно нового процесса должно быть видимым wShowWindow := sw_ShowNormal; end; { Создаем дочерний процесс. В общем случае указывать имя исполняемо го файла в параметре CommandLine разумнее, так как это обеспечивает правильный вызов 16-разрядных приложений в Windows NT } i f n o t C r e a t e P r o c e s s ( N I L , PChar(CommandLine), NIL, NIL, F a l s e , NORMAL_PRIORITY_CLASS, NIL, NIL, S t a r t U p I n f o , P r o c l n f o ) then ShowMessage('Ошибка ' + I n t T o S t r ( G e t L a s t E r r o r ) ) end;
Если вызов был успешным, функция возвращает значение True.
Современные 32-разрядные операционные системы Windows обеспечивают не только многозадачность, то есть возможность параллельной работы нескольких программ, но и многопоточность, когда в рамках одной программы организуется несколько параллельно выполняемых фрагментов (потоков команд), каждый из которых конкурирует с другими потоками за наиболее важный ресурс — время центрального процессора. В многопоточном режиме время центрального процес сора выделяется для каждого потока команд небольшими порциями (квантами), по истечении этого времени управление передается другому потоку, и так про должается до тех пор, пока потоки не закончат свою работу. В любой работаю щей программе организуется как минимум один поток для команд программы. С помощью объектов класса T T h r e a d программа может создать дополнитель ные потоки для проведения некоторой фоновой работы (например, текстовый процессор Word создает дополнительные потоки для проверки орфографии, раз бивки на страницы, печати документа и т. п.).
В листинге 18.6 показан пример вызова функции C r e a t e P r o c e s s . В каталоге запуска примера должен располагаться файл Child.exe.
Для создания дополнительного потока команд в программах Delphi предназна чен класс TThread, точнее — потомок этого класса. Необходимость наследова-
4 0 6 Глава 18 • Программа ния связана с тем, что класс TThread содержит абстрактный метод E x e c u t e , который, собственно, и должен исполняться в рамках нового потока команд и ко торый, следовательно, обязан перекрываться в потомках. Программирование потока команд ничем не отличается от программирования обычной программы, за одним важным исключением: поток не должен исполь зовать методы и свойства визуальных компонентов, которые приводят к измене нию внешнего вида окна программы. Точнее, он может это делать только при обращении к специальному методу S y n c h r o n i z e , с помощью которого осуще ствляется синхронизация исполнения главного потока команд программы с до полнительным потоком команд. Для иллюстрации приемов работы с потоками команд создадим программу, ко торая будет непрерывно обновлять содержимое многострочного текстового поля и при этом осуществлять математические вычисления. Основной поток команд программы активизируется при щелчке на кнопке Квад рат: вначале содержимое расположенного над ней поля со счетчиком S p i n E d i t l возводится в квадрат до тех пор, пока отображаемое в нем значение не станет слишком большим (больше 10 +1233 ). В этот момент надпись на кнопке меняется на Корень, а щелчок на ней вычисляет корень квадратный из величины в поле SpinEditl. Вот обработчик щелчка на этой кнопке: procedure TForml . B u t t o n l C l i c k (Sender: TObject) ; begin if Tag=0 then begin S p i n E d i t l . T e x t := F l o a t T o S t r ( s q r (StrToFloat ( S p i n E d i t l . T e x t ) ) ) ; if S t r T o F l o a t ( S p i n E d i t l . T e x t ) > l e l 2 3 3 then begin Tag := 1; Buttonl.Caption := 'Корень' end end else begin SpinEditl.Text := FloatToStr(sqrt(StrToFloat(SpinEditl.Text))); if StrToFloat(SpinEditl.Text) < 2 then begin SpinEditl.Value := 2; Tag := 0; Buttonl.Caption := 'Квадрат' end end end; В дополнительном потоке команд будем непрерывно формировать по 100 строк в многострочном текстовом поле mmOutput и показывать процент заполнения поля с помощью индикатора Gaugel.
Программа и Windows 4 0 7 Окончательный текст модуля потока команд представлен в листинге 18.7. Листинг 18.7. Пример модуля потока команд unit U n i t 2 ; interface uses Classes; type ThreadDemo = class(TThread) private { Private declarations } protected S: String; N: I n t e g e r ; procedure UpdateMemo; procedure UpdateGauge; procedure E x e c u t e ; override; end; var TDemo: ThreadDemo; implementation uses Unitl,SysUtils; { Important: Methods and properties of objects in VCL can used m a method called using Synchronize, for example, Synchronize and
(UpdateCaption);
UpdateCaption
procedure begin
only be
could
look
like,
ThreadDemo.UpdateCaption;
Forml.Caption end; }
:=
'Updated
in
a
thread';
{ ThreadDemo } procedure ThreadDemo.Execute; var Di k: Integer; продолжение &
408
Программа и Windows
Глава 18 • Программа
Листинг 18.7 (продолжение) begin repeat S:= Synchronize(UpdateMemo); for к := 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 Forml.mmOutput.Lines do if (S='') or (Count>1000) then Clear else Add (S) end; procedure ThreadDemo.UpdateGauge; begin Forml.Gaugel.Progress := N end; end.
Дополнительный поток команд запускается в обработчике OnActivate главной формы: procedure TForml.FormActivate(Sender: TObject); begin TDemo := ThreadDemo.Create(False) ; end;
Вот так просто запускается дополнительный поток команд — мы инициализируем объект TDemo, передавая в его конструктор ThreadDemo.Create единственный параметр False — этот параметр показывает, должен ли вновь созданный поток команд «спать» (True) или он обязан немедленно начать работу (False). Про грамма в любой момент может приостановить работу потока команд, присвоив его свойству Suspended значение True, и продолжить его выполнение, присвоив этому свойству значение False. Обратите внимание: метод Execute потока вынесен
409
в секцию protected и поэтому недоступен из основного модуля. Выполнение этого метода начинается автоматически, как только свойство Suspended примет значе ние False. Для обращения к свойствам и методам визуальных компонентов формы Forml предназначен специальный метод Synchronize потока команд. Единственным параметром обращения к этому методу должно быть имя любой потоковой проце дуры без параметров. Внутри такой процедуры разрешается обращаться к мето дам и свойствам визуальных компонентов. В нашем потоке команд имеется две такие процедуры — UpdateMemo и UpdateGuage. В первой строка S добавляется к содержимому поля mmOutput, причем для предотвращения переполнения внут реннего буфера поля оно периодически очищается. В процедуре UpdateGuage гло бальная переменная N присваивается свойству P r o g r e s s индикатора Gaugel. Поскольку эти процедуры не могут иметь параметров, для управления их работой приходится использовать глобальные переменные S и N.
Использование памяти . В 32-разрядных версиях Windows используется так называемая «плоская» модель памяти. Это означает, что каждому процессу выделяется адресное пространство размером 4 Гбайт, причем 32-разрядный указатель определяет порядковый номер ячейки этой воображаемой (виртуальной) памяти. Я не сомневаюсь, что ваш ком пьютер (как, впрочем, и любой другой «нормальный» компьютер — эти строки пишутся в конце 2004 г.) не имеет такого большого объема физической оператив ной памяти. Более того, сейчас трудно вообразить программу, которой такая па мять будет принципиально необходима. Речь идет о том, что любой процесс (точ нее, любой поток команд любого процесса) может обращаться к памяти такого размера. Фактически это означает, что память вашего компьютера объемом 16, 32 и более мегабайт отображается на гигантское пространство 4 Гбайт. В рамках ме ханизма отображаемой памяти создается так называемый файл подкачки, исполь зующийся для хранения страниц виртуальной памяти, которые превышают разме ры физической памяти. Когда программа (поток команд) обращается к адресам, которых нет в физической памяти, на свободное место оперативной памяти считывается фрагмент файла подкачки, а после использования требуемых адресов он вновь переписывается на диск. Обмен виртуальной памяти с физической памятью (подкачка) выполняется страницами размером по 4 Кбайт. Подкачка, разумеется, замедляет скорость работы программы, но зато программист может не заботиться об экономии физической памяти. Замечу, что в 16-разрядной версии Windows ис пользовалась так называемая сегментная модель памяти, в которой память про грамме выделялась блоками размером по 64 Кбайт. Это создавало определенные ограничения. В частности, ни один массив, ни одна запись и никакой другой тип произвольного размера не могли превышать длину сегмента — 64 Кбайт. В 32-раз рядных версиях Windows это ограничение «отодвинуто» до 4 Гбайт. ПРИМЕЧАНИЕ — Следует уточнить, что первые («верхние») 2 Гбайт виртуальной памяти отводятся ядру опера ционной системы и недоступны программе.
Программа и Windows
4 1 0 Глава 18 • Программа Плоская модель памяти гарантирует, что каждый процесс получит в свое распо ряжение отдельный блок виртуальной памяти размером 4 Гбайт. Это означает, что процессы «не видят» друг друга и не могут общаться через общую память (речь не идет о процессах, созданных с параметром I n h e r i t H a n d l e s = T r u e ) .
Windows-сообщения Сообщениями в Windows называются небольшие пакеты информации, которы ми обмениваются ядро Windows с программами и программы друг с другом. Со общение связано с некоторым событием и извещает адресата о его наступлении.
Структура сообщений В модуле Windows сообщение описано следующим образом: type TMsg = packed r e c o r d hWnd: HWND; // Дескриптор окна-получателя M e s s a g e : UINT; // Код сообщения WParam: WPARAM; // Уточняющий параметр LParam: LPARAM; // Уточняющий параметр T i m e : DWORD; // Время создания сообщения p t : TPoint; // Координаты указателя мыши в этот момент end; Типы HWND, UINT, DWORD описаны в том же модуле и соответствуют типу LongWord, а типы WPARAM, LPARAM — типу L o n g l n t . Важно заметить, что сообщение всегда адресуется некоторому окну-получателю, определяемому полем hWnd. Любой оконный класс Delphi порожден от класса T C o n t r o l и имеет метод WndProc, предназначенный для получения и обработ ки сообщений (этот метод называется оконной процедурой).
Система сообщений Система сообщений функционирует следующим образом. При создании любого окна программы операционная система Windows выделяет ему некоторые ресур сы и регистрирует его оконную процедуру. Оконная процедура главного окна будет создана и зарегистрирована путем вызова метода A p p l i c a t i o n . C r e a t e F o r m в файле проекта. При этом Windows выделит дополнительные ресурсы для организации очереди сообщений, адресованных любому окну данной про граммы. Далее в этом файле осуществляется вызов метода A p p l i c a t i o n . R u n , ядро которого составляет следующий цикл: repeat HandleMessage until Terminated; В ходе реализации цикла A p p l i c a t i o n . H a n d l e M e s s a g e вызывается такая пос ледовательность операторов: if PeekMessage(Msg, 0, О, О, PM_REMOVE) then begin гС\
411
TranslateMessage(Msg); DispatchMessage(Msg); end; Вызывая API-функцию PeekMessage, программа извлекает из очереди сообще ний новое сообщение, а вызывая методы T r a n s l a t e M e s s a g e и D i s p a t c h Message, соответственно обрабатывает его (преобразует нажатие клавиш в со общения WM_CHAR) и направляет нужной оконной процедуре.
Обработка сообщений Операционная система Windows построена таким образом, что в подавляющем большинстве случаев программа, которой посылается сообщение, обязана долж ным образом отреагировать на него. Хочу подчеркнуть, что многие сообщения приводят к возникновению событий компонентов. Программа может игнориро вать эти события, но обязана обработать сообщение, их породившее. Значительную часть работы по обработке сообщений программы, созданные с по мощью Delphi, выполняют автоматически. Для этого Delphi, в частности, преоб разует тип TMsg в собственный тип TMessage: type TMessage = packed record Msg: Cardinal; case Integer of 0: ( WParam: Longint; LParam: Longint; Result: Longint); 1: ( WParamLo: Word; WParamHi: Word; LParamLo: Word; LParamHi: Word; ResultLo: Word; ResultHi: Word) ; end; Нетрудно заметить, что запись TMessage содержит меньше информации, чем TMsg; Вместе с тем, в нее введено поле R e s u l t . Связано это с тем, что програм ма анализирует характер полученного сообщения и создает многочисленные спе циализированные сообщения. Например, при нажатии клавиши или ее отпуска нии создается сообщение такой структуры: type TWMKey = packed record Msg: Cardinal; CharCode: Word;
4 1 2 Глава 18 • Программа Unused: Word; KeyData: Longint; R e s u l t : Longint; end; А при манипуляциях пользователя мышью структура сообщения иная: type TWMMouse = packed record Msg: Cardinal; Keys: Longint; case Integer of 0: ( XPos: Smallint; YPos: Smallint); 1: ( Pos: TSmallPoint; Result: Longint); end; Дополнительное поле R e s u l t вставлено потому, что в некоторых случаях ядро Windows требует ответа на посланное сообщение. Например, на сообщение о пе ремещении мыши в режиме перетаскивания (Drag&Drop) программа должна от вечать, готова ли она принять «посылку». В подавляющем большинстве случаев для обработки сообщений программист использует обработчики событий компонентов. Реализуется это следующим об разом. Оконная процедура анализирует тип сообщения и определяет, создан ли в программе нужный обработчик события. Если создан, оконная процедура пре образует сообщение в наиболее удобную для обработки форму и передает управ ление обработчику, если нет (и после завершения работы обработчика), проис ходит стандартная (заданная по умолчанию) обработка. Еще раз подчеркну, что обработка сообщения реализуется всегда, независимо от того, создан для этих целей обработчик соответствующего события или нет. В некоторых специальных случаях программисту может понадобиться нестан дартная обработка того или иного события. Для этого он должен создать метод объекта, объявив его с директивой message: procedure WMPaint(var Msg: TWMPaint); message WM_PAINT; В такого рода обработчиках обычно вначале реализуются нестандартные дей ствия, а потом вызывается унаследованный обработчик, который совершает стан дартную обработку: procedure TForml.WMPaint(var Msg: TWMPaint); begin // Реализуем нестандартные действия inherited end;
// Вызываем унаследованный обработчик
Программа и Windows
413
Однако в некоторых случаях удобнее сначала реализовать стандартную обработ ку или даже вовсе отказаться от нее. В ряде случаев можно организовать нестандартную обработку в обработчике OnMessage объекта A p p l i c a t i o n . Его заголовок таков: procedure TForml.AppMessageHandler( var Msg: TMsg; var Handled: Boolean); Этот обработчик получает любое сообщение до какой-либо предварительной его обработки и в параметре H a n d l e d сообщает оконной процедуре, нуждается ли переданное ему сообщение в дополнительной обработке ( F a l s e — нуждается). Обычно в файле проекта название процедуры обработчика помещается в свой ство OnMessage перед обращением к методу Run: program P r o j e c t l ; begin Application.OnMessage := Forml.AppMessageHandler; Application.Run; end.
ВНИМАНИЕ
——
Программа обычно получает множество Windows-сообщений, поэтому обработчик OnMessage является самой часто вызываемой процедурой вашей программы. Вряд ли стоит выполнять в нем хоть какую-то работу, требующую интенсивных вычислений, иначе ваша программа пе рестанет немедленно реагировать на сообщения и «зависнет».
Посылка сообщений Программа может не только получать, но и посылать сообщения — самой себе или другой программе. Для этого в распоряжении программиста имеются мето ды Perfom компонентов, методы B r o a d c a s t потомков класса T W i n C o n t r o l и, наконец, API-функции SendMessage и P o s t M e s s a g e . Метод Perfom есть у любого наследника класса T C o n t r o l , в котором он описан следующим образом: function TControl.Perfom(Msg: Cardinal; WParam, LParam: Longint): Longint; Обращение к этому методу удобно использовать для ускоренной обработки со общения, так как в этом случае его доставкой Windows не занимается. Метод передает компоненту сообщение и ожидает результата его обработки. Для передачи одного и того же сообщения сразу группе компонентов все на следники класса T W i n C o n t r o l (а именно этот класс является родителем всех компонентов-контейнеров) имеют метод B r o a d c a s t : procedure TWinControl.Broadcast(var Message); Этот метод также ожидает результата обработки сообщения каждым помещен ным в контейнер компонентом. Если какой-то компонент в поле R e s u l t пере данного ему сообщения возвращает не ноль, рассылка сообщения остальным ком понентам прекращается и метод завершает свою работу.
414
Глава 18 • Программа
Для посылки сообщения с использованием механизма Windows предназначены следующие функции: f u n c t i o n SendMessage(hWnd: WParam: WPARAM; LParam: f u n c t i o n PostMessage(hWnd: WParam: WPARAM; LParam:
HWND; Msg: UINT; LPARAM): LRESULT; HWND; Msg: UINT; LPARAM): B 0 0 1 ;
Функции имеют одинаковый набор параметров и различаются тем, что первая ожи дает окончания обработки переданного сообщения и возвращает его результат, а вторая сразу же возвращает управление вызывающей программе и лишь сообщает о том, смогла ли она поместить сообщение в очередь. Замечу, что параметр WPARAM в первом случае обычно имеет значение 1, а во втором 0 — так Windows извещает программу, ждет операционная система немедленного ответа или нет. Пользовательские сообщения Программа может создать и передать другой программе (или другому окну своей программы) нестандартное для Windows (пользовательское) сообщение. С этой це лью Windows резервирует диапазон значений кодов сообщений WM_USER...$7FFFF, например: const SX_MYMESSAGE = WM_USER+1; begin SendMessage(SomeForm.Handle, SX_MYMESSAGE, 1, 0 ) ; end;
Чтобы оконная процедура формы-адресата смогла правильно отреагировать на такое сообщение, в ней следует предусмотреть соответствующий обработчик: TSomeForm = c l a s s ( T F o r m ) private procedure SXMyMessage(var Msg: TMessage);
end;
property CharacterCasing: CharacterCasing;
procedure SomeRorm.SXMyMessage(var Msg: TMessage); begin Код
реакции
на
пользовательское
Замечательной особенностью среды Delphi является то, что процесс ее создания и совершенствования осуществлялся независимым (от Microsoft) разработчи ком — корпорацией Borland. В результате Delphi содержит множество компонен тов и других программных решений, которых нет в продуктах Microsoft. При пе реходе на технологию .NET разработчики Delphi не только сохранили эти различия (некоторые из них описаны в главах предыдущей части), но и предос тавили программистам средства, которыми традиционно пользовались програм мисты, ориентирующиеся на продукты корпорации Microsoft. Речь идет о систе мах программирования Visual Studio .NET (Visual Basic .NET, C#, J# и пр.). В главах этой части описывается инструментарий, который, хотя и поддержива ется средой CLR, не является прерогативой исключительно Delphi. В рамках кни ги этот инструментарий назван библиотекой WinForms. Как и в предыдущей ча сти, здесь рассматриваются компоненты WinForms общего назначения. ПРИМЕЧАНИЕ При описании типов данных в этой части книги использован синтаксис Visual Basic .NET, в соот ветствии с которым имя свойства (функции) может совпадать с именем типа, что недопустимо в Delphi. Например, в следующем операторе объявляется, что-свойство c h a r a c r e t c a s i n g от носится к типу CharacterCasing:
message SX_MYMESSAGE;
//
Создание WinFormsприложений
сообщение
end; ВНИМАНИЕ — Никогда не посылайте сообщение, если не уверены, что адресат сможет его обработать.
с\\
Класс Delegate — обработка событий HelpLink:
Классы общего назначения
417
String;
URL (Universal Resource Locator — универсальный локатор ресурса) однознач но определяет файл и тему помощи, связанной с исключением. InnerException: Exception; Ссылка на объект исключения (только для чтения). Message: String; Содержит поясняющую строку. Sourse: String; Имя приложения, вызвавшего исключение. StackTrace: String; Содержимое стека на момент исключения.
Класс Delegate — обработка событий Как и в VCL, в WinForms есть классы, которые не имеют непосредственного ком понентного воплощения, но без которых было бы трудно и даже невозможно соз дание полноценных программ. В данной главе рассматриваются некоторые из этих классов. Замечу, что в VCL некоторые классы аналогичны классам WinForms, однако последние, как правило, обладают большими возможностями. СОВЕТ Чтобы воспользоваться описываемыми в этой главе классами в VCL-приложениях, достаточно объявить ссылки на соответствующие пространства имен.
ПРИМЕЧАНИЕ В рамках WinForms широко используются перегруженные методы. Поэтому в дальнейшем в це лях сокращения объема книги при описании перегруженного метода приводится лишь один его прототип, а в конце описания дается ссылка: «Информацию о перегруженных методах см. в спра вочной службе».
Специфика событий и их обработки такова, что объект, возбудивший событие, ничего не может знать об объекте, который получит это событие и будет его об рабатывать. Для установления связи между источником события и его получате лем во многих системах программирования (и в том числе — в Delphi предыду щих версий) используется механизм указателей. Свойство-событие в рамках класса-источника трактуется как указатель на некоторую процедуру. Класс-по лучатель записывает в это свойство ссылку на собственный метод, предназна ченный для обработки события. Как уже говорилось в главе 3, в CLR возникают определенные трудности при обработке указателей вообще и указателей на обработчики событий в частности. Для разрешения этих трудностей в CTS создан специальный класс D e l e g a t e . Его объекты называются делегатами. Делегаты — это просто разновидности ука зателей: они указывают на метод. Их особенностью является то, что они уста навливают связь источника события с его получателем. Использование делега тов не исчерпывается только обработкой событий, но в рамках этой книги они описываются именно с этой целью. В отличие от других классов, класс D e l a g a t e имеет сигнатуру (прототип), по этому он может ссылаться только на метод, имеющий такую же сигнатуру. Эта сигнатура часто бывает такой:
Класс Exception — обработка исключений
procedure NameProc(Sender: Object; e: System.EventArgs);
Несмотря на полное совпадение названий, классы E x c e p t i o n в WinForms и VCL — это разные классы (пространства имен— System и B o r l a n d . D e l p h i . System). Класс S y s t e m . E x c e p t i o n дает программисту большие возможности для анализа ошибки, вызвавшей исключение. Напомню, что в классе B o r l a n d . D e l p h i . Sys t e m . E x c e p t i o n имеется свойство p r o p e r t y M e s s a g e : S t r i n g , содержащее поясняющую строку, и свойство p r o p e r t y H e l p C o n t e x t : I n t e g e r , использую щееся для вызова развернутой справки. В классе S y s t e m . E x c e p t i o n имеются такие свойства:
Здесь NameProc — имя обработчика события; S e n d e r — ссылка на объект, воз будивший событие; е — ссылка на объект, содержащий связанные с событием параметры. По соглашению все делегаты ссылаются на метод-процедуру с двумя параметрами. Различие проявляется только в классе параметра е, который дол жен быть наследником базового класса S y s t e m . E v e n t A r g s . Все классы-делегаты (а в CTS их несколько сотен) произведены от прямого на следника D e l e g a t e — класса M u l t i c a s t D e l e g a t e . Последний отличается от своего предка способностью объединять несколько обработчиков одного и того же события. Для этого он передает своим потомкам методы Combinelmpl и Remo14 Зак. 126
418
Глава 19 • Классы общего назначения
v e l m p l . Первый присоединяет к текущему делегату другой, второй осуществляет обратную операцию. Объединенные делегаты работают последовательно в порядке их присоединения. Таким способом создаются события с несколькими обработ чиками. В рамках Delphi операции с делегатами затруднены, так как вся работа по их соз данию делается компилятором без участия программиста (в отличие от других языков программирования .NET, в Delphi нет зарезервированного слова d e l e g a t e ) . Для создания делегата объявляются процедурный тип и свойство-событие этого типа, например: type TMyEvent = procedure(Sender: TObject; MyParEvent: I n t e q e r ) of object; MyClass = c l a s s private FMyEvent: TMyEvent; published property MyEvent: TMyEvent read FMyEvent write FMyEvent; end; В результате обработки этого фрагмента кода компилятор создаст класс-делегат TMyEvent как наследник от M u l t i c a s t D e l e g a t e . В этом нетрудно убедиться с помощью утилиты ILDasrn.exe (дизассемблер языка CIL). Утилита поставляет ся вместе с .NET Framwork, VisualStudio .NET 2003 и некоторыми другими про дуктами. Запустите утилиту и загрузите в нее ЕХЕ-файл проекта, в котором объявляется предыдущий делегат (рис. 19.1).
Класс ArrayList— наборы объектов
419
В новом окне вы увидите такие строки: . c l a s s p u b l i c auto a n s i s e a l e d TMyEvent extends [mscorlib]System.MulticastDelegate {
} // end of c l a s s TMyEvent Таким образом, процедурный тип TMyEvent стараниями компилятора превратил ся в одноименный класс-делегат, но программист об этом не подозревает, a Delphi тщательно скрывает от него этот факт: если, например, вы объявите переменную типа TMyEvent и попытаетесь трактовать ее как объект-наследник от M u l t i c a s t D e l e g a t e , компилятор объявит о несовместимости типов, так как по-прежнему считает тип TMyEvent процедурным! Несмотря на то что программист Delphi лишен возможности явно использовать сред ства делегатов, он может создавать события с несколькими слушателями. Для этого в Delphi 2005 (8) перегружены стандартные процедуры I n c l u d e и E x c l u d e . В пре дыдущих версиях они обслуживали множества, а в Delphi 2005 (8) они могут также присоединять нескольких делегатов к одному событию (удалять из него). Чтобы добавить делегат, нужно обратиться к процедуре I n c l u d e с таким прототипом: Include(<событие>, <обработчик>) Например: Include(Buttonl.Click, Include(Buttonl.Click,
Buttonl_Click); Bitton2_Click);
Объединенные делегаты работают последовательно в порядке их подключения процедурой. В нашем примере обработчик B u t t o n 2 _ C l i c k будет вызван сразу после завершения работы обработчика B u t t o n l _ C l i c k .
Класс ArrayList— наборы объектов Класс A r r a y L i s t относится к коллекциям'- и во многом подобен VCL-классу T L i s t : он также предназначен для хранения множества объектов разного типа. Важным достоинством класса является возможность простого манипулирования не ^единственным объектом, а их набором. В табл. 19.1 и 19.2 перечисляются важ нейшие свойства и методы класса. Таблица 19.1. Свойства класса ArrayList £^£[во
Рис. 19.1. Дизассемблирование исполняемого файла
Раскройте узел TMyEvent и дважды щелкните на строке .class public auto ansi sealed
Назначение
property Capacity: Integer;
Емкость коллекции
property Count: I n t e g e r ; property IsFixedSize : Boolean;
Содержит количество элементов коллекции Указывает, может ли коллекция изменять свои размеры Указывает, можно ли изменять элементы коллекции Открывает индексированный доступ к элементам коллекции
property IsReadOnly: Boolean; property Iteraflnd: Integer] : upject;
Коллекциями называются многочисленные классы CTS, исполняющие интерфейс I L s t .
420
Глава 19 • Классы общего назначения
Т а б л и ц а 1 9 . 2 . Методы класса ArrayList Метод
Назначение
Добавляет новый элемент в конец коллекции function Add (AObject: Object) : и возвращает его индекс Integer; Добавляет в конец коллекции все элементы procedure AddRange (Coll: из коллекции C o l l Object); function BinarySearch (AObject) : Ищет в отсортированной коллекции нужный элемент методом двоичного поиска Integer; Удаляет (но не уничтожает) все элементы procedure Clear; из коллекции. Свойство C a p a c i t y не меняет своего значения Возвращает копии всех элементов коллекции function Clone : Object; как значимого, так и ссылочного типов. Если элемент ссылочного типа, не копируется то, на что он ссылается. Возвращаемый объект исполняет интерфейс I C l o n a b l e . C l o n e function Contains (AObj ect: Object): Boolean; procedure CopyTo (var Arr : Array) procedure CopyTo (var Arr : Array; Place: Integer); overloaded; function FixedSize (Source: ArrayList) : ArrayList;
function GetEnumerator: lEnumerator;
function GetRange (Index, Count: Integer) : ArrayList; function IndexOf (Item: Object) : Integer; procedure Insert(Index: Integer; Item: Object); procedure InsertRange (Index: Integer; c: ICollection) ; function LastlndexOf(AL: ArrayList): Integer; function Readonly (AL: ArrayList): ArrayList; function Remove (AOb j ect: Object); procedure RemoveAt (Index: Integer); procedure RemoveRange (Index, Count: Integer) ; function Repeat (Index, Count: Integer: ArrayList;
Возвращает T r u e , если объект A O b j e c t принадлежит коллекции Копирует содержимое коллекции в одномерный массив. P l a c e — индекс массива, начиная с которого размещается содержимое коллекции Возвращает коллекцию, в которой можно изменять элементы, но нельзя их добавлять или удалять (информацию о перегруженных методах см. в справочной службе) Возвращает итератор для перемещения по коллекции от одного элемента к соседнему (информацию о перегруженных методах см. в справочной службе) Возвращает подмножество коллекции Ищет элемент I t e m и возвращает его индекс (информацию о перегруженных методах см. в справочной службе) Помещает элемент I t e m на указанное место Index Вставляет все элементы коллекции с в текущую коллекцию, начиная с индекса I n d e x Возвращает последний индекс коллекции AL (информацию о перегруженных методах см. в справочной службе) Возвращает коллекцию, элементы которой нельзя изменять (информацию о перегруженных методах см. в справочной службе) Удаляет из коллекции (но не уничтожает) элемент A O b j e c t Удаляет из коллекции (но не уничтожает) элемент с индексом I n d e x Удаляет из коллекции группу, состоящую из C o u n t элементов, начиная с элемента I n d e x Возвращает коллекцию, содержащую C o u n t элементов исходной, начиная с индекса I n d e x
Класс ArrayList— наборы объектов
421
Метод
Назначение
procedure R e v e r s e ;
Меняет порядок следования элементов на обратный (информацию о перегруженных методах см. в справочной службе) Копирует в текущую коллекцию содержимое коллекции С, начиная с индекса I n d e x Сортирует коллекцию, применяя к каждому ее элементу интерфейс I C o m p o r a b l e (информацию о перегруженных методах см. в справочной службе)
procedure SetRange (Index: Integer; С : ArrayList) ; procedure S o r t ;
f u n c t i o n ToArraw: O b j e c t ;
Копирует элементы коллекции в одномерный массив (информацию о перегруженных методах см. в справочной службе)
Доступ к элементам коллекции возможен как по индексу (свойство I t e m ) , так и с помощью итератора (метод G e t E n u m e r a t o r ) . Итератор исполняет интер фейс l E n u m e r a t o r с методами C u r r e n t , MoveNext и R e s e t . Метод MoveNext смещает итератор к очередному (или первому) элементу и возвращает T r u e , если таковой найден. Метод C u r r e n t возвращает текущий элемент, если MoveNext = T r u e . Метод R e s e t возвращает итератор к первому элементу. Следующий пример иллюстрирует использование итератора: procedure T W i n F o r m l . B u t t o n l _ C l i c k ( s e n d e r : S y s t e m . O b j e c t ; e: System.EventArgs); var AL: ArrayList; Enum: lEnumerator; begin AL := ArrayList.Create; Al.Add('y Пэгги был смешной щенок - ' ) ; Al.Addf'OH танцевать под дудку мог.'); Al.Add('Ax, до чего смешной щенок!'); Al.Add('Спляшем, Пэгги, спляшем!'); Enum := AL.GetEnumerator; while Enum.MoveNext do ListBoxl.Items.Add(Enum.Current); Al.Free; end.
// //
Создаем коллекцию Наполняем ее
// // //
Получаем итератор Извлекаем поочередно все элементы коллекции
Метод C u r r e n t итератора позволяет только читать элементы коллекции. При создании коллекции ей первоначально выделяется память для размещения 16 указателей (свойство C a p a c i t y ) . Если в коллекцию помещается 17-й эле мент, ее емкость наращивается еще на 16, и т. д. Если заранее известно, сколько элементов будет содержать коллекция, можно указать ее емкость в конструкторе и тем самым снизить затраты времени на ее расширение: AL := A r r a y L i s t . C r e a t e ( 4 ) ; Элементы коллекции можно сортировать методом S o r t , который перегружен и имеет три прототипа:
4 2 2 Глава 19 • Классы общего назначения procedure Sort; procedure Sort(Comparer : IComparer); procedure Sort(Indexl, Index2: Integer; Comparer: IComparer); В первом случае элементы коллекции должны исполнять интерфейс ICompar a b l e . Во втором и третьем случаях интерфейс сравнения I C o m p a r e r исполня ет параметр Comparer. В этом интерфейсе имеется единственный метод Compare: function Compare(X,
У: Object):
Integer;
Этот метод получает два элемента коллекции, сравнивает их и выдает: •
любое отрицательное число, если первый элемент меньше второго;
•
ноль, если элементы равны;
• любое положительное число, если первый элемент больше второго. В следующем примере, представленном в листинге 19.1, в коллекцию помеща ются 10 случайных вещественных чисел, которые затем сортируются и выводят ся в компонент L i s t B o x l (рис. 19.2).
Класс String и преобразование строк 423 end; constructor Int64.Create(AValue: Double); // Конструктор класса-оболочки begin inherited Create; // Вызываем унаследованный конструктор Val := AValue // Инициируем поле Val end; function CompareClass.Compare(X, Y: TObject): Integer; // Сравнение полей двух объектов класса Int64 begin if Int64(X).Val < Int64(Y).Val then Result := -1 else if Int64(X).Val = Int64(Y).Val then Result := 0 else if Int64 (X-) .Val > Int64(Y).Val then Result := 1 end; procedure TWinForm2.Buttonl_Click(sender: System.Object; e: System.EventArgs); var k: Integer; AL: ArrayList; Enum: IEnumerator; begin AL
Рис. 19.2. Пример сортировки коллекции Листинг 19.1. Пример сортировки коллекции type Int64 = class // Класс-оболочка для чисел Double Val: Double; public constructor Create(AValue: Doublesend; CompareClass = class(TInterfacedObject, IComparer) // Компонентный класс для реализации интерфейса IComparer public function Compare(X, Y: TObject): Integer;
:= ArrayList.Create(10);
// Коллекция рассчитана //на 10 элементов // Наполняем ее
for k := 1 to 10 do AL.Add(Int64.Create(Random)); AL.Sort(CompareClass.Create); // Сравниваем элементы Enum := AL.GetEnumerator; // Получаем итератор while Enum.MoveNext do // Выводим результат ListBoxl.Items.Add((Enum.Current as Int64).Val.ToString); AL.Free; end;
Класс String и преобразование строк Преобразование строк в значимые типы и обратно встречается в программах очень часто. В разделе «Строки» главы 3 были рассмотрены эти действия над типом S t r i n g , входящим в состав VCL. В этом разделе те же действия рассматривают ся с позиций CTS.
424
Класс String и преобразование строк
Глава 19 • Классы общего назначения
Класс String В WinForms тип S t r i n g является классом. В нем определены свойства, пред ставленные в табл. 19.3. Т а б л и ц а 1 9 . 3 . Свойства класса String Свойство
Назначение.
p r o p e r t y Chars ( I n d e x : I n t e g e r ) :
Возвращает указанный символ (только для чтения)
Char; property Length:
Integer;
Возвращает длину строки (только для чтения)
В табл. 19.4 указаны наиболее важные методы класса. Таблица 1 9 . 4 . Методы класса Strings
Метод
Назначение
function Clone: String; function Compare (SI, S2 : String): Integer;
Возвращает копию строки Сравнивает строки и возвращает отрицательное число, ноль или положительное число в зависимости оттого, меньше, равна или больше строка S1 строки S2 (информацию о перегруженных методах см. в справочной службе) function CompareOrdinal (SI, S2 : Сравнивает строки путем сравнения числовых значений их символов (информацию String) : Integer; о перегруженных методах см. в справочной службе) Сравнивает строку с указанным объектом Obj function CompareTo (Obj : (информацию о перегруженных методах TObject): Integer; см. в справочной службе) function Concate(Obj: TObject): Преобразует объект Obj в строку (информацию о перегруженных методах см. в справочной службе) String; Копирует строку function Copy(S: String):
String; procedure CopyTo(Sourcelndex: Integer; Dest: Char; Destlndex, Count: Integer); function EndsWith(S: String): Boolean; function Equals (Obj: TObject) : Boolean; function Format(Fmt: String; Obj: TObject): String;
Копирует не более C o u n t символов исходной строки, начиная с символа S o u r c e l n d e x , в массив D e s t Unicode-символов, начиная с индекса
425
Кроме того, все значимые классы CTS, такие как целые, вещественные, логичес кий, даты-времени, реализуются в виде записей (структур), имеющих методы T o S t r i n g и P a r s e . Первый преобразует значение в строку, второй осуществля ет обратное преобразование. Например, следующий обработчик выведет в надписи L a b e l l строку F a l s e : procedure TWinForml.Buttonl_Click(sender: System.Object; e: System.EventArgs); var B: Boolean; begin В := 10 > 1 1 ;
Labell.Text := B.ToString end; Точно так же каждый значимый тип имеет метод P a r s e , который получает стро ковое представление типа и, если это возможно, возвращает значение типа: procedure TWinForml.Buttonl_Click(sender: System.Object; e: System.EventArgs); var B: Boolean; begin В := В . P a r s e ( ' T r u e ' ); Labell.Text := B.ToString end; Обратите внимание: при обращении к методу P a r s e нельзя указывать имя типа Например, такой оператор вызовет сообщение об ошибке: В := Boolean.Parse('True'); ВНИМАНИЕ
Методы T o S t r i n g и P a r s e нельзя применять к выражениям— только к переменным соот ветствующего типа.
Destlndex
Возвращает T r u e , если текущая строка заканчивается подстрокой S Возвращает T r u e , если текущая строка равна объекту Obj (информацию о перегруженных методах см. в справочной службе) Заменяет некоторый фрагмент строки Fmt текстовым эквивалентом значения объекта Obj (информацию о перегруженных методах см. в справочной службе)
Преобразования нетекстовых данных в строку и обратно — едва ли не самая рас пространенная задача. Для преобразования в строку следует помнить, что класс ТОЬ j e c t (а следовательно, и все классы WinForms и VCL) имеет метод T o S t r i n g , который возвращает тек стовое представление класса. Поскольку большинство классов имеют более или менее сложную структуру, этот метод чаще всего возвращает имя класса.
Преобразования числовых значений
Числовые значения можно преобразовывать в строку с указанием формата. На пример, следующий оператор представит текстовую строку числовой величины D в научном формате (в надписи L a b e l l появится строка 2, 004000Е+003): D := 2004; Labell.Text := D.ToString('E');
Форматирующие спецификаторы задаются в виде строк Ann, где А — тип специ фикатора, а необязательные цифры пп определяют точность спецификатора в диа пазоне от 0 до 99.
В табл. 19.5 перечислены форматирующие спецификаторы для числовых вели чин. Примеры форматирования приводятся для числа 3,14159 (или -314159) и ло кализованной версии Windows.
426
Класс String и преобразование строк
Глава 19 • Классы общего назначения
Т а б л и ц а 1 9 . 5 . Форматирующие спецификаторы для числовых величин Тип
Описание
С (с)
Денежный формат: 3 , 1 4 р . Параметр пп определяет желаемое количество знакомест, которое отводится под строковое представление значения Только целочисленные величины: - 3 1 4 1 5 9 . Если параметр пп меньше, чем необходимо для правильного представления значения, он игнорируется, если больше — строка дополняется ведущими нулями
D (d)
Е (е)
Научный (экспоненциальный) формат: 3 , 1 4 1 5 9 0 Е + 0 0 0 . Параметр пп определяет желаемое количество знакомест, которое отводится под строковое представление значения
F(f)
Формат с фиксированной точкой (запятой) (F10): 3 , 1 4 1 5 9 0 0 0 0 0
G (g) N (п)
Выбирает наиболее компактное представление между форматами Ей G: 3 , 1 4 1 5 9 Числовой формат с разделителями тысяч. Параметр пп указывает количество знаков после запятой (если опущен — 2 знака): - 3 1 4 1 5 9 , 0 0 Проценты от единицы: 314,16%
Р(р) R (г)
Только для вещественных чисел, параметр пп игнорируется. Гарантирует, что полученный строковый эквивалент будет преобразован в точное вещественное значение. Вначале назначается пп = 15 для D o u b l e и 7 для S i m p l e . Если точности недостаточно, она увеличивается до 17 (9)
X (х)
Только для 1б-ричного представления целых чисел: FFFB34D1
В большинстве случаев методы T o S t r i n g и P a r s e решают все проблемы пре образования в строку и обратно. В более сложных случаях может помочь функ ция S t r i n g . Format. Например, следующий обработчик поместит в заголовок окна строку Да а? а: 2 8 . 7 . 2 0 0 4 : procedure TWinForm4.Buttonl__Click(sender: System.Object; e: System.EventArgs); var Y, M, D: I n t e g e r ; S: String; begin Y := 2004; M := 07; D := 28; Text := S.Format('Дата: {0}.{1}.{2}', D.ToString, M.ToString, Y.ToString) end; ПРИМЕЧАНИЕ —
42]
матирующей строкой. Число в фигурных скобках указывает порядковый номе] подставляемого объекта (нумерация начинается с 0). Если в форматирующун строку нужно вставить фигурные скобки, они удваиваются. Перегруженные ме тоды F o r m a t позволяют вставлять до трех замен в строку.
Преобразования перечислений В классическом языке Pascal и в Delphi ранних версий нельзя получить строко вое представление перечисления. В CTS это можно сделать, преобразовав пере числение к типу Enum. Например (в заголовке окна появится строка t h r e e ) : type Dig =
(one,
two,
three);
procedure TWinForml.Buttonl_Click(sender: System.Object; e: System.EventArgs); var C: Dig; begin С := t h r e e ; Text := Enum(C).ToString; end; При обращении к методу Enum. T o S t r i n g можно указывать спецификаторы фор мата, перечисленные в табл. 19.6. Таблица 19.6. Спецификаторы формата для перечислений Тип
Описание
F (f) D (d) х (х)
Показывает текстовое представление текущего перечисления Показывает десятичное значение текущего перечисления Показывает 16-ричное представление текущего перечисления
В спецификаторах для перечислений нельзя указывать точность.
Преобразование значений даты-времени —
Напомню, что в разделе «Строки» главы 3 описана мощная функция F o r m a t , в которой нет ограничений на количество форматирующих замен. Она станет доступна вашей WinForms-npoграмме, если в предложении u s e s будет указана ссылка на пространство имен B o r l a n d . VCL.SysUtils.
При обращении к функции Format форматирующая строка может содержать про извольный текст, куда вставлены фрагменты типа {0 }, которые при формирова нии строки будут заменяться указанным объектом из списка, следующего за фор-
Тип DateTime из CTS является базовым для типа TDateTime из VCL. Реаль ными различиями типов являются точка отсчета и исчисление значений. Если С в TDateTime соответствует 30.12.1899 12:00:00, то в типе DateTime ему соот ветствует значение 1.01.0001 12:00:00. Значение DateTime исчисляется как ко личество 100-наносекундных тиков, прошедших от начального значения. Тип DateTime отличается богатым набором свойств и методов, которые частич но доступны потомку T D a t e S t r i n g , — в том числе методы T o S t r i n g и P a r s e .
В табл. 19.7 и 19.8 перечислены наиболее важные свойства и методы типа D a t e Time.
428
Графический инструментарий
Глава 19 • Классы общего назначения
Т а б л и ц а 1 9 . 7 . Свойства типа DateTime Назначение
Свойство
Возвращает дату Возвращает день месяца (от 1 до 31) Возвращает день недели (0 — воскресенье, 6 — суббота) Возвращает день года (от 1 до 366) Возвращает час (от 0 до 23) Возвращает минуты в дате (от 0 до 59) Возвращает месяц даты (от 1 до 12) Возвращает текущие дату и время Возвращает количество тиков (100-наносекундных интервалов), прошедших от 00:00 1.01.0001 до хранящегося в экземпляре значения property TimeOf Day: TimeSpan Возвращает время, прошедшее с 0 часов значения экземпляра Возвращает текущую дату property Today: DateTime; Возвращает год даты property Year: Integer;
property Date: DateTime; property Day: Integer; property DayOfWeek: Integer; property DayOf Year : Integre; property Hour: Integer; property Minute : Integer; property Month: Integer; property Now: DateTime; property Ticks : Int64;
Метод
Назначение
f u n c t i o n IsLeapYear(Year: I n t e g e r ) : Boolean; f u n c t i o n Parse (S : S t r i n g ) : DateTime;
Возвращает True, если год Year — високосный
429
Преобразует строку s в значение даты-времени
Для присваивания переменной даты-времени нового значения можно использо вать свойства Now, TimeOf Day и Today, обеспечивающие установку в перемен ную, соответственно, текущих даты и времени, текущего времени и текущей даты. Кроме того, многочисленные перегруженные конструкторы позволят установить любое значение, например: D : = D a t e T i m e . C r e a t e (2004,
8,
1,
20,
0,
0);
Переменная D будет иметь такое значение: 01.08.2004
20:00:00
Такой же результат дает метод Parse: D
:= D . P a r s e ( ' 0 1 . 0 8 . 2 0 0 4 2 0 : 0 0 : 0 0 ' ) ;
Таблица 19.8. Методы типа DateTime Метод
Назначение
f u n c t i o n Add ( V a l u e : TimeSpam): D a t e T i m e ; f u n c t i o n AddDays ( V a l u e : Double) : D a t e T i m e ; f u n c t i o n AddHours ( V a l u e : Double) : D a t e T i m e ; function AddMilliseconds ( V a l u e : D o u b l e ) : DateTime, f u n c t i o n A d d M i n u t e s (Value Doublt) : DataTime; f u n c t i o n AddMonth ( V a l u e : I n t e g e r ) : DateTime; f u n c t i o n AddSeconds (Value Double) : D a t e T i m e ; f u n c t i o n AddTicks (Value: Long): DateTime; f u n c t i o n AddYears ( V a l u e : I n t e g e r ) : DateTime; f u n c t i o n Compare ( T l , T2 : DateTime) : I n t e g e r ;
Возвращает дату и время, увеличенные на интервал времени V a l u e Возвращает дату и время, увеличенные на V a l u e дней Возвращает дату и время, увеличенные на V a l u e часов Возвращает дату и время, увеличенные на V a l u e миллисекунд Возвращает дату и время, увеличенные на V a l u e минут Возвращает дату и время, увеличенные на V a l u e месяцев Возвращает дату и время, увеличенные на V a l u e секунд Возвращает дату и время, увеличенные на V a l u e тиков Возвращает дату и время, увеличенные на V a l u e лет
Сравнивает два значения D a t e T i m e и возвращает отрицательное число, если Т1 < Т2, ноль, если Т1 = Т2, и положительное число, если Т1 > Т2 f u n c t i o n CompareTo ( V a l u e : Сравнивает текущее значение со значением, увеличенным на V a l u e , и возвращает - 1 , 0 или +1 в зависимости TObject) : I n t e g e r ; от того, меньше, равно или больше значение экземпляра указанной даты-времени f u n c t i o n DaysOfMonth (Year, Возвращает количество дней в указанном месяце Month: I n t e g e r ) : I n t e g e r ; f u n c t i o n E q u a l s (DTI, DT2) : Сравнивает две даты-времени Boolean;
Графический инструментарий В VCL существует класс TCanvas, который инкапсулирует в себе все необходи мое для отрисовки изображений (см. подраздел «Класс TCanvas» в разделе «Гра фический инструментарий» главы 10). В WinForms такого класса нет. Вместо него предлагаются классы Pen, Brush, Font, Region, Bitmap, P a l e t t e и Graphics. Каждый из них может работать только в рамках заданного контекста графическо го устройства. Графическими устройствами являются дисплей, принтер, плоттер и т. п. Контекст графического устройства (далее просто контекст устройства) пред ставляет собой набор параметров, характеризующих это устройство и использую щихся для вывода изображений, текста, линий и других графических объектов. В дальнейшем изложении контекст устройства и характеризующие это устройство параметры считаются единым целым — графическим устройством. Все графические инструменты .NET Framework связаны с пространствами имен System. Drawing, System.Drawing. Drawing2D и System. Drawing. Imaging. На практике отрисовка изображений часто происходит в обработчике события Paint. В наборе параметров PaintEventArgs обработчику передается контекст устройства в виде параметра Graphics. В таких обработчиках ни создавать, ни освобождать контекст устройства не нужно. В листинге 19.2 приводится текст обработчика, создающего окно, показанное на рис. 19.3. Л и с т и н г 1 9 . 2 . Пример вывода текста в форме procedure TWinForml.TWinForml_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var продолжение &
430
Графический инструментарий
Глава 19 • Классы общего назначения
Л и с т и н г 1 9 . 2 (продолжение)
System.Drawing.Font; // Шрифт для вывода System.Drawing.Brush; // Кисть для заполнения надписи Graphics; // Контекст устройства SW, SH: S i n g l e ; // Размеры надписи const S = 'Delphi 2005 For .NET Framework'; begin g := e . G r a p h i c s ; // Контекст устройства получаем // в параметре вызова // Создаем шрифт и кисть: F := S y s t e m . D r a w i n g . F o n t . C r e a t e ( ' A r i a l ' , 30, FontStyle.Bold); В := System.Drawing.SolidBrush.Create(Color.Red) ; // Согласуем размеры окна с размерами надписи: SW := g . M e a s u r e S t r i n g ( S , F).Width; SH := g . M e a s u r e S t r i n g ( s , F ) . H e i g h t ; Height := Round(3 * SH) ; Width := Round(1.2 * SW) ; // Выводим надпись: g. DrawString(S, F, B, Round(Width - SW) div 2, Height div 4 ) ; end;
Замечу, что описанные в главе 10 классы VCL TBitmap и TCanvas проецируют ся на свойства и методы класса G r a p h i c s , который, таким образом, в функцио нальном отношении полностью заменяет их. В табл. 19.9 и 19.10 указаны наиболее важные свойства и методы класса G r a p h i c s . Таблица 19,9. Свойства класса Graph cs Свойство
type TCompositingMode = (SourseCopy, SoureseOver); property CopositingMode : TCompositingMode; type TCompositingQuality = (AssumeLiner, Default, HighQuality, Highspeed, I n v a l i d ) ; property CompositingQuality: ; property DPiX: S i n g l e ; property DPiY: S i n g l e ; type TInterpolationMode = (Bicubic, B i l i n e a r , Default, High, HighQualityBicubic, HighQualityBilinear, I n v a l i d , Low);property InterpolationMode: TInterpolationMode;
property Transform: Matrix;
Класс G r a p h i c s определяет набор свойств и методов для отрисовки различных изображений на поверхности контекста устройства. Он передается как параметр вызова обработчикам события P a i n t , и в этом случае его не нужно создавать или уничтожать. Для самостоятельного создания объекта G r a p h i c s могут ис пользоваться его методы FromXXX (см. далее).
отрисовки Определяет, будут ли пикселы изображения заменять (SourseCope) собой пикселы фона или взаимодействовать с ними
Определяет качество отрисовки: A s s u m e L i n e r — допускается линеаризация значений; D e f a u l t — умалчиваемое качество; H i g h Q u a l i t y — высокое качество, низкая скорость; H i g h s p e e d — низкое качество, высокая скорость; I n v a l i d — неправильное значение качества Горизонтальная разрешающая способность контекста Вертикальная разрешающая способность контекста Определяет режим интерполяции (изменение цвета между двумя точками изображения): B i c u b i c — бикубическая интерполяция; B i l i n e a r — билинейная интерполяция; D e f a u l t — интерполяция по умолчанию; H i g h — интерполяция высокого качества; H i g h Q u a l i t y B i c u b i c — бикубическая интерполяция высокого качества; H i g h Q u a l i t y B i l i n e a r — билинейная интерполяция высокого качества; I n v a l i d — неправильное значение; Low— интерполяция низкого качества Содержит матрицу преобразования координат отрисовки
Таблица 19.10. Методы класса Graph cs Метод
Описание
function BeginContainer: GraphicsContainer;
Начинает работу с графическим контейнером (информацию о перегруженных методах см. в справочной службе) Очищает поверхность отрисовки и заливает ее указанным цветом Освобождает все ресурсы, связанные с объектом G r a p h i c s Вычерчивает пером Р дугу эллипса, вписанного в прямоугольник R, в секторе, ограниченном углами S t a r t и F i n i s h (информацию о перегруженных методах см. в справочной службе)
procedure Clear (Col: Color) ;
Класс Graphics
Назначение
Содержит область отрисовки property C l i p : Region; propertyClipBounds: RectangleF, Возвращает вещественные координаты области
Рис. 19.3. Пример вывода текста в форме
Классы F o n t , Pen, B r u s h и B i t m a p имеют такое же функциональное назначе ние, что и, соответственно, классы TFont, TPen, TBrush и TBitmap библиотеки VCL. Хотя реализованы эти классы по-разному, указанные WinForms-классы в це лях сокращения объема книги здесь не рассматриваются. Классов, подобных G r a p h i c s и Region, в VCL нет. Они описаны далее.
431
procedure Dispose; procedure DrawArc(P: Pen; R: Rectangle; S t a r t , Finish: Single);
продолжение -^
432
Графический инструментарий
Глава 19 • Классы общего назначения
Таблица 19.10 (продолжение) Метод
Описание
procedure DrawBezier (P : Pen; Т1, Т2, ТЗ, Т4 : Point) ;
Вычерчивает кривую Безье, проходящую по указанным точкам (информацию о перегруженных методах см. в справочной службе) Вычерчивает замкнутую кривую, проходящую по заданным точкам (информацию о перегруженных методах см. в справочной службе) Вычерчивает кривую, проходящую по заданным точкам (информацию о перегруженных методах см. в справочной службе)
procedure DrawClosedCurve (Р: Pen; Points: array of Point) procedure DrawCurve (P: Pen; Points: array of Point) ; procedure DrawEllipse (P : Pen; Rect: Rectangle) ; procedure DrawIcon(Ic: Icon; Rect: Rectangle);
Вычерчивает эллипс, вписанный в прямоугольник R e c t (информацию о перегруженных методах см. в справочной службе) Вычерчивает значок 1с в области R e c t (информацию перегруженных методах см. в справочной службе)
procedure Drawlmage (Im: Image; Rect: Rectangle);
Вычерчивает изображение Im в области R e c t (информацию о перегруженных методах см. в справочной службе)
procedure DrawLine(P: Pen; Tl, T2: Point) ;
Вычерчивает линию между указанными точками (информацию о перегруженных методах см. в справочной службе) Вычерчивает линии, соединяющие указанные точки (информацию о перегруженных методах см. в справочной службе) Вычерчивает пером Р объект P a t h
procedure DrawLines(P: PeruP o i n t s : array o f P o i n t ) ; procedure DrawPath(P: Pen; Path: GraphicsPath); procedure DrawPie(P: Pen; R e c t : Rectangle; StartAngle, FinishAngle: I n t e g e r ) ;
procedure DrawPolygon (P : Pen; P o i n t s : array of P o i n t ) ; procedure DrawRectangle (P: Pen; Rect: R e c t a n l e ) ; procedure D r a w S t r i g ( S : S t r i n g ; F: Font; B: Brush; S t a r t : Single); procedure EndContainer (Container: GraphicsContainer) procedure E x c l u d e C l i p ( R e c t : Rectangle); procedure F i l l C l o s e d C u r v e (B: Brush; P o i n t s : array o f P o i n t ) ;
procedure F i l l E l l i p s e (B : Brush; Rect:Rectangle);
Вычерчивает сектор эллипса, вписанного в прямоугольник R e c t , ограниченный радиальными лучами S t a r t A n g l e , FinishAngle (информацию о перегруженных методах см. в справочной службе) Вычерчивает полигон — замкнутую ломаную линию, соединяющую заданные точки (информацию о перегруженных методах см. в справочной службе)
433
Метод
Описание
procedure FillPath (В: Brush; Path: GraphicsPath) ; procedure FillPie (В : Brush; Rect: Rectangle; StartAngle, FinishAngle: Single); procedure FillPolygon(B: Brush; Points: array of Point) ;
Закрашивает внутреннюю область объекта P a t h
procedure FillRectangle (B: Brush; Rect: Rectangle);
Закрашивает внутреннюю область прямоугольника (информацию о перегруженных методах см. в справочной службе)
procedure FillRectangles (B: Brush; Rects: array of Rectangle) ; procedure FillRegion (B: Brush; Reg: Region);• function FromHdc (DC: IntPtr) : Graphics;
Закрашивает внутренние области нескольких прямоугольников (информацию о перегруженных методах см. в справочной службе)
function FromHWND(HWND: IntPtr): Graphics;
Создает объект G r a p h i c s по заданному дескриптору окна HWND
funcation Fromlmage(Img: Image): Graphics;
Создает объект G r a p h i c s по заданному объекту Img
function GetHDC: IntPtr;
Возвращает контекст устройства объекта
function IsClipEmpty: Boolean;
Возвращает T r u e , если объект пуст
Закрашивает внутреннюю область сектора эллипса (информацию о перегруженных методах см. в справочной службе) Закрашивает внутреннюю область полигона (информацию о перегруженных методах см. в справочной службе)
Закрашивает область Reg (информацию о перегруженных о методах см. в справочной службе) Создает объект G r a p h i c s по заданному контексту устройства DC
function IsVisible(T: Point): Boolean;
Возвращает T r u e , если точка Т принадлежит видимой области объекта (информацию о перегруженных методах см. в справочной службе) function MeasureCharacterRanges Метод возвращает объект R e g i o n , который (S: String; F: Font; Layout: содержит заданную подстроку строки S RectanglF; SF: array of StringFormat) : array of Region; function MeasureString (S: String;F: Font): SizeF;
Метод возвращает размеры строки S (информацию о перегруженных методах см. в справочной службе)
Вычерчивает прямоугольник (информацию о перегруженных методах см. в справочной службе) Выводит строку S текста: F— шрифт; В — кисть; S t a r t — начальная точка (информацию о перегруженных методах см. в справочной службе) Заканчивает работу с графическим контейнером
procedure ReleaseHdc (Hdc: IntPtr);
Освобождает контекст устройства, полученный методом GetHDC
procedure ResetClip;
Восстанавливает бесконечные границы области отрисовки
procedure Restore(GS: GraphicsObject);
Восстанавливает состояние объекта, сохраненное в объекте GS, методом Save
Исключает из области отрисовки прямоугольник R e c t (информацию о перегруженных методах см. в справочной службе) Закрашивает кистью В внутреннюю область замкнутой кривой, проходящей через точки P o i n t s (информацию о перегруженных методах см. в справочной службе) Закрашивает кистью В внутреннюю область эллипса (информацию о перегруженных методах см. в справочной службе)
function Save: GraphicsState;
Сохраняет текущее состояние объекта
procedure SetClip(G: Graphics); Устанавливает размеры области отрисовки текущего объекта, указанные в свойстве C l i p объекта G (информацию о перегруженных методах см. в справочной службе) procedure (CS1, CS2: CoordinatSpace;Points: array of Point) ;
Преобразует массив точек P o i n t s из одной координатной системы в другую (информацию о перегруженных методах см. в справочной службе) продолжение &
434
Глава 19 • Классы общего назначения
Таблица 1 9 . 1 0 (продолжение) Метод
Описание
p r o c e d u r e T r a n s l a t e C l i p (X, Y: Integer); procedure T r a n s l a t e T r a n s f o r m (X, Y: S i n g l e ) ;
Изменяет размеры области отрисовки (информацию о перегруженных методах см. в справочной службе) Изменяет начало координат области отрисовки (информацию о перегруженных методах см. в справочной службе)
Как видите, класс имеет богатейший набор методов, облегчающих отрисовку изоб ражений. Методами B e g i n C o n t a i n e r и E n d C o n t a i n e r создается и уничтожается графи ческий контейнер — специальный объект класса G r a p h i c s C o n t a i n e r (простран ство имен S y s t e m . D r a w i n g . Drawing2D), в котором сохраняются текущие па раметры объекта G r a p h i c s . При вызове метода B e g i n C o n t a i n e r параметры помещаются в стек. Любые изменения параметров до вызова E n d C o n t a i n e r уничтожаются, восстанавливаются сохраненные в стеке. Контейнеры могут быть вложенными, но каждый вызов E n d C o n t a i n e r уничтожает самый последний контейнер (количество вызовов E n d C o n t a i n e r может быть меньше вызовов BeginContainer). В следующем примере создается контейнер, затем начало координат смещается на 100 пикселов влево и вниз, вычерчивается черной утолщенной линией пря моугольник, и контейнер уничтожается. После этого вычерчивается синий пря моугольник того же размера (рис. 19.4).
Графический инструментарий begin with e . G r a p h i c s do begin GC:= B e g i n C o n t a i n e r ;
//
Создаем
контейнер
T r a n s l a t e T r a n s f o r m ( 1 0 0 , 1 0 0 ) ; // Смещаем начало координат // Вычерчиваем черный прямоугольник: D r a w R e c t a n g l e ( P e n . C r e a t e ( C o l o r . B l a c k , 2 ) , 0 , 0 , 200, EndContainer(GC); // Удаляем контейнер // Заливаем синий прямоугольник FillRectangle(SolidBrush.Create(Color.Blue) , 0, 0, 200, 2 0 0 ) ; end end;
200);
Аналогично работают методы Save и R e s t o r e . Методы DrawArc и DrawPie используют ограничивающие углы S t a r t и F i n i s h . Начало отсчета углов соответствует радиальной горизонтальной линии, направ ленной направо. Положительные углы отсчитываются по часовой стрелке, отри цательные — против. Методы S e t C l i p , I n t e r s e c t C l i p и R e s e t C l i p управляют размерами и поло жением области отрисовки. В следующем примере (листинг 19.3) сначала созда ется область отрисовки, соответствующая прямоугольнику 1 на рис. 19.5, затем область отрисовки методом I n t e r s e c t C l i p пересекается с областью прямоуголь ника 2 и выводится синий (на рисунке - темный) прямоугольник с размерами и по ложением, соответствующими прямоугольнику 3. В результате закрашенной ока зывается лишь область, соответствующая прямоугольнику 4. В заключение область отрисовки восстанавливается методом R e s e t C l i p и утолщенной черной линией рисуется периметр закрашенного прямоугольника.
Рис. 19.4. Демонстрация работы с контейнером Uses S y s t e m . D r a w i n g . D r a w i n g 2 D ; procedure TWinForml.TWinForml_Paint(sender: S y s t e m . O b j e c t ; e : System.Windows.Forms.PaintEventArgs) ; var GC: G r a p h i c s C o n t a i n e r ; // Графический контейнер
435
Рис. 19.5. Пересечение областей отрисовки
436
Глава 19 • Классы общего назначения
Листинг 19.3. Пример пересечения областей отрисовки procedure TWinForml.TWinForml_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var ClipRect, IntersectRect: Rectangle; F: System.Drawing.Font; // He путать со свойством формы! В: Brush; begin // Создаем первую область: ClipRect := Rectangle.Create (0, 0, 200, 200); // Показываем ее: е.Graphics.DrawRectangle(Pen.Create(Color.Black) , ClipRect); // Создаем вторую область: IntersectRect := Rectangle. Create (100, 100', 500, 400); // Показываем ее: e.Graphics.DrawRectangle(Pen.Create(Color.Black) , IntersectRect); // Устанавливаем область 1: e . Graphics.SetClip(ClipRect); // Яересекаем ее с областью 2: е.Graphics.IntersectClip(IntersectRect); // Заливаем прямоугольник 3: е.Graphics.FillRectangle(SolidBrush.Create(Color.Blue), 0, 0, 500, 400); // Отменяем все установки: e.Graphics.ResetClip; // Показываем прямоугольник 3: е.Graphics.DrawRectangle(Pen.Create(Color.Black, 2) , 0, 0, 500, 400); end; Обратите внимание: форма TWinForml имеет свойство Font, а обработчик TWinF o r m l _ P a i n t работает в контексте формы. Чтобы получить доступ к классу F o n t (а не к свойству формы), нужно указать полное квалификационное имя класса System.Drawing.Font. Если вместо метода I n t e r s e c t C l i p в предыдущем примере вызвать метод E x c l u d e C l i p , получим результат, показанный на рис. 19.6. Класс G r a p h i c s дает возможность использования графических объектов класса G r a p h i c s P a t h (пространство имен System. Drawing. Drawing2D). Эти объекты имеют методы, позволяющие наполнять их графическими примитивами, линия ми и кривыми. Метод D r a w P a t h вычерчивает все содержимое объекта G r a p h i c s P a t h разом. Рисунок 19.7 получен с помощью программы, представлен ной в листинге 19.4.
Графический инструментарий 4 3 7
Рис. 19.7. Демонстрация метода DrawPath Листинг 19.4. Пример работы метода DrawPath implementation uses System.Drawing.Drawing2D; procedure TWinForml.TWinForml_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var GP: GraphicsPath; begin GP := GraphicsPath.Create; // Создаем объект GraphicsPath GP.AddEllipse(60, 0, 60, 40); // Наполняем его эллипсами GP.AddEllipse(60, 40, 60, 80); GP.AddEllipse(31, 60, 30, 30); GP.AddEllipse(119, 60, 30, 30); продолжение #
4 3 8 Глава 19 • Классы общего назначения Листинг 19.4 (продолжение) G P . A d d E l l i p s e ( 5 5 , 120, 30, 2 0 ) ; G P . A d d E l l i p s e ( 9 5 , 120, 30, 2 0 ) ; е.Graphics.TranslateTransform(60, 60); // Отрисовываем все содержимое объекта GpahicsPath: е . G r a p h i c s . D r a w P a t h ( P e n . C r e a t e ( C o l o r . B l a c k , 3 ) , GP) ; end; Методы DrawBezier и DrawCurve отрисовывают кривые линии. Кривая Безье определяется начальной, конечной, а также несколькими промежуточными точ ками (в методе D r e w B e z i e r используются две промежуточные точки). В мето де DrawCurve используется сплайн-интерполяция между произвольным коли чеством точек. На рис. 19.8 показаны примеры кривых, создаваемых этими методами.
Рис. 19.8. Демонстрация методов DrawLines, DrawCurve и DrawBezier Этот рисунок создан обработчиком, представленным в листинге 19.5. Листинг 19.5. Пример работы методов DrawLines, DrawCurve и DrawBezier procedure TWinForml.TWinForml__Paint(sender: S y s t e m . O b j e c t ; e: S y s t e m . W i n d o w s . F o r m s . P a i n t E v e n t A r g s ) ; Curve: array [1..7] of Point; Bezier: array [1..4] of Point; begin Curve[1] := Point.Create(50, 50); Curve[2] := Point.Create(100, 25); Curve[3] := Point.Create(200, 50); Curve[4] := Point.Create(100, 150) Curve[5] := Point.Create(300, 100) Curve[6] := Point.Create(350, 200) Curve[7] : = Point.Create(250, 150) e.Graphics.DrawCurve(Pen.Create(Color.Black, 2 ) , Curve);
Графический инструментарий 4 3 9 e.Graphics.DrawLines(Pen.Create(Color.Red), Curve) ; Bezier [1] := Point.Create(500, 50); Bezier[2] := Point. Create (350, 100); Bezier[3] := Point.Create (650, 150); Bezier [4] := Point.Create (500, 200); e.Graphics.DrawBezier(Pen.Create (Color.Black, 2 ) , Bezier[1], Bezier [2], Bezier[3], Bezier[4]); e.Graphics.DrawLines(Pen.Create(Color.Red) , Bezier); end; Метод MeasureCharacterRanges возвращает объекты типа Region, положе ние и размеры которых соответствуют положению и размерам некоторых под строк в заданной строке. Параметр обращения SF класса StringFormat имеет метод SetMeasurableCharacterRanges; с помощью этого метода задаются по рядковый номер символа (нумерация начинается с нуля) и количество симво лов, положение которых нужно определить. В листинге 19.6 представлена про грамма, создающая окно, показанное на рис. 19.9.
Рис. 19.9. Демонстрация метода MeasureCharacterRanges Листинг 19.6. Пример работы методов MeasureCharacterRanges procedure TWinForml.TWinForml_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var F: System.Drawing.Font; SF: StringFormat; CR: array [1..2] of CharacterRange; Layout: RectangleF; Reg: array [ 1. . 2] of System.Drawing.Region; MR: RectangleF; const S = 'First and Second ranges'; begin
//
Массив
подстрок
//
Массив
областей
//
Строка
вывода
F := System.Drawing.Font.Create // Создаем шрифт ('Times New Roman', 22, FontStyle.Bold); продолжение ^
440
Глава 19 • Классы общего назначения
Листинг 19.6 (продолжение) CR[1] := C h a r a c t e r R a n g e . C r e a t e ( 0 , 5 ) ; // Подстрока First CR[2] := C h a r a c t e r R a n g e . C r e a t e ( 1 0 , 6 ) ; // Подстрока Second Layout:= RectangleF.Create(50.0, 50.0, // Размеры и положение e . G r a p h i c s . M e a s u r e S t r i n g ( S , F ) . W i d t h , / / строки e.Graphics.MeasureString(S, F).Height); e . G r a p h i c s . D r a w S t r i n g (S, F, // Выводим строку SolidBrush.Create(Color.Black) , 50.0, 50.0); SF := S t r i n g F o r m a t . C r e a t e ; // Указываем направление вывода: SF.FormatFlags := StringFormatFlags.DirectionRightToLeft; // Указываем массив подстрок: SF.SetMeasurableCharacterRanges(CR); // Получаем массив областей для подстрок: Reg := е . G r a p h i c s . M e a s u r e C h a r a c t e r R a n g e s ( S , F, L a y o u t , SF) ; MR := R e g [ 1 ] . G e t B o u n d s ( e . G r a p h i c s ) ; // Размеры 1-й области e.Graphics.DrawRectangle( // Очерчиваем ее P e n. C r e a t e ( C o l o r. R e d ), Rectangle.Round(MR)); MR := R e g [ 2 ] . G e t B o u n d s ( e . G r a p h i c s ) ; // Размеры 2-й области e.Graphics.DrawRectangle( // Очерчиваем ее P e n . C r e a t e ( C o l o r . R e d ), Rectangle.Round(MR)); end; За счет вызова метода R e c t a n g l e . Round (MR) прямоугольник с вещественными координатами преобразуется в прямоугольник с целочисленными координатами. Свойство T r a n s f o r m содержит объект класса M a t r i x (пространство имен Sys tem. Drawing.Drawing2). Этот объект определяет матрицу 3x3 для так называе мых аффинных преобразований координат. Аффинные преобразования обеспечи вают поворот и масштабирование изображений путем нужного изменения системы координат. Для этого в состав класса включены методы T r a n s l a t e (смещение на чала координат), S c a l e (масштабирование) и R o t a t e A t (поворот на заданный угол).
Графический инструментарий
441
В следующем примере демонстрируется метод T r a n s l a t e : сначала в левом вер хнем углу экрана создается синий квадрат со стороной 100 пикселов; затем нача ло координат смещается на 100 пикселов вниз и вправо, после чего квадрат вы черчивается вновь красным цветом. В результате в окне формы оказываются два квадрата: синий в левом верхнем углу и смещенный на 100 пикселов вправо и вниз красный (рис. 19.10). Рисунок создан обработчиком события P a i n t формы, представленным в лис тинге 19.7. Листинг 19.7. Пример смещения начала координат implementation uses System.Drawing.Drawing2D; procedure TWinForm.TWinForm4_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var M: Matrix; R: Rectangle; begin // Создаем квадрат: R := Rectangle.Create(0, 0, 100, 100); // Выводим его: e.Graphics.DrawRectangle(Pen.Create(Color.Blue, 2 ) , R) ; // Создаем матрицу: M := Matrix.Create; // Смещаем начало координат: М.Translate(100, 100); // Вставляем в контекст устройства: е . G r a p h i c s . T r a n s f o r m := М; // Вновь выводим квадрат: е.Graphics.DrawRectangle(Pen.Create (Color.Red, 2 ) , R); end; Для демонстрации метода S c a l e поместите на форму два компонента T r a c k B a r и назовите их t b H o r и tbVer. В свойство O r i e n t a t i o n компонента t b V e r по местите значение V e r t i c a l и разместите компоненты на форме примерно так, как показано на рис. 19.11. В свойства Maximum, Minimum, T i c k F r e g u e n c y и V a l u e компонентов помес тите значения 100, 1, 10, 10 соответственно. Создайте для их события V a l u e Change и для события P a i n t формы такие обработчики: implementation uses System.Drawing.Drawinq2D;
Рис. 19.10. Смещение начала координат
Procedure T W i n F o r m . t b H o r _ V a l u e C h a n g e d ( s e n d e r : S y s t e m . O b j e c t ; e: System.EventArgs);
442
Глава 19 • Классы общего назначения
Графический инструментарий
443
форма с надписями, повернутыми на разные углы относительно горизонтально го направления.
Рис. 19,11. Масштабирование изображения // При изменении // форму заново begin Refresh end;
значения
свойства
Value
отрисовываем
procedure TWinForm.TWinForm4_Paint(sender: S y s t e m . O b j e c t ; e: System.Windows.Forms.PaintEventArgs); var M: M a t r i x ; begin // Создаем матрицу: M := M a t r i x . C r e a t e ; // Масштабируем с учетом положения движков ТгаскВаг: М . S c a l e ( t b H o r . V a l u e / 10, t b V e r . V a l u e / 1 0 ) ; // Вставляем матрицу в контекст устройства: е . G r a p h i c s . T r a n s f o r m := М; // Вычерчиваем прямоугольник: е . G r a p h i c s . D r a w R e c t a n g l e ( P e n . C r e a t e ( C o l o r . B l u e , 2) , 70, 70, 200, 2 0 0 ) ; end; При масштабировании в матрицу помещаются два коэффициента (dX и dY), на которые будут умножаться значения горизонтальной и вертикальной осей соот ветственно. Если коэффициент меньше 1, изображение сжимается, если больше — растягивается. Так как значения свойства Value компонента ТгаскВаг не могут быть меньше единицы, эти значения при формировании матрицы делятся на 10. Метод R a t a t e A t класса M a t r i x создает матрицу, поворачивающую оси коор динат на заданный угол относительно указанной точки. На рис. 19.12 показана
Рис. 19.12. Поворот изображения Эту форму создал обработчик, представленный в листинге 19.8. Листинг 19.8. Пример поворота изображения implementation uses System.Drawing.Drawing2D; procedure TWinForml.TWinForml__Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs) ; var S: String; GP: GraphicsPath; FF: FontFamily; Matrix; Integer; Point; begin
// // // // // //
Строка для вывода Графический путь для строки Семейство шрифтов для ГП Матрица поворота Параметр цикла Точка старта надписи продолжение &
4 4 4 Глава 19 « Классы общего назначения Листинг 19.8 (продолжение) // Строка для вывода: S := ' Delphi 8 for .NET"; // Создаем графический путь: GP := GraphicsPath.Create; // Создаем семейство шрифтов: FF := FontFamily.Create('Courier'); // Точка вывода - середина формы: Р := Point.Create(Width div 2, Height div 2 ) ; // Помещаем строку в ГП: GP.AddString(S, FF, 1, 20, P, StringFormat.GenericDefault); // Создаем матрицу поворота: M := Matrix.Create; // Цикл вывода: for к := 1 to 8 do begin // Выводим надпись с текущим поворотом осей: е.Graphics.DrawPath(Pen.Create(Color.Black, 1 ) , GP) ; // Поворачиваем оси для следующего прохода: M.RotateAt(45, Р ) ; GP.Transform(M); end end; При добавлении в графический путь строки помимо собственно строки нужно задать семейство шрифтов, стиль шрифта, его размер, точку старта строки и фор мат строки. Для задания стиля шрифта (полужирный, курсив и т. п.) можно ис пользовать перечисление F o n t S t y l e . Однако мои попытки использовать его в следующем виде не увенчались успехом: System.Drawing.FontStyle.Bold Пришлось указать его числовой эквивалент (1). Объект класса S t r i n g F o r m a t содержит всю необходимую информацию о формате надписи: центрирование по осям, расстояние между символами и строками и т. п. Обратите внимание: при отрисовке графического пути заданным пером Реп очер чиваются все находящиеся в нем объекты. Надпись очерчивается по периметру букв, поэтому буквы получаются «пустыми».
Класс Region Класс R e g i o n представляет собой область. Области — это, в общем случае, уча стки поверхности отрисовки самой разнообразной формы и конфигурации. На рис. 19.13 показана форма в виде эллипса с «дырками». Для повторения примера начните новое приложение, в свойство F o r m B o r d e r S t y l e формы поместите значение None и напишите такие два обработчика со бытий P a i n t и C l i c k формы:
Графический инструментарий
Рис. 19.13. Форма, созданная на основе области implementation uses System.Drawing.Drawing2D; procedure TWinForml.TWinForml_Click(sender: System.Object; e: System.EventArgs); // Закрывает приложение при щелчке на форме begin Close end;
procedure TWinForml.TWinForml_Paint(sender: e: System.Windows.Forms.PaintEventArgs) ; var GP: GraphicsPath; RG: System.Drawing.Region; begin
System.Object
// He путать со свойством!
GP := GraphicsPath.Create; // Создаем объект GP.AddEllipse(0, 0, 400, 200); // Заполняем его GP.AddEllipsedOO, 50, 50, 50), GP.AddEllipse(250, 50, 50, 5 0 ) ; GP.AddPiedOO, 120, 200, 50, 0, 180); // Отрисовываем объект GraphicsPath: е-Graphics.DrawPath (Pen.Create(Color.Black, 3 ) , GP) // Создаем область: RG := System.Drawing.Region.Create(GP); Self.Region := RG // Изменяем end;
форму
GraphicsPath
445
446
Глава 19 • Классы общего назначения
Графический инструментарий
447
Класс R e g i o n не имеет свойств. Его перегруженные конструкторы имеют такой вид: constructor C r e a t e ; Этот конструктор без параметров создает пустую область нулевых размеров. constructor Create(GP: G r a p h i c s P a t h ) ; Этот конструктор создает область по объекту G r a p h i c s P a t h . constructor C r e a t e ( R e c t : R e c t a n g l e ) ; constructor C r e a t e ( R e c t : R e c t a n g l e F ) ; Эти конструкторы создают область прямоугольной формы. constructor Create(RD: RegionData); Этот конструктор создает область по объекту R e g i o n D a t a . В табл. 19.11 показаны некоторые методы класса Region. Таблица 19.11. Методы класса Region Метод
Назначение
function Clone : Region; procedure Complement(GP: GraphicsPath);
Возвращает копию объекта R e g i o n Дополняет область частью объекта GP, которая не входит в область (информацию о перегруженных методах см. в справочной службе) Исключает из области объект GP (информацию о перегруженных методах см. в справочной службе) Создает новую область по дескриптору H r g n существующей Возвращает прямоугольник с вещественными координатами области: GP — объект, в котором отрисована область Возвращает объект R e g i o n D a t a , по которому можно воссоздать область Включает в область объект GP (информацию о перегруженных методах см. в справочной службе) Возвращает T r u e , если область пуста (GP — объект, в котором отрисована область) Возвращает T r u e , если точка Р принадлежит области (информацию о перегруженных методах см. в справочной службе) Очищает область Поворачивает область на угол, заданный матрицей М Смещает область на dX пикселов по горизонтали и dY — по вертикали Объединяет область с объектом GP (информацию о перегруженных методах см. в справочной службе) Объединяет область с объектом GP с использованием поразрядной операции XOR (информацию о перегруженных методах см. в справочной службе)
procedure Exclude (GP: GraphicsPath); function FromHrgn(Hrgn: I n t P t r ) : Region; function GetBounds(GR: G r a p h i c s ) : RectangleF; function GetRegionData: RegionData; procedure I n t e r s e c t ( G P : GraphicsPath); function IsEmpty(GR: Graphics) Boolean; function I s V i s i b l e ( P : Point) : Boolean; procedure MakeEmpty; procedure Transform(M: M a t r i x ) ; procedure T r a n s l a t e ( d X , dY: Integer); procedure Union (GP: GraphicsPath); procedure XOR(GP: GrahicsPath)
Рисунок 19.14 поясняет использование методов T r a n s f o r m и T r a n s l a t e .
Рис. 19.14. Поворот и смещение области
Сплошной синей (на рисунке — темной) линией очерчена исходная область, заш трихована область после поворота на 45° относительно левого верхнего угла ис ходной, наконец, залита красным цветом повернутая область (на рисунке она справа) после смещения ее на 130 пикселов по горизонтали и на 20 пикселов по вертикали. Этот рисунок создан программой, представленной в листинге 19.9. Л и с т и н г 1 9 . 9 . Пример поворота и смещения области
implementation Uses System.Drawing.Drawing2D; procedure TWinFormll.TWinFormll_Paint(sender: e: System.Windows.Forms.PaintEventArgs); var R: Rectangle; M: Matrix; Reg: System.Drawing.Region; begin
System,Object;
// Создаем исходный прямоугольник: R := Rectangle.Create (120, 50, 100, 150); // Очерчиваем его голубым цветом: е.Graphics.DrawRectangle(Pen.Create(Color.Blue) , // Создаем на его основе начальную область: Reg := System.Drawing.Region.Create(R); // Готовим матрицу преобразования: M := Matrix.Create; M.RotateAt(45, Point.Create(120, 50)); // Поворачиваем область: Reg.Transform(M);
R);
продолжение &
4 4 8 Глава 19 • Классы общего назначения Листинг 19.9 (продолжение) // Штрихуем ее: е.Graphics.FillRegion(НаtchBrush.Create ( HatchStyle.DarkVertical, Color.Black, Color.White), Reg); // Смещаем область: Reg.Translate(130, 20); // Заливаем ее красным цветом: е.Graphics.FillRegion(SolidBrush.Create(Color.Red), Reg); end;
Метод XOR добавляет к области объект, причем пересекающиеся части области и объекта объединяются одноименной поразрядной операцией. На рис. 19.15 показан пример применения этого метода.
Графический инструментарий 4 4 9 Rect := Rectangle.Create(20, 20, 100, 100); // Очерчиваем его: е.Graphics.DrawRectangle(Pen.Create(Color.Black) , Rect); // Создаем правый прямоугольнику: XRect := RectangleF.Create (90, 30, 100, 100); // Очерчиваем его: e.Graphics.DrawRectangle(Pen.Create(Color.Red) , Rectangle.Round(XRect)); // Создаем область из 1-го прямоугольника: Reg := System.Drawing.Region.Create(Rect); // Присоединяем к ней 2-й прямоугольник: Reg.SXor(XRect); // Xor - это не зарезервированное слово! // Заливаем область: е.Graphics.FillRegion(SolidBrush.Create(Color.Blue), Reg); end;
Метод XOR совпадает с зарезервированным словом Delphi. Чтобы компилятор не воспринимал его таковым, ставим перед ним символ амперсанта (&).
Рис. 19.15. Объединение областей методом XOR
Сначала создается первый прямоугольник (Rect), который обводится черной рамкой (на рисунке он слева). Затем создается второй прямоугольник (XRect), частично пересекающийся с первым. Он обводится красной рамкой. На основе прямоугольника Rect создается область, к которой методом XOR присоединя ется прямоугольник XRect. После этого область заливается синей краской. В ли стинге 19.10 показан обработчик, создавший этот рисунок. Листинг 19.10. Пример объединения областей методом XOR procedure TWinForml2.TWinForml2_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs); var Rect: Rectangle; XRect: RectangleF; Reg: System.Drawing.Region; begin // Создаем левый прямоугольник: •5 Зак. 126
Свойства компонентов
Общие свойства, методы и события компонентов
451
общений звуковые или представлять их на динамическом дисплее с азбукой Брайля и т. п. Для пользователей с ослабленным слухом, наоборот, вместо звуковых сообщений выдавать письменные, использовать визуальные эффекты (мигание текста, анимацию и т. п.).
Категория Appearance В категории Appearance собраны свойства, ответственные за появление компонен та на экране. Набор свойств этой категории существенно зависит от функцио нальности компонента, но во всех есть свойство C u r s o r , ответственное за форму указателя мыши в момент, когда он оказывается на компоненте. Если компонент работает с текстом, он имеет свойства Font и R i g h t T o L e f t . Первое определяет шрифт текста, второе — направление письма (справа налево или наоборот). Для компонентов, имеющих пояснительную надпись (форма, кнопка, надпись и т. п.), в этой категории присутствует свойство Text.
Категория Behavior
Иерархия компонентных классов в WinForms несколько отличается от иерархии в VCL. Основоположником всех компонентов — как видимых, так и не имеющих визуального представления — является класс Component (пространство имен S y s t e m . ComponentModel). Этот класс умеет взаимодействовать со средой разработчика и позволяет инспектору объектов отображать свойства и события компонентов. Все видимые компоненты — это наследники класса C o n t r o l (про странство имен S y s t e m . Windows . Forms), который, в свою очередь, является прямым наследником класса Component. В отличие от VCL, визуальные компо ненты не делятся на имеющие оконный ресурс и не имеющие его. В этой главе описываются общие свойства, методы и события визуальных компонентов.
Свойства компонентов
Свойства, представленные в категории Behavior, определяют поведение компонен та. Свойства ContextMenu и E n a b l e d указывают на связанное с компонентом контекстное меню и доступность компонента. Большинство компонентов имеют также свойства V i s i b l e , T a b S t o p и T a b l n d e x , определяющие, будет ли ком понент виден на экране и будет ли он доступен для выбора нажатием клавиши Tab, а также порядок его выбора.
Категория Data В категории Data обычно представлены два свойства: D a t a B i n d i n g s и Tag. Оба, кстати, унаследованы от класса C o n t r o l и, следовательно, имеются у каждого видимого компонента. Первое открывает доступ к коллекции класса C o n t r o l B i n d i n g s C o l l e c t i o n , которая содержит компоненты, предоставляющие дан ные для текущего компонента. На практике это свойство обычно связывается с полем набора данных. Таким образом, в отличие от VCL, все визуальные ком поненты WinForms позволяют отображать (вводить, редактировать) данные из баз данных. Второе свойство класса O b j e c t программист может использовать по своему усмотрению.
Для удобства описания свойства сгруппированы по категориям.
Категория Accessibility Группа свойств категории Accessibility регулирует свойства компонента так, чтобы максимально облегчить взаимодействием с ним пользователей с ослабленным здоровьем. Например, для людей с ослабленным зрением можно предложить крупный шрифт, контрастную цветовую гамму, выдавать вместо письменных со-
Категория Design Свойство Locked категории Design запрещает ( T r u e ) или разрешает многопо точный доступ к компоненту. В свойстве M o d i f i e r s указывается область види мости, в которой программа может изменять свойства компонента. Значение по умолчанию P r i v a t e позволяет изменять свойства только методам класса, зна чение P u b l i c — всей программе.
4 5 2 Глава 20 • Общие свойства, методы и события компонентов
Категория Layout Категория Layout объединяет свойства, влияющие на положение и размеры ком понента. Свойство A n c h o r привязывает компонент к границам своего контейнера. По умолчанию это свойство имеет значение Top, L e f t , что означает фиксацию ком понента относительно левого верхнего угла контейнера. В результате возмож ные изменения размеров контейнера никак не скажутся на изменении положе ния и/или размеров компонента. Если установить значение Bottom, R i g h t , правая и нижняя кромки компонента всегда будут располагаться на одном и том же расстоянии от соответствующих кромок контейнера. Так как положение ле вого верхнего угла компонента при этом не фиксируется, компонент будет «пла вать» внутри контейнера, всегда располагаясь на одном и том же расстоянии от его правой и нижней кромок. Однако если установлено значение Top, Bottom, L e f t , R i g h t , левый верхний угол фиксируется и компонент будет пытаться от слеживать расстояние до правого нижнего угла контейнера за счет изменения своих размеров. Свойство L o c a t i o n содержит координаты левого верхнего угла компонента в пикселах относительно границ своего компонента. Свойство S i z e задает ши рину и высоту компонента. Свойство Dock управляет положением компонента внутри своего контейнера. Его значениями могут быть R i g h t , L e f t , Top, Bottom (располагают компонент вдоль правой, левой, верхней или нижней стороны контейнера соответственно), F i l l (компонент занимает всю свободную часть клиентской области контейне ра) и None.
Свойства класса Control
453
ми. Для этого в классе C o n t r o l определено свойство P a r e n t , которое хранит ссылку на родителя. При конструировании формы размещение компонента в пре делах контейнера (формы, панели, группы и т. п.) автоматически заносит в это свойство компонента ссылку на форму (панель). При программном создании об этом должен позаботиться программист, например: procedure TWinForml.TWinForml_Load( s e n d e r : System.Object; e: System.EventArgs) ; var Btn: Button; begin Btn := B u t t o n . C r e a t e ; // Создаем кнопку B t n . P a r e n t := Self // Указываем родителя end; Если удалить последний оператор из предыдущего обработчика, кнопка не по явится на форме. Свойство C o n t r o l s : C o n t r o l C o l l e c t i o n представляет собой коллекцию до черних компонентов. Методы и свойства объекта C o n t r o l C o l l e c t i o n позволя ют гибко управлять свойством, вставляя в него новые или удаляя существующие компоненты. Вместо последнего оператора в предыдущем примере можно использовать такой: Self.Controls.Add(Btn); В табл. 20.1 перечислены некоторые другие свойства класса C o n t r o l . Таблица 20.1. Некоторые свойства класса Control
Свойства класса Control В этом разделе рассматриваются свойства класса C o n t r o l , которые не являют ся опубликованными и, следовательно, не отображаются в окне инспектора объек тов. Эти свойства наследуют все наследники класса, то есть все видимые компо ненты. Напомню, что в VCL разделяются понятия собственника компонента и его роди теля. Собственник создает компонент, родитель управляет им. Собственник от ветственен за удаление всех ресурсов порожденных им компонентов при своем разрушении. В WinForms под собственником подразумевается не компонент, создавший теку щий компонент, а, скорее, его родитель, то есть компонент, управляющий дан ным компонентом. Большинство конструкторов компонентов не имеют никаких параметров, в том числе параметра AOwner: TComponent, который обязателен для всех компонентов VCL. Удаление распределенных ресурсов происходит либо явно в деструкторе компонента, либо неявно сборщиком мусора CLR после про гона программы. Каждый видимый компонент WinForms (кроме формы) имеет родителя, кото рый управляет им, например согласует размеры дочернего компонента со свои-
Свойство
Назначение
property BackColor: Color; property BackGroundlmage: Image; property BindingContext: BindingContext;
Определяет фоновый цвет компонента Определяет фоновый рисунок Открывает доступ к объектам B i n d i n g C o n t e x t B a s e , поставляющим данные для дочерних компонентов Содержит расстояние в пикселах от нижней стороны компонента до верхней стороны контейнера (только для чтения)
property Bottom: Integer; property Bounds: Rectangle; property С an Focus : Boolean; property CanSelect: Boolean; property Capture: Boolean; property ClientRectangle: Rectangle;
Определяет границы компонента, включая его неклиентскую область Содержит T r u e , если компонент может получать фокус ввода Содержит T r u e , если компонент может выбираться нажатием клавиши Tab Определяет, будет ли компонент перехватывать сообщения мыши, даже если ее указатель находится вне границ компонента Возвращает размеры и положение клиентской области компонента продолжение •&
454
Глава 20 • Общие свойства, методы и события компонентов
Таблица 2 0 . 1 (продолжение) Свойство
Назначение
property C l i e n t S i z e : S i z e ;
Возвращает размеры клиентской области компонента Возвращает T r u e , если компонент или один из его дочерних компонентов имеет фокус ввода Возвращает клиентскую область компонента
property ContainsFocus : Boolean;
property D i s p l a y R e c t a n g l e : Rectangle; p r o p e r t y E v e n t s : E v e n t H a n d l e r L i s t ; Содержит список всех обработчиков событий компонента property Focused: Boolean; Возвращает T r u e , если компонент имеет фокус ввода property HasChildren: Boolean; Возвращает T r u e , если компонент имеет хотя бы один дочерний компонент p r o p e r t y Modif i e r R e y s : K e y s ; Показывает, нажата ли клавиша модификации (Chift, Ctrl, Alt) property MouseButtons : Возвращает состояние кнопок мыши MouseButtons; property MousePosition: P o i n t ; Возвращает координаты указателя мыши относительно верхнего левого угла экрана property Region: Region; Определяет область, которую занимает компонент property ResizeRedraw: Boolean; Возвращает T r u e , если компонент будет отрисовывать себя сам при изменении размеров p r o p e r t y ShowFocusCues : B o o l e a n ; Возвращает T r u e , если компонент выделяется пунктиром при получении фокуса p r o p e r t y T o p L e v e l C o n t r o l : C o n t r o l ; Возвращает левый верхний дочерний компонент
Все общие методы компонентов определены в их общем классе-предке Control. Некоторые из них перечислены в табл. 20.2. Таблица 2 0 . 2 . Методы класса Control
procedure AccessibilityNotifyClients (Event: A c c e s s i b i l i t y E v e n t ; ClientID: Integer); procedure B e g i n T o F r o n t ; procedure C l e a r C h i l d V i e w S t a t e ; function C o n t a i n t s (Ctr : C o n t r o l ) : Boolean; function C r e a t e A c c e s s i b i l i t y Instance: AccessibleObject; procedure C r e a t e C o n t r o l ; function C r e a t e C o n t r o l Collection: ControlCollection;
Метод
Назначение
function CreateControlsInstance: ControlCollection; function CreateGraphics: Graphics; procedure DataBind;
Создает новый экземпляр объекта ControlCollection Создает объект G r a p h i c s для компонента
procedure Dispose(Managed: Boolean); procedure E n s u r e C h i l d C o n t r o l s ;
function function
F i n d F o r m : Form; Focus : B o o l e a n ;
function GetChildAtPoint (Pt: P o i n t ) : C o n t r o l ; function GetNextControl ( C t l : C o n t r o l ; Forward: Boolean) : Control; function G e t S t y l e ( F l a g : C o n t r o l S t y l e s ) : Boolean; function GetTopLevel: Boolean; function
HasControls : Boolean;
procedure Hide; procedure I n v a l i d a t e ;
Методы компонентов
Метод
Методы компонентов
Назначение Обязывает клиента C l i e n t I D извещать о наступлении события E v e n t Перемещает компонент наверх в z-порядке Делает невидимыми все дочерние компоненты Возвращает T r u e , если компонент C t r является дочерним Создает для компонента объект класса AccessibleObject Создает компонент и все его дочерние компоненты Создает о б ъ е к т C o n t r o l C o l l e c t i o n
f u n c t i o n I s I n p u t C h a r (Ch: Char) Boolean;
f u n c t i o n I s I n p u t K e y (K: k e y s ) : Boolean;
function P o i n t T o C l i e n t (P: P o i n t ) : P o i n t ; function PointToScreeen (P: P o i n t ) : P o i n t ; function R e c t a n g l e T o C l i e n t ( R : Rectangle) : Rectangle; function RectangleToScreen(R: Rectangle): Rectangle; procedure S c a l e ( S c l : S i n g l e ) ;
455
Связывает с источником данных серверный компонент и все его дочерние компоненты Освобождает все связанные с компонентом ресурсы или только неуправляемые Проверяет текущее свойство C h i l d C o n t r o l s C r e a t e d и, если оно имеет значение F a l s e , вызывает метод CreateChildControls Возвращает форму, которая владеет компонентом Передает компоненту фокус ввода и возвращает T r u e , если фокус передан Возвращает дочерний компонент, располагающийся в указанной точке Возвращает следующий ( F o r w a r d = T r u e ) или предыдущий по отношению к компоненту C t l компонент в порядке обхода по нажатию клавиши Tab Возвращает T r u e , если в стиле компонента установлен указанный флаг Возвращает T r u e , если компонент расположен на вершине z-порядка Возвращает T r u e , если серверный компонент имеет хотя бы один дочерний компонент Прячет компонент Заставляет компонент отрисовать себя заново (информацию о перегруженных методах см. в справочной службе) Возвращает T r u e , если полученный символ не нуждается в предварительной обработке и должен направляться непосредственно компоненту Возвращает T r u e , если полученный код К является кодом обычной клавиши (а не клавиш PageUp, PageDown, Enter, Esc и Tab, а также клавиш управления курсором) Преобразует экранные координаты в координаты клиентской области компонента Преобразует координаты клиентской области компонента в экранные координаты Преобразует экранные координаты в координаты клиентской области компонента Преобразует координаты клиентской области компонента в экранные координаты Масштабирует компонент с заданным коэффициентом S c l (информацию о перегруженных методах см. в справочной службе) продолжение #
456
События компонентов
Глава 20 • Общие свойства, методы и события компонентов
Т а б л и ц а 2 0 . 2 (продолжение)
Назначение
Метод procedure ScaleCore(dx, dy: Single); procedure Select;
Масштабирует компонент с заданными коэффициентами по осям X и Y Активизирует компонент (информацию о перегруженных методах см. в справочной службе) procedure SelectNextControl Активизирует указанный компонент: C t r — (Ctr:'Control; Forward: Boolean; начальный компонент; F o r w a r d — направление TabStopOnly: Boolean; Wrap: обхода; T a b S t o p O n l y — обходить только Boolean): Boolean; компоненты с установленным свойством T a b S t o p ; N e s t e d — обходить вложенные компоненты; Wrap — переходить от первого компонента к последнему и наоборот procedure SendToBack; Делает компонент последним в z-порядке Устанавливает новые положение и размеры procedure SetBounds(X, Y, компонента Width, Height: Single); Устанавливает или сбрасывает указанный стилевой procedure SetStyle(Flag: ControlStyle; Value: Boolean) признак Показывает компонент procedure Show;
Методы G e t S t y l e и S e t S t y l e оперируют с перечислением C o n t r o l S t y l e s . Это перечисление имеет атрибут F l a g s A t t r i b u t , что позволяет рассматривать его как множество и, следовательно, объединять сразу несколько членов в одном значении. В табл. 20.3 показаны члены (флаги) перечисления-множества. Т а б л и ц а 2 0 . 3 . Флаги перечисления ControlStyles Флаг
Назначение
AllPaintinglnWmPaint
Если установлен, компонент игнорирует сообщение W M _ E R A S E B K G N D , что уменьшает мерцание при отрисовке. Игнорируется, если не установлен флаг U s e r P a i n t Если установлен, компонент сохраняет копию текста в буфере. По умолчанию флаг сброшен, что повышает производительность, но затрудняет синхронизацию текста
CacheText ContainerControl DoubleBuffer
Если установлен, компонент может иметь дочерние компоненты Если установлен, отрисовка компонента сначала происходит в буфере, и по окончании готовое изображение выводится на экран. Это уменьшает мерцание. Для двойной буферизации
необходима установка флагов AllPaintinglnWmPaint и UserPaint
EnableNotifyMessage FixedHeight FixedWidth Opaque ResizeRedraw
Если установлен и оконная процедура компонента получает сообщение, вызывается метод O n N o t i f yMessage компонента Если установлен, ширина компонента не меняется при его автоматическом масштабировании Если установлен, высота компонента не меняется при его автоматическом масштабировании Если установлен, отрисовка компонента не сопровождается отрисовкой его фона Если установлен, компонент отрисовывается заново при изменении его размеров
457
Флаг
Назначение
Selectable
Если установлен, компонент может получать фокус ввода Если установлен, щелчок на компоненте порождает событие
StandardClick
Click StandardDoubleClick
Если установлен, двойной щелчок на компоненте порождает
событие Doubleclick SupportsTransparentBackColor UserMouse
Если установлен, компонент может менять свою прозрачность
UserPaint
Если установлен, компонент отрисовывает себя сам
Если установлен, компонент сам обрабатывает события от мыши
Обратите внимание на явное несоответствие названий флагов F i x e d H e i g h t и F i x e d W i d t h и расшифровки их действий. Я просто точно воспроизвожу данные справочной службы. На практике мне не удалось заметить влияния этих флагов. Скажу лишь, что, если, например, в надписи L a b e l свойство A u t o S i z e имеет значение T r u e , оба флага у компонента оказываются уста новленными. Если в компоненте нужно установить (сбросить) сразу несколько флагов, назва ния флагов объединяются логической операцией o r : MyComponent.SetStyle(ControlStyles.UserPaint ControlStyles.AllPaintinglnWmPaint or ControlStyles.DoubleBuffer, True);
or
События компонентов Все обработчики событий компонентов оформлены в виде процедур, которые по лучают два параметра: S e n d e r : O b j e c t и е : E v e n t A r g s . Первый обычно со держит ссылку на объект, возбудивший событие, а второй — на объект, содержа щий связанные с событием параметры. Очень многие события (например, T i m e r . Tick, B u t t o n . C l i c k , все события, связанные с изменением свойств компонен та) не нуждаются в сопутствующих параметрах и просто фактом своего возник новения информируют обработчик об определенном изменении ситуации. В этом случае параметр е ссылается на объект класса E v e n t A r g s , который не содержит параметров. Другие события передают обработчику дополнительную информацию в объектах XXXXEventArgs, где ХХХХ — имя события. Такие объек ты являются экземплярами классов, унаследованных от E v e n t A r g s . Они имеют набор параметров, предоставляющих обработчику необходимую дополнительную информацию. Например, объект класса P a i n t E v e n t A r g s содержит параметры C l i p R e c t (размеры и положение области, нуждающейся в отрисовке) и G r a p h i c s (контекст графического устройства). Поскольку все визуальные компоненты произошли от родительского класса C o n t r o l , в табл. 20.4 представлены наиболее важные события этого класса.
События компонентов
458 Глава 20 • Общие свойства, методы и события компонентов Таблица 20.4. События класса Control Событие
Описание
Возникает при щелчке на компоненте Возникает при удалении дочернего компонента. Параметр е содержит ссылку на удаляемый компонент Возникает при двойном щелчке на компоненте Возникает при завершении операции перетаскивания (Drag&Drop). Параметр е содержит следующие значения: A l l o w e d E f f e c t — ссылка на объект, содержащий дозволенные операции над перемещаемым объектом; D a t a — ссылка на перемещаемый объект; E f f e c t — действие, которое разрешает обработчик; K e y S t a t e — ссылка на объект, содержащий состояние клавиатуры; X, Y — экранные координаты указателя мыши DragEnter: DragEventHandler; Возникает при входе указателя мыши с объектом в пределы границ компонента DragLeave: DragEventHandler; Возникает при выходе указателя мыши с объектом из границ компонента Возникает при перемещении указателя мыши DragOver: DragEventHandler; с объектом в границах компонента Возникает при входе указателя мыши в пределы границ Enter: EventHandler; компонента GiveFeedback: Возникает в момент начала операции перетаскивания GiveFeedbackEvent; (Drag&Drop). Параметр е содержит следующие значения: E f f e c t — действие, которое разрешено над объектом; U s e D e f a u l t C u r s o r — признак, который разрешает/запрещает использование стандартного для операции указателя мыши GotFocus: EventHandler; Возникает при получении фокуса ввода HelpRequest: Возникает при обращении пользователя к справочной HelpEventHandler; службе. Параметр е содержит следующие значения: H a n d l e d = T r u e , если требование будет удовлетворено; MousePos — экранные координаты указателя мыши Возникает при необходимости отрисовать компонент. Invalidate: Параметр е содержит значение I n v a l i d R e c t , InvalidateEventHandler; определяющее границы отрисовки Возникает при нажатии клавиши. Параметр е содержит KeyDown: KeyEventHandler; следующие значения: A l t , C o n t r o l , S h i f t = T r u e , если нажата и удерживается соответствующая клавиша; H a n d l e d = T r u e , если событие будет обработано; KeyCode — элемент перечисления Keys, указывающий название клавиши; K e y D a t a — элементы перечисления Keys, указывающие название нажатой клавиши и нажатых совместно с ней модифицирующих клавиш; K e y V a l u e — код клавиши; M o d i f i e r s — элемент перечисления Keys, указывающий название модифицирующей клавиши Возникает при нажатии алфавитно-цифровой клавиши. KeyPress: Параметр е содержит следующие значения: KeyPressEventHandler; H a n d l e d = T r u e , если событие будет обработано; KeyChar — связанный с клавишей символ с учетом выбранного языка и регистра клавиатуры
Click: EventHandler; ControlRemoved: ControlExentHandler; Doubleclick: EventHandler; DragDrop: DragEventHandler;
Событие
459
Описание
Возникает при отпускании клавиши Возникает при потере фокуса ввода Возникает при создании формы Возникает при потере фокуса ввода Возникает при нажатии кнопки мыши. Параметр е содержит следующие значения: B u t t o n — элемент перечисления M o u s e B u t t o n s , указывающий нажатую кнопку; C l i c k s — количество нажатий/ отпусканий; D e l t a — угол поворота колесика; X, Y — координаты указателя мыши Возникает, когда указатель мыши появляется MouseEnter: EventHandler; в пределах компонента Возникает, когда указатель мыши покидает MouzeLeave: E v e n t H a n d l e r ; компонент Возникает при перемещении указателя мыши на MouseMove: M o u s e E v e n t H a n d l e r ; компоненте Возникает при отпускании кнопки мыши на MouseUp: M o u s e E v e n t H a n d l e r ; компоненте MouseWheel: M o u s e E v e n t H a n d l e r ; Возникает при повороте колесика мыши Paint: PaintEventHandler; Возникает при отрисовке компонента Resize: EventHandler; Возникает при изменении размеров компонента Validate: EventHandler; Возникает при завершении отрисовки компонента V a l i d a t i n g : C a n c e l E v e n t H a n d l e r ; Возникает перед отрисовкои компонента. Параметр е содержит значение C a n c e l = T r u e , если отрисовка отменяется
KeyUp: K e a y E v e n t H a n d l e r ; Leave: E v e n t H a n d l e r ; Load: E v e n t H a n d l e r ; LostFocus: EventHandler; MouseDown: M o u s e E v e n t H a n d l e r ;
События, связанные с получением/потерей фокуса ввода, вырабатываются в та кой последовательности: 1. Enter. 2. GotFocus. 3. Leave. 4. Validating. 5. Validate. 6. LostFocus. События, связанные с мышью, возникают в такой последовательности:
1. MouseEnter. 2. MouseMove. 3. MouseHover/MouseDown/MouseWheei. 4. MouseUp. 5. MouseLeave. Клавиатурные события возникают в следующем порядке: 1. KeyDown. 2. KeyPress. 3. KeyUp.
LinkLabel — надпись с гиперссылкой
Компоненты категории Windows Forms
461
Рисунок можно поместить также в компонент I m a g e L i s t (свойство I m a g e L i s t ) и указать его индекс ( I m a g e l n d e x ) . Положение надписи и рисунка задаются свойствами T e x t A l i g n и I m a g e A l i g n соответственно. Еще одно различие — компонент способен получать фокус ввода (свойство T a b I n d e x ) , но не способен его удерживать: если в тексте компонента есть символ быстрого выбора (задается предшествующим символом &), при нажатии клавиш А1г+<символ> компонент тут же передает фокус ввода следующему компоненту в порядке обхода по нажатию клавиши Tab. Прозрачность компонента достигается установкой в свойство C o l o r значения T r a n s p a r e n t . В этом случае будут видны компоненты, расположенные на фор ме за надписью.
LinkLabel — надпись с гиперссылкой В этой главе рассматриваются визуальные компоненты, предназначенные для формирования WinForms-приложений и позволяющие создавать основные эле менты пользовательского интерфейса. Многие из них имеют такую же функцио нальность, как и соответствующие компоненты библиотеки VCL.
Label — надпись Компонент L a b e l , как него тезка из VCL, предназначен для вывода поясняю щих надписей. Он отличается тем, что помимо текста (задается свойством T e x t ) способен отображать рисунок, который задается свойством Image (рис. 21.1).
Особенностью компонента L i n k L a b e l является возможность связать с надпи сью одну или несколько гиперссылок. Для связи с одной гиперссылкой можно воспользоваться свойством L i n k A r e a , для связи с несколькими — свойством L i n k s . К сожалению, свойство L i n k s в реализации компонента для Delphi не опубликовано и, таким образом, недоступно в окне инспектора объектов. Это зат рудняет создание множества гиперссылок. Общая схема использования компонента такова. В свойство T e x t компонента помещается произвольный текст, часть которого (или весь текст) будет играть роль гиперссылки, указывающей на некоторый ресурс в Интернете. С помощью редактора свойства L i n k A r e a определяются порядковый номер первой входя щей в гиперссылку буквы и общее количество образующих ее букв. В свойство L i n k V i s i t e d помещается значение True, если предполагается автоматический доступ к ресурсу после щелчка на гиперссылке. Основная работа осуществляется в обработчике события L i n k C l i c k e d . Параметр е этого обработчика содержит объект класса Link, связанный с гиперссылкой, на которой произошел щелчок. Обработчик может выделить текст гиперссылки и выз вать метод S y s t e m . D i a g n o s t i c s . P r o c e s s . S t a r t , чтобы обратиться к ресурсу. Поясню сказанное следующим примером: 1- На пустую форму приложения WinForms поместите компонент L i n k L a b e l . 2. Поместите в свойство T e x t строку «Русскоязычный сайт для пользователей Delphi: www.DelphiKingdoni.com», а в свойство L i n k V i s i t e d — значение True. 3. Щелкните на кнопке с многоточием в строке свойства LinkArea, чтобы выз вать редактор этого свойства. 4. В окне редактора выделите мышью собственно ссылку и щелкните на кнопке ОК.
Рис. 21.1. Компонент Label
5. Напишите следующий обработчик события L i n k C l i c k e d компонента: uses System.Diagnostics;
CheckBox — флажок
4 6 2 Глава 21 • Компоненты категории Windows Forms procedure TWinForm.LinkLabell_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); begin with Sender as LinkLabel do if L i n k V i s i t e d and not LinkArea.IsEmpty then System.Diagnostics.Process.Start ( copy(Text, L i n k A r e a . S t a r t + 1, LinkArea.Length)) end; 6. Запустите программу и щелкните на ссылке — должен стартовать интернетбраузер с поисковой строкой http://www.delphikingdom.com. Некоторые пояснения. Как уже говорилось, параметр е обработчика содержит ссылку на объект класса L i n k : T O b j e c t . В языках С#, VB.NET и C++ имеется встроенная функция СТуре, позволяющая извлечь из параметра е. L i n k текст гиперссылки. В Delphi такой функции нет, поэтому приходится извлекать текст гиперссылки из свойства T e x t программно (нумерация символов в свойстве Text начинается с 0, поэтому содержимое свойства LinkArea . S t a r t необходимо уве личить на 1): var НТ: String; НТ := copy(LinkLabell.Text, LinkLabell.LinkArea.Start + 1, LinkLabell.LinkArea.Length);
Button — кнопка Класс B u t t o n предоставляет в распоряжение программиста кнопку, весьма по хожую на кнопку T B i t B t n из библиотеки VCL: она может содержать помимо надписи еще и изображение (свойство Image или I m a g e L i s t + I m a g e l n d e x ) . При щелчке на кнопке возбуждается событие C l i c k .
TextBox — текстовое поле Компонент T e x t B o x способен хранить множество текстовых строк (свойство M u l t i l i n e ) . Свойство Text используется для ввода единственной строки, а свой ство L i n e s — для ввода нескольких. Строки в этом свойстве хранятся в виде массива, что позволяет организовать индексный доступ к ним. В следующем при мере уничтожаются все строки в поле TextBox 1: var A: System.Array; К: Integer; begin A := TextBoxl.Lines;
463
TextBoxl.Readonly := False; for k := A.GetUpperBound(O) downto 0 do TextBoxl.Lines[k] := '•; end; Обратите внимание: чтобы получить верхнюю границу массива строк мы пред варительно преобразовали строки ъколлещию А (тип S y s t e m . A r r a y является коллекцией). Верхняя граница индекса по нужному измерению возвращается ме тодом GetUpperBound этой коллекции, которому в качестве параметра переда ется номер измерения. Доступ к содержимому компонента регулируется свойством R e a d o n l y . В одно строчном режиме высота компонента автоматически меняется так, чтобы пока зывать только одну строку. Если в этом случае в значение P a s s w o r d C h a r поме стить некоторый символ, поле будет работать в режиме ввода пароля, заменяя все вводимые символы символом P a s s w o r d C h a r .
Panel — панель Компонент P a n e l имеет ту же функциональность, что и компонент Т P a n e l биб лиотеки VCL, но отличается от него несколькими обстоятельствами. Во-первых, он не может содержать надпись. Его свойство F o n t определяет шрифт текста дочерних компонентов. Во-вторых, если свойство A u t o S c r o l l имеет значение True, он автоматически вставляет полосы прокрутки, когда какой-либо дочер ний компонент выходит за его границы, то есть ведет себя подобно компоненту T S c r o l l B o x библиотеки VCL. Свойство B o r d e r S t y l e компонента имеет всего три возможных значения: None, F i x e d S i n g l e и Fixed3D, — то есть компонент имеет значительно меньшие возможности выделения, чем T P a n e l .
CheckBox — флажок Компонент CheckBox во многом подобен компоненту TCheckBox библиотеки VCL, но имеет расширенные возможности. С помощью свойства Image или свойств I m a g e L i s t и I m a g e l n d e x в него можно поместить изображение. Рас положение надписи, рисунка и собственно флажка регулируется свойствами CheckAlign, I m a g e A l i g n и T e x t A l i g n . Свойство B a c k g r o u n d l m a g e опреде ляет фоновый рисунок, который, повторяясь, заполняет все пространство ком понента. Если в свойство E n a b l e d поместить F a l s e , компонент станет недоступным, а вот если то же значение поместить в свойство AutoCheck, компонент останется до ступным, но не будет реагировать на щелчок мыши изменением своих свойств Checked и C h e c k S t a t e (событие C l i c k будет возникать и в этом случае). Свой ство Checked может содержать только два значения ( T r u e или F a l s e ) , а свой ство C h e c k S t a t e — три: Checked, Unchecked и I n d e t e r m i n a t e . Последнее соответствует неопределенному состоянию флажка (установлен, но затенен). Это состояние будет возникать при щелчке мыши, если свойство T h r e e S t a t e имеет значение True.
4 6 4 Глава 21 • Компоненты категории Windows Forms Если свойство A p p e a r a n c e имеет значение B u t t o n , в компоненте исчезает изображение флажка и он будет вести себя как фиксируемая (в нажатом со стоянии) кнопка. При этом в нажатом состоянии при значении C h e c k S t a t e = I n d e t e r m i n a t e исчезает фоновый рисунок, задаваемый свойством В а с к g r o u n d l m a g e , и меняется фоновый цвет компонента.
ListBox—список
465
ListBox — список
RadioButton — переключатель
Компонент L i s t B o x представляет собой список с возможностью выбора одного или нескольких пунктов. Свойство S e l e c t M o d e может иметь одно из следую щих значений: None — выбор пунктов запрещен; One — можно выбирать только один пункт; M u l t i S i m p l e — можно выбирать несколько пунктов; M u l t i E x t e n d e d — можно выбирать несколько пунктов с учетом нажатых клавиш Shift и Ctrl: если нажата и удерживается клавиша Chift, выбирается непрерывный диа пазон пунктов; если нажата и удерживается клавиша Ctrl, выбирается произволь ный (необязательно непрерывный) диапазон пунктов.
Компонент R a d i o B u t t o n предназначен для тех же целей, что и T R a d i o B u t t o n из библиотеки VCL, — для выбора одного из взаимоисключающих решений. Его свойства и события аналогичны свойствам и событиям компонента CheckBox, исключая отсутствие свойств C h e c k S t a t e , T h r e e S t a t e и события C h a n g e CheckState.
Если в свойство MultiColumn помещено значение True, пункты списка могут располагаться в несколько колонок, при этом свойство ColumnWidth определяет ширину колонок. Если колонки выйдут за ширину компонента, автоматически вставляется горизонтальная полоса прокрутки, а если список превышает длину ком понента — вертикальная полоса.
Помимо события C l i c k компонент имеет еще ряд специфических событий, в том числе ChangeChecked и C h a n g e C h e c k S t a t e .
Следующий обработчик создал окно, показанное на рис. 21.3:
ComboBox— комбинированный список Компонент ComboBox повторяет функциональность компонента TComboBox из библиотеки VCL. Свойство DropDownStyle определяет одну из трех возмож ных модификаций компонента, показанных на рис. 21.2: S i m p l e (слева), D r o p Down (справа вверху) и DropDownList (справа внизу).
procedure TWinForra.Buttonl_Click(sender: System.Object; e: System.EventArgs); var k: I n t e g e r ; begin for k := 1 to 100 do ListBoxl.Items.Add('Пункт ' + k.Tostring); ListBoxl.MultiColumn := True; ListBoxl.ColumnWidth := 50; end;
Рис. 21.2. Модификации компонента ComboBox Свойство I t e m s открывает доступ к элементам списка, в свойствах S e l e c t e d I t e m и S e l e c t e d l n d e x возвращается выбранный элемент и его индекс. Эти свой ства доступны только для чтения, поэтому их нет в окне инспектора объектов. События Drawltem H M e a s u r e l t e m позволяют организовать программную отрисовку элементов списка.
Рис. 21.3. Компонент ListBox с несколькими колонками
4 6 6 Глава 21 • Компоненты категории Windows Forms
CheckedListBox— группа флажков Компонент C h e c k e d L i s t B o x позволяет создать группу флажков (рис. 21.4). Компонент C h e c k e d L i s t B o x имеет такие же свойства и события, как и компо нент L i s t B o x . Кроме того, у него имеются свойства C h e c k e d l t e m s и ChekedI n d e c e s , возвращающие коллекции установленных флажков. Свойство CheckO n L i s t регулирует доступ к флажкам: если оно имеет значение F a l s e , щелчки на флажках не приводят к изменению их состояния. Событие I t e m C h e c k воз никает при изменении состояния любого флажка. Обработчик этого события получает индекс флажка, его прежнее и новое состояния.
TreeView — иерархическое дерево 4 6 7 Для наполнения компонента на этапе проектирования используется редактор свойства Nodes, который создает окно, показанное на рис. 21.6. Щелчок на кнопке Add Root позволяет создать узел той же иерархии, что и роди тельский, щелчок на кнопке Add Child — дочерний узел. Для создания очередного узла нужно выделить родительский узел и щелкнуть на одной из этих кнопок. Щелчок на кнопке Delete уничтожает выделенный узел. В строке Label задается надпись узла. Если с помощью свойства I m a g e L i s t связать с компонентом хра нилище изображений I m a g e L i s t (категория Components), станут доступными списки Image и Selected Image. С помощью этих списков можно выбрать изобра жения, связанные с каждым узлом как в обычном, так и в выбранном состоянии.
Рис. 21.4. Компонент CheckedListBox
TreeView— иерархическое дерево
Рис. 21.6. Редактор компонента TreeView
Компонент TreeView предназначен для отображения ветвящихся иерархических структур, таких как дерево наследования объектов или файловая структура диска. Элементами отображаемой структуры являются узлы класса TreeNode, которые в совокупности образуют коллекцию Nodes класса T r e e N o d e C o l l e c t i o n . На рис. 21.5 показан пример компонента.
Рис. 21.5. Компонент TreeView
Рис. 21.7. Пример применения компонента TreeView
TreeView — иерархическое дерево 4 6 9
4 6 8 Глава 21 • Компоненты категории Windows Forms Для программного наполнения списка используются методы Add, AddRange, I n s e r t , Remove класса T r e e N o d e C o l l e c t i o n . В листинге 21.1 на основе ком понента TreeView создается простой браузер файлов (рис. 21.7). Листинг 21.1. Пример применения компонента TreeView uses System.10;
Labell.Text := SB; Application.DoEvents; // Создаем новое дерево:
TempNode := TreeNode.Create(SB); // Рекурсивный вызов для обработки каталога:
procedure TWinForm.TreeViewl_AfterSelect(sender: System.Object; e: System.Windows.Forms.TreeViewEventArgs); // Обработчик формирует список файлов из выбранного каталога
begin ListBoxl.Items.Clear;
{ Два следующих оператора введены для иллюстрации хода наполнения дерева. Первый выводит текущее имя в метку, второй приостанавливает работу для отрисовки метки }
ProcessDirectory(SB, TempNode); // Добавляем
очередной узел:
Node.Nodes.Add(TempNode); end end;
//Очищаем старый список и готовим новый
FileListBuilder (TreeViewl.SelectedNode.Text) end;
procedure TWinForm.Buttonl_Click(sender: System.Object; e: System.EventArgs); // Обработчик щелчка на кнопке 0К запускает процесс
procedure TWinForm.FileListBuilder(Dir: String); // Вспомогательная процедура: готовит список файов
var FileList: array of String; // Список файлов FileName: String; // Текущий файл begin // Получаем список файлов:
FileList := System.10.Directory.GetFiles(Dir); // Цикл наполнения именами компонента ListBoxl:
for FileName in FileList do ListBoxl.Items.Add(FileName) end;
var DirRoot: String; TN: TreeNode; begin
// Начальный каталог // Корневой узел
// Получаем полное имя каталога:
DirRoot :=System.IO.Path.GetFullPath (TextBoxl.Text); // Если его нет, выходим:
if not System.10.Directory.Exists(DirRoot) then Exit; TextBoxl.Text := DirRoot; TreeViewl.BeginUpdate; // Начинаем формирование //
дерева
// Рекурсивная процедура обхода дерева подкаталогов
TreeViewl.Nodes.Clear; // Удаляем старое дерево TN := TreeNode.Create; // Создаем корневой узел ProcessDirectory(DirRoot, TN); // и наполняем его TreeViewl.Nodes.Add(TN); // Добавляем к дереву TreeViewl.Nodes.Item[0].Expand; // Распахиваем узел
// каталога Dir
TreeViewl.Select;
procedure TWinForm.ProcessDirectory(Dir: String; var Node: TreeNode);
var SubDir:
array of String;
SB: String;
TempNode: TreeNode; begin
// Список подкаталогов / / Текущий подкаталог
// Формируемое дерево
// Получаем полное имя каталога:
SubDir := System.10.Directory.GetDirectories(Dir) ; // Цикл выделения имен подкаталогов:
for SB in SubDir do begin
TreeViewl.EndUpdate end;
/ / Активизируем компонент
//
Фиксируем
дерево
В основе программы лежит рекурсивный обход дерева каталогов с помощью про цедуры P r o c e s s D i r e c t o r y . Перед началом изменения дерева вызывается метод B e g i n U p d a t e , который бло кирует отрисовку изменений. После формирования дерева вызывается метод EndUpdate, который отрисовывает новое дерево. Для работы с каталогами и файлами используются соответствующие классы про странства имен S y s t e m . 10. Это имя указывается в предложении u s e s раздела i m p l e m e n t a t i o n модуля формы.
470
Глава 21 • Компоненты категории Windows Forms
ListView — список со значками
47]
ListView — список со значками Компонент L i s t V i e w отображает список, пункты которого могут снабжаться значками и флажками. Пункты списка являются объектами класса L i s t V i e w Item. Отображение пунктов списка регулирует свойство View, которое может иметь одно из перечисленных далее значений: •
D e t a i l s — каждый пункт отображается в отдельной строке с дополнитель ными данными, причем каждый пункт этих дополнительных данных отобра жается в отдельном столбце. Крайний левый столбец содержит маленький зна чок и надпись;
• L a r g e I c o n — каждый пункт отображается в виде большого значка с надпи сью под ним; в
L i s t — каждый пункт отображается в виде маленького значка с надписью справа от него. Пункты размещены по столбцам без заголовков;
•
S m a l l l c o n — каждый пункт отображается в виде маленького значка с над писью справа от него.
Для наполнения компонента в режиме проектирования щелкните на кнопке с мно готочием в строке свойства Items, чтобы вызвать редактор этого свойства (рис. 21.8).
-':::;|::Й!: BackCotor
1 щ
:
Щ
; Q Window
Checked
: | Щ WindowText
^у-:^Ш magelndex
||
ЙШ11Я1 f •
,:
j
Ш •'
'
§1
: Microsoft S a n s Seril:;;;
:::::. ForeColor
щ
ext
;l /;••: I
I (none) i (none)
H ',че
К
...
_TJ
1
I
II
стекущий ными Для Кнопка ства хранилищами добавления Su значками bAdd iпункт. tem позволяет s (рис. крупных свВ помощью пункт свойство 21.9). добавить дополнительных иРис. мелких T свойств 21.8. e x tновый помещается Редактор значков, I mпункт a gданных eсвойства l пункт n dсписка, надпись. e x следует Items исписка Sm кнопка aЕсли lвызвать lможно l m aкомпонент Remove g e lредактор n связать d e x—. удалить связан ссвой нуж •Jl
Как уже говорилось, дополнительные данные показываются только в режим< D e t a i l s , причем каждый элемент этих данных размещается в отдельном столб це. Столбцы можно снабдить заголовками. Для этого используется редактор свой ства Columns компонента (рис. 21.10).
: False
bi Font
|:|
Рис. 21.9. Редактор свойства Subitems
lb.
.•:•?!
Help
Рис. 21.10. Редактор свойства Columns
На рис. 21.11 показан пример отображения пунктов списка в режиме D e t a i l s .
В компоненте имеются свойства L a r g e l m a g e L i s t , S m a l l l m a g e L i s t и S t a t e I m a g e L i s t , предназначенные для связи с соответствующими хранилищами знач
4 7 2 Глава 21 • Компоненты категории Windows Forms
Splitter— вешка разбивки
473
ков. Экспериментально нетрудно установить, что в режиме D e t a i l s использу ются значки из хранилища S t a t e l m a g e L i s t .
Рис. 21.13. ВИД компонента при различных значениях свойства Appearance
Рис. 21.11. Пример отображения данных в режиме Details
TabControl — набор вкладок
PictureBox— изображение Компонент P i c t u r e B o x представляет собой контейнер для изображения (рис. 21.14).
Компонент T a b C o n t r o l представляет собой набор вкладок класса TabPage. Вкладки создаются с помощью редактора свойства TabPages, окно которого по казано на рис. 21.12.
Рис. 21.14. Пример использования компонента PictureBox
Свойство Image открывает диалоговое окно загрузки изображения из файла в од ном из графических форматов, поддерживаемых Windows. Свойство SizeMode может иметь значение S t r e t c h l m a g e , заставляющее изображение изменять свои размеры так, чтобы заполнить все пространство компонента.
Рис. 21.12. Редактор свойства TabPages
Свойство A p p e a r a n c e определяет вид ярлычков вкладок (рис. 21.13). В отличие от компонента T T a b C o n t r o l библиотеки VCL, ярлыки вкладок ком понента T a b C o n t r o l всегда располагаются в его верхней части.
Splitter— вешка разбивки компонент S p l i t t e r имеет такую же функциональность, как и компонент T S p l i t t e r библиотеки VCL (см. главу 13). Свойство Dock определяет ориен тацию компонента.
4 7 4 Глава 21 • Компоненты категории Windows Forms
Toolbar— панель инструментальных кнопок Для наполнения компонента T o o l b a r используется редактор его свойства B u t t o n s (рис. 21.15). В редакторе щелчок на кнопке Add позволяет поместить на панель очередную ин струментальную кнопку щелчок на кнопке Remove - удалить текущую кнопку. Свойство S t y l e в редакторе определяет тип создаваемой кнопки- P u s h B u t t o n обычная кнопка; T o g g l e B u t t o n - фиксируемая кнопка (после щелчка остается в нажатом состоянии до повторного щелчка); S e p a r a t o r - разделитель групп кно пок; DropDownButton - левая часть кнопки является обычной, а правая (со стре лочкой) вызывает связанное с кнопкой контекстное меню (рис. 21.16)
HScrollBar и VScrollBar— полосы прокрутки
475
Контекстное меню (компонент ContextMenu категории Components) размещается на форме и связывается с кнопкой типа DropDownButton с помощью свойства DropDownMenu редактора свойства B u t t o n s . При формировании контекстного меню следует определить обработчики событий, связанных с выбором того или иного пункта меню. С компонентом связаны два специфичных события, возникающие при щелчке на какой-либо кнопке: B u t t o n C l i c k и ButtonDropDown. Обработчики этих событий в параметре е получают объект-кнопку типа B u t t o n .
MonthCalendar— календарь Компонент M o n t h C a l e n d a r предназначен для выбора или отображения даты При выборе даты возникает событие DataChange, обработчик которого получает выб ранную дату в свойстве е. S t a r t . Если пользователь выбирает диапазон дат на чало диапазона содержится в параметре е . S t a r t , а конец - в параметре е. End. Компонент может отображать несколько смежных месяцев, если это позволяют его размеры.
DateTimePicker— ввод и отображение даты/времени
Рис. 21.15. Редактор свойства Buttons
В функциональном отношении компонент D a t e T i m e P i c k e r полностью повто ряет компонент T D a t e T i m e P i c k e r из библиотеки VCL (см. главу 14) Свой ство Format управляет форматом отображаемых данных. Оно может иметь одно из следующих значений: l o n g - д а т а с полным названием месяца; s h o r t - дата в формате дд.ммхг; t i m e - время; custom - формат задается свойством Customt o r m a t . Строка свойства CustomFormat формируется с помощью специфика торов формата даты/времени, приведенных в приложении А. Свойство ShowUpDown вставляет в правый угол компонента две небольшие стрел ки, а свойство ShowCheckBox вставляет в него флажок.
TrackBar — ползунок ту*
ч и ° ™ Т Н е Н Т T r a c k B a r предназначен для управления некоторой числовой велио п п Г ; К 0 Т ° Р у Ю в о з в Р а щ а е т его свойство Value. Свойства Minimum и Maximum Феделяют диапазон изменения значения Value. Свойство O r i e n t a t i o n зада^^ ориентацию ползунка - по горизонтали или по вертикали. Свойства T i c k ховых мето К И T l C k S t y l e Р е г У л и РУют частоту нанесения и расположение штри-
HScrollBar и VScrollBar— полосы прокрутки i. Иллюстрация функционирования кнопки типа DropDownButton
посТп* К ° М П 0 ™ В H S c r o l l B a r и V S c r o l l B a r определяют их функциональгь. ини, подобно компоненту TrackBar, могут регулировать или отображать
4 7 6 Глава 21 • Компоненты категории Windows Forms численную величину значения Value. Свойства Minimum и Maximum задают ди апазон изменения этой величины. Свойства L a r g e C h a n g e и S m a l l C h a n g e оп ределяют шаги изменения V a l u e при щелчке на полосе или на концевых кноп ках со стрелками.
GroupBox— панель группирования 4 7 7 InitializeComponent; // // TODO: Add any constructor code, after // InitializeComponent call
//
NumericUpDown — поле со счетчиком Компонент представляет собой комбинацию поля ввода и счетчика — сдвоенной кнопки со стрелками (рис. 21.17).
with NumericUpDownl do begin Minimum := -90; // Устанавливаем диапазон Maximum := 90; // возможных значений! Value end end; procedure TWinForm.NumericUpDownl_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs) ; // Рисует повернутую прямую begin e.Graphics.TranslateTransform(100, 100) ; e.Graphics.RotateTransform(Single(NumericUpDownl.Value)) ; e.Graphics.DrawLine(Pen.Create(Color.Red, 5), 0, 0, 0, 100); end;
Рис. 21.17. Компонент NumericUpDown В поле ввода можно вводить только числа (целые или вещественные). При щелчке на сдвоенной кнопке содержимое поля (оно представлено в свойстве V a l u e ) из меняется на величину, заданную в свойстве I n c r e m e n t . Свойства Minimum и Maximum определяют диапазон вводимых значений. Свойство H e x a d e c i m a l при установленном в нем значении True преобразует введенное число в 16-ричный формат. Свойство D e c i m a l P l a c e s определяет количество знаков после за пятой для вещественных чисел. В качестве примера использования компонента рассмотрим программу, с помо щью которой было создано окно, показанное на рис. 21.17. Эта программа вво дит целое число и интерпретирует его как угол, на который нужно повернуть рисуемую прямую (листинг 21.2). Листинг 21.2. Пример применения компонента NumericUpDown constructor TWinForm.Create; // Используем конструктор для установки // некоторых свойств компонента begin inherited Create; // // Required for Windows Form Designer support //
procedure TWinForm.NumericUpDownl_ValueChanged( sender: System.Object; e: System.EventArgs); begin Refresh end;
DomainUpDown — текстовое поле с памятью Компонент DomainUpDown внешне похож на NumericUpDown, но имеет совершен но другую функциональность. Его свойство I t e m s обслуживает коллекцию произ вольных строк, которые отображаются при щелчках на сдвоенной кнопке. Пользо ватель может ввести в поле собственную строку, которая, однако, игнорируется до тех пор, пока не попадет в коллекцию I t e m s . Строки добавляются и удаляются ме тодами Add и Remove коллекции. Свойство S o r t e d сортирует строки в лексико графическом порядке. Если свойство Wrap имеет значение True, показ строк «за цикливается» — за последней выводится первая и наоборот. Свойство S e l e c t e d l t e m содержит отображаемую строку, а свойство S e l e c t e d l n d e x - ее индекс.
GroupBox— панель группирования омпонент GroupBox служит контейнером для размещения других визуальных компонентов и представляет собой прямоугольную рамку и текст в разрыве рам ки. Обычно с его помощью группа компонентов объединяется по функциональ ному назначению.
4 7 8 Глава 21 • Компоненты категории Windows Forms
PropertyGrid — браузер свойств Компонент P r o p e r t y G r i d отображает текущие значения всех свойств связан ного с ним компонента (рис. 21.18). Связанный компонент указывается в свойстве S e l e c t e d O b j e c t . Свойства H e l p B a c k C o l o r и H e l p F o r e C o l o r определяют цвета фона и текста в поле с описа нием текущего свойства, а свойство H e l p V i s i b l e управляет его видимостью. Свойство L a r g e B u t t o n s управляет размерами трех кнопок в верхней части ком понента. Эти кнопки обеспечивают сортировку свойств. То же самое реализует и свойство P r o p e r t y S o r t компонента.
ProgressBar— индикатор процесса
479
Компонент отображает текущие значения свойств. Если какое-либо свойство свя занного объекта изменилось, новое значение будет отображено в браузере ком понента только при выборе другого свойства или при вызове метода R e f r e s h компонента P r o p e r t y G r i d .
StatusBar— строка состояния Компонент S t a t u s B a r предназначен для создания строки состояния, которая обычно располагается у нижней кромки окна. Компонент может иметь несколь ко секций (панелей) и кнопку изменения размеров окна. Секции управляются свойством P a n e l s , редактор которого показан на рис. 21.19. С помощью редактора можно управлять свойствами секции — надписью, связан ным с ней значком, стилем рамки и т. п. Свойство S i z i n g G r i p разрешает/зап рещает появление в правом нижнем углу компонента кнопки управления разме рами окна.
RichTextBox — поле формата RTF Компонент RichTextBox представляет собой многострочное редактируемое тек стовое поле, работающее с текстом в формате RTF (Rich Text Format — расширен ный текстовый формат). Текст формата RTF хранит дополнительную служебную информацию, управляющую свойствами каждого абзаца и сменой шрифта по ходу текста. Замечу, что формат RTF не использует кодировку Unicode и имеет свои сред ства сохранения информации о языке. Однако он «понимает» Unicode и спосо бен читать и записывать файлы этого формата. Рис. 21.18. Компонент PropertyGrid
Ограниченный объем книги не позволяет привести более или менее подробную информацию о многочисленных свойствах и методах этого компонента. Замечу лишь, что сведения о компоненте T R i c h E d i t , приведенные в главе 14, полностью применимы и к компоненту RichTextBox. Кроме того, в папке Demos\DelphiWin32\ VCLWin32\RichEdit папки размещения Delphi вы найдете проект RichEdit.dpr, создаю щий окно, показанное на рис. 21.20. Анализ текста модуля основного окна проекта (файл REMain.pas) даст вам исчер пывающую информацию о технике использования компонента.
ProgressBar— индикатор процесса Компонент P r o g r e s s B a r предназначен для отображения хода выполнения дли тельного по времени процесса. Он имеет много общего с компонентами X X S c r o l l B a r и T r a c k B a r , но, в отличие от них, у него нет регулятора, то есть с его помощью можно только отображать числовую величину, но пользователь не может ее изменять.
Рис. 21.19. Редактор свойства Panels
Свойства Minimum и Maximum устанавливают диапазон изменения отображае мой величины, которая хранится в свойстве Value. Свойство S t e p определяет шаг наращивания V a l u e методом P e r f o r m S t e p .
4 8 0 Глава 21 • Компоненты категории Windows Forms
Компоненты категорий Components и Dialogs •
Рис. 21.20. Пример использования компонента RichTextBox
ToolTip — управление оперативной справкой В отличие от других компонентов, предназначенных для формирования WinFormsприложений, компонент T o o l T i p не имеет визуального представления в работа ющей программе. Он содержит свойства, управляющие оперативной справкой — маленьким окошком со справочной информацией (так называемой всплывающей подсказкой), появляющимся рядом с компонентом, на который указывает мышь. Каждый видимый компонент имеет свойство T o o l T i p , содержащее строку тек ста. Эта строка появится в окне оперативной справки, только если на форму по мещен также и компонент T o o l T i p . Его свойство A c t i v e включает/отключает оперативную справку. Свойство A u t o P o p D e l a y определяет интервал, в течение которого показывается окно справки при неподвижном состоянии указателя мыши. Свойство I n i t i a l D e l a y определяет задержку перед появлением окна, свойство ReshowDelay — задерж ку при переходе указателя мыши с одного компонента на соседний. Наконец, свойство A u t o m a t i c D e l a y является базовым для остальных: при его измене нии свойство A u t o P o p D e l a y получает значение в 10 раз большее, чем значение свойства A u t o m a t i c D e l a y , свойство I n i t i a l D e l a y получает такое же значе ние, а свойство ReshowDelay — 1/5 этой величины.
В категориях Components и Dialogs собраны компоненты, не имеющие видимого представления на экране. Некоторые из них ( E r r o r P r o v i d e r и все компонен ты диалоговых окон) создают изображения при обращении к их методам. При размещении на форме они появляются не в самой форме, а в специальном окне несколько ниже нее.
MainMenu — главное меню Компонент класса MainMenu обладает редактором, позволяющим довольно про сто сконструировать главное меню формы (программы). Для создания меню по местите значок компонента на форму. У верхней кромки формы появится свет лая полоса с предложением Туре Неге (вводите здесь). Как только начнется ввод, справа и снизу от этого места появятся аналогичные надписи (рис. 22.1). Каждая команда меню представляет собой объект класса Menultem, имеющий событие C l i c k . Обработчик события должен выполнить необходимые действия, связанные с выбранной командой. ли в н а дписи команды какому-то символу предшествует символ амперсанта (&), этот символ в команде подчеркивается, а сама команда выбирается клави шами Alt+< Буква >. Например, нажатие клавиш Alt+Ф для меню, показанного на рис. 22.1, эквивалентно щелчку мыши на команде Файл. Для одной формы может быть сколько угодно главных меню, но рабочим будет только то из них, которое указано в свойстве Menu формы. Это позволяет ме нять главное меню динамически, в зависимости от контекста программы. 16
Зак. 126
Timer—таймер
4 8 2 Глава 22 • Компоненты категорий Components и Dialogs
483
с ним можно связать контекстное меню (свойство C o n t e x t M e n u компонента) и существенно расширить диапазон возможных действий.
ImageList— хранилище изображений Компонент I m a g e L i s t является хранилищем изображений. Он используется вез де, где требуется индексированный доступ к изображениям (индексация начина ется с 0). Его важнейшим свойством является Images, управляющее коллекци ей изображений. Щелчок на кнопке в окне свойства открывает окно редактора, показанное на рис. 22.3.
Рис. 22.1. Создание главного меню
ContextMenu — контекстное меню
м ill
Контекстное меню ContextMenu отличается от главного только тем, что связа но с конкретным элементом управления и появляется при щелчке на нем правой кнопкой мыши. Процесс создания контекстного меню и его связывания с про граммой ничем не отличается от создания и связывания с программой главного меню.
Notifylcon — извещающий значок Компонент N o t i f y l c o n предназначен для создания в правом углу системной области окна Windows — в так называемом систрее (SysTray) — небольшого знач ка, с которым можно связать контекстное меню. Подобного рода значки обычно связываются с фоновыми процессами, такими как фоновая печать, сканирова ние вирусов, обслуживание файловой системы и т. п. На рис. 22.2 показана сис темная область экрана Windows со значком в виде косого креста, связанным с за пущенной программой WinForm.
Рис. 22.2. Демонстрация компонента Notifylcon
Этот значок определяется свойством Icon компонента. При наведении на него указателя мыши (и в случае активности фоновой программы) появляется всплы вающая подсказка, задаваемая свойством Text (в нашем случае — Демо-программа). Чтобы значок был видимым, необходимо в свойство v i s i b l e компонента поместить значение True. Щелчок правой кнопкой на компоненте вызывает событие C l i c k и может обра батываться обычным образом (например, закрыть фоновую программу). Однако
Рис. 22.3. Редактор коллекции изображений Компонент способен хранить изображения разных форматов и размеров.
Timer —таймер Компонент T i m e r служит для отсчета временных интервалов. Его свойство I n t e r v a l определяет интервал времени в миллисекундах, который должен прой ти от включения таймера до наступления события Tick. Таймер включается при установке значения T r u e в его свойство E n a b l e d . Однажды включенный тай мер все время будет возбуждать события T i c k до тех пор, пока его свойство E n a b l e d не примет значения F a l s e . Следует учесть, что в силу специфики реализации стандартного аппаратного тай мера IBM-совместимого компьютера минимальный реально достижимый интер вал отсчета времени (этот интервал называется тиком) в операционных системах MS-DOS, Windows 3.x и Windows 95/98/ME не может быть меньше 55 мс. Более того, любой интервал времени, отсчитываемый с помощью таймера, в этих опера-
4 8 4 Глава 22 • Компоненты категорий Components и Dialogs
HelpProvider — поставщик справочной информации
485
ционных системах всегда кратен 55 мс. Замечу, что для операционных систем Windows NT Server (Workstation), Windows 2000, Windows XP длительность тика составляет 10 мс. Пример, подтверждающий сказанное, вы найдете в разделе «TTimer — таймер» главы 15.
ErrorProvider — сигнализатор ошибок Компонент E r r o r P r o v i d e r предназначен для отображающего значка в виде вос клицательного знака на красном фоне, информирующего пользователя об ошиб ке, и представляет собой альтернативу отображению диалогового окна с предуп реждающим сообщением. Значок появляется рядом с элементом управления, состояние которого вызвало ошибку. Обычно этот компонент используется для проверки корректности вводимых пользователем данных. В табл. 22.1 перечислены основные свойства компонента.
Рис. 22.4. Пример использования компонента ErrorProvider
Контроль правильности введенных данных обычно осуществляется в момент, когда управляющий компонент теряет фокус ввода и возбуждается его событие V a l i d a t e d . Обработчик события должен осуществить контроль данных и обра титься к методу S e t E r r o r компонента E r r o r P r o v i d e r . Этот метод имеет та кой прототип:
procedure TWinForml.TextBoxl_Validated( sender: System.Object; e: System.EventArgs) ; var S: String; N: Integer; begin S := TextBoxl.Text; if S = " then Exit; // Нет текста — нет проверки try N := N. Parse(TextBoxl.Text); // Число? except // Her ErrorProviderl.SetError(TextBoxl, 'He число'); Exp end; if not (N in [6..100]) then // Число не в нужном диапазоне: ErrorProviderl.SetError(TextBoxl, 'Неверный возраст') else // Все в порядке - гасим значок: ErrorProviderl.SetError (TextBoxl, ' '); end;
procedure SetError(Component: Component; var Msg: S t r i n g ) ; Здесь Component — компонент, вызвавший ошибку; Msg — сообщение об ошиб ке. Сообщение появляется в виде оперативной подсказки, когда указатель мыши задерживается на предупреждающем значке. Замечу, что, если ошибка устране на и выполняется условие Msg = ' ', предупреждающий значок гаснет.
HelpProvider— поставщик справочной информации
Таблица 22.1. Свойства класса ErrorProvider Свойство
Назначение
property B l i n k R a t e : I n t e g e r ;
Определяет частоту мигания предупреждающего значка type E r r o r B l i n k S t y l e = Определяет стиль мигания: AlwaysBlink — (AlwaysBlink, B l i n k l f постоянно мигать; B l i n k l f D i f f e r e n t E r r o r Dif f e r e n t E r r o r , NeverBlink) ; мигать до устранения ошибки; NeverBlink — property B l i n k S t y l e : никогда не мигать ErrorBlinkStyle; property Icon: Icon; Задает предупреждающий значок (по умолчанию в виде восклицательного знака на красном фоне) Стиль A l w a y s B l i n k повторяет мигание значка вплоть до устранения ошибки, вто время как стиль B l i n k l f D i f f e r e n t E r r o r прекращает мигание в момент, когда компонент с ошибкой вновь получает фокус ввода. В обоих случаях после прекращения мигания предупреждающий значок остается на форме, и его следу ет уничтожить (см. далее).
В качестве примера рассмотрим случай контроля за диапазоном значений вво димого числа (рис. 22.4). Поместите на пустую форму компоненты TextBox и E r r o r P r o v i d e r . Напи шите такой обработчик события V a l i d a t e d компонента T e x t B o x l :
Компонент H e l p P r o v i d e r организует контекстную справочную службу. Справ ка создается в двух форматах: в виде развернутого раздела некоторого справоч ного файла со всеми необходимыми гипертекстовыми ссылками и в виде строки в окне вспомогательной справки.
4 8 6 Глава 22 • Компоненты категорий Components и Dialogs Для показа раздела справочного файла нужно в свойство HelpNameSpace ком понента поместить маршрут доступа к справочному файлу и вызывать метод S e t HelpKeyWord для указания ключевого слова или раздела справки. При этом компонент, нуждающийся в справке, должен иметь фокус ввода и должна быть нажата клавиша F1. Для показа строки используется метод S e t H e l p S t r i n g . При этом компонент, нуждающийся в справке, должен иметь фокус ввода, также дол жна быть нажата клавиша F1 и выполнен щелчок правой кнопкой мыши. Модифицируем пример из предыдущего раздела так, чтобы при активном ком поненте T e x t B o x l нажатие клавиши F1 вызывало появление начального разде ла справочного файла mspaint.chm (это справка по программе Paint; обычно она находится в папке Windows\Help операционных систем Windows 98/2000/XP), а в сочетании с нажатой правой кнопкой мыши создавало оперативную подсказ ку Введите целое число в диапазоне от б до 100. Для этого поместите на форму компонент H e l p P r o v i d e r , установите в его свойство HelpNameSpace марш рут доступа к справочному файлу mspaint.chm (или к любому другому файлу с рас ширением chm или hip) и напишите такой обработчик события Load формы: procedure TWinForml.TWinForml_Load( s e n d e r : System.Object; e: System.EventArgs); begin HelpProviderl.SetHelpString(TextBoxl, 'Введите целое число в диапазоне от 6 до 1 0 0 ' ) ; HelpProviderl.SetShowHelp(TextBoxl, T r u e ) ; end; Метод H e l p P r o v i d e r l . SetShowHelp открывает доступ к справочной службе компоненту T e x t B o x l .
OpenFileDialog и SaveFileDialog — диалоговые окна открытия и сохранения файлов 4 8 7 procedure TWinForml.Buttonl_Click( sender: System.Object; e: System.EventArgs); var FS : FileStream; SR: StreamReader; Line: Variant;
окружение
Тип файлов: |в-.е докумен-ы Ло-d
j»J
Рис. 22.5. Диалоговое окно открытия файла
OpenFileDialog и SaveFileDialog — диалоговые окна открытия и сохранения файлов Компоненты O p e n F i l e D i a l o g и S a v e F i l e D i a l o g имеют приблизительно оди наковый набор свойств и методов и поэтому рассматриваются вместе. Первый из них предназначен для создания и обслуживания окна открытия (рис. 22.5), второй — окна сохранения файлов (рис. 22.6). Для вызова диалоговых окон используются методы компонентов ShowDialog, ко торые возвращают результат диалога с пользователем в виде члена перечисления D i a l o g R e s u l t . Если метод возвращает D i a l o g R e s u l t . O K , это означает, что пользователь выбрал (или ввел) правильное имя файла и щелкнул по кнопке ОК диалогового окна открытия или сохранения, то есть может выполняться соответ ствующая операция. В следующем примере компонент O p e n F i l e D i a l o g используется для выбора текстового файла и чтения его содержимого в компонент L i s t B o x : implementation uses System.10, Borland.VCL.Variants;
Рис.22.6. Диалоговое окно сохранения файла
4 8 8 Глава 22 • Компоненты категорий Components и Dialogs begin // Создаем диалоговое окно // и анализируем результат диалога: if OpenFileDialogl.ShowDialog = System.Windows.Forms.DialogResult.OK then begin // Пользователь выбрал файл - читаем его FS := FileStream.Create(OpenFileDialogl.FileName, FileMode.Open, FileAccess.Read); SR := StreamReader.Create(FS); ListBoxl.Items.Clear; // Очищаем содержимое ListBoxl repeat // Цикл чтения строк Line := SR.ReadLine; if VarType(Line) <> varEmpty then ListBoxl.Items.Add(Line) until VarType(Line) = varEmpty end; SR.Close; FS.Close end; При чтении строк используется переменная L i n e типа V a r i a n t , так как только с помощью следующей проверки можно обнаружить конец файла: until VarType(Line) = varEmpty
OpenFileDialog и SaveFileDialog — диалоговые окна открытия и сохранения файлов 4 8 9 ...
// Дальше
без изменений
end; Свойство F i l t e r задает одну или несколько масок выбора файлов, например: O p e n F i l e D i a l o g l . F i l t e r : = 'Текстовые файлы ( * . t x t ) | * . t x t | ' 'Все файлы (* . *) I * . * ' ; В строке используются символы вертикальной черты ( | ) для отделения маски от ее описания и масок друг от друга. Свойство F i l t e r l n d e x определяет ин декс маски (индексация начинается с 1).
Перекодировка русскоязычных текстов Как уже говорилось, в технологии .NET Framework используется двухбайтная кодировка Unicode. В то же время в существующих русифицированных версиях Windows используется однобайтная кодировка с применением для кириллицы кодовой страницы 1251. В связи с этим может возникнуть необходимость пере кодировки русскоязычных файлов. В этом подразделе описывается программа перекодировки. В CTS для перекодировки предусмотрен класс E n c o d i n g в пространстве имен S y s t e m . T e x t . Его метод GetEncoding(<номер_кодовой_страницы>) создает кодировщик, настроенный на нужную кодовую страницу. Кодировщику переда ется массив байтов, содержащих текст в однобайтной кодировке, а он возвраща ет двухбайтную строку S t r i n g (листинг 22.1).
Вариант получает значение varEmpty, когда метод S t r e a m R e a d e r . R e a d L i n e возвращает значение N o t h i n g , сигнализирующее о конце файла. Тип V a r i a n t не входит в CTS, поэтому мы его экспортируем из модуля B o r l a n d . V C L . V a r i a n t s (см. предложение u s e s ) .
Листинг 22.1. Программа перекодировки русскоязычных текстов implementation uses System.10, System.Text;
Замечу, что мне пришлось изрядно помучиться, прежде чем я догадался указать полное пространство имен для перечисления D i a l o g R e s u l t . Если указать пе речисление без ссылки на пространство имен, компилятор обнаружит ошибку:
procedure TWinForml. FromWinl251To(Jnicode; const
if OpenFileDialogl.ShowDialog = DialogResult.OK then
BLOCK_SIZE = 1 0 0 0 ; var
Последует сообщение Record, object or class type required («Требуются запись, объект или класс»), и будет выделен идентификатор D i a l o g R e s u l t . Оба компонента имеют функцию O p e n F i l e , которая возвращает результат в ви де потока для чтения/записи файла. При использовании этой функции наш при мер стал бы таким: var FS: Stream;
// Не FileStream!
begin if OpenFileDialogl.ShowDialog = System.Windows.Forms.DialogResult.OK then begin FS := OpenFileDialogl.OpenFile;
// Размер массива байтов
ByteValues: array [1..BLOCK_SIZE] of Byte; //Массив байтов TestFile: BinaryReader; // Объект чтения исходного файла BytesRead: Integer; // Количество прочитанных байтов StringValue: String; // Очередная преобразованная строка Strings: String; // Результат преобразования ASCIIConverter: Encoding; // Кодировщик ' Str: Stream; // Поток чтения SW: StreamWriter; // Объект для записи в файл begin if OpenFileDialogl.ShowDialog = // Выбираем файл для чтения System.Windows.Forms.DialogResult.OK then begin // Меняем форму указателя мыши:
490
Глава 22 • Компоненты категорий Components и Dialogs Cursor := Cursors.WaitCursor; // Создаем поток для чтения: Str := OpenFileDialogl.OpenFile; // Создаем объект для чтения: TestFile := BinaryReader.Create(Str) ; // Создаем кодировщик для страницы 1251: ASCIIConverter := Encoding.GetEncoding(1251) ; // Читаем первый блок: BytesRead := TestFile.Read(ByteValues, 0, BLOCK_SIZE); while BytesRead > 0 do // Цикл преобразования всего файла
FontDialog — диалоговое окно выбора шрифта
491
FontDialog — диалоговое окно выбора шрифта Компонент F o n t D i a l o g обслуживает стандартное окно выбора шрифта (рис. 22.7).
begin // Преобразуем прочитанный буфер: StringValue := ASCIIConverter.GetString( ByteValues, 0, BytesRead); // Формируем суммарный результат: Strings := Strings + StringValue; // Очищаем буфер: SArray.Clear(ByteValues, 0, BLOCK_SIZE); // Читаем очередной блок: BytesRead := TestFile.Read(ByteValues, 0, BLOCK_SIZE) end; TestFile.Close; Cursor := Cursors.Default;
Закрываем исходный файл // Восстанавливаем форму // указателя мыши if SaveFileDialogl.ShowDialog = // Запрашиваем выходной файл System.Windows.Forms.DialogResult.OK then begin // Создаем объект записи: SW := StreamWriter.Create(SaveFileDialogl.FileName); // Записываем перекодированный файл: SW.WriteLine(Strings); // Закрываем файл: SW.Close end end end; //
Рис. 22.7. Окно выбора шрифта Диалоговое окно создается стандартным образом — обращением к его методу ShowDialog: if F o n t D i a l o g l . S h o w D i a l o g = System.Windows.Forms.DialogResult.OK then В своем свойстве F o n t компонент возвращает выбранный шрифт, а в свойстве C o l o r — его цвет. Некоторые другие свойства компонента перечислены в табл. 22.2. Т а б л и ц а 2 2 . 2 . Свойства класса FontDialog Свойство
procedure TWinForml.Buttonl_Click( sender: System.Object; e: System.EventArgs); begin FromWinl251ToUnicode end;
BooT
e r t y
Воо?
е Г
Обратите внимание: зарезервированное слово A r r a y Delphi совпадает с именем класса C T S . Ч т о б ы воспользоваться методом C l e a r этого класса, необходимо отменить действие зарезервированного слова с помощью префикса & (амперсант).
n;
Назначение A l l o w S c r i
P
t c h a n
g
e :
^
A l l o w S i m u l a t i o n s :
B o o ?а п ;
e r t y
A l l o w V e c t o r F o n t s :
BooiertY —-—еап;
A l l o w V e r t i c a l F o n t s :
Если
содержит T r u e , пользователь может изменять набор символов с помощью списка Script (Набор символов) Если содержит T r u e , шрифт может моделироваться Если содержит T r u e , пользователь может выбирать векторные шрифты Если содержит T r u e , в окне отображаются как горизонтальные, так и вертикальные шрифты продолжение гЗ>
492
Глава 22 • Компоненты категорий Components и Dialogs
Таблица 2 2 . 2 (продолжение)
1. Создается объект класса S t r e a m R e a d e r , связанный с нужным файлом. Назначение
Свойство
PrintPreviewDialog — предварительный просмотр и печать файла 4 9 3
Устанавливает максимальный размер шрифта Устанавливает минимальный размер шрифта Если содержит T r u e , не показываются нестандартные шрифты Если содержит T r u e , в окне будет кнопка Apply (Применить) property ShowColor: Boolean; Если содержит T r u e , в окне будет список выбора цвета шрифта Если содержит T r u e , в окне будут флажки для выбора property ShowEffects: подчеркнутого и зачеркнутого шрифтов Boolean;
property MaxSize:Integer; property MinSize:Integer; property ScriptsOnly: Boolean; property ShowApply: Boolean;
PrintPreviewDialog — предварительный просмотр и печать файла Компонент P r i n t P r e v i e w D i a l o g создает окно, показанное на рис. 22.8.
2. Создается объект класса P r i n t D o c u m e n t , связанный с этим потоком. 3. Создается обработчик события P r i n t P a g e этого объекта. 4. Объект P r i n t D o c u m e n t указывается в свойстве Document компонента. 5. Вызывается компонентный метод ShowDialog. В ходе выполнения метода объект P r i n t D o c u m e n t возбуждает событие P r i n t Page, обработчик которого принимает печатаемые строки и наполняет ими объект G r a p h i c s , получаемый в качестве параметра вызова. Одновременно он анали зирует конец файла и возвращает в переменной На sMo re P a g e s параметра обра щения значение F a l s e , если файл исчерпан. После завершения формирования всех страниц создается и показывается окно пред варительного просмотра (см. рис. 22.8). Кнопки этого окна позволяют напечатать документ на системном принтере, выбрать масштаб изображения, количество од новременно показываемых в окне страниц и нужную страницу (по ее номеру). Для демонстрации компонента в действии поместите на пустую форму кнопку, компонент P r i n t P r e v i e w D i a l o g и компонент O p e n F i l e D i a l o g . Поскольку нам нужно будет создать новый класс T e x t F i l e P r i n t D o c u m e n t , поддерживающий работу с файлами и печать, добавьте в предложение u s e s интерфейсной секции ссылку на пространства имен S y s t e m . D r a w i n g . P r i n t i n g , S y s t e m . 10 и B o r l a n d . VCL. V a r i a n t s . Напишите такой обработчик щелчка на кнопке: procedure TWinForm.Buttonl_Click( s e n d e r : System.Object; e: System.EventArgs) ; var s t r e a m T o P r i n t : StreamReader; // Объект для чтения файла pd: TextFilePrintDocument; // Документ для печати begin // Выбираем печатаемый файл: if OpenFileDialogl.ShowDialog = System.Windows.Forms.DialogResult.OK then begin
Рис. 22.8. Окно просмотра/печати файла
С помощью этого окна можно просмотреть печатаемый файл и при необходимо сти напечатать его. Для использования компонента нужна определенная подго товительная работа, основные этапы которой перечислены далее.
// Создаем объект для чтения: StreamToPrint := StreamReader.Create( OpenFileDialogl.FileName); // Создаем печатаемый документ: pd := T e x t F i l e P r i n t D o c u m e n t . C r e a t e ( s t r e a m T o P r i n t ) ; // Указываем его диалоговому окну: PrintPreviewDialogl.Document := pd; // Наполняем документ и показываем его в окне: PrintPreviewDialogl.ShowDialog; end end;
496
Глава 22 • Компоненты категорий Components и Dialogs
После нормального завершения диалога выбранный пользователем цвет содер жит свойство C o l o r .
Форма и Win Formsпрограмма
Рис. 22.9. Диалоговое окно выбора цвета Другие свойства компонента представлены в табл. 22.3. Таблица 22.3. Свойства класса ColorDialog Свойство
p r o p e r t y AllowFullOpen: Boolean; p r o p e r t y AnyColor: Boolean;
p r o p e r t y CustomColors : a r r a y of Integer; p r o p e r t y FullOpen : Boolean;
Назначение
Если содержит True, пользователь может задействовать дополнительное окно определения цвета Если содержит T r u e , пользователь может выбирать любые цвета (не обязательно чистые, то есть относящиеся к цветам из основного набора)
Класс Form библиотеки WinForms не имеет компонентного воплощения, то есть его нет в палитре компонентов. Чтобы создать форму для главного окна, нужно выбрать команду File • New • Windows Forms Application - Delphi for .NET. Можно так же в окне New Items (рис. 23.1), которое появляется после щелчка на одноимен ной инструментальной кнопке или выбора команды File • New • Others, выбрать узел Delphi for .NET Projects и щелкнуть на значке Windows Forms Application, после чего закрыть окно щелчком на кнопке ОК.
Определяет дополнительные цвета Если содержит True, окно появляется с развернутым дополнительным окном
p r o p e r t y ShowHelp: Boolean;
Если содержит True, в окне появляется кнопка Help
p r o p e r t y SolidCilorOnly: Boolean; Если содержит True, в окне отображаются только чистые (относящиеся к основному набору) цвета
Рис. 23.1. Окно New Items 1 7
Зак. 126
498
Глава 23 • Форма и WinForms-программа
Класс Form
Для добавления к проекту новой (не главной) формы нужно выбрать узел New Files в окне New Items и значок Windows Form. Первоначально никакой разницы между модулями главного окна к любого допол нительного нет. Определение главного окна важно для правильного создания файла проекта, который с помощью атрибута [STAThreade] выделяет программе от дельный поток команд и запускает в этом потоке модуль главного окна. В отличие от программ библиотеки VCL, файл проекта программы создает всегда только одно окно — главное. Все остальные окна должны создаваться динамически. Важным отличием от VCL является то обстоятельство, что главное (или вспомо гательное) окно WinForms не объявляет переменной типа формы. Например, если в VCL-приложении созданы главная форма TForml и вспомогательная форма TForm2, то в каждом из модулей автоматически объявляются переменные Forml и Form2 соответствующего типа. В WinForms это не так. В результате, если из модуля WinForml нужно запустить окно TWinForm2, нужно сначала объявить переменную: var
WForm2: TWinForm2; procedure TWinForml.Buttonl_Click( sender: System.Object; e: System.EventArgs); begin WForm2:= TWinForm2.Create; WForm2.ShowDialog end; Класс A p p l i c a t i o n тоже не представлен в палитре компонентов. Объект этого класса автоматически создается для каждой программы. В этой главе рассматриваются свойства, методы и события классов Form и App lication.
Класс Form Конструктор формы F o r m . C r e a t e не имеет параметров. По умолчанию он со здает форму размером 300x300 пикселов. Текст конструктора расположен в на чале раздела i m p l e m e n t a t i o n модуля формы и выглядит так: constructor TWinForm21.Create; begin inherited Create; // //
Required
for
Windows
Form
// InitializeComponent; // // TODO: Add any constructor // InitializeComponent call
// end;
Конструктор делится на две части: операторы первой части располагаются пе ред оператором вызова метода I n i t i a l i z e C o m p o n e n t , операторы второй ча сти — после. Метод I n i t i a l i z e C o m p o n e n t запускает дизайнер форм, кото рый инициализирует все размещенные на форме компоненты — вызывает их конструкторы и устанавливает в свойства значения, определенные на этапе кон струирования. Предварительно вызывается унаследованный конструктор фор мы, чтобы создать необходимый контейнер для компонентов. Непосредственно перед вызовом I n i t i a l i z e C o m p o n e n t программист может разместить любой код, который может повлиять на работу дизайнера форм. Например, он может проверить легальность программной копии и отменить вызов редактора форм, если копия нелегальная. В этом случае на экране появится пустая форма без компонентов и даже без заголовка. После завершения работы редактора форм (после вызова I n i t i a l i z e C o m p o n e n t ) программист также может вставить код. В этом месте удобно создавать динамические экземпляры классов. В самом начале раздела Implementation размещается реализация метода D i s pose. Этот метод освобождает все выделенные программе ресурсы, кроме памяти. Если обратиться к нему с параметром True, будут разрушены все компоненты, а затем и сама форма. Если эта форма — главная и нет активных вспомогательных окон, произойдет завершение работы программы. Но если программа помимо глав ного создала и активизировала одно или несколько других окон или обратилась к методу D i s p o s e с параметром F a l s e , программа продолжит работу и закрыть ее можно будет только с помощью диспетчера задач.
Свойства формы В табл. 23.1 представлены некоторые свойства класса Form. Таблица 23.1. Свойства класса Form Свойство
Назначение
property A c c e p t B u t t o n : IButtonControl;
Наделяет связанную со свойством кнопку способностью перехватывать нажатие клавиши Enter Возвращает ссылку на активное окно Возвращает ссылку на активное дочернее MDI-OKHO Если содержит T r u e , окно будет автоматически изменять свои размеры и размеры своих управляющих компонентов на основе размера текущего шрифта Если содержит T r u e , окно будет автоматически вставлять полосы прокрутки, чтобы получить доступ к компонентам, не умещающимся в пределах текущих размеров формы Содержит ссылку на кнопку по умолчанию, закрывающую окно с результатом C a n c e l при нажатии клавиши Esc Если содержит F a l s e , окно не будет содержать системные кнопки в своем заголовке
p r o p e r t y A c t i v e F o r m : Form; p r o p e r t y ActiveMDIForm: Form; property A u t o S c a l e : Boolean;
Property A u t o S c r o l l : Boolean; Designer
support Property C a n c e l B u t t o n : J-ButtonControl;
code
after
499
Property ControlBox: Boolean;
продолжение &
500
Глава 23 • Форма и WinForms-программа
Таблица 23.1
(продолжение)
Свойство
Свойство Назначение
Содержит положение и размеры окна в координатах экрана Содержит положение левого верхнего угла окна в координатах экрана Указывает результат, который модальное диалоговое окно вернет вызывающей программе. Все члены перечисления обычно соответствуют щелчку на одноименной кнопке. Значение None означает, что диалог еще продолжается type FormBorderStyle = (Fixed3d, Определяет стиль рамки окна: F i x e d 3 d — фиксированных размеров трехмерная рамка; FixedDialog, FixedSingle, FixedToolWindow, None, Sizable, F i x e d D i a l o g — фиксированных размеров рамка для диалоговых окон; F i x e d S i n g l e — SizableToolWindow);property фиксированных размеров рамка толщиной FormBorderStyle: в одну линию; F i x e d T o o l W i n d o w — FormBorderStyle; фиксированных размеров рамка для инструментального окна; None — окно без рамки; S i z a b l e — обычная трехмерная рамка; S i z a b l e T o o l W i n d o w — рамка для инструментального окна с изменяемыми размерами
property DesktopBounds: Rectangle; property DesktopLocation: Point; type DialogResult = (Abort, Cancel, Ignore, No, None, OK, Retry, Yes); property DialogResult: DialogResult;
property HelpButton: Boolean;
property Icon: Icon; propertyIsMDIChild:
Boolean;
property IsMDIContainer: Boolean; property Menu: MainMenu; property MergedMenu: MainMenu; property KeyPreview: Boolean; property MaximizeBox: Boolean; property MaximizedBounds: Rectangle; property MaximumSize: Size; property MDIChildren: array of Form; property MDIParent: Form; property MinimizeBox: Boolean; property MinimizedBounds : Rectangle; property MinimumSize: S i z e ; property Modal: Boolean;
Класс Form
Если имеет значение T r u e , в заголовок окна вставляется кнопка со значком в виде знака вопроса; игнорируется, если в заголовке окна имеются кнопки свертывания и развертывания Определяет значок, который появляется в левом верхнем углу заголовка Возвращает T r u e , если окно является дочерним для MDI-интерфейса Если имеет значение T r u e , окно является рамочным для MDI-интерфейса Содержит указатель на главное меню формы Содержит объединенное меню рамочного и дочернего MDI-OKOH
Если имеет значение T r u e , окно получает события от клавиатуры раньше компонента с фокусом ввода Если имеет значение T r u e , в заголовок окна вставляется кнопка развертывания Определяет размеры и положение развернутого окна Определяет размеры развернутого окна Открывает индексированный доступ к дочерним MDI-окнам Содержит указатель на рамочное MDI-OKHO Если имеет значение T r u e , в заголовок окна вставляется кнопка свертывания Определяет размеры и положение свернутого окна Определяет размеры свернутого окна Содержит T r u e , если окно показывается в модальном режиме
property property of Form; property property Boolean; property
501
Назначение Opacity: Double; OwnedForms : a r r a y Owner: F o r m ; ShowInTaskbar : TopMost: B o o l e a n ;
Определяет степень прозрачности окна Содержит коллекцию всех форм, для которых данная форма является родительской Содержит ссылку на родительскую форму Разрешает/запрещает показывать ссылку на форму в строке задач Windows Если содержит T r u e , окно формы располагается поверх всех других окон
property TransparencyKey: Color; Определяет цвет прозрачности (см. далее) Свойство A c c e p t B u t t o n обычно используется в диалоговых окнах для прида ния кнопке свойства действующей по умолчанию, то есть реагирующей на нажа тие клавиши Enter. Замечу, что реакция на нажатие клавиши Enter реализуется в обработчике события C l i c k кнопки, действующей по умолчанию. В отличие от этого свойство C a n c e l B u t t o n указывает на кнопку, которая при нажатии клавиши Esc закрывает диалоговое окно с результатом D i a l o g R e s u l t . C a n c e l . В следующем примере демонстрируются оба свойства. На пустую форму помес тите компонент TextBox и кнопку B u t t o n . В конструкторе формы сделайте кноп ку срабатывающей по умолчанию: constructor TWinForm.Create; begin inherited C r e a t e ; // // Required for Windows Form Designer support
// InitializeComponent; AcceptButton := Buttonl; // Делаем кнопку срабатывающей по умолча нию
// // TODO: Add any constructor code // InitializeComponent call // end;
after
Напишите такой обработчик события Click кнопки: procedure TWinForm.Buttonl_Click( sender: System.Object; e: System.EventArgs); var FmDlg: Form; Btn: Button;
TextBoxl: begin FmDlg
// Диалоговое окно // Кнопка Cancel
TextBox;
:= Form.Create;
with FMDlg do begin
// Создаем диалоговое окно
5 0 2 Глава 23 • Форма и WinForms-программа Text := 'Демонстрация CancelButton'; FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; // Создаем кнопку: Btn := Button.Create; 1 Btn.Text := 'Cancel ; // Располагаем кнопку в центре окна: Btn.Left := (Width - Btn.Width) div 2; Btn.Top := (Height - Btn.Height) div 2; // Создаем поле ввода: TextBoxl := TextBox.Create; // Располагаем его над кнопкой: TextBoxl.Top := Btn.Top - 2 * TextBoxl.Height; TextBoxl.Left := (Width - TextBoxl.Width) div 2; // Сначала помещаем на форму поле: Controls.Add(TextBoxl); // Затем - кнопку: Controls.Add(Btn); CancelButton := Btn; // Делаем кнопку действующей по умолчанию ShowDialog // Показываем модальное диалоговое окно end end;
Заметьте: на пустую форму вначале помещено поле TextBox, свойство Tablndex которого получает значение 0. Таким образом, это поле в момент старта програм мы имеет фокус ввода. Тем не менее нажатие клавиши Enter активизирует кноп ку Buttonl, так как она объявлена в свойстве AcceptButton формы. В резуль тате срабатывает обработчик кнопки, который создает и показывает диалоговое окно с кнопкой Cancel. В этом окне также есть поле ввода, которое получает фо кус в момент появления окна. Это не мешает кнопке Cancel закрыть диалоговое окно при нажатии клавиши Esc.
Класс Form begin inherited Create; // // Required for Windows Form Designer support // Устанавливаем размеры и положение развернутого окна: Self.MaximizedBounds := Rectangle.Create(0, 0, 300, 400); InitializeComponent; // // TODO: Add any constructor code after // InitializeComponent call // end;
Свойства MaximizeSize HMinimizeSize определяют размеры развернутого и свернутого окон. Если они имеют размер 0x0, эти размеры устанавливает Windows. Эти свойства игнорируются, если заданы свойства MaximizeBounds и MinimizeBounds. Свойство Opacity определяет степень прозрачности окна. Значение 1 соответ ствует непрозрачному окну, значение 0 — полностью прозрачному. Это свойство работает только в Windows 2000/XP и при условии, что в видеокарте компьюте ра на каждый пиксел приходится по два и более байта видеопамяти (режимы High Color, True Color). Любопытные эффекты дает использование свойства TransparencyKey (рис. 23.2).
Если в свойство DialogResult диалогового окна поместить значение перечис ления DialogResult, окно будет закрыто с указанным результатом. Например, следующий обработчик закроет диалоговое окно и вернет в вызывающую про грамму значение Abort: procedure TWinForm.Buttonl_Click( sender: : System.Object; e: System.EventArgs); begin Self.DialogResult := System.Windows.Forms.DialogResult.Abort end;
Свойства MaximizeBounds и MinimizeBounds определяют размеры и положе ние развернутого и свернутого окон. Эти свойства недоступны в окне инспекто ра объектов, и их нужно устанавливать динамически. Наиболее подходящим ме стом для этого является конструктор окна, например: constructor TWinForml.Create;
503
Рис. 23.2. Демонстрация свойства TransparencyKey
504
Глава 23 • Форма и WinForms-программа
Это свойство задает цвет прозрачности: любые участки формы или размещенных на ней компонентов, окрашенные этим цветом, будут прозрачными. Для воспро изведения примера поместите на пустую форму три панели и каждую из них ок расьте своим цветом (свойство BackColor). Создайте для одной из них обработ чик события C l i c k и затем назначьте этот обработчик двум другим панелям: procedure TWinForm.Panell_Click( s e n d e r : System.Object; e: System.EventArgs); begin Self.TransparencyKey := (Sender as Panel).BackColor end; Запустите программу и щелкните на любой панели — она тут же станет прозрач ной (на рис. 23.2 прозрачной является верхняя панель).
Класс Form
логовые окна поиска и замены. Собственника формы можно удалить или заме нить также с помощью свойства Owner. Метод ShowDialog показывает модальное (диалоговое) окно. Это окно блоки рует взаимодействие пользователя с другими окнами до тех пор, пока тот не за кроет его. Чтобы показать/спрятать обычное (немодальное) окно, используются унаследованные от C o n t r o l методы Show и Hide.
События формы В табл. 23.3 перечислены события формы (кроме унаследованных от класса Control). Таблица 2 3 . 3 . События класса Form Событие
Методы формы В табл. 23.2 представлены наиболее важные методы класса Form, не унаследо ванные от C o n t r o l . Таблица 2 3 . 2 . Методы класса Form Метод
Назначение
procedure A c t i v a t e ; procedure AddOwnerForm (OwnedForm: Form); function GetAutoScaleSize (aFont: Font) : S i z e ;
Активизирует форму и передает ей фокус ввода Устанавливает владельца текущей формы Возвращает размеры формы для заданного шрифта в режиме автоматического масштабирования (при установленном значении T r u e в свойстве
AutoScaleBaseSize) type MDILayout= (Arrangelcons, TileHorizontal, TileVertical); Cascade, procedure LayoutMDI (Value: MDILayout) ;
procedure RemoveOwnedForm ( OwnedForm: Form); procedure SetDesktopBounds (X, Y, Width, H e i g h t : I n t e g e r ) ; procedure SetDesktopLocation (X, Y: I n t e g e r ) ; function ShowDialog: DialogResult;
Устанавливает положение дочерних окон внутри рамочного MDI-окна: A r r a n g e l c o n s — упорядочивает значки свернутых дочерних окон; Cascade — располагает дочерние окна каскадом; T i l e H o r i z o n t a l — упорядочивает положение и размеры дочерних окон по горизонтали; T i l e V e r t i c a l — упорядочивает положение и размеры дочерних окон по вертикали Удаляет указанную форму из списка владельцев текущей формы Устанавливает положение формы на экране и ее размеры Устанавливает положение формы на экране Показывает окно в режиме диалога (информацию о перегруженных методах см. в справочной службе)
Метод AddOwnerForm устанавливает владельца текущей формы до тех пор, пока не будет вызван метод RemoveOwnerForm. Форма, имеющая владельца, свора чивается и закрывается, когда сворачивается или закрывается ее владелец. Она появляется всегда на поверхности своего владельца. Примером могут быть диа-
505
Описание
Возникает при активизации окна Возникает при закрытии окна Возникает перед закрытием окна. Параметр е содержит логическую переменную C a n c e l , с помощью которой программа может отменить действие Deactivated: EventHandler; Возникает, когда форма теряет фокус ввода Возникает при изменении языка ввода. Параметр е может InputLanguageChanged: быть равен одному из следующих значений: C h a r S e t — InputLanguageChangedзначение типа B y t e , определяющее набор символов; EventHandler; C u l t u r e — объект класса C u l t u r e l n f о , содержащий параметры локализации; I n p u t L a n g u a g e — объект класса I n p u t L a n g u a g e , определяющий новый язык ввода
A c t i v a t e d : EventHandler; Closed: EventHandler; Closing: CancelEventArgs;
InputLanguageChanging: InputLanguageChangingEventHandler;
Load: EventHandler; MDIChildActivate: EventHandler; MenuComplete: EventHandler; MenuStart: EventHandler;
Возникает при попытке пользователя изменить язык ввода. Параметр е может быть равен одному из следующих значений: C a n c e l — логическая переменная, с помощью которой обработчик может отменить действие; S y s C h a r S e t — значение типа B o o l e a n , указывающее, будет ли системный шрифт, используемый по умолчанию поддерживать требуемый набор символов; C u l t u r e — объект класса C u l t u r e l n f о, содержащий параметры локализации; I n p u t L a n g u a g e — объект класса I n p u t L a n g u a g e , определяющий новый язык ввода Возникает перед появлением формы на экране Возникает при активизации дочернего MDI-окна Возникает, когда меню окна теряет фокус ввода Возникает, когда меню окна получает фокус ввода
Интерфейс MDI Некоторые свойства, методы и события класса Form направлены на поддержку многодокументного интерфейса (MDI). В этом интерфейсе создается главное (или рамочное) окно, в клиентской области которого размещаются несколько дочер них окон. Примером программы, использующей этот интерфейс, может служить Microsoft Excel (рис. 23.3).
506
Глава 23 • Форма и WinForms-программа
Класс Application
507
Свойства Application В табл. 23.4 указаны наиболее важные свойства класса A p p l i c a t i o n . Таблица 23.4. Свойства класса Application Свойство
Описание
property A l l o w Q u i t : Boolean;
Возвращает значение, указывающее, может ли обращающийся к этому свойству фрагмент программы закрыть ее
p r o p e r t y CommonAppDataPath: String; property CommonAppDataRegistery: S t r i n g ; property C u r r e n t C u l t u r e : Culturelnfo; property CurrentlnputLanguage: InputLanguage;
Возвращает маршрут доступа к данным, разделяемым с другими пользователями Возвращает ключ реестра для приложения, которое разделяет данные с другими пользователями Содержит ссылку на объект с параметрами локализации Возвращает ссылку на объект, определяющий текущий язык ввода
property ExecutablePath: String; p r o p e r t y MessageLoop: B o o l e a n ;
Возвращает маршрут доступа и имя файла приложения Указывает, будут ли сообщения Windows обрабатываться в текущем потоке команд Возвращает маршрут доступа к исполняемому файлу приложения (без имени файла) Возвращает маршрут доступа к данным приложения
property StartupPath: String; Рис. 23.3. Окно программы, использующей интерфейс MDI При разработке MDI-приложения нужно сначала создать рамочное окно, а за тем — клиентские. Для создания рамочного окна в свойство I s M D I C o n t a i n e r главной формы по местите значение T r u e . Дочерние окна создаются динамически, так как для это го нужно использовать свойство формы M d i P a r e n t , которое недоступно на эта пе конструирования. Далее показан пример обработчика выбора в меню рамочного окна команды Файл • Новое окно: procedure TWinForm.MenuItem2_Click( sender: System.Object; e: System.EventArgs); // Создает новое дочернее окно var Client: Form; begin Client := Form.Create; Client.MdiParent := Self; Client.Show end;
Класс Application Объекты класса A p p l i c a t i o n автоматически создаются для каждого WinFormsприложения. Он инкапсулирует общие для всех приложений свойства, методы и события.
property UserAppDataPath: String; property UserAppDataRegistery: String;
Возвращает ключ реестра для приложения
Методы Application В табл. 23.5 перечислены методы класса A p p l i c a t i o n . Таблица 23.5. Методы класса Application Метод
Описание
procedure A d d M r s s a g e F i l t e r (Filter: IMessageFilter); procedure DoEvents;
Добавляет фильтр для отсева сообщений
procedure E n a b l e V i s u a l S t y l e s ; procedure E x i t ;
procedure E x i t T n r e a d ; procedure RemoveMessageFilter f i l t e r : IMessageFilter); oc e d u r e Run;
Обрабатывает сообщения из очереди сообщений Windows Отрисовывает компоненты приложения в стиле Windows XP Информирует Windows о завершении работы приложения и закрывает программу после обработки всех сообщений из очереди Завершает работу текущего потока и закрывает все его окна Удаляет фильтр для отсева сообщений Запускает петлю обработки сообщений в текущем потоке (информацию о перегруженных методах см. в справочной службе)
5 0 8 Глава 23 • Форма и WinForms-программа Метод DoEvents — аналог метода P r o c e s s M e s s a g e s класса T A p p l i c a t i o n библиотеки VCL. Он приостанавливает текущую работу до тех пор, пока не бу дут обработаны все сообщения из очереди сообщений. Например, при измене нии текста надписи в очередь будет помещено соответствующее сообщение, но отрисовка надписи будет отложена до завершения текущей работы. Вызов мето да позволяет прервать текущую работу и отрисовать надпись заново.
События Application В табл. 23.6 представлены события класса A p p l i c a t i o n . Таблица 23.6. События класса Application Событие
Описание
ApplicationExit: EnventHandler; I d l e : TnentArgs;
Возбуждается методом E x i t
ThreadExit: EventHavler;
Возникает при исчерпании очереди сообщений и наступлении паузы в работе приложения Возбуждается методом T h r e a d E x i t
Приложения
Подпрограммы модуля Match
Некоторые стандартные подпрограммы, переменные, классы
511
Подпрограмма
Описание
function Sec (const X: Extended) : Extended; function Sin (X: Extended) : Extended; procedure SinCos (Theta: Extended; var Sin, Cos: Extended);
Секанс (l/cos(^)) Синус Возвращает одновременно синус и косинус угла T h e t a (почти в 2 раза быстрее, чем раздельное получение синуса и косинуса) Тангенс
function Tan(X: Extended) : Extended; функции преобразования углов function CycleToRad(Cycles: Extended) : Extended; functionDegToRad(Degrees: Extended): Extended; function GradToRad(Grads: Extended) : Extended; function RadToDeg (Radians : Extended) : Extended; function RadToGrad(Radians: Extended) : Extended; function RadToCycle(Radians : Extended) : Extended;
R a d i a n s := C y c l e s x 2я R a d i a n s := D e g r e e s x л/180 R a d i a n s := G r a d s x я/200 D e g r e e s := R a d i a n s x 180/я G r a d s := R a d i a n s x 200/тс C y c l e s := Radians/2n
Гиперболические функции
В этом приложении приводятся некоторые стандартные подпрограммы, систем ные переменные и классы, определенные в различных программных модулях.
Подпрограммы модуля Match Подпрограмма
function Cos (X: Extended) : Extended; function Cosecant (const X: Extended) : Extended; function Cotan (X: Extended) : Extended; function Hypot (X, Y: Extended) : Extended
Арккосинус Арккотангес Арксинус Вычисляет арктангенс Y/X и возвращает угол в правильном квадранте (функция А г с Т а п модуля S y s t e m не учитывает квадрант) Косинус Косеканс (l/sin(A')) Котангенс Корень квадратный из (X2 + У2) — гипотенуза прямоугольного треугольника по двум катетам
Гиперболический Гиперболический Гиперболический Гиперболический Гиперболический Гиперболический
арккосинус арксинус арктангенс косинус синус тангенс
Логарифмические функции function LnXPl (X: Extended) : Extended;
Описание
Тригонометрические функции function ArcCos (X: Extended) : Extended; function ArcCot (const X: Extended) : Extended; function ArcSin (X: Extended) : Extended; function ArcTan2 (Y, X: Extended) : Extended;
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; functionTanh (X: Extended) : Extended;
function LoglO (X: Extended) : Extended; function Log2 (X: Extended) : Extended; function LogN (Base, X: Extended) : Extended;
Логарифм натуральный от (X + 1). Используется, когда значение X близко к нулю Десятичный логарифм Двоичный логарифм Логарифм от X при основании Base
Экспоненциальные функции function IntPower(Base: Extended; Возведение Base в целочисленную Exponent: Integer): Extended; степень E x p o n e n t function Power (Base, Exponent: Extended) : Возведение Base в вещественную Extended; степень E x p o n e n t Подпрограммы разного назначения f u n c t i o n C e i l (X: E x t e n d e d ) : I n t e g e r ; procedure DivMod ( D i v i d e n d : I n t e g e r ; " i v x s o r : Word; v a r R e s u l t , f i n d e r : Word) ;
Ближайшее меньшее целое Целочисленное деление: R e s u l t div; R e m a i n d e r — mod продолжение •&
5 1 2 Приложение А • Некоторые стандартные подпрограммы, переменные, классы Подпрограмма
Описание
function EnsureRange (const AValue, AMin, AMax: Integer) : Integer;
Возвращает результат внутри диапазона AMin...AMax: если A V a l u e в диапазоне, возвращает AValue, иначе AMin или АМах. Вместо типа I n t e g e r могут использоваться типы I n t 6 4 или D o u b l e function Floor (X: Extended) : Integer; Ближайшее большее целое procedure Frexp (X: Extended; var Возвращает мантиссу и степень Mantissa: Extended; var Exponent: вещественного числа Integer); function InRange (const AValue, Возвращает T r u e , если A V a l u e AMin, AMax: Integer): Boolean; в диапазоне AMin...AMax. Вместо типа I n t e g e r могут использоваться типы I n t 6 4 или D o u b l e Возвращает х • Р2 function Ldexp (X: Extended; P: Integer): Extended; function Poly (X: Extended; const Значение полинома А • N + В • N'1 + ... + Z. Коэффициенты задаются в порядке Coefficients: array of Double) : возрастания степени Extended; Статистические подпрограммы Возвращает максимальное из двух чисел. Вместо типа I n t e g e r могут использоваться типы I n t 6 4 , S i n g l e , D o u b l e , Extended Возвращает максимальное из набора целых 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; Возвращает минимальное из двух чисел. Вместо типа I n t e g e r могут использоваться overload; типы Int64, Single, Double, Extended Возвращает минимальное из набора целых function MinlntValue (const Data: чисел array of Integer) : Integer; Возвращает минимальное из набора function MinValue (const Data : array вещественных чисел of Double) : Double; Вычисляет статистические моменты procedure MomentSkewKurtosis (const Data: array of Double; var Ml, M2, M3, порядков с первого по четвертый, а также асимметрию Skew и эксцесс K u r t o s i s M4, Skew, Kurtosis: Extended); для набора чисел Возвращает норму (квадратный корень function Norm (const Data: array of из суммы квадратов) вещественных чисел Double): Extended; function PopnStdDev (const Data : array Выборочное стандартное отклонение. Отличается от обычного стандартного of Double) : Extended; отклонения тем, что использует выборочное значение дисперсии (см. далее функцию PopnVariance) function Max (А, В: Integer) : Integer; overload;
Подпрограммы модуля Match
513
Подпрограмма
Описание
function PopnVariance (const Data: array of Double) : Extended;
Выборочная дисперсия. Использует «смещенную» формулу T o t a l V a r i a n c e / / V (см. далее функцию T o t a l V a r i a n c e ) Генерирует псевдонормально распределенную последовательность чисел с заданным средним значением Mean и стандартным отклонением StdDev Вычисляет среднеквадратическое отклонение для набора чисел Вычисляет сумму чисел
function RandG (Mean, StdDev: Extended): Extended; function StdDev (const Data : array of Double) : Extended; function Sum (const Data: array of Double) : Extended register; procedure SumsAndSquares (const Data: array of Double; function Sumlnt (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;
Одновременное вычисление суммы и суммы квадратов для набора чисел Сумма набора целых чисел Сумма квадратов чисел Сумма квадратов расстояний всех величин от их среднего арифметического Выборочная дисперсия для набора чисел. Использует «несмещенную» формулу T o t a l V a r i a n c e / ( / V - 1)
Финансовые функции type TPaymentTime = (ptEndOfPeriod, ptStartOfPeriod); function DoubleDecliningBalance (Cost, Salvage: Extended; Life, Period: Integer) :Extended; function FutureValue(Rate: Extended; NPeriods:Integer; Payment, PresentValue: Extended; PaymentTime: TPaymentTime): Extended; function InterestPayment(Rate : Extended; Period, NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; function InterestRate(NPeriods : Integer; Payment, PresentValue, FutureValue: 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 NumberOf Periods (Rate, Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime) : Extended;
Перечисленный тип, используемый в финансовых функциях Вычисление амортизации методом двойного баланса Будущее значение вложения
Вычисление процентов по ссуде
Норма прибыли, необходимая для получения заданной суммы Вычисление внутренней скорости оборота вложения для ряда последовательных выплат Вычисление чистой текущей стоимости вложения для ряда последовательных выплат с учетом процентной ставки Количество периодов, за которые вложение достигнет заданной величины продолжение ^
514
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Подпрограммы модуля DateUtils
515
Подпрограмма
Описание
Подпрограмма
Описание
function Payment (Rate : Extended; NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; function PeriodPayment (Rate: Extended; Period, NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; function PresentValue(Rate: Extended; NPeriods: Integer; Payment, FutureValue: Extended; PaymentTime: TPaymentTime) : Extended;
Размер периодической выплаты для погашения ссуды при заданных числе периодов, процентной ставке, а также текущем и будущем значениях ссуды Платежи по процентам за заданный период
f u n c t i o n D a y s I n Y e a r ( c o n s t AValue : TDateTime) : Word; f u n c t i o n DaySpan ( c o n s t ANow, AThen : TDateTime) : D o u b l e ; p r o c e d u r e D e c o d e D a t e (Date : T D a t e T i m e ; v a r Y e a r , Month, Day: Word); procedure DecodeDateDay ( c o n s t AValue : TDateTime; o u t AYear, ADayOfYear: Word); f u n c t i o n D e c o d e D a t e F u l l y ( c o n s t DateTime : TDateTime; v a r Y e a r , Month, Day, DOW: Word) : B o o l e a n ;
Возвращает количество дней в году
function SLNDepreciation (Cost, Salvage: Extended; Life: Integer): Extended; function SYDDepreciation (Cost, Salvage: Extended; Life, Period: I n t e g e r ) : Extended;
Вычисление амортизации методом постоянной нормы
Текущее значение вложения
Вычисление амортизации методом весовых коэффициентов
Подпрограммы модуля DateUtils Подпрограмма
Описание
type TValueRelationship = -1..1; function CompareDate(const A, B: TDateTime) : TValueRelationship; function CompareDateTime(const A, B: : TDateTime)TValueRelationship; function CompareTime (const A, B: TDateTime): TValueRelationship; function CurrentYear: Word;
Сравнивает две даты
function DateOf (const AValue: TDateTime) : TDateTime; function DayOf (const AValue: TDateTime) : Word; function DayOf TheWeek (const AValue : TDateTime) : Word; function DayOf TheYear (const AValue : TDateTime) : Word; function DayOf Week (Date: TDateTime) : Integer; „ function DaysBetween (const ANow, AThen : TDateTime) : Integer; function DaysInAMonth (const AYear, AMonth: Word) : Word; function DaysInAYear (const AYear: Word) : Word; f u n c t i o n DaysInMonth ( c o n s t AValue : TDateTime) : Word;
Сравнивает две даты-времени Сравнивает два времени Возвращает текущий год по григорианскому календарю Возвращает дату Возвращает номер дня в месяце (1...31) Возвращает день недели (1...7, 1 — понедельник, 7 — воскресенье) Возвращает порядковый номер дня в году (1...366) Возвращает день недели (1...7, 1 — воскресенье, 7 — суббота) Возвращает полное количество дней между двумя датами Возвращает количество дней в месяце Возвращает количество дней в году Возвращает количество дней в месяце
procedure DecodeDateMonthWeek ( c o n s t AValue: T D a t e T i m e ; o u t AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word); procedure DecodeDateTime ( c o n s t AValue : TDateTime; o u t AYear, AMonth, ADay, AHour, AMinute, ASecond, A M i l l i S e c o n d : Word); procedure DecodeDateWeek ( c o n s t AValue : TDateTime; o u t AYear, AWeekOfYear, ADayOfWeek: Word); procedure DecodeDayOfWeeklnMonth ( c o n s t AValue: T D a t e T i m e ; o u t AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word); procedure DecodeTime (Time: T D a t e T i m e ; var Hour, Min, S e c , MSec: Word); f u n c t i o n E n c o d e D a t e (Year, Month, Day: Word): T D a t e T i m e ; f u n c t i o n T r y E n c o d e D a t e (Year, Month, Day: Word; o u t D a t e : TDateTime) : B o o l e a n ; f u n c t i o n E n c o d e D a t e D a y ( c o n s t AYear, ADayOfYear: Word) : ' T D a t e T i m e ; f u n c t i o n EncodeDateMonthWeek ( c o n s t AYear, AMonth, AWeekOfMonth: Word; c o n s t ADayOfWeek: Word = 1 ) : T D a t e T i m e ; f u n c t i o n E n c o d e D a t e T i m e ( c o n s t AYear, AMonth, ADay, AHour, A M i n u t e , ASecond, AMilliSecond: Word):TDateTime; f u n c t i o n EncodeDateWeek ( c o n s t AYear, AWeekOfYear: Word; c o n s t ADayOfWeek: Word= 1) : T D a t e T i m e ;
Возвращает разницу дат, включая время По дате-времени возвращает год, месяц и день месяца По дате-времени возвращает год и день недели По дате-времени возвращает год, месяц, день месяца и день недели. Возвращает T r u e , если год високосный По дате-времени возвращает год, месяц, неделю месяца и день недели По дате-времени возвращает год, месяц, день месяца, часы, минуты, секунды и миллисекунды По дате-времени возвращает год, неделю года (1...52), день недели По дате-времени возвращает год, месяц, неделю года (1...52), день недели По дате-времени возвращает часы, минуты, секунды и миллисекунды По году, месяцу, дню формирует дату-время
По году и номеру дня формирует дату-время По году, месяцу, неделе месяца и дню недели формирует дату-время По году, месяцу, дню, часу, минутам, секундам и миллисекундам формирует дату-время По году, неделе года и дню недели формирует дату-время
f u n c t i o n EncodeTime (Hour, Min, S e c , MSec: Word) : T D a t e T i m e ; f u n c t i o n TryEncodeTime (Hour, Min, S e c , MSec: Word; o u t Time : IDateTime) : B o o l e a n ; f u n c t i o n EndOfADay ( c o n s t AYear, ADayOfYear: W o r d ) : T D a t e T i m e ; o v e r l o a d ; j u n c t i o n EndOf ADay ( c o n s t AYear, AMonth, ADay: W o r d ) : T D a t e T i m e ; o v e r l o a d ;
По часу, минутам, секундам и миллисекундам формирует дату-время
w U n ? t i o n E n d O f AMonth ( c o n s t AYear, AMonth : Word) : T D a t e T i m e ;
Возвращает дату-время для последней миллисекунды последнего дня указанного месяца
Возвращает дату-время для последней миллисекунды указанной даты
продолжение &
516
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Подпрограмма
Описание
function EndOfAWeek (const AYear, AWeekOfYear: Word; const ADayOfWeek: Word =7) : TDateTime; function EndOf AYear (const AYear) : TDateTime;
Возвращает дату-время для последней миллисекунды последнего дня указанной недели Возвращает дату-время для последней миллисекунды последнего дня указанного года
function EndOf TheXXXX (const AValue : TDateTime): TDateTime;
ХХХХ— Day, M o n t h , Week, Year. Возвращает дату-время для последней миллисекунды указанной даты (месяца, недели, года)
function HourOf (const AValue : TDateTime) : Word; function HourOf TheXXXX (const AValue : TDateTime) : Word;
Возвращает час указанной даты ХХХХ — M o n t h , Week, Year. Возвращает количество часов между указанной датой и началом первого дня того же месяца (недели, года)
function HoursBetween (const ANow, AThen : TDateTime) : Int64; procedure IncAMonth (var Year, Month, Day: Word; NumberOfMonths : Integer = 1 ) ;
Возвращает количество часов между датами
function IncXXXX (const AValue: TDateTime; const ANumberOfXXXX: Integer = 1) : TDateTime;
ХХХХ— Day, Hour, M i l l i s e c o n d , M i n u t e , M o n t h , Second, Week, Year. Увеличивает указанную дату на нужное количество дней (часов, миллисекунд, минут, месяцев, секунд, недель, лет)
function IsInLeapYear(const AValue: TDateTime) : Boolean; function IsLeapYear (Year: Word) : Boolean;
Возвращает T r u e , если указанная дата принадлежит високосному году Возвращает T r u e , если указанный год високосный Возвращает T r u e , если обе датывремени принадлежат одному дню
function IsSameDay (const AValue, ABasis: TDateTime): Boolean; function IsToday (const AValue: TDateTime) Boolean; function MilliSecondOf(const AValue: TDateTime) : Word; function MilliSecondOf TheXXXX (const AValue: TDateTime): LongWord;
function MilliSecondsBetween (const ANow, AThen: TDateTime):Int64; function MinuteOf (const AValue : TDateTime) : Word; function MinuteOf TheXXXX (const AValue : TDateTime) : Word;
Увеличивает указанную дату на нужное количество месяцев
Возвращает T r u e , если указанная дата-время принадлежит текущему дню Возвращает количество миллисекунд указанного времени ХХХХ— Day, Hour, Minute, Month, Second, Week, Year. Возвращает количество миллисекунд от начала указанного дня (часа, минуты, месяца, секунды, недели, года) Возвращает количество миллисекунд между двумя датами Возвращает количество минут указанного времени ХХХХ— Day, Hour, Month, Week, Year. Возвращает количество минут от начала указанного дня (часа, месяца, недели, года)
Подпрограммы модуля DateUtils
51!
Подпрограмма
Описание
function MinutesBetween (const ANow, AThen : TDateTime) : Int64; function MonthOf (const AValue: TDateTime) : Word; function MonthsBetween (const ANow, AThen: TDateTime) : Integer; function RecodeDateTime (const AValue : TDateTime; const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word) : TDateTime; function RecodeDate (const AValue: TDateTime; const AYear, AMonth, ADay: Word) : TDateTime; function RecodeXXXX (const AValue : TDateTime; const AXXXX: Word) : TDateTime; procedure ReplaceDate (var DateTime : TDateTime; const NewDate : TDateTime); procedure ReplaceTime (var DateTime: TDateTime; const NewTime : TDateTime); function SameDate (const A, B: TDateTime) : Boolean; function SameTime (const A, B: TDateTime) : Boolean; function SecondOf (const AValue : TDateTime) : Word; function SecondOf TheXXXX (const AValue : TDateTime): LongWord;
Возвращает количество минут между двумя датами Возвращает месяц указанной даты
function SecondsBetween const ANow, AThen: TDateTime) : Int64; function StartOfAXXXX() TDateTime; function Today: TDateTime; function Tomorrow: TDateTime; function WeekOf (const AValue: TDateTime) : Word; function WeekOf TheXXXX (const AValue : TDateTime) : Word; function WeeksBetween (const ANow, AThen : TDateTime) : Integer; function WeeksInAYear (const AYear: Word) : Word; function WithinPastXXXXs (const ANow, AThen: TDateTime; const AXXXXs: Integer) : Boolean;
Возвращает количество месяцев между двумя датами ХХХХ— Day, Hour, M i l l i s e c o n d M i n u t e , M o n t h , Second, Year. Заменяет нужные части указанной даты-времени
Заменяет дату Заменяет время Возвращает T r u e , если обе даты одинаковы Возвращает T r u e , если оба времен одинаковы вплоть до миллисекунд Возвращает количество секунд в указанном времени ХХХХ— Day, Hour, M i n u t e , M o n t h , Week, Year. Возвращает количество секунд от начала указанного дня (часа, минуты, месяца, секунды, недели, года) Возвращает количество секунд между двумя датами ХХХХ — Day, M o n t h , Week, Year. Возвращает первый момент дня (месяца, недели, года) Возвращает текущую дату и 00:00:0( время Возвращает завтрашнюю дату и 00:00:00 время Возвращает неделю (1...53) указанной даты ХХХХ — M o n t h , Year. Возвращает порядковый номер недели в месяце (1...6) или в году (1...53) Возвращает количество недель между двумя датами Возвращает количество полных недель в указанном году ХХХХ — D a y , H o u r , M i l l i S e c o n c M i n u t e , M o n t h , Second, Week, Year. Возвращает T r u e , если обе даты отстоят не более чем на указанное количество дней (часов, миллисекунд, минут, месяцев, секун/ недель, лет) продолжение £
518
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Системные переменные, управляющие стандартным отображением данных
Системные переменные, управляющие стандартным отображением данных
Подпрограмма
Описание
function YearOf (const AValue : TDateTime) : Word;
Возвращает год указанной даты
f u n c t i o n Y e a r s B e t w e e n ( c o n s t ANow, TDateTime) : I n t e g e r ; function Yesterday: TDateTime;
Возвращает количество лет между двумя датами
Переменная
Возвращает вчерашнюю дату и 00:00:00 время
CurrencyString:
AThen :
Спецификаторы формата даты/времени1 Спецификатор
Описание
Описание String;
Отображает день без ведущего нуля: 8
dddd
Отображает день недели: суббота (для нерусифицированной версии Windows: Saturday)
ddddd
Отображает дату в формате дд.мм.гг. 08.06.02
dddddd
Отображает дату в формате д Месяц год. 8 Июнь 2002 (для нерусифицированной версии Windows: 8 June 2002)
m
Отображает число месяца без ведущего нуля: 6
mm
Отображает число месяца сведущим нулем: 06
mmm
Отображает сокращенное название месяца: июн
mmmm
Отображает полное название месяца: Июнь
у или уу
Отображает две последние цифры года: 02
yyy или уууу
Отображает все цифры года: 2002
h
Отображает час без ведущего нуля: 19
hh
Отображает час с ведущим нулем: 19
Символ или символы денежной единицы; для русифицированной версии Windows ими являются символы р
CurrencyFormat: Byte;
Определяет положение символов денежной единицы0 - $ 1 ; 1 — 1$; 2— $ 1 ; 3 - 1 $; в русифицированной версии Windows используется формат 1 ( 1 р . )
NegCurrFormat: Byte;
Определяет формат отрицательной суммы: 0 — ($ 1) • 1 $ 1 ; 2 — $ - 1 ; 3 — $ 1 - ; 4 — (1$); 5 1$; 6 - 1 - $ ; 7 - 1 $ - ; 8 - - 1 $ ; 9 - - $ 1 ; 10— 1 $ - ; 1 1 - $ 1 - ; 12— $ - 1 ; 1 3 — 1 - $ ; 1 4 - ( $ 1 ) ; 15 — (1 $ ) ; в русифицированной версии Windows используется формат 5 ( - 1 р . )
ThousandSeparator: Char;
Разделитель тысяч; в русифицированной версии Windows используется символ #166
DecimalSeparator: Char;
Разделитель дробной и целой частей числа, в русифицированной версии Windows используется запятая ( ' , ')
CurrencyDecimals : Byte;
Количество цифр после запятой в денежном формате. Обычно содержит 0, что блокирует вывод мелкой денежной единицы. Чтобы в сумме присутствовали не только рубли, но и копейки, в переменную следует установить значение 2
DateSeparator: Char;
Разделитель даты, в русифицированной версии Windows используется точка (' . ')
ShortDateFormat: String;
Обычно используется формат ' d d . mm. у у ' , что соответствует, например, дате ' 0 8 . 0 6 . 0 2 '
LongDateFormat: String;
Для русифицированной версии содержит символы ' dd MMMM у у у у г. ', что дает ' 0 8 Июнь 2 0 0 2 г. '
TimeSeparator: Char;
Разделитель времени, в русифицированной версии Windows используется двоеточие (' : ')
Отображает сначала дату в формате дд.мм.гг, затем пробел и время в формате чч:мм:сс. 08.06.02 19:45 d dd
519
Отображает день с ведущим нулем: 08
n
Отображает минуты без ведущего нуля: 45
nn
Отображает минуты с ведущим нулем: 45
s
Отображает секунды без ведущего нуля: 0
ss
Отображает секунды сведущим нулем: 00
t
Отображает время в формате чч:мм\ 19:45
tt
Отображает время в формате чч:мм:сс. 19:45:00
ShortTimeFormat:
LongTimeFormat: String;
Полный формат времени (обычно ' hh : mm: s s ')
ampm
Отображает время в 12-часовом формате (am — до полудня, рт — после полудня). Для спецификаторов hh:mm am/pm получим 07:45 pm Отображает время в 12-часовом формате, но без указания до/после полудня. Для спецификаторов hh :mm ampm получим 07:45
ShortMonthNames: array [1- .12] of S t r i n g ;
Краткие имена месяцев (янв, фев, мар, апр, май, июн, июл, авг, сен, окт, ноя, дек)
a/p
Отображает время в 12-часовом формате (а — до полудня, р — после полудня). Для спецификаторов hh :mm а / р получим 07:45 р
LongMonthNames: array U..12] of String;
/
Отображает используемый в Windows разделитель даты. Для спецификаторов d/m/y получим 8.6.02
Полные имена месяцев (Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь)
ShortDayNames: array [1- .7] of S t r i n g ;
Краткие имена дней недели (Вс, Пн, Вт, Ср, Чт, Пт, Сб)
LongDayNames: a r r a y I1- -7] of S t r i n g ;
Полные имена дней недели (воскресенье, понедельник, вторник, среда, четверг, пятница, суббота)
Ii£stSeparator:
Разделитель списка строк (' ; ')
am/pm
Отображает используемый в Windows разделитель времени. Для спецификаторов h: n : s получим 19:45:0 1
Примеры приводятся для даты 8 июня 2002 г. и времени 19:45:00.
String;
Char;
Краткий формат времени (обычно ' h: mm')
520
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Правила использования параметров 1 функции FloatToStrF Описание
f fExponent
Научная форма представления с множителем еХХ («умножить на 10 в степени хх»). P r e c i s i o n задает общее количество десятичных цифр мантиссы, D i g i t s — количество цифр в десятичном порядке хх. Число округляется с учетом первой отбрасываемой цифры: ЗД416Е+00
f
f
Формат с фиксированным положением разделителя целой и дробной частей. P r e c i s i o n задает общее количество десятичных цифр в представлении числа, D i g i t s — количество цифр в дробной части. Число округляется с учетом первой отбрасываемой цифры: 3,14 General
Универсальный формат, использующий наиболее удобную для чтения форму представления вещественного числа. Соответствует формату f f F i x e d , если количество цифр в целой части меньше или равно P r e c i s i o n , а само число— больше или равно 0,00001, в противном случае соответствует формату f f E x p o n e n t : 3,1416
f fNumber
Отличается от ff F i x e d использованием символа-разделителя тысяч при выводе больших чисел (для русифицированной версии Windows таким разделителем является пробел). Для V a l u e = к • 1000 получим 3 141,60
ff
Денежный формат. Соответствует f fNumber, но в конце строки ставится символ денежной единицы (для русифицированной версии Windows — символы р . ) . Для V a l u e = л • 1000 получим 3 141,60р
Currency
Спецификаторы форматирования вещественных чисел Спецификатор
Описание
0
Определяет поле для цифры. Если в данной позиции форматируемое число имеет значащую цифру, она выводится, если нет— выводится 0
#
Определяет поле для цифры. Если в данной позиции форматируемое число имеет значащую цифру, она выводится, если нет— ничего не выводится Поле для разделителя целой и дробной частей числа
, Е+, е+, Е-, е-
;
Поле для разделителя тысяч Признак представления числа в научном формате. В этом случае число представляется мантиссой и десятичным порядком, между которыми стоит символ Е. Спецификаторы Е+ и е+ предписывают выводить знак + перед неотрицательным десятичным порядком, при использовании Е- и е- знак + перед порядком не выводится Разделитель спецификаторов формата для положительного, отрицательного и нулевого чисел
521
Подпрограммы для работы с файлами
Значение Format
ff F i x e d
Подпрограммы для работы с файлами
^
Примеры преобразования даются для Value = р (3,141593654), Precision - 5 и Digits = 2.
Подпрограмма
Описание
function ChangeFileExt (const FileName, Extension: String): String; procedure ChDir (Path: String) ;
Изменяет существующее расширение файла расширением, заданным параметром E x t e n s i o n
function DateTimeToFileDate (DateTime: TDateTime): Integer; function DiskFree (D: Byte) : Longlnt;
Изменяет текущий каталог: P a t h — строковое выражение, содержащее путь к устанавливаемому по умолчанию каталогу Преобразует значение D a t e T i m e в системный формат времени создания (обновления) файла Возвращает объем в байтах свободного пространства на указанном диске: D — номер диска (0 — устройство по умолчанию, 1 — диск А, 2 — диск В и т . д.). Функция возвращает значение - 1 , если указан номер несуществующего диска
function DiskSize (D: Byte) : Longlnt;
Возвращает объем в байтах полного пространства на указанном диске: D— номер диска (0 — устройство по умолчанию, 1 — диск А, 2 — диск В и т . д.). Функция возвращает значение - 1, если указан номер несуществующего диска function FileAge (const FileName: Для файла F i l e N a m e возвращает время его String) : Integer; последнего обновления (в системном формате) или - 1 , если такого файла не существует function Исключает из строки S замыкающий символ \ (если этот символ не замыкает строку, возвращает S ExcludeTrailingBackslash (constS: String): String; без изменения) function ExpandFileName(const Дополняет имя файла текущим каталогом (и диском) FileName: String): String; function ExpandUNCFileName (const Дополняет имя файла текущим сетевым каталогом FileName: String): String; (и диском) function ExtractFileDir(const Извлекает из полного имени файла маршрут доступа FileName: String) : String; к нему (без последнего символа \) function ExtractFileExt(const Извлекает из полного имени файла его расширение FileName: String): String; (с ведущей точкой) function ExtractFileName(const Извлекает из полного имени файла его имя FileName: String): String; (с расширением) function ExtractFilePath(const Извлекает из полного имени файла маршрут доступа FileName: String): String; к нему (с последним символом \) function ExtractRelativePath Извлекает из полного имени файла имя маршрута (const BaseName, DestName: относительно DestName (промежуточные каталоги String): String; заменяются символами . . \) function ExtractShortPathName Преобразует имя файла к короткому формату 8.3 (const FileName: String): для MS-DOS и Windows 3.x String; function FileDateToDateTime (UleDate: Integer) : TDateTime; funct ion FileGetDate (Handle: I n t eger) : I n t e g e r ;
Преобразует системный формат F i l e D a t e времени создания файла в формат дата-время По заданному^цескриптору файла H a n d l e возвращает время и дату его создания в системном формате. Возвращает 0 в случае успеха или код ошибки продолжение &
522
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Стандартные классы исключений
523
Подпрограмма
Описание
Класс
Родитель
Обрабатываемое исключение
function FileSetDate (Handle: Integer; Age: Integer) : Integer;
Для файла с дескриптором H a n d l e устанавливает новые время и дату его создания Age в системном формате. Возвращает 0 в случае успеха или код ошибки Возвращает полный маршрут доступа к файлу с ведомым символом \
EClassNotFound
EFilerError
Для компонента, читаемого из потока данных, не найден соответствующий класс. Обычно возникает в случае, когда в форму вставлен нестандартный компонент, а в библиотеке компонентов Delphi нет связанного с ним класса Возникает в объектах класса TCommonCalendar и его потомках, когда вводится неверная дата
functionIncludeTrailingBackslash(const S: String) : String; function IOResult: Integer; function IsPathDelimiter (const S: String; Index: Integer): Boolean; function MatchesMask (const Filename, Mask: String): Boolean; procedure ProcessPath (const EditText: String; var Drive: Char; var DirPart: String; var FilePart: String); procedure RmDir (Dir: String) ;
ECommonCalendarError Exception Возвращает условный признак последней операции ввода-вывода Возвращает T r u e , если в строке S символ I n d e x есть \
EComponentError
Возвращает T r u e , если имя F i l e N a m e соответствует групповому имени Mask EControlC Возвращает имя диска, маршрут поиска и имя файла в переменных D r i v e , D i r P a r t и F i l e P a r t соответственно; E d i t T e x t — полное имя файла Удаляет каталог D i r . Удаляемый каталог должен быть пустым, то есть не содержать файлов или имен каталогов нижнего уровня
EConvertError ECorbaDispatch
ECorbaException
Стандартные классы исключений
ECorbaUserException
Класс
Родитель
Обрабатываемое исключение
EDatabaseError
EAbort
Exception
EDateTimeError
EAbstractError
Exception
EAccessViolation
Exception
EAppletException
Exception
EArrayError
Exception
Реализует «тихую» (без какого-либо сообщения) обработку любого исключения Программа пытается вызвать абстрактный метод Программа пыталась обратиться к не принадлежащей ей области памяти или использует недействительный указатель Ошибка связана с созданием управляющих панелей в апплетах Возникает из-за различного рода ошибок при работе с массивами (неверный индекс, попытка вставить элемент в массив фиксированной длины или в отсортированный массив и т. п.) Возбуждается отладочной процедурой A s s e r t , когда тестируемое ею логическое выражение имеет значение F a l s e Программа пыталась обратиться к свойству B i t s объекта T B i t s с индексом меньше нуля или больше максимально допустимого значения Объект-брокер не может найти сервер Ошибка в наборе данных для компонента TDecisionCube
EAssertionFaild
Exception
EBitsError
Exception
EBrоkerException ECacheError
Exception Exception
EDBClient EDBEditError EDBEngineError EDimensionMarError EDimlndexError EDivByZero EDSWriter EExternalException EFCreateError
Возникает при различных манипуляциях программы с компонентом (программа не может зарегистрировать компонент, переименовать его или для его работы требуется интерфейс СОМ, который компонентом не поддерживается) Возникает при нажатии клавиш Ctrl+C Exception в случае работы приложения в режиме консоли Exception Ошибка преобразования в функциях S t r T o I n t или S t r T o F l o a t Exception Возникает в программах, использующих технологию CORBA, при ошибках, связанных с несовпадением интерфейсов сервера и брокера данных Exception Возникает в программах, использующих технологию CORBA ECorbaException Возникает как определяемая пользователем реакция на ошибки интерфейса Exception Возникает, когда компонент обнаруживает ошибку в базе данных Exception Возбуждается компонентом T D a t e T i m e P i c k e r при попытке ввода неверных даты или времени EDatabaseError Ошибка связана с неправильной работой TClientDataSet Exception Возникает, когда компонент пытается использовать данные, несовместимые с заданной маской EDatabaseError Связана с ошибками BDE Exception Возникает, когда используемый в кубе решений набор данных не имеет агрегатных полей Связана с нарушением размерности массива Exception данных для куба решений EIntError Ошибка целочисленного деления на ноль Exception Ошибка при подготовке провайдером пакета данных для набора данных Возникла ошибка, код которой не является EStreamError предопределенным в Delphi EStreamError Ошибка при создании файла. Например, попытка создать файл на устройстве, предназначенном только для чтения, или в несуществующем каталоге Exception
продолжение ё>
524
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Стандартные классы исключений
525
Класс
Родитель
Обрабатываемое исключение
Класс
Родитель
Обрабатываемое исключение
EFilerError
EStreamError
EinvalidPointer
EHeapException
EFOpenError
EStreamError
EListError
Exception
EHeapException
Exception
EIBClientError
EIBError
ELowCapacityError
Exception
EIBError EIBInterbaseError
EDatabaseError EIBError
Программа пытается повторно зарегистрировать в потоке данных один и тот же класс Ошибка открытия потока данных. Например, попытка открыть несуществующий файл Ошибка связана с неправильными операциями с динамической памятью Ошибка связана с функционированием IBXклиента Общая ошибка технологии IBX Ошибка связана с функционированием сервера в технологии IBX Любая ошибка в файловых операциях. Поле E r r o r C o d e объекта этого класса содержит код ошибки Возникает, когда компонент класса T D a t a B l o c k l n t e r p e t e r не может интерпретировать данные блока данных Любая ошибка в целочисленных вычислениях Попытка недопустимого приведения типов в OLE-объектах Ошибка целочисленного переполнения: программа пытается присвоить целочисленной переменной значение, выходящее из 32 двоичных разрядов Возбуждается математическими функциями при выходе аргумента из допустимого диапазона Программа пытается осуществить недопустимое преобразование типов с помощью оператора as Программа пытается загрузить в контейнер изображение из файла, который имеет недопустимый формат (допустимыми форматами являются растр, метафайл, курсор, значок) Программа пытается выполнить недопустимую графическую операцию Программа пытается выполнить недопустимую операцию с таблицей (например, обратиться к несуществующим столбцу или строке) Программа пытается прочитать ресурс изображения из файла, в котором этого ресурса нет Ошибка в операциях с плавающей точкой (недопустимая инструкция, переполнение стека сопроцессора и т. п.) Не имеющий окна компонент пытается выполнить операцию, требующую дескриптора окна
Попытка использовать недействительный указатель Эта ошибка связана с неверными действиями программы по отношению к разного рода спискам. Например, обращение к элементу списка с индексом меньше нуля или больше максимально допустимого Ошибка возникает при попытке выделения памяти на устройстве, у которого нет нужной свободной памяти
EMatchError
Exception
EMenuError
Exception
EMCIDiviceError EMethodNotFound
Exception EFilerError
EMonthCalError
ECommonCalendarError
EOleCtrlError
Exception
EOleError EOleException
Exception EOleSysError
EOleRegistrationError
EOleError
EInOutError
EInterpreterError
EIntError
Exception
Exception
Exception
EIntfCastError
Exception
EIntOverflow
EIntError
EInvalidArgument
EMatchError
EInvalidCast
Exception
EInvali'dGraphic
Exception
ElnvalidGraphicOperation ElnvalidGridOperation
Exception
EInvalidlmage
EFilerError
EInvalidOp
EMatchError
EInvalidOperation
Exception
Exception
EOleSysError EOutlineError* EOutOfMemory
EOleError Exception EHeapException
EOutOfResource
EOutOfMemory
EOverflcw
EMatchError
EPackageError EParserError
Exception Exception
EPrinter
Exception
EPrivilege
Exception
Любая ошибка при выполнении вычислений с плавающей точкой Ошибка при работе программы с меню. Например, при добавлении элемента с идентификатором, который уже определен в меню Ошибка возникла в медиа-плеере Программа прочитала из потока данных объект, но не может найти связанный с классом объекта метод Возбуждается компонентом класса T M o n t h C a l e n d a r при попытке ввода неправильной даты Программа не может установить связь с элементом ActiveX Низкоуровневая ошибка в технологии OLE Программа использует неверный OLEинтерфейс Ошибка регистрации OLE-объекта в реестре Windows Возникает при неправильном выполнении команды OLE-автоматизации Возникает при ошибке доступа к компоненту класса T O u t L i n e Эта ошибка возникает, когда программа запрашивает слишком большой для данной конфигурации Windows объем памяти Программа требует от Windows дескриптор окна, но операционная система исчерпала лимит дескрипторов Результат операций с плавающей точкой слишком велик, чтобы уместиться в регистрах сопроцессора Возникает при ошибке доступа к пакету Ошибка преобразования текста в двоичные данные при чтении из потока Система Windows сообщила программе об ошибке принтера Программа пытается выполнить привилегированную операцию. Привилегированные операции могут выполняться только ядром Windows продолжение &
526
Приложение А • Некоторые стандартные подпрограммы, переменные, классы
Класс
Родитель
Обрабатываемое исключение
EPropertyConvertError EPropertyError
Exception
EPropReadOnly
Exception
EPropWriteOnly
Exception
ERangeError
EIntError
EReadError
EFilerError
EReconcileError
EDatabaseError
ERegistryException
Exception
EResNotFound
Exception
ESocketConnectionError ESocketError
Exception
EStackOverflow
Exception
EStreamError EStringListError
Exception Exception
EThread
Exception
ETreeViewError
Exception
EUnderflow
EMatchError
EUnsupportedTypeError
Exception
Ошибка при чтении или записи значения свойства Ошибка доступа к свойству при чтении или записи Программа пытается присвоить значение свойству, из которого можно только читать (при использовании технологии OLE) Программа пытается прочитать свойство, предназначенное только для записи Целочисленный результат превышает емкость целого типа данных Программа не может прочитать из потока данных нужное количество байтов Ошибка обновления данных BTClientDataset Ошибка, связанная с операцией над реестром Windows Программа не может найти указанный ресурс в файле ресурсов Ошибка связана с работой ссокетами Windows Ошибка связана с работой ссокетами Windows Исчерпан объем выделенного программе стека Любая ошибка при работе с потоком данных Программа ссылается на строку, индекс которой выходит из диапазона возможных значений для списка строк Ситуация борьбы за общий ресурс в программе с несколькими потоками команд Указан неверный индекс при обращении KTTreeView Результат операций с плавающей точкой слишком мал, чтобы уместиться в регистрах сопроцессора (исчезновение порядка) Выбран недопустимый тип поля в качестве измерения в кубе решений Ошибка обновления набора данных провайдера Ошибка при работе с типом V a r i a n t : недопустимое приведение типов; недопустимая операция; обращение к скалярной переменной как к вариантумассиву; индекс варианта-массива выходит из допустимых значений Ошибочное обращение к API-функции Windows. Свойство Message содержит номер ошибки и связанное с ней сообщение Ошибка записи в поток данных Вещественное деление на ноль
EUpdateError EVariantError
Exception
Exception
Exception Exception
EWin32Error
Exception
EWriteError EZeroDivide
EFilerError EMatchError