DELPHI

advertisement
МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
Государственное образовательное учреждение
высшего профессионального образования
"Ижевский государственный технический университет"
УТВЕРЖДАЮ
Ректор
______________ И.В. Абрамов
"_____"________________ 200__г.
УДК 519.682.1
ПРОГРАММИРОВАНИЕ В DELPHI
Методические указания для проведения практических и лабораторных занятий по дисциплине "Программирование на языке
высокого уровня" для студентов специальностей
220200 Автоматизированные системы обработки информации и
управления,
220300 Системы автоматизированного проектирования,
направления 552800 Информатика и вычислительная техника
Форма обучения очная и заочная
Ижевск 2004
Кафедра "Автоматизированные системы обработки информации и управления".
Составители: Барков И.А., к.т.н., доцент,
Шутов Е.А., ст. преподаватель.
Методические указания составлены на основании государственного образовательного стандарта высшего профессионального образования и утверждены на
заседании кафедры
Протокол от "____" ________________ 200__ г. № ______.
Заведующий кафедрой
____________________ В.Н. Кучуганов
"____" ________________ 200__ г.
СОГЛАСОВАНО:
Председатель учебно-методической комиссии
по специальности
____________________ В.Н. Кучуганов
"____" ________________ 200__ г.
Методические указания предназначены для проведения практических и лабораторных занятий со студентами специальностей 220300 Системы автоматизированного проектирования, 220200 Автоматизированные системы обработки информации и управления, направления 552800 Информатика и вычислительная
техника.
Начальник учебно-инженерного отдела
____________________ А.М. Ефимова
"____" ________________ 200__ г.
2
СОДЕРЖАНИЕ
1. СВЕДЕНИЯ О СРЕДЕ РАЗРАБОТКЕ DELPHI………………………..
2. ОБЩИЕ СВЕДЕНИЯ О КОМПОНЕНТАХ……………………………
2.1. Списки……………………………………………………………………
2.2. Элементы управления…………………………………………………...
2.3. Положение, размеры и выравнивание элементов управления……….
2.4. Активность и видимость элементов управления……………………..
2.5. Оконные элементы управления………………………………………..
2.6. Реакция на события от мыши и клавиатуры…………………………..
2.7. Фокус ввода……………………………………………………………...
2.8. Графическая подсистема………………………………………………..
3. РАБОТА С КОМПОНЕНТАМИ…………………………………………
3.1. Работа с меню……………………………………………………………
3.2. Работа с кнопками……………………………………………………….
3.3. Ввод и редактирование текста………………………………………….
3.4. Ввод и выбор значений………………………………………………….
3.5. Группирование компонентов……………………………………………
3.6. Компоненты – стандартные диалоговые окна Windows………………
3.7. Компоненты по работе с файловой системой………………………….
3.8. Вспомогательные компоненты………………………………………….
3.9. Форма и ее свойства……………………………………………………..
3.10. Управление дочерними компонентами……………………………….
3.11. Приложение и среда его выполнения. Объект TApplication………
3.12. Системные объекты TClipboard и TScreen………………………….
3.13. Файлы инициализации……………………………………………….
3.14. Печать данных из приложения………………………………………
4. СПРАВОЧНИК ПО ФУНКЦИЯМ DELPHI……………………………
4.1. Функции работы со строками………………………………………….
4.2. Функции работы с файлами…………………………………………….
4.3. Функции форматирования строк……………………………………….
4.4. Функции преобразования чисел с плавающей точкой………………..
4.5. Функции работы с датами и временем…………………………………
3
4
9
9
13
15
17
18
20
21
23
26
26
32
34
36
44
45
48
51
52
57
60
64
66
67
68
69
71
74
77
80
1. СВЕДЕНИЯ О СРЕДЕ РАЗРАБОТКЕ DELPHI
Delphi - это комбинация нескольких важнейших технологий:

Высокопроизводительный компилятор в машинный код

Объектно-ориентированная модель компонент

Визуальное (а, следовательно, и скоростное) построение приложений из программных прототипов

Масштабируемые средства для построения баз данных
Объектно-ориентированная модель программных компонент
Основной упор этой модели в Delphi делается на максимальном реиспользовании кода. Это позволяет
разработчикам строить приложения весьма быстро из заранее подготовленных объектов, а также дает
им возможность создавать свои собственные объекты для среды Delphi. Никаких ограничений по типам объектов, которые могут создавать разработчики, не существует. Действительно, все в Delphi
написано на нем же, поэтому разработчики имеют доступ к тем же объектам и инструментам, которые
использовались для создания среды разработки. В результате нет никакой разницы между объектами,
поставляемыми Borland или третьими фирмами, и объектами, которые вы можете создать.
В стандартную поставку Delphi входят основные объекты, которые образуют удачно подобранную
иерархию из 270 базовых классов. Для начала - неплохо. Но если возникнет необходимость в решении
какой-то специфической проблемы на Delphi, советуем, прежде чем попытаться начинать решать проблему “с нуля”, просмотреть список свободно распространяемых или коммерческих компонент, разработанных третьими фирмами, количество этих фирм в настоящее время превышает число 250, хотя,
возможно, я не обо всех знаю. Скептики, возможно, не поверят мне, когда я скажу, что на Delphi можно одинаково хорошо писать как приложения к корпоративным базам данных, так и, к примеру, игровые программы. Тем не менее, это так. Во многом это объясняется тем, что традиционно в среде
Windows было достаточно сложно реализовывать пользовательский интерфейс. Событийная модель в
Windows всегда была сложна для понимания и отладки. Но именно разработка интерфейса в Delphi
является самой простой задачей для
программиста.
Быстрая разработка работающего
приложения из прототипов
Игровая программа Rendzu была
собрана моим коллегой из готовых
кусков за рабочий день, причем
большая часть времени была посвящена прихорашиванию и приукрашиванию. Screen Saver в виде прыгающих часиков был также изготовлен на Delphi за весьма незначительное время. Теперь эти часики украшают почти каждую IBMсовместимую машину в нашем Демо-центре клиент-серверных технологий. Конечно, на разработку
4
серьезной информационно-поисковой системы в архитектуре клиент-сервер может уйти гораздо большее время, чем на разработку программы-игрушки. Тем не менее многие наши коллеги, до Delphi программировавшие на других языках, утверждают, что на Delphi скорость изготовления сложного проекта выше раз в 10.
Cреда Delphi включает в себя полный набор визуальных инструментов для скоростной разработки
приложений (RAD - rapid application development), поддерживающей разработку пользовательского интерфейса и подключение к корпоративным базам данных. VCL - библиотека визуальных компонент,
включает в себя стандартные объекты построения пользовательского интерфейса, объекты управления
данными, графические объекты, объекты мультимедиа, диалоги и объекты управления файлами,
управление DDE и OLE. Единственное, что можно поставить в вину Delphi, это то, что готовых компонент, поставляемых Borland, могло бы быть и больше. Однако, разработки других фирм, а также свободно распространяемые программистами freeware-компоненты уже восполнили этот недостаток. Постойте, - скажете вы, ведь это уже было. Да, это было в Visual Basic.
Соответствующий стандарт компонент назывался VBX. И этот стандарт так же поддерживается в
Delphi. Однако, визуальные компоненты в Delphi обладают большей гибкостью. Вспомним, в чем была
проблема в VB. Прикладной программист программировал, вообще говоря, в среде языка бэйсик. А
компоненты в стандарте VBX готовили ему его коллеги-профессионалы на С++.
VBX’ы приходили, “как есть”, и ни исправить, ни добавить ничего было нельзя.
А для изготовления VBX надо было осваивать “кухню” языка C++. В Delphi визуальные компоненты
пишутся на объектном паскале, на том же паскале, на котором пишется алгоритмическая часть приложения. И визуальные компоненты Delphi получаются открытыми для надстройки и переписывания.
Чувствуете разницу?
Delphi: настраиваемая cреда разработчика
После запуска Delphi в верхнем окне горизонтально располагаются иконки палитры компонент. Если
курсор задерживается на одной из иконок, под ней в желтом прямоугольнике появляется подсказка
5
Из этой палитры компонент вы можете выбирать компоненты, из которых можно строить приложения.
Компоненты включают в себя как визуальные, так и логические компоненты. Такие вещи, как кнопки,
поля редактирования - это визуальные компоненты; а таблицы, отчеты - это логические.
Понятно, что поскольку в Delphi вы визуальным образом строите свою программу, все эти компоненты
имеют свое графическое представление в поле форм для того, чтобы можно было бы ими соответствующим образом оперировать. Но для работающей программы видимыми остаются только визуальные
компоненты. Компоненты сгруппированы на страницах палитры по своим функциям. К примеру, компоненты, представляющие Windows “common dialogs” все размещены на странице палитры с названием
“Dialogs”.
Delphi позволяет разработчикам настроить среду для максимального удобства. Вы можете легко изменить палитру компонент, инструментальную линейку, а также настраивать выделение синтаксиса цветом.
Заметим, что в Delphi вы можете определить свою группу компонент и разместить ее на странице палитры, а если возникнет необходимость, перегруппировать компоненты или удалить неиспользуемые.
Интеллектуальный редактор
Редактирование программ можно осуществлять, используя запись и исполнение макросов, работу с текстовыми блоками, настраиваемые комбинации клавиш
и цветовое выделение строк .
Графический отладчик
Delphi обладает мощнейшим, встроенным в редактор графическим отладчиком, позволяющим находить и устранять ошибки в коде. Вы можете установить точки останова, проверить и изменить переменные, при помощи пошагового выполнения в точности понять поведение программы. Если же требуются возможности более тонкой отладки, Вы можете использовать отдельно доступный Turbo
Debugger,
проверив
ассемблерные инструкции и
регистры
процессора.
Инспектор
объектов
Этот ин-
6
струмент представляет из себя отдельное окно, где вы можете в период проектирования программы
устанавливать значения свойств и событий объектов (Properties & Events).
Менеджер проектов.
Дает возможность разработчику просмотреть все модули в соответствующем проекте и снабжает удобным механизмом для управления проектами.
Менеджер проектов показывает имена файлов, время/дату выбранных форм и пр.
Можно немедленно попась в текст или форму, просто щелкнув мышкой на соответствующее имя.
Навигатор объектов
Показывает библиотеку доступных объектов и осуществляет навигацию по вашему приложению.
Можно посмотреть иерархию объектов, прекомпилированные модули в библиотеке, список глобальных имен вашего кода.
Формы, модули и метод разработки “Two-Way Tools”
Формы - это объекты, в которые вы помещаете другие объекты для создания пользовательского интерфейса вашего приложения. Модули состоят из кода, который реализует функционирование вашего
приложения, обработчики событий для форм и их компонент.
Информация о формах хранится в двух типах файлов - .dfm и .pas, причем первый тип файла - двоичный - хранит образ формы и ее свойства, второй тип описывает функционирование обработчиков событий и поведение компонент. Оба файла автоматически синхронизируются Delphi, так что если доба-
7
вить новую форму в ваш проект, связанный с ним файл .pas автоматически будет создан, и его имя будет добавлено в проект.
Такая синхронизация и делает Delphi two-way-инструментом, обеспечивая полное соответствие между
кодом и визуальным представлением. Как только вы добавите новый объект или код, Delphi устанавливает т.н. “кодовую синхронизацию” между визуальными элементами и соответствующими им кодовыми представлениями.
Например, предположим, вы добавили описание поведения формы (соотв. обработчик событий), чтобы
показывать окно сообщения по нажатию кнопки. Такое описание появляется, если дважды щелкнуть
мышкой непосредственно на оъект Button в форме или дважды щелкнуть мышью на строчку OnClick
на странице Events в Инспекторе объектов. В любом случае Delphi создаст процедуру или заголовок
метода, куда вы можете добавить код.
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
Cоздавая этот код, Delphi автоматически формирует декларацию объекта TForm1, которая содержит
процедуру ButtonClick, представляющую из себя собственно обработчик события.
TForm1 = class (TForm)
Button1: Tbutton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Конечно вы запросто можете решить после получения этого кода, что автоматически созданные имена
Вас не устраивают, и заменить их. Например, Button1 на Warning. Это можно сделать изменив свойство
Name для Button1 при помощи Инспектора объектов. Как только вы нажмете Enter, Delphi автоматически произведет соответствующую синхронизацию в коде. Так как объект TForm1 существует в коде, вы
свободно можете добавлять любые другие поля, процедуры, функции или object definition. К примеру,
вы можете дописать в коде свою собственную процедуру, обрабатывающую событие, а не делать это
визуальным методом.
Следующий пример показывает, как это можно сделать. Обработчик принимает аргумент типа TObject,
который позволяет нам определить, если необходимо, кто инициировал событие. Это полезно в случае,
когда несколько кнопок вызывают общую процедуру для обработки.
TForm1 = class(TForm)
Warning: TButton;
Button1: TButton;
procedure WarningClick(Sender: TObject);
procedure NewHandler(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Здесь мы имеем дело уже с второй стороной синхронизации. Визуальная среда в данном случае распознает, что новая процедура добавлена к объекту и соответствующие имена появляются в Инспекторе
объектов.
8
Обработка исключительных ситуаций
Серьезные приложения должны надежным образом обрабатывать исключительные ситуации, сохранять, если возможно, выполнение программы или, если это невозможно, аккуратно ее завершать.
Написание кода, обрабатывающего исключительные ситуации, всегда было непростой задачей, и являлось источником дополнительных ошибок.
В Delphi это устроено в стиле С++. Исключения представлены в виде объектов, содержащих специфическую информацию о соответствующей ошибке (тип и место- нахождение ошибки). Разработчик может оставить обработку ошибки, существо-вавшую по умолчанию, или написать свой собственный обработчик.
Обработка исключений реализована в виде exception-handling blocks (также еще называется protected
blocks), которые устанавливаются ключевыми словами try и end. Существуют два типа таких блоков:
try...except и try...finally.
Общая конструкция выглядит примерно так:
try
{ выполняемые операторы }
except
on exception1 do statement1; { реакция на ситуации }
on exception2 do statement2;
else
{ операторы по умолчанию }
end;
Конструкция try....finally предназначена для того, чтобы разработчик мог быть полностью уверен в том,
что, что бы ни случилось, перед обработкой исключительной ситуации всегда будет выполнен некоторый код (например, освобождение ресурсов).
try
{ выполняемые операторы }
finally
{ операторы, выполняемые безусловно }
end;
2. ОБЩИЕ СВЕДЕНИЯ О КОМПОНЕНТАХ
2.1. Списки
Класс TList
Класс TList — универсальный список. Он представляет собой массив нетипированных указателей и поэтому годится для хранения набора любых, в том числе разнотипных, данных и объектов. При
добавлении/удалении в список данные не создаются и не уничтожаются — эта обязанность лежит на
программисте. Приведем доступные ему методы и свойства класса:
property Items[Index: Integer]:
Pointer;
property Count: Integer;
Возвращает указатель на содержимое элемента списка с индексом
Index. Это свойство является векторным свойством, принимаемым по
умолчанию, и его имя можно при записи опускать (см. раздел "Свойства").
Определяет число элементов в списке.
9
property Capacity: Integer;
Определяет максимальное число элементов в списке. Оно может изменяться как явно — пользователем, так и при добавлении элементов
в список, в том случае, когда Count>=Capacity. Максимальная емкость списка — 16380 элементов.
Управляют списком следующие методы:
function Add(Item: Pointer): Добавляет в конец списка элемент, который будет равен Item (т. е. укаInteger;
зывать на те же данные).
function Remove(Item: Point- Удаляет из списка элемент, который равен Item.
er): Integer;
procedure Insert(Index: Inte- Вставляет элемент, равный Item, перед элементом с индексом Index.
ger; Item: Pointer) ;
procedure Delete(Index: Inte- Удаляет из списка элемент с индексом Index.
ger);
procedure Clear;
Очищает список, устанавливая величины Count и Capacity в 0.
procedure Exchange(Indexl,
Index2: Integer);
function Expand: TList;
Меняет местами элементы списка с индексами Indexl и Index2.
function First: Pointer; function Last: Pointer;
function IndexOf(Item: Pointer): Integer;
procedure Move(CurIndex,
Newlndex: Integer) ;
procedure Pack;
Возвращают значения первого и последнего (с индексом Count-1) элементов списка соответственно.
Возвращает индекс элемента, равного Item.
При соблюдении равенства Count=Capacity расширяет список. При емкости списка менее пяти элементов, он по умолчанию расширяется на
четыре элемента, при пяти-восьми — на восемь, более восьми — на
шестнадцать.
Перемещает элемент списка с положения Curlndex в положение Newlndex.
Упаковывает список, сдвигая элементы к началу на пустующие места.
Наконец, если приведенных методов почему-либо недостаточно, то свойство
(RC; property List: pPointerList;
pPointerList = ^TPointerList;
TPointerList = array[0..65520 div SizeOf(Pointer)] of Pointer;
возвращает указатель непосредственно на список указателей ((ко) означает, что свойство доступно только для чтения).
Класс TStrings
Многофункциональный класс, предназначенный для хранения текстовых строк и связанных с
ними объектов (любых потомков TObject). TStrings — абстрактный класс; он только описывает методы
работы с наборами строк и сопутствующих им объектов, но как именно они хранятся, на его уровне не
определено. Его потомки очень многочисленны; они играют основную роль в компонентах-списках
(TListBox, TComboBox), редакторе (TMemo) и других. Так что вам чаще всего придется иметь дело с
TStrings как со свойством одного из компонентов. В дальнейшем экземпляры этого класса и порожденных от него классов мы-будем называть наборами строк. Для создания собственных наборов строк вне
компонентов предназначен потомок TStrings — TStringList, который будет рассмотрен ниже.
К строкам и объектам соответственно можно получить доступ через свойства:
10
property Strings[Index: Integer]: string;
property Objects[Index: Integer]: TObject;
Первое из них является векторным свойством, принимаемым по умолчанию. Общее количество
пар в списке равно значению свойства:
(Ro'l property Count: Integer;
Класс TStrings также предназначен для хранения пар вида 'параметр=значение', например, в
файлах инициализации (.INI). Эту возможность реализует следующее свойство:
property Values[const Name: string]: string;
При обращении к этому свойству для чтения ищется строка, содержащая подстроку (параметр)
Name и символ '='. Если она найдена, возвращается то, что находится в этой строке после '='. Если нет,
Values[Name] равно пустой строке. При записи: если строка, содержащая параметр Name, найдена — ее
значение после '=' заменяется новым значением, если нет — строка добавляется. Если существующему
параметру присваивается пустая строка (Valu-es[Name] := ";), то он удаляется из набора строк.
Методы класса приведены в таблице:
procedure BeginUpdate; procedure
Пара процедур, которые устанавливают и сбрасывают флаг обEndUpdate;
новления набора. Между ними, для ускорения работы, нужно
заключать все операции по копированию, удалению и т. д.
большого количества элементов.
procedure Clear;
Осуществляет полную очистку набора.
procedure Insert(Index: Integer; const S: Вставляет строку S под индексом Index.
string);
procedure Delete(Index: Integer);
Удаляет строку с индексом Index.
function IndexOf(const S: string): Inte- Возвращает индекс (номер в наборе) строки S. Если она не
ger;
найдена, функция возвращает -1.
function IndexOfObject(AObject:
TObject): Integer;
function Equals(Strings: TStrings):
Boolean;
Возвращает индекс объекта в наборе. В случае неудачи возвращает -1.
Сравнивает строки вызвавшего его объекта со строками объекта Strings и возвращает True в случае равенства (сравниваются
число строк и все строки попарно).
function Add(const S: string): Integer-
Добавляет строку S в конец набора и в случае успеха возвращает присвоенный ей индекс (он должен быть равен значению
Count до добавления строки).
function AddObject(const S: string;
AObject: TObject): Integer;
procedure Exchange(Indexl, Index2:
Integer);
procedure Move(Curlndex, Newlndex:
Integer);
procedure InsertObject(Index: Integer;
const S: string; AObject: TObject);
Добавляет строку в паре с объектом. Возвращает то же, что и
метод Add.
Меняет местами пары строка+объект с индексами Indexl и
Index2.
Перемещает пару строка+объект с позиции Curlndex в позицию
Newlndex.
Вставляет объект AObject и соответствующую ему строку S в
набор под индексом Index.
Шесть методов предназначены для экспорта/импорта наборов строк:
а) в поток:
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
11
б) в файл (создавая поток и вызывая два предыдущих метода):
procedure LoadFrornFile (const FileName: strings-procedure SaveToFile(const FileName: string);
в) в данные в формате текстового редактора (подряд расположенные строки, оканчивающиеся
парой символов CR/LF (16-ричные коды SOD/SOA)).
procedure AddScrings(Strings:
Добавляет в конец набора другой набор Strings.
TStrings! ;
procedure Assign!Source: T'Persisier-t l Уничтожает прежнее содержимое набора и подставляет вместо
;
него Source, если источник имеет тип TStrings. В противном
случае возникает исключительная ситуация EConvertError.
При этом метод function GetText: PChar; выгружает строки в единый массив, где они разделены
парами символов CR/LF; в конце такого массива ставится нулевой байт. Размер массива не может превышать 65520 байт; поэтому строки выгружаются до тех пор, пока их суммарная длина не превосходит
этого значения.
Метод procedure SetText(Text: PChar); читает строки из массива Text. Строки в массиве должны
быть отделены друг от друга парой символов CR/LF; допускается и один символ LF (16-ричный код
$ОА). Символы с кодами 0, $lA(<Ctrl>+<Z>) воспринимаются как конец текста. При этом прежнее содержимое набора уничтожается.
Класс TStringList
Этот класс объединяет в себе свойства TStrings и TList простейшим способом — указатель на
объект и соответствующая строка объединяются в запись, указатель на которую хранится в списке. В
классе переопределены многие виртуальные методы TStrings: Add, Clear, Delete, Exchange, IndexOf,
Insert; он является полностью функциональным и вы можете создавать экземпляры TStringList в своей
программе для работы с наборами строк и объектов (помимо тех, которые уже есть в компонентах).
Кроме унаследованных от TStrings, определены дополнительно полезные методы и свойства:
function Find(const S: string; var Index: Метод ищет в наборе строку S и в случае успеха возвращает
Integer): Boolean;
результат True, а в параметре Index — ее индекс.
property Sorted: Boolean;
Свойство — признак отсортированности элементов (сортировка осуществляется через посимвольное сравнение строк). Установка Sort := True вызывает процедуру сортировки, которую
можно вызвать и явно при помощи метода:
procedure Sort;
Попытка добавить или вставить элемент в отсортированный
список вызывает исключительную ситуацию EListError; в этом
случае до выполнения действия свойству Sorted нужно присвоить значение False.
property Duplicates: TDuplicates;
TDuplicates = (duplgnore, dupAccept,
dupError);
Свойство определяет, что происходит при попытке добавить в
список дубликат уже имеющейся строки: duplgnore — добавление игнорируется (отклоняется); dupError — добавление приводит к созданию исключительной ситуации EListError; dupAccept — одинаковые строки разрешены. В этом случае при поиске в неотсортированном списке не определено, которая из
строк будет найдена первой.
12
property OnChange: TNotiДва свойства, предусмотренные для определения польfyEvent; property OnChanging: TNoti- зователем своей реакции на изменение данных. Событие
fyEvent;
OnChanging вызывается во многих рассмотренных выше методах до внесения первого изменения, OnChange — после последнего.
2.2. Элементы управления
Потомком TComponent является класс TControl — элемент управления Windows. Все то, что
видит (или может увидеть) пользователь в клиентской области вашей формы во время выполнения, порождено от класса TControl (клиентская область — вся рабочая поверхность окна Windows, исключая
заголовок, полосу меню и обрамление). Таким образом, потомки TControl являются визуалъньши компонентами. Далее будем называть их, как принято в Windows, элементами управления, или, где это
уместно, просто элементами.
Отметим также подвох, связанный с названием "Библиотека визуальных компонентов" (Visual
Components Library, VCL). Называя так свою библиотеку, разработчики из фирмы Borland были абсолютно правы, так как речь идет в первую очередь о новой — визуальной — технологии программирования. Но нужно помнить, что в VCL входит множество полноправных невизуальных компонентов.
Например, меню — оно, конечно, видимо на экране, но не в клиентской области; поэтому формально
меню не является визуальным компонентом.
Большинство из свойств, которые вы будете видеть и изменять в визуальных компонентах, помещенных в Инспектор объектов, впервые описаны в классе TControl. Этот класс уже обладает "поведением" — в нем предусматривается реакция на основные события.
Объект класса TControl не является окном Windows и в силу этого не может получить фокус
ввода. Однако у него обязательно имеется родительский элемент (см. главу 2, раздел "Наследование.
Методы"), обладающий этим свойством и отвечающий за показ дочернего элемента и обработку некоторых поступающих ему сообщений. (Раз TControl не является окном, то он не имеет контекста
устройства для рисования. Этот контекст обеспечивает ему родитель. Тип родительского элемента —
TWinControl — будет рассмотрен ниже).
Обратите внимание на терминологию: Owner — это владелец компонента, а Parent — его родитель, определяемый свойством property Parent: TWinControl;
Элемент управления может обрабатывать сообщения Windows. Чтобы послать собственной
функции обработки элемента сообщение Windows вида Msg с параметрами WParam и LParam, можно
воспользоваться методом:
function Perform(Msg, WParam: Word; LParam: Longint): Longint;
С каждым элементом управления связан некий текст, который может играть роль заголовка или
редактироваться. В зависимости от роли текст может содержаться в свойствах Caption:
(РЬ) property Caption: TCaption;
TCaption = string[255];
либо Text:
property Text: TCaption;
Не задумываясь о том, в каком именно свойстве содержится текст, получить доступ к нему
можно посредством методов:
function GetTextBuf(Buffer: PChar; BufSize: Integer): Integer;
procedure SetTextBuftBuffer: PChar);
function GetTextLen: Integer;
Все три метода работают, посылая в собственную функцию-обработчик сообщений объекта
(через вызов метода Perform) сообщения Windows вида WM_GETTEXT, WM_SETTEXT и
WM_GETTEXTLENGTH.
Курсор, который будет устанавливаться на компоненте, определен свойством:
property Cursor: TCursor;
13
TCursor = -32768..32767;
В Delphi предопределены стандартные типы курсоров. Их имена: crDefault, crNone, crArrow,
crCross, crIBeam, crSize, crSizeNESW, crSizeNS, crSizeNWSE, crSizeWE, crUpArrow, crHourGlass,
crDrag, crNoDrop, crHSplit, crVSplit, crMultiDrag, crSQLWait.
Этим именам соответствуют константы со значениями от 0 до -17; под этими идентификаторами все курсоры доступны в свойстве Cursors глобального объекта Screen. Можно добавить к ним собственные курсоры, загрузив их из ресурсов и присвоив положительные идентификаторы:
{$R cursors.RES} const Curl = 1; Cur2 = 2;
procedure TFormI. For-mCreate (Sender: TObject);
begin
Screen.Cursors[Curl] := LoadCursor(hinstance, 'CUR_1');
Screen.Cursors[Cur2] := LoadCursor(hinstance, 'CUR_2<);
end;
Познакомиться с имеющимися видами курсоров можно, скомпилировав прилагаемый на дискете пример CURSORS.
У каждого элемента есть два свойства, отражающие его стиль и состояние. Они могут сослужить программисту хорошую службу.
Первый из них — набор флагов, управляющих поведением компонента:
property ControlStyle: TControlStyle;
TControlStyle = set of (csAcceptsControls, csCaptureMouse, csDesignInteractive, csClickEvents,
csFramed, csSetCaption, csOpaque, csDoubleClicks, csFixedWidth, csFixedHeight);
Эти флаги означают, что данный компонент имеет следующие особенности:
с sAccept sControls Может содержать другие (дочерние) элементы управления. Таким свойством обладают не все элементы: обладающие им называются группирующими и рассмотрены в отдельном
разделе.
csCaptureMouse
Может получать сообщения от мыши.
csDesignInteractive Транслирует нажатия правой кнопки мыши в нажатия левой во время разработки.
csFramed
Имеет обрамление (черным прямоугольником единичной толщины).
csSetCaption
Позволяет при изменении имени менять синхронно и свойство Text (если Text не
был явно переустановлен).
Фон элемента непрозрачен. Это означает, что при пересечении нескольких компонентов на экране располо„.:мный под ним виден не будет.
csOpaque
csClickEvents
с sDoubleC1i cks
csFixedWidth,
csFixedHeight
Воспринимает щелчки мышью.
Воспринимает двойные щелчки мышью. Если этот флаг отсутствует, двойные
щелчки воспринимаются как простые.
Имеет фиксированную ширину или высоту соответственно. Она не изменяется при
масштабировании компонента.
Набор флагов, отражающих состояние элемента, описывается свойством:
property ControlState: TControlState;
TControlState = set of (csLButtonDown, csClicked, csPalette, csReadingState, csAlignmentNeeded,
csFocusing, csCreating);
Они означают следующее:
csLButtonDown
Над элементом в данный момент нажата левая кнопка мыши.
csClicked
Если элемент может воспринимать щелчки мышью, этот флаг устанавливается,
пока кнопка мыши находится в нажатом состоянии.
14
csPalette
csReadingState
Элемент поддерживает собственную палитру и должен получать извещения о перерисовке в необходимых случаях.
Элемент в данный момент читается из потока.
сsAlignmentNeeded Элемент требует выравнивания относительно родительского (см. раздел "Положение, размеры и выравнивание элементов управления").
csFocusing
В данный момент происходит получение элементом фокуса ввода.
csCreating
Элемент создается (этот флаг в настоящий момент не задействован).
Свойства ControlStyle и ControlState описаны не в пользовательской документации, а в документации разработчика новых компонентов. Вы можете читать их значения для получения информации об элементе управления, но изменять их стоит только тогда, когда вы полностью отдаете себе отчет в том, к чему это приведет.
Подавляющее большинство элементов управления имеет собственное вспльша-ющее меню, появление которого связано с нажатием правой кнопки мыши. Доступ к нему возможен через свойство:
property PopupMenu: TPopupMenu;
Подробно о его создании и использовании рассказано в разделе, посвященном меню.
2.3. Положение, размеры и выравнивание элементов управления
О каждом визуальном компоненте должно быть известно, где он будет показан и какой будет
иметь размер. Свойство
property BoundsRect: TRect;
определяет прямоугольник, содержащий координаты верхнего левого и правого нижнего углов компонента в системе координат клиентской области родительского элемента. Для формы верхний левый
угол выражен в системе координат экрана. Также можно установить положение и размер компонента,
изменяя координаты верхнего левого угла, длины и ширины методом:
procedure SetBounds(ALeft, АТор, AWidth, AHeight: Integer);
К каждой из этих величин есть и раздельный доступ во время разработки с помощью свойств:
(pb) property Left: Integer;
J property Top: Integer;
property Width: Integer;
(Pb) property Height: Integer;
Другое свойство задает прямоугольник, определяющий положение и размеры клиентской области окна элемента управления:
(Ro) property ClientRect: TRect;
Эта величина доступна только для чтения. Если необходимо переустановить размеры клиентской области, нужно воспользоваться парой свойств:
property ClientHeight: Integer;
property ClientWidth: Integer;
Свойство ClientOrigin задает положение начала клиентской области относительно экрана:
(Ro) property ClientOrigin: TPoint;
Если же нужно связать с координатной системой экрана произвольную точку, пользуйтесь парой методов (не путать с одноименными функциями Windows API):
function ClientToScreen(const Point: TPoint): TPoint;
function ScreenToClient(const Point: TPoint): TPoint;
Очень важную часть работы по управлению размерами и расположением элементов выполняет
свойство:
15
(Pb) property Align: TAlign;
Оно определяет выравнивание компонента относительно границ родителя. Может принимать
одно из предопределенных значений:
TAlign = (aiNone, alTop, alBottom, alLeft, alRight, alClient);
aINone — выравнивание отсутствует;
alTop, alBottom, alLeft, alRight — выравнивание происходит по соответствующей стороне родителя;
alClient — компонент занимает все пространство клиентской области родителя.
Выравнивание гарантирует, что при изменении размеров родителя относительная позиция дочернего элемента не меняется. Это свойство имеет приоритет над простым изменением положения и
размеров. Если новые координаты элемента противоречат способу его выравнивания (например, перемещение вверх при alBottom), изменения отвергаются и элемент возвращается к первоначальным координатам. Свойство Align незаменимо при организации панелей инструментов и строк состояния — они
могут перемещаться и видоизменяться вместе с содержащей их формой. Для временного отключения
действия свойства Align предназначены методы:
procedure DisableAlign;
procedure EnableAlign;
Эти методы управляют возможностью выравнивания потомков данного эле-мекга, они должны
вызьшаться в паре. Для восстановления выравнивания элементов в соответствии с Align есть метод:
procedure Realign;
В следующем примере использование методов DisableAlign и EnableAlign позволяет настроить
выравнивание панели по тому краю формы, на который пользователь перетащит ее мышью:
procedure TFormI.FormCreate(Sender: TObject);
begin
Panell.Align := alBottom;
Moving := False;
end;
procedure TFormI.PanellMouseDown(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X,
Y:
Integer);
begin
if not Moving then
begin
Formi.DisableAlign;
Moving := True;
end;
end;
procedure TFonnl.PanellMouseMove(Sender: TObject;Shift: TShiftState; X, Y: Integers);
begin
if Moving then with Panell do
begin
Left := Left + X - Width div 2;
Top := Top + Y - Height div 2;
Panell.Caption := Format('%d,%d',[Left,Top]);
end;
end;
procedure TFormI.PanellMouseUp(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X, Y:
Integer);
var LastPos : TPoint; rO, rl, r2 : real;
begin
if Moving then
begin
Moving := False;
16
Panell.Caption := '';
LastPos := Point(Panell.Left + X, Panell.Top + Y) ;
if LastPos.X<=0 then LastPos.X := 1;
if LastPos.X>=ClientWidth then LastPos.X := ClientWidth-1;
if LastPos.Y<=0 then LastPos.Y := 1;
if LastPos.Y>=ClientHeight then LastPos.Y := ClientHeight-1;
rO := ClientWidth/ClientHeight;
rl := LastPos.X/LastPos.Y;
r2 := LastPos.X/(ClientHeight - LastPos.Y);
with Panell do if rl < rO then if r2 < rO
then Align := alLeft else
Align := alBottom else if r2 < rO
then Align := alTop else Align := alRight;
Formi.EnableAlign;
end;
end;
2.4. Активность и видимость элементов управления
Активность элемента позволяет ему получать и обрабатывать сообщения от клавиатуры, мыши
и таймера. Она определяется свойством:
(Pb) property Enabled: Boolean;
Значение True делает управляющий элемент активным. При смене состояния Enabled выполняется перерисовка его на экране, при которой пассивные элементы, как правило, изображаются серьм
цветом.
Свойство, определяющее возможность видимости элемента во время исполнения:
(Pb) property Visible: Boolean;
Во время разработки все компоненты являются видимыми. Изменять это свойство непосредственно во время выполнения можно, используя два следующих метода:
procedure Show;
procedure Hide;
Напомним, что, так как Visible является свойством, то выражение visible: =True не является
простым присваиванием. Оно неявно содержит все необходимые операции для показа элемента управления. Это же касается всех остальных свойств компонентов, реакция на изменение которых должна
произойти немедленно.
Почему выше была применена формулировка "возможность видимости", а не "видимость"?
Чтобы элемент был виден на экране, одного значения свойства Visible недостаточно. Нужно, чтобы
видимыми были все предки элемента в иерархии. Реально видимость элемента можно узнать, пользуясь свойством:
(Ro) property Showing: Boolean;
Это свойство устанавливается при изменении свойства Visible. Оно доступно только для чтения. Нужно иметь в вицу, что при изменении видимости родительского элемента Showing не изменяется и может в какой-то момент не соответствовать истинному состоянию. Обновляет состояние свойства
Showing для компонента метод:
procedure UpdateControlState;
Отрисовкой (изменением изображения на экране) элемента управления "заведуют" следующие
методы:
procedure Invalidate;
Отрисовывает компонент, вызывая функцию API InvalidateRect.
procedure update;
Предусматривает дополнительные операции, необходимые Windows при отрисовке окна. Для компонентов-окон, например, в ней вызывается функция UpdateWindow.
17
procedure Repaint;
procedure Refresh;
Этот метод объединяет два предыдущих. Если элемент управления непрозрачный (имеет флаг csOpaque), занимаемый им прямоугольник предварительно
очищается.
Представляет собой вызов Repaint. Рекомендуется для вызова отрисовки.
2.5. Оконные элементы управления
Понятие окна Windows инкапсулировано в потомке TControl — классе TWinControl. Такой
компонент получает соответствующий атрибут _ дескриптор окна, определяемый свойством:
(Ro) property Handle: HWnd;
С помощью этого дескриптора вы можете вызывать функции API Windows, если средств VCL
вам недостаточно для решения задачи. Компоненты-потомки TWinControl — в дальнейшем будем
называть оконными элементами управления, а элементы управления, не имеющие дескриптора окна, —
неоконными.
Возможны ситуации, когда компонент уже создан, но еще не имеет дескриптора как окно. Два
метода управляют созданием дескриптора:
function HandleAllocated:Boolean;
procedure HandleNeeded;
Первая сообщает о наличии выделенного дескриптора, а вторая при его отсутствии посылает
запрос на его выделение. Такой метод должен применяться перед каждой операцией, требующей дескриптора.
Важным свойством TWinControl является то, что он может содержать другие — дочерние —
элементы управления. Они упорядочены в виде списка. Если быть точным, то списков на самом деле
два — для неоконных и оконных дочерних элементов. Но "видны" они как один объединенный — сначала первый, потом второй. Методы и свойства для работы с этим списком приведены в таблице:
(Ro) property Controls[Index: Integer]: Содержит список дочерних элементов.
TControl;
(Ro) property ControlCount: Integer;
Содержит число элементов в списке.
function ContainsControl(Control:
TControl): Boolean;
function ControlAtPos(const Pos:
TPoint; AllowDisabled: Boolean):
TControl ;
Проверяет наличие элемента в списке.
procedure InsertControl(AControl:
TControl) ;
procedure RemoveControl(AControl:
TControl);
procedure Broadcast(var Message);
Вставляет элемент в конец списка.
Отыскивает в списке элемент, которому принадлежит заданная
точка (в системе координат собственной клиентской области).
Флаг AllowDisabled показывает, разрешен ли поиск среди пассивных (свойство Enabled которых равно False) элементов.
Удаляет элемент из списка.
Рассылает всем дочерним элементам из списка сообщение Message.
С каждым оконным компонентом можно связать контекстную помощь. Контекст помощи —
это индекс, указывающий на определенную информацию в файле справочной системы, связанном с
приложением:
property HelpContext: THelpContext;
Когда компонент находится в фокусе, то при нажатии клавиши <F1> загружается система контекстной помощи, и пользователь сразу получает информацию, связанную с заданным контекстом. Если контекст равен нулю, то система пытается отыскать в цепочке родительских компонентов первый,
18
имеющий ненулевой контекст.
Оконный компонент может управлять положением и размерами своих дочерних компонентов.
Прокрутку (скроллинг) элементов на интервал DeltaX, DeltaY осуществляет метод:
procedure ScrollBy(DeltaX, DeltaY: Integer);
Прибегая к вызову этой процедуры, можно при желании осуществить прокрутку нестандартным способом, т. е. без привлечения полос прокрутки. Приведенный ниже фрагмент кода — составная
часть примера IMGSCROL на дискете— позволяет "тащить" изображение Imagel вслед за мышью с
нажатой кнопкой:
type TMouseState = (msNormal, msDragging);
var OldPos, NewPos, MaxShift: TPoint; PMouseState : TMouseState;
procedure TFormI.ScrollMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integers);
begin
MaxShift.X := Imagel.Parent.Width - Imagel.Width;
MaxShift.Y := Imagel.Parent.Height - Imagel.Height;
if (MaxShift.X > 0) and (MaxShift.Y > 0) then Exit;
FMouseState := msDragging;
OldPos := Point(X, Y) ;
Screen.Cursor := crDrag;
end;
procedure TFormI.ScrollMouseMove(Sender : TObject; Shift: TShiftState; X, Y: Integers);
begin
if FMouseState = msDragging then
begin
NewPos := Point(X - OldPos.X, Y - OldPos.Y) ;
if Imagel.Left + NewPos.X > 0 then NewPos.X := - Imagel.Left;
if Imagel.Left + NewPos.X < MaxShift.X
then NewPos.X := MaxShift.X - Imagel.Left;
if Imagel.Top + NewPos.Y > 0 then NewPos.Y := - Imagel.Top;
if Imagel.Top + NewPos.Y < MaxShift.Y
then NewPos.Y := MaxShift.Y - Imagel.Top;
Imagel.Parent.ScrollBy(NewPos.X, NewPos. Y) ;
end;
end;
procedure TFormI.ScrollMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FMouseState -.= msNormal;
Screen.Cursor := crDefault;
end;
Обратите внимание, что прокрутка неоконного компонента Imagel осуществляется посредством
вызова Image l.Parent.ScrollBy. Это свидетельствует о том, что конкретный родительский тип для этого
безразличен. В примере изображение помещено на панель (TPanel). Впрочем, метод ScrollBy используется также и полосами прокрутки, которые есть в компоненте TScrollingWinControl и его потомках,
например, в TForm.
В VCL предусмотрена возможность написания приложений, которые будут сохранять относительный размер и положение при всех разрешениях дисплея. Более подробно эти механизмы описаны в
разделе, посвященном формам; для TWinControl упомянем лишь метод
procedure ScaleBy(M, D: Integer);
который изменяет масштаб элемента управления в M/D раз, при этом верхний левый угол остается неподвижным. Так же изменяются и размеры всех дочерних элементов. Соответственно изменяется и
масштаб шрифта (свойство Font). Флаги csFixedWidth и csFixedHeight в свойстве ControlStyle предот-
19
вращают изменение ширины или высоты.
При изображении большинства оконных элементов управления используют эффект "трехмерности", создающий иллюзию приподнятости или вдавленное™ за счет подбора внешнего вида обрамления. Наличие "трехмерности" задается свойством:
(Рb) property Ctl3D: Boolean;
Нужно уточнить, что это свойство есть не у всех компонентов. Для части компонентов трехмерность реализована средствами VCL; другая же часть (радиокнопки, флажки и др.) требует для создания трехмерного эффекта доступа к библиотеке CTL3DV2.DLL.
Шрифт, которым выводится текст, связанный с элементом управления:
property Font: TFont;
Кисть, используемая для закрашивания рабочей области оконного элемента управления, представлена свойством:
(Ro) property Brush: TBrush;
Она имеет цвет, содержащийся в свойстве Color (по умолчанию clWindow). На уровне TControl
оно доступно только по чтению:
property Color: TColor;
2.6. Реакция на события от мыши и клавиатуры
Традиционно пользователь может предусмотреть реакцию на нажатие и отпускание любой из
кнопок и перемещение курсора мыши. Эти три события обеспечивают интерфейс каждого элемента
управления с мышью. Первые два из них имеют формат:
(р^) property OnMouseDown: TMouseEvent;
(Pb) property OnMouseUp: TMouseEvent;
TMouseEvent = procedure(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer) of object;
Параметры:
Sender — элемент-источник сообщения (обычно равен Self);
Button — идентификатор одной из кнопок;
TMouseButton = (mbLeft, mbRight, mbMiddle);
Shift — множество, которое может содержать элементы:
ssAlt, ssCtrl, ssShift — в зависимости от состояния этих клавиш;
ssLeft, ssRight, ssMiddle, ssDouble — в зависимости от нажатия кнопок мыши (ssDouble —
нажать! и правая, и левая кнопки);
X, Y — координаты нажатия (в системе координат клиентской области получателя).
При перемещении мыши возникает событие:
(Pb) property OnMouseMove: TMouseMoveEvent ;
TMouseMoveEvent = procedure(Sender: TObject; Shift: TShiftState; X, Y: Integer) of object;
Использование сообщений от мьшш уже встречалось в примерах, приведенных вьппе (например, см. разд. "Положение, размеры и выравнивание элементов управления").
Два события извещают о щелчке и двойном щелчке левой кнопкой мыши над компонентом:
(pt) property OnClick: TNotifyEvent;
(Pb) property OnDblClick: TNotifyEvent;
Отменить генерацию этих событий можно, удалив флаг csClickEvents из слова состояния элемента (ControlStyle). Для некоторых компонентов (например, кнопок) OnClick возникает еще и при
нажатии определенных клавиш на клавиатуре, а также вследствие вызова метода Click.
События, связанные с мышью, могут быть получены потомками TControl. В отличие от них, реакцию на события от клавиатуры могут иметь только оконные элементы управления ("могут", т. к. на
уровне TControl и TWinControl эти события только описаны, но не опубликованы). Таким образом, есть
компоненты (в том числе в Палитре компонентов), не имеющие связи с этими событиями из-за ее ненадобности. Впрочем, их меньшинство, и материал в этом разделе обобщен вполне обоснованно.
Нажатие и отпускание клавиш клавиатуры могут инициировать следующие события:
property OnKeyDown: TKeyEvent;
20
property OnKeyUp: TKeyEvent;
eyEvent = procedure(Sender: TObject; var Key: Word;
Shift: TShiftState) of object;
Генерация этих событий встроена в обработчики сообщений Windows WMJCEYDOWN,
WMJSYSKEYDOWN и WM_KEYUP, WM_SYSKEYUP соответственно. Обработчику передаются:
Sender — источник сообщения;
Shift — состояние специальных клавиш и кнопок мыши во время нажатия (отпускания);
Key — код нажатой клавиши, представляющий собой виртуальный код клавиши Windows (константы вида VK_XX, например, VK_F1, VK_ESCAPE и т. п.). Обратите внимание, что Key является
var-параметром; т. е. его значение может быть изменено программистом.
Другое событие, возникающее вследствие нажатия клавиши:
property OnKeyPress :. TKeyPressEvent;
TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;
Это событие возникает при вводе с клавиатуры символа ASCII, т. е. оно не генерируется,
например, при нажатии функциональных клавиш или <CapsLock>. Обработчик события вызывается
при нажатии буквенных (в т. ч. вместе с <Shift>), цифровых клавиш, комбинаций <Ctri>+<A> ..
<Ctd>+<Z> (коды ASCII #1..#26), <Enter>, <Esc>, <Backspace>, <Ctrl>+<Break> (код #3) и некоторых
других. Также код ASCII можно сгенерировать, нажав <А11>+<десятичньш код символа> на числовой
клавиатуре (Numeric Pad).
Событие OnKeyPress соответствует сообщению Windows WM_CHAR.
Все сообщения клавиатуры поступают тому элементу управления, который в данный момент
имеет фокус ввода. Однако из этого правила возможно одно исключение. Если у формы, которая содержит этот элемент управления, свойство
(Pb) property KeyPreview: boolean;
установлено в True, то сначала все три вида сообщений поступают к ее обработчикам, и только потом
— к элементу управления. Если при этом в них обнулить параметр Key, то в элемент сообщение не поступит вообще. В приведенном ниже примере клавиша <F5> резервируется для изменения состояния
формы:
procedure TFormI.FormCreate(Sender: TObject);
begin
KeyPreview := True;
end;
procedure TFonnl.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_F5 then
begin
if ssCtrl in Shift then WindowState := wsNormal
else if Shift = [] then
WindowState := wsMaximized;
Key : = 0 ;
end;
end;
2.8. Фокус ввода
Будучи окнами Windows, TWinControl и его потомки должны управлять фокусом ввода (состоянием, когда они получают и обрабатывают входные сообщения). Они имеют предназначенные для
этого методы:
Поскольку оконные и неоконные элементы управления фактически находятся в разных списках, эти операции касаются только элементов соответствующего списка. Оконные элементы всегда
имеют приоритет над неоконными: вы можете поместить первый над вторым, наоборот — никогда.
21
function Focused: Boolean-
Показывает, имеет ли элемент в данный момент фокус ввода.
function CanFocus: Boolean;
Возвращает True, если оконный элемент может получить фокус ввода
(для этого он и все его родительские оконные элементы управления
должны быть активными (Enabled) и видимыми).
procedure SetFocus;
Запрашивает у родительской формы получение фокуса ввода.
При получении и утере фокуса оконными компонентами происходят события:
(Pb) property OnEnter: TNotifyEvent;
(Р1э) property OnExit: TNotifyEvent;
TNotifyEvent — простейшее событие — извещение, не имеющее параметров. Свойство
(Pb) property TabStop: Boolean;
показывает, есть ли на данном элементе табулостоп. Между элементами формы, у которых TabStop
установлено в Тше, можно передвигаться (перемещать фокус ввода) нажатиями клавиш
<Tab>/<Shift>+<Tab>. Очередность перехода фокуса определяется специальным списком. Положение
элемента управления в родительском списке табулостопов определяется при помощи свойства:
(Pb) property TabOrder: TTabOrder;
TTabOrder = -1..32767;
При разработке формы номера присваиваются последовательно в порядке добавления компонентов, начиная с 0. Нулевой компонент первым получит фокус при активизации. Программист может
менять значение этого свойства, не заботясь об учете других элементов, т. к. перенумерация производится автоматически. При отсутствии табулостопа на элементе его свойство TabOrder равно -1.
Можно получить и весь список, содержащий иерархию дочерних элементов, имеющих табулостоп:
procedure GetTabOrderList(List: TList) ;
Этот метод добавляет в уже существующий список List все дочерние компоненты, имеющие
табулостоп; каждый из них при добавлении делает то же самое. Таким образом, элементы списка имеют тип TWinControl.
Два метода предназначены для управления показом перекрывающихся дочерних элементов
управления:
procedure BringToFront;
procedure SendToBack;
Метод BringToFront переносит элемент в конец родительского списка (визуализация начинается с первого) и показывает его поверх остальных. SendToBack, наоборот, переносит элемент в начало и
таким образом сверху окажутся все перекрывающиеся с ним. При этом элемент теряет фокус ввода
(если имел).
Класс инкапсулирует шрифт Windows. В Delphi допускаются только горизонтально расположенные шрифты. В конструкторе объекта по умолчанию принимается шрифт System цвета clWindowText размера 10 пунктов.
Привязка к родительским свойствам
Для придания приложению строгого внешнего вида требуется, чтобы все визуальные компоненты имели единый стиль. Чтобы избавиться от необходимости изменять код элементов-потомков
при изменении внешнего вида предка, в них нужно правильно установить свойства:
(PDI property ParentColor: boolean;
@ property ParentCtl3D: boolean;
(Pb) property ParentFont: boolean;
(pS) property ParentShowHint: boolean;
Все четыре свойства отвечают за наличие связи между соответствующими свойствами в родительских и дочерних элементах. Если какое-то из них у элемента установлено в True, это означает, что
22
он наследует свойство от родителя и меняет цвет, трехмерность, шрифт или показ ярлычков вместе и
вслед за ним. Но если само свойство дочернего элемента (например, Color или ParentColor) переустановить явно, то связь с родительским свойством разрьюается.
2.8. Графическая подсистема
Разработчики Delphi уделили большое внимание возможностям работы с деловой графикой:
простота и удобство ее использования напрямую сказывается на простоте и удобстве созданных приложений. Вместо дебрей графического интерфейса Windows разработчик получил несколько инструментов, сколь понятных, столь же и мощных.
Другой бич работы с графикой в Windows — проблема рационального использования ресурсов.
Для больших программных комплексов она стала в последнее время нешуточной (это касается, по
крайней мере, версий Windows до 3.11 включительно). Такие монстры, как Microsoft Office, потребляют во время работы львиную долю ресурсов. В Delphi ресурсами GDI незримо для пользователя "заведуют" специальные менеджеры, ведущие списки всех размещенных кистей, перьев, шрифтов и т. п. и
управляющие их использованием.
Обязательным для любого объекта, связанного с графикой в Delphi является событие
property OnChange: TNotifyEvent;
Его обработчик вызывается всякий раз, когда изменились какие-то характеристики объекта,
влияющие на его внешний вид.
В стандартном GDI основой для рисования служит дескриптор контекста устройства hDC и
связанные с ним шрифт, перо и кисть. В состав VCL входят объектно-ориентированные надстройки
над последними, назначением которых является удобный доступ к свойствам инструментов и прозрачная для пользователя обработка всех их изменений. Сначала опишем три этих класса.
Класс TCanvas
TCanvas = class(TPersistent)
Этот класс — сердцевина графической подсистемы Delphi. Он объединяет в себе и "холст"
(контекст конкретного устройства GDI), и "рабочие инструменты" (перо, кисть, шрифт) и даже "подмастерьев" (набор функций по рисованию типовых геометрических фигур).
В дальнейшем для обозначения этого класса мы будем пользоваться термином "канва". Сознавая неоднозначность такого перевода, авторы тем не менее считают, что у него наилучшие шансы прижиться.
Канва не является компонентом, но она входит в качестве свойства во многие другие компоненты, которые должны уметь нарисовать себя и отобразить какую-либо информацию.
Дескриптор контекста устройства, над которьм "построена" канва, может быть востребован для
различных низкоуровневых операций. Он задается свойством:
property Handle: HDC;
Для рисования канва включает в себя шрифт, перо и кисть:
(р^) property Font: TFont ;
(Pt^ property Pen: TPen;
(РЙ property Brush: TBrush;
Кроме того, можно рисовать и поточечно, получив доступ к каждому пикселу. Значение свойства
property Pixels[X, Y: Integer]: TColor;
соответствует цвету точки с координатами (X,Y).
Канва содержит методы-надстройки над всеми основными функциями рисования GDI Windows
и свойства, которые приведены ниже в таблице. При их рассмотрении имейте в виду, что все геометрические фигуры рисуются текущим пером. Те из них, которые можно закрашивать, закрашиваются с
23
помощью текущей кисти. Кисть и перо при этом имеют текущий цвет.
procedure Arc (XI, Yl, Х2, Y2, ХЗ, Y3, Метод рисует сегмент эллипса. Эллипс определяется описываХ4, Y4: Integer) ;
ющим прямоугольником (X1,Y1) — (X2,Y2); его размеры должны лежать в диапазоне от 2 до 32767 точек. Начальная точка
сегмента лежит на пересечении эллипса и луча, проведенного из
его центра через точку (X3.Y3). Конечная точка сегмента лежит
на пересечении эллипса и луча, проведенного из его центра через точку (X4.Y4). Сегмент рисуется против часовой стрелки.
procedure Chord(Xl, Yl, Х2, Y2, ХЗ,
Y3, Х4, Y4: Integer);
Рисует хорду и заливает отсекаемую ею часть эллипса. Эллипс,
начальная и конечная точки определяются, как в методе Arc.
procedure EllipsefXl, Yl, Х2, Y2: Inte- Рисует и закрашивает эллипс, вписанный в прямоугольник
ger) ;
(X1.Y1) — (X2.Y2).
procedure LineTo(X, Y: Integer);
Проводит линию текущим пером из текущей точки в (X,Y).
Перемещает текущее положение пера (свойство PenPos) в точку
(X,Y).
procedure BrushCopy(const Dest:
Производит специальное копирование. Прямоугольник Source из
TRect; Bitmap: TBitmap; const Source: битовой карты Bitmap копируется в прямоугольник Dest на канTRect; Color: TColor);
ве; при этом цвет Color заменяется на цвет текущей кисти
(Brush.Color). С помощью этого метода можно нарисовать "прозрачную" картинку. Для этого нужно выбрать соответствующий
фону цвет кисти и затем заменить на него фоновый или наиболее
часто встречающийся цвет битовой карты (см. Bitmap. TransparentColor).
procedure MoveTo(X, Y: Integer);
procedure CopyRect(const Dest: TRect; Производит копирование прямоугольника Source из канвы CanCanvas: TCanvas; const Source: TRect) vas в прямоугольник Dest в области самого объекта.
;
procedure FillRect(const Rect: TRect) ; Производит заливку прямоугольника (текущей кистью).
procedure FrameRectfconst Rect:
TRect);
procedure Draw(X, Y: Integer; Graphic:
TGraphic) ;
Производит оконтуривание прямоугольника цветом текущей кисти (без заполнения).
Осуществляет рисование графического объекта Graphic (точнее,
вызов метода его рисования) в области с верхним левым углом
(X,Y).
procedure StretchDraw(const Rect:
TRect; Graphic: TGraphic);
Осуществляет рисование объекта Graphic в заданном прямоугольнике Rect. Если размеры их не совпадают, Graphic масштабируется.
procedure DrawFocusRect(const Rect:
TRect);
Производит отрисовку прямоугольной рамки из точек (как на
элементе, имеющем фокус ввода). Поскольку метод использует
логическую операцию XOR (исключающее ИЛИ), повторный
вызов для того же прямоугольника приводит изображение к
начальному виду.
24
procedure FloodFilKX, Y: Integer; Col- Производит заливку области текущей кистью. Процесс начинаor: TColor; FillStyle: TFillStyle); TFill- ется с точки (X,Y). Если режим FillStyle равен fsSurface, то он
Style = (fsSurface, fsBorder) ;
продолжается до тех пор, пока есть соседние точки с цветом
Color. В режиме fsBorder закрашивание, наоборот, прекращается
при выходе на границу с цветом Color.
procedure Pie (XI, Yl, Х2, Y2, ХЗ, Y3, Рисует сектор эллипса, описываемого прямоугольником (X1,Y1)
Х4, Y4: Integers— (X2,Y2). Стороны сектора лежат на лучах, проходящих из
центра эллипса через точки (X3.Y3) и (X4,Y4).
procedure Polygon(const Points: array
of TPoint) ;
Строит многоугольник, используя массив координат точек
Points. При этом последняя точка соединяется с первой и внутренняя область закрашивается.
procedure Polyline(const Points: array
of TPoint) ;
procedure Rectangle(XI, Yl, Х2, Y2 :
Integer) ;
procedure RoundRect (XI, Yl, Х2, Y2,
ХЗ, Y3: Integer);
Строит ломаную линию, используя массив координат точек
Points.
Рисует прямоугольник с верхним левым углом в (XI ,Y1) и нижним правым в (X2.Y2).
Рисует прямоугольник с закругленными углами. Координаты
вершин — те же, что и в методе Rectangle. Закругления рисуются
как сегменты эллипса с размерами осей по горизонтали и вертикали ХЗ и Y3.
function. TextHeight(const Text:
string): Integer;
function TextWidth(const Text: string):
Integer;
procedure TextOut(X, Y: Integer; const
Text: string);
Возвращает высоту строки Text в пикселах.
Возвращает ширину строки Text в пикселах.
Производит вывод строки Text. Левый верхний угол помещается
в точку канвы (X,Y).
procedure TextRect(Rect: TRect; X, Y: Производит вывод текста с отсечением. Как и в TextOut, строка
Integer; const Text: stringi ;
Text выводится с позиции (X,Y); при этом часть текста, лежащая
вне пределов прямоугольника Rect, отсекается и не будет видна.
(Ro) property ClipRect: TRect;
Определяет область отсечения канвы. То, что при рисовании попадает за пределы этого прямоугольника, не будет изображено.
Свойство доступно только для чтения — его значение переустанавливается системой в контексте устройства канвы.
property PenPos: TPoint;
Содержит текущую позицию пера канвы (изменяется посредством метода MoveTo).
Метод
procedure Refresh;
сбрасывает текущие шрифт, перо и кисть, заменяя их на стандартные, заимствованные из Windows (BLACK.PEN, HOLLOW_BRUSH, SYSTEM.FONT).
Предусмотрено два события для пользовательской реакции на изменение канвы:
property OnChange: TNotifyEvent;
property OnChanging: TNotifyEvent;
25
Эти события возникают при изменениях свойств и вызовах методов TCanvas, меняющих вид
канвы (то есть при любом рисовании. В MoveTo, например, они не возникают). Отличие их в том, что
OnChanging вызывается до начала изменений, a OnChange — после их завершения.
Идентификатор (код) растровой операции при копировании прямоугольных блоков содержится
в свойстве
(Pb) property CopyMode: TCopyMode;
TCopyMode = Longint;
и определяет правило сочетания пикселов, копируемых на канву, с ее текущим содержимым.
При этом можно создавать разные изобразительные эффекты. В Delphi определены следующие константы кодов: cmBlackness, cmDstInvert, cmMergeCopy, cmMergePaint, cmNotSrcCopy, cmNotSrcErase,
cmPatCopy, cmPatInvert, cmPatPaint, cmSrcAnd, cmSrcCopy, cmSrcErase, cmSrcInvert, cmSrcPaint,
cmWhiteness.
Все они стандартно определены в Windows, и подробное их описание можно найти в документации по GDI. Значением CopyMode по умолчанию является cmSrcCopy — копирование пикселов источника поверх существующих.
Использование графики иллюстрируют несколько примеров, имеющихся на дискете, прилагаемой к книге. Обратите внимание на пример MOVLINES — в нем показано, как создавать и использовать канву для всего экрана. Помимо графических примитивов, таких как линии и фигуры, на канве
можно разместить готовые изображения. Для их описания создан класс TGraphic.
3. РАБОТА С КОМПОНЕНТАМИ
После знакомства с общими принципами работы компонентов перейдем к их предметному рассмотрению. В этом разделе описаны все элементы Палитры компонентов, сгруппированные по функциональному назначению. Вы встретите здесь и компоненты, не входящие в Палитру; как правило, они
являются предками других компонентов, важными для понимания.
Для каждого из компонентов приводятся все методы и свойства, которые доступны программисту, работающему с этим компонентом, то есть описанные как public и published (в т. ч. недокументированные). Мы попытались проиллюстрировать некоторые неочевидные вещи хотя бы коротким фрагментом кода. В развернутом виде примеры использования компонентов можно найти на дискете, прилагаемой к книге.
3.1. Работа с меню
В приложениях, написанных на Delphi, могут быть реализованы меню двух основных видов:
• Главное меню. Такое меню принадлежит форме и отображается вместе с ней под ее панелью
заголовка. Если в приложении несколько форм, то для удобства можно объединять меню всех активных форм в одном.
• Всплывающее меню. Такое меню предусмотрено почти у всех компонентов — элементов
управления Windows. Оно возникает (всплывает) при нажатии правой кнопки мыши на этом компоненте. Предусмотрено такое меню и у формы.
Меню являются иерархическими структурами, состоящими из пунктов меню. Каждый пункт
может быть выбран. Это может произойти вследствие щелчка кнопкой мыши, нажатия соответствующих клавиш на клавиатуре или вызова процедуры в программе.
На нижнем уровне лежат команды меню — пункты, с выбором которых должна быть связана та
или иная реакция приложения. Команда! объединяются в подменю. Подменю — это пункты, выбор
которых означает показ или свертывание списка входящих в него команд и подменю.
Принципы создания и работы с меню в Delphi очень просты. Каждому пункту меню соответствует свой компонент класса TMenuItem. Вы добавляете к меню новые пункты (а к форме — новые
компоненты) либо во время разработки (при помощи Конструктора меню), либо во время исполнения.
При выборе пункта меню для описывающего его компонента инициируется событие OnClick, в обработчике которого и нужно предусмотреть соответствующие действия.
26
Компонент TMenuItem
TObject—>TPersistent—”TComponent—>TMenuItein
Модуль MENUS
В Палитру компонентов не входит
Этот компонент, который является основой системы меню в Delphi, вы не встретите в Палитре
компонентов — он входит в состав имеющихся там компонентов TMainMenu и TPopupMenu.
Текст, содержащийся в пункте меню, определяется свойством:
(Pb) property Caption: string;
Помимо основной, он несет еще две дополнительные нагрузки. Во-первых, если в строке имеется амперсанд ('&'), то он вместе с следующим за ним символом является акселератором. Например,
для строки '&File' нажатие <Alt>+<F> означает выбор этого пункта. Во-вторых, если текст состоит из
одного символа '-', то этот пункт служит в меню разделителем (имеет стиль MFJSEPARATOR);
Помимо акселератора может быть описана еще одна комбинация клавиш, нажатие которой равнозначно выбору пункта. Она должна содержаться в свойстве: •
(Pb) property Shortcut: TShortCut;
TShortCut = Low(Word)..High(Word);
Способы выбора пункта меню — щелчок мышью, нажатие <Enter> на сфокусированном пункте, нажатие акселератора или горячей комбинации, наконец, вызов метода
procedure Click;
приводят к возникновению события:
(Р1э) property OnClick: TNotifyEvent;
Компонент TMenuItem может различать только это событие, с которьм должны быть связаны
действия, ради которых вы ввели данный пункт в меню. Воспринимают это событие те пункты меню,
которые в данный момент активны, что означает-установленное в True свойство:
(РЬ) property Enabled: Boolean;
Если Enabled равно False, пункт изображается серьм цветом и не воспринимает сообщений.
Часто пункты меню используются для переключения каких-либо режимов работы программы.
При этом они могут быть отмечены "галочкой" перед началом текста. Свойство
(Pb) property Checked: Boolean;
отвечает за то, является ли этот пункт отмеченным.
Если в меню слишком много команд, то их расположение одна под другой может вызвать серьезные неудобства для пользователя. Свойство
(Pb) property Break: TMenuBreak;
TMenuBreak = (mbNone, mbBreak, mbBarBreak) ;
призвано решить эту проблему. Если оно установлено в mbBreak, то команды меню, начиная с
этой, располагаются в новом — соседнем с прежним — столбце (при этом их принадлежность не изменяется). Если оно равно mbBarBreak, столбцы при этом разделяются вертикальной чертой. В Delphi все
компоненты меню типа TMenuItem могут быть как простыми командами, так и подменю, имеющими
собственный список пунктов. В случае, если у компонента дочерних подменю и пунктов нет, для него
имеет смысл свойство:
(Ro) property Command: Word;
Прежде при написании меню нужно было выдумывать и присваивать уникальный идентификатор каждому его пункту — то есть собственно код команды, который посылался в качестве параметра
сообщения WM.COMMAND. Сейчас эта задача возложена на Delphi — программист не только не
определяет, но может и не знать кода команды, связанного с этим пунктом меню. Система выберет
уникальное значение автоматически и присвоит это значение свойству Command. Изменять его не разрешается.
Интерпретация сообщений меню скрыта от программиста. Система сама определяет, какой
пункт выбран, и вызывает его обработчик события OnClick.
Если для каких-то целей понадобился код команды, можно воспользоваться вышеуказанным
27
свойством (см. пример в описании компонента TMenu).
Если у компонента TMenuItem имеются дочерние пункты, то он является подменю, имеет статус MF_POPUP и дескриптор меню Windows, доступный для чтения через свойство:
(RO) property Handle: HMENU;
Пункты меню иерархически связаны. Методы и свойства для работы с дочерними пунктами
меню приведены в таблице.
(До) property Parent: TMenuItem;
Содержит указатель на родительское подменю.
(Ro) property I terns[Index: Integer]:
TMenuItem;
fRo) property Count: Integer;
Содержит список дочерних пунктов меню.
procedure Insert(Index: Integer; Item:
TMenuItem) ;
Вставляет пункт Item в меню на место Index. Поскольку структура меню строго иерархическая, вставляемый пункт не должен
быть частью другого меню (его свойство Parent должно быть
равно nil).
procedure Delete(Index: Integer) ;
Удаляет пункт с индексом Index из меню.
function IndexOf(Item: TMenuItem):
Integer;
procedure Add(Item: TMenuItem);
Возвращает индекс пункта Item.
Содержит количество дочерних пунктов меню.
Добавляет пункт Item в конец меню.
procedure Remove(Item: TMenuItem); Удаляет пункт Item из меню.
Если пункт меню находится в фокусе, нажатие <F1> вызовет систему помощи с контекстом,
определенным в свойстве:
(Pb) property HelpContext: THelpContext;
Свойство
(Pb) property Grouplndex: Byte;
используется при объединении нескольких меню между собой. Подробное объяснение его
назначения см. в описании компонента TMainMenu.
Компонент TMenu
TObject—”TPersistent->TComponent->TMenu
Модуль MENUS
В Палитру компонентов не входит
Этот компонент отсутствует в Палитре компонентов, но содержит методы и свойства, приведенные в таблице, общие для двух потомков — TMainMenu и TPopupMenu, которые туда входят.
(Ro) property Handle: HMENU;
Содержит дескриптор меню.
property WindowHandle: HWND;
Содержит дескриптор окна Windows (формы или оконного элемента управления), с которым связано меню.
(Ro) property Items: TMenuItem;
Содержит список элементов меню. На самом верху иерархии меню
есть единственный элемент (со статусом MFPOPUP), чей список и
используется.
function DispatchCoinn>and(ACommand: Word):
Boolean;
function DispatchPopupfAHandle:
HMENU): Boolean;
Отыскивает пункт меню с заданной командой, в случае успеха вызывает его метод Click и возвращает True.
Работает как Di spatchCommand, но отыскивает пункт меню с дескриптором AHandle.
28
function Findltem(Value: Word;
Kind: TFindItemKind): TMenuItem;
TFindItemKind = (fkComrriand,
fkHandle, fkShortCut);
Возвращает указатель на объект, соответствующий заданному
пункту меню. Параметр Value должен содержать величину, которая
интерпретируется в соответствии с одним из трех способов поиска
(по команде, дескриптору или комбинации горячих клавиш).
function GetHelpContext(Value:
Word; ByComniand: Boolean) :
THelpContext;
Возвращает значение контекста помощи элемента меню. Если параметр ByCommand установлен в True, Value содержит связанную с
пунктом команду, в противном случае — дескриптор. Если контекст у пункта отсутствует (равен 0), то отыскивается ближайший
родительский ненулевой контекст. Если и он не найден, возвращается 0.
function IsShortCut(var Message:
TWMKey): Boolean;
Определяет, какая комбинация горячих клавиш ShortCut нажата, и
отыскивает соответствующий пункт меню. Если пункт с таким значением ShortCut найден, и его метод Click отработал успешно, метод возвращает True.
В следующем примере метод обрабатывает сообщение Windows WM_MENUSELECT, которое
возникает при перемещении фокуса между пунктами меню. В зависимости от типа пункта показывается его дескриптор или команда:
procedure TFormI.wmMenuSelect(var Msg :TWMMenuSelect) ;
var Anitem : TMenuItem;
begin
with Msg do
begin
if (MenuFlag and MF_POPUP <>0 )
then
begin
Anitem := Formi.Menu.Findltem(Idltem, fkHandle);
if Anitemonil then Labell .Caption :=
'Handle:'+IntToHex(Anitem.Handle,4) ;
end
else
begin
Anitem := Formi.Menu.Findltem(Idltem, fkCommand);
if Anitemonil then Labell .Caption :=
'Command:'+IntToHex(Anitem.Command,4) ;
end;
end;
inherited;
end;
Компонент TMainMenu
TObj ect—”TPersi stent-”TCornponent->TMenu->TMainMenu
Модуль MENUS
Страница Палитры компонентов Standard
Этот компонент представляет главное меню формы и наследует все методы и свойства TMenu.
Особенность его в том, что в нем реализован сложный механизм объединения меню. Это необходимо
по следующим причинам:
• Если в приложении имеется несколько форм со своими меню, то для упрощения работы целесообразно соединить их в одно и управлять меню из главной формы.
• Объединение меню нужно при работе с интерфейсом MDI и его подокнами.
29
• Механизм объединения меню используется серверами OLE, запускаемыми по месту нахождения объекта OLE. Загружаясь, сервер дописывает осуществляемые им операции к меню другого приложения.
Для того чтобы реализовать объединение меню, у тех форм, меню которых будут присоединены к главному, установите в True свойство:
(Р1э) property AutoMerge: Boolean;
При этом у главного меню оно должно оставаться равным False, иначе главное меню будет вообще невидимым. Объединение будет происходить автоматически при активизации новых форм или
серверов OLE. Кроме автоматического режима, объединение меню можно выполнить при вызове метода:
procedure Merge(Menu: TMainMenu);
Присоединяемое меню при необходимости может быть легко отсоединено вызовом метода:
procedure Unmerge(Menu: TMainMenu) ;
При установленном в True свойстве AutoMerge ссылка на присоединенное меню будет сохраняться в специальном поле компонента и отсоединяться в нужных случаях автоматически (например,
при закрытии формы, которой оно принадлежит).
Объединение меню происходит по специальным правилам, в основе которых лежит использование группового индекса (свойства Group Index) каждого объекта TMenuItem.
У пунктов меню одного уровня, в частности всех подменю верхнего уровня в главном меню,
свойство Grouplndex является неубывающим, т. е. у последующего пункта групповой индекс больше
либо равен индексу предыдущего. Это требование отслеживается как на этапе разработки, так и на этапе исполнения. Например, пусть пункты меню имеют индексы О, 3, 4, 5, 6. Если вы включите пункт
меню с индексом 5 между пунктами с индексами 0 и 3, то 3
и 4 будут изменены на 5. А вот изменить большее значение Х на меньшее Y, если впереди есть
пункты с индексом, большим Y, невозможно. Если в этом примере попытаться изменить индекс 6 на 4,
то это приведет к возникновению исключительной ситуации EMenuError.
Для обычных форм объединение происходит только на верхнем уровне в главном меню во время их активизации. В объединенном меню все подменю будут располагаться по возрастанию номера
группового индекса, при этом:
• если в присоединяемом меню есть пункты с таким же групповым индексом, что и в исходном,
то все их множество заменяет все множество таких пунктов в исходном меню;
• все пункты присоединяемого меню, групповой индекс которых не встречается в исходном,
добавляются к нему и вставляются на соответствующие их индексу места.
К окнам интерфейса MDI все сказанное относится только при запуске приложения. Если в
формах приложения со стилем fsMDIChild есть свои главные меню, то в этот момент они автоматически сольются с главным меню формы fsMDIForm независимо от состояния AutoMerge.
На уровне работы с серверами OLE предусмотрены дополнительные возможности по объединению меню. Если в компонент TOLEContainer загружен объект OLE, то в конец подменю Edit обычно
добавляется подменю, из которого можно вызвать функции открытия и редактирования этого объекта.
После активизации сервера он может не только вставлять свои подменю в главное, но и добавлять новые пункты к уже существующим подменю.
Три метода TMainMenu используются для работы с меню OLE:
procedure PopulateOle2Menu(SharedMenu: HMenu; Groups:array of Integer; var Widths: array of
Longint);
procedure GetOle2AcceleratorTable(var hAccel : THandle;
var numAccels: Word; Groups: array of Integer) ;
procedure Set01e2MenuHandle(Handle: HMENU);
Конструктор меню Delphi поможет значительно упростить разработку меню. В нем имеются
готовые шаблоны типовых подменю верхнего уровня: File, Edit и др; пример их использования см. в
проекте DEMOEDIT на дискете, прилагаемой к книге. Можно также определить свои шаблоны.
Компонент TPopupMenu
30
TObject-”TPersistent—”TComponent->TMenu->TPopupMenu
Модуль MENUS
Страница Палитры компонентов Standard
Этот компонент описывает всплывающее меню. В отличие от главного, собственное меню такого типа может быть почти у каждого оконного элемента управления на форме (кроме переключателей), а также у самой формы.
Всплывающее меню обычно связывают с нажатием правой кнопки мьппи. Чтобы это правило
соблюдалось, нужно установить в True свойство
(Pb) property AutoPopup: Boolean;
Для вызова этого меню из программы используйте метод:
procedure Popup(X, Y: Integer);
который показывает его, помещая в точку экрана (X,Y) точку панели меню, определенную
свойством:
(Pb) property Alignment: TPopupAlignment;
TPopupAlignment = (paLeft, paRight, paCenter) ;
В зависимости от его значения в точке щелчка появится:
paLeft — левый верхний угол;
paRight — правый верхний угол;
paCenter — середина верхней стороны.
Разумеется, если нажатие произошло в самом низу экрана и развернуться вниз невозможно, меню автоматически будет сдвинуто вверх.
При вызове всплывающего меню перед его показом на экране программист извещается событием:
(Pb) property OnPopup: TNotifyEvent;
Одно и то же всплывающее меню могут разделять несколько компонентов. Свойство
property PopupComponent: TComponent ;
показывает, который из них инициировал меню (на котором был щелчок правой кнопки мыши).
Если меню вызвано из метода Popup, значение этого свойства не определено, и присвоить ему значение
должен сам программист.
Контекст помощи всплывающего меню задается свойством:
(Pb) property HelpContext: THelpContext;
Функции для работы с меню
Рассмотрим также описанные в модуле Menus полезные функции для управления меню.
Четыре функции предназначены для преобразования типа TShortCut, представляющего горячую комбинацию клавиш:
а) в символ+состояние специальных клавиш и обратно:
procedure ShortCutToKey(Shortcut: TShortCut; var Key: Word;
var Shift: TShiftState);
function Shortcut(Key: Word; Shift: TShiftState): TShortCut;
б) в текст и обратно:
function ShortCutToText(Shortcut: TShortCut): string;
function TextToShortCut(Text: string): TShortCut;
Новые меню и их составные части можно создавать, пользуясь функциями:
function NewMenu(Owner: TCompo- Создает новое главное меню с именем AName и пунктами Items,
nent; const AName: string; Items: array которое будет принадлежать владельцу Owner.
of TMenuItem): TMairiMenu;
function NewPopupMenu(Owner:
TCoiriponent; const AName: string;
Alignment: TPopupAlignment; AutoPopup: Boolean; Items: array of
TMenuItem): TPopupMenu;
Создает новое всплывающее меню. Назначение дополнительных
параметров см. описание TPopupMenu.
31
function NewSubMenu(const ACaption:
string; hCtx: Word; const AName:
string; Items: array of TMenuItem):
TMenuItem;
function Newltemfconst ACaption:
string; AShortCut: TShortCut;
AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent; hCtx: Word; const
AName: string):
function NewLine: TMenuItem;
Создает новое подменю. Здесь AName — имя объекта, ACaption
— его текст, hCtx — контекст системы помощи.
Создает новый объект типа TMenuItem. Параметры метода соответствуют свойствам класса.
Создает новый элемент меню типа разделитель (TMenuItem с
Caption = '-').
Все функции в случае успешного завершения возвращают указатель на созданный объект.
3.2. Работа с кнопками
Группа элементов управления-кнопок в VCL велика и разнообразна. Здесь и стандартные кнопки Windows, и популярные в последнее время кнопки с картинками, и специальные кнопки для работы
в модальных формах (диалогах), и даже группы кнопок. Многие из них имеют одинаковые свойства,
которые мы и рассмотрим сначала.
Основным событием, связанным с нажатием кнопки (любым способом) является:
(Pb) property OnClick: TNotifyEvent;
Под "любым способом" здесь подразумевается щелчок мышью, нажатие комбинации клавиши
— акселератора, нажатие <Enter> или <Esc> (только для некоторых кнопок) или вызов метода Click из
программы.
Текст кнопки, появляющийся на ее поверхности, определен в свойстве:
(Pb) property Caption: string;
Если в составе текста есть амперсанд ('&'), то следующий за ним символ используется в акселераторе, и нажатие комбинации клавиш <АН>+<символ> вызывает нажатие кнопки.
Водораздел среди кнопок проходит по тому, что именно означает нажатие. Ряд кнопок — TRadioButton, TCheckBox и группа TRadioGroup — предназначен для ввода или переключения фиксированных параметров, и программиста более интересует их состояние в некоторый момент времени, чем
сам факт нажатия. Кнопки TButton, TSpinButton и TBitBtn напротив, как правило, своим нажатием
инициируют немедленные действия. Кнопка TSpeedButton может успешно служить для решения обеих
задач.
Как уже было сказано, нажатие влечет за собой OnClick. Но это не означает, что всегда нужно
писать код для обработки всех нажатий. Например, исключением из этого правила является использование кнопок в модальных формах.
Модальная форма, или модальный диалог — специальная разновидность окон Windows (и форм
Delphi), предназначенная для ввода пользователем необходимых программе данных или выбора одного
ответа из нескольких вариантов. Обычно при этом ему предоставляется несколько кнопок, соответствующих вариантам. Вплоть до получения ответа в модальном диалоге пользователь не может переключиться на другие окна той же задачи, а в системном модальном диалоге — и на другие задачи. Для
использования в таких случаях предназначены стандартная кнопка Windows TButton и ее младшая
сестра TBitBtn, отличающаяся только наличием картинки на ее поверхности. При их нажатии значение
свойства ModalResult кнопки передается одноименному свойству формы, где такое изменение означает
вывод формы из модального состояния и ее закрытие:
(Pb) property ModalResult: TModalResult;
TModalResult = Low(Integer) ..High(Integer);
В модальной форме могут быть предусмотрены две специальные кнопки, соответствующие положительному и отрицательному решениям. Одна из них срабатывает при нажатии на клавиатуре <Enter>, другая — <Esc>. Любая кнопка может получить такой статус, установив в True соответственно
32
одно из свойств:
(Pb) property Default: Boolean;
(Pb) property Cancel: Boolean;
У двух рассмотренных кнопок результат, который при нажатии кнопки передается модальной
форме, будет соответственно равен mrOk и mrCancel.
Ниже рассмотрим имеющиеся в распоряжении программиста варианты кнопок. Описанные
выше свойства перечисляются, но не комментируются.
Компонент TButton
TObject-”TPersistent->TCornponent->TControl->TWinControl->
-*TButtonControl—>TButton
Модуль STDCTRLS
Страница Палитры компонентов Standard
Обычная кнопка Windows. В этом компоненте опубликованы только приведенные выше свойства Default, Cancel, Caption, ModalResult и OnClick. Двойной щелчок на кнопке не предусмотрен.
Компонент TRadioGroup
TObject->TPersistent->TComponent-”TControl-”TWinControl-”TCustomControl->
-”TCustomGroupBox—”TCustomRadioGroup-”TRadioGroup
Модуль EXTCTRLS
Страница Палитры компонентов Standard
Готовая группа радиокнопок, содержащая все средства для управления ими. Каждая радиокнопка в группе наследует все свойства TRadioButton. Радиокнопки могут располагаться в несколько
столбцов.
Свойство
(Pb) property Columns: Integer;
устанавливает число столбцов с радиокнопками. Оно не должно превышать 16. Индекс нажатой
радиокнопки в группе определяется свойством:
(Pb) property Itemlndex: Integer;
Индекс исчисляется от 0. Если он равен -1, mi одна радиокнопка в группе не нажата.
Набор строк с заголовками радиокнопок содержится в свойстве:
(Pb) property Items: TStrings;
Все изменения этого свойства — добавление, удаление, переименование и т. п. — немедленно
отражаются на радиокнопках в составе группы. Но доступ к методам и свойствам каждой радиокнопки
пользователь компонента получить не может, т. к. сами объекты-радиокнопки содержатся в отдельном
скрытом списке.
Компонент TSpinButton
TObject->TPersistent—”TComponent-”'TControl-”TWinControl-”TSpeenButton
Модуль SPIN
Страница Палитры компонентов Samples
Пара кнопок с двумя противоположно направленными стрелками, предназначенная для увеличения или уменьшения какой-то величины нажатием. Компонент не имеет своего заголовка.
Рисунки на кнопках по умолчанию представляют собой треугольники, указывающие вверх и
вниз. Их можно изменить, используя свойства:
(Pb) property DownGlyph: TBitmap;
(Pb) property UpGlyph: TBitmap;
Кнопка не имеет события OnClick. При нажатии нижней и верхней кнопок соответственно возникают события:
J property OnDownClick: TNotifyEvent;
33
property OnUpClick: TNotifyEvent;
Этот компонент может работать в паре с другими, например, редактором (см. компонент
TSpinEdit). В этом случае, получая фокус, он передает его "напарнику", указатель на который содержится в свойстве:
(Pb) property FocusControl: TWinControl;
3.3. Ввод и редактирование текста
В Палитру компонентов входят три компонента, позволяющие вводить и редактировать текст
(далее — редактирующие элементы). На базе стандартного редактирующего элемента управления
Windows построены два основных компонента — строка ввода TEdit и многострочный редактор
TMemo. На базе первого из них для ввода данных по шаблону создан третий компонент — TMaskEdit.
В начале раздела опишем компонент TCustomEdit. Хотя вы не найдете его в Палитре компонентов, он является общим предком для трех доступных вам редактирующих элементов. Поэтому здесь
рассмотрим только его свойства, общие для всех трех.
В отличие от других визуальных компонентов, у перечисленных в этой группе текст содержится не в свойстве Caption, а в свойстве Text:
property Text: TCaption;
TCaption = string[255] ;
Часть текста может быть выделена. Свойства
property SelStart: Integer;
property SelLength: Integer;
определяют начало и длину выделенного в редакторе текста (измеряемые в количестве символов). Сам выделенный текст содержится в строке, определяемой свойством:
property SelText: string;
Метод
procedure ClearSelection;
исключает из текста весь выделенный фрагмент, а метод
procedure SelectAll;
выделяет весь текст в редакторе. Доступны также рабочие методы по чтению/записи выделенного текста, которые используются свойством SelText:
function GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer-procedure SetSelTextBuf(Buffer:
PChar);
Они могут быть полезны для получения текста типа pChar, который применяется в функциях
API Windows.
Текст можно передавать и принимать из буфера обмена Windows — для этого предназначены
три следующих метода. Если в окне редактора выделен текст, то передается (заменяется) именно он. В
противном случае в операции участвует весь текст:
procedure CopyToClipboard;
procedure CutToClipboard;
procedure PasteFromClipboard;
Очистить весь текст в редакторе можно при помощи метода: procedure Clear;
Наконец, свойство property Modified: Boolean; устанавливает, изменялся ли текст в процессе редактирования.
Компонент TEdit
TObject-^TPersistent^TComponent-”TControl->TWinControl-”TCustomEdit-”TEdit
Модуль STDCTRLS
Страница Палитры компонентов Standard
Этот компонент не содержит собственного кода, в нем только опубликованы свойства его
предка TCustomEdit. Он представляет собой редактируемую строку (далее — просто редактор).
Стиль обрамления этого компонента
(Pb) property BorderStyle: TBorderStyle;
34
по умолчанию равен bsSingle.
Если свойство
(Pb) property AutoSize: Boolean;
равно True, компонент изменяет свою высоту в зависимости от размера шрифта (свойство Font).
Для того чтобы изменения имели место, должен еще быть установлен стиль обрамления bsSingle.
Вводимый в редактор текст может автоматически подвергаться некоторым преобразованиям.
Преобразование, задаваемое свойством CharCase, позволяет автоматически преобразовывать
все символы только в верхний или только в нижний регистр:
(Pb) property CharCase: TEditCharCase;
TEditCharCase = (ecNormal, ecUpperCase, ecLowerCase) ;
По умолчанию установлен стиль ecNormal и преобразования не происходит. Аналогично, свойство
(Pb) property OEMConvert: Boolean;
определяет необходимость автоматического преобразования вводимых символов из кодировки
OEM в ANSI и обратно. Такое свойство часто бывает нужно при обработке текста в кодировке MSDOS.
Два следующих свойства описывают поведение выделенного текста при переходе фокуса. Первое из них
(Pb) property AutoSelect: Boolean;
описывает реакцию редактирующего элемента при получении фокуса. Если оно установлено в
True (по умолчанию это так), то при получении фокуса ввода весь текст в нем выделяется независимо
от состояния свойства SelText. Если AutoSelect установлено в False, то при получении фокуса выделяется лишь то, что было выделено до его утери.
После утери фокуса редактором выделенный в нем текст обычно теряет цветовое выделение.
Чтобы оно оставалось, установите в False второе свойство:
(Pb) property HideSelection: Boolean;
На длину текста может быть наложено ограничение. Свойство
(Pb) property MaxLength: Integer;
определяет максимальную длину текста редактора в символах. Если значение этого свойства
равно 0, то ограничений на длину текста нет.
Свойство PasswordChar предназначено для ввода пароля с использованием редактора:
(Pb) property PasswordChar: Char;
Его значение — это символ, используемый для отображения вместо любых вводимых символов.
Можно запретить возможность редактирования текста. Если значение свойства:
(Pb) property Readonly: Boolean;
равно True, текст изменить нельзя.
Вы можете отследить изменения текста в редакторе, обрабатывая поступающие события:
(Pb) property OnChange: TNotifyEvent;
Компонент ТМеmo
TObject->TPersistent->TComponent->TControl->TWinControl->TCustomEdit->
—”TCu s t omMerno-”TMemo
Модуль STDCTRLS
Страница Палитры компонентов Standard
Компонент представляет собой многострочный редактор текста. Содержимое редактора представлено как объект, содержащий текст в виде набора строк:
(Pb) property Lines: TStrings;
Текст в редакторе может выравниваться по левому, правому краям и по центру:
(Р1э) property Alignment: TAlignment;
TAlignment = (taLeftJustify, taRightJustify, taCenter) ;
При наборе текста пользователь может ввести различные управляющие символы, в частности,
35
клавишами <Enter> и <ТаЬ>. Эти символы могут быть обработаны редактором, а могут быть сразу переданы форме. В случае, если свойства
§ property WantReturns: Boolean;
property WantTabs: Boolean;
обращены в True, символы передаются редактору. Обратим внимание на то, что если установлено WantTabs, то с помощью клавиатуры передать фокус такому редактору можно, а после этого отдать другому компоненту — нельзя. Если свойства равны False, символы передаются форме. В этом
случае для ввода этих символов в редактор можно воспользоваться комбинациями <Ctrl>+<Enter> и
<Ctrl>+<Tab> соответственно.
Два свойства отвечают за организацию прокрутки текста в окне редактора:
(Pb) property Wordwrap: Boolean ;
— отвечает за поведение редактора при достижении правой границы во время набора текста.
Если свойство равно True, то при этом происходит переход на новую строку. В случае False при достижении правой границы происходит горизонтальная прокрутка текста и пользователь может продолжать
набор; на новую строку можно перейти, нажав <Enter>;
(Pb) property ScrollBars: TScrollStyle;
TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth) ;
— устанавливает наличие полос прокрутки в вертикальном и горизонтальном направлениях.
Если есть горизонтальная полоса, то свойство Wordwrap теряет смысл: вместо переноса происходит
прокрутка.
Следующие свойства аналогичны определенным в TEdit — BorderStyle, HideSelection,
MaxLength, OEMConvert и ReadOnly.
Для получения полноценного приложения — текстового редактора, в него нужно включить
компонент TMemo и снабдить средствами чтения, записи и печати файлов, поиска и замены текста и т.
п. Такой редактор вы найдете в примере DEMOEDIT на дискете, прилагаемой к книге.
3.4. Ввод и выбор значений
Общим для описанных в этом разделе компонентов является то, что с их помощью можно интерактивно выбрать или установить значения каких-то величин, которые доступны через их свойства и
могут в нужный момент быть считаны. Для выбора эти компоненты предоставляют пользователю различные упрощающие приемы, поэтому "чистые" редакторы не рассматриваются в этой группе.
Компонент TListBox
TObject—>TPer si stent—”TComponent—>TControl—>TWinControl—>
—>TCustomListBox—>TListBox
Модуль STDCTRLS
Страница Палитры компонентов Standard
Этот компонент соответствует списку выбора — стандартному элементу управления
Windows. С его помощью пользователь может выбрать одну из строк, которые хранятся в свойстве:
(Pb) property Items: TStrings;
В списке Items. Strings хранится текст строк, а список Items.Objects пользователь может использовать для хранения связанных с ними объектов, например, картинок для нестандартно изображаемого
списка.
Индекс текущего (сфокусированного) элемента списка содержится в свойстве:
property Itemlndex: Integer;
Не путайте сфокусированный элемент (стандартно он помещается в рамку из точек) и выделенный (цветным фоном), они могут не совпадать. Смысл этих понятий будет объяснен ниже. Значение
индекса Itemlndex лежит в диапазоне от 0 до Items.Count-1. Он доступен для чтения и записи.
Индекс первого видимого элемента в списке представлен свойством:
property Toplndex: Integer;
36
Он будет ненулевым в том случае, когда все элементы не помещаются в окне списка, и была
сделана прокрутка.
Список выбора имеет свою канву:
(Ro) property Canvas: TCanvas;
и на его поверхности можно рисовать.
Когда в списке нужно предусмотреть выделение одновременно более одного элемента, оперируйте свойствами:
(Pb) property MultiSelect: Boolean;
(Pb) property ExtendedSelect: Boolean;
Если MultiSelect установлено в False, то в списке одновременно не может быть выделено несколько элементов и значение ExtendedSelect не играет роли. В противном случае дело обстоит так.
При ExtendedSelect = False каждый раз изменяется состояние только сфокусированного элемента. Каждый щелчок мышью или нажатие пробела меняет его состояние выделения на противоположное. Если
ExtendedSelect = True, то выбор происходит при передвижении мыши с нажатой левой кнопкой, каждом щелчке мышью на новом элементе списка при нажатых <Ctri> или <Shift>, или при нажатии
<Shift>+<t/4->.
Количество выделенных элементов можно узнать из свойства:
(Ro) property SelCount: Integer;
Проверку и установку выделения для каждого элемента можно провести, используя свойство:
property Selected[Index: Integer]: Boolean;
При задании ошибочного индекса при доступе к списку возникает исключительная ситуация
EList Error.
Чтобы расположить строки в алфавитном порядке, нужно установить в True свойство:
(Pb) property Sorted: Boolean;
Элементы списка могут появляться как в одном столбце — Друг под другом, так и в нескольких
соседних. В этом случае список может иметь горизонтальную полосу прокрутки. Число столбцов определяется свойством:
(Pb) property Columns: Integer;
Если столбец один, то значение этого свойства равно 0. Очистить список можно при помощи
метода:
procedure Clear;
Стиль обрамления компонента определяется свойством:
(Pb) property BorderStyle: TBorderStyle;
Найти индекс элемента, которьш содержит точку Pos, можно при помощи метода:
function ItemAtPos(Pos: TPoint; Existing: Boolean): Integer;
Параметр Existing определяет, что возвращается в случае неудачи (значение Items.Count либо 1).
Прямоугольник, отведенньш элементу списка с индексом Index, определяется с помощью метода:
function ItemRect(Index: Integer): TRect;
При создании и визуализации списка система обычно подгоняет его высоту таким образом,
чтобы в видимое поле помещалось целое число элементов. Это соответствует значению True свойства:
(Pb) property IntegralHeight: Boolean;
Если IntegralHeight равно False, то высота списка не изменяется. Это свойство не играет роли
при стиле списка IbOwnerDrawVariable.
Стиль списка может быть стандартным или определенным пользователем через свойство:
(Pi-y property Style;
TListBoxStyle = (IbStandard, IbOwnerDrawFixed,
IbOwnerDrawVariable) ;
Рассмотрим назначение этого свойства более подробно.
В стандартном варианте (IbStandard) в списке отображаются только строки из свойства Items; в
двух других случаях рисуемые пользователем списки могут иметь фиксированную (IbOwnerDrawFixed)
37
или переменную (IbOwnerDrawVariable) высоту элемента. В первом случае нужно задать свойство:
(Pb) property ItemHeight: Integer;
Для стиля IbOwnerDrawVariable высота каждого элемента определяется программистом, которьш должен предусмотреть обработку события:
(Pb) property OnMeasureItem: TMeasureItemEvent;
TMeasureItemEvent = procedure(ListBox: TListBox; Index:
Integer; var Height: Integer) of object;
Имея указатель на список, индекс измеряемого элемента и начальную высоту, необходимо переустановить Height так, чтобы в элементе поместилось все, что нужно в нем нарисовать.
Для рисования каждого элемента инициируется событие:
(Pb) property OnDrawItem: TDrawItemEvent;
TDrawItemEVent = procedure(ListBox: TListBox; Index: Integer;
Rect: TRect; State: TOwnerDrawState) of object;
Обработчик этого события получает указатель на список ListBox, индекс элемента Index, отведенньш для рисования прямоугольник Rect, и состояние элемента в параметре State:
TOwnerDrawState = set of (odSelected, odGrayed, odDisabled,
odChecked, odFocused) ;
Для списка выбора из этого множества действительны только флаги odSelected, odDisabled,
odFocused.
Компонент TComboBox
TObject—>TPersistent->TComponent—”TControl—”TWinControl—>
—>TCustomCornboBox—>TComboBox
Модуль STDCTRLS
Страница Палитры компонентов Standard
Этот стандартный элемент управления Windows — комбинированный список -— имеет много
общего с TListBox. Он состоит из собственно списка и помещенного рядом поля ввода редактирующего элемента. Таким образом, пользователь может выбрать одно из готовых значений какой-то величины или ввести свое. Стиль компонента в части правил взаимного сочетания списка и редактора определяется свойством:
(РЁ) property Style: TComboBoxStyle;
TComboBoxStyle = (csDropDown, csSimple, csDropDownList,
csOwnerDrawPixed, csOv'nerDrawVariable) ;
Его значения имеют следующий смысл:
csSimple — весь список виден все время. Текущий выбор отображается в редакторе, его можно
изменить;
csDropDown — список открывается (выпадает) и скрывается при нажатии кнопки, расположенной справа от текста. Такой список будем называть вьша дающим;
csDropDownList — список вьшадающий, но редактирующий элемент заменен статическим текстом и вводить свои данные пользователь не может;
csOwnerDrawFixed — определяемьш пользователем стиль списка с постоянной высотой элемента;
csOwnerDrawVariable — определяемьш пользователем стиль списка с переменной высотой элемента.
Принципы пользовательской отрисовки списка для csOwnerDrawFixed и csOwnerDrawVariable,
а также связанные с этим методы и события остались такими же, как в TListBox. По умолчанию устанавливается сталь csDropDown.
Организация списка также сходна с предыдущей. Приведенные свойства и методы имеют то же
назначение:
(Pb) property Items: TStrings;
property Itemlndex: Integer;
procedure Clear;
38
(Pb) property Sorted: Boolean;
(RcS) property Canvas: TCanvas;
Текст, содержащийся в редактирующем элементе списка, доступен через свойство:
(р5) property Text: TCaption;
Его максимальная длина ограничена числом символов, равным значению свойства:
(Р1э) property MaxLength: Integer;
Если MaxLength равно 0, то ограничений на длину строки нет (до 255 символов).
При изменеюш текста (а также при смене выбранного элемента) возникает событие OnChange.
Часть текста может быть выделена. Три свойства содержат выделенный текст, его положение и
длину:
property SelText: strings-property SelStart: Integers-property SelLength: Integer;
Метод
procedure SelectAll;
выделяет весь текст. Значение свойства
property DroppedDown: Boolean;
соответствует состоянию списка: True соответствует открытому ("выпавшему") списку. С его
помощью можно показывать или скрывать список из программы. При изменении состояния списка
возникает событие:
(Р}з\ property OnDropDown: TNotifyEvent ;
Максимальное число элементов, которые показываются при выпадении списка:
(Pb) property DropDownCount: integer;
По умолчанию оно равно 8. В первой версии VCL это свойство описано, но не реализовано (не
играет роли).
Компонент TStringGrid
TObject-”TPersistent—>TComponent-*TControl-”TWinControl->
—”TCustomControl”TCustomGrid-”TDrawGrid->TStringGrid Модуль GRIDS Страница Палитры компонентов Additional
Этот компонент реализует возможности своего предка TDrawGrid применительно к таблице
строк.
В состав компонента добавлен объект класса TStrings, в котором хранится содержимое ячеек.
Он доступен как векторное свойство — двумерный массив текстовых строк (размерностью ColCount x
RowCount), соответствующих ячейкам таблицы:
property Cells[ACol, ARow: Integer]: string;
Доступен и двумерньш массив объектов, соответствующих ячейкам:
property Objects[ACol, ARow: Integer]: TObject;
Необходимо помнить, что самих объектов в таблице нет, и программист должен создавать, а по
окончании использования таблицы удалять объекты самостоятельно.
Можно работать отдельно как со строками, так и со столбцами таблицы. Свойства
property Cols[Index: Integer]: TStrings;
property Rows[Index: Integer]: TStrings;
описывают наборы строк (также в виде TStrings), содержащие текст и объекты столбцов и строк
таблицы.
При значении True свойства DefaultDrawing для этого компонента происходит вывод строки в
соответствующей ячейке. Так что если кроме текста ничего отображать не требуется, то менять значение DefaultDrawing и определять обработчик события OnDrawCell не нужно.
Отметим, что перенос строк и столбцов таблицы (при установленных опциях goColMoving или
goRowMoving) осуществляется вместе с их текстовыми строками.
Компонент TOutline
TObject->TPersistent—>TCoinponent—”TControl—”TWinControl->
—>TCustomControl>TCustomGrid->TCustomOutline-”TOutline Модуль OUTLINE Страница Палитры компонентов Additional
39
TOutline создан для ведения и отображения иерархических структур данных — деревьев. Это
один из наиболее сложных и интересных компонентов. В этот раздел он попал потому, что является
потомком таблицы (TCustomGrid), хотя и мало чем ее напоминает.
Типичным примером применения TOutline можно назвать отображение структуры файловой
системы (для этого даже есть отдельный компонент TDirectory Outline). В описанном ниже примере
OBJTREE этот компонент используется для отображения дерева классов библиотеки VCL.
Дерево состоит из элементов (будем называть их узлами), упорядоченных по уровням. Каждый
из них имеет родительский узел (на более высоком уровне) и список дочерних узлов-потомков. Исключением являются корневой узел (нулевого уровня) — он не имеет предка, и узлы последнего уровня, не
имеющие потомков.
Каждый узел является объектом класса TOutlineNode:
TOutlineNode = class(TPersistent) Рассмотрим сначала методы и свойства этого объекта. С каждым узлом можно связать информацию — имя и произвольные данные:
property Text: string;
property Data: Pointer;
Указатель на родительский узел определяется свойством:
(Ro) property Parent: TOutlineNode;
Если список дочерних узлов не пуст, что можно проверить при помощи свойства
(Ro) property Hasltems: Boolean;
то для получения информации о них есть метода,!:
function GetFirstChild: Longint;
function GetLastChild: Longint;
function GetNextChild(Value: Longint): Longing-function GetPrevChiId(Value: Longint): Longint;
В последних двух методах параметр Value содержит индекс предыдущего найденного потомка.
Индекс — это уникальный идентификатор узла в
дереве, определенный свойством:
(Ro) property Index: Longint;
Используя его, можно получить указатель на узел (см. ниже сам компонент TOutline). Узлы
пронумерованы таким образом, что если родитель имеет номер N, то первый из его потомков — номер
(N+1).
Можно узнать и полное имя (путь) узла, представляющее собой конкатенацию всех родительских имен и имени узла, разделенных специальным символом. Этот символ определен в свойстве ItemSeparator объекта TCustomOutline, которому принадлежат узлы. Полный путь определяется свойством:
(Ro) property FullPath: string;
Каждый узел расположен на своем иерархическом уровне. На самом верху находится узел
уровня 0. Все другие являются для пего дочерними; он не виден и не доступен пользователю. Поэтому
на первом доступном уровне — с номером 1 — могут находиться по вашему желанию один или несколько узлов. Уровень иерархии узла можно узнать в его свойстве:
(RO) property Level: Cardinal;
Чтобы изменить уровень, надо вызвать метод:
procedure ChangeLevelBy(Value: TChangeRange);
которьш перемещает узел в список другого уровня. Диапазон перемещения TChangeRange
ограничен:
TChangeRange = -1..1;
Чтобы переместить узел в дереве, нужно вызвать следующий метод
procedure MoveTo(Destination: Longint; AttachMode: TAttachMode);
TAttachMode = (oaAdd, oaAddChild, oalnsert) ;
Он перемещает узел (вместе со всеми потомками) в положение с индексом Destination. Родительский узел не может переместиться на место любого из своих потомков. Режим перемещения AttachMode означает:
oaAdd — добавить последним на том же уровне, что и Destination;
oaAddChild — добавить последним к потомкам узла Destination;
oalnsert — заменить в положении Destination прежний узел, которьш смещается дальше по
40
списку на том же уровне.
Индекс того из предков узла, которьш находится в списке самого высокого уровня, известен из
свойства:
(Ro) property Topltem: Longint;
Для корневого узла это свойство равно 0.
Компонент TOutline не только хранит древовидную структуру, он еще и отображает ее. На
экране каждый узел может находиться в двух состояниях, свер
нутом и развернутом. В свернутом состоянии потомки узла не видны, в развернутом они изображаются чуть ниже и правее друг под другом. Состояние узла может иллюстрироваться значком
(плюс/минус, открытая/закрытая папка — см. описание свойства OutlineStyle компонента TOutline). В
таблице приведены методы и свойства, управляющие состоянием узла на экране:
property Expanded: Boolean; Указывает, в каком состоянии находится узел; True — развернутое состояние.
procedure Collapse;
Сворачивает узел, пряча все дочерние узлы.
procedure Expand;
Разворачивает узел списка, показывая дочерние узлы.
procedure FullExpand;
Полностью разворачивает узел, показывая все дочерние узлы всех подуровней.
Сворачивать/разворачивать узлы дерева, помимо
щелчков мышью,
можно
нажатием
клавиш:
<+> — соответствует Expand;
<-> — соответствует Collapse;
<*> — соответствует FullExpand. Свойство
(Ro) property IsVisible: Boolean;
означает, может ли быть виден узел. Это возможно только в том случае, если виднь! все его родители. При отрисовке ширина узла сообщается методом:
function GetDisplayWidth: Integer;
Теперь перейдем от описания узла к описанию самого дерева — компонента TOutline. Он представляет собой совокупность узлов типа TOutlineNode. Всего в дереве содержится число узлов, равное
значению свойства:
(Ro) property ItemCount: Longint;
К каждому из них можно обратиться, используя свойство:
(Ro) property Items[Index: Longint]: TOutlineNode;
На текущий (выделенный) узел можно сослаться через свойство:
property Selectedltem: Longint;
Например, выполнение оператора
Items[Items[Selectedltem].Topltem].FullExpand;
приведет в развернутое состояние ту часть дерева, в которой находится выделенный узел
(начиная от его самого далекого предка Topltem). Свойство
property Row: Longint;
показывает, какая строка дерева в данный момент имеет фокус. Зная "содержимое" узла, то есть
его имя или данные, можно найти его в дереве и узнать индекс. Для этого нужно вызвать один из методов, возвращающих его:
function GetDataItem(Value: Pointer): Longint;
function GetTextItem(const Value: string): Longint;
Можно найти узел и по координатам точки в клиентской области компонента (на самом деле
играет роль только координата Y) с помощью следующего метода:
function GetItemfX, Y: Integer): LongInC;
Целых три пары методов добавляют новый узел в дерево. Его имя инициализируется параметром Text. Различие между первым и вторым методами в парах в том, что второй позволяет связать с
41
узлом данные (параметр Data), а в первом вместо них записывается nil.
function Add(Index: Longint; const Text: string): Longint;
function AddObject(Index: Longint; const Text: string;
const Data: Pointer): Longint;
— добавляют новый узел в качестве последнего в тот же список, где находится узел Index. Поскольку на самом верху иерархии может быть только один узел, то в случае задания Index = 0 он добавляется в список верхнего уровня. Соответствуют режиму oaAdd;
function AddChild(Index: Longint; const Text: string): Longint;
function AddChildObject(Index: Longint; const To" : string;
const Data: Pointer): Longint;
— добавляют новый узел в качестве последнего потомка узла Index. Соответствуют режиму
oaAddChild;
function Insert(Index: Longint; const Text: string): Longint;
function InsertObject(Index: Longint; const Text: string;
const Data: Pointer): Longint;
— вставляют узел в положение Index. При этом прежний узел с таким индексом и все другие на
этом уровне сдвигаются вниз. Единственное исключение — задание в качестве параметра нулевого индекса. В этом случае узел добавляется последним в список самого верхнего уровня. Соответствуют режиму oalnsert. Все методы вызывают переиндексирование дерева и возвращают уже новый индекс для
вставленного узла. Если нужно удалить узел, то метод procedure Delete(Index: Longint);
удаляет узел с индексом Index, а метод
procedure Clear;
очищает все дерево.
Если нужно вставить или удалить сразу много узлов и при этом избежать трудоемких операций
переиндексации и перерисовки дерева, то соответствующий код надо заключить между вызовами методов:
procedure BeginUpdate;
procedure EndUpdate;
Первый устанавливает специальное состояние обновления и на время запрещает все пересчеты
и перерисовки, а второй сбрасывает его и обновляет дерево. Это состояние можно изменить и при помощи метода:
procedure SetUpdateSCate(Value: Boolean);
Дерево можно полностью свернуть или полностью развернуть вызовом методов:
procedure FullExpand;
procedure FullCollapse;
Каждый узел, изменяя свое состояние, предупреждает об этом родительское дерево. В зависимости от того, свернулся он или развернулся, вызываются обработчики событий
(pb) property OnExpand: EOutlineChange;
(РЬ) property OnCollapse: EOutlineChange;
EOutlineChange = procedure (Sender: TObject; Index: Longint) of object;
где параметр Index означает индекс узла, измешшшего состояние. Метод
function GetNodeDisplayWidth(Node: TOutlineNode): Integer;
возвращает ширину, занимаемую изображением узла (если установленньш стиль не osOwnerDraw, см. ниже).
Дерево может быть загружено и выгружено в поток и файл при помощи методов:
procedure LoadFromFile(const FileName: strings-procedure LoadFromStream(Stream: TStream);
procedure SaveToFile(const FileName: strings-procedure SaveToStream(Stream: TStream) ;
Для прокрутки информации TOutline по умолчанию имеет обе полосы, определяемые свойством:
(Pb) property ScrollBars: TScrollStyle;
TScrollStyle = (ssNone, ssHorizontal, ssVertical, ssBoth);
Строка (символ), разделяющий имена узлов при составлении полного имени узла, содержится в
42
свойстве:
(Pb) property ItemSeparator: string;
На стадии разработки можно набрать строки, соответствующие будущим узлам дерева, в специальном редакторе. При этом узел попадет на уровень, соответствующий количеству пробелов или
знаков табуляции перед его названием. Набранные строки текста содержит свойство:
(Pb) property Lines: TStrings;
Это свойство предназначено для использования именно на этапе разработки, так как свойство
Items в это время недоступно. Во время исполнения с информацией об узлах нужно работать, используя свойство Items. При рисовании дерева, помимо собственно текста, вместе с ним может изображаться одна из пиктограмм:
Из) property PicturePlus: TBitmap;
Соответствует свернутому узлу. По умолчанию — "плюс".
(Pb) property PictureMinus: TBitmap;
Соответствует развернутому узлу. По умолчанию — "минус".
(Pb) property PictureOpen: TBitmap;
Соответствует развернутому узлу. По умолчанию — "открытая
папка".
Соответствует свернутому узлу. По умолчанию — "закрытая
папка".
Соответствует "листу" — узлу без потомков. По умолчанию —
"документ".
(Pb) property PictureClosed: TBitmap;
(Pb) property PictureLeaf: TBitmap;
Желательно, чтобы картинки имели "прозрачный" фон, то есть чтобы их свойство TransparentColor соответствовало цвету рабочей области компонента. Эти пиктограммы можно переопределить
в случае необходимости.
Свойство OutlineStyle определяет, в частности, когда и какие пиктограммы будут показаны:
(Pb) property OutlineStyle: TOutlineStyle;
TOutlineStyle = (osText, osPlusMinusText, osPictureText, osPlusMinusPictureText, osTreeText, osTreePictureText);
В зависимости от значения этого свойства изображаются:
osText — только текст;
osPlusMinusText — текст и пиктограммы PicturePlus и PictureMinus в зависимости от состояния
узла. "Лист" не имеет значка;
osPictureText — текст и пиктограммы PictureOpen, PictureClosed и PictureLeaf в зависимости от
состояния узла;
osPlusMinusPictureText — объединяет в себе два предыдущих стиля;
osTreeText — текст и специальные линии, иллюстрирующие связь между родительским и дочерними узлами;
osTreePictureText — объединяет в себе стили osTreeText и osPictureText. По умолчанию установлен стиль osTreePictureText;
На внешний вид дерева влияют и опции, содержащиеся в свойстве:
(Pb) property Options: TOutlineOptions;
TOutlineOption =- (ooDrawTreeRoot, ooDrawFocusRect, ooSCretchBitmaps) ;
TOutlineOptions =- set of TOutlineOption;
Это множество может содержать элементы:
ooDrawTreeRoot — задает соединение линией всех узлов верхнего уровня. В случае отсутствия
опции каждый из них выглядит как вершина отдельного дерева. Эта опция играет роль только для стилей osTreeText, osTreePictureText;
ooDrawFocusRect — задает выделение сфокусированного узла рамкой из точек;
ooStretchBitmaps — задает приведение размеров пиктограмм к размерам шрифта текста путем
масштабирования. В противном случае либо из пиктограммы вырезается часть (если она больше), либо
остается свободное место (если меньше).
Рисование дерева по умолчанию осуществляется системой, но может быть возложено и на про-
43
граммиста. Определяется это свойством:
(№) property Style: TOutlineType;
TOutlineType = (otStandard, otOwnerDraw) ;
Стиль osStandard подразумевает, что дта каждого узла будет изображено то, что предусмотрено
стилем OutlineStyle. Для реализации стиля otOwnerDraw нужно нарисовать содержимое узла в обработчике события:
(Pb) property OnDrawItern: TDrawItemEvent;
TDrawItemEvent = procedure(ListBox: TListBox; Index: Integer;
Rect: TRect; State: TOwnerDrawState) of object;
Параметры:
Index — индекс узла;
Rect — отведенный ему прямоугольник;
State — множество, в которое могут входить состояния odSelected, odFocused.
Высота каждого узла постоянна и в этом случае определяется свойством:
(Pb) property ItemHeight: Integer;
Для рисования у компонента есть своя канва:
property Canvas: TCanvas ;
Установить ее можно только для объекта стиля osOwnerDraw; при osStandard канва игнорируется.
Обрамление компонента задается свойством:
(Pb) property BorderStyle: TBorderStyle;
Разобраться с применением этого компонента поможет пример OBJTREE. В нем по заранее заданному массиву компонентов ClassesSet выстраивается иерархическое дерево, в которое добавляются
и все объекты-предки. Для примера были выбраны только 32 компонента, при желании можно включить и все остальные. Не забывайте при этом добавить содержащие их модули к тем, что содержатся в
операторе uses. Когда вы перемещаете фокус по дереву, отображается имя объекта и имя модуля, в котором он описан. Для этого используется информация, возвращаемая недокументированным (пока?)
методом класса TObject.ClassInfo. Возвращаемая им структура описана в исходных текстах VCL.
3.5. Группирование компонентов
По умолчанию родителем для большинства компонентов, размещенных на форме, является она
сама. В ряде случаев, однако, есть необходимость отказаться от этого правила, как визуально, так и
функционально подчеркнув обособленность каких-то элементов управления. Для этого вы можете использовать специальные группирующие компоненты TGroupBox и TPanel. Мы также отнесли в эту
группу компонент TScrollBox, он также обычно имеет дочерние компоненты, которые все вместе подвергаются прокрутке. Общим признаком для всех группирующих компонентов является наличие опции
csAcceptsControls в свойстве ControlStyle.
Компонент TGroupBox
TObject—^TPersistent—”TCorr]ponent—”TControl—>TWinControl—>
—>TCustomControl—>TCustomGroupBox—^TGroupBox
Модуль STDCTRLS
Страница Палитры компонентов Standard
Единственное назначение этого компонента — служить предком (Parent) для других компонентов. Это играет роль тогда, когда они должны быть объединены в группу по какому-либо признаку.
Типичный пример — радиокнопки. Когда одна из них нажимается, все остальные радиокнопки в группе (т. е. с одним значением свойства Parent) автоматически отпускаются.
Поэтому, для того чтобы на форме иметь несколько независимых групп радиокнопок, их нужно
поместить на разные объекты типа TGroupBox. (Кстати, именно для решения задачи с радиокнопками
есть специальный компонент — TRadioGroup).
Будучи потомком класса TCustomControl, TGroupBox наследует от него общие свойства, опи-
44
санные ранее. Специфических свойств и методов этот компонент не имеет.
Компонент TPanel
TObject->TPersistent-”TComponent->TControl->TWinControl—>
->TCustomControl—”TCustomPanel-”TPanel
Модуль EXTCTRLS
Страница Палитры компонентов Standard
Этот компонент — панель — является несущей конструкцией для размещения других элементов управления. В отличие от простой рамки (TBevel) панель сама является оконным элементом управления и родителем для всех размещенных на ней компонентов. Часто ее используют для создания панелей инструментов, строк состояния и т. п.
За внешнее оформление панели отвечают свойства:
(Pb) property Bevel Inner: TPanelBevel;
(Р1э) property BevelOuter: TPanelBevel;
TPanelBevel = (bvNone, bvLowered, bvRaised) ;
(Pb) property BevelWidth: TBevelWidth;
TBevelWidth = 1..Maxint;
(Pb) property BorderWidth: TBorderWidth;
TBorderWidth = 0..Maxint ;
На границах панели размещаются две специальные окаймляющие рамки (bevels): Bevellnner и
BevelOuter. При помощи комбинации белого и серого цветов они имитируют трехмерность — приподнятость (bvRaised) или утоп-ленность (bvLowered). Обе рамки имеют ширину BevelWidth. Наружная —
BevelOuter — расположена прямо по периметру панели, вторая — Bevellnner — внутри на расстоянии
BorderWidth от нее. Обе могут быть невидимыми (bvNone), приподнятыми или утопленными.
Комбинируя сочетания bvLowered/bvRaised, можно создать вокруг панели "ров" или "вал".
Иллюзию трехмерности может подчеркнуть также стиль обрамления компонента:
(Pb) property BorderStyle: TBorderStyle;
TBorderStyle = bsNone .. bsSingle;
Значение bsSingle означает, что панель будет окаймлена черной линией единичной толщины
(по периметру, снаружи от обеих рамок).
Выравнивание текста заголовка панели определяется при помощи свойства:
(Pb) property Alignment: TAlignment;
Свойство
(Pb) property Locked: boolean;
предназначено для определения способа взаимодействия панели
с размещенными на ней
объектами OLE. Если оно равно True, последние при активизации сервера OLE "по месту'" не могут
заместить панель. В обработчике события:
(Pb) property OnResize: TNotifyEvent;
может быть предусмотрена реакция пользователя на изменение размеров панели.
3.6. Компоненты — стандартные диалоговые окна Windows
В Windows, начиная с версии 3.1, появилась библиотека стандартных диалоговых окон
COMMDLG.DLL, которая позволила заменить вызовом функций программирование рутинных операций в типовых случаях взаимодействия с пользователем. К этим случаям относятся выбор имени файла
для чтения и записи, выбор цвета или шрифта, поиск и замена текста, настройка принтера и параметров
печати. В Delphi предусмотрены компоненты, представляющие собой оболочку для этих стандартных
диалогов.
Поместив компонентыдиалоги на форму, вы освобождаетесь от необходимости вы-
45
зывать их конструкторы и деструкторы. Все использование этих компонентов укладывается в трехэтапную схему:
1. Настройка параметров диалога, во время которой устанавливаются те или иные возможности.
У всех диалогов для этого предусмотрено свойство Options, но у некоторых есть и дополнительные
свойства.
2. Вызов метода Execute, который показывает диалоговое окно на экране и инициирует взаимодействие с пользователем. Execute является функцией:
он возвращает True, если пользователь подтвердил ввод значения (т. е. нажал кнопку ОК на
экране или клавишу <Enter> на клавиатуре) и False, если он отказался от выбора (т. е. нажал кнопку
Cancel или клавишу <Esc>).
3. В случае положительного ответа — чтение установленных значений в представляющем их
свойстве (свойствах).
Примечание
Несколько отличается принцип взаимодействия в диалогах TFindDialog и TReplaceDialog, где
нажатие кнопки подтверждения (Find и Replace соответственно) инициирует событие, которое пользователь должен обработать.
По этой схеме и будет строиться описание компонентов. Естественно, метод Execute при этом
опущен. Кроме того, общим для диалогов является свойство HelpContext. При нажатии кнопки Help
вызывается система помощи именно с этим контекстом.
Компоненты TOpenDialog и TSaveDialog
TObj ect—>TPersistent—>TCoinponent—>TConimonDialog—”TOpenDialog—”
-+TSaveDialog
Модуль DIALOGS
Страница Палитры компонентов Dialogs
Эти компоненты-диалоги предназначены для выбора имени файла, который будет в дальнейшем использован для чтения или записи. Рассмотрим их совместно, т. к. они отличаются только интерпретацией некоторых опций.
Диалог может быть настроен на представление имен файлов какого-нибудь одного типа или нескольких типов. При этом только эти типы отображаются в списке, и только из них может сделать выбор пользователь, с помощью свойства:
(РЬ) property Filter: string;
Формат строки фильтра состоит из двух частей. В первой задается краткое описание типа. Во
второй части, которая отделяется символом '|' — маска поиска нужных файлов по расширению. В список файлов попадут только те, которые имеют указанные расширения. Примеры задания фильтра: 'Delphi projectsl*.dpr' или 'All graphics files I *-bmp,*.ico,*.wmf '.
Таких пар строк для разных типов в фильтре может быть несколько, при этом форматы представления фильтра в самой системе Windows и в Delphi имеют одно отличие. При вызове стандартных
диалогов Windows эти пары строк должны разделяться нулевым байтом, после последней строки также
должен стоять нулевой байт. В рассматриваемых компонентах все строки разделяются символом 'Г.
Создавать строки вручную нужно лишь в редких случаях — для ввода значения свойства на
этапе проектирования предназначен специальный редактор. Есть также простой способ составить
фильтр для графических файлов — для этого предназначена процедура GraphicFilter (ее описание см. в
разделе "Графическая подсистема").
В качестве начального фильтра при вызове диалога будет выбрана та пара строк, номер (индекс) которой совпадает со значением свойства:
(Pb) property Pilterlndex: Integer;
Если свойству не присваивалось значения, то по умолчанию выбирается первая пара.
Путь к файлам, которые первоначально будут отображены в диалоге, задается свойством:
(Pb) property InitialDir: string;
В случае, если в поле ввода имени файла пользователь при ручном наборе не определил его
расширение, это за него пытается сделать диалог. Для этого свойству
46
(Pb) property DefaultExt: TFileExt;
TFileExt = string[3];
присваивается строка (до трех символов, без точки), которая и будет использоваться в качестве
расширения по умолчанию. Например, если пользователь ввел в поле имени файла 'mybitmap', a DefaultExt равно 'bmp', то компонент вернет полное имя 'mybitmap. bmp'.
Поле ввода FileName, где отображаются и редактируются имена файлов, может быть простым
редактирующим элементом, а может быть и комбинированным списком. В этот список должны попасть имена файлов, которые были прежде прочитаны (записаны). Стиль редактирующего элемента
задан свойством
(Pb) property FileEditStyle: TFileEditStyle;
TFileEditStyle = (fsEdit, fsComboBox);
а содержимое списка ранее возвращавшихся имен — свойством:
(Pb) property HistoryList: TStrings;
В случае действия стиля fsEdit это свойство не играет роли. Если же установлено fsComboBox,
в выпадающем списке появляется содержимое HistoryList.
Можно создать список для этого свойства во время разработки программы. Но для того, чтобы
оно действительно играло роль "предыстории", программист должен пополнять список после успешного окончания диалога, что иллюстрируется следующим фрагментом кода:
procedure TFormI-FileOpenClick(Sender: TObject);
begin
with OpenDialogI do
if Execute then
begin
Memol.Lines.LoadFromFile(FileName) ;
HistoryList.Add(FileName);
end;
end;
У двух диалогов имеется большой набор опций. Часть из них является общей, часть — играет
роль только для одного из диалогов:
(Pb) property Options: TOpenOptions;
TOpenOption = (ofReadOnly, ofOverwritePrompt, ofHideReadOnly, ofNoChangeDir, ofShowHelp,
ofNoValidate, ofAllowMuItiSelect, ofExtensionDifferent, ofPathMustExist, ofFileMustExist, ofCreatePrompt,
ofShareAware, ofNoReadOnlyReturn, ofNoTestFileCreate) ;
TOpenOptions = set of TOpenOption;
Три опции отвечают за работу с файлами со статусом "только для чтения":
ofReadOnly — делает флажок "Read only" помеченным при появлении;
ofHideReadOnly — прячет этот флажок в появляющемся диалоге;
ofNoReadOnlyRetum — запрещает выбор файлов "только для чтения", извещая о необходимости выбрать другой файл при нажатии ОК.
Также три опции ограничивают ввод имен для новых (несуществующих) файлов:
ofPathMustExist — указывает на то, что файл может находиться только в одном из существующих каталогов. В случае ввода несуществующего пути к файлу пользователь извещается об ошибке;
ofFileMustExist — аналогичньм образом указывает на то, что может быть выбран только один
из существующих файлов;
ofCreatePrompt — опция устанавливает реакцию на предыдущую ситуацию. Если она установлена, то вместо сообщения об ошибке вьшодится запрос на создание нового файла.
Другие опции:
ofOverwritePrompt — запрашивает подгверждение, если пользователь выбрал для записи уже
существующий файл;
ofNoChangeDir — запрещает изменение начального каталога, с которьм диалог будет проинициализирован. Если она установлена, диалог каждый раз появляется с тем каталогом, который был
установлен при первом запуске;
47
ofShowHelp — включает в состав диалога кнопку Help;
ofNo Validate — выключает проверку введенного имени файла на наличие в нем недопустимых
символов;
ofAllowMultiSelect —позволяет выбирать несколько файлов одновременно;
ofShareAware — отключает проверку на возможность совместного доступа к выбранному файлу. В случае отстутствия этой опции файл нельзя выбрать, если он открыт другим приложением;
ofNoTestFileCreate — эта опция применяется только для файлов на тех узлах локальной сети,
которым разрешено создание, но не модификация файлов. Если она установлена, диалог не проверяет
возможность записи на выбранном устройстве.
Наконец, одна опция — ofExtensionDifTereiit — является выходной. Она устанавливается после
завершения диалога в том случае, если расширение у введенного имени файла отличается от того, которое определено по умолчанию (в свойстве DefaultExt).
Текст, появляющийся в заголовке диалога, определяется свойством:
(Pb) property Title: string;
Если оно не было задано, то в заголовках диалогов появится "Open" для TOpenDialog и
"SaveAs" для TSaveDialog.
В случае успешного завершения диалога в свойстве
(Ro) property Files: TStrings;
содержатся имена выбранных пользователем файлов. Если выбран один файл, его имя можно
получить как Files.Strings[0], но есть для этого и отдельное свойство:
(Pb) property FileName: TFileName;
Компонент TColorDialog
TObject->TPersistent—>TCoinponent-^TCommonDialog->TColorDialog
Модуль DIALOGS Страница Палитры компонентов Dialogs
Предназначен для определения цвета пользователем. Настройка диалога осуществляется через
свойство:
(Pb) property Options: TColorDialogOptions;
TColorDialogOption = (cdFullOpen, cdPreventFullOpen, cdShowHelp);
TColorDialogOptions = set of TColorDialogOption;
Данные опции определяют:
cdFullOpen — полный показ диалога. В этом случае пользователь может выбрать не только
один из цветов системной палитры, но и задать собственный;
cdPreventFullOpen — запрещение полного показа диалога;
cdShowHelp — показ кнопки Help.
В диалоге пользователь может определить несколько (до 16) собственных цветов, которые доступны в свойстве:
(РЙ property CustomColors: TStrings;
Каждый из этих цветов представлен в свойстве CustomColors строкой вида
ColorA = SOffaO
где ColorA — условное название цвета (могут быть определены цвета с названиями от ColorA
до ColorP), а справа от знака равенства стоит его шестнадцатиричное значение в формате RGB.
В случае нажатия кнопки ОК возвращаемое значение цвета содержится в свойстве:
(РЬ) property Color: TColor;
3.7. Работа с файловой системой
Зачастую возможностей стандартных диалогов (компоненты TOpenDialog и TSaveDialog) недостаточно для реалшации замыслов программиста. Во многих современных коммерческих приложениях
перед открытием файла есть возможность получить его характеристики и предварительно просмотреть
содержимое. Чтобы дать разработчикам доступ к таким функциям, в библиотеку VCL были включены
по отдельности и все составные части стандартного диалога — списки имен дисков, каталогов, файлов
48
и фильтров расширений. Если установить между ними взаимные ссылки, то они будут работать вместе,
как в TOpenDialog, безо всякого дополнительного программирования. Вместе с тем, можно описать и
специфическую реакцию на изменения этих списков.
Кроме того, в эту группу компонентов включен TDirectoryOutline — пример настройки компонента TOutline на конкретное приложение. Он отображает файловую структуру в виде дерева и также
может работать в связке с остальными компонентами этого раздела.
Компонент TDriveComboBox
TObject—”TPersistent—>TComponent—^TControl—>TWinControl->
->TCustornComboBox-”TDriveComboBox
Модуль FILECTRL
Страница Палитры компонентов System
Компонент представляет комбинированный список, содержащий информацию о имеющихся в
системе устройствах. При инициализации приложения все они заносятся в этот список. Во время выполнения отображаются односимвольное имя устройства, метка в квадратных скобках и пиктограмма,
соответствующая его типу (винчестер, сетевой диск, дисковод CD-ROM и т. п.):
TDriveType = (dtUnknown, dtNoDrive, dtFloppy, dtFixed, dtNetwork, dtCDROM, dtRAM) ;
К сожалению, функция, определяющая тип диска по его номеру или имени, недоступна программисту. Имеющаяся в API Windows функция GetDriveType не умеет распознавать dtCDROM и
dtRAM и сообщает о них как о dtFixed.
Имя текущего диска соответствует значению свойства
property Drive: Char;
а имя с меткой (так, как это показывается в списке) — значению свойства:
property Text;
При этом текст метки может отображаться в верхнем или нижнем регистрах, в зависимости от
значения свойства:
(Pb) property TextCase: TTextCase;
TTextCase = (tcLowerCase, tcUpperCase) ;
Пиктограмму типа диска можно получить, используя свойство списка Items.Objects — каждому
устройству соответствует объект типа TBitmap.
Изменение текущего диска, как правило, должно повлечь за собой изменения в соответствующих списках каталогов и файлов. Для этого в обработчике собьщга
(Pb) property OnChange: TNotifyEvent;
нужно предусмотреть соответствующий код, например:
procedure TFormI.DriveComboBoxlChange(Sender: TObject);
begin
DirectoryListBoxl.Drive := DriveComboBoxl.Drive ;
end;
Существует и более простой путь для такого связывания. Достаточно связать компонент-список
дисков с компонентом-списком каталогов, используя свойство:
(Pb) property DirList: TDirectoryListBox;
Все изменения первого будут автоматически отслеживаться вторым. Компонент
TDirectoryListBox
TObject-”TPersistent->TComponent—”TControl->TWinControl—>
—>TCustomListBox->TDirectoryListBox
Модуль FILECTRL
Страница Палитры компонентов System
Компонент представляет список каталогов на текущем устройстве.
Устройство задается свойством
property Drive: Char;
а текущий каталог на нем — свойством:
49
property Directory: string;
При помощи метода
function GetIternPath(Index: Integer): string;
можно получить строку, содержащую полный путь к каталогу. Все символы в ней приводятся к
верхнему регистру. Параметр Index означает номер каталога в списке (начиная с 0).
Если список каталогов связать со статическим текстом (меткой), пользуясь свойством
(Р)э) property DirLabel: TLabel ;
то в ней также будет отображаться путь к текущему каталогу. При этом, если имя полного пути
не помещается в метке, то оно показывается в сокращенном виде (см. функцию MinimizeName).
Метод
procedure OpenCurcent ;
открывает текущий каталог, заменяя прежнее содержимое списка списком его дочерних подкаталогов.
Наконец, обновление содержимого списка у этого компонента осуществляет метод:
procedure Update;
Связать список каталогов с компонентом-списком файлов можно, обрабатывая событие:
(РЬ) property OnChange: TNotifyEvent;
или присвоив указатель на последний свойству:
(РЫ property FileList: TFileListBox;
В свойстве Items.Objects списка для каждого его элемента хранится объект типа TBitmap (пиктограмма, представляющая собой открытую или закрытую папку в зависимости от состояния каталога),
доступный для чтения и записи.
Компонент TFileListBox
TObject->TPersistent-”TComponent->TControl-”TWinControl->
—>TCustomListBox->TFlleListBox
Модуль FILECTRL
Страница Палитры компонентов System
Компонент представляет список файлов в текущем каталоге. Аналогично предыдущим компонентам, свойства
property Drive: char;
property Directory: string;
содержат имя устройства и каталога на нем, а метод
procedure Update;
обновляет список файлов. Метод
procedure AppiyFilePath (const EditText: string);
пытается полностью переустановить путь, заменив текущее значение на то, что содержится в
параметре EditText. При этом, если компонент связан со списками дисков и каталогов, то и там значения изменяются.
Свойство
(РЫ property FileEdit: TEdit;
содержит указатель на редактирующий элемент, которому автоматически передается имя текущего файла, которое также доступно как значение свойства:
property FileName: String;
В список можно включить не все файлы, а только обладающие атрибутами, определенными в
свойстве:
(Pb) property FileType: TFileType;
TFileAttr = (ftReadOnly, ftHidden, ftSystem, fCVolumeID, ftDirectory, ftArchive, ftNormal) ;
TFileType = set of TFileAttr;
Шаблон, используемый при включении файлов в список, задается свойством:
(Pb) property Mask: string;
Если свойство
50
(Pb) property MultiSelect: Boolean;
установлено в True, то в списке может быть одновременно выделено несколько
файлов.
При смене текущего файла возникает событие:
(РЙ property OnChange: TNotifyEvent;
Как и прежде, объекты Items.Objects в этом разделе представляют пиктограммы для разных типов файлов. Задает их показ значение True следующего свойства:
(Pb) property ShowGlyphs: Boolean;
Компонент TDirectoryOutline
TObject—>TPersistent->TComponent->TControl—”TWinControl—>
-^TCustomControl—^CustomGrid—^TCustomOutline-^TDirectoryOutline
Модуль DIROUTLN
Страница Палитры компонентов Samples
Этот компонент создан для отображения иерархического дерева файлов и каталогов текущего
диска и, таким образом, может заменить сразу компоненты TDirectoryListBox и TFileListBox. Он имеет
много общего с компонентом TOutline, отличие заключается в возможности работы только с файловой
структурой.
Значения текущего диска, содержимое файловой системы которого отображается в дереве,
устанавливается посредством свойства:
property Drive: Char;
Текущий каталог соответствует значению свойства:
property Directory: TFileName;
Как и у компонента-предка, у TDirectoryOutline имеется свойство Lines. В него на этапе разработки считывается иерархическое дерево файловой структуры. Добавлять туда новые строки не имеет
смысла, т. к. при запуске приложения файловая структура снова считывается, и строки с именами несуществующих каталогов и файлов удаляются.
Стиль отображаемых имен файлов и каталогов определяет свойство:
(РЬ) property TextCase: TTextCase;
TTextCase = (tcLowerCase, tcUpperCase, tcAsIs) ;
tcLowerCase — преобразование к нижнему регистру;
tcUpperCase — преобразование к верхнему регистру;
tcAsIs — без преобразования. Метод
function ForceCase(const AString: string): string;
может использоваться для пребразования строки, передаваемой в параметре AString, в соответствии с текущим стилем свойства TTextCase.
3.8. Вспомогательные компоненты
Таймер
TObject—>TPersistent—>TCoinponent—^TTimer
Модуль EXTCTRLS
Страница Палитры компонентов System
Этот невизуальный компонент предназначен для уведомления программы об истечении определенных промежутков времени. Компонент инкапсулирует системный таймер Windows и работает
через посылку сообщений WM_TIMER.
Свойство
(Pb) property Enabled: Boolean;
включает/выключает таймер, влияя на генерацию им событий. Будучи установлен в Enabled,
таймер начинает генерировать события OnTimer через интервал времени
(РЬ| property Interval: Word;
который измеряется в миллисекундах. После каждого истечения такого интервала инициируется обработчик, связанный с событием:
51
(Pb) property OnTimer: TNotifyEvent;
При этом программист получает очередной квант времени.
При активизации и деактивизации таймера или изменении интервала в системе может не оказаться свободных таймеров. В этом случае генерируется исключительная ситуация EOutOfResources.
Компонент TCalendar
TObject^TPersistent^TComponent-”TControl->TWinControl—>
—>TCustomControl—”TCustomGr id—”ТСа lends r
Модуль CALENDAR
Страница Палитры компонентов Samples
Компонент представляет собой календарь. Текущие год, месяц и день в календаре соответственно задаются свойствами:
J property Year: Integer;
property Month: Integer;
property Day: Integer;
Внешний вид его определяется свойствами:
(Pb) property GridLineWidth;
— толщина разграничивающих линий;
(Pa property Color;
— цвет панели компонента.
День, с которого начинается отображение недели, определяется свойством:
(Pb) property StartOfWeek: TDayOfWeek;
TDayOfWeek = 0..6 ;
О определяет неделю, принятую в англоязычных странах (первый день недели — воскресенье),
1 задает более привычный порядок — с понедельника. Следующие значения последовательно смещают
начало недели дальше.
Свойство
(pVl property UseCurrentDate: Boolean;
возвращает True в случае совпадения текущих дат компонента и системных часов компьютера.
Свойство
property CalendarDate: TDateTime;
содержит текущую дату в формате TDataTime. Все даты текущего месяца размещаются в векторном свойстве:
(Ro) property CellText[ACol, ARow: Integer]: string;
Методы
procedure NextMonth;
procedure NextYear;
procedure PrevMonth;
procedure PrevYear;
соответственно увеличивают или уменьшают на единицу значение текущего месяца или года.
Метод
procedure UpdateCalendar;
обновляет текущую дату календаря.
3.9. Форма и ее свойства
Последним в этом разделе будет рассмотрен самый важный, особый компонент — форма. Форма в Delphi — это синоним окна. В библиотеках для Windows (MFC, OWL) существуют отдельные
классы для каждой важной разновидности окон — диалогового окна, клиентских и дочерних окон MDI
и так далее. Разработчики VCL пошли по новому пути, объединив в одном классе свойства и методы
всех этих разновидностей. Специализация при этом все равно осталась — она определяется свойством:
(РЬ) property PcrmStyle: TPormStyle;
52
TPormStyle = [fsNoririal, fsMDIChild, fsMDIForm, fsStayOnTop) ;
fsNormal — обычное окно;
fsMDIChild — дочернее окно приложения MDI;
fsMDIForm — главное окно MDI;
fsStayOnTop — окно, остающееся поверх других даже в неактивном состоянии. (В данном
случае это касается только других окон приложения).
Такая разновидность, как диалоговое окно, не имеет собственного стиля.
Каким образом реализуются его особенности, описано ниже.
У TForm есть методы, которые
имеют смысл только для некоторых из
стилей FormStyle. Рассмотрим сначала
общие свойства и методы формы, а затем
— специфические.
Набор пиктограмм в заголовке
окна описывается свойством:
(РЬ) property Borderlcons: TBorderIcons;
TBorderIcon = (biSystemMenu,
biMiniinize, biMaximize);
TBorderIcons = set of TBorderIcon;
По умолчанию у обычной формы имеются все три пиктограммы — для вызова системного меню, разворачивания и сворачивают окна.
Обрамление формы задается свойством:
(Pb) property BorderStyle: TFormBorderStyle ;
TPormBorderStyle = (bsNone, bsSingle, bsSizeable, bsDialog);
Помимо двух стандартных стилей обрамления (bsNone и bsSingle) у формы есть еще два своих
типа (bsSizeable и bsDialog). Эти возможности стандартны для Windows. Особенности реализации здесь
заключаются в том, что стиль обрамления может изменяться в процессе выполнения программы.
Каждое окно может находиться в одном из состояний — нормальном, свернутом (минимизированном) и развернутом (максимизированном), что определяется свойством:
(рта property WindowState: TWindowState;
TWindowState = [wsNormal, wsMinimized, wsMaximized);
Значение этого свойства можно изменить как на этапе разработки, так и во время исполнения.
Окно в свернутом состоянии на экране отображается значком формы, который задается в свойстве:
(Pb) property Icon: TIcon;
Значок главной формы приложения идентифицирует само приложение в TaskManager Windows
3.11 и Windows NT или TaskBar Windows 95. Если он не определен, то используется значок приложения (объекта Application).
Каждая форма может иметь главное и всплывающее меню. Главное меню определяется свойством:
(РЬ| property Menu: TMainMenu;
Оно может быть у каждой формы (и только у формы). Для того чтобы не возникало путаницы с
несколькими одновременно видимыми меню в одном приложении, у компонентов TMainMenu предусмотрена возможность объединения в одном.
Всплывающее меню формы представлено свойством:
(Pb) property PopupMenu: TPopupMenu;
Форма может содержать объекты OLE. Для этого она должна быть зарегистрирована в качестве
их приемника. Свойство
53
property DropTarget: Boolean;
устанавливает, может ли форма быть приемником объектов OLE. Для размещения подменю
возможностей сервера OLE можно выделить отдельный пункт:
(Pb) property ObjectMenuItem: TMenuItem;
Этот пункт будет активизироваться при получении фокуса компонентом TOLEContainer.
Создание формы
Правила создания форм на этапе выполнения задаются в опциях проекта (страница Forms). Все
формы, которые вы включили в состав приложения на этапе визуального проектирования, могут быть
автоматически созданы при его запуске, для этого их имена должны находиться в списке Auto-Create
Forms. Все прочие формы (список Available Forms) нужно создавать самостоятельно, вызвав конструктор Create или метод Application.CreateForm.
Самым первьм событием, которое возникает при создании формы, является:
(Pb) property OnCreate: TNotifyEvent;
Это событие инициируется в конструкторе формы и позволяет выполнить операции по начальной установке формы и ее элементов до визуализации. В обработчик этого события стоит включить
присвоение значений неопубликованным свойствам формы и свойствам компонентов, которых нет в
Палитре компонентов, например, TApplication. Также здесь нужно "подключить" написанные вами обработчики событий для этих компонентов. В этом случае внешний вид и поведение форм будут корректными с самого начала.
Первоначально показана будет только одна — главная форма. Вы также можете придать этот
статус любой из всех автоматически создаваемых форм посредством опций проекта. По умолчанию
главной будет первая включенная в проект форма.
Для показа и активизации прочих форм необходимо вызвать одну из методов — Show или
ShowModal. Последний используется только для модальных форм, работа с которыми описана ниже.
При вызове метода
procedure Show;
происходит показ формы на экране. При показе она становится видимой и помещается поверх
других форм приложения, что сопровождается инициализацией события:
(РЬ) property OnShow: TNotifyEvent;
При этом форма получает то положение и размеры, которые задаются значениями свойства:
(Pa property Position: TPosition;
TPosition = (poDesigned, poDefault, poDefaultPosOnly,
poDefaultSizeOnly, poScreenCenter) ;
poDefault
— принять положение и размеры, определенные средой Windows по умолчанию;
poDefaultPosOnly — принять только положение, определенное средой Windows по умолчанию;
poDefaultSizeOnly — принять только размеры, определенные средой Windows по умолчанию;
poScreenCenter
— разместить окно по центру экрана (при этом размеры будут равны половине ширины и высоты экрана в пикселах, а для дочерней формы MDI — половине соответствующих
размеров клиентского окна);
poDesigned
— принять те значения положения и размеров, которые были определены во
время разработки приложения.
После того, как форма стала видимой, возникает необходимость в ее перерисовке. Программист
может определить, что и как изображается в клиентской области формы, описав реакцию на событие:
(Plo) property OnPaint: TNotifyEvent;
Для этого он должен использовать канву формы:
(Ro) property Canvas: TCanvas;
Обработчик события OnPaint вызывается до того, как начнется отрисовка всех компонентов,
принадлежащих форме. Поэтому нарисованное в нем может послужить фоном. Пример:
procedure TFormI.FormCreate(Sender: TObject) ;
begin
gr := TBitmap.Create ;
54
gr.LoadFromFile('c:\delphi\iinages\splash\256color\chemical.b(np') ,-end;
procedure TFormI.FormPaint(Sender: TObject);
begin
Canvas.Draw(0,0,gr) ;
end;
После отрисовки форма получает фокус ввода сообщений от системных устройств, т. е. становится активной. Об этом извещает наступление события:
(Pb) property OnActivate: TNotifyEvent;
Узнать, активна ли форма, можно путем обращения к ее свойству:
(Ro) property Active: Boolean;
При активизации формы ей посылается сообщение о настройке ее размеров (WM_SIZE). После
того, как размеры изменены, возникает событие
(Pb) property OnResize: TNotifyEvent;
которое может быть использовано для переустановки размеров как формы, так и всех элементов управления на ней. В следующем примере форма настраивает размеры своей клиентской области
точно по размерам содержащейся в ней таблицы DrawGridI, которая, в свою очередь, тоже пытается
получить максимально возможный в данной области размер:
procedure TFormI.FormResize(Sender: TObject);
var i,w,h : integer;
begin
if WindowState=wsNorinal then with DrawGridI do
begin
w := (ClientWidth - ColCount*GridLineWidth) div ColCount;
if w < 1 then w := 1;
h := (ClientHeight - RowCount*GridLineWidth) div RowCount;
if h < 1 then h := 1;
for i := 0 to ColCount-1 do ColWidths[i] := w;
for i := 0 to RowCount-1 do RowHeights [i] := h;
Inc(w, GridLineWidth);
Inc(h, GridLineWidth);
if (ClientWidth <> w * ColCount) or
(ClientHeight oh* RowCount) then
begin
Forml.ClientWidth := w * ColCount + 2*GridLineWidth;
Forml.ClientHeight := h * RowCount + 2*GridLineWidth;
end;
end;
end;
Здесь нужно обратить внимание на два момента. Во-первых, изменение размеров внутри обработчика OnResize влечет за собой как минимум один новый вызов этого события, поэтому возможны
неоднократные перерисовки и изменения размеров. Во-вторых, чтобы этот процесс рано или поздно
остановился, нужно ввести условия, при достижении которых размеры изменяться не будут, иначе
наступит зацикливание.
После изменения размеров вновь возникает событие OnPaint.
Разумеется, описанные выше события (OnPaint, OnActivate и OnShow) возникают не только при
инициализации формы, а и, к примеру, при каждом переключении между приложениями или формами
в приложении. Последние два действия имеют парные события, возникающие при утере фокуса или
исчезновении формы с экрана:
(pb) property OnDeactivate: TNotifyEvent;
(Pb) property OnHide: TNotifyEvent;
Метод
procedure Hide;
55
дает возможность сделать форму невидимой (изменяет свойство Visible). Во всех случаях, когда форма становится невидимой, возникает событие OnHide.
С помощью пары методов Show/Hide обычно управляют из главной формы показом прочих
форм. Будьте внимательны! При вызове Hide для главной формы подумайте о том, каким путем вы собираетесь после этого снова визуализировать ее.
Основной способ, используемый для закрытия формы — это вызов метода:
procedure Close;
В первую очередь внутри Close вызывается метод:
function CloseQuery: Boolean;
Для закрытия формы необходимо, чтобы он вернул True. Этот метод — прямой аналог метода
CanClose в библиотеке OWL. Для проверки на возможность закрытия он вызывает обработчик события:
(Pb) property OnCloseQuery: TCloseQueryEvent;
TCloseQueryEvent = procedure(Sender: TObject; var CanClose: Boolean) of object;
Здесь вы должны проверить возможность закрытия формы и вернуть нужное значение в параметре CanClose (который изначально имеет значение True). Например:
procedure TPorm2.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
begin
if Memo 1.Modified then case MessageDlgf' Save text?', mtWarning, mbYesNoCancel, 0) of
mrYes: Memol.Lines.SaveToFile('MYTEXT.TXT ') ;
mrCancel: CanClose := False;
end;
end;
Таким образом, если обработчик события OnCloseQuery не описан, возвращается True (форму
можно закрыть).
Для форм стиля fsMDIForm (родительских окон MDI) сначала должны закрываться все дочерние формы (вызываются их методы CloseQuery). Если хотя бы одна из них возвращает False, метод тут
же возвращает False, и обработчик события OnCloseQuery закрываемой родительской формы управления не получает.
Затем метод Close вызывает обработчик следующего события
(Pb) property OnClose: TCloseEvent;
TCloseEvent = procedure(Sender: TObject; var Action : TCloseAction) of object;
TCloseAction = (caNone, caHide, caFree, caMinimize);
что позволяет определить дальнейшую реакцию. Дело в том, что закрытие можно реализовать
различными способами, в зависимости от значения параметра Action:
caNone — не делать ничего;
caHide — сделать форму невидимой (скрыть);
caMinimize — свернуть форму в значок;
caFree — уничтожить форму, вызвав ее деструктор.
Перед вызовом OnClose в параметр Action засылается значение caHide. Исключение составляют
дочерние формы MDI, для которых засылается значение caMinimize или caNone в зависимости от возможности их сворачивания.
Если обработчик вернул значение caNone, форма не закрывается. Таким путем можно продублировать OnCloseQuery.
При закрытии главной формы приложения любое отличное от caNone значение вызывает завершение приложения. Для формы, не являющейся главной, значения caHide и caMinimize позволяют
соответственно скрыть и свернуть ее в значок. Выбор значения caFree влечет за собой вызов деструктора путем обращения к методу:
procedure Release;
Последним при уничтожении формы инициализируется событие:
(Pb) property OnDestroy: TNotifyEvent;
56
Это событие является парным по отношению к OnCreate; обработчик его вызывается в деструкторе формы. Здесь программист может предумотреть необходимые действия при уничтожении формы,
к примеру, записать значения в файл JNI.
Модальные формы
Компонент TForm в VCL объединяет в себе свойства и может играть роль как главного окна
программы, так и диалогового (модального) окна. Чтобы форма работала в качестве диалога, вместо
метода Show нужно вызвать метод:
function ShowModal: Integer;
Этот метод реализует весь жизненный цикл модальной формы. Он показывает ее на экране, активизирует ее и запрещает переключение на другие формы приложения. Начинается обработка сообщений, которая происходит до тех пор, пока свойство
(Rb) property ModalResult: TModalResult ;
не изменит своего значения, первоначально нулевого. Напомним, что это свойство формы меняется непосредственно с помощью некоторых видов принадлежащих ей кнопок (см. раздел "Кнопки").
Если для модальной формы программистом вызывается метод Close, то ее единственным действием
является то, что свойству ModalResult присваивается значение mrCancel (без вызовов OnCloseQuery и
OnClose).
Как только получено ненулевое значение ModalResult, вызьшается метод:
procedure CloseModal;
Его роль такая же, как у Close для обычной формы: сначала вызов CloseQuery, затем — генерация события OnClose. Установив в параметре Action этого события значение caNone, можно обнулить
ModalResult и тем самым воспрепятствовать закрытию. В противном случае форма деактивизируется и
делается невидимой.
Этот набор методов неприменим к дочерним формам MDI (со стилем fsMDIChild).
3.10. Управление дочерними элементами
Если вы хотите передать фокус одной из форм проекта, используйте метод этой формы:
procedure SetFocus;
Форма при этом обязана быть видимой и активной (Enabled), иначе создается исключительная
ситуация EInvalidOperation.
Два метода отвечают за передачу фокуса между визуальными компонентами в составе формы.
Метод
procedure FocusControl(Control: TWinControl);
передает фокус элементу управления Control, а метод
procedure DefocusControl(Control: TWinControl; Removing: Boolean);
отбирает его. Параметр Removing, равный True, означает, что элемент при этом должен передать фокус своему родительскому элементу. Напомним, что для элементов в эти моменты генерируются события OnEnter и OnExit. Имеющий фокус элемент определяется свойством:
(РЁ) property ActiveControl: TWinControl;
Оно может принимать значение nil, если фокус имеет сама форма.
В компоненте TForm доступен еще один метод, по предназначению сходный с FocusControl:
function SetFocusedControl(Control: TWinControl): Boolean;
Этот метод вызывается внутри многих компонентов (находящихся вне модуля FORMS) при получении фокуса ввода — именно поэтому он находится в секции public. Программисту не стоит его
использовать, т. к. он не документирован.
Форма относится к компонентам с возможностью прокрутки содержимого клиентской области.
К полосам прокрутки можно обратиться как к отдельным объектам с помощью свойств:
(pb) property HorzScrollBar;
(Pis) property VertScrollBar;
Если все элементы управления не могут быть видны при данных размерах формы, она может
быть снабжена полосами прокрутки автоматически. Это происходит, если свойство
(Pb) property AutoScroll;
57
установлено в значение True. Следующий метод помещает элемент управления AControl в видимую часть клиентской области:
procedure ScrollInView(AControl: TControl);
Он изменяет, если необходимо, положение бегунков полос прокрутки. При передаче фокуса
между элементами управления формы те из них, которые находятся вне ее видимой части, автоматически вызывают этот метод.
Форма имеет возможность перехватить ввод от клавиатуры, предназначенный имеющему фокус элементу управления. Установка свойства
(Pb) property KeyPreview: Boolean;
в True означает, что поступающие от клавиатуры сообщения будут сначала поступать форме, и
только затем — элементу. Пример использования этой возможности приведен в разделе "Обработка
событий от мыши и клавиатуры".
Немаловажной проблемой является то, как заставить форму и ее элементы выглядеть одинаково
при различных режимах работы видеоадаптера. Текущую разрешающую способность в точках на дюйм
можно задать при помощи свойства:
(Pb) property PixelsPerInch: Integer;
Вы можете присвоить этому свойству любое значение, превышающее 36, либо 0.
При ненулевом значении этого свойства во время создания форма и все ее элементы масштабируются в некоторой пропорции. Эта пропорция равна отношению числа точек на дюйм на экране в
данный момент к этому же числу во время разработки. Если же PixelsPerInch = 0, никакого масштабирования не производится.
Чтобы не изменять PixelsPerInch напрямую, разработчики предусмотрели другое — обобщающее — свойство:
(Pb) property Scaled: Boolean;
При установке Scaled в False PixelsPerInch обнуляется, в True — получает новое значение, равное числу точек на дюйм на экране в текущем видеорежиме.
Нужно учитывать, что если вы хотите сделать приложение, независимое от разрешения экрана,
то значение свойства Position у всех его форм должно отличаться от poDesigned — иначе они будут появляться в разных местах (в том числе и за пределами экрана).
Внешний вид формы и ее составных частей можно отобразить не только на экране. Метод
function GetForrnImage: TBitmap;
возвращает "моментальный снимок" формы — битовую карту с ее изображением. Печать на
принтере изображения формы, полученного при помощи GetFormImage, осуществляется методом:
procedure Print;
Полученное изображение передается объекту TPrinter (см. ниже), при этом его масштабирование производится в соответствии со значением свойства:
(Pb) property PrintScale: TPrintScale;
TPrintScale = (poNone, poProportional, poPrintToFit) ;
poNone — отсутствует;
poProportional — производится изменение масштаба в пропорции, равной соотношению точек
на дюйм у формы и принтера;
poPrintToFit — аналогично предыдущему, но осуществляется приведение размера изображения
к размеру листа.
Свойства форм MDI
На базе TForm можно создавать специфические виды форм. Перечисленные ниже свойства и
методы работают только в главных окнах приложений MDI.
(ро) property ClientHandle: HWND; procedure Содержит дескриптор клиентского окна (для главного окна MDI).
Arrangelcons;
Упорядочивает расположение свернутых в значок дочерних форм
MDI.
58
procedure Cascade;
Располагает дочерние формы MDI каскадом.
procedure Tilet-property TileMode: TTiieMode; TTileMode = (tbHorizontal, tbVertical) ;
Располагает дочерние формы MDI мозаикой. Стиль определяется
свойством TileMode.
Определяет стиль расположения дочерних форм — вертикальный
или горизонтальный.
Передает фокус следующей форме в списке дочерних форм.
procedure Previous; ($o) property ActiveMDIChild: TForm;
Передает фокус предыдущей форме в списке дочерних форм.
Содержит указатель на активную дочернюю форму.
(Ro) property MDIChildren[I: Integer]: TForm; Определяет количество дочерних форм.
Содержит указатель на 1-тую дочернюю форму.
(Pb) property WindowMenu: TMenuItem;
Указывает на тот пункт меню, к которому достраивается список
имен дочерних форм.
Функции из модуля FORMS
Топп) содержится также ряд функций. пвия формы и ее дочерних комВ модуле FORMS (где описан класс Они понентов.
важны для понимания взаимодейс!
function GetParentForm(Control: TControl)
: TForm;
function ValidParentForm(Control: TControL): TForm;
Возвращает указатель на форму, которой принадлежит элемент управления Control.
Делает то же, что и предыдущая функция, при этом, если форма не
найдена, возникает исключительная ситуация EInvalidOperation.
function DisableTaskWindows(ActiveWindow: HWnd) : Pointer;
Переводит в неактивное состояние все окна, кроме ActiveWindow. Эта
функция возвращает копию исходного списка окон (TaskWindowList).
procedure EnableTaskWindows(WindowList: Pointer);
Используется в паре с предыдущей функцией. Получив созданный ею список окон, переведенных в неактивное состояние,
EnableTaskWindows активизирует все окна (через вызов функции
API EnableWindow) и очищает список. Такой прием необходим
при создании модальных окон и управлении ими, например,
внутри метода MessageBox.
function AllocateHWnd(Method:
TWndMethod) : HWND;
Создает экземпляр окна вспомогательного класса TPUtilWindow
(не путать с классом как понятием объектно-ориентированного
программирования), обработчиком сообщений у которого служит метод Method.
procedure DeallocateHWnd(Wnd:
HWND) ;
Уничтожает созданный предыдущей функцией экземпляр окна.
59
function IsAccel(VK: Word; const Str:
string): Boolean;
Проверяет, является ли символ VK акселератором для текстовой
строки Str. Функция возвращает True, если в строке содержится
сочетание & ' VK '.
function KeysToShiftState(Keys: Word) Преобразует флаги состояния клавиатуры и кнопок мыши из ти: TShiftState;
па MKSHIFT, MKLBUTTON в принятый в Delphi тип
TShiftState.
function KeyDataToShiftState(KeyData: Производит аналогичное преобразование из виртуальных кодов
Longint): TShiftState;
клавиш Windows (например, типа VKSHIFT).
3.11. Приложение и среда его выполнения
Над всеми формами и компонентами стоит объект, отражающий собственно приложение, —
это объект Application (класса TApplication). Он держит в руках все нити управления другими объектами — создание и уничтожение форм, обработку сообщений, обслуживание исключительных ситуаций.
Помимо форм и принадлежащих им компонентов, "действующими лицами" для приложения
служат несколько других важных объектов. К ним относятся дисплей Screen (класс TScreen), принтер
Printer (класс TPrinter) и буфер обмена (класс TClipboard). Все они являются глобальными: именно на
их использование рассчитаны различные методы многих компонентов. Создание этих объектов происходит автоматически при запуске приложения, поэтому ни при каких обстоятельствах программистом
не должны создаваться другие их экземпляры.
Для хранения параметров приложения вы можете создать объекты, соответствующие файлам
инициализации (TIniFile).
Объект Application
TApplication = class(TComponent)
На уровне методов и свойств этого компонента осуществляется управление выполняемым приложением. Он отсутствует в Палитре компонентов, и все его свойства должны изменяться из программы.
Поводов для обращения к TApplication может быть два. Во-первых, это получение информации
о приложении, общей для всех ее форм. Во-вторых, это управление процессом выполнения приложения и входящими в него формами. Эти методы тесно связаны с Windows, и работа с многими из них
требует определенного опыта.
К информационным могут быть отнесены следующие свойства:
property Title: string;
Имя приложения. Под этим именем оно отображается в
TaskManager, TaskBar и т. п. По умолчанию равно имени проекта.
(Ro) property ExeName: string;
Имя файла с расширением .ЕХЕ, содержащего приложение (то же
самое имя возвращает функция ParamStr(0)).
property Icon: TIcon;
Значок приложения. Первоначально, при запуске, значок загружается из ресурса с именем MAINICON в файле ресурсов проекта.
Теперь рассмотрим методы и свойства в последовательности, соответствующей "жизненному
циклу" приложения. При запуске на выполнение Application сначала создает и инициализирует формы
приложения при помощи метода:
procedure CreateForm(FormClass: TFormClass; var Reference);
Параметр Reference будет указывать на созданный экземпляр формы класса FormClass.
С обращений к этому методу начинается код, содержащийся в файле с расширением .DPR; при
этом в нем создаются только те формы, которые содержатся в списке Auto-Create Forms в опциях про-
60
екта. Метод работает таким образом, что в случае, если указатель на главную форму равен nil, то созданная форма становится главной. Из этого следует, что главной всегда будет первая из создаваемых
форм. Указатель на нее описан в свойстве:
(Ro) property MainForm: TForm;
Здесь нужно сделать специальную оговорку. Помимо главной и других форм у приложения
есть еще одно — невидимое — окно. Именно оно является по-настоящему главным и именно в нем обрабатываются все сообщения Windows, управляющие состоянием приложения. Его дескриптор доступен в свойстве:
property Handle: HWnd;
После создания всех форм метод
procedure Run;
запускает цикл обработки сообщений главной формы
приложения. Его код очень прост
и выглядит следующим образом:
procedure TApplication.Runt-begin
AddExitProc(DoneApplication);
if FMainForm о nil then begin
FMainForm.Visible := True;
repeat
HandleMessage until Terminated;
end;
end;
Вызываемый в нем метод
procedure HandleMessage;
извлекает следующее сообщение из очереди и передает его на обработку следующим образом:
procedure TApplication.HandleMessage;
begin
if not ProcessMessage then Idle;
end;
Метод ProcessMessage ищет нужный обработчик сообщения и передает ему управление. Назначение метода Idle поясняется ниже.
О поступлении сообщения оповещает вызов обработчика события:
property OnMessage: TMessageEvent;
TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object;
Используя обработку OnMessage, программист может переопределить обработку любого (или
любых) сообщений (кроме WM_QUIT и сообщений системы оперативной подсказки). Для этого, выполнив внутри метода необходимые действия, в параметре Handled нужно вернуть True. Тогда сообщение не поступит в функцию окна приложения. Необходимо быть крайне осторожным, подменяя системные обработчики сообщений.
Существует другой способ перехвата сообщений. Для этого нужно написать метод, имеющий
следующий rim
TWindowHook = function (var Message: TMessage): Boolean of object;
и зарегистрировать его в качестве перехватчика сообщений приложения с помощью метода:
procedure HookMainWindowfHook: TWindowHook);
Если одна из процедур-перехватчиков типа TWindowHook в цепочке вернет True, это будет
означать, что сообщение обработано.
Когда необходимость в перехвате сообщений отпадет, созданный вами метод нужно исключить
из списка при помощи вызова метода:
procedure UnhookMainWindow(Hook: TWindowHook);
Если сообщений в очереди нет, то может быть выполнена "фоновая" работа, предусмотренная
программистом. Для этого нужен обработчик события, которое генерируется в этой ситуации внутри
метода Idle в HandleMessage:
property Onldle: TIdleEvent;
61
TIdleEvent = procedure (Sender: TObject; var Done: Boolean) of object;
Обработчик должен вернуть признак потребности в последующих событиях Onldle в булевом
параметре Done. Если "все сделано" и он присваивает Done значение True, то приложение переходит в
режим ожидания сообщения и обработчик не вызывается до возникновения следующей ситуации отсутствия сообщений в очереди. Если возвращается значение False, метод будет вызываться все время
при отсутствии сообщений.
Пример обработки сообщений OnMessage и Onldle имеется на дискете (название проекта
MOVLINES). Их использует программа сохранения экрана, которая вызывается системой по истечении
некоторого времени, в обработчике Onldle она рисует на экране линии и прекращает работу, когда обработчик OnMessage получает сообщение от мыши или клавиатуры.
Как видно из приведенного кода метода Run, сообщения обрабатываются вплоть до установки
флага:
(Ro) property Terminated: Boolean;
Такой флаг устанавливается при получении собщения WM_QUIT. Используя его, можно вставить свои действия на завершающем этапе (после окончания обработки сообщений перед вызовом деструктора Application). Свойство это доступно только для чтения, поэтому завершить приложение
можно, вызвав метод
procedure Terminate;
который и посылает сообщение WM_QUIT. Для обработки сообщений предназначен также метод
procedure ProcessMessages;
который возвращает управление тогда, когда обработаны все сообщения в очереди. Пример:
procedure TApplication.ProcessMessages;
begin
while ProcessMessage do;
end;
Одна или более форм приложения могут иметь статус fsStayOnTop, то есть постоянно находятся поверх других форм (не имеющих этого статуса). Для того, чтобы на время отключить его действие,
применяется пара методов:
procedure NormalizeTopMosts;
— отключает статус;
procedure RestoreTopMosts;
— восстанавливает его.
Эти методы могут быть полезными, когда необходимо вывести информацию поверх формы со
статусом fsStayOnTop. В самом объекте Application они вызываются соответственно при деактивизации/актнвизации приложения, сво-рачивании/разворачивании главной формы.
Активизация и деактивизация происходят тогда, когда пользователь или приложение осуществляют переключение между задачами Windows. Приложение способно отслеживать эти моменты.
При активизации и деактивизации приложения возникают события:
property OnActivate: TNotifyEvent;
property OnDeactivate: TNotifyEvent;
Текущее состояние активности можно узнать в свойстве:
property Active: Boolean;
События, возникающие соответственно при сворачивании и восстановлении главной формы
приложения, также относятся к объекту Application:
property OnMinimize: TNotifyEvent;
property OnRestore: TNotifyEvent;
Для программного выполнения таких операций есть два метода:
procedure Minimize;
procedure Restore;
У Application есть метод:
procedure BringToFront;
62
У формы также есть метод с этим названием, который показывает форму поверх остальных и
активизирует ее. Отличие этих методов в том, что Form. BringToFront активизирует вызвавшую его
форму, а метод Application.BringToFront — ту форму, которая была активна последней.
Во время выполнения приложения могут возникать исключительные ситуации. При их возникновении обработку осуществляет метод:
procedure HandleException(Sender: TObject);
Стандартная обработка подразумевает вызов метода
procedure ShowException(E: Exception);
который отображает диалоговое окно с именем приложения в заголовке и сообщением об
ошибке (содержащемся в параметре — объекте Е класса Exception).
Стандартную обработку можно перекрыть (во всех случаях, кроме обработки исключительной
ситуации EAbort), определив обработчик события:
property OnException: TExceptionEvent;
TExceptionEvent = procedure (Sender: TObject; E: Exception) of object;
Целый ряд методов управляет системой помощи. Для ее нормального функционирования в
первую очередь необходимо, чтобы было определено имя файла помощи в свойстве:
property HelpPile: string;
Если оно определено, то вызов помощи можно осуществить тремя видами запросов, которьм
соответствуют методы:
function HelpContext(Context:
Осуществляет вызов помощи по заданному контексту.
function HelpJump(const JumpID:
string): Boolean-
Осуществляет вызов помощи для контекстной строки JumpID.
Например, вызов Application.HelpJump('HelpJump ') даст подсказку по этой функции.
function HelpConunand (Command:
Word; Data: Longint): Boolean;
Посылает системе помощи команду Command с параметром Data. Для описания типов команд см. справку по процедурам WinHelp API Windows.
Все функции возвращают True, если файл Help определен, и произошел вызов приложения
WinHelp.
Если вы хотите перехватить обращение к справочной системе, то необходимо написать обработчик события:
property OnHelp: THelpEvent;
THelpEvent = funcс ion(Command: Word; Data: Longint;
var CallHelp: Boolean):Boolean of object;
В нем вы можете предусмотреть предоставление собственной помощи пользователю. Результат
True функции означает успех (помощь предоставлена). Если после обработки события по-прежнему
нужно вызвать систему помощи Windows (через WinHelp), то в параметре CallHelp нужно вернуть
True.
Метод
function MessageBox(Text, Caption: PChar; Flags: Word): Integer;
содержит вызов стандартного диалогового окна с тем же названием и назначением.
Если вы хотите использовать в составе Delphi и вызывать из форм приложения диалог, созданный с помощью других средств программирования, то дескриптор такого диалога должен быть присвоен свойству:
property DialogHandle: HWnd;
Подробные правила работы с этим свойством описаны в документации разработчика компонентов Delphi.
Наконец, упомянем о системе оперативной подсказки. У приложения имеется собственный
текст подсказки, определяемый свойством:
63
property Hint: string;
В отличие от подсказок для других компонентов, он не отображается при остановке мыши. Его
содержимое обычно передается строке состояния.
Целый ряд методов и свойств объекта, отвечающих за реализацию системы оперативной подсказки, подробно описан в соответствующем разделе. Поэтому они здесь только перечисляются:
property OnHint: TNotifyEvent;
property ShowHint: Boolean;
property HintPause: Integer;
property OnShowHint: TShowHintEvent ;
property HintColor: TColor;
procedure CancelHint;
3.12. Системные объекты - TClipboard и TSreen
TClipboard = class(TPersistent)
Объект TClipboard представляет программисту интерфейс с буфером обмена (Clipboard) Windows. При включении в проект модуля CLIPBRD этот глобальный объект создается автоматически и
доступен приложению в течение всего цикла.
Методы открытия и закрытия буфера обмена
procedure Open;
procedure Close;
вызываются во всех остальных методах TClipboard, поэтому программист редко нуждается в
обращении к ним. В объекте ведется счетчик числа обращений к этим функциям, так что соответствующие функции API Windows вызываются только при первом открытии и последнем закрытии.
Очистка содержимого буфера (для всех форматов) производится вызовом метода:
procedure Clear;
О доступных форматах можно узнать, пользуясь следующими свойствами и методами:
(Ro) property FormatCount: Integer;
— содержит число форматов в буфере на данный момент;
(Ro) property Formats[Index: Integer]: Word;
— содержит их полный список. Функция
function HasFormat(Format: Word): Boolean;
проверяет, содержится ли в данный момент формат Format.
Волею разработчиков различаются способы обмена графической и текстовой информацией через буфер обмена. Рассмотрим их независимо.
Через вызов метода
procedure Assign(Source: TPersistent);
в буфер обмена помещаются данные классов TGraphic, точнее, его потомков — классов TBitmap (формат CF.BITMAP) и TMetaffle (CF.METAFILEPICT). Данные класса TIcon не имеют своего
формата и с TClipboard несовместимы.
Допустимо и обратное: в TClipboard есть специальные (скрытые) методы для присваивания содержимого объектам классов TPicture, TBitmap и TMetafile. Допустимы выражения вида:
My Image.Picture.Assign(Clipboard) ;
Для работы с текстом предназначены методы:
function GetTextBuf(Buffer: PChar; BufSize: Integer): Integer;
— читает текст из буфера обмена в буфер Buffer, длина которого ограничена значением
BufSize. Функция возвращает истинную длину прочитанного текста;
procedure SetTextBuf(Buffer: PChar);
— помещает текст из Buffer в буфер обмена в формате CF_TEXT;
Свойство
property AsText: string;
соответствует содержимому буфера обмена в текстовом формате
CF_TEXT
(приведенному к типу string). При отстутствии там данных этого формата возвращается пу-
64
стая строка.
Методы
function GetAsHandle(Format: Word): THandle;
procedure SetAsHandle(Format: Word; Value: THandle);
соответственно читают и пишут данные в буфер в заданном формате Format. При чтении возвращается дескриптор находящихся в буфере данных (или О при отсутствии). Для дальнейшего использования эти данные должны быть скопированы. При записи данные, передаваемые в параметре
Value, в дальнейшем должны быть уничтожены системой (а не программой пользователя).
Два метода предназначены для обмена компонентами через буфер обмена (в специально зарегистрированном формате CF_COMPONENT):
function GetComponent(Owner, Parent: TComponent): TComponent;
procedure SetComponent(Component: TComponent);
Они используются составными частями среды Delphi.
Компонент TScreen
TScreen = class(TComponent);
Этот компонент представляет свойства дисплея, на котором выполняется приложение. Поскольку экземпляр данного класса только один (он создается системой при запуске приложения), то
большинство методов и свойств имеют информационный характер и недоступны для записи.
Курсор приложения, общий для всех форм, доступен через свойство:
property Cursor: TCursor;
Часто приходится включать "песочные часы" на время выполнения длительной операции. Лучше всего это сделать следующим образом:
Screen.Cursor := crHourglass;
try
(Calculations...} finally
Screen.Cursor := crDefault;
end;
Возвращает дескриптор курсора с индексом Index свойство:
property Cursors[Index: Integer]: HCURSOR;
Няпомшм, что шщексы зарегистрированных курсоров лежат в диапазоне от -17 (crSQLWait) до
0 (crDefault).
Рассмотренный ниже фрагмент кода при инициализации формы заносит имена всех зарегистрированных в системе курсоров в список ListBoxl. Затем, при выборе элемента списка, устанавливается соответствующий ему курсор.
procedure TFormI.FormCreate(Sender: TObject);
type
TGetStrPunc = function(const Value: string): Integer of object;
var
CursorNames: TStringList;
AddValue: TGetStrPunc;
begin
CursorNames := TStringList.Create;
AddValue := CursorNames.Add;
GetCursorValues(TGetStrProc(AddValue));
ListBoxl.Items.Assign(CursorNames) ;
end;
procedure TFormI-ListBoxlClick(Sender: TObject);
begin Screen.Cursor :=
StringToCursor(ListBoxl.Items[ListBoxl.Itemlndex]);
end;
Имена всех установленных в системе шрифтов помещаются в список, опреде-ленньш в свой-
65
стве:
(Ro\ property Fonts: TStrings;
Компонент сообщает неизменяемые свойства экрана (в данном видеорежиме). Его размеры в
пикселах определены в свойствах:
(RO) property Height: Integer;
(Ro) property Width: Integer;
Число точек на дюйм дисплея содержится в свойстве:
(Ro) property PixelsPerInch: Integer;
При появлении каждая форма заносит себя в список форм глобального объекта Screen. Два (доступных только для чтения) свойства дают информацию об этом списке:
J property Forms[Index: Integer]: TForm;
property FormCount: Integer;
Нужно иметь в виду, что в списке указаны только формы, открытые приложением, а не все окна
системы.
Следующие два свойства указывают на активную в данный момент форму и ее активный элемент управления:
(Ro) property ActiveControl: TWinControl;
(RO) property ActiveForm: TForm;
При их изменении генерируются соответственно события:
property OnActiveControlChange: TNotifyEvent;
property OnActiveFormChange: TNotifyEvent;
3.13. Файлы инициализации
TIniFile = class(TObject)
Этот класс — надстройка над файлом инициализации (его расширение .INI) и процедурами
чтения и записи в него значений разных типов. Обратите внимание, что все читаемые и записываемые
строки имеют тип string (а не pChar, как в стандартном API Windows).
Конструктор класса
constructor Create(const FileName: string);
создает объект, соответствующий файлу инициализации с именем FileName. В дальнейшем оно
доступно через свойство:
(Ro) property FileName: string;
Файл инициализации состоит из строк вида "параметр=значение". Такие строки сведены в секции; имена секций заключаются в квадратные скобки.
Методы класса, посредством которых осуществляется работа с файлом, приведены в таблице:
function ReadStringfconst Section, Ident, Возвращает строку из секции Section, являющуюся значением
Default: string): string;
параметра Ident. При отсутствии секции или параметра возвраprocedure WriteString(const
щается строка Default.
Записывает (или перезаписывает) в секцию Section строку
Ident=Value.
function Readlnteger(const Section,
Ident: string; Default: Longint): Longint;
Возвращает значение параметра Ident из секции Section, преобразованное в целое число. В случае любых ошибок возвращается значение Default.
procedure Writelnteger(const Section,
Преобразует Value в строку и записывает его в качестве значеIdent: string; Value: Longint);
ния Ident в секции Section.
function ReadBool(const Section, Ident: Работает как Readlnteger, но преобразует значение параметра в
string; Default: Boolean): Boolean;
булеву переменную.
66
procedure WriteBool(const Section,
Ident: string; Value: Boolean);
procedure ReadSection(const Section:
string; Strings: TStrings) ;
procedure ReadSectionValues(const Section: string; Strings: TSCrings) ;
procedure EraseSection(const Section:
string);
Работает, как Writelnteger, но преобразует булево значение Value к виду '0'/"Г.
Считывает в набор строк Strings секцию с именем Section. Максимальная длина секции — 8191 байт.
Считывает из секции Section в набор строк Strings только значения параметров.
Удаляет всю секцию с именем Section.
3.14. Печать данных из приложения
Практически каждое приложение, работающее в Windows, имеет набор возможностей, позволяющих печатать те или иные данные. В состав Delphi включен специальный модуль — PRINTERS, в
котором описан класс TPrinter, инкапсулирующий интерфейс печати Windows.
TPrinter = class(TObject)
Свойства и методы этого класса позволяют разработчику реализовать достаточно широкий
набор возможностей для печати из приложения. Экземпляр объекта TPrinter с именем Printer создается
автоматически при запуске приложения, если в его состав включен соответствующий модуль.
Обычно перед началом печати какого-либо документа из приложения необходимо проверить и
при необходимости переустановить стандартные параметры. Этот процесс может быть как автоматическим (проверка и переопределение по умолчанию из программы), так и интерактивным (при помощи
стандартных диалоговых окон PrintDialog и PrintSetupDialog).
Информацию обо всех инсталлированных в системе принтерах содержит список свойства:
(Ro) property Printers: TStrings;
Свойство доступно только при вьшолнении приложения. Информация о том, какой принтер из
списка является текущим, содержится в свойстве:
property Printerlndex: Integer;
Оно возвращает порядковый номер принтера в списке. Значение -1 используется для идентификации принтера, установленного по умолчанию.
Метод
procedure GetPrinter(ADevice, ADriver, APort: PChar;
var ADeviceMode: THandle);
возвращает параметры текущего принтера, используя для его идентификации значение свойства Printerlndex.
Метод
procedure SetPrinter(ADevice, ADriver, APort: PChar;
ADeviceMode: THandle);
проверяет, инсталлирован ли в системе принтер с заданными параметрами. В случае успеха
этот принтер становится текущим, в случае неудачи инсталлируется новый принтер. Использование
этих двух методов не рекомендуется справочным руководством, так как по мнению разработчиков
вполне достаточно свойств Printers и Printerlndex. Авторы также рекомендуют применять эти методы
только подготовленньм разработчикам, если им необходимо работать с драйверами принтеров.
Свойство
(Ro) property Fonts: TStrings;
содержит список шрифтов, поддерживаемых текущим принтером. Свойство доступно только
при вьшолнении приложения.
Расположение листа бумаги определяется свойством:
property Orientation: TPrinterOrientation;
TPrinterOrientation = (poPortrait, poLandscape) ;
Свойство доступно только при выполнении приложения. Высоту и ширину листа бумаги со-
67
держат свойства:
(Ro) property PageHeight: Integer;
(R^ property PageWidth: Integer;
Свойство
property Title: string;
содержит текстовую строку, которая используется для идентификации процесса печати (работы) в списке Диспетчера печати Windows.
Дескриптор принтера, с которым связан объект TPrinter, возвращается в свойстве:
(Ro) property Handle: HDC;
Поверхность печатаемой страницы доступна для вывода графических объектов (например,
изображений) через свойство:
(Ro) property Canvas: TCanvas;
При использовании канвы необходимо проверить используемый принтер на предмет поддержки графики. Свойство доступно только при выполнении приложения.
Для управления процессом печати объект содержит набор методов и свойств:
procedure BeginDoc;
Начинает печать документа.
procedure EndDoc;
Завершает печать документа.
(Ro) property Printing: Boolean;
Определяет состояние процесса. Возвращает True во время печати.
procedure Abort;
Используется для прерывания печати.
(Ro) property Aborted: Boolean;
Устанавливается в True, если печать прервана.
procedure NewPage;
Отменяет печать текущей страницы и начинает распечатку следующей. При этом значение свойства PageNumber увеличивается на
единицу.
(Ro) property PageNumber: Integer; Содержит номер печатаемой страницы. Обращение к нему имеет
смысл только в процессе печати документа.
В модуле PRINTERS описан метод
procedure AssignPrn(var F: Text);
который связывает текстовую файловую переменную с текущим принтером, что позволяет
направлять на принтер текстовые данные, используя традиционные процедуры Write и Writeln. При
печати используется шрифт, определенный в канве объекта TPrinter. Следующий пример распечатывает содержимое многострочного редактора
PrintMemo
при
нажатии кнопки PrintBtn:
procedure PrintForm.PrintBtnClick(Sender: TObject);
var
PrnTxt: System.Text; i: Integer;
begin
AssignPrn(PrnTxt) ;
Rewrite(PrnTxt) ;
for i := 0 to PrintMemo. Lines .Count -1 do Writeln (PrnTxt, PrintMemo.Lines [ i ]);
CloseFile(PrnTxt) ;
end;
Для печати графики необходимо передать требуемый графический объект (изображение, график, фигуру) в канву объекта Printer. В следующем примере при нажатии кнопки PrintBfcn печатается
изображение из компонента Printlmage:
procedure PrintForm.PrintBtnClick(Sender: TObject);
begin
with Printer do
begin
BeginDoc;
68
Canvas.Draw(0, 0, Printlmage.Picture.Graphic);
EndDoc ;
end
end;
При необходимости пропорции распечатываемого графического объекта можно скорректировать при помощи свойства формы-контейнера PrintScale.
4. СПРАВОЧНИК ПО ФУНКЦИЯМ DELPHI
В этом разделе мы приведем справочную информацию по функциям, имеющимся в библиотеке
времени выполнения Delphi. Они сгруппированы по областям применения. Не рассматриваются:
• функции, содержащиеся в модуле SYSTEM и составляющие неотъемлемую часть самого языка Паскаль. Они не изменялись фирмой Borland уже на протяжении многих версий, и приводить их
здесь нет необходимости;
Большинство упомянутых здесь функций сосредоточено в модуле SYSUTILS. Для функции,
которая относится к другому модулю, приведено его имя.
4.1. Функции работы со строками
В Delphi вынужденно уживаются два стандарта работы со строковыми данными. Первый из
них, когда длина строки записывается в ее первом байте, традиционен для Паскаля. Ему соответствует
тип данных string. Другой подразумевает, что строка заканчивается нулевым символом. Такие строки
имеют тип PChar и применяются в основном при обращении к функциям API Windows.
Естественно, для выполнения операций со строками этих типов нужно предусмотреть два различных набора функций, которые и описаны ниже.
Первая таблица содержит функции для работы с типом string. Все перечисленные в ней функции находятся в модуле SYSUTILS.
function NewStrtconst S: String): Создает копию строки S и возвращает указатель на нее.
Pstring;
procedure DisposeStr(P: PString) Уничтожает строку, на которую указывает Р.
;
procedure AssignStr(var P:
Pstring; const S: strings-
Уничтожает строку, на которую указывает Р и затем присваивает ему
адрес созданной копии строки S.
procedure AppendStrfvar Dest:
string; const S: string);
Добавляет строку S в конец строки Dest.
function Uppercase(const S:
string): string;
Преобразует символы 'a'..'z' в строке S к верхнему регистру.
function LowerCase(const S:
string): string;
Преобразует символы 'A'..'Z' в строке S к нижнему регистру.
function CompareStr(const SI,
S2: string): Integer;
Сравнивает две строки S1 и S2 с учетом регистра символов. Возвращаемое значение равно 0 в случае равенства строк или разности кодов
пары первых несовпадающих символов.
function CompareText(const SI,
S2: string): Integer;
Сравнивает две строки без учета регистра символов.
function AnsiUpperCase(const S: Преобразует символы в строке к верхнему регистру с учетом языковоstring): string;
го драйвера.
69
function AnsiLowerCase(const S: Преобразует символы в строке к нижнему регистру с учетом языковоstring) : string;
го драйвера.
function AnsiCompareStr(const
SI, S2: string): Integer;
Сравнивает две строки с использованием языкового драйвера и с учетом регистра символов.
function AnsiCompareText(const Сравнивает две строки с использованием языкового драйвера и без
SI, S2 : string) : Integer;
учета регистра символов.
function IsValidldent(const Ident: Возвращает True, если строка Ident может служить идентификатором в
string): Boolean;
программе на Object Pascal (т. е. содержит только буквы и цифры,
причем первый символ — буква).
function IntToStr(Value:
Longint): string;
Преобразует целое число в строку.
function IntToHex(Value:
Преобразует целое число в строку с его шестнадцатиричным предLongint; Digits: Integer): s t r ing ставлением.
;
function StrToInt(const S: string): Преобразует строку в целое число. При ошибке возникает исключиLongint;
тельная ситуация EConvertError.
function StrToIntDef(const S:
string; Default; Longint): Longint
;
function LoadStr(Ident: Word) :
string;
function FmtLoadStr(Ident:
Word; const Args: array of const):
string;
Работает как StrToInt, но при ошибке возвращает значение Default.
Загружает строку с индексом Ident из ресурсов приложения.
Загружает строку с индексом Ident из ресурсов приложения с форматированием (см. описание функции Format).
Следующая таблица содержит функции для работы со строками типа PChar (также находятся в
модуле SYSUTILS):
function StrLIComp(Strl, Str2:
Работает как StrLComp, но без учета регистра символов.
PChar; MaxLen: Cardinal) : Integer;
function StrScantStr: PChar; Chr: Отыскивает первое вхождение символа Chr в строку Str и возвращает
Char) : PChar;
указатель на него или nil в случае отстутствия.
function StrRScanfStr: PChar;
Работает как StrScan, но отыскивается последнее вхождение Chr.
Chr: Char) : PChar;
function StrPos(Strl, Str2: PChar) Отыскивает первое вхождение строки Str2 в строку Strl и возвращает
: PChar;
указатель на нее или nil в случае отстутствия.
function StrUpperfStr: PChar) :
PChar;
function StrLower(Str: PChar):
PChar;
function StrPaslStr: PChar):
String;
function StrAlloc(Size: Cardinal):
PChar;
Преобразует строку к верхнему регистру.
Преобразует строку к нижнему регистру.
Преобразует строку Str в строку типа string.
Размещает в куче памяти новую строку размером Size и возвращает
указатель на нее.
70
function StrBufSize(Str: PChar):
Cardinal;
function StrNewfStr: PChar):
PChar ;
procedure StrDispose(Str: PChar);
Возвращает размер блока памяти, выделенного для строки при помощи функции StrAlloc.
Размещает в куче памяти копню строки Str и возвращает указатель на
нее.
Уничтожает строку, размещенную при помощи StrAlloc или StrNew.
function StrLenfStr: PChar):
Возвращает число символов в строке Str (без учета завершающего нулевого).
Возвращает указатель на завершающий нулевой символ строки Str.
function StrEndfStr: PChar):
PChar;
function StrMove(Dest, Source:
PChar; Count: Cardinal): PChar;
Копирует из строки Source в строку Dest ровно Count символов, причем строки могут перекрываться.
function StrCopy(Dest, Source:
PChar): PChar;
function StrECopy(Dest, Source:
PChar): PChar;
function StrLCopy(Dest, Source:
PChar; MaxLen: Cardinal):
PChar;
function StrPCopy(Dest: PChar;
const Source: String): PChar;
Копирует Source в Dest и возвращает указатель на Dest.
function StrPLCopy(Dest: PChar;
const Source: string; MaxLen:
Cardinal): PChar;
function StrCat(Dest, Source:
PChar): PChar;
function StrLCatfDest, Source:
PChar; MaxLen: Cardinal) :
PChar;
function StrCoirip(Strl, Str2:
PChar): Integer;
Работает как StrPCopy, но копирует не более MaxLen символов.
function StrIComp(Strl, Str2:
PChar): Integer;
function StrLComp(Strl, Str2:
PChar; MaxLen: Cardinal): Integer;
Работает как StrComp, но без учета регистра символов.
Копирует Source в Dest и возвращает указатель на завершающий символ Dest.
Работает как StrCopy, но копирует не более MaxLen символов.
Копирует строку Source (типа string) в Dest и возвращает указатель на
Dest.
Дописывает Source к концу Dest и возвращает указатель на Dest.
Работает как StrCat, но копирует не более MaxLen-StrLen(Dest) символов.
Сравнивает две строки (посимвольно). Возвращает значение: <0 —
при Strl <Str2, 0 — при Strl =Str2, >0 — при Strl >Str2.
Работает как StrComp, но сравнение происходит на протяжении не более чем MaxLen символов.
4.2. Функции работы с файлами
Эта часть библиотеки претерпела изменения в Delphi. К функциям, которые работали с файлом
через файловую переменную, добавились функции, работающие с дескриптором файла. Они рассматривают файл как двоичный; с текстовыми файлами нужно работать "по старинке".
Файловые функции, описанные в модуле SYSUTILS, приведены в таблице:
function File0pen(const FileОткрывает существующий FileName файл в режиме Mode (см. примеч.
Name: string; Mode: Word) : In- 1). Значение, возвращаемое в случае успеха, — дескриптор открытого
teger;
файла. В противном случае — код ошибки DOS.
71
function FileCreate(const PileСоздает файл с именем FileName. Возвращает то же, что и FileOpen.
Name: string): Integer;
function FileRead(Handle: Inte- Считывает из файла с дескриптором Handle Count байт в буфер Buffer.
ger; var Buffer; Count: Longint): Возвращает число реально прочитанных байт или -1 при ошибке.
Longint;
function FileWrite(Handle: Integer; const Buffer;
Записывает в файл с дескриптором Handle Count байт из буфера
Buffer. Возвращает число реально записанных байт или -1 при ошибке.
function FileSeek(Handle: Integer; Позиционирует файл с дескриптором Handle в новое положение. При
Offset: Longint; Origin: Integer): Origin = 1,2,3 положение смещается на Offset байт от начала файла,
Longint;
текущей позиции и конца файла соответственно. Возвращает новое
положение или -1 при ошибке.
procedure FileClose(Handle:
Закрывает файл с дескриптором Handle.
function FileAge(const
Возвращает значения даты и времени создания файла или -1, если
файл не существует.
function FileExists(const
Возвращает True если файл FileName существует к найден.
function FindFirst(const Path:
Ищет первый файл, удовлетворяющий маске поиска, заданной в Path и
string; Attr: Integer; var
с атрибутами Attr (см. примеч. 2). В случае успеха заполняет запись
SearchRec: TSearchRec): Integer; SearchRec (см. примеч. 3) и возвращает 0, иначе возвращает код
ошибки DOS.
function FindNext(var SearchRec: Продолжает процесс поиска файлов, удовлетворяющих маске поиска.
TSearchRec): Integer;
Параметр SearchRec должен быть заполнен при помощи FindFirst.
Возвращает 0, если очередной файл найден, или код ошибки DOS.
Изменяет SearchRec.
procedure FindClose(var
Завершает процесс поиска файлов, удовлетворяющих маске поиска.
function FileQetDate(Handle: In- Возвращает время создания файла с дескриптором Handle (в формате
teger) : Longint;
DOS) или -1, если дескриптор недействителен.
procedure
FileSetDate(Handle: Integer;
Устанавливает время создания файла с дескриптором Handle (в формате DOS).
function FileGetAttr(const FileName: string): Integer;
Возвращает атрибуты (см. примеч. 2) файла с именем FileName или
код ошибки DOS, если файл не найден.
function FileSetAttrtconst FileName: string; Attr:
Устанавливает атрибуты файла с именем FileName.
Уничтожает файл с именем FileName и в случае успеха возвращает
True.
function RenameFile(const Old- Переименовывает файл с именем OldName в NewName и возвращает
Name, NewName: string): Boole- True в случае успеха.
an;
function DeleteFile(const
72
function ChangeFileExt(const
FileName, Extension: string):
string;
function ExtractFilePath(const
FileName: string): string;
Изменяет расширение в имени файла FileName на Extension и возвращает новое значение FileName. Имя файла не изменяется.
function ExtractFileName(const
FileName: string): string;
Извлекает из строки с полным именем файла FileName часть, содержащую его имя и расширение.
function ExtractFileExt(const
FileName: string): string;
Извлекает из строки с полным именем файла FileName часть, содержащую его расширение.
function ExpandFileName(const
FileName: string): string;
Возвращает полное имя файла FileName, добавляя при необходимости
путь к нему и переводя все символы в верхний регистр.
Извлекает из строки с полным именем файла FileName часть, содержащую путь к нему.
function FileSearch(const Name, Производит поиск файла с именем Name в группе каталогов, заданных
DirList: string): stringsпараметром DirList. Имена каталогов должны отделяться друг от друга
точкой с запятой. Возвращает в случае успеха полное имя файла или
пустую строку, если файл не найден.
function DiskFree(Drive: Byte):
Longint;
Возвращает количество в байтах свободного места на заданном диске.
Значение параметра Drive: 0 — для текущего диска, 1 — для А, 2 —
для В и т. д. Если параметр неверен, функция возвращает -1.
function DiskSize(Drive: Byte):
Longint;
function FileDateToDateTime(FileDate: Longint):
TDateTime;
function DateTimeToFileDate(DateTime:
TDateTime): Longint;
Возвращает размер диска Drive в байтах. Параметр Drive означает то
же, что и в DiskFree.
Преобразует дату и время в формате DOS в принятый в Delphi формат
TDateTime.
Преобразует дату и время из формата TDateTime в формат DOS.
Примечания:
1. Допустимы следующие режимы открытия файлов:
Режим
Значение
Что означает
fmOpenRead
$0000
Открыть только для чтения.
fmOpenWrite
$0001
Открыть только для записи.
fmOpenReadWrite
$0002
Открыть для чтения и записи.
fmShareCompat
$0000
Обеспечить совместимость со старой моделью доступа к файлам.
Режим
Значение
Что означает
fmShareExclusive
fmShareDenyWrite
fmShareDenyRead
fmShareDenyNone
$0010
$0020
$0030
$0040
Запретить другим доступ к файлу.
Запретить другим запись в файл.
Запретить другим чтение файла.
Разрешить другим все виды доступа.
2. Файлы могут иметь следующие атрибуты:
73
faReadOnly = $01;
faHidden = $02;
faSysFile = $04;
faVolumeID = $08;
faDirectory = $10;
faArchive = $20;
faAnyFile = $3F;
3. Формат структуры TSearchRec таков:
TSearchRec = record
Fill: array[1..21] of
Byte;
Attr: Byte;
Time: Longint;
Size: Longint;
Name: string[12] ;
end;
Приведем типовой вариант организации групповой обработки файлов при помощи функций
FindFirst, FindNext и FindClose.
В
нем в список добавляются имя и длина всех файлов в каталоге с
именем CADELPHI:
var SearchRec: TSearchRec; I : Integer; List : TStringList;
begin
List := TStringList.Create;
I := FindFirst('c:\delphi\*.*', faAnyFile, SearchRec);
while I = 0 do
begin
List.Add(Format (' File %s has length %d
bytes ', [SearchRec.Name,
SearchRec.Size])) ;
I := FindNext(SearchRec);
end;
FindClose(SearchRec) ;
List.Free;
end;
При поиске файла наиболее вероятным является код ошибки -18 (файл не найден).
Процедура FindClose не делает ничего в 16-разрядной версии Delphi, однако будет играть роль
в 32-разрядной. Употреблять ее следует из соображений переносимости приложений.
4 Ряд "старых" функций переименованы: это связано с появлением одноименных методов у
объектов. Теперь вместо Assign следует употреблять AssignFile, вместо Close — CloseFile. Старые имена поддерживаются ддя совместимости, но могут вызвать проблемы при одновременном употреблении
с именами методов.
4.3. Функции форматирования строк
Еще со времен языка FORTRAN у программистов появилась необходимость форматировать
строки, направляемые на устройства вывода. Требовался легкий способ для того, чтобы не просто преобразовать число в строку, но также объединить эту строку с другими и включить ее в состав целого
предложения.
В Delphi существует группа функций форматирования строк. Их рассмотрение начнем с наиболее часто встречающегося представителя — функции Format:
function Format (const Format: string; const Args: array of const) : string;
Первым параметром функции выступает форматирующая строка. Это — обычная текстовая
строка, но в ней на нужных местах стоят специальные символы, которые определяют, какие и как туда
будут подставлены параметры.
Второй параметр функции Format называется списком аргументов. Он и содержит "вставляе-
74
мые" в форматирующую строку параметры. Обратите внимение, что этот открытый массив имеет тип
array of const. и в нем может передаваться переменное число разнотипных параметров. Например, после выполнения
S := FormatC Product %s , version %d.%d', [ 'Borland Delphi' , 1, 0]);
строке S будет присвоено • Product Borland Delphi , version 1.0'. Рассмотрим подробнее правила,
по которьм составляется форматирующая строка.
"Специальные" места в ней называются спецификаторами формата и представляют собой
следующие конструкции
"%" [index":"] ["-"I [width] ["."prec] type
где обозначены:
• символ -%", с которого начинаются все спецификаторы формата (обязательный);
• поле индекса аргумента [ index " : " ] (необязательное);
• признак выравнивания по левому краю ["-"I (необязательный);
• поле ширины [width] (необязательное);
• поле точности [ - . - prec ] (необязательное);
• символ типа преобразования type (обязательный).
Каждый спецификатор формата соответствует как минимум одному из элементов списка аргументов и определяет, как именно рассматривать его и преобразовывать в строку.
Функция Format поддерживает следующие символы типа преобразования:
Символ Тип
Что означает
d
Десятичный Элемент должен быть целым числом и преобразуется в строку десятичных
цифр. Если при символе есть поле точности, то в выходной строке будет минимум ргес цифр; если при преобразовании числа их потребуется меньше, то
строка дополняется слева нулями.
х
Шестнадца- Как тип d, но элемент преобразуется в строку шестнадцатиричных цифр.
тиричный
Научный
Элемент должен быть числом с плавающей точкой. Он преобразуется к нормализованному виду -d.dddE+ddd. Перед десятичной точкой всегда находится одна цифра (при необходимости, с минусом). После символа "Е" идет показатель
степени, имеющий знак и состоящий не менее чем из 3 цифр. Общее число символов в представлении числа определяется полем ргес (по умолчанию 15).
е
f
Фиксированный
д
Обобщенный
пm
Числовой
Денежный
Элемент должен быть числом с плавающей точкой. Он преобразуется к виду ddd.ddd. Поле ргес означает количество цифр после десятичной точки (по умолчанию 2).
Элемент должен быть числом с плавающей точкой. Он преобразуется к одному
из двух вышеперечисленных видов — выбирается тот, который обеспечивает
более короткую запись. Если требуемое число цифр слева от десятичной точки
меньше значения поля ширины и само число больше 10" , применяется фиксированный тип. Удаляются все впереди идущие нули, десятичная точка (при
возможности), а также символы, разделяющие строку на группы по три цифры.
Совпадает с типом f за исключением того, что после каждой группы из трех
цифр ставятся разделительные символы:
d,ddd, ddd.ddd....
Элемент должен быть числом с плавающей точкой, отражающей денежную
сумму. Способ ее представления зависит от значений глобальных констант,
определенных в модуле SYSUTILS (см. ниже). В свою очередь, они зависят от
настройки Windows на обозначения, принятые в данной стране.
75
Р
Указатель
s
Строка
Элемент должен быть указателем. На выходе он будет представлять из себя
строку шестнадцатиричных цифр вида
ХХХХ: YYYY, где ХХХХ — селектор, YYYY — смещение.
Элемент может иметь тип string, PChar или быть отдельным символом (Char).
Эта строка (или символ) вставляются вместо спецификатора формата. Если
присутствует поле ргес и длина строки больше его значения, то она усекается.
Примечания:
1. Форматирующие символы можно задавать как в верхнем, так и в нижнем регистрах.
2. Тип преобразования m (денежный) тесно связан с правилами представления, принятыми в
конкретной стране. Для этого в модуле SYSUTILS определены значения ряда типизированных констант, начальные значения которых берутся из секции [Inti] файла WIN.INI. Вы можете изменять их
значения в соответствии с вашими потребностями:
Имя и тип
Что означает
CurrencyString: string[7];
Символ (или символы), добавляемые к строке с представлением денежной суммы и обозначающие национальную денежную единицу. Это может быть, например, '$', 'DM' или 'руб'.
CurrencyFormat: Byte;
Определяет способ добавления знака денежной единицы к строке. Число
1 будет преобразовано так в зависимости от значения этого параметра:
'$!' при 0; '1$' при 1; '$ Г при 2; '1 $' при 3.
NegCurrFormat: Byte;
Определяет способ добавления знака денежной единицы и минуса к
строке в том случае, если происходит преобразование отрицательного
числа (к положительным числам отношения не имеет). Число -1 будет
преобразовано так в зависимости от значения этого параметра: '($!)' при
0; '-$!' при 1; '$-1' при 2; '$!-' при 3; '(!$)' при 4; '-!$' при 5; 'i-$' при 6; '!$-'
при 7; '-1 $' при 8; '-$ Г при 9; '$ 1-' при 10.
CurrencyDecimals: Byte;
Число знаков после запятой в представлении денежных сумм. Например,
число 10.15 при разных значениях этого параметра отобразится так: '$10'
при 0, '$10.15' при 2, '$10.1500' при 4.
ThousandSeparator: Char;
Символ, разделяющий строку на группы по три цифры справа налево
(разделитель тысяч). Применяется, в частности, в типе преобразования п.
DecimalSeparator: Char;
Символ, отделяющий дробную часть числа от целой.
Мы закончили рассмотрение символа типа преобразования. Рассмотрим остальные составные
части спецификатора формата.
Поле ширины устанавливает минимально допустимое количество символов в преобразованной
строке. Это означает, что если она короче, чем задано в этом поле, то происходит добавление пробелов
до требуемого количества. По умолчанию пробелы добавляются спереди (выравнивание по правому
краю), но признак выравнивания по левому краю ("-"; он должен стоять перед полем ширины) позволяет
выравнивать строку по-иному.
Поле индекса позволяет динамически изменить последовательность извлечения аргументов из
массива. Обычно аргументы извлекаются последовательно, по мере их востребования спецификаторами формата. Поле индекса означает, что следующим нужно извлечь аргумент с данным индексом.
Пользуясь индексом, одни и те же аргументы могут быть использованы многократно. Например, вызов
Format ( ' %s %s %0 : s %s ', [ 'Yes', 'No' ] ) Даст на выходе строку 'Yes No Yes No'.
Поле точности играет разную роль в зависимости от того, с каким типом преобразования применяется. Его особенности приведены вместе с описанием типа (с теми из них, где оно имеет смысл).
76
Поля индекса, ширины и точности могут быть заданы напрямую или косвенно. Первый способ
подразумевает явное указание значения поля (например, ' %10 . 5f '). Для косвенного задания значений
нужно в соответствующих местах цифры заменить звездочками (например, '%*.*f'). В этом случае вместо звездочек будут подставлены следующие значения из списка аргументов (они обязательно должны
быть целыми числами). Например, выражение
Format('Value is %*.*',[10,5, 2.718]);
эквивалентно:
Format('Value is %10.5',[2.718]);
Как уже упоминалось, наиболее употребимой функцией из группы форматирующих является
Format, которая работает со строками типа string и максимально освобождает программиста от рутинной работы. Другие функции используют то же ядро и правила преобразования, но отличаются параметрами:
procedure FmtStr(var Result: string; const To же, что и Format, но оформлено в виде процедуры. РезульFormat: string; const Args: array of const); тат преобразования возвращается в параметре Result.
function StrFmt(Buffer, Format: PChar;
const Args: array of const): PChar;
Форматирующая строка должна находиться в параметре Format, а результирующая помещается в буфер Buffer (он должен
иметь нужную длину). Функция возвращает указатель на Buffer.
function StrLFmt(Buffer: PChar; MaxLen: Работает как StrFmt, но длина результирующей строки не буCardinal; Format: PChar; const Args: ar- дет превышать MaxLen символов.
ray of const) : PChar;
function FormatBuf(var Buffer; BufLen: Форматирующая строка находится в буфере Format длиной
Cardinal; const Format; FmtLen: Cardinal; FmtLen, a результирующая — в буфере Buffer длиной BufLen.
const Args: array of const) : Cardinal;
Функция возвращает число реально помещенных в Buffer символов, причем оно всегда меньше или равно BufLen.
4.4. Функции преобразования чисел с плавающей точкой
Преобразование числа с плавающей точкой (далее в этом разделе просто числа) в текстовую
строку и обратно всегда было достаточно сложной задачей. Для ее решения в Delphi есть функции сразу трех уровней сложности.
Первый — самый простой — представлен функцией FloatToStr:
function FloatToStr ••-••-alue : Extended): string;
Число, заданное параметром Value, преобразуется в возвращаемую функцией строку. Формат
преобразования соответствует типу преобразования g функции Format, причем длина выходной строки
принимается равной 15 символам.
Больше возможностей для управления форматом вывода дает функция:
function PloatToStrF(Value: Extended; Format:
TFloatFormat; Precision, Digits: Integer): string;
Здесь Value — преобразуемое значение, Format — один из предопределенных форматов. Хотя
этот параметр имеет тип TFloatFormat, он имеет очень много общего с типами преобразований в функции Format (ссылки на них есть в предлагаемой таблице). Параметр Precision задает общее число символов в выходной строке и не должен превышать 7 для фактического параметра типа Single, 15 — для
Double и 18 — для Extended. Digits — это параметр, интерпретируемый в зависимости от значения параметра Format:
77
ffExponent
Научный формат, соответствует типу е. Precision задает общее число символов, Digits —
число знаков в показателе экспоненты {0-4).
ffFixed
Формат с фиксированной точкой; соответствует типу f. Precision задает общее число
символов, Digits — число знаков после запятой (0-18). Если значение Precision мало для
представления числа, используется научный формат.
ffGeneral
Обобщенный формат, соответствует типу д (см. описание функции Format).
ffNumber
Отличается от fTFixed наличием символов-разделителей тысяч (см. тип преобразования
п).
ffCurrency
Соответствует типу преобразования т. Параметр Digits задает число символов после десятичной точки в выходной строке (0-18).
В случае, когда в функцию переданы значения Value, соответствующие особым случаям сопроцессора ("не-число", плюс и минус бесконечность), она возвращает соответственно строки 'NAN', 'INF'
и '-INF'.
Наконец, возможность полного управления форматом предоставляет функция FormatFloat:
function FormatFloat(const Format: string; Value: Extended): string;
Она преобразует число в строку в соответствии со спецификатором формата, содержащимся в
параметре Format. Правила его задания следующие:
0
Поле для цифры. Если форматируемая величина имеет в этой позиции цифру, то вставляется она, в противном случае вставляется 0.
#
Поле для цифры. Если форматируемая величина имеет в этой позиции цифру, то вставляется она, в противном случае ничего не вставляется.
Поле для десятичной точки. Сюда вставляется символ, определенный константой DecimalSeparator.
;
Поле для разделителя тысяч. Оно означает, что группы по три цифры, считая влево от десятичной точки, будут разделяться специальным символом (он задан константой ThousandSeparator). Местоположение поля может быть произвольным.
Е+, Е-, е+,
е-
Признаки представления числа в научном формате. Появление любого из этих аргументов
означает, что число будет преобразовано с характеристикой и мантиссой. Вставка нулей
после такого аргумента позволяет определить ширину мантиссы. Разница между Е+, е+ и
Е-, е-в том, что в первых двух случаях ставится "+" при выводе положительных чисел.
'хх' "хх"
Символы, заключенные в обычные или двойные кавычки, напрямую включаются в выходную строку.
;
Разделяет спецификаторы формата для положительных, отрицательных и нулевых чисел.
Примечания:
1. Число всегда округляется до той точности, которую позволяет заданное программистом количество полей для размещения цифр ('0' и '#').
2. Если у преобразуемого числа слева от десятичной точки получается больше значащих цифр,
чем задано полей для их размещения, то цифры все равно добавляются в строку. Если полей недостаточно справа от точки, происходит округление.
78
3. Символ ';' позволяет задать три разных формата вывода для чисел с разным знаком. При различном количестве форматов они применяются следующим образом:
• один: применяется для всех чисел;
• два: первый применяется для чисел, больших или равных нулю, второй — для отрицательных;
• три: первьш применяется для положительных, второй — для отрицательных чисел, третий —
для нуля.
Если форматы для отрицательных чисел или нуля пусты, применяется формат для положительных чисел.
Если пуст формат для положительных чисел или спецификатор формата вообще не задан (пустая строка), то числа форматируются согласно обобщенному формату (как в функции FloatToStr). Такое форматирование применяется также в случае, если число значащих цифр слева от десятичной точки превысило 18 и не задан научный формат.
Применение спецификатора иллюстрируется в таблице на примере преобразования четырех чисел:
Спецификатор
1234
-1234
0.5
0
0
0.00
#.##
1234
1234.00
1234
-1234
-1234.00
-1234
1
0.50
.5
0
0.00
#.##0.00
#,##0.00;(#,##0.00
)
#,##0.00;;Zero
О.ОООЕ+00
#.###Е-0
1,234.00
1,234.00
-1,234.00
(1,234.00)
0.50
0.50
0.00
0.00
1,234.00
1.234Е+03
1.234ЕЗ
-1,234.00
-1.234Е+03
-1.234ЕЗ
0.50
5.000Е-01
5Е-1
Zero
0.000Е+00
0Е0
Две следующие функции применяют те же правила, что и рассмотренные выше функции, но
отличаются параметрами:
function FloatToText(Buffer: Соответствует FloatToStrF, но выходная строка помещается в буфер
PChar; Value: Extended; For- Buffer (без начальной длины!), а число символов в ней возвращается саmat: TFloatFormat; Precision, мой функцией.
Digits: Integer) : Integer;
function FloatToСоответствует FormatFloat, но выходная строка помещается в буфер
TextFmt(Buffer: PChar; Value: Buffer (без начальной длины!), а число символов в ней возвращается саExtended; Format: PChar): In- мой функцией.
teger;
Наконец, процедура:
procedure FloatToDecimal(var Производит подготовительный анализ преобразуемого числа, занося в
Result: TFloatRec; Value: Ex- поля записи Result различные его характеристики.
tended; Precision, Decimals:
Integer);
Перейдем к рассмотрению функций преобразования текстовой строки в число. Их две — соответственно для строк типа string и PChar:
function StrToPloat(const S: string): Extended;
function TextToFloat(Buffer: PChar; var Value: Extended): Boolean;
Общие правила для передаваемой в функцию строки таковы:
79
• допускаются как научный, так и фиксированный форматы;
• в качестве десятичной точки должен выступать символ, который содержится в DecimalSeparator;
• не допускаются символы-разделители тысяч (ThousandSeparator), а также символы обозначения денежньк единиц.
В случае ошибки преобразования функция StrToFloat генерирует исключительную ситуацию
EConvertError, a TextToFloat — возвращает значение False.
4.5. Функции работы с датами и временем
В Delphi для хранения даты и (или) времени предусмотрен формат TDateTime, представляющий
собой, на самом деле, обычное число с плавающей точкой.
При этом дата представляется целой частью числа, а время — дробной. Преимущество этого
подхода в том, что теперь даты и времена можно корректно складывать и вычитать, например:
var StartTime: tDateTime;
procedure TFormI.PormCreate(Sender: TObject);
begin
StartTime := Now;
end;
procedure TFormI.FormClick(Sender: TObject);
begin
Labell.Caption := 'Co времени запуска прошло' +
DateTimeToStr(Now - StartTime);
end;
Функции этой группы приведены в таблице:
function EncodeDate(Year,
Преобразует дату, заданную раздельно годом, месяцем и днем, в формат
Month, Day: Word):
TDateTime. Если они выходят за допустимые пределы, возникает ИС
TDateTime;
EConvertError.
procedure DecodeDatefDate:
TDateTime; var Year, Month,
Day: Word);
function EncodeTimefHour,
Min, Sec, MSec: Word):
TDateTime;
procedure DecodeTime(Time:
TDateTime; var Hour, Min,
Sec, MSec: Word) ;
function DayOfWeektDate:
TDateTime): Integer;
function Date: TDateTime;
Преобразует дату в виде TDateTime к раздельным составляющим: году,
месяцу и дню.
function Time: TDateTime;
function Now: TDateTime;
function DateToStr(Date:
TDateTime): string;
function TimeToStr(Time:
TDateTime): string;
function DateTimeToStr(DateTime: TDateTime):
string;
Возвращает текущее время.
Возвращает текущие дату и время.
Преобразует дату Date в текстовую строку.
Преобразует значение времени, заданное часом, минутой, секундой и
миллисекундой в формат TDateTime.
Преобразует значение времени в формате TDateTime в раздельные составляющие: час, минуту, секунду и миллисекунду.
Возвращает номер текущего дня недели от 1 (воскресенье) до 7 (суббота).
Возвращает текущую дату.
Преобразует время из типа TDateTime в текстовую строку.
Преобразует дату и время из типа TDateTime в текстовую строку.
80
function StrToDatefconst S:
string): TDateTime;
Преобразует текстовую строку в дату типа TDateTime. Строка должна
содержать два или три числа, разделенных символом, содержащимся в
DateSeparator. Формат даты в строке определяется в соответствии со значением переменной ShortDateFormat.
function StrToTime(const S:
string): TDateTime;
Преобразует текстовую строку S в значение времени. Должен использоваться формат HH:NN:SS, где SS — может принимать значения 'AM' или
ТМ', иначе означает секунды. Если необходимо получать время в 24-х
часовой шкале, то элемент SS формата опускается. Если преобразование
невозможно, возникает ИС EConvertError.
function
StrToDateTime(const S:
string): TDateTime;
function FormatDateTime(const Format:
string; DateTime: TdateTime):
string;
procedure DateTimeToStringfvar Result: string;
const Format: string;
DateTime: TDateTime);
Преобразует текстовую строку S в дату и время в формате MM/DD/YY
HH:NN:SS (элемент SS см. выше).
Преобразует дату и время из типа TDateTime в текстовую строку, используя формат, задаваемый параметром Format (см. примеч. 1).
Преобразует дату и время типа TDateTime в строку Result, используя
формат, передаваемый параметром Format (см. примеч. 1).
Примечания:
1. Правила задания спецификатора формата для даты и времени отличаются от рассмотренных
выше. В первую очередь надо иметь в виду то, что функции вроде FormatDateTime оперируют над одним значением TDateTime, а спецификаторы формата извлекают из него те или иные составные части.
При этом могут использоваться:
с
Отображает дату, используя формат ShortDateFormat, затем отображается время в формате
LongTimeFormat. Время не отображается, если дробная часть переменной DateTime нулевая.
d
Отображает число месяца без нуля в левом разряде (1-31).
dd
Отображает число месяца с нулем в левом разряде (01-31).
ddd
dddd
ddddd
dddddd
m
mm
Отображает день недели в соответствии с сокращенными именами из переменной
ShortDayNames.
Отображает день недели в соответствии с полными именами из переменной
LongDayNames.
Отображает дату в соответствии с форматом ShortDateFormat.
Отображает дату в соответствии с форматом LongDateFormat.
Отображает месяц как число без нуля в левом разряде (1-12).
Отображает месяц как число с нулем в левом разряде (01-12).
Отображает месяц в соответствии с сокращенными именами из переменной ShortMonthNames.
П1ГПГПП Отображает месяц в соответствии с полными именами из переменной Long MonthN am es.
1
УУ
Отображает год двумя цифрами.
irirnm
81
УУУУ
h
hh
n
nn
s
ss
t
tt
am/pin
arnpm
a/p
/
Отображает год четырьмя цифрами.
Отображает час как число без нуля в левом разряде (0-23).
Отображает час как число с нулем в левом разряде (00-23).
Отображает минуты как число без нуля в левом разряде (0-59).
Отображает минуты как число с нулем в левом разряде (00-59).
Отображает секунды как число без нуля в левом разряде (0-59).
Отображает секунды как число с нулем в левом разряде (00-59).
Отображает время в соответствии с форматом ShortTimeFormat.
Отображает время в соответствии с форматом LongTimeFormat.
Отображает время в 12-часовой шкале. 'AM' означает часы до полудня, 'РМ' — часы после
полудня.
Отображает время в 12-часовой шкале. При этом для времени до полудня отображается
содержимое переменной TimeAMString, а после полудня — TimePMString.
Отображает время в 12-часовой шкале, 'а' означает часы до полудня, 'р' — часы после полудня.
Отображает символ — разделитель даты, содержащийся в переменной DataSeparator.
Отображает символ — разделитель времени, содержащийся в переменной TimeSeparator.
'XX'/'XX'
Символы, заключаемые в простые или двойные кавычки, отображаются как есть и не подвергаются форматированию.
Например, результатом вызова функции с параметрами
FormatDateTime (' "Совещание состоится в" dddd, dirrmim, yyyy, ' + ' "в" hh:nn',
StrToDateTime('12/31/95 23:55')) ;
будет строка 'Совещание состоится в среду, 31 декабря 1995 в 23:55' (естественно, если вы переопределили значения элементов массива LongMonth-Names).
2. Спецификаторы am/pm, ampm, a/p используются вместе со спецификаторами h и hh.
3. Спецификаторы могут быть указаны как в верхнем, так и в нижнем регистрах символов —
это не играет роли.
4. Начальные значения всех используемых при форматировании даты и времени переменных
берутся из секции [inti] файла WIN.INI.
2Л4.6. Функции для работы с памятью
Для работы с памятью Delphi предоставляет следующие функции:
function
AllocMemfSize: SYSUTILS Выделяет блок памяти размером Size и возвращает указатель
Cardinal): Pointer;
на него. Выделенная память обнуляется.
function ReAllocMem(P:
SYSUTILS Изменяет размер блока памяти размером CurSize, на который
Pointer; CurSize, NewSize:
указывает Р. Если удается выделить блок размером NewSize,
Cardinal): Pointer;
функция возвращает указатель на него. Содержимое старого
блока переносится в новый. При этом, если CurSize>NewSize,
часть содержимого отбрасывается, а если CurSize<NewSize, то
остаток свободного места заполняется нулями.
82
function MemAlloc(Size:
Longint): Pointer;
GRAPHICS Выделяет блок размером Size. Если он меньше 64К, память
выделяется в куче памяти Delphi, в противном случае — глобальная.
Память, выделенная при помощи этих функций, может быть освобождена при помощи функции
FreeMem.
У компонента же определение конструктора на удивление лаконично, но в нем присутствует
указатель на Owner:
constructor Create(AOwner: TComponent);
Ответ прост: Owner — тот объект, который при создании вызывает конструкторы всех объектов, владельцем которых он является, а при уничтожении — их деструкторы. Таким образом, поместив
при разработке компонент на форму, вы можете не заботиться о его создании и уничтожении в программе. Все свойства таких компонентов и все связи между ними запоминаются в файле формы .DFM
и при запуске приложения создаются вновь автоматически.
Деструктор компонента
destructor Destroy;
уничтожает все дочерние компоненты (вызывая их деструкторы) и вычеркивает себя из списка
компонента-владельца.
Конструкторы нужно вызывать только для тех компонентов, которые создаются вами во время
исполнения. При этом они получат владельца и явный вызов деструктора не понадобится. Практически
все конструкторы и деструкторы описанных ниже компонентов имеют тот же вид, что приводится
здесь. Поэтому в дальнейшем их описания опущены, где это возможно.
Принадлежащие компоненты оформлены в виде списка, доступного через свойство
Components. В классе TComponent предусмотрен набор методов и свойств для управления этим списком:
property Components[Index: Integer]: Возвращает указатель на компонент с индексом в списке Index.
TComponent;
property ComponentCount: Integer;
Возвращает число компонентов в списке.
function FindComponent(const AName:
Возвращает ссылку на компонент-потомок с заданным именем.
string): TComponent;
procedure InsertComponent(AComponent: TComponent) ;
procedure Rerr.oveCornponent
(AComponent: TComponent) ;
Вставляет компонент в конец списка.
procedure DestroyComponents;
Удаляет все компоненты из списка.
Удаляет компонент из списка.
Изменять владельца компонента во время исполнения можно, но без особой необходимости не
нужно. Дело в том, что используемый для этого метод RemoveComponent не только вычеркивает компонент из списка, но и обнуляет указатель на него в соответствующем поле владельца. Например, если
во время визуального проектирования вы поместили на форму кнопку Buttoni, а затем во время выполнения изменили ее владельца, то все обращения к ней в программе как к Buttoni стали недействительны.
Рассмотрим другие свойства компонента. Он может находиться в одном из множества следующих состояний:
property ComponentState: TCornponentState;
TComponentState = set of (csLoading, csReading, csWriting, csDestroying, csDesigning) ;
Эти состояния используются средой во время визуального проектирования, при загрузке/выгрузке компонентов из файлов ресурсов. Во время исполнения приложения множество состояний
83
пусто. Лишь метод
procedure Destroying;
устанавливает в состоянии компонента флаг csDestroying и вызывает аналогичный метод у всех
потомков из списка. Он также используется в деструкторе компонента.
Свойство Designlnfo относится к стадии разработки (конструирования) приложения:
property Designlnfo: Longint;
В нем хранятся координаты верхнего левого угла компонента во время разработки.
84
Download