Вопросы для экзамена по программированию в Delphi

advertisement
Вопросы для экзамена по
программированию в Delphi
Основы языка Object Pascal в Delphi
1. Структура программы на Паскале. Расширения в Object Pascal по
сравнению с классическим Паскалем.
Паскаль и Delphi (Object Pascal)
В системе Delphi 7 используется специализированная, постоянно совершенствуемая
версия языка программирования Паскаль, которая называется Delphi (в шестой и
более ранних вариантах системы Delphi она называлась Object Pascal, ≪Объектный
Паскаль≫). Эта версия включает набор расширений, ориентированных только на
применение в рамках среды Delphi 7 и предназначенных для ускоренного создания приложений.
Запись программы
Программа на Паскале записывается с помощью набора символов, включающего
латинские буквы (регистр не имеет значения), цифры, символ подчеркивания и
стандартные знаки препинания. Элементы программы отделяются друг от друга с
помощью произвольного числа пробелов и пустых строк.
Некоторые элементы языка записываются путем комбинации двух специальных
символов, например:
.. // := <>
Программа содержит ключевые (или зарезервированные) слова, как стандартные,
так и пользовательские (включаемые в программу разработчиком), а Также идентификаторы и выражения.
В качестве идентификатора может выступать любая последовательность из букв,
цифр и символа подчеркивания, начинающаяся не с цифры. Например:
Unitl
Integer
х
for
There_are_Dates
Go478
•} ЗАМЕЧАНИЕ В качестве пользовательских идентификаторов нельзя использовать
зарезервированные слова и стандартные идентификаторы.
Основы языка Delphi (Object Pascal) 23
При анализе исходного текста программы компилятор не различает прописных и
строчных букв, то есть можно написать, например:
begin
end;
а можно:
Begin
End;
Эти записи тождественны.
Правила записи команд Паскаля путем комбинирования ключевых слов и идентификаторов называются синтаксическими правилами или просто синтаксисом записи.
Главный файл
В программе может быть любое количество модулей (несколько сот или вообще
ни одного), но только один главный файл проекта. Этсп файл чаше всего невелик и
содержит обращения к модулям. Он имеет расширение .DPR и создается средой
Delphi 7 автоматически. Начинается этот файл с ключевого слова program (программа), за которым следует название программы и точка с запятой.
program DemoProgram,Стандартные функции и процедуры
Решение практически любой задачи можно запрограммировать самостоятельно от
начала до конца. Однако при составлении программ очень часто возпикае-i потребность выполнить какое-либо действие, которое уже использовалось в различных
программах. Например, при математических расчетах нужно вычисление тригонометрических функций, а программированием этих вычислений наверняка уже не
раз занималось множество программистов. Поэтому в систему Delphi 7 входит
обширный набор стандартных модул ей, содержащих стандартные функции. Такие
модули представляют собой готовый откомпилированный и оптимизированный
код, предназначенный для решения самых разных задач. Чтобы вычислить значение
синуса числа, не надо реализовывать алгоритм вычисления синуса заново, достаточно просто написать;
sin(3)
ЗАМЕЧАНИЕ Функции для вычисления синусо, косинуса и арктангенса входят в
стандартный модуль System.
Все функции в Паскале (не только стандартные) записываются так: сначала следует
название функции, потом в круглых скобках — список параметров через запятую
(если параметров несколько).
Основы языка Delphi (Object Pascal) 29
Помимо функций, в Паскале имеются стандартные процедуры. Если функции
используются для вычисления конкретных значений (как функция синуса), то процедуры предназначены для выполнения каких-то часто встречающихся действий
(например для вывода информации на экран). И процедуры, и функции могут не
требовать ни одного параметра — тогда круглые скобки за их названием не указываются.
ReadLn;
(отличия и расширения)рассказать структуру программы(слово program
далее блоки объявления переменных и тд.все,что используется должно быть
СТРОГО ранее объявлено)расширения касаются того,что блоков var,const мб
больше.еще можно сказать,что добавлена модульность.
http://www.intuit.ru/department/pl/intdelphi/11/
2.Модули Object Pascal (unit), их структура и спользование.
Модули
Программа на Паскале состоит из набора модулей (Unit), в каждом из которых содержится описание логически независимой части программы (например, описание
работы конкретного окна или описание алгоритма вычисления сложной математической функции). Расширение имени файлов, содержащих модули — .PAS. Модули
Основы языка Delphi (Object Pascal) 27
программы часто создаются системой Delphi 7 автоматически, например при добавлении новой формы. При этом происходит автоматическая генерация исходного
текста соответствующего модуля, что избавляет программиста от рутинной работы.
') ВНИМАНИЕ Вносить изменения в исходный код программы, созданный автоматически, в большинстве случаев не разрешается. Это может при вести
к возникновению серьезных ошибок на этапе компиляции.
Модули могут иметь связь друг с другом, то есть из одного модуля разрешается
обращаться к функциям других модулей. Применение модулей во время разработки программы напоминает применение компонентов во время проектирования
экранных форм в том плане, что позволяет повторно использовать программный
код, созданный ранее.
ЗАМЕЧАНИЕ В реальности исходные тексты компонентов Delphi 7 представляют
собой обычные модули Паскаля, содержащие описание логики
работы и способа отображения на экране соответствующих компонентов,__
Способы подключения модулей
Модули подключаются к главной программе и к другим модулям с помощью следующей конструкции языка:
uses список-мо дул ей ,Список модулей представляет собой список названий модулей, перечисленных
через запятую. Он может включать как модули, созданные разработчиком для текущей программы, так и стандартные, входящие в поставку Delphi 7. Все указанные
модули должны существовать.
Например:
uses SysUТils, Forms, MyUnit;
В некоторых случаях местонахождение исходного текста модуля требуется задавать явно. Подобная потребность возникает в следующих случаях:
О модуль расположен в отдельном каталоге, и в настройках Delphi 7этот каталог
не указан;
О модули из разных каталогов имеют одинаковые имена.
Для решения этих проблем в операторе uses после названия соответствующего модуля указывается ключевое слово in, а за ним в одинарных кавычках приводится
путь к исходному тесту данного модуля:
uses Windows,
MyUnit in 'c:\projects\games\dune5\MyUnit.pas', Main;
Структура модуля
Ранее была рассмотрена структура простой консольной программы, содержащей в
одном файле весь исходный текст. Полноценные приложения Windows строятся
по другому принципу. В их главной части с расширением .DPR хранится только
вызов нескольких команд, открывающих главное окно, а также выполняющих завершающие действия. Вся остальная логика содержится в файлах, хранящих описание дополнительных подключаемых модулей.
Каждый модуль имеет жестко заданную структуру, которая обычно автоматически
генерируется системой Delphi 7 при его создании. Модуль состоит из четырех частей:
интерфейсной части, части реализации (обязательные части), части инициализации
И части завершения (необязательные части). Сначала указывается заголовок модуля ключевое слово Unit, за ним произвольное название модуля (оно должно совпадать с названием файла, в котором модуль хранится) и точка с запятой:
Unit TestUnit;
(Данный модуль будет храниться в файле testunit.pas.)
Интерфейсная часть описывает информацию, которая доступна из других частей
программы: из других модулей и главной части. Часть реализации описывает информацию, которая недоступна из других модулей. Подобное разделение модуля на
части позволяет создавать и распространять модули в откомпилированном виде
(расширение .DCU), прикладывая к ним только описание интерфейсной части (наличпя
исходных текстов модуля, если имеется файл .DCU, не требуется). При этом внести
изменения в такой модуль нельзя, а исходный код, реализующий описанные в интерфейсной части возможности, недоступен. Такой подход, во-первых, позволяет повторно использовать ранее написанные для других программ и уже отлаженные
модули, во-вторых, разграничивает доступ к модулю нескольких программистов,
в-третьих, позволяет разбивать программу на набор логически независимых модулей.
ЗАМЕЧАНИЕ Модули, применяемые при создании программ с помощью системы
Delphi 7, могут быть созданы прикладным разработчиком, а могут
быть стандартными: входящими в поставку Delphi 7 и включающими в себя множество самых разных полезных возможностей. К стандартным относится, е частности, модуль Sysfem, который содержит
основные и постоянно встречающиеся подпрограммы Паскаля. Его
не требуется подключать и явно указывать с помощью слова uses —
он считается исходно подключенным к каждому модулю автоматически.
Интерфейсная часть всегда идет первой и начинается с ключевого слова interface,
а часть реализации начинается с ключевого слова implementation:
Unit TestUnit;
interface
imp1ementat ion
82 Урок 1. Язык Delphi (Object Pascal) и его использование
После заголовков этих частей можно дополнительно указать модули, подключаемые
к данному модулю, с помощью ключевого слова uses, за которым следует список
модулей через запятую, а в конце списка ставится точка с запятой.
ЗАМЕЧАНИЕ Модули, подключаемые в интерфейсной части, доступны из любого
места модуля, а модули, подключаемые в части реализации, — во
всем модуле за исключением интерфейсной части.
Части инициализации и части завершения необязательны. Указанные в них действия выполняются, соответственно, в самом начале и в самом конце работы программы и только один раз.
Если модулей в программе несколько, то последовательность выполнения их частей инициализации соответствует порядку их описания в модуле, где они подключаются с помощью ключевого слова uses, а последовательность выполнения частей завершения ей противоположна.
Например, если имеются модули А и В и в главной программе они подключаются
так:
uses В, А;
то первой выполнится часть инициализации модуля В, а второй — часть инициализации модуля А. Когда программа завершит свою работу, первой выполнится
завершающая часть модуля А, а второй — завершающая часть модуля В.
Часть инициализации начинается с ключевого слова
initialization
часть завершения — с ключевого слова
finalization
В конце модуля всегда ставится слово end и точка.
В самом общем случае структура пустого модуля будет такой:
Unit имя-модуля;
interface uses список-модулей,Implementation uses список-модулей;
initialization
finalization
end.
ЗАМЕЧАНИЕ Если в модуль вносятся изменения, то при выполнении компиляции
среда Delphi 7 автоматически проверяет все взаимосвязи между
модулями с помощью ключевого слова uses и при необходимости
выполняет также компиляцию других модулей, связанных с измененным.__
3.Числовые типы данных в Delphi: целочисленные, действительные,
диапазоны. Размещение в памяти, основные функции для работы с
ними. Способы задания литералов.(литералы для VB.NETдля )
ВОПРОС:как извлечь с такого-то по такой-то бит из числа?
Функции:
SizeOf(<Тип>) - размер в байтах
TypeInfo(<Тип>) – RTTI Run Time Type Information (Информация о
типах времени исполнения программы)
№ бита в байте
0
Адрес байта
№ бита в слове
7 0
7
0
0
7
1
7 8
0
Адрес байта
Поле
№ бита в значении поля
Смещение поля в записи
15
15
7 0
8 7
7
7
1
A
1
0
(обратный)
0
0
0
0
0
(прямой)
№ бита в байте
0 7
B
5 0
6
(прямой)
0 7
0
0
C
701
14
1
A
5
0
B
0 7
6
C
0 10
14
(обратный)
Типы данных
Все данные, используемые в программе, всегда относятся к конкретным типам
данных. Например, число 32000 относится к типу Integer (целое), число 2,87 — к типу
Real (число с десятичной запятой). Применяемые разработчиком значения должны
укладываться в допустимый диапазон значений для имеющихся в Паскале типов.
Целые числа
Целые числа записываются в программе с помощью последовательности цифр,
перед которой может стоять знак числа: символ ≪+≫ или •≪-≫. Если знак не указан,
то считается, что число положительное. Например:2 -3 3
+617
В следующей таблице перечислены стандартные типы целых чисел и соответствующие им диапазоны допустимых значений. Чем большее количество значений может
содержать тип, тем больше памяти он занимает.
Название
Длина, байт
Диапазон значений
Cardinal
4
0. .. 2 147 483 647
Byte
1
0...255
Shortint
1
-128...+127
Smallint
2
-32 768...+32 767
Word
2
0...65 535
Integer
4
-2 147 483 648...+2 147 483 647
Longint
4
-2 147 483 648...+2 147 483 647
Int64
8
-9*1018...+9*1018
LongWord
4
0. . .4 294 967 295
Следует отметить, что целые числа могут быть представлены не только в десятичной, но и
в шестнадцатеричной системе счисления, т.е. в виде $xxxxxxxx, где x - один из символов
0, 1, ..., 8, 9, A, B, ..., E, F. К примеру, все цвета (точнее, их коды) представляются именно
в виде шестнадцатеричных чисел.
Дробные числа
Дробные числа содержат дробную часть, которая отделяется от целой части десятичной точкой. В таких числах допускается также дополнительно указывать символ е
(или Е), за которым следует число, сообщающее, что левую часть дополнительно
надо умножить на 10 в соответствующей степени. Например:
Запись 2е+5 означает 2, умноженное на 10 в степени 5 (200000);
Запись 31.4Е-1 означает 31,4, умноженное на 10 в степени -1 (3,14).
Ниже приведены основные стандартные типы дробных чисел и соответствующие
им диапазоны допустимых значений. В таблице для большинства типов указан
только диапазон положительных значений, однако допустимым также является
аналогичный диапазон отрицательных значений, а также число 0 (0.0).
Таблица 1.2. Основные стандартные типы дробных чисел
Название типа(стандартные идентификаторы)
5e-3Z4.. 1.76+308
Real
Real4S
Single
Double
Extended
Comp
Currency
Диапазон допустимых значений
2.9е-39.. 1.7е+38
1.5е-45..3.4е38
5е-324..1.7е+308
З.бе-4951..1.1е493г
-2н..+2"-1
-922337203685477.5808 ..
922337203685477.5807
Для представления информации в памяти ЭВМ (как числовой, так и не числовой)
используется двоичный способ кодирования.
Элементарная ячейка памяти ЭВМ имеет длину 8 бит (байт). Каждый байт имеет свой
номер (его называют адресом). Наибольшую последовательность бит, которую ЭВМ
может обрабатывать как единое целое, называют машинным словом. Длина машинного
слова зависит от разрядности процессора и может быть равной 16, 32, 64 битам и т.д.
Кодирование символов
Для кодирования символов достаточно одного байта. При этом можно представить 256
символов (с десятичными кодами от 0 до 255). Набор символов персональных ЭВМ,
совместимых с IBM PC, чаще всего является расширением кода ASCII (American Standard
Code for Information Interchange — стандартный американский код для обмена
информацией). В настоящее время используются и двухбайтовые предсталения символов.
Двоично-десятичное кодирование
В некоторых случаях при представлении чисел в памяти ЭВМ используется смешанная
двоично-десятичная "система счисления", где для хранения каждого десятичного знака
нужен полубайт (4 бита) и десятичные цифры от 0 до 9 представляются
соответствующими двоичными числами от 0000 до 1001. Например, упакованный
десятичный формат, предназначенный для хранения целых чисел с 18-ю значащими
цифрами и занимающий в памяти 10 байт (старший из которых знаковый), использует
именно этот вариант.
Представление целых чисел в дополнительном коде
Другой способ представления целых чисел — дополнительный код. Диапазон значений
величин зависит от количества бит памяти, отведенных для их хранения. Например,
величины типа Integer (все названия типов данных здесь и ниже представлены в том виде,
в каком они приняты в языке программирования Turbo Pascal. В других языках такие типы
данных тоже есть, но могут иметь другие названия) лежат в диапазоне от -32768 (-215) до
32767 (215 - 1) и для их хранения отводится 2 байта (16 бит); типа LongInt — в диапазоне
от -231 до 231 - 1 и размещаются в 4 байтах (32 бита); типа Word — в диапазоне от 0 до
65535 (216 - 1) (используется 2 байта) и т.д.
Как видно из примеров, данные могут быть интерпретированы как числа со знаком, так и
без знака. В случае представления величины со знаком самый левый (старший) разряд
указывает на положительное число, если содержит нуль, и на отрицательное, если —
единицу.
Вообще, разряды нумеруются справа налево, начиная с 0. Ниже показана нумерация бит в
двухбайтовом машинном слове.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Дополнительный код положительного числа совпадает с его прямым кодом. Прямой
код целого числа может быть получен следующим образом: число переводится в
двоичную систему счисления, а затем его двоичную запись слева дополняют таким
количеством незначащих нулей, сколько требует тип данных, к которому принадлежит
число.
Например, если число 37(10) = 100101(2) объявлено величиной типа Integer
(шестнадцатибитовое со знаком), то его прямым кодом будет 0000000000100101, а если
величиной типа LongInt (тридцатидвухбитовое со знаком), то его прямой код будет
00000000000000000000000000100101. Для более компактной записи чаще используют
шестнадцатеричное представление кода. Полученные коды можно переписать
соответственно как 0025(16) и 00000025(16).
Дополнительный код целого отрицательного числа может быть получен по следующему
алгоритму:
1. записать прямой код модуля числа;
2. инвертировать его (заменить единицы нулями, нули — единицами);
3. прибавить к инверсному коду единицу.
Например, запишем дополнительный код числа -37, интерпретируя его как величину типа
LongInt (тридцатидвухбитовое со знаком):
1. прямой код числа 37 есть 00000000000000000000000000100101;
2. инверсный код 11111111111111111111111111011010;
3. дополнительный код 11111111111111111111111111011011 или FFFFFFDB(16).
При получении числа по его дополнительному коду прежде всего необходимо определить
его знак. Если число окажется положительным, то просто перевести его код в десятичную
систему счисления. В случае отрицательного числа необходимо выполнить следующий
алгоритм:
1. вычесть из кода числа 1;
2. инвертировать код;
3. перевести в десятичную систему счисления. Полученное число записать со знаком
минус.
Примеры. Запишем числа, соответствующие дополнительным кодам:
1. 0000000000010111. Поскольку в старшем разряде записан нуль, то результат будет
положительным. Это код числа 23.
2. 1111111111000000. Здесь записан код отрицательного числа. Исполняем алгоритм:
1) 1111111111000000(2) - 1(2) = 1111111110111111(2); 2) 0000000001000000;
3) 1000000(2) = 64(10).
Ответ: -64.
Кодирование вещественных чисел
Несколько иной способ применяется для представления в памяти персонального
компьютера действительных чисел. Рассмотрим представление величин с плавающей
точкой.
Любое действительное число можно записать в стандартном виде M × 10p, где 1  M < 10,
p — целое. Например, 120100000 = 1,201 × 108. Поскольку каждая позиция десятичного
числа отличается от соседней на степень числа 10, умножение на 10 эквивалентно сдвигу
десятичной запятой на одну позицию вправо. Аналогично деление на 10 сдвигает
десятичную запятую на позицию влево. Поэтому приведенный выше пример можно
продолжить: 120100000 = 1,201 × 108 = 0,1201 × 109 = 12,01 × 107. Десятичная запятая
"плавает" в числе и больше не помечает абсолютное место между целой и дробной
частями.
В приведенной выше записи M называют мантиссой числа, а p — его порядком. Для того
чтобы сохранить максимальную точность, вычислительные машины почти всегда хранят
мантиссу в нормализованном виде, что означает, что мантисса в данном случае есть
число, лежащее между 1(10) и 2(10) (1  M < 2). Основание системы счисления здесь, как
уже отмечалось выше, — число 2. Способ хранения мантиссы с плавающей точкой
подразумевает, что двоичная запятая находится на фиксированном месте. Фактически
подразумевается, что двоичная запятая следует после первой двоичной цифры, т.е.
нормализация мантиссы делает единичным первый бит, помещая тем самым значение
между единицей и двойкой. Место, отводимое для числа с плавающей точкой, делится на
два поля. Одно поле содержит знак и значение мантиссы, а другое содержит знак и
значение порядка.
Современный персональный компьютер позволяет работать со следующими
действительными типами (диапазон значений указан по абсолютной величине; в
некоторых случаях перечень типов данных может быть расширен):
Тип
Real
Диапазон
2,9×10-39..1,7×1038
Мантисса Байты
11-12
6
Single
1,5×10-45..3,4×1038
7-8
4
Double
5,0×10-324..1,7×10308
15-16
8
19-20
10
-4932
Extended 3,4×10
..1,1×10
4932
Покажем преобразование действительного числа для представления его в памяти ЭВМ на
примере величины типа Double.
Как видно из таблицы, величина это типа занимает в памяти 8 байт. На рисунке ниже
показано, как здесь представлены поля мантиссы и порядка (нумерация битов
осуществляется справа налево):
S
Смещенный
порядок
Мантисса
63
62..52
51..0
Можно заметить, что старший бит, отведенный под мантиссу, имеет номер 51, т.е.
мантисса занимает младшие 52 бита. Черта указывает здесь на положение двоичной
запятой. Перед запятой должен стоять бит целой части мантиссы, но поскольку она всегда
равна 1, здесь данный бит не требуется и соответствующий разряд отсутствует в памяти
(но он подразумевается). Значение порядка хранится здесь не как целое число,
представленное в дополнительном коде. Для упрощения вычислений и сравнения
действительных чисел значение порядка в ЭВМ хранится в виде смещенного числа, т.е. к
настоящему значению порядка перед записью его в память прибавляется смещение.
Смещение выбирается так, чтобы минимальному значению порядка соответствовал нуль.
Например, для типа Double порядок занимает 11 бит и имеет диапазон от 2-1023 до 21023,
поэтому смещение равно 1023(10) = 1111111111(2). Наконец, бит с номером 63 указывает на
знак числа.
Таким образом, из вышесказанного вытекает следующий алгоритм для получения
представления действительного числа в памяти ЭВМ:
1. перевести модуль данного числа в двоичную систему счисления;
2. нормализовать двоичное число, т.е. записать в виде M × 2p, где M — мантисса (ее
целая часть равна 1(2)) и p — порядок, записанный в десятичной системе счисления;
3. прибавить к порядку смещение и перевести смещенный порядок в двоичную
систему счисления;
4. учитывая знак заданного числа (0 — положительное; 1 — отрицательное),
выписать его представление в памяти ЭВМ.
Пример. Запишем код числа -312,3125.
1. Двоичная запись модуля этого числа имеет вид 100111000,0101.
2. Имеем 100111000,0101 = 1,001110000101 × 28.
3. Получаем смещенный порядок 8 + 1023 = 1031. Далее имеем 1031(10) =
10000000111(2).
4. Окончательно
1 10000000111 0011100001010000000000000000000000000000000000000000
63 62..52
51..0
Очевидно, что более компактно полученный код стоит записать следующим образом:
C073850000000000(16).
Другой пример иллюстрирует обратный переход от кода действительного числа к самому
числу.
Над целыми данными выполняются все операции, определенные для порядковых типов.
Операции над целыми типами:
Операция Результат
Abs (X)
Возвращает абсолютное целое значение Х
Х Div Y
Возвращает целую часть частного деления Х на Y
Х Mod Y Возвращает остаток частного деления Х на Y
Odd (X)
Возвращает булево True (истина), если Х - нечетное целое,
и False (ложь) - в
противном случае
Sqr (X)
Возвращает целый
Функции действительных типов:
Функция Возвращаемое значение
Abs (x)
Абсолютная величина х
АгсТаn(х) Арктангенс х
Cos (х)
Косинус х (х выражается в радианах, а не в градусах)
Ехр (х)
Экспоненциальная функция от х
Frac(x)
Дробная часть х
Int (х)
Целая часть х. Несмотря на название, возвращает действительное
значение (с плавающей запятой), т.е. просто устанавливает нуль в
дробной части
Ln (х)
Натуральный логарифм от х
Pi
Число Пи (3.1416...)
Ближайшее к х целое значение. Возвращает значение целого типа.
Условие "ближайшее к х" не работает, если верхнее и нижнее
значения оказываются равноудаленными (например, ес-ли дробная
Round (х)
часть точно равна 0,5). В этих случаях Delphi перекладывает
решение на опера-ционную систему. Обычно процессоры Intel
решают эту задачу в соответствии с рекоменда-цией IEEE
округлять в сторону ближайшего четного целого числа. Иногда
такой подход на-зывают "банкирским округлением"
Sin(x)
Синус х
Sqr(x)
Квадрат х, т.е. X*X
Sqrt (х)
Квадратный корень от х
Тrunc (х)
Целая часть х. В отличие от Int, возвращающей
Также различают сдвиг влево (в направлении от младшего бита к старшему) и вправо (в
направлении от старшего бита к младшему).
Логический сдвиг
Арифметический сдвиг (правый)
Циклический сдвиг
Циклический сдвиг через перенос
[править] Логический сдвиг
При логическом сдвиге значение последнего бита по направлению сдвига теряется
(копируясь в бит переноса), а первый приобретает нулевое значение.
Логические сдвиги влево и вправо используются для быстрого умножения и деления на 2,
соответственно.
[править] Арифметический сдвиг
Арифметический сдвиг аналогичен логическому, но значение слова считается знаковым
числом, представленным в дополнительном коде. Так, при правом сдвиге старший бит
сохраняет свое значение. Левый арифметический сдвиг идентичен логическому.
[править] Циклический сдвиг
При циклическом сдвиге, значение последнего бита по направлению сдвига копируется в
первый бит (и копируется в бит переноса).
Также различают циклический сдвиг через бит переноса — при нём первый бит по
направлению сдвига получает значение из бита переноса, а значение последнего бита
сдвигается в бит переноса.
[править] В языках программирования
В следующей таблице для некоторых языков программирования приведены встроенные
операторы и функции, реализующие побитовые логические операции.
Язык
НЕ
И ИЛИ Искл. ИЛИ Сдвиг влево Сдвиг вправо Другие
[4]
C/С++, Java, C#
~
&
|
^
<<
>>
[5]
Pascal
not and
or
xor
shl
shr
INOT IAND IOR
IEOR
PL/I[6]
BOOL
¬
&
|
¬
Prolog[7]
\
/\
\/
Литералы и их соответствие типам данных
Литералом называется последовательность символов, которая может интерпретироваться
как значение одного из примитивных типов. Но с типами (даже примитивными) в VB
.NET дело обстоит несколько сложнее, чем в более ранних версиях VB.
Хотя возможность непосредственной интерпретации данных предусмотрена в любом
языке программирования, решить, как именно следует интерпретировать те или иные
данные, иногда бывает непросто. Наверное, все согласятся с тем, что 3 — это число 3 и
его следует интерпретировать именно так. Но что такое число 3 с точки зрения
компилятора? Сколько байт памяти следует под него выделить? Теоретически для
хранения числа 3 хватит 2 бит, но в современных языках программирования обычно
происходит не так.
Итак, компилятор должен проанализировать литерал и принять необходимые решения,
поэтому вы должны по возможности точнее описать, что вы имеете в виду, не полагаясь
на разумность компилятора. Вернемся к примеру с простым числом 3. В VB .NET оно
может представлять собой (среди прочего):


Байт: фактически вы сообщаете компилятору, что для хранения числа следует
выделить минимальный объем памяти.
Короткое целое: старый тип Integer из VB6.

Целое .NET: старый тип Long из VB6 (компилятор выделяет для хранения числа 4
байта).
К счастью, символ 3 никогда не будет автоматически интерпретироваться как строковая
константа (если не переопределять стандартную логику VB). В VB .NET строки и числа
по умолчанию не смешиваются — более подробно эта тема рассматривается в разделе
«Преобразования разнотипных значений» этой главы.
С точки зрения компилятора простой констатации «это число 3» недостаточно.
Разумеется, VB .NET, как и любой язык программирования, позволяет уточнить смысл
литерала. Например, 31 — литерал типа Integer со значением 3, а литерал "3" относится к
строковому типу String (тип String рассматривается ниже в этой главе; он несколько
отличается от строкового типа в прежних версиях VB).
Примитивные типы можно рассматривать как атомарные элементы языка, хотя в VB .NET
они представляют собой псевдонимы для классов из библиотеки System.
В переменной, объявленной с примитивным типом, хранятся значения указанного типа.
Ниже перечислены примитивные числовые типы VB .NET.



Byte: 1-байтовое целое без знака в интервале от 0 до 255.
Short: 2-байтовое целое со знаком в интервале от -32 768 до 32 767, аналог типа
Integer в прежних версиях VB. Признаком типа Short в литералах является суффикс
S — например, 237S.
Integer: 4-байтовое целое со знаком в интервале от -2 147 483 648 до 2 147 483 647,
аналог типа Long в прежних версиях VB. Признаком типа Integer в литералах
является суффикс I — например, 2371.
Если суффикс не указан, а число входит в интервал допустимых значений типа Integer, по
умолчанию оно сохраняется в формате Integer. Это связано с тем, что на 32-разрядных
процессорах тип Integer обрабатывается эффективнее остальных типов.

Long: 8-байтовое целое со знаком в интервале от -9 223 372 036 854 775 808 до 9
223 372 036 854 775 807. Не имеет аналогов в прежних версиях VB. Признаком
типа Long в литералах является суффикс L — например, 2371.
При объявлении числовых переменных можно использовать старые суффиксы типов %, &
и т. д. — например, литерал 1234% относится к типу Long. Но при этом следует помнить,
что в VB6 и VB .NET эти суффиксы имеют разный смысл, поскольку тип Integer VB .NET
соответствует типу Long V86. По этой причине использовать старый синтаксис не
рекомендуется.
Любой целочисленный литерал можно записать в шестнадцатеричной системе счисления
(по основанию 16), для чего он снабжается префиксом &Н. Например, литерал &HF
соответствует десятичному числу 15, хранящемуся в формате Integer, поскольку суффикс
типа не указан, а число входит в интервал допустимых значений типа Integer. Числа,
записанные в восьмеричной системе счисления (по основанию 8), снабжаются префиксом
&0.
При выполнении операций с вещественными числами используются следующие типы:


Single: 4-байтовое вещественное число. Признаком типа Single в литералах
является суффикс F — например, 1.23F или 3F.
Double: 8-байтовое вещественное число. Если в числе с десятичной точкой не
указан суффикс, по умолчанию оно сохраняется в формате Double. Это связано с
тем, что Double работает эффективнее Single; на 32-разрядных процессорах Double
является основным типом для выполнения вещественных операций. Признаком
типа Double в литералах является суффикс R (или #).
Новый тип Decimal пришел на смену старому типу Currency, использовавшемуся в
прежних версиях VB. Он используется в ситуациях, когда ошибки округления
недопустимы.

Decimal: 12-байтовое вещественное число, гарантирующее отсутствие ошибок
округления в громадном интервале допустимых значений с 28 значащими
цифрами. Формальное определение гласит, что тип Decimal предназначен для
хранения чисел с мантиссой в интервале ±79 228 162 514 264 337 593 543 950 335,
масштабируемых до произвольного порядка при условии, что количество значащих
цифр не превышает 28. Таким образом, наименьшее число, представляемое типом
Decimal, равно ±0.0000000000000000000000000001. Признаком типа Decimal в
литералах является суффикс D.
Применение суффикса типа в литералах помогает избежать путаницы и случайных
ошибок переполнения, возникающих при умножении двух чисел. При выполнении
следующей команды:
Console.WriteLine(12345678 * 4567)
компилятор выдает ошибку:
This constant expression produces a value that is not representable in type System.Integer.
Проблема решается при помощи суффикса типа Long:
Console.WriteLine(123456781 * 4567)
Общие методы MaxValue и MinValue, ассоциированные с типом, возвращают
соответственно верхнюю и нижнюю границы интервала допустимых значений. Пример:
Console.WriteLine(Integer.MaxValue)
В табл. 3.2 собраны данные о соответствии числовых типов VB .NET, типов .NET
Framework и их аналогов из VB6 (если они есть).
Таблица 3.2. Соответствие между числовыми типами
4.Порядковые типы данных в Delphi. Основные функции для работы
с ними.
Не то вот ,что надо http://borlpasc.narod.ru/refer/2/tip.htm
http://www.tspu.tula.ru/ivt/old_site/umr/delphi/delphi_help/D5ophlp2_web
/33.html
Определение собственных типов данных
Зачем нужны новые типы
При создании практически любой серьезной программы обойтись без дополнительных, более сложных, чем числа и строки, типов данных бывает довольно трудно.
Гораздо удобнее работать, например, с типом данных Цвет и его значениями: красный, желтый, зеленый. — нежели просто с числами 1,2,3. Программа при этом получается значительно нагляднее, а это залог ее качества.
Описание нового типа
Чтобы описать (ввести в программу) новый тип данных, в Паскале имеется специальное ключевое слово type:
type название-типа = описание-типа;
Название типа — это произвольный идентификатор Паскаля. Описание типа может
представлять собой описание перечислимого типа, описание сложного типа, описание массива и так далее.
Перечислимые типы
Помимо обычных числовых и строковых типов Паскаль позволяет создавать типы,
диапазон значений которых — просто набор идентификаторов. Это удобно в тех
случаях, когда в решаемой задаче имеется понятие, значения которого нагляднее
описывать не числами, а словами.
ЗАМЕЧАНИЕ Хотя такие же значения можно создать с помощью констант, перечислимый тип представляет собой именно тип данных, с помощью
которого можно описывать переменные и выполнять над ними различные операции.
Перечислимый тип записывается взятой в круглые скобки последовательностью
идентификаторов — значений этого типа, перечисляемых через запятую. Первые
элементы типа считаются младшими по сравнению с идущими следом.
Например, тип, описывающий названия футбольных команд, может быть сформирован так:
type TFootballTeam =
(Spartak, CSKA, Dynamo, Locomotive, Torpedo};
var Team: TFootballTeam;
,b egi.n
Team := Locomotive;
Определение собственных типов данных 39
Б Паскале под перечислимыми типами обычно понимаются не только типы, представляющие собой списки идентификаторов, но и другие базовые типы, для которых можно формально определить последовательность значений и их старшинство.
К таковым относятся(знать диапазоны!)
О все целочисленные типы (Integer, Byte и так далее), для которых всегда можно
сказать, какое следующее число будет следовать за числом N;
О символьные типы (Char): за символом 'а' всегда следует символ Ъ', за символом
'О' — символ Т и так далее;
О логические типы — тип Boolean представляет собой не что иное, как перечислимый тип (False, True).__
Основные стандартные функции
для работы с типами
В дальнейшей работе с Паскалем не обойтись без базового набора стандартных
функций и процедур (табл. 1.7 и 1.8).
Таблица 1.7. Стандартные функции
Имя функции Возвращаемое значение
Ord Порядковый номер элемента для перечислимых типов, код ASCII для типа Char
Chr Символ (тип Char), преобразованный из числового аргумента
Pred Предыдущее по порядку значение данного типа. Например, значение Pred(5)
равно 4
Succ Следующее по порядку значение данного типа. Например, значение Succ(5)
равно 6
Length Длина строки или число элементов е массиве
High Максимально допустимое значение (для типа). Например, значение High(Byte)
равно 255. Верхняя граница (для массива). Для динамических массивов это
значение всегда равно LengthQ - 1
Low Минимально допустимое значение (для типа). Например, значение Low(Byte)
равно 0. Нижняя граница (для массива). Для динамических массивов это
значение всегда равно О
SizeOf Размер элемента данных указанного типа в байтах. Например, значение
SizeOf(Byte) равно 1, значение SizeOf (Integer) равно 4.
Таблица 1.8. Стандартные процедуры
Имя процедуры Назначение
Операции над порядковыми типами
Операция Описание
Low (T)
Минимальное значение порядкового типа Т
High(T)
Максимальное значение порядкового типа Т
Ord(X)
Порядковый номер значения выражения порядкового
типа. Для целого выражения - просто его значение. Для
остальных порядковых типов Ord возвращает
физическое представление результата выражения,
трактуемое как целое число. Возвращаемое значение
всегда принадлежит одному из целых типов
Pred(X)
Предыдущее по порядку значение. Для целых
выражений эквивалентно Х-1
Succ(X)
Следующее по порядку значение. Для целых выражений
эквивалентно Х+1
Dec(V)
Уменьшает значение переменной на 1. Эквивалентно V
:= Pred(V)
Inc(V)
Увеличивает значение переменной на 1. Эквивалентно V
:= Succ(V)
5. Символьные и строковые типы данных. Размещение в памяти,
основные функции для работы с ними. Способы задания литералов.
Символы
Помимо чисел, в Паскале разрешается обрабатывать данные в виде одиночных символов и их последовательностей (строк). Символы имеют тип Char и записываются
в виде знака, взятого в одиночные кавычки:
'5'
Иногда требуется обрабатывать символы, имеющие значения, которые невозможно
отобразить на экране. В таких случаях символ записывается в виде числа, перед
которым стоит знак # (в соответствии с кодами символов в кодировке ANSF).
Например:
#0
#40
Полным аналогом типа Char является тип AnsiChar. Допустимый диапазон его значений (при записи с помощью чисел) — от #0 до #255. В Паскале имеется еще тип
WideChar, соответствующий шрифтовой кодировке UNICODE (первые 256 символов этого типа соответствуют кодировке ANSI).
Строки
Строка – это последовательность символов ASCII. При использовании в
выражениях строка заключается в апострофы. Количество символов в строке (длина
строки) может динамически изменяться в пределах от 0 до 255. Для определения
данных строкового типа используется идентификатор string, за которым следует
заключенное в квадратные скобки значение максимально допустимой длины строки
данного типа. Если это значение не указывается, то по умолчанию длина строки
принимается равной 255 байтам.
Последовательность символов, заключенная в одиночные кавычки, называется
строкой (тип String — зарезервированное слово). Например:
'это текстовая строка Паскаля'
Если требуется поместить сам символ одиночной кавычки внутрь строки, его надо
повторить дважды:
'это '' - символ одиночной кавычки1
Некоторые символы могут иметь значения, которые невозможно непосредственно
отобразить на экране (символы, не соответствующие стандарту ANSI). В этом случае
коды соответствующих символов можно прямо (без разделяющих пробелов) включать в состав строки.
'в этой строке'#10#13' имеются непечатные символы'#0
Строки в Паскале могут быть различной максимальной длины. Строка типа
ShortString содержит до 255 символов (этот тип введен для совместимости со старыми версиями), строка типа AnsiString — 231 (2 Гбайт) символов, относящихся к
типу AnsiChar (данный строковый тип совпадает со стандартным типом string),
строка типа WideString — 230 символов типа WideChar.
Строка может быть пустой, не содержащей ни одного символа. Тогда она записывается как две идущие подряд одиночные кавычки — ".
Строки с нулем в конце (null-terminated strings)
Структура строки в Паскале (в той версии языка, которая была реализована компанией Borland еще для системы MS-DOS, когда операционной системы Windows
не существовало) отличается от структуры строки, которая обрабатывается сис-
темными вызовами Windows. Эта структура характеризуется тем, что отсчет символов в строке начинается с нуля, а завершается строка символом с кодом 0 (#0).
Тип строки с нулем в конце в зависимости от типа составляющих ее символов называется РСhаг или PWideChar,
Л ПОДСКАЗКА В Паскале в большинстве случаев разрешается смешивать эти типы,
но при программировании рекомендуется придерживаться, в
основном, типа String, а к типу РСпаг прибегать только, когда без
этого не обойтись.
Строки фиксированной длины
По умолчанию строка типа String может иметь размер до 2 Гбайт, а оперативная
память для нее выделяется программой автоматически, в зависимости от текущей
длины строки. В некоторых случаях бывает полезно ограничить длину строки небольшим фиксированным значением. Чаще всего это требуется при работе с файлами,
которые содержат текстовую информацию в заранее известном формате.
Для явного указания длины после ключевого слова string в квадратных скобках
задается число, определяющее эту длину.
string[50]
Для такой строки на этапе компиляции будет выделена область памяти в 50 символов. Строку большей длины (например, 51 символ) записать в нее нельзя (меньшей можно, но объем зарезервированной для строки памяти останется неизменным).
Строковые выражения
Для строк операция — сложение или сцепление, обозначаемая символом ≪+≫. Результатом является строка, полученная сцеплением левого
и правого операндов.
Выражение '* это' + ' строка' +'!' имеет значение '* это строка!'
Операции отношения: =, <, >, <=, >=, <>. Позволяют произвести сравнение двух строк, в
результате чего получается логическое значение (true или false). Операция отношения
имеет приоритет более низкий, чем операция сцепления. Сравнение строк производится
слева направо до первого несовпадающего символа, и та строка считается больше, в
которой первый несовпадающий символ имеет больший номер в таблице символьной
кодировки. Если строки имеют различную длину, но в общей части символы совпадают,
считается, что более короткая строка меньше, чем более длинная. Строки равны, если они
полностью совпадают по длине и содержат одни и те же символы.
Пример:
Выражение
Результат
‘True1’<’True2’
True
‘Mother’>’MOTHER’ True
‘Мама ‘ <> ‘Мама’
True
‘Cat’=’Cat’
True
Функция Copy(S, Pozition, N) выделяет из строки S подстроку длиной N символов,
начиная с позиции Pozition. Здесь N и Pozition — целочисленные выражения.
Пример:
Значение S
Выражение Результат
‘Мама мыла раму’ Copy(S, 6, 4) ‘мыла’
‘Маша ела кашу’
Copy(S, 1, 8) ‘Маша ела’
Функция Concat(S1, S2, …, SN) выполняет сцепление (конкатенацию) строк S1, S2, …, SN
в одну строку.
Пример:
Выражение
Результат
Concat('Маша ', 'ела ', 'кашу') 'Маша ела кашу'
Функция Length(S) — определяет текущую длину строки S. Результат — значение целого
типа.
Пример:
Значение S Выражение Результат
'test-5'
Length(S)
6
'(A+B)*C'
Length(S)
7
Функция Pos(S1, S2) — обнаруживает первое появление в строке S2 подстроки S1.
Результат — целое число, равное номеру позиции, где находится первый символ
подстроки S1. Если в S2 подстроки S1 не обнаружено, то результат равен 0.
Пример:
Значение S2 Выражение Результат
'abcdef'
Pos('cd', S2) 3
'abcdcdef'
Pos('cd', S2) 3
'abcdef'
Pos('k', S2) 0
Процедура Delete(S, Poz, N) — удаление N символов из строки S, начиная с позиции Poz.
Пример:
Исходное значение S Оператор
Конечное значение S
'abcdefg'
Delete(S, 3, 2) 'abefg'
'abcdefg'
Delete(S, 2, 6) 'a'
В результате выполнения процедуры уменьшается текущая длина строки в переменной S.
Процедура Insert(S1, S2, Poz) — вставка строки S1 в строку S2, начиная с позиции Poz.
Пример:
Исходное значение S2 Оператор
Конечное значение S2
'ЭВМ РС'
Insert('IBM-', S2, 5) 'ЭВМ IBM-PC'
'Рис. 2'
Insert('N', S2, 6)
'Рис. N 2'
ShortString, String[15]
Length(S) = Ord(S[0])
S := 'ABC'; //S[1]='A'
PChar  ^Char (ASCIIZ)
CP := 'ABC'; //CP^='A', CP[0]='A', CP+1='BC', CP+3='', CP[3]=#0
String (HugeString)
6. Логический тип данных. Использование в программе. Операции
сравнения. Логические операции. Побитовые операции с целыми
числами.
Логические данные
Помимо чисел, символов и строк, в Паскале имеется тип данных Boolean, в диапазон
значений которого входят всего две величины: True (истина, да) и False (ложь, нет).
Их нельзя использовать в выражениях в качестве числовых или символьных величин.
'J ЗАМЕЧАНИЕ Значение False считается меньше, чем эначени! True.
Размеры переменных булевых типов:
Тип
Размер
Boolean
1 байт
ByteBool
1 байт
WordBool
2 байт (объем Word)
LongBool
4 байт (объем Longint)
Логические выражения
Для манипулирования логическими величинами True и False в Паскале имеются
четыре операции.
Таблица 1.4. Логические операции в Паскале
Обозначение операции в Паскале Назначение
And Логическое И. Результат равен True, если оба операнда
равны True, в противном случае результат равен False
Or Логическое ИЛИ. Результат равен True, если хотя бы
один из операндов равен True, в противном случае
результат равен False
Хог Исключающее ИЛИ. Результат равен True, если операнды
не равны друг Другу, в противном случае результат
равен False
Not Отрицание. Имеет только один операнд, указываемый
справа. Результат равен True, если значение операнда
равно False, в противном случае результат равен False
Операция not имеет наивысший приоритет, операция and — более низкий, операции or и хог имеют самый низкий приоритет среди логических операций.
Как и В случае с арифметическими выражениями, порядок вычисления логического выражения можно менять с помощью круглых скобок.
X or Y
not One
{al or Ы} and (al or cl)
Операции сравнения (возвращают лог рез-т и могут комбинироваться логическими
операциями)надо брать в скобки при включении в логические выражения.
Правильно:
(A>0) or (A<10)
Битовые выражения
В Паскале имеется возможность выполнять операции над отдельными битами числа
(которое представлено в машинном коде программы в виде одного или нескольких
байтов). Если типы (длины) операндов битовых операций отличаются, то результат
имеет тип, соответствующий типу данных самой короткой длины (в байтах).
Результат вычисляется путем применения битовой операции к соответствующим
парным битам каждого операнда.
Таблица 1.5. Битовые операции в Паскале
Обозначение операции в Паскале Назначение
And Битовое И. Бит результата равен 1,если оба бита
операндов равны 1, в противном случае итоговый бит
равен О
Or Битовое ИЛИ. Бит результата равен 1, если хотя бы один
из битов каждого операнда равен 1, в противном случае
итоговый бит равен О
Хог Битовое исключающее ИЛИ. Бит результата равен 1, если
соответствующие биты операндов не равны друг другу, в
противном случае итоговый бит равен О
Not Битовое отрицание. Операция имеет один операнд,
указываемый справа. Бит результата равен 1, если бит
операнда равен 0, в противном случае итоговый бит
равен О
Shi Битовый сдвиг влево (младшие, правые биты заполняются
нулями). Левый операнд побитно сдвигается влево на
число битов, заданное правым операндом
Shr Битовый сдвиг вправо (старшие, левые биты заполняются
нулями). Левый операнд побитно сдвигается вправо на
число битов, заданное правым операндом
Операция not имеет наивысший приоритет, операции and, shl и shr — более низкий,
операции or и хог имеют приоритет, самый низкий среди битовых операций.
Примеры выполнения битовых операций:
Выражение 11110000 and 10111101 имеет значение 10110000
Выражение 11110000 or 10111101 имеет значение 11111101
Выражение 11110000 хог 10111101 имеет значение 01001101
Выражение not 11110000 имеет значение 00001111
Выражение 00001111 shl 2 имеет значение 00111100
Выражение 11101111 shr 3 имеет значение 00011101
7. Переменные и константы в Delphi, в том числе, константы сложных
типов. Синтаксис объявления, использование, размещение в памяти.
Переменные
Во время работы программы данные в ней могут храниться в неизменном виде,
как константы (тогда они указываются в тексте программы явно), или же они записываются и обрабатываются как переменные. Переменные можно рассматривать
как ячейки памяти компьютера, имеющие свои имена (идентификаторы). Содержимое переменных может многократно меняться. Каждая переменная имеет тип,
определяющий, какого рода данные в ней хранятся. Паскаль не допускает использования переменных с неопределенным типом и не разрешает записывать в переменную одного типа данные другого типа.
Для того чтобы переменную можно было использовать в программе, ее предварительно надо объявить (декларировать, описать).
Команда описания переменных в Паскале записывается так:
var имя-переменной: тип-переменной;
Слово var — ключевое. В качестве имени переменной выступает любой допустимый
идентификатор, если он не был описан ранее и не является зарезервированным
словом, а в качестве типа — одно из названий допустимых типов.
var X007: integer;
var Pi: real;
Если несколько описаний переменных следуют друг за другом, то ключевое слово
var можно повторно не указывать.
var X007: integer,Pi: real;
Имен переменных может быть указано несколько, в таком случае они перечисляются через запятую, и эти переменные будут иметь одинаковый тип:
var А, В, X, Count, Delta2: string;
Константы
В некоторых случая бывает удобно вместо явного указания конкретных значений
(чисел или строк) использовать константы — фиксированные значения, для
которых определено имя. Константы отличаются от переменных тем, что не могут
менять свои значения. Они предназначены только для удобства программиста.
Пусть, например, заранее неизвестно, какая потребуется точность при вычислении некоторой функции, а пороговое значение точности используется в исходном
тексте программы в разных местах. Чтобы не пришлось для изменения этого значения выполнять трудоемкий поиск и замену конкретного числа во всем тексте
программы, правильнее описать порог один раз как константу и в дальнейшем обращаться к нему только по имени. Тогда при необходимости внести изменение достаточно будет поменять всего одну строчку в программе.
Константы описываются способом, напоминающим описание переменных, только
вместо ключевого слова var применяется ключевое слово const, а тип можно указывать, а можно и не указывать. Значение константы задается после знака равенства:
const PI = 3.14,Е: Real = 2.87;
Сложные структуры данных
В Паскале допускается произвольное комбинирование структур данных и неограниченное создание новых типов на основе определенных ранее. В частности, разрешено описывать массивы структур:
type MyReс =
record
Кг integer;
S: String;
end;
MyArr = array [1..10] of MyRec;
var A: MyArr;
К переменной А теперь можно обращаться так:
А [ 5 ] .Ы : = 1;
А[10].S := '333';
или так:
with A[l] do
begin
N := 0;
. S : = ";
end;
Сначала происходит выделение элемента массива, а затем — выделение элемента
записи.
Возможен и противоположный подход.
type MyArr = array [1..10] of integer;
MyRec =
record
Определение собственных типов данных 5 1
А: МуАгг;
Ss string;
end;
var R: MyRec;
К переменной R разрешается обращаться так:
R . A [ 5 ] := 1;
Уровней вложения структур в массивы и наоборот может быть сколько угодно.
Например, допускается следующая запись:
А1[2]-А2.АЗ[5].А4.А5.Аб[1,2,88]-А7
Присваивание значений сложных типов
Подобные длинные цепочки вложений конструкций разных типов друг в друга
могут свободно использоваться в выражениях Паскаля наравне с обычными
переменными. От них они, впрочем, ничем и не отличаются — разве что формой
записи. При этом можно копировать не только значения самых простых (базовых)
типов наподобие Integer или Real, но и значения массивов и структур в одном операторе присваивания, Надо только, чтобы типы и правой, и левой частей оператора
присваивания совпадали.
type TAr - array[1..1000] of Byte;
TRc = record
Ar: TAr;
Count: integer;
end;
var А: ТДг;
R: TRc;
begin
A := R.Ar;
R.Ar := A;__
Инициализация констант сложных типов
Часто требуется использовать в работе константы не только базовых типов, но и
константы сложных типов (массивы, записи). Например, таблицы всевозможных
начальных значений и параметров удобно размещать в массивах.
Инициализация массивов
Значения массива располагаются последовательно в круглых скобках. Если изме-
рений у массива более одного, то измерения вкладываются друг в друга, начиная
54 Урок 1. Язык Delphi (Object Pascal) и ею использование
с младших измерений (справа налево, если смотреть на описание массива). Например, если создан тип
type Tl = array[1..2, 1..5] of byte;
то в первую очередь надо сформировать значения для диапазона 1. .5. Таких последовательностей из 5 чисел требуется две — в соответствии с размером диапазона
первого измерения.
Записывается константа сложного типа так:
const Т: Т1 =
( ( 1 , 2 , 3 , 4 , 5 ) , (100,200,300,400,500)];
ВНИМАНИЕ Число элементов при инициализации массива должно в точности
равняться его размеру (произведению длин всех измерений).
Инициализация записи
Форма инициализации записи отличается от формы инициализации массива тем,
что приходится дополнительно указывать названия элементов, а за ними через
двоеточие — значения. Разделяются такие конструкции точками с запятой.
type T2 =
record
Name: str-ing;
Num: integer;
end,const TT: T2 = (Name:'первый'; Num:l|;
)
Инициализация указателей
Указатель инициализируется значением адреса ранее описанной переменной.
var N: Integer;
const PI: ~Integer = @H;
const constPStr: PChar = 'нуль-строка';__
Каждая программа использует при работе память (*). Память занимает любая переменная
в вашей программе. Будь это форма, компонент, массив, запись, строка или же простой
Integer. Под некоторые переменные память выделяется автоматически (например, под
переменные типа Integer и статические массивы), под другие - вы должны выделять её
сами, явно (например, динамические массивы). Собственно, с точки зрения операционной
системы каждая переменная характеризуется адресом в памяти (местоположением) и
размером. Понятно, что обычно данные разных переменных не пересекаются - за
исключением случаев обращением к одной области памяти через разные имена с
помощью указателей.
Грубо говоря, обычно в программе используется три типа памяти: область памяти для
глобальных переменных, стек и куча.
Память для глобальных переменных выделяется загрузчиком ОС при загрузке
исполняемого модуля программы в память и освобождается при выгрузке модуля (выходе
из программы). Глобальные переменные - это любые переменные, объявление которых
располагается вне класса или процедуры. Стек используется для размещения локальных
переменных (объявленных в процедуре/функции) и служебных данных (типа адресов
возврата и адресов обработчиков исключений). Куча же используется для размещения
динамических данных.
Заметим, что для переменных динамических типов данных (динамические массивы,
строки, любые объекты, компоненты), хотя сама переменная может размещаться в
области для глобальных переменных или в стеке (а, значит, память для неё выделяется
автоматически), но данные, на которые она указывает, всегда размещаются в куче и,
зачастую, должны управляться вручную.
Вне зависимости от того, кто выделяет память для переменной (вы вручную или
компилятор автоматически), память для любой переменной должна быть выделена перед
использованием, а потом, когда переменная уже не будет нужна - освобождена.
8. Массивы в Delphi. Способы задания массивов. Обращение к
элементам массива в выражениях.
Структурные типы данных
Обойтись только простыми — линейными типами в большой программе довольно
сложно. Желательно, чтобы структура данных прикладной программы отвечала
структуре данных решаемой задачи. Для этого в Паскале есть набор структурных
типов данных.
Массивы
Массив — это структура данных, доступ к элементам которой осуществляется по
номеру (или индексу). Все элементы в массиве имеют одинаковый тип. Индекс
элемента массива может быть вычисляемым, что позволяет организовывать компактную и эффективную обработку больших наборов данных.
Описание массива имеет вид:
type имя-типа-маесива = array[ диапазон ] of тип-элемента;
В КВАДРАТНЫХ СКОБКАХ ПИШЕТСЯ ТИП ИНДЕКСА,который в свою очередь
должен быть порядковым типом!!!
Слова array (массив) и of — ключевые. Диапазон определяет нижнюю и верхнюю
границы массива и, соответственно, число элементов в нем. При обращении к массиву индекс должен лежать в этом диапазоне. Тип элемента определяет тип каждого элемента массива.
Массив из 100 элементов целого типа (первый элемент будет иметь номер 1, последний — номер 100) описывается в программе так:
type TMyArray = array[ 1..100 ] of'integer;
При задании диапазона могут выступать не только числовые значения:
type TMyArray = array[ TMyTeams ] of string;
Таким способом определяется тип, описывающий массив из трех строк. При обращении к первой строке индекс должен иметь значение С5КА (самое маленькое значение из диапазона TMyTeams), а при обращении к третьей строке — значение
Locomotive.
Доступ к элементу массива описывается путем указания имени переменной соответствующего типа вместе со следующим за ней в квадратных скобках индексом.
В качестве индекса может выступать произвольное выражение Паскаля, значение
которого должно укладываться в диапазон, указанный при описании массива.
Например:
type TMyArray = array[ 1..100 ] of integer;
var MyArray: TMyArray;
N: integer;
begin
N := 50;
MyArray[ 1 ] := 100,Му Array[ К ] := MyArray[ N+l ] * 2;
MyArray[ N+12 ] := N div 4;
Определение собственных типов данных 4 1
Другой пример:
type TMyArray = array] TMyTeams ] of string;
var Teams: TMyArray;
begin
Teams[ CSKA ] := 'это ЦСКА';
Teams[ Dynamo ] := 'это Динамо';
Teams[ Locomotive ] := 'это Локомотив';
Вместо создания в программе нового типа на основе описания массива можно явно
декларировать переменную как массив с помощью ключевого слова van
var Teams: array[ TMyTeams ] of string;
Хотя такой подход использовать не рекомендуется. Дело в том, что при этом методе
описания, даже когда две переменные внешне описаны одинаково, в Паскале они
считаются относящимися к разным типам. Б таком случае присваивать их значения
(весь массив) друг другу в одном операторе присваивания не разрешается.
var Tl: array[ TMyTeams ] of string;
T2: array[ TMyTeams ] of string;
begin
Tl := T2,- // неправильно!
Чтобы разрешить копирование массивов, надо переменные описать как имеющие
один тип:
type TMyArray = array! TMyTeams ] of string;
var Tl: TMyArray;
T2: TMyArray;
begin
Tl := T2; // правильно!
Массив может иметь несколько измерении, перечисляемых через запятую.
аггау[ -5..10, 0 . . 2 ] of'byte;
Каждое измерение характеризуется своим диапазоном, тип которого может не совпадать с типами диапазонов других измерений. Например, если требуется описать
массив дробных чисел с тремя измерениями, первое из которых охватывает диапазон целых чисел от 1 до 10, второе — диапазон значений от False до True (Boolean), a
третье — диапазон TMyTeams, то выглядеть это будет так:
tyjtp<e THewArray = J
array[ 1 .. 10, boolean, TMyTeams ] of real;
При доступе к элементам массива значения индексов также перечисляются через
запятую:
var AA: TNewArray;
begin
АА[ 5, true, CSKA ) = 3.14;
42 Урок 1. Язык Delphi (Object Pascal) и его использование
Строковые данные формально представляют собой массивы символов, и к элементам строк можно обращаться с помощью индексов. Это позволяет выделять конкретные символы и менять их значения. Отсчет символов для переменных типа string
начинается с единицы.
var S: string,begin
S := '54321';
S[5] := '0'; //в строке окажется значение '54320'
Однако явно описывать строки как массивы символов вместо использования ключевого слова string неправильно. Единственное отличие — строки, заканчивающиеся нулевым символом. Их можно описывать в программе как массивы символов,
первый элемент которых имеет номер 0. При занесении значений в такие переменные в конце строки надо указывать символ с кодом 0:
var SO: array[ 0 . . 5 0 ] of char;
begin
SO := 'строка с нулем'#О;
9. Массивы переменной длины (динамические массивы) и открытые
массивы (с неопределёнными границами) в параметрах
процедуры.
Обычно размеры массива неизменны и поэтому должны описываться явно с
помощью констант, задающих нижнюю и верхнюю границы диапазона, или названий типов. Такие массивы называются статическими. Паскаль позволяет определять также динамические массивы, размер которых заранее не известен и может
меняться во время работы программы. Для описания динамических массивов указывать диапазоны значений его измерений не нужно.
type TDynArray: array of integer;
Первоначально в переменной, описанной как динамический массив, нет ни одного
элемента. Ее размер задается с помощью стандартной процедуры SetLengthQ.
В качестве первого параметра указывается имя переменной, в качестве второго —
число элементов в массиве. Если новое число элементов больше старого, то новые
элементы добавляются в конец массива, а ихзначения исходно будут не определены.
Если новое число элементов меньше старого, то последние элементы массива отбрасываются и теряются. Отсчет элементов динамического массива всегда начинается
с нуля.
var a : TDynAirray ;
begin
SetLengthl a, 1 );
а[0] := 2;
Чтобы освободить память, выделенную динамическому массиву, и сделать его
длину нулевой, надо соответствующей переменной присвоить значение nil:
а := nil;
'J ЗАМЕЧАНИЕ Ключевое слово языка Паскаль nil обозначает «отсутствие значения».
Определение собственных типов данных 43
После этого в массиве а не окажется ни одного значения. В дальнейшем длину
массива а можно изменить снова.
Как и статические массивы, динамические массивы могут иметь несколько измерений. Для каждого из измерений при описании массива надо повторить описание
array of. Например, если требуется объявить динамический массив целых чисел с
двумя измерениями, надо записать так:
type ТЗПАггау = array of array of integer;
Далее надо объявить размер этого массива по каждому измерению. Реализация
динамических массивов в Паскале позволяет, в отличие от описания статических
массивов, задавать разные диапазоны для одного измерения. Например, когда описан двумерный массив (обычно это карта, график, иоле), сначала задается размер
по первому измерению (число столбцов):
var D: ТЗВАггау;
begin
SetLength(D,4);
Далее нужно указать число элементов для каждого из четырех столбцов (при описании статических массивов это число считается одинаковым для всех столбцов).
Длина соответствующего столбца задается указанием имени массива с номером
столбца с помощью процедуры SetLength():
SetLength( D[0], 3 );
SetLengthl D[l], 5 );
SetLengthf D[2], 30 ] ,SetLengthf D[3], 1 );
ПОДСКАЗКА Динамические массивы позволяют существенно экономить память
и запрашивать ее у операционной системы по мере необходимости, однако работа с ними в программе происходит значительно
медленнее, чем со статическими массивами.
Передача массивов в качестве параметров
Стандартный прием Паскаля состоит в описании массива как типа данных и указании этого типа для параметра подпрограммы. Однако таким способом удается
обрабатывать только динамические массивы и статические массивы заранее заданной длины. Часто требуется, чтобы подпрограмма не ограничивалась конкретными
размерами массива. Например, функция, вычисляющая сумму всех элементов массива, должна уметь получать в качестве параметра массив любой длины. ДинамиПодпрограммы 59
ческие массивы использовать не всегда удобно и не всегда правильно, так как они
ухудшают эффективность работы программы.
В Паскале разрешается указывать в качестве параметров массив с неопределенными
границами.
procedure Sum( A: array of Byte );
ВНИМАНИЕ Данное описание похоже но описание динамического массива, но
не является токовым. Это просто форма записи параметра подпрограммы для передачи статического массива заранее не известной
длины.
Теперь массив, описанный как
var Ar: array [1..25] of Byte;
можно без проблем передавать в процедуру Sum:
Sum(Ar];
При этом внутри тела подпрограммы действуют следующие правила:
О Нумерация элементов массива начинается с нуля (число элементов можно
определить с помощью стандартной функции SizeOf);
О Функция High возвращает верхнюю границу массива (равную SizeOf()-l);
О Копирование массива одним оператором присваивания не разрешается;
О Вместо массива в подпрограмму может быть передана обычная переменная
соответствующего типа. Внутри подпрограммы она будет представлена в
виде массива из одного элемента (с индексом 0).__
Открытые массивы в параметрах процедур и функций
function IntArrayMax(const A: array of integer): Integer;
var i: integer;
Begin
Result := A[Low(A)];
for i:=Succ(Low(A)) to High(A) do
if Result<A[i] then
Result := A[i];
end ;
M := IntArrayMax(B);
M := IntArrayMax(Slice(B,N));
M := IntArrayMax([0,I,I+J,J]);
1.когда знаем размер массива то просто пишем в кв скобках диапозон
2Когда мы не знаем какой длины массив и массив надо обрезать,то используем
функцию:
Описание
Функция Slice создает подмассив из первых Count элементов массива
SourceArray.
Эта функция может использоваться ТОЛЬКО как аргумент для параметра с типом
"открытый массив" в процедуре или функции.
Параметр с типом "открытый массив" имеет неизвестное число элементов на
момент компиляции.
Это позволяет процедурам работать с массивами переменной длины.
Пример кода : Передача сектора массива в процедуру
var
i : Integer;
Source : array[0..4] of Integer;
begin
// Создание исходного массива с значениями элементов 0..4 для элементов 0..4
for i := 0 to 4 do
Source[i] := i;
// Использование команды Slice, чтобы передать только первые 3 элемента
// Source как открытый массив в процедуру ShowSlice.
ShowSlice(Slice(Source, 3));
end;
// Показ массива неизвестного размера - он передан как 'Открытый' массив
procedure TForm1.ShowSlice(SubArray : array of Integer);
var
i : Integer;
begin
// Показ каждого элемента этого массива
for i := 0 to Length(SubArray)-1 do
ShowMessage('SubArray['+IntToStr(i)+'] : '+ IntToStr(SubArray[i]));
end;
SubArray[0] : 0
SubArray[1] : 1
SubArray[2] : 2
3И можно еще (3ий способ передачи массива!)просто в квадратных скобках
указать те элементы массива,которые нам надо будет потом использовать!
Функция Format использует этот прием для того,чтобы передавать аргументы.
http://www.programmersclub.ru/array/
http://www.transl-gunsmoker.ru/2009/09/of-const.html (ПРО ОТКР МАССИВ)
10.Тип данных запись. Способы работы с записями и их полями в
коде. Упакованные и неупакованные записи.
Записи
Запись — это структура данных, доступ к элементам которой осуществляется по
имени (названию элемента). Элементы записи могут иметь разный тип, поэтому
при описании записи надо указывать и название каждого элемента, и его тип.
Описание записи имеет следующий вид:
type название-типа-записи =
record
название-элемента: тип-элемента,end,Урок 1. Язык Delphi (Object Pascol) и его использование
В качестве названий элементов выступают обычные идентификаторы Паскаля —
так же, как при описании переменных. Например, описание записи, характеризующей футбольную команду (содержащую название команды, города, страны и год
создания) может выглядеть так:
type TFootballTeamRecord =
record
Name: TFootballTeam;
City, Country: string;
Year: 1800..3000;
end;
Элемент записи Year (год) можно описать как имеющий тип Integer, но более корректно явно указать его допустимый диапазон значений.
Для обращения к элементу записи сначала указывается имя переменной, затем
точка и название соответствующего элемента.
var T: TFootballTeamRecord;
begin
Т.Name := Spartak;
Т.City := 'Москва';
Когда надо задать значения большому числу элементов переменной-записи, каждый
раз предварительно указывать ее имя неудобно. Б Паскале имеется ключевое слово
with, которое позволяет опускать имя переменной в логическом блоке. При этом
перед каждым упоминанием элемента в этом блоке имя переменной будет ставиться
автоматически.
with T do
begin
Name := Spartak,City := 'Москва';
end,Если в логическом блоке, охваченном словом with, требуется использовать также
переменную, имя которой совпадает с названием одного из элементов записи, перед
ней необходимо указать название модуля (или программы), в котором эта переменная описана.
var T: TFootballTeamRecord;
Name: string;
begin
Name := 'Москва';
with T do
begin
Name := Spartak;
City := Projectl.Name;
end;
Определение собственных типов дойных 45
Если в блоке with модуль для конкретной переменной не указан, то компилятор
прежде всего ищет ее имя в списке названий элементов записи, и только потом среди обычных переменных.
После ключевого слова with можно указывать но одно, а несколько имен переменных-записей:
with T, MyTeam, Foo do
begin
Записи с вариантами
Строго фиксированная структура записи ограничивает возможность ее применения.
Поэтому в языке Delphi имеется возможность задать для записи несколько вариантов
структуры. Такие записи называются записями с вариантами. Они состоят из
необязательной фиксированной и вариантной частей.
Вариантная часть напоминает условный оператор case. Между словами case и of
записывается особое поле записи – поле признака. Оно определяет, какой из вариантов в
данный момент будет активизирован. Поле признака должно быть равно одному из
расположенных следом значений. Каждому значению сопоставляется вариант записи. Он
заключается в круглые скобки и отделяется от своего значения двоеточием. Пример
описания записи с вариантами:
type
TFigure = record
X, Y: Integer;
case Kind: Integer of
0: (Width, Height: Integer); // прямоугольник
1: (Radius: Integer); // окружность
end;
Обратите внимание, что у вариантной части нет отдельного end, как этого можно было бы
ожидать по аналогии с оператором case. Одно слово end завершает и вариантную часть, и
всю запись.
На этом мы пока закончим рассказ о записях, но хотим надеяться, что читатель уже
догодался об их потенциальной пользе при организации данных с более сложной
структурой.
Вне зависимости от того, каким способом была определена запись (на основе
таблицы, курсора или явного использования оператора TYPE для записи), работа со
всеми записями ведется одинаково. Вы можете работать с данными, хранящимися
в записи, на уровне записи (запись воспринимается как единое целое), а также
обращаться напрямую к ее отдельным полям.
Операции на уровне записи
Когда вы работаете на уровне записи, то какие бы то ни было ссылки на отдельные
поля отсутствуют. На текущий момент PL/SQL поддерживает следующие операции
над записями:
•
Вы можете копировать содержимое одной записи в другую (в случае
совместимости их структур, то есть у них должно быть одинаковое количество
полей и одинаковые или взаимно преобразуемые типы данных).
•
Вы можете присваивать записи значение NULL простым присваиванием.
•
Вы можете определять и передавать запись как аргумент в списке
параметров.
•
Вы можете возвращать (оператором RETURN) запись из функции.
Операции на уровне записей можно применять к любым записям с совместимыми
структурами. Другими словами, записи должны иметь одинаковое количество полей и
одинаковые или взаимно преобразуемые типы данных, но они не обязаны быть одного
типа. Предположим, что мы создали такую таблицу:
CREATE TABLE cust_sales_roundup ( customer_id
NUMBER (5), customer_name VARCHAR2 (100),
total_sales NUMBER (15,2)
);
Три записи, определенные подобным образом, имеют совместимые структуры, и я могу
перемешивать и сопоставлять данные в этих записях:
DECLARE
cust_sales_roundup_rec cust_sales_roundup%ROWTYPE;
CURSOR cust_sales_cur IS SELECT * FROM cust_sales_roundup;
cust_sales_rec cust_sales_cur%ROWTYPE;
TYPE customer_sales_rectype IS RECORD (customer_id
NUMBER(5), customer_name customer.name%TYPE,
total_sales NUMBER(15,2)
);
prefererred_cust_rec customer_sales_rectype;
BEGIN
Присвоить одну запись другой. cust_sales_roundup_rec :=
cust_sales_rec; prefererred_cust_rec := cust_sales_rec;
END;
Операции на уровне поля
Если вам необходимо получить доступ к полю записи (чтобы прочитать или изменить
значение), необходимо использовать точечную нотацию, в точности как при ссылке на
столбец определенной таблицы базы данных. Синтаксис ссылки на поле будет таким:
[имя_схемы.][имя_пакета.]имя_записи. имя_поля
Имя пакета должно быть указано только в том случае, если запись определяется в
спецификации пакета, отличного от того, в котором вы работаете в настоящий момент.
Имя схемы следует указывать лишь в том случае, если пакет принадлежит не той схеме, в
которой вы компилируете свой код.
После того как вы определили поле с помощью точечной нотации, вы можете ссылаться
на значение этого поля и изменять его в соответствии с обычными правилами, принятыми
в PL/SQL.
Упакованные типы
При описании массива (или других структурных типов) можно указать зарезервированное слово packed, которое означает, что переменные этого типа будут размещаться в памяти компьютера плотно и компактно. Такой подход позволяет сэкономить оперативную память, отведенную для данных, но значительно понижает
скорость работы программы при работе с упакованными массивами.
type TpA = packed arraylL.S] of integer;
type TpR = packed record
N: integer;
S: etring[50];
end;
Упакованные записи
Пару слов о том, что такое упакованные записи, и с чем их едят. По умолчанию память
под записи выделяется не очень экономно - помимо самих данных добавляются и
служебные байты, которые отделяют блоки данных друг от друга. Существует
принудительный способ заставить Delphi упаковать запись, т.е. минимизировать
занимаемую ей память. Делается это указанием слова packed перед словом record.
Разница порой может быть достаточно ощутимой. Пример: запись из строки длиной 5
символов, одного символа и трёх чисел разного типа. Объявим две разные записи: одна
обычная, а другая упакованная:
TRecord1 = packed record
Name: String[5];
A: LongInt;
C: Char;
D: Double;
N: Integer;
end;
TRecord2 = record
Name: String[5];
A: LongInt;
C: Char;
D: Double;
N: Integer;
end;
А теперь самое интересное: посмотрим, сколько памяти занимает каждая из записей.
Сделаем это функцией SizeOf():
var R1: TRecord1; R2: TRecord2;
begin
ShowMessage(IntToStr(SizeOf(R1)));
ShowMessage(IntToStr(SizeOf(R2)));
end;
В первом сообщении мы увидим 24, а во втором 32. Обычная запись занимает на треть
больше памяти, чем упакованная! А теперь представьте, что у вас 100 000 таких записей?
Тем не менее, не стоит пренебрегать этим способом экономии памяти. В некоторых
случаях использование пакованных записей может создавать разные ошибки в работе
программы. Понять, что дело именно в packed, удаётся далеко не сразу. Так что, если
храните десяток отрезков - не торопитесь паковать - если и выиграете, то не сильно.
11.Тип данных множество. Представление в памяти. Основные
операции с множествами.
Множества
Множество напоминает перечислимый тип, но отличается от него тем, что элементы
в нем не упорядочены (в множестве нет ни самого младшего, ни самого старшего
элементов). В множество входят также все допустимые подмножества (все мыслимые комбинации его элементов), что приводит к огромному числу вариантов,
поэтому количество элементов в множестве не может быть больше, чем 256.
Множество описывается так:
type тип-множества = eet of диапазон-значений-множества;
В качестве диапазона указывается любой тип Паскаля, число элементов в котором
не более 256: Char, Byte, 0..255, TFootballTeam и так далее.
type MySet = set of 0 . . 2 5 5 ;
type MySet = set of Byte;
type MySet = set of (Spartak, CSKA, Dynamo!;
Конкретные значения множества задаются перечислением списка значений через
запятую, который берется в квадратные скобки.
MySet := [1, 2 , 5, 10];
В таком списке можно дополнительно указывать диапазоны значений:
MySet := [2, 4, 8, 12 . .21];
Дополнительные операции над множествами описаны в следующей таблице.
Таблица 1.6. Дополнительные операции над множествами
Название операции Действие
+ (объединение) Результирующее множество состоит из элементов обеих множеств,
указанных в качестве операндов (одинаковые элементы не дублируются).
Выражение [1,2,3] +• [3/4,5] равно [1,2,3,4,5]
- (разность) Результирующее множество состоит из тех элементов множества,
указанного в качестве левого операнда, которые отсутствуют во
множестве, указанном в качестве правого операнда.
Выражение [1,2,3] - [3,4,5] равно [1,2]
* (пересечение) Результирующее множество состоит из элементов, имеющихся
в каждом из множеств,указанных в качестве операндов.
Выражение [1,2,3] * [3,4,5] равно [3]__
При работе с множествами допускается использование операций отношения (=, <>, >=,
<=), объединения, пересечения, разности множеств и операции in.
Операции сравнения (=, <>). Два множества считаются равными, если они состоят из
одних и тех же элементов. Порядок следования элементов в сравниваемых множествах
значения не имеет. Два множества A и B считаются не равными, если они отличаются по
мощности или по значению хотя бы одного элемента.
Выражение
Результат
[1, 2] <> [1, 2, 3]
True
[1, 2] = [1, 2, 2]
True
[1, 2, 3] = [3, 2, 1] True
[1, 2, 3] = [1..3]
True
Операции принадлежности (>=, <=). Выражение A >= B равно True, если все элементы
множества B содержатся в множестве A. Выражение A <= B равно True, если выполняется
обратное условие, т.е. все элементы множества A содержатся в множестве B.
Выражение
Результат
[1, 2] <= [1, 2, 3] True
[1, 2, 3] >= [1, 2] True
[1, 2] <= [1, 3]
False
Операция in. Используется для проверки принадлежности элемента указанному
множеству. Обычно применяется в условных операторах.
Выражение
5 in [1..9]
Результат
True
5 in [1..4, 6..9] False
Операция in позволяет эффективно и наглядно выполнять сложные проверки условий,
заменяя иногда десятки других операций. Например, оператор
if (X = 1) or (X = 2) or (X = 3) or (X = 5) or (X = 7) then
можно заменить более коротким:
if X in [1..3, 5, 7] then
Операцию in иногда пытаются записать с отрицанием: X not in S. Такая запись является
ошибочной, так как две операции следуют подряд. Правильная запись имеет вид: not (X in
S).
Процедура Include(S, I) включает в множество S элемент I. Она дублирует операцию +
(плюс) с той лишь разницей, что при каждом обращении включает только один элемент и
делает это более эффективно.
Процедура Exclude(S, I) исключает из множества S элемент I. Она дублирует операцию –
(минус) с той лишь разницей, что при каждом обращении исключает только один элемент
и делает это более эффективно.
Выражение Результат
S := [1, 3];
[1, 3]
Include(S, 2); [1, 2, 3]
Exclude(S, 3) [1, 2]
Использование в программе множеств дает ряд преимуществ: значительно упрощаются
сложные операторы if, улучшается наглядность программы и понимание алгоритма
решения задачи, экономится время разработки программы. Поэтому множества широко
используются в библиотеке компонентов среды Delphi.
12.Способы преобразования типов в Delphi.(еще чет для классов надо)
Преобразование типов
В ряде случаев требуется гарантировать, что результат вычисления выражения принадлежит конкретному типу. Например, вычисления могут производиться над числами типа Longint, а результат должен принадлежать типу Byte. Такая операция
называется приведением типов. Для этого применяют так называемые преобразователи типов, которые напоминают стандартные функции Паскаля с именами, совпадающими с именами базовых типов (Byte, Integer и другие). Результат, возвращаемый таким преобразователем, гарантированно лежит в диапазоне указанного типа.
При этом, конечно, возможна потеря значащих цифр: берется остаток от деления
значения аргумента на максимально допустимое значение соответствующего типа.
Определение собственных типов данных 53
Например, значение Byte{300) равно 44
var X: Integer;
L: Longint;
begin
X ;= IntegerjL * 1234567);
Однако подобным способом невозможно преобразовать число типа Real в число типа
Integer или любое число в строку. Для таких, более сложных, преобразований типов
в Delphi 7 имеется набор стандартных функций, упрощающих процесс преобразован! 1я.
Таблица 1.9. Стандартные функции преобразования типов
Имя функции Назначение
Round Округление дробного числа до ближайшего целого. Значение Round{ 3.74) равно 4
Trunc Отбрасывание дробной части числа. Значение Тшпс(3.74 ) равно 3
IntToStr Преобразование целого числа в строку. Значение IntToStr( 12987 ) равно '12987'
FloatToStr Преобразование дробного числа в строку. Значение FloatToStr( 3.74 ) равно
'3.74'
StrToInt Преобразование строки в целое число. Значение StrToInt( '129B5') равно 12985
StrToFloat Преобразование строки в дробное число. Значение StrToFloatf '3.14') равно 3.14
Приведение типов переменных
Помимо стандартных функций, в Паскале имеется еще одна возможность явно указывать, в какой тип должны быть приведены конкретные данные. Эта возможность
используется в операторе присваивания, когда переменная, стоящая в левой части,
≪охватывается^ названием типа, соответствующего типу выражения правой части
оператора. Например:
var С: Byte;
Char(C) := ' А ' ;
Переменной С будет присвоен код ASCII символа 'А'. При этом необходимо, чтобы
длины (в байтах) переменной и присваиваемого значения совпадали.
Другая форма записи этого оператора:
С := Byte( ' А ' ) ;__
http://www.programmersclub.ru/is-as-gruzin/ про классы
13.Указатели. Основные операции с указателями. Как получить адрес
переменной.
Указатели(4 байта всегда)
С целью повышения эффективности создаваемых программ в первых версиях Паскаля было реализовано понятие указа теля — переменной, которая хранит не значение конкретного типа, а адрес памяти (условно говоря, номер ячейки), где хранится
значение соответствующего типа. Указатель только указывает на место в памяти,
а получить доступ к данным в этом месте можно с помощью специальных операций.
') ПОДСКАЗКА Указатели в некоторых случаях позволяют получить определенны и
выигрыш в быстродействии, однако их использование нередко приводит к серьезным ошибкам. Так как проконтролировать, правильный пи адрес памяти хранит указатель, трудно, в результате ошибок
можно изменить или затереть участки системной памяти или
области данных программы, вследствие чего сделать полностью
неработоспособной как прикладную программу, так и всю систему
Windows. Поэтому указатели, реализованные в языке Delphi (Object
Pascal) в целях совместимости со старыми версиями, применять не
рекомендуется.
Признаком того, что переменная содержит не данные, а указатель, служит символ Л,
который ставится перед названием типа переменной.
var BytePtr: лЬуЬе;
Переменная __________BytePtr описана как указатель на данные типа byte. Чтобы
присвоить
ей адрес конкретного места памяти, используется операция @.
var BytePtr: ^byte;
ByteVal: byte;
begin
ByteVal := 255;
BytePtr := @ByteVal;
В переменной BytePtr будет храниться адрес байта памяти, который связан с переменной ByteVaL.
Чтобы получить доступ к содержимому памяти, на которую указывает переменнаяуказатель, используется знак операции Л. В данном случае он ставится после имени
переменной.
N := BytePtr^;
В переменную N запишется значение, которое расположено по адресу, хранимому
в переменной BytePtr (фактически это адрес байта, отведенного компилятором для
переменной ByteVal).
По адресу памяти можно поместить значение подходящего типа.
ByfcePtr^ := 50;
Определение собственных типов данных 47
ВНИМАНИЕ В данном примере содержимое указатели BytePtr указывает на
область памяти, отведенную для переменной Byte Vol. Таким образом, при записи значения по адресу из переменной By!ePtr реально
произойдет запись значения в переменную Byte Vol. To есть после
выполнения донного оператора физически изменится содержимое
одного байта, а в программе с точки зрения программиста
изменится как значение BylePlr", так и значение переменной
ByteVoi. Подобная работа с указателями усложняет логику программы, поэтому использовать указатели нежелательно.
В некоторых случаях без указателей обойтись все же не удается, потому что главное
их преимущество — возможность манипулировать не большими объемами памяти
(например, массивом из тысячи элементов), а указателями на эту область (.указатель
занимает обычно 4 байта, и на его копирование лишние такты процессора не тратятся). При обращении к системным функциям Windows обычно требуется использовать именно указатели, поэтому в Паскале имеется несколько базовых типов
указателей, объявленных заранее.
ЗАМЕЧАНИЕ В Паскале принято типы, связанные с указателями, начинать не с
буквы Т, а с буквы Р (от английского Pointer — указатель).
type PByte = "Byte;
Это, в частности, тип PChar — указатель на символ (в реальности — на область
памяти, которая представлена как последовательность символов, заканчивающаяся
символом с кодом 0).
Дополнительные операции над указателями
Указатели можно складывать (операция ≪+≫) друге другом и с целыми числами, а
также вычитать (операция ≪-≫). Смысл этих операций состоит либо в сдвиге (смещении) адреса памяти на заданное целое число единиц соответствующего типа
вперед или назад, либо в определении разности (числа единиц данных) между двумя
указателями. Складывать два указателя в языке Паскаль не разрешается.
Например, если переменная Р1 имеет тип PChar и указывает на место в памяти, где
расположена строка '12345', то после выполнения оператора
Р1 := Р1 + 2;
переменная Р1 будет указывать на строку '345'. Если теперь указатель сдвинуть
обратно на 1:
Р1 := Р1 - 1;
то переменная Р1 укажет на строку '2345'.
Урок 1. Язык Delphi (Objecl Pascal) и его использование
ВНИМАНИЕ Смещение указателя происходит не на число байтов, а на число
элементов того типа, к которому принадлежит переменная-указатель. Для переменной типа PInteger сдвиг будет происходить физически на 4 байта (размер элемента данных типа Integer), и указатель
всегда будет корректно указывать на начало очередного элемента
заданного типа.
Если переменная Р2 указывает на последний символ строки '12345' (то есть, символ '5'), то разность Р2 - Р1 даст значение 3 (отличие в три символа типа Char).__
Inc(p) инкрементирует указатель. То есть добавляет к нему единицу, ‘продвигая’ его
на SizeOf(Type) ячеек вверх. Инкремент (Inc) и декремент (Dec) возможен с любыми
типизированными указателями.
Какие типы данных имеют природу указателей?(PChar и тд)
14.Операции с динамической памятью.
Переменные, создаваемые динамически
По аналогии с динамическими массивами в Паскале имеется возможность создавать динамические переменные, память для которых выделяется во время работы
программы по специальному запросу.
*) ПОДСКАЗКА Такая технология активно применялась в старых версиях Паскаля
для MS-DOS, когда на счету был каждый байт оперативной памяти
и приходил ось прибегать к всевозможным ухищрениям для ее экономии. Сегодня подобными средствами лучше не пользоваться.
При создании динамических переменных в программу надо передавать адрес памяти, где находится новый выделенный операционной системой блок памяти. При
этом не обойтись без указателей. Процедура New() выделяет оперативную память
в соответствии с типом переменной-указателя, используемого в качестве аргумента,
а процедура DisposeQ освобождает эту память.
var PL "Integer;
begin
// выделяется память, для хранения числа типа Integer
// адрес этого участка памяти записывается в переменную Р
New(P) ;
Рл := 12000;
// Память освобождается
Dispose(Р);
// Далее обращаться к содержимому памяти,
// куда указывает переменная Р (Р"), нельзя
В статье про указатели мы рассмотрели способ адресации и доступа к динамической
памяти в программах на Delphi. Но, практически не затронули вопроса о работе с самой
динамической памятью. А именно, выделение и освобождение этого вида ресурса.
Как и в стандартной библиотеке C/C++ в Delphi существует свой собственный менеджер
памяти (кучи, heap). Он хорошо оптимизирован для работы с большим количеством
небольших буферов, которые требуется часто выделять и освобождать. Как видно, заточен
он для обеспечения работы ООП. Именно для этого метода построения программ
характерно подобное обращение с динамической памятью.
При использовании Delphi`йного менеджера памяти, происходит работа именно с кучей.
Хотя при желании мы можем вызывать напрямую функции предоставляемой нам ОС
(VirtualAlloc() и иже с ней), в этом случае мы не используем кучу. Куча – это механизм
работы с памятью, выделенной в свободной области памяти процесса (это, довольно
вольное определение). У ОС также имеется свой собственный менеджер кучи, но об этом
позже.
Чем же по-сути занимается менеджер памяти в Delphi? Он оптимизирует использование
кучи программой. Если бы выделяли память с помощью функций API, то ядро выделяло
бы целую станицу памяти (8 Кб) даже при маленьких запросах. Этот способ никак нельзя
назвать экономичным и его использование совсем не оправданно при ООП. Менеджер
памяти Delphi же заблаговременно резервирует необходимую память блоками по 1М и
выделяет её программе блоками по 16К. Причем блоки всегда выровнены по 4 байта. Так
же в блоке присутствует заголовок – двойное слово в котором хранится информация о
размере этого блока и его статусная информация.
Менеджер кучи берет память у системы, используя API`шные функции VirtualAlloc(),
VirtualFree() и т.д. Все операции работы с динамической памятью в юзермод опираются
на эти самые интерфейсы API.
Использование кучи в Delphi.
Программируя в Delphi мы постоянно явно или неявно взаимодействуем с менеджером
кучи. Неявно его используют все функции или конструкции языка, требующие выделения
памяти: создания объекта класса, создание динамического массива или длинной строки.
Явное взаимодействие с этим механизмом происходит при использовании следующих
функций Delphi: New, Dispose, GetMem, AllocMem, ReallocMem, FreeMem.
procedure New(var P: Pointer);
Функция выделяет динамическую память в куче. Параметром принимает типизированный
указатель, затем инициирует его адресом выделенного участка. Не имеет смысла
использовать функцию с нетипизированным указателем, он просто останется со
значением NIL.
procedure Dispose(var P: Pointer);
Функция освобождает память в куче. Параметром принимает типизированный указатель
возвращенный функцией New(). Используется только в паре с функцией выделения
памяти New().
function summ( var1, var2: integer ): integer;
var
f_int
:
^integer;
s_int
:
^integer;
begin
// выделим память
new(f_int);
new(s_int);
f_int^ := var1;
s_int^ := var2;
Result := f_int^ + s_int^;
// освободим память
dispose(f_int);
dispose(s_int);
end;
Вышеприведенные функции используются для динамического выделения памяти с
использованием типизированных указателей малого размера. Для работы с двоичными
буферами и иными структурами данных большого размера используется следующий
набор функций.
procedure GetMem(var P: Pointer; Size: Integer);
Выделяет блок памяти указанного размера. Можно использовать нетипизированный
указатель. Если свободной памяти не окажется, то будет сгенирированно исключение
EOutOfMemory. После выделения в памяти содержится всякий мусор.
function AllocMem(Size: Cardinal): Pointer;
Соответствует функции GetMem(), но после выделения память будет инициирована
нулями. Возвращает указатель, как результат, а не как параметр, в отличие от той же
GetMem().
procedure ReallocMem(var P: Pointer; Size: Integer);
Деиствие зависит от значений P и Size. P может быть пустым указателем или содержать
адрес участка памяти, возвращенный функциями GetMem, AllocMem и ReallocMem.
Варианты:
( ( P = NIL) and ( Size = 0 ) ): ReallocMem ничего ни делает;
( ( P = NIL ) and ( Size <> 0 ) ): выделяет новый
его адрес. Можно использовать вместо GetMem();
блок памяти и устанавливает P на
( ( P <> NIL ) and ( Size = 0 ) ): освобождает память, адресуемую P. P будет
установлен в NIL. Похоже на FreeMem(), но в отличае от него чистит указатель.
перевыделяет указанный блок памяти (изменяет
его размер). Существующие данные затронуты не будут, но если память увеличиться, то
новое пространство будет содержать всякий мусор. Если для изменения размера не будет
хватать памяти, то блок может быть перенесен на другое место в пределах кучи, P будет
указывать на новое место.
( ( P <> 0 ) and ( Size <> 0 ) ):
procedure FreeMem(var P: Pointer[; Size: Integer]);
Функция освобождает память, выделенную в GetMem и AllocMem. Может принимать
размер памяти, которую нужно освободить. Надо быть крайне осторожным с этим
параметром, так как тут может появиться утечка. После освобождения памяти указатель P
будет содержать мусор. Если в качестве параметра передана структура, содержащая
длинные строки, варианты, динамические массивы или интерфейсы, тогда перед
выполнением FreeMem будет вызвана Finalize.
Небольшой пример:
procedure useMemoryManager;
type
// шаблончик для доступа к памяти побайтово
memcells
=
array[0..$7FFFFFFE] of byte;
var
p: pointer;
i: integer;
begin
// выделим память
GetMem(p, 100);
// и инициируем ее какими-нибудь числами
for i:=0 to 99 do memcells(p^)[i] := byte(i);
// добавим памяти
ReallocMem(p, 200);
// и допишим числа
for i:=0 to 99 do memcells(p^)[i+100] := byte(i+100);
// … посмотрим, что получилось
for i:=0 to 199 do writeln(inttostr(memcells(p^)[i]));
// уберем после себя
FreeMem(p);
end;
Работа с менеджером памяти
Помимо выделения освобождения памяти существует возможность непосредственного
взаимодействия с менеджером памяти в Delphi. Например мы можем получить
информацию о текущем состоянии менеджера, о произошедших ошибках и даже
назначить свои механизмы выделения и освобождения памяти.
Во-первых есть несколько глобальных переменных, управляемых менеджером.
var AllocMemCount: integer;
Эта переменная содержит количество выделенных блоков памяти. Используется для
определения оставшихся блоков.
Использовать эту переменную стоит с осторожностью. Например, в статически
слинкованных программах, модули будут иметь свои собственные экземпляры
AllocMemCount.
var AllocMemSize: integer;
Содержит размер в байтах всех выделенных блоков. Сколько всего памяти из кучи
выделенно программе. Эта переменная тоже глобальна, поэтому так же как и
AllocMemCount следует использовать с осторожностью.
var HeapAllocFlags: word;
Набор флагов, устанавливающих опции для менеджера памяти. (по умолчанию –
GMEM_MOVEABLE)
Выделяет фиксированную память. Т.к. ОС не может перемещать
блоки памяти в юзермод, то и нет нужды блокировать память (не
GMEM_FIXED
может комбинироваться с GMEM_MOVEABLE)
Выделяет перемещаемую память. В юзермод блоки не могут быть
GMEM_MOVEABLE перемещены, если они расположены в физической памяти, но
могут перемещаться в пределах кучи.
При выделении памяти (например, функцией GetMem) все байты
GMEM_ZEROINIT
этой памяти будут выставлены в 0
Используется для изменения атрибутов выделенного блока памяти
GMEM_MODIFY
Введёны для совместимости с 16-разрядными версиями, но может
GMEM_DDESHARE
использоваться для оптимизации DDE операций.
- // GMEM_SHARE
GMEM_FIXED + GMEM_ZEROINIT
GPTR
GMEM_MOVEABLE + GMEM_ZEROINIT
GHND
Во-вторых, существует ряд полезных функций для управления менеджером.
function GetHeapStatus(): TheapStatus;
Узнать текущее состояние диспетчера памяти.
Структура TheapStatus:
THeapStatus = record
// Размер памяти в байтах которая доступна программе
TotalAddrSpace:
Cardinal;
// Сколько памяти из TotalAddrSpace не находятся в SWAPе
TotalUncommitted: Cardinal;
// Сколько памяти из TotalAddrSpace находятся в SWAPе
TotalCommitted:
Cardinal;
// Сколько всего динамической памяти выделено программе
TotalAllocated:
Cardinal;
// Сколько памяти еще доступно для выделения (увеличивается)
TotalFree:
Cardinal;
// Сколько памяти доступно в маленьких блоках
FreeSmall:
Cardinal;
// Сколько памяти доступно в больших блоках
// непрерывно идущие маленькие блоки могут складываться
FreeBig:
Cardinal;
// Доступная, но еще не выделявшаяся память
Unused:
Cardinal;
// Размер памяти используемой для нужд менеджера
Overhead:
Cardinal;
// Внутренний статус кучи
HeapErrorCode:
Cardinal;
end;
Если используется SharedMem, то статус относится к куче разделяемой несколькими
процессами.
HeapErrorCode - значения кодов ошибок
Код: Константа:
cHeapOk
0
cReleaseErr
1
2
3
4
5
6
7
8
Значение:
Все путем, пока
ОС вернула ошибку при попытке освободить память
ОС вернула ошибку при попытке освободить память,
cDecommitErr
выделенную в swapе
Список блоков, выделенных в swap-файле, выглядит
cBadCommittedList
подозрительно
cBadFiller1
Плохой филлер. Происходит кода что-то не так со swapом.
cBadFiller2
- // cBadFiller3
- // cBadCurAlloc
Проблемы с текущей зоной выделения памяти
cCantInit
Инициализация сорвалась
9
10
11
cBadUsedBlock
12
cBadFreeList
13
cBadFreeBlock
14
cBadBalance
cBadPrevBlock
cBadNextBlock
Используемому блоку памяти поплохело
… и предыдущему тоже
… и до следующего очередь дошла
Проблемы со списком свободных блоков. (никогда такого не
видел)
Свободному блоку памяти плохо.
Несрастухи в бухгалтерии свободных блоков. (вот почему всем
так сразу поплохело:) )
function IsMemoryManagerSet():Boolean;
Проверяет какой менеджер памяти сейчас работает. Если TRUE, то дефолтный менеджер
заменен на свой кем-то.
procedure GetMemoryManager(var MemMgr: TMemoryManager);
Возвращает указатель на текущий диспетчер памяти.
type
PMemoryManager = ^TMemoryManager;
TMemoryManager = record
// должна выделить блок памяти размером Size
// (Size никогда не может быть равным нулю) и вернуть на
// него указатель. Если она не может этого сделать,
// она должна вернуть nil.
GetMem
: function(Size: Integer): Pointer;
// должна освободить память Size по адресу P.
// P никогда не должен быть равен NIL
FreeMem
: function(P: Pointer): Integer;
// должна перевыделить память. При необходимости должна
// скопировать блок памяти на новое место и вернуть на нее
// указатель. Если действие невозможно возвращает NIL
ReallocMem: function(P: Pointer; Size: Integer): Pointer;
end;
procedure SetMemoryManager(const MemMgr: TmemoryManager);
С помощью этой функции можно установить свой диспетчер памяти. Структкра
TmemoryManager описана выше. Диспетчер будет использоваться функциями GetMem,
FreeMem, ReallocMem, New, Dispose; а также при вызове конструкторов и деструкторов
объектов и работе с длинными строками и динамическими массивами.
Пример из справочной системы:
var
GetMemCount: Integer;
FreeMemCount: Integer;
ReallocMemCount: Integer;
OldMemMgr: TMemoryManager;
function NewGetMem(Size: Integer): Pointer;
begin
Inc(GetMemCount);
Result := OldMemMgr.GetMem(Size);
end;
function NewFreeMem(P: Pointer): Integer;
begin
Inc(FreeMemCount);
Result := OldMemMgr.FreeMem(P);
end;
function NewReallocMem(P: Pointer; Size: Integer): Pointer;
begin
Inc(ReallocMemCount);
Result := OldMemMgr.ReallocMem(P, Size);
end;
const
NewMemMgr: TMemoryManager = (
GetMem: NewGetMem;
FreeMem: NewFreeMem;
ReallocMem: NewReallocMem);
procedure SetNewMemMgr;
begin
GetMemoryManager(OldMemMgr);
SetMemoryManager(NewMemMgr);
end;
Также, следуя советам справочной системы, если определен свой менеджер памяти, есть
смысл использовать следующие 3 функции для работы с динамической памятью (сам этим
никогда не пользуюсь, поэтому не знаю на сколько это правдиво):
function SysGetMem(Size: Integer): Pointer;
Для выделения памяти. Соответствует GetMem.
function SysReallocMem(P: Pointer; Size: Integer): Pointer;
Перевыделение памяти. Соответствует ReallocMem.
function SysFreeMem(P: Pointer): Integer;
Освобождение памяти. Соответствует MemFree.
Если есть желание, то дополнительную информацию по этой теме можно найти в
справочной системе Delphi или можно посмотреть в модуль System.pas.
Заключение
Все вышесказанное относится к внутренним механизмам работы с динамической памятью
для Delphi (еще C++ Builder, по-моему этим пользуется). Менеджер кучи в Delphi, как уже
было сказано выше, заточен для работы с ООП, поэтому при переопределении менеджера
надо быть готовым к тому, что сильно упадет производительность ваших программ.
Можно воспользоваться менеджером кучи, предоставляемым самой ОС. За эти функции
отвечает семейство функций HeapCreate() и т.д. Также можно работать с динамической
памятью совсем в обход механизма кучи. Для этого используется еще одно семейство API
функций ОС – VirtualAlloc(). Это может быть оправданно при работе или с большими
объемами данных, требующих непрерывности (как правило, в куче, данные сильно
фрагментируются) или для каких-то других сильно специфических задач. В любом случае
злоупотреблять этим методом не следует, в итоге вы придете к тому, что реализуете свой
механизм кучи
15.Тип данных Variant. Основные функции. Где он применяется.
(16байт-большая)
Иногда бывает необходимо обрабатывать данные, тип которых на этапе компиляции не
известен. В этом случае выходом может служить использование типа Variant. Этот тип
представляет такие значения, тип которых может изменяться на этапе выполнения
программы.
Тип Variant предоставляет большую гибкость, однако поглощает больше памяти по
сравнению с соответствующими переменными и операции над данными типа Variant
выполняются медленнее.
Более того, недопустимые операции над данными этого типа чаще приводят к ошибкам на
этапе выполнения программы, в то время как подобные ошибки над данными другого
типа были бы выявлены еще на этапе компиляции программы.
Variant может содержать данные любого типа, за исключением:
· структурных типов;
· указателей;
· Int64 (начиная с Delphi 6 – может).
В Delphi 6.0 вариант может включать такие типы (приведенные константы описаны в
модуле System):
const
varEmpty = $0000;
varNull = $0001;
varSmallint = $0002;
varInteger = $0003;
varSingle = $0004;
varDouble = $0005;
varCurrency = $0006;
varDate = $0007;
varOleStr = $0008;
varDispatch = $0009;
varError = $000A;
varBoolean = $000B;
varVariant = $000C;
varUnknown = $000D;
varShortInt = $0010;
varByte = $0011;
varWord = $0012;
varLongWord = $0013;
varInt64 = $0014;
varStrArg = $0048;
varString = $0100;
varAny = $0101;
varTypeMask = $0FFF;
varArray = $2000;
varByRef = $4000;
Этот тип может содержать COM и CORBA объекты, чьи методы и свойства могут быть
доступны посредством этого типа.
Также тип Variant может содержать динамические массивы и специфический вид
статических массивов, называемых вариантными массивами (variant array).
Variant можно смешивать (в выражениях и операторах) с другими вариантами,
числовыми, строковыми и булевскими данными. При этом компилятор автоматически
выполняет преобразование типа.
Варианты, содержащие строки, не могут, однако, индексироваться (V[i} не допустимо).
Вариант занимает 16 байт памяти и включает код типа и значение или указатель на
данные того типа, на который указывает код типа.
На этапе создания вариант инициализируется специальным значением Unassigned. Другое
специальное значение – Null – указывает на неизвестные или отсутствующие данные
(unknown or missing data).
Стандартная функция VarType возвращает код типа, представленного в варианте. Каждый
код типа представлен своей (16-битовой) константой. Константа varTypeMask=$0FFF
предназначена для извлечения кода, возвращаемого VarType. Например, выражение
(VarType(V) and varTypeMask) = varDouble
будет иметь значение true, если V содержит данное типа Double или массив Double. Маска
varTypeMask просто скрывает 13-й бит (строго говоря, четыре старших бита), который
указывает на массив.
В модуле System определен также тип-запись TVarData, с помощью которого можно
получить доступ к внутреннему представлению данных в типе Variant.
В Delphi 6.0 имеется модуль Variants, содержащий ряд подпрограмм для работы с
данными типа Variant.
Преобразования типа Variant.
Как было сказано выше, типы integer, real, string, character и Boolean (за исключением
Int64) являются совместимыми по присваиванию с типом Variant. Тип выражения может
быть явно приведен к типу Variant. Для изменения внутреннего представления варианта
может быть использована подпрограмма
VarAsType:
Function VarAsType(const V: Variant; VarType: TVarType): Variant;
Следующий код иллюстрирует использование вариантов и некоторые автоматические
преобразования типов при смешивании вариантов с другими типами.
var
V1, V2, V3, V4, V5: Variant;
I: Integer;
D: Double;
S: string;
begin
V1 := 1; { integer value }
V2 := 1234.5678; { real value }
V3 := 'Hello world!'; { string value }
V4 := '1000'; { string value }
V5 := V1 + V2 + V4; { real value 2235.5678}
I := V1; { I = 1 (integer value) }
D := V2; { D = 1234.5678 (real value) }
S := V3; { S = 'Hello world!' (string value) }
I := V4; { I = 1000 (integer value) }
S := V5; { S = '2235.5678' (string value) }
end;
Компилятор выполняет преобразования типов по правилам, приведенным в следующей
таблице.
Table 5.7 Variant type conversion rules
Присваивание переменной значения вне диапазона допустимых для ее типа значений
часто приводит к тому, что переменная получает максимально возможное значение.
Недопустимые присваивания или приведения типа вызывают ошибку (исключительную
ситуацию) EVariantError.
Специальные правила преобразования применяются к типу TDateTime (вещественный тип
double).
Когда тип TDateTime преобразуется к любому другому типу, он трактуется просто как
вещественный. Когда какой-либо числовой или булевский тип преобразуется к типу
TDateTime, он сначала приводится к типу double, а затем читается так же, как тип
TDateTime.
Использование Variant в выражениях.
К типу Variant можно применять все операции, за исключением ^, is и in. Операции над
операндами типа Variant возвращают и значение типа Variant. Они возвращают Null, если
такое значение имеет один или оба операнда. Если хотя бы один операнд имеет значение
Unassigned, возникает ошибка.
Если один из операндов (для бинарных операций) является Variant, второй преобразуется
к типу Variant.
В основном при выполнении операций над вариантами действуют те же правила, что и
для "обычных" операндов. Например, если V1 и V2 являются вариантами, один из которых
содержит вещественное значение, а другой – целочисленное, результат будет
вещественным.
Вариантные массивы.
Можно создавать и обрабатывать массивы вариантов, но можно также создать и
вариантный массив. Вариантные массивы не описываются, а создаются с помощью
стандартных функций VarArrayCreate и VarArrayOf. Например,
var V: Variant;
...
V := VarArrayCreate([ - 1, 9], varInteger);
Теперь переменная V может быть индексирована, однако ее нельзя передавать
подпрограмме как параметр-переменную. Вариантные массивы всегда индексируются
целочисленным индексом.
Функция VarArrayOf создает и заполняет вариантный массив. Значения элементов массива
передаются функции в качестве параметров, например
V := VarArrayOf([1, 10, 100, 1000]);
После выполнения этого оператора V[1] = 10. Вариантные массивы могут быть
многомерными, чего можно добиться таким образом:
V[i] := VarArrayOf([1, 10, 100, 1000]);
Теперь V – двухмерный массив.
В модуле System определен также ряд других подпрограмм, которые предназначены для
выполнения различных действий над вариантными массивами (получение границ
индексов, изменение числа элементов массива и т.д.).
Пример из Help Delphi 6.0:
var A: Variant;
begin
A := VarArrayCreate([0, 4], varVariant);
A[0] := 1;
A[1] := 1234.5678;
A[2] := 'Hello world';
A[3] := True;
A[4] := VarArrayOf([1, 10, 100, 1000]);
WriteLn(A[2]); { Hello world }
WriteLn(A[4][2]); { 100 }
end;
Тип OleVariant представляет собой такой вариант, который содержит только COMсовместимые типы. Когда Variant присваивается OleVariant, несовместимые типы
преобразуются в совместимые аналоги.
Рассмотрим следующий программный код. var
V1,V2,V3 : variant; begin
VI: ='1';
V2:='5';
V3:=10;
V1:=V1+V2+V3; end;
Как вы думаете, какой результат будет в переменной На первый взгляд, должно быть
число 16, если программа попытается сложить все как числа, или строка "1510", если
сложит как строку. А реально будет 25, потому что сначала будет сложение строк "1" и
"5", в результате чего получиться 15, а
потом произойдет сложение чисел 15 и 10.
Активно используется в БД в таких методах,как LookUp и тд…
16.Подпрограммы Delphi. Виды параметров. Локальные переменные,
вложенные подпрограммы.
Подпрограммы
Ранее уже рассказывалось о стандартных подпрограммах: процедурах и функциях, —
во множестве входящих в поставку Delphi 7. Далее описывается технология создания собственных подпрограмм.
Структура подпрограммы
Описание подпрограммы состоит из трех частей: заголовка подпрограммы, локального описания и тела подпрограммы. Заголовок используется, чтобы явно ввести в
Подпрограммы 55
программу новую подпрограмму и обозначить начало ее описания. Локальные описания представляют собой набор описаний типов, переменных, констант и других
подпрограмм, которые действуют только в рамках данной подпрограммы. Тело подпрограммы — это логический блок begin/end, содержащий операторы и команды
Паскаля и реализующий нужную логику работы.
J ВНИМАНИЕ Описание подпрограммы —этотолько описание, которое никогда
не выполняется само по себе и может располагаться в любом месте
исходного текста (но обязательно до первого вызова подпрограммы). Вызывается процедура или функция только внутри логического
блока begin/end с указанием конкретных значений для каждого из
ее параметров.
Заголовок
Заголовок подпрограммы состоит из трех частей: ключевого слова, характеризующего тип подпрограммы (процедура — procedure, или функция — function), списка
параметров, перечисленных через точку с запятой и взятых в круглые скобки, и
(только для функций) типа возвращаемого значения, указываемого вслед за двоеточием.
Список параметров содержит параметры, каждый из которых представляет собой произвольное имя переменной и соответствующий ей тип через двоеточие. Если несколько параметров имеют одинаковый тип, то их можно перечислить через запятую.
procedure ComputefX: Integer; S: String);
function Compare(Al, A2 : Byte; Stroka: String): Real;
procedure ShowMap;.
function GameOver: Boolean;
Список параметров может быть опущен. Для некоторых параметров могут быть
заданы значения по умолчанию (все параметры с такими значениями должны
располагаться вместе и после параметров, для которых не задано значений по умолчанию).
procedure MyProc
( I: integer; il: integer = 1; 12: integer = 2 ) ;
В подобных случаях заключительные параметры при вызове процедуры можно
опускать — они получат значения по умолчанию.
МуРгос( 1, 2 ];
Реально процедура вызывается с тремя параметрами, как если бы использовался
следующий оператор.
МуРгос( 1, 2, 2 ) ;
Передача параметров по имени и по значению
Когда происходит обращение к подпрограмме, переменным, указанным в списке
параметров в описании (формальные параметры), присваиваются значения, указанные в списке параметров в момент вызова (фактические параметры), после чего
5 О Урок 1. Язык Delphi (Object Pascal] и его использование
выполняются необходимые вычисления. Это называется передачей параметров по
значению: в подпрограмму передаются значения нужных типов. Формальные переменные играют роль локальных переменных.
В списке параметров перед любой переменной разрешается указывать ключевое
слово var. Тогда считается, что данный параметр будет передаваться по имени: то
есть копирования значения не происходит, а вместо формального параметра подставляется имя переменной— фактического параметра. (Отсюда следует, что в
качестве фактических параметров, подставляемых вместо формальных параметров
с ключевым словом var, можно применять только имена переменных.)
Например:
procedure MyProc( X: Byte; var Y: Byte );
Параметр X будет передаваться по значению, параметр Y — по имени. При вызове
МуРгос( 2+2, N ) ;
операторы в теле процедуры MyProc будут обрабатывать локальную переменную X,
имеющую значение 4 (2+2), и переменную Y, которая на самом деле является переменной N из другой части программы. На ≪физическом≫ уровне произойдет просто
подстановка адреса переменной N. Использование слова var в списке параметров
по способу действия аналогично использованию указателя.
При этом, если в теле процедуры значение переменной X будет меняться, то по
окончании работы MyProc эта локальная переменная будет удалена из памяти (до
следующего вызова процедуры), а вот если произойдет изменение значения переменной Y, то изменится и значение переменной N.
Это потенциально чревато ошибками, так как разработчик может не рассчитывать
на то. что во время работы подпрограммы произойдет изменение значения одного
из ее парамегров. В данном случае переменная Y является не чем иным, как
указателем на переменную N, только описан он немного иначе: не с помощью операции Л, а как обычная переменная.
Следует избегать передачи параметров по имени за исключением случая, когда требуется передать в подпрограмму данные большого объема. Например, если описан тип'
type TBigArray = array [1..100000] of string[50];
то передавать переменную этого типа по значению очень неэффективно, особенно
если вызов подпрограммы происходит часто, потому что при этом требуется копировать большие объемы данных. То есть описание
procedure Sum( A: TBigArray );
неудачно в плане эффективности (хотя компилятор может самостоятельно выполнить передачу параметра по имени в процессе автоматической оптимизации кода).
Правильнее написать так:
procedure Sum( var A: TBigArray );
При этом надо проследить, чтобы изменения элементов массива А внутри процедуры Sum не происходило (если этого не требует логика ее работы).
Подпрограммы 57
Параметры-константы
Если некоторый параметр будет использоваться в теле подпрограммы только для
считывания данных (фактически, как константа), лучше не рассчитывать на возможности компилятора, а подсказать ему об этом явно. В этом случае присваивание данному параметру нового значения станет недопустимым.
Подобная ≪подсказка≫ осуществляется указанием зарезервированного слова const
в списке параметров, что позволяет организовать эффективную обработку соответствующего параметра, не беспокоясь о возможных изменениях его значения.
procedure Sum( const A; TBigArray ) ,Менять значение элементов переменной А в теле процедуры Sum теперь нельзя:
компилятор сообщит об ошибке.
Параметры-результаты
В Паскале имеется еще одна возможность передачи параметра по имени — с помощью зарезервированного слова out. Такой параметр может использоваться внутри
тела подпрограммы только для присваивания значения (но не для считывания данных). По смыслу использование слова out противоположно использованию слова
const.
Если заголовок процедуры описан так:
procedure Sum( out A: TBigArray );
то в теле процедуры Sum можно указывать операторы присваивания
А[10000] := 'rmml72855' ;
но нельзя считывать значения элементов массива А:
х := А [ 2 ] * 2; // нельзя!
ПОДСКАЗКА Для получения результатов от подпрограммы всегда правильнее
использовать функции. Процедуры, возвращающие результат
через свои параметры, лучше применять только тогда, когда без
этого не обойтись (например, параметры-результаты требуются
при создании приложений СОМ и CORBA),
Преобразования сложных типов
Ранее рассматривалась возможность явного преобразования типов путем использования названия нужного типа как функции (Byte(300)). Паскаль допускает подобный способ преобразования не только для базовых, но и для любых других типов,
определенных разработчиком.
ЗАМЕЧАНИЕ В Паскале не делается различий между базовыми типами, исходно
существующими в языке, и типами, созданными программистами.
58 ' Урок 1. Язык Delphi (Object Pascal] и его использование
Потребность в этом хоть и редко, но все же возникает. Например:
type T1 = аггау[1..2] of Byte;
var Al: Tl;
A2: array[1..2] of Byte;
Переменные _____Al и. A2 относятся к рачным типам, хотя физически (в оперативной
памяти) они представлены одииаково. Поэтому переменную А2, обычный массив,
можно преобразовать (привести) к типу Т1:
Т1(А2)
Такая запись аналогична простому обращению к имени массива А2, только при
этом считается, что он имеет новый тип Tl. К элементам массива нового типа можно
обращаться, как и раньше, с помощью квадратных скобок:
Т1(А2)[1]
Параметры без типов
Паскаль позволяет указывать в заголовке подпрограммы параметры без типов (но
с предшествующим словом const, var или out).
procedure Surn( const A ) ;
Переменная А в таком случае считается не имеющей никакого типа, и ее нельзя
использовать ни в каких выражениях или операторах, не выполнив предварительно
преобразование — приведение к нужному типу.
Например, в теле процедуры Sum переменную А можно использовать так:
X := Т1 (А) [5] * 2,Передача строк фиксированной длины
Строки можно описывать, как уже говорилось ранее, двумя способами: просто как
тип string, или как тип string с конкретной длиной строки в квадратных скобках.
Однако указывать строки фиксированной длины в качестве параметров подпрограмм нельзя. Их сначала надо описать как новый тип.
procedure Sum( S: string[50] ); // неверно!
Правильно написать так:
type stringSO = string[50];
procedure Sum( S: stringSO };
Передача массивов в качестве параметров
Стандартный прием Паскаля состоит в описании массива как типа данных и указании этого типа для параметра подпрограммы. Однако таким способом удается
обрабатывать только динамические массивы и статические массивы заранее заданной длины. Часто требуется, чтобы подпрограмма не ограничивалась конкретными
размерами массива. Например, функция, вычисляющая сумму всех элементов массива, должна уметь получать в качестве параметра массив любой длины. ДинамиПодпрограммы 59
ческие массивы использовать не всегда удобно и не всегда правильно, так как они
ухудшают эффективность работы программы.
В Паскале разрешается указывать в качестве параметров массив с неопределенными
границами.
procedure Sum( A: array of Byte );
ВНИМАНИЕ Данное описание похоже но описание динамического массива, но
не является токовым. Это просто форма записи параметра подпрограммы для передачи статического массива заранее не известной
длины.
Теперь массив, описанный как
var Ar: array [1..25] of Byte;
можно без проблем передавать в процедуру Sum:
Sum(Ar];
При этом внутри тела подпрограммы действуют следующие правила:
О Нумерация элементов массива начинается с нуля (число элементов можно
определить с помощью стандартной функции SizeOf);
О Функция High возвращает верхнюю границу массива (равную SizeOf()-l);
О Копирование массива одним оператором присваивания не разрешается;
О Вместо массива в подпрограмму может быть передана обычная переменная
соответствующего типа. Внутри подпрограммы она будет представлена в
виде массива из одного элемента (с индексом 0).
Передача значений как массива
Вместо указания в параметрах при обращении к подпрограмме имени переменноймассива можно указать непосредственно содержимое этого массива: список значений через запятую в квадратных скобках.
Sum( [1, 5, X, а+Ь'2] );
Передача массива вариантного типа
Массив, описанный как
array [L.MaxNum] of Variant;
может быть передан в качестве параметра в подпро]рамму, если этот параметр описан как array of const:
procedure Sum( A; array of const };
Внутри тела подпрограммы надо анализировать перед обработкой тип каждого элемента этого массива.
60 Урок 1. Язык Delphi [Object Pascal) и его использование
Способы вызова подпрограмм
•Подпрограммы с точки зрения прикладного программиста вызываются всегда одинаково, но машинный код, который создается компилятором, для разных подпрограмм может отличаться. Это зависит от целей применения конкретной подпрограммы. Она может использоваться:
О в рамках разрабатываемой прикладной программы;
О как функция, доступная из динамической библиотеки .DLL;
О как процедура, вызываемая из внешних программ или из Windows и т. д.
Для каждого из таких нестандартных случаев вслед за заголовком подпрограммы
(за точкой с запятой) должно следовать одно из ключевых слов.
Таблица 1.10. Ключевые слова в заголовке подпрограмм
Ключевое слово Способ передачи параметров
pascal Стандартный (параметры помещаются в стек)
register Способ, применяемый по умолчанию. Аналогичен использованию ключевого
слова pascal, но параметры передаются с помощью трех регистров
процессора, а не помещаются в стек (область оперативной памяти),
что обычно приводит к повышению быстродействия программы
cdecL В соответствии с соглашениями компиляторов для языков программирования
С и C++. Применяется, когда происходит обращение к динамическим
библиотекам DLL, написанным на этих языках
stdcall В соответствии с соглашениями Windows
safecall Используется при работе с компонентными технологиями
При использовании ключевых слов register и pascal вычисление параметров выполняется слева направо и располагаются они в оперативной памяти перед вызовом
подпрограммы в таком же порядке. При использовании ключевых слов cdecL, stdcall
и safecall параметры располагаются в обратном порядке (справа налево).
procedure Sum( A: array of const ); stdcall;
Существует еще одно зарезервированное слово Паскаля, forward, которое, при указании вслед за заголовком, говорит компилятору о том, что в данном месте расположен только заголовок подпрограммы, а все ее описание находится в исходном
тексте далее. Такое описание обычно применяют, если в тексте имеется несколько
подпрограмм, которые вызывают друг друга по кругу. Например, из процедуры Р1
вызывается процедура Р2, а из процедуры Р2 — процедура Р1.
Паскаль требует, чтобы любой идентификатор, будь то переменная или подпрограмма, был предваритачьно, до первого своего использования, описан. В таком круговом процессе (пример условный, потому что приведенная схема может привести к бесконечной цепочке вызовов) процедура Р2, вызываемая в теле процедуры Р1,
расположенной выше в исходном тексте, еще не описана, следовательно, обращаться
к ней нельзя и компилятор сообщит, что обнаружен неопределенный идентификатор.
Подпрограммы 61
Неправильно:
procedure PI;
begin
P2;
end;
procedure P2;
begin
PI;
end,Правильно:
procedure P2; forward;
procedure PI;
begin
P2;
end;
procedure P2;
begin
PI;
end,Теперь компилятор знает, как выглядит заголовок процедуры Р2, и может корректно
сгенерировать машинный код для обращения к ней.
'1 ПОДСКАЗКА Без таких запутанных круговых ссылок и ключевого слова forward
всегда можно обойтись. Лучше не усложнять структуру программы
его использованием.
Перегружаемые подпрограммы
Хотя в Паскале не допускается использование одинаковых названий для переменных, констант и других идентификаторов, для локальных переменных и подпрограмм делается исключение. Так как в Паскале предъявляются строгие требования к типам данных, обращаться к подпрограмме, формальные параметры которой
имеют тип Integer, с фактическими параметрами, имеющими тип Real, нельзя. Однако
при решении задачи подчас бывает необходимо, чтобы подпрограмма с одним и
тем же именем работала с разными типами данных.
Здесь есть два способа действий: либо использовать данные типа Variant (что чревато ошибками преобразования, снижает общую эффективность программы и требует от разработчика повышенной бдительности), либо применить перегружаемые подпрограммы. Они отличаются от обычных подпрограмм тем, что имеют
совпадающие имена, а различаются только типами аргументов. Чтобы указать компилятору, что конкретная подпрограмма — перегружаемая, надо вслед за ее заголовком указать зарезервированное слово overload.
62 Урок 1. Язык Delphi (Object Pascalj и его использование
При вызове такой подпрограммы компилятор по типам параметров автоматически
определит, какую же подпрограмму конкретно надо использовать в данном месте.
procedure Ovl ( X: Real ) ,- overload;
begin
end;
procedure Ovl ( X: Byte ) ,- overload;
begin
and;
Ovlf 1 ); // вызывается процедура Ovl( X: Byte )
Ovl( 1.0 ); // вызывается процедура Ovlf X: Real )
Необходимое требование к перегружаемым процедурам состоит в том, чтобы списки
параметров совпадали во всем, за исключением типов переменных.
Надо особенно осторожно использовать перегружаемые подпрограммы с параметрами по умолчанию. Например:
procedure Ovl( X: Byte; Y: Real - 1 ); overload;
begin
.
end;
procedure Ovl ( X: Byte ) ,- overload;
begin
end;
При вызове
Ovl( 1 )
компилятор не сможет понять, какую из двух процедур ему вызывать, и сообщит
об ошибке.
Локальное описание
Сразу за заголовком подпрограммы следует локальное описание типов, переменных и
констант, локальных для данной подпрограммы и существующих только в ее границах. Такое описание подчиняется обычным правилам Паскаля. В нем разрешается
использовать слова type, var и const. Локальное описание может быть опущено.
Вложенные подпрограммы
Помимо обычных описаний, внутри подпрограммы допускается объявлять также
локальные подпрограммы, к которым можно обращаться (вызывать) только из тела
≪родительской≫ подпрограммы. При этом локальная подпрограмма может свободно
Подпрограммы 63
обращаться к любым локальным описаниям (переменным, типам), которые расположены до описания данной подпрограммы.
Такая возможность полезна, когда во время кодирования подпрограмма начинает
непредвиденно разрастаться. Ее приходится делить на более мелкие фрагменты,
которые в то же время желательно не выносить за пределы текущей подпрограммы,
чтобы иметь возможность пользоваться ранее сделанными локальными описаниями.
Например:
procedure Demo;
type Tl = array [1..2] of Real;
var D, Dl: Tl;
S: Real;
procedure InDemo;
begin
Dl := D;
S := Dl[l] + Dl[2]
end;
begin
end;
Уровень вложенности локальных подпрограмм неограничен.
Тело подпрограммы
Тело подпрограммы заключается в логические скобки begin/end. В них располагаются только операторы и вызовы других подпрограмм.
Возврат значений из функции
Если описывается функция, то в ее теле надо определить, как значение будет возвращено в вызываемую программу. Для этого есть два способа.
1. Соответствующее значение присваивается переменной, имя которой совпадает
с названием функции.
function Sum( А, В: Integer ): Integer;
begin
Sum := A + В
end;
Имя функции для возврата значения разрешается указывать только в левой
части оператора присваивания.
2. Соответствующее значение присваивается специальной локальной переменной Result (эту переменную описывать не надо).
64 Урок 1. Язык Delphi (Object Pascal) и его использование
function Sumf А, В: Integer ): Integer;
begin
Result := A + В
end,Вызов подпрограммы
Когда в тексте программы указывается имя ранее описанной подпрограммы с фактическими параметрами, то выполнение основной части программы останавливается и управление передается подпрограмме, до тех пор пока в ходе работы не будет
достигнут ее конец (зарезервированное слово end). После этого управление передается обратно в программу (или другую подпрограмму), вызывавшую данную
подпрограмму.
Параметры должны следовать в "строгом соогветствии с порядком их описания в
заголовке подпрограммы. Типы их так же должны точно совпадать с указанными.
Если параметров у подпрограммы нет, то записывается только название подпрограммы и следующая за ней точка с запятой.
Demo;
Функции, возвращающие значение, могут использоваться так же, как и процедуры.
Например, описанную выше функцию Sum можно вызывать так:
X := Sumf 2,2 );
а можно и так:
Sum( 2,2 );
Б последнем случае значение, возвращаемое функцией, просто теряется.__
17.Процедурные типы данных.(указатель на код)
Процедуры, играющие роль операторов
С развитием языка Паскаль в рамках среды Delphi 7 в него добавлялось множество
новых полезных возможностей, нередко заимствованных из других языков программирования. Эти возможности вводились в Паскаль не в виде новых операторов,
что нарушило бы идеологию языка, а в виде стандартных подпрограмм, которые,
хотя и не выделяются цветом наравне с другими ключевыми словами, тем не менее,
фактически являются таковыми. И реализуются подобные подпрограммы не в виде
обращений к машинному коду, хранимому в программной библиотеке. Компилятор не добавляет в генерируемый код ссылку, а превращает данную ≪процедуру≫,
подобно обычным операторам, в небольшой набор машинных инструкций (а иногда
и в одну такую инструкцию).
Одна из таких весьма полезных процедур — Exit (без параметров).
Exit ;
При ее выполнении происходит немедленное завершение текущей подпрограммы
и передача управления вызывающей программе. Такая возможность часто требуется, когда логика, реализуемая в подпрограмме, достаточно сложна и организовать
линейный выход из подпрограммы (по достижении ее конца) затруднительно.
Подпрограммы 65
Полезна подпрограмма Exit и в тех случаях, когда при определенных значениях
параметров вычислить значение функции удается сразу. Например, если при вычислении факториала числа параметр равен 1 , можно сразу определить возвращаемое
значение, также равное 1, и покинуть подпрограмму.
Процедурные типы
Процедуры в Паскале разрешается использовать при описании новых типов. На
основании таких типов, обладающих равными правами с другими типами, можно
описывать переменные, что позволяет передавать подпрограммы в качестве параметров. Подобный прием в некоторых случаях помогает эффективно решить сложную задачу, с трудом поддающуюся кодированию обычным способом, однако
пользоваться им нежелательно, по крайней мере начинающим разработчикам.
Примеры:
type TSumFun = function! А, В: Integer ): integer;
TEmptyProc = procedure;
TMyProc = procedure ( X: Real ) ;
var SumP: TSumFun;
EmptyProc : TEmptyProc ;
MyPr : TMyPrOC ;
ВНИМАНИЕ При описании процедурных типов названия подпрограмм не указываются. Приводится только основная схема заголовка.
Далее, если в тексте описана подпрограмма, то ее можно присвоить переменной
соответствующего типа:
SumP := Sum;
EmptyProc := Demo;
ВНИМАНИЕ В правой части оператора присваивания указывается только название подпрограммы без параметров.
Для обращения к нужной подпрограмме теперь можно указывать не только ее имя,
но и имя переменной, хранящей ≪описание≫ этой подпрограммы:
X : = SumP ( 2,2 ) ,Реально здесь произойдет вызов функции Sum с аргументами 2 и 2.
ЗАМЕЧАНИЕ Переменные процедурных типов являются указателями. В них хранится только адрес ячейки оперативной памяти, где начинается со-
ответствующая процедура.
Подпрограмму можно передавать в другую подпрограмму как параметр. Это удобно,
когда над аргументами надо выполнять различные сложные действия в зависимости
3 За* 348
66 Урок 1. Язык Delphi (Object Pascal] и его использование
от некоторых условий. В приведенном ниже примере функция MathAction будет
вычислять или сумму, или разность параметров, в зависимости от того, какая функция указана в качестве параметра.
type TMathFun = function! X,Y: Integer ): Integer;
function Add( X,Y: Integer ): Integer;
begin
Add := X+Y
end;
function Sub( X,Y: Integer ): Integer;
begin
Sub := X-Y
end;
function MathActionl X,Y: Integer;
Proc: TMathFun ): Integer;
begin
Result := Proc! X,Y )
end;
Теперь к функции MathAction можно обращаться так:
WriteLn( MathActioni 10, 4, Add ) );
при этом будет напечатано число 14, или так:
WriteLnl MathActionl 10, 4, Sub ) };
В этом случае будет напечатано число 6.
Чтобы проверить, содержит ли переменная процедурного типа описание конкретной
подпрограммы, используется стандартная функция AssignedQ, которая в качестве
аргумента получает процедурную переменную, а возвращает значение типа Boolean
(если оно равно True, то переменная имеет корректное значение).
С переменными, хранящими указатели на функции, надо обращаться осторожно.
Переменной, хранящей указатель на процедуру, можно присвоить значение такой же
переменной. В то же время, если в левой части оператора присваивания стоит переменная типа, совпадающего с типом значения, возвращаемого функцией, то произойдет вызов функции. Когда надо выполнять копирование указателей, а когда —
вызывать функции, решает компилятор в зависимости от контекста.
type TP = procedure;
TF = function: integer,var pi, p2: TP;
fl, £2: TF;
N: Integer;
Операторы 67
procedure А;
begin
end;
function В: integer;
begin
В := О
end;
p2 := A;
Pi := p2;
// произойдет копирование указателя
f2 : = В,fl :- f2;
// произойдет копирование указателя
N := f2;
// произойдет вызов функции В
//и запись значения в переменную N
Указатели на подпрограммы
Хотя переменные, описанные как процедурные типы, фактически являются указателями, от программиста эта их особенность скрыта. Но Паскаль разрешает также
явно описывать переменные-указатели на подпрограммы. Для этого в языке введен
особый тип данных Pointer, представляющий собой указатель на информацию, не
имеющую конкретного типа. Получение адреса начала подпрограммы выполняется
с помощью операции @, так же, как и для получения адреса любых других данных.
var X: Pointer;
X := OMyProcedure;
Подобным способом в программе определяются и процедурные константы-указатели:
const P: Pointer = OMyFunction;__
(объявление ссылок на метки??)
@F адрес того,что лежит в переменной
@@Fадрес самой переменной
18.Управляющие конструкции if и case.
Операторы
С помощью оператора присваивания можно написать простые программы, преимущественно ориентированные на математические вычисления, но для создания
приложений, реализующих сложную алгоритмическую логику, нужны средства
68 Урок 1. Язык Delphi (Object Pascal) и его использование
управления ходом работы программы: изменения порядка выполнения операторов в зависимости от различных условий и эффективной реализации часто повторяющихся вычислений.
Условный оператор
Условия
Один из важнейших операторов Паскаля — условный оператор. Он позволяет изменить порядок выполнения операторов в зависимости от некоторого условия, представляющего собой логическое выражение типа Boolean. Если это значение равно
True, то выполняется одна группа операторов, если оно равно False, то выполняется
другая группа операторов или не выполняется ничего.
Условия представляют собой логические выражения. В них происходит сравнение значений выражений, вызов функций, возвращающих значение типа Boolean,
и комбинирование этих значений с помощью логических операций. Ниже приведены основные операции сравнения данных.
Таблица 1.11. Основные операции сравнения данных
Знак операции Название операции
Равно
о Не равно
> Больше
< Меньше
>- Больше или равно
<- Меньше или равно
Если используются логические операции or, and и так далее, то связываемые ими
проверки заключаются в круглые скобки.
X>5
(I >= 1) and (I <= 10)
(а+5 о Ы or BoolFunc
Alf = 3
Для некоторых типов данных в Паскале имеются дополнительные операции, позволяющие сформировать более сложные условия. В частности, для множеств определена операция in (зарезервированное слово), которая проверяет, входит ли конкретное значение в множество:
X := [2,4,6,8,10] ;
Выражение 2 in X имеет значение True.
Выражение 5 in X имеет значение False.
Такой способ очень удобен тем, что позволяет выполнить проверки более наглядно
и компактно.
Операторы 69
Например, вместо того, чтобы писать
(I >= 1) and (I <= 10)
можно использовать операцию in:
I in [1..10]
Выполняются подобные проверки тоже значительно эффективнее.
Оператор if... then ...
Условный оператор записывается в такой форме:
if условие then действие;
Слова !f (если) и then (то) — зарезервированные.
Действие выполняется только в том случае, если значение условия равно True. В противном случае ничего не происходит. Действие —это любой оператор Паскаля, или
группа
операторов, взятых в логические скобки begin/end, или вызов подпрограммы.
Например:
if X > 0 then X := 0;
if (Ы = 2) or (N = 3) then CallProc;
if Sim in ['а'.-'я'] then
begin
WriteLn( Sim );
Sim := Chr( 100 )
end;
Оператор if ...then ... else ...
Нередко требуется выполнить определенные действия и в том случае, когда проверяемое условие ложно. Для этого можно использовать другую форму условного
оператора:
if условие
then действие-1
else действие-2;
Действие-1 будет выполнено, если условие истинно (равно true), действие-2 выполняется, если условие ложно.
if X > 0 then X := -1 else X := +1;
ЗАМЕЧАНИЕ Перед ключевым словом else (иначе) точка с запятой не ставится.
Напишем небольшую программу, которая принимает ввод числа с клавиатуры и
сообщает, четное оно или нет. Проверку на четность можно выполнить двумя способами: использовать стандартную функцию Odd(}, которая возвращает значение
True, если ее аргумент нечетный, или проверить, равен ли нулю остаток отделения
числа на 2 (операция mod).
70 Урок 1. Язык Delphi (Object Pascal] и его использование
program Projectl;
{$APPTYPE CONSOLE}
uses sysutils,var N: Integer;
begin
ReadLn ( N ) ,if Gdd(N) then WriteLn('Число нечетно'}
else WriteLn('Число четно']
end.
Вложенные условия
Условные операторы могут быть неограниченно вложены друг в друга. Рассмотрим следующий пример. Надо ввести с клавиатуры два числа, и если их сумма
меньше чем 100, то напечатать большее число, а в противном случае — напечатать
меньшее число.
program Projectl;
(SAPPTYPE CONSOLE)
uses sysutils;
var X, Y: Integer;
begin
ReadLn( X, Y );
/ / если сумма меньше ста, то
if X+Y < 100 then
// определить большее число
if X > Y then Writeln(X)
else WriteLn(Y)
// иначе определить меньшее число
else if X < Y then Writeln(Xi
else WriteLn(Y)
end;
При решении данной задачи использованы вложенные условные операторы,
В некоторых случаях бывает сложно разобраться, в каком порядке такие вложенные операторы будут выполняться. Например:
if a > b then
if a > е then с := 1
else с := 2;
Здесь непонятно, в каком случае выполняется оператор с := 2: когда ложно условие
а > b или когда оно истинно, но ложно условие а > е.
В Паскале действует простой принцип: часть условного оператора else относится к
ближайшему if. В данном случае, часть else с := 2 относится к ближайшему оператору
if а > е then, а не к оператору If a > b then, тем самым правильно второе толкование.
Операторы 71
Надо стараться записывать подобные вложенные операторы максимально наглядно,
используя отступы из пробелов, избегая запутанных конструкций и не допуская
слишком большого уровня вложенности.
Если в приведенном примере требуется, чтобы оператор с := 2 выполнялся, когда
ложно условие а > Ь, то промежуточный условный оператор надо вынести в логический блок, чтобы он не влиял на порядок выполнения условных частей then и else:
if а > Ь then
begin
if a > е then с := 1
end
else с := 2;
Оператор выбора
Когда требуется осуществить проверку множества условий, например выполнить
один из пяти операторов в зависимости от того, чему равно значение переменной
X, приходится записывать цепочки условных операторов наподобие следующей:
if X = 1 then а : = 1 else
if X - 2 then a :- 2 else
if (X = 3) or (X = 4) then a := 3 else
if X = 5 then a := 4 else
if (X = 6) or (X in [8..100]) then a := 5
else a := 0;
Подобная запись довольно громоздка и преобразовывается компилятором в не
очень эффективный машинный код. В Паскале имеется более удобный оператор
выбора case, позволяющий наглядно описать выбор выполняемого оператора или
группы операторов в зависимости от ряда условий.
case выражение of
список-условий-!: действие-l;
список-условий-n: действие-п;
else действие-п+1;
end;
Тип выражения может быть одним из стандартных типов: целым числом, псречислимым типом, символьным типом и так далее. Список условий может содержать
произвольные выражения, состоящие из констант и имеющие подходящий тип.
В этом списке допускается использовать как обычные константы, так и символы и
диапазоны значений. Результат выражения будет поочередно сравниваться с каждым из значений в списках, и при первом совпадении будет выполнено соответствующее действие (оператор или группа операторов, взятых в логические скобки
begin/end), а все оставшиеся действия будут пропущены. В случае, когда ни одного
совпадения результата выражения с заданными значениями не произошло, выгтол72 Урок 1. Язык Delphi (Object Pascol) и его использование
няется действие, указанное за словом else (если оно имеется), или не выполняется
ничего, если слово else внутри оператора выбора отсутствует.
Переписанный с помощью данного оператора вышеприведенный пример будет
выглядеть так(через запятую,отдельные значения и в виде массива)
case X of
1: а := 1,2: а := 2;
3, 4: а := 3;
5 г а := 4;
6, 8. .100: а := 5;
else a := О
end;
Условное описание
Использовать оператор case в Паскале разрешается также в разделе описания переменных, когда создается новый тип на основе записи (record). Внутри записи допустимо указывать несколько форм ее представления, и описываются все эти формы
≪в стиле≫ оператора case (это только правило описания, способ нужного представления, понятный компилятору и разработчику, а не реальный машинный код).
Например, программисту требуется использовать два типа данных: одну запись с
полями:
Name: string[50];
Num: integer;
а другую — с полями:
TeamName: TFootballName;
Win: Boolean;
Gol: Byte;
При этом программист точно знает, что он не будет применять эти группы полей
одновременно друг с другом. Тогда вместо описания двух новых типов программист может описать один, только разделив внутреннюю структуру записи на две:
type МуТуре =
record
case MyVar: Boolean of
True: (Name: string[50];
Num: integer);
False: fTeamName: TFootballName;
Win: Boolean;
Gol: Byte);
end;
Подобная оригинальная запись расшифровывается следующим образом. Слово case
говорит, _____что далее следует несколько форм представления типа (записи) МуТуре.
Сколько? Это определяется следующим за ключевым словом case описанием MyVar:
Операторы /3
Boolean. Имя переменной MyVar реально больше нигде в программе не используется,
это просто требование Паскаля. А тип этой переменной определяет возможный
диапазон форм представления записи. Тип Boolean имеет два значения True и False,
значит форм представления будет две. Можно было указать, например, тип Byte и
сформировать не два, а пять или двадцать пять описаний внутри записи.
Далее следует последовательность значений указанного типа, и для каждого из
них в круглых скобках приводится очередной вариант внутренней структуры.
'J ВНИМАНИЕ Закрывает описание case не собственное ключевое слово end,
а слово end, завершающее описание всей записи.
Обращаться к полям записи в тексте программы можно как обычно:
var R: МуТуре;
R.Name := 'test' ;
R.Win : = true;
Конечно, одновременно обращаться к полям из разных описаний не следует: ведь
создаются подобные раздельные описания как раз исходя из того, что совместно
использоваться они не будут.
Есть еще одна область применения подобных условных описаний. В памяти для
них отведена одна общая область, поэтому одно и то же представление можно трактовать по-разному, в зависимости от размера (длины в байтах) значения конкретного типа. Например, поскольку тип Integer соответствует данным длиной в 4 байта,
доступ к его отдельным байтам можно организовать так:
type IntDetail =
record
case V: Byte of
1: (I: Integer);
2: (Bl, B2, ВЗ, В4: Byte)
end;
var ID: IntDetail;
begin
ID.I := 55555;
WriteLn (ID. Bl, ' ' , ID. B2 , ' ' , ID. B3 , ' ' , ID. B4 ) ,ID.B1 := $FF;
'J ПОДСКАЗКА Возможность розного представления структуры записи сохраняется в.Паскале с времен его первых версий, когда остро стояли
проблемы-эконом и и памяти и приходилось прибегать к всевозможным ухищрениям. Сегодня такие возможности Паскаля неактуальны
и только усложняют понимаемость исходных текстов, поэтому желательно обходиться без них.__
19.Операторы циклов в Паскале. Структурные команды перехода.
Оператор цикла
Б программировании постоянно возникают задачи, требующие для своего решения многократного повторения одной и той же последовательности действий или
однообразной обработки однородных объемов информации (массивов). Например, когда требуется определить сумму всех элементов массива, найти его элемент
с максимальным значением, вычислить квадраты всех элементов и так далее.
В Паскале имеется несколько операторов, позволяющих организовать подобную
работу наглядно и эффективно. Операторы, предназначенные для многократного
(циклического) выполнения заданной последовательности команд, называются
операторами цикла. Они всегда имеют заголовок цикла, определяющий число повторений, и тело цикла — повторяемое действие.
Оператор цикла записывается так:
for переменная-счетчик := выражение-1 to выражение-2 do
повторяемое-действие;
Переменная-счетчик должна быть объявлена перед логическим блоком, в котором
этот оператор расположен, то есть если оператор цикла используется внутри подпрограммы, то в качестве счетчика должна выступать локальная переменная.
ЗАМЕЧАНИЕ Это сделано с целью максимально оптимизировать быстродействие
оператора цикла, ориентированного на многократные интенсивные
вычисления. Переменные-счетчики представляют собой, как правило,
быстрые регистры процессора, а не обычные ячейки оперативной
памяти.
Первоначально, перед первым выполнением тела цикла, счетчик получает значение, равное результату вычисления, выражен ия-1. Переменная-счетчик должна обязательно относиться к одному из перечислимых типов. Выражение-2 определяет
конечное значение, по достижении которого счетчиком тело цикла будет выполнено в последний раз.
После очередного выполнения тела цикла счетчик принимает значение, следующее
за текущим (точнее, это будет значение, равное SuccQ от текущего). Затем выполняется проверка, не превышено ли конечное значение выражения-2 (значения и
начального, и конечного выражений вычисляются только один раз перед первым
выполнением тела цикла). Если оно превышено, то работа оператора цикла заканчивается. В противном случае тело цикла выполняется еще раз.
Рассмотрим пример подпрограммы, вычисляющей факториал числа N. Для этого
требуется последовательно перемножить все числа от 1 до N. Наиболее компактно
такие вычисления реализуются с помощью оператора цикла.
function Factorialf N: integer ]: integer;
var i, R: integer;
begin
if N <= 0 then
begin
Операторы 75
Result := 0;
Exit
end;
R := 1;
for i := 1 to N do
R := R * i;
Result := R;
end;
Счетчик i будет последовательно принимать значения от 1 до значения параметра N, а
s переменной R будет храниться промежуточное значение результата. Обратите
внимание на условный оператор, который проверяет корректность значения параметра
(оно должно быть положительным числом). Если значение параметра N меньше
или равно нулю, то значение факториала принимается равным нулю без вычислений, после чего происходит выход из подпрограммы с помощью процедуры Exit.
В Паскале имеется еще одна форма записи оператора цикла. Она предназначена
для использования в случаях, когда счетчик должен последовательно принимать
не возрастающие, а убывающие значения.
for счетчик := выражение-1 downto выражение-2 do
тело-цикла;
Значение счетчика в этом случае будет не увеличиваться, а уменьшаться (как при
вызове процедуры PredQ). Соответственно, необходимо, чтобы значение выражения-2 было меньше значения выражения-1. Тип счетчика — не обязательно целое
число. Это может быть, в частности, перечислимый тип.
type TLoop = (MinVal, AveVal, MaxVal);
var L: TLoop;
begin
for L := MinVal to MaxVal do
Условный оператор цикла
Оператор for удобно применять, когда заранее известно, сколько раз требуется
выполнить тело цикла (например, при обработке всех элементов массива). Но в
большом количестве задач цикл приходится выполнять неизвестное число раз. Это
происходит, если вычисление значения функции заканчивается по достижении
заданной точности, если выполнение операторов зависит от информации, введенной пользователем, если надо найти в массиве элемент с конкретным значением и
так далее. В таких случаях правильнее использовать условный оператор цикла:
while условие do
тело-цикла;
Тело цикла будет выполняться, пока истинно условие (логическое выражение,
воз вращающее значение типа Boolean). В отличие от оператора for условие окончания
цикла каждый раз вычисляется заново. Это позволяет гибко управлять числом повторений цикла.
76 Урок 1. Язык Delphi [Object Pascal] и его использование
ВНИМАНИЕ Если перед выполнением оператора while значение условия равно False, тело цикла не будет выполнено ни разу.
Допустим, надо написать программу, которая вычисляет факториал вводимого с
клавиатуры числа, но делает это не один раз, а многократно — до тех пор, пока
человек не введет число 0, служащее условным признаком завершения работы программы. Используя ранее подготовленную подпрограмму Factorial, такую программу
можно записать следующим образом:
program Projectl;
{SAPPTYPE CONSOLE}
uses sysutils;
/ / здесь следует
// описание подпрограммы Factorial
var N: integer;
begin
N := 1,while N o 0 do
begin
ReadLn(N);
WriteLnf Factorial (N) ) ,end;
end.
При выполнении тела цикла вводится начальное значение для вычисления факториала и печатается результат вызова функции Factorial(). Цикл повторяется до тех
пор, пока введенное значение не окажется равным нулю, после чего работа программы завершится. Первый оператор присваивания N :- \ нужен, чтобы войти в
условный цикл (так как начальное значение переменной N не определено).
ПОДСКАЗКА Надо внимательно следить за вычислением условия завершения.
Если в случае с оператором for число повторений цикла известно
компилятору заранее, то в случае с оператором while контроль
завершения цикла полностью возлагается на программиста.
Иногда оператор while может повторять тело цикла бесконечное
число раз, что приводит к зацикливанию и ≪зависанию≫ программы.
Условный оператор повторения
Исторически, в Паскале имеется еще одна форма условного оператора цикла, отличающаяся от формы записи оператора while. Это отличие состоит, во-первых, в
том, что проверка условия выполняется не в начале цикла, а в его конце (что гарантирует как минимум однократное выполнение тела цикла), а во-вторых, в том, что
завершение цикла происходит, когда условное выражение равно не False, a True (эта
особенность часто вызывает ошибки у начинающих программистов).
Операторы 77
repeat
тело-циклa
until условие;
ВНИМАНИЕ В отличие от всех остальных операторов Паскаля при использовании оператора repeat тело цикла, состоящее из нескольких команд,
заключать в логические скобки begin/end не требуется. Компилятор
определяет границы тела цикла по ключевым словам repeat/until.
С помощью данного оператора можно переписать программу вычисления факториала так:
begin
repeat
ReadLn(N) ;
WriteLnf Factorial (N) );
until N = 0,end.
Условие завершения цикла изменилось на противоположное — теперь для его окончания требуется, чтобы значение выражения N = 0 стало равно True (то есть, чтобы
значение переменной N стало равным нулю), а лишний оператор инициализации
переменной N не нужен.
Команда прерывания цикла
Условный оператор цикла позволяет остановить выполнение тела цикла, только
когда все операторы, входящие в него, выполнены и достигнута проверка условия
окончания. Такой подход иногда неудобен, особенно если тело цикла представляет
собой длинную последовательность операторов и необходимость завершения цикла
выясняется в середине этой последовательности. Для немедленного завершения
текущего оператора цикла можно использовать подпрограмму Break без параметров (это подпрограмма, играющая роль оператора). Полезна данная команда и при
использовании оператора for, например, когда в массиве с известными границами
найдено нужное значение и дальнейшие вычисления выполнять не надо.
Например, если в строке S требуется найти номер первого пробела, можно приме-
нить следующие операторы:
•
N := 0;
for i := 1 to Length(S) do
if S[i] = ' ' then
begin
N := i;
Break;
end;
if N > 0 then
78 Урок 1. Язык Delphi (Objecl Pascal) и его использование
В переменной N хранится номер подходящего символа (первоначально — 0). Б цикле
выполняется проверка каждого символа строки, при обнаружении пробела происходит запоминание номера символа и прерывание выполнения тела цикла. Затем
значение переменной N сравнивается с нулем, чтобы определить, был ли найден
нужный символ.
SЗАМЕЧАНИЕ В системе Delphi 7 имеется стандартная функция Роз[), которая,
получая в качестве параметров подстроку и проверяемую строку,
возвращает номер вхождения подстроки в строку или ноль, если
совпадения не найдено:
Ров ( ' ', S)
Команда ____________продолжения цикла
В Паскале имеется команда, по своему действию противоположная команде прерывания цикла. Она позволяет немедленно продолжить выполнение цикла, пропустив все оставшиеся операторы в теле цикла. Эта команда (подпрограмма без
параметров, играющая роль оператора) записывается так:
Continue,С ее помощью предыдущий пример можно записать следующим образом:
N := 0;
for i := 1 to Length(S] do
begin
if S[i] <> ' ' then Continue;
N := i;
Break;
end;
if N > 0 then ...
При очередном выполнении тела цикла сначала произойдет проверка текущего
символа на равенство пробелу, и если это не пробел, то выполнится команда продолжения цикла — все последующие операторы будут пропущены, а счетчик примет
новое значение.
Вложенные циклы
При решении некоторых задач возникает потребность в организации вложенных
циклов. Например, при анализе двумерного массива требуется выполнять цикл
как по первому, так и по второму измерениям. В таких случаях используют вложенные циклы. Процедуры Break и Conti nue всегда воздействуют только иа ближайший
оператор цикла, поэтому прекратить выполнение всех циклов с их помощью невозможно.
Например, требуется написать программу, которая печатает все целые числа, сумма
квадратов которых равна заданному числу. Проще всего сделать это с помощью
двух вложенных циклов, которые последовательно перебирают все возможные
Операторы 79
значения, а в теле внутреннего цикла проверяется, не равна ли сумма квадратов
значений счетчиков заданной величине.
program Projectl;
{$APPTYPE CONSOLE}
uses sysutils;
var i,j,N: integer;
begin
ReadLn(N| ,for i := 1 to N dlv 2 do
for j := 1 to N div 2 do
if i*i + j*j = N then
WriteLn(i, ' , ' , j I ;
end.
В каждом из циклов рассматривается диапазон значении от 3 до половины величины введенного числа (потому что сумма квадратов половин числа заведомо больше
или равна этому числу). Можно найти дополнительные способы улучшить этот
код, в частности, вынести вычисление произведения i*i из тела вложенного цикла,
потому что это произведение не имеет смысла многократно вычислять во вложенном
цикле, где оно всегда будет иметь одно и то же значение, а операция умножения
для процессора достаточно дорогая (медленная).__
20.Работа с текстовыми файлами.
Работа с файлами
Способы работы с файлами в системе Delphi 7
При работе с файлами в системе Delphi 7 возможны два принцип иально разных
подхода. Первый состоит в использовании стандартных подпрограмм (они имелись
еще в классической версии Паскаля тридцатилетней давности), позволяющих записывать содержимое переменных в файлы и считывать их обратно из файлов в переменные. К этим средствам добавились также библиотеки стандартных функций
по работе с файлами, основанные на системных функциях Windows.
В связи с появлением версии Object Pascal и реализации понятия класса в языке
появились средства объектной работы с данными. Это второй подход к работе с
файлами в системе Delphi 7. В свою очередь в рамках каждого из подходов применяются также существенно различающиеся приемы. Например, при классическом
подходе в работе с файлами могут использоваться прямые обращения к функциям
Windows или обращения к функциям BIOS.
Общая технология работы с файлами в Delphi 7
Несмотря на все различия, независимо от используемого подхода технология работы
с файлами в системе Delphi 7 требует определенного порядка действий.
1. Прежде всего файл должен быть открыт. Это означает, что операционная система дает добро на внесение изменений в данный файл (например, на запись
данных) и следит, чтобы обращения других пользователей и программ к этому
файлу (если компьютер подключен к сети) выполнялись корректно. Так, считывание данных из файла, в который другой пользователь в этот момент вносит
изменения, невозможно.
При открытии файла системе управления файлами обычно сообщается, в каком
режиме файл будет открыт: планируется ли вносить изменения в его содержимое
или же файл открывается только для считывания из него данных. В последнем
случае к файлу, как правило, могут обращаться и другие пользователи. Обычно
указывается также, какова внутренняя структура открываемого файла — это
требуется, чтобы выполнять операции с ним максимально быстро.
. i После того как файл успешно открыт, в программу возвращается его идентификатор — переменная, которая будет использоваться для идентификации этого
файла во всех процедурах обработки.
2. Начинается работас файлом. Это может быть считывание из него данных, запись,
поиск и другие операции.
3. Файл закрывается. Это означает, что он снова доступен другим приложениям
без ограничений. Кроме того, закрытие файла гарантирует, что все внесенные
в него изменения не пропадут, потому что для повышения скорости работы
результаты промежуточных действий обычно сохраняются в специальных буферах
операционной системы.
Текстовые файлы
В Паскале имеется еще один тип файлов, занимающий промежуточное положение
между типизированными и нетипизированными файлами. Он называется Text и
предназначен исключительно для обработки строк, которые, с одной стороны, описываются базовым типом String, ас другой стороны, не имеют фиксированной длины.
В таких файлах считывание и запись происходят построчно, причем символы перевода строки и возврата каретки используются как управляющие. Для этих файлов
дополнительно реализованы две процедуры, явно осуществляющие ввод/вывод с
новой строки: ReadLn и WriteLn. При этом размер считанной строки определяется
автоматически, по наличию управляющих символов, которые в строку не записываются. Если применять процедуры Read и Write без элемента Ln, означающего переход на новую строку, то считывание и запись текста производятся сплошным
потоком, без разделения на строки.
Специально для работы с текстовыми файлами в системе Delphi 7 имеется набор
стандартных подпрограмм, приведенных ниже.
Работа с текстовыми файлами. Чтение из текстового файла
На прошлых уроках мы как, в компоненте Memo процесс загрузки и записи текстового
файла делался следующим образом:
Memo1.Lines.LoadFromFile(Имя_файла); // загрузка
Memo1.Lines.SaveToFile(Имя_файла); // сохранение
Все это благодаря свойству Lines, в котором хранятся строки.
Но на практике иногда необходимо прочитать только определенную строку или
совершить операцию добавления строки в уже существующий файл.
Следующий пример обработки текстового файла очень похож на аналогичную на языке
Pascal.
Знающие люди могут ощутить разницу, поскольку есть некоторые отличия.
procedureTForm1.Button1Click(Sender: TObject);
Varf:TextFile; // объявление файловой переменной
st:String; // строковая переменная
begin
AssignFile(f,'c:\1.txt'); // привязка названия файла к файловой переменной
{$I-} // отключение контроля ошибок ввода-вывода
Reset(f); // открытие файла для чтения
{$I+} // включение контроля ошибок ввода-вывода
ifIOResult<>0 then // если есть ошибка открытия, то
begin
ShowMessage('Ошибка открытия файла C:\1.TXT');
Exit; // выход из процедуры при ошибке открытия файла
end;
While not EOF(f) do // пока не конец файла делать цикл:
begin
ReadLn(f,st); // читать из файла строку
ShowMessage(st); // выводить строку пользователю
end;
CloseFile(f); // закрыть файл
end;
Прокомментирую некоторые строки этого примера.
Команда AssignFile осуществляет привязку строки пути файла к файловой переменной.
Все дальнейшие операции с файловой переменной автоматически осуществляются с
указанным файлом. Для избежания путаниц, указывайте полный путь к файлу.
{$I-} и {$I+} являются директивами компилятору, что в этом месту соответственно
следует отключить и включить контроль ошибок ввода-вывода. В данном случае при
неудачной попытке открытия файла c:\1.txt (файл отсутствует или открыт для записи
другой программой) наша программа не выдаст аварийной ошибки и продолжит
выполнение данной процедуры. Это свойство полезно для обработки всех возможных
случаев в работе программы.
IOResult – переменная, которая хранит в себе код ошибки последней операции вводавывода. Если она равна нулю, то последняя операция была успешно выполнена.
EOF(Файл) – функция, возвращающая признак конца файла. Т.е. она показывает,
достигнут или нет конец открытого файла.
ReadLn(Файл,Переменная) – процедура считывания переменной из файла. В отличие от
команды Read производит считывание строки с завершающимся символом перевода
строки под кодами 13 и 10 (клавиша Enter).
CloseFile(Файл) – процедура закрытия ранее открытого файла.
Работа с текстовыми файлами. Запись в текстовый файл
Рассмотрим пример:
procedure TForm1.Button1Click(Sender: TObject);
Var f:TextFile; // указатель на текстовый файл
begin
AssignFile(f,'c:\1.txt'); // привязка названия к переменной
{$I-}
Append(f); // открыть файл для добавления
if IOResult<>0 then // если ошибка открытия (напр. файла нет)
begin
{$I-}
Rewrite(f); // создать новый файл
{$I+}
if IOResult<>0 then // ошибка создания файла
begin
ShowMessage('Ошибка создания файла C:\1.TXT');
Exit;
end;
end;
WriteLn(f,'Привет'); // запись в файл строки с символами перевода строки
CloseFile(f); // закрыть файл
end;
Процедура Append(Файл) открывает файл для записи и устанавливает указатель записи в
конец файла, т.е. все добавляемые к файлу строки будут записаны в конец файла.
В нашем случае в самом начале файла 1.txt может не оказаться на диске, поэтому команда
открытия файла для добавления вызовет ошибку. В этом случае срабатывает наш
собственный контроль ошибок и выполняется команда создания файла.
procedure Append (var F: Text);
Открытие текстового файла в режиме записи. Отличается от процедуры Rewrite тем, что
не стирает все содержимое, а устанавливает текущую позицию в самый конец файла, что
позволяет добавлять информацию
procedure AssignPrn(var F: Text);
Вся информация, записываемая в файл, перенаправляется на принтер. Файл должен быть
открыт с помощью процедуры Rewrite
function Eoln (var F: Text): Boolean;
Возвращает значение True, если текущая позиция в файле расположена либо в конце
файла, либо в конце строки. Такая проверка может понадобиться, если ввод выполняется
с помощью процедуры Read, не переходящей к началу следующей строки автоматически.
procedure Erase(var F: Text);
Удаление файла. Он должен быть определен с помощью процедуры AssignFile, но не
должен быть открыт
procedure Flush(var F: Text);
Информация, которая была записана в файл из программы, но находится во временном
буфере, физически перемещается в файл на диске
function SeekEof (var F: Text): Boolean;
Возвращает значение True, если текущая позиция расположена в конце файла
function SeekEotn(var F: Text): Boolean;
Возвращает значение True, если текущая позиция расположена е конце строки
procedure SetTextBuf (var F: Text;var Buf; Size: Integer);
Устанавливает размер буфера для операций ввода/вывода с текстовыми файлами
(параметр Size). Этот буфер располагается внутри программы. Он указывается в
качестве второго параметра. Это может быть, например, массив символов
Конец файла
В процессе обработки файла часто возникает потребность обнаруживать его конец.
Например, когда файл необходимо прочитать от начала до конца при поиске нужной
записи. Для подобного контроля в Паскале имеется функция Eof, единственный
параметр которой — файловая переменная. Эта функция возвращает значение True,
если после выполнения последней операции ввода/вывода текущая позиция оказывается в самом конце файла и дальнейшее считывание данных невозможно. Как
правило, данная функция применяется при вводе, потому что запись в файл, открытый с помощью процедуры Rewrite, выполняется последовательно и текущая позиция всегда расположена в конце файла.
ВНИМАНИЕ При использовании функции Eof надо помнить, что если она вернула значение True, то попытка выполнить считывание из файла
приведет к ошибке.
В следующем примере выполняется подсчёт числа строк в текстовом файле.
AssignFile(F, 'C:\A.TXT' ) ;
Reset (F) ;
i := 0;
while not Eof (F) do
begin
ReadLn(F,S) ;
inc ( i ) ;
end;
CloseFile(F) ;
Значение переменной i будет увеличиваться при каждой операции ввода строки.
Другие стандартные подпрограммы для работы
с типизированными файлами и каталогами
В выше приведен большой список подпрограмм для работы с файлами и каталогами. Эти подпрограммы хранятся в двух модулях: System и SysUtils — и в некоторых областях дублируют друг друга. Это связано с необходимостью поддерживать совместимость со старыми версиями системы Delphi и языка Паскаль, а также
с появлением новых мощных и гибких средств, более полно отвечающих потребностям разработчиков. В частности, функции модуля SysUtils в большинстве своем
позволяют контролировать результат работы по возвращаемому значению (если
это True, то операция выполнена успешно).
procedure ChDir(S: string);
function SetCurrentDir(const Dir: string): Boolean;
Устанавливает текущий каталог в соответствии с путем поиска, заданным параметром 5
(Dir)
function GetCurrentDir: string;
Возвращает имя текущего каталога
function CreateDir(const Dir: string): Boolean;
procedure MkDir(S: string);
Создает новый каталог, путь поиска которого указан в качестве параметра. Все
промежуточные каталоги (кроме самого последнего) должны существовать
function DeleteFile(const FHeName: string): Boolean;
Удаляет файл
function DirectoryExists(Name: string): Boolean;
Проверяет, существует ли указанный каталог
function DiskFree(Drive: Byte):Int64;
Определяет число свободных байтов на диске, который задан параметром Drive. Значение
0 соответствует текущему диску, 1 — диску А, 2 — диску В, 3 — диску С и так далее
function DiskSize(Drive: Byte):Int64;
Возвращает объем указанного диска в байтах
function FileExists(const FileName: string): Boolean;
Проверяет, существует ли указанный файл
function FileGetAttr(const FileName: string): Integer;
function FileSetAttr(const FiteName:string; Attr: Integer): Integer;
Получает или устанавливает атрибуты указанного файла. Набор атрибутов представляет
собой число типа Integer, разные биты которого определяют разные характеристики
файла. faReaclOnly — только для чтения; faHidden — скрытый; faSysFile — системный;
faVolumelD — метка диска; faDirectory — каталог; faArchive — архивный; faAnyFHe —
произвольный
function FilePos(var F): Longint;
Возвращает номер текущей записи в файле. Нумерация начинается с нуля
function FHeSize(var F): Integer;
function ForceDirectories(Dir: string):
Boolean;
procedure GetQir(D: Byte; var S: string);
procedure RmDir(S: string};
function RemoveDir(const Dir: string):
Boolean;
Возвращает число записей в файле или
его размер в байтах
Создает каталог. Если указаны
несуществующие промежуточные
каталоги, то они также будут созданы
Для диска, заданного параметром D (0 —
А, 1 — С, 2 — D и так далее), записывает
в переменную S
Удаляет пустой каталог, полный путь
поиска для которого задан параметром S
(Dir)
procedure Rename(var F; Newname:
string);
Переименовывает файл,
ассоциированный с переменной F при
помощи процедуры AssignFile.
Файл получает имя, определяемое
строковым параметром Newname
Обработка имен файлов
Во многих случаях разработчику приходится анализировать структуру полного
имени файла вместе с его путем поиска. Это требуется, например, при создании
всевозможных программ установки (инсталляции), поиска информации, составления каталогов ресурсов, при решении системных задач. В системе Delphi 7 имеется богатый набор подпрограмм, позволяющих автоматизировать большинство
аспектов подобного анализа. Эти подпрограммы входят в стандартный модуль
FileCtrl
function ChangeFiLeExt(const FileName, Extension: string): string;
Изменяет расширение имени файла FileName на новое, заданное параметром Extension
function ExcludeTrai ling Вас kslash(const S: string): string;
Удаляет завершающий символ \ если он присутствует в строке
function ExpandFueName(const FileName: string): string;
Формирует полный путь поиска для файла FileName
function ExpandUNCFileName(const FileName: string): string;
Формирует полный путь поиска файла в формате UNC (\\имя-сервера\имя-ресурса)
function ExtractFileDir(const FileName: string): string;
Выделяет имя каталога и диска в формате, пригодном для непосредственного
использования в функциях CreateDir, GetCurrentDir, RemoueDir и SetCurrentDir
function ExtractFileDrive(const FiLeName: string): string;
Выделяет имя локального диска или сетевого устройства
function ExtractFUeExt(const FileName: string): string;
Выделяет расширение имени файла
function ExtractFileName(const FileName: string): string;
Выделяет имя файла, отделяя его от пути поиска
function ExtractFilePath(const FileName: string): string;
Выделяет путь поиска (с завершающим символом \) из полного имени файла
function ExtractRelativePath(const BaseName, DestNarne: string}: string;
Преобразует полный путь поиска файла (параметр DestName) в строку, которая указывает
относительный путь поиска из каталога, заданного параметром BaseName. Элементы пути
поиска могут включать промежуточные переходы на уровень вверх, например, ..\
function ExtractShortPathName(const FileName: string): string;
Преобразует путь поиска в формат коротких имен (не более 8 символов на имя файла или
каталога). Например, путь поиска C:\Program Files\A.TXT будет преобразован как
C:\Progra~l\A.TXT
function IndudeTraHingBackslash(constS: string): string;
Добавляет в конец строки символ \, если он там отсутствует
function IsPathDelimiter(constS: string;Index: Integer): Boolean;
Определяет, находится ли в позиции строки, определяемой параметром Index, символ \
function MatchesMask(const Filename, Mask: string): Boolean;
Возвращает значение True, если файл Filename соответствует строке Mask, в которой
задана маска имен файлов, содержащая подстановочные символы
procedure ProcessPath(const EditText: string; var Drive: Char;
var DirPart: string; var File Part: string);
Выделяет имя диска (один символ), путь поиска и имя файла из параметра EditText
Поиск файлов
Процесс поиска файлов выполняется в системе Delphi 7 в три этапа.
1. Сначала находится первый файл, удовлетворяющий заданной маске. Этот поиск
осуществляется с помощью функции
function FindFirst (const Path: string; Attr: Integer; var F: TSearchRec): Integer;
Параметр Path содержит путь доступа для каталога, в котором производится
поиск. Путь доступа должен завершаться маской имен файлов, например:
С:\ТМР\*.*
*.txt
Кроме маски можно указать и набор атрибутов, учитываемых при отборе файлов.
Эти атрибуты были указаны при описании функций FileGetAttr/FiieSetAttr.
Результат поиска сохраняется в переменной F, имеющей тип TSearchRec.
type TSearchRec =record
Time: Integer;
Size: Integer;
Attr: Integer;
Name: TFileName;
ExcludeAttr: Integer;
FindHandle: THandle;
FindData: TWin32FindData;
end;
Среди ее полей надо отметить следующие:
О поле Time содержит время создания файла в формате DOS;
О поле Size содержит размер файла в байтах;
О поле Name содержит имя файла.
2. Вызывается функция
function FindNext(var F: TSearchRec): Integer;
Переменная типа TSearchRec, использованная в функции FindFirst, Передается в
качестве параметра. На основании записанной в нее информации будет продолжен поиск следующего подходящего файла.
3. Процесс поиска завершается вызовом процедуры
procedure FindClosefvar F: TSearchRec);
Эта процедура освобождает память, которая была выделена для проведения
процесса поиска.
Типичный цикл посимвольной обработки
var f : TextFile; // объявлена файловая переменная f типа TextFile
begin
AssignFile( f , <имя файла>); // открыть файл
Reset(f); // подготовить файл к работе
While not(eof(f)) do // пока файл не кончится выполнять цикл
begin
Read ( f, char); // посимвольное чтение
//или
Readln (f, string); // читать строку
end;
close(f); // закрыть файл
end;
В лекциях написаны фрагменты кода без Try\Finallу,но мы должны
иметь представление где их поставить.после того,как что-то открыли
(сделали Reset\Rewrite) поставить Try,потом идет работа с файлом
Finally и Close.
В ЧЕМ РАЗНИЦА МЕЖДУ ТЕКСТОВЫМИ И БИНАРНЫМИ
ФАЙЛАМИ(текстовые могут быть связаны с какими-то внешними
устройствами.можем узнать когда будет конец файла,сколько в нем
символов,где длина….
21.Работа с бинарными файлами.
Работа с файлами
Способы работы с файлами в системе Delphi 7
При работе с файлами в системе Delphi 7 возможны два принцип иально разных
подхода. Первый состоит в использовании стандартных подпрограмм (они имелись
еще в классической версии Паскаля тридцатилетней давности), позволяющих записывать содержимое переменных в файлы и считывать их обратно из файлов в переменные. К этим средствам добавились также библиотеки стандартных функций
по работе с файлами, основанные на системных функциях Windows.
В связи с появлением версии Object Pascal и реализации понятия класса в языке
появились средства объектной работы с данными. Это второй подход к работе с
файлами в системе Delphi 7. В свою очередь в рамках каждого из подходов применяются также существенно различающиеся приемы. Например, при классическом
подходе в работе с файлами могут использоваться прямые обращения к функциям
Windows или обращения к функциям BIOS.
Общая технология работы с файлами в Delphi 7
Несмотря на все различия, независимо от используемого подхода технология работы
с файлами в системе Delphi 7 требует определенного порядка действий.
1. Прежде всего файл должен быть открыт. Это означает, что операционная система дает добро на внесение изменений в данный файл (например, на запись
данных) и следит, чтобы обращения других пользователей и программ к этому
файлу (если компьютер подключен к сети) выполнялись корректно. Так, считывание данных из файла, в который другой пользователь в этот момент вносит
изменения, невозможно.
При открытии файла системе управления файлами обычно сообщается, в каком
режиме файл будет открыт: планируется ли вносить изменения в его содержимое
или же файл открывается только для считывания из него данных. В последнем
случае к файлу, как правило, могут обращаться и другие пользователи. Обычно
указывается также, какова внутренняя структура открываемого файла — это
требуется, чтобы выполнять операции с ним максимально быстро.
. i После того как файл успешно открыт, в программу возвращается его идентификатор — переменная, которая будет использоваться для идентификации этого
файла во всех процедурах обработки.
2. Начинается работас файлом. Это может быть считывание из него данных, запись,
поиск и другие операции.
3. Файл закрывается. Это означает, что он снова доступен другим приложениям
без ограничений. Кроме того, закрытие файла гарантирует, что все внесенные
в него изменения не пропадут, потому что для повышения скорости работы
результаты промежуточных действий обычно сохраняются в специальных буферах
операционной системы.
Типы файлов
В системе Delphi 7 имеется стандартный тип File (ключевое слово), на основе которого можно создавать новые файловые типы для работы со структурированными
данными. Если переменная описана так:
var F: File;
то она считается нетипизированной файловой переменной, позволяющей работать
с файлами на низком уровне (структура файла неизвестна). При этом данные будут
считываться и записываться блоками по 128 байт (значение по умолчанию). Размер
блока можно изменить в момент открытия файла. Рекомендуется назначать этот
размер равным 1 байту, чтобы корректно обрабатывать файлы любой структуры.
Чаше всего в программах используются файлы, состоящие из последовательности
одинаковых записей. Для работы с ними применяется следующая форма описания.
var имя-переменной: File of тип,Б качестве типа файла должен быть указан тип, для которого точно известен фиксированный размер в байтах. К таковым относятся все базовые типы (за исключением типа String, если для него явно не задан размер), структуры, статические массивы и прочие, например:
type TMyFile = record
Name: String[20] ;
Number: integer;
end;
var F: File of TMyFile,ВНИМАНИЕ Файловые типы нельзя использовать в качестве элементов массивов
и полей структур.
Открытие файлов
Перед тем как выполнить физическое открытие файла, программе надо сообщить,
где он расположен. Для этого файловая переменная должна быть связана с именем
файла с помощью процедуры AssignFile. Первый параметр этой процедуры — имя
переменной, а второй — строка, содержащая название файла. Если полный путь
поиска не указан, то файл будет разыскиваться в текущем каталоге.
AssignFilef F, 'test.daf I;
AssignFilei F, 'c:\projects\test.dat1 ];
Процедур открытия файлов в Паскале две. Первая из них, Rewrite, используется
для открытия файла в режиме записи (при этом происходит полное уничтожение его
содержимого, а размер файла становится равным нулю), а вторая, Reset, — для
открытия файла в режиме чтения (при этом вносить изменения в содержимое файла не
разрешается). Процедура Rewrite может также применяться для создания нового
файла.
Каждая из этих процедур может иметь второй необязательный параметр, который
определяет длину записи нети пикированного файла в байтах.
var F: File;
Rewrite! F, 1
ЗАМЕЧАНИЕ В программе одновременно может быть открыто большое число
фойлов (до нескольких сотен). Конкретное значение определяется
настройками Windows.
Запись в файл
Для записи данных в файл, имеющий определенную структуру (описанный с
помощью ключевых слов File of...), применяется процедура Write. В качестве первого параметра указывается файловая переменная, а далее следует список переменных типа, соответствующего типу файла.
var F: File of TMyFile;
A, MyData, N5: TMyFile;
Rewrite( F };
Write! F, N5, MyData |;
Значения из этих переменных будут последовательно записаны в конец файла.
Считывание из файла
Чтобы считать данные из файла, надо использовать процедуру Read. Она записывается аналогично процедуре Write.
var F: File of TMyFile;
A, MyData, N5: TMyFile;
Reset ( F ) ;
Read( F, MyData, A ) ;
Начиная с текущей позиции в файле F (исходно это начало файла) из него будут
последовательно считаны блоки данных, соответствующие размерам экземпляра
класса TMyFile, и записаны в переменные MyData и А. По окончании операции ввода
текущая позиция в файле F сдвинется на два элемента.
Специальная процедура procedure Truncate(var F);
позволяет отсечь (удалить) все содержимое файла, начиная с текущей позиции до его
конца.
Закрытие файла
По завершении работы с файлом его надо закрыть. Это выполняется вызовом процедуры CloseFile:
CloseFilef (F ) ;
Работа с нетипизированными файлами
Так как при работе с нетипизированными файлами данные считываются и записываются побайтно, допускается обрабатывать такие файлы, как последовательность
байтов, не имеющую строгой внутренней структуры. Для этого применяют процедуры блочного ввода/вывода BLockRead и BlockWrite. Они имеют одинаковый
список
параметров и отличаются только названиями.
procedure BlockWrite(var f: File; var Buf;
Count: Integer; var AmtTransferred: Integer);
Параметр Buf — это произвольная переменная, параметр Count — число блоков считываемой или записываемой информации. Если при открытии файла размер блока
не был указан явно, считается, что он равен 128 байтам. Однако нетипизированные
файлы применяют, как правило, для побайтной обработки данных, поэтому длину
блока обычно задают равной 1 байту и в параметр Count записывают просто число
байтов. Параметр AmtTransf erred — необязательный. По окончании выполнения
процедуры в нем будет храниться число реально считанных или записанных блоков.
Установка новой позиции в файле
Считывание и запись информации в файл происходит последовательно, блок за
блоком. Если же, например, требуется считать пятую запись из файла, в котором
ранее было сохранено 10 записей, или изменить ее, не перезаписывая все остальные, то можно воспользоваться процедурой Seek.
procedure Seekfvar F; N: Longint);
Первый параметр — файловая переменная (определенного типа или нетипизированная), второй параметр — номер записи в файле, начиная с которой будет выполнена следующая операция ввода/вывода. Этот номер обычно называется позицией
в файле. Нумерация записей в файле начинается с нуля. В случае нетипизированного файла второй параметр определяет номер байта, с которого начнется запись
или чтение информации.
С помощью этой процедуры можно легко выполнять редактирование содержимого
любых файлов. Рассмотрим примеры создания и редактирования нетипизированных файлов и файлов, состоящих из набора записей.
var f: File;
b: Byte;
i: Integer;
begin
// создание нетипизированного файла:
AssignFile(F,'C:\ A.DAT ');
Rewrite(F,l);
b := $41;
for i := 1 to 100 do
BlockWrite (F, b, 1);
CloseFile (F);
В результате выполнения данного участка кода на диске С: (в корневом каталоге)
будет создан файл A. DAT, содержащий 100 символов 'А' (значение байта $41 в шестнадцатеричном формате соответствует символу 'А').
Следующий текст изменит одиннадцатый байт в этом файле, который получит новое
значение $42 (символ 'В'). Хотя в процедуре Seek указано смещение 10, изменен будет
именно одиннадцатый байт, потому что отсчет блоков внутри файла ведется с нуля.
AssignFile(F, 'СЛА.ВАТ' ) ;
b := $42;
Reset(F,l) ;
Seek(F,10) ;
BlockWrite(F,b,l) ,CloseFile(F);
ВНИМАНИЕ Для редактирования содержимого файла с использованием процедуры Seek его надо открывать с помощью процедуры Reset ( которая
обычно используется для открытия файла в режиме “только для чтения”) , а не с помощью процедуры Rewrite, которая уничтожает всю
информацию в файле.
22.Объектные типы данных в Delphi. Вид наследования в Delphi.
Объекты — это крупнейшее достижение в современной технологии программирования.
Они позволили строить программу не из чудовищных по сложности процедур и функций,
а из кирпичиков-объектов, заранее наделенных нужными свойствами. Самое приятное в
объктах то, что их внутренняя сложность скрыта от программиста, который просто
пользуется готовым строительным материалом.
Сейчас преимущества использования объектов очевидны для всех. Однако так было не
всегда. Сначала старая гвардия не поняла и не приняла объекты, поэтому они почти 20 лет
потихоньку развивались в различных языках, первым из которых была Simula 67.
Постепенно объектно-ориентированный подход нашел себе место и в более мощных
языках, таких как C++, Delphi и множестве других языков. Блестящим примером
реализации объектов была библиотека Turbo Vision, предназначенная для построения
пользовательского интерфейса программ в операционной системе MS-DOS.
Полную победу объекты одержали с приходом эпохи многофункциональных графических
пользовательских интерфейсов. Теперь без объектов в программировании просто не
обойтись. Чтобы вы не рылись в других книгах, собирая информацию по крохам, мы не
поленились и объединили в этой главе все, что нужно знать об объектах. Для новичка
важнейшее здесь: инкапсуляция, наследование, полиморфизм, остальное можно просто
просмотреть и возвращаться к материалу по мере накопления опыта. Профессионалу
полезно прочитать внимательно все от начала до конца. Поэтому давайте засучим рукава
и приступим к делу.
3.1. Краеугольные камни ООП
3.1.1. Формула объекта
Авторы надеются, что читатель помнит кое-что из главы 2 и такие понятия как тип
данных, процедура, функция, запись для него не в новинку. Это прекрасно. Так вот, в
конце 60-х годов кому-то пришло в голову объединить эти понятия, и то, что получилось,
назвать объектом. Рассмотрение данных в неразрывной связи с методами их обработки
позволило вывести формулу объекта:
Объект = Данные + Операции
На основании этой формулы была разработана методология объектно-ориентированного
программирования (ООП).
3.1.2. Природа объекта
Об объектах можно думать как о полезных существах, которые "живут" в вашей
программе и коллективно решают некоторую прикладную задачу. Вы, как Демиург,
лепите этих существ, распределяете между ними обязанности и устанавливаете правила
их взаимодействия.
В общем случае каждый объект "помнит" необходимую информацию, "умеет" выполнять
некоторый набор действий и характеризуется набором свойств. То, что объект "помнит",
хранится в его полях. То, что объект "умеет делать", реализуется в виде его внутренних
процедур и функций, называемых методами. Свойства объектов аналогичны свойствам,
которые мы наблюдаем у обычных предметов. Значения свойств можно устанавливать и
читать. Программно свойства реализуются через поля и методы.
Например, объект "кнопка" имеет свойство "цвет". Значение цвета кнопка запоминает в
одном из своих полей. При изменении значения свойства "цвет" вызывается метод,
который перерисовывает кнопку.
Кстати, этот пример позволяет сделать важный вывод: свойства имеют первостепенное
значение для программиста, использующего объект. Чтобы понять суть и назначение
объекта вы обязательно должны знать его свойства, иногда — методы, очень редко —
поля (объект и сам знает, что с ними делать).
3.1.3. Объекты и компоненты
Когда прикладные программы были консольно-ориентированными, а пользовательский
интерфейс был простым, объекты казались пределом развития программирования,
поскольку были идеальным средством разбиения сложных задач на простые подзадачи.
Однако с появлением графических систем программирование пользовательского
интерфейса резко усложнилось. Программист в какой-то мере стал дизайнером, а
визуальная компоновка и увязка элементов пользовательского интерфейса (кнопок, меток,
строк редактора) начали отнимать основную часть времени. И тогда программистам
пришла в голову идея визуализировать объекты, объединив программную часть объекта с
его видимым представлением на экране дисплея в одно целое. То, что получилось в
результате, было названо компонентом.
Компоненты в среде Delphi — это особые объекты, которые являются строительными
кирпичиками визуальной среды разработки и приспособлены к визуальной установке
свойств. Чтобы превратить объект в компонент, первый разрабатывается по
определенным правилам, а затем помещается в палитру компонентов. Конструируя
приложение, вы берете компоненты из Палитры Компонентов, располагаете на форме и
устанавливаете их свойства в окне Инспектора Объектов. Внешне все выглядит просто, но
чтобы достичь такой простоты, потребовалось создать механизмы, обеспечивающие
функционирование объектов-компонентов уже на этапе проектирования приложения! Все
это было придумано и блестяще реализовано в среде Delphi. Таким образом,
компонентный подход значительно упростил создание приложений с графическим
пользовательским интерфейсом и дал толчок развитию новой индустрии компонентов.
В данной главе мы рассмотрим лишь вопросы создания и использования объектов. Чуть
позже мы научим вас превращать объекты в компоненты (см. главу 13).
3.1.4. Классы объектов
Каждый объект всегда принадлежит некоторому классу объектов. Класс объектов — это
обобщенное (абстрактное) описание множества однотипных объектов. Объекты являются
конкретными представителями своего класса, их принято называть экземплярами класса.
Например, класс СОБАКИ — понятие абстрактное, а экземпляр этого класса МОЙ ПЕС
БОБИК — понятие конкретное.
3.1.5. Три кита ООП
Весь мир ООП держится на трех китах: инкапсуляции, наследовании и полиморфизме.
Для начала о них надо иметь только самое общее представление.
Объединение данных и операций в одну сущность — объект — тесно связано с понятием
инкапсуляции, которое означает сокрытие внутреннего устройства. Инкапсуляция делает
объекты похожими на маленькие программные модули, в которых скрыты внутренние
данные и у которых имеется интерфейс использования в виде подпрограмм. Переход от
понятий "структура данных" и "алгоритм" к понятию "объект" значительно повысил
ясность и надежность программ.
Второй кит ООП — наследование. Этот простой принцип означает, что если вы хотите
создать новый класс объектов, который расширяет возможности уже существующего
класса, то нет необходимости в переписывании заново всех полей, методов и свойств. Вы
объявляете, что новый класс является потомком (или дочерним классом) имеющегося
класса объектов, называемого предком (или родительским классом), и добавляете к нему
новые поля, методы и свойства. Процесс порождения новых классов на основе других
классов называется наследованием. Новые классы объектов имеют как унаследованные
признаки, так и, возможно, новые. Например, класс СОБАКИ унаследовал многие
свойства своих предков — ВОЛКОВ.
Третий кит — это полиморфизм. Он означает, что в производных классах вы можете
изменять работу уже существующих в базовом классе методов. При этом весь
программный код, управляющий объектами родительского класса, пригоден для
управления объектами дочернего класса без всякой модификации. Например, вы можете
породить новый класс кнопок с рельефной надписью, переопределив метод рисования
кнопки. Новую кнопку можно "подсунуть" вместо стандартной в какую-нибудь
подпрограмму, вызывающую рисование кнопки. При этом подпрограмма "думает", что
работает со стандартной кнопкой, но на самом деле кнопка принадлежит производному
классу кнопок и отображается в новом стиле. Пока достаточно самого поверхностного
понимания всех приведенных выше понятий, ниже мы рассмотрим их подробнее и
покажем, как они реализованы в среде Delphi.
Объект — основа Паскаля
До сих пор мы рассматривали типы данных, которые существовали в языках программирования еще тридцать лет назад. С их помощью можно разработать очень
большую программу, однако потребует это значительных усилий группы профессиональных программистов. А в одиночку, используя только числа, строки, массивы и
записи, можно создать программу объемом в лучшем случае в несколько тысяч
строк исходного текста. Это является своеобразным пределом возможностей человека. Дело в том, что структура данных исходного алгоритма дробится на элементарные, слишком маленькие и явно не связанные друг с другом и с программным
кодом частички. Они связаны только через операторы присваивания, разбросанные
по разным модулям. По мере роста объема исходных текстов корректно обрабатывать переменные, не затрагивая уже нормально функционирующие части программы,
становится невозможно.
В 80-х годах стали появляться первые коммерческие системы разработки, в которых
была реализована новая парадигма программирования, так называемый объектный подход, что позволило резко повысить производительность труда программистов. Подход был основан на понятии объекта, типа данных, к котором сочетаются
как свойства, сгруппированные данные (пример — поля в записи), так и методы
их обработки (подпрограммы).
Фактически объект стал отражать реальные и даже абстрактные понятия окружающего мира. Например, автомобиль характеризуется такими свойствами, как марка,
тип двигателя, наличие колес и руля, а файл — названием и размером. ≪Методы≫
автомобиля определяют его способность двигагься в нужном направлении в соответствии со значениями своих свойств: объемом бензина в баке, углом поворота
руля. Из файла можно считывать данные, менять их и записывать обратно.
Благодаря этому теперь удается выполнять проектирование программ, основываясь на понятии объекта, что значительно проще и быстрее, чем раньше. Работать с
привычными понятиями человеку легче, нежели с абстрактными числами. При
этом специалистам удалось выделить большой набор объектов, которые нужны
при создании самых разных программ. Эти объекты используются повторно, без
расходования времени на их программирование. Именно такой подход и реализован в среде Delphi 7.
Описание
Ключевое слово Class это центральная часть Объектно-ориентированного кода.
Это определение содержит так называемые 'члены' - данные и методы
(подпрограммы). Когда объект класса создан, он становится автономным элементом
- вы можете обратиться к данным и методам объекта независимо от любого
другого объекта. Оно похоже на запись Delphi, но с активными компонентами методами.
Эти элементы определены в определении типов класса. Элементы метода
осуществлены в разделе implementation модуля.
Объявление класса имеет следующее типичное описание :
type
className = class(BaseClass)
private
// Определения данных/методов локальные для этого модуля
protected
// Определения данных/методов, локальных для этого класса + потомки
public
// Определения данных/методов пригодных для использования всеми объектами этого
класса
published
// Внешне общественные определения
end;
Параметры можно передать при создании объектного образца класса. Они
передаются методу Constructor класса. Слово Constructor используется вместо
функции или метода. Вы можете иметь множество конструкторов для различных
наборов параметра. Они снабжены ключевым словом overload; после конца
определения конструктора.
Обычно, название метода конструктора - Create.
См. код для примера.
Когда объект разрушают, вызывают метод Destructor. Вы можете использовать
его, чтобы предпринять специальное действие прежде, чем объектная память будет
исправлена.
Обычно, название метода деструктора - Destroy.
Есть множество использований слова Class:
1. Определение класса базируется, по умолчанию на классе TObject. Все классы
должны быть основаны на другом классе, с классом TObject, в самом верхнем уровне.
В пределах определений класса, Вы можете приставлять определения функции или
процедуры с ключевым словом Class. Это позволяет подпрограмме быть вызванным
из самого Класса в дополнение к объектному образцу класса. Поскольку класс - не
реальный объект, он не имеет никакой памяти, распределенной для данных. Так что
подпрограмма Класса должна работать бех ссылок на внутренние данные класса.
2. Определение класса базируются на указанном классе, но без местных добавлений
или изменений. Вы имеете новый класс, который действует тождественно к классу
BaseClass
3. Ускоренное объявление класса. Оно позволяет всем классам в модуле быть
перечисленными в начале раздела type. Так что это вопрос удобства, а не чего-нибудь
еще.
4. Определение класса базируется на указанном классе. Как 1-ый выше, но вы можете
определить, на каком классе вы базируете ваш новый класс. Вашим классом будет
смесь предка и местных объявлений.
5. Класс может содержать выполнение внешне предопределенных интерфейсов.
5. Ссылка metaclass позволяет переменной быть использованной для указания класса,
а не объекта
Обджект считается устаревшим,тем не менее есть свои
преимущества.можем вместо записи(record)написать обджест и добавить новый
метод(?).но все-таки в основном используются
Перем.типа класс.она всегда явл указателем.все данные экземляра класс
размещаются всегда на куче.у классов есть нормальная поддержка работы со
свойствами
.
Наследование(в делфи используют одиночное наследование)что такое и как
используется
Чтобы наиболее эффективно повторно использовать ранее созданные классы, одного
сочетания данных и методов в единой структуре недостаточно. Например, автомобиль может быть легковым и грузовым, и соответствующие классы будут иметь
как общие поля и методы, так и отличия (например, дополнительное свойство ≪грузовой кузов* и связанный с ним метод ≪разгрузить≫). Однако полностью заново
определять новый тип данных, если требуется изменить или добавить несколько
новых свойств к старому типу, нерационально. Это плохо еще и потому, что если в
Клоссы и обьекты 85
метод, имеющийся в обоих классах, потребуется внести исправления, то их придется делать дважды, в двух одинаковых копиях подпрограмм.
Чтобы избежать ненужной работы, в объектном программировании был введен
принцип наследования свойств и методов. Программисту достаточно описать один
базовый класс (например, ≪автомобиль*), а классы ≪легковой автомобиль≫ и ≪грузовой автомобиль* основывать на этом базовом классе. При этом будут наследо-
ваться все поля, свойства и методы базового (или родительского) класса, а дополнительно описывать их не требуется.
Цепочки наследования могут быть неограниченной длины. Так, у класса ≪грузовой
автомобиль≫ могут быть классы-наследники (или дочерние классы) ≪МАЗ* и ≪КАМАЗ≫, обладающие дополнительными специфическими свойствами и методами
(это классы, а не объекты; объектом будет конкретный грузовик МАЗ, а не марка
этого автомобиля), у класса ≪кнопка* наследники — ≪графическая кнопка*,
≪круглая кнопка≫ и так далее. При этом различные методы для каждого из наследников разрешается переопределять. Например, метод ≪двигаться≫ для классов
≪МАЗ≫ и ≪КАМАЗ≫ будет, хоть и немного, но отличаться: по-разному расходуется
горючее (снижается значение соответствующего свойства), по-разному набирается
скорость и так далее.
3.7. Наследование
3.7.1. Понятие наследования
Классы инкапсулируют (т.е. включают в себя) поля, методы и свойства; это их первая
черта. Следующая не менее важная черта классов — способность наследовать поля,
методы и свойства других классов. Чтобы пояснить сущность наследования обратимся к
примеру с читателем текстовых файлов в формате "delimited text".
Класс TDelimitedReader описывает объекты для чтения из текстового файла элементов,
разделенных некоторым символом. Он не пригоден для чтения элементов, хранящихся в
другом формате, например в формате с фиксированным количеством символов для
каждого элемента. Для этого необходим другой класс:
type
TFixedReader = class
private
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FItemWidths: array of Integer;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string;
const AItemWidths: array of Integer);
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
end;
{ TFixedReader }
constructor TFixedReader.Create(const FileName: string;
const AItemWidths: array of Integer);
var
I: Integer;
begin
AssignFile(FFile, FileName);
FActive := False;
// Копирование AItemWidths в FItemWidths
SetLength(FItemWidths, Length(AItemWidths));
for I := 0 to High(AItemWidths) do
FItemWidths[I] := AItemWidths[I];
end;
destructor TFixedReader.Destroy;
begin
Active := False;
end;
function TFixedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TFixedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TFixedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TFixedReader.NextLine:
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then
//
begin
Readln(FFile, S);
//
N := ParseLine(S);
//
if N <> ItemCount then
SetLength(FItems, N); //
end;
end;
Boolean;
Если не достигнут конец файла
Чтение очередной строки из файла
Разбор считанной строки
Отсечение массива (если необходимо)
function TFixedReader.ParseLine(const Line: string): Integer;
var
I, P: Integer;
begin
P := 1;
for I := 0 to High(FItemWidths) do
begin
PutItem(I, Copy(Line, P, FItemWidths[I])); // Установка элемента
P := P + FItemWidths[I];
// Переход к следующему
элементу
end;
Result := Length(FItemWidths); // Количество элементов постоянно
end;
procedure TFixedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then
// Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item;
// Установка соответствующего элемента
end;
procedure TFixedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile)
// Открытие файла
else
CloseFile(FFile);
// Закрытие файла
FActive := AActive;
// Сохранение состояния в поле
end;
end;
Поля, свойства и методы класса TFixedReader практически полностью аналогичны тем,
что определены в классе TDelimitedReader. Отличие состоит в отсутствии свойства
Delimiter, наличии поля FItemWidths (для хранения размеров элементов), другой
реализации метода ParseLine и немного отличающемся конструкторе. Если в будущем
появится класс для чтения элементов из файла еще одного формата (например,
зашифрованного текста), то придется снова определять общие для всех классов поля,
методы и свойства. Чтобы избавиться от дублирования общих атрибутов (полей, свойств и
методов) при определении новых классов, воспользуемся механизмом наследования.
Прежде всего, выделим в отдельный класс TTextReader общие атрибуты всех классов,
предназначенных для чтения элементов из текстовых файлов. Реализация методов
TTextReader, кроме метода ParseLine, полностью идентична реализации TDelimitedReader,
приведенной в предыдущем разделе.
type
TTextReader = class
private
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
// Методы получения и установки значений свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetItem(Index: Integer): string;
function GetEndOfFile: Boolean;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string);
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
end;
...
constructor TTextReader.Create(const FileName: string);
begin
AssignFile(FFile, FileName);
FActive := False;
end;
function TTextReader.ParseLine(const Line: string): Integer;
begin
// Функция просто возвращает 0, поскольку не известно,
// в каком именно формате хранятся элементы
Result := 0;
end;
...
При реализации класса TTextReader ничего не известно о том, как хранятся элементы в
считываемых строках, поэтому метод ParseLine ничего не делает. Очевидно, что создавать
объекты класса TTextReader не имеет смысла. Для чего тогда нужен класс TTextReader?
Ответ: чтобы на его основе определить (породить) два других класса — TDelimitedReader
и TFixedReader, предназначенных для чтения данных в конкретных форматах:
type
TDelimitedReader = class(TTextReader)
FDelimiter: Char;
function ParseLine(const Line: string): Integer; override;
constructor Create(const FileName: string; const ADelimiter: Char =
';');
property Delimiter: Char read FDelimiter;
end;
TFixedReader = class(TTextReader)
FItemWidths: array of Integer;
function ParseLine(const Line: string): Integer; override;
constructor Create(const FileName: string;
const AItemWidths: array of Integer);
end;
...
Классы TDelimitedReader и TFixedReader определены как наследники TTextReader (об
этом говорит имя в скобках после слова class). Они автоматически включают в себя все
описания, сделанные в классе TTextReader и добавляют к ним некоторые новые. В
результате формируется дерево классов, показанное на рисунке 3.1 (оно всегда рисуется
перевернутым).
Рисунок 3.1. Дерево классов
Класс, который наследует атрибуты другого класса, называется порожденным классом
или потомком. Соответственно класс, от которого происходит наследование, выступает в
роли базового, или предка. В нашем примере класс TDelimitedReader является прямым
потомком класса TTextReader. Если от TDelimitedReader породить новый класс, то он
тоже будет потомком класса TTextReader, но уже не прямым.
Очень важно, что в отношениях наследования любой класс может иметь только одного
непосредственного предка и сколь угодно много потомков. Поэтому все связанные
отношением наследования классы образуют иерархию. Примером иерархии классов
является библиотека VCL; с ее помощью в среде Delphi обеспечивается разработка GUIприложений.
3.7.2. Прародитель всех классов
В языке Delphi существует предопределенный класс TObject, который служит неявным
предком тех классов, для которых предок не указан. Это означает, что объявление
type
TTextReader = class
...
end;
эквивалентно следующему:
type
TTextReader = class(TObject)
...
end;
Класс TObject выступает корнем любой иерархии классов. Он содержит ряд методов,
которые по наследству передаются всем остальным классам. Среди них конструктор
Create, деструктор Destroy, метод Free и некоторые другие методы.
Таким образом, полное дерево классов для чтения элементов из текстового файла в
различных форматах выглядит так, как показано на рисунке 3.2.
Рисунок 3.2. Полное дерево классов
Поскольку класс TObject является предком для всех других классов (в том числе и для
ваших собственных), то не лишним будет кратко ознакомиться с его методами:
type
TObject = class
constructor Create;
procedure Free;
class function InitInstance(Instance: Pointer): TObject;
procedure CleanupInstance;
function ClassType: TClass;
class function ClassName: ShortString;
class function ClassNameIs(const Name: string): Boolean;
class function ClassParent: TClass;
class function ClassInfo: Pointer;
class function InstanceSize: Longint;
class function InheritsFrom(AClass: TClass): Boolean;
class function MethodAddress(const Name: ShortString): Pointer;
class function MethodName(Address: Pointer): ShortString;
function FieldAddress(const Name: ShortString): Pointer;
function GetInterface(const IID: TGUID; out Obj): Boolean;
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual;
procedure AfterConstruction; virtual;
procedure BeforeDestruction; virtual;
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;
Некоторые конструкции этого описания будут вам непонятны, поскольку мы их еще не
изучали. Сейчас это не важно. Снова вернитесь к этому описанию после прочтения всей
главы.
Краткое описание методов в классе TObject:
Create — стандартный конструктор.
Free — уничтожает объект: вызывает стандартный деструктор Destroy, если значение
псевдопеременной Self не равно nil.
InitInstance(Instance: Pointer): TObject — при создании объекта инициализирует нулями
выделенную память. На практике нет необходимости вызывать этот метод явно.
CleanupInstance — освобождает память, занимаемую полями с типом string, Variant,
динамический массив и интерфейс. На практике нет необходимости вызывать этот метод
явно.
ClassType: TClass — возвращает описатель класса (метакласс).
ClassName: ShortString — возвращает имя класса.
ClassNameIs(const Name: string): Boolean — проверяет, является ли заданная строка
именем класса.
ClassParent: TClass — возвращает описатель базового класса.
ClassInfo: Pointer — возвращает указатель на соответствующую классу таблицу RTTI (от
англ. Runtime Type Information). Таблица RTTI используется для проверки типов данных
на этапе выполнения программы.
InstanceSize: Longint — возвращает количество байт, необходимых для хранения в памяти
одного объекта соответствующего класса. Заметим, что значение, возвращаемое этим
методом и значение, возвращаемое функцией SizeOf при передаче ей в качестве аргумента
объектной переменной — это разные значения. Функция SizeOf всегда возвращает
значение 4 (SizeOf(Pointer)), поскольку объектная переменная — это ни что иное, как
ссылка на данные объекта в памяти. Значение InstanceSize — это размер этих данных, а не
размер объектной переменной.
InheritsFrom(AClass: TClass): Boolean — проверяет, является ли класс AClass базовым
классом.
MethodAddress(const Name: ShortString): Pointer — возвращает адрес published-метода,
имя которого задается параметром Name.
MethodName(Address: Pointer): ShortString — возвращает имя published-метода по
заданному адресу.
FieldAddress(const Name: ShortString): Pointer — возвращает адрес published-поля, имя
которого задается параметром Name.
GetInterface(const IID: TGUID; out Obj): Boolean — возвращает ссылку на интерфейс
через параметр Obj; идентификатор интерфейса задается параметром IID. (Интерфейсы
рассмотрены в главе 6)
GetInterfaceEntry(const IID: TGUID): PInterfaceEntry — возвращает информацию об
интерфейсе, который реализуется классом. Идентификатор интерфейса задается
параметром IID.
GetInterfaceTable: PInterfaceTable — возвращает указатель на таблицу с информацией
обо всех интерфейсах, реализуемых классом.
AfterConstruction — автоматически вызывается после создания объекта. Метод не
предназначен для явного вызова из программы. Используется для того, чтобы выполнить
определенные действия уже после создания объекта (для этого его необходимо
переопределить в производных классах).
BeforeDestruction — автоматически вызывается перед уничтожением объекта. Метод не
предназначен для явного вызова из программы. Используется для того, чтобы выполнить
определенные действия непосредственно перед уничтожением объекта (для этого его
необходимо переопределить в производных классах).
Dispatch(var Message) — служит для вызова методов, объявленных с ключевым словом
message.
DefaultHandler(var Message) — вызывается методом Dispatch в том случае, если метод,
соответствующий сообщению Message, не был найден.
NewInstance: TObject — вызывается при создании объекта для выделения динамической
памяти, чтобы разместить в ней данные объекта. Метод вызывается автоматически,
поэтому нет необходимости вызывать его явно.
FreeInstance — вызывается при уничтожении объекта для освобождения занятой
объектом динамической памяти. Метод вызывается автоматически, поэтому нет
необходимости вызывать его явно.
Destroy — стандартный деструктор.
3.7.3. Перекрытие атрибутов в наследниках
В механизме наследования можно условно выделить три основных момента:



наследование полей;
наследование свойств;
наследование методов.
Любой порожденный класс наследует от родительского все поля данных, поэтому классы
TDelimitedReader и TFixedReader автоматически содержат поля FFile, FActive и FItems,
объявленные в классе TTextReader. Доступ к полям предка осуществляется по имени, как
если бы они были определены в потомке. В потомках можно определять новые поля, но
их имена должны отличаться от имен полей предка.
Наследование свойств и методов имеет свои особенности.
Свойство базового класса можно перекрыть (от англ. override) в производном классе,
например чтобы добавить ему новый атрибут доступа или связать с другим полем или
методом.
Метод базового класса тоже можно перекрыть в производном классе, например чтобы
изменить логику его работы. Обратимся к классам TDelimitedReader и TFixedReader. В
них методы PutItem, GetItem, SetActive и GetEndOfFile унаследованы от TTextReader,
поскольку логика их работы не зависит от того, в каком формате хранятся данные в
файле. А вот метод ParseLine перекрыт, так как способ разбора строк зависит от формата
данных:
function TDelimitedReader.ParseLine(const Line: string): Integer;
var
S: string;
P: Integer;
begin
S := Line;
Result := 0;
repeat
P := Pos(Delimiter, S); // Поиск разделителя
if P = 0 then
// Если разделитель не найден, то считается,
что
P := Length(S) + 1;
// разделитель находится за последним символом
PutItem(Result, Copy(S, 1, P - 1)); // Установка элемента
Delete(S, 1, P);
// Удаление элемента из строки
Result := Result + 1;
// Переход к следующему элементу
until S = '';
// Пока в строке есть символы
end;
function TFixedReader.ParseLine(const Line: string): Integer;
var
I, P: Integer;
begin
P := 1;
for I := 0 to High(FItemWidths) do
begin
PutItem(I, Copy(Line, P, FItemWidths[I])); // Установка элемента
P := P + FItemWidths[I];
// Переход к следующему
элементу
end;
Result := Length(FItemWidths); // Количество элементов постоянно
end;
В классах TDelimitedReader и TFixedReader перекрыт еще и конструктор Create. Это
необходимо для инициализации специфических полей этих классов (поля FDelimiter в
классе TDelimitedReader и поля FItemWidths в классе TFixedReader):
constructor TDelimitedReader.Create(const FileName: string;
const ADelimiter: Char = ';');
begin
inherited Create(FileName);
FDelimiter := ADelimiter;
end;
constructor TFixedReader.Create(const FileName: string;
const AItemWidths: array of Integer);
var
I: Integer;
begin
inherited Create(FileName);
// Копирование AItemWidths в FItemWidths
SetLength(FItemWidths, Length(AItemWidths));
for I := 0 to High(AItemWidths) do
FItemWidths[I] := AItemWidths[I];
end;
Как видно из примера, в наследнике можно вызвать перекрытый метод предка, указав
перед именем метода зарезервированное слово inherited. Когда метод предка полностью
совпадает с методом потомка по формату заголовка, то можно использовать более
короткую запись. Воспользуемся ей и перепишем деструктор в классе TTextReader
правильно:
destructor TTextReader.Destroy;
begin
Active := False;
inherited; // Эквивалентно: inherited Destroy;
end;
Два последних примера демонстрируют важный принцип реализации конструкторов и
деструкторов. В конструкторах сначала вызывается конструктор предка, а затем
инициализируются дополнительные поля данных. В деструкторах применяется обратная
последовательность действий: сначала разрушаются данные, недоступные предку, а затем
вызывается унаследованный деструктор. Всегда пользуйтесь этими правилами в своих
программах, чтобы избежать ошибок.
23.Основные принципы ООП и их поддержка в Object Pascal.
(в лекциях есть.)как это все поддерживается в обджект паскаль
Три принципа объектного программирования
Наследование
Чтобы наиболее эффективно повторно использовать ранее созданные классы, одного
сочетания данных и методов в единой структуре недостаточно. Например, автомобиль может быть легковым и грузовым, и соответствующие классы будут иметь
как общие поля и методы, так и отличия (например, дополнительное свойство ≪грузовой кузов* и связанный с ним метод ≪разгрузить≫). Однако полностью заново
определять новый тип данных, если требуется изменить или добавить несколько
новых свойств к старому типу, нерационально. Это плохо еще и потому, что если в
Клоссы и обьекты 85
метод, имеющийся в обоих классах, потребуется внести исправления, то их придется делать дважды, в двух одинаковых копиях подпрограмм.
Чтобы избежать ненужной работы, в объектном программировании был введен
принцип наследования свойств и методов. Программисту достаточно описать один
базовый класс (например, ≪автомобиль*), а классы ≪легковой автомобиль≫ и ≪грузовой автомобиль* основывать на этом базовом классе. При этом будут наследоваться все поля, свойства и методы базового (или родительского) класса, а дополнительно описывать их не требуется.
Цепочки наследования могут быть неограниченной длины. Так, у класса ≪грузовой
автомобиль≫ могут быть классы-наследники (или дочерние классы) ≪МАЗ* и ≪КАМАЗ≫, обладающие дополнительными специфическими свойствами и методами
(это классы, а не объекты; объектом будет конкретный грузовик МАЗ, а не марка
этого автомобиля), у класса ≪кнопка* наследники — ≪графическая кнопка*,
≪круглая кнопка≫ и так далее. При этом различные методы для каждого из наследников разрешается переопределять. Например, метод ≪двигаться≫ для классов
≪МАЗ≫ и ≪КАМАЗ≫ будет, хоть и немного, но отличаться: по-разному расходуется
горючее (снижается значение соответствующего свойства), по-разному набирается
скорость и так далее.
Полиморфизм(у класса по сути полиморфным явл именно указатель.сама
переменная типа Тобджект не явл полиморфной,поскольку она имеет
фиксированный размер)
Когда будет происходить обращение к переменной, относящейся к классу ≪КАМАЗ≫,
и вызов унаследованного метода ≪двигаться≫, программе придется решить, какой
конкретно метод надо вызвать: метод класса ≪автомобиль*, ≪грузовой автомобиль*
или ≪КАМАЗ≫. В соответствии с принципом полиморфизма решение принимается
в зависимости от типа переменной, вызывающей этот метод. То есть, если переменная описана как относящаяся к типу ≪КАМАЗ*, будет вызван метод ≪двигаться≫,
определенный именно для КАМАЗа.
Инкапсуляция(инкапсуляция поддерживается как раз таки за счет того,что у нас
есть области видимости членов классов,можно какие-то фрагменты кода скрыть от
внешней программы и позволять работать только через методы.За счет того,что
модули не дают доступа к implementation)
Инкапсуляция позволяет разграничить доступ разработчиков к различным полям
и свойствам класса, примерно так, как это сделано в модулях Delphi, когда из других
модулей видима только интерфейсная часть. Точно так же и внутри классов некоторые поля и методы можно сделать свободно доступными для использования {видимыми) в любом месте программы, а другие поля и методы сделать доступными только
внутри текущего модуля и собственных методов класса. Это позволяет скрыть внутри
описания различные характеристики и возможности класса, чтобы сосредоточить
внимание разработчиков, повторно использующих этот класс, на его важнейших
свойствах.
Кроме того, желательно не допускать бесконтрольного изменения значений
свойств, так как это может привести к нарушению запланированной и сбалансированной взаимосвязи между этими свойствами. Например, нельзя бездумно изменить значение свойства ≪текущая скорость≫, просто записав в него новое число.
Надо вызвать соответствующий метод, который учтет изменение потребления бензина и выполнит ряд дополнительных действий. Хорошим стилем программирова86 Урок 1. Язык Delphi (Object Pascal) и его использование
ния считается недоступность всех полей и свойств объекта для прямого изменения.
Вместо этого создаются методы, позволяющие получить значение поля и занести
в него новое значение. Сами поля помещаются в скрытую часть класса.
События
Помимо этих трех фундаментальных возможностей объектно-ориентированного
программирования, в среде Delphi реализована новая характеристика объекта —
возможность обработки так называемых сообщений (или событий), получаемых
от системы Windows или самой программы. Этот принцип лежит в основе работы
всех визуальных компонентов Delphi, которые обрабатывают различные события,
возникающие в процессе выполнения программы.
24.Классы в Delphi. Члены классов. Области видимости членов
класса.
Понятие класса
В Паскале имеется четкое разграничение между понятиями объекта и класса.
Класс — это тип данных (как Integer или ТМу Record), а объект — конкретный существующий в памяти компьютера экземпляр класса, переменная соответствующего
типа. В ранних версиях Паскаля существовала некоторая терминологическая путаница, потому что первая реализация объектного подхода использовала для описания
84 Урок 1. Язык Delphi (Ob|ecl Pascal) и его использование
объектного типа данных ключевое слово object, и в то же время объектом назывались экземпляры этого типа. Применять слово object можно и сейчас, однако подобная возможность поддерживается только для совместимости со старыми версиями
системы Delphi. Вместо ключевого слова object правильно использовать ключевое
слово class.
Классы имеют поля (как тип данных record), свойства (напоминающие поля, но имеющие дополнительные описатели, определяющие механизмы записи и считывания
данных, что позволяет повысить строгость декларирования внутренней структуры
класса) и методы (подпрограммы, которые обрабатывают поля и свойства класса).
ЗАМЕЧАНИЕ Поля, свойства и методы клосса называются членами класса.
Когда описывается переменная типа class, для ее полей и свойств в памяти выделяется соответствующий объем (как и для записей), но машинный код, в который
транслируются методы класса, наличествует в единственном экземпляре, так как
меняться он не может, и хранить несколько одинаковых копий подпрограмм не
имеет смысла. Когда объект создается, однократно вызывается специальный метод,
называемый конструктором. В нем выполняются различные действия по начальной инициализации полей объекта. Когда объект уничтожается (например, он был
описан внутри процедуры как локальная переменная и удаляется из памяти при
ее завершении), вызывается другой метод — деструктор, который выполняет различные дополнительные действия по освобождению памяти, если это необходимо.
Явно вызывать конструктор и деструктор из программного кода нельзя. Это происходит только автоматически.
Переменная, описанная как класс, фактически является указателем на экземпляр
класса. Это сделано для повышения эффективности работы с ним. Однако при
исполюовании таких переменных применять операции работы с указателями (", @)
не надо. Достаточно обычного обращения к ним как к обычным переменным, а к
членам класса — как к полям записи, через точку.
ЗАМЕЧАНИЕ Описывать переменные, принадлежащие к типу class, внутри подпрограмм в виде локальных типов не разрешается.__
Описание класса
Новый тип (класс) описывается в Паскале обычным способом, с помощью ключевого слова class.
type имя-класса =
class(имя-родительского-класса)
список-членов-класса ;
end;
Имя родительского класса указываегся, если новый класс должен наследовать все
его характеристики (и характеристики всех родителей этого класса). Исключать
какие-то члены родительских классов из наследования нельзя. Если имя родительского класса опустить:
type имя-класса =
class
список-членов-класса;
end;
то новый класс по умолчанию будет наследовать характеристики базового класса
TObject, который содержит встроенные конструктор, деструктор и общие свойства
и методы классов в среде Delphi, предназначен; 1ые для создания объекгов (экземпляров класса), инициализации и освобождения памяти, и так далее. Корпорация
Inprise рекомендует всегда явно указывать базовый родительский класс TObject.
Б некоторых случаях возникает необходимость ввести в программу новый класс,
пока не описывая его структуру, а только определив заголовок (по аналогии с описанием заголовка подпрограмм с ключевым словом forward). Эго допускается сделать, просто указав название класса и ключевое слово class:
type TMyClass = class;
В дальнейшем, конечно, класс TMyCtass должен быть полностью определен.
Надо отличать такое предварительное упреждающее описание от реального описания класса следующего вида:
type TMyClass = class(TObject|;
Классы и объекты 87
Таким образом описывается полноценный класс TMyCtass, структурно совпадающий с классом TObject.
Список членов класса представляет собой список полей, свойств и методов, которые записываются как обычные поля записи или объявления подпрограмм в интерфейсном разделе, например:
type TMyClass = class
Count: Integer;
Name: String,procedure ShowMyClass( Dis: Boolean );
function GetCount: Integer;
end;
В классе TMyClass имеются два поля: Count и Name, — процедура ShowMyCLass (которая, возможно, показывает данный объект па экране) и функция GetCount, возвращающая значение поля Count. Пока что поле Count доступно для изменения: по
умолчанию считается, что все члены класса не имеют никаких ограничений на видимость.
ВНИМАНИЕ Описание полей и свойств в классе должно предшествовать описанию методов.
Теперь в программу можно ввести новую переменную, представляющую собой
объект — экземпляр класса TMyCLass:
var MyClass: TMyClass;__
Пять уровней инкапсуляции
Доступность любых членов класса определяется принадлежностью к одному из
пяти уровней видимости класса, определяемых специальными ключевыми словами
Паскаля, которые разделяют описание класса на секции путем простого их указания
без дополнительных элементов типа точки с запятой. По умолчанию видимость
родительских членов класса наследуется в точности, однако разрешено повышать
видимость — делать поля, свойства и методы более доступными. Понижение видимости не допускается.
Раздел public. Члены класса, находящиеся в данном разделе, доступны из любой
точки программы.
type TMyClass = claae
public
Count: Integer;
Name: String;
procedure ShowMyClass( Dis: Boolean ) ;
function GetCount: Integer;
end;
Раздел private. Этот раздел накладывает самые жесткие ограничения на видимость
указанных в нем членов класса. Они доступны только в том модуле, где данный
класс описан. Как правило, поля класса помещаются в эту секцию.
type TMyClass = class
private
Count: Integer;
Name: String;
public
procedure ShowMyClass( Dis: Boolean );
function GetCount: Integer;
end;
По умолчанию считается, что все поля класса расположены в данном разделе.
Вышеприведенное описание аналогично следующему описанию (без ключевого
слова private).
type TMyClass = class
Count: Integer;
Name: String;
public
procedure ShowMyClasst Dis: Boolean ) ,function GetCount: Integer;
end;
Раздел protected. Видимость членов класса, расположенных в этом разделе, совпадает с видимостью раздела private с единственным отличием. Члены класса раздела
protected доступны также внутри методов классов, являющихся наследниками данного класса и описанных в других модулях.
Классы и обьекты 89
Раздел published. В этом разделе располагаются свойства класса: поля, доступные
для редактирования и изменения значений во время проектирования и из Инспектора
объектов. По видимости свойства не отличаются от членов классов, расположенных
в разделе public. Более подробно о свойствах и разделе published рассказывается в
главе, посвященной созданию собственных компонентов.
Раздел automated. Правила видимости членов раздела automated совпадают с правилами видимости для раздела public. Описания разрешается размещать в этом
разделе, только если класс является наследником стандартного класса TAutoObject, •
предназначенного для создания так называемых серверов автоматизации при использовании технологии СОМ (см. соответствующую главу).
Присваивание объектов
Так Kajc класс — это обычный тип данных Паскаля, то переменным можно свободно присваивать значения соответствующих объектов (реально копируются только
значения полей и свойств), причем как в точности своего типа, так и всех классов,
являющихся наследниками класса этой переменной.
Например, если в программе имеется следующее описание:
type Tl = class (TObject) ,Т2 = class(Tl);
var X: Tl;
TO в переменную X можно записывать как объекты типа Tl, так и объекты типа Т2.
Доступ к полям и методам объекта осуществляется в точности, как при доступе к
полям записи, — указанием нужного поля или метода через точку.
var О: TMyClass;
О. Count := 0;
О.Name := 'Класс';
О.ShowMyClass(true);
Понятие свойства
Помимо полей и методов в объектах существуют свойства. При работе с объектом
свойства выглядят как поля: они принимают значения и участвуют в выражениях. Но в
отличие от полей свойства не занимают места в памяти, а операции их чтения и записи
ассоциируются с обычными полями или методами. Это позволяет создавать необходимые
сопутствующие эффекты при обращении к свойствам. Например, в объекте Reader
присваивание свойству Active значения True вызовет открытие файла, а присваивание
значения False — закрытие файла. Создание сопутствующего эффекта (открытие или
закрытие файла) достигается тем, что за присваиванием свойству значения стоит вызов
метода.
Объявление свойства выполняется с помощью зарезервированного слова property,
например:
type
TDelimitedReader = class
...
FActive: Boolean;
...
// Метод записи (установки значения) свойства
procedure SetActive(const AActive: Boolean);
property Active: Boolean read FActive write SetActive; // Свойство
end;
Ключевые слова read и write называются спецификаторами доступа. После слова read
указывается поле или метод, к которому происходит обращение при чтении (получении)
значения свойства, а после слова write — поле или метод, к которому происходит
обращение при записи (установке) значения свойства. Например, чтение свойства Active
означает чтение поля FActive, а установка свойства — вызов метода SetActive. Чтобы
имена свойств не совпадали с именами полей, последние принято писать с буквы F (от
англ. field). Мы в дальнейшем также будем пользоваться этим соглашением. Начнем с
того, что переименуем поля класса TDelimitedReader: поле FileVar переименуем в FFile,
Items — в FItems, а поле Delimiter — в FDelimiter.
type
TDelimitedReader = class
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FDelimiter: Char;
...
end;
// FileVar
// Items
-> FFile
-> FItems
// Delimiter
-> FDelimiter
Обращение к свойствам выглядит в программе как обращение к полям:
var
Reader: TDelimitedReader;
IsOpen: Boolean;
...
Reader.Active := True;
// Эквивалентно Reader.SetActive(True);
IsOpen := Reader.Active; // Эквивалентно IsOpen := Reader.FActive
Если один из спецификаторов доступа опущен, то значение свойства можно либо только
читать (задан спецификатор read), либо только записывать (задан спецификатор write). В
следующем примере объявлено свойство, значение которого можно только читать.
type
TDelimitedReader = class
...
FItems: array of string;
...
function GetItemCount: Integer;
...
property ItemCount: Integer read GetItemCount; // Только для чтения!
end;
function TDelimitedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
Здесь свойство ItemCount показывает количество элементов в массиве FItems. Поскольку
оно определяется в результате чтения и разбора очередной строки файла, пользователю
объекта разрешено лишь узнавать количество элементов.
В отличие от полей свойства не имеют адреса в памяти, поэтому к ним запрещено
применять операцию @. Как следствие, их нельзя передавать в var- и out-параметрах
процедур и функций.
Технология объектно-ориентированного программирования в среде Delphi предписывает
избегать прямого обращения к полям, создавая вместо этого соответствующие свойства.
Это упорядочивает работу с объектами, изолируя их данные от непосредственной
модификации. В будущем внутренняя структура класса, которая иногда является
достаточно сложной, может быть изменена с целью повышения эффективности работы
программы. При этом потребуется переработать только методы чтения и записи значений
свойств; внешний интерфейс класса не изменится.
25.Виды методов классов. Создание и удаление объектов. Размещение
данных объектов в памяти.
Реализация методов
После того как в модуле описан новый класс, в нижеследующей части исходного
текста необходимо описать реализацию всех его методов (если они есть). Чтобы
компилятор понял, к какому классу относится конкретная подпрограмма, перед ее
названием указывается имя соответствующего класса и точка:
function TMyClass.GetCount: Integer;
begin
Result := Count
end;
Заголовок подпрограммы должен в точности соответствовать заголовку, указанному в описании класса.
Типы методов
В Паскале каждый метод класса может иметь дополнительные характеристики,
определяющие, как будет реализовываться этот метод в классах-наследниках.
Статические методы
Все методы по умолчанию считаются статическими. Это означает, что их вызов
будет происходить в соответствии с принципом полиморфизма.
Например, описаны два класса.
type TCar = class(TObect]
procedure Move;
end;
TMAZ = class(TCar)
procedure Move;
end;
var Car: TCar;
MAZ: TMAZ;
begin
MAZ := TMAZ.Create;
Car := TCar.Create;
MAZ.Move;
// вызовется метод Move класса TMAZ
Car.Move;
// вызовется метод Move класса TCar
Однако переменную Car можно инициализировать объектом типа TMAZ:
Car. Free ,Car := TMAZ.Create;
Если теперь обратиться к методу Move:
92 Урок 5. Язык Delphi (Object Pascal) и его использование
Car.Move;
то вызовется метод TCar.Move, так как переменная Саг, реально храня экземпляр
класса TMAZ, описана как ТСаг. Обращаться к методам и полям переменной Саг, как
к методам и полям типа TMAZ, можно, только явно выполнив приведение типа:
TMAZ(Car). Об этом рассказывалось при описании оператора присваивания:
TMAZ(Car].Move;
При этом вызовется метод Move, относящийся к классу TMAZ.
Если при вызове метода Move класса TMAZ необходимо предварительно вызвать родительский метод, выполняющий общие для всех автомобилей действия, в реализации
метода Move надо указать этот вызов с помощью ключевого слова inherited.
procedure TMAZ.Move,-
begin
inherited Move,end;
Виртуальные и динамические методы
Статические _____методы удобны, когда в программе заранее известно, какие типы
объектов будут использоваться, и приведения типов, как правило, не требуется.
Б ряде случаев, особенно в крупных проектах, часто приходится хранить в переменных объекты-наследники (как в примере с ТСаг и TMAZ), причем конкретный тип
этих объектов может быть неизвестен. Поэтому в Паскале реализован новый тип
методов — виртуальные методы (для их описания существует зарезервированное
слово virtual). Описать виртуальный метод можно так.
type TCar = class(TObect)
procedure Move; virtual;
end;
Перегружаемые методы
Ранее рассматривались перегружаемые подпрограммы, имеющие одинаковые имена,
но различные типы параметров. Компилятор автоматически определяет, какую конкретно подпрограмму надо вызывать в зависимости от типов ее аргументов.
Имеется в Паскале аналогичная возможность и для методов.
type Tl ~ class(TObject)
procedure Sum(X,Y: Integer); overload;
end;
type T2 = class(Tl)
procedure SumfX,Y: Real]; overload;
end;
var CT: T2,CT := T2. Create;
CT.Sum(2,2);
// вызывается Tl.Sum()
CT.Sum(2.0,2.0);
// вызывается T2.Sum()
Перегружаемым может быть и виртуальный метод. Чтобы у компилятора не возникало претензий к программисту по поводу несоответствия заголовков перегружа94 Урок 1: Язык Delphi (Object Pascal) него использование
емых виртуальных методов, в наследуемом методе надо дополнительно указать
ключевое слово reintroduce:
type Tl = class(TObjecti
procedure Sum(X,Y: Integer); overload; virtual;
end;
type T2 = class(Tl)
procedure Sura(X,Y: Real); reintroduce; overload;
end,Создание и удаление объекта
Помимо обычных методов, в Паскале имеются так называемые методы классов.
Их особенность заключается в том, что методы классов разрешается вызывать,
используя для этого вызова не объект, а класс. Это означает, что подобные методы не
90 Урок 1. Язык Delphi (Objecl Pascal) и его использование
могут обращаться к полям класса — ведь поля существуют, только когда существует
объект (экземпляр класса), а методы в виде программного кода, созданного на этапе
компиляции, присутствуют в памяти компьютера постоянно.
Однако вызывать таким образом можно только методы, описанные с помощью
ключевого слова class, указываемого перед заголовком ] Lсопрограммы. Это нужно,
чтобы компилятор проверил, насколько корректно реализован соответствующий
метод, в частности, не происходит ли в нем обращения к полям и свойствам класса.
type TMyClass = class
Item.: integer;
public
class function Sozdanie: integer;
end;
В заголовке реализации функции Sozdanie также надо указать ключевое слово class:
class function TMyClass.Sozdanie: integer;
begin
end;
Так как методы классов не привязаны к объектам, то их можно вызывать, указывая название класса, а не объекта:
N := TMyClass.Sozdanie;
Это одна из фундаментальных возможностей Паскаля, благодаря которой можно,
в частности, динамически (во время работы программы) ____________создавать объекты
и записывать ссылки на них в переменные. Данный подход активно используется для
задания переменным-классам начальных значений. У базового класса TObject имеется метод Create (в реальности это конструктор, выполняющий вес необходимые
действия по запросу памяти и начальной инициализаций), который практически
всегда применяется при создании объектов.
var MyClass: TMyClass;
MyClags := TMyClass.Create;
Это стандартный способ создания объектов и инициализации указывающих на них
переменных. Пока таким способом и переменную не записан указатель на объект,
обращаться к этой переменной нельзя.
Когда работа с объектом полностью закончена, занятую им намять необходимо
освободить. Для этого существует специальный метод Free, который уничтожает
объект, автоматически вызывая его деструктор,
MyClass.Free;
Методы
Процедуры и функции, предназначенные для выполнения над объектами действий,
называются методами. Предварительное объявление методов выполняется при описании
класса в секции interface модуля, а их программный код записывается в секции
implementation. Однако в отличие от обычных процедур и функций заголовки методов
должны иметь уточненные имена, т.е. содержать наименование класса. Приведем
возможную реализацию одного из методов в классе TDelimitedReader:
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if AActive then
Reset(FileVar)
// Открытие файла
else
CloseFile(FileVar);
// Закрытие файла
end;
Обратите внимание, что внутри методов обращения к полям и другим методам
выполняются как к обычным переменным и подпрограммам без уточнения экземпляра
объекта. Такое упрощение достигается путем использования в пределах метода
псевдопеременной Self (стандартный идентификатор). Физически Self представляет собой
дополнительный неявный параметр, передаваемый в метод при вызове. Этот параметр и
указывает экземпляр объекта, к которому данный метод применяется. Чтобы пояснить
сказанное, перепишем метод SetActive, представив его в виде обычной процедуры:
procedure TDelimitedReader_SetActive(Self: TDelimitedReader;
const AActive: Boolean);
begin
if AActive then
Reset(Self.FileVar)
// Открытие файла
else
CloseFile(Self.FileVar);
// Закрытие файла
end;
Согласитесь, что метод SetActive выглядит лаконичнее процедуры
TDelimitedReader_SetActive.
Практика показывает, что псевдопеременная Self редко используется в явном виде. Ее
необходимо применять только тогда, когда при написании метода может возникнуть
какая-либо двусмысленность для компилятора, например при использовании одинаковых
имен и для локальных переменных, и для полей объекта.
Если выполнить метод SetActive,
Reader.SetActive(True);
то обрабатываемый файл будет открыт. При этом неявный параметр Self будет содержать
значение переменной Reader. Такой вызов реализуется обычными средствами
процедурного программирования приблизительно так:
TDelimitedReader_SetActive(Reader, True);
Методы, обслуживающие несколько свойств
Один и тот же метод может использоваться для получения (установки) значений
нескольких свойств одного типа. В этом случае каждому свойству назначается
целочисленный индекс, который передается в метод чтения (записи) первым параметром.
В следующем примере уже известный Вам метод GetItem обслуживает три свойства:
FirstName, LastName и Phone:
type
TDelimitedReader = class
...
property FirstName: string index 0 read GetItem;
property LastName: string index 1 read GetItem;
property Phone: string index 2 read GetItem;
end;
Обращения к свойствам FirstName, LastName и Phone заменяются компилятором на
вызовы одного и того же метода GetItem, но с разными значениями параметра Index:
var
Reader: TDelimitedReader;
...
Writeln(Reader.FirstName); // Эквивалентно: Writeln(Reader.GetItem(0));
Writeln(Reader.LastName); // Эквивалентно: Writeln(Reader.GetItem(1));
Writeln(Reader.Phone);
...
// Эквивалентно: Writeln(Reader.GetItem(2));
Обратите внимание, что метод GetItem обслуживает как свойство-массив Items, так и
свойства FirstName, LastName и Phone. Удобно, не правда ли!
Перед тем, как перейти к более сложным понятиям ООП, приведем полную реализацию
класса TDelimitedReader. Настоятельно рекомендуем Вам внимательно ознакомиться с
этой реализацией, поскольку в ней сведено воедино все то, о чем говорилось в
предыдущих разделах.
type
TDelimitedReader = class
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FDelimiter: Char;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string; const ADelimiter: Char =
';');
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
property Delimiter: Char read FDelimiter;
end;
{ TDelimitedReader }
constructor TDelimitedReader.Create(const FileName: string;
const ADelimiter: Char = ';');
begin
AssignFile(FFile, FileName);
FActive := False;
FDelimiter := ADelimiter;
end;
destructor TDelimitedReader.Destroy;
begin
Active := False;
end;
function TDelimitedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TDelimitedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TDelimitedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TDelimitedReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then
// Если не достигнут конец файла
begin
Readln(FFile, S);
// Чтение очередной строки из файла
N := ParseLine(S);
// Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
function TDelimitedReader.ParseLine(const Line: string): Integer;
var
S: string;
P: Integer;
begin
S := Line;
Result := 0;
repeat
P := Pos(Delimiter, S); // Поиск разделителя
if P = 0 then
// Если разделитель не найден, то считается,
что
P := Length(S) + 1;
// разделитель находится за последним символом
PutItem(Result, Copy(S, 1, P - 1)); // Установка элемента
Delete(S, 1, P);
// Удаление элемента из строки
Result := Result + 1;
// Переход к следующему элементу
until S = '';
// Пока в строке есть символы
end;
procedure TDelimitedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then
// Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item;
// Установка соответствующего элемента
end;
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile)
// Открытие файла
else
CloseFile(FFile);
// Закрытие файла
FActive := AActive;
// Сохранение состояния в поле
end;
end;
26.Виртуальные методы. Разновидности. Процесс вызова
виртуальных методов.
Методы Мб виртуальными,а могут быть статическими
Виртуальные и динамические методы
Статические методы удобны, когда в программе заранее известно, какие типы
объектов будут использоваться, и приведения типов, как правило, не требуется.
Б ряде случаев, особенно в крупных проектах, часто приходится хранить в переменных объекты-наследники (как в примере с ТСаг и TMAZ), причем конкретный тип
этих объектов может быть неизвестен. Поэтому в Паскале реализован новый тип
методов — виртуальные методы (для их описания существует зарезервированное
слово virtual). Описать виртуальный метод можно так.
(??мин 42)В случае вёрчиал извлекается по индексам бит таблица адреса вируальной
процедуры и по этому адресу
вёрчиал позволяет вызывать быстрее ,но зато таблицы виртуальных методов сильно
распухают.те как только мы какому-нибудь классу решили добавить виртуальный
метод ради того,что бы у одного потомка его переопределить ,но у этого класса есть
20 потомков ,то у всех 20ти потомков в соответствующих виртуальных методах
появится по новому элементу.а если мы используем дайнэмик ,то у нас просто в двух
таблицах у базового класса добавится один элемент и вот у этого…???...появится по
одному элементу.во всех остальных вообще ничего не изменится
type TCar = class(TObect)
procedure Move; virtual;
end;
Такие методы в классах-наследниках могут быть перекрыты методами с одноименными заголовками. Чтобы явно указать компилятору, что определенный метод перекрывает виртуальный метод родителя, надо использовать ключевое слово override.
type TMAZ = class<TCar>
procedure Move; override;
end;
Теперь в приведенном примере можно не выполнять приведение типов. Во время
работы программы она самостоятельно определит тип хранимого в переменной
Саг объекта и вызовет нужный метод.
Car := TMAZ.Create;
Car. Move ,Вызовется метод Move класса TMAZ. Если же этот метод не перекрыть (не указать
ключевое слово override), то вызовется метод Move класса Саг.
Вместо ключевого слова virtual можно применять ключевое слово dynamic, выполняющее тс же функции и описывающее метод как динамический.
Классы и объекты 93
type TCar = class(TObectj
procedure Move; dynamic;
end;
Разница между виртуальными и динамическими методами заключается только в
деталях программной реализации. Виртуальные методы оптимизированы для максимального быстродействия, динамические — для максимальной экономии памяти
(создания компактного кода).__
Абстрактные методы
В некоторых случаях не имеет смысла выполнять реализацию определенных методов
базового класса, например, когда все реализации некоторого метода сильно отличаются друг от друга, а метод родительского класса не используется. Вместе с тем соответ-
ствующий метод обязан быть реализован в каждом из классов-наследников.
Такой метод надо объявить в родительском классе как абстрактный.
type TCar = class(TObect)
procedure Move; virtual; abstract;
end;
Теперь описывать реализацию метода TCar.Move не надо.
ЗАМЕЧАНИЕ Абстрактным может быть только динамический или виртуальный
метод.
Понятие виртуального метода
Все методы, которые до сих пор рассматривались, имеют одну общую черту — все они
статические. При обращении к статическому методу компилятор точно знает класс,
которому данный метод принадлежит. Поэтому, например, обращение к статическому
методу ParseLine в методе NextLine (принадлежащем классу TTextReader) компилируется
в вызов TTextReader.ParseLine:
function TTextReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then
begin
Readln(FFile, S);
N := ParseLine(S); // Компилируется в вызов TTextReader.ParseLine(S);
if N <> ItemCount then
SetLength(FItems, N);
end;
end;
В результате метод NextLine работает неправильно в наследниках класса TTextReader, так
как внутри него вызов перекрытого метода ParseLine не происходит. Конечно, в классах
TDelimitedReader и TFixedReader можно продублировать все методы и свойства, которые
прямо или косвенно вызывают ParseLine, но при этом теряются преимущества
наследования, и мы возвращаемся к тому, что необходимо описать два класса, в которых
большая часть кода идентична. ООП предлагает изящное решение этой проблемы —
метод ParseLine всего-навсего объявляется виртуальным:
type
TTextReader = class
...
function ParseLine(const Line: string): Integer; virtual; //Виртуальный
метод
...
end;
Объявление виртуального метода в базовом классе выполняется с помощью ключевого
слова virtual, а его перекрытие в производных классах — с помощью ключевого слова
override. Перекрытый метод должен иметь точно такой же формат (список параметров, а
для функций еще и тип возвращаемого значения), что и перекрываемый:
type
TDelimitedReader = class(TTextReader)
...
function ParseLine(const Line: string): Integer; override;
...
end;
TFixedReader = class(TTextReader)
...
function ParseLine(const Line: string): Integer; override;
...
end;
Суть виртуальных методов в том, что они вызываются по фактическому типу экземпляра,
а не по формальному типу, записанному в программе. Поэтому после сделанных
изменений метод NextLine будет работать так, как ожидает программист:
function TTextReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then
begin
Readln(FFile, S);
N := ParseLine(S); // Работает как <фактический класс>.ParseLine(S)
if N <> ItemCount then
SetLength(FItems, N);
end;
end;
Работа виртуальных методов основана на механизме позднего связывания (late binding). В
отличие от раннего связывания (early binding), характерного для статических методов,
позднее связывание основано на вычислении адреса вызываемого метода при выполнении
программы. Адрес метода вычисляется по хранящемуся в каждом объекте описателю
класса.
Благодаря механизму наследования и виртуальных методов в среде Delphi реализуется
такая концепция ООП как полиморфизм. Полиморфизм существенно облегчает труд
программиста, поскольку обеспечивает повторное использование кода уже написанных и
отлаженных методов.
Механизм вызова виртуальных методов
Работа виртуальных методов основана на косвенном вызове подпрограмм. При косвенном
вызове команда вызова подпрограммы оперирует не адресом подпрограммы, а адресом
места в памяти, где хранится адрес подпрограммы. Вы уже сталкивались с косвенным
вызовом при использовании процедурных переменных. Процедурная переменная и была
тем местом в памяти, где хранился адрес вызываемой подпрограммы. Для каждого
виртуального метода тоже создается процедурная переменная, но ее наличие и
использование скрыто от программиста.
Все процедурные переменные с адресами виртуальных методов пронумерованы и
хранятся в таблице, называемой таблицей виртуальных методов (VMT — от англ. Virtual
Method Table). Такая таблица создается одна для каждого класса объектов, и все объекты
этого класса хранят на нее ссылку.
Структуру объекта в оперативной памяти поясняет рисунок 3.3:
Рисунок 3.3. Структура объекта TTextReader в оперативной памяти
Вызов виртуального метода осуществляется следующим образом:
1. Через объектную переменную выполняется обращение к занятому объектом блоку
памяти;
2. Далее из этого блока извлекается адрес таблицы виртуальных методов (он записан
в четырех первых байтах);
3. На основании порядкового номера виртуального метода извлекается адрес
соответствующей подпрограммы;
4. Вызывается код, находящийся по этому адресу.
Покажем, как можно реализовать косвенный вызов виртуального метода ParseLine (он
имеет нулевой номер в таблице виртуальных методов) обычными средствами
процедурного программирования:
type
TVMT = array[0..9999] of Pointer;
TParseLineFunc = function (Self: TTextReader; const Line: string):
Integer;
var
Reader: TTextReader;
// объектная переменна
ObjectDataPtr: Pointer; // указатель на занимаемый объектом блок памяти
VMTPtr: ^TVMT;
// указатель на таблицу виртуальных методов
MethodPtr: Pointer;
// указатель на метод
begin
...
ObjectDataPtr := Pointer(Reader);
// 1) обращение к данным объекта
VMTPtr := Pointer(ObjectDataPtr^);
// 2) извлечение адреса VMT
MethodPtr := VMTPtr^[0];
// 3) извлечение адреса метода из
VMT
TParseLineFunc(MethodPtr)(Reader, S); // 4) вызов метода
...
end.
Поддержка механизма вызова виртуальных методов на уровне языка Delphi избавляет
программиста от всей этой сложности.
27.Использование виртуальных конструкторов и ссылок на класс.
Контроль принадлежности объекта классу.
Конструкторы и деструкторы
Конструктор — это метод, который вызывается только один раз в момент создания
экземпляра объекта соответствующего класса. Конструктор считается методом класса
(class procedure), что позволяет вызывать его, указывая не только имя объекта, но и
имя класса. Для конструктора выделено специальное ключевое слово constructor.
constructor CreateMyObject(MySize: Integer);
Конструкторов у объекта может быть сколько угодно, однако создаваться объект
может с помощью только одного из них.
') ПОДСКАЗКА В базовом классе TObjecl имеется готовый конструктор Create,
который и рекомендуется использовать, если нет необходимости в
конструкторах с особыми возможностями. В любом случае в реализации собственного конструктора желательно вызывать базовый
конструктор Creole с помощью директивы inherited.
Деструктор — это метод, который вызывается только один раз в момент уничтожения экземпляра объекта соответствующего класса. Класс TObject имеет стандартный
деструктор Destroy, Разрешается создавать неограниченное число деструкторов с
помощью ключевого слова destructor:
destructor MyDestructor;
ПОДСКАЗКА Рекомендуется создавать в классе только один деструктор, а в его
реализации вызывать деструктор Destroy базового класса TObject
с помощью директивы inherited.
Тип ≪класс≫
В Паскале имеется возможность объявлять новый тип, являющийся ссылкой на класс:
type имя-класса - class of тип-класса;
Например:
type TMetaClass = class of TObject;
Классы и объекты 95
Переменная, описанная как
var MetaClass: TMetaClass;
может хранить в себе объект любого класса, унаследованного oTTObject. Это позволяет
записывать в подобные переменные объекты произвольных классов во время выполнения программы, не зная конкретных типов этих объектов. Например, если имеется
стандартная подпрограмма обработки объектов, тип которых будет известен только в
процессе работы приложения, то в качестве типа соответствующего параметра надо указать тип, представляющий собой ссылку на класс. Тогда в момент вызова подпрограммы
можно задавать объект, имеющий произвольный тип, унаследованный от TObject
procedure UseObject( Obj: TMetaClass ) ;
UseObjectl TButton );
UseObject( TEdit ) ;
Классы TButton и TEdit — стандартные классы Delphi, описывающие компоненты
≪кнопка≫- и ≪текстовое поле≫.
Проверка типа объекта
Так как в Паскале допускается применять объекты, тип которых на этапе компиляции не известен, то в языке должны быть и средства, позволяющие выполнять
проверки типов таких объектов.
Специальная операция Is (ключевое слово) позволяет проверить, соответствует
ли тип объекта конкретному типу Паскаля.
объект is тип
В качестве типа должен указываться один из классов.
Obj is TButton
Это выражение возвращает значение логического типа (True, если тип объекта Obj
соответствует типу, указанному в правой части, — TButton).
Приведение типа объекта
Если объект описан как ссылка на класс, то часто требуется применять этот объект как
относящийся к конкретному типу, чтобы иметь доступ к его определенным полям и
методам. Выполнить приведение к конкретному типу можно, применив операцию as:
объект as класс
Например, если в переменную MetaClass записан объект с типом TEdit, то получить
доступ к полю Text класса TEdit можно с помощью следующей конструкции:
(MetaClass as TEdit).Text := 'содержимое1;
Ссылки на классы
Язык Delphi позволяет рассматривать классы объектов как своего рода объекты, которыми
можно манипулировать в программе. Такая возможность рождает новое понятие — класс
класса; его принято обозначать термином метакласс.
Для поддержки метаклассов введен специальный тип данных — ссылка на класс (class
reference). Он описывается с помощью словосочетания class of, например:
type
TTextReaderClass = class of TTextReader;
Переменная типа TTextReaderClass объявляется в программе обычным образом:
var
ClassRef: TTextReaderClass;
Значениями переменной ClassRef могут быть класс TTextReader и все порожденные от
него классы. Допустимы следующие операторы:
ClassRef := TTextReader;
ClassRef := TDelimitedReader;
ClassRef := TFixedReader;
По аналогии с тем, как для всех классов существует общий предок TObject, у ссылок на
классы существует базовый тип TClass, определенный, как:
type
TClass = class of TObject;
Переменная типа TClass может ссылаться на любой класс.
Практическая ценность ссылок на классы состоит в возможности создавать программные
модули, работающие с любыми классами объектов, даже теми, которые еще не
разработаны.
Физический смысл и взаимосвязь таких понятий, как переменная-объект, экземпляр
объекта в памяти, переменная-класс и экземпляр класса в памяти поясняет рисунок 3.4.
Рисунок 3.4. Переменная-объект, экземпляр объекта в памяти, переменная-класс и
экземпляр класса в памяти
Виртуальные конструкторы
Особые возможности ссылок на классы проявляется в сочетании с виртуальными
конструкторами. Виртуальный конструктор объявляется с ключевым словом virtual.
Вызов виртуального конструктора происходит по фактическому значению ссылки на
класс, а не по ее формальному типу. Это позволяет создавать объекты, классы которых
неизвестны на этапе компиляции. Механизм виртуальных конструкторов применяется в
среде Delphi при восстановлении компонентов формы из файла. Восстановление
компонента происходит следующим образом. Из файла считывается имя класса. По этому
имени отыскивается ссылка на класс (метакласс). У метакласса вызывается виртуальный
конструктор, который создает объект нужного класса.
var
P: TComponent;
T: TComponentClass; // TComponentClass = class of TComponent;
...
T := FindClass(ReadStr);
P := T.Create(nil);
...
На этом закончим изучение теории объектно-ориентированного программирования и в
качестве практики рассмотрим несколько широко используемых инструментальных
классов среды Delphi. Разберитесь с их назначением и работой. Это поможет глубже
понять ООП и пригодится на будущее.
Если есть ссылка на класс,то мы можем обращаться к методам класса
Посмотреть пример программы про фигуры.
28.Свойства классов в Delphi (property). Описание и использование
свойств.
Поля, свойства и методы
Поля класса являются переменными, объявленными внутри класса. Они
предназначены для хранения данных во время работы экземпляра
класса (объекта). Ограничений на тип полей в классе не предусмотрено.
В описании класса поля должны предшествовать методам и свойствам.
Обычно поля используются для обеспечения выполнения операций
внутри класса.
Примечание
При объявлении имен полей принято к названию добавлять заглавную
букву F. Например FSomeField.
Итак, поля предназначены для использования внутри класса. Однако
класс должен каким-либо образом взаимодействовать с другими
классами или программными элементами приложения. В подавляющем
большинстве случаев класс должен выполнить с некоторыми данными
определенные действия и представить результат.
Для получения и передачи данных в классе применяются свойства. Для
объявления свойств в классе используется зарезервированное слово
property.
Свойства представляют собой атрибуты, которые составляют
индивидуальность объекта и помогают описать его. Например, обычная
кнопка в окне приложения обладает такими свойствами, как цвет,
размеры, положение. Для экземпляра класса "кнопка" значения этих
атрибутов задаются при помощи свойств — специальных переменных,
определяемых ключевым словом property. Цвет может задаваться
свойством Color, размеры — свойствами Width и Height и т. д.
Так как свойство обеспечивает обмен данными с внешней средой, то для
доступа к его значению используются специальные методы класса.
Поэтому обычно свойство определяется тремя элементами: полем и
двумя методами, которые осуществляют его чтение/запись:
type
TAnObject = class(TObject)
function GetColor: TSomeType;
procedure SetColor(ANewValue: TSomeType);
property AColor: TSomeType read GetColor write SetColor;
end;
В данном примере доступ к значению свойства AColor осуществляется
через вызовы методов GetColor и SetColor. Однако в обращении к этим
методам в явном виде нет необходимости: достаточно написать:
AnObject.AColor := AValue;
AVariable := AnObject.AColor;
и компилятор самостоятельно оттранслирует обращение к свойству AColor
в вызовы методов Getcolor или Setcolor. Tо есть внешне свойство выглядит
в точности как обычное поле, но за всяким обращением к нему могут
стоять нужные вам действия. Например, если у вас есть объект,
представляющий собой квадрат на экране, и его свойству "цвет" вы
присваиваете значение "белый", то произойдет немедленная
перерисовка, приводящая реальный цвет на экране в соответствие со
значением свойства. Выполнение этой операции осуществляется
методом, который связан с установкой значения свойства "цвет".
В методах, входящих в состав свойств, может осуществляться проверка
устанавливаемой величины на попадание в допустимый диапазон
значений и вызов других процедур, зависящих от вносимых изменений.
Если же потребности в специальных процедурах чтения и/или записи
нет, можно вместо имен методов применять имена полей. Рассмотрим
следующую конструкцию:
TPropObject = class(TObject)
FValue: TSomeType;
procedure DoSomething;
function Correct(AValue: Integer):boolean;
procedure SetValue(NewValue: Integer);
property AValue: Integer read FValue write SetValue;
end;
...
procedure TPropObject.SetValue(NewValue: Integer);
begin
if (NewValueoFValue) and Correct(NewValue) then EValue := NewValue;
DoSomething;
end;
В этом примере чтение значения свойства AValue означает просто чтение
поля rvalue. Зато при присвоении значения внутри SetValue вызывается
сразу два метода.
Если свойство должно только читаться или записываться, в его описании
может присутствовать соответствующий метод:
type
TAnObject = class(TObject)
property AProperty: TSomeType read GetValue;
end;
В этом примере вне объекта значение свойства можно лишь прочитать;
попытка присвоить свойству AProperty значение вызовет ошибку
компиляции.
Для присвоения свойству значения по умолчанию используется
ключевое слово default:
property Visible: boolean read FVisible write SetVisible default True;
Это означает, что при запуске программы свойство будет установлено
компилятором в True.
Свойство может быть и векторным; в этом случае оно внешне выглядит
как массив:
property APoints[Index : Integer]:TPoint read GetPoint write SetPoint;
На самом деле в классе может и не быть соответствующего поля —
массива. Напомним, что вся обработка обращений к внутренним
структурам класса может быть замаскирована.
Для векторного свойства необходимо описать не только тип элементов
массива, но также имя и тип индекса. После ключевых слов read и write в
этом случае должны стоять имена методов — использование здесь полей
массивов недопустимо. Метод, читающий значение векторного свойства,
должен быть описан как функция, возвращающая значение того же типа,
что и элементы свойства, и имеющая единственный параметр того же
типа и с тем же именем, что и индекс свойства:
function GetPoint(Index:Integer):TPoint;
Аналогично, метод, помещающий значения в такое свойство, должен
первым параметром иметь индекс, а вторым — переменную нужного типа
(которая может быть передана как по ссылке, так и по значению):
procedure SetPoint(Index:Integer; NewPoint:TPoint);
У векторных свойств есть еще одна важная особенность. Некоторые
классы в Delphi (списки т-List, наборы строк TStrings) "построены" вокруг
основного векторного свойства (см. гл. 7). Основной метод такого класса
дает доступ к некоторому массиву, а все остальные методы являются как
бы вспомогательными. Специально для облегчения работы в этом случае
векторное свойство может быть описано с ключевым словом default:
type
TMyObject = class;
property Strings[Index: Integer]: string read Get write Put; default;
end;
Если у объекта есть такое свойство, то можно его не упоминать, а
ставить индекс в квадратных скобках сразу после имени объекта:
var AMyObject: TMyObject;
begin
...
AMyObject.Strings[1] := 'First'; {первый способ}
AMyObject[2] := 'Second'; (второй способ}
...
end.
Будьте внимательны, применяя зарезервированное слово default, — как
мы увидели, для обычных и векторных свойств оно употребляется в
разных случаях и с различным синтаксисом.
О роли свойств в Delphi красноречиво говорит следующий факт: у всех
имеющихся в распоряжении программиста стандартных классов 100%
полей недоступны и заменены базирующимися на них свойствами.
Рекомендуем при разработке собственных классов придерживаться этого
же правила.
Внимательный читатель обратил внимание, что при объяснении терминов
"поле" и "свойство" мы использовали понятие метода, и наверняка понял
его общий смысл. Итак, методом называется объявленная в классе
функция или процедура, которая используется для работы с полями и
свойствами класса. Согласно принципам ООП (см. разд. "Инкапсуляция"
далее в этой главе), обращаться к свойствам класса можно только через
его методы. От обычных процедур и функций методы отличаются тем,
что им при вызове передается указатель на тот объект, который их
вызвал. Поэтому обрабатываться будут данные именно того объекта,
который вызвал метод. На некоторых особенностях использования
методов мы остановимся ниже.
http://256bit.ru/Delphi7/Glava1/Index3.html
http://256bit.ru/Delphi7/
или другой вариант(ЛУЧШЕ)
Свойство (property), подобно полю, является атрибутом, некоторой характеристикой
объекта. Однако, в отличие от поля, которое просто является областью для хранения
некоторого данного, свойство ассоциировано со специфическими действиями по чтению и
модификации данных свойства. Значения свойств вообще могут не существовать в
памяти, например, они могут быть прочитаны из базы данных, вычислены и т.д.
Свойства обеспечивают контроль над доступом к данным и позволяют достичь двух
основных положительных эффектов:
· упрощение доступа к полям данных, которые, в соответствии с парадигмами ООП,
должны быть закрытыми (private);
· гарантированное выполнение обязательных операций, которые должны быть выполнены
при получении значений свойств или их модификации.
Описание свойства определяет его имя, тип и по крайней мере один спецификатор
доступа:
property propertyName[indexes]: type index integerConstant specifiers;
Элементы описания свойства имеют такой смысл.
PropertyName – любой допустимый идентификатор, являющийся именем свойства.
Элемент [indexes] не является обязательным и используется для описания свойствмассивов.
Элемент описания type должен быть идентификатором типа свойства и не может быть
определением типа. Тип свойства может быть одним из следующих:
v любой порядковый, вещественный или строковый;
v множество set;
v класс или интерфейс.
Зарезервированное слово index и integerConstant являются необязательными и
используются в том случае, если несколько свойств используют одни и те же методы для
доступа к значениям свойств.
Спецификаторы specifiers– это последовательность зарезервированных слов read, write,
stored, default (или nodefault) и implements, которая включает и другие, дополнительные
идентификаторы.Любое свойство должно иметь по крайней мере один спецификатор read
или write. Спецификатор implements относится к интерфейсам и будет рассмотрен позже.
Доступ к свойствам (property access).
Каждое свойство имеет спецификатор read, write или оба. Они называются
спецификаторами доступа и имеют такой формат:
read fieldOrMethod
write fieldOrMethod
Здесь fieldOrMethod – идентификатор поля или метода, объявленного в том же классе или
в родительском классе. Если fieldOrMethodобъявлены в этом же классе, они должны быть
описаны выше, т.е. до описания свойства (в классе). Если они объявлены в родительском
классе, то должны быть видимы в классе наследнике.
Если fieldOrMethod является полем, то оно должно быть того же типа, что и свойство.
Если в спецификаторе read fieldOrMethod является методом, то это должна быть функция
без параметров, возвращающая значение того же типа, что и свойство.
Если в спецификаторе write fieldOrMethod является методом, то это должна быть
процедура с единственным параметром того же типа, что и свойство. Параметр должен
быть параметром значением или константой.
Таким образом, при наличии такого описания свойства
property Color: TColor read GetColor write SetColor;
метод GetColor должен быть объявлен как
function GetColor: TColor;
и процедура SetColor как
procedure SetColor(Value: TColor);
procedure SetColor(const Value: TColor);
Разумеется, что имя параметра, как и имена методов, могут быть любыми.
Когда свойство встречается в выражении, его значение читается из поля или с помощью
функции, определенной спецификатором read. Если свойство появляется в левой части
оператора присваивания, его значение записывается в поле или с помощью процедуры,
указанных в спецификаторе write.
В приведенном ниже примере объявляется класс TCompass с опубликованным свойством
Heading. Значение этого свойства читается из поля FHeading и записывается с помощью
процедуры SetHeading.
type
THeading = 0..359;
TCompass = class(TControl)
private
FHeading: THeading;
procedure SetHeading(Value: THeading);
published
property Heading: THeading read FHeading write SetHeading;
...
end;
С учетом приведенного описания операторы
if Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;
соответствуют следующим
if Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);
В этом классе чтение значения свойства не связано ни с какими действиями: просто
читается поле FHeading. Присваивание же свойству нового значения выполняется с
помощью процедуры, которая, возможно, тоже сохраняет значение свойства в поле
FHeading, но при этом выполняет некоторые действия. Например, этот метод может быть
реализован так:
procedure TCompass.SetHeading(Value: THeading);
begin
if FHeading <> Value then
begin
FHeading := Value;
Repaint; // обновить пользовательский интерфейс для
// отображения нового значения
end;
end;
Свойство, чье описание включает только спецификатор read, является свойством только
для чтения (read-only property). Если свойство включает только спецификатор write, оно
называется свойством только для записи (write-only property). Попытка присвоить
значение свойству только для чтения или получить значение свойства только для записи
вызывает ошибку.
29.Свойства-массивы. Свойство класса по умолчанию.
http://delphikingdom.ru/lyceum/seminar.asp?id=6
Свойства-массивы (array properties).
Свойства-массивы – это индексированные свойства. Они могут представлять такие
сущности, как элементы списков, управляющие элементы, принадлежащие некоторому
родительскому, пикселы изображения и т.д.
Объявление свойства-массива включает список параметров, который определяет имена и
типы индексов, например:
property Objects[Index: Integer]: TObject read GetObject write SetObject;
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
property Values[const Name: string]: string read GetValue write SetValue;
Формат описания индексов такой же, как и параметров подпрограмм за тем исключением,
что список параметров заключается в квадратные скобки вместо круглых. В отличие от
массивов, которые допускают индексы только порядкового типа, индексы свойств могут
иметь любой тип, например:
TShortSet = set of 0..9;
MyClass = class
private
F1 : integer;
Function PNameRead(i1: string; i2 : single; i3:TObject; i4:ShortSet): string;
public
property PName[ i1 : string;
i2 : single;
i3 : TObject;
i4 : TShortSet]:string read PNameRead;
end;
В частности, свойства-массивы не могут быть опубликованными.
Для свойств-массивов спецификаторы доступа должны быть методами, а не полями.
Метод в спецификаторе read должен быть функцией, число, порядок и тип параметров
которой должны точно соответствовать списку индексов свойства. Возвращаемое
функцией значение должно иметь, разумеется, тип свойства.
Метод в спецификаторе write должен быть процедурой, число, порядок и тип параметров
которой должны точно соответствовать списку индексов свойства. К списку параметров
необходимо также добавить один параметр значение или константу того же типа, что и
тип свойства.
Например, методы доступа для описанных выше свойств-массивов должны быть описаны
так:
function GetObject(Index: Integer): TObject;
function GetPixel(X, Y: Integer): TColor;
function GetValue(const Name: string): string;
procedure SetObject(Index: Integer; Value: TObject);
procedure SetPixel(X, Y: Integer; Value: TColor);
procedure SetValue(const Name, Value: string);
Доступ к значениям свойств массивов осуществляется путем индексирования имени
свойства. Например, операторы
if Collection.Objects[0] = nil then Exit;
Canvas.Pixels[10, 20] := clRed;
Params.Values['PATH'] := 'C:\DELPHI\BIN';
соответствуют
if Collection.GetObject(0) = nil then Exit;
Canvas.SetPixel(10, 20, clRed);
Params.SetValue('PATH', 'C:\DELPHI\BIN');
Определение свойства-массива может сопровождаться директивой default, что делает
свойство свойством по умолчанию. Например, если класс описан как
type
TStringArray = class
public
property Strings[Index: Integer]: string ...; default;
…
end;
доступ к его свойству по умолчанию может быть получен путем простого индексирования
имени объекта object[index], что эквивалентно object.property[index]. Для приведенного
выше примера выражение StringArray.Strings[7] может быть сокращено до StringArray[7].
Класс может иметь только одно свойство по умолчанию, которое в порожденных классах
нельзя изменять или прятать.
Спецификатор Index (Index specifiers).
Этот спецификатор предоставляет возможность нескольким свойствам использовать один
и тот же метод, возвращающий разные значения (одного типа). Спецификатор Index
сопровождается целой константой из диапазона
-2147483647..2147483647. Если свойство имеет спецификатор Index, оно должно иметь
методы для доступа к его значениям, а не поля. Например
type
TRectangle = class
private
FCoordinates: array[0..3] of Longint;
function GetCoordinate(Index: Integer): Longint;
procedure SetCoordinate(Index: Integer; Value: Longint);
public
property Left: Longint index 0 read GetCoordinate write SetCoordinate;
property Top: Longint index 1 read GetCoordinate write SetCoordinate;
property Right: Longint index 2 read GetCoordinate write SetCoordinate;
property Bottom: Longint index 3 read GetCoordinate write SetCoordinate;
property Coordinates[Index: Integer]: Longint read GetCoordinate
write SetCoordinate;
...
end;
Метод доступа для свойства со спецификатором index должен иметь дополнительный
параметр – значение целого типа. Для метода (функции) read это должен быть последний
параметр, а для метода write это должен быть предпоследний параметр, т.е. параметр,
предшествующий параметру, задающему значение свойства. Когда программа
осуществляет доступ к значению свойства, значение (константа) index автоматически
передается методу.
С учетом приведенного выше описания код
var
Rectangle : TRectangle;
…
Rectangle.Right := Rectangle.Left + 100;
соответствует такому вызову метода
Rectangle.SetCoordinate(2, Rectangle.GetCoordinate(0) + 100);
Спецификаторы сохранения (Storage specifiers).(дифолт=свойства по умолчанию)
Так называются необязательные директивы stored, default и nodefault. Они не оказывают
никакого влияния на работу программы, однако управляют тем, как Delphi поддерживает
информацию о типах времени выполнения (RTTI). Конкретно, эти спецификаторы
определяют, сохраняет ли Delphi значения опубликованных свойств в файлах форм (.dfm).
Вслед за директивой stored необходимо указать True, False, имя поля булевского типа или
имя функции без параметров, которая возвращает значение булевского типа. Например
property Name: TComponentName read FName write SetName stored False;
Если для свойства не указана директива stored, она трактуется как имеющая значение
True.
Вслед за директивой default должна следовать константа того же типа, что и тип свойства.
Например
property Tag: Longint read FTag write FTag default 0;
Для перекрытия унаследованного значения по умолчанию без задания нового надо указать
директиву NoDefault. Директивы default и nodefault поддерживаются для свойств только
порядкового типа, а также множественного типа диапазона значений 0..31. Если свойство
не имеет директив default или nodefault, оно трактуется как имеющее директиву
nodefault.
Когда Delphi сохраняет состояние компонента, она проверяет обсуждаемые
спецификаторы для опубликованных свойств. Если текущее значение свойства отличается
от его значения по умолчанию (или если значение по умолчанию не задано) и
спецификатор stored имеет значение True, то значение свойства сохраняется. В
противном случае – нет.
Отметим, что спецификаторы сохранения не поддерживаются для свойств-массивов. Для
свойств-массивов директива default трактуется иначе.
Перекрытие и повторное описание свойств (Property overrides and redeclarations).
Описание свойства, в котором не указан его тип, называется перекрытием свойства.
Перекрытие свойства позволяет изменить унаследованную видимость свойства или
спецификаторы.
Простейший способ перекрытия состоит в указании только зарезервированного слова
property и имени свойства. Этот прием используется для изменения видимости свойства
(property’s visibility). Например, если в родительском классе свойство описано как
protected, производный класс может переобъявить его в секции public или published.
Повторное описание свойства может включать директивы read, write, stored, default и
nodefault, и любая из них перекрывает соответствующую унаследованную. Перекрытие
(повторное описание) может изменить унаследованный спецификатор доступа, содержать
отсутствующий спецификатор или увеличить видимость свойства, однако оно не может
удалить спецификатор доступа или уменьшить видимость свойства. Повторное описание
может также включать директиву implements, которая добавляет свойство в список
интерфейсов без удаления унаследованных.
Следующие описания иллюстрируют перекрытие описаний свойств.
type
TAncestor = class
…
protected
property Size: Integer read FSize;
property Text: string read GetText write SetText;
property Color: TColor read FColor write SetColor stored False;
…
end;
type
TDerived = class(TAncestor)
...
protected
property Size write SetSize;
published
property Text;
property Color stored True default clBlue;
...
end;
Перекрытие свойства Size добавляет спецификатор write, делая свойство доступным и по
записи. Перекрытие свойств Text и Color изменяет их видимость, а для свойства Color
задает также и значение по умолчанию.
Повторное объявление свойства с указанием его типа скрывает унаследованное свойство,
а не перекрывает его. Это значит, что новое свойство создается с тем же именем, что и
"доставшееся по наследству". Вследствие этого любое повторное описание свойства
должно быть семантически полным и должно включать по меньшей мере один
спецификатор доступа.
Независимо от того, перекрывается ли свойство или скрывается в производном классе,
поиск свойства объекта имеет статический характер. Это значит, что для идентификации
объекта, которому принадлежит свойство, используется информация времени
компиляции. Например, пусть есть такой фрагмент программы:
type
TAncestor = class
...
property Value: Integer read Method1 write Method2;
end;
TDescendant = class(TAncestor)
...
property Value: Integer read Method3 write Method4;
end;
var MyObject: TAncestor;
...
MyObject := TDescendant.Create;
Теперь получение или изменение значения свойства MyObject.Value будет выполняться с
использованием методов Method1 и Method2, хотя MyObject является экземпляром класса
TDescendant. Вместе с тем возможно использование приведения типа MyObject к типу
TDescendant для получения доступа к свойствам и соответствующим методам
производного класса.
Создание собственного редактора свойства (property editor).
Несмотря на то, что инспектор объектов включает редактирование по умолчанию для всех
типов свойств, можно реализовать свой собственный редактор свойства. Можно
зарегистрировать редактор только для одного свойства своего собственного компонента
или для всех свойств определенного типа.
Принципиально редактор свойств может иметь один из двух режимов работы (или оба):
отображение и редактирование значения свойства как строки символов: создание
диалогового окна, которое может предоставлять другой сособ редактирования свойства.
Разработка редактора свойства выполняется в следующей последовательности:
1 Deriving a property-editor class
2 Editing the property as text
3 Editing the property as a whole
4 Specifying editor attributes
5 Registering the property editor
30.Консольные приложения в Delphi.
Консольное приложение(консоль Мб передана от вызывающей программы к
вызываемой)
С помощью системы Delphi 7 можно создавать приложения Windows практически
неограниченной сложности, использующие графический интерфейс. Однако для
тех, кто только начинает знакомство с основными операторами Паскаля, имеется
возможность создания простых программ в стиле MS-DOS {в качестве учебных).
Это так называемые консольные приложения. Внешне они выглядят как программы
с текстовым интерфейсом, но способны обращаться к большинству функций
Windows.
Чтобы создать консольное приложение, надо дать команду File > New >• Other (Файл >
Создать > Другое) и в диалоговом окне New Items (Создание программы) выбрать значок Console Application (Консольное приложение) (рис. 1.1).
f' New Items
Система 1)е/рЙ1?автоматически сгенерирует в текстовом редакторе исходный код
заготовку будущего приложения.
Основы языка Delphi (Objecl Pascal) 35
program Projectl;
{$APPTYPE CONSOLE)
uaea sysutils;
begin
{TODO -oUser -cConsole Main : Insert code here}
end.
Код начинается с заголовка программы, состоящего из ключевого слова program и
автоматически сгенерированного названия Projectl.
Далее в виде комментария следует директива компилятора. Она отличается от
обычного комментария тем, что сразу за символом { следует символ $, а следом —
слово, задающее определенную настройку компилятора. Хотя настройки компилятора можно задавать и непосредственно в среде Delphi 7, нужные настройки не
всегда могут быть включены разработчиком вручную, поэтому лучше явно указывать их в тексте там, где они обязательно требуются.
, 1.ир< K i in'.] {5APPTVPE CO.NSOLE} говорит компилятору, ч го д;ши;1!( нршрамма
представляет собой консольное приложение.
Следующая строка задает подключение стандартного модуля SysUtib с помощью
ключевого слова uses.
Далее идет собственно программа — для нее подготовлен логический блок, внутри
которого система Delphi /добавила комментарий Insert user code here (Вставьте сюда
свой исходный текст).
Обмен информацией
Так как создаваемая программа — консольная, общаться с пользователем с помощью графического интерфейса она не может. Для этого нужны более простые средства обмена информацией с человеком.
В Паскале имеются две стандартные процедуры ReadLn (для ввода данных) и WriteLn
(для записи данных), которые могут использоваться в консольных приложениях.
Первая процедура выполняет ввод значения с клавиатуры и его передачу в переменную, которая указана в качестве параметра (можно указать и список переменных — тогда при вводе соответствующие им значения надо разделять пробелами).
Вторая процедура выводит одно или несколько значений переменных или выра-
жений в текущую строку экрана, разделяя их пробелами.
Процедура ReadLn требует, чтобы человек после окончания ввода нажал клавишу
ENTER, а процедура WriteLn после окончания вывода значений осуществляет перевод курсора на следующую строку. Другая процедура вывода, Write, не выполняет
такого перевода и продолжает вывод данных в текущую строку.
ВНИМАНИЕ Переход на следующую строку в процедуре WriteLn происходит
не до, а после вывода значений параметров.
36 Урок 1. Язык Delphi (Object Pascal) и его использование
У процедуры ReadLn может не быть ни одного параметра. Тогда при ее выполнении
программа просто ожидает, когда пользователь нажмет клавишу ENTER. Если не
указан ни один параметр процедуры WriteLn, то на экране произойдет пропуск одной
пустой строчки.
Для первого, пробного запуска в главный логический блок программы можно
поместить один оператор ReadLn — тогда программа просто высветит черное консольное окно и закроется, когда будет нажата клавиша ENTER.
begin
ReadLn
end.
Обратите внимание: точку с запятой перед ключевым словом end можно не ставить.__
Др вариант
Консольным называется приложение, имитирующее работу в текстовом режиме.
Пользователь работает с программой практически так же, как в среде DOS. При
запуске консольного приложения Windows выделяет окно как для DOS-программы, в
заголовке окна отображается название исполняемого файла. Ввод/вывод данных
осуществляется с помощью процедур read, readln, write и writeln. К консольному
приложению автоматически подключаются файлы input и output. Несмотря на то, что
пользователь работает с консольным приложением так же, как с DOS-программой, оно
является приложением Windows и не работает под DOS.
Достоинство консольных приложений – относительная простота использования и
лёгкость переноса программ, написанных на языке Pascal, в систему
программирования Delphi. Кроме того, исполняемый ехе-файл консольной программы
намного меньше по размеру (десятки килобайт) по сравнению с исполняемым файлом
Delphi-варианта такой же программы (сотни килобайт).
Проще всего создать консольное приложение через репозиторий. В репозитории
(команда File|New|Other) на странице New имеется объект Console Аpplication,
представляющий собой Console Wizard – Мастер консольного приложения. Если
выбрать этот объект, то будет создан новый проект, состоящий из одного файла с
расширением dpr. Этот файл и является консольной программой. Созданный
Мастером код похож на заготовку обычной программы на языке Pascal, написанной
под DOS. Единственным отличием является директива $APPTYPE, которая значением
CONSOLE сообщает компилятору, что Delphi-программа работает в консольном
режиме.
Консольное приложение можно создать также на основе проекта обычного
приложения следующим образом. Первоначально по команде File|New|Aplication
создаётся новое приложение. Консольное приложение не включает формы, поэтому из
проекта нужно удалить форму Form1. С этой целью по команде Project|Remove from
Project (Проект|Удалить из проекта) вызывается диалоговое окно Remove from Project
удаления форм из проекта, в котором для данного проекта содержится один модуль
unit1 формы. Нужно выбрать этот модуль и удалить его.
Затем вызывается окно редактора кода, в нем открывается файл проекта (dpr), в
который вносятся соответствующие изменения.
Далее к заготовке консольного приложения добавляется необходимый код. Как и
любая программа на языке Pascal, консольное приложение может включать отдельные
модули.
program demo;
{$APPTYPE CONSOLE}
uses SysUtils;
begin
writeln('Проверка');
end;
Завершив работу с консольным приложением, нужно нажать клавишу Enter. После
этого окно, в котором функционировало консольное приложение, автоматически будет
закрыто.
Консольное приложение Delphi представляет собой не просто программу, написанную
на языке Object Pascal и выполняемую в среде Windows. Система Delphi поддерживает
создание 32-разрядных консольных приложений, имеющих доступ к ресурсам
операционной системы и использующих различные функции API Windows. При этом в
разделе uses нужно подключать модули, средства которых применяются в программе.
Для организации ввода-вывода информации используются стандартные процедуры:
Read(<файловая_переменная>,<список_ввода>);
Readln(<файловая_переменная>,<список_ввода>);
Write(<файловая_переменная>,<список_вывода>);
Writeln(<файловая_переменная>,<список_вывода>);
Первым параметром в любой из перечисленных процедур может стоять файловая
переменная. В этом случае выполняется обращение к дисковому файлу или
логическому устройству, связанному с файловой переменной. Если файловая
переменная не указана, то подразумевается использование текстовых файлов input или
output. Input используется при чтении данных с клавиатуры, а output – при выводе
данных на экран:
Read (a,b,x2);
Readln (p,q5);
Write (n:5,x:4:1;y:7:2);
Writeln (name, ' ':3, 2*x+3:6:0);
Специфика обработки текстовых файлов заключается в том, что процедуры вводавывода позволяют работать со значениями не только символьного типа: фактическими
параметрами могут быть символы, строки и числа. Последовательность символов
автоматически преобразуется к значению переменной того типа, который задан в
списке ввода-вывода.
При вводе числовых значений процедура Read выделяет подстроку по следующему
правилу:
все ведущие пробелы, символы табуляции и маркеры конца строк пропускаются;
после появления первого значащего символа любой из перечисленных символов или
код конца файла служат признаком конца подстроки.
Выделенная таким образом подстрока рассматривается как символьная запись числа
соответствующего типа и преобразуется во внутреннее представление; полученное
значение присваивается переменной.
Если при вводе чисел нарушен формат (например описана переменная типа integer, а
задано число real), то во время работы программы возникнет ошибка ввода-вывода.
Если при пропуске ведущих пробелов встретится символ конца файла, то переменная
получит значение ноль.
При вводе с клавиатуры символьные строки запоминаются в буфере. Содержимое
буфера передаётся процедуре только после нажатия клавиши Enter. Это позволяет
редактировать данные при вводе. Ввод информации сопровождается эхо-повтором на
экране.
Отличие процедуры Readln от Read заключается в том, что после считывания
последней переменной, фигурирующей в списке ввода, оставшаяся часть строки до
маркера конца строки пропускается. Следующее обращение к Readln или Read
выполняет чтение с первого символа новой строки. Readln можно использовать без
списка ввода, это обеспечивает пропуск строки.
Процедуры Write, Writeln выводят информацию в файл или на логическое устройство.
Если файловая переменная описана как текстовая, то список вывода может содержать
символы, строки, вещественные и целые числа, а также выражения типа boolean. В
последнем случае записывается true или false, в зависимости от текущего значения
логической переменной.
Элемент списка вывода, например х, может быть записан в виде x:k:m. Значение k
задаёт ширину поля вывода символьного представления переменной x. Если
символьное представление имеет меньшую длину, то оно будет дополнено пробелами
слева; если длиннее – то параметр k игнорируется и переменная выводится целиком.
Параметр m определяет выводимое количество десятичных знаков в дробной части
числа. Этот параметр используется только для вещественных чисел и только
совместно с параметром k. Если ширина поля вывода не указана, то переменные
выводятся подряд, без разделения, вещественные числа записываются в
экспоненциальном формате.
При использовании Writeln выводимая строка завершается символами конца строки.
Последующее применение процедур Write и Writeln обеспечивает запись информации
с новой строки.
31.Обработка исключительных ситуаций в Delphi.
Исключительные ситуации
В языке Паскаль важную роль играет обработка исключительных ситуаций, связанных с попыткой выполнения во время работы программы какого-то действия,
приводящего к ошибке или просто нарушающего ее функционирование и делающего невозможным дальнейшее нормальное выполнение.
Исключительные ситуации контролируются специальным обработчиком исключительных ситуаций. Он перехватывает практически все возникающие в программе
ошибки, приостанавливает программу, не давая выполниться разрушительной команде, и сообщает об этом пользователю (рис. 3.18) и программе, передавая ей информацию об обнаруженной ошибке в виде объекта, относящегося к специальной
иерархии классов, описывающих исключительные ситуации. Базовым в этой иерархии является класс Exception. Он описан в модуле SysUtHs.
Project Project! еке raised exception ctassEDrtfByHeru with •
message 'DMsion by leio'. Process slopped. Use Step or Him ID
OK
Puc. 3.18. Уведомление а возникновении в программе
исключительной ситуации (деления па ноль)
Для оперативного вывода коротких сообщений можно использовать стандартную
функцию ShowMessage, которая имеет один параметр-строку и отображает очень
похожее диалоговое окно с единственной кнопкой ОК:
ShowMessage('Это сообщение от программы');
1 76 Урок 3. Отладка программ
Генерация исключительной ситуации
Классы иерархии Exception обладают несколькими видами полезных конструкторов, которые можно применять для искусственной генерации соответствующей
исключительной ситуации.
Таблица 3.2. Конструкторы классов иерархии Exception
Конструктор Параметры
Createfconst Msg: string); Параметр — строка, которая будет отображаться а
диалоговом окне, информирующем о возникновении
исключительной ситуации
CreateRes(ResStrirgRec: PresStringRec); Параметр — строка сообщения, которая
загружается
overload; из ресурсов программы
CreateFmt (const Msgr string; Массив Args содержит значения, на основе которых
const Args: array of const); с использованием строки форматирования Msg
CreateResFmt(ResStringRec: PresStringRec; (см. функцию Format) или ResStringRec
(загружается •
const Args: array of const); overload; из ресурсов программы) формируется
результирующее сообщение
CreateHeip (const Msg: string; Идентификатор раздела справки AHeLpContext
AHelpContext: Integer); указывает, где пользователь сможет подробнее
узнать о возникшей ошибке. Справочная система для
текущей программы должна быть создана заранее
Создание объекта и вызов нужного конструктора осуществляется с помощью ключевого слова raise:
if N < 1 then
raise Exception.Createf'Значение переменной К меньше 1');
Стандартные классы исключительных ситуаций
В библиотеках системы Delphi 7 имеется немало классов, ответственных за обра-
ботку различных исключительных ситуаций. В отличие от остальных типов Паскаля, названия которых принято записывать начиная с буквы Т, имена этих классов
начинаются с буквы Е (Exception). Основные классы исключительных ситуаций
приведены в табл. 3.3.
Таблица 3.3. Основные классы исключительных ситуаций
Имя класса Когда возникает
EAbort Данный класс предназначен для формирования и обработки ≪невидимых≫
для пользователя ошибок и используется разработчиками для управления
ходом выполнения программы. Диалоговое окно с сообщением об ошибке
не показывается
EAbstractError Попытка выполнения абстрактного метода
EAccessViolation Обращение к недоступной области памяти, например при выходе
индекса
за границы мА
Е Assertion Failed
EControlC
ECon vert Error
EDivByZero
EExternal
£ Exte rnalException
EHeapException
EFHeStream Error
EInOutError
EIntError
EIntOverfLow
ElntfCastError
ElnvalidCast
EInvaLidOp
Elnvalid Pointer
EMathError
EOutOf Memory
E Overflow
EPackageError
EPrivilege
EPropReadOnly
ERangeError
EStackQverflow
E Underflow
EVariantError
EWinBZError
EZero Divide
Значение выражения в процедуре Assert равно False
Нажата комбинация клавиш CTRL+C в консольном приложении
Попытка неверного преобразования типов, например при вызове StrToInt('aaa')
Деление на ноль
Неверное функционирование системы Windows
Невозможность распознать исключительную ситуацию средствами Delphi 7
Неверное динамическое распределение памяти или некорректная работа с указателями
Ошибка ввода/вывода при использовании файла, название которогоуказано в параметре FHeName данного класса
Ошибка файлового ввода/вывода
Базовый класс, на основе которого созданы классы исключительных ситуаций при работе с целыми числами
Слишком большой результат при операции с целыми числами
Неверное приведение типа объекта с помощью операции as во время обращения к интерфейсу
Неверное приведение типа с помощью операции as
Неверная операция над числами с плавающей запятой
Неверная операция при работе с указателями
Базовый класс, на основе которого созданы классы исключительных ситуаций при работе с числами с плавающей зап
Нехватка памяти
Переполнение при выполнении операции над числами с плавающей запятой
Некорректная работа с пакетами {см. далее). Возникает только на этапе проектирования в среде Delphi 7
Попытка выполнить неразрешенную привилегированную инструкцию процессора
Попытка занесения значения в свойство объекта, доступное ≪только для чтения≫
Значение выражения целого типа выходит за допустимый для этого типа диапазон
Нехватка памяти в стеке. Возникает, когда используются слишком объемные локальные переменные (они создаются и
хранятся в стеке) или когда слишком длинна последовательность вызовов вложенных подпрограмм
Результатом выражения над числами с плавающей запятой является число, которое слишком мало для его
представления в программе
Некорректное использование переменных типа Variant например при попытке неверного приведения типов
Ошибка 32-разрядной"версии Window?
Деление
на
ноль__
ЗАМЕЧАНИЕ Иногда исключительные ситуации используют, чтобы изменить порядок выполнения операторов программы. Если, например, обнаружена ошибка при работе какого-либо метода, желательно не
просто завершить его работу, а передать управление в часть программы, ответственную за исключительные ситуации. Для этого
обычно применяют процедуру Abort [без параметров), генерирующую исключительную ситуацию класса EAbort.
Контроль над исключительными ситуациями
Для контроля над исключительными ситуациями в группе операторов Паскаля
применяется следующая конструкция:
try
операторы;
except
обрабатываемые классы исключительных ситуаций;
else оператор;
end,Ключевое слово try (попытка) обозначает начало блока контроля выполнения операторов, следующих до ключевого слова except. В случае возникновения исключительной ситуации происходит обращение к списку классов, перечисленных перед
завершающим ключевым словом end. При этом выполняется действие, указанное
для соответствующего класса, а затем управление передается первому оператору,
следующему за завершающим ключевым словом end. Операторы, оставшиеся в
части try. пропускаются. Если исключительные ситуации не встретились, то пропускаются все действия, следующие за ключевым словом except.
Если возникшая ситуация не относится ни к одному из явно обрабатываемых классов, то выполняется команда Паскаля, указанная после ключевого слова else. Часть
else в блоке try указывать не обязательно.
Классы исключительных ситуаций, предназначенные для обработки, записываются
в следующем формате:
on название-класса do операторы;
Таких классов может быть несколько, например:
try
Assert ( Y о 5, ' ' ) ,X := 100 div Y;
except
on EZeroDivide do ZeroProc;
on EAssertionFailecl do
begin
Исключительные ситуации 1 79
ShowMessage('Ошибка # 22'};
X :- 0;
end;
else ShowMessage|'Непонятно что ' );
end;
Если в операторе присваивания будет обнаружена попытка деления на ноль (для
этого надо, чтобы значение переменной У было равно 0), то выполнится процедура
ZeroProc, которая должна быть определена ранее. Если же значением переменной Y
окажется число 5, то обработчиком исключительных ситуаций будет сгенерирован
объект класса Е Assertion Failed и выполнится группа операторов в логических скобках. Оператор X := 100 div Y при этом будет пропущен. Если встретится какая-то
другая исключительная ситуация, то выведется сообщение Непонятно что.
Поиск класса подходящей исключительной ситуации осуществляется в последо-
вательном порядке. Если возникшую ошибку можно отнести к нескольким классам, то вызван будет обработчик для класса, расположенного первым, например:
try
X := Y + Z;
except
on EIntError do PI;
on EIntOverflow do P2,end;
Если при выполнении оператора X :- Y + Z возникнет ошибка переполнения
EIntOverflow, то вызвана, тем не менее, будет подпрограмма Р1, потому что эта ошибка
относится также и к классу EIntError, расположенному первым в списке за ключевым словом except.
Можно создать единый обработчик для любой исключительной ситуации. Тогда
блок try записывается так:
try
операторы;
except
действия,end,Например:
try
X := 0;
except
GlobalProc;
end;
В случае возникновения во время исполнения оператора X :- 0 произвольной исключительной ситуации вызывается процедура GlobalProc.
1 80 Урок 3. Отладке программ
Иногда реализованной программистом обработки ошибки недостаточно. Тогда, указав ключевое слово raise без параметра, можно передать управление стандартному
обработчику Delphi 7:
try
X := 100 div У;
except
on EZeroDivide do
begin
ShowMessage('Ошибка # 22'),raise;
end;
end;
После выдачи сообщения Ошибка # 22 исполнится стандартный обработчик ошибки
EZeroDivide.
Из текста обработчика ошибки можно генерировать другие исключительные ситуации, например:
try
X := У +• Z;
except
on ElntError do
raise EIntOverflow.Create('Возможно переполнение');
end;
Если при сложении целых чисел, хранящихся в переменных Y и Z, возникнет какая-то арифметическая ошибка (базовый класс ElntError), то в обработчике этой
ошибки будет сгенерирована другая исключительная ситуация EIntOverflow, а обработка ошибки ElntError завершится.
Выполнение завершающих действий
В некоторых ситуациях программисту не нужен собственный обработчик ошибок,
но требуется, чтобы программа гарантированно выполнила определенные действия,
связанные, например, с освобождением ресурсов. В такой ситуации удобнее использовать следующий блок:
try
операторы
finally
заключительные действия
end;
Заключительные действия будут выполнены в любом случае, независимо от того,
возникнет ли исключительная ситуация в операторах части try или, например,
выполнится попытка выхода из подпрограммы с помощью процедуры Exit.
Исключительные ситуации 181
В следующем тексте освобождение памяти, выделенной для динамического массива DynArr (финальный оператор присваивания значения nil), произойдет независимо от того, успешно ли создан и обработан массив DynArr или памяти для него не
хватило:
var DynArr: array of integer;
try
SetLength(DynArr,100000);
finally
DynArr := nil
end;
Передача объектов, связанных
с исключительными сит•у* ациями
Когда в программе возникает исключительная ситуация, специальный обработчик создает соответствующий ей объект. Чтобы получить доступ к этому объекту,
в описании класса в части except надо предварительно указать произвольный идентификатор:
ОП Е: EIntError do Р;
В случае возникновения ошибки EIntError объект соответствующего класса будет
сохранен в переменной Е (описывать се не надо), к которой можно обращаться,
например для занесения новых значений в ее свойства.
У любого класса исключительной ситуации есть два свойства: Message, в котором
хранится строка, выводимая в окне сообщения, и HelpContext, число-идентификатор раздела справочной системы для вывода более подробной информации об
ошибке.
on E: EIntError do
begin
Е.Message := 'Ошибка вычислений';
raise;
end;
Если доступ к объекту, описывающему ошибку, надо получить в части else блока
except, где никакие классы явно не упоминаются, можно использовать функцию
ExceptObject, которая возвращает такой объект.
Программный обработчик ошибок
По умолчанию при возникновении исключительной ситуации для ее обработки
вызывается процедура HandleException. Она проверяет, обрабатывает ли приложе1 82 Урок 3. Отладка программ
ние событие OnException. Если обработка отсутствует, выводится диалоговое окно
с кратким сообщением об ошибке. Такое окно можно вызывать с помощью стандартной процедуры ShowException, которая получает в качестве параметра объект, имеющий тип исключительной ситуации:
on E: EIntError do
ShowException(E);
Обработчик события OnException задается явно, с помощью оператора присваивания, например в методе создания или активации формы:
procedure TMyForm.FormActivate(Sender: TObject);
begin
Application.OnException := AppException;
end;
Процедуру AppException надо определить в классе TmyForm:
procedure AppException(Sender: TObject; E: Exception);
Затем ее надо описать в части реализации. В следующем примере при обнаружении исключительной ситуации отображается стандартное информационное окно,
после чего работа приложения завершается:
procedure TMyForm.AppExcept ion (Sender: TObj ect; E:
Exception) ,begin
Application.ShowException(E);
Application. Terminate ,end;__
32.Получение информации о файлах, имеющихся в каталоге.
Атрибуты файла. Поиск файла
Еще одна часто выполняемая с файлом операция — поиск файлов в
заданном каталоге. Для организации поиска и отбора файлов
используются специальные процедуры, а также структура, в которой
сохраняются результаты поиска.
Запись
type
TFileName = string;
TSearchRec = record
Time: Integer; {Время и дата создания}
Size: Integer; {Размер файла}
Attr: Integer; {Параметры файла}
Name: TFileName; {Полное имя файла}
ExcludeAttr: Integer; (He используется}
FindHandle: THandle; {Дескриптор файла}
FindData: TWin32FindData; {He используется}
end;
обеспечивает хранение характеристик файла после удачного поиска.
Дата и время создания файла хранятся в формате MS-DOS, поэтому для
получения этих параметров в принятом в Delphi формате TDateTime
необходимо использовать следующую функцию:
function FileDateToDateTime(FileDate: Integer): TDateTime;
Обратное преобразование выполняет функция
function DateTimeToFileDate(DateTime: TDateTime): Integer;
Свойство Attr может содержать комбинацию следующих
значений:
флагов-
faReadOnly — только для чтения;
faDirectory — каталог;
faHidden — скрытый;
faArchive — архивный;
faSysFile — системный;
faAnyFile — любой.
favoiumeio — метка тома;
Для определения параметров файла используется оператор AND:







if (SearchRec.Attr AND faReadOnly) > 0
then ShowMessage('Файл только для чтения');
Непосредственно для поиска файлов используются функции FindFirst и
FindNext.
Функция
function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer;
находит первый файл, заданный полным маршрутом Path и параметрами
Attr (см. выше). Если заданный файл найден, функция возвращает 0,
иначе — код ошибки Windows. Параметры найденного файла
возвращаются в записи F типа TSearchRec.
Функция
function FindNext(var F: TSearchRec): Integer;
применяется для повторного поиска следующего файла,
удовлетворяющего критерию поиска. При этом используются те
параметры поиска, которые заданы последним вызовом функции FindFirst.
В случае удачного поиска возвращается 0.
Для освобождения ресурсов, выделенных для выполнения поиска,
применяется функция:
procedure FindClose(var F: TSearchRec);
В качестве примера организации поиска файлов рассмотрим фрагмент
исходного кода, в котором маршрут поиска файлов задается в
однострочном текстовом редакторе DirEdit, а список найденных файлов
передается в компонент TListBox.
procedure TForml.FindBtnClick(Sender: TObject);
begin
ListBox.Items.Clear;
FindFirst(DirEdit.Text, faArchive + faHidden, SearchRec);
while FindNext(SearchRec) = 0 do
ListBox.Iterns.Add(SearchRec.Name);
FindClose(SearchRec);
end;
Визуальное программирование и основные компоненты Delphi
33.Модуль SysUtils. Что оттуда можно взять?
В состав среды Delphi входит великолепный набор модулей, возможности которых
удовлетворят даже самого привередливого программиста. Все модули можно разбить на
две группы: системные модули и модули визуальных компонентов.
К системным модулям относятся System, SysUtils, ShareMem, Math. В них содержатся
наиболее часто используемые в программах типы данных, константы, переменные,
процедуры и функции. Модуль System - это сердце среды Delphi; содержащиеся в нем
подпрограммы обеспечивают работу всех остальных модулей системы. Модуль System
подсоединяется автоматически к каждой программе и его не надо указывать в операторе
uses.
Модули визуальных компонентов (VCL - Visual Component Library) используются для
визуальной разработки полнофункциональных GUI-приложений - приложений с
графическим пользовательским интерфейсом (Graphical User Interface). Эти модули в
совокупности представляют собой высокоуровневую объектно-ориентированную
библиотеку со всевозможными элементами пользовательского интерфейса: кнопками,
надписями, меню, панелями и т.д. Кроме того, модули этой библиотеки содержат простые
и эффективные средства доступа к базам данных. Данные модули подключаются
автоматически при помещении компонентов на форму, поэтому вам об этом заботиться не
надо. Их список слишком велик, поэтому мы его не приводим.
Все основные модули среды Delphi, включая модули визуальных компонентов,
поставляются вместе с их исходными текстами на языке Delphi. По мере роста вашего
профессионального опыта мы рекомендуем чаще обращаться к этим исходным текстам.
Во-первых, в них вы найдете ответы на многие вопросы о внутреннем устройстве среды
Delphi, а во-вторых, они послужат образцовым примером профессионального подхода в
решении широкого круга задач. И, в-третьих, что не менее важно, это поможет научиться
красиво и правильно (в рамках устоявшегося стиля) оформлять тексты Ваших
собственных программ так, чтобы их с легкостью читали и понимали другие
программисты.
Исходные тексты стандартных модулей среды Delphi находятся в каталоге Delphi/Source.
Модуль SysUtils содержит функции разного назначения. Должен быть включен в список
используемых модулей uses.
Включает операции приведения типов. Наиболее часто в преобразованиях участвует тип
string, так как в приложениях под Windows данные вводятся и выводятся, как строки.
http://www.delphisources.ru/pages/faq/faq_delphi_basics/navSysUtils.php.html
ПЕРЕМЕННЫЕ, ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ SYSTEM
Модуль SYSTEM автоматически связывается с любой программой, поэтому объявленные
в его интерфейсной части типы, константы, переменные и подпрограммы доступны
программисту в любой момент. В этом приложении приводится (в алфавитном порядке)
список всех объявлений модуля в его интерфейсной секции. Следует заметить, что
большинство объявлений осталось неизменным еще со времен давнего предшественника
Delphi - системы программирования Turbo Pascal. Кроме того, многие подпрограммы
описаны в других разделах книги (работа с файлами, с вариантами, преобразование строк
и т. п.).
function Abs(X) ;
Возвращает абсолютную величину выражения x (целого или вещественного типа)
function Addr(X): Pointer;
Возвращает адрес переменной или подпрограммы х
var AllocMemCount: Integer;
Возвращает полное количество блоков памяти, выделенных программе
var A11оcMemSize: Integer;
Возвращает полное количество памяти (в байтах), выделенное программе
procedure Append (var F: Text);
Открывает текстовый файл F для добавления новых строк
function ArcTan(X: Extended): Extended;
Возвращает Арктангенс (в радианах) X
procedure Assert (expr : Boolean;const msg: Strings);
Проверяет условное выражение ехрr. Выдает сообщение msg и останавливает программу,
если выражение имеет значение False, в противном случае ничего не делает
procedure AssignFile (var F; File Name: String) ;
Связывает файловую переменную F с файлом, указанным в FileName
function Assigned (var P) : Boolean;
False, если указатель P связан с nil (т. е. не указывает на область памяти)
procedure BlockRead(var F: File;var Buf; Count: Integer; var AmtTransferred: Integer);
Читает из файла, связанного с F, в буфер Buf не более Count блоков и возвращает в
AmtTransfered истинное количество прочитанных блоков
procedure BlockWrite (var f: File;var Buf; Count: Integer ; var AmtTransferred: Integer);
Записывает в файл, связанный с F, в AmtTransfered истинное количество записанных
блоков
procedure Break;
Немедленно прекращает выполнение циклов for, while или repeat
procedure ChDir(S: Strings);
Изменяет умалчиваемый каталог на каталог, заданный выражением S
function Chr(X: Byte): Char;
Преобразует байт X в символ
procedure Close(var F) ;
Закрывает файл F
procedure CloseFile(var F);
Закрывает файл F
var CmdLine: PChar;
Содержит параметры запуска программы
var CmdShow: Integer;
Содержит статус окна, открытого API-функцией ShowWindow
function CompToCurrency(acomp:Comp): Currency; cdecl;
Преобразует тип Comp к типу Currency
function CompToDouble(acomp:Comp): Double; cdecl;
Преобразует тип Comp к типу Double
function Concat(sl , s2,..., sn: String): String;
Объединяет строки Si в единую строку
procedure Continue;
Прекращает очередную итерацию цикла for, while или repeat
function Copy(S; Index, Count:Integer): String;
function Copy(S; Index, Count:Integer): array;
Возвращает подстроку или сегмент динамического массива
function Cos(X: Extended): Extended;
Возвращает косинус аргумента X, заданного в радианах
procedure CurrencyToComp(acurrency: Currency; var result:Comp); cdecl;
Преобразует тип Currency к типу Comp
var DLLProc: Pointer;
Указывает точку входа в DLL-процедуру, которая выполняется в данный момент
procedure Dec(var X ; N: LongInt);
Уменьшает X на N, а если N опущено - на 1. X, N - любые порядковые типы, в том числе
Int64
var Default8087CW: Word;
Содержит 0, если в ПК нет арифметического сопроцессора, в противном случае разряды
слова указывают некоторые технические параметры сопроцессора
procedure Delete(var S: String;Index, Count: Integer);
Удаляет из строки S Count символов начиная с символа с номером Index
procedure Dispose(var P:Pointer) ;
Освобождает память, связанную с указателем P
function DoubleToComp(adouble:Double; var result: Comp); cdecl;
Преобразует тип Double к типу Comp
var EmptyParam: OleVariant;
Указывает, что умалчиваемый параметр индуального интерфейса не используется
procedure EnumModules(Func: TEnumModuleFunc; Data: Pointers):overload;
procedure EnumModules(Func:TEnumModuleFuncLW; Data: Pointer):overload;
Реализует обратный вызов (callback) для всех модулей и всех пакетов программы
procedure EnumResourceModules(Func: TEnumModuleFunc; Data: Pointer);
procedure EnumResourceModules(Func: TEnumModuleFuncLW;Data: Pointer) ;
Реализует обратный вызов (callback) для всех ресурсных модулей программы
function Eof(var F): Boolean;
Возвращает True, если достигнут конец файла F (любого типа)
function Eoln (var F: Text):Boolean;
Возвращает True, если достигнут конец строки или конец текстового файла
procedure Erase(var F) ;
Уничтожает файл, связанный с переменной F
var ErrorAddr: Pointer;
Содержит адрес исполняемого оператора, вызвавшего ошибку при прогоне программы
var ErrorProc: Pointer;
Содержит адрес входа в умалчиваемый обработчик ошибок
var ExceptProc: Pointed;
Содержит адрес входа в низкоуровневый обработчик исключений
procedure Exclude(var S: Set of T; I:T) ;
Исключает элемент I из множества S
procedure Exit;
Завершает работу подпрограммы
var ExitCode: Integer;
Возвращает код завершения программы. В современных разработках используйте для
этих целей секцию finalization
var ExitProc: Pointer;
Содержит адрес входа в процедуру завершения программы
function Exp(X: Real): Real;
Возвращает X, где Exp - основание натурального логарифма
var FileMode: Byte;
Указывает режим файла, открытого процедурой Reset: 0 - только чтение; 1 - только
запись; 2 - чтение и запись
function FilePos(var F): Longint;
Возвращает текущую позицию в файле F
function FileSize(var F): Integer;
Возвращает размер нетекстового файла
procedure FillChar(var X; Count:Integer; Value: Byte);
Заполняет переменную X не более Count байтами со значением Value
procedure Finalize( var V ;Count: Integer ) ;
Освобождает динамически распределенную память, связанную с F
function FindClassHInstance(ClassType: TClass): Long Word;
Возвращает дескриптор модуля, в котором определен экземпляр класса ClassType
function FindHInstance( Address:Pointer): LongWord;
Возвращает дескриптор модуля, в котором определен адрес Address
function FindResourceHInstance (Instance: LongHord): LongWord;
Возвращает дескриптор ресурсного файла для экземпляра Instance
procedure Flush(var F: Text);
Записывает буфер файла на диск
function Frac(X: Extended): Extended;
Возвращает дробную часть х
procedure FreeMem(var P:Pointer; Size: Integer);
Освобождает динамически распределенную память, связанную с P
procedure GetDir(D: Byte; var S: String) ;
Возвращает имя диска по его номеру
procedure GetMem(var P: Pointer;Size: Integer);
Связывает с указателем P Size байт динамической памяти
procedure GetMemoryManager(var MemMgr: TMemoryManager);
Возвращает точку входа в менеджер динамической памяти
procedure Halt (ExitCode: Integer);
Прекращает выполнение программы с кодом завершения ExitCode
var HeapAllocFlags: Word = 2;
Указывает, какую память получает менеджер динамической памяти от операционной
системы (по умолчанию - gmemmoveable)
function Hi(X): Byte;
Возвращает старший байт 16-разрядного числа
function High(X);
Возвращает наивысшее значение диапазона порядкового типа, строки или открытого
массива, к которому принадлежит X
function IOResult: Integer;
Возвращает статус последней операции ввода-вывода
procedure Inc(var X ; N: LongInt );
Наращивает X на N, а если N отсутствует – на единицу
procedure Include(var S: Set of T; I:T);
Включает элемент I в множество S
procedure Initialize(var V ;Count: Integer );
Инициализирует динамически распределенную переменную V, если она была создана не
процедурой New
var Input: Text;
Системный файл ввода
procedure Insert(Source: String; var S: String; Index: Integers);
Вставляет подстроку Source в строку S начиная с символа Index
function Int(X: Extended): Extended;
Возвращает целую часть вещественной переменной
var IsConsole: Boolean;
Содержит True, если выполняется приложение консольного типа
var IsLibrary: Boolean;
Содержит True, если выполняется подпрограмма из DLL
function IsMemoryManagerSet: Boolean;
Возвращает True, если менеджер динамической памяти был изменен процедурой SetMemoryManager
var IsMultiThread: Boolean;
Содержит True, если в программе инициировано несколько потоков
var J I TEnable: Byte;
Указывает тип обработки исключения, возникающего в процессе отладки
function Length (S): Integer;
Возвращает длину строки
function Ln(X: Real): Real;
Возвращает натуральный логарифм X
function Lo(X): Byte;
Возвращает младший байт аргумента
function Low(X);
Возвращает наименьшее значение диапазона порядкового типа, строки или открытого
массива, к которому принадлежит X
var Mainlnstance: LongWord;
Содержит дескриптор экземпляра главного модуля программы
var MainThreadID: LongWord;
Содержит дескриптор главного потока для текущего модуля
const MaxInt = High(Integer);
Содержит максимальное значение типа Integer (2 147 483 647)
const MaxLongint = High(Longint);
Содержит максимальное значение типа LongInt (2 147 483 647)
procedure MkDir(S: String);
Создает новый каталог
procedure Move(const Source; var Dest; Count: Integers);
Переносит не более Count байт из источника Source в приемник Dest
procedure New(var P: Pointer);
Создает новую динамическую переменную и связывает ее с P
var NoErrMsg: Boolean=False;
Указывает, будут ли ошибки периода исполнения программы сопровождаться
сообщениями (False - будут)
var Null: Variant;
Используется для указания значения Null переменной вариантного типа
function Odd(X: Longint): Boolean;
Возвращает True, если аргумент - нечетное число
procedure OleStrToStrVar (Source: PWideChar; var Dest: String);
Копирует “широкую” (двухбайтную) строку в обычную строку Object Pascal
function Ord(X): Longint;
Возвращает порядковое значение переменной порядкового типа (в том числе и Int64)
var Output: Text;
Файл системного вывода для консольных приложений
function ParamCount: Integer;
Возвращает количество параметров запуска программы
function ParamStr(Index: Integer) : String;
Возвращает параметр запуска под номером Index (для Index=0 - имя исполняемого файла)
function Pi: Extended;
Возвращает число Pi=3,141592653589793
function Pos(Substr: String; S:String): Integer;
Возвращает номер символа, начиная с которого в строке S располагается подстрока Substr
function Pred(X) ;
Возвращает предыдущее значение для выражения X порядкового типа
function Ptr(Address: Integer):Pointer;
Преобразует Address в указатель
var RandSeed: LongInt;
Определяет стартовое значение для генератора псевдослучайных последовательностей
function Random( Range: Integer) ;
Возвращает очередное псевдослучайное число.
procedure Randomize;
Инициирует генератор псевдослучайных последовательностей.
procedure Read(var F / VI / V2,...,Vn);
Читает из файла F заданное количество значений и помещает их в переменные Vi
procedure Readin(var F: Text; VI, V2, . . ., Vn) ;
Читает из файла F заданное количество строк и помещает их в переменные Vi
procedure ReallocMem(var P:Pointer; Size: Integers);
Изменяет размер динамической переменной
procedure Rename(var F; Newname:String) ;
procedure Rename(var F; Newname:PChar) ;
Переименовывает файл, связанный с файловой переменной F
procedure Reset(var F : File;RecSize: Word ) ;
Открывает существующий файл для чтения и/или записи
procedure Rewrite(var F: File;Recsize: Word ) ;
Создает новый файл и открывает его для записи
procedure RmDir(S: Strings);
Удаляет пустой каталог S
function Round(X: Extended):Int64;
Округляет вещественное число до ближайшего целого
procedure RunError ( ErrorCode:Byte ) ;
Останавливает работу программы и сообщает код ошибки
procedure Seek(var F; N: LongInt) ;
Пропускает N байт от начала файла
function SeekEof (var F: Text): Boolean;
Пропускает все байты до конца файла
function SeekEoln (var F: Text): Boolean;
Пропускает все символы текстового файла до конца текущей строки
procedure Set8087CW(NewCW: Word);
Временно устанавливает новый режим работы арифметического сопроцессора
procedure SetLength(var S;NewLength: Integer);
Устанавливает новую длину строки или динамического массива
procedure SetMemoryManager (const MemMgr: TMemoryManager);
Устанавливает новый менеджер динамической памяти
procedure SetString(var S: String; Buffer: PChar; Len: Integer) ;
Копирует Len символов буфера Buffer в строку S
procedure SetTextBuf(var F: Text;var Buf ; Size: Integer );
Устанавливает внешний буфер Buf длиной Size символов для файловой переменной F
function Sin(X: Extended): Extended;
Возвращает синус аргумента (в радианах)
function SizeOf(X): Integer;
Возвращает длину переменной X в байтах
function Slice(var A: array;Count: Integer): array;
Возвращает открытый массив, содержащий первые Count элементов массива A
function Sqr(X: Extended): Extended;
Возвращает квадрат аргумента
function Sqrt(X: Extended): Extended;
Возвращает корень квадратный из аргумента
procedure Str(X : Width : Decimals; var S) ;
Преобразует X целого или вещественного типа в строку S с учетом длины Width и
количества знаков после запятой Decimals
function StringOfChar(Ch: CharCount: Integer): String;
Создает строку, состоящую из Count раз повторенного символа Ch
function StringToOleStr(const Source: String): PWideChar;
Копирует обычную строку в двухбайтную
function StringToWideChar(const Source: String; Dest: PWideChar;DestSize: Integer):
PWideChar;
Преобразует обычную строку в строку с символами UNICODE
function Succ(X) ;
Возвращает следующее значение для порядкового аргумента X
function Swap(X) ;
Меняет местами байты в 16-разрядном слове
function Trunc(X: Extended): Int64;
Преобразует вещественное число к целому путем отбрасывания дробной части
procedure Truncate(var F) ;
Отсекает оставшуюся часть файла до его конца
const Unassigned: Variant;
Используется для указания варианта с несвязанным (пустым) значением
function UpCase(Ch: Char): Char;
Преобразует строчный символ Ch в заглавный
procedure Val(S: String; var V;var Code: Integer);
Преобразует строковое значение в целую или вещественную величину
function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant;
Создает вариантный массив
function VarArrayDimCount(const A: Variant): Integer;
Возвращает количество измерении вариантного массива
function VarArrayHighBound(const A: Variant; Dim: Integer):Integers;
Возвращает верхнюю границу измерения вариантного массива
function VarArrayLock(var A:Variant): Pointers;
Блокирует вариантный массив и возвращает указатель на его данные
function VarArrayLowBound(const A: Variant; Dim: Integer): Integer;
Возвращает нижнюю границу измерения вариантного массива
procedure VarArrayRedim(var A:Variant; HighBound: Integer) ;
Перестраивает вариантный массив
function VarArrayRef(const A:Variant): Variant;
Преобразует вариантный массив к форме, необходимой при обращении к API- функциям
procedure VarArrayUnlock(var A:Variant) ;
Отменяет действие функции VarArrayLock
function VarAsType(const V: Variant; VarType: Integer): Variants;
Преобразует вариант к заданному типу
procedure VarCast(var Dest: Variant; const Source: Variant; VarType: Integer);
Преобразует вариант к заданному типу и сохраняет результат в Dest
procedure VarClear(var V : Variant) ;
Помещает в вариант пустое значение
procedure VarCopy(var Dest: Variant; const Source: Variants);
Копирует вариант-источник Source в вариант-приемник Dest
function VarFromDateTime(DateTime: TDateTime): Variants;
Преобразует дату-время в значение варианта
function VarIsArray(const V:Variant): Boolean;
Возвращает True, если V - вариантный массив
function VarIsEmpty(const V:Variant): Boolean;
Возвращает True, если вариант имеет пустое значение
function VarIsNull(const V: Variant) : Boolean;
Возвращает True, если вариант имеет значение Null
function VarToDateTime(const V:Variant): TDateTime ;
Преобразует вариант в значение дата-время
function VarToStr(const V: Variant) : String;
Преобразует вариант в строку.
function VarType(const V: Variant) : Integers;
Возвращает тип хранящегося в варианте результата
procedure WideCharLenToStrVar(Source: PWideChar; SourceLen:Integer; var Dest:
String);
Преобразует не более SourceLen символов строки UNICODE к обычной строке
function WideCharLenToString(Source: PWideChar; SourceLen:Integer): Strings;
Преобразует не более SourceLen символов строки UNICODE к обычной строке
procedure WideCharToStrVar(Source: PWideChar; var Dest:String);
Преобразует строку UNICODE к обычной строке
(там выполняются различные виды преобразования из одних типов
данных в другие.Рассказать основные.типа:IntToStr… и тд.Обязательно
упомянуть,что там есть функция Format. Обработчики
искл.ситуаций.Рассказать основные полезные
вещи.ФайнФёрст,ФайндНекст)
34.Функция Format. Строки форматирования.
В Delphi существует группа функций форматирования строк. Их рассмотрение начнем с
наиболее часто встречающегося представителя — функции Format:
function Format (const Format: string; const Args: array of const) : string;
Первым параметром функции выступает форматирующая строка. Это — обычная
текстовая строка, но в ней на нужных местах стоят специальные символы, которые
определяют, какие и как туда будут подставлены параметры.
Второй параметр функции Format называется списком аргументов. Он и содержит
"вставляемые" в форматирующую строку параметры. Обратите внимение, что этот
открытый массив имеет тип 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 цифр. Общее
число символов в
представлении числа
f
д
пm
Р
s
определяется полем ргес (по
умолчанию 15).
Элемент должен быть числом с
плавающей точкой. Он
Фиксиропреобразуется к виду -ddd.ddd.
ванный
Поле ргес означает количество
цифр после десятичной точки
(по умолчанию 2).
Элемент должен быть числом с
плавающей точкой. Он преобразуется к одному из двух
вышеперечисленных видов —
выбирается тот, который
обеспечивает более короткую
запись. Если требуемое число
цифр слева от десятичной точки
Обобщенный
меньше значения поля ширины
и само число больше 10" ,
применяется фиксированный
тип. Удаляются все впереди
идущие нули, десятичная точка
(при возможности), а также
символы, разделяющие строку
на группы по три цифры.
Совпадает с типом f за
исключением того, что после
каждой группы из трех цифр
ставятся разделительные
символы:d,ddd,
ddd.ddd....Элемент должен быть
числом с плавающей точкой,
Числовой
отражающей денежную сумму.
Денежный
Способ ее представления
зависит от значений глобальных
констант, определенных в
модуле SYSUTILS (см. ниже). В
свою очередь, они зависят от
настройки Windows на
обозначения, принятые в
данной стране.
Элемент должен быть
указателем. На выходе он будет
представлять из себя строку
Указатель
шестнадцатиричных цифр
видаХХХХ: YYYY, где ХХХХ
— селектор, YYYY —
смещение.
Элемент может иметь тип string,
PChar или быть отдельным
Строка
символом (Char). Эта строка
(или символ) вставляются
вместо спецификатора формата.
Если присутствует поле ргес и
длина строки больше его
значения, то она усекается.
Примечания:
1. Форматирующие символы можно задавать как в верхнем, так и в нижнем регистрах.
2. Тип преобразования m (денежный) тесно связан с правилами представления,
принятыми в конкретной стране. Для этого в модуле SYSUTILS определены значения
ряда типизированных констант, начальные значения которых берутся из секции [Inti]
файла WIN.INI. Вы можете изменять их значения в соответствии с вашими
потребностями:
Имя и тип
CurrencyString: string[7];
CurrencyFormat: Byte;
NegCurrFormat: Byte;
CurrencyDecimals: Byte;
ThousandSeparator: Char;
DecimalSeparator: Char;
Что означает
Символ (или символы), добавляемые к строке
с представлением денежной суммы и
обозначающие национальную денежную
единицу. Это может быть, например, '$', 'DM'
или 'руб'.
Определяет способ добавления знака
денежной единицы к строке. Число 1 будет
преобразовано так в зависимости от значения
этого параметра: '$!' при 0; '1$' при 1; '$ Г при
2; '1 $' при 3.
Определяет способ добавления знака
денежной единицы и минуса к строке в том
случае, если происходит преобразование
отрицательного числа (к положительным
числам отношения не имеет). Число -1 будет
преобразовано так в зависимости от значения
этого параметра: '($!)' при 0; '-$!' при 1; '$-1'
при 2; '$!-' при 3; '(!$)' при 4; '-!$' при 5; 'i-$'
при 6; '!$-' при 7; '-1 $' при 8; '-$ Г при 9; '$ 1-'
при 10.
Число знаков после запятой в представлении
денежных сумм. Например, число 10.15 при
разных значениях этого параметра
отобразится так: '$10' при 0, '$10.15' при 2,
'$10.1500' при 4.
Символ, разделяющий строку на группы по
три цифры справа налево (разделитель тысяч).
Применяется, в частности, в типе
преобразования п.
Символ, отделяющий дробную часть числа от
целой.
Мы закончили рассмотрение символа типа преобразования. Рассмотрим остальные
составные части спецификатора формата.
Поле ширины устанавливает минимально допустимое количество символов в
преобразованной строке. Это означает, что если она короче, чем задано в этом поле, то
происходит добавление пробелов до требуемого количества. По умолчанию пробелы
добавляются спереди (выравнивание по правому краю), но признак выравнивания по
левому краю ("-"; он должен стоять перед полем ширины) позволяет выравнивать строку
по-иному.
Поле индекса позволяет динамически изменить последовательность извлечения
аргументов из массива. Обычно аргументы извлекаются последовательно, по мере их
востребования спецификаторами формата. Поле индекса означает, что следующим нужно
извлечь аргумент с данным индексом. Пользуясь индексом, одни и те же аргументы могут
быть использованы многократно. Например, вызов Format ( ' %s %s %0 : s %s ', [ 'Yes', 'No'
] ) Даст на выходе строку 'Yes No Yes No'.
Поле точности играет разную роль в зависимости от того, с каким типом преобразования
применяется. Его особенности приведены вместе с описанием типа (с теми из них, где оно
имеет смысл).
Поля индекса, ширины и точности могут быть заданы напрямую или косвенно. Первый
способ подразумевает явное указание значения поля (например, ' %10 . 5f '). Для
косвенного задания значений нужно в соответствующих местах цифры заменить
звездочками (например, '%*.*f'). В этом случае вместо звездочек будут подставлены
следующие значения из списка аргументов (они обязательно должны быть целыми
числами). Например, выражение
Format('Value is %*.*',[10,5, 2.718]);
эквивалентно:
Format('Value is %10.5',[2.718]);
Как уже упоминалось, наиболее употребимой функцией из группы форматирующих
является Format, которая работает со строками типа string и максимально освобождает
программиста от рутинной работы. Другие функции используют то же ядро и правила
преобразования, но отличаются параметрами:
To же, что и Format, но оформлено в виде
procedure FmtStr(var Result: string; const
процедуры. Результат преобразования
Format: string; const Args: array of const);
возвращается в параметре Result.
Форматирующая строка должна находиться в
параметре Format, а результирующая
function StrFmt(Buffer, Format: PChar;
помещается в буфер Buffer (он должен иметь
const Args: array of const): PChar;
нужную длину). Функция возвращает
указатель на Buffer.
Работает как StrFmt, но длина резульfunction StrLFmt(Buffer: PChar;
MaxLen: Cardinal; Format: PChar; const тирующей строки не будет превышать
MaxLen символов.
Args: array of const) : PChar;
Форматирующая строка находится в буфере
function FormatBuf(var Buffer; BufLen: Format длиной FmtLen, a результирующая — в
буфере Buffer длиной BufLen. Функция
Cardinal; const Format; FmtLen:
возвращает число реально помещенных в
Cardinal; const Args: array of const) :
Buffer символов, причем оно всегда меньше
Cardinal;
или равно BufLen.
(Ксю)
В Delphi существует группа функций форматирования строк. Их рассмотрение начнем с
наиболее часто встречающегося представителя — функции Format:
function Format (const Format: string; const Args: array of const) : string;
Первым параметром функции выступает форматирующая строка. Это — обычная
текстовая строка, но в ней на нужных местах стоят специальные символы, которые
определяют, какие и как туда будут подставлены параметры.
Второй параметр функции Format называется списком аргументов. Он и содержит
"вставляемые" в форматирующую строку параметры. Обратите внимение, что этот
открытый массив имеет тип 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, но элемент
Шестнадцапреобразуется в строку
тиричный
шестнадцатиричных цифр.
е
f
д
пm
Элемент должен быть числом с
плавающей точкой. Он
преобразуется к
нормализованному виду d.dddE+ddd. Перед десятичной
точкой всегда находится одна
цифра (при необходимости, с
Научный
минусом). После символа "Е"
идет показатель степени,
имеющий знак и состоящий не
менее чем из 3 цифр. Общее
число символов в
представлении числа
определяется полем ргес (по
умолчанию 15).
Элемент должен быть числом с
плавающей точкой. Он
Фиксиропреобразуется к виду -ddd.ddd.
ванный
Поле ргес означает количество
цифр после десятичной точки
(по умолчанию 2).
Элемент должен быть числом с
плавающей точкой. Он преобразуется к одному из двух
вышеперечисленных видов —
выбирается тот, который
обеспечивает более короткую
запись. Если требуемое число
цифр слева от десятичной точки
Обобщенный
меньше значения поля ширины
и само число больше 10" ,
применяется фиксированный
тип. Удаляются все впереди
идущие нули, десятичная точка
(при возможности), а также
символы, разделяющие строку
на группы по три цифры.
Совпадает с типом f за
исключением того, что после
каждой группы из трех цифр
ставятся разделительные
символы:d,ddd,
ddd.ddd....Элемент должен быть
Числовой
числом с плавающей точкой,
Денежный
отражающей денежную сумму.
Способ ее представления
зависит от значений глобальных
констант, определенных в
модуле SYSUTILS (см. ниже). В
свою очередь, они зависят от
настройки Windows на
Р
Указатель
s
Строка
обозначения, принятые в
данной стране.
Элемент должен быть
указателем. На выходе он будет
представлять из себя строку
шестнадцатиричных цифр
видаХХХХ: YYYY, где ХХХХ
— селектор, YYYY —
смещение.
Элемент может иметь тип string,
PChar или быть отдельным
символом (Char). Эта строка
(или символ) вставляются
вместо спецификатора формата.
Если присутствует поле ргес и
длина строки больше его
значения, то она усекается.
Примечания:
1. Форматирующие символы можно задавать как в верхнем, так и в нижнем регистрах.
2. Тип преобразования m (денежный) тесно связан с правилами представления,
принятыми в конкретной стране. Для этого в модуле SYSUTILS определены значения
ряда типизированных констант, начальные значения которых берутся из секции [Inti]
файла WIN.INI. Вы можете изменять их значения в соответствии с вашими
потребностями:
Имя и тип
CurrencyString: string[7];
CurrencyFormat: Byte;
NegCurrFormat: Byte;
CurrencyDecimals: Byte;
Что означает
Символ (или символы), добавляемые к строке
с представлением денежной суммы и
обозначающие национальную денежную
единицу. Это может быть, например, '$', 'DM'
или 'руб'.
Определяет способ добавления знака
денежной единицы к строке. Число 1 будет
преобразовано так в зависимости от значения
этого параметра: '$!' при 0; '1$' при 1; '$ Г при
2; '1 $' при 3.
Определяет способ добавления знака
денежной единицы и минуса к строке в том
случае, если происходит преобразование
отрицательного числа (к положительным
числам отношения не имеет). Число -1 будет
преобразовано так в зависимости от значения
этого параметра: '($!)' при 0; '-$!' при 1; '$-1'
при 2; '$!-' при 3; '(!$)' при 4; '-!$' при 5; 'i-$'
при 6; '!$-' при 7; '-1 $' при 8; '-$ Г при 9; '$ 1-'
при 10.
Число знаков после запятой в представлении
ThousandSeparator: Char;
DecimalSeparator: Char;
денежных сумм. Например, число 10.15 при
разных значениях этого параметра
отобразится так: '$10' при 0, '$10.15' при 2,
'$10.1500' при 4.
Символ, разделяющий строку на группы по
три цифры справа налево (разделитель тысяч).
Применяется, в частности, в типе
преобразования п.
Символ, отделяющий дробную часть числа от
целой.
Мы закончили рассмотрение символа типа преобразования. Рассмотрим остальные
составные части спецификатора формата.
Поле ширины устанавливает минимально допустимое количество символов в
преобразованной строке. Это означает, что если она короче, чем задано в этом поле, то
происходит добавление пробелов до требуемого количества. По умолчанию пробелы
добавляются спереди (выравнивание по правому краю), но признак выравнивания по
левому краю ("-"; он должен стоять перед полем ширины) позволяет выравнивать строку
по-иному.
Поле индекса позволяет динамически изменить последовательность извлечения
аргументов из массива. Обычно аргументы извлекаются последовательно, по мере их
востребования спецификаторами формата. Поле индекса означает, что следующим нужно
извлечь аргумент с данным индексом. Пользуясь индексом, одни и те же аргументы могут
быть использованы многократно. Например, вызов Format ( ' %s %s %0 : s %s ', [ 'Yes', 'No'
] ) Даст на выходе строку 'Yes No Yes No'.
Поле точности играет разную роль в зависимости от того, с каким типом преобразования
применяется. Его особенности приведены вместе с описанием типа (с теми из них, где оно
имеет смысл).
Поля индекса, ширины и точности могут быть заданы напрямую или косвенно. Первый
способ подразумевает явное указание значения поля (например, ' %10 . 5f '). Для
косвенного задания значений нужно в соответствующих местах цифры заменить
звездочками (например, '%*.*f'). В этом случае вместо звездочек будут подставлены
следующие значения из списка аргументов (они обязательно должны быть целыми
числами). Например, выражение
Format('Value is %*.*',[10,5, 2.718]);
эквивалентно:
Format('Value is %10.5',[2.718]);
Как уже упоминалось, наиболее употребимой функцией из группы форматирующих
является Format, которая работает со строками типа string и максимально освобождает
программиста от рутинной работы. Другие функции используют то же ядро и правила
преобразования, но отличаются параметрами:
To же, что и Format, но оформлено в виде
procedure FmtStr(var Result: string; const
процедуры. Результат преобразования
Format: string; const Args: array of const);
возвращается в параметре Result.
Форматирующая строка должна находиться в
параметре Format, а результирующая
function StrFmt(Buffer, Format: PChar;
помещается в буфер Buffer (он должен иметь
const Args: array of const): PChar;
нужную длину). Функция возвращает
указатель на Buffer.
Работает как StrFmt, но длина резульfunction StrLFmt(Buffer: PChar;
MaxLen: Cardinal; Format: PChar; const тирующей строки не будет превышать
MaxLen символов.
Args: array of const) : PChar;
Форматирующая строка находится в буфере
function FormatBuf(var Buffer; BufLen: Format длиной FmtLen, a результирующая — в
буфере Buffer длиной BufLen. Функция
Cardinal; const Format; FmtLen:
возвращает число реально помещенных в
Cardinal; const Args: array of const) :
Buffer символов, причем оно всегда меньше
Cardinal;
или равно BufLen.
1 function Format ( Const Formatting : string; Const Data : array of const ) : string;
2 function Format ( Const Formatting : string; Const Data : array of const; FormatSettings
: TFormatSettings ) : string;
Функция Format обеспечивает 'C' подобное форматирование множества простых типов
данных в строке. Она обеспечивает очень точное управление по этому форматированию.
Параметр Formatting определяет, как массив Data Данных управляется в возвращенной
строке.
Форматируемая cтрока может включать соединение обычных символов (которые
передаются неизменными в строку результата), и символам форматирования данных.
Такое форматирование лучше всего объясняется кодом примера.
В простых условиях каждые данные, форматирующие подстроку начинаются с % и
заканчиваются индикатором типа данных:
d = Десятичное (целое число)
e = Научный
f = Установленный
g = Генерал
m = Деньги
n = Число (плавающее)
p = Указатель
s = Строка
u = Десятичное число без знака
x = Шестнадцатеричный
Общий формат форматирования каждой подстроки следующий:
%[Index:][-][Width][.Precision]Type
где квадратные скобки относятся к дополнительным параметрам, и :. - символы литералы, первые 2 из которых используются, чтобы идентифицировать два из
дополнительных параметров.
Версия 2 этой функции - для использования в пределах потоков. Вы должны заполнить
записьFormatSettings перед вызовом запроса. Она берёт местную копию глобальной
переменной, форматирующей переменные, которые делают подпрограмму потокобезопасной.
35.Модуль Classes. Основные стандартные классы.
Тип TBits Объект, который может содержать бесконечное число Булевых значений
Тип TList Универсальный контейнер списков объектов Тип TStringList Содержит
список переменной длины, состоящий из строк
Рассказать о классах и их потомках
http://www.delphisources.ru/pages/faq/faq_delphi_basics/navClasses.php.html
Функция Bounds
Передаёт координаты вершин объекта типа TRect (прямоугольник)
function Bounds ( const Top, Left, Width, Height : Integer ) : TRect;
Функция Bounds передаёт координаты вершин объекта типа TRect (прямоугольник).
Указываются 2 вершины - правая верхняя и левая нижняя.
Функция Point
Генерирует значение TPoint из значений X и Y
function Point ( const X, Y : Integer ) : TPoint;
Функция Point берет параметр X и Y и возвращает значение TPoint, содержащее их.
Функция PointsEqual
Сравнивает два значения TPoint на предмет равенства
function PointsEqual ( const Point1, Point2 : TPoint ) : Boolean;
Функция PointsEqual сравнивает значения параметров Point1 иPoint2, и возвращает
Истину (True), если они равны.
Функция Rect
Создаёт величину TRect с указанием 2 точек или 4 координат
1 function Rect ( Left, Top, Right, Bottom : Integer ) : TRect;
2 function Rect ( TopLeft, BottomRight : TPoint ) : TRect;
Delphi функция Rect создает объектTRect (прямоугольник), для создания прямоугольника
необходимо указать 4 координаты или 2 точки.
При создании с указанием двух точек TopLeft и BottomRight, вы можете передать две
величиныTPoint или использовать функциюPoint.
Примечания
В модулях Classes и Types есть две функции Rect. Только прежняя поддерживает второй
синтаксис.
При использовании в вашем коде обоих из этих модулей, и Вы
указали Types после Classes, то вы должны использовать Rect с прификсом Classes, чтобы
использовать этот второй синтаксис.
Тип TBits
Объект, который может содержать бесконечное число Булевых значений
Тип TBits содержит гибкий набор битов (Булевых значений). Размер набора может быть
изменен в любое время (используя свойство size).
К битам обращаются, используя свойство Bits, подобно этому:
flag := myBits.Bits[2];
или, ещё прощё:
flag := myBits[2];
Есть одна сервисная функция -OpenBit, которая возвращает индекс первого ложного
значения. Подобной функции для поиска истинного значения нет.
Для хранения списка указателей на размещенные в адресном пространстве структуры
(объекты, динамические массивы, переменные) предназначен класс TList. Так же, как и
список строк TStringList, список указателей обеспечивает эффективную работу с
элементами списка.
Основой класса TList является список указателей. Сам список представляет собой
динамический массив указателей, к которому можно обратиться через индексированное
свойство
Так как элементы списка являются указателями на некоторые структуры, прямое
обращение к составным частям этих структур через свойство Items невозможно.
Примечание
В списке могут содержаться указатели на разнородные структуры. Не обязательно
хранить в списке только указатели на объекты или указатели на записи.
Реализованные в классе TList операции со списком обеспечивают потребности
разработчика и совпадают с операциями списка строк.
Списки используются для поддержки индексов объектов любого классового типа. Сам
индекс хранит указатели на объекты и является динамическим — его размер можно
изменить во время выполнения. Благодаря своей способности следить за объектами
любого типа, TList является гибким, но требующим большего программирования по
сравнению с другими классами из списка VCL, которые разработаны для обработки
конкретных типов объектов (подобных TStrings и TImageList). TList не следует путать с
полномасштабной коллекцией классов или связанными списками, поскольку он не
обрабатывает распределение или высвобождение памяти для индексируемых объектов. В
случае выбора TList выполнение всей черновой работы придется отвечать
самостоятельно.
Хотя внутренняя работа TList связана с интенсивной и отвратительной манипуляцией
указателями, Delphi часто защищает программиста от такой реализации, предоставляя
удобные свойства и методы доступа к списку. Однако некоторые
методы TList принимают в качестве параметров общие типы указателей. В таких случаях
помните о том, что поименованные объекты являются косвенными указателями, поэтому
в дальнейших манипуляциях нет необходимости. Если вы увидите параметр типа Pointer,
то можете использовать его так, будто это тип TObject. Часто TList можно обрабатывать
как массив указателей, используя свойство Items для доступа к отдельным элементам
списка с помощью индексной записи.
С другой связанной с использованием указателей проблемой можно столкнуться из-за
того, что TList применяет обобщенные указатели. Может потребоваться приведение
указателей, возвращаемых методами TList, к конкретному индексируемому объекту.
TList и его потомок TPopupList.
TList
TList является базовым классом для списков. TList реализует множество методов и
свойств, которые используются для манипулирования списком, для доступа и
модифицирования элементов списка и для сбора информации о списке и его элементах.
TPopupList является списком, который поддерживает элементы, видимые во
всплывающем (pop-up) меню. Хотя методы Add и Remove спискаTPopupList настроены
на работу с элементами всплывающего меню, концептуальное действие этих методов
такое же, как и одноименных методов вTList.
Тип TList
Универсальный контейнер списков объектов
Класс TList очень полезный универсальный контейнер списков. Он отличается от
массивов, в которых он обеспечивает более богатые функциональные возможности.
В частности объекты TList могут быть отсортированы. Эта сортировка может быть с
использованием любых выбранных критериев. Например, список может содержать набор
объектов, которые имеют строку и численные поля. Вы можете отсортировать список по
строке, по числу, по обоим, с возрастанием или убыванием, как Вы желаете. И
пересортировать позже по другим критериям.
Ключевые свойства и методы упомянуты ниже.
Свойство Capacity
Используется для установления размера (число указателей на объекты) списка.
Предварительно установив в разумное значение, можно избежать множественных
перераспределений памяти.
Свойство Count
Число элементов (указателей) в списке. Может быть прочитано или записано. Если размер
уменьшен в результате изменения значения Count, то удаляются элементы в конце
списка.
Свойство Items
Позволяет обращаться к элементам в списке. Например, myList.Items[2]; возвращает 3-ий
элемент в списке. Это свойство, заданное по умолчанию, вышеупомянутое может быть
упрощено до myList[2];.
Свойство List
Возвращает элементы в массиве.
Метод Add
Добавляет элемент в конец списока.
Метод Assign
Заменяет список содержанием другого списка.
Метод Clear
Удаляет все элементы списка, устанавливая Count в 0.
Метод Delete
Удаляет элемент из списка по его позиции в списке.
Метод Remove
Удаляет элемент из списка по его объектному указателю.
Метод Exchange
Меняет позиции двух элементов
Метод Move
Перемещает элемент в новую позицию списка.
Метод Insert
Вставляет новый элемент в список в данную позицию.
Метод First
Получает первый элемент в списке.
Метод Last
Получает последний элемент в списке.
Метод Sort
Сортирует список в соответствии с вашими указанными критериями. Сортировка списка
проводится внутри TList, но каждая пара элемента сравнивается, вызывая функцию,
которую вы указали для этого метода.
Метод IndexOf
Выдает позицию указанного объекта в списке.
Класс TStrings — это абстрактный классовый тип, который не используется
непосредственно, однако поддерживает создание производных классов, оперирующих со
строковыми данными. Наиболее общим производным от TStrings является TStringsList,
который используется для большинства обобщенных строковых операций. В данной главе
вначале рассматриваются детали класса TStrings, а затем описываются производные
классы TStrings.
Хотя класс и называется TStrings, но он сконструирован для выполнения более общего
множества операций, нежели простое манипулирование строками. Фактически TStrings
не оперирует со строками вообще. Скорее, его работа заключается в действиях с
коллекциями строк. В этом смысле TStrings является абстрактным контейнерным
классом, и все порожденные от него классы обеспечивают детали реализации,
предоставляющие возможность методам, определенным в TStrings, выполнять свою
работу. Часто аргумент типа TStrings специфицируется просто для того, чтобы функция
могла полиморфически оперировать с различными производными TStrings. Одним из
самых интересных и потенциально полезных механизмов TStrings является его
способность функционировать в качестве ассоциированного списка или словаря, в
котором различные строковые входы действуют в качестве ключевых имен, которые
можно задействовать для извлечения любого объекта по его имени.
Каждый строковый вход в производных классах TStrings способен содержать не
только строку, но и ссылку на любой объект Delphi. Данный механизм представляет
собой то, что позволяет производным класса TStrings функционировать подобно
элементарным символьным таблицам. Такая функциональность часто используется
различными элементами управления для обеспечения механизма поддержки рисования
владельцем. В данном случае ссылка на объект указывает на битовый образ, подлежащий
отрисовке, в сочетании с текстом, который появляется в определенном списке. Наиболее
фундаментальным производным классом TStrings является TStringList. Данный класс
перекрывает все необходимые виртуальные функции TStrings для того, чтобы
реализовать желаемое поведение списка строк, способного хранить объекты. Если
потратить некоторое время на глубокое изучение TStrings и TStringList, появятся
предпосылки для лучшего понимания некоторых тонких вещей, которые можно
выполнить посредством этого полезного семейства классов.
Вот как выглядят общедоступные объявления класса TStrings. Обратите внимание на
большое количество виртуальных функций. Это обстоятельство подсказывает, что
большая часть детальной работы будет реализована в производных классах.
TComboBoxStrings
Класс TComboBoxStrings используется всеми управляющими классами,
порожденными от TCustomComboBox. Среди них — TComboBox,
TDriveComboBox и TDBComboBox. Методы, определенные именно этим
производным TStrings, работают в сочетании с элементом управления комбинированным
окном Windows для обеспечения функциональности, которую ожидают от любого класса
подобного рода. TComboBoxStrings по функциям похож на класс TListBoxStrings за
исключением того, что базовый элемент управления Windows является комбинированным
окном, а не окном списка.
THeaderStrings
Класс THeaderStrings определен для использования компонента THeader и
соответствует свойству компонента THeader. Другими словами, свойство Sections
компонента THeader имеет тип TheaderStrings. Размер и содержимое
списка THeaderStrings в большой степени определяют то, что появляется в компоненте
THeader, когда он отображается в родительской форме.
Класс TListBoxStrings используется классом TCustomListBox для управления списком
элементов, представленного классами, порожденными отTCustomListBox. Это
классы TListBox и TFileListBox. Обратите внимание, что каждый из методов данного
класса перекрывает виртуальную функцию, определенную в TStrings. Данные функции
внутренне сконструированы для такого взаимодействия, чтобы извлекать или запоминать
требуемую информацию из соответствующего элемента управления Windows. Это
отличается от работы TStringList, который создает и поддерживает требуемую память для
списка элементов самостоятельно. В данном случае Delphi полагается на поведение,
обеспечиваемое для соответствующей функциональности внутренними механизмами
Windows.
Класс TMemoStrings обеспечивает строковый основанный на списке интерфейс для
стандартного элемента управления редактированием Windows. Отдельные строки
элемента управления редактированием отображаются в индивидуальные элементы в
списке строк. Обратите внимание на то, что в определении данного класса нет поддержки
хранения и извлечения ссылок на объекты, как это имеет место в
классах TListBoxStrings иTComboBoxStrings, поскольку окно редактирования вообще
предназначено для хранения только текстовой информации.
TStringGridSTrings
Класс TStringGridSTrings используется TStringGrid для обеспечения поддержки
базового класса памяти для строк, содержащихся в ячейках компонента TStringGrid.
Объект TStringGrid обеспечивает свойства индексирования членов Cols и Rows, которые
возвращают ссылки на объекты типа TStringGridSTrings. Используя подобную
технологию, можно получить список строк, представляющий как строки, так и колонки
компонента TStringGrid, и манипулировать содержимым как и у любого другого потомка
TStrings.
Строковый тип данных широко используется программистами. Во-первых, многие данные
действительно необходимо представлять при помощи этого типа. Во-вторых, множество
функций преобразования типов позволяют представлять числовые типы в виде строк,
избегая тем самым проблем с несовместимостью типов.
Класс TStrings является базовым классом, который обеспечивает потомков основными
свойствами и методами, позволяющими создавать работоспособные списки строк. Его
прямым предком является класс TPersistent.
Класс TStrings реализует все вспомогательные свойства и методы, которые обеспечивают
управление списком. При этом методы, непосредственно добавляющие и удаляющие
элементы списка, не реализованы и объявлены как абстрактные.
ВниманиеПопытка прямого использования в приложении экземпляра класса TStrings
вызовет ошибку применения абстрактного класса на этапе выполнения программы, а
именно при попытке заполнить список значениями. Простая замена типа объектной
переменной списка на TStringList делает приложение полностью работоспособным без
какого-либо дополнительного изменения исходного кода.
Классы-наследники должны перекрывать методы добавления и удаления элементов
списка. Реализованный в Delphi класс TStringList практически полностью повторяет
функциональность предка, добавляя лишь несколько новых свойств и методов. Поэтому
мы не станем останавливаться подробнее на классе TStrings, а перейдем сразу к его
работоспособному потомку TStringList.
Класс TStringList обеспечивает реальное использование списков строк в приложении. По
существу, класс представляет собой оболочку вокруг динамического массива значений
списка, представленного свойством Strings.
Тип TStringList
Содержит список переменной длины, состоящий из строк
TStringList - полезный тип класса. Он чрезвычайно полезен для многих видов обработок
списков. Элементы в строковом списке могут быть вставлены, перемещены и
отсортированы.
Список может быть сформирован строка за строкой, или загружен из большой строки
разделенной запятой или даже из текстового файла.TStringList происходит от TStrings.
Вы можете использовать и TStrings, но это не рекомендуется, так как он не полный некоторые из его методов абстрактны. TStringList осуществляет эти абстрактные методы
(Clear, Delete и Insert).
Мы рассмотрим основные свойства и методы TStringList, включая полученные
из TStrings.
Свойство Count
Возвращает число строк в списке.
Свойство Capacity
Устанавливает или получает текущую вместимость строкового списка. Вы можете
управлять этой вместимостью по необходимости.
Свойство Strings
Получает или корректирует строку по данному индексу в списке (первый элемент списка
имеет индекс 0).
Обратите внимание, что свойствоStrings является свойством, заданным по умолчанию.
Это означает, что вы можете использовать его без его указания:
myName := names.Strings[4];
является эквивалентным:
myName := names[4];
Свойство Text
Устанавливает или получает список в виде большой строки. Эта строка будет содержать
каждую строку закончивающуюся комбинацией символов перевода каретки и перевода
строки (CRLF). Полезно для загрузки из визуального объекта, который может содержать
многочисленные строки текста.
Свойство CommaText
Получает или устанавливает список в виде большой строки. Эта строка будет иметь
список строк разделенных запятыми. Это полезно для загрузки из экспорта текстовой
электронной таблицы. Если при получении строка содержит вложенные пространства, то
она будет заключена в двойные кавычки.
Cвойство DelimitedText
Получает или устанавливает список через большую строку. Эта строка содержит список
строк разделенных значением Delimiter (по умолчанию - запятая). Строки, содержащие
вложенные пробелы должны быть заключены в QuoteChar (по умолчанию - ").
Свойство QuoteChar
Используется для замыкания строк, которые имеют вложенные пробелы при
использовании DelimitedText.
Свойство Delimiter
Используется для разделения строк при использовании DelimitedText
Свойство Names
Строки в строковом списке могут быть обработаны, как пары название/значение, как во
втором примере кода. Каждая строка не должна иметь никаких внедренных пробелов, и
содержать знак =. Это очень полезное понятие. См. свойства Value и ValueFromIndex, и
метод IndexOfName.
Свойство Values
Возвращает значение для данного названия, когда используются строки пары
название/значение (см. выше).
Свойство ValueFromIndex
Возвращает значение по индексу строки (начинается с 0), когда используются пары
название/значение.
Свойство CaseSensitive
Когда true, Delphi обрабатывает строки чувствительно к регистру при выполнении
некоторых операций, таких как Sort.
Свойство Duplicates
Это свойство может иметь одно из следующих перечислимых TDuplicatesзначений:
dupIgnore Игнорирует (отбрасывает) дубликаты
dupAccept Позволяют дубликаты
dupError Выбрасывает исключение, если имеются дубликаты
Свойство Sorted
Когда true, все строки будут добавляться в свою позицию отсортированной
последовательности. Когда false, они будут добавляться в конец. См. также метод Sort.
Свойство Objects
Возвращает объект, связанный со строкой по данному индексу, если он существует.
Метод Add
Добавляет данную строку в список, возвращая ее позицию в списке (начинается с 0).
Метод Append
Так же как и Add, но без возвращения индексного значения.
Метод Insert
Вставляет строку в заданную индексом позицию. Позиция 0 вызовет вставку в начало.
Метод Delete
Удаляет строку по данному индексу.
Метод Clear
Удаляет все строки из списка.
Метод Move
Перемещает строку из одной позиции в другую, сдвигая другие строки соответственно.
Метод Exchange
Перестанавливает две строки в списке, идентифицированные по их индексным позициям.
Метод IndexOf
Получает индекс позиции строки в списке соответствующей данной строке. Если строка
не найдена, то возвращается -1.
Метод IndexOfName
Получает индекс позиция первой пары название/значение строки, где название
соответствует данной строке. Если не найдена - возвращается -1.
Метод Find
То же самое, что и IndexOf, но с использованием списков сортированных строк.
Метод Sort
Если Sorted является ложным, то это вызовет сортировку списка.
Метод AddStrings
Добавляет строки из другого списка.
Метод Assign
Заменяет текущий список содержанием другого списка.
Метод LoadFromFile
Очень полезный метод, загружает строковый список из текстового файла. Каждая
текстовая строка (законченая CRLF - см.DelimitedText) становится строкой списка.
Метод SaveToFile
Сохраняет строковый список в текстовый файл.
TCollection и TCollectionltem необходимы для поддержки работоспособности ряда
элементов управления Windows, не являющимися родными для среды Windows и
поддерживающими список элементов. Распространенным примером является линейка
состояния, которая, как правило, разделена на несколько областей, содержащих
различные текстовые сообщения, относящиеся к состоянию текущих операций
определенной программы. Линейка состояния не является фундаментальным типом
элементов управления Windows, каким является окно списка, однако определяется Delphi
VCL. Такого же рода примерами являются
объекты TListColumns и TDBGridColumns, которые используются для поддержки
других элементов управления в Delphi VCL. Такие элементы управления, не родные для
Windows и требующие управления списком объектов, для корректной организации
данных по соглашению должны использовать классы TCollection и TCollectionltem.
Класс TCollection похож на другие классы, определенные в Delphi, которые
управляют списками объектов, в частности — на TList и TStrings.
КлассTCollection используется для хранения объектов типа TCollectionltem.
TCollection является абстрактным классом и непосредственно не задействуется.
Примером порожденного класса является коллекция TStatusPanels, которая размещена в
определении элемента управления TStatusBar.
36.Класс TList. Основные методы. Использование в программах на
Delphi.
Класс Список (TList)
Он представляет собой массив указателей на объекты произвольных типов. С помощью
класса TList можно добавлять в список строк новые объекты, удалять их, находить,
сортировать и переупорядочивать.
Свойства класса TList перечислены ниже
Capacity
Размер массива указателей, определяющий отводимую для него память
Count
Текущее число элементов в списке
Items
Свойство для обращения к конкретному объекту по номеру
List
Массив указателей на объекты. Лучше использовать свойство Items
Методы класса TList
function Addjltemr Pointer): Integer;
Добавление нового элемента
procedure Clear;
Удаление всех элементов
procedure Delete(Index: Integer);
Удаление конкретного элемента
procedure Exchange(Indexl, Index2: Integer);
Обмен двух элементов местами
function Expand: TList;
Расширение объема списка на 4-8-16 элементов в зависимости от его текущего размера
function IndexOf(Item: Pointer): Integer;
Номер в списке элемента, на который ссылается указатель Item. Нумерация начинается с
нуля
procedure Insert(Index: Integer; Item: Pointer);
Вставка нового элемента
procedure Move(CurIndex. Newlndex: Integer);
Перемещение элемента из позиции Cutlndex в позицию Newlndex
procedure Pack;
Удаление всех элементов, имеющих значение nil, то есть удаление пустых элементов, не
ссылающихся на объекты
function Remove (Item: Pointer): Integer;
Удаление элемента, который определяется первым совпадением параметра Item
(указатель) со значением указателя в списке
Для сортировки элементов списка применяется процедура Sort В качестве единственного параметра она получает ссылку на функцию, которая выполняет действия по
сравнению значений двух объектов. Это связано с тем, что заранее не известно,
объекты какой структуры будут храниться в списке.
type TListSort Compare =
function (Iteml, Item2: Pointer): Integer;
procedure Sort(Compare: TListSortCompare);
Приведем небольшой пример создания и сортировки списка. Пусть на форме расположены два объекта: список ListBoxl и кнопка Button1. При щелчке на кнопке будет
создан список элементов типа TMyltem.
type TMyltem =record
S: string;
Ind: integer
end;
PMyltem = ^TMyltem;
Тип PMyltem — это указатель на тип TMyltem. Поле S заполняется случайными значениями из 10 символов, а поле Ind примет случайное значение от 1 до 1000. Затем
этот список сортируется по возрастанию значений поля Ind, его содержимое выводится в объект ListBoxl, и, в заключение, содержимое списка вместе с его элементами
будет ликвидировано. Предварительно надо описать функцию для определения, какой
из объектов больше, которая будет использоваться при сортировке. Она должна возвращать значение -1, если первый объект меньше второго, 0, если объекты равны, и
+1, если первый объект больше. Так как параметры функции имеют тип Pointer, в
теле этой подпрограммы их надо явно приводить к типу PMyltem. Объекты будут
сравниваться по значениям полей Ind.
function Compare(Iteml, Item2: Pointer): Integer;
begin
if PMyltem(Iteml)".Ind < PMyItem(Item2)Л.Ind
then Result := -1 else
if PMyItem(Iteml)".Ind > PMyItem{Item2}Л.Ind
then Result := +1
else Result := 0
end;
Основная процедура (обработчик нажатия кнопки) будет выглядеть так.
procedure TForml.ButtonlClickfSender: TObject);
var List: TList;
P: PMyltem;
i, j: integer;
begin
// создание списка:
List := TList.Create;
try
for i := 1 to 10 do
begin
// создание нового элемента:
New(P);
// заполнение поля S случайной строкой
// (значение $40 соответствует пробелу)
РЛ.5 := ' ' ;
for j := 1 to 10 do
P".S := P".S + chr( $40 + random<20) ) ,// случайное значение для поля Ind:
P".Ind := random(l000);
// добавление элемента в список:
List.Add(P);
end;
// выполнение сортировки:
List.Sort(Compare);
// заполнение списка ListBoxl:
for i := 0 to 9 do
begin
P := List.Items[i];
ListBoxl. Items. AddflntToStr (P^ . Ind) + ' ' + P^.S);
end;
finally
// удалить из памяти список
//и все его элементы:
List.Free;
End;
(можно не нужно)
Компонент Список (TListBox)
Компонент Список (TListBox) очень часто применяется в программах для
Windows. Он позволяет выбрать одну или несколько строк в списке.
Первоначально компонент TListBox, размещенный на форме, изображается в виде
пустого квадрата. Его размеры можно настроить протягиванием мыши.
Список может иметь несколько столбцов. Это не означает, что каждый столбец
представляет собой отдельный список, просто при заполнении видимой части списка
строками донизу очередная строка отображается в следующем столбце. В остальном
работать такой список будет так же, как обычный список.
1 24 Урок 2. Основы программирования в среде Delphi 7
Число столбцов задается в свойстве Columns. Если оно больше 0, то на каждую колонку отводится часть общей ширины списка (значение свойства Width, деленное
на число столбцов). Список может принадлежать к одному из трех возможных
типов (свойство Style).
Таблица 2.5. Значение свойства Style
Значение Стиль списка
IbStandard Стандартный список (стиль по умолчанию)
IbOwnerDrawFixed Каждый элемент списка имеет фиксированную высоту, но способ его
отображения определяется программистом
IbOwnerDrawVariable Кроме способа рисования в тексте программы необходимо явно
задавать размер каждого элемента списка (что позволяет создавать
списки с элементами разных размеров)
Если значение свойства Style не равно IbStandard, программист должен сформировать для списка обработчик события OnDrawItem, в котором самостоятельно организовать отрисовку содержимого. Такой список может содержать не только текст, но
и другие объекты, например рисунки. Узнать высоту элемента в пикселах можно,
обратившись к свойству ItemHeight
Если высота элементов списка может быть различной, то перед обработкой события
OnDrawItem надо обработать событие OnMeasureltem. В заголовке соответствующего
обработчика имеется параметр, описанный так:
var Height: Integer;
В этот параметр, передаваемый по ссылке, надо записать размер очередного элемента (его номер указывается в параметре Index), а затем уже обработать событие
OnDrawItem с учетом текущей высоты элемента.
Выбирать элементы в списке можно по одному, а можно и по нескольку одновременно. Свойство MultiSelect (тип Boolean) позволяет выбирать несколько элементов,
если его значение равно True. Выделить сразу группу элементов можно, если при
щелчке на последнем из них держать нажатой клавишу SHIFT. Такая возможность
появляется, если в свойство ExtendedSelect занесено значение True.
Свойство SelCount (оно доступно только для чтения и не \южет быть изменено в
тексте программы) содержит число выделенных строк списка. Чтобы определить,
выделен ли конкретный элемент, надо, используя его номер, обратиться к свойству Selected, представляющему собой массив типа Boolean, Значение выражения
ListBox.Selected!4]
будет равно True, если пятый элемент (отсчет ведется с нуля) в списке выделен.
Если выделение нескольких элементов не разрешено, то узнать, какой элемент выделен, можно, обратившись к свойству Itemlndex, хранящему номер единственного
выделенного элемента.
Создание программ для Windows 125
'1 ПОДСКАЗКА Исходно выделенные элементы в списке отсутствуют. Это означает, что в свойстве Ifemlndex записано значение -1- При попытке
обратиться к какому-то свойству, допускающему индексацию
(например, Items), используя Itemlndex в качестве индекса, возникнет ошибка выхода индекса за допустимые границы. Поэтому желательно после первого заполнения списка явно выделить один из
элементов в тексте программы, занеся в свойство Itemlndex значение, отличное от -1.
Содержимое списка хранится в уже знакомом нам свойстве Items (список строк,
класс TStrings). Строки можно задать на этапе проектирования, в специальном редакторе, так же, как задавалось содержимое группы переключателей, а можно и во
время работы программы с помощью метода Add класса TStrings.
Например, если имеется потребность в занесении в список значений, получаемых
в результате сложения, то на форму можно добавить новую кнопку Добавить (она
автоматически получит название Button?), а в обработчике щелчка на этой кнопке
(событие OnClick) вызывать метод Add (рис. 2.12).
/'MvForm
Г
HodbSuttqnl;
Рис. 2.12. В левом нижнем углу формируется список результатов
procedure TMyForm.Button2Click(Sender: TObject);
begin
ListBoxl.Items.Add( Labell.Caption ];
end;
Элементы списка можно упорядочить в алфавитном порядке, занеся в свойство
Sorted значение True. После этого при добавлении новых элементов они будут сортироваться автоматически.
1 26 Урок 2. Основы программирования в среде Delphi 7
Для очистки всего содержимого списка используется метод Clear:
ListBoxl. Clear;
Для удаления конкретного элемента служит метод DeleteString:
ListBoxl.DeleteString(4};
При этом, конечно, помимо перерисовки списка вносятся соответствующие изменения в свойство Items.
Еще один полезный метод, который часто используется для вызова контекстного
меню конкретного элемента списка, называется ItemAtPos. Он переводит координату точки внутри списка в номер элемента, в рамках которого лежит эта точка.
Его удобнее всего использовать в обработчике щелчка (отпускания) кнопки мыши
для объекта ListBox.
procedure TMyForm.ListBoxlMouseUp (Sender: TObject;
Button: TMouseButCon,Shift: TShiftState; X, Y: Integer);
var Point: TPoint;
Index: Integer;
begin
Point.X := X; Point.Y := Y;
Index := ListBoxl .ItemAtPos (Point, True) ,end,В переменную Index запишется номер элемента, на область которого внутри списка
пришелся щелчок мыши.
У функции ItemAtPos два параметра. Первый — объект типа TPoint, содержащий
координаты точки. Второй параметр определяет, какое значение будет возвращено,
если щелчок выполнен вне границ реально присутствующих в списке строк (это
может быть только в области за последней строкой). Если в такой ситуации в качестве
второго параметра указано значение True, функция вернет значение -1, если False увеличенный на единицу номер последнего элемента списка.
ЗАМЕЧАНИЕ Для вызова контекстного меню, привязанного к конкретному
объекту, можно использовать обработчик события OnContextPopup
(которое генерируется по умолчанию, если пользователь нажимает
правую кнопку мыши над областью объекта).__
37.Классы TStrings и TStringList. Основные методы. Использование в
программах на Delphi.
Список строк
Список строк (класс TStrings, описанный в модуле Classes) полностью соответствует
своему названию и предназначен для:
О хранения строк;
О обращения к списку строк как к массиву и получения строки по ее номеру
(индексация начинается с нуля);
О поиска строк;
О добавления или удаления строк в конкретные места списка.
for i := 1 to 10 do
SList.Add('добавление в конец списка ' + IntToStr(i));
SList.Insert(5, 'добавление в 6-ю позицию списка');
SList[3] :- 'изменение 4-й строки';
Создание программ для Windows 115
WriteLn(SList[1]); // вывод 2-й строки
SList.Delete(O); // удаление первой строки
Для удаления всех строк применяется метод Clear:
SList.Clear;
Для сравнения списков служит метод Equals:
if SList.Equals(MyList) then ...
Для перестановки двух строк местами предназначен метод Exchange:
SList.Exchange(0,3 1 ;
Переместить строку со старого места на новое позволяет метод Move:
SList.Moved,8) ;
Поиск первого вхождения строки в список осуществляет метод IndexOf:
i := SList.IndexOf('строка 5 ' ) ;
Если строка в списке не найдена, то возвращается значение -1.
Сохранить весь список в файле можно при помощи метода SaveToFile:
SList. SaveToFile('С:\Strings.TXT');
Для чтения списка из файла служит метод LoadFromFite:
SList-LoadFromFilef'C:\Strings.TXT') ;
Каждая строка текста в исходном файле должна быть отделена от следующей символом возврата каретки.
Все содержимое списка строк можно представить в виде одной длинной строки,
обратившись к свойству Text:
WriteLnl SList.Text );
Список строк имеет определенную прикладную направленность. В системе Windows
стандартным считается способ хранения настроек программы в виде файла .INI.
когда название характеристики и ее значение разделяются знаком равенства. Вместо
знака равенства можно использовать другие символы — они задаются свойством
fJameValueSeparator. В частности, собственные настройки Windows хранятся в файлах
WIN .INI и SYSTEM JNL
iCountry=7
Список строк позволяет автоматически выделять из таких строк название (часть
текста до знака равенства) и значение (часть текста после знака равенства). Для
этого служат соответственно свойства
Names[номер-строки]
и
Values[название]
Например, если выполнен оператор
SList[l] := 'Левая позиция окна=100';
116 Урок 2. Основы программирования в среде Delphi 7
то значением выражения Names[l] будет строка ≪Левая позиция окна≫, а значением
выражения Values [≪Левая позиция окна≫] — строка ≪100≫. Для получения значения
по индексу можно воспользоваться свойством ValueFromIndextnoMep-строки].
В строках подобного форматаможно выполнять поиск с помощью метода IndexOfName,
который в качестве параметра получает часть строки, относящуюся к названию, а возвращает номер первой строки с подходящим названием или число -1 при неудаче.
В описанном примере вызов функции
SList.IndexOfName('Левая позиция окна')
вернет значение 1.
Очень полезная особенность списка строк — возможность хранить связанные со
строками объекты (класса TObject и унаследованных от него классов). Представьте
себе, что пользователю предлагается выбрать строку из длинного списка (в элементе
управления), который последовательно заполнялся значениями строк из массива, а
затем был автоматически отсортирован. В такой ситуации невозможно определить
соответствие выбранной строки номеру в исходном массиве обычными средствами
без сравнения выбранной строки с каждым элементом массива (и даже это не гарантирует результат, если в массиве могут присутствовать одинаковые строки).
Возможность связывать объекты со строками пригодится вам и в том случае, когда в
списке хранятся названия объектов и объекты желательно связать с этими названиями.
Если требуется заполнить список, который будет затем отсортирован автоматически, и сохранить возможность определения исходных номеров строк, то удобнее
воспользоваться методом AddObject, который позволяет вместе со строкой указать
сопутствующую информацию в виде объекта класса TObject.
for i := 1 to 10 do
SList.AddObject('строка'.TObject(i));
Хотя все элементы списка SList теперь имеют одинаковые значения, их можно без
проблем различать по уникальным номерам от 1 до 10. Для этого надо воспользоваться методом IndexOfObject, который в качестве параметра получает ссылку на
объект, а в качестве значен ия возвращает номер соответствующей строки в списке
или -1 в случае неудачи:
i := SList. IndexOfObject f TObject (.7) ) ;
У класса TStrings имеется также полезное свойство Count, хранящее число строк в
списке.
Хотя в компонентах в качестве типа соответствующих свойств обычно указывается
только что описанный класс TStrings, использовать его в программе явно нельзя.
В частности, нельзя пытаться создать новый объект с помощью конструктора:
SList := TStrings.Create;
Но так как программисту вполне могут потребоваться возможности такого мощного
класса, в системе Delphi 7 реализован его наследник, который называется TStringList,
Создание программ для Windows 117
содержит всю функциональность родителя и допускает свое использование в программе. Чтобы включить в исходный текст приведенные выше команды, надо переменную SList описать как имеющую тип TStringList:
var SList: TStringList;
SList := TStringList.Create;
SList.AddObject('строка1,TObject(12));
В классе TStringList добавилось несколько новых полезных методов и свойств. Метод
Sort позволяет выполнить сортировку всех строк в списке в порядке возрастания,
при условии, что свойство Sorted имеет значение False (в противном случае при
добавлении новой строки она будет автоматически помещаться в список в соответствии с требованиями упорядоченности). В связи с тем, что класс TStringList
поддерживает сортировку, немного изменилась работа ранее описанных методов.
Теперь при добавлении, перемещении или изменении строк в списке выполняется
проверкасвойс гна Sorted, и при необходимости изменения вносятся в упорядоченный список с учетом значений строк. Кроме того, хранить в списке строки с одинаковыми значениями не разрешается. Чтобы при попытке добавить новую строку не
возникало ошибок, надо предварительно проверить, не присутствует ли уже такая
строка в списке. Для этого предназначен метод Find, который имеет следующий
заголовок:
function Find(const S: string; var Index: Integer)
: Boolean; virtual;
Проверяется, можно ли добавить строку в отсортированный список. Если строка S
в списке уже есть, функция возвращает значение True, если нет — False, а в переменную Index записывается номер позиции в списке, куда будет добавлена строка S.
ВНИМАНИЕ Функцию Find можно применять только для отсортированных
списков. Для несортированных списков надо использовать метод
IndexOf.
Из новых свойств надо отметить свойство Objects, представляющее собой массив
объектов, ассоциированных со строками. Получить доступ к нужному объекту можно
с помощью индекса:
SList.Objects[4]
Свойство Sorted указывает, надо ли выполнять автоматическую сортировку списка
(то есть, добавлять новые записи не в коней списка, а в соответствии с правилами
упорядочивания символов для национальной версии Windows). Если это свойство
имеет значение False и в него записывается значение True, то программа немедленно
выполняет сортировку всего списка (для больших списков это может оказаться
длительным процессом).
Свойство Duplicates определяет, что произойдет в программе, когда обнаружится попытка
добавления в отсортированный список строки, которая уже содержится в нем.
1 1 8 Урок 2. Основы программирования в среде Delphi 7
Это свойство может принимать одно из трех значений.
Таблица 2.2. Значение свойства Duplicates
Значение свойства Действие
duplgnore Попытка добавления строки игнорируется
dupError Возникает ошибка
dupAccept Производится добавление строки, совпадающей с одной из существующих
ЗАМЕЧАНИЕ Другие стандартные классы Delphi будут далее рассматриваться
по мере необходимости.__
38.Класс TBits. Основные методы. Использование в программах на
Delphi.
Описание
Тип TBits содержит гибкий набор битов (Булевых значений). Размер набора может
быть изменен в любое время (используя свойство size).
К битам обращаются, используя свойство Bits, подобно этому:
flag := myBits.Bits[2];
или, ещё прощё:
flag := myBits[2];
Есть одна сервисная функция - OpenBit, которая возвращает индекс первого
ложного значения. Подобной функции для поиска истинного значения нет.
Похожие команды
Array
Тип данных содержащий индексируемую коллекцию данных
Boolean
Позваляет принимать только True и False значения
Пример кода : Простой пример
var
flags : TBits;
// Наша коллекция переменных Булевых значений
i : Integer;
begin
// Создание нашего объекта TBits
flags := TBits.Create;
// Добавление несколько элементов в нашу Boolean коллекцию флагов
flags.Size := 5;
// И установка нескольких значений
flags[0] := true;
flags[1] := true;
flags[4] := true;
// Теперь показываем содержимое коллекции
// Обратите внимание, что начинается с 0
for i := 0 to flags.Size-1 do
if flags[i] = true
then ShowMessageFmt('Bit %d - true',[i])
else ShowMessageFmt('Bit %d - false',[i]);
// TBits имеет один основной метод // нахождение индекса первого ложного значения
ShowMessageFmt('Index of the first false value is %d',[flags.OpenBit]);
end;
Bit 0 - true
Bit 1 - true
Bit 2 - false
Bit 3 - false
Bit 4 - true
Индекс первого ложного значения - 2
Класс TBits
Определение
property Size: Integer;
Описание
Свойство Size задает число элементов, которые могут храниться в массиве типа TBits.
Значение этого свойства в процессе работы может увеличиваться или уменьшаться. При
увеличении Size значения всех новых элементов делается равным false. При уменьшении
Size значения элементов с индексами, не превышающими Size - 1 сохраняются, а
остальные элементы теряются. Если при обращении к объекту в свойстве TBits задан
индекс, больший чем Size - 1, генерируется исключение EBitsError.
Класс TBits
Определение
property Bits[Index: Integer]: Boolean
Описание
Свойство Bits используется для чтения или задания значений отдельных элементов
объекта типа TBits. Параметр Index задает индекс элемента. Его значение может меняться
в пределах от 0 до Size - 1, где Size - свойство, задающее размер массива. Если значение
Index выходит из этих пределов, генерируется исключение EBitsError. Все
неустановленные элементы имеют значение false.
39.Класс TStream и его потомки. Основные методы. Использование в
программах на Delphi.
Базовые классы TStream и THandleStream
В основе иерархии классов потоков лежит класс Tstream. Он обеспечивает
выполнение основных операций потока безотносительно к реальному
носителю информации. Основными из них являются чтение и запись
данных.
Класс Tstream порожден непосредственно от класса TObject.
Потоки также играют важную роль в чтении/записи компонентов из
файлов ресурсов (DFM). Большая группа методов обеспечивает
взаимодействие компонента и потока, чтение свойств компонента из
ресурса и запись значений свойств в ресурс.
Таблица 9.3. Свойства и методы класса Tstream
Объявление
Описание
property Position: Longint;
property Size: Longint;
function CopyFrom( Source:
TStream; Count: Longint) :
Longint;
function Read(var Buffer;
Count: Longint) : Longint;
virtual; abstract;
procedure Read3uffer (var
Buffer; Count: Longint) ;
function Seek (Off set:
Longint; Origin: Word):
Longint; virtual; abstract;
function Write (const Buffer;
Count: Longint): Longint;
virtual; abstract;
procedure WriteBuffer (const
Buffer; Count: Longint);
function ReadComponent
(Instance: TComponent):
TComponent;
function ReadComponentRes
(Instance: TComponent) :
TComponent;
Определяет текущую позицию
в потоке
Определяет размер потока в
байтах
Копирует из потока Source
Count байты, начиная с
текущей позиции. Возвращает
число скопированных байтов
Абстрактный класс,
перекрываемый в
наследниках. Считывает из
потока Count байты в буфер
Buffer. Возвращает число
скопированных байтов
Считывает из потока Count
байты в буфер Buffer.
Возвращает число
скопированных байтов
Абстрактный класс,
перекрываемый в
наследниках. Смещает
текущую позицию в реальном
носителе данных на Offset
байтов в зависимости от
условия Origin (см. ниже)
Абстрактный класс,
перекрываемый в
наследниках. Записывает в
поток Count байты из буфера
Buffer. Возвращает число
скопированных байтов
Записывает в поток Count
байты из буфера Buffer.
Возвращает число
скопированных байтов
Передает данные из потока в
компонент instance, заполняя
его свойства значениями
Считывает заголовок ресурса
компонента Instance и
значения его свойств из
потока.
procedure ReadResHeader;
Считывает заголовок ресурса
компонента из потока
procedure WriteComponent
(Instance: TComponent) ;
Передает в поток значения
свойств компонента Instance
procedure
WriteComponentRes (const
ResName: string; Instance:
TComponent) ;
Записывает в поток заголовок
ресурса компонента Instance и
значения его свойств
Итак, в основе операций считывания и записи данных в потоке лежа!
методы Read и Write. Именно они вызываются для реального выполнения
операции внутри методов ReadBuffer И WriteBuffer, ReadComponent И
WriteComponent. Так как класс TStream является абстрактным, то методы
Read и write также являются абстрактными. В классах-наследниках они
перекрываются, обеспечивая работу с конкретным физическим
носителем данных.
Метод Seek используется для изменения текущей позиции в потоке.
"Точка отсчета" позиции зависит от значения параметра Origin:

soFromBeginning — смещение должно быть положительным и
отсчитывается от начата потока;

soFromCurrent — смещение относительно текущей позиции в
потоке;

soFromEnd — смещение должно быть отрицательным и
отсчитывается от конца потока.
Группа методов обеспечивает чтение и запись из потока ресурса
компонента. Они используются при создании компонента на основе
данных о нем, сохраненных в формате файлов ресурсов. Для чтения
ресурса используется метод ReadComponentRes, в котором последовательно
вызываются:

метод ReadResHeader — для считывания заголовка ресурса
компонента из потока;

метод ReadComponent — для считывания значений свойств
компонента. Для записи ресурса в поток применяется метод
writeComponentRes.
Класс THandleStream инкапсулирует поток, связанный с физическим
носителем данных через дескриптор.
Для создания потока используется конструктор
constructor Create(AHandle: Integer);
в параметре которого передается дескриптор. Впоследствии доступ к
дескриптору осуществляется через свойство:
property Handle: Integer;
Класс TFileStream
Класс TFileStream позволяет создать поток для работы с файлами. При
этом поток работает с файлом без учета типа хранящихся в нем данных
(см. выше).
Полное имя файла задается в параметре FileName при создании потока:
constructor Createfconst FileName: string; Mode: Word);
Параметр Mode определяет режим работы с файлом.
флагов режима открытия:

fmCreate — файл создается;
Он составляется из
fmOpenRead — файл открывается для чтения;
fmopenwrite — файл открывается для записи;
fmOpenReadWrite — файл открывается для чтения и записи.
И флагов режима совместного использования:

fmShareExciusive — файл недоступен для открытия другими
приложениями;

fmShareDenyWrite — другие приложения могут читать данные из
файла;

fmShareDenyRead — другие приложения могут писать данные в
файл;

fmShareDenyNone — другие приложения могут производить с
файлом любые операции.
Для чтения и записи из потока используются методы Read и write,
унаследованные от класса THandleStream:



procedure TForml.CopyBtnClick(Sender: TObject);
var Streaml, Stream2: TFileStream;
IntBuf: array[0..9] of Integer/begin
if Not OpenDlg.Execute then Exit;
try
Streaml := TFileStream.Create(OpenDlg.FileName, fmOpenRead);
Streaml.ReadBuffer(IntBuf, SizeOf(IntBuf));
try
Stream2 := TFileStream.Create('TextFile.tmp', fmOpenWrite);
Stream2.Seek(0, soFromEnd);
Stream2.WriteBuffer(IntBuf, SizeOf(IntBuf));
finally
Stream2.Free;
end;
finally
Streaml.Free;
end;
end;
Обратите внимание, что в данном фрагменте кода функция seek
используется для записи данных в конец файлового потока.
При необходимости копирования одного файла в другой целиком
используется метод CopyFrom, унаследованный от класса Tstream:
procedure TForml.CopyBtnClick(Sender: TObject);
var Streaml, Stream2: TFileStream;
begin if Not OpenDlg.Execute then
Exit;
try
Streaml := TFileStream.Create(OpenDlg.FileName, fmOpenRead);
Stream2 := TFileStream.Create('Sample.tmp1, fmOpenWrite);
Stream2.Seek{0, soFromEnd);
Stream2.CopyFrom(Streaml, Streaml.Size);
finally
Streaml.Free;
Stream2.Free;
end;
end;
Обратите внимание, что в данном случае идя определения размера
передаваемого потока необходимо использовать свойство stream, size,
которое дает реальный объем данных, содержащихся в потоке. Функция
sizeof (stream) в этом случае даст размер объекта потока, и не более того.
Класс TMemoryStream
Класс TMemoryStream обеспечивает сохранение данных в адресном
пространстве. При этом методы доступа к этим данным остаются теми же,
что и при работе с файловыми потоками. Это позволяет использовать
адресное пространство для хранения промежуточных результатов
работы приложения, а также при помощи стандартных методов
осуществлять обмен данными между памятью и другими физическими
носителями.
Свойство
property Memory: Pointer;
определяет область памяти, отведенную для хранения данных потока.
Изменение размера отведенной памяти осуществляется методом
procedure SetSize(NewSize: Longint); override;
Для очистки памяти потока используется метод
procedure Clear;
Чтение/запись данных в память выполняется привычными методами Read
и Write.
Также запись данных в память может осуществляться методами:

procedure LoadFromFile(const FileName: string); — из файла;

procedure LoadFromStream(Stream: TStream) ; — из другого
потока.
Дополнительно можно использовать методы записи данных в файл или
поток:
procedure SaveToFile(const FileName: string);
procedure SaveToStream(Stream: TStream);
http://www.cyberguru.ru/programming/delphi/delphi-components-part1-page5.html
http://www.cyberguru.ru/programming/delphi/delphi-components-part1-page5.html
Классы для представления потока данных
В среде Delphi существует иерархия классов для хранения и последовательного вводавывода данных. Классы этой иерархии называются потоками. Потоки лучше всего
представлять как файлы. Классы потоков обеспечивают различное физическое
представление данных: файл на диске, раздел оперативной памяти, поле в таблице базы
данных (таблица 3.1).
Класс
Описание
TStream
Абстрактный поток, от которого наследуются все остальные.
Свойства и методы класса TStream образуют базовый интерфейс
потоковых объектов.
THandleStream
Поток, который хранит свои данные в файле. Для чтения-записи
файла используется дескриптор (handle), поэтому поток называется
дескрипторным. Дескриптор — это номер открытого файла в
операционной системе. Его возвращают низкоуровневые функции
создания и открытия файла.
TFileStream
Поток, который хранит свои данные в файле. Отличается от
ThandleStream тем, что сам открывает (создает) файл по имени,
переданному в конструктор.
Поток, который хранит свои данные в оперативной памяти.
Моделирует работу с файлом. Используется для хранения
TMemoryStream
промежуточных результатов, когда файловый поток не подходит из-за
низкой скорости передачи данных.
TResourceStream Поток, обеспечивающий доступ к ресурсам в Windows-приложении.
TBlobStream
Обеспечивает последовательный доступ к большим полям таблиц в
базах данных.
Таблица 3.1. Классы потоков
Потоки широко применяются в библиотеке VCL и наверняка вам понадобятся. Поэтому
ниже кратко перечислены их основные общие свойства и методы.
Общие свойства:
Position: Longint — текущая позиция чтения-записи.
Size: Longint — текущий размер потока в байтах.
Общие методы:
CopyFrom(Source: TStream; Count: Longint): Longint — копирует Count байт из потока
Source в свой поток.
Read(var Buffer; Count: Longint): Longint — читает Count байт из потока в буфер Buffer,
продвигает текущую позицию на Count байт вперед и возвращает число прочитанных
байт. Если значение функции меньше значения Count, то в результате чтения был
достигнут конец потока.
ReadBuffer(var Buffer; Count: Longint) — читает из потока Count байт в буфер Buffer и
продвигает текущую позицию на Count байт вперед. Если выполняется попытка чтения за
концом потока, то генерируется ошибка.
Seek(Offset: Longint; Origin: Word): Longint — продвигает текущую позицию в потоке на
Offset байт относительно позиции, заданной параметром Origin. Параметр Origin может
иметь одно из следующих значений: 0 — смещение задается относительно начала потока;
1 — смещение задается относительно текущей позиции в потоке; 2 — смещение задается
относительно конца потока.
Write(const Buffer; Count: Longint): Longint — записывает в поток Count байт из буфера
Buffer, продвигает текущую позицию на Count байт вперед и возвращает реально
записанное количество байт. Если значение функции отличается от значения Count, то при
записи была ошибка.
WriteBuffer(const Buffer; Count: Longint) — записывает в поток Count байт из буфера
Buffer и продвигает текущую позицию на Count байт вперед. Если по какой-либо причине
невозможно записать все байты буфера, то генерируется ошибка.
Ниже приводится фрагмент программы, демонстрирующий создание файлового потока и
запись в него строки:
var
Stream: TStream;
S: AnsiString;
StrLen: Integer;
begin
// Создание файлового потока
Stream := TFileStream.Create('Sample.Dat', fmCreate);
...
// Запись в поток некоторой строки
StrLen := Length(S) * SizeOf(Char);
Stream.Write(StrLen, SizeOf(Integer)); // запись длины строки
Stream.Write(S, StrLen);
// запись символов строки
...
// Закрытие потока
Stream.Free;
end;
40.Программирование графики в Delphi. Класс TCanvas. Основные
свойства и методы.(тут еще нужно упомянуть Тимейдж и
ТпайнтБокс.билет52)
Свойства и методы класса TCanvas
В качестве свойств, в первую очередь, используются классы, описывающие цвет и
способ заполнения областей формы, цвет и толщину линий, стиль и размер шрифта
и другие. Дополнительные методы предназначены для вывода на экран изображений и рисунков.
Класс Карандаш (ТРеп). Карандаш — свойство Реп класса TCanvas, определяется
основными свойствами, перечисленными в табл. 4.12.
Таблица 4.12. Основные свойства класса ТРеп
Свойство Назначение
Color Цвет карандаша
Mode Режим рисования. Определяет, в частности, способ комбинирования своего цвета с
текущим цветом холста; например, значение ргпХог позволяет рисовать линии, которые
при повторной отрисовке на том же месте исчезают. Это удобно, когда надо
динамически отображать постоянно меняющиеся линии вслед за движущимся
указателем
Style Стиль линии определяет, будет ли она сплошной или пунктирной. Возможные
значения: psSolid (сплошная линия); psDash (пунктирная) и другие
Width Толщина линии в пикселах
Класс Кисть (TBrush). Кисть — свойство Brush класса TCanvas, предназначена для
заполнения сплошных областей клиентской части формы в соответствии с заданным
шаблоном. Помимо свойств Color и Style, совпадающих с аналогичными свойствами
класса ТРеп, в класс TBrush добавлено новое свойство Bitmap, которое позволяет заполнять область не только сплошным цветом или пунктирными линиями, но и заранее
подготовленным точечным изображением.
Работа с графикой 193
Класс Шрифт (TFont). Шрифт — свойство Font класса TCanvas, служит оболочкой
ресурса Windows, определяющего текущий шрифт. Содержит множество стандартных свойств, описывающих характеристики шрифта. Наиболее важные из них
приведены в табл. 4.13.
Таблица 4.13. Основные свойства класса TFont
Свойство Назначение
Color Цвет
Charset Набор символов, определяемый используемой кодировкой. Например, для шрифта
с
русскими буквами могут существовать различные наборы: СР-1251, КОИ-8 и другие
Height Высота шрифта в пикселах. Реально эта высота вычисляется по специальной
формуле
и может принимать отрицательные значения. Подробнее об этом можно узнать в
справочном руководство го программированию в Windows. Вместо данного свойства
лучше использовать свойство Size
Name Название шрифта, под которым ок зарегистрирован в Windows, например Times
New
Roman, Courier и прочие
Pitch Профиль шрифта, определяющий, будет ли расстояние между символами
фиксированным (fpFixed) или переменным (fpVariable), как это имеет место в шрифтах
Courier и Times New Roman соответственно. Если для шрифта явно задано значение, не
соответствующее реальному профилю, система Windows автоматически подберет
шрифт, все символы которого наиболее точно соответствуют указанным параметрам
Size Высота шрифта в пикселах
Style Оиль ирифтг. Возможно е этзчения: ;<3о1з (по.пуии:ныГ'); fsltslic (курсив);
fsllnderline (подчеркнутый); fsStrikeOut (зачеркнутый)
Свойства холста
Выше были перечислены самые важные свойства холста, которые активно используются в процессе вывода графической информации. Однако помимо них необходимо отметить некоторые свойства самого класса TCanvas, приведенные в табл. 4.14.
Класс TCanvas содержит большое количество методов. Их краткое описание приведено в табл. 4.15.
Таблица 4.14. Некоторые свойства класса ТСапоах
Свойство Назначение
CanvasO Mentation
Данное свойство, доступное только для чтения, определяет позицию начала
координат. Значение по умолчанию —coLeftToRight (отсчет ведется от
левого верхнего угла клиентской области). В некоторых случаях
используется значение coRightToLeft (когда в соответствии с
национальными требованиями текст пишется справа налево). В этом
случае отсчет идет от верхнего правого угла
CLipRect
Область холста, которая реально отрисовывается. Обычно эта область
совпадает с клиентской областью, что означает вывод всей графической
информации. 8 ряде случаев удается значительно повысить скорость
отображения информации благодаря ограничению области вывода
небольшим прямоугольником
CopyMode Режим копирования графического образа на холст. С помощью данного
свойства удается создавать самые разные графические и анимационные
эффекты путем выполнения операций логического сложения, умножения
инвертирования битов исходной и результирующей графических областей.
Например, значение cmSrcInvert {логическая операция XOR) активно
применяется при перемещении спрайтов
PenPos Текущая позиция графического курсора (тип TPoint)
Pixels Двумерный массив, хранящий цвета каждого пиксела изображения. Это
свойство очень полезно при поточечной обработке рисунка, однако им
нельзя злоупотреблять, так как обработка отдельных пикселов — весьма
медленная операция. Пример использования:
Canvas.Pixels[123,50]:- dRed;
TextFlags Способ вывода текста на холст. Возможные значения: — ETO_CLIPPED
(обычный вывод, по умолчанию); ETO_OPAQUE (вывод текста с заливкой
фона, что ускоряет процесс вывода, но перекрывает фоновый рисунок)
Таблица 4.15. Краткое описание методов класса TCanvas
Метод Назначение
procedure Arc(
XI, Y1, Х2, YZ, ХЗ, УЗ, Х4, Y4: Integer);
procedure BmshCopy(const Dest: TRect;
Bitmap: TBitmap; const Source: TRect;
Color: TColor);
procedure Chord(
XI, Yl, X2, Уг, ХЗ, УЗ, Х4, Y4): Integer;
procedure Copy4ect(Qest: TRect;
Canvas: TCanvas; Source: TRect);
procedure Draw(X,Y: Integer;
Graphic: TGraphic);
procedure Draw Foe usRectf
const Rect: TRect);
procedure EUipse(const Rect: TRect);
procedure FillRect(const Rect: TRect);
procedure FloodRU(X,Y: Integer;
Color: TColor; FiltStyle: TRllStyle);
procedure FrameRectfconst Rect; TRect);
Рисование части эллипса
Копирование заданной части графич.изображения на холст. При этом цвет, указанный в
качестве параметра, трактуется как прозрачный (не отображаемый на экране)
Рисуется замкнутая фигура, созданная пересечением
эллипса и отрезка прямой линии (хорды)
Копирование на данный холст области с другого холста.
Способ вывода определяется значением свойства CopyMode
Вывод графического изображения в заданной точке
холста
Рисуется прямоугольник в стиле, принятом в
Windows
для отображения элементов, имеющих
фокус.
Повторный вывод такого прямоугольника в
том же месте приводит к его исчезновению
(логическая операция XOR)
Рисуется эллипс
Рисуется прямоугольник, который заполняется в соответствии со значением свойства
Brush
Заполнение указанным цветом области холста,прилегающей к точке, заданной в
качестве параметра,и имеющей цвет, совпадающий с цветом этой точки
Рисуется прямоугольник заданного размера с толщиной
границы в 1 пиксел. Вид линии определяется значением
свойства Brush
стр195Таблица 4.15, Краткое описание методов класса TCamias
(продолжение)
Назначение
procedure LineTo(X,Y: Integer);
procedure MoveTo(X,Y: Integer);
procedure Pie{
XI, Yl, X2, Y2, X3, Y3, X4, Y4: Longint);
procedure PolyBezier(
const Points: array of TPoint);
procedure Poly8ezierTo(
const Points: array of TPoint);
procedure Polygon(
Points: array of TPoint);
procedure PoLylinef
Points: array of TPoint)
procedure Rectangle (const Rect: TRect);
procedure RoundRect(
XI, Y1,X2,Y2,X3,Y3: Integer);
procedureStretchDraw(
const Rect: TRect; Graphic: TGraphic);
function Text£xtent(
const Text: string): TSize;
function TextHeight(
const Text: string): Integer;
procedure TextOut(X, Y: Integer;
const Text: string);
procedure TextRect(Rect: TRect;
X, Y: Integer; const Text: string);
function TextWidth(
const Text: string): Integer;
Рисует линию от точки, определенной свойством PenPos (текущая позиция
графического курсора) до точки,указанной в качестве параметра
Устанавливает текущую позицию графического курсора в точку, заданную в параметрах
метода
Рисуется сектор эллипса, расположенный внутри
заданного прямоугольника
Рисуется кривая Безье — гладкая линия, соединяющая заданные точки. Точки
передаются в динамическом массиве, состоящем из элементов типа TPoint. При
рисовании очередного фрагмента линии учитываются три последовательные точки
Метод аналогичен предыдущему, но по окончании отрисовки линии графический курсор
перемещается в ее последнюю точку. Значение свойства PenPos, в отличие от метода
PolyBezier, когда графический курсор остается на старом месте, обновляется
Рисуется сложная фигура, состоящая из отрезков,последовательно соединяющих
точки, представленные ввиде динамического массива элементов TPoint. Последняя
точка соединяется с первой. Внутренняя часть фигуры заполняется в соответствии со
значением свойства Brush
Метод аналогичен предыдущему, но заполнения внутренней части фигуры не
происходит
Рисуется прямоугольник. Его внутренняя область заполняется в соответствии со
значением свойства Brush
Метод аналогичен предыдущему. Рисуется прямоугольник со скругленными углами
Вывод графического изображения в область, заданную пара метром-прямоугольником.
Изображение масштабируется в соответствии с размерами этого прямоугольника
Возвращает ширину и высоту в пикселах строки, выведенной текущим шрифтом
Возвращает высоту в пикселах строки, выведенной
текущим шрифтом
Вывод строки в конкретную позицию холста
Вывод строки в указанную позицию в рамках прямоугольника Rect. Часть строки, не
попавшая в указанную область, на холст не выводится
Ширина в пикселах строки, выведенной текущим
шрифтом__
41.Класс TGraphic, и его потомки (TBitmap, TMetaFile,и др.): основные
свойства и методы, применение для создания и вывода изображений.
Класс TGraphics
Данный класс является абстрактным и сам по себе не применяется. На его основе
созданы классы, предназначенные для использования в программах конкретных
графических объектов (точечное изображение, значок и прочие).
Абстрактный класс TGraphic является родительским для трех видов изображений,
общепринятых в графике Windows — значка (компонент Ticon), метафайла (компонент
TMetafile) И растровой картинки (компонент TBitmap). Четвертым потомком TGraphic
является TuPEGimage — сжатая растровая картинка в формате JPEG.
От TGraphics такие объекты наследуют свойства, приведенные в табл. 4.10. Методы
класса TGraphics имеют характеристики virtual и abstract и определяются в конкретных
классах-наследниках. Они приведены в табл. 4.11.
1 92 Урок 4. Современные компоненты интерфейсе пользователя
Таблица 4,10. Наследуемые свойства класса TGraphics
Свойство Назначение
Width Height Ширина и высота объекта в пикселах
Modified Имеет значение True, если объект был изменен (например, отредактирован)
Palette Идентификатор цветовой палитры Windows
Transparent Имеет значение True, если объект будет рисоваться в ≪прозрачном≫ режиме.
Цвет, определяющий уровень прозрачности, задается в конкретном классе
Таблица 4.11. Абстрактные методы класса TGraphics
Метод Назначение
procedure LoadFrom File (const FileName: string); Загрузка и сохранение графической
информации
procedure SaveTo File (const FileName: string); в файле
procedure LoadFromClipboardFoimat; Загрузка и сохранение графической информации
procedure SaveToClip board Form at; в буфере обмена Windows
procedure Load From Stream (Stream: TStream); Загрузка и сохранение графической
информации
procedure SaveToStrearn(Stream: TStream); в потоке
(
Метод:
procedure Assign(Source: TPersistenti;
переопределяет одноименный метод предка, допуская полиморфное присваивание
графических объектов.
Загрузку и выгрузку графики в поток осуществляют методы:
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
а загрузку и выгрузку в файл — методы:
procedure LoadFromFile(const Filename: string); procedure SaveToFile(const Filename: string);
Эти методы создают соответствующий файловый поток и затем вызывают методы
LoadFromStream/SaveToStream.
Два метода осуществляют взаимодействие с буфером обмена Windows:
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
Здесь AFormat — используемый графический формат; AData и APalette — данные и
палитра (если она требуется). Потомок должен иметь свой формат представления в
буфере обмена и уметь обрабатывать данные, представленные в нем.
Загрузка больших графических файлов может продолжаться очень долго. Чтобы скрасить
пользователю ожидание, программист может обработать событие OnProgress!
type
TProgressStage = (psStarting, psRunning, psEnding);
TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
PercentDone: Byte; RedrawNow: Boolean; const
R: TRect; const Msg: string) of object; property OnProgress: TProgressEvent;
Оно вызывается графическими объектами во время длительных операций. Параметр stage
означает стадию процесса (начало/протекание/завершение), a PercentDone — процент
сделанной работы. Сразу оговоримся, что не все из тех объектов, которые будут нами
описаны, вызывают обработчик события OnProgress.
Свойство:
property Empty: Boolean;
устанавливается в значение True, если графический объект пуст (в него не загружены
данные).
Высота и ширина графического объекта задаются свойствами:
property Height: Integer; property Width: Integer;
Для каждого дочернего типа эти параметры вычисляются своим способом. Наконец,
свойство:
property Modified: Boolean;
показывает, модифицировался ли данный графический объект. Это свойство
устанавливается в значение True внутри обработчика события OnChange.
Многие графические объекты при отрисовке должны быть прозрачными. Одни из них
прозрачны всегда (значок, метафайл), другие — в зависимости от значения свойства
property Transparent: Boolean;)
Наследники класса TGraphics
Класс Точечное изображение (TBitmap)
Это специальный класс, с помощью которого можно хранить, загружать из файла
или буфера обмена Windows, сохранять в файле или буфере обмена графические
точечные изображения в формате битовой карты (расширение. BMP), а также выполнять над ними ряд вспомогательных операций. Использование icnaccaTBitmap неразрывно связано с понятием холста.
Свойства и методы класса TBitrnap приведены в табл. 4.16 и 4.17.
Таблица 4,16. Свойства класса TBitmap
Свойство Назначение
Canvas Область изображения (холст), на которой можно выполнять рисование.
Данное свойство используется,'когда надо подготовить изображение,
нарисовав на нем, например, спрайты в скрытом режиме, а затем быстро
отобразить рисунок на экране. Такой подход применяется, в частности,
при создании анимационных эффектов
Empty Имеет значение True, если объект не содержит никакого изображения
PixelFormat Число битов на пиксел (глубина цвета)
ScanLine Массив указателей на каждую строку точечного изображения. Число
элементов в массиве равно значению свойства Height. С помощью
указателя можно получить доступ к конкретному пикселу. Пример
использования:
varBitMap:T8itMap; P: PbyteArray;... Р := BitMap.ScanLine[y];
for х 1= 0 to BitMap.Width-1 do
P[x] := Color;
TransparentColor Цвет, который будет считаться прозрачным (не отображаемым) при
выводе
изображения на экран
TransparentMode Способ определения прозрачного цвета. Цвет определяется по пикселу
в левом верхнем углу точечного изображения или задается программно
Таблица 4.17. Методы класса TBitmap
Метод Назначение
procedure Mask(
TransparentColor: TColor);
procedure Freelmage; Уменьшение объема памяти для хранения точечного изображения
путем уменьшения глубины цвета
procedure Load From ResourceID(Instance: THandle; ResID: Integer);Загрузка
изображения из ресурсов программы
procedure Mask( TransparentColor: TColor); Установка конкретного цвета изображения
в качестве
прозрачного
Класс TBitmap можно использовать для создания несложной спрайтовой мультипликации следующим образом. В память компьютера загружается заранее подготовленное фоновое изображение и набор небольших картинок-спрайтов. Затем испольРобота с трофикой 1 97
зуется динамически созданный объект класса TBitmap. Б него сначала копируется
фон, а затем на него накладываются отдельные спрайты. Данные заносятся в область
объекта, доступную для рисования (свойство Canvas). Можно использовать такие
методы холста, как Draw, CopyRect и другие. После этого сформированный рисунок
выводится на экран (свойство Bitmap свойства Picture компонента TImage) и становится видимым.
Промежуточный объект класса TBitmap необходим, потому что если выполнять вывод
спрайтов сразу на фоновое изображение, то при последующих циклах создания
итогового рисунка па этом изображении останутся предыдущие спрайты.
Класс Значок (TIcon)
Этот класс предназначен для работы с изображениями в формате значка Windows
(расширение файла .ICO). Его свойства и методы не отличаются от свойств и методов
класса TBitmap за исключением того, что значок всегда имеет определенный прозрачный цвет, а масштабировать его с помощью метода StretchOraw нельзя.
Класс Метафайл (TMetafile)
В Windows имеется специальный тип графических данных, называемый метафайлом (файлы с расширениями .ЕМ Ей .WMF).On отличается от точечного изображения
тем, что хранит не пикселы, а специальный код, который при выводе интерпретируется как набор команд типа ≪провести линию из точки А в точку В красным
цветом≫ или ≪закрасить прямоугольник≫.
Соответствующий класс имеет несколько специфических свойств.
Таблица 4.18. Свойства класса TMetafile
Свойство Назначение
Description Внутренний комментарий
Enhanced Имеет значение True, если метафайл хранится в формате .EMF
Inch Число точек на дюйм с учетом системы координат метафайла
MMWidth, MMHeight Ширина и высота изображения в условных точках (0,01
миллиметра}
Класс Изображение в формате JPG (TJPEGIrnage)(?)
Данный класс предназначен для работы с изображениями JPEG, представленными
в специальном формате, позволяющем компактно хранить большие рисунки.
') ВНИМАНИЕ При работе с этим классом использовать свойство Canvas, то есть
рисовать на холсте, нельзя. KnaccTJPEGImage применяется только
для отображения на экране.
Характерные свойства и методы класса TJPEGIrnage приведены в табл. 4.19 и 4.20.
198 Урок 4. Современные компоненты интерфейса пользователя
(???)
Таблица 4.19. Некоторые свойства класса TjPEGImage
Свойство
Com press) on Quality
Grayscale
Performance
PixelFormat
Progressive Display
Progressive En coding
Scale
Smoothing
Назначение
Соотношение между качеством изображения и размером файла, в котором это изображение
хранится. Свойство может принимать значения от! до 100. Большее значение соответствует
худшему качеству, номеньшей величине файла
Имеет значение True, если изображение будет выводиться на экран в серой шкале (255 оттенков),
что существенно повышает скорость распаковки рисунка
Более высокое качество (jpBestQuaLity) или более высокая скорость распаковки (jpBestSpeed)
при считывании Изображения из файла
Указывает формат рисунка jpeg (8-битный или 24-битный)
Имеет значение True, если выводимый рисунок будет показываться на экране постепенно, по
частям
Имеет значение True, если разрешается выводить рисунок по частям
Масштаб изображения. Возможные значения — jsFullSize (полный размер}, jsHaLf (в половину
размера), jsQuarter (в четверть размера), jsEightf) (в 1/8 размера)
Имеет значение True, если во время вывода изображения по частям оно будет постепенно
повышать свою четкость. 8 противном случае изображение будет выводиться небольшими
порциями построчно
Таблица 4.20. Некоторые методы класса TjPEGImage
Назначение
procedure Compress; Сжатие изображения в соответствии со значением свойства
Compression Quality
procedure DIBNeeded; Распаковка изображения JPEG в битовый формат BMP
procedure JPEGNeeded; Создание изображения JPEG на основе внутреннего битового
формата BMP__
42.Класс TComponent. Основные методы и свойства.
TComponent
Класс TComponent является базовым классом для всех компонентов среды Delphi.
Именно он обеспечивает, с одной стороны, возможность размещения их в палитре
компонентов, а с другой – возможность сохранения свойств в поток (благодаря наличию
среди предков TPersistent). Именно он вводит понятия имени компонента (свойство
Name) и свойства Tag, которое резервируется для нужд пользователя (честно говоря, в
основном используется для латания дыр в проектировании системы классов). Далее
следует отметить реализацию интерфейса IUnknown. Благодаря этому каждый
компонент может реализовывать интерфейсы.
Одним из наиболее важных и часто используемых свойств TComponent является
ComponentState. Оно отображает состояния компонента в контексте взаимодействия с
интегрированной средой разработки и состояния потокового ввода/вывода свойств.
В таблице 1 приведены возможные значения этого свойства:
csAncestor Компонент был размещен на форме, являющейся предком текущей формы.
Устанавливается только если флаг csDesigning установлен.
csDesigning Компонент находится в режиме проектирования (design time). Данный флаг
установлен при проектировании формы в IDE Delphi
csDestroying
Компонент сейчас будет разрушен
csFixups
Компонент связан с компонентом на другой форме, которая еще не
загружена. Флаг сбрасывается после загрузки всех необходимых форм
csFreeNotification Один или несколько компонентов должны быть извещены при
разрушении данного объекта. Флаг устанавливается вызовом метода FreeNotification.
csInline
Компонент является контейнером и может редактироваться во время
проектирования и внедрен в форму. Данный флаг используется для идентификации
фреймов во время загрузки и сохранения формы.
csLoading Компонент загружается из файла формы
csReading Компонент считывает значения своих свойств из потока
csWriting Компонент записывает значения своих свойств в поток
csUpdating Компонент требует обновления, для актуализации изменений,
произведенных с ним в родительском классе. Устанавливается только при установке
флага csAncestor
csDesignInstance Данный компонент является корневым компонентом дизайнера форм.
Например, фрейм в режиме проектирования. Однако, фрейм уже размещенный на
форме, выступает как обычный компонент и не является корневым объектом.
Распространенной практикой использования свойства ComponentState является
определение, манипулируют ли компонентом во время выполнения программы или во
время ее проектирования. Например, компонент TImage использует это свойство для
того, чтобы нарисовать пунктирную границу вокруг элемента управления на этапе
проектирования. Следующий фрагмент кода иллюстрирует, как это происходит:if
csDesigning in ComponentState then
with inherited Canvas do
begin
Pen.Style := psDash;
Brush.Style := bsClear;
Rectangle(0, 0, Width, Height);
end;
…… {Продолжение рисования }
Класс TComponent развивает концепцию принадлежности (ownership), которая заложена
в TPersistent и распространяется на все компоненты. Суть понятия принадлежности
заключается в том, что определенный компонент отвечает за уничтожение других
компонентов, которыми он владеет. Это освобождает разработчика от многих рутинных
действий. Например, форма при уничтожении не «забывает» уничтожить все
компоненты, которые на ней находятся. В свою очередь, заканчивая работу, приложение
разрушает работы все свои формы.
Данная концепция реализуется с помощью двух свойств:
Owner, который ссылается на другой компонент, как на своего владельца. В свою
очередь любой компонент может сам выступать в роли владельца.
Components – массив, содержащий ссылки на компоненты, которыми владеет данный
класс.
Конструктор компонента принимает параметр Owner, указывающий на владельца. Если
передаваемый в конструкторе владелец существует, то ссылка на созданный
конструктором компонент сохраняется в массиве Components владельца. Этот механизм
позволяет осуществлять разрушение компонентов, принадлежащих владельцу. Код для
автоматического разрушения размещен в деструкторе TComponent, и, следовательно,
используется всеми наследниками данного класса.
Компонент-владелец может быть не задан. В этом случае реализация корректного
уничтожения компонента ложится на разработчика.
Еще одним довольно полезным методом является Notification (уведомление). Этот метод
вызывается каждый раз, когда компонент вставляется или удаляется из списка
владельца. Метод Notification перекрывается в потомках TComponent, чтобы обеспечить
действительность ссылок на внешние компоненты.
Обеспечение метода Notification особенно актуально на этапе проектирования. В
качестве примера приведем код метода Notification компонента метки. Данный код
должен обеспечить корректировку свойства FocusControl метки. Свойство FocusControl
указывает на оконный элемент управления, с которым ассоциирована метка.procedure
TCustomLabel.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FFocusControl) then
FFocusControl := nil;
end;
Данный код при вызове, обнаружив, что удаляется внешний компонент, на который
ссылается свойство FocusControl, обнуляет указатель на него. Если этого не делать, то
после удаления внешнего компонента свойство FocusControl будет содержать указатель
на несуществующий компонент.
TComponent предоставляет возможность получения сообщений об удалении любого
компонента. Для указания того, что ваш компонент должен получать извещение об
удалении другого компонента, в TComponent существует метод FreeNotification.
Единственный параметр метода AComponent:TComponent указывает на тот компонент,
извещение об удалении которого вы хотите обработать. Другой метод –
RemoveFreeNotification(AComponent: TComponent) – позволяет отписаться от получения
извещений об удалении компонента, переданного в качестве параметра метода.
Метод Loaded – инструмент для корректной инициализации компонента после
окончания чтения значений свойств из потока. Этот виртуальный метод вызывается
сразу после того, как все значения свойств загружены в компонент. Вызов Loaded
происходит раньше, чем форма отобразится. Это позволяет избежать ненужной
перерисовки компонента. Особенно часто этот метод используется для компонентов
манипулирования данными. В самом деле, до тех пор, пока не будет инициализированы
все свойства, например DBGrid, смысла в отображении данных на нем абсолютно нет.
Удачным примером использования Loaded является метод пользовательского класса
TCustomRadioGroup (элемент группы переключателей):procedure
TCustomRadioGroup.Loaded;
begin
inherited Loaded;
ArrangeButtons;
end;
В данном методе сразу после загрузки группы переключателей производится их
выравнивание.
Теперь, рассмотрев свойства и методы класса TComponent, попробуем построить на его
базе несколько реальных компонентов. Это так называемые невизуальные компоненты,
назначение которых – инкапсулировать некоторый часто используемый код.
Классическим примером такого компонента является всем известный TTimer.
Псевдовизуальные компоненты (в их число входят диалоги, компоненты для настройки
хинтов и т.д.) также являются подмножеством невизуальных компонентов и,
следовательно, также являются прямыми потомками TComponent.
На этапе проектирования приложения они отображаются в виде пиктограммы на форме,
а на этапе выполнения программы вообще не видны пользователю. Главная причина
создания невизуальных компонентов – это возможность удобной визуальной настройки
их свойств в Object Inspector. Внутри себя данные компоненты могут инкапсулировать
практически любые участки кода. Основные типы задач, выполняемых невизуальными
компонентами это:
Создание классов-оберток функций API Windows.
Создание диалогов
Инкапсуляция участков кода, выполняющих какие-либо функции и имеющих набор
входных параметров, влияющий на их поведение.
С первого взгляда компоненты диалогов могут показаться визуальными, однако на
самом деле они инкапсулируют формы диалогов и представляют удобный интерфейс
настройки свойств этих форм.
В пределах раздела построения невизуальных компонентов мы рассмотрим два примера:
компонент-регулятор уровня громкости звука и компонент-диалог поиска записи в
наборе данных.
43.Принципы визуального программирования. Использование Object
Inspector. Обработчики событий.
Среда разработки Delphi ориентирована, прежде всего, на создание программ для
Windows, хотя имеется возможность подготовки исходных текстов, на основе которых
будет собрана программа, одинаково работающая в Windows и Linux. При этом особое
внимание уделяется возможности визуальной разработки приложений с помощью
большого набора готовых компонентов (стандартных классов), позволяющих избежать
ручного кодирования. Эти компоненты охватывают практически все аспекты применения
современных информационных технологий.
Использование визуальных компонентов
Компоненты первостепенной важности расположены на панели Standard (Стандартные)
палитры компонентов (рис. 2.1, стр 98). Они соответствуют основным элементам
управления Windows, без которых не обходится ни одна программа.
Рассмотрим процесс создания простейшей программы
Прежде всего надо закрыть все текущие файлы Delphi 7, которые имеют отношение к
консольной программе. Это выполняется с помощью команды File > Close All (Файл >
Закрыть все). После этого надо создать заготовку будущего приложения Windows,
выполнив команду File > New >Application (Файл > Создать >Приложение). На экране
возникнет пустая форма — прообраз будущего главного окна программы, а в редакторе откроется файл Unitl.pas, соответствующий модулю Unitl, в котором хранится
описание работы этой формы (она называется Forml). В разделе реализации выполняется
подключение стандартных модулей:
Interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs;
затем объявляется тип TForml, содержащий описание формы, и декларируется одна
переменная этого типа:
var Forml: TForml;
ЗАМЕЧАНИЕ
Все компоненты Delphi 7 хранятся в библиотеке визуальных компонентов Visual
Component Library (VCL). Каждый из компонентов,а также форма (будущее окно)
описаны соответствующими классами Паскаля: к названию компонента добавляется буква
Т; например, форма описывается классом TForm, кнопка (компонент Button) — классом
TButton и так далее.
Библиотека визуальных компонентов VCL и ее базовые классы
Все классы библиотеки визуальных компонентов произошли от группы базовых классов,
которые лежат в основе иерархии VCL. Самый общий предок компонентов — это
класс TObject, инкапсулирующий простейший объект. Как известно (см. гл. 1), каждый
объект наследует свойства и методы родительского класса. К объекту можно добавить
новые свойства и методы, но нельзя удалить унаследованные. Объект-наследник в свою
очередь может стать родительским для нового класса, который унаследует возможности
всех своих предков.
Поэтому иерархия базовых классов VCL продумана чрезвычайно тщательно — ведь на их
основе создано все множество компонентов, используемых в Delphi. Особое место среди
базовых классов, помимо TObject, занимают TComponent (от него происходят все
компоненты) и TControl(от него происходят все элементы управления).
Когда создается новое приложение, на основании этих классов система Delphi 7
формирует новый тип данных, наследующий характеристики своего родителя:
type
TForml = class(TForm1)
private
{ Private declarations }
public
{ Public declarations }
end;
Форма Forml представлена в программе типом TForml, который исходно не имеет
новых полей и методов, так как форма пуста. В дальнейшем члены класса TForml
будут добавляться по мере необходимости, как автоматически (при размещении
их на форме в Проектировщике форм), так и самим программистом.
Далее в модуле следует раздел реализации, в котором имеется всего одна директива
компилятора
{$R *.DFM)
Она предназначена для связывания модуля с описанием соответствующей ему формы.
ЗАМЕЧАНИЕ Описание формы и всех размещенных на ней компонентов система
Delphi 7 хранит в файлах с расширением .DFM. Эти файлы имеют бычный текстовый
формат (в отличие от прежних версий Delphi, де они хранились в двоичном виде, что
делало невозможным их нализ и ручное редактирование).
Создаваемая в среде Delphi 7 программа состоит из нескольких файлов. Это файлы с
исходными текстами на Паскале и файлы описаний форм. Все они связаны друг с
другом. Кроме того, в среде Delphi 7 имеется множество настроек, которые желательно
сохранять на жестком диске в промежутках между сеансами работы с системой.
Такой набор файлов, в которых содержатся исходные тексты и различные настройки,
называется проектом. Разделение на проекты очень удобно, потому что позволяет
выделить все файлы, относящиеся к конкретной задаче, в отдельную группу.
Чтобы сохранить текущий проект, надо выполнить команду File >Save All
(Файл > Сохранить все) или щелкнуть на одноименной командной кнопке.
Сначала разработчику будет предложено сохранить файл с исходным текстом Unitl.pas), а
затем — файл проекта Project1с расширением .DPR. Сохранять их лучше в отдельном
каталоге, специально отведенном для данного проекта. В этом же каталоге компилятор в
будущем создаст и исполнимое приложение.
Теперь можно приступать к этапу проектирования пользовательского интерфейса —
подготовке внешнего вида окна программы и размещению на нем элементов управления.
Для этого надо переключиться в Проектировщик форм с помощью клавиши F12 или
командной кнопки Toggle Form/Unit (Выбрать Форму/Модуль).
Проектировщик форм работает по принципу WYSIWYG, в соответствии с которым
окно созданной программы будет выглядеть в точности так, как оно было подготовлено в Проектировщике форм. Например, изменить размер формы можно стандартным способом для системы Windows — протягиванием мыши.
ВНИМАНИЕ Создаваемая в Проектировщике форма — это только внешнее
представление будущего окна, а не работоспособное приложение.
Любой объект Пректировщика форм доступен в исходном тексте программы по его
имени. Имя (или название) объекта — это одно из его свойств, которые можно изменять и настраивать в Инспекторе объектов. Соответствующее свойство называется
Name (Имя). Оно имеется у всех объектов без исключения и расположено в Инспекторе
объектов в категории Miscellaneous (Дополнительныв) (рис. 2.2, стр 100).
Первоначально значением этого свойства является строка Forml, сгенерированная
системой Delphi 7 автоматически.
ЗАМЕЧАНИЕ Общепринято определять назвонив компонента в соответствии с
названием класса.
Разместить на форме новый компонент не представляет труда. Для этого сначала нужно
его выбрать (щелкнуть по нему мышью) в палитре компонентов, а затем щелкнуть мышью
по точке рабочего пространства формы, где должен располагаться левый верхний угол
компонента. Первоначальные размеры и положение компонента на форме легко
изменяются мышью, поэтому добиваться полного сходства с рисунком необязательно.Как
только вы начнете вводить новую надпись, вид компонента на форме начнет меняться,
динамически отражая все изменения, производимые вами в окне Инспектора
объектов.Delphi обладает замечательной способностью визуальной реализации любых
изменений свойств компонента не только на этапе прогона программы, но и на этапе
проектирования формы.
ЗАМЕЧАНИЕ После размещения объектов на форме их можно свободно перетаскивать
при помощи мыши в другое место, причем не только по одному, но и группой. Для этого
надо выделить группу элементов протягиванием (они помечаются серыми маркерами) и
переместить их как один объект.
Щелкните мышью внутри обрамляющих надпись черных прямоугольников и, не отпуская
левую кнопку мыши, сместите ее указатель так, чтобы он расположился левее в центре
окна, после чего отпустите кнопку. Таким способом можно буксировать компонент по
форме, добиваясь нужного его положения.
С помощью обрамляющих черных квадратиков можно изменять размеры компонента. Для
этого следует поместить острие указателя мыши над одним из них (в этот момент
указатель меняет свою форму на двунаправленную стрелку), затем нажать левую кнопку
мыши и, не отпуская ее, буксировать сторону или угол компонента в нужном
направлении, после чего отпустить кнопку.
Замечу, что все видимые компоненты[В Delphi могут использоваться как видимые, так и
невидимые компоненты. Невидимые компоненты не имеют визуального отображения на
этапе прогона программы.] имеют свойства Left (Слева), тор (Сверху), width (Ширина) и
Height (Высота), числовые значения которых определяют положение левого верхнего угла
компонента и его размеры в так называемых пикселях, т. е. в минимальных по размеру
точках экрана, светимостью которых может управлять программа. При буксировании
компонента или изменении его размеров мышью эти значения автоматически меняются и
наоборот - изменение этих свойств в окне Инспектора объектов приводит к
соответствующему изменению положения и размеров компонента. В Delphi 4, 5 и 6
значения Left и тор автоматически появляются в небольшом окне рядом с указателем
мыши при буксировке компонента по форме.
3.3. РЕАКЦИЯ НА СОБЫТИЯ
Настало время познакомиться с языком программирования Object Pascal, т. к. только с его
помощью мы можем придать программе нужную функциональность и заставить ее
выполнять полезную работу. Как уже говорилось, функциональность программы
определяется совокупностью ее реакций на те или иные события. В связи с этим каждый
компонент помимо свойств характеризуется также набором событий, на которые он может
реагировать.
3.3.1. Модификация формы
Проведем очередную модернизацию нашей первой программы: вставим в ее форму еще
один компонент - кнопку - и заставим ее откликаться на событие, связанное с нажатием
левой кнопки мыши.
Компонент кнопка изображается пиктограммой на странице standard палитры
компонентов. Поместите этот компонент на форму и расположите его ниже метки и
посередине формы (рис. 3.2).
3.3.2. Обработчик события OnClick
При щелчке по кнопке мышью в работающей программе возникает событие OnClick. (По
щелчку). Пока это событие никак не обрабатывается программой, и поэтому “нажатие”
кнопки не приведет ни к каким последствиям. Чтобы заставить программу реагировать на
нажатие кнопки, необходимо написать на языке object pascal фрагмент программы,
который называется обработчиком события.
Рис. 3.2 Форма со вставленной кнопкой
Этот фрагмент должен представлять собой последовательность текстовых строк, в
которых программист указывает, что именно должна делать программа в ответ на нажатие
кнопки. Фрагмент оформляется в виде специальной подпрограммы языка Object Pascal процедуры.
Чтобы заставить Delphi самостоятельно сделать заготовку для процедуры обработчика
события OnClick, дважды подряд без заметной паузы щелкните мышью по вновь
вставленному компоненту[У начинающего пользователя Windows не всегда получается
двойной щелчок. Этот нехитрый прием широко используется и в Windows, и в Delphi, так
что вам придется потренироваться в его освоении: без него Delphi не вставит заготовку
процедуры, и вам понадобится самостоятельно писать не только этот текст, но и
производить дополнительные манипуляции с Инспектором объектов и окном кода.]. В
ответ Delphi активизирует окно кода, и вы увидите в нем такой текстовый фрагмент:
procedure TFormI.ButtonlClick(Sender: TObject);
begin
end;
Попробуем разобраться в том, что он содержит. Слово procedure извещает компилятор о
начале подпрограммы-процедуры (в Delphi могут использоваться также подпрограммыфункции; в этом случае вместо procedure (процедура) используется слово function
(функция); разницу между процедурами и функциями мы обсудим позже). За ним следует
имя процедуры TFormi.ButtonlClick. Это имя - составное: оно состоит из имени класса
TForm1 и собственно имени процедуры Button1Click.
Классами в Delphi называются функционально законченные фрагменты программ,
служащие образцами для создания подобных себе экземпляров. Однажды создав класс,
программист может включать его экземпляры (копии) в разные программы или в разные
места одной и той же программы. Такой подход способствует максимально высокой
продуктивности программирования за счет использования ранее написанных фрагментов
программ. В состав Delphi входит несколько сотен классов, созданных программистами
корпорации Borland (так называемых стандартных классов). Совокупность стандартных
классов определяет мощные возможности этой системы программирования.
Каждый компонент принадлежит к строго определенному классу, а все конкретные
экземпляры компонентов, вставляемые в форму, получают имя класса с добавленным
числовым индексом. По используемому в Delphi соглашению все имена классов
начинаются с буквы Т. Таким образом, имя TFormi означает имя класса, созданного по
образцу стандартного класса TForm. Если вы посмотрите начало текста в окне кода, то
увидите следующие строки:
type
TFormi = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure ButtonlClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
varForm1: TForm1;
Строка
TForm1 = class(TForm)
определяет новый класс Tform1, который порожден от (создан по образцу) стандартного
класса TForm. Строка
Form1: Tform1;
создает экземпляр этого класса с именем Formi. Стандартный класс TForm описывает
пустое Windows-окно, в то время как класс TFormI описывает окно с уже вставленными в
него компонентами метка и кнопка. Описание этих компонентов содержат строки
Button1: TButton;
Label 1: TLabel;
Они указывают, что компонент Buttoni (Кнопка!) представляет собой экземпляр
стандартного класса TButton, а компонент Label 1 (Метка 1) - экземпляр класса TLabel.
За именем процедуры TFormi. Buttoniciick в круглых скобках следует описание параметра
вызова
Sender: TObject
(параметр с именем Sender принадлежит классу TObject). Как мы увидим дальше,
процедуры могут иметь не один, а несколько параметров вызова или не иметь их вовсе.
Параметры вызова (если они есть) служат для настройки реализованного в процедуре
алгоритма на выполнение конкретной работы. Параметр Sender вставлен Delphi “на
всякий случай”: с его помощью подпрограмма Button1Click может при желании
определить, какой именно компонент создал событие OnClick. Вся строка в целом
procedure TFormI.Buttoniciick(Sender: TObject);
называется заголовком процедуры. Ее завершает символ “;”. Этот символ играет важную
роль в Object Pascal, т. к. показывает компи-- лятору на конец предложения языка. Из
отдельных предложений составляется весь текст программы. В конце каждого
предложения нужно ставить точку с запятой - это обязательное требование синтаксиса
языка.
Три следующие строки определяют тело процедуры:
begin
end;
Слово begin (начало) сигнализирует компилятору о начале последовательности
предложений, описывающих алгоритм работы процедуры, а слово end (конец) - о конце
этой последовательности. В нашем случае тело процедуры пока еще не содержит
описания каких-либо действий, что и неудивительно: Delphi лишь создала заготовку для
процедуры, но она ничего “не знает” о том, для чего эта процедура предназначена.
Наполнить тело нужными предложениями -задача программиста.
Для нас важно то обстоятельство, что каждый раз при нажатии кнопки Button1 управление
будет передаваться в тело процедуры, а значит, между словами begin и end мы можем
написать фрагмент программы, который будет выполняться в ответ на это событие.
Чтобы убедиться в этом, сделаем нашу кнопку “звучащей”: напишите в пустой строке
между словами begin...end следующее предложение:
MessageBeep (МВ_ОК);
и сделайте прогон программы[Если вы работаете с версией Delphi 1, используйте строку
MessageBeep (0);], предварительно включив звуковую систему компьютера. Теперь в
ответ на нажатие кнопки Button1 в динамике компьютера будет раздаваться звуковой
сигнал, т. к. вставленная строка реализует обращение к стандартной процедуре, которая
умеет извлекать из динамика различные стандартные для Windows звуки.
3.3.3. Динамическое изменение свойств компонента
Поскольку кнопка Buttoni в нашей программе способна “звучать”, полезно изменить ее
надпись: вместо умалчиваемой надписи Buttoni, которую автоматически формирует
Delphi по имени компонента, назовем кнопку, например, “Звук”. Проще всего это сделать
с помощью окна формы и Инспектора объектов, т. е. на этапе конструирования формы
(для этого нужно просто изменить свойство caption компонента Button1 в окне Инспектора
объектов), но для более полного знакомства с Delphi мы рассмотрим другой способ динамического[К сожалению, эта процедура работает не всегда: если ваш ПК не
оснащен звуковой картой, динамик будет молчать. В этом случае вместо MessageBeep
(MB_OK) напишите просто Веер. Изменения на этапе конструирования называются
статическими, а в ходе прогона программы - динамическими.] изменения надписи на
этапе прогона программы. Для этого создадим обработчик события OnCreate (По
созданию) для формы и изменим в нем это свойство.
Событие OnCreate возникает после создания windows-окна, но до появления этого окна на
экране. Чтобы создать обработчик этого события, раскройте список компонентов в
верхней части окна Инспектора объектов, выберите компонент Formi и дважды щелкните
по свойству OnCreate на странице Events этого компонента (щелкать нужно по правой
части строки oncreate). В ответ Delphi вновь активизирует окно кода и покажет вам
заготовку для процедуры TForm1. FormCreate. Отредактируйте ее следующим образом:
procedure TFormi.FormCreate(Sender: TObject);
begin
Button1.Caption:= 'Звук';
end;
Единственная вставленная нами строка представляет собой так называемый оператор
присваивания языка Object Pascal. В левой части оператора указывается свойство Buttoni.
caption, а в правой части - значение ' звук ', которое мы хотим придать этому свойству.
Связывает обе части комбинация символов “: =”, которая читается как “присвоить
значение”. Символы “:=” всегда пишутся слитно, без разделяющих пробелов, хотя перед
двоеточием и после знака равенства можно для лучшей читаемости программы вставлять
пробелы, что мы и сделали. Как и любое другое предложение языка, оператор
присваивания завершается точкой с запятой.
Составное имя Button1.Caption необходимо для точного указания компилятору, о каком
свойстве идет речь: в нашей программе используются три компонента (включая саму
форму), каждый из которых имеет свойство Caption; уточняющий префикс Button1
заставит изменить это свойство у кнопки, а не у метки или формы. Присваиваемое
свойству значение является текстовой строкой. По правилам Object Pascal текстовая
строка должна заключаться в обрамляющие апострофы. Внутри апострофов можно
написать любое количество произвольных символов - именно они (без обрамляющих
апострофов) будут определять новую надпись на кнопке.
После очередного прогона программы вы увидите измененную надпись на кнопке, а мы
сделаем важный вывод: любое свойство любого компонента можно
изменять динамически, т. е. в ходе исполнения программы.
3.4. НЕКОТОРЫЕ ИТОГИ
Перед тем как навсегда расстаться с нашей первой программой, сформулируем главные
выводы, полученные в ходе работы с ней.








Процесс создания Delphi-программы разбивается на две фазы: фазу
конструирования формы и фазу кодирования.
Конструирование формы осуществляется с помощью выбора
компонентов из палитры и размещения их на форме.
Программист может перемещать любой размещенный на форме
компонент и изменять его размеры с помощью мыши.
Чтобы придать компоненту нужные свойства, используется страница
Properties Инспектора объектов.
Чтобы компонент мог откликаться на то или иное событие, программист
должен создать обработчик события и указать его имя на странице Events
Инспектора объектов.
Обработчик события оформляется в виде процедуры, имеющей составное
имя. Первая часть имени представляет собой имя класса для формы,
вторая часть отделяется от первой точкой и может быть произвольной.
Если Delphi автоматически формирует заготовку для обработчика, то
вторая часть имени представляет собой объединение имени компонента и
имени события без предлога On.
Тело процедуры ограничено словами begin... end и состоит из отдельных
предложений (операторов) языка Object Pascal. В конце каждого
предложения ставится точка с запятой.
Свойства компонента могут изменяться на этапе прогона программы.
После загрузки на экране появится главное окно.
Верхнею часть окна занимает меню и панель инструментов. В левой части окна
находятся Object TreeViewи Object Inspector. В центре находится форма
разрабатываемого приложения и форма для внесения кода.
Object TreeView – служит для отображения всех объектов расположенных на
текущей форме разрабатываемого проекта.
Object Inspector – служит для отображения и редактирование свойств компонентов
расположенных на форме разрабатываемого приложения.
Теперь введем понятия «проект». Проект – это совокупность файлов, которые
используются средой разработки для итоговой генерации программы. Когда мы будем
создавать с вами первый проект, то познакомимся со всеми составляющими проекта и его
структурой.
Теперь рассмотрим состав главного меню. Оно позволяет вызывать все
инструменты, необходимые для работы с проектом. Рассмотрим назначение раздела меню
и связанные с ними функции:
File – содержит набор команд для работы с файлами, позволяет создавать новые
проекты, добавлять новые файлы в проект на основе различных шаблонов,
переименовывать файлы проекта, а также распечатывать их. Сюда же включена команда
закрытия среды разработки;
Edit – здесь, в соответствии с названием, расположены команды, предназначенные
для редактирования текста, удаления и перемещения его в буфер обмена, вставки текста
из буфера и отмены операций редактировании. Стоит отметить, что все эти команды
работают не только с текстом, но и с компонентами в конструкторе форм – элементами
управления, из которых «собирается» интерфейс приложения. Кроме того, здесь же
сосредоточены команды управления положением компонентов на поверхности формы, а
также фиксации компонентов – функции, позволяющей заблокировать компоненты, что
бы в последствии его случайно не изменить;
Search – содержит набор команд для работы с текстом, его поиска и замены,
причем и то и другое может производится как в одном файле, так и во всех файлах
проекта, либо в любом каталоге и его подкаталогах;
View – под этим названием объединены команды вызова инструмента управления
проектом, такие как инспектор объектов, конструктор форм, менеджер проектов и т.д.;
Project – предназначен для того чтобы добавлять и удалять модули проекта,
сохранять проект в репозитории, добавлять проекты в группу и убирать их из неё,
компилировать как отдельные проекты, так и все проекты в группе, загружать файл
самого проекта в редактор кода, а также вызывать диалог настройки свойств проекта;
Run – позволяет запускать проект на выполнение как под отладчиком, так и без
него, конфигурировать строку параметров проекта при запуске, производить отладку,
задать точки останова, осуществлять пошаговое выполнение кода, просматривать
значения переменных и изменять их;
Component – здесь сосредоточены команды, предназначенные для установки
новых компонентов и пакетов компонентов и создания новых компонентов и шаблонов
компонентов;
Database – здесь сосредоточены команды управления базами дынных;
Tools – позволяет настраивать свойства рабочей среды Delphi и отладчика,
произвести настройки репозитория, добавлять и удалять дополнительные утилиты, а так
же команды запуска этих самых утилит;
Window – позволяет переключаться между окнами, если вы отроете какой – либо
модуль для редактирования в новом окне;
Help – объединяет команды вызова справочной системы Delphi и её настройки, а
так же позволяет обратиться к Web-ресурсам компании Borland для получения
дополнительной информации.
Теперь рассмотрим панель инструментов. Вы можете настроить панель
инструментов таким образом, что бы вам было удобно работать. Для этого нужно вызвать
диалоговое окно по ссылке View-Toolbars-Customize. На закладке Toolbars вы можете
выбрать те группы кнопок, которые желаете видеть на панели инструментов. Вам следует
снять флажок слева от имени группы, чтобы скрыть её, и поставить – чтобы показать.
ЗакладкаCommands, позволяет указать, какие кнопки нужно показать в группе. Настройка
выполняется следующим образом. Если вы хотите добавить кнопку на панель
инструментов, то нажмите и удерживайте левую кнопку мыши на названии этой кнопки в
списке Commands, перетащите её на панель инструментов и там отпустите кнопку мыши.
Если вы хотите скрыть какую-либо кнопку, то подобным образом перетащите её в
обратном направлении с панели инструментов на окно диалога настройки. В дополнение к
этому на закладке Optionsданного окна вы можете установить показывать или нет
подсказки при перемещении курсора мыши над кнопками панели инструментов
(флажок Show tooltips) и включить или нет в подсказку комбинации «быстрых» клавиш
для вызова команды, запускаемой щелчком по кнопке
(флажок Show shortcut keys on tooltips).
Ниже будут перечислены и описаны некоторые палитры компонентов:
Standard. Большинство компонентов на этой странице являются аналогами
экранных элементов самой Windows. Меню, кнопки, полосы прокрутки — здесь есть все.
Но компоненты Delphi обладают также некоторыми удобными дополнительными
встроенными возможностям.
Additional. Эта страница содержит более развитые компоненты. Данная страница
также содержит компоненты, главное назначение которых — отображение графической
информации. Компонент Image загружает и отображает растровые изображения, а
компонент Shape, украсит ваши формы окружностями, квадратами и т.д.
System. Страница System содержит компоненты, обрабатывающие обмен высокого
уровня между программами посредством OLE (Object Linking and Embedding). А
компонент Timer может генерировать события через определенные, заранее
установленные промежутки времени.
Win32. Эта страница содержит компоненты, позволяющие созданным с помощью
Delphi программам использовать такие нововведения в пользовательском интерфейсе 32разрядной Windows, как просмотр древовидных структур, просмотр списков, панель
состояния, присутствующая в интерфейсе программы Windows Explorer (Проводник),
расширенный текстовый редактор и др.
Dialogs. Windows ввела в употребление стандартные диалоговые окна для
операций над файлами, выбора шрифтов, цветов и т.д. Однако для использования их в
обычной программе Windows может потребоваться написать немало вспомогательного
кода. Страница, Dialogs предоставляет программам Delphi простой доступ к этим
стандартным диалоговым окнам.
Data Access и Data Controls. Delphi использует механизм баз данных компании
Borland (Borland Database Engine, BDE) для организации доступа к файлам баз данных
различных форматов. Компоненты этих двух страниц облегчают программам Delphi
использование сервиса баз данных, предоставляемого BDE, например
многопользовательского считывания, записи, индексации и выдачи запросов для таблиц
dBASE и Paradox. С использованием этих компонентов создание программы просмотра и
редактирования базы данных почти не требует программирования.
Win 3.1. На этой странице, как в сибирской ссылке, находятся компоненты Delphi
1.0, возможности которых перекрываются аналогичными компонентами Windows XP.
Internet. Эта страница предоставляет компоненты для разработки приложений,
позволяющих создавать HTML-файлы непосредственно из файлов баз данных и других
типов, взаимодействующих с другими приложениями для Internet. Delphi дает вам
возможность создавать приложения для Web-сервера в виде DLL-файлов : (Dynamic Link
Library — Динамически компонуемая библиотека), способных содержать невизуальные
компоненты. С помощью компонентов страницы Internet довольно просто создавать
обработчики событий для обращения к определенному URL (Uniform Resource Locator —
Унифицированный локатор ресурса), представлению документов в HTML-формате и
пересылки их клиент-программе.
Samples. Эта отличающаяся полнотой страница содержит компоненты, которые не
встроены в Delphi, но демонстрируют мощь системы компонентов. Для этих компонентов
нет встроенной интерактивной справки. Все же они не менее полезны, чем компоненты с
других страниц.
ActiveX. Эта страница содержит компоненты ActiveX, разработанные
независимыми производителями программного обеспечения: сетка, диаграмма, средство
проверки правописания.
QReport. Эта страница предоставляет компоненты баз данных. Здесь содержатся
особые версии надписей, полей, примечаний и других элементов управления.
Теперь перейдем к рассмотрению конструктора форм. Он занимает центральную
часть окна. Там же расположено исходный код программы, а точнее окно содержащие его.
Несмотря на то, что вы еще не написали ни единой строчки текста программы, это окно
уже содержит код необходимый для отображения окна программы. Запомните, что при
создании проекта у вас уже автоматически готова основа программы.
Теперь перейдем к инспектору объектов (Object Inspector). Он имеет две
вкладки Properties (Свойства)и Events (События). Первая вкладка предназначена для
редактирования свойств объектов. Объект может обладать различными свойствами, и эти
свойства могут объединяться в группы. Вторая вкладка используется для описания
событий, на которые будет реагировать выделенный объект.
Особенности доступа к свойствам компонентов и рекомендации по использованию свойств
Для доступа к свойствам компонента, значок которого расположен на форме (или для доступа к
свойствам формы), достаточно навести указатель "мыши" на этот значок (для формы – на участок формы, не
занятый значками) и выполнить один щелчок левой кнопкой "мыши", после чего при открытой
вкладке Properties окна Object Inspector, будет получен доступ к свойствам компонента (или формы).
Кроме того, для доступа к свойствам можно использовать значки компонентов и формы в
окне Object TreeView или строки раскрывающегося списка с именами компонентов в верхней части
окна Object Inspector.
Не рекомендуется оставлять неизменными предлагаемые по умолчанию для компонентов и формы значения
свойства Caption, а также значения тех других свойств, которые (значения) могут быть видны в окне
выполняемого приложения (такие, например, как Form1, Button1, Edit1 и т.п.).
Особенности использования обработчиков событий и рекомендации по их
использованию
Для создания в окне модуля проекта обработчика заданного события для какого-либо компонента (или
формы) можно тем или иным способом выбрать этот компонент (или форму) по аналогии со способами выбора
компонента для изменения его свойств и затем на вкладке Events окна Object Inspector найти строку с заданным
событием, в этой же строке навести указатель "мыши" на область раскрывающегося списка правее названия
события и выполнить двойной щелчок левой кнопкой "мыши".
При этом в окне модуля будет создана "пустая" процедура обработчика именно заданного события.
Для того чтобы использовать для обработки заданного события уже имеющийся обработчик событий,
достаточно на вкладке Events окна Object Inspector найти строку с заданным событием и в этой же строке в
области раскрывающегося списка правее названия события выбрать один из имеющихся обработчиков
событий.
Для быстрого доступа к обработчикам событий компонента, значок которого расположен на форме (или к
обработчикам событий формы), достаточно навести указатель "мыши" на этот значок (для формы – на участок
формы, не занятый значками) и выполнить двойной щелчок левой кнопкой "мыши".
В процедуре обработчика события рекомендуется вводить инструкции (операторы) только между
строками с begin и end;. Настоятельно не рекомендуется без достаточного опыта работы со средой
программирования "трогать" чем-либо, кроме комментариев, эти строки с begin и end;, строку с именем и
параметрами созданной процедуры, а также последнюю строку модуля, содержащую end..
44.RTTI (Run-time type information (RTTI) - это специальный механизм определения
типа объекта во время выполнения.)в программе на Delphi. Использование
RTTI для работы с ресурсами форм.
Delphi— это мощная среда визуальной разработки программ сочетающая в себе весьма
простой и эффективный язык программирования, удивительный по быстроте
компилятор и подкупающую открытость (в состав Delphi входят исходные тексты
стандартных модулей и практически всех компонент библиотеки VCL). Однако, как и
на солнце, так и в Delphi существуют пятна (на солнце черные, а в Delphi — белые),
пятна недокументированных (или почти не документированных) возможностей. Одно
из таких пятен — это информация о типах времени исполнения и методы работы с ней.
Информация о типах времени исполнения.(Runtime Type Information, RTTI) —это
данные, генерируемые компилятором Delphi о большинстве объектов вашей
программы. RTTI представляет собой возможность языка, обеспечивающее
приложение информацией об объектах (его имя, размер экземпляра, указатели на
класс-предок, имя класса и т. д.) и о простых типах во время работы программы. Сама
среда разработки использует RTTI для доступа к значениям свойств компонент,
сохраняемых и считываемых из dfm-файлов и для отображения их в Object Inspector,
Компилятор Delphi генерирует runtime информацию для простых типов,
используемых в программе, автоматически. Для объектов, RTTI информация
генерируется компилятором для свойств и методов, описанных в секции published в
следующих случаях:
Объект унаследован от объекта, дня которого генерируется такая
информация. В качестве примера можно назвать объект TPersistent.
2.
Декларация класса обрамлена директивами компилятора {$M+} и {$M-}.
Необходимо отметить, что published свойства ограничены по типу данных. Они могут
быть перечисляемым типом, строковым типом, классом, интерфейсом или событием
(указатель на метод класса). Также могут использоваться множества (set), если верхний
и нижний пределы их базового типа имеют порядковые значения между 0 и 31 (иначе
говоря, множество должно помещаться в байте, слове или двойном слове). Также
можно иметь published свойство любого из вещественных типов (за исключением
Real48). Свойство-массив не может быть published. Все методы могут быть published,
но класс не может иметь два или более перегруженных метода с одинаковыми
именами. Члены класса могут быть published, только если они являются классом или
интерфейсом.
1.
Корневой базовый класс для всех VCL объектов и компонент, TObject, содержит ряд
методов для работы с runtime информацией. Наиболее часто используемые из них
приведены в таблице
ClassType
Возвращает тип класса объекта. Вызывается неявно компилятором при определении
типа объекта при использовании операторов is и as
ClassName
Возвращает строку, содержащую название класса объекта. Например, для объекта типа
TForm вызов этой функции вернет строку "TForm"
ClassInfo
Возвращает указатель на runtime информацию объекта
Object Pascal предоставляет в распоряжение программиста два оператора, работа
которых основана на неявном для программиста использовании RTTI информации.
Это операторы is и as. Оператор is предназначен для проверки соответствия экземпляра
объекта заданному объектному типу. Так, выражение вида:
AObject is TSomeObjectType
является истинным в том случае, если объект AObject является экземпляром класса
TSomeObjectType или одного из порожденных от него классов. Следует отметить, что
определенная проверка происходит еще на этапе компиляции программы. если
фактические объект и класс несовместимы, компилятор выдаст ошибку в этом
операторе. Так, следующий программный код
if Edit1 is TForm
then ShowMessage('Враки!');
даже не будет пропущен компилятором, и он выдаст сообщение о не совместимости
типов (разумеется, что Edit1 — это компонент типа TEdit):
Incompatible types: 'TForm' and 'TEdit'.
Перейдем теперь к оператору as. Он введен в язык специально для приведения
объектных типов. Посредством него можно рассматривать экземпляр объекта как
принадлежащий к другому совместимому типу:
AObject as TSomeObjectType
Использование оператора as отличается от обычного способа приведения типов
TSomeObjectType(AObject)
наличием проверки на совместимость типов. Так при попытке приведения этого
оператора с несовместимым типом он сгенерирует исключение EInvalidCast.
Определенным недостатком операторов is и as является то, что присваиваемый
фактически тип должен быть известен на этапе компиляции программы и поэтому на
месте TSomeObjectType не может стоять переменная указателя на класс.
Для иллюстрации только что написанного рассмотрим небольшой пример.
Предположим у вас на форме имеется ряд компонент типа TEdit, и вы хотите
реализовать их очистку их свойств перед созданием формы. С применением RTTI это
можно сделать следующим программным кодом:
var
I: Integer;
begin
for I := 0 to ComponentCount - 1 do
if Components[I] is TEdit then
(Components[I] as TEdit).Text := '';
{ или так TEdit (Components[I]).Text := ''; }
end;
Хочу обратить ваше внимание, а то, что стандартное приведение типа в данном
примере предпочтительнее, поскольку в операторе if мы уже установили что
компонент является объектом нужного нам типа и дополнительная проверка
соответствия типов, проводимая оператором as, нам уже не нужна.
Первые шаги в понимании RTTI мы уже сделали. Теперь переходим к подробностям.
Все основополагающие определения типов, основные функции и процедуры для
работы с runtime информацией находятся в модуле TypInfo. Этот модуль содержит
две фундаментальные структуры для работы с RTTI — TTypeInfo и TTypeData (типы
указателей на них — PTypeInfo и PTypeData соответственно).
Суть работы с RTTI выглядит следующим образом. Получаем указатель на структуру
типа TTypeInfo (для объектов указатель можно получить, вызвав метод,
реализованный в TObject, ClassInfo, а для простых типов в модуле System существует
функция TypeInfo). Затем, посредством имеющегося указателя и вызова функции
GetTypeData получаем указатель на структуру типа TTypeData. Далее используя оба
указателя и функции модуля TypInfo творим маленькие чудеса.
Для пояснения написанного выше рассмотрим пример получения текстового вида
значений перечисляемого типа. Пусть, например, это будет тип TBrushStyle. Этот тип
описан в модуле Graphics следующим образом:
TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal,
bsCross, bsDiagCross);
Вот мы и попробуем получить конкретные значения этого типа в виде текстовых строк.
Для этого создайте пустую форму. Поместите на нее компонент типа TListBox с
именем ListBox1 и кнопку. Реализацию события OnClick кнопки замените следующим
кодом:
var
ATypeInfo: PTypeInfo;
ATypeData: PTypeData;
I: Integer;
S: string;
begin
ATypeInfo := TypeInfo(TBrushStyle);
ATypeData := GetTypeData(ATypeInfo);
for I := ATypeData.MinValue to ATypeData.MaxValue do
begin
S := GetEnumName(ATypeInfo, I);
ListBox1.Items.Add(S);
end;
end;
Ну вот, теперь, когда на вооружении у нас есть базовые знания о противнике, чье имя,
на первый взгляд выглядит непонятно и пугающее — RTTI настало время большого
примера.
Мы приступаем к созданию объекта опций для хранения различных параметров,
использующего в своей работе мощь RTTI на полную катушку. Чем же примечателен,
будет наш будущий класс? А тем, что он реализует сохранение в ini-файл и считывание
из него свои свойства секции published. Его потомки будут иметь способность
сохранять свойства, объявленные в секции published, и считывать их, не имея для
этого никакой собственной реализации. Надо лишь создать свойство, а все остальное
сделает наш базовый класс. Сохранение свойств организуется при уничтожении
объекта (т.е. при вызове деструктора класса), а считывание и инициализация
происходит при вызове конструктора класса. Декларация нашего класса имеет
следующий вид:
{$M+}
TOptions = class(TObject)
protected
FIniFile: TIniFile;
function Section: string;
procedure SaveProps;
procedure ReadProps;
public
constructor Create(const FileName: string);
destructor Destroy; override;
end;
{$M-}
Класс TOptions является производным от TObject и по этому, что бы компилятор
генерировал runtime информацию его надо объявлять директивами {$M+/-}.
Декларация класса весьма проста и вызвать затруднений в понимании не должна.
Теперь переходим к реализации методов.
constructor TOptions.Create(const FileName: string);
begin
FIniFile:=TIniFile.Create(FileName);
ReadProps;
end;
destructor TOptions.Destroy;
begin
SaveProps;
FIniFile.Free;
inherited Destroy;
end;
Как видно реализация конструктора и деструктора тривиальна. В конструкторе мы
создаем объект для работы с ini-файлом и организуем считывание свойств. В
деструкторе мы в сохраняем значения свойств в файл и уничтожаем файловый объект.
Всю нагрузку по реализации сохранения и считывания published-свойств несут методы
SaveProps и ReadProps соответственно.
procedure TOptions.SaveProps;
var
I, N: Integer;
TypeData: PTypeData;
List: PPropList;
begin
TypeData:= GetTypeData(ClassInfo);
N:= TypeData.PropCount;
if N <= 0 then Exit;
GetMem(List, SizeOf(PPropInfo)*N);
try
GetPropInfos(ClassInfo,List);
for I:= 0 to N - 1 do
case List[I].PropType^.Kind of
tkEnumeration,
tkInteger: FIniFile.WriteInteger(Section, List[I]^.Name,GetOrdProp(Self,List[I]));
tkFloat: FIniFile.WriteFloat(Section, List[I]^.Name, GetFloatProp(Self, List[I]));
tkString,
tkLString,
tkWString: FIniFile.WriteString(Section, List[I]^.Name, GetStrProp(Self, List[I]));
end;
finally
FreeMem(List,SizeOf(PPropInfo)*N);
end;
end;
procedure TOptions.ReadProps;
var
I, N: Integer;
TypeData: PTypeData;
List: PPropList;
AInt: Integer;
AFloat: Double;
AStr: string;
begin
TypeData:= GetTypeData(ClassInfo);
N:= TypeData.PropCount;
if N <= 0 then Exit;
GetMem(List, SizeOf(PPropInfo)*N);
try
GetPropInfos(ClassInfo, List);
for I:= 0 to N - 1 do
case List[I].PropType^.Kind of
tkEnumeration,
tkInteger: begin
AInt:= GetOrdProp(Self, List[I]);
AInt:= FIniFile.ReadInteger(Section, List[I]^.Name, AInt);
SetOrdProp(Self, List[i], AInt);
end;
tkFloat: begin
AFloat:=GetFloatProp(Self,List[i]);
AFloat:=FIniFile.ReadFloat(Section, List[I]^.Name,AFloat);
SetFloatProp(Self,List[i],AFloat);
end;
tkString,
tkLString,
tkWString: begin
AStr:= GetStrProp(Self,List[i]);
AStr:= FIniFile.ReadString(Section, List[I]^.Name, AStr);
SetStrProp(Self,List[i], AStr);
end;
end;
finally
FreeMem(List,SizeOf(PPropInfo)*N);
end;
end;
function TOptions.Section: string;
begin
Result := ClassName;
end;
Теперь, для проверки работоспособности, и отладки объекта опций создадим новое
приложение и подключим к нему модуль, в котором описан и реализован объект
TOptions. Ниже приведен программный код, иллюстрирующий создание наследника от
класса TOptions и его использования в главной (и единственной) форме нашего
тестового приложения интерфейсная часть выглядит так:
TMainOpt = class(TOptions)
private
FText: string;
FHeight: Integer;
FTop: Integer;
FWidth: Integer;
FLeft: Integer;
procedure SetText(const Value: string);
procedure SetHeight(Value: Integer);
procedure SetLeft(Value: Integer);
procedure SetTop(Value: Integer);
procedure SetWidth(Value: Integer);
published
property Text: string read FText write SetText;
property Left: Integer read FLeft write SetLeft;
property Top: Integer read FTop write SetTop;
property Width: Integer read FWidth write SetWidth;
property Height: Integer read FHeight write SetHeight;
end;
TForm1 = class(TForm)
Edit1: TEdit;
procedure Edit1Change(Sender: TObject);
private
FMainOpt: TMainOpt;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
А вот и реализация:
constructor TForm1.Create(AOwner: TComponent);
var
S: string;
begin
inherited Create(AOwner);
S := ChangeFileExt(Application.ExeName, '.ini');
FMainOpt := TMainOpt.Create(S);
Edit1.Text := FMainOpt.Text;
Left := FMainOpt.Left;
Top := FMainOpt.Top;
Width := FMainOpt.Width;
Height := FMainOpt.Height;
end;
destructor TForm1.Destroy;
begin
FMainOpt.Left := Left;
FMainOpt.Top := Top;
FMainOpt.Width := Width;
FMainOpt.Height := Height;
FMainOpt.Free;
inherited Destroy;
end;
{ TMainOpt }
procedure TMainOpt.SetText(const Value: string);
begin
FText := Value;
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
FMainOpt.Text := Edit1.Text;
end;
procedure TMainOpt.SetHeight(Value: Integer);
begin
FHeight := Value;
end;
procedure TMainOpt.SetLeft(Value: Integer);
begin
FLeft := Value;
end;
procedure TMainOpt.SetTop(Value: Integer);
begin
FTop := Value;
end;
procedure TMainOpt.SetWidth(Value: Integer);
begin
FWidth := Value;
end;
В заключение своей статьи хочу сказать, что RTTI является недокументированной
возможностью Object Pascal и поэтому информации на эту тему в справочной системе
и электронной документации весьма мало. Наиболее легкодоступный способ изучить
более подробно эту фишку — просмотр и изучение исходного текста модуля TypInfo.
Для работы с RTTI в раздел проекта uses необходимо подключить модуль TypInfo. Для
извлечения информации RTTI обычно требуется две структуры: PTypeInfo и PTypeData.
Примечание: Следует помнить, что в RTTI доступны только те свойства и методы,
которые определены в секции published исследуемого объекта, т.е. те которые видны в
инспекторе объектов среды разработки Delphi. Свойства и методы необъявленные в
секции published через технологию RTTI будут недоступны.
45.Компоненты Delphi. Визуальные и невизуальные компоненты.
Понятие компонента является фундаментальным для Delphi. Без компонентов все
преимущества визуальной разработки приложения исчезают и говорить становится не о
чем.
Существует два взгляда на компоненты.
• Взгляд снаружи, точнее — из визуальной среды разработки. С этой точки зрения
компоненты — это самодостаточные строительные блоки, которые вы берете из Палитры
Компонентов и переносите на форму для создания собственно приложения. Примеры
компонентов вам известны: это кнопки, метки, строки редакторов и т. д.
• Существует еще и взгляд изнутри, т. е. взгляд из программы на языке Object Pascal. С
этой точки зрения компоненты — это классы, порожденные прямо или косвенно от класса
TComponent и имеющие опубликованные (published) свойства. Экземпляры компонентов
— это объекты этих классов, существующие в качестве полей формы. Среди
опубликованных свойств компонентов обязательно присутствует имя (Name), под
которым экземпляр компонента представляется в Инспекторе Объектов.
Объединение этих двух точек зрения дает цельное представление о том, что такое
компоненты. При работе с компонентами из визуальной среды вы всегда видите их
лицевую сторону. Однако как только вы начинаете писать обработчики событий и
управлять компонентами программно, вы соприкасаетесь с программной стороной
компонентов, суть которой — объекты. Таким образом, Delphi обеспечивает симбиоз
визуального и объектно-ориентированного программирования.
Компоненты представляют собой элементы, из которых конструируется видимое
изображение, создаваемое работающей программой. Также существует значительное
количество компонентов, которые не создают видимого изображения, но которые тем не
менее играют важную роль в тех или иных случаях. Правильнее думать о компонентах как
о заранее приготовленных для вас фрагментах программы, которые можно вставлять, если
в этом есть необходимость, в разрабатываемую программу. В этом разделе приводится
начальный обзор компонентов, который даст вам самое общее представление о богатстве
возможностей Delphi.
При всем этом компоненты, происходящие от класса TComponent, не обязательно
являются визуальными. Если же говорить именно о визуальных компонентах, то нам
следует продвинуться еще дальше, к классу TControl, являющимся общим предком для
всех элементов графического интерфейса в созданных при помощи Delphi
приложениях Windows. Но реальные компоненты, как правило, происходят не от самого
класса TControl, а от 2 его разновидностей, представленных, в случае для Windows,
классами TWinControl и TGraphicControl. Отметим, что полноценными оконными
элементами управления (с поддержкой ввода с клавиатуры, визуальной реакцией на
действия пользователя и т.д.) являются только наследники класса TWinControl. Что
касается компонент, происходящих от TGraphicControl, то они являются облегченным
вариантом элементов интерфейса, не требующими поддержки всех функциональных
возможностей управления со стороны операционной системы. Такими элементами
являются, например, статические картинки, используемые в оформлении программ (вроде
логотипа в окне About) и т.п.
Другими наследниками класса TComponent, являются такие классы,
как TScreen и TApplication. Класс TScreen инкапсулирует в себе свойства и методы,
необходимые для работы с экраном, на котором запущено приложение. А
класс TApplication является основой для самого графического приложения, обеспечивая
его взаимодействие с операционной системой и выполняя рад иных вспомогательных
действий вроде поддержки системного меню или перехвата и обработки нажатий
клавиатуры.
Стандартные компоненты
Стандартные компоненты инкапсулируют наиболее распространенные средства
управления Windows. К их числу относятся классы: TButton, ТЕdit, TListBox,
TMemo, TMainMenu, TScrollBar, TPopupMenu, TCheckBox, TRadio-Button,
TRadioGroup, TGroupBox, TPanel и TActionList.
Большинство из перечисленных классов инкапсулирует какое-нибудь средство
управления Windows, поэтому я не стану обсуждать каждый из них прямо сейчас.
Класс TMainMenu инкапсулирует главное меню приложения. Двойной щелчок на
компоненте MainMenu во время проектирования приводит к активации конструктора
меню. С помощью свойств MainMenu можно управлять такими аспектами поведения
элементов меню, как, например, их блокировка. Кроме того, вы можете проверять,
являются ли они "отмеченными", задавать для них идентификаторы контекстной
справки, текст подсказки и т. д. С каждым элементом меню ассоциировано событие
OnClick, что позволяет связать с каждым выбранным пунктом свой обработчик
события.
Компонент Panel
Еще одним интересным стандартным компонентом является Panel (панель).
Панель — это прямоугольная область на форме, рассматриваемая как единое целое
вместе с размещенными на ней элементами управления.
Компонент Panel является контейнером, который может содержать другие
компоненты. Свойства панели управляют типом ее обрамления, внешним видом
(панель может быть приподнятой, утопленной или плоской), а также шириной рамки.
Различные сочетания перечисленных свойств позволяют создавать множество
разновидностей трехмерных панелей.
Компонент ActionList
Этот компонент появился только в Delphi 4. С его помощью может быть легко
реализован механизм разрешения/блокировки команд, относящихся к компоненту или
группе компонентов. Например, приложение, использующее буфер обмена, может
иметь набор команд Cut (вырезать), Copy (копировать) и Paste (вставить) как в
главном и контекстном меню, так и на инструментальной панели. При наличии данных
в буфере обмена для команды Paste следует разрешить использование
соответствующих пунктов меню и командной кнопки. При их отсутствии эти пункты
меню и командная кнопка должны быть заблокированы. С помощью компонента
TActionList может быть достигнута согласованная блокировка/включение всех
участвующих в этом процессе элементов управления (кнопки на панели инструментов
и пунктов меню).
Компоненты страницы Additional
В Delphi существует еще одна группа компонентов, которую мы рассмотрим вместе
со стандартными средствами управления. Речь идет об элементах управления, которые
можно найти на странице Additional палитры компонентов. Эти компоненты
представлены следующими классами: TBitBtn,
TSреedButton,TMaskEdit,TStringGrid, TDrawGrid, TImage, TShape, TBevel,
TScrolIBox, TCheckListBox, TSplitter, TStaticText и TChart. Класс TBitBtn
представляет собой кнопку с помещенной на нее пиктограммой.
Компонент SpeedButton — по виду также кнопка с рисунком, — по сути дела,
таковой не является. (SpeedButton представляет собой лишь графическое изображение
командной кнопки.) Это позволяет вам использовать значительное число таких
"кнопок" и при этом весьма экономно расходовать ресурсы Windows.
Компонент Image используется для отображения метафайлов, пиктограмм и
битовых матриц, хранящихся в дисковых файлах. С помощью компонента Bevel вы
можете создавать приподнятые и вдавленные линии и прямоугольники. Визуально
выделяя с их помощью различные области формы, можно добиться значительного
улучшения внешнего вида приложения. Классы TStringGrid и TDrawGrid служат
средством представления информации в виде сеток (они отображают ее в строках и
столбцах).
Компоненты категории Win32
В VCL существуют классы компонентов, инкапсулирующие многие 32-разрядные
нестандартные элементы управления Windows. Вот эти классы: TListView,
TTreeView, TTrackBar, TProgressBar, TTabControl, TPageControl, TRichEdit,
TImageList, TStatusBar, TAnimate, TDateTimePicker, TToolBar, TCoolBar и другие.
Известная сложность, присущая самой природе некоторых из этих элементов
управления, отражается и на представляющих их классах VCL.
VCL действительно упрощает работу с этими весьма распространенными
средствами управления. Однако вам придется потратить некоторое время на изучение
этих классов, прежде чем вы сумеете добиться полного понимания их работы.
Компоненты для работы с базами данных
В VCL есть значительное число как визуальных, так и невизуальных компонентов,
предназначенных для работы с базами данных. К числу последних относятся
DataSource, Database, Table и Query. Все они инкапсулируют операции базы данных,
происходящие "за кулисами".
Классы визуальных компонентов баз данных видимы для пользователя и
рассчитаны на непосредственное взаимодействие с ним. Например, компонент DBGnd
используется для отображения таблиц в виде сеток. Таким образом, он представляет
собой интерфейс между базой данных и пользователем, предназначенный для
просмотра и редактирования хранящихся на диске таблиц.
Компонент DBNavigator обеспечивает удобный кнопочный интерфейс для
передвижения пользователей по таблицам баз данных. Он включает командные кнопки
для перемещения на следующую, предыдущую, первую и последнюю записи и кнопки
для принятия и отмены результатов редактирования.
Еще одна группа привязанных к данным компонентов предназначена для сцепления
стандартных средств управления Windows с полями баз данных. Среди них следует
отметить классы TDBText, TDBEdit, TDBListBox, TDBimage и другие.
Обычно ассоциируются с программированием баз данных и компоненты,
расположенные на странице QReport палитры компонентов. Они значительно
облегчают написание отчетов, особенно в тех случаях, когда источником информации
являются таблицы баз данных
Классы стандартных диалогов
Вы, конечно, уже знаете, что в Windows существуют стандартные диалоговые
панели для таких распространенных операций, как, например, открытие и сохранение
файлов, а также выбор цвета или шрифта. Каждая из них представлена в VCL одним
из следующих классов: TOpenDialog, TSaveDialog, TOpenPictureDialog,
TSavePictureDialog, TFontDialog, TColorDialog,TPrintDialog и TPnnterSetupDialog.
Сюда же можно отнести классы TFindDialog и TReplaceDialog. Все компоненты этой
группы являются невизуальными, поскольку у них отсутствует интерфейс времени
проектирования. Разумеется, все они становятся видимыми во время выполнения
программы.
Компоненты категории System
Страница System палитры компонентов содержит как визуальные, так и
невизуальные компоненты. Невизуальный компонент Timer используется для
представления системного таймера Windows. Единственным событием, на которое он
реагирует, является OnTimer, которое возникает при каждой его активации. Интервал
активации таймера вы можете установить с помощью свойства Interval.
На этой же странице находится компонент MediaPlayer. С его помощью можно
проигрывать мультимедийные файлы различных типов, например, звуковые (WAV- и
MIDI-файлы) и видео (AVI-файлы). Пользовательский интерфейс этого компонента
включает стандартный набор командных кнопок, таких как "Воспроизведение",
"Пауза", "Стоп", "Предыдущая метка", "Следующая метка", "Перемотка назад",
"Перемотка вперед" и некоторые другие. Класс TMediaPlayer содержит широкий
спектр свойств и событий, которые значительно упрощают работу с интерфейсом MCI
(интерфейсом управления носителями) операционной системы Windows.
Компонент PaintBox предоставляет вам пустую канву (Canvas), на которой вы
можете что-нибудь нарисовать. Этот компонент может быть использован несколькими
различными способами. Кроме того, категория System содержит классы,
инкапсулирующие OLE-объекты и динамический обмен данными (DDE-обмен).
Компоненты категории Win 3.1
Было бы непоправимой ошибкой пренебрегать этой группой компонентов только
из-за названия страницы, на которой они расположены. Здесь можно обнаружить
настоящие шедевры. (Корни происхождения названия Win 3.1 следует искать в Delphi
1.) Особенно мне нравятся компоненты TabSet и Notebook. Здесь же находится
несколько классов компонентов, с помощью которых можно создавать нестандартные
диалоги открытия и сохранения файлов:
TFileListBox, TDirectoryListBox, TDnveComboBox И TFilterComboBox.
Компоненты категории Internet
В зависимости от версии Delphi (Standard, Professional или Client/Server) в окне
палитры компонентов вы можете обнаружить закладку Internet, которая содержит
компоненты, предназначенные для программирования в Internet — HTML, FTP,
SMTP, POPS и HTTP. Здесь же находятся компоненты, предназначенные для общего
сетевого программирования посредством интерфейса Winsock API. Большинство из
них — оригинальные компоненты VCL, хотя по крайней мере один, THTML, является
элементом управления ActiveX.
Компоненты категории Sample
Эта страница содержит несколько примеров компонентов VCL. Каждый из них
поставляется с исходными файлами, что позволяет вам изучить все тонкости их
работы. Вот эти компоненты: Gauge, ColorButton, SpinButton, SpinEdit,
DirectoryOutline и Calendar.
Компоненты категории ActiveX
Страница ActiveX палитры компонентов содержит элементы управления ActiveX,
которые вы можете использовать в своих приложениях. Эти средства включают Chart
FX компании Software FX, Inc., элемент управления Graph компании Bits Per Second,
Ltd., а также программные продукты Visual Components, Inc., — Visual Speller,
Formula One Spreadsheet и Formula One VtChart.
Классы GDI
Классы, представляющие интерфейс графических устройств (GDI), выполняют
немалый объем работы в GUI-приложениях Windows. Они инкапсулируют такие
объекты, как битовые матрицы, шрифты, контексты устройств (DC), кисти и перья.
Именно благодаря им становится возможным отображение в окнах графики и текста.
GDI-классы не связаны с каким-то конкретным компонентом, однако многие
компоненты содержат в качестве свойств экземпляры этих классов. Например, у поля
редактирования есть свойство Font (шрифт), которое является объектом типа TFont.
Программистам, знакомым с традиционным программированием в Windows,
хорошо известно такое понятие, как контекст устройства. Тем не менее, в VCL вы не
найдете широкого применения этого термина. Это связано с тем, что VCL
инкапсулирует контексты устройств Windows в классе TCanvas. В VCL для ссылки на
традиционный контекст устройства используется термин канва. Канва предлагает
программисту область, в которой он может рисовать с помощью таких методов, как,
например, MoveTo, LineTo или TextOut. Там же посредством методов Draw и
StretchDraw могут отображаться битовые матрицы. Согласитесь, что концепция
канвы, на которой можно рисовать, представляется более осмысленной, чем
архаичный термин контекст устройства. А теперь приведем список наиболее часто
используемых классов GDI:
q Класс TCanvas содержит объекты других GDI-классов. Например, при
выполнении последовательности операций MoveTo/LineTo цвет проводимой
линии определяется цветом текущего пера канвы, заданного с помощью
свойства Pen (объекта типа треп). Свойства класса треп определяют также тип
проводимой линии: ее толщину, стиль (сплошная, штриховая, пунктирная и т.
д.) и режим рисования.
q Класс TBrush представляет кисть, с помощью которой происходит
закрашивание объектов канвы: прямоугольников (метод FillRect),
многоугольников (Polygon) и эллипсов (Ellipse). Свойствами TBrush являются
Color, Style и Bitmap. Свойство Style служит для задания стиля закрашивания
объектов, а свойство Bitmap позволяет определить битовую матрицу - шаблон
заполнения.
q Класс TBitmap инкапсулирует растровые операции VCL. Его свойствами
являются Palette, Height, Width и TransparentColor, а методами —
LoadFromFile, LoadFromResourceID и SaveToFile. Кроме TCanvas, TBitmap
используют другие классы компонентов, такие как, например, Timage, TBitBtn
и TSpeedButton. Объект класса TBitmap может служить в качестве
внеэкранной битовой матрицы. Битовые матрицы этого типа обычно
используются в приложениях с интенсивным применением графики. Они
позволяют реализовать методики, уменьшающие мерцание экрана и
существенно повышающие эффективность работы таких приложений.
q Класс TFont отвечает за операции со шрифтами. Его свойства — Color, Height и
Style (стиль может быть полужирным, курсивным, нормальным и т. д.). Этот
класс используется всеми классами компонентов, в которых происходит
отображение текста.
В дополнение к перечисленным, существуют и другие классы GDI, которые или
выполняют вспомогательные задачи или, будучи расширениями базового класса,
придают ему дополнительные функциональные возможности. По мере приобретения
опыта программирования на Delphi вы гораздо больше узнаете об этих классах и
научитесь их использовать.
Служебные классы
VCL содержит множество служебных классов, которые вы можете использовать в
своих приложениях. Служебные классы предназначены для упрощения некоторых
задач Windows-программирования. Например, класс TIniFile облегчает чтение и
запись файлов конфигурации Windows (так называемых INI-файлов). Правда,
общепринятой стала сентенция, что с INI-файлами по сути покончено и будущее — за
системным реестром. Чтобы помочь вам при работе с реестром, в VCL предусмотрены
классы TRegistry и TRegkeylnfo.
Класс TStringList позволяет создавать массивы строк. Он используется для
хранения текста многими классами компонентов. Например, класс TMemo использует
объект TStringList в качестве свойства Lines. Методы LoadFromFile и SaveToFile
класса TStringList дают возможность программисту сохранять/загружать список строк
в файле на диске. TStringList также можно использовать для чтения и записи
текстовых файлов.
Еще одним полезным служебным классом библиотеки VCL является класс TList.
Поскольку в нем хранится просто список указателей, вы можете создавать с его
помощью массивы объектов любого типа. Основным достоинством класса TList
является то обстоятельство, что по мере добавления/удаления в него новых объектов
его размер динамически увеличивается или уменьшается.
Кроме того, в VCL входит ряд классов, реализующих механизм чтения/записи
потоков (поток представляет собой обычный блок данных). Каждый из классов
TStream, TFileStream, TMemoryStream и TResourceStream предназначен для чтения
или записи информации в поток. Базовым для всех потоковых классов является класс
TStream. Класс TFileStream используется для работы с дисковыми файлами,
TMemoryStream для манипулирования данными в памяти, и наконец,
TResourceStream для загрузки двоичных ресурсов из ехе- и dll-файлов. Все эти
классы, конечно, не предназначены для новичков, однако их значение трудно
переоценить, когда вам нужны специальные возможности, которые они могут
предложить. Более подробную информацию об этих классах можно получить в
оперативной справке Delphi по VCL.
46.Свойства визуальных компонентов, управляющие их размещением
(Left, Width, Anchors, Align и т.д.).
Местоположение и размер
Для определения местоположения и размеров визуального компонента введены два
опубликованных свойства для задания координат левого верхнего угла:
Для представления их в таком виде нужно отметить флажок By Category в пункте меню
Arrange всплывающего меню Инспектора объектов
property Top: Integer;
property Left: Integer;
и два опубликованных свойства для определения размеров:
property Height: Integer;
property Width: Integer;
Значения свойств задаются в пикселах. Для определения местоположения используется
система координат рабочей области владельца данного компонента. Начало отсчета
находится в левом верхнем углу. Оси направлены вправо и вниз. Под рабочей областью
понимается та часть площади владельца (формы, панели), которая предназначена для
размещения дочерних элементов. Эти свойства можно использовать как на этапе
разработки, так и во время выполнения.
Свойство
property ClientOrigin: TPoint;
содержит координаты левого верхнего угла элемента управления в системе координат
экрана. Координаты любой точки можно пересчитать в экранные при помощи метода
function ClientToScreen(const Point: TPoint): TPoint;
и наоборот:
function ScreenToClient(const Point: TPoint): TPoint;
Для приведения компонента в соответствие текущим значениям указанных выше свойств
используется метод
procedure AdjustSize; dynamic;
Параметры рабочей области компонента определяются следующими свойствами:

property ClientHeight: Integer;
определяет высоту рабочей области в пикселах.

property ClientWidth: Integer;
определяет ширину рабочей области в пикселах.

property ClientRect: TRect;
значение которого есть не что иное, как (0, 0, .clientwidth, ClientHeight). Кому-то будет
удобнее пользоваться этим свойством.
Если разработчику неизвестны текущие параметры рабочей области, то он может
воспользоваться следующими методами.
Функция возвращает координаты левого верхнего угла рабочей области.
function GetClientOrigin: TPoint; virtual;
Функция возвращает размеры прямоугольника рабочей области.
function GetClientRect: TRect; virtual;
Выравнивание
Для выравнивания компонента в рабочей области его владельца (обычно это форма)
применяется свойство
property Align: TAlign;
Тип TAlign объявлен следующим образом:
type TAlign = (alNone, alTop, alBottom, alLeft, alRight, alClient);
При значении alNone выравнивание отсутствует. При следующих четырех значениях
выравнивание осуществляется по соответствующей стороне. Значение alClient приводит к
тому, что элемент управления изменяет свои размеры до размеров рабочей области
владельца.
Свойство
property Anchors: TAnchors;
type TAnchors = set of TAnchorKind;
type TAnchorKind = (akTop, akLeft, akRight, akBottom);
обеспечивает фиксацию элемента управления по сторонам владельца. "Якорь" можно
установить по одной, двум, трем или четырем сторонам. При задании якоря по любой
стороне расстояние между стороной и элементом управления сохраняется неизменным.
Комбинируя якоря для сторон, можно добиться различных вариантов поведения
компонента при изменении размеров владельца.
Если по вертикали или горизонтали якорь не установлен вообще, то при изменении
размеров владельца компонент остается на том же месте с учетом изменившегося размера.
Если по вертикали или горизонтали установлены оба якоря, то при изменении размеров
владельца размер элемента управления изменяется таким образом, чтобы расстояния до
сторон владельца остатись неизменными.
Свойство
property AutoSize: Boolean;
обеспечивает изменение размеров компонента в соответствии с размерами его
содержимого (текста, изображения, списка, иерархического дерева и т. д.).
ОБЩИЕ СВОЙСТВА УПРАВЛЯЮЩИХ ЭЛЕМЕНТОВ
Местоположение управляющего элемента внутри формы или управляющего элемента
владельца определяется значениями свойств Left и Тор. Горизонтальный и вертикальный
размеры управляющего элемента хранятся в свойствах Width и Height соответственно.
Свойство Caption содержит заголовок управляющего элемента. Установленная в свойстве
текстовая строка может содержать управляющий символ амперсанд (&). Если в строке
встречается амперсанд, то следующий за ним символ отображается подчеркнутым
(амперсанд при этом не отображается) и называется символом-сокращением. Нажимая
клавишу с изображением символа в сочетании с клавишей Alt, пользователь может
активизировать управляющий элемент с помощью клавиатуры.
Свойство Color определяет цвет управляющего элемента. Управляющий элемент может
иметь независимый цвет, а может брать его у своего владельца (формы или другого
компонента).
Это
определяется
значением
свойства ParentColor. Если
свойство ParentColor имеет значение True, то изменение цвета владельца, например
формы, автоматически приводит к изменению значения свойства Color управляющего
элемента, например кнопки. Однако, если вы сами установите свойство Color, то
свойство ParentColorавтоматически получит значение False и цвет управляющего
элемента станет независимым.
Свойство Ctl3D определяет, какой внешний вид имеет управляющий элемент: рельефный
(говоря на жаргоне — 3-мерный) или плоский. Если свойство имеет значение True, то
управляющий элемент имеет рельефный вид, а если False, — то плоский. Свойство
рельефности управляющего элемента может зависеть от рельефности его владельца
(формы
или
другого
компонента),
что
определяется
значением
свойства ParentCtl3D. Если свойство ParentCtl3D имеет значение True, то изменение
рельефности владельца, например формы, автоматически приводит к изменению значения
свойства C113D управляющего элемента, например кнопки. Однако, если вы сами
установите
свойство C113D, то
свойствоParentCtl3D автоматически
получит
значение False и рельефность управляющего элемента станет независимой. Заметим, что в
Windows 95 и Windows NT 4. 0 значение свойстваCtl3D не играет роли, так как
управляющие элементы сразу имеют рельефный вид.
Свойство Cursor определяет, какое изображение принимает курсор мыши, когда
пользователь переводит его на управляющий элемент. Возможны следующие значения
свойства:
crAppStart,crArrow,crCross,crDefault,crDrag,crHelp,crHourGlass,crHSpilt,crlBeam,
crMultiDrag,crNo,crNoDrop,crSizeNESW,crSizeNS,crSizeNWSE,crSizeWE,crSQLWait,c
rUpArrow,crVSpilt.
Свойство DragCursor определяет вид курсора мыши, когда управляющий элемент
является пунктом назначения в операции буксировки. Список возможных значений этого
свойства такой же, как и у свойства Cursor (см. табл. выше).
Свойство Drag Mode определяет режим буксировки управляющего элемента. Если в
свойстве установлено значение dmManual (принято по умолчанию), то буксировка
должна
инициироваться
программно.
Если
же
в
свойстве
установлено
значение dmAutomatic, то управляющий элемент уже готов к буксировке, пользователю
достаточно просто нажать кнопку мыши в момент, когда курсор находится над элементом
и отбуксировать его в нужное место.
Свойство Enabled определяет, доступен ли управляющий элемент для пользователя. Если
свойство имеет значение True, то управляющий элемент доступен, а если False, то
недоступен. Недоступный управляющий элемент обычно имеет блеклый вид.
Свойство Font является сложным и определяет шрифт надписи на управляющем
элементе. Параметры шрифта задаются с помощью вложенных свойств Color, Name, Size,
Style, Height. Шрифт управляющего элемента может быть независимым, а может
копироваться у владельца (формы или другого компонента). Это определяется значением
свойстваParentFont. Если свойство ParentFont имеет значение True, то изменение
шрифта владельца, например формы, автоматически приводит к изменению значения
свойства Fontуправляющего элемента, например кнопки. Однако, если вы сами
установите
свойство Font, то
свойство ParentFont автоматически
получит
значение False и шрифт управляющего элемента станет независимым.
Свойство HelpContext содержит номер темы в файле справочника, которая контекстно
связана с управляющим элементом (см. гл. 17). Когда управляющий элемент обладает
фокусом ввода, пользователь может нажать клавишу F1, чтобы получить по нему
оперативную справку. Если свойство HelpContext имеет значение 0, то управляющий
элемент берет номер темы у своего владельца.
Свойство Hint содержит строку подсказки, которая всплывает под управляющим
элементом, если пользователь временно задерживает над ним курсор мыши. Всплытие
подсказки может быть разрешено или запрещено с помощью другого свойства —
ShowHint. Значение свойства ShowHint может зависеть от запрета подсказки для
владельца (формы или другого компонента). Это определяется значением
свойства ParentShowHint. Если свойство ParentShowHint имеет значение True, то запрет
подсказки для владельца, например формы, автоматически приводит к запрету подсказки
для управляющего элемента, например кнопки. Однако, если вы сами установите
свойство ShowHint, то свойствоParentShowHint автоматически получит значение False и
запрет подсказки для управляющего элемента станет независимым.
Свойство PopupMenu используется для привязки к управляющему элементу локального
всплывающего меню. Это меню всплывает по щелчку правой кнопки мыши, когда курсор
находится над управляющим элементом (меню обсуждаются в гл. 6).
Свойство TabOrder содержит порядковый номер управляющего элемента в пределах
своего владельца. Это номер очереди, в которой управляющий элемент получает фокус
ввода при нажатии клавиши Tab.
Свойство TabStop определяет, может ли управляющий элемент получать фокус ввода.
Если свойство имеет значение True, то управляющий элемент находится в очереди на
фокус ввода, а если False, — то нет.
Свойство Visible управляет видимостью управляющего элемента на экране. Если свойство
имеет значение True, то управляющий элемент виден, а если False, — то скрыт.
47.Свойства Parent и Owner у визуальных компонентов. Свойство Name.
Программное создание компонентов.
Связь с родительским элементом управления
Механизм связывания визуального компонента с родительским компонентом
(владельцем) позволяет автоматически задавать для нового элемента управления
некоторые свойства, отвечающие за его внешний вид .В результате все дочерние
элементы управления для одного родительского (формы, панели) будут выглядеть
одинаково оформленными.
Родительский компонент задается свойством
property Parent: TWinControl;
Для каждого дочернего элемента управления можно задать значения нескольких свойств:
property ParentBiDiMode: Boolean;
property ParentColor: Boolean;
property ParentFont: Boolean;
property ParentShowHint: Boolean;
Каждое из них управляет одной характеристикой визуализации элемента.
Метод используется для того, чтобы определить, имеется ли у компонента владелец
вообще.
function HasParent: Boolean; override;
В классе TControl впервые появляются методы-обработчики событий, которые
обеспечивают передачу в элемент действия мыши, присоединение и перетаскивание.
РОДИТЕЛЬСКИЕ И ДОЧЕРНИЕ КОМПОНЕНТЫ
Класс TControl со своими наследниками образуют всю палитру видимых компонентов
Delphi. Терминологически они называются элементами управления, так как на их основе
прежде всего реализуются управляющие элементы Windows - кнопки, переключатели,
списки и т. п. В тексте книги я часто буду употреблять слова компонент и элемент как
синонимы.
Некоторые из наследников TControl обладают дескрипторами окон и способны получать
и обрабатывать Wwdows-сообщения, другие окон не имеют, но обязательно включаются в
состав оконных компонентов, которые управляют ими, согласуясь с требованиями
(сообщениями) Windows. Оконные элементы управления обладают специальной оконной
функцией, в которую Windows посылает управляющие сообщения (например, извещения
о манипуляции пользователя с мышью или о нажатии клавиш клавиатуры). В
терминологии Windows такие элементы называются родительскими, а связанные с ними
неоконные элементы - дочерними. Оконный компонент может выступать как
родительский не только по отношению к неоконным компонентам, но и к оконным. В
этом случае он просто транслирует управляющие сообщения Windows в оконные
функции дочерних компонентов. Обязательным требованием Windows является
визуальная синхронизация дочерних элементов: они не могут выходить из границ своего
родителя и появляются и исчезают вместе с ним. Иными словами, родитель с дочерними
элементами рассматривается Windows как единое целое.
Класс TControl определяет свойство Parent, которое содержит ссылку на родительский
компонент:
property Parent: TWinControl;
Это свойство не следует путать с собственником Owner: Owner создал компонент (не
обязательно - видимый), a Parent управляет видимым компонентом. Поскольку
конструктор TComponent.Create не изменяет свойства Parent (в родительском классе
TComponent такого свойства нет), при создании видимых компонентов на этапе прогона
программы это свойство необходимо изменять программно. Например, Следующий
обработчик События OnCreate формы Form1
вставит надпись дочерний элемент в левый верхний угол формы:
procedure TFormI.FormCreate(Sender: TObject);
var
IbLabel: TLabel;
begin
IbLabel := TLabel.Create(Self);
IbLabel.Parent := Self;
IbLabel.Caption := 'Дочерний элемент';
end;
Если убрать оператор
IbLabel.Parent := Self;
метка никогда не узнает о том, что пришла пора прорисовать себя на экране, и ее текст не
будет виден. Наоборот, изменение свойства Parent подключает метку к списку дочерних
элементов формы, и оконная функция формы обратится к нужному методу метки, чтобы
заставить ее появиться на экране в момент появления самой формы. (Замечу, что
приведенный выше пример является образчиком того, как не надо программировать:
локальная переменная IbLabel будет уничтожена после выхода из процедуры FormCreate,
содержащийся в ней указатель будет безвозвратно потерян для программы, и она никогда
не сможет освободить связанные с меткой ресурсы памяти [На самом деле ресурсы будут
автоматически освобождены при завершении работы программы.]. Правильнее было бы
расположить переменную IbLabel вне процедуры, например, в секции public класса
TForm1, чтобы в обработчике события OnDestroy формы выполнить вызов деструктора
метки.)
Помимо свойства components каждый оконный компонент получает от своего родителя
TWinControl свойство property Controls[Index: Integer]: TControl;
содержащее список дочерних элементов.
Свойство
property ControlCount: Integer;
возвращает количество дочерних элементов (длину массива Controls). Работать со
списком Controls можно с помощью следующих методов:
function ContainsControl( Control: TControl): Boolean;
Возвращает True, если компонент Control является дочерним элементом
function ControlAtPos(constPos: TPoint; AllowDisabled: Boolean): TControl;
Возвращает ссылку на компонент, которому принадлежит точка с координатами Роs. Если
AllowDisabied=True, поиск ведется также среди запрещенных для выбора компонентов
function FindNextControl (Curcontrol: TWinControl; GoForward, CheckTabStop,
CheckParent: Boolean): TWinControl;
Ищет новый элемент в списке Controls. Curcontrol определяет начальную позицию
поиска; GoForward=True, если поиск идет от начальной позиции к концу списка;
CheckTabStop=True, если поиск идет только среди компонентов со свойством
TabStop=True; если CheckParent=True, поиск идет только среди оконных элементов
procedure GetTabOrderList(List: TList) ;
Создает список List всех дочерних компонентов в порядке их выбора клавишей Tab
procedure InsertControl(AControl: TControl);
Вставляет новый дочерний элемент в список Controls. Программа не должна обращаться
непосредственно к этому методу: для вставки дочернего элемента следует установить
нужное значение свойства Parent во вставляемом компоненте
procedure NotifyControls(Msg:Word) ;
Посылает сообщение Msg во все дочерние оконные компоненты
procedure PaintControls(DC:HDC; First: TControl);
Перерисовывает все дочерние компоненты начиная с компонента First. Каждый дочерний
компонент получает дескриптор графического устройства DC, с помощью которого он
сможет прорисовать себя
procedure Realign;
Перерисовывает все компоненты со свойством Align не равным noAlign. Вызывается
после изменения размеров родительского компонента.
procedure Repaint;
Перерисовывает родительский компонент и все его дочерние компоненты
procedure ScaleControls(M, D:Integer) ;
Перерисовывает все дочерние элементы с раз мерами, измененными в M./D раз по
сравнению с предыдущими размерами
procedure SelectFirst;
Выбирает первый в порядке обхода клавишей Tab дочерний компонент
procedure SelectNext(CurControl: TWinControl; GoForward, CheckTabStop:Boolean) ;
Выбирает очередной в порядке обхода клавишей Tab дочерний компонент. CurControl
определяет начальную точку поиска; GoForward= True, если поиск идет к концу списка;
CheckTabStop=True, если выбираются только компоненты со свойством TabStop=True
procedure SetChildOrder(Child:TComponent; Order: Integers);
Устанавливает новый порядок выбора клавишей Tab компонента Child
procedure ShowControl(AControl:TControl);
Показывает дочерний компонент AControl
В некоторых случаях бывает необходимо послать сообщение в собственную оконную
функцию, чтобы заставить компонент выполнить действия, не предусмотренные набором
его свойств и методов. Для посылки сообщения служит метод
function Perform(Msg, WParam: Word; LParam: Longint): Longint;
Параметры обращения к методу соответствуют параметрам вызова оконной функции:
Msg - код сообщения; WParam, LParam - уточняющие параметры.
Cвойство Name. Это свойство определяет имя компонента, под которым компонент будет
известен программе. Первое время можно оставлять имя компонента таким, какое
задает Delphi по умолчанию. Так Вы запомните названия компонентов. В дальнейшей
работе рекомендуется задавать компонентам имена «со смыслом» вместо однотипных
имен, которые «придумывает» Delphi.
Компоненты, с которыми вы работаете на этапе проектирования, являются экземплярами
реальных объектов. Когда вы переносите компонент из окна Tool Palette в окно Designer
Surface. IDE-среда вызывает соответствующий конструктор для создания экземпляра
объекта. Конструктор компонента отличается от стандартного конструктора объекта.
Конструктор компонента принимает параметр TComponent:
constructor Create(AOwner: TComponent);
Параметр AOwner используется для определения владельца компонента. Компонентвладелец отвечает за освобождение памяти, занятой принадлежащими ему компонентами.
При проектировании приложений в IDE-среде Delphi форма автоматически становится
владельцем всех компонентов.
В то время как компоненты могут иметь только свойство Owner (Владелец), элементы
управления (компоненты, порожденные от класса TControl) имеют свойства Owner и
Parent (Родитель). Родительский элемент управления отвечает за отображение элемента
управления на экране. Все контейнерные компоненты могут быть родительскими. Если вы
переносите на форму компонент, то форма становится и владельцем, и родителем
этого компонента. Если вы добавляете компонент в контейнерный компонент, подобный
TPanel, то компонент TPanel становится родительским (Parent) компонентом.
И последнее, о чем следует упомянуть в отношении контейнерных компонентов, это
использование памяти. Контейнерные элементы управления действительно являются
полезными и незаменимыми, если нужно сгруппировать компоненты, однако это вовсе не
означает, что их следует применять повсеместно в качестве графических декораций
формы.
Создание визуальных компонентов в Delphi
При первом знакомстве с delphi несомненно удивляешься великому множеству разных
визуальных компонентов. Кнопочки, панельки, надписи и многое другое. Но после
нескольких месяцев пользования этой средой разработки появляется желание написать
что-то свое. Именно эту задачу мы и попытаемся решить используя инвентарь delphi
который есть в у нас в наличии и естественно свое воображение.
Постановка задачи
Для начала определимся, что и как мы будем делать. В этом вопросе большую роль
играет ваше воображение, эстетические предпочтения и т.д. Я же в силу своей
распущенности предложу Вам в качестве примерного варианта создать кнопку
нестандартной формы, а именно – овальной.
Реализация
Наиболее правильным, с точки зрения иерархии VCL, методом решения первого
пункта поставленной задачи, будет создание нового компонента, в качестве базового
класса которого мы выберем TCustomControl. Этот класс является базовым для
создания компонентов-надстроек над визуальными объектами Windows, и
предоставляет методы для отрисовки объектов разных форм. Если же у вас нет
необходимости наследовать все особенности поведения объектов Windows то можете в
качестве базового класса использовать TGraphicControl, наследники которого
отрисовываются быстрее, поскольку не должны следить за уймой Виндовских
служебных сообщений.
Сам компонент TCustomControl определен в модуле Controls.pas следующим
образом:
tcustomcontrol = class(twincontrol)
private
fcanvas: tcanvas;
procedure wmpaint(var message: twmpaint); message wm_paint;
protected
procedure paint; virtual;
procedure paintwindow(dc: hdc); override;
property canvas: tcanvas read fcanvas;
public
constructor create(aowner: tcomponent); override;
destructor destroy; override;
end;
Здесь самым интересным для нас является метод paint и свойство canvas. Посредством
этих двух членов класса TCustomControl мы и будет рисовать нашу кнопку.
Кроме этого мы немножко расширим функциональность нашего компонента и
придадим ему возможность устанавливать цвет темного и светлого участка своей
границы, а также ее толщину, и наконец определим свойство Flat которое отвечает за
функциональность аналогичного свойства стандартных компонентов Delphi.
Исходя из вышесказанного прототип нашего компонента (tellipsebutton) будет
выглядеть следующим образом:
tellipsebutton = class(tcustomcontrol)
private
fdarkcolor,flightcolor,fbackcolor:tcolor;
fsize:integer;
fpushed:boolean;
rgn:hrgn;
fflat:boolean;
fdrawflat:boolean;
fonmouseenter,fonmouseleave:tnotifyevent;
{ private declarations }
protected
procedure setdarkcolor(value:tcolor);
procedure setlightcolor(value:tcolor);
procedure setsize(size:integer);
procedure setbackcolor(value:tcolor);
procedure dblclick;override;
procedure drawflat;dynamic;
procedure drawnormal;dynamic;
procedure drawpushed;dynamic;
procedure wmlbuttondown(var message:twmmouse);message wm_lbuttondown;
procedure wmlbuttonup(var message:twmmouse);message wm_lbuttonup;
procedure wmmousemove(var message:twmmousemove);message wm_mousemove;
procedure cmmouseenter(var message:tmessage);message cm_mouseenter;
procedure cmmouseleave(var message:tmessage);message cm_mouseleave;
procedure cmtextchanged(var message:tmessage);message cm_textchanged;
procedure setflat(value:boolean);
procedure domouseenter;
procedure domouseleave;
{ protected declarations }
public
constructor create(aowner:tcomponent);override;
procedure afterconstruction;override;
destructor destory;virtual;
procedure repaint;override;
procedure paint;override;
{ public declarations }
property canvas;
published
property darkcolor:tcolor read fdarkcolor write setdarkcolor default clblack;
property lightcolor:tcolor read flightcolor write setlightcolor default clwhite;
property backcolor:tcolor read fbackcolor write setbackcolor default clbtnface;
property size:integer read fsize write setsize;
property flat:boolean read fflat write setflat;
property caption;
{events}
property onclick;
property ondblclick;
property onmousemove;
property onmousedown;
property onmouseup;
property onmouseenter:tnotifyevent read fonmouseenter write fonmouseenter;
property onmouseleave:tnotifyevent read fonmouseleave write fonmouseleave;
{ published declarations }
end;
Как видим, здесь помимо базовых конструктора create и метода afterconstruction
переопределены и методы Paint и Repaint.
Вся функциональность этого компонента в основном заключена в динамических
методах DrawFlat, DrawNormal, DrawPushed которые отвечают за рисование
компонента соответственно в режиме Flat, в нормальном приподнятом режиме и в
нажатом режиме.
Собственно рисование делается с помощью метода canvas.arc, который рисует часть
эллипса заданным цветом. Таким образом мы рисуем одну половину темным цветом а
другую – светлым и получаем эффект выпуклости. Поменяв местами цвета мы
достигаем эффекта «нажатия» для нашей кнопки. Ну а использовав в качестве цвета
фона – средний между темным и светлым цветами границы – мы получаем ефект Flat:
procedure tellipsebutton.drawflat;
var x,y:integer;
begin
canvas.lock;
try
inherited paint;
canvas.brush.color:=backcolor;
canvas.pen.color:=clgray;
canvas.arc(0,0,width,height,0,height,width,0);
canvas.brush.style:=bsclear;
canvas.ellipse(clientrect);
canvas.font.size:=5;
x:=self.clientwidth-canvas.textwidth(caption);
x:=x div 2;
y:=self.clientheight-canvas.textheight(caption);
y:=y div 2;
canvas.textrect(self.clientrect,x,y,caption);
finally
canvas.unlock;
end;
end;
procedure tellipsebutton.drawnormal;
var i:integer;x,y:integer;
begin
canvas.lock;
try
inherited paint;
canvas.brush.style:=bsclear;
canvas.brush.color:=backcolor;
canvas.pen.color:=darkcolor;
canvas.arc(0,0,width,height,0,height,width,0);
for i:=0 to fsize do
canvas.arc(i,i,width-i,height-i,i,height-i,width-i,i);
canvas.pen.color:=lightcolor;
canvas.arc(0,0,width,height,width,0,0,height);
for i:=0 to fsize do
canvas.arc(i,i,width-i,height-i,width-i,i,i,height-i);
canvas.brush.style:=bsclear;
canvas.font.size:=5;
x:=self.clientwidth-canvas.textwidth(caption);
x:=x div 2;
y:=self.clientheight-canvas.textheight(caption);
y:=y div 2;
canvas.textrect(self.clientrect,x,y,caption);
finally
canvas.unlock;
end;
end;
procedure tellipsebutton.drawpushed;
var i:integer;x,y:integer;
begin
canvas.lock;
try
inherited paint;
canvas.brush.style:=bsclear;
canvas.brush.color:=backcolor;
canvas.pen.color:=lightcolor;
canvas.arc(0,0,width,height,0,height,width,0);
for i:=0 to fsize do
canvas.arc(i,i,width-i,height-i,i,height-i,width-i,i);
canvas.pen.color:=darkcolor;
canvas.arc(0,0,width,height,width,0,0,height);
for i:=0 to fsize do
canvas.arc(i,i,width-i,height-i,width-i,i,i,height-i);
canvas.brush.style:=bsclear;
canvas.font.size:=5;
x:=self.clientwidth-canvas.textwidth(caption);
x:=x div 2;
y:=self.clientheight-canvas.textheight(caption);
y:=y div 2;
canvas.textrect(self.clientrect,x,y,caption);
finally
canvas.unlock;
end;
end;
Теперь, оснастив наш компонент необходимыми функциями мы можем приступить к
его «причесыванию», т.е. написанию рутинных методов по присвоению значений
свойствам и отладке. Первым делом здесь надо реализовать реакцию компонента на
события мыши. Это мы делаем посредством методов wmlButtonDown, wmlButtonUp,
wmMouseMove.
procedure tellipsebutton.wmlbuttondown;
begin
inherited;
paint;
end;
procedure tellipsebutton.wmlbuttonup;
begin
inherited;
paint;
end;
procedure tellipsebutton.wmmousemove;
begin
inherited;
if csclicked in controlstate then
begin
if ptinrect(clientrect,smallpointtopoint(message.pos)) then
begin
if not fpushed then drawpushed;
fpushed:=true;
end else
begin
if fpushed then drawnormal;
fpushed:=false;
end
end;
end;
Здесь также мы реализуем функциональность свойства Flat. (в wmmousemove).
Кроме этого мы используем методы cmMouseEnter, cmMouseLeave для вызова
соответствующих обработчиков событий.
А также реализовываем метод cmTextChanged для правильного отображения текста
кнопки:
procedure tellipsebutton.cmtextchanged;
begin
invalidate;
end;
Теперь же дело только за методами Paint и Repaint, которые мы реализовываем
следующим образом:
procedure tellipsebutton.paint;
begin
if not fflat then
begin
if not (csclicked in controlstate) then
drawnormal else drawpushed;
end else
if fdrawflat then drawflat else
if not (csclicked in controlstate) then drawnormal else drawpushed;
end;
procedure tellipsebutton.repaint;
begin
inherited;
paint;
end;
Все. Теперь наш компонент готов к испытаниям. И перед тем как его регистрировать и
кидать на палитру компонентов настоятельно рекомендую Вам проверить его
функциональность в runtime режиме. В противном же случае вы рискуете повесить всю
IDE Delphi при добавлении компонента на форму.
Проверка компонента
Проверка компонента в RunTime режиме не вызовет осложнений даже у новичка.
Всего-то лишь надо:
-создать новое приложение
-в секции uses разместить ссылку на модуль с вашим компонентом (ellipsebutton.pas)
-объявить переменную типа tellipsebutton
-создать компонент, заполнить все его свойства и показать.
unit main;
interface
uses
windows, messages, sysutils, variants, classes, graphics, controls, forms,
dialogs, mycontrols;
type
tform1 = class(tform)
ellipsebutton1: tellipsebutton;
procedure formcreate(sender:tobject);
procedure formdestroy(sender:tobject);
private
{ private declarations }
public
{ public declarations }
end;
var
form1: tform1;
implementation
{$r *.dfm}
procedure tform1.formcreate(sender:tobject);
begin
ellipsebutton1:=tellipsebutton.create(self);
ellipsebutton1.parent:=self;
ellipsebutton1.setbounds(10,10,100,100);
ellipsebutton1.visible:=true;
end;
procedure tform1.formdestroy(sender:tobject);
begin
ellipsebutton1.free;
end;
end.
После такой, наглядной проверки и отладки вы можете спокойно регистрировать ваш
компонент:
procedure register;
begin
registercomponents('usable', [tellipsebutton]);
end;
И использовать уже в ваших приложениях для быстрого создания эллипсоидных
кнопок.
Итоги
Теперь, обладая, мастерством рисования, и зная методику написания визуальных
компонентов для Delphi вы можете преспокойно написать любой замысловатый
элемент интерфейса и даже продавать его как отдельный программный продукт за
немаленькие деньги.
48.Стандартные компоненты Delphi, предназначенные для ввода
информации (Edit, Memo, ComboBox, CheckBox, и т.п.).
Компонент Текстовая область (ТМето)
Обойтись простым текстовым полем удается не всегда. Если пользователь
должен ввести большой объем информации (например, полный почтовый
адрес или произвольный комментарий), ему может понадобиться несколько
строк текста. В таком случае следует использовать компонент ТМето.
При вводе текста для перехода на новую строку (к новому абзацу) обычно используется клавиша ENTER. Однако в диалоговых окнах Windows эта клавиша часто применяется для завершения ввода. Способ использования клавиши ENTER определяется значением свойства WantReturns. Если оно имеет значение True, то клавиша
ENTER позволяет переходить к новой строке внутри текстовой области, в противном случае она служит для завершения ввода и перехода к следующему элементу
управления, а для перехода к новой строке применяется комбинация клавиш
CTRL+ENTER.
Главное свойство данного компонента — Lines (Строки), имеющее тип TStrings. В нем
хранится список строк, введенных пользователем. Эти строки можно обрабатывать
всеми методами, доступными в классе TStrings, например сохранять в файле:
Memol.Lines. SaveToFilef 'C:\Merrio.TXT1 ) ;
Наличие у текстовой области полос прокрутки задается в свойстве ScrollBars.
Таблица 2.3. Настройка свойства ScrollBars
Значение Вид текстовой области
ssNone Полосы прокрутки отсутствуют
ss Horizontal Имеется горизонтальная полоса прокрутки
ss Vertical Имеется вертикальная полоса прокрутки
ssBoth Имеются две полосы прокрутки
Если включена горизонтальная полоса прокрутки, значение свойства Wordwrap игнорируется. Это свойство определяет, будет ли выполняться автоматический перенос
Создание программ для Windows 119
слов на новую строку при достижении правой границы области (при этом никаких
символов новой строки в текст не добавляется — перенос отображается только на
экране).
При выделении фрагмента текста в текстовой области в свойство SelStart записывается позиция первого выделенного символа, а в свойство SelLength — число выделяемых символов. Выделенный текст доступен через свойство SelText (тип string).
Для выделения всего текста применяется метод SelectALL, для удаления выделенного
текста — метод ClearSetection.
Чтобы очистить содержимое текстовой области, используется метод Clear, чтобы
отменить последние изменения — метод Undo, а чтобы очистить буфер, хранящий
историю изменений, и сделать такую отмену невозможной — метод ClearUndo.
Группа методов предназначена для работы с буфером обмена Windows. Для копирования выделенного текста в буфер обмена применяется метод CopyToClipboard, для вырезания текста — метод CutToClipboard, для вставки текста из буфера — метод
PasteFrom Clip board.
Когда в текстовой области происходит изменение текста, генерируется событие
QnChange.
Компонент Флажок (TCheckBox)
Данный компонент используется для фиксации включенного или выключенного состояния (одного из двух).
После размещения компонента Флажок на форме подпись к этому элементу управления можно задать в свойстве Caption. Расположение этой подписи определяется
свойством Alignment: значение taRightHustify означает расположение подписи справа,
а значение taLeftJustify — слева. Главное свойство флажка называется Checked. Оно
доступно для изменения и на этапе проектирования, и на этапе работы программы.
Это свойство принимает значение True, если флажок включен, и False, если он сброшен.
Некоторые флажки могут находиться в третьем, ≪частично установленном* состоянии, когда признак установки флажка отображается приглушенным цветом. Такая
возможность нужна, когда требуется сообщить пользователю о неполном соответствии указанному статусу (например, в ходе установки программ таким образом
сообщается, что для установки выбраны не все доступные средства).
Если для свойства AllowGrayed задано значение True, то флажок при последовательных щелчках на нем будет поочередно принимать состояния ≪сброшен≫, ≪установлен
частично≫, ≪установлен≫. Определить текущее состояние или задать новое из числа
доступных можно, проверив или изменив свойство State (табл. 2.4). Чтобы реагировать на изменение состояния флажка, надо создать обработчик события OnCLkk
(При щелчке).
120 Урок 2. Основы программирования в среде Delphi 7
Таблица 2.4. Значения свойства State
Значение
cbUnchecked
cbGrayed
cbChecked
Состояние флажна
Сброшен
Установлен частично
Установлен
Рассмотрим пример, когда при изменении состояния флажка его текущее состояние
выводится в надпись Labell. Для этого надо разместить на форме соответствующий
компонент, установить значение свойства AllowGrayed равным True, сформировать
обработчик события OnCLick и записать в нем оператор выбора текущего состояния
флажка (рис. 2.9).
IjP'MvForm
Добавление флажка на форму
Состояние флажка
отображается в виде надписи
затемнено
Рис. 2.9. Программа в работе
procedure TMyForm.CheckBoxlClickfSender: TObject);
begin
case CheckBoxl.State of
cbUnchecked: Labell-Caption := 'выключено';
cbGrayed : Labell.Caption := 'затемнено';
cbChecked : Labell.Caption := 'включено';
end
end;__
Компонент Переключатель (TRadioButton)
В отличие от флажка, переключатель предназначен для выбора одного значения из ряда возможных. Переключатели всегда используются группами.
Когда пользователь выбирает один из них, то выделение с текущего переключателя снимается. Таким образом, в группе выделен всегда ровно один переключатель.
Создание программ для Windows 121
Так как мы создаем группу переключателей, на форме надо расположить несколько
компонентов TRadioButton. Программа поймет, что выделенным должен быть только
один из них.
Свойства компонента Переключательаналогичны свойствам компонента Флажок. Свойство Alignment определяет положение подписи справа или слева от переключателя,
а свойство Checked — состояние объекта (True, если переключатель включен).
Методы GetChecked и SetChecked, позволяющие обращаться к свойству Checked и
изменять его значение, в тексте программы явно не используются. Они предназначены для использования в классах-наследниках TRadioButton (конкретных реализациях переключателя) в соответствии с принципом инкапсуляции.
Для отслеживания состояния конкретного переключателя можно обрабатывать
событие OnCMck.
На форме достаточно разместить несколько переключателей, и после компиляции
и запуска программы будет выделен всегда только один из них. Во время проектирования один из переключателей желательно включить, а все остальные по умолчанию оставить выключенными.
Если требуется отслеживать состояние переключателей динамически, надо создать
обработчик события OnClick для каждого из них. О новом статусе переключателя
можно сообщить, например, с помощью надписи La bell.
procedure TMyForm.RadioButtonlClicklSender: TObject];
begin
if RadioButtonl.Checked
then Labell.Caption := 'Включен первый1
end;
procedure TMyForm.RadioButton2Click(Sender: TObject);
begin
if RadioButton2.Checked
then Labell.Caption := 'Включен второй'
end;
Компонент Группа переключателей (TRadioGroup)
Если в программе требуется использовать несколько групп переключателей
(например, одну для указания пола человека, а другую для выбора возрастной
категории), можно применить один из двух подходов. Первый состоит в выделении для каждой группы специального объекта (панели), чтобы система Delphi
могла понять, как объединяются переключатели. Второй подход состоит в использовании компонента TRadioGroup, который объединяет свойства и методы, обеспечивающие поддержку работы группы переключателей.
После размещения на форме компонента TRadioGroup входящие в него переключатели задаются перечислением их названий. Эти названия вводятся в свойство Items,
имеющее тип TString. Так как требуется ввести не одну строку, а несколько, для их
122 Урок 2. Основы программирования в среде Delphi 7
ввода предусмотрен специальный редактор, который вызывается щелчком на специальной кнопке -jjj, расположенной справа в строке, описывающей свойство Items.
Большая текстовая область окна редактора предназначена для ввода названий переключателей, по одному в каждой строке. Не следует забывать о поддержке возможности управления программой с помощью клавиатуры, поэтому перед некоторыми
буквами в названиях надо указать символ &, чтобы сделать эти буквы ≪горячими*.
Затем щелкните на кнопке ОК, и внешний вид объекта RadioGroupl на форме сразу
изменится (рис. 2.10).
String List editor
. м/i .'.•! 1 1 1
...
Ввод названий переключателей
при помощи редактора списка строк
Форма, содержащая созданную
группу переключателей
••
—≪
Г"
-' Р *яепка2
! С кчэпиэЗ
Рис. 2.10. Создание двух групп переключателей
Если теперь откомпилировать и запустить программу, то в ней будут независимо
работать две группы переключателей. Первая создана ранее из отдельных элементов
RadioButtonl и RadioButton2, а вторая, RadioGroupl, сформирована как единый объект
(рис.2.11).
Так как компонент TRadioGroup представляет единое целое со своими переключателями, использовать его в программе надо совсем не так, как компонент TRadi о Button.
Так, свойство Caption определяет не подпись каждого переключателя (эти подписи
теперь задаются в свойстве Items), а заголовок группы (исходно она называется
RadioGroupl). Свойство Columns задает число столбцов (первоначально один), образованных переключателями. Свойство Itemlndex (исходное значение -1) содержит
номер выделенного переключателя (число -1 показывает, что ни один переключатель
не выбран). Значение этого свойства изменяется автоматически, когда пользователь
выбирает один из переключателей группы. Разрешается менять его и программно:
при занесении нового значения в свойство Itemlndex изменится и текущий выбранный переключатель на форме.
Создание программ для Windows 123
7 MyForm
i ^ jjnrwa jj
i Г ≪ямка 2
/*uc. 2.f/. Программа в работе. Две группы переключателей
созданы с использованием различных средств
Динамически реагировать навыбор нового переключателя в фуппе можно с помощью
обработчика события OnClick. Например, чтобы отображать с помощью надписи
Labell название текущего переключателя, надо обратиться к свойству Items (список
строк) и выделить тот элемент, номер которого записан в свойстве Itemlndex. Предварительно следует проверить, имеется ли вообще выделенный переключатель (не
равно ли значение свойства Itemlndex -1), или выбрать один из переключателей на
этапе проектирования (например, присвоив свойству Itemlndex значение 0).
procedure TMyForm.RadioGrouplClickfSender: TObject];
begin
if RadioGroupl.Itemlndex > -1 then
Labell.Caption :- 'Выбран ' +
RadioGroupl.Items[ RadioGroupl.Itemlndex ]
end;
Компонент Поле со списком (TComboBox)
Этот компонент представляет собой вариант списка с присоединенным
дополнительным полем, в котором отображается выбранный элемент списка.
Это же поле может использоваться для ввода новых элементов или для
быстрого поиска элемента по начальным символам. Если на экране отображается
Создание программ для Windows 1 27
только присоединенное поле (≪раскрывающийся список*), то для раскрытия списка
можно использовать клавиатурную комбинацию ALT+BHH3.
Компонент Поле со списком может работать в трех разных режимах, определяемых
значением свойства Style.
Таблица 2.6. Значения свойства Style
Значение Механизм работы списка
csDropDown В присоединенном поле можно указывать значения, отсутствующие в
списке. Свойство MaxLength определяет максимально допустимое
число символов, которое можно ввести в это поле (значение О
указывает на отсутствие ограничений). Текст, введенный
пользователем, доступен через свойство Text.
Список раскрывающийся
csDropDown List Допустим только выбор значений, уже имеющихся в списке. Список
раскрывающийся
csSimple Отличается от стиля csDropDown только тем, что список не является
раскрывающимся
Как и в случае обычного списка, вместо режима работы csDropDown можно указать
аналогичные режимы csOwnerDraw Fixed и csOwnerDrawVariable, которые отличаются
только необходимостью программной отрисовки содержимого каждого элемента
(см. компонент TListBox).
Вводимый текст может автоматически преобразовываться к верхнему регистру
(если свойство CharCase имеет значение ecUpperCase), к нижнему (ecLowerCase) или
никак не преобразовываться (ecNormal, по умолчанию).
Максимальное число элементов, одновременно отображаемых в видимой части
списка, задается в свойстве DropDownCount. Чтобы открыть список из программы,
свойству Dropped Down (Раскрыт) надо присвоить значение True.
Понятия ≪выделенная строка* в раскрывающемся списке нет. В нем имеется только
текущая выбранная строка (ее номер в списке хранится в свойстве Itemlndex). Соответственно, нельзя и выделять строки списка. Единственный метод, связанный с
выделением данных, — это процедура SeledAU, которая выделяет весь текст, введенный пользователем в присоединенное поле. При работе раскрывающегося списка
наиболее важными являются представленные ниже события.
Таблица 2.7. События класса ТСотЬоВох
Название Условия генерации
ОпСЬапде Пользователь изменил текст в присоединенном поле
OnDropDown Список раскрывается. Это событие необходимо обрабатывать, если
содержимое списка может меняться во время работы программы. Тогда
в обработчике этого события можно заново сформировать содержимое
списка (свойство Items)__
49.Компоненты StringGrid и DrawGrid. Редактирование значений в
DrawGrid.
Компонент Таблица строк (TStringGrid)
Использование многими пользователями электронных таблиц типа Excel стало
практически неотъемлемой частью применения компьютеров. В системе Delphi 7
имеются два компонента, которые позволяют до некоторой степени сымитировать
работу электронной таблицы, оставляя при этом, конечно, основную работу по
реализации конкретных функций такой таблицы программистам.
Первый компонент — это таблица строк, позволяющая работать с текстовой
информацией в двумерной таблице, имеющей столбцы и строки (их размеры
можно менять с помощью мыши). Дополнительно, к каждой ячейке таблицы можно
''привязать'' свой объект, характеристики которого программист представит в виде
строки, расположенной в этой ячейке.
Основное свойство таблицы строк — это двумерный массив Cells, позволяющий
обращаться к содержимому ячеек и изменять их содержимое. Первое измерение это номер строки, второе — номер столбца.
ЗАМЕЧАНИЕ Нумерация элементов в таблице строк начинается с нуля.
Число столбцов задается в свойстве ColCount, число строк — в свойстве RowCount.
Следующий код программы изменяет размер таблицы, помещенной на форму в
режиме проектирования (по умолчанию принят размер 5x5 элементов), на размер 10x10
ячеек и заполняет ячейки строками, содержащими их координаты (рис, 4.5, стр 231)
procedure TForml.FormCreatefSender: TObject);
var с,r: integer;
begin
StringGridl.ColCount := 10;
StringGridl.RowCount := 10;
for с := 0 to StringGridl.ColCount-1 do
for r := 0 to StringGridl.RowCount-1 do
StringGridl.Cells[c,r] :=
'(' + IntToStr(c) + ' , ' + IntToStr(r) + ')';
end;
Обратите внимание, что левый столбец и верхняя строка, хотя и содержат текстовую
информацию, фактически являются заголовочными областями. Использовать их наравне с
другими ячейками не совсем правильно.
ЗАМЕЧАНИЕ Число строк и столбцов, имеющих характер заголовка, задается
свойствами FixedCols и FixedRows. Если таблица не содержит заголовочной информации,
эти свойство должны принимать значение 0.
Можно получить доступ ко всем элементам одного столбца или одной строки.
Соответствующие свойства Col и Row имеют тип TStrings, позволяющий обращаться к
нужному элементу по номеру.
Чтобы привязать к ячейке объект (наследник класса TObject), надо использовать
свойство Objects, представляющее собой такой же массив, как Cells, но содержащий
не строки, а объекты. Эти объекты должны создаваться, а также уничтожаться
программистом вручную, иными словами, весь контроль над состоянием этого массива
полностью возлагается на разработчика. Свойство Objects предоставляет только
доступ к нужному объекту. Остальные свойства, предназначенные для оформле-
ния таблицы строк, приведены ниже
SorderStyle
Стиль отрисовки ячейки. Значение этого свойства можно комбинировать со
значением свойства Ctrl3D для получения оригинального вида ячеек
Ctrl3D
Таблица представляется в ''трехмерном'' стиле
ColWidths
Массив, хранящий ширину каждого столбца в пикселах
DefaultColWidth
Начальная ширина столбца по умолчанию
DefaultDrawing
Если значение свойства — True, производится автоматическая отрисовка
содержимого каждой ячейки. В противном случае для таблицы необходимо
определить обработчик события OnDrawCell чтобы запрограммировать процесс
отрисовки ячейки
DefaultRowHeight
Начальная высота строки по умолчанию
FixedColor
Цвет области строк и столбцов, служащих заголовком таблицы
GridHeight
Высота всей таблицы (в пикселах)
GridLineWidth
Ширина (в пикселах) линий, разделяющих ячейки таблицы
GridWidth
Ширина всей таблицы (в пикселах}
Options
Множество значений (тип set of TGridOption}, позволяющее задавать различные
режимы работы таблицы: выделение нескольких ячеек, способ использования
линий прокрутки и прочие. В частности, чтобы разрешить изменение размеров
строк и столбцов, надо установить значение True для свойств goRowSizing и
goColSizing, вложенных в свойство Options. Чтобы разрешить редактирование
содержимого ячеек, надо записать значение True в подсвойство goEditing
RowHeights
Массив, хранящий высоту каждой строки в пикселах
ScrolLBars
Наличие полос прокрутки
VisibleColCount
Число видимых в таблице столбцов (без области заголовка)
Чтобы установить для просмотра нужную область таблицы, надо задать номер начальной
строки в свойстве TopRow, а номер начального столбца — в свойстве LeftCol.
Область заголовков при этом не изменяется. Например, расположив на форме
кнопку и включив указанные далее операторы в обработчик щелчка, можно быстро
прокрутить таблицу к ячейке (3,5), которая будет расположена в верхнем левом
углу таблицы под заголовком (рис. 4.6, стр 233),
procedure TForml.ButtonlClick(Sender: TObject) ,begin
StringGridl. LeftCol := 3;
StringGridl.TopRow := 5;
End;
Чтобы выделить прямоугольную область ячеек (или одну ячейку) другим цветом,
надо использовать свойство Selection. Оно доступно только во время работы программы и имеет тип TGridRect, напоминающий тип TRect.
Например, если требуется по щелчку на кнопке Buttonl выделить область от ячейки
(2,2) до ячейки (3,5), надо выполнить следующие операторы.
procedure TForml.ButtonlClick(Sender: TObject);
Var GRect: TGridRect;
begin
GRect.Left := 2;
GRect.Top := 2;
GRect.Right := 3;
GRect.Bottom := 5;
StringGridl.Selection := GRect;
end;
ВНИМАНИЕ Чтобы такое выделение работало, необходимо в свойстве Options
включить подсвойство goRangeSelect (установить значение True).
Если запустить описанный пример, то после выделения группы ячеек окажется,
что нижняя ячейка (3,5), которая была автоматически сделана текущей, не подсвечена. Чтобы текущая ячейка таблицы выделялась отдельным цветом, надо подсвойству
goDrawFocusSelected свойства Options задать значение True. Можно также
программно включить его в множество Options.
.StringGridl .Options : =StringGridl.Options + [goDrawFocusSelected];
Экранные координаты конкретной ячейки (в пикселах) определяются с помощью
метода (функции) CellRect, которая, получая в качестве первого параметра номер
столбца, а в качестве второго — номер строки, возвращает структуру типа TRect с
координатами прямоугольника, охватывающего заданную ячейку. Если эта ячейка
невидима, то все поля структуры будут иметь значения 0.
Обратный пересчет осуществляется вызовом процедуры MouseToCeLL
procedure MouseToCell
(X, Y: Integer; var ACol, ARow: Longint);
Здесь X и Y — экранные координаты точки, а значения, возвращаемые через параметры
ACoL и ARow (передаваемые по ссылке), соответствуют номерам столбца и строки для
ячейки, содержащей эту точку.
Программная отрисовка таблицы
Чтобы выполнять отрисовку каждой ячейки, надо определить обработчик события
OnDrawCell
procedure TForml.StringGridlDrawCell(Sender: TObj ect;ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
Параметры ACol и ARow содержат номера столбца и строки рисуемой ячейки, параметр Rect — клиентские координаты области таблицы, которая должна быть отрисована, параметр State определяет статус ячейки: gdSelected (выделена), gdFocused
(имеет фокус) или gdFixed (лежит в области заголовка таблицы).
Нарисовать содержимое таблицы теперь можно так (рис. 4.7, стр 234)
procedure TForml.StringGridlDrawCell(Sender: TObject;
ACol, ARow: Integer;Rect: TRect; State: TGridDrawState);
begin
with Sender asTStringGrid do
begin
Canvas.Font.Color := clWhite;
Canvas.Brush.Color := clBlack;
Canvas.FillRect (Rect) ;
Canvas .TextOut (Rect. Left+ 5 , Rect .Top+5,
'(' + IntToStrfACol) +',' + IntToStr (ARow) + ')');
end;
end;
ПОДСКАЗКА Хотя с помощью данного обработчика в ячейках можно выводить и
картинки, и другую графическую информацию, для подобных целей
лучше применять компонент TDrawGrid, а данный компонент предназначен только для отображения строк.
Чтобы контролировать выбор пользователем конкретной ячейки, можно использовать событие OnSelectCell Например, поместив на форму надпись, можно выводить в нее координаты текущей выбранной ячейки:
procedure TForml.StringGridlSelectCell(Sender: TObject;ACol, ARow: Integer;
var CanSelect: Boolean);
begin
Labell.Caption := ' ( ' + IntToStr(ACol) + ',' +
IntToStr(ARow) + ' ) '
end;
Параметры ACoL и ARow содержат координаты выбранной ячейки. Значение параметра CanSelect (он передается по ссылке), в котором первоначально записано значение True, можно изменить на False, чтобы запретить выделение данной ячейки,
ЗАМЕЧАНИЕ Событие OnSelectCell генерируется до начала отрисовки
(автоматической или явно заданной разработчиком) ячейки.
Событие OnSetEditText возникает, когда пользователь редактирует содержимое ячейки.
Это событие полезно обрабатывать, если требуется отслеживать изменение строк
в ячейках, чтобы обновлять содержимое объектов, связанных с ячейками.
procedure TForml.StringGridlSetEditText(Sender: TObject;
ACol, ARow: Integer;
const Value: String);
В заголовке обработчика события появился новый параметр Value, который содержит
новое значение, вводимое пользователем в ячейку с координатами ACoL, ARow.
Компонент Рисуемая таблица (TDrawGrid)
Для создания таблицы строк удобнее всего использовать компонент
TStringGrid. В более общем случае, когда в каждой ячейке может храниться
произвольный объект, надо применять компонент TDrawGrid. При этом вся работа
по визуальному представлению каждого объекта в ячейке полностью возлагается
на программиста.
Большинство свойств, описывающих поведение и внешний вид ячеек таблицы, совпадают с аналогичными свойствами таблицы строк. Однако в классе TDrawGrid отсутствуют свойства Cells и Objects. Создавать массивы объектов или определять, что
надо нарисовать в конкретной ячейке (обработчик события OnDrawCell), необходимо
отдельно. Класс TDrawGrid может быть использован как базовый класс для создания собственных электронных таблиц со сложным поведением.
Редактирование значений в TDrawGrid
Для отображения значений в TDrawGrid необходимо реализовать обработчик
события OnDrawCell. Пример:
procedure TForm1.dgMtrDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
V: Integer;
begin
if ACol=0 then begin
if ARow=0 then
Exit;
V := ARow
end
else if ARow=0 then
V := ACol
else
V := ACol*ARow;
dgMtr.Canvas.TextRect(Rect,Rect.Left+3,Rect.Top+1,IntToStr(V));
end;
Для того, чтобы значения в TDrawGrid можно было редактировать необходимо включить
флаг goEditing в Options и реализовать обработчик события OnGetEditText. Пример:
procedure TForm1.dgMtrGetEditText(Sender: TObject; ACol, ARow: Integer;
var Value: String);
begin
Value := IntToStr(FMtr[ARow-1, ACol-1]);
end;
(здесь FMtr – массив значений ячеек)
Для того, чтобы значения, отредактированные в TDrawGrid, сохранялись необходимо
реализовать обработчик события OnSetEditText. Пример:
procedure TForm1.dgMtrSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: String);
var
V: Integer;
begin
if TryStrToInt(Value,V) then
FMtr[ARow-1, ACol-1] := V;
end;
(здесь FMtr – массив значений ячеек)
50.Работа с компонентом TListView. Отображение в ListView больших
объёмов данных в виртуальном режиме (OwnerData=true).
Компонент Список элементов (TListView)
В отличие от компонента TListBox, представляющего собой стандартный список строк,
существующий еще с первых версий Windows, список элементов содержит значительно
больше возможностей представления информации.
ВНИМАНИЕ Данный компонент ориентирован на представление данных в виде
структуры "объект — набор свойств", например файлов вместе со своими
характеристиками: размером, датой создания, атрибутами. Использовать его как список
однородной информации некорректно.
Первоначально, сразу после создания, в списке не содержится ни одного элемента.
Режим его будущей работы определяется значением свойства ViewStyle. Возможны
следующие значения.
vsIcon
Каждый элемент представлен полноразмерным значком с подписью, который
можно перетаскивать. Так работают папки Windows 9x в режиме Крупные значки
vsSmallIcon
Каждый элемент представлен маленьким значком с подписью справа от него. Эти
значки можно перетаскивать. Так работают папки Windows 9x в режиме Мелкие
значки
vsList
Каждый элемент представлен маленьким значком с подписью справа от него.
Эти значки, расположенные по столбцам, перетаскивать нельзя. Там работают
папки Windows 9x в режиме Список
vsReport
Объект работает как обычный список с несколькими столбцами
Число и свойства столбцов задаются в свойстве Columns, которое представляет собой
коллекцию объектов THnaTListColumn. Основное их свойство — Caption. Оно содержит заголовок столбца. Пользователь может редактировать его во время работы
программы, если значение свойства списка Readonly не равно True и если заголовки
не работают в режиме кнопок.
Выбор элементов может выполняться путем ввода первых букв имени, при этом
фокус перескакивает по элементам списка в соответствии с набранной пол ьзователем
строкой.
Данные для списка формируются в свойстве Items (тип TListltems), представляющим собой список объектов типа TListItem. Эти объекты можно создавать на этапе
проектирования с помощью специального редактора (рис. 4.20, стр 285).
После щелчка на кнопке New Item (Новый элемент) в поле Caption (Заголовок) задается
его имя, в поле Image Index (Номер рисунка) — номер рисунка (значка) из списка рисунков LargeImages или SmallImages (см. ниже), в поле State Index (Номер состояния) —
номер рисунка из списка рисунков StateImages. Каждый элемент, в свою очередь,
может состоять из нескольких вложенных элементов (больший уровень вложенности не
допускается), представляющих собой, по замыслу создателей, свойства
этого элемента.
Названия элементов и номера картинок хранятся в свойстве Items, а обратиться к
вложенным объектам (свойствам) можно через подсвойство SubItems (см, далее).
Настройка структуры и внешнего вида списка осуществляется с помощью свойств:
АllосВу
Число элементов списка, хранимое в памяти. Управляя этим значением, можно
существенно повысить быстродействие некоторых операций по работе со списком,
например сортировки
Checkboxes
Имеет значение True, если в начале каждой строки списка отображается флажок
ColumnClick
Имеет значение True, если заголовкам столбцов разрешено работать в режиме кнопок:
допустимы щелчки на заголовках и обработка этих щелчков. Такая возможность полезна
для сортировки содержимого списка щелчком на его заголовке
FlatScrollBars
Имеет значение True, если полосы прокрутки должны выглядеть плоскими
FullDrag
Имеет значение True, если разрешается полностью перерисовывать заголовки столбцов во
время перетаскивания, а не только отображать границы
GridLines
Имеет значение True, если между элементами списка рисуются разделительные линии
HideSelection
Имеет значение True, если выделение текущего элемента списка автоматически
сбрасывается при переключении фокуса на другой элемент формы. Такая возможность
используется, когда ведется одновременная работа с несколькими списками, что
позволяет быстро определить, какой список имеет фокус
HotTrack
Имеет значение True, если выбор элемента осуществляется наведением на него указателя
(без щелчка)
HotTrackStyles
Если значение свойства HotTrack равно True, то способ выделения элемента
определяется комбинацией значений данного свойства (множества).
Возможные значения — htHandPoint (указатель мыши принимает вид руки),
htUnderLineCold (невыделенные элементы подчеркиваются), htUnderLineHot
(выделенный элемент подчеркивается)
HoverTime
Если значение свойства HotTrack равно True, то данное свойство определяет время (в
миллисекундах), по истечении которого после наведения на него указателя элемент будет
считаться выделенным
IconOptions
Способ упорядочения значков в списке. Имеет три подсвойства.
Подсвойство Arrangement определяет порядок выравнивания (слева направо или сверху
вниз), Подсвойство AutoArrange определяет, будут ли значки переупорядочиваться
автоматически. Подсвойство WrapText определяет, будет ли заголовок выравниваться по
ширине значка или располагаться слева от него
LargeImages
Список картинок-значков. Стиль отображения определен значением vsIcon
MultiSelect
Имеет значение True, если разрешается выбирать несколько элементов списка
OwnerData
Имеет значение True, если обработка содержимого списка выполняется в тексте
программы. Подобный список называется виртуальным, и разработчик берет на себя
программирование его основных функций,связанных с динамическим формированием
значений элементов.Это требуется при обработке больших наборов данных
OwnerDraw
Имеет значение True, если рисование списка и его элементов явно выполняется в тексте
программы по алгоритму разработчика
RowSelect
Имеет значение True, если разрешается выделять целую строку списка
ShowColumnHeaders
Имеет значение True, если отображаются заголовки столбцов
SmallImages
Список картинок-значков. Стиль отображения определен значением,отличающимся от
vsIcon
SortType
Способ автоматической сортировки списка. Возможные значения — stNone (сортировка
не выполняется), stData (сортировка выполняется на основе значений свойства Data
каждого элемента списка), stText (сортировка выполняется на основе значений свойства
Caption каждого элемента списка), stBoth (сортировка выполняется на основе значений
как свойства Data, так и свойства Caption)
Statelmages
Список картинок, отражающих промежуточное состояние объекта
ВНИМАНИЕ Общая идеология работы данного компонента не меняется при переходе к
различным формам его внешнего представления и отражает работу папок Windows 9x. В
крайнем левом столбце отображается значок, затем идут его имя и свойства. Над каждым
столбцом выводится заголовок. В режиме vsReport самый левый столбец (с номером 0)
содержит имя элемента, а все последующие — именно его свойств, что соответствует
классическому представлению файлов папки.
Пример:
Рассмотрим работу списка элементов в различных режимах на примере. Заранее надо
подготовить три набора картинок (значков) по три элемента в каждом. Они
будут указаны в свойствах LargeImages, SmallImages и StateImages.
Установим для свойства ViewStyle значение vsReport. Редактируя свойство Columns,
создадим три столбца: КО, К1 и К2 (рис. 4.21, стр 287).
С помощью редактора свойства Items добавим три элемента Э1, Э2 и Э3, каждый из
которых будет иметь по два подсвойства СЭ*1 и СЭ*2. Звездочкой здесь обозначен
номер элемента (рис. 4.22, стр 287).
Укажем для каждого элемента номер его картинки. Список примет вид, показанный на рис. 4.23, стр 287.
Теперь можно откомпилировать и запустить программу. Размеры столбцов регулируются
с помощью мыши стандартным способом.
Добавим на форму четыре кнопки. Каждая из них будет отвечать за представление
списка в ином режиме.
procedure TForrnl .ButtonlClick (Sender : TObject) ,begin
ListViewl.ViewStyle := vsReport;
end;
procedure TForml.Button2Click(Sender: TObj ect);
begin
ListViewl.ViewStyle := vsIcon;
end;
procedure TForml.Button3Click(Sender: TObject];
begin
ListViewl.ViewStyle := vsSmallIcon;
end;
procedure TForml. Button4Click (Sender: TObject) ,begin
ListViewl.ViewStyle := vsList;
end;
Как только изменяется значение свойства ViewStyLe, тут же изменяется и внешний
вид списка.
Теперь научимся заполнять список динамически, во время работы программы. Допустим,
требуется показывать содержимое заданного каталога: список имен всех вложенных
каталогов и файлов с их характеристиками (датой создания, размером и признаками
"скрытый" и "системный"). Таким образом, всего столбцов в списке будет пять, а сам
список будет работать в режиме vsReport.
Создадим новое приложение и разместим на нем пустой список, текстовое поле и
кнопку. Для визуального разделения файлов и папок подготовим список (ImageList)
из двух маленьких значков, первый из которых (под номером 0) соответствует файлу,
а второй (под номером 1) — папке.
В обработчике создания формы сформируем структуру списка и определим его
внешний вид. Для добавления и настройки свойств нового столбца используется
метод Add свойства Columns, который создает новый объект класса TListColumn и
возвращает ссылку на него. Заключительный цикл устанавливает ширину каждого
столбца равной 150 пикселам.
procedure TForml.FormCreate(Sender: TObj e c t ) ;
var NewColunm: TListColumn;
i: integer;
begin
with ListViewl do
begin
ViewStyle: = vsReport;
NewColumn := Columns.Add;
NewColumn.Caption := 'Название';
NewColumn := Columns.Add;
HewColumn.Caption := 'Дата создания';
NewColumn := Columns.Add,NewColumn.Caption := 'Размер, байтов';
NewColumn := Columns.Add;
HewColumn .Caption := 'Скрытый';
NewColumn ;= Columns.Add,NewColumn.Caption := 'Системный';
for i := 0 to 4 do
Columns[i].Width := 100;
end
end;
Перебор файлов будем выполнять с помощью функций Find First/Find Next, Для этого
подготовим процедуру (метод формы TForml), которая будет записывать содержимое структуры TSearchRec в конец списка.
procedure TForml.AddNewFile(F: TSearchRec);
begin
with ListViewl.Items.Add, F do
begin
Caption := Name;
if (Attr and faDirectory) о О
then Imagelndex := 0
else Imagelndex := 1;
SubItems.Add(DateTimeToStr((FileDateToDateTime(Time))));
SubItems.Add(IntToStr(Size)) ;
if (Attr and faHidden] о О
then SubItems.Add('да')
else SubItems.Add ( 'нет' );
if (Attr and faSysFile) о О
then SubItems.Add('да')
else SubItems.Add('нет');
end;
end;
В операторе with используются два параметра. Первый, представляющий собой экземпляр
класса TListItem, создается динамически с помощью метода Add, а второй описывает
текущую структуру TSearchRec, из которой необходимо извлечь нужную информацию
для списка.
Первоначально задается название самому элементу списка, а также указывается
номер картинки в зависимости от типа файла (каталог или обычный файл). Далее
новые строки добавляются с помощью метода Add свойства SubItems, принадлежащего
свойству Items, У этого метода один параметр — текстовая спрока, записываемая в
свойство Caption. Ее содержимое зависит от наличия или отсутствия различных
характеристик у анализируемого файла.
В поле Editl будет вводиться полный путь поиска для каталога и маска файла, например
С:\*.*. Содержимое этого каталога будет отображаться после щелчка на кнопке
Button1. Вот обработчик этого события.
procedure TForml.ButtonlClick(Sender: TObject);
var F: TSearchRec;
fa: integer;
begin
fa := faAnyFile;
if FindFirst(Edit 1.Text, fa, F] <> 0 then Exit;
AddNewFile(F);
while FindNext(F) = 0 do
AddNewFile(F);
FindClosefF);
end;
После запуска программы и щелчка на кнопке Buttonl будет получен результат,
подобный изображенному на рис. 4.24, стр 290.
Папки и файлы здесь расположены вперемешку. Это неправильно. Их надо отсортировать в соответствии с номерами картинок: первыми должны идти папки (значение свойства ImageIndex равно 0), за ними — обычные файлы. Сортировка в классе
TListView выполняется путем вызова метода AlphaSort, который обычно сортирует
все элементы в списке в порядке "возрастания" их имен, если для объекта ListViewl
не определен обработчик события OnCompare (Сравнить два элемента). Если же он
определен, то сортировка выполняется на основе результатов его работы.
Сформируем такой обработчик. Два объекта, Iteml и Item2, будут сравниваться по
значениям свойства Imagelndex. Б параметр Compare записывается отрицательное
число, если первый объект "меньше" второго; 0, если они равны; положительное
число, если первый объект "больше" второго.
procedure TForml.ListViewlCompare(Sender: TObject; Iteml, Item2: TListItem; Data: Integer;
var Compare: Integer);
begin
if Iteml.Imagelndex = Item2.Imagelndex
then Compare := 0 else
if Iteml .Iraagelndex < Itern2 .Imagelndex
then Compare := -1
else Compare :- +1
end;
Осталось только добавить в конец метода ButtonlClick вызов сортировки.
ListViewl.AlphaSort ;
Теперь каталоги будут отображаться в списке первыми.
Класс TListView содержит немало дополнительных возможностей. В частности, каждый элемент списка (класс TListltem) имеет свойство Data (тип Pointer), в котором
может храниться произвольная информация, связанная с конкретным элементом.
В заключение приведем перечень наиболее важных методов и событий для класса
TListView (приведены ниже). Некоторые методы (например, получение элемента,
ближайшего к указанной точке клиентской области) могут показаться на первый
взгляд странными. Не надо забывать, что основное назначение данного компонента — работа в стиле папок Windows 9x, когда пользователь может щелкать на значке или
рядом с ним.
procedure Arrange(Code: TList Arrangement);
Задает способ выравнивания значков, когда значение свойства ViewStyle равно vsIcon или
vsSmallIcon
function FindCaption (StartIndex: Integer;Value: string;Partial,Inclusive, Wrap:
Boolean):TListltem;
Поиск элемента списка, заголовок которого совладает со значением параметра Value.
Если параметр Inclusive имеет значение True, то поиск выполняется начинал с элемента с
номером, хранящимся в параметре Startlndex. При этом, если параметр Wrap также
имеет значение True, то поиск по достижении конца списка продолжается c его начала.
Параметр Partial разрешает не искать полное совпадение заголовков, а использовать
значение Value как подстроку
function FindData(Startlndex: Integer; Value: Pointer;Inclusive, Wrap: Boolean):
TListltem;
Аналогично предыдущему, только производится сравнение не заголовков, а связанных с
объектом данных (свойство Data)
function GetHitTestInfoAt(X, Y: Integer):THitTests;
Возвращает подробную информацию об указанной точке клиенткой области. Тип
THitTests описывает различные местоположения точки
function GetItemAt(X, Y: Integer): TListItem;
Возвращает элемент списка, области которого принадлежит указанная точка
function GetNearestltem(Point: TPoint;Direction: TSearchDirection): TListltem;
Возвращает элемент списка, ближайший к указанной точке по заданному направлению
(параметр Direction)
function GetNextItem(StartItem: TListItem;Direction: TSearch Direction; States:
TItemStates): TListItem;
Возвращает элемент списка, следующий за указанным в параметре StartItem в
направлении Direction. Если используется список StateImages, то можно учитывать и
наличие свойства State
function GetSearchString: String;
Возвращает текущую строку, которую пользователь ввел для поиска нужного элемента
procedure Scroll(DX, DY: Integer);
Прокрутка содержимого списка на DX пикселов по горизонтали и DY пикселов по
вертикали
function StringWidth(S: string): Integer;
Возвращает ширину строки S в пикселах с учетом текущего шрифта списка
procedure UpdateItems(FirstIndex, LastIndex: Integer);
Перерисовка диапазона элементов списка в диапазоне от FirstIndex до LastIndex
Основные события класса TListView
OnCustornDraw,
OnAdvancedCustomDraw
Программная отрисовка внешнего вида списка
OnCustom DrawItem,
OnAdvancedCustomDrawItem
Программная отрисовка элемента списка
OnCustom DrawSubItem,
OnAdvancedCustomDrawSubItem
Программная отрисовка вложенного элемента
(свойства) списка
OnChange
Элемент списка был изменен
OnChanging
Происходит изменение элемента списка
OnColumnClick
Щелчок мышкой на заголовке столбца
OnColumnDragged
Заголовок столбца был перемещен (перетащен мышью) в новое место
OnColumnRightClick
Щелчок на заголовке столбца правой кнопкой мыши
OnData
Генерируется перед тем, как элемент списка должен быть нарисован. Данное сообщение
обрабатывается, когда содержимое каждого элемента формируется программно (режим
"виртуального списка")
OnDataFind
Запрос на поиск данных от метода FindData
OnDataHint
Изменен диапазон видимых на экране элементов (например, при прокрутке)
OnDataStateChange
Изменение состояния элемента (событие возникает, только если значение свойства
OwnerData равно True)
On Deletion
Пользователь отдал команду на удаление элемента
OnDrawItem
Программная отрисовка содержимого элемента (событие возникает, только если значение
свойства OwnerData равно True)
OnEdited
Завершено редактирование элемента
OnEditing
Происходит редактирование элемента
OnGetImagelndex
Генерируется перед отображением элемента на экране. Его можно обрабатывать, чтобы
динамически задавать номер картинки-значка из списка картинок
OnGetSubItemImage
То же для вложенного элемента (SubItem)
OnInfoTip
Пользователь навел указатель мыши на элемент и задержал его
OnInsert
В список добавлен новый элемент
OnSelectItem
В списке выбран элемент
Для использования всех возможностей компонента TListView необходимо также
познакомиться с методами класса TListItems (свойство Items в списке). Эти методы
представлены ниже. Важны также свойства и методы класса TListItem, который
характеризует конкретный элемент списка (приведены ниже).
Методы класса TListItem
function Add: TListltem;
Создание нового элемента и его добавление в конец списка. Функция возвращает ссылку
на этот элемент
procedure BeginUpdate;
procedure EndUpdate;
Процедура BeginUpdate блокирует перерисовку списка, а процедура EndUpdate снимает
блокировку. Эти методы обычно используют во время выполнения большого числа
изменений, чтобы не замедлять работу перерисовкой ненужных временных деталей
procedure Clear;
Удаление всех элементов списка и освобождение занимаемой ими памяти
procedure Delete (Index: Integer);
Удаление указанного элемента
function IndexOf(Value: TListItem): Integer;
Возвращает номер элемента, указанного в качестве параметра
function Insert(Index: Integer): TListItem;
Создание нового элемента и его добавление в указанную позицию списка. Функция
возвращает ссылку на этот элемент
procedure SetCount(Value: Integer);
Задание числа элементов в списке
Свойства класса TListItem
Caption
Заголовок элемента
Checked
Имеет значение True, если флажок элемента включен (свойство CheckBoxes должно
иметь значение True)
Cut
Элемент рисуется в виде, показывающем, что он вырезан командой Cut (Вырезать). Все
действия по реализации процедуры такого рисования разработчик должен
программировать самостоятельно
Data
Свойство, имеющее тип Pointer и указывающее на связанный с элементом объект
Focused
Имеет значение True, если элемент имеет фокус
ImageIndex
Номер значка в списке картинок
Index
Положение элемента в коллекции TListItems
Left
Горизонтальный сдвиг от левой границы списка
Position
Свойство типа TPoint, определяющее координаты (в пикселах) элемента внутри списка
Selected
Имеет значение True, если элемент выделен
StateIndex
Номер значка из дополнительного списка картинок
SubItemImages
Список картинок для свойств данного элемента
Subltems
Список названий свойств элемента (тип TStrings)
Методы класса TListItem
procedure Delete;
Удаление элемента из списка. Для освобождения занимаемой им памяти надо
использовать метод Free
function DisplayRect(Code: Т Display С ode): TRect;
Определяет прямоугольные координаты элемента с учетом параметра Code (границы
всего элемента, только значка, только текста, значка и текста)
function GetPosition: TPoint;
Определение положения элемента в списке: смещение верхнего левого угла относительно
начала списка
procedure SetPosition(const Value: TPoint);
Установка нового положения элемента
procedure MakeVisible(PartialOK: Boolean);
Прокрутка списка так, чтобы элемент стал видимым. Если значение параметра PartialOK
равно True и элемент уже частично виден, то прокрутка не выполняется
procedure Update;
Перерисовка элемента
(С практике у Хмельнова)
1.Загрузка значений в ListView
Для того, чтобы не происходило перерисовки при каждом изменении
вызываем BeginUpdate:
ListView.Items.BeginUpdate;
try
ListView.Items.Clear;
//Далее идёт код по созданию элементов списка
finally
ListView.Items.EndUpdate;
end ;
Для создания элемента списка вызываем метод ListView.Items.Add, и задаём свойства
элемента списка:
Пример:
var
LI: TListItem;
SR: TSearchRec;
…
LI := ListView.Items.Add;
LI.Caption := SR.Name; //Задаём название элемента, отображаемое в столбце №0
LI.ImageIndex := Ord(SR.Attr and faDirectory<>0);
//Выбираем код иконки узла из ImageList
LI.SubItems.Add(IntToStr(SR.Size)); //Задаём значение, отображаемое в столбце №1
2. Виртуальный ListView
Для работы в виртуальном режиме включаем свойство OwnerData := true у ListView.
В коде задаём общее количество элементов:
ListView.Items.Count := 1000000000;
В обработчике события OnData задаём свойства того узла, номер которого указан в его
свойстве Index
Пример:
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
Item.Caption := IntToStr(Item.Index+1);
end;
51.Работа с компонентом TreeView. Динамическая загрузка элементов в
TreeView по мере раскрытия узлов.
Компонент Дерево (TTreeView)
Сложные структуры данных в Windows обычно представляются двумя способами: в виде
списка и в виде дерева. Так, например, отображается структура каталогов в Проводнике:
слева иерархическая структура диска в виде раскрывающихся значков с обозначениями
"-" (развернут) и ''+'' (свернут), а справа — содержимое выбранного каталога в виде
списка.
Для создания подобных деревьев, отображающих иерархические структуры данных, в
системе Delphi 7 реализован компонент TTreeView.
Процесс создания дерева достаточно прост. Его начальную структуру можно сформировать в редакторе, аналогичном редактору компонента TListView, только уровень
вложенности элементов в таком списке не ограничен. У компонента TListView поддерживался только один уровень вложенности по схеме "объект — набор свойств".
Каждому узлу дерева может соответствовать своя картинка (рис. 4.25, стр 295). Ее номер
указывается в поле редактора Image Index (Номер картинки), а сам список картинок
задается в свойстве Images. Дополнительно, для каждого узла можно указать номер
картинки, отражающей его выделенное состояние (свойство Selected Index), и номер
картинки, отражающей его дополнительное состояние (свойство State Index).
Имена узлов можно редактировать, как обычные названия объектов Windows.
Многие свойства дерева совпадают со свойствами объекта TListView, но есть и небольшие отличия, вызванные необходимостью отображать неограниченные иерархии
объектов и только одним режимом работы.
Основные свойства компонента TTreeView приведены ниже. Сами узлы хранятся в свойстве Items (класс TTreeNodes) и имеют тип TTreeNode. Класс TTreeNodes
содержит свойство Item — массив объектов типа TTreeNode. Основные свойства класса
TTreeNode приведены в табл. 4.79.
ПОДСКАЗКА Доступ к узлом по номеру и, особенно, формирование новых элементов дерева во время работы программы — процесс, требующий значительных
вычислительных ресурсов, поэтому желательно выполнять максимально возможную часть
работы по формированию структуры дерева на этапе проектирования.
Основные свойства компонента TTreeView
AutoExpand
Имеет значение True, если узлы дерева будут автоматически разворачиваться
и сворачиваться при перемещении фокуса
ChangeDelay
Пауза в миллисекундах между выделением узла дерева и генерацией
сообщения OnChange. Обработка этого сообщения позволяет, например,
отобразить содержимое данного узла в другой части формы. Для Проводника
Windows эта пауза равна 50 мс
HideSelection
Имеет значение True, если с элемента, теряющего фокус, снимается выделение
Indent
Расстояние в пикселах между узлами дерева
RightClickSelect
Имеет значение True, если разрешается выделять узлы дерева с помощью
правой кнопки мыши
ShowButtons
Имеет значение True, если слева от узлов отображаются кнопки с символами
+иShowLines
Имеет значение True, если отображаются линии, соединяющие узлы
ShowRoot
Имеет значение True, если отображаются линии, соединяющие узлы верхнего уровня
Statelmages
Список картинок для отображения дополнительного состояния узлов
ToolTips
Имеет значение True, если разрешена всплывающая подсказка для каждого узла дерева.
Показывать такую подсказку надо в обработчике события OnHint
Основные свойства класса TTreeNode
Absolutelndex
Абсолютный номер узла в дереве. Самый первый узел имеет номер 0, далее
происходит нумерация всех потомков этого узла. При этом, если у потомка в
свою очередь есть подчиненные узлы, то нумерация продолжается с первого
потомка и так далее
Count
Число потомков узла
Cut
Имеет значение True, если объект рисуется как "вырезанный". Действия по
поддержке этой операции программист должен реализовать самостоятельно
Data
Свойство имеет тип Pointer и указывает на связанный с узлом объект
Deleting
Имеет значение True, если данный узел находится в состоянии удаления. Этот
процесс может быть длительным, если удаляется узел с большим числом потомков
Expanded
Имеет значение True, если узел развернут, то есть кнопка находится в состоянии "-"
Focused
Имеет значение True, если узел имеет фокус
HasChildren
Имеет значение True, если узел имеет потомков
ImageIndex
Номер картинки в списке картинок
Index
Номер узла в списке потомков вышестоящего родителя. Первый узел-потомок
имеет номер 0(?), второй — 1 и так далее
Item
Массив узлов, являющихся потомками данного
IsVisible
Имеет значение True, если узел виден
Level
Уровень глубины узла. Верхний уровень имеет номер 0, следующий уровень —
номер 1 и так далее
Selected
Имеет значение True, если узел выделен
SelectedIndex
Номер картинки, которая показывается, если узел выделен
Text
Текст, выводимый в узле
TreeView
Ссылка на родительский объект TTreeView
Работу дерева проще всего понять па следующем примере.
Пусть на форме имеется пустой объект TreeViewl, текстовое поле и две кнопки: Узел и
Потомок. После ввода имени и щелчка на кнопке Узел в дерево добавляется новый узел на
текущем уровне.При щелчке на кнопке Потомок новый узел добавляется в число
потомков текущегоузла.
Главное, что требуется в этом примере, — оперативно отслеживать в программе
перемещение пользователем фокуса по дереву, чтобы не просматривать в поисках
выделенного узла (значение свойства Selected которого равно True) весь массив узлов
каждый раз заново. Для этого можно обрабатывать событие OnGetSelectedIndex,
которое формируется, когда возникает потребность в отрисовке конкретного выделенного узла. Запомним этот узел в переменной MyNode, которая будет принадлежать классу формы TForm 1 и иметь тип TTreeNode.
MyNode: TTreeNode;
В момент создания формы эта переменная должна получить начальное значение,
указывающее, что ни один узел не выбран.
procedure TForml.FormCreate(Sender: TObj ect);
begin
MyNode := nil
end;
Обработчик события OnGetSelectedIndex запишется следующим образом.
procedure TForml.TreeViewlGetSelectedlndex(Sender: TObject;Node: TTreeNode);
begin
MyNode := Node;
end;
При щелчке на кнопке Узел новый узел будет добавляться к дереву с помощью
метода
function Add(Node: TTreeNode; const S: string): TTreeNode;
Этот метод добавляет новый узел на один уровень с узлом Node (или на самый
верхний уровень, если вместо узла указано значение nil) и возвращает ссылку на
этот узел. Новому узлу в свойство Text записывается значение строки S.
procedure TForml.ButtonlClick(Sender: TObject);
begin
If TreeViewl.Items.Count = 0
then TreeViewl.Items.Add(nil, Editl.Text)
else TreeViewl.Items.Add(MyNode,Editl.Text)
end;
Предварительно проверяется, есть ли в дереве хотя бы один узел.
Для добавления нового потомка узла используется метод
function AddChild(Node: TTreeNode; const S: string): TTreeNode;
Он аналогичен предыдущему, за исключением того, что новый узел добавляется
не на один уровень с узлом Node, а становится его потомком, последним в списке
всех потомков.
procedure TForml.Button2Click(Sender: TObjectl;
begin
if TreeViewl.Items.Count =0
then TreeViewl.Items.AddChild(MyNode,Editl.Text)
end;
При добавлении узла-потомка требуется, чтобы родительский узел уже существовал, поэтому достаточно проверить, что в дереве есть хотя бы одни элемент.
Для удаления текущего элемента можно использовать следующий оператор.
TreeViewl.Items.Delete(MyNode);
При этом фокус переместится на родительский узел удаляемого объекта. Узел удаляется
вместе со всеми его потомками.
Теперь можно формировать дерево произвольной сложности (рис. 4.26, стр 298).
Когда дерево создано во время работы программы, его структуру желательно сохранить на жестком диске до следующего сеанса. Это можно сделать с помощью метода
SaveToFile.
TreeViewl.SaveToFile('TREE.TXT');
Дерево сохраняется в текстовом формате в наглядном виде — с отступами. Впоследствии
загрузить дерево из файла можно с помощью метода
procedure LoadFrornFile (const FileName: string);
Узлы в дереве можно автоматически сортировать. Момент пересортировки задается в свойстве SortType одним из трех следующих значений.
stData
Узлы пересортировываются, когда изменяется их свойство Data
stText
Узлы пересортировываются, когда изменяется их свойство Text
stBoth
Узлы пересортировываются, когда изменяются оба эти свойства
Значение stNone означает, что сортировка не выполняется.
Способ сортировки определяется обработчиком события OnCompare, о котором рассказывалось при описании компонента TListView. Например, чтобы отсортировать все
элементы дерева в убывающем порядке их названий, надо установить значение свойства
SortType равным stText и написать следующий текст обработчика.
procedure TForml. TreeViewlCornpare (Sender: TObject; Nodel, Node2: TTreeNode; Data:
Integer; var Compare: Integer);
begin
if Nodel.Text > Node2.Text then Compare := -1 else
if Nodel.Text < Node2.Text then Compare := +1
else Compare := 0
end;
ВНИМАНИЕ Реально процедура сортировки выполняется, когда происходит
редактирование названия узла или связанных с ним данных, а также при изменении
значения свойства SortType.
Основные методы класса TTreeView
function AlphaSort: Boolean;
Сортировка всех узлов дерева в алфавитном порядке
procedure FullCollapse;
Сжатие всех раскрытых узлов дерева
procedure FullExpand;
Раскрытие всех узлов дерева
function GetHitTestInfoAt(X, Y: Integer): THitTests;
Подробная информация и том, какой части дерева (тип THitTests) принадлежит указанная
точка клиентской области (координаты в пикселах)
function GetNodeAt( X, Y: Integer): TTreeNode;
Получение узла дерева, которому принадлежит указанная точка клиентской области.
Возвращает значение nil, если такого узла нет
function IsEditing: Boolean;
Возвращает значение True, если выполняется редактирование одного из узлов дерева
Основные события класса TTreeView, не совпадающие с событиями класса
TListView
OnCollapsed
Узел был свернут
OnCollapsing
Идет процесс сворачивания узла
OnExpanded
Узел был развернут
OnExpanding
Идет процесс разворачивания узла
Выше уже говорилось, что обращение к узлам дерева напрямую по номеру — операция
очень неэффективная.
Следующий пример показывает, как быстро перебрать все узлы дерева.
var CurItern: TTreeHode;
begin
CurItem := TreeViewl.Items.GetFirstNode;
while CurItem <> nil do
begin
// выполнить нужные действия над узлом CurItem
CurItem := CurItem.GetNext;
end;
end;
Основные методы класса TTreeNodes
function AddChildFirst(Node: TTreeNode;const S: string): TTreeNode;
Добавление узла первым потомком узла Node (метод AddChild добавляет узел последним
потомком)
function AddChildObjectFirst(Node: TTreeNode;const S: string; Ptr: Pointer):
TTreeNode;
function AddChildObject(Node: TTreeNode;const S: string; Ptr: Pointer): TTreeNode;
То же, но с новым узлом через его свойство Data связывается объект, передаваемый через
указатель Ptr. Метод AddChildObjectFirst добавляет узел в начало, а метод
AddChildObject в конец списка узлов-потомков
function AddFirst(Node: TTreeNode;const S: string): TTreeNode;
Добавить узел первым на уровне узла Node (метод Add добавляет узел последним на этом
уровне)
function AddObject(Node: TTreeNode;const S: string; Ptr: Pointer): TTreeNode;
function AddObjectFirst(Node: TTreeNode;const S: string; Ptr: Pointer): TTreeNode;
То же, но с новым узлом через его свойство Data связывается объект, передаваемый через
указатель Ptr. Метод AddObjectFirst добавляет узел в начало, а метод AddObject — в
конец списка узлов на уровне узла Node
procedure BeginUpdate;
procedure Endllpdate;
Приостановка и возобновление перерисовки дерева. Применяется для ускорения
продолжительных операций над деревом
function GetFirstNode: TTreeNode;
Получить первый узел дерева (с номером 0)
function Insert(Node: TTreeNode;const S: string): TTreeNode;
Добавить узел перед узлом Node
function InsertObject(Node: TTreeNode;const S: string; Ptr: Pointer): TTreeNode;
То же, но с добавляемым узлом через его свойство Data связывается объект,
передаваемый через указатель Ptr
Примечания.
О Узел считается видимым, если все его родительские узлы развернуты.
О Если найти подходящий узел не удалось, соответствующие методы возвращают
значение nil.
Методы класса TTreeView (узел дерева).
function AlphaSort: Boolean;
Сортировка всех потомков узла
procedure Collapse(Recurse: Boolean);
Сжатие узла
procedure Delete;
Удаление узла и всех его потомков
procedure Delete Children;
Удаление всех потомков узла
function DisplayRect(TextOnty: Boolean): TRect;
Возвращает прямоугольник, которым узел ограничивается на экране. Если значение
параметра TextOnly равно True, то в прямоугольник записывается только область
текстового имени узла
function EditText: Boolean;
Начинает редактирование имени узла
procedure EndEdit(Cancel: Boolean);
Завершает редактирование узла. Если значение параметра Cancel равно Тrue, то
восстанавливается прежнее значение свойства Text
procedure Expand(Recurse: Boolean);
Разворачивает узел. Если значение параметра Recurse равно True, то разворачиваются и
все узлы-потомки
function GetFirstChild: TTreeNode;
Возвращает первый узел из списка потомков
function GetLastChild: TTreeNode;
Возвращает последний узел из списка потомков
function GetNext: TTreeNode;
function GetPrev: TTreeNode;
Возвращает следующий (GetNext) или предыдущий (GetPrev) узел по отношению к
текущему с учетом невидимых узлов и узлов-потомков
function GetNextChild(Value: TTreeNode): TTreeNode;
function GetPrevChild(Value: TTreeNode): TTreeNode;
Возвращает следующий (GetNextChild) или предыдущий (GetPrevChild) узел-потомок по
отношению к узлу-потомку Value
function GetNextSibling: TTreeNode;
function GetPrevSibling: TTreeNode;
Возвращает следующий (GetNextSibling) или предыдущий (GetPrevSibling) узел на
уровень текущего узла, независимо от того, виден ли он
function GetNextVisible: TTreeNode;
function GetPrevVisible: TTreeNode;
Возвращает следующий (Get NextVisible) или предыдущий (GetPrevVisible) видимый
узел
function HasAsParent(Value: TTreeNode): Boolean;
Возвращает значение True, если узел Value является родительским для текущего узла
Function IndexOf(Value: TTreeNode): Integer;
Возвращает позицию узла в списке узлов-потомков узла Value. Если узел Value не прямой
родитель текущего узла, то функция возвращает значение -1
Procedure MakeVisible;
Разворачивает подходящие вышестоящие узлы таким образом, чтобы текущий узел стал
видимым
Procedure MoveTo(Destination: TTreeNode;Mode: TNodeAttachMode);
Перемещает текущий узел в область узла Destination.Конкретное положение определяется
значением параметра Mode
(Практика Хмельнова)
1. Программное создание узлов в TreeView
Для создания узлов, используем методы TreeView.Items:
TN := tvFiles.Items.AddChild(Par,sr.Name);
if SR.Attr and faDirectory<>0 then
TN.HasChildren := true;
NDX := …; //Получить номер иконки узла в ImageList
TN.ImageIndex := NDX;
TN.SelectedIndex := NDX;
2. Динамическая загрузка узлов в TreeView
При представлении в TreeView больших деревьев (например, дерева всех файлов,
находящихся на компьютере), нецелесообразно загружать сразу всё дерево (долго, и
может не хватить памяти) . Вместо этого те узлы, которые могут содержать подузлы
(например, папки) помечаем, как имеющие потомков, пока не создавая сами подузлы:
TN.HasChildren := true;
при этом рядом с таким узлом будет отображаться плюс ([+]), позволяющий раскрыть
узел. В момент раскрытия узла вызывается обработчик события OnExpanding. В этом
обработчике событий в том случае, если у узла установлен признак HasChildren, но
отсутствуют потомки (Node.getFirstChild=Nil), выполняем подгрузку узлов в дерево:
procedure TForm1.tvFilesExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var
Path: String;
P: TTreeNode;
begin
if Node.HasChildren and(Node.getFirstChild=Nil) then begin
{Формируем путь к каталогу:}
Path := Node.Text+'\';
P := Node.Parent;
while P<>Nil do begin
Path := Format('%s\%s',[P.Text,Path]);
P := P.Parent;
end ;
{Вызываем загрузку файлов - этот метод TForm1 надо написать,
используя FindFirst/FindNext/FindClose}
if not LoadFiles(Node,Path) then begin
Node.HasChildren := false; //Если не нашли подчинённых файлов, то [+] убираем
AllowExpansion := false;
end ;
end ;
end;
52.Компоненты, которые могут использоваться для рисования и вывода
изображений (TPaintBox, TImage), рисование на поверхности формы.
Компонент Область рисования (TPaintBox)
Этот компонент расположен на панели System. Он не имеет никаких отличительных особенностей и обычно используется для выделения на форме
нескольких областей рисования. Такой подход удобен, когда в программе
происходит активный йывод графической информации на экран и желательно разделить этот процесс на независимые части.
Компонент TPaintBox может охватывать произвольную прямоугольную область
формы. Он содержит единственное главное свойство Canvas, имеющее собственную систему координат. Единственное обрабатываемое событие OnPaint генерируется системой Windows автоматически при необходимости перерисовать одну или
несколько областей (или их части). Разработчик должен только определить, что
будет изображено в каждом объекте класса TPaintBox.
Этот компонент также может самостоятельно вызывать процесс перерисовки области
холста с помощью метода Paint.
Компонент Изображение (TImage)
Данный компонент активно используется во многих программах, причем
не только для отображения статических картинок, но и для создания различных анимационных эффектов.
В большинстве случаев содержимое изображения загружается из файла на этапе
проектирования. Для этого служит свойство Picture (класс TPicture), описывающее
точечное изображение(.ВМР), значок, графический метафайл Windows или другой
пользовательский графический ресурс. Класс TPicture (рисунок) не является компонентом De/pAi 7, он просто входит в состав библиотеки VCL как вспомогательный, но
на его основе могут быть созданы полноценные компоненты.
Текущее содержимое экземпляра класса хранится в одном из свойств: Bitmap (класс
TBitmap), Icon (значок, класс TIcon) или Metafile (класс TMetafile, формат графического метафайла Windows .EMF). Обратиться к любому из этих свойств для отображения графики можно через свойство Graphic. Ширина и высота изображения (в
пикселах) задаются в свойствах Width и Height.
Основные методы класса TPicture (помимо ранее рассмотренных методов класса
TGraphic) приведены в табл. 4.43.
Таблица 4.43. Основные методы класса TPicture
Метод Назначение
class procedure Regi ste rClip board Form a t( Регистрация нового графического формата
данных для
Aformat: Word; работы с ним через буфер обмена Windows, в результате
AGraphicClass: TGraphicCLass); чего объект класса TPicture сможет обращаться к
данным в буфере обмена с помощью метода
LoadFrom Clipboard Format. С регистрируемым форматом
связывается класс AgraphicClass
class procedure RegisterFiLeFormat( Регистрация нового графического формата данных,
const AExtension, ADescription: string; записанного в файлах с расширением AExtension.
При
AGraphicQass: TGraphicCLass); обращении к этому формату в стандартных диалоговых
окнах Windows {Открыть и Сохранить) он отображается
с названием, указанным в параметре ADescription
(например, Мои картинки)
class procedure Re g i ste r File Form at Res ( Метод аналогичен предыдущему, только
вместо явного
const AExtension: String; указания строки с названием рисунка задается
ADescriptionResID: Integer; идентификатор строки ресурсов
AGraphicClass: TGraphicCLass};
class function SupportsClipboarci Format ( Проверка поддержки данного формата
графических
AFormat: Word): Boolean; данных при передаче через буфер обмена Windows
class procedure UnregisterGraphicCtass( Отмена ранее зарегистрированного графического
AClass: TGraphicClass); класса ACLass
Дополнительные компоненты Delphi 7 (панель Additional) 239
Используемый далее метакласс TGraphicCLass описывается так:
type TGraphicClass = clasa of TGraphic;
Это означает, что на его месте может присутствовать любой объект класса TGraphic
или его наследник.
Событий, которые можно обрабатывать,}'компонента TImage два. Это событие OnChange,
возникающее, когда графическое содержимое изменилось, и событие OnProgress,
возникающее при работе с некоторыми графическими форматами (в частности, с
форматом JPEG), когда При обработке больших изображений надо извещать программу о текущем выполненном объеме работ.
Заголовок обработчика события OnProgress выглядит следующим образом.
procedure (Sender: TObject,- Stage: TProgressStage;
PercentDone: Byte; RedrawNow: Boolean;
const R: TRect;
const Msg: string!;
Параметр Stage относится к перечислимому типу и может принимать одно из трех
значений: psStarting (обработка начата), psRunning (обработка продолжается),
psEnding (обработка завершена).
Параметр PercentDone содержит примерный объем обработанной части изображения в процентах.
Если полученную программой часть изображения (эта область описывается прямоугольником R) можно корректно нарисовать (или перерисовать, например при отображении форматов с чередованием линий типа GIF), то параметр RedrawNow будет
иметь значение True.
В параметре Msg хранится некоторая строка, словесно характеризующая этап обработки изображения (например, Loading).
ВНИМАНИЕ Чтобы изображение действительно перерисовывалось, надо в
свойстве IncrementalDisploy объекта Image задать значение True.
После размещения объекта Image на форме появится пунктирная рамка, которая
задает (по умолчанию) размеры будущей картинки. Эти размеры желательно заранее указать в свойствах Width и Height.
Выбрав в Инспекторе объектов свойство Picture, можно вызвать специальный редактор, с помощью которого можно загрузить изображения в форматах .BMP, .ICO, JPG
или в одном из форматов графического метафайла Windows.
ЗАМЕЧАНИЕ Этот редактор был описан при описании компонента TS peed Button.
Если класс Т Picture предназначен только для загрузки, хранения и сохранения изображения, то с помощью класса TImage с изображением (из свойства Picture) можно
осуществлять определенные манипуляции.
240 Урок 4. Современные компоненты интерфейса пользователя
Прежде всего, в классе TImage имеется свойство Canvas (холст), с помощью которого
можно выводить различную графическую информацию непосредственно на изображение. С помощью свойства Center картинку можно центрировать внутри заданной
рамки (если она меньше рамки). Если свойство Center имеет значение True, рисунок центрируется, в противном случае его верхний левый угол совмещается с верхним левым углом рамки. Установив значение свойства Stretch равным True, можно
включить режим автоматического растяжения или сжатия изображения в соответствии с положением границ рамки.
Некоторые области изображения можно сделать прозрачными, чтобы сквозь них
≪просвечивал≫ фон формы. Прозрачность определяется заданием значения True
для свойства Transparent. Этот режим не применим к точечному изображению в
формате .BMP__
ДРУГОЕ
Два способа вывода графической информации
Построение графических изображений всегда было одним из самых интересных и важных
вопросов программирования, поэтому рекомендуем вам обратить на графику особое
внимание.
Существует два способа вывода графической информации:


Вывод заранее приготовленных изображений
Рисование из программы
Первый способ не требует программирования и прекрасно подходит для вывода
статичных изображений. Он основан на использовании компонентов Image и Shape.
Второй способ требует определенных навыков программирования, но зато предоставляет
безграничные возможности для динамического создания изображений и их анимации. Он
основан на использовании доступного программно свойства Canvas, присутствующего в
форме и управляющих элементах.
 В стандартную библиотеку визуальных компонент Delphi входит несколько
объектов, с помощью которых можно придать своей программе совершенно
оригинальный вид. Это - TImage (TDBImage), TShape, TBevel.
TImage позволяет поместить графическое изображение в любое место на
форме. Этот объект очень прост в использовании - выберите его на странице
Additional и поместите в нужное место формы. Собственно картинку можно
загрузить во время дизайна в редакторе свойства Picture (Инспектор Объектов).
Картинка должна храниться в файле в формате BMP (bitmap), WMF (Windows
Meta File) или ICO (icon). (TDBImage отображает картинку, хранящуюся в
таблице в поле типа BLOB. При этом доступен только формат BMP.)
Как известно, форматов хранения изображений гораздо больше трех
вышеназванных (например, наиболее известны PCX, GIF, TIFF, JPEG). Для
включения в программу изображений в этих форматах нужно либо перевести их
в формат BMP, либо найти библиотеки третьих фирм, в которых есть аналог
TImage, “понимающий” данные форматы (есть как VBX объекты, так и
“родные” объекты для Delphi).
При проектировании следует помнить, что изображение, помещенное на форму
во время дизайна, включается в файл .DPR и затем прикомпилируется к EXE
файлу. Поэтому такой EXE файл может получиться достаточно большой. Как
альтернативу можно рассмотреть загрузку картинки во время выполнения
программы, для этого у свойства Picture (которое является объектом со своим
набором свойств и методов) есть специальный метод LoadFromFile. Это
делается, например, так:
if OpenDialog1.Execute then
Image1.Picture.LoadFromFile(OpenDialog1.FileName);
Важными являются свойства объекта Center и Stretch - оба имеют булевский
тип. Если Center установлено в True, то центр изображения будет совмещаться с
центром объекта TImage. Если Stretch установлено в True, то изображение будет
сжиматься или растягиваться таким образом, чтобы заполнить весь объект
TImage.
TShape - простейшие графические объекты на форме типа круг, квадрат и т.п.
Вид объекта указывается в свойстве Shape. Свойство Pen определяет цвет и вид
границы объекта. Brush задает цвет и вид заполнения объекта. Эти свойства
можно менять как во время дизайна, так и во время выполнения программы.
TBevel - объект для украшения программы, может принимать вид рамки или
линии. Объект предоставляет меньше возможностей по сравнению с TPanel, но
не занимает ресурсов. Внешний вид указывается с помощью свойств Shape и
Style.
Создание и отображение картинок
Из предыдущих глав вы уже знаете, что разместить на форме готовую картинку можно с
помощью компонента Image. Поэтому позволим себе воздержаться от повтора и
рассмотрим проблему создания картинки для компонента lmage. Специально для
создания картинок разработчики фирмы Borland включили в состав Delphi небольшой, но
достаточно мощный графический редактор Image Editor (если он для вас слабоват, купите
Adode Photoshop).
Основное назначение Image Editor – создание и редактирование несложных точечных
рисунков. Мы рекомендуем его как профессионалам, так и новичкам. Графический
редактор Image Editor запускается из среды Delphi по команде меню Tools I Image Editor.
Выполните эту команду и через несколько секунд на экране появится следующее окно
редактора (рис.9.1).
Теперь откройте выпадающее меню File I New … (рис. 9. 2.)
Как видите, Image Editor позволяет создавать не только точечные рисунки (bitmap), но и
значки (icon) , и курсоры (cursor), причем один проект может состоять из нескольких
картинок различного формата(для создания таких проектов служат два первых пункта
выпадающего меню New…) Пока нас интересует создание точечного рисунка, поэтому
выберите пункт меню Bitmap file (. Bmp). В ответ Image Editor попросит вас создать
размеры изображения в пикселах необходимым цветом (рис. 9.3.)
Размеры должны быть уже задуманы, поэтому смело установите нужные вам параметры
картинки и щелкните на кнопке ОК. Окно Bitmar Properties закроется и вы увидите окно
редактора картинки (рис. 9.4).
Image Editor имеет интуитивно понятный интерфейс. Вы быстро найдете там "холст",
"кисть" и "краски", поэтому не будем тратить время на скучное описание управляющих
элементов окон и команд меню, изучите рисунок и все станет понятно. Для тренировки
попробуйте что-нибудь нарисовать. Когда рисунок готов, сохраните его в файле с
помощью команды меню File | Save.
Теперь переключитесь на среду Delphi и установите только что созданную картинку в
компоненте Image. Напомним, что для этого надо установить значение свойства Picture
(рис.9.5)
Специфика отображения картинки регулируется свойствами компонента Image.
Отображение геометрических фигур
Наш рисунок довольно сложный, совсем не обязательно рисовать целую картину, чтобы
показать простую геометрическую фигуру, например прямоугольник, эллипс и т.п.
Намного проще и лучше воспользоваться компонентом Shape. Он находится в Палитре
Компонентов рядом с компонентом Image (рис.9.6 ).
Таблица 9.1. Важнейшие свойства компонента Shape
Свойство
Описание
Brush
Цвет и штриховка для заполнения фигуры.
Pen
Цвет линий и способ вывода фигуры.
Shape
Вид геометрической фигуры
Вид геометрической фигуры задается свойством Shape , заполнение ее внутреннего
пространства - составным свойством Brush, а отрисовка внешних границ - составным
свойством Pen . Эти свойства стоят нескольких замечаний.
Перебирая значения свойства Shape, вы получите все виды геометрических фигур,
поддерживаемые компонентом Shape (рис.9.7.) :
Метод заполнения внутреннего пространства фигуры определяется значением свойства
Drush.Style. Если оно равно dsSolid, то фигура заливается цветом, заданным в свойстве
Brush.Color. Если стиль кисти равен dsClear, то фигура рисуется прозрачной. Остальные
значения стиля задают всевозможные варианты штриховки внутренней области. Цвет
штриховочнфых линий определяется значением свойства Brush.Color = clWhite ( рис.9.8)
Цвет граничной линии содержится в свойстве Pen.Color, а ее толщина – в свойстве
Pen.Width. Если толщина равна 1, то изменив значение свойства Pen. Style, можно
выбрать другой тип линии. Следующий рисунок демонстрирует всевозможные типы
линий (стили пера), принимая цвет пера черным (Pen.Color = clBlack) (рис.9.9)
Исследуя составное свойство Pen, вы обнаружите еще один очень важный параметр –
Mode. Это режим наложения графической фигуры на экран или другую поверхность
отображения. Отметим, что режим наложения действует не только на контур фигуры, но
также на ее внутреннюю область, контролируемую кистью. Возможные значения свойства
Mode описаны в таблице 9.2.
Таблица 9.2. Значения свойства Pen.Mode
Значение
Описание
PmBlack
Результирующий пиксел всегда черный.
PmWhite
Результирующий пиксел всегда белый.
PmNop
FinalColor : =ScreenColor
PmNot
FinalColor : = njt ScreenColor
PmCopy
FinalColor : = PenColor
PmNotCopy
EinalColor : = not PenColor
PmMask
FinalColor : = PenColor and ScreenColor
PmMaskPenNot
FinalColor : = PenColor and not ScreenColor
PmMaskNotPen
FinalColor : = not PenColor and ScreenColor
PmNotMask
FinalColor : = not(PenColor and ScreenColor)
PmMerge
FinalColor : = PenColor or ScreenColor
PmMergePenPenNot
FinalColor : = PenColor or not ScreenColor
PmMergeNotPen
FinalColor : = not PenColor or ScreenColor
PmNotMegre
FinalColor : = not (PenColor or ScreenColor)
PmXor
FinalColor : = PenColor xor ScreenColor
PmNotXor
FinalColor : = not(PenColor xor ScreenColor)
Поясним используемые в таблице имена : FinalPixel означает цвет результирующего
пиксела; PenColor – цвет пера; ScreenColor – цвет фона.
Различные режимы наложения позволяют создавать очень интересные эффекты при
выводе графических фигур. Взгляните на приведенный ниже рисунок – такое
впечатление, будто на картинку попал солнечный “ зайчик”. Вас интересует, как получить
такой результат? Поместите в форму компонент lmage и загрузите в него картинку из
файла Delphi 2.0\ Images\Splash\16color\Chip.bmp. Сверху картинки поместите компонент
Shape и установите в нем следующие свойства
Brush.Color = clSilver
Brush.Style = dsSolid
Pen.Mode = pmMergeNotPen
Pen.Style = psCleer
Shape = stCircle
Внимание! Получаемый результат зависит от глубины цвета в графическом адаптере
(количество бит видеопамяти, приходящегося на одну точку экрана).Например, данный
рисунок получен в режиме 256 цветов – 8 бит на одну точку.
Экспериментируя со свойствами Pen.Mode и Brush.Color, вы узнаете много интересного
о режимах наложения и получите не менее впечатляющие результаты. Среди наиболее
интересных эффектов отметим взгляд на картинку через цветное стекло и получение
негатива.
Рисование в ограниченном прямоугольнике
На практике бывает необходимо рисовать не на всей форме, а в ограниченном
прямоугольнике. Для этого применяется компонент PaintBox, который находится в
Палитре Компонентов на странице System (рис.9.15)
Компонент PaintBox генерирует событие OnPaint, в ответ на которое вы можете рисовать
в его области все, что угодно. Например, давайте рассмотрим рисование “звездного неба”.
Шаг 1. Начинайте новый проект и сделайте новую форму окном диалога. Для этого
установите свойство формы BorderStyle в значение dsDialog.
Шаг 2. Поместите в нижней части формы кнопку типа BitBtn и установите ему
следующий обработчик события OnPaint :
procedure TForm1 . PaintBox1Paint (Sender : TObject) ;
var
I : Integer ;
begin
with PaintBox1 do
begin
( Использовать темно-синюю кисть для фона )
Canvas . Brush . Color : = c1Navy ;
( Заполнить фон)
Canvas. FillRect (Rect ( 0, 0, Width, Heiight));
(Нарисовать 200 точек в случайных местах)
for I : = to 200 do
Canvas . Pixels [ Random (Width) , Random (Height)] : = cIWhite;
end ;
end;
После компиляции и запуска проекта вы получите форму, изображенную на рисунке
(см.каталог \Chap9\Drawing\Step5 на компакт-диске) .
Рисование на форме (как, впрочем, и на многих других объектах) происходит через
контекст устройства (холст). Этот объект появляется в виде подсказки после точки при
наборе программы:
К этому объекту (Canvas) мы можем припысывать разные другие объекты, в частности
кисть (TBrush), перо (TPen) и шрифт (TFont). Кроме того, на холсте (Canvas) мы можем
использовать картинку (TBitmap). Эти объекты будут рассмотрены подробнее в
следующих уроках.
Так как при рисовании нам постоянно придется использовать конструкции вида
...
Form1.Canvas...
...
то лучше эту часть вынести за скобки с помощью with:
...
with Form1.Canvas do
begin
...
end;
...
Между begin и end мы как раз и будем рисовать. Куда поместь весь этот код, зависит от
задачи. Можно написать его в обработчике нажатия какой-нибудь кнопки или еще гденибудь, где вам надо. Мы же поместим его в обработчик FormPaint для нашей формы.
Логично это сделать потому, что, в частности, это событие возникает и при создании
формы. Кроме того, если окно нашей формы будет закрыто другим окном, а потом снова
окажеться видимым, то код FormPaint также будет выполнятся, так что мы остановимся
именно на этом обработчике.
Давайте для начала нарисум кружок желтого цвета:
procedure TForm1.FormPaint(Sender: TObject);
begin
with Form1.Canvas do
begin
//Задаем кисть желтого цвета
Brush.Color:=RGB(255, 255, 0);
//Рисуем круг
Ellipse(10, 10, 30, 30);
end;
end;
Результат будет таким:
Аналогичным образом можно нарисовать прямоугольник (используем Square), напечатать
некой текст (TextOut) или вывести еще какие-нибудь примитивы.
Для рисования линий используются методы LineTo и MoveTo. Первый из них рисует
отрезок, второй - просто передвигает точку рисования. Вот пример их использования:
...
//Передвигаем перо в точку (10, 10)
Form1.Canvas.MoveTo(10, 10);
//Рисуем три линии
Form1.Canvas.LineTo(30, 70);
Form1.Canvas.LineTo(80, 40);
Form1.Canvas.LineTo(10, 10);
...
А вот результат выполнения этого фрагмента:
Тот же результат можно получить и через метод Polygon. Он в качестве параметра берет
массив точек (вершин):
var
points: Array [1..3] of TPoint;
...
points[1].X:=10; points[1].Y:=10;
points[2].X:=30; points[2].Y:=70;
points[3].X:=80; points[3].Y:=40;
Form1.Canvas.Polygon(points);
...
В приведеном примере треугольник не закрашивается. Для его закраски (а также любой
фигуры) используется метод FloodFill. Он закрашивает область текущей кистью, начиная
с точки, передаваемой в него в качестве параметра.
...
//Берем кисть красного цвета
Form1.Canvas.Brush.Color:=RGB(255, 0, 0);
//Закрашиваем
Form1.Canvas.FloodFill(12, 12, RGB(0, 0, 0), fsBorder);
...
Если последний параметр равен fsBorder, то заполнение цветом идет до тех пор, пока
наша волна закраски не упрется в границу, заданную вторым параметром. Если же он
равен fsSurface, то закрашиваться будут именно точки с цветом, задаваемым вторым
параметром (начиная от точки, определяемой первыми двумя параметрами).
А вот и результат нашей закраски:
Кроме линий, можно выводить и отдельные точки. Вот пример:
...
//Выводим точку красного цвета
Form1.Canvas.Pixels[9, 9]:=RGB(255, 0, 0);
...
Вообще говоря, рисовать можно не только на форме.
53.Формы (TForm), модули данных (TDataModule), и фреймы (TFrame):
основные свойства, использование в программе. Вызов форм в
модальном режиме.
Форма
Форма — это важнейший компонент Delphi 7, на котором основана вся работа этой
системы по проектированию и разработке приложений. Форма (класс TForm) содержит богатый набор свойств (табл. 2.13), методов и событий (табл. 2.14), позволяюших легко настраивать и организовывать самые сложные алгоритмы ее функционирования.
Таблица 2.13. Свойства класса TForm
Свойство Назначение
Active Содержит значение True, если форма имеет фокус ввода
ActiveControl Объект на форме, который имеет фокус ввода
Bcrderkons Список системных значков формы
BorderStyte Вид границ формы
Canvas Область рисования формы
CLientRect Размеры формы
ClientHeight
ClientWidth
DropTarget Содержит значение True, если форма может работать как приемник
в операциях перетаскивания
Floating Содержит значение True, если форма может пристыковываться к другим
окнам
FormState Текущее состояние формы
FormStyle Стиль формы
HelpFile Название файла справки для формы
Icon Значок, обозначающий форму, когда она свернута
KeyPreview Содержит значение True, если форма будет получать информацию
о нажатых клавишах раньше, чем расположенные на ней объекты
Menu Ссылка на главное меню формы (TMenu)
ModalResuSt Значение, возвращаемое формой, если она работает как модальное
диалоговое окно
Parent ≪Хозяин≫ формы
PixelsPerlncrt Число пикселов на дюйм. Применяется для настройки размера формы
в зависимости от экранного разрешения
Position Положение формы на экране в момент ее открытия в программе
PrintScale Масштабирование формы при выводе на печать
Scaled Содержит значение True, если размер формы будет подгоняться
в соответствии со значением свойства PixelsPerlnch
Visible Содержит значение True, если форма будет видима во время работы
программы
WindowState Состояние формы (свернута, развернута, нормальный размер)
132 Урок 2 Основы программирования в среде Delphi 7
Таблица 2.14. События, поддерживаемые классом TForm
Событие Условия генерации
OnActivate Форма стала активной
OnClose Форма закрывается
OnQoseQuery Запрос на закрытие формы
OnCreate Форма создается
OnDeactivate Форма потеряла фокус ввода
QnDestroy Форма уничтожается
OnHelp Форма получила запрос на выдачу справочной информации
OnHide Форма стала невидимой (значение свойства Visible установлено равным False)
OnPaint Форма должна быть перерисована
OnShortCut Пользователь нажал клавиатурную комбинацию, которая пока не обработана
OnShow Форма стала видимой (значение свойства Visible установлено равным True)
Управление проектом
Как уже говорилось, вся информация, относящаяся к текущей разрабатываемой
программе, объединяется в рамках системы Delphi 7в один проект, который хранит
все необходимые настройки в специальных файлах. Проектом управляет Менеджер
проекта — программа, входящая в состав системы Delphi 7. В функции Менеджера
входит визуальное представление структуры проекта и его содержимого (это могут
быть не только файлы с исходными текстами и файлы форм, но и любые другие
файлы). Менеджер проекта дополнительно позволяет вести одновременно несколько
проектов, объединенных в одну группу. Он вызывается командой View >- Project
Manager (Вид х Менеджер проекта) и наглядно показывает структуру группы
ProjectGroupl
(рис. 2.13).
Добавление компонента проекта • Удаление компонента проекта
Имя текущего
проекта
Project мамаpf?!
|Proi≪l1 exe
"Ив • Pah
Структура группы
проектов
G ЛТосМСмпрЛег з
Pioject1.exe £ sohMJaf
Jjj] Unitl G:\Toolt\Conipteri\D7\Proects ,
§1 UnJI gas Б \Tooli\CompeHs\D7\Proiect;
Ш MyFam G \Toote\Compileis\DAProiect;
Каталоги, в которых
размещены файлы
Рис. 2.13. Отображение сведении в Менеджере проектов
В эту группу пока что входит единственный проект Projectl, состоящий, в свою
очередь, из единственного модуля Unitl (он включает форму MyForm и файл Unitl.pas
Упровление проектом 1 33
с исходными текстами, описывающими работу этой формы). Тип результирующего
приложения, которое будет получено в результате компиляции (это либо исполнимый код — файл .ЕХЕ, либо динамическая библиотека — файл -DLL), указан полужирным шрифтом. Для проекта, созданного командой File >• New > Application (Файл >•
Создать > Приложение), по умолчанию считается, что он предназначен для получения исполнимого кода, поэтому в Менеджере проекта полужирной строкой выделено
Projectl.exe.
Любой компонент из проекта можно удалить, щелкнув на кнопке Remove (Удалить).
С помощью кнопки New (Создать) можно добавить новый компонент как в проект,
так и в группу. Свойства любого из объектов, доступных в Менеджере (от группы
проектов до отдельной формы), можно изменить, щелкнув на значке объекта правой
кнопкой мыши. Контекстное меню содержит набор пунктов, позволяющих выполнить и настройку, и компиляцию, и редактирование. Для быстрого перехода из Менеджера проекта к редактированию конкретного объекта (исходного текста или формы)
достаточно дважды щелкнуть на значке этого объекта.
К группе проектов можно добавлять ранее созданные проекты. Это выполняется
командой Add Existing Project (Добавить существующий проект) контекстного меню.
Ранее созданные формы и соответствующие им файлы с исходными текстами
добавляются к конкретному проекту командой Add (Добавить).
Структура проекта представлена в виде ≪дерева≫. Это стандартный подход к отображению иерархически организованной информации в Windows. Стандартны и основные приемы по работе с подобными ≪деревьями≫: отдельные ≪листья* и ≪ветви≫
можно перемещать между узлами с помощью мыши.
Добавление новой формы
Продолжать изучение компонентов и возможностей вистемы Delphi 7, размещая
объекты на одной форме, неудобно. Уже сейчас наша экспериментальная форма
перенасыщена элементами управления. Поэтому надо либо создать новый проект
(его лучите включить в текущую группу ProjectGroupl, чтобы сохранить целостность
примеров), либо добавить к текущему проекту Projectl новую форму. Пока остановимся на последнем варианте.
Новая форма добавляется к текущему проекту одним щелчком мыши на
командной кнопке New Form (Создать форму) или командой File > New >• Form
(Файл > Создать > Форма). При этом в Проектировщике сразу появится новая пустая
форма. Называться она будет Form2, а соответствующий ей файл с исходными текстами добавится в редактор на новую панель Unit2. Теперь проект надо сохранить,
при этом система Delphi 7 поинтересуется названием нового модуля (пока что его
лучше оставить без изменений — Unit2).
ЗАМЕЧАНИЕ Переключаться между имеющимися в проекте формами можно с
помощью командной кнопки View Form (Отобразить форму] или
комбинации клавиш SHIFT+F12,
134 Урок 2. Основы программирования в среде Delphi 7
У программы может быть только одна главная форма — это форма, которая показывается при запуске программы, — и неограниченное число подчиненных форм,
вспомогательных окон, которые исходно на экране не появляются, а вызываются
по команде из программы с помощью специальных методов.
ЗАМЕЧАНИЕ Исходно подчиненные формы не показываются по одной простой
причине — значение их свойства Visible (Видимость) первоначально
установлено в False. Свойство Visible имеют все без исключения
компоненты Delphi 7, представляющие собой элементы управления.
Изменяя значения свойства Visible во время работы программы,
можно мгновенно делать любые объекты видимыми или невидимыми.
Добавим, например, к главной форме еще одну кнопку Buttons и назовем ее Окно.
При щелчке на ней должна отображаться форма Form2. Сделать это можно несколькими способами.
Показ формы как обычного окна
Чтобы форма отображалась как обычное окно, проще всего записать в свойство
Visible формы Form2 значение True.
procedure TMyForm.Button3Click(Sender: TObject);
begin
Forra2 .Visible := true;
end;
Если теперь выполнить компиляцию проекта, то система Delphi 7 сообщит об ошибке:
идентификатор Form2 в модуле Unitl неизвестен. Одновременно система предложит
включить в список подключаемых модулей новый модуль Unit2, где, по предположению системы, находится описание соответствующей переменной (рис 2.14).
nformation
Ф
toaddil?
£eT 'Cancel
Puc. 2.14, Система предлагает включить в список подключаемых модулей новый модуль
Unit2
В данном случае это действие оправдано, поэтому ответить надо Yes (Да) и выполнить
компиляцию повторно — ошибок уже не будет. Однако лучше всего не забывать
указывать ссылки на новые подключаемые к проекту модули самостоятельно.
Теперь, после запуска программы и щелчка на кнопке Окно на экране возникнет
новое пустое окно (форма Form2). Она связана с родительским окном (главной формой
MyForm); между ними можно свободно переключаться, а при закрытии главного
окна автоматически закроются и все вспомогательные формы (но не наоборот!).
Управление проектом 1 35
Если закрыть форму Form2, реально произойдет изменение значения ее свойства
Visible с True на False, поэтому при щелчке на кнопке Окно форма появится опять.
Если щелкнуть на кнопке Окно, когда форма Form2 видима, ничего не изменится.
Вместо оператора присваивания для показа формы лучше применять ее метод Show.
procedure TMyForm.Button3Click(Sender: TObject);
begin
Form2 . Show,end;
Он хорош тем, что показывает форму, перемещает ее на передний план экрана и
делает активной.
Показ формы как модального окна
Между появившейся на экране формой Form2 и главным окном (MyForm), а также
между другими подчиненными формами проекта, если бы они тоже были созданы
и показаны, можно переключаться произвольным способом. Такой подход не всегда
удобен, потому что не дает пользователю сосредоточиться на конкретном действии
и позволяет, не закончив работу в одном окне, выполнять новые действия в другом
окне. Подобный многооконный принцип при создании программ себя не оправдывает, так как требует от разработчика больших усилий по непрерывной координации состояний множества окон и только усложняет общение с человеком. Сегодня
он применяется в основном в сложных системах, наподобие систем разработки типа
Delphi 7, где одним окном не обойтись. А вспомогательные формы в обычных прикладных программах используются, как правило, для создания диалоговых окон, с
которых невозможно переключиться на другие окна приложения, пока они не будут
закрыты (такой режим работы окна еще называется модальным). Модальные окна
хорошо подходят, в частности, для задания всевозможных настроек, выполнения
ввода промежуточных значений, отображения результатов и других операций.
Чтобы вызвать форму в модальном режиме, надо использовать метод ShowModal.
procedure TMyForm.Button3Click(Sender: TObject) ,begin
Form2.ShowModal;
end;
Теперь, когда после щелчка на кнопке Окно на переднем плане появится форма
Form2, переключиться с нее на главное окно не удастся, пока она не будет закрыта.
Переключаться на любые другие приложения Windows, конечно, можно без ограничений.
К оформлению модальных окон предъявляется набор негласных, но практически
не имеющих исключений требований.
1. Диалоговое окно не должно позволять менять свои размеры. Для этого в свойстве BorderStyle (Стиль границы) надо выбрать любое значение, не допускающее
изменения размера.
136 Урок 2. Основы программирования в среде Delphi 7
Таблица 2.15. Значения свойства BorderStyle
Значение Вид границы окна
bsDialog Размер окна менять не разрешается. Вид границ — как у стандартных
диалоговых окон Window?
bsSingle Размер окна менять не разрешается. Вид границ — тонкая полоса
bsNone Размер окна менять не разрешается. Видимая граница отсутствует
bsSizeable Стандартная граница, допускающая изменение размеров окна
bsToolWindow Аналогично bsSingle, но высота заголовка окна уменьшена
bsSizeToolWin Аналогично bsSizeable, но высота заголовка окна уменьшена
Лучше всего использовать значение bsDialog, специально предназначенное для
оформления диалоговых окон.
2. Свойство BorderWidth (Ширина границы окна) определяет область, на которой не
разрешается размещать элементы управления. Для этого свойства надо задать
подходящее значение (например 2), потому что диалоговые окна имеют достаточно широкие границы.__
ДРУГОЕ
Работа с формами в Delphi.
В Windows основной элемент пользовательского интерфейса - форма. В Delphi почти
каждый проект имеет по крайней мере одно окно - главное окно приложения. Все окна в
Delphi основаны на объекте TForm. В данной статье мы рассмотрим основные события,
участвующие в "жизни формы".
Форма Delphi
Формы имеют свои свойства, события и методы, при помощи которых Вы можете
управлять видом и поведением формы. Форма, это обычный компонент Delphi, но в
отличие от других, её нет на панели компонентов. Обычно форма создаётся при создании
нового проекта (File | New Application). Вновь созданная форма будет главной формой
приложения.
Дополнительные формы в проекте создаются через File | New Form. Так же существуют и
другие способы создания форм, но здесь мы не будем рассматривать их...
Как и любой другой компонент (объект) форма имеет свои методы и реагирует на
события. Давайте рассмотрим некоторые из этих событий...
Рождение формы в Delphi
OnCreate -> OnShow -> OnActivate -> OnPaint -> OnResize -> OnPaint ...
OnCreate Событие OnCreate возникает при создании TForm и только один раз. При
создании формы (у каторой свойство Visible установлено в True), события произойдут в
следующем порядке: OnCreate, OnShow, OnActivate, OnPaint. В обработчике события
OnCreate можно сделать какие-либо инициализационные действия, однако, любые
объекты созданные в OnCreate будут уничтожены в событии OnDestroy.
OnShow Это событие генерируется, когда форма станет видимой. OnShow вызывается
сразу перед тем, как форма станет видимой. Это событие случается, если установить
свойство формы Visible в True, либо при вызове методов Show или ShowModal.
OnActivate Это событие генерируется, когда форма становится активной, тоесть когда
форма получает фокус ввода. Это событие можно использовать для того, чтобы сменить
элемент формы который должен получить фокус.
OnPaint, OnResize Эти события вызываются каждый раз, когда форма изначально
создаётся. При этом OnPaint вызывается каждый раз, когда какому-нибудь элементу
формы необходимо перерисоваться (это событие можно использовать, если необходимо
при этом рисовать на форме что-то особенное).
Жизнь формы в Delphi
Когда форма создана и все её элементы ждут своих событий, чтобы обрабатывать их,
жизнь формы продолжается до тех пор, пока кто-нибудь не нажмёт крестик в верхнем
правом углу формы!
Уничтожение формы в Delphi
При уничтожении формы, события генерируются в следующем порядке:
... OnCloseQuery -> OnClose -> OnDeactivate -> OnHide -> OnDestroy
OnCloseQuery Если мы попытаемся закрыть форму при помощи метода Close либо
другим доступным способом (Alt+F4 либо через системное меню), то сгенерируется
событие OnCloseQuery. Таким образом, это событие можно использовать, чтобы
предотвратить закрытие формы. Обычно, событие OnCloseQuery используется для того,
чтобы спросить пользователя - уверен ли он (возможно в приложении остались
несохранённые данные).
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if MessageDlg('Really close this window?', mtConfirmation, [mbOk,
mbCancel], 0) = mrCancel then
CanClose := False;
end;
Обработчик события OnCloseQuery содержит переменную CanClose, которая определяет,
можно ли форме закрыться. Изначальное значение этой переменной True. Однако в
обработчике OnCloseQuery можно установить возвращаемое значение CloseQuery в False,
чтобы прервать выполнение метода Close.
OnClose Если OnCloseQuery вернул CanClose=True (что указывает на то, что форма
должна быть закрыта), то будет будет сгенерировано событие OnClose. Событие OnClose
даёт последний шанс, чтобы предотвратить закрытие формы. Обработчик OnClose имеет
параметр Action со следующими четырьмя возможными значениями:




caNone. Форме не разрешено закрыться. Всё равно, что мы установим CanClose в
False в OnCloseQuery.
caHide. Вместо закрытия, форма будет скрыта.
caFree. Форма будет закрыта, и занятые ей ресурсы будут освобождены.
caMinimize. Вместо закрытия, форма будет минимизирована. Это значение
устанавливается поумолчанию у дочерних форм MDI.
Замечание: Когда пользователь выключает Windows, то будет вызвано OnCloseQuery, а не
OnClose. Если Вы не хотите, чтобы Windows завершила свою работу, то поместите свой
код в обработчик события OnCloseQuery, хотя CanClose=False не сделает, того, что
сделано здесь.
OnDestroy После того, как метод OnClose будет обработан и форма будет закрыта, то
будет вызвано событие OnDestroy. В OnCreate обычно делаются действия,
противоположные тем, которые проделывались в OnCreate, то есть уничтожение
созданных объектов и освобождение выделенной памяти.
Естественно, что когда главная форма проекта будет закрыта, то и приложение будет
завершено.
Свойства формы Form
Form.ActiveControl - указывает на компонент, который должен быть по умолчанию
Form.Align - выравнивание компонента
Form.AlphaBlend - указывает, является ли форма прозрачной или нет
Form.AlphaBlendValue - степень прозрачности формы
Form.Anchors - показывает как происходит прикрепление к родительскому объекту
Form.AutoScroll - указывает, будет ли форма автоматически производить скроллинг или
нет
Form.AutoSize - указывает, должны ли компоненты на форме автоматически
корректировать размеры или нет
Form.BorderIcons - свойство определяющее какие кнопки должны присутствовать у окна
формы
• biSystemMenu - показывает или убирает все кнопки а также иконку у окна формы
• biMinimize - кнопка минимизации окна
• biMaximize - кнопка максимизации окна
• biHelp - кнопка помощи
Form.BorderStyle - свойство формы, отвечающее за вид оборки окна
• bsSizeable - установлено по умолчанию. стандартное окно которое может изменять свои
размеры
• bsDialog - окно выглядит как диалоговое
• bsNone - окно без оборки. у такого окна нет оборки и меню
• bsSingle - обычное окно только нельзя менять размеры формы
• bsSizeToolWin - окно с тонкой оборкой и только с одной кнопкой "Закрыть"
• bsToolWindow - ничем не отличается от bsSizeToolWin, только нельзя менять размеры
формы
Form.BorderWidth - ширина оборки окна
Form.Caption - заголовок окна
Form.ClientHeight - задаёт высоту клиентской области окна формы. высота берётся без
учёта ширины оборки и системного меню
Form.ClientWidth - задаёт ширину клиентской области окна формы. ширина берётся без
учёта ширины оборки и системного меню
Form.Color - цвет клиентской области окна формы. чтобы выбрать любой цвет, нужно
кликнуть 2 раза по этому параметру
Form.Constraints - свойство установки максимального и минимального значение
размеров окна
• MaxHeight - максимальная высота окна
• MaxWidth - максимальная ширина окна
• MinHeight - минимальная высота окна
• MinWidth - минимальная ширина окна
Form.Ctl3D - указывает, показывать окно / компонент в псевдо-3D плоскости или нет
Form.Cursor - свойство отвечает за курсор, который будет отображаться при наведении
на форму / компонент
Form.DockSite - указывает, можно ли на форму / компонент бросать другие компоненты с
помощью Drag & Drop
Form.DragKind - вид перетаскивания объекта при Drag & Drop
• dkDrag - стандартный Drag & Drop, при котором объект остаётся на месте
• dkDock - перетаскивается сам объект. если нужно чтобы компонент мог прикрепляться к
другим компонентам, установите этот параметр
Form.DragMode - режим Drag & Drop из двух вариантов
• dmManual - ручной режим. при таком режиме вы сами должны запускать перетаскивания
объекта
• dmAutomatic - режим Drag & Drop. будет включаться автоматически, если пользователь
начал перетаскивать компонент мышью
Form.Enabled - определяет доступность формы
Form.Font - шрифт, используемый при выводе текста на форме. кликните 2 раза чтобы
выбрать шрифты которые находятся в папке Fonts, а токже изменить начертание, размер,
цвет шрифта
Form.FormStyle - стиль формы
• fsNormal - нормальное окно формы
• fsMDIForm - окно является родительским для MDI-окон. эти окна относятся к классу
MDI - мультидокументные окна
• fsMDIChild - окно является дочерным MDI-окном. это окно будет внутри окна,
созданного в fsMDIForm
• fsStayOnTop - окно с этим параметром будет всегда поверх остальных
Form.Height - высота окна формы
Form.Hint - текст всплывающей подсказки, который будет появляться при наведении на
форму / компонент
Form.HorzScrollBar - параметры горизонтальной полосы прокрутки
Form.Icon - иконка, отображающая в заголовке окна. кликните 2 раза по этому свойству
для загрузки иконки
Form.Left - левая позиция окна
Form.Menu - меню которое используется в главном окне
Form.Name - имя формы / компонента
Form.ParentFont - если это свойство равно true, то для вывода текста оно будет
использовать тот же шрифт, что и для родительского объекта
Form.Position - позиция окна формы при старте приложения
• poDefault - Windows сам будет решать, где расположить окно и какие будут его размеры
• poDefaultPosOnly - Windows сам будет решать только, где расположить окно, а размеры
его будут такими, какие установите вы в свойствах
• poDefaultSizeOnly - Windows сам будет решать только, какими будут размеры окна, а
позиция будет такая, какую вы укажите в свойствах
• poDesigned - и размер и позиция будет такими,какими вы укажите в свойствах
• poDesktopCenter - окно формы будет распологаться по центру рабочего стола
• poMainFormCenter - окно будет распологаться по центру основной формы
• poOwnerFormCenter - окно будет распологаться по центру окна владельца, того окна
которое вызвало это
• poScreenCenter - окно будет распологаться по центру экрана
Form.ShowHint - устанавливает, нужно ли показывать всплывающие подсказки или нет
Form.Tag
Form.Top - верхняя позиция окна
Form.TransparentColor - определяет, является ли форма или компонент прозрачным. в
отличии от Form.AlphaBlend, эта прозрачность работает всегда. но здесь нельзя сделать
полупрозрачными формы и компоненты
Form.TransparentColorValue - значение прозрачного окна
Form.VertScrollBar - вертикальная полоса прокрутки. имеет те же параметры что и
горизонтальная
Form.WindowState - состояние окна после запуска
• wsNormal - окно показывается в нормальном состоянии
• wsMaximized - окно показывается максимизированным
• wsMinimized - окно показывается минимизированным
События формы Form
Form.OnActivate - когда приложение стало активным
Form.OnCanResize - это событие генерируется перед тем, как изменить размер окна
Form.onclick - генерируется когда пользователь щёлкнул по форме
Form.OnClose - генерируется когда окно формы закрывается
Form.OnCloseQuery - генерируется до закрытия окна
Form.OnCreate - генерируется когда окно создаётся
Form.ondblclick - генерируется когда пользователь дважды щёлкнул по окну формы
Form.Deactivate - генерируется когда окно деактивируется
Form.Destroy - это событие происходит когда окно уничтожается
Form.Hide - генерируется когда окно исчезает из виду
Form.onkeydown - событие генерируется когда нажата клавиша на клавиатуре
Form.onkeypress - генерируется когда нажата и отпущена клавиша на клавиатуре
Form.onkeyup - событие генерируется когда отпущена клавиша на клавиатуре
Form.onmousedown - генерируется когда нажата кнопка мыши
Form.OnMouseMove - генерируется когда двигается мышка
Form.onmouseup - событие генерируется когда отпускается кнопка мыши
Form.OnMouseWheel - генерируется колёсиком мыши
Form.OnMouseWheelDown - событие генерируется когда колёсико мыши прокручено
вниз
Form.OnMouseWheelUp - событие генерируется когда колёсико мыши прокручено вверх
Form.OnPaint - генерируется когда надо перерисовать окно
Form.OnResize - генерируется когда надо изменить размеры окна формы
Form.OnShortCut - событие происходит когда нажата горячая клавиша
Form.OnShow - когда показывается окно, но до фактической прорисовки
Модальные формы
http://www.berdaflex.com/ru/delphi/articles/modal_forms/default.html
Компонент TForm в VCL объединяет в себе свойства и может играть роль как главного
окна программы, так и диалогового (модального) окна. Чтобы форма работала в качестве
диалога, вместо метода Show нужно вызвать метод:
function ShowModal: Integer;
Этот метод реализует весь жизненный цикл модальной формы. Он показывает ее на
экране, активизирует ее и запрещает переключение на другие формы приложения.
Начинается обработка сообщений, которая происходит до тех пор, пока свойство
(Rb) property ModalResult: TModalResult;
не изменит своего значения, первоначально нулевого. Напомним, что это свойство формы
меняется непосредственно с помощью некоторых видов принадлежащих ей кнопок (см.
раздел "Кнопки"). Если для модальной формы программистом вызывается метод Close, то
ее единственным действием является то, что свойству ModalResult присваивается
значение mrCancel (без вызовов OnCloseQuery и OnClose).
Как только получено ненулевое значение ModalResult, вызьшается метод:
procedure CloseModal;
Его роль такая же, как у Close для обычной формы: сначала вызов CloseQuery, затем —
генерация события OnClose. Установив в параметре Action этого события значение
caNone, можно обнулить ModalResult и тем самым воспрепятствовать закрытию. В
противном случае форма деактивизируется и делается невидимой.
Модуль данных
Для размещения компонентов доступа к данным в приложении баз
данных желательно использовать специальную "форму" — модуль
данных (класс TDataModule). Обратите внимание, что модуль данных не
имеет ничего общего с обычной формой приложения, ведь его
непосредственным предком является класс TComponent. В модуле данных
можно размещать только невизуальные компоненты. Модуль данных
доступен разработчику, как и любой другой модуль проекта, на этапе
разработки. Пользователь приложения не может увидеть модуль данных
во время выполнения.
Для создания модуля данных можно воспользоваться Репозиторием
объектов или главным меню Delphi. Значок модуля данных Data Module
расположен на странице New.
Как уже говорилось, модуль данных имеет мало общего со стандартной
формой, хотя бы потому, что класс TDataModule происходит
непосредственно от класса TComponent. У него почти полностью
отсутствуют свойства и методы-обработчики событий, ведь от платформы
для других невизуальных компонентов почти ничего не требуется, хотя
потомки модуля данных, работающие в распределенных приложениях,
выполняют весьма важную работу.
Для создания структуры (модели, диаграммы) данных, с которой
работает приложение, можно воспользоваться возможностями,
предоставляемыми страницей Diagram Редактора кода. Любой элемент
из иерархического дерева компонентов модуля данных можно перенести
на страницу диаграммы и задать связи между ними.
При помощи управляющих кнопок можно задавать между элементами
диаграммы отношения синхронного просмотра и главный/подчиненный.
При этом производится автоматическая настройка свойств
соответствующих компонентов.
Для создания модуля данных (рис. 11.2) можно воспользоваться
Репозиторием объектов или главным меню Delphi. Значок модуля данных
Data Module расположен на странице New.
Для обращения компонентов доступа к данным, расположенным в
модуле данных, из других модулей проекта необходимо включить имя
модуля в секцию uses:
unit InterfaceModule;
...
implementation
uses DataModule;
...
DataModule.Tablel.Open;
...
Рис. 11.2. Модуль данных
Преимуществом размещения компонентов доступа к данным в модуле
данных является то, что изменение значения любого свойства проявится
сразу же во всех обычных модулях, к которым подключен этот модуль
данных. Кроме этого, все обработчики событий этих компонентов, т. е.
вся логика работы с данными приложения, собраны в одном месте, что
тоже весьма удобно.
Продолжая тему создания заготовок, упрощающих процесс разработки программного
обеспечения, хочется обратить Ваше внимание на фреймы. В отличие от внешнего
модуля, который мы рассматривали в предыдущей статье: "Работа с модальными
формами", доступ к фреймам осуществляется через палитру компонентов. Данное
свойство упрощает процесс визуальной разработки. А последующие изменения кода
фрейма автоматически переносятся на объекты, в которых данный фрейм используется.
Попробуем создать что-нибудь полезное. Первым претендентом для фрейма становиться
блок кода, который приходится часто повторять по ходу создания проекта. Одним из
таких участков можно назвать метод выравнивания кнопок диалоговых окон по правому
краю.
Создадим новый проект в Delphi. Новый фрейм создается вызовом пункта меню File-New,
и в выбранной палитре объектов выбором Frame. В созданный фрейм добавим панельконтейнер TPanel и две кнопки TBitBtn.
Рисунок 1. Заготовка фрейма
Для реализации эффекта выравнивания создадим метод на событие Resize панеликонтейнера.
procedure TFrameBtns.pnlBtnsResize(Sender: TObject);
begin
bbtnOk.Left:=Self.Width-10-bbtnOk.Width-bbtnCancel.Width;
bbtnCancel.Left:=Self.Width-5-bbtnCancel.Width;
end;
Чтобы обеспечить возможность включения данного фрейма в другие проекты сохраним
его в каталог общего доступа (в моем случае это $(DELPHI)\Projects\Common). Для
добавления ранее созданных фреймов в другие проекты предназначен пункт меню ProjectAdd to project. А для добавления фрейма в форму или контейнер нужно выбрать первый
элемент в стандартной палитре компонентов показанный на рисунке.
Рисунок 2. Кнопка добавления фрейма в палитре компонент Delphi
После выбора элемента-контейнера Вам будет предложен список доступных фреймов.
При выборе фрейма его элементы переносятся в контейнер. Код фрейма скрыт от
программиста, что разгружает общий код приложения, и позволяет сконцентрироваться на
решении основной задачи проекта.
Поместив фрейм в ранее созданный проект, мы получили форму, в которой кнопки
автоматически выравниваются по правому краю. Данное свойство мы можем
использовать многократно и с минимальными усилиями.
Рисунок 3. Пример использования фрейма
И напоследок «ложка дегтя». Хочется обратить Ваше внимание, что увлекаться фреймами
особенно не стоит. По крайней мере, для сложных конструкций. Код фрейма должен быть
тщательно продуман, отлажен и не должен изменяться после создания. Так как любые
изменения в коде фрейма влияют на все проекты его использующие. А это может
привести к неприятным сюрпризам.
54.Панель Dialogs. Использование стандартных диалогов в Delphi.
Панель Dialogs
На панели Dialogs расположен ряд невизуальных компонентов, позволяющих использовать в программе стандартные диалоговые окна Windows, например окна выбора и
сохранения файлов или изображений, окна выбора цвета и шрифта, окно настройки
принтера и другие.
ВНИМАНИЕ Эти компоненты не предназначены для выполнения конкретных
действий: загрузки фойла, печати, изменения текущего шрифта и
прочих. Они применяются только для получения от пользователя
желаемых значений настроек, например ввода полного имени
файла вместе с путем поиска, указания гарнитуры шрифта, задания
числа печатаемых страниц.
Все эти компоненты являются наследниками класса TCommon Dialog. Самый важный
его метод — это функция
function Execute: Boolean;
Она выполняет открытие соответствующего окна и возвращает значение True, если
пользователь щелкнул на кнопке ОК. Реальные поля ввода и заголовки определяются в конкретных компонентах. Когда диалоговое окно открывается в первый
раз, возникает событие OnShow, а при закрытии окна — событие OnCLose.
Компонент Окно выбора файла (TOpenDialog)
Компонент предназначен для выбора файла с целью последующего открытия. Свойства и
события класса TOpenOialog приведены ниже
DefaultExt
Расширение имени, используемое по умолчанию. Добавляется в конец выбранного
пользователем имени файла, если расширение не указано явно
FileName
Выбранное пользователем имя файла вместе с полным путем поиска
Files Список выбранных имен файлов. В свойстве Options должен быть включен флажок
ofAllowMultiSelect
Filter
Набор масок, в соответствии с которыми отбираются имена файлов для отображения в
диалоговом окне. Каждая маска состоит из двух частей: названия и
шаблона, — разделенных символом . Одному названию могут соответствовать
несколько шаблонов. Маски отделяются друг от друга символом |
Fitterlndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
Список ранее выбранных файлов (тип TStrings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии
диалогового окна
Options
Набор флажков, определяющих работу окна выбора файлов
Title
Заголовок диалогового окна
Среди методов этого класса следует отметить функцию
function GetStaticRect: TRect;
Она возвращает координаты прямоугольной области диалогового окна (часть клиентской области), зарезервированной для нужд разработчика (например, для отображения
содержимого текущего выбранного файла).
События класса TOpenDialog
OnCanClose
Пользователь пытается закрыть диалоговое окно. Обработчик этого события
позволяет проконтролировать правильность выбранного или введенного в
соответствующее голе окна имени файла и разрешить или запретить закрытие
OnFolderChange
Пользователь переключился в другой каталог
On IncludeItem
К текущему списку файлов в диалоговом окне будет добавлено новое имя.
Обработчик данного события дает возможность отбирать допустимые имена
по алгоритму, определяемому программистом
OnSelectionChange
Пользователь выбрал новое имя файла в диалоговом окне
OnTypeChange
Пользователь выбрал новую маску файлов (свойство Filter)
В следующем примере при щелчке на кнопке отображается диалоговое окно выбора
имени файла (объект OpenDialog1), которое имеет заголовок Выбор нужного файла, а
в списке отображаются все файлы, имеющие расширение .PAS. Это обеспечивается
присвоением свойству Filterlndex значения 2.
procedure TForml .ButtonlClick(Sender: TObject);
begin
OpenDialogl.Filter := 'Все файлы (* . * } | *.* | Файлы Паскаля
(*.pas)|'.PAS';
OpenDialogl.Title := 'Выбор нужного файла ';
OpenDialogl.Filterlndex := 2;
if OpenDialogl-Execute then
begin
AssignFile(F, OpenDialogl.FileName);
// работа с файлом F
end;
end;
Компонент Окно сохранения файла (TSaveDialog)
Этот компонент практически ничем не отличается от компонента TOpenDialog
за исключением некоторых настроек, специфичных для процесса сохранения файла.
Компоненты Окно открытия рисунка (TOpenPictureDialog)
и Окно сохранения рисунка (TSavePictureDialog)
Эти компоненты являются, соответственно, наследниками класса TOpenDialog и класса TSaveDialog. Диалоговые окна содержат дополнительную
область для быстрого просмотра содержимого выбранного графического файла.
Компонент Окно выбора шрифта (TFontDialog)
Компонент предназначен для вызова стандартного диалогового окна выбора
шрифта, доступного в системе. В соответствии с полями этого окна компонент имеет набор свойств, которые приведены ниже.
Device
Устройство, для которого отображается список доступных шрифтов. Возможные
значения — fdScreen (экран), fdPrinter (принтер) и fdBoth (как экран, так и
принтер)
Font
Выбранный пользователем шрифт (тип TFont)
MaxFontSize
Максимальный размер шрифта, ограничивающий содержимое показываемого
списка шрифтов
MinFontSize
Минимальный размер шрифта, ограничивающий содержимое показываемого
списка шрифтов
Options
Дополнительные характеристики внешнего вида диалогового окна
Если, например, на форме имеется надпись Labell, то при щелчке на кнопке Buttonl
следующий обработчик вызовет диалоговое окно выбора шрифта. После того как
пользователь сделает выбор, шрифт, которым сделана надпись, изменится.
procedure TForml.ButtonlClick(Sender: TObject);
begin
if FontDialogl.Execute then
Labell.Font.Assign (FontDialogl.Font);
end;
Компонент Окно выбора цвета (TColorDialog)
С помощью данного компонента вызывается стандартное диалоговое окно
выбора цвета (рис. 4.1,стр 217).
Свойство Color (тип ТСоlor) содержит выбранный пользователем цвет, а свойство
CustomColors (тип TStrings) хранит в текстовом формате описание дополнительных
пользовательских цветов. Цвет в этом формате задается шестью символами, определяющими в шестнадцатеричном виде значение цвета в соответствии с требованием
цветовой системы RGB. Каждый байт задается двумя символами, например FFFFFF
или 08ЕЕ08. Имеется также свойство Options, присутствующее у всех подобных компонентов и позволяющее выполнять тонкие специфические настройки работы окна.
В следующем примере демонстрируется, как при щелчке на кнопке Buttonl происходит
вызов окна выбора цвета. Выбранный цвет будет использован для изменения цвета
фигуры Shapel.
procedure TForml.ButtonlClick(Sender: TObject);
begin
if ColorDialogl.Execute
then Shapel.Color := ColorDialogl.Color;
end;
Компоненты Печать и Настройка принтера и
Настройка параметров страницы печати
(TPrintDialog, TPrinterSetupDialog, TPageSetupDialog)
Компонент TPrinterSetupDialog, предназначенный для настройки параметров
работы принтера, не имеет оригинальных свойств, потому что эти настройки
существенно различаются для разных видов принтеров. На основании этого
компонента можно создавать свои собственные компоненты для конкретных принтеров.
Компонент TPrintDialog отображает стандартное окно печати Windows.BmM
можно задать различные параметры печати, которые определяются следующими
свойствами.
Collate
Флажок Разобрать по копиям
Copies
Число копий
FromPage
Номер страницы, с которой начнется печать
МахРаgе
Максимальное число страниц, которое может быть напечатано
MinPage
Минимальное число страниц, которое может быть напечатано
Options
Дополнительные параметры настройки
PrintRange
Вид диапазона печатаемых страниц документа. Возможные значения: prAUPages
(все страницы); prSelection (страницы выбранного фрагмента); prPageNums
(страницы из диапазона From Page/To Page)
PrintToFile
Имеет значение True, если вывод должен осуществляться не на принтер, а в файл
ТоРаgе
Номер страницы, на которой заканчивается печать
Компонент TPageSetupDialog позволяет настроить характеристики печатаемых страниц. Они задаются в свойствах Margin Bottom, MarginLeft, Margin Right, MarginTop
(нижняя, левая, правая, верхняя границы печати), PageHeightH PageWidth (высота и ширина страницы), а также в свойстве Options, описывающем дополнительные
параметры. Единицы измерения размеров задаются в свойстве Units.
Компонент Поиск (TFindDiaLog)
Компонент используется для отображения диалогового окна поиска текстовой строки.
Свойства класса TFindDialog приведены ниже.
FindText
Строка для поиска
Options
Дополнительные настройки
Position
Координата левого верхнего угла диалогового окна при его выводе на экран
(в пикселах)
Из методов этого класса следует отметить процедуру
procedure CloseDialog;
Эта процедура закрывает окно, но не меняет значений установленных свойств, чтобы
в дальнейшем можно было выполнить повторный поиск со старыми параметрами.
При щелчке на кнопке Найти далее генерируется событие OnFind.
Компонент Поиск и замена (TReplaceDialog)
Данный компонент является наследником компонента TFindDialog. Он несколько
расширяет его возможности и позволяет вводить строку для замены найденого текста.
Компонент имеет новое свойство ReplaceText и соответствующее ему
поле в диалоговом окне. При щелчке на кнопке Заменить или Заменить все генерируется сообщение On Replace.
55. Работа с Базами данных в Delphi. Основные невизуальные
компоненты.
http://www.intuit.ru/department/pl/intdelphi/29/
здесь на сайте начинается тема по БД. Может пригодится при подготовке или на экзамене
если что=)
Существует три основных механизма доступа к данным: BDE, ADO, DBExpress.
Имеется абстрактный базовый класс TDataSet, набор данных. Он объявлен в модуле
DB. У класса TDataset есть свои потомки в каждом из указанных механизмов. Например: в
BDE - TTable, TQuery; В ADO - TADOTable, TADOQuery; в DBExpress - TSQLTable,
TSQLQuery.
Для того, чтобы создать приложение, которое работает с БД нужно описать, как мы
соединяемся с данными. Для BDE - это TDataBase создается и в нем все описывается, для
ADO - это TADOConnection создается и в нем все описывается, в DBExpress - это
TSQLConnetion создается и в нем все описывается. Уже дальше с помощью этой
информации мы указываем, как работать с конкретной таблицей.
Компонент DataBase представляет базу данных как единое целое, т. е. совокупность
таблиц, а компонент Table — одну из таблиц базы данных. Компонент DataSource
(источник данных) обеспечивает связь компонента отображения-редактирования данных
(например, компонента DBGrid) и источника данных, в качестве которого может
выступать таблица (компонент Tаblе) или результат выполнения SQL-запроса к таблице
(компонент SQL). Компонент DataSource позволяет оперативно выбирать источник
данных, использовать один и тот же компонент, например, DBGrid для отображения
данных из таблицы или результата выполнения SQL-запроса к этой таблице.
Механизм взаимодействия компонентов:
БД ->Table->DataSource->Компонент отображения(например DBGrid)
БД -> <- (туда обратно)Query->DataSource->Компонент отображения(например DBGrid)
((Не знаю точно нужно это в этом вопросе или нет, но на всякий случай.
Свойства компонента Table:
Name Database - Имя компонента. Используется для доступа к свойствам компонента.
NameTable - Имя базы данных, частью которой является таблица (файл данных), для
доступа к которой используется компонент. В качестве значения свойства следует
использовать псевдоним базы данных
Name Table - Имя файла данных (таблицы данных), для доступа к которому используется
компонент
Type - Тип таблицы. Таблица может быть набором данных в формате Paradox («Paradox),
dBase (ttDBase), FoxPro («FoxPro) или представлять собой форматированный текстовый
файл (ttASCII).
Active – признак активизации файла данных (таблицы). В результате присваивания
свойству значения True происходит открытие файлы таблицы.
Свойства компонента DataSource:
Name – Имя компонента. Используется для доступа к свойствам компонента.
DataSet – Имя компонента, представляющего собой входные данные.))
Набор данных
Информация таблицы базы данных описывается классом TDataSet, на основе которого, в
частности, создан компонент ТТаble. С его помощью выполнить добавление новой записи
проще всего.
При работе с наборами данных TDataSet (и их наследниками) используется понятие
указателя набора данных. Он определяет, какая запись таблицы базы данных в настоящий
момент является текущей. Когда мы работаем с таблицами TDBGrid, менять, удалять или
добавлять в любой момент времени можно только одну запись. Она выделяется в таблице
звездочкой и считается текущей. У класса TDataSet имеются свойства и методы для
перемещения указателя по набору данных. Считается, что любые операции по изменению
или удалению информации выполняются над текущей записью — той, на которой
установлен указатель. Запись изменений в базе данных выполняется только после вызова
метола Post. Подобный подход удобен тем, что позволяет отменить изменения, внесенные
в текущую запись, если вдруг обнаруживается, что значение некоторого поля указано
некорректно. Когда запись состоит из большого числа полей, такое случается нередко.
Для отказа от модификации следует вызвать метод Cancel.
Модификация набора данных
Чтобы выполнить модификацию набора данных, надо прежде всего убедиться, что он
открыт. Состояние набора проверяется значением свойства Active, которое в этом случае
должно быть равно True. Открытие набора выполняется с помощью метода Open (без
параметров).
Следующий шаг — определение типа вносимых изменений. Данные могут добавляться
(метод Insert без параметров) или модифицироваться (метод Edit без параметров).
Модификации подвергаются поля текущей записи. После выполнения всех изменений их
надо зафиксировать в базе данных (метод Post) или отказаться от сохранения (метод
Cancel).
Поля записи
Процесс внесения изменений в поля текущей записи выполняется с помощью обычных
операторов присваивания. Поля текущей записи хранятся в свойстве набора данных
Fields, имеюшем тип TFields. Это список элементов типа TField, описывающих
конкретные поля. Нумерация полей начинается с нуля. Класс TField имеет набор очень
удобных свойств, позволяющих обращаться к каждому полю в зависимости от его
реального типа в базе данных. Например, свойство AsString позволяет получить доступ к
значению поля в текстовом формате.
Пример:
Edit1.Text := Table1.Fields[0].AsString;
Tablel.Fields[0].AsString := Editl.Text;
Другие аналогичные свойства приведены ниже.
AsBoolean - Формат Boolean
AsCurrency - Формат Currency
AsDateTime - Формат TDateTime
AsRoat - Формат Double
Aslnteger - Формат Integer
AsString - Формат String
AsVariant - Формат Variant
При этом следует учитывать реальный тип поля в базе данных, чтобы не возникало
ошибочных ситуаций типа попытки записать строку в числовое поле.
Другое важнейшее свойство класса TField — Value позволяет обращаться к текущему
содержимому поля напрямую.
Для таблиц определен еще один способ доступа к полю: по его имени с помощью
метода FieldByName:
function FieldByName(const FieldName: string]: TField;
Например, вместо оператора
DataModule2.Games.Fields[l] .AsString := Editl. Text , записывающего данные в поле таблицы Games с индексом 1, можно использовать
метод доступа к полю по его названию Name.
DataModule2.Games.FieldByName('Name1).AsString :=
Editl.Text;
(как сказал Хмельнов, нужно сказать про основные свойства и методы, они указаны ниже, но еще ниже указаны ВСЕ
свойства, методы и событий и жирным и подчеркнутым выделены ну такие основные, просто на всякий случай описала
здесь все)
САМЫЕ ОСНОВНЫЕ МЕТОДЫ класса TDataSet
procedure First - Указатель устанавливается на первую запись набора данных
procedure Last - Указатель устанавливается на последнюю запись набора
данных
procedure Next - Указатель перемещается к следующей записи набора данных
procedure Prior - Указатель перемещается к предыдущей записи набора
данных
function MoveBy (Distance: Integer): Integer - Происходит перемещение указателя на число
записей, указанное в параметре, по отношению к текущей записи. Если значение
параметра отрицательно, то перемещение осуществляется к началу набора. Функция
возвращает число записей, на которое указатель был смещен реально.
Основные свойства TDataSet
Active - Открывает (True) и закрывает (False) набор данных
AutoCalcFields - Определяет способ вычислений с использованием полей
Bof - Возвращает True, если курсор находится на первой записи базы данных, и False в
противном случае
CachedUpdates - Если установлено значение True, изменения сохраняются в кэше на
компьютере клиента до полного завершения транзакции В противном случае все
изменения в базе данных производятся при завершении работы с текущей записью
CanModify - Определяет, может ли пользователь редактировать данные
DataSource - Имя компонента DataSource, связанного с набором данных
DatabaseName - Имя базы данных, используемой в настоящий момент
Eof - Возвращает True, если курсор находится на конце файла, и False в противном случае
FieldCount - Количество полей в наборе данных. Поскольку набор данных может быть
динамическим (например, результат запроса), количество полей может варьироваться от
запроса к запросу
Fields - Массив объектов TFields, которые содержат информацию о полях базы данных
FieldValues - Возвращает значение указанного поля текущей записи. Значение имеет тип
Variant
Filter - Выражение, используемое для фильтрации записей
Filtered - Если установлено значение True, фильтрация набора данных осуществляется в
зависимости от свойства Filter или события OnFilter-Record В противном случае
возвращается весь набор данных
FilterOptions - Управляет работой фильтров
Found - Показывает, была ли успешной операция поиска
Handle - Дескриптор курсора BDE Используется только при прямых обращениях к BDE
Modified - Показывает, была ли текущая запись изменена
RecNo - Номер текущей записи в наборе данных
RecordCount - Возвращает количество записей в наборе данных
State - Возвращает текущее состояние набора данных (dsEdit, dsBrowse, dslnsert и т д )
UpdateOb^ect - Указывает компонент TUpdateOb^ect, используемый для кэшируе-мых
изменений
Updates Pending - Значение True указывает, что буфер кэшируемых изменений содержит
данные, не сохраненные в базе данных
Основные методы TDataSet
Append - Создает пустую запись и добавляет ее в конец набора данных
AppendRecord - Добавляет запись в конец набора данных, используя заданные значения
полей
ApplyUpdates - Указывает базе данных на необходимость сохранения всех кэшированных
изменений Реальное обновление базы данных не происходит до вызова метода
CommitUpdates
Cancel - Отменяет все изменения в текущей записи, если они еще не были сохранены
CancelUpdates - Отменяет отложенные изменения, занесенные в кэш
ClearFields - Очищает все поля текущей записи
CommitUpdates - Сохраняет в базе данных все изменения и очищает буфер кэшируемых
изменений
Close - Закрывает набор данных
Delete - Удаляет текущую запись
DisableControls - Запрещает ввод из всех компонентов, связанных с набором данных
Edit - Разрешает редактирование текущей записи
EnableControls - Разрешает ввод из всех компонентов, связанных с набором данных
FetchAll - Считывает и локально сохраняет все записи от курсора до конца набора данных
FieldByName - Возвращает указатель TField по заданному имени поля
FindFirst - Осуществляет поиск первой записи, удовлетворяющей текущему критерию
фильтрации
PindNext - Осуществляет поиск следующей записи, удовлетворяющей текущему критерию
фильтрации
FindLast - Осуществляет поиск последней записи, удовлетворяющей текущему критерию
фильтрации
FindNext - Осуществляет поиск предыдущей записи, удовлетворяющей текущему
критерию фильтрации
FindField - function FindField(count FieldName: string): TField; Метод FindField возвращает
объект TField (в массиве Fields TDataSet), свойство FieldName которого равно имени поля,
заданного в качестве параметра. Если поле не найдено, то возвращается Nil. Заметьте, что
метод FieldByName является удобной оболочкой вокруг FindField. Достоинство
FieldByName заключается в том, что если указанное поле не найдено, метод возбуждает
исключение и предоставляет возможность реализации более живучего кода в случае,
когда структура основной таблицы должна измениться (когда поля должны быть
переименованы или удалены). Вы можете использовать FindField в ситуациях, когда не
хотите возбуждения исключения и знаете, как обработать случай, если поле не найдено
(или хотите самостоятельно возбудить другое исключение).
First - Перемещает курсор на первую запись набора данных
FreeBookmark - Удаляет закладку, установленную с помощью GetBookmark, и
освобождает память, выделенную для закладки
GetBookmark - Устанавливает закладку на текущей записи
GetFieldNames - Возвращает список имен полей набора данных
GotoBookmark - Устанавливает курсор на запись, отмеченную закладкой
Insert - Вставляет запись и переводит набор данных в режим редактирования
InsertRecord - Вставляет запись в набор данных, используя заданные значения полей
Last - Устанавливает курсор на последнюю запись набора данных
Locate - Осуществляет поиск записи в наборе данных. Locate Этот универсальный метод
поиска устанавливает текущую запись как первую строку, удовлетворяющую набору
критериев поиска. Используя метод Locate мы можем искать значения одного или более
полей, расположенных в массиве переменных.
Lookup - Осуществляет поиск записи в наборе данных самым быстрым методом и
возвращает значения ее полей
MoveBy - Перемещает курсор на заданное количество строк
Next - Перемещает курсор на следующую запись
Open - Открывает набор данных
Post - Записывает измененную запись в базу данных или в буфер кэшируемых изменений
Prior - Перемещает курсор на предыдущую запись
Refresh - Обновляет набор данных
RevertRecord - При использовании кэширования этот метод отменяет все ранее сделанные
изменения, еще не сохраненные в базе данных
SetFields - Устанавливает значения для всех полей записи
UpdateStatus - При использовании кэширования возвращает текущий статус изменений
Основные события TDataSet
AfterCancel - Генерируется после отмены редактирования записи
AfterClose - Генерируется при закрытии набора данных
AfterDelete - Генерируется после удаления записи из набора данных
AfterEdit - Генерируется после редактирования записи
Afterlnsert - Генерируется после вставки записи
AfterOpen - Генерируется после открытия набора данных
AfterPost - Генерируется после отправления изменений в базу данных или кэш
BeforeCancel - Генерируется перед отменой редактирования
BeforeClose - Генерируется перед закрытием набора данных
BeforeDelete - Генерируется перед удалением записи
BeforeEdit - Генерируется перед переходом набора данных в режим редактирования
Beforelnsert - Генерируется перед вставкой записи
BeforeOpen - Генерируется непосредственно перед открытием набора данных (между
установкой Active в True и действительным открытием)
BeforePost - Генерируется перед тем, как изменения будут отправлены в базу данных (или
кэш изменений)
OnCalcField - Генерируется при выполнении вычислений с использованием полей
OnDeleteError - Генерируется при ошибке удаления записи
OnEditError - Генерируется при ошибке редактирования записи
OnFilterRecord - Генерируется при доступе к новой записи, если для свойства Field
установлено значение True
OnNewRecord - Генерируется при добавлении новой записи к набору данных
OnPostError - Генерируется при ошибке сохранения изменений
OnUpdateError - Генерируется при ошибке во время сохранения кэшированных изменений
в базе данных
OnUpdateRecord - Генерируется при сохранении кэшированных изменений
56. Класс TDataSet. Основные свойства и методы. Цикл перебора всех
записей TDataSet
http://www.intuit.ru/department/se/dbpdelphi/4/
Класс TDataSet
TDataSet класс - один из наиболее важных объектов БД. Чтобы начать работать с ним, Вы
должны взглянуть на следующую иерархию:
TDataSet
|
TDBDataSet
|
|-- TTable
|-- TQuery
|-- TStoredProc
TDataSet содержит абстрактные методы там, где должно быть непосредственное
управление данными. TDBDataSet знает, как обращаться с паролями и то, что нужно
сделать, чтобы присоединить Вас к определенной таблице. TTable знает (т.е. уже все
абстрактные методы переписаны), как обращаться с таблицей, ее индексами и т.д.
Как Вы увидите в далее, TQuery имеет определенные методы для обработки SQL
запросов.
TDataSet - инструмент, который Вы будете использовать чтобы открыть таблицу, и
перемещаться по ней. Конечно, Вы никогда не будете непосредственно создавать объект
типа TDataSet. Вместо этого, Вы будете использовать TTable, TQuery или других
потомков TDataSet (например, TQBE).
Рабочие состояния (Статусы) Dataset
Наборы данных могут быть в нескольких состояниях (Статусах), которые отображаются в
свойстве State. Вот какие состояния могут быть (смотрите TDataSetState в исходниках):
State
dsInactive - Набор данных закрыт, доступ к данным невозможен
dsBrowse - В этом состоянии можно просматривать набор данных, искать какие-либо
значения
dsEdit - В этом состоянии можно редактировать данные в текущей строке. Изменённые
значения не будут записаны, пока не применён метод Post.
dsInsert - В этом состоянии только что вставлена новая строка данных и туда можно
заносить новые значения. Строка не будет записана, пока не применён метод Post.
Открытие и закрытие DataSet
Если Вы используете TTable для доступа к таблице, то при открытии данной таблицы
заполняются некоторые свойства TTable (количество записей RecordCount, описание
структуры таблицы и т.д.).
Прежде всего, Вы должны поместить во время дизайна на форму объект TTable и указать,
с какой таблицей хотите работать. Для этого нужно заполнить в Инспекторе объектов
свойства DatabaseName и TableName.
В DatabaseName можно либо указать директорию, в которой лежат таблицы в формате
dBase или Paradox (например, C:\DELPHI\DEMOS\DATA), либо выбрать из списка
псевдоним базы данных (DBDEMOS).
Псевдоним базы данных (Alias) определяется в утилите Database Engine Configuration.
Теперь, если свойство Active установить в True, то при запуске приложения таблица будет
открываться автоматически.
Имеются два различных способа открыть таблицу во время выполнения программы. Вы
можете написать следующую строку кода:
Table1.Open;
Или, если Вы предпочитаете, то можете установить свойство Active равное True:
Table1.Active := True;
Нет никакого различия между результатом производимым этими двумя операциями.
Метод Open, однако, сам заканчивается установкой свойства Active в True, так что может
быть даже чуть более эффективно использовать свойство Active напрямую.
Также, как имеются два способа открыть a таблицу, так и есть два способа закрыть ее.
Самый простой способ просто вызывать Close:
Table1.Close;
Или, если Вы желаете, Вы можете написать:
Table1.Active := False;
Еще раз повторим, что нет никакой существенной разницы между двумя этими
способами. Вы должны только помнить, что Open и Close это методы (процедуры), а
Active - свойство.
Навигация (Перемещение по записям)
После открытия таблицы, следующим шагом Вы должны узнать как перемещаться по
записям внутри него. Следующий обширный набор методов и свойства TDataSet
обеспечивает все , что Вам нужно для доступа к любой конкретной записи внутри
таблицы:
procedure First;
procedure Last;
procedure Next;
procedure Prior;
property BOF: Boolean read FBOF;
property EOF: Boolean read FEOF;
procedure MoveBy(Distance: Integer);
Дадим краткий обзор их функциональных возможностей:
Table1.First перемещает Вас к первой записи в таблице.
Table1.Last перемещает Вас к последней записи.
Table1.Next перемещает Вас на одну запись вперед.
Table1.Prior перемещает Вас на одну запись Назад.
Вы можете проверять свойства BOF или EOF, чтобы понять, находитесь ли Вы в начале
или в конце таблицы.
Процедура MoveBy перемещает Вас на N записей вперед или назад в таблице. Нет
никакого функционального различия между запросом Table1.Next и вызовом
Table1.MoveBy(1). Аналогично, вызов Table1.Prior имеет тот же самый результат, что и
вызов Table1.MoveBy(-1).
простой способ пробежать по всем записям в dataset:
Table1.First;
while not Table1.EOF do begin
//делаем какую-то обработку, получаем доступ к полям
Table1.Next;
end;
MoveBy, которая позволяет Вам переместиться на N записей вперед или назад в таблице.
Если Вы хотите переместиться на две записи вперед, то напишите:
MoveBy(2);
И если Вы хотите переместиться на две записи назад, то:
MoveBy(-2);
Поля( как получать доступ к полям)
В большинстве случаев, когда Вы хотите получить доступ из программы к
индивидуальным полям записи, Вы можете использовать одно из следующих свойств или
методов, каждый из которых принадлежат TDataSet:
property Fields[Index: Integer];
function FieldByName(const FieldName: string): TField;
property FieldCount;
1) Свойство FieldCount возвращает число полей в текущей структуре записи.
2) Если Вы хотите программным путем прочитать имена полей, то используйте
свойство Fields для доступа к ним:
var
S: String;
begin
S := Fields[0].FieldName;
end;
Если Вы работали с записью в которой первое поле называется CustNo, тогда код
показанный выше поместит строку “CustNo” в переменную S. Если Вы хотите получить
доступ к имени второго поля в вышеупомянутом примере, тогда Вы могли бы написать:
S := Fields[1].FieldName;
Короче говоря, индекс передаваемый в Fields (начинающийся с нуля), и определяет номер
поля к которому Вы получите доступ, т.е. первое поле - ноль, второе один, и так далее.
3) Если Вы хотите прочитать текущее содержание конкретного поля конкретной
записи, то Вы можете использовать свойство Fields или метод FieldsByName. Для
того, чтобы найти значение первого поля записи, прочитайте первый элемент
массива Fields:
S := Fields[0].AsString;
Предположим, что первое поле в записи содержит номер заказчика, тогда код, показанный
выше, возвратил бы строку типа “1021”, “1031” или “2058”.
Другие аналогичные свойства приведены ниже.
AsBoolean - Формат Boolean
AsCurrency - Формат Currency
AsDateTime - Формат TDateTime
AsRoat - Формат Double
Aslnteger - Формат Integer
AsString - Формат String
AsVariant - Формат Variant
Если хотите, Вы можете использовать функцию FieldsByName вместо свойства Fields:
S := FieldsByName(‘CustNo’).AsString;
Как показано в примерах выше, и FieldsByName, и Fields возвращают те же самые данные.
Два различных синтаксиса используются исключительно для того, чтобы обеспечить
программистов гибким и удобным набором инструментов для программного доступа к
содержимому DataSet.
4) FindField - function FindField(count FieldName: string): TField; Метод FindField
возвращает объект TField (в массиве Fields TDataSet), свойство FieldName которого
равно имени поля, заданного в качестве параметра. Если поле не найдено, то
возвращается Nil. Заметьте, что метод FieldByName является удобной оболочкой
вокруг FindField. Достоинство FieldByName заключается в том, что если указанное
поле не найдено, метод возбуждает исключение и предоставляет возможность
реализации более живучего кода в случае, когда структура основной таблицы
должна измениться (когда поля должны быть переименованы или удалены). Вы
можете использовать FindField в ситуациях, когда не хотите возбуждения
исключения и знаете, как обработать случай, если поле не найдено (или хотите
самостоятельно возбудить другое исключение).
Поля(встать на какую-то запись)
1) Locate - Осуществляет поиск записи в наборе данных. Locate Этот универсальный
метод поиска устанавливает текущую запись как первую строку,
удовлетворяющую набору критериев поиска. Используя метод Locate мы можем
искать значения одного или более полей, расположенных в массиве переменных.
(если значений много, то используем параметр типа Variant, туда можно просто
записывать значения разных типов
Если значений много то мы пишем: Var array of [пишем все значения, которые буду
помещены(которые будут искаться)] – это он сказал на консультации.)
function Locate (const KeyFields r string,- const KeyValues:
Variant; Options: TLocateOptions): Boolean;
Получает в качестве параметров список полей, по которым будет
выполняться поиск, список значений для каждого из полей и набор вспомогательных настроек поиска (тип TLocateOptions).
Например, поиск значения 5 в индексированном поле Playability может выполняться
следующим образом.
DataModule2.Games.Locate('Playability1, VarArrayOf([5]), []);
Обратите внимание на то, как множество [5] преобразуется к типу Variant.
Пример.
AdoTable1.Locate('Name','Zoom',[]);
{...или...}
var ffield, fvalue: string;
opts : TLocateOptions;
ffield := 'Name';
fvalue := 'zoom';
opts := [loCaseInsensitive];
if not AdoTable1.Locate(ffield, fvalue, opts) then ShowMessage(fvalue + ' not found in ' +
ffield);
еще вариант:
Метод Locate
Метод Locate ищет первую запись, удовлетворяющую условию поиска. Если запись
найдена, метод делает ее текущей и возвращает True. В противном случае метод
возвращает False и курсор не меняет положения. Поле, по которому ведется поиск, не
обязательно должно быть индексировано. Однако если поле индексировано, то метод
ищет запись по индексу, что значительно ускоряет поиск. Поиск может вестись как по
одному полю, так и по нескольким полям. Метод имеет три параметра:
function Locate (const KeyFields: String; const KeyValues: Variant;
Options: TLocateOptions) : Boolean;
Параметр KeyFields задает поле или список полей, по которым ведется поиск. Если
имеется несколько полей, их разделяют точкой с запятой.
Параметр KeyValues является вариантным массивом, в котором задаются критерии
поиска. При этом первое значение KeyValues ставится в соответствие с первым полем,
указанным в KeyFields. Второе - со вторым, и так далее.
Третий параметр Options позволяет задать некоторые опции поиска:
loCaseInsensitive - поиск ведется без учета высоты букв, то есть, считаются одинаковыми
строки "строка", "Строка" или "СТРОКА".
loPartialKey - запись будет удовлетворять условию, если ее часть содержит искомый текст.
То есть, если мы ищем "ст", то удовлетворять условию будут "строка", "станция",
"стажер" и т.п.
Пустой набор [] указывает, что настройки поиска игнорируются. То есть, строка ищется
"как есть".
Примеры использования метода Locate:
Table1.Locate('Фамилия', Edit1.Text, []);
Table1.Locate('Фамилия;Имя',
VarArrayOf(['Иванов', 'Иван']), [loCaseInsensitive]);
Как видно из примера, если для поиска вы используете одно поле, то значение может
передаваться напрямую из компонента Edit. Если же вы используете список полей, то
должны передать в метод массив вариантов, в которых содержатся искомые значения, по
одному на каждое поле. При установке компонента ADOTable в раздел uses
прописывается модуль ADODB, который содержит описания всех свойств, методов и
событий компонента. Желательно использовать метод в том модуле, где установлен этот
компонент.
Рассмотрим применение этого метода на примере. Откройте проект. Перейдите на модуль
DM, где у нас хранятся компоненты доступа к базе данных. Процедуру поиска реализуем
в этом модуле, а чтобы с ней можно было работать из других форм, опишем ее в разделе
public:
public
{ Public declarations }
procedure MyLocate(s: String);
Как видите, в процедуру передается параметр - строка. В ней мы будем передавать
искомую фамилию. Если курсор находится на описании нашей процедуры, то нажмите
<Ctrl + Shift + C>, чтобы сгенерировать процедуру автоматически. Процедура будет иметь
следующий код:
procedure TfDM.MyLocate(s: String);
begin
TLichData.Locate('Фамилия', s, [loPartialKey]);
end;
Таким образом, при нахождении подходящей записи курсор будет перемещаться к ней.
На главной форме выделите компонент Edit, предназначенный для поиска по фамилии.
Создайте для него событие onChange, которое наступает при изменении текста в поле
компонента. В созданной процедуре пропишите вызов поиска:
fDM.MyLocate(Edit1.Text);
Сохраните пример, скомпилируйте и опробуйте результаты поиска. Метод Locate
рекомендуется использовать везде, где это возможно, поскольку он всегда пытается
применить наиболее быстрый поиск. Если поле индексировано, и использование индекса
ускорит процесс поиска, Locate использует индекс. Если поле не имеет индекса, Locate все
равно ищет данные наиболее быстрым способом. Это делает вашу программу
независимой от индексов.
2) Lookup - Осуществляет поиск записи в наборе данных самым быстрым методом и
возвращает значения ее полей
Метод Lookup не перемещает курсор в соответствующую строку, а только возвращает её
значение. Lookup возвращает массив переменных, содержащих значения из полей,
указанных в разделённом точкой с запятой списке имён, значения которых должны быть
возвращены из интересующей нас строки. Если соответствующих нашему запросу строк
не найдено, то Lookup вернёт пустую (Null) переменную.
function Lookup (const KeyFields: string,- const KeyValues:
Variant; const ResultFields: string): Variant;
В дополнение к поиску возвращает массив значений полей (типа Variant), названия
которых указаны в параметре ResultFields.
DataModule2.Games.Lookup('Playability1 , VarArrayOf([5]), 'Sound');
Пример.
Следующий пример заполняет заполняет массив переменных LookupRes
var LookupRes: Variant;
LookupRes := ADOTable1.Lookup
('Name', 'Zoom', 'Author; Description');
if not VarIsNull(LookupRes) then ShowMessage(VarToStr(LookupRes[0])) //имя автора
Одно из преимуществ методов Locate и Lookup, состоит в том, что они не требуют, чтобы
таблица была проиндексирована. Однако, функция Locate будет работать намного
быстрее, если таблица будет проиндексирована.
(Возвращается значение типа Variant, если это будет несколько полей то Variant
массив(сказал он на консультации))
Еще вариант:
Метод Lookup
Метод Lookup, в отличие от Locate, не меняет положение курсора в таблице. Вместо этого
он возвращает значения некоторых ее полей. Причем в отличие от Locate, этот метод
осуществляет поиск лишь на точное соответствие. Такой способ поиска востребован реже,
однако в иных случаях этим методом очень удобно пользоваться. Рассмотрим синтаксис
этого метода.
function Lookup (const KeyFields: String;
const KeyValues: Variant;
const ResultFields: String) : Variant;
Как вы видите, первые два параметра такие же, как у Locate. А вот третий параметр и
возвращаемое значение отличаются. В строке ResultFields через точку с запятой
перечисляются поля таблицы, значения которых метод должен вернуть. Возвращаются
эти значения в виде вариантного массива. Проблема в том, что вернуться может значение
Null, то есть, ничего, или Empty (пустой) и это нужно проверять. Рассмотрим работу
метода Lookup на примере нашей программы.
Прежде всего, вспомним, как работает тип данных Variant. В переменную типа Variant
можно поместить любое значение, в том числе и массив. Этот тип данных обычно
используют, когда не известно заранее, данные какого типа нам понадобятся на этапе
выполнения программы. Когда переменной типа Variant присвоено значение, имеется
возможность проверить тип данных этого значения. Для этого служит функция VarType():
function VarType(const V: Variant): TVarType;
В качестве параметра в функцию передается переменная вариантного типа. Функция
возвращает значение типа TVarType. Это значение указывает, какого типа данные
содержаться в переменной. Значение может быть varSmallint (короткое целое), varInteger
(целое), varCurrency (денежный формат) и так далее. Чтобы увидеть полный список
возвращаемых функцией значений, в редакторе кода установите курсор на название
функции и нажмите <Ctrl + F1>, вызвав контекстный справочник. Нас же в данном
примере интересуют всего два значения: varNull (записи нет) и varEmpty (запись пустая).
Если в программе мы заранее не проведем проверку на эти значения, то вполне можем
вызвать ошибку программы. Если же поиск прошел успешно, то будет возвращен массив
вариантных значений, элементы которого начинаются с нуля. Каждый элемент массива
будет содержать данные одного из указанных полей.
Загрузите проект программы. Для поиска воспользуемся кнопкой с надписью "Найти",
расположенной в верхней части главной формы. Идея такова: пользователь вводит в поле
Edit1 какую то фамилию и нажимает кнопку "Найти". Событие onClick этой кнопки
собирает в строковую переменную значения четырех указанных полей найденной записи.
Причем после каждого значения в строку добавляется символ "#13" (переход на новую
строку), формируя многострочный отчет. Затем эту строку мы выведем на экран
функцией ShowMessage().
Итак, в окне главной формы дважды щелкните по кнопке "Найти", генерируя событие
onClick. Полный листинг процедуры приведен ниже:
{щелкнули по кнопке Найти}
procedure TfMain.BitBtn1Click(Sender: TObject);
var
myLookup: Variant; //для получения результата
s : String; //для отчета
begin
//получаем результат:
myLookup := fDM.TLichData.Lookup('Фамилия', Edit1.Text,
'Фамилия;Имя;Отчество;Образование');
//проверяем, не Null ли это:
if VarType(myLookup) = varNull then
ShowMessage('Сотрудник с такой фамилией не найден!')
else if VarType(myLookup) = varEmpty then
ShowMessage('Запись не найдена!')
//если это массив, то из его элементов собираем
//многострочную строку:
else if VarIsArray(myLookup) then begin
s := myLookup[0] + #13 + myLookup[1] + #13 +
myLookup[2] + #13 + myLookup[3];
//и выводим ее на экран:
ShowMessage(s);
end; //else if
end;
Комментарии достаточно подробны, чтобы вы разобрались с кодом. Сохраните проект,
скомпилируйте его и запустите. Опробуйте этот способ поиска.
Работа с Данными
Следующие методы позволяют Вам изменить данные, связанные с TTable:
procedure Append;
procedure Insert;
procedure Cancel;
procedure Delete;
procedure Edit;
procedure Post;
Все эти методы - часть TDataSet, они унаследованы и используются TTable и TQuery.
Всякий раз, когда Вы хотите изменить данные, Вы должны сначала перевести DataSet в
режим редактирования. Как Вы увидите, большинство визуальных компонент делают это
автоматически, и когда Вы используете их, то совершенно не будете об этом заботиться.
Однако, если Вы хотите изменить TTable программно, Вам придется использовать
вышеупомянутые функции.
Имеется a типичная последовательность, которую Вы могли бы использовать при
изменении поля текущей записи:
Table1.Edit;
Table1.FieldByName(‘CustName’).AsString := ‘Fred’;
Table1.Post;
Первая строка переводит БД в режим редактирования. Следующая строка присваивает
значение ‘Fred’ полю ‘CustName’. Наконец, данные записываются на диск, когда Вы
вызываете Post.
При использовании такого подхода, Вы всегда работаете с записями. Сам факт
перемещения к следующей записи автоматически сохраняет ваши данные на диск.
Например, следующий код будет иметь тот же самый эффект, что и код показанный выше,
плюс этому будет перемещать Вас на следующую запись:
Table1.Edit;
Table1.FieldByName(‘CustNo’).AsInteger := 1234;
Table1.Next;
Общее правило, которому нужно следовать - всякий раз, когда Вы сдвигаетесь с текущей
записи, введенные Вами данные будут записаны автоматически. Это означает, что вызовы
First, Next, Prior и Last всегда выполняют Post, если Вы находились в режиме
редактирования.
http://www.helloworld.ru/texts/comp/lang/delphi/delphi1/les35.htm почитать можно=)
Основные свойства TDataSet
Active - Открывает (True) и закрывает (False) набор данных
AutoCalcFields - Определяет способ вычислений с использованием полей
Bof - Возвращает True, если курсор находится на первой записи базы данных, и False в
противном случае
CachedUpdates - Если установлено значение True, изменения сохраняются в кэше на
компьютере клиента до полного завершения транзакции В противном случае все
изменения в базе данных производятся при завершении работы с текущей записью
CanModify - Определяет, может ли пользователь редактировать данные
DataSource - Имя компонента DataSource, связанного с набором данных
DatabaseName - Имя базы данных, используемой в настоящий момент
Eof - Возвращает True, если курсор находится на конце файла, и False в противном случае
FieldCount - Количество полей в наборе данных. Поскольку набор данных может быть
динамическим (например, результат запроса), количество полей может варьироваться от
запроса к запросу
Fields - Массив объектов TFields, которые содержат информацию о полях базы данных
FieldValues - Возвращает значение указанного поля текущей записи. Значение имеет тип
Variant
Filter - Выражение, используемое для фильтрации записей
Filtered - Если установлено значение True, фильтрация набора данных осуществляется в
зависимости от свойства Filter или события OnFilter-Record В противном случае
возвращается весь набор данных
FilterOptions - Управляет работой фильтров
Found - Показывает, была ли успешной операция поиска
Handle - Дескриптор курсора BDE Используется только при прямых обращениях к BDE
Modified - Показывает, была ли текущая запись изменена
RecNo - Номер текущей записи в наборе данных
RecordCount - Возвращает количество записей в наборе данных
State - Возвращает текущее состояние набора данных (dsEdit, dsBrowse, dslnsert и т д )
UpdateOb^ect - Указывает компонент TUpdateOb^ect, используемый для кэшируе-мых
изменений
Updates Pending - Значение True указывает, что буфер кэшируемых изменений содержит
данные, не сохраненные в базе данных
Основные методы TDataSet
Append - Создает пустую запись и добавляет ее в конец набора данных
AppendRecord - Добавляет запись в конец набора данных, используя заданные значения
полей
ApplyUpdates - Указывает базе данных на необходимость сохранения всех кэшированных
изменений Реальное обновление базы данных не происходит до вызова метода
CommitUpdates
Cancel - Отменяет все изменения в текущей записи, если они еще не были сохранены
CancelUpdates - Отменяет отложенные изменения, занесенные в кэш
ClearFields - Очищает все поля текущей записи
CommitUpdates - Сохраняет в базе данных все изменения и очищает буфер кэшируемых
изменений
Close - Закрывает набор данных
Delete - Удаляет текущую запись
DisableControls - Запрещает ввод из всех компонентов, связанных с набором данных
Edit - Разрешает редактирование текущей записи
EnableControls - Разрешает ввод из всех компонентов, связанных с набором данных
FetchAll - Считывает и локально сохраняет все записи от курсора до конца набора данных
FieldByName - Возвращает указатель TField по заданному имени поля
FindFirst - Осуществляет поиск первой записи, удовлетворяющей текущему критерию
фильтрации
PindNext - Осуществляет поиск следующей записи, удовлетворяющей текущему критерию
фильтрации
FindLast - Осуществляет поиск последней записи, удовлетворяющей текущему критерию
фильтрации
FindNext - Осуществляет поиск предыдущей записи, удовлетворяющей текущему
критерию фильтрации
FindField - function FindField(count FieldName: string): TField; Метод FindField возвращает
объект TField (в массиве Fields TDataSet), свойство FieldName которого равно имени поля,
заданного в качестве параметра. Если поле не найдено, то возвращается Nil. Заметьте, что
метод FieldByName является удобной оболочкой вокруг FindField. Достоинство
FieldByName заключается в том, что если указанное поле не найдено, метод возбуждает
исключение и предоставляет возможность реализации более живучего кода в случае,
когда структура основной таблицы должна измениться (когда поля должны быть
переименованы или удалены). Вы можете использовать FindField в ситуациях, когда не
хотите возбуждения исключения и знаете, как обработать случай, если поле не найдено
(или хотите самостоятельно возбудить другое исключение).
First - Перемещает курсор на первую запись набора данных
FreeBookmark - Удаляет закладку, установленную с помощью GetBookmark, и
освобождает память, выделенную для закладки
GetBookmark - Устанавливает закладку на текущей записи
GetFieldNames - Возвращает список имен полей набора данных
GotoBookmark - Устанавливает курсор на запись, отмеченную закладкой
Insert - Вставляет запись и переводит набор данных в режим редактирования
InsertRecord - Вставляет запись в набор данных, используя заданные значения полей
Last - Устанавливает курсор на последнюю запись набора данных
Locate - Осуществляет поиск записи в наборе данных. Locate Этот универсальный метод
поиска устанавливает текущую запись как первую строку, удовлетворяющую набору
критериев поиска. Используя метод Locate мы можем искать значения одного или более
полей, расположенных в массиве переменных.
Lookup - Осуществляет поиск записи в наборе данных самым быстрым методом и
возвращает значения ее полей
MoveBy - Перемещает курсор на заданное количество строк
Next - Перемещает курсор на следующую запись
Open - Открывает набор данных
Post - Записывает измененную запись в базу данных или в буфер кэшируемых изменений
Prior - Перемещает курсор на предыдущую запись
Refresh - Обновляет набор данных
RevertRecord - При использовании кэширования этот метод отменяет все ранее сделанные
изменения, еще не сохраненные в базе данных
SetFields - Устанавливает значения для всех полей записи
UpdateStatus - При использовании кэширования возвращает текущий статус изменений
Основные события TDataSet
AfterCancel - Генерируется после отмены редактирования записи
AfterClose - Генерируется при закрытии набора данных
AfterDelete - Генерируется после удаления записи из набора данных
AfterEdit - Генерируется после редактирования записи
Afterlnsert - Генерируется после вставки записи
AfterOpen - Генерируется после открытия набора данных
AfterPost - Генерируется после отправления изменений в базу данных или кэш
BeforeCancel - Генерируется перед отменой редактирования
BeforeClose - Генерируется перед закрытием набора данных
BeforeDelete - Генерируется перед удалением записи
BeforeEdit - Генерируется перед переходом набора данных в режим редактирования
Beforelnsert - Генерируется перед вставкой записи
BeforeOpen - Генерируется непосредственно перед открытием набора данных (между
установкой Active в True и действительным открытием)
BeforePost - Генерируется перед тем, как изменения будут отправлены в базу данных (или
кэш изменений)
OnCalcField - Генерируется при выполнении вычислений с использованием полей
OnDeleteError - Генерируется при ошибке удаления записи
OnEditError - Генерируется при ошибке редактирования записи
OnFilterRecord - Генерируется при доступе к новой записи, если для свойства Field
установлено значение True
OnNewRecord - Генерируется при добавлении новой записи к набору данных
OnPostError - Генерируется при ошибке сохранения изменений
OnUpdateError - Генерируется при ошибке во время сохранения кэшированных изменений
в базе данных
OnUpdateRecord - Генерируется при сохранении кэшированных изменений
57. Работа с базами данных в Delphi. Основные компоненты, которые
потребуются, для редактирования данных в виде таблицы.
Table, Query, DataSource, DBGrid, DBNavigator (нужно просто рассказать как их связать и
расположить на форме, чтобы простейшее приложение работало)
Свойства компонента DataSource(основные для создания приложения)
Name – Имя компонента. Используется для доступа к свойствам компонента.
DataSet – Имя компонента, представляющего собой входные данные.))
Свойства компонента DBGrid (основные для создания приложения)
Name - Имя компонента
DataSource - Источник отображаемых в таблице данных
Columns - Отображаемую в таблице информацию
Options . dgTitles - Разрешает вывод строки заголовка столбцов
Options . dgIndicator - Разрешает вывод колонки индикатора. Во время работы с базой
данных текущая запись помечается в колонке индикатора треугольником, новая запись —
звездочкой, редактируемая — специальным значком
Options . dgColumnResize - Разрешает менять во время работы программы ширину
колонок таблицы
Options . dgColLines - Разрешает выводить линии, разделяющие колонки таблицы
Options . dgRowLines - Разрешает выводить линии, разделяющие строки таблицы
Чтобы получить возможность редактировать, добавлять или удалять записи таблицы,
писать программный код не требуется. Достаточно разместить на форме компонент
TDBGrid с панели Data Controls (Элементы управления данными). В его свойстве
DataSource следует указать нужный источник данных.
Столбцы таблицы описываются свойством Columns, представляющим собой коллекцию
элементов типа TDBGridColumn. По умолчанию отображаются все столбцы, а с помощью
редактора формируется определенный набор.
свойство Columns
Для того чтобы в компонент DBGrid добавить колонку, обеспечивающую просмотр
содержимого поля записи файла данных, необходимо нажать кнопку Add New,
находящуюся на панели инструментов в верхней части окна (это единственная доступная
после запуска редактора кнопка), выделить добавленный элемент и, используя Object
Inspector, установить значения свойств этой колонки.
Свойство columns компонента DBGrid представляет собой массив компонентов типа
TColumn. Каждой колонке соответствует элемент массива. Устанавливая значения
свойств компонентов column, программист задает вид колонок компонента DBGrid, тем
самым определяет вид всей таблицы.
Свойства компонента column
FieldName - Поле записи, содержимое которого выводится в колонке
Width - Ширину колонки в пикселах
Font - Шрифт, используемый для вывода текста в ячейках колонки
Color - Цвет фона колонки
Alignment - Способ выравнивания текста в ячейках колонки. Текст может быть выровнен
по левому краю (taLeftJustify), по центру (taCenter) или по правому краю (taRight Justify)
Title. Caption - Заголовок колонки. Значением по умолчанию является имя поля записи
Title .Alignment - Способ выравнивания заголовка колонки. Заголовок может быть
выровнен по левому краю (taLeftJustify), по центру (taCenter) или по правому краю
(taRight Justify)
Title. Color - Цвет фона заголовка колонки
Title. Font - Шрифт заголовка колонки
Query(будем использовать Table, написала на всякий случай)
Для выборки из базы данных записей, удовлетворяющих некоторому критерию,
предназначен компонент Query.
Компонент Query похож на компонент Table, но, в отличие от последнего, он
представляет не всю базу данных (все записи), а только ее часть — записи,
удовлетворяющие критерию запроса.
Свойства компонента Query(основные при создании простого приложения)
Name - Имя компонента. Используется компонентом Datasource для связи результата
выполнения запроса (набора записей) с компонентом, обеспечивающим просмотр записей,
например DBGrid
SQL - Записанный на языке SQL запрос к базе данных (к таблице)
Active - При присвоении свойству значения True активизирует выполнение запроса
TDBNavigator
Он обычно размещается на форме под компонентом TDBGrid и привязывается к нему
через свойство DataSource. Значение этого свойства должно совпадать со значением
такого же свойства связанной таблицы.
Навигатор позволяет перемещаться по набору записей вперед и назад, переходить к
первой и последней записи и выполнять ряд других функций. Функции Навигатора
доступны как при щелчках на его кнопках во время работы приложения, так и из
Программного кода.
Компонент содержит десять кнопок.
First - Переход к первой записи
Prior - Переход к предыдущей записи
Next - Переход к следующей записи
Ust - Переход к последней записи
Insert - Добавление новой записи перед текущей
Delete - Удаление текущей записи
Edit - Разрешить редактирование текущей записи
Post - Записать изменения, внесенные в текущую запись, в таблицу базы данных
Cancel - Отменить режим редактирования и восстановить старые значения полей
текущей записи
Refresh - Обновить содержимое текущего набора данных (требуется, если с набором
одновременно работают несколько пользователей)
Свойства
Confirm Delete - Имеет значение True, если необходимо отображать диалоговое окно с
запросом
на подтверждение удаления записи
DataSource - Источник данных
Flat - Имеет значение True, если кнопки на объекте будут выглядеть плоскими
Hints - Массив строк (тип TStrings), хранящий всплывающие подсказки для каждой
из кнопок
Visible Buttons - Указание на видимость кнопок
Метод
procedure Btn Click (Index: TNavigateBtn); - Имитация щелчка на кнопке
Навигатора
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); - Изменение размера объекта
на
форме во время работы программы
Событие
BeforeAction - Пользователь щелкнул на кнопке, но соответствующее действие еще не выполнено
OnClick - Выполнено одно из действий навигатора
TTable
Свойства компонента Table(которые нужны для создания простого приложения)
Name Database - Имя компонента. Используется для доступа к свойствам компонента.
NameTable - Имя базы данных, частью которой является таблица (файл данных), для
доступа к которой используется компонент. В качестве значения свойства следует
использовать псевдоним базы данных
Name Table - Имя файла данных (таблицы данных), для доступа к которому используется
компонент
Type - Тип таблицы. Таблица может быть набором данных в формате Paradox («Paradox),
dBase (ttDBase), FoxPro («FoxPro) или представлять собой форматированный текстовый
файл (ttASCII).
Active – признак активизации файла данных (таблицы). В результате присваивания
свойству значения True происходит открытие файлы таблицы.
Свойство (Унаследованные от класса TDataSet)
Active - Имеет значение True, если набор данных открыт
Bof - Имеет значение True, если указатель расположен на первой записи в наборе данных
Eof - Имеет значение True, если указатель расположен на последней записи в наборе
данных
FieldCount - Число полей в наборе данных
FieldDefs - Список определений полей дли набора данных (тип TFieldDefs)
FieldList - Список названий полей набора данных
Fields - Основное свойство для доступа к полям набора данных
FieldValues - Массив полей набора данных, доступ к которым осуществляется по
названию
поля. Например: DataModule2. Games. FieldValues ['Name'] :='NEW_FIELD';
Found - Имеет значение True, если выполнение одного из методов поиска в наборе
закончилось успешно
Modified - Имеет значение True, если текущая запись изменена
State - Состояние набора данных. Определяет, открыт ли набор, выполняется ли
редактирование или добавление записи и так далее
Свойство (Унаследованные от класса TBDEDataSet)
RecNo- Номер текущей записи в наборе данных
RecordCount - Число записей в наборе данных. Для большинства СУБД получение
значения этого свойства может потребовать значительных расходов ресурсов
RecordSize - Размер записи в байтах
Свойство (Класса TTable)
CanModify - Имеет значение True, если разрешено редактировать, изменять или удалять
записи в наборе данных
Defaultlndex - Имеет значение True, если при открытии таблицы записи сортируются в
соответствии со значением первичного ключа
Exclusive - Имеет значение True, если таблица на время работы с ней программы
блокируется для доступа к ней других программ. Такой режим поддерживается не всеми
СУБД
Exists - Имеет значение True, если таблица существует в базе данных. Свойство
проверяется во время работы программы, чтобы выяснить, существует ли физически
таблица, указанная в программе
IndexDefs - Коллекция определений индексов (каждый из которых относится к классу
TIndexDef), сформированных для данной таблицы
IndexFieldCount - Число полей для текущего индекса
KeyExclusive - Имеет значение True, если граничные записи заданного диапазона записей
таблицы не входят в этот диапазон
KeyFieldCount - Число полей ключа, используемое при поиске на его основе
ReadOnly - Таблица доступна только для просмотра
StoreDefs - Имеет значение True, если определения полей и индексов хранятся вместе с
модулем данных или формой, на которой создан объект класса ТТаЫе
TableLevel - Требование к наличию драйвера ВОЕ определенного уровня
ТаЫеТуре - Тип таблицы (Paradox, dBase, текстовый и так далее)
Наиболее важные методы класса ТТаЫе, унаследованные от класса TDataSet
Procedure Append; - Добавление новой пустой записи в конец набора данных
Procedure AppendRecord(const Values: array of const); - Добавление новой записи в конец
набора данных, заполнение ее полей значениями из параметра-массива и сохранение этих
значений в базе данных
Procedure ClearFields; - Удаление содержимого всех полей текущей записи
Procedure Close; - Закрытие набора данных
Procedure Delete; - Удаление текущей записи и перемещение указателя к следующей
записи набора
Procedure Edit; - Начало режима редактирования текущей записи (по окончании
редактирования надо вызывать метод Post для сохранения значений в базе данных)
Function FindField(const FieldName: string): TField; - Поиск поля с именем, указанным в
параметре FieldName, в текущем наборе данных. Если поле не найдено, функция
возвращает значение nil. Данный метод полезно использовать для гарантированного
безопасного изменения значений полей:
//безопасно:
DataModule2.Games.FindField ('Payability') .Aslnteger:= 9;
// небезопасно:
DataModule2.Games.Fields[10].AsInteger:=9; ЕСЛИ поле отсутствует (например, по какимто причинам удалено администратором СУБД), то в последнем случае в программе
возникнет ошибка
Procedure GetFieldNames(List: Tstrings); Список названий полей возвращается в массиве
строк List. Его можно занести в обычный элемент управления типа Список:
DataModule2.Games.GetFieldNames(ListBoxl. Items);
Procedure Insert; - Добавление новой пустой записи в набор данных. Ее позиция обычно
определяется позицией указателя, но может зависеть и от конкретной СУБД
ProcedureInsertRecord(const Values: array of const); - Добавление новой записи в набор
данных. Ее поля заполняются значениями из параметра-массива, а позиция определяется
этими значениями в зависимости от наличия индексированных полей
Function IsEmpty: Boolean; - Возвращает значение True, если в наборе данных нет ни
одной записи
Function IsLinkedTo(DataSource: TDataSource): Boolean; - Возвращает значение True, если
набор данных связан с указанным источником данных
Procedure Open; - Открытие набора данных
Procedure Refresh; - Обновление набора данных и загрузка обновленных записей.
Полезная операция, если с базой данных работает несколько пользователей
Procedure SetFields(const Values: array of const); - Занесение во все поля текущей записи
значений из параметра-массива
Наиболее важные методы, унаследованные от класса TBDEDataSet
Procedure Cancel; - Отмена изменений, внесенных в текущую запись, если
еще не был вызван метод Post
Procedure Flush Buffers; - Запись всех сделанных в наборе изменений в базу
данных
Function GetBlobFieldData(FieldNo: Integer; var Buffer: TBlob Byte Data): Integer; Копирование двоичных данных (Blob) из записи с указанным в параметре FieLdNo
номером в динамический
массив данных Buffer
procedure Getlndexlnfo; - Обновление информации о текущем индексе набора
function IsSequenced: Boolean; - Возвращает значение True, если разрешено перемещение
от записи к записи присваиванием номера записи свойству RecNo. В противном случае
для перехода к нужной записи надо применять методы Next/Prior.
procedure Post; - Запись внесенных изменений в базу данных
procedure Trans late (Src, Dest: PChar; ToOem: Boolean); По умолчанию считается, что
наборы данных ВОЕ работают со строками в формате OEM, а система Delphi 7 использует
формат ANSI, принятый в Windows. Если значение параметра ToOem раано True, то
строка Src преобразовывается в строку Dest из формата ANSI в формат OEM. Если
значение ToOem
равно False, преобразование происходит в противоположном направлении
Наиболее важные методы, унаследованные от класса TBDDataSet
function CheckOpen(Status: DBIResult): Boolean; - Возвращает значение True, если
результат обращения к механизму ВОЕ успешен. В противном случае в параметр Status
будет записан код ошибки
Некоторые методы класса ТТаЫе
function BatchMove(ASource: TBDEDataSet;AMode: TbatchMode): Longint; - Перемещение
записей из набора данных TBOEDataSet в таблицу
procedure DeleteTable; - Удаление таблицы (стирание всех ее записей и удаление
структуры из базы данных)
procedure EmptyTable; - Удаление всех записей из таблицы
procedure GotoCurrent(Table: ТТаЫе); - Синхронизация указателя для двух наборов
данных. Указатель устанавливается на запись, являющуюся текущей для таблицыпараметра. Эти таблицы (объекты класса ТТаЫе) должны быть созданы на основе одной
физической таблицы базы данных, то есть значения их свойств DatabaseName и
TableName должны совпадать
procedure RenameTabte(const NewTableName: String); - Переименование текущей таблицы
и всех вспомогательных файлов. Метод работает только с базами данных Paradox и
dBASE
Важнейшие события класса ТТаЫе
AfterCancel, BeforeCancel - После (After) или до (Before) завершения метода Cancel
(отмена внесенных изменений)
AfterClose, Before Close - После (After) или до (Before) завершения закрытия набора
данных
AfterDelete, Before Delete - После (After) или до (Before) выполнения удалении записи
AfterEdit, BeforeEdit - После (After) или до (Before) редактирования записи
Afterlnsert, Вefоre insert - После (After) или до (Before) вставки новой записи
AfterOpen, BeforeOpen - После (After) или до (Before) открытия набора данных
AfterPost, BeforePost - После (After) или до (Before) сохранения записи в базе данных
AfterRefresh, Before Refresh - После (After) или до (Before) обновления данных в наборе
данных
AfterScroll, Before Scroll - После (After) или до (Before) перемещения указателя к новой
записи
OnCalcFields - Происходит определение значения вычисляемого поля
On Delete Error - Была выполнена попытка удаления записи и возникла исключительная
ситуация
OnEditError - Была выполнена попытка изменения или добавления записи и возникла
исключительная ситуация
OnFiLterRecord - Данное событие может обрабатываться для задания оригинального
метода фильтрации записей (когда значение свойства Filtered установлено в True)
OnNewRecord - Выполняется вставка или добавление новой записи
OnPostEnor - Была выполнена попытка сохранения новой или измененной записи и
возникла исключительная ситуация
58. Работа с базами данных в Delphi. Основные компоненты, которые
потребуются для редактирования данных в виде формы.
Для того чтобы обеспечить просмотр базы данных в режиме формы, в форму приложения
нужно добавить компоненты, обеспечивающие просмотр и, если нужно, редактирование
содержимого полей записи, причем по одному компоненту для каждого поля.
Компонент DBText позволяет только просматривать содержимое поля, а компоненты
DBEdit и DBMеmо — просматривать и редактировать.
TDBText (Надпись данных)
По аналогии с компонентом TLabel компонент TDBText позволяет отображать
содержимое отдельного поля записи. Источник данных указывается в свойстве
DataSource, нужное поле — в свойстве Data Field.
TDBEdit (Поле редактирования)
Компонент позволяет редактировать значение отдельного поля текущей записи.
Используемые свойства не отличаются от свойств компонента TDBText. Дополнительное
свойство Readonly позволяет запретить редактирование данных. Компонент является
наследником компонента THaskEdit и позволяет проверять соответствие введенной
информации заданной маске с помощью метода ValidateEdit, который в случае
несоответствия маски и текущих данных генерирует исключительную ситуацию. Маска
доступна через свойство EditMask поля записи, связанного с компонентом (но не через
закрытое свойство EditMask своего родителя), а значение, созданное на основе маски,
хранится в свойстве EditText.
TDBMemo (Компонент Многострочное поле)
С помощью этого компонента отображается содержимое двоичного поля записи
(например, Blob). Если требуется, чтобы в поле показывалось его полное содержимое
(графика, текст),
значение свойства AutoDisplay надо сделать равным True. Метод LoadMemo позволяет
переносить содержимое поля Blob в данный компонент, если известно, что оно хранит
текстовые данные. Значение свойства AutoDisplay при этом не проверяется.
TDBImage (Изображение)
С помощью компонента TD BImage можно воспроизводить на экране рисунки,
хранящиеся в базе данных. Например, несложно расширить базу данных справочника так,
чтобы она хранила в поле Blob картинки из игр. Рисунки отображаются в данном поле
автоматически, если значение свойства AutoDisplay равно True. В противном случае
рисунок можно загрузить вызовом метода LoadMemo. Предполагается, что в поле
текущей записи хранится изображение в формате, который данный компонент распознает
корректно (например .BMP). Это изображение можно, например, скопировать, используя
свойство Picture.
Imagel.Picture.Assign(DBImagel.Picture);
Задав свойству QuickDraw значение True, ускоряют вывод рисунка на экран, что часто
полезно при активном просмотре больших наборов записей, но при этом ухудшается
качество изображения.
Свойство Stretch позволяет подстраивать (сжимать или растягивать) изображение
под текущие размеры компонента TDBImage на форме.
TDBComboBox (Поле данных со списком)
Как и компонент TDBList, данный компонент позволяет показывать и выбирать новое
значение поля в раскрывающемся списке значений. Набор текста в области ввода
позволяет быстро переместиться к нужной записи в списке или ввести значение,
отсутствуюшее в нем.
В дополнение к свойствам, унаследованным от компонента TComboBox, в классе
TDBComboBox имеется свойство Style типа TComboBoxStyle, позволяющее определить
способ отображения элементов в списке. В остальном данный компонент ничем не
отличается от TComboBox.
(эти компоненты на всякий случай)
TDBCheckBox (Флажок данных)
С помощью элемента-флажка, привязанного к полю таблицы, имеющему логический тип
(Boolean), можно отображать состояние этого поля и менять его значение. Не
возбраняется применять этот компонент и для полей, которые могут принимать одно из
двух произвольных значений. Список значений, при которых компонент будет считаться
«включенным», задается в текстовом свойстве ValueChecked. Элементы перечисляются
через точку с запятой.
DBCheckBoxl.ValueChecked := 'True;Yes';
Соответственно, список значений, при которых компонент будет считаться выключенным, задается в свойстве ValueUn checked.
DBCheckBoxl.ValueUnchecked := 'False;No';
Регистр, в котором записаны эти значения, не учитывается.
TDBRadioGroup (Группа переключателей данных)
Этот компонент позволяет выводить ограниченный набор значений полей в наглядном
виде. Каждому значении) можно поставить в соответствие один из переключателей
группы.
Список названий переключателей заносится в свойство Items (тип TStrings). Список
соответствующих им значений поля хранится в свойстве Values (тип TStrings).
Например, значениям 5,7 и 10 для поля Payability можно поставить в соответствие
три переключателя группы: средне, хорошо и отлично.
with DBRadioGroupl do
begin
Items . Add ( ' средне ' ) ,Items. Add ( ' хорошо ') ,Items.Add( 'отлично1 ) ,-'
Values.Add( ' 5 ') ,Values.Add( ' 7 ') ,Values.Add('10');
end;
Доступ к текущему значению поля, связанного с компонентом, можно получить
через свойство Value.
ТDВ Rich Edit (Поле форматирования)
Компонент наследует все свойства компонента TRichEdit и позволяет представлять содержимое двоичных полей Blob из базы данных как форматированный текст.
Компонент используется так же, как и компонент TDBMemo.
TDBCtrlGrid (Свободная форма)
Компонент напоминает таблицу записей (TDBGrid), но позволяет представлять данные не
в жестко заданном табличном виде, а с помощью выбранного набора компонентов с
панели Data Controls (Элементы управления данными) и в произвольном виде. Например,
можно задать число столбцов в свободной форме. Когда компонент помещается на форму,
он представляется в виде набора больших панелей, первая из которых пуста, а остальные
заштрихованы и приведены только для наглядности. На первой панели надо разместить
компоненты, предназначенные для отображения конкретных полей записи, и связать их
через свойство DataField с соответствующим полем набора данных. Само имя набора
заносится в свойство DataSource автоматически. Оно совпадает со значением
аналогичного свойства родительского компонента TDBCtrlGrid.
ВНИМАНИЕ: В число размещаемых компонентов не могут входить такие компоненты,
как TDBGrid, TDBNavigolor, TDBListBox, TDBRadioGroup, TD В Look up List,
TDBRichEdi1,тo есть все, представляющие несколько значений поля.
TDBChart (Диаграмма данных)
Ранее мы рассматривали компонент TChart, позволяющий строить всевозможные графики
и диаграммы на основе данных, вводимых пользователем или генерируемых программно.
Компонент TDBChart, являющийся наследником компонента TChart, предназначен для
отображения в подобном виде информации, получаемой из базы данных. Он
настраивается так же, как компонент TChart, но в качестве источника данных для него
указывается таблица или запрос. Допустим, на форме имеется запрос Query1, структура
записи в котором состоит из
трех полей: двух числовых PI, P2 и текстового РЗ. Третье значение может быть привязано
к первой паре и через ключевое поле. Оно может описывать характеристики текущей
записи, например указывать, к какому опыту относится текущая пара значений. Добавим
на форму компонент TDBChart (рис. 5.21) и определим ряд данных.
Далее на вкладке Data Source (Источник данных) выбираются:
О в раскрывающемся списке — значение DataSet (Набор данных);
О в списке Dataset (Набор данных) — имя запроса Queryl;
О в поле Labels (Подписи) — имя поля РЗ;
О в поле X — имя поля Р1, значения из которого будут откладываться по оси X;
О в поле Y — имя ноля Р2, значения из которого будут откладываться по оси 7.
Свойства компонентов DBText, DBEdit и DBMеmо
Name - Имя компонента. Используется для доступа к свойствам компонента
DataSource - Компонент-источник данных
DataField - Поле базы данных, для отображения или редактирования которого
используется компонент
Добавить компоненты Tаblе и Datasource и установить значения их свойств (в
предыдущем вопросе описаны свойства)
Для того чтобы иметь возможность просматривать другие записи файла данных, в форму
приложения нужно добавить компонент DBNavigator(в предыдущем вопросе описаны
свойства)
59. Работа с базами данных в Delphi. Отношение master-detail между
таблицами. Описание отношений master-detail средствами визуального
программирования.
При отношении 'Master-Detail' (главный-подчиненный) между таблицами одна из
них объявляется главной (Master), вторая – подчиненной (зависимой, Detail); связь
таблиц осуществляется по полю с одинаковым именем, входящему в состав обоих
таблиц.
Типичным примером является связь между списком заказчиком (таблица
CUSTOMER.DB) и заказов (ORDERS.DB). Каждая из таблиц имеет (числовое) поле
CustNo, однозначно идентифицирующее заказчика (для таблицы заказчиков уникальное). Конечная цель проекта – получение информации о заказах (из таблицы
ORDERS.DB), сделанных конкретным заказчиком (соответствующим выбранной записи в
таблице CUSTOMER.DB). Естественно, у некоторых заказчиков может не быть заказов
вообще, у некоторых – много заказов; при попытке удаления заказчика с ненулевым
количеством заказов возникает ситуация нарушения ссылочной целостности. Добавление
заказа должно предваряться дополнением списка заказчиков (если информация о данном
заказчике отсутствует).
На форму помещаются по два компонента типа TDBGrid, TTable и TDataSource, для
навигации по (главной) таблице служит TDBNavigator. Настройки компонентов приведены в таблице
Дополнительно в свойстве MasterSource компонента Table2 (Detail-таблица) установить
значение DataSource1 (т.е. фактически дать ссылку на соответствующий компонент,
связанный с Master-таблицей).
Путем щелчка мыши на кнопке ... свойства Master Fields компонента Table2 вызвать
окно Field Link Designer (окно инструмента для установления связи таблиц по
одноименным полям), в выпадающем списке Available Indexes выбрать CustNo (имя поля,
по которому будет осуществлено связывание). Выбрать (щелчком левой кнопки мыши) в
левом списке Detail Fields и в правом списке Master Fields имен связываемого поля
CustNo, нажатием кнопки Add подтвердить выбор этого поля и проконтролировать
внесение строки CustNo → CustNo в список Joined Fields (рис.11). Напомним, что
возможен просмотр данных из таблиц в DesignTime при установке значений свойства
Active компонентов TTable в True.
Таблица Настройки свойств компонентов VCL
при создании приложения ‘Master-Detail’.
Тип
Настраиваемое свойство
Значение настраиваемого
компонента
(Property)
свойства
DBGrid1
DataSource
DataSource1
DBGrid2
DataSource
DataSource2
DataSource1
DataSet
Table1
DataSource2
DataSet
Table2
Table1
DatabaseName
BCDEMOS (*)
TableName
CUSTOMER.DB
IndexFieldName
CustNo (*)
Table2
DatabaseName
BCDEMOS (*)
TableName
ORDERS.DB
IndexFieldName
CustNo (*) или пусто
(*) - возможность выбора из ниспадающего списка
Функциональность приложения достигается тем, что каждой (соответствующей
определенному значению поля CustNo) записи о заказчиках таблицы CUSTOMER
соответствует запись (записи) о заказах (таблица ORDERS) с тем же значением CustNo.
Дополнительная ссылочная целостность БД (каскадное изменение или удаление записей
главной таблицы и др.) должна быть реализована дополнительно (программными
средствами).
Программно можно так задать все свойства:
Установить свойство Table2.MasterSource: = DataSource1
Установить свойство Table2.MasterField := ‘CustNo’
Установить свойство Table2.IndexName := ‘CustNo’
(что означают все эти свойства):
Свойство MasterSource в Table2 определяет DataSource от которого Table2 может
получить информацию. То есть, оно позволяет таблице ORDERS знать, какая запись в
настоящее время является текущей в таблице CUSTOMERS.
Но тогда возникает вопрос: Какая еще информация нужна Table2 для того, чтобы
должным образом отфильтровать содержимое таблицы ORDERS? Ответ состоит из двух
частей:
 Требуется имя поля по которому связанны две таблицы.
 Требуется индекс по этому полю в таблице ORDERS (в таблице ‘многих записей’),
которая будет связываться с таблицей CUSTOMER(таблице в которой выбирается
‘одна запись’).
Чтобы правильно воспользоваться информацией описанной здесь, Вы должны сначала
проверить, что таблица ORDERS имеет нужные индексы. Если этот индекс первичный,
тогда не нужно дополнительно указывать его в поле IndexName, и поэтому Вы можете
оставить это поле незаполненным в таблице TTable2 (ORDERS). Однако, если таблица
связана с другой через вторичный индекс, то Вы должны явно определять этот индекс в
поле IndexName связанной таблицы.
В примере показанном здесь таблица ORDERS не имеет первичного индекса по полю
CustNo, так что Вы должны явно задать в свойстве IndexName индекс CustNo.
Недостаточно, однако, просто yпомянуть имя индекса, который Вы хотите использовать.
Некоторые индексы могут содержать несколько полей, так что Вы должны явно задать
имя поля, по которому Вы хотите связать две таблицы. Вы должны ввести имя ‘CustNo’ в
свойство Table2.MasterFields. Если Вы хотите связать две таблицы больше чем по одному
полю, Вы должны внести в список все поля, помещая символ ‘|’ между каждым:
Table1.MasterFields := ‘CustNo | SaleData | ShipDate’;
В данном конкретном случае, выражение, показанное здесь, не имеет смысла, так как хотя
поля SaleData и ShipDate также индексированы, но не дублируются в таблице
CUSTOMER. Поэтому Вы должны ввести только поле CustNo в свойстве MasterFields.
60. Data-aware компоненты для редактирования значений из БД,
принцип их работы.
Принцип работы
TDataLink, является базовым классом для объекта, используемого компонентами для
работы с базами данных и осуществляющий функцию связи с набором данных DataSet
(TTable или TQuery). DataLink используется DataSet'ом для информирования всех БДкомпонентов об изменении записи, о необходимости обновления записи перед
помещением ее в базу данных, о том, что DataSet сменила свое состояние на активное или
неактивное, и т.д.. И наоборот, DataLink используется БД-компонентами для обновления
DataSet, например, его статуса.
DataSet может быть связан с несколькими источниками данных DataSource, каждый
DataSource может быть связан с несколькими DataLink, и каждый DataLink может быть
связан с единственным БД-компонентом. В большинстве случаев, отдельный компонент
использует только один DataLink, тем не менее, имеются компоненты, такие как,
например, DBLookupList или DBLookupCombo, использующих два DataLink. В этих
элементах управления первый DataLink используется для чтения данных из lookup
DataSet, второй DataLink используется для записи этих данных (при их изменении) во
второй имеющийся DataSet. Каждый DataSet поддерживает связанный с ним список
DataSource и, аналогично этому, каждый DataSource поддерживает список связанных с
ним Datalink'ов.
В момент, когда DataSet должен уведомить БД-компоненты о наступлении какого-то
события, например, при изменении пользователем какой-либо записи, он рассылает это
сообщение всем DataSource, находящимся в его списке. Каждый DataSource затем
повторяет этот процесс и рассылает сообщение всем Datalink'ам, находящимся в его
списке. Другими словами, связь не зависит от элемента управления, при этом логика
программирования должна отслеживать передачу сообщения каждому элементу
управления, пользующемуся услугами DataLink и изолировать только те события, на
которые элементу необходимо отреагировать. Связывая компонент с набором данных
другим способом, мы не получим в свое распоряжение столько управляющих функций,
гибкости и мониторинга, сколько даст нам один DataSet, соединенный с помощью
DataLink.
Кроме функции обеспечения поддержания коммуникационного канала между DataSet и
ДБ-компонентами, DataLink также обеспечивает управление буфером для каждого
компонента. Большинство элементов управления, таких как, например, TDBEdit,
отображающий только отдельно взятую запись, буферизация не требуется, тем не менее,
таким компонентам, как, например, TDBGrid и TDBLookupList, отображающим
множество записей, буферизация нужна. Физически DataLink данные не буферизирует,
эта функция выполняется DataSet. Всесто этого DataLink поддерживает виртуальный
буфер, который, в сущности, небольшое "окно" в физический буфер DataSet. Размер этого
виртуального буфера может быть установлен с помощью свойства DataLinks BufferCount,
а количество записей, реально в нем хранимых, с помощью свойства RecordCount.
(тут как бы уже в принципе сказано все о чем он говорил на консультации, дальше
добавлено то, что еще нашла)
TDataLink и его производные
TDataLink
TTableDataLink
TFieldDataLink
TQueryDataLink
TNavDataLink
TDataSourceLink
TListSourceLink
TGridDataLink
TDBCtrlGridLink
Класс TDataLink играет фундаментальную роль в обеспечении чувствительности к
данным для соответствующих компонентов. Класс буквально образует путепровод между
чувствительным к данным компонентом и TDataSet, который играет роль источника
данных для такого компонента.
Ни TDataLink, ни его производные в Палитре компонент Delphi недоступны. Они
порождены от TPersistent, а не от TComponent. Прикладные программисты никогда не
беспокоятся о TDataLink, но если вы являетесь разработчиком компонентов, то TDataLink
играет весьма существенную роль. При реализации чувствительного к данным
компонента наиболее типичен следующий сценарий: компонент будет иметь приватную
или защищенную переменную, объявленную с типом TDataLink или одним из его
производных. Такая переменная инициализируется в конструкторе чувствительного
компонента, где создается соответствующий объект TDataLink и присваивается
переменной. (Кроме того, полям обработчика событий TDataLink в конструкторе
присваиваются соответствующие процедуры обработчиков событий. Естественно, нельзя
забывать о разрушении всей конструкции в деструкторе чувствительных к данным
компонентов.)
Существуют два основных вида связей данных: один представляет всю запись (или
множество записей) в базовом TDateSet, а другой — указанное поле в текущей записи
базового TDataSet. Первый вид используется для реализации чувствительных
компонентов, которые взаимодействуют с целыми записями (подобно TDBGrid), а второй
вид — для реализации чувствительных к данным компонентов, представляющих
одиночное поле (подобно TDBEdit).
TDataLink
Класс TDataLink представляет собой базовый класс для всех связей данных. На
рисунке 22-1 показаны генеалогия TDataLink и его потомков. В таблице 22-1 перечислены
свойства и методы, которые реализованы TDataLink.
РИСУНОК 22-1
Генеалогия TDataLink и
их потомки
http://www.rsdn.ru/article/delphi/dbaware.xml
остальное взято с этого сайта.. выбрала самое основное.. можно на него зайти посмотреть, елси нужно чтото совсем подробно.
Структура DB-Aware компонентов
Компоненты, работающие с данными, часто называют DB-Aware-компонентами (далее
DB-A). Их отличительной особенностью является возможность подключения к какойлибо части базы данных, скажем, к таблице или отдельному полю. Создание компонентов
для работы с данными не является сложной задачей. В отличие от других категорий
компонентов, DB-A не имеют единой иерархии классов. Процесс создания таких
компонентов может быть определен как преобразование какого-либо элемента управления
в элемент управления, работающий с данными.
Для осуществления этого преобразования необходимо всего лишь включить в имеющийся
класс компонента дополнительное поле и написать несколько методов, обеспечивающих
связь между компонентом и набором данных.
Другими словами, чтобы создать DB-A, нужно подобрать наиболее соответствующий
нашим требованиям компонент, после чего описать поведенческую модель связки
компонент-данные. Необходимо заметить, что никаких особенных требований к
имеющемуся компоненту не предъявляется, это может быть графический или оконный
элемент управления, невизуальный компонент, диалог и так далее.
В зависимости от возможностей DB-A можно условно разделить на две категории:
компоненты, только отображающие данные
компоненты, способные модифицировать наборы данных.
Естественно, что первый тип компонентов устроен проще, чем второй.
В общем случае схема построения
компонентов, работающих с
данными, может быть
представлена следующим образом
(рисунок 1):
Класс TDataLink
Класс TDataLink является базовым классом для иерархии классов связи с БД. Класс
обеспечивает коммуникационный канал для чтения/записи в БД.
Данный класс описан в модуле DB
(Его свойства по ссылке выше есть, но я не думаю что это понадобится, но на всякий
случай)
61. Способ построения отчётов с использованием вывода информации
в HTML.
http://win-ni.ru/db/lab8_1.html
http://window.edu.ru/window_catalog/pdf2txt?p_id=9223&p_page=1
Это информация по SQL запросу взята со второй ссылки
При работе с БД широко применяется понятие запроса к ней. Под
запросом понимается требование совершить определенное действие с
информацией из БД. Можно выбрать данные в соответствии с заданным
условием и отобразить результат – запрос на выборку. Можно также
вставить, удалить, обновить определенные записи в БД, используя
соответствующие запросы. Такие требования формируются на специальных
языках программирования QBE и SQL. SQL нашел более широкое
распространение, в Delphi его можно использовать при создании приложения
для работы с БД.
SQL-запрос
На языке SQL можно создать запросы различных типов:
запрос на выборку данных;
запрос на добавление;
запрос на обновление;
запрос на удаление.
SQL-запрос, созданный с помощью DBD, помещается в файл с
расширением sql.
Каждый запрос на языке SQL состоит из нескольких частей, которые
начинаются с определенного зарезервированного слова. При записи запросов
можно использовать как прописные, так и строчные буквы. Информацию
можно разбивать на строки произвольным образом, но желательно в
отдельные строки помещать информацию, имеющую определенное
смысловое назначение. Для разделения отдельных зарезервированных слов,
идентификаторов, чисел, строк, знаков операций и т.д. следует использовать
пробелы.
1.Запрос на выборку.
Он предназначен для извлечения данных, находящихся в определенных
полях одной или нескольких таблиц и соответствующих задаваемым
условиям. Конструкция запроса следующая:
SELECT [DISTINCT] <список выводимых полей>
FROM <список используемых таблиц>
[WHERE <условие отбора>]
[ORDER BY <порядок сортировки>]
[GROUP BY <порядок группировки>]
[HAVING <условие отбора групп>]
[UNION <объединяемый запрос на выборку>]
Рассмотрим подробнее отдельные части запроса.
DESTINCT – указывает, что в результате запроса не должно быть
повторяющихся значений.
<список выводимых полей>:
[<таблица>.]<поле>[AS <псевдоним>]{,[<таблица>.]<поле>[AS
<псевдоним>]}, где таблица – имя таблицы, если в запросе присутствует
только одна таблица, его можно опустить; поле – имя поля; псевдоним –
псевдоним для имени поля является локальным в запросе; AS – оператор,
ставящий в соответствие конкретному полю псевдоним. Если требуется
получить данные из всех полей таблицы, то достаточно указать * вместо
перечисления всех имен полей.
<список используемых таблиц>:
“<файл с таблицей>” [<псевдоним>]{,”<файл с
таблицей>”[<псевдоним>]}.
WHERE – зарезервированное слово, с которого начинается оператор задания
условий.
<условие отбора>:
Условие, устанавливающее объединение:
[(<таблицаM.поле1> <условие> <таблицаN.поле2>)]{AND|OR
(<таблицаI.поле1> <условие> <таблицаJ.поле2>)}, где таблицаM, таблицаN,
таблицаI, таблицаJ – имена объединяемых таблиц; поле1, поле2 – имена
полей в этих таблицах, по соответствию значений которых устанавливается
взаимосвязь таблиц; условие – определяет соответствие значений в заданных
полях двух таблиц, например: равенство, больше, меньше, не равно и другие
операции сравнения; условий может быть несколько, в этом случае они
объединяются при помощи круглых скобок и логических операций AND и
OR. Если условие для объединения отсутствует, формируется результат,
представляющий собой всевозможные объединения записей всех таблиц.
Условие на значение полей:
[[<таблица>.]<поле> <условие> <значение>] {AND|OR
[<таблица>.]<поле> <условие> <значение>}.
Помимо математических, используются также логические операторы
сравнения, предикаты IN, LIKE и другие.
ORDER BY – зарезервированное слово, с которого начинается оператор
задания порядка сортировки.
<порядок группировки>:
[<таблица>.]<поле> {,[<таблица>.]<поле>}.
2.Запрос на добавление.
Этот запрос позволяет добавить запись в таблицу и присвоить значения
ее полям. Конструкция запроса следующая:
INSERT INTO “<файл с таблицей>” (<поле>{,<поле>})
VALUES (<значение>{,<значение>}).
Рассмотрим подробнее отдельные части запроса:
файл с таблицей – имя файла с таблицей, в которую добавляется запись;
должен быть с расширением и содержать путь;
поле – имя поля, которому задается значение.
VALUES - зарезервированное слово, с которого начинается оператор
задания списка значений полям таблицы; значение – значение из списка
значений, которое задается соответствующему полю новой записи (их
количество должно равняться числу полей).
3.Запрос на обновление.
Этот запрос позволяет обновить значения в записях, которые
соответствуют задаваемым условиям. Конструкция запроса следующая:
UPDATE “<файл с таблицей>”
SET <поле> = <значение>{, <поле> = <значение>}
[WHERE <условие отбора>]
Рассмотрим некоторые части запроса:
SET – зарезервированное слово, с которого начинается оператор, задающий
список подставляемых значений; <поле> = <значение>{, <поле> =
<значение>} – список полей и значений, которые задаются этим полям.
[WHERE <условие отбора>] – оператор, задающий условие отбора тех
записей, в которых следует обновить данные.
4.Запрос на удаление.
Этот запрос позволяет удалить из таблицы записи, удовлетворяющие
заданным условиям. Конструкция запроса следующая:
DELETE FROM «<файл с таблицей>»
[WHERE <условие отбора>].
Запрос к БД (Query)
Использование одной компоненты Table позволяет работать только с
одной таблицей. Поэтому, когда необходимо работать с несколькими
связанными таблицами, нужно создавать соответствующее число компонент
Table. В этом случае целесообразно взять компоненту Query класса TQuery,
являющегося потомком класса TDBDataSet. Эта компонента позволяет
определить набор данных на основе нескольких таблиц с помощью SQLзапроса. Ее также удобно применять, когда таблица БД чрезмерно велика; в
этом случае с помощью запроса можно ограничить набор рассматриваемых
данных. Использование этой компоненты связано со знанием языка SQL.
Отметим сразу, что можно создать оператор Delphi SQL при помощи
компоненты Query следующим образом:
1) поместить на форму объекты Query, DataSource и связать их вместе;
2) присвоить псевдоним объекта TQuery свойству DataBaseName;
3) с помощью свойства SQL ввести указание SQL;
3) установить свойство Active в значение True.
Если каждый шаг был завершен правильно и если BDE установлено
корректно, сетка должна будет содержать записи из указанной таблицы.
Delphi позволяет создавать SQL-запросы к БД как статические, в
которых текст запроса остается неизменным, так и динамические (или
запросы с параметрами). Динамические SQL-запросы содержат параметры,
определенные в условиях отбора записей, которые в процессе выполнения
программы могут изменяться. Таким образом, с помощью одного
динамического запроса можно получать различные результаты при
выполнении приложения. Синтаксическая конструкция динамических
запросов аналогична статическим, за исключением того, что у них в секции,
определяющей условия отбора записи, вместо значения записывается
<параметр>, где параметр – имя параметра, вместо которого при выполнении
приложения будет подставляться значение.
У класса TQuery отметим следующие наиболее важные свойства,
которые он добавляет к наследуемым от классов TDataSet, TBDEDataSet и
TDBDataSet:
Local – определяет расположение таблиц (True – локальные таблицы,
False – таблицы на SQL-сервере); свойство только для чтения.
RequestLive – свойство логического типа (по умолчанию имеет значение
False), определяет возможность изменять набор данных, полученный в
результате выполнения запроса. Эта возможность имеется, если свойство
имеет значение True, и у запросов, которые основаны на одной таблице и не
используют сортировку и агрегатные функции. У остальных запросов
результат доступен только для чтения независимо от значения данного
свойства. В этих случаях можно посмотреть свойство CanModify для того
чтобы увидеть, успешен ли запрос, если же нужно откорректировать таблицу
с помощью запроса SQL, то следует использовать команду SQL Update.
SQL – свойство типа TStrings, определяет текст SQL-запроса, который
используется при выполнении методов Open или ExecSQL.
UniDirectional – определяет направление перемещения курсора по
набору данных, полученному в результате выполнения запроса.
UpdateMode – свойство типа TUpdateMode, определяет способ
обновления записей из промежуточного буфера.
Следующие свойства компоненты TQuery используются в динамических
SQL-запросах. Приведем некоторые из них:
DataSource – свойство типа TDataSource, определяет источник данных,
значения полей которого используются как параметры для динамического
запроса.
Params[Index] – свойство типа TParams, задает список элементов типа
TParams, которые определяют параметры в динамическом запросе. С
помощью этого свойства в редакторе значений параметров задаются
начальные величины параметров. Чтобы перейти к редактору значений
параметров запроса, нужно активизировать мышью значение этого свойства.
Класс TQuery добавляет к наследуемым от классов TDataSet,
TBDEDataSet и TDBDataSet следующие методы (некоторые из них):
ExecSQL – процедура выполняет SQL-запрос.
Prepare – процедура посылает запрос в BDE для проверки синтаксиса и
оптимизации. Рекомендуется выполнять для динамических запросов.
События, которые обрабатываются компонентой TQuery, полностью
наследуются от класса TDataSet.
При подстановке переменных связи в динамический запрос
программным методом с помощью свойства Params выполняются обычно
следующие шаги:
1) необходимо убедиться в том, что таблица закрыта;
2) подготавливается объект TQuery с помощью выдачи команды Prepare;
3) Свойству Params присваиваются конкректные значения;
4) Открывается запрос.
Пример № 1. Создание запроса для вывода записей, имеющих фамилию,
начинающуюся с одной и той же буквы, из БД «Телефонная книжка»
Для работы с электронной телефонной книжкой как с обычной
желательно иметь возможность просматривать абонентов только с
фамилиями, начинающимися на одну букву. Для этого воспользуемся
запросом с параметром.
1. Загрузим заготовку проекта, созданную в лабораторной работе № 2.
Зададим свойству Caption формы значение «Информация о людях (запрос)».
Сразу же сохраним все составные части проекта в файлах с теми же именами,
что и прежние, но с добавлением цифры «5» в конце имени:
MyExUnitDB5.pas, MyExampleDB5.dpr.
2. Со страницы DataAccess поместим на форму Form1 компоненту Query
(Запрос).
Выберем компоненту Query1, активизируем свойство SQL и в появившемся
текстовом редакторе введем следующие строки, которые задают запрос на
языке SQL:
SELECT D.*, D1.*
FROM “<путь к файлу>\People.db” D, “<путь к файлу>\Tel.db” D1
WHERE
(D.IDPeople = D1.IDPeople) AND (D.Family Like :FirstChar).
Оператор Like позволяет отобрать записи, которые определяются
параметром FirstChar в поле Family. Затем активизируем свойство Params и в
появившемся окне редактора параметров запроса Form1.Query1 Parameters
выполним следующее. Выберем из списка Parameter Name (Название
параметра) FirstChar, в строке ввода со списком DataType (тип данных)
выберем String и в строке Value (Значение) введем А% - начальное значение.
Завершим настройку нажатием кнопки ОК.
3. Установим у свойств Active и RequestLive компоненты Query1 значение
True, используя инспектор объектов.
4. Со страницы DataAccess палитры компонент поместим на форму Form1
еще одну компоненту DataSource. Активизируем ее и установим свойство
DataSet в Query1.
5. Перейдем к форме Form1. Справа от компоненты DBGrid1 поместим со
страницы Standart палитры компонент две компоненты Label. Свойству
Caption компоненты Label6 зададим значение Постраничный, а свойству
Caption компоненты Label7 – Просмотр.
6. Ниже, под компонентами Label6 и Label7, поместим со страницы Standart
кнопку Button. Изменим ее свойство Caption на Просмотр. При нажатии на
эту кнопку будет происходить переход на новую форму, на которой будет
реализован постраничный просмотр БД «Телефонная книжка».
Активизируем ее и в появившемся обработчике события OnClick введем
следующее:
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Visible := True;
end;
Свойство Visible обеспечивает отображение формы на экране после своего
создания.
7. Добавим в форму Form1 со страницы Additional палитры компонент
компоненту Bevel. Поместим ее позади компонент Label6, Label7 и Button1.
Теперь эта группа компонент будет выделяться на форме. Изменим ее
свойство Style на bsRaised (она будет выпуклой).
8. Создадим теперь вышеуказанную форму. Для этого выполним следующие
действия:
8.1. Добавим в проект форму командой главного меню File|New Form.
8.2. Изменим свойство Caption формы Form2 на «Постраничный
просмотр» и сохраним форму под именем MyExUnitQuery.pas.
8.3. В тексте модуля Form2 в секции implementation напишем
следующую строчку для связи данной формы с Form1:
USES MyExUnitDB5;
Перейдем снова к форме Form1. В тексте ее модуля установим связь с
Form2, для чего в секции implementation допишем:
USES MyExUnitQuery;
8.4. В верхний левый угол формы поместим компоненту ComboBox со
страницы Standart палитры компонент. Изменим ее свойство Text на A.
Затем активизируем свойство Items и в появившемся окне редактора
введем на каждой строке последовательно по одной букве от «А» до
«Я», за исключением, быть может, букв «Ь» и «Ъ». Завершим ввод
нажатием кнопки ОК.
8.5. Со страницы Standart палитры компонент с помощью мыши
поместим рядом с компонентой ComboBox1 компоненту Button.
Изменим ее свойство Caption на «Страница». Затем активизируем
кнопку и в появившемся обработчике события OnClick введем
оператор, задающий отображение в форме результатов выполнения
запроса в компоненте Query1. Обработчик будет иметь следующий
вид:
procedure TForm2.Button1ClickSender: TObject);
begin
WITH Form1.Query1 DO
BEGIN
Close;
Prepare;
Params[0].AsString := ComboBox1.Text + ‘%’;
Open;
END;
end;
9. Поместим на форму Form2 еще одну компоненту Button, справа от
компоненты Button1. Изменим ее свойство Caption на Назад. При помощи
этой кнопки мы сможем вернуться к форме Form1 во время работы
приложения. Активизируем кнопку Button2 и в открывшемся обработчике
события OnClick введем следующее:
procedure TForm2.Button2Click(Sender: TObject);
begin
Close;
end;
10. Ниже помещенных нами компонент разместим на всю оставшуюся часть
формы компоненту DBGrid со страницы DataControls палитры компонент.
Зададим свойству DataSourse значение Form1.DataSource3. При помощи
редактора списка полей Editing DBGrid1.Columns определим поля, которые
будут отображаться в таблице (эти действия были описаны в лаб./раб. №2):
Family, Name, SecName, Number, Type, задавая при этом через сложное
свойство Titel заголовки столбцов в DBGrid1: Фамилия, Имя, Отчество,
Номер, Тип соответственно.
11. Поместим в низ формы Form1, приблизительно в центр, еще одну кнопку
Button, которая в тексте модуля будет иметь имя Button2. Изменим ее
свойство Caption на Выход (выход из приложения). Активизируем ее и в
обработчике события напишем:
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
12. Запустим программу командой Run|Run. В результате появится окно, в
котором при нажатии на кнопку Просмотр появится еще одно окно, где,
выбирая в выпадающем списке разные буквы и нажимая кнопку Страница,
можем видеть, как меняется набор отображаемых записей в таблице DBGrid
в соответствии с первой буквой значения фамилии.
13. Завершим работу нажатием кнопки Выход в основном окне.
14.Сохраним все изменения в проекте.
При программном использовании TQuery следует сначала закрыть
текущий запрос и удалить все строки, которые уже могут находиться в
свойстве SQL:
Query1.Close;
Query1.SQL.Clear;
При этом всегда безопасно вызывать Close, т.к. если запрос уже закрыт,
то это не вызовет никакой ошибки. Следующим шагом является добавление
новых строк, которые необходимо выполнить, например:
Query1.SQL.Add(‘Select * From People.db’);
Можно использовать свойство Add для добавления в запрос SQL от
одной до x строк.
Для того, чтобы Delphi обработало операторы SQL и вернуло курсор с
результатами запроса, требуется написать следующий оператор:
Query1.Open;
Если возвращать курсор не обязательно, то можно сделать вызов
ExecSQL. Вызывать ExecSQL также требуется, если происходит вставка,
исключение и корректировка данных, т.е. вместо строки Query1.Open нужно
будет записать:
Query1.ExecSQL;
Query1.Refresh;
Отметим также, что операторы Select возвращают курсор и поэтому
требуют вызова Open. Delete, Insert и Update не возвращают курсор и
поэтому сопровождаются вызовом ExecSQL.
Если требуется составить серию операторов, то ускорить работу можно
за счет использования динамических запросов.
Пример № 2. Создание запроса для ограничения числа
отображаемых записей из БД «Телефонная книжка»
(программный путь)
В телефонной книжке, как правило, располагается много записей и
просмотр всех их последовательно затруднителен. Рассмотрим простейшее
разбиение таких записей на следующие две части: в первую войдут те,
которые содержат фамилии, начинающиеся на буквы от «А» до «М», а во
вторую – от «Н» до «Я», причем записи будут выводиться в алфавитном
порядке. Будем использовать TQuery программно.
1. Загрузим заготовку проекта, созданную в предыдущем примере. Зададим
свойству Caption формы Form1 значение «Информация о людях (запрос 2)».
Сразу же сохраним все составные части проекта в файлах с теми же именами,
что и прежние, но с добавлением цифры 6 в конце имени: MyExUnitDB6.pas,
MyExUnitQuery6.pas и MyExampleDB6.dpr. В тексте модуля формы Form1
заменим раздел объявления используемых модулей в секции implementation
USES MyExUnitQuery;
на раздел
USES MyExUnitQuery6;
А в тексте модуля формы Form2 заменим раздел объявления используемых
модулей в секции implementation
USES MyExUnitDB5;
на раздел
USES MyExUnitDB6;
А ее свойство Caption изменим на «Ограниченный просмотр».
Изменим немного вид формы Form1: свойство Caption у компоненты Label6
с Постраничный на Ограниченный.
2. Перейдем к форме Form2. Удалим из нее две компоненты – ComboBox1 и
Button1 (кнопка Страница).
На место удаленных компонент поместим со страницы Standart две
компоненты Button, а у компоненты DBGrid1 свойство DataSource сделаем
пустым, чтобы первоначально в ней не было никаких записей.
3. Выберем кнопку Button1 и изменим ее свойство Caption на «А – М».
Активизируем ее и в появившемся обработчике события OnClick введем
следующие строки:
procedure TForm2.Button1Click(Sender: TObject);
begin
WITH Form1.Query1 DO
BEGIN
Close;
SQL.Clear;
SQL.Add('SELECT D.*, D1.*');
SQL.Add('FROM "People.db" D, "Tel.db" D1');
SQL.Add('WHERE');
SQL.Add('(D1.IDPeople = D.IDPeople)AND(D.Family < "Н")');
SQL.Add('ORDER BY D.Family ASC');
Open;
END;
Form1.DataSource3.DataSet := Form1.Query1;
DBGrid1.DataSource := Form1.DataSource3;
end;
4. Выберем кнопку Button3 и изменим ее свойство Caption на «Н – Я».
Активизируем ее и в появившемся обработчике события OnClick внесем
следующее:
procedure TForm2.Button3Click(Sender: TObject);
begin
WITH Form1.Query1 DO
BEGIN
Close;
SQL.Clear;
SQL.Add('SELECT D.*, D1.*');
SQL.Add('FROM "People.db" D, "Tel.db" D1');
SQL.Add('WHERE');
SQL.Add('(D1.IDPeople = D.IDPeople)AND(D.Family >= "Н")');
SQL.Add('ORDER BY D.Family ASC');
Open;
END;
Form1.DataSource3.DataSet := Form1.Query1;
DBGrid1.DataSource := Form1.DataSource3;
end;
5. Запустим программу командой Run|Run.
6. Перейдем ко второй форме, нажав кнопку Просмотр. Нажимая кнопки «А
– М» и «Н – Я», можем видеть, как меняется набор отображаемых записей.
7. Завершим работу приложения.
8. Сохраним все изменения в проекте.
62.Взаимодействие с приложениями-серверами через OLE. Способ
получения информации о необходимых командах в продуктах Microsoft
Office.
63.Основные средства отладки программ в Delphi.
(?нужен ли просмотр и анализ кода и раздел расширенные средства
отладки.основное,что он говорил присутствует)
Синтаксические ошибки
Синтаксические ошибки обнаруживаются компилятором автоматически. Сообщения
о найденных ошибках отображаются в нижней части редактора, в небольшом окне
(рис. 3.1):
Main pas
'•:.::: *Г '*+"*ariw3Ke itiiG
uar i,l, j : Integer;
Fi: T&KtFile;
3: String;
begin
К := О;
oe i :- 1 -co МА*_РАТТЕКНЯ do
begin
AsaigriFiJ-e (Fi, XntTaatc f i > + " .
Си* W tanA uted ml ≪3*1 MS'
Puc. 3.1. Информация о синтаксической ошибке, обнаруженной компилятором
При двойном щелчке на строке с сообщением об ошибке система Delphi 7 переключится в редактор, где подсветит строку, в которой эта ошибка обнаружена. Само
сообщение (на английском языке) описывает ошибку достаточно подробно, чтобы
можно было понять ее причину. Например:
Undeclared identifier: ' X '
В данном случае указано, что идентификатор X не объявлен.
Логические ошибки
Существует несколько способов предотвращения, выявления и устранения логических ошибок. Все они используются программистами, как правило, в комбинации
друг с другом.
За наиболее часто встречающимися ошибками можно заставить следить саму программу. Для этого в настройках проекта — соответствующее диалоговое окно
вызывается командой Project > Options (Проект >• Настройки) — на вкладке Compiler
(Компилятор) надо выполнить следующие действия (рис. 3.2).
154 Урок 3. Отладке программ
Project Options (or TeiilMainf roq.exe
• Code generator;
Го
Г
гга
Stricl vaf
trended i>rta
; F Atwj^e l>BeiJ cofstvU
Rurifne SICKS -----JJ Hangt chpcfors
ОеЬиетпд —
P Eebupniofmaljcn
Сим!
Рис. 3.2. Настройка компилятора для максимального контроля ошибок
О На панели Code generation (Генерация машинного кода) сбросьте флажок
Optimization (Оптимизация). Когда компилятор создает оптимизированный
код, он нередко вносит существенные улучшения в детали алгоритма, реализованного на Паскале. Например, если программист вводит в процедуре локальную переменную X для хранения промежуточных результатов:
function Sum: integer;
var X: integer;
begin
X := StrToInt( Editl.Text ) + StrToInt( Edit2.Text );
Result := X
end;
то после оптимизации этого кода программа вполне может не использовать
промежуточную переменную, а результат вычислений условно будет представлен так:
Result := StrToInt( Editl.Text ) +
StrToInt( Edit2.Text );
Это означает, что программист не сможет узнать значения локальной переменной X во время работы программы, потому что реально для нее не будет
отводиться место в оперативной памяти.
') ЗАМЕЧАНИЕ Кок правило, для повышения эффективности вычисления подобных
выражений компилятор активно использует регистры процессора.
О На панели Runtime errors (Ошибки времени выполнения) должны быть установлены флажки Range checking (Контроль выхода индекса за границы массива),
I/O Checking (Контроль ошибок ввода/вывода) и Overflow checking (Контроль
Что такое отладка 1 55
переполнения при целочисленных операциях). Так как все типы Паскаля имеют
строго ограниченные диапазоны, то, например, при сложении двух чисел,
близких к максимально допустимому значению, получится число, которое
не укладывается в этот диапазон. Последний флажок позволяет обнаруживать такие ошибки.
О На панели Debugging (Отладка) установите флажки Debug information (Добавление отладочной информации), Local symbols (Просмотр значений локальных
переменных), Reference info (Просмотр структуры кода), Assertions (Включение
процедуры Assert в машинный код) и Use Debug DCUs (Использование отладочных
версий стандартных модулей библиотеки компонентов VCL).
Без отладочной информации отладка программы в среде Delphi 7 вообще
невозможна. Процедура Assert выполняет отладочные функции. В заключительной версии программы она, как правило, не нужна, а удалять ее вызовы
из исходного текста неудобно — их могут насчитываться сотни. Отключить
генерацию машинного кода для этой процедуры можно с помощью флажка
Assertions. Отладочные версии стандартных модулей содержат дополнительные режимы проверки корректности работы с компонентами Delphi 7.
Теперь, если в программе, запущенной непосредственно из среды Delphi 7, встретится ошибка (например, вышеупомянутый выход индекса за допустимые границы),
выполнение программы прервется, а строка, Б которой встретилась ошибка (в данном случае — Arr[i] :- 0;), будет подсвечена.
При этом система Delphi 7 позволит быстро определить, в чем причина ошибки.
Например, наводя указатель мыши на различные переменные, можно сразу увидеть
их значения в окне всплывающей подсказки (i = 11).
ЗАМЕЧАНИЕ 8 некоторых случаях, как в рассматриваемом примере, реальные
^^^~^^^~ значения переменных i и Агг могут не совпадать с отображаемыми,
потому что произошла запрещенная попытка обращения к недоступной области памяти, в результате чего значения локальных
переменных могли измениться.
Все же в большинстве ситуаций, когда работа программы, запущенной из Delphi 7,
прервалась по ошибке, подобным образом можно посмотреть значения любых переменных и констант.
За более сложными ошибками разработчик должен следить из среды Delphi 7 самостоятельно. Для этого применяется ряд стандартных приемов, однако требуется,
чтобы отлаживаемая программа была запущена непосредственно из среды Delphi 7.
Только тогда среда разработки сможет должным образом контролировать ход выполнения программы и изменение значений различных переменных.
Выполнение по шагам
Обычно разработчику приблизительно известно, в какой подпрограмме возникает
ошибка, однако обнаружить ее быстро, просто рассматривая исходный текст, не
всегда удается, особенно новичкам в программировании (хотя просмотр исходных
156 Урок 3. Отладке программ
тестов признан наиболее эффективным средством обнаружения ошибок). Поэтому
возникает необходимость выполнить эту подпрограмму по шагам: каждый оператор
по очереди, с возможностью контроля значений используемых переменных.
Рассмотрим пример, связанный с проектом Projectl (предварительно в Менеджере
проектов его надо сделать активным).
Добавим в обработчик списка действий AddAction следующие описание и оператор:
var Arr: array[1..10] of integer;
begin
for i := 1 to 11 do
if i > 3 then A r r t i ] := 0
else Arr[i] := 1;
В данном месте программы скрывается ошибка. Чтобы перейти к выполнению этой
подпрограммы по шагам, в ней надо создать точку прерывания (или точку остановки), встретив которую программа прервет свою работу и временно передаст
управление системе Delphi 7.
Точки прерывания можно ставить не в любой строке исходного теста, а только
там, где выполняются какие-то действия. Такие строки помечены на левом поле в
редакторе синими круглыми маркерами, которые появляются после успешно
выполненной компиляции.
В нашем случае точку прерывания можно поставить в строке с оператором цикла.
Это делается нажатием клавиши F5 или щелчком мыши на синем маркере. При
этом соответствующая строка выделяется красным цветом (рис. 3.3). Снимается
точка прерывания аналогичным способом.
Строка, содержащая
точку прерывания —
Признак точки
прерывания
Маркеры выполняемых —'
операторов
:= 1 to in do
else Arr [i] := J ;
Label 1. Caption := IntToStr ( M888 + StrToInt (Ed:
end;
Рис. 3.3, Подготовка подпрограммы к пошаговой отладке
Если теперь запустить программу и выполнить операцию сложения (щелкнув па
кнопке Сложить или выбрав соответствующий пункт в строке меню или в контекстном
меню), то программа остановится и управление будет передано в систему Delphi 7, где
строка с точкой прерывания помечается зеленой стрелкой на левом поле (рис, 3.4).
ft: i. : = I t o iu do
else Ac r I i } := 1;
Рис. З.4. По достижении точки прерывания праерамма останавливает работу
Что такое отладка 1 57
В заголовке главного окна системы Delphi 7 появится информационное сообщение Projectl [Stopped] (Выполнение проекта Projectl остановлено).
Далее выполнение метода AddActionExecute можно продолжить по шагам.
Для этого используются команда Run > Step Over (Запуск >• Перешагнуть), клавиша F8 или кнопка Step Over (Перешагнуть).
Если выполнить очередной шаг, то в редакторе подсвечивается голубым цветом и
помечается зеленой стрелкой следующий оператор, ожидающий выполнения.
if i > 3 then Arr[i] := 0
Сейчас можно проверить значение переменной i, наведя на нее указатель мыши.
Появится окно всплывающей подсказки с надписью i - 1. Если навести указатель
на переменную Arr, то в круглых скобках будет показан список всех значений массива, однако, пока инициализация не закончена, значения, хранимые в массиве,
непредсказуемы.
Продолжив выполнение метода по шагам, мы увидим, что следующим выполняемым оператором станет оператор
else Arr[i] := 1;
Предыдущий оператор присваивания Arr[i] := 0 пропущен, потому что условие i > 3
не выполнено. Наконец, на следующем шаге управление опять будет передано оператору цикла, который будет выполнять свое ≪тело≫ 11 раз, и на последнем шаге
возникнет ошибка.
Полностью остановить работу программы можно с помощью команды Run > Program
Reset (Запуск > Сброс программы) или комбинацией клавиш CTRL+F2. Теперь можно
исправить параметр окончания цикла с 11 на 10 и вновь запустить программу. Вернуться к точке прерывания можно, например, щелкнув на кнопке Сложить.
Однако выполнять циклы по шагам не очень удобно. Если число повторений достигает сотен или тысяч, то постоянно жать на клавишу F8 бессмысленно. Чтобы пропустить выполнение группы операторов, можно использовать команду Run > Run to
Cursor (Запуск х Выполнение до курсора) или клавишу F4.
Предварительно надо выполнить следующие действия. Текстовый курсор надо установить в строку, начиная с которой требуется продолжить выполнение по шагам,
например в начало оператора
LabelI.Caption := IntToStrf
StrToInt(Editl.Text) +
StrToInt(Edit2.Text) );
Затем следует убрать точку прерывания (так как при попытке выполнить группу
операторов, в данном случае цикл, система Delphi 7 встретит эту точку прерывания
и вновь остановится на старом месте) и нажать клавишу F4. Работа цикла завершится (можно убедиться, что в массиве Arr хранятся только единицы и нули, наведя
на его название указатель мыши) и подсветится оператор сложения чисел, введенных в поля. Продолжить выполнение программы (не по шагам) можно с помощью
команды Run (Запуск) или клавиши F9.
158 Урок 3. Отладка программ
Просмотреть список всех установленных точек прерывания можно с помощью
команды View > Debug Windows > Breakpoints (Вид >• Окна отладки >• Точки прерывания) (рис. 3.5).
Breakpoint List
Filename JA6*ess ,
Рис. 3-5. Список созданных точек прерывания
ПОДСКАЗКА Если в момент, когда программа остановлена, выполнялись какието действия по просмотру файлов и текущее место остановки было
потеряно, быстро вернуться к нему поможет команда Run >• Show
Execution Point (Запуск > Показать точку выполнения).
В некоторых случаях при выполнении программы по шагам требуется также следить за тем, как работают различные подпрограммы, вложенные в отлаживаемый
модуль. Рассмотрим это на примере — добавим к форме MyForm новую кнопку и
сформируем обработчик ее нажатия. В нем поместим обращение к методу
AddActionExecute.
procedure TMyForm.Button4Click(Sender: TObject);
begin
AddActionExecute(Sender)
end;
Поставим на вызове этого метода точку прерывания. Запустим программу, щелкнем на новой кнопке, и выполнение прервется в нужном месте. Если теперь нажать
на клавишу F8, то подпрограмма AddActionExecuteQ выполнится за один шаг (произойдет шаг через подпрограмму). Это не всегда удобно — ведь нам хочется выяснить, какова логика работы самого метода AddActionExecute. Для этого надо применить команду Run > Trace into (Запуска Войти внутрь), нажать клавишу F7
или щелкнуть на кнопке Trace into (Войти внутрь). В результате управление
передается первой команде метода AddActionExecute.
ВНИМАНИЕ Можно входить внутрь только тех методов, которые написаны разработчиком. С помощью команды Trace into (Войти внутрь] удастся
выполнить по шагам далеко не все стандартные функции и процедуры. Это связано с тем, что некоторые из них не существуют в
отладочных версиях, а их исходные тексты в поставку системы не
входят. Допустимо заходить внутрь методов обьектов, включенных
в состав библиотеки компонентов VCL, если используются отладочные версии ее модулей. Эта библиотека полностью поставляется в и сходных текстах.
Что такое отладка 159
Иногда сразу становится ясно, что делает подпрограмма, в которую разработчик
вошел с помощью клавиши F7. В таких случаях можно быстро ее покинуть с помощью
команды Run >• Run Until Return (Запуск >• Выполнять до выхода) или комбинации клавиш SHIFT+F8, в результате чего управление передается на оператор, следующий за
вызовом данной подпрограммы. Б нашем примере, если программист вошел внутрь
метода AddActionExecute, то быстро покинуть его и вернуться в обработчик
Button4CLick можно с помощью команды Run Until Return (Выполнять до выхода).
Довольно часто в программах встречаются ситуации, когда число вызовов вложенных подпрограмм весьма велико — оно может достигать десятков. Чтобы взглянуть на подобную последовательность подпрограмм с конкретными параметрами
во время выполнения программы, надо дать команду View >• Debug Windows >• Call
Stack (Вид > Окна отладки >• Стек вызовов) (рис. 3.6).
Execute
TGMctO*
TBuUtnOck
TBU>cnOJCoran*v((lM01.3№6. 0. 2556, ВД
TCBViolWntfKell'Wm 2556. 2556. 0.2556.0.2556.0.0.0]|
TW>iCOnKilWntPHK{|43WI. 2556. ?55£. 0. 2556. 0.2556.0.0. M)
TBiBonConta!lWr>dPiiK(l4ai<)1.2556. 2556.0. 2556. D. 2556. 0. 0 OB
IConOol Pet[an<4W01.2556.2556)
DoCanlrcMsg(2556{ra valuel)
TW.iConWil.WMC»mB™([273. 2556. 0.2556. On
THoturfomWMCaimevU^S. 2556.0.2556.0]]
TCix*ijtWridPioDl]273. 2556.2556.0. 2566.0. 2556. u 0. Qlj
TWnCoreotWn<Fii!cU273. 2556.2556.0. 2556.0. 2556.0.0, Щ
TCiBtomF≫mWndProc[(273.2556.2556. D. 2556. 0.2556. Q. 0.0)!
TWnConlral.MerWn*r<Kl[273. 2556. 2556,0, 2556. 0,2556. 0.0.0])
kLJ
Рис. З.6. Стек вызовов подпрограмм
Просмотр значений
Когда во время работы программы приходится контролировать множество значений разных переменных, использовать для этой цели мышь неэффективно. Система
Delphi 7 позволяет помешать переменные в специальное окно, где наряду с их именами показываются также и текущие значения. При выполнении программы по
шагам переключаться между этим окном и окном редактора неудобно, поэтому в
контекстном меню окна списка переменных имеется пункт Stay on Top (Поверх других окон). Если его включить, это окно будет во время отладки располагаться выше всех остальных окон, что позволяет
следить за изменениями переменных при пошаговом выполнении программы. С помощью флажков (крайняя левая колонка) можно включать и выключать отслеживание конкретных переменных.
Добавление новых переменных в такое окно осуществляется с помощью команды
Run > Add Watch (Запуск > Добавить слежение) или нажатием клавиш CTRL+F5. В
появившемся окне Watch Properties (Своиава слежения) имя переменной вводится в поле
160 УрокЗ. Отладка программ
Поле для ввода
выражения
Тип значения
Watch Properties . - :E3
ЕФИЯЭСИ. J 1
RmeatCCUiC 10 i Djgte jlB —
W Ejjebled Г~ ABnw ^u*MicnCob
. С £hafacie« Г rfe&fliiKirrb^ P ^Et&d/SBuctie
_ ^ S^ire ^* Floating piwl ^ Ddai^
irEKim* Г !>*≪, Гы.^уО,™ OK Carest JJcfc
Число знаков
после запятой
I
Рмс. 3.7. Ввод выражения для постоянного контроля
Expression (Выражение). Можно следить за изменениями не только отдельных переменных, но и за значениями целых выражений, например x-t-y или i-5 (рис. 3.7).
В нижней части окна Watch Properties имеется ряд переключателей, с помощью которых можно указать тип результирующего значения (например, переменная может
иметь тип Integer, а показывать требуется символ, соответствующий этому значению). По умолчанию считается, что показываться будет значение, имеющее тот же
тип, что и переменная — переключатель Default (По умолчанию).
В поле Repeat Count (Число элементов) указывается, сколь-
ко элементов массива будут отображаться. Например,
можно указать элемент массива Агг[3] и число элементов,
равное 5. Тогда при выполнении программы по шагам в этом
поле будут отображаться через запятую значения пяти элементов, начиная с Агг[3]: Агг[3], Агг[4], Агг[5], Агг[6], Агг[7].
В поле Digits (Разрядность) задается число значащих цифр после запятой, которые
будут выводиться для чисел дробных типов.
С помощью переключателя Enable (Включено) можно временно отключать контроль
значений некоторых переменных списка.
Флажок Allow Function Calls (Разрешить вызов функций) допускает использование в
выражениях вызовов функций. Например, выражение IntToStr(Arr[3]) будет показывать текстовое представление значения, хранящегося в элементе Агг[3].
Содержимое переменной, тип которой основан на записи (record), отображается в
виде, принятом для инициализации записей:
имя-поля : значение;
Значение точки Р в методе FormMouseUp будет показано так:
Р: (х: 129; у: 312]
Чтобы отредактировать выражение, ранее введенное в список, надо дважды щелкнуть на нем. Удаление выражение производится нажатием клавиши DELETE. В контекстном меню окна слежения имеются также пункты Delete All Watches (Удалить
все элементы), Disable AIL Watches (Отключить все элементы), Enable All Watches (Включить все элементы), позволяющие, соответственно, удалить, временно выключить
или включить все элементы. В Delphi 7 появилась возможность объединять элеЧто такое отладка 161
менты в несколько групп, каждой из которых соответствует своя закладка в нижней части окна просмотра. Этот удобный подход позволяет собирать отслеживаемые переменные программы в компактные группы по смыслу, а не показывать в
одном длинном списке. Добавление новой группы выполняется командой л скального меню просмотра значений Add Group (Добавить группу).
ЗАМЕЧАНИЕ В системе Delphi 7 имеется также специальное окно для показа
значений всех локальных переменных текущей подпрограммы или
метода. Оно открывается командой View >• Debug Windows > Local
Variables (Вид >• Окна отладки > Локальные переменные). Б этом
окне отображается, в частности, значение переменной Self, указывающей на объект, которому принадлежит данный метод.
Просмотр и изменение значений
Помимо простого просмотра различных значений во время работы программы иногда
требуется какое-нибудь значение изменить. Пусть, например, в процессе отладки
по тагам обнаружена ошибка, но выполнение программы желательно продолжить.
Чтобы обойти ошибку, неверное значение можно исправить вручную. Это делается
в окне быстрого просмотра значений, которое открывается командой Run > Evaluate/
Modufy (Запуск > Определить/Изменить) или комбинацией клавиш CTRL+F7 (рис. 3.8).
Поле для ввода выражения
Поле для отображения
вычисленного значения
Поле для ввода нового значении
Рис. 3.8. Окно быстрого просмотра значений
В поле Expression (Выражение) вводится вычисляемое выражение. По щелчку на
кнопке Evaluate (Вычислить) в поле Result (Результат) появится его значение. Это
поле (панель) сделано таким большим, потому что в нем отображаются не только
отдельные значения, но и массивы, и записи. В поле New value (Новое значение)
выводится измененное значение. С помощью кнопки Watch (Следить) выражение,
указанное в поле Expression (Выражение), можно добавить в окно слежения.
Просмотр и анализ кода
В системе Delphi 7 имеются мощные средства для просмотра исходных текстов
программ, позволяющие отображать структуру классов, типы переменных, их ме6 За*. 348
162 Урок 3. Отладка программ
стоположение в программе, осуществлять навигацию по этим структурам и типам
и быстро перемещаться по исходным текстам. Таких средств в системе несколько.
Навигатор проекта (Project Browser) вызывается командой View > Browser (Вид > Навигатор) (рис. 3.9).
Категория Инспектор Панели Проводник
информации проекта Проводника объектов
JFxnlormg Cliisses
≪I . T
≫, ТУЯ-cterfTObetl)
* , TMoJW.O≪s(I01jiod)
Ѓ}1
•^4"
*!. TOjdHajLut-daKfTObetll
ТРя1т-Ыав(ТСЬ|М]
, rPanetrWanaga - dasilTObiectl
IPHMUnt . аМТОЬясИ
≪4 TWnCaitiol - dass(TCgnlrgl]
S ≪^ TOnlcrfam Ei-^ TFcm -d≪i(TCu3tonifaml
•^ TFam2-dai5[IFofm)
•t
; 1J Putfclwi
: IAc*rtjn
Biitonl.TBJIon
ВиПвй TBJtoi
* Bunai3Ok*[Sendtr lObedl
-*≫ Bultciri
- *^ Che*B≫l- TCheckBmc
:.!•• -Ed,i
Е*Й IE*
EdHCtoyl TEiCtoy
Label TLaW
L t;.- Tbue
Рис. 3.9. Окно Наашатора проекта
Если нажата кнопка Globals (Глобальные объекты), то в левой части окна (это Инспектор проекта) отображаются списки используемых в программе классов, типов,
свойств, методов, переменных и подпрограмм. Детально настроить способ отображения каждого элемента можно, выбрав в контекстном меню Навигатора пункт
Properties (Свойства).
Если нажата кнопка Classes (Классы), отображается детальная иерархическая структура классов, используемых в программе.
Если нажата кнопка Units (Модули), отображаются все модули со взаимными ссылками и входящие в них переменные,
ЗАМЕЧАНИЕ Навигатор проекта также обычно присутствует в левой части редактора под именем Проводника по коду (Code Explorer), способного
отображать структуру текущего модуля. Он вызывается командой
контекстного меню редактора View Explorer (Открыть Проводник)
или командой View >• Code Explorer (Вид >• Проводник по коду).
В Навигаторе каждый элемент структуры помимо названия содержит также небольшой значок (всего их восемь), поясняющий назначение этого элемента.
Что такое отладка 1 63
Таблица 3.1. Назначения элементов структуры
Значок Элемент Значок Элемент
•4И
*4
Класс
Модуль
Метод (процедура)
Свойство
Нл Интерфейс
^ Переменная, константа
<jjb Метод (функция)
81 Тип
1 to ID do
thsn AL'ir[i] :=
else Aer[i] : =
Нужный объект можно быстро найти, просто введя его имя. Навигатор автоматически перемещается по структуре, показывая объекты, подходящие по названию.
Если некоторый метод или подпрограмма только определены в интерфейсной части
модуля, но не реализованы, их имена выделяются полужирным начертанием. Дважды
щелкнув на таком имени, можно перейти к определению соответствующего метода
в редакторе, а выбрав в контекстном меню (не убирая указатель с имени метода)
пункт Complete class at cursor (Закончить реализацию класса), можно сразу автоматически сгенерировать пустую реализацию метода.
Для быстрой навигации по коду в системе Delphi 7 имеются еще два метода.
1. Чтобы переключаться между описаниями подпрограммы в интерфейсном разделе и разделе реализации, надо, находясь внутри описания подпрограммы,
использовать комбинации клавиш CTRL+SHIFT+UP и CTRL+SHIFT+DOWN.
2. Чтобы отметить некоторое место в тексте,
надо сформировать для него закладку — нажать комбинацию клавиш CTRL+K и затем
цифру от 0 до 9. При этом на внутренней кайме редактора появится зеленый
прямоугольник с номером закладки.
В дальнейшем вернуться к нужной закладке можно, нажав комбинацию клавиш
CTRU-номер закладки (например, CTRL+1).
В правой части окна расположен Проводник объектов (Symbol Explorer). Его также
можно вызвать командой Search > Browse Symbol (Поиск > Выбор объекта) или непосредственно из редактора, наведя указатель на переменную и выбрав в контекстном
меню пункт Browse Symbol at Cursor (Выбрать объект под указателем).
Этот Проводник показывает подробную информацию об объекте, выбранном на
левой панели Навигатора проекта. На панели Scope (Область видимости) отображается
список идентификаторов,объявленных внутри класса или модуля, на панели Inheritance
(Наследование) —локальная иерархия в рамках текущего проекта для выбранного
класса, на панели References (Ссылки) — список имен файлов и номеров строк в исходном тексте, где описан соответствующий идентификатор. Чтобы перейти к его
описанию, надо выполнить двойной щелчок на нужном имени в Проводнике.
Навигация по коду. Порой разработчик забывает, что означает тот или иной класс
или определение. Можно обратиться к справочной системе, но профессиональным
1 64 Урок 3. Отладка программ
программистам обычно удобнее взглянуть на реализацию конкретного определения внутри системы Delphi 7 (в стандартной библиотеке VCL). Для этого служит
технология навигации по коду. Находясь в редакторе, надо нажать и удерживать
клавишу CTRL, переместив при этом указатель к нужному определению. Курсор
примет вид указательного пальца, а расположенный пол ним идентификатор, если он
доступен для просмотра, выделяется синим цветом и подчеркиванием (рис. ЗЛО).
resources!; ring ErrorMsg = 'Не вер ко*;
procedure TMyForm.AddActionEKecute((
var i: integer;
А|ГГ : array [ 1 . . lu] of integer;
begin
Puc, 3.10. Выделение идентификатора в ходе навигации по коду
Теперь достаточно щелкнуть на нем, и редактор переключится на файл, в котором
хранится нужное определение. Например, если в модуле Unitl в строке
type
TMyForm = class (TForrn)
щелкнуть таким способом 1та слове TForm, то в редакторе будет открыт файл Forms.pas
(реализация стандартного модуля Forms из библиотеки компонентов VCL), а курсор
будет установлен в строку с началом определения TForm:
TForm = class(TCustomForm)
Автоподсказки. В системе Delphi 7 реализовано несколько типов автоподсказок.
Они включаются и выключаются в диалоговом окне настроек редактора, которое
открывают с помощью пункта Properties (Свойства) в контекстном меню. Для этого
используются флажки, расположенные на вкладке Code Insight (Анализ кода), как
показано на рис. 3.11.
Флажок Code completion (Автозавершение) помогает при вводе членов класса. Когда в
редакторе набирается название класса и ставится точка, система отображает список
методов этого класса. Подходящий метод можно выбрать с помощью курсорных
клавиш без ручного ввода (рис. 3.12).
Флажок Code parameters (Параметры вызова) упрощает ввод параметров для метода.
Когда введено имя метода и поставлена открывающая скобка, система Delphi 7 подскажет тип следующего параметра (рис. 3.13).
Флажок Tooltip expression evaluation (Быстрое вычисление значений) рекомендуется
всегда держать установленным. В этом случае во время отладки значения переменных отображаются во вплывающем окне при наведении указателя.
Флажок Tooltip symbol insight (Информация об описании) управляет отображением
всплывающей подсказки, которая поясняет, в каком модуле соответствующий идентификатор описан и какой тип имеет.
С помощью движка Delay (Задержка) устанавливается величина задержки перед
появлением всплывающих подсказок (от 0,5 до 1,5 с).
Что такое отладка 165
Параметры вызова
Editor Properties
General] SouiceQDtHws} Display | fse/Mapphgs] Color
Быстрое вычисление значений
Сведения о модуле
__J7 CodejompteiiQU '
_U^ Code fiaiaiTFeteis
— _J7 TooBip e^esstirt e
- . . _ £t TooBji symbol |n$ig
•"CoddnsigM Gofers
ubitiacJ Si?mbot |Ш
Const** Symbol Щ
FuncOoii S ymfcii. | H
LAelSj^dtp
.v Procedure Symbol j Щ
•p-^uaton
Jл
Red
Green
8k*
Teal
Teal
d^
J
^JJ
J
ES*
[1 j - :,
Cf-1 sec 15iec
PpperlV SyinboE
IVpeSjimbot
УП? Symbol
Variable Symbol
• ^ jj :
• Okve ^
Ш Bbck _-|.
Д Maroon *j;
| j Window Вас yj:
CsiKef
Puc. 3-11, Выбор средств автоматизации ввода кода
Список существующих
методов
U nit t .pas
| Un(2
for i : = '; to IB do
if i > 3 then Art[i]
else Arr[i]
Имя класса
ГForm. |
trToInt (Ed.
liincJion: ShodString
Puc. 3.12. Автоматизированный ввод имен методов
Jwn параметра
Вызов метода Unit! .pas
| Un≫2 ™
tor i : = _;. to ^0 do
if i > 3 then Arrti] := 0
else Arr[i] := Ѓ};
TForm. Create (I
Label 1. Caption := InЃ}ToStr( Ы888 + StrToInt(Ed.
Puc. 3.13. Сведения о типе очередного параметра
166 Урок 3. Отладке программ
ВНИМАНИЕ Если включены все режимы автоподсказок, работа редактора
может заметно замедлиться.__
64.Создание и использование пользовательских компонент в Delphi.
Данная глава посвящена творческому процессу создания собственных компонентов. Мы
рассмотрим различные способы разработки новых компонентов, научимся строить свои
невизуальные компоненты.
Основы создания компонентов
Итак, мы приступаем к процессу создания собственного визуального или невизуального
компонента. Для создания собственного компонента важно иметь представление о
библиотеке визуальных компонентов Delphi, об иерархии компонентов. Все это мы уже
рассматривали выше. Для чего же нужны новые компоненты? Ответ неоднозначный.
Решение о создании но вых компонентов может быть принято по ряду причин, среди
которых:
- разработка нового пользовательского интерфейса, с дальнейшим использованием его в
других приложениях;
- создание принципиально нового класса, которого нет в стандартной библиотеке Delphi и
среди элементов ActiveX;
- упрощение кода приложения, путем введения новых компонентов; - распространение
своих компонентов среди других программистов;
- и, наконец, желание глубоко изучить Delphi, разобраться с тонкостями
программирования.
Естественно, кроме упомянутых причин, вы можете назвать множество собственных.
Создание компонентов по сложности практически не отличается от разрабокти
приложений. Конечно, все зависит от сложности компонента. Но если вы уже решились
на сотворение компонента, рекомендации будут следующими:
- определите для себя, какие действия должен выполнять компонент; - разработайте
краткий алгоритм, по которому будет работать компонент;
- разбейте всю конструкцию компонента на независимые части;
- предоставьте возможность дальнейшей разработки компонента (возможно, в будущем
вы захотите создать на его основе компонент-потомок);
- напишите код компонента (этот пункт разбивается на такие этапы):
выбор предка для вашего компонента;
создание заготовки (модуля) компонента;
создание свойств, событий и методов компонента;
отладка и тестирование;
регистрация компонента в среде Delphi;
создание справки для вашего компонента. Далее мы рассмотрим перечисленные выше
этапы создания компонента.
Выбор предка компонента
Итак, вы уже знаете основные классы, имеющиеся в VCL Delphi. Ранее мы рассмотрели
базовые классы, которые могут являться предками вашего компонента (см. главу 6). Эти
классы перечислены в табл. 2.9.
Таблица 2.9. Базовые классы VCL
Класс
Возможности класса
TObject
Классы, предком которых является данный класс, не
являются компонентами. Класс TObject применяется
при создании объектов, которые, обычно, являются
предками для других компонентов
TComponent
Применяется для создания невизуальных
компонентов
Применяется для создания не оконных компонентов,
т. е. компонентов без дескриптора окна. Потомки
данного класса размещаются в клиентской области
своих родительских компонентов и не требуют
системных ресурсов
TGraphicControl
TWinControl
Применяется для создания компонентов, имеющих
дескриптор окна. Данные компоненты являются
компонентами оконного типа и могут содержать в
себе другие компоненты
TCustomControl
Этот класс является потомком TWinControl и
дополняет его областью вывода (канвой). В данный
класс добавлен метод Paint. Рекомендуется
использовать настоящий класс для создания
пользовательских оконных компонентов
TCustomClassName
Библиотека визуальных компонентов содержит
несколько классов, у которых не все свойства
объявлены как published, т. е. доступные из других
модулей, но на основе данных классов можно
создавать классы-потомки, в которых и объявлять
данные свойства. Таким образом, разработчик
может создать несколько идентичных классов на
основе одного класса ClassName и в каждом из этих
классов определять необходимые свойства из
набора предопределенных свойств
Позволяет создавать компоненты-потомки,
предками которых являются обычные компоненты
или классы VCL Delphi. Таким образом, если перед
разработчиком стоит задача расширить
возможности какого-либо компонента Delphi, можно
использовать данный класс
TComponentName
Обратите внимание на то, что для правильного выбора класса-предка, вам нужно очень
хорошо ориентироваться в возможностях уже существующих в Delphi классов.
Создание заготовки компонента
Итак, вы выбрали класс-предок для вашего компонента. Теперь можно приступать к
созданию модуля вашего компонента. Создание модуля (заготовки) для нового
компонента можно выполнить путем вызова окна Delphi, которое называется экспертом
компонентов (Component Expert). Данное окно можно вызвать путем выбора в главном
меню Delphi пункта Component/New Component (Компонент/Новый компонент). При
этом появляется окно, изображенное на рис. 2.13.
Рис. 2.13. Окно эксперта компонентов
Рассмотрим данное окно подробнее. Итак, поле ввода Ancestor type предназначено для
ввода класса-предка для нового компонента. Это поле ввода содержит в выпадающем
списке все зарегистрированные классы библиотеки VCL. Предположим, что мы будем
создавать компонент, предком которого является кнопка TButton. Для этого выберем в
выпадающем списке класс TButton. Поле Class Name предназначено для ввода имени
нового класса. Пусть в нашем случае это будет новый класс TMyButton. Заметьте, что по
умолчанию Delphi заполняет это поле именем класса-предка с добавлением порядкового
номера (в нашем случае TButtoni). Еще одно поле Palette Page показывает, на какой
вкладке палитры компонентов будет расположен новый компонент после его регистрации.
Оставим в этом поле значение, предлагаемое Delphi по умолчанию samples. Два
следующих поля Unit file name и Search path заполняются средой Delphi самостоятельно,
но разработчик может их изменить. Мы не будем этого делать в нашем примере. В
результате окно эксперта компонентов должно быть заполнено так, как показано на рис.
2.14.
Рис. 2.14. Заполненное окно эксперта компонентов
После заполнения полей данного окна нажимаем кнопку ОК, и Delphi автоматически
создаст заготовку модуля вашего компонента. Модуль заготовки для нашего примера
представлен на листинге 2.7.
Листинг 2.7
unit MyButton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls; type
TMyButton = class(TButton)
private
{ Private declarations }
protected
{ Protected declarations } public
{ Public declarations } published
{ Published declarations } end;
procedure Register; implementation procedure Register; begin
RegisterComponents('Samples', [TMyButton]); end;
end.
Итак, заготовка для нового компонента готова. Она не содержит никаких новых свойств,
методов и событий для создаваемого компонента. Нужно отметить, что данный компонент
уже имеет все свойства, события и методы, которые определены для класса TButton. Мы
уже кратко рассматривали процедуру создания нового класса (см. раздел "Объектноориентированное программирование" главы 1). Там же мы обсуждали основные типы
свойств класса. Обратим теперь взор на основные типы методов компонента.
Все методы могут быть одного из нескольких типов: статические (static), виртуальные
(virtual), динамические (dynamic) или методы-сообщения (message). По умолчанию,
методу присваивается статический тип.
Статические методы
Статические методы аналогичны обычным функциям или процедурам Delphi. Адрес
такого метода известен среде Delphi на стадии компиляции, поэтому Delphi производит
статический вызов метода во время выполнения программы. Статические методы
работают быстрее всех других методов, но не могут быть перегружены (overload).
Перегрузка метода подразумевает, что класс или компонент может содержать несколько
методов с одинаковым именем, но разными списками параметров.
Статический метод может быть описан так:
type
TComponent = class
procedure MyProcedure;
end;
Здесь, метод MyProcedure является статическим. Мы опустили в данном примере название
базового класса TObject после слова class. По умолчанию, компилятор создает
наследников класса TObject.
Виртуальные методы
Виртуальные методы, в отличие от статических, поддерживают перегрузку, поэтому
вызов таких методов для среды Delphi намного сложнее (заранее неизвестен адрес
конкретного вызываемого метода). Для того чтобы решить эту проблему, Delphi строит
таблицу виртуальных методов (Virtual Method Table), благодаря которой компилятор
может определить адрес метода во время выполнения программы. Эта таблица содержит
виртуальные методы не только самого класса или компонента, но и его предков.
Естественно, хранение такой таблицы увеличивает расходы памяти, но вызов
виртуальных методов выполняется быстрее, чем вызов динамических методов.
Описание виртуального метода выглядит следующим образом:
type
TComponent = class
procedure MyProcedure; virtual;
end;
В данном случае метод MyProcedure - виртуальный.
Динамические методы
Динамические методы похожи на виртуальные, они также могут быть перегружены.
Основная разница между виртуальными и динамическими методами - в способе их
вызова. Если для виртуальных методов строится таблица виртуальных методов, то
каждому динамическому методу присваивается уникальное число-идентификатор, после
чего строится таблица динамических методов (Dynamic Method Table), в которую
заносится данное число, а также адрес метода. Еще одно отличие динамических методов
от виртуальных заключается в том, что таблицы динамических методов содержат методы
только одного компонента или класса (не включая его предков). Поэтому существенно
экономится память, но замедляется время работы, т. к. для поиска адреса метода обычно
приходится просматривать несколько таблиц динамических методов.
Описание динамического метода может выглядеть так:
type
TComponent = class
procedure MyProcedure; dynamic;
end;
Методы-сообщения
Методы-сообщения не вызываются из программы непосредственно, как другие методы.
Этот тип методов предназначен для того, чтобы выполнить какие-либо действия в ответ
на сообщение Windows. В качестве примера рассмотрим описание метода-сообщения:
type
TComponent = class
procedure MyProcedure(Var A: TMessage); message
wm_MessageWindows;
end;
После служебного слова message ставится значение (в нашем случае wmjfessagewindows),
определяющее сообщение Windows, в ответ на которое будет вызванан метод
MyProcedure.
Виртуальные и динамические методы могут быть замещенными (overriden) или
абстрактными (abstract).
Замещенные методы
Замещение методов предполагает передачу и изменение методов от компонента (класса)
предка компоненту (классу) наследнику. Как мы уже отметили, только виртуальные или
динамические методы могут быть замещенными. Рассмотрим пример:
type
TComponentChild = class (TComponentParent)
procedure MyProcVirtual; override;
procedure MyProcDynamic; override;
end;
Применение служебного слова override после названия метода позволяет заместить
оригинал метода компонента предка методом компонента наследника. При этом
замещение происходит непосредственно в таблице виртуальных методов (или таблице
динамических методов). При использовании
Служебных слов virtual или dynamic вместо override, произойдет просто создание нового
метода вместо замещения старого.
Замещение методов не работает со статическими методами - при замещении статического
метода новым произойдет простая замена метода родителя в потомке.
Абстрактные методы
Абстрактными могут быть виртуальные или динамические методы. Абстрактными
методами называются такие методы, которые описаны внутри определения класса или
компонента, но не содержат никаких действий и никогда не вызываются. Такие методы
используются только в компонентах или классах-предках. Описание абстрактного метода
выглядит так:
procedure MyProcedure; virtual; abstract;
Примечание
Никогда не вызывайте на выполнение абстрактные методы, т. к. они не содержат
никаких команд, которые могли бы выполниться. Вызов абстрактного метода приведет
к генерации исключительной ситуации EAbstractError
В данной главе мы дополним модуль-заготовку нового компонента всем необходимым:
свойствами, событиями и методами. Создадим работоспособный компонент и
зарегистрируем его в среде Delphi. Затем мы рассмотрим, как можно создать справочную
систему по своему компоненту, если вы хотите создавать компоненты для
распространения их среди других программистов. Ну и, конечно, мы рассмотрим, как
можно модифицировать уже существующие компоненты визуальной библиотеки
компонентов Delphi.
Создание свойств компонента
Добавление новых свойств в компонент осуществляется очень просто. Достаточно задать
поля и свойства, определив при этом их тип и доступ (чтение, запись). Пример простого
задания свойств в новом компоненте представлен на листинге 2.8.
Листинг 2.8
TMyButton = class(TButton)
private
{ Private declarations }
FMyCount: Integer;
FStirngOfText: String;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyCount: Integer read FMyCount write FMyCount;
property StringOfText: String read FStringOfText write
FStringOfText;
end;
На приведенном выше листинге мы задаем два новых поля для компонента TMyButton и
определяем два новых свойства компонента: одно типа integer, другое string. Для
простоты, значения данных свойств считываются и записываются в одноименные поля.
Задание этих свойств будет гарантировать доступ к ним в окне инспектора объектов
(благодаря описанию свойств в разделе published) и не потребует написания
дополнительных методов для доступа к свойствам.
Примечание
По взаимной договоренности принято названия полей начинать с буквы F (поле, field), а
названия компонентов и любых объектов с буквы т (тип, type).
Создание перечисляемых свойств компонента
К свойствам перечисляемого типа относятся такие свойства компонента, которые при их
редактировании в окне инспектора объектов вызывают выпадающий список, содержащий
возможные значения данного свойства. К числу подобных свойств относятся Align,
Borderstyle, color и др. Для того чтобы самостоятельно добавить в новый компонент
перечисляемое свойство, необходимо сначала определить новый перечисляемый тип,
например:
TMyEnumType = (eFirst, eSecond, eThird);
После этого нужно определить поле компонента, которое будет хранить значение данного
перечисляемого типа, затем определить свойство. Пример добавления перечисляемого
типа в новый компонент TMyButton приведен на листинге 2.9.
Листинг 2.9
type
TMyEnumType = (eFirst, eSecond, eThird);
type
TMyButton = class(TButton)
private
{ Private declarations }
FMyEnum: TMyEnumType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
end;
Таким образом, в окне инспектора объектов, при изменении свойства MyEnumProp, будет
выдан выпадающий СПИСОК, Содержащий Три пункта: eFirst, eSecond И eThird (рис.
2.15).
Рис. 2.15. Перечисляемое свойство MyEnumProp в новом компоненте TMyButton
Создание свойств-множеств в компоненте
Тип множества часто фигурировал в Object Pascal, и некоторые свойства компонентов
Delphi имеют данный тип. Когда вы используете свойство типа set, вы должны учитывать,
что каждый элемент множества будет являться отдельным свойством, имеющим
логический тип в инспекторе объектов.
Для создания свойства-множества сначала зададим нужный тип:
TMySetTypeFirst = (poFirst, poSecond, poThird); TMySetType = set
of TMySetTypeFirst;
Первая строка задает перечисляемый тип TMySetTypeFirst, который определяет диапазон
множества, а вторая строка - само множество TMySetType.
Пример добавления свойства-множества в компонент TMyButton приведен на листинге
2.10.
Листинг 2.10.
type
TMyEnumType = (eFirst, eSecond, eThird); TMySetTypeFirst = (poFirst, poSecond, poThird);
TMySetType = set of TMySetTypeFirst;
type
TMyButton = class(TButton)
private
{ Private declarations } FMyEnum: TMyEnumType; FMyOptions: TMySetType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
property MyOptions: TMySetType read FMyOptions write FMyOptions;
end;
Для удобства, мы не стали исключать определение перечисляемого свойства в компоненте
TMyButton.
В результате, в окне инспектора объектов свойство-множество будет отображаться, как
представлено на рис. 2.16.
Рис. 2.16. Свойство-множество MyOptions в новом компоненте TMyButton
Создание свойства-объекта в компоненте
Каждый компонент может содержать в себе свойство-объект. В качестве свойства-объекта
может выступать любой компонент или объект Delphi. Кроме того, свойствами-объектами
нового компонента могут быть новые компоненты или объекты, которые вы создали
самостоятельно. Важным условием является тот факт, что свойства-объекты должны быть
потомками класса TPersistent. Это необходимо для того, чтобы свойства объекта-свойства
отображались в окне инспектора объектов. Приведем пример создания свойства-объекта в
нашем компоненте TMyButton.
Для начала создадим произвольный новый объект, являющийся прямым потомком класса
TPersistent (листинг 2.11).
Листинг 2.11
type
TMyObject = class (TPersistent}
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
В качестве предка нового класса может выступать не только класс TPersistent, но любой
его потомок. В вышеприведенном листинге мы создаем новый класс TMyObject, в
котором присутствует два простых свойства - Propertyl и Property2. Кроме того, в новый
объект включена процедура Assign. Данная процедура необходима для обеспечения
правильного доступа к свойству нашего будущего компонента TMyButton. Ниже приведен
листинг 2.12, в котором мы добавляем в компонент TMyButton новое свойство-объект.
Листинг 2.12
type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
Как вы можете видеть, мы добавляем в код нового компонента конструктор и деструктор
объекта.
Теперь осталось дописать конструктор и деструктор для компонента TMyButton, в
которых необходимо соответственно создать и уничтожить, кроме компонента
TMyButton, еще и объект TMyObject. А также нужно написать код метода setMyObject,
который будет помещать новое значение в свойства объекта TMyObject. Ну и, конечно,
написать код метода Assign для объекта TMyObject. Полную версию кода представляет
листинг 2.13. Здесь, кроме ранее описанного, приводится код ко всем этим методам.
Листинг 2.13
type
TMyObject = class (TPersistent)
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
procedure Register;
implementation
procedure TMyButton.SetMyObject (Value: TMyObject);
begin
if Assigned (Value) then FMyObject.Assign (Value);
end;
constructor TMyButton.Create (AOwner; TComponent);
begin
inherited Create (AOwner);
FMyOb j ect:=TMyObject.Create;
end;
destructor TMybutton.Destroy;
begin
FMyObject.Free;
inherited Destroy;
end;
procedure TMyObject.Assign (Source: TPersistent);
begin
if Source is TMyObject then
begin
FPropertyl:=TmyObject (Source).Property1;
FProperty2:=TmyObject (Source).Property2;
inherited Assign (Source);
end;
end;
Обратите внимание на реализацию метода TMyObject.Assign. Здесь сначала выполняется
проверка на то, что передается правильный экземпляр объекта TMyObject. Если он
правильный, то значения свойств (Source) Переносятся в поля FPropertyl и FProperty2
объекта TMyButton. Результатом исполнения вышеприведенного кода будет новый
компонент TMyButton, содержащий в себе свойство-объект TMyObject. В окне инспектора
объектов это будет выглядеть, как представлено на рис. 2.17.
Рис. 2.17. Свойство-объект MyObject в новом компоненте TMyButton
Создание свойства-массива в компоненте
Свойства компонента могут быть практически любого типа, которые поддерживает язык
Object Pascal. Некоторые свойства могут быть массивами. Яркими примерами свойств
такого типа являются тмето. Lines, TDBGrid. columns и др. Подобные свойства требуют
собственных редакторов. Мы пока остановимся на создании простого свойства, которое
представляет из себя массив (о создании собственных редакторов свойств читайте далее в
этой главе). Создадим новый компонент Tweek, содержащий два свойства: Month и
Number. Свойство Month будет представлять собой массив, возвращающий название
месяца по переданному целому числу от 1 до 12. Свойство Number - тоже массив, который
возвращает число, соответствующее названию месяца.
Итак, на листинге 2.14 приведен код компонента TWeek.
Листинг 2.14
unit Week; interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TWeek = class(TComponent)
private
{ Private declarations }
function GetMonthName (const AIndex: Integer): String;
function GetMonthNumber (const AMonthNarae: String): Integer;
protected
{ Protected declarations }
public
{ Public declarations }
property MonthName[const AIndex: Integer]: String read
GetMonthName; default;
property MonthNumber[const AMonthName: String]: Integer read
GetMonthNumber;
published
{ Published declarations }
end;
procedure Register;
implementation
const
MonthNames: array[1..12] of String[8]=
('Январь','Февраль','Март','Апрель','Май',
'Июнь','Июль','Август','Сентябрь', 'Октябрь',
'Ноябрь','Декабрь');
function TWeek.GetMonthName(const AIndex: Integer): String;
begin
if (AIndex<=0) or (AIndex>12) then
raise Exception.Create('Номер месяца должен быть числом от 1 до
12')
else Result:- MonthNames[AIndex];
end;
function TWeek.GetMonthNumber(const AMonthName: String):
Integer;
var i:integer;
begin
Result:=0;
for i:=l to 12 do begin
if Uppercase(AMonthName)=UpperCase(MonthNames[i]) then
Result:=1;
end;
end;
procedure Register;
begin
RegisterComponents('Samples', [TWeek]);
end;
end.
Рассмотрим вышеприведенный код более подробно. Как вы можете видеть, свойства типа
Array объявляются вместе с индексами. Для свойства MontnName мы определили индекс
Aindex, а для свойства MonthNumber - индекс AMonthName. Для доступа к свойствам
такого типа необходимо использовать методы. Внутренних полей здесь нет. Если
свойство типа Array многомерно, то свойство-массив объявляется сразу с несколькими
индексами. При вызове методов доступа к ним нужно передавать параметры в том же
порядке, в каком вы их указали в свойстве.
Функция GetMonthName возвращает строку, содержащую имя месяца, соответствующего
целому числу от 1 до 12, переданному в качестве параметра данной функции. В случае
передачи функции числа, не принадлежащему данному диапазону, будет сгенерировано
исключение командой raise (об исключениях читайте главу 2).
Наконец, функция GetMonthNumber возвращает число от 1 до 12, которое соответствует
названию месяца, переданного в качестве параметра в данную функцию. В случае если ни
один месяц не соответствует названию, определенному массивом MonthNames,
результатом выполнения данной функции будет ноль.
Таким образом, если поместить на форму экземпляр компонента TWeek с именем weeki,
при выполнении строки ShowMessage (Weekl.MonthName[5]}; будет выдано окно с
сообщением Май.
Создание собственных редакторов свойств
Перед тем как создавать собственный редактор свойств компонента, рассмотрим сначала
имеющиеся в Delphi редакторы свойств. Их достаточно легко видеть в окне инспектора
объектов, когда вы пытаетесь изменить какое-либо свойство компонента. Например, когда
вы изменяете свойство Color для того или иного компонента, вы видите редактор свойства
цвета. Важно заметить, что все свойства компонентов имеют свои редакторы, даже такие
простейшие свойства, как Caption, Height, Enabled. Особенностью этих редакторов
является то, что компоненты "не знают", какие редакторы вы используете для изменения
их свойств. Это может навести на мысль взять собственный редактор свойств вместо
предопределенного. Например, можно написать редактор свойства width, который будет
ограничен каким-либо числом.
Редакторы свойств имеют свою иерархию. Рассмотрим ее.
Базовым классом в иерархии редакторов свойств является TPropertyEditor. Названия
классов говорят сами за себя. Например, класс TColorProperty отвечает за свойство цвета
компонента, класс TlntegerProperty связан с теми свойствами, которые имеют тип integer и
т. д. На листинге 2.15 приведен код, определяющий базовый класс TPropertyEditor.
Листинг 2.15
TPropertyEditor = class
private
FDesigner: TFormDesigner;
FPropList: PInstPropList;
FPropCount: Integer;
constructor Create(ADesigner: TFormDesigner; APropCount:
Integer);
function GetPrivateDirectory: string;
procedure SetPropEntry(Index: Integer; AInstance: TComponent;
APropInfo: PPropInfo};
protected
function GetPropInfо: PPropInfo;
function GetFloatValue: Extended;
function GetFloatValueAt(Index: Integer): Extended;
function GetMethodValue: TMethod;
function GetMethodValueAt(Index: Integer): TMethod;
function GetOrdValue: Longint;
function GetOrdValueAt(Index: Integer): Longint;
function GetStrValue: string;
function GetStrValueAt(Index: Integer): string;
procedure Modified;
procedure SetFloatValue(Value: Extended);
procedure SetMethodValue(const Value: TMethod);
procedure SetOrdValue(Value: Longint);
procedure SetStrValue(const Value: string);
public
destructor Destroy; override;
procedure Activate; virtual;
function AllEqual: Boolean; virtual;
procedure Edit; virtual;
function GetAttributes: TPropertyAttributes; virtual;
function GetComponent(Index: Integer): TComponent;
function GetEditLimit: Integer; virtual;
function GetName: string; virtual;
procedure GetProperties(Proc: TGetPropEditProc);virtual;
function GetPropType: PTypelnfo;
function GetValue: string; virtual;
procedure GetValues(Proc: TGetStrProc); virtual;
procedure Initialize; virtual;
procedure SetValue(const Value: string); virtual;
property Designer: TFormDesigner read FDesigner;
property PrivateDirectory: string read GetPrivateDirectory;
property PropCount: Integer read FPropCount;
property Value: string read GetValue write SetValue;
end;
Методы данного класса, которые приведены ниже, можно переопределять для изменения
поведения редактора свойств.
Метод Activate вызывается, когда данное свойство выбирается в окне инспектора
объектов.
Метод AllEqual вызывается при выборе на форме более одного компонента.
Метод Edit вызывается при нажатии кнопки (...) или при двойном щелчке мыши на
свойстве. Данный метод может вызвать диалоговое окно для редактирования свойства,
например, как это происходит при редактировании свойства Font.
Метод GetAttributes возвращает множество значений типа TProperyAttributes,
определяющих, каким образом свойство будет отображаться в окне инспектора объектов.
Метод Getcomponent предназначен для определения компонента по его номеру (параметр
index), в случае, когда на форме выбрано несколько компонентов одновременно.
Метод GetEditLimit возвращает максимальное число символов в строке, которые
пользователь может ввести при редактировании свойства.
Метод GetName предназначен для получения имени свойства. Данный метод
целесообразно изменять только в том случае, когда имя свойства отличается от имени,
отображаемом в окне инспектора объектов.
Метод GetPropType применяется для определения указателя на информацию о типе
редактируемого свойства.
Метод Getvalue возвращает значение свойства в виде строки.
Метод initialize вызывается при создании (инициализации) редактора свойств.
Метод setvalue применяется для установки значения свойства.
В большинстве случаев, при создании нового редактора свойств нет необходимости
использовать в качестве класса-предка базовый класс TPropertyEditor. Часто разработчик
просто переделывает уже существующий для данного свойства редактор, переопределяя
некоторые его методы.
Рассмотрим в качестве примера переработанное свойство Hint, которое применяется для
показа всплывающей подсказки при задержании указателя мыши над компонентом. В
стандартном случае такая подсказка имеет всего одну строку. Попробуем сделать
свойство Hint многострочным. Нижеприведенный листинг 2.16 показывает, как создать
новый редактор свойств THintProperty. В качестве класса-предка для данного редактора
свойств выберем редактор TStringProperty.
Листинг 2.16
THintProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
function GetValue : String; override;
procedure Edit; override;
end;
function THintProperty.GetAttributes: TPropertyAttributes;
begin
Result := inherited GetAttributes + [paDialog, paReadOnly];
end;
function THintProperty.GetValue : string;
var i : Byte;
begin
result:=inherited GetValue;
for i:=l to Byte(resultt0]) do
if result[i]<#32 then result[i]: = '>';
end;
procedure THintProperty.Edit; var
HintEditDlg : TStrEditDlg; s : string;
begin
HintEditDlg:=TStrEditDlg.Create(Application);
with HintEditDlg do
try
Memo.MaxLength := 254;
s:=GetStrValue+#0;
Memo.Lines.SetText(@s[1]);
UpdateStatus(nil);
ActiveControl := Memo;
If ShowModal = mrOk then begin
s:=StrPas(Memo.Lines.GetText);
if s[0]>#2 then Dec(Byte(s[0]),2);
SetStrValue(s);
end; finally
Free;
end;
end;
Рассмотрим методы нового класса:
- функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (при
этом появляется кнопка (...) в окне инспектора объектов) и paReadOnly (который
применяется для того, чтобы редактирование данного свойства было возможно только
через диалог);
- функция Getvalue заменяет символы перевода каретки (#10) -и переход на новую строку
(#13) на символ больше (>).
Наконец, процедура Edit применяется для вызова диалога для ввода строк всплывающей
подсказки.
Для регистрации нового редактора нужно в интерфейсной части модуля поместить
объявление процедуры Register. После чего В части implementation модуля написать саму
процедуру регистрации (листинг 2.17).
Листинг 2.17.
procedure Register;
begin
RegisterPropertyEditor (Typelnfо(String), TControl, 'Hint',
THintProperty);
end;
Данная процедура позволяет привязать один и тот же редактор к свойствам, в зависимости
от их названия или типа. Это определяется параметрами, которые передаются процедуре
RegisterPropertyEditor. Первый параметр определяет тип свойства (в нашем примере - это
string), второй параметр - класс компонента. Третий параметр позволяет задать имя
свойства. Четвертый параметр указывает имя редактора свойства.
Для того чтобы установить новый редактор свойств в Delphi, нужно выполнить
следующие шаги:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля.
После того как произойдет компиляция библиотеки, можно создать новую форму и
разместить на ней какой-либо компонент. После чего установим у этого компонента
свойство showHint в true и нажмем кнопку (...) в свойстве Hint. Вы видите на экране новый
многострочный редактор для свойства Hint.
Команды Default и NoDefault
Итак, мы уже умеем создавать свойства произвольного типа для собственного
компонента. Осталось заметить, что многим свойствам можно присвоить конкретное
значение, которое будет установлено по умолчанию. Для этого достаточно присвоить это
значение полю компонента, например:
FMyProperty := 10;
В результате чего, при каждом добавлении компонента на форму свойство Myproperty
будет принимать значение 10.
Команды Default и NoDefault применяются для ускорения процесса загрузки формы при
работе приложения. Например,
property MyCount: Integer read FMyCount write FmyCount Default
0;
Данный код не присваивает значение о свойству MyCount. При выполнении
вышеприведенного кода команда Default о означает следующее: если при сохранении
формы, содержащей компонент, значение свойства MyCount не будет равно нулю, то
новое значение сохранится в файле формы, иначе значение данного свойства не будет
сохранено.
Примечание
Рекомендуется использовать команду Default во всех случаях, когда это возможно, если
вы хотите создать быстро работающее приложение.
Команда NoDefault предназначена для нейтрализации команды Default. Команда
применяется для отмены команды Default компонентов-предков. Пример использования
команды NoDefault:
TSecondComponent = class (TMyButton)
published
property MyCount NoDefault 0;
Создание событий компонента
Стандартные события мы с вами уже рассматривали в предыдущих главах. В настоящий
момент нам предстоит дать четкое определение событию, а также обработчику события.
Итак, событие - это любое действие, произошедшее благодаря операционной системе,
действиям пользователя, работе программы.
Событие можно "перехватить" и обработать с помощью программы-обработчика
события. Связь между событием и программой-обработчиком называется свойством-
событием. Таким образом, когда происходит какое-либо событие компонента, он может
обработать данное событие. Для этого сначала происходит проверка наличия кода
обработки события. Если подобный код есть - он выполняется.
Рассмотрим в качестве примера такое часто возникающее событие, как нажатие левой
кнопки мыши Onclick. Данное событие, как и многие другие, имеет так называемые
методы диспетчеризации событий (event-dispatching methods). Эти методы нужны как раз
для того, чтобы определять, создан ли код обработки произошедшего события для
данного компонента. Эти методы объявляются как защищенные (protected). Таким
образом, для свойства Onciick определен метод диспетчеризации события click (листинг
2.18).
Листинг 2.18
TControl = class (TComponent)
private
FOnClick: TNotifyEvent;
protected
procedure Click; dynamic;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
implementation
procedure TControl.Click;
begin
if Assigned (FOnClick) then FOnClick (Self);
end;
Листинг 2.19
procedure TForm1.FormMouseDown(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, '('+IntToStr(X)+', '+IntToStr(Y)+')');
end;
Рис. 2.18. Результат обработки события OnMouseDown
Пример создания нового события компонента
Попробуем теперь создать собственное событие. Для этого нужно сначала убедиться, что
такого события нет в VCL Delphi. Предположим, возникла необходимость создания
события, которое возникает каждые 30 секунд. Естественно, для этого случая можно
воспользоваться компонентом Timer, который расположен на вкладке System палитры
компонентов Delphi. Но, предположим, что наш компонент должен иметь такое событие
для удобства работы с ним. Код для создания события представлен на листинге 2.20.
Листинг 2.20
unit halfmin; interface
uses
Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms,
Dialogs,
ExtCtrls;
type
TTimeEvent = procedure (Sender: TObj'ect; TheTime: TDateTime) of
object;
THalfMinute = class (TComponent)
private
FTimer: TTimer;
FOnHalfMinute: TTimeEvent;
FOldSecond, FSecond: Word;
procedure FTimerTimer (Sender: TObject);
protected
procedure DoHalfMinute (TheTime: TDateTime); dynamic;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property OnHalfMinute: TTimeEvent read FOnHalfMinute write
FOnHalf-Minute;
end;
implementation
constructor THalfMinute.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
if not (csDesigning in ComponentState) then begin
FTimer:=TTimer.Create(self);
FTimer.Enabled:=True;
FTimer.Interval:=500;
FTimer.OnTimer:=FTimerTimer;
end;
end;
destructor THalfMinute.Destroy;
begin
FTimer.Free;
inherited Destroy;
end;
procedure THalfMinute.FTimerTimer (Sender: TObject);
var
DT: TDateTime;
Temp: Word;
begin
DT:=Now;
FOldSecond:=FSecond;
DecodeTime (DT,Temp,Temp,FSecond,Temp);
if FSecond <> FOldSecond then
if ((FSecond=30) or (FSecond=0)) then
DoHalfMinute (DT);
end;
procedure THalfMinute.DoHalfMinute(TheTime: TDateTime);
begin
if Assigned (FOnHalfMinute) then
FOnHalfMinute (Self, TheTime);
end;
end.
Для проверки работоспособности вышеприведенного кода вы можете добавить еще одну
процедуру для регистрации нового компонента с именем ThalfMinute, предварительно
добавив в interface-часть программы строку:
procedure Register;
Ниже представлен код для регистрации компонента:
procedure Register;
begin
RegisterComponents('Samples', [THalfMinute]);
end;
Для просмотра работоспособности нового компонента после его регистрации создадим
новую форму и разместим на ней новый компонент. Добавим на форму компонент TEdit.
Затем добавим обработчик события onHaifMinute для формы (листинг 2.21).
Листинг 2.21
procedure TForml.HalfMinutelHalfMinute(Sender: TObject; TheTime:
TDateTime);
begin
Editl.Text: = ('Время '+TimeToStr(TheTime)) ; Editl. Refresh;
end;
В результате работы данной программы в компоненте Editl будет выводиться текущее
время каждые 30 секунд.
Создание методов компонента
Добавление методов в новый компонент - операция несложная. Однако нужно обратить
внимание на некоторые особенности, которые в дальнейшем облегчат взаимодействие
пользователя с вашим компонентом.
Во-первых, необходимо, чтобы методы не были взаимозависимыми, т. е. каждый метод
должен быть самостоятельным и законченным. Во-вторых, метод не должен блокировать
компонент. И, в-третьих, метод должен иметь имя, соответствующее выполняемым
действиям.
Как вы уже знаете, методы объявляются в секциях private, public и protected. При создании
нового метода важно учитывать, как он будет использоваться в дальнейшем, сможет ли
данный компонент быть предком для других компонентов, и, в зависимости от этого,
разместить методы в нужных секциях. Табл. 2.10 поможет вам при выборе секций для
методов компонента.
Таблица 2.10. Размещение методов компонента в различных секциях
Секция
Размещаемые методы
Private
В данной секции лучше всего размещать те методы, которые
не могут изменяться в компонентах-потомках. Эти методы не
доступны вне данного компонента
Protected
В этой секции размещают методы, которые будут доступны
для изменения в компонентах-потомках
Public
Данная секция предназначена для размещения методов,
которые доступны любому пользователю компонента. Доступ
полный во время работы приложения, но не во время
разработки, т. е. данные методы недоступны в окне
инспектора объектов
В этой секции размещаются свойства компонента, которые
доступны во время разработки приложения в окне
инспектора объектов
Published
Регистрация компонента в среде Delphi
Регистрация компонента необходима для размещения компонента в палитре компонентов.
При использовании эксперта компонентов для создания нового компонента Delphi
самостоятельно создает процедуру регистрации компонента в модуле-заготовке.
Создателю компонента в данном случае ничего не нужно более делать, кроме выполнения
следующих шагов:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля (естественно, предварительно нужно сохранить
модуль компонента).
После компиляции на выбранной вкладке палитры компонентов появится новый
компонент.
Если же вы создаете компонент без использования эксперта компонентов, вам придется
самостоятельно дописывать процедуру регистрации компонента. В разделе interface
модуля компонента нужно дописать строку:
procedure Register;
А в разделе implementation добавить процедуру регистрации, например:
procedure Register;
begin
RegisterComponent ('Samples', [TMyComponent]);
end;
В результате, компонент с именем TMyComponent будет размещен на вкладке samples
палитры компонентов.
Обратите внимание на значок, которым обозначается новый компонент. Его можно
поменять на любой другой. Для создания собственного значка разумно воспользоваться
стандартной программой Image Editor, которая входит в комплект поставки Delphi. Можно
использовать и любой другой редактор растровых изображений.
Создайте значок для вашего компонента размером 24x24 пиксела. Данное изображение
сохраните в файле формата DCR. Имя файла - это имя вашего компонента, в котором все
буквы - заглавные. Например, для компонента TMyButton имя файла картинки будет
TMYBUTTON.DCR. Затем поместите файл картинки в ту папку, в которой находится
файл с модулем компонента. Перекомпилируйте модуль, и ваш компонент будет
изображаться в палитре компонентов вашим рисунком.
Related documents
Download