Глава 16

КЛАССЫ ОБЩЕГО НАЗНАЧЕНИЯ

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

16.1. КЛАСС EXCEPTION - ОБРАБОТКА ИСКЛЮЧЕНИЙ

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

При работе в среде Delphi эксперименты с исключениями плохо прослеживаются, т. к. при каждом исключении среда; перехватывает управление программой. В этом случае бывает полезно отменить такое поведение среды. Для этого вызовите опцию Tools. [.- -Debugger Options и на странице Languaqe Exception уберите флажок в переключателе Stop on delphi. Exceptions (для шэсдьиуш-лх версий Delphi выберите опцию меню Tools | Environent: Options и на странице Preference уберите флажок в персключателе Break on exception).

16.1.1. Защищенные блоки

Для обработки исключений в Object Pascal предусмотрен механизм защищенного блока:

except

<обработчики исключений>

else

<операторы>

end;

finally

<операторы>

end;

Try

<операторы>

Try

<операторы>

 

Защищенный блок начинается зарезервированным словом try (попытаться [выполнить]) и завершается словом end. Существуют два типа защищенных блоков - except (исключить) и finally (в завершение), отличающихся способом обработки исключения. В блоке except порядок выполнения операторов таков: сначала выполняются операторы секции try... except; если операторы выполнены без возникновения исключительной ситуации, работа защищенного блока на этом прекращается, и управление получает оператор, стоящий за end; если при выполнении части try возникло исключение, управление получает соответствующий обработчик в секции except, а если таковой не найден - первый из операторов, стоящих

За словом else. В блоке finally операторы В секции finally. . .end получают управление всегда, независимо от того, возникло ли исключение в секции try.. .finally или нет. Если исключение возникло, все операторы в секции try.. .finally, стоящие за “виновником” исключения, пропускаются, и управление получает первый оператор секции finally... end. Если исключения не было, этот оператор получает управление после выполнения последнего оператора секции try.. .finally.

Обработчики исключений в блоке except имеют такой синтаксис:

on <класс исключения> do <оператор>;

Здесь on, do - зарезервированные слова; <класс исключения> -класс обработки исключения; <оператор> - любой оператор Object Pascal, кроме оператора передачи управления goto на метку вне блока except.

Обратите внимание: имя класса служит своеобразным ключом выбора, а собственно обработка осуществляется оператором, стоящим за do (этот оператор может быть составным, так что обработка исключения может занимать произвольное количество операторов Object Pascal).

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

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

try

except

ShowMessage('Ощибка!') ;

end;

Защищенные блоки могут вкладываться друг в друга на неограниченную глубину, т. к. везде, где в предыдущих описаниях использовались <операторы> ИЛИ <оператор>, могут использоваться любые операторы Object Pascal, в том числе и try... except или try...finally:

try

try finally

end;

except

on EMatchError do begin

try try end;

end;

end

end;

16.1.2. Класс Exception

Класс Exception является родительским классом для всех классов-исключений. Этот класс объявляется в модуле sysUtils следующим образом:

type

Exception = class(TObject)

private

FMessage: String;

FHelpContext: Integer;

public

constructor Create(const Msg: Strings);

constructor CreateFmt(const Msg: String;

const Args: array of const);

constructor CreateRes(Ident: Integers);

constructor CreateResFmt(Ident: Integer;

const Args: array of const) ;

constructor CreateHelp(const Msg: String; aHelpContext:

Integer) ;

constructor CreateFmtHelp(const Msg: String;

const Args: array of const; aHelpContext: Integers);

constructor CreateResHelp(Ident: Integer; aHelpContext:

Integer) ;

constructor CreateResFmtHelp(Ident: Integer;

const Args: array of const;

aHelpContext: Integers);

property HelpCoatext: Integer read FHelpContext write FHeipContext;

property Message: String read FMessage write FMessage;

end;

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

16.1.3. Стандартные классы исключений

В Delphi определены стандартные классы исключений, перечисленные в табл. 16.1. Именно эти имена, а также имена пользовательских классов (см. п. 16.1.5) могут использоваться в обработчиках исключении.

Таблица 16.1

Класс

Родитель

Обрабатываемое исключение

EAbort

Exception

Реализует “тихую” (без какого-либо сообщения) обработку любого исключения

EAbstractErrpr

Exception

Программа пытается вызвать абстрактный метод

EAccessViolation

Exception

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

EAppletException

Exception

Ошибка связана с созданием управляющих панелей в апплет-приложениях

EArrayError

Exception

Возникает из-за различного рода ошибок при работе с массивами (неверный индекс, попытка вставить элемент в массив фиксированной длины или в отсортированный массив и т. п.)

EAssertionFaild

Exception

Возбуждается отладочной процедурой Assert, когда тстируемое ею логическое выражение имеет значение False.

EBitsError

Exception

Программа пыталась обратиться к свойству Bits объекта TBits с индексом меньше нуля или больше максимально допустимого значения

EBrokerException

Exception

Объект-брокер не может найти сервер

ECacheError

Exception

Ошибка в наборе данных для компонента Tde-cisionCube

EClassNotFound

EFilerError

Для компонента, читаемого из потока данных, не найден соответствующий класс. Обычно возникает"в случае, когда в форму вставлен нестандартный компонент, а в библиотеке компонентов Delphi нет связанного с ним класса

ECommonCalendar-Error

Exception

Возникает в объектах класса TCommonCalendar и его потомках, когда вводится неверная дата

EComponentError

Exception

Возникает при различных манипуляциях программы с компонентом (программа не может зарегистрировать компонент, переименовать его или когда для его работы требуется интерфейс СОМ, который компонентом не поддерживается)

EControlC

Exception

Возникает при нажатии Ctrl-C при работе приложения в режиме консолиЛ

EConvertError

Exception

Ошибка преобразования в функциях StrToint или StrToFloat

ECorbaDispatch

Exception

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

ECorbaException

Exception

Возникает в программах, использующих технологию CORBA

ECorbaUser-Exception

ECorbaException

Возникает как определяемая пользователем реакция на ошибки интерфейса

EDatabaseError

Exception

Возникает, когда компонент обнаруживает ошибку в базе данных

EDateTimeError

Exception

Возбуждается компонентом TDateTimePicker при попытке ввода неверной даты или времени

EDBClient

EDatabaseError

Ошибка связана с неправильной работой Tcli-entDataSet

EDBEditError

Exception

Возникает, когда компонент пытается использовать данные, несовместимые с заданной маской

EDBEngineError

EDatabaseError

Связана с ошибками BDE

EDimensionMar-Error

Exception

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

EDimIndexError

Exception

Связана с нарушением размерности массива данных для куба решений

EDivByZero

EIntError

Ошибка целочисленного деления на ноль

EDSWriter

Exception

Ошибка при подготовке провайдером пакета данных для набора данных

EExternal-Exception

EStream-Error

Возникла ошибка, код которой не является предопределенным в Delphi

EFCreateError

EStream-Error

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

EFilerError

EStream-Error

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

EFOpenError

EStream-Error

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

EHeapException

Exception

Ошибка связана с неправильными операциями над динамической памятью

ElBClientError

ElBError

Ошибка связана с функционированием IBX-клиента

ElBError

EDatabaseError

Общая ошибка технологии IBX

ElBInterbase-Error

ElBError

Ошибка связана с функционированием сервера в технологии IBX

EInOutError

Exception

Любая ошибка в файловых операциях. Поле ErrorCode объекта этого класса содержит код ошибки

EInterpreterError

Exception

Возникает, когда компонент класса TDataBlockinterpeter не может интерпретировать данные блока данных

EIntError

Exception

Любая ошибка в целочисленных вычислениях

EIntfCastError

Exception

Попытка недопустимого приведения типов в OLE-объектах

EIntOverflow

EIntError

Ошибка целочисленного переполнения: программа пытается присвоить целочисленной переменной значение, выходящее из 32-двоичных разрядов

EInva1i dArgument

EMatchError

Возбуждается математическими функциями при выходе аргумента из допустимого диапазона

EInvalidCast

Exception

Программа пытается осуществить недопустимое преобразование типов с помощью оператора as

EInvalidGraphic

Exception

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

EInvalidGraphic-Operation

Exception

Программа пытается выполнить недопустимую графическую операцию

EInvalidGrid-Operation

Exception

Программа пытается выполнить недопустимую операцию над таблицей (например, обратиться к несуществующему столбцу или РЯДУ)

EInvalidImage

EFilerError

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

EInvalidOp

EMatchError

Ошибка в операциях с плавающей точкой (недопустимая инструкция, переполнение стека сопроцессора и т. п.)

EInvalidOpera-tion

Exception

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

EInvalidPointer

EHeap-Exception

Попытка использовать недействительный указатель

EListError

Exception

Эта ошибка связана с неверными действиями программы по отношению к разного рода спискам. Например обращение к элементу списка с индексом меньше нуля или больше максимально допустимого

ELowCapacity-Error

Exception

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

EMatchError

Exception

Любая ошибка при выполнении вычислений с плавающей точкой.

EMenuError

Exception

Ошибка при работе программы с меню. Например, при добавлении элемента с идентификатором, который уже определен в меню

EMCIDiviceError

Exception

Ошибка возникла в медиаплейере

EMethodNotFound

EFilerError

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

EMonthCalError

ECommon-Calendar-Error

Возбуждается компонентом класса TMonthcal-endar при попытке ввода неправильной даты

EOleCtrlError

Exception

Программа не может установить связь с элементом ActiveX

EOleError

Exception

Низкоуровневая ошибка в технологии OLE

EOleException

EOleSysError

Программа использует неверный OLE-интерфейс

EOleRegistration Error

EOleError

Ошибка регистрации OLE-объекта в реестре Windows

EOleSysError

EOleError

Возникает при неправильном выполнении команды OLE автоматизации

EOutlineError

Exception

Возникает при ошибке доступа к компоненту класса ToutLine

EOutOfMemory

EHeap-Exception

Эта ошибка возникает, когда программа запрашивает слишком большой для данной конфигурации Windows объем памяти

EOutOfResource

EOutOfMemory

Программа требует от Windows дескриптор окна, но Windows исчерпала лимит дескрипторов

EOverflow

EMatchError

Результат операций с плавающей точкой слишком велик, чтобы уместиться в регистрах сопроцессора

EPackageError

Exception

Возникает при ошибке доступа к пакету

EParserError

Exception

Ошибка преобразования текста в двоичные данные при чтении из потока

EPrinter

Exception

Windows сообщила программе об ошибке принтера

EPrivilege

Exception

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

EPropertyConvert Error

Exception

Ошибка при чтении или записи значения свойства

EPropertyError

Exception

Ошибка доступа к свойству при чтении или записи

EPropReadOnly

Exception

Программа пытается присвоить значение свойству, из которого можно только читать (при использовании технологии OLE)

EPropWriteOnly

Exception

Программа пытается прочитать свойство, предназначенное только для записи

ERangeError

EIntError

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

EReadError

EFilerError

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

EReconcileError

EData-baseError

Ошибка обновления данных в TclientDataset

ERegistry-Exception

Exception

Ошибка, связанная с операцией над реестром Windows

EResNotFound

Exception

Программа не может найти указанный ресурс в файле ресурсов

ESocketConnec-tionError

Exception

Ошибка связана с работой с сокетами Windows

ESocketError

Exception

Ошибка связана с работой с сокетами Windows

EStackOverflow

Exception

Исчерпан объем выделенного программе стека

EStreamError

Exception

Любая ошибка при работе с потоком данных

EStringListError

Exception

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

EThread

Exception

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

ETreeViewError

Exception

Указан неверный индекс при обращении к

TtreeView

EUnderflow

EMatchError

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

EUnsupportedTypeE rror

Exception

Выбран недопустимый тип поля в качестве измерения в кубе решений

EUpdateError

Exception

Ошибка обновления провайдерского набора данных

EVariantError

Exception

Ошибка при работе с типом Variant: недопустимое приведение типов; недопустимая операция; обращение -к скалярной переменной как к варианту-массиву; индекс варианта-массива выходит из допустимых значений

EWin32Error

Exception

Ошибочное обращение к API-функции Windows. Свойство Message содержит номер ошибки и связанное с ней сообщение

EWriteError

EFilerError

Ошибка записи в поток данных

EZeroDivide

EMatchError

Вещественное деление на ноль

 Важно помнить, что ищется самый первый из, возможно, нескольких обработчиков, класс которого способен обрабатывать данное исключение. Если, например, в списке первым стоит EAbort, который может обработать любое исключение, ни один из стоящих за ним обработчиков никогда не получит управления. Точно так же, если указан обработчик для класса EintError, за ним бесполезно размещать Обработчики EDivByZero, ERangeError или EIntOverflow:

try

except

// He имеет смысла делать так:

on EintError do .....;

on ERangeError do .....;

on EDivByZero do .....;

// Надо так:

on ERangeError do .....;

on EDivByZero do .....;

on EintError do .....;

end;

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

on EObject: EClassName do .....;

Для стандартных классов такой прием фактически позволяет использовать единственное строковое свойство Message со стандартным сообщением об ошибке, которое получают все наследники класса Exception. Исключение составляет класс EInOutError, в котором для программиста может представлять интерес целочисленное свойство ErrorCode с кодом ошибки ввода/вывода.

Например:

Reset(F) ;

while not EOF(F) do begin

end;

CloseFile(F) ;

except

on E: EInOutError do

ShowMessage('При выполнении файловой операции возникла'+ ' ошибка №'+ IntToStr(E.ErrorCode));

end;

16.1.4. Вызов исключения

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

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

raise EInOutError.Create('Ощибка!') ;

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

16.1.5. Создание собственного класса

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

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

Создадим новый класс EIntCheckError и будем возбуждать исключение этого класса при обнаружении ошибки в данных:

type

EIntCheckError = class(EAbort)

end;

var

F: TextFile;

S: String;

k: Integer;

begin

try

// Готовимся к работе: открываем файл AssignFile(F, FileName);

Reset(F); // Здесь возможна ошибка EinOutError // Цикл ввода-контроля-преобразования while not EOF(F) do begin

// Вводим символы очередного числа

ReadLn(F,S);// Здесь возможна ошибка EinOutError

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

k := StrToInt(S); // Здесь возможна ошибка EConvertError

// Проверяем число

if k < 0 then

raise EIntCheckError.Create("Отрицательное число');

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

..... // Здесь возможна ошибка EIntError

// Вновь проверяем число

if k <= 0 then

raise EIntCheckError.Create('He положительное число');

end;

except

on E: EIntCheckError do

ShowMessage(E.Message) ;

on EInOutError do

ShowMessage('Некорректная файловая операция');

on EConvertError do

ShowMessage('Ошибка в записи числа');

on EIntError do

ShowMessage('Ошибка преобразования');

end;

end;

В этом примере создается класс EIntCheckError, который ничем, кроме названия, не отличается от своего родителя EAbort. В реальной программе потомок обычно расширяет набор полей (свойств) своего родителя или перекрывает его методы; приведенный пример лишь иллюстрирует, что делать это необязательно. При неудачной проверке операторами

raise EIntCheckError.Create('Отрицательное число') ;

и

raise EIntCheckError.Create('Ошибка преобразования');

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

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

16.2. КЛАСС TLIST - СПИСКИ

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

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

Свойства класса:

property List: pPointerList;

Возвращает указатель на массив элементов списка

property Capacity: Integers;

Содержит количество элементов массива указателей списка. Всегда больше Count. Если при добавлении очередного элемента Count стало равно Capacity, происходит автоматическое расширение списка на 16 элементов

property Count: Integer;

Количество элементов списка. Это свойство изменяется при добавлении или удалении элемента

property Items(Index: Integer): Pointers;

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

 

Тип pPointerList определен следующим образом:

type

pPointerList = PPointerList;

TPointerList = array [0..MaxListSize] of Pointer;

Константа MaxListSize для Delphi 1 ограничена значением 16379 элементов. Для старших версий Delphi она ограничивается доступной памятью.

Следует учесть, что свойство count определяет количество помещенных в список элементов, в то время как capacity - текущую емкость списка. Если при добавлении очередного элемента обнаруживается, что емкость списка исчерпана, происходит наращивание емкости на фиксированную величину (для count < 5 - на 4 элемента, для 4 < count < 8 - на 8, для Count > 7 - на 16). При этом сначала резервируется память для размещения расширенного массива указателей, затем в нее копируется содержимое старого массива, после чего старый массив указателей уничтожается (занимаемая им память возвращается Windows).

Если вы заранее знаете, сколько элементов необходимо поместить :в список, установите в начале работы нужное значение в свойство Саpacity - это снизит непроизводительные затраты времени на расширение списка.

Методы класса:

function Add(Item:

Pointer): Integer; procedure Clear;

Добавляет элемент item в конец ci;

вращает его индекс Очищает список, удаляя из него вес Не освобождает память, связанную удаленным элементом. Устанавлив: ства Count и Capacity значение 0

procedure Delete(Index:Integer);

Удаляет из списка элемент с индекс все элементы, расположенные за удаляемым, смещаются на одну позицию вверх

class procedure Er

ror(const Msg: Stringy; Data: Integer); virtual;

Возбуждает исключение ElistErr метрами Msg и Data  

procedure Ex

change (Indexl, Index2:Integer) ;

Меняет местами элементы с индексами index1 иI ndex2

function Expand: TList; function Extract(Item: Pointer): Pointer;

Расширяет массив, увеличивая Capacity Удаляет из списка элемент Item

function First: Pointer;

Возвращает указатель на самый первый элемент списка

function IndexOf(Item:Pointer): Integer;

Отыскивает в списке элемент Item и возвращает его индекс

procedure Insert(Index:

Integer; Item: Pointer) ; 

 

 

 

Вставляет элемент Item в позицию Index списка: новый элемент получает индекс Index, все элементы с индексами Index и больше увеличивают свой индекс на 1. При необходимости расширяет список

function Last: Pointer;

 

Возвращает указатель на последний элемент

списка

procedure Move(Curlndex,

Newlndex: Integers;  

Перемещает элемент в списке с позиции CurIndex в позицию Newlndex. Все элементы старого списка с индексами от Curlndex-1 до Newlndex уменьшают свой индекс на 1

procedure Pack;

 

Упаковывает список: удаляет пустые элементы в конце массива индексов

function Remove(Item:

Pointer): Integer;

Отыскивает в списке элемент Item и удаляет его

procedure Sort(Compare: rListSortCompare);

Сортирует коллекцию с помощью функции Compare

Методы Add и insert получают указатель на вставляемый элемент. Чтобы воспользоваться ими, программист должен сам разместить в куче данные и получить соответствующий указатель. Точно так же методы Delete, Remove и Сlear не уничтожают распределенные в памяти данные, которые программист должен, если это необходимо, уничтожить сам.

Например:

var

List: TList;

Item: Pointer;

Value: AnyType;

begin

List := TList.Create; // Создаем список

Item := New(Value); // Размещаем в куче данные

List.Add(Item); // Добавляем элемент к списку .....

List.Remove(Item); // Удаляем элемент из списка

Dispose(Item); // Удаляем его из кучи

List.Free; // Удаляем ненужный список

end;

Метод sort сортирует список по критерию, устанавливаемому функцией compary. Тип TListSortCompare определен следующим образом:

TListSortCompare = function(Iteml, Item2: Pointer): Integer;

Таким образом, функция compare получает указатели на два элемента списка. Результат сравнения:

Критерий сравнения данных устанавливается программистом и реализуется в функции Compare.

В следующем примере в список List помещается 20 случайных вещественных чисел, равномерно распределенных в диапазоне 0...1.

Список сортируется по возрастанию чисел и отображается в компоненте mmOutput (многострочный редактор из учебной формы fmExample).

type

PDouble = ^Double;

Function Comp(Iteml, Item2:. Pointer): Integer;

// С помощью этой функции реализуется сортировка чисел

begin

if PDouble(Iteml)^ < PDouble(Item2)^ then

Result := -1 else

if PDouble(Iteml^ > PDouble (Item2) ^ then

Result := 1 else

Result := 0 end;

procedure TfmExample.bbRunClick(Sender: TObject);

// Обработчик нажатий кнопки bbRun выполняет основную работу

var

k: Integer;

List: TList;

pD: PDouble;

begin

List := TList.Create; // Создаем список

for k := 1 to 20 do // Наполняем его

begin

New(pD); // Резервируем память

pD^ := Random; // Помещаем в нее случайное число

List.Add(pD); // Добавляем к списку

end/List. Sort (Comp) ; // Сортируем список по возрастанию mmOutput.Lines.Clear;

{ Очищаем редактор mmOutput. В следующем цикле наполняем mmOutput и уничтожаем элементы List }

for k := 0 to List.Count-1 do

begin

pD := List[k]; // Очередное число из списка

mmOutput. Lines .Add (FloatToStr (pD^ );

{Помещаем в mmOutput}

Dispose(pD) // Уничтожаем число

end;

List.Free; // Уничтожаем список

end;

16.3. КЛАССЫ TSTRINGS И TSTRINGLIST -НАБОРЫ СТРОК И ОБЪЕКТОВ

16.3.1. TStrings

Абстрактный класс TStrings инкапсулирует поля и методы для работы с наборами строк. От него порождены многочисленные специализированные потомки, обслуживающие наборы строк в таких компонентах, как TComboBox, TListBox, TRichEdit и др. Эти классы

(TComboBoxStrings, TListBoxStrings, TRichEditStrings и др.) объявляются в разделах Implementation соответствующих модулей (Stdctris, Сomctris и др.) и поэтому скрыты от браузера Delphi и не включены в Help-службу. Единственным доступным наследником TStrings является TStringList - полнофункциональный класс общего назначения.

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

Свойства класса:

property Capacity: Integer;

Текущая емкость набора строк

property CommaText: String;

Служит для установки или получения всего набора строк в виде единой строки с кавычками и запятыми (см. ниже пояснения) Текущее количество строк в наборе.

property Count: Integer;

Интерпретирует все строки списка в виде одной длинной строки

property DelimitedText: String;

Каждая строка окружается символами QuoteChar и отделяется от соседней символом Delimiter

property Delimiter: Char;

Символ для выделения строк в свойстве DelimitedText

property Names[Index: Integer] : String;

Для строки с индексом Index возвращает часть Name, если это строка вида Name=Value, в противном случае возвращает пустую строку

property Objects[Index: Integer] : TObject;

Открывает доступ к объекту, связанному со строкой с индексом Index

property QuoteChar: Char;

Символ для разделения строк в свойстве DelimitedText

property Strings[Index: Integer] : String;

Открывает доступ к строке с индексом Index

property StringsAdapter: TStringsAdapter;

Это свойство используется только при разработке компонентов, отвечающих стандарту Act iv

property Text: String;

Интерпретирует набор строк в виде одной длинной строки с разделителями eoln между отдельными строками набо

property Values[const Name: String]: String;

По части Name отыскивает в наборе и возвращает часть Value для строк вида

Name=Valu

Набор строк технически реализуется подобно TList - в виде массива указателей. Свойство Capacity показывает текущую длину этого массива, а свойство count - количество элементов, занятых в нем. Если при добавлении очередного элемента capacity окажется меньше count, происходит автоматическое расширение массива. При этом в динамической памяти резервируется место для размещения Capacity + 16 указателей, в новый массив переписывается содержимое старого массива, после чего старый массив уничтожается. Если вам известно количество элементов в создаваемом наборе строк, имеет смысл заранее нужным образом установить свойство capacity, чтобы сократить непроизводительные расходы на многократные расширения массива указателей.

Свойство commaText интерпретирует содержимое набора строк в виде одной длинной строки с элементами вида "первая строка", "вторая строка", "третья строка" и т. д. (каждая строка набора заключается в двойные кавычки и отделяется от соседней строки запятой; если в строке встречается символ “"”, он удваивается). Свойство Text интерпретирует содержимое набора в виде одной длинной строки с элементами, разделенными стандартным признаком eoln (#13#10).

Свойства Names И Values обрабатывают строки вида Name=Value.

Такие строки широко используются в различных файлах инициации, например, в файле win. ini. Методы класса:

function Add(const S: String) : Integer;

function AddObject(const S:String; aObject: TObject):Integer;

Добавляет строку в набор данных и возвращает ее индекс добавляет строку и объект в набор данных

procedure AddStrings( Strings: TStrings);

Добавляет к текущему набору новый набор строк

procedure Append(const S:String) ;

То же, что Add, но не возвращает индекс вставленной строки

procedure Assign(Source: TPersistent) ;

  

 

 

Уничтожает прежний набор строк и загружает из Source новый набор. В случае не удачи возникает исключение EсоnvertError

procedure BeginUpdate;

Устанавливает флаг начала обновления.

До вызова EndUpdate блокируется сортировка строк, что ускоряет процесс много кратного изменения данных

procedure Clear;

Очищает набор данных и освобождает связанную с ним память

procedure Delete(Index: Integer) ;

 

Уничтожает элемент набора с индексом Index и освобождает связанную с ним память

procedure EndUpdate;

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

function Equals(Strings:TStrings): Boolean;

Сравнивает построчно текущий набор данных с набором Strings и возвращает True, если наборы идентичны

procedure Exchange(Indexl,

Index3: Integers;

 

 

Меняет местами строки с индексами Index1 и Index2. Если объект сортируется,

вызывает исключение EstringListError

function GetObject(Index:Integer): TObject; virtual;

Возвращает объект, связанный со строкой Index

function GetText: PChar;

 

Загружает строки из единого массива, в котором они отделяются друг от друга признаком EOLN

function IndexOf(const S:

String): Integer;

Для строки S возвращает ее индекс или -1, если такой строки в наборе нет

function IndexOfName(const Name: String): Integer;

Возвращает индекс первой строки вида Name=Value, в которой часть Name совпадает с параметром обращения

function IndexOfObject (aObject: TObject): Integer;

Для объекта aObj ect возвращает индекс строки или -1, если такого объекта в наборе нет

procedure Insert(Index: In

teger; const S: Strings;

Вставляет строку в набор и присваивает ей индекс Index

procedure InsertObject (Index: Integer; const S: String; aObject: TObject)

Вставляет строку и объект в набор и присваивает им индекс Index

procedure LoadFromFile (const FileName: Strings;

Загружает набор из файла 

 

procedure LoadFromStream (Stream: TStream) ;

Загружает набор из потока 

 

procedure Move(Curlndex, Nwlndex: Integers;

Перемещает строку из положения Curlndex в положение Newlndex

procedure SaveToFile(const FileName: Strings;

Сохраняет набор в файле 

 

procedure SaveToStream (Stream: TStream) ;

Сохраняет набор в потоке

procedure SetText(Text:

PChar);

Выгружает строки в единый массив, в ко

тором они отделяются друг от друга при знаком EOLN

Методы Add, Append, Insert, Clear И Т. П. В классе TStrings - абстрактные. Связано это с тем, что класс инкапсулирует их и таким

образом делает доступными во всех потомках, но он при этом не накладывает никаких ограничений на то, как располагаются в памяти строки и объекты. Каждый потомок решает эту задачу наиболее удобным для него способом. Например, потомок TStringList располагает строки и объекты в общей динамической памяти, для чего перекрывает все абстрактные методы своего родителя. Замечу, что? если вы создадите экземпляр класса TStrings с помощью его конструктора Create, компилятор предупредит вас о том, что этот экземпляр содержит абстрактные методы, так что пользоваться им нужно лишь в исключительных случаях.

16.3.2. TStringList

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

property Duplicates: TDu-plicates;

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

property Sorted: Boolean;

Признак необходимости сортировки строк в алфавитном порядке

property OnChange: TNotifyEvent;;

Определяет реакцию на изменение набора строк. Возникает после последнего изменения

property OnChanging: TNotifyEvent

Определяет реакцию на изменение набора строк. Возникает до очередного изменения

function Find(const S:String; var Index: Integer) : Boolean;

Ищет в наборе строку S и в случае успеха в параметре index возвращает ее индекс

 При sorted = True строки набора автоматически сортируются в алфавитном порядке. При этом свойство Duplicates разрешает коллизию, связанную с добавлением в набор строки, идентичной одной из ранее вставленных. Если Duplicates = dulgnore, идентичная строка отвергается и программе ничего об этом не сообщается; если Duplicates = duError, возбуждается исключение EListError; значение Duplicates = duAccept разрешает вставлять в набор сколько угодно идентичных строк.

Следует заметить, что сортировка строк в Windows 32 осуществляется не совсем так, как это происходит в MS-DOS или Windows 3-х. Чтобы убедиться в этом, советую прогнать следующий вариант учебной программы (см. рис. 16.1). В ней обработчик события OnClick кнопки bbRun создает два списка строк - List1и List2. Список List1 первоначально способен сортировать строки (в его свойство sorted устанавливается значение True). Затем в цикле от 32 до 255 (О...31 - это коды служебных символов) происходит наполнение обоих списков: в List1 помещается строка, содержащая символ и его код, а в List2 - наоборот, сначала код, а затем сам символ. Поскольку символьное представление кода дополняется до трех символов (в двузначных числах слева добавляется ведущий ноль), строки в списке List1 окажутся отсортированными по символу, а в списке List2 - по коду символа. Затем оба списка для наглядности объединяются и помещаются в редактор mmOutput.

procedure TfmExample.bbRunClick (Sender: TObject);

var

k: Byte;

Listi, List2: TStringList;

S: String;

begin

// Создаем два списка

List1 := TStringList.Create;

Listi.Sorted := True;

List2 := TStringList.Create;

// Цикл наполнения списков монотонно возрастающими кодами

for k := 32 to 255 do

begin

S := IntToStr(k); // Код символа

if k<100 then

S := '0' + S;// Двузначное число дополняем ведущим нулем

// Формируем строку Listi из символа + табуляция + код символа Listi.Add(Char(k) + #9 + S) ;

// Формируем строку List2 из кода + табуляция + символ

List2.Add(S + #9 + Char(k));

end;

// Объединяем оба списка

Listi.Sorted := False; // Отключаем сортировку List1

for k := 0 to List1.Count-1 do

List1[k] := List1[k] + #9 + List2[k];

// Переносим результат в mmOutput mmOutput.Lines.Assign(List1);

// Уничтожаем List1 и List2 Listi.Free;

List2.Free;

end;

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

mmOutput.Lines.Assign(List1);

все строки List1 помещаются в свойство Lines редактора mmOutput. Это возможно из-за того, что mmOutput. Lines и Listi имеют общего родителя TStrings, который умеет копировать родственные наборы строк с помощью своего метода Assign. Во-вторых, перед объединением строк мы отключили свойство Sorted списка Listi. если этого не сделать, любое изменение строк в отсортированном списке привело бы к исключентю EStringListError.

Рис. 16.1. Окно прогона программы сравнения сортировки строк для Windows 32 и Windows З.х

Как видим, строки в 32-разрядных Windows сортируются не с учетом внутреннего кода символа, а с учетом “смысла”: в начале располагаются все знаки препинания и разного рода “мусор”, затем идут цифры, буквы латинского алфавита и символы кириллицы. Буквы упорядочены парами - сначала заглавная, за ней строчная - и не идут сплошным массивом. “Виновником” такой сортировки является API-функция AnsiCompareText, К который обращается метод TStringList. Quicksort. Если вам понадобится отсортировать так, как это принято в MS-DOS или Windows З.х, т. е. по коду символа, проделайте следующее.

  1. Разыщите в каталоге source | Rtl | common исходный файл classes . pas и скопируйте его в ваш рабочий каталог (в предыдущих версиях этот файл располагался в каталоге Source | vci). Если вы не найдете этого файла, вы не сможете изменить сортировку, т. к. в классе TStringList метод Quicksort, отвечающий за сортировку, определен в секции private и, следовательно, недоступен наследникам.
  2. В раздел implementation этого модуля сразу после предложения Uses поместите такое описание функции AnsiCompareText:

Function AnsiCompareText(SI, S2: String): Integer;

begin

if S1 < S2 then Result := -1

else if SI == S2 then Result := 0

else Result := 1

end;

3. С помощью опции Project | Add To Project добавьте измененный модуль classes к вашему проекту и сделайте новый прогон программы. Теперь оба столбца будут идентичны.

16.4. ГРАФИЧЕСКИЙ ИНСТРУМЕНТАРИЙ

Богатство изобразительных возможностей Windows связано с так называемым дескриптором контекста графического устройства DC (Device Context) и тремя входящими в него инструментами - шрифтом, пером и кистью. В Delphi созданы специализированные классы-надстройки, существенно упрощающие использование графических инструментов Windows: для контекста - класс TCanvas, для шрифта - TFont, для пера - ТРеп и для кисти - TBrush.

Связанные с этими классами объекты автоматически создаются для всех видимых элементов и становятся доступны программе

через свойства Canvas, Font, Pen и Brush.

16.4.1. Класс TFont

С помощью класса TFont создается объект-шрифт для любого графического устройства (экрана, принтера, плоттера и т. п.).

Свойства класса:

property Color: TColor;

Цвет шрифта

property Charset: TFontCharSet;

Набор символов. Для русскоязычных программ это свойство обычно имеет значение DEFAULTCHARSET или russian charset. Используйте значение OEMCHARSET для отображения текста MS-DOS (альтернативная кодировка)

property FontAdapter: IChangeNotifier;

Поставляет информацию о шрифте в компоненты ActveX  

property Handle:hFont;

Дескриптор шрифта. Используется при непосредственном обращении к API-функциям Windows

property Height: Integer;

Высота шрифта в пикселях экрана

property Name: TFontName;

Имя шрифта. По умолчанию имеет значение MS

Sans Serif

property Pitch: TFontPitch;

 

 

Определяет способ расположения букв в тексте: значение fpFixed задает моноширинный текст, при котором каждая буква имеет одинаковую ширину; значение fpVariabel определяет ропорциональный текст, при котором ширина буквы зависит от ее начертания; fpDefault определяет ширину, принятую для текущего шрифта

property PixelPerInch: Integer;

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

property Size: Integer;

Высота шрифта в пунктах (1/72 дюйма). Изменение этого свойства автоматически изменяет свойство Height и наоборот.

property Style: TFontStyles;

Стиль шрифта. Может принимать значение как комбинация следующих признаков: fsBold (жирный), fsltalic (курсив), fsUnderline (подчеркнутый), fsStrikeOut (перечеркнутый)

Для некоторых случаев может оказаться полезным метод

procedure Assign(Source: TPersistent);

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

16.4.2. Класс ТРеп

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

property Color: TColor;

Цвет вычерчиваемых пером линий

property Handle: Inte

ger;

Дескриптор пера. Используется при непосредственном обращении к API-функциям Windows

property Mode: TPenMode;

Определяет способ взаимодействия линий с фоном (см.ниже)

property Style: TPenStyle;

Определяет стиль линий (см. рис. 16.2). Учитывается только для толщины линий 1 пиксель. Для толстых линий стиль всегда ps Solid (сплошная)

property Width: Integer;

Толщина линий в пикселях экрана

Свойство Mode может принимать одно из следующих значений:

 pmBlack

Линии всегда черные. Свойства Color и Style игнорируются

pmWhite

Линии всегда белые. Свойства Color и Style игнорируются

pmNop

Цвет фона не меняется (линии не видны)

pmNot

Инверсия цвета фона. Свойства Color и Style игнорируются

pmCopy

Цвет линий определяется свойством Color пера

pmNotCopy

Инверсия цвета пера. Свойство Style игнорируется

pmMergePenNot

Комбинация цвета пера и инверсионного цвета фона

pmMaskPenNot

Комбинация общих цветов для пера и инверсионного цвета фона. Свойство Style игнорируется

pmMergeNotPen

Комбинация инверсионного цвета пера и фона

pmMaskNotPen 

Комбинация общих цветов для инверсионного цвета пера и фона. Свойство Style игнорируется

pmMerge

Комбинация цветов пера и фона

pmNotMerge

Инверсия цветов пера и фона. Свойство Style игнорируется

pmMask

Общие цвета пера и фона

pmNotMask

Инверсия общих цветов пера и фона

pmXor

Объединение цветов пера и фона операцией XOR

pinNotXor

Инверсия объединения цветов пера и фона операцией XOR

 Стили линий показаны на рис. 16.2.

Рис. 16.2. Стили линий

16.4.3. Класс TBrush

Объекты класса TBrush (кисти) служат для заполнения внутреннего пространства замкнутых фигур.

Свойства класса:

property Bitmap: TBitmap;

Содержит растровое изображение, которое будет использоваться кистью для заполнения. Если это свойство определено, свойства Color и Style игнорируются

property Color: TColor;

Цвет кисти

property Handle: Integer;

Дескриптор кисти. Используется при не посредственном обращении к API- функциям Windows

property Style: TBrushStyle;

Стиль кисти (см. рис. 16.3)

 

16.4.4. Класс TCanvas

Этот класс создает “канву”, на которой можно рисовать чертежными инструментами - пером, кистью и шрифтом. Объекты класса ЕСаnvas автоматически создаются для всех видимых компонентов, которые должны уметь нарисовать себя. Они инкапсулируют объекты Font, pen, Brush, а также многочисленные методы, использующие эти объекты.

Свойства класса:

property Brush: TBrush; property ClipRect: TRect;

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

property CopyMode: TCopyMode;

Устанавливает способ взаимодействия растрового изображения с цветом фона

property Font: TFont;

Объект-шрифт

property Handle: Integer;

Дескриптор канвы. Используется при непосредственном обращении к API-функциям Windows

property LockCount: In

tegers;

 

 

Счетчик блокировок канвы. Увеличивается на единицу при каждом обращении к методу Lock и уменьшается на единицу при обращении к Unlock

property Pen: TPen;

Объект-перо

property PenPos:TPoint;

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

property Pixels[X,Y:Integer]: TColor;

Массив пикселей канвы

 

 

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

cmBlackness

Заполняет область рисования черным цветом

cmDestInvert

Заполняет область рисования инверсным цветом фона

cmMergeCopy

Объединяет изображение на канве и копируемое изображение операцией and

cmMergePaint

Объединяет изображение на канве и копируемое изображение операцией OR

cmNotSrcCopy

Копирует на канву инверсное изображение источника

cmNotSrcErase 

Объединяет изображение на канве и копируемое изображение операцией OR и инвертирует полученное

cmPatCopy

Копирует образец источника

cmPatInvert

 

 

Комбинирует образец источника с изображением на канве с

помощью операции XOR

cmPatPaint

Комбинирует изображение источника с его образцом с помощью операции OR, затем полученное объединяется с изображением на канве также с помощью OR

cmSrcAnd 

 

Объединяет изображение источника и канвы с помощью операции AND

cmSrcCopy

Копирует изображение источника на канву

cmSrcErase

Инвертирует изображение на канве и объединяет результат с изображением источника операцией AND

cmSrcInvert

Объединяет изображение на канве и источник операцией XOR

cmSrcPaint

Объединяет изображение на канве и источник операцией OR

cmWhitness

Заполняет область рисования белым цветом

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

Методы класса:

procedure Arc(X1, Y1, X2, Y2, ХЗ, Y3, Х4, Y4: Integer);

Чертит дугу эллипса в охватывающем прямоугольнике (Xl,Yl)-(X2,Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (ХЗ, Y3), а конец - на пересечении с лучом из центра в точку (Х4, Y4). Дуга чертится против часовой стрелки (см. рис. 16.4, а)

procedure BrushCopy

(const Dest: TRect;

Bitmap: TBitmap;

const Source: TRect;

Color: TColor);

Копирует часть изображения Source на участок канвы Dest. Color указывает цвет в Dest, который должен заменяться на цвет кисти канвы. Метод введен для совместимости с ранними версиями Delphi. Вместо него следует пользоваться классом Т Image List

procedure Chord(XI,

Yl, X2, Y2, X3, Y3,

Чертит сегмент эллипса в охватывающем прямоуголь нике (XI, У1) - (X2, Y2). Начало дуги сегмента лежит на пересечении эллипса и луча, проведенного из его центра в точку (X3, Y3), а конец - на пересечении с лучом из центра в точку (X4, Y4). Дуга сегмента чертится против часовой стрелки, а начальная и конечная точки дуги соединяются прямой (см. рис. 16.4, б)

procedure CopyRect (Dest: TRect; Canvas: TCanvas;Source: TRect) ;

Копирует изображение Source канвы Canvas в участок Dest текущей канвы. При этом разнообразные специальные эффекты достигаются с помощью свойства CopyMode

procedure Draw(X, Y:Integer; Graphic:TGraphic) ;

Осуществляет прорисовку графического объекта Graphic так, чтобы левый верхний угол объекта расположился в точке (X, Y)

procedure DrawFocusRect (const Rect:TRect) ;

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

procedure Ellipse(XI, Yl, X2,

Y2: Integers;

Чертит эллипс в охватывающем прямоугольнике (XI, Yl) - (X2, Y2). Заполняет внутреннее пространство эллипса текущей кистью

procedure FillRect(const Rect: TRect);

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

procedure Flood-

Fill(X, Y: Integer;

Color: TColor; Fill Style: TFillStyle);

Производит заливку канвы текущей кистью. Залив

ка начинается с точки (X, Y) и распространяется вовсе стороны от нее. Если FillStyle=fsSurface, заливка распространяется на все соседние точки с цветом Color. Если FillStyle=fsBorder, наоборот, заливка прекращается на точках с этим цветом

procedure FrameRect(const Rect: TRect);

Очерчивает границы прямоугольника Rect текущей кистью толщиной в 1 пиксель без заполнения внутренней части прямоугольника

procedure LineTo(X,

Y: Integer);

Чертит линию от текущего положения пера до точки

(X,Y)

procedure Lock;

 

 

Блокирует канву в многопоточных приложениях для предотвращения использования канвы в других по токах команд

procedure MoveTo(X,Y: Integer);

Перемещает перо в положение (X, Y) без вычерчивания линий

procedure Pie(XI,

Yl, X2, Y2, X3, Y3,

X4, Y4: Longint);

 

 

 

Рисует сектор эллипса в охватывающем прямоугольнике (XI, Yl) - (X2, Y2). Начало дуги лежит на пересечении эллипса и луча, проведенного из его центра в точку (X3, Y3), а конец - на пересечении случом из центра в точку (X4, Y4). Дуга чертится против часовой стрелки. Начало и конец дуги соединяются прямыми с ее центром (см. рис. 16.4, в)

procedure Polygon.

(Points: array of TPoint) ; 

 

Вычерчивает пером многоугольник по точкам, заданным в массиве Points. Конечная точка соединяется с начальной и многоугольник заполняется кистью. Без заполнения используйте - Polyline

procedure Polyline

(Points: array of

TPoint);

Вычерчивает пером ломаную прямую по точкам, заданным в массиве Points

 

 

procedure Rectan

gle(XI, Yl, X2, Y2:

Integer);

Вычерчивает и заполняет прямоугольник (XI, Yl) -S(X2, Y2). Для вычерчивания без заполнения используйте FrameRect или Polyline

procedure Refresh;

Устанавливает в канве умалчиваемые шрифт и кисть

procedure RoundRect(Xl, Yl, X2,Y2, X3, Y3: Inte ger) ;

Вычерчивает и заполняет прямоугольник (XI,Yl)- (X2, Y2) со скругленными углами. Прямоугольник (XI, Yl) - (X3, Y3) определяет дугу эллипса для округления углов (см. рис. 16.4, г)

procedure Stretch Draw (const Rect:TRect; Graphic:

TGraphic );

Вычерчивает и при необходимости масштабирует графический объект Graphic так, чтобы он полностью занял прямоугольник Rect

 

function TextExtent

(const Text:String): TSize;

Возвращает ширину и высоту прямоугольника, охватывающего текстовую строку Text

 

function TextHeight

(const Text: String): Integer;

Возвращает высоту прямоугольника, охватывающего текстовую строку Text 

 

procedure TextOut(X,

Y: Integer; const Text: String) ;

Выводит текстовую строку Text так, чтобы левый

верхний угол прямоугольника, охватывающего текст, располагался в точке (X, Y)

procedure TextRect(Rect: TRect; X, Y:

Integer; const Text:

String) ;

Выводит текстовую строку Text так, чтобы левый верхний угол прямоугольника, охватывающего текст, располагался в точке (х, Y). Если при этом какая-либо часть надписи выходит из границ прямоугольника Rect, она отсекается и не будет видна

function TextWidth

(const Text:String): Integer;

Возвращает ширину прямоугольника, охватывающего текстовую строку Text 

 

function TryLock:

Boolean; 

Пытается заблокировать канву. Если она не заблокирована другим потоком команд, - True, в противном случае ничего не делает и возвращает False

procedure Unlock;

Уменьшает на 1 счетчик блокировок канвы

 

16.4.4.1. Дополнительные возможности API-функций

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

Некоторые текстовые функции API

type TLogFont = record

If Height: Integer;

IfWidth: Integer; IfEs-

capment: Integer; IfOri-

entation: Integer; IfWeight: Integer;

If Italic: Byte; IfUnder-

line: Byte; IfStrikeOut:

Byte; IfCharSet: Byte;

IfOutPrecision: Byte;

IfClipPrecision: Byte;

IfQuality: Byte; IfPitchAndFamily: Byte;

1fFaceName: PChar ;

end;

function CreateFont(Font:

TLogFont): hFont;

Создает новый шрифт на основе данных в параметре Font. Назначение полей структуры TLogFont см. после таблицы

 

 

 

 

 function DrawText(DC:hDC; pText: PChar; varRect:'TRect; Format:UINT): Integer;

В прямоугольнике Rect выводит многострочный текст, на который указывает pText. Параметр Format используется для форматирования (см.ниже)

function ExtTextOut(DC:

hDC; X, Y: Integer; Op

tions: Integer; Rect:

TRect; pText: PChar;

Count: Integer; PX: PInteger): Bool; 

 

Выводит текст с нестандартными меж сим вольными расстояниями: X, Y - верхняя левая точка текста; Options - параметр, управляющий выводом (см. ниже); Rect - ограничивающий прямоугольник; pText - указатель на строку вывода; Count - количество выводимых символов; px - указатель на массивцелочисленных значений, определяющих межсимвольные расстояния: 1-й параметр - расстояние от 1-го до 2-го символа; 2-й параметр- расстояние от 2-го до 3-го символа и т. д.; если какой-то параметр равен 0, используется умалчиваемое межсимвольное расстояние

function GetBkColor(DC:hDC): TColor;

Возвращает цвет фона

 

function GetBkMode(DC:hDC) : Integer;

 

Возвращает режим прорисовки фона: Opaque -

фон прорисовывается заново при выводе текста; Transparent - фон не прорисовывается

function GetTextAlign(DC:

hDC) : Integer;

Возвращает выравнивание текста

 

function GetTextCharacterExtra(DC: hDC): Integer;

Возвращает межсимвольное расстояние

function SetBkColor(DC:hDC; Color: TColor): TColor;

Устанавливает новый цвет фона и возвращает старый, если обращение успешно

function SetBkMode(DC:hDC; Mode: Integer): Integer;

Устанавливает новый режим прорисовки фона и возвращает старый, если операция успешна

function SetTextAlign(DC:

hDC; Flags: Integer): Integer;

Устанавливает новое выравнивание текста и

возвращает старое, если вызов успешен  

function SetTextCharacterExtra(DC: hDC; CharExtra: Integer): Integer;

Устанавливает новое межсимвольное расстояние и возвращает старое, если вызов успешен

 Отдельные поля структуры TLogFont для функции CreateFontIndirect имеют следующий смысл:

if Height - высота шрифта в пунктах (1 пункт = 1/72 дюйма); если больше 0, определяет высоту “знакоместа” (с учетом выступающих над заглавным символом элементов в буквах Е, И), если меньше нуля - высоту “чистого” символа, если 0 - высоту выбирает Windows;

if width - средняя ширина символа; если 0, ширину устанавливает Windows;

ifEscapment - угол наклона базовой линии текста в десятых долях градуса относительно горизонтального направления; положительные значения - поворот по часовой стрелке; в Windows 95/98 совпадает СIfOrientation;

iforientation - угол наклона символов по отношению к базовой линии; в Windows NT для шрифтов True Type может отличаться от ifEscapment; для этого следует установить режим устройства отображения равным gm_Advanced (по умолчанию устанавливается gm_Compatible);

IfWeight - плотность шрифта (fm_DontCare=0 - плотность выбирает Windows; fm_Thin=100 - очень тонкий шрифт;

fm_ExtraLight=200 - очень светлый; fm_Light=300 - светлый;

fm_Normal=400 - нормальный; fm_Medium=500 - утолщенный;

fm_SemiBold=600 - полужирный; fm_Bcld=700 - жирный;

fm_ExtraBold=800 - усиленный; fm_Heavy=900 - тяжелый);

ifitalic, ifunderiine, ifStrikeOut - ненулевое значение означает соответственно наклонный, перечеркнутый и подчеркнутый шрифт;

IfCharSet - Набор символов (ANSI_CharSet=0, Default_CharSet=l;

Symbol_CharSet=2;ShiftJis_CharSet=128;OEM_CharSet=255);

ifoutprecision - точность представления шрифта; рекомендуется out__TT_prec (выбирает True Type и векторные шрифты, если есть несколько разновидностей одноименных шрифтов) или out_TT_oniy_prec (только True Type);

ifciipprecision - определяет точность отсечения надписи границами области прорисовки (Clip_Character_Precis, Clip_Embedded, Clip_Mask, Clip_TT_Always, Clip_Default_Precis - рекомендуется, Clip_LH_Angles, Clip_Stroke_Precis);

IfQuality - Определяет качество прорисовки (Default_Quality, Draft_Quality,Proof_Quality);

ifpitchAndFamily - в четырех младших разрядах указывается тип шрифта, в четырех старших ~ его семейство;

IfFaceName - имя гарнитуры шрифта.

Вот как реализован этот пример.

procedure TForm1.FormPaint(Sender:TObject) ;

var

X: Integer;

LF: TLogFont;

Fnt: HFont;

const

Text = 'Лучшая в мире система программирования';

begin

// Определяем параметры нового шрифта

FillChar(LF, SizeOf(LF), 0) ;

with LF do

begin

If Height := 20;

If Weight := fw_Normal;

If Underline := 1;

If Escapement := 450;

StrPCopy(lfFaceName, 'Courier New Cyr');

end;

with Forml.Canvas do

begin

// Создаем шрифт

Fnt := CreateFontIndirect(LF) ;

// Присваиваем его дескриптор шрифту канвы

Font.Handle := Fnt;

// Выводим текст под углом +45 градусов

Text0ut(0, 300, Text);

X := TextWidth(Text) ;

DeleteObject(Fnt); // Удаляем ненужный шрифт

// Изменяем параметры шрифта

with LF do

begin

If Height := 90;

IfEscapement := -900;

IfWeight := fw_Heavy;

StrPCopy(LF.lfFaceName, 'Arial Cyr');

end;

Fnt := CreateFontIndirect(LF); // Создаем новый шрифт

Font.Handle := Fnt;

Font.Color := clRed;

// Выводим с наклоном -90 градусов

TextOut(X-10, 10, 'Delphi 5');

DeleteObject(Fnt); // Удаляем ненужный шрифт

end;

end;

 

Параметр Format функции DrawText может содержать один или несколько следующих флагов.

Возможные значения параметра Format функции DrawText

dt Bottom

Текст прижимается к нижней части области Rect

dtCalcRect

Разрешает динамически изменять размеры области Rect

dtCenter

Текст центрируется по горизонтали

dtEditControl

 

 

 

 

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

dtExpandTabs

Символы табуляции заменяются пробелами

dt ExternalLeading

В высоту строки включается высота межстрочного интервала

dtLeft

Текст прижимается к левой части области Rect

dtNoClip

Текст не отсекается границами Rect

dtNoPrefix

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

dtRight

Текст прижимается к правой части области Rect

dt SingleLine

Весь текст выводится единственной строкой, символы EOLN игнорируются

dt TabsStop

Символы табуляции не заменяются пробелами

dtTop

Текст прижимается к верхней части области R'ect

dtVCenter

Текст центрируется по вертикали

dtWordBreak

Разрешает переход на новую строку при достижении пра вой границы Rect; разрыв строки - на границе слова; символы EOLN также переводят вывод на следующую строку

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

Параметр options функции ExtTextOut может быть комбинацией следующих значений:

eto_Сlipped - текст будет отсекаться границами Rect;

eto_Gliph_index - блокирует обработку языковым драйвером;

eto_opaque - фон перерисовывается заново;

eto_RTLReading - вывод для чтения справа налево. При выводе текста стандартными методами rcanvas всегда заново прорисовывается фон символов (цвет фона возвращает функция GetBkColor). Если цвет фона символов отличается от фона канвы, вывод сопровождается неприятными побочными эффектами. Если в предыдущем примере для формы оставить стандартный цвет ciFaceBtn, окно вывода будет таким, как на рис. 16.6. Конечно, с помощью функции setBkcoior можно установить цвет формы совпадающим с цветом канвы, однако это не всегда возможно. Ясно, что установить переменный цвет функцией SetBkcoior невозможно. Более того, заглавные надписи программ Setup.exe также традиционно выводятся утолщенным наклонным шрифтом Times New Roman белыми буквами с черной тенью. Реализовать такой эффект достаточно просто: нужно вообще отказаться от прорисовки фона, установив с помощью функции SetBkMode режим Transparent, и вывести надпись дважды: первый раз черным цветом, а второй -белым, сместив вторую надпись немного влево и вверх относительно первой. Следующий листинг иллюстрирует сказанное.

procedure TFormI.FormPaint(Sender: TObject);

var

Y: Integer;

Blue: Byte;

const

Text = 'Фон для программы Setup.exe';

begin

with Forml.Canvas do

begin

for Y := 0 to Forml.Height-1 do

begin

// Уменьшаем интенсивность цвета с ростом ординаты Y

Blue := Round($FF*(Forml.Height-Y)/Forml.Height);

Pen.Color := RGB(0, 0, Blue); // Формируем цвет

MoveTo(0, Y); // Чертим линию

LineTo(Forml.Width-1, Y) ;

end; //for Y := 0 to Forml.Height-1 do Font.Size := 32;

Font.Style := [fsBold, fsltalic, fsUnderline];

Font.Name := 'Times New Roman";

// Это обращение накладывает текст на фон SetBkMode(Handle, Transparent);

// Сначала выводим тень надписи

Font.Color := clBlack;

Text0ut(40, 30, Text);

// Теперь саму надпись Font.Color := clWhite;

Text0ut(36, 26, Text)

end; //with Forml.Canvas do end;

Замечу, что любая заливка фона, в том числе его прорисовка, реализуется с помощью кисти. Поэтому отказаться от прорисовки фона текста можно, если установить стиль кисти bsciear: вместо

SetBkMode(Handle, Transparent);

можно использовать

Brush.Style := bsClear;

16.4.4.2. Области

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

uses

Forms,

Unit1 in 'Unitl.pas' {Formi}, Windows; // Этот модуль экспортирует функции API

{$R *.RES} var

Rgn: Cardinal;

begin

// Сначала создаем обычное окно:

Application.CreateForm(Tform1, Form1);

with Formi.Canvas, Formi do

begin

// Теперь создаем в нем эллиптическую область

Rgn := CreateEllipticRgn(0, 0, Width, Height);

// и показываем ее вместо окна

SetWindowRgn(Handle, Rgn, True);

end;

Application.Run;

end.

Ниже перечислены основные функции для работы с областями.

Работа с областями

function CombineRgn(DestRgn, Rgnl, Rgn2: Cardinal; Com-bineMode: Ineger): Integer;

Объединяет две области в одну: DestRgn - дескриптор результирующей области; Rgnl, Rgn2 -дескрипторы объединяемых областей; CombineMode - параметр, указывающий способ объединения; этот параметр, а также возвращаемое значение описаны ниже

function CreateEllipticRgn(XI, Yl, X2, Y2: Integer) : Cardinal;

Создает эллиптическую область

function CreatePoly-gonRgn(var Points; Count, PolyFillMode: Integer): Cardinal;

Создает область в виде многоугольника: Points - массив координат всех вершин многоугольника; Count - количество вершин; PolyFillMode -режим заполнения многоугольника (см. ниже)

function CreateRectRgn(XI,

Yl, X2, Y2: Integer): Cardi

nal;

Создает прямоугольную область

 

 

 

 

function CreateRoundRectRgn(Xl, Yl, X2, Y2, X3, Y3: Integer): Cardinal;

Создает область со скругленными углами; параметры обращения иллюстрирует

function EqualRgn(Rgnl,Rgn2: Cardibal): Bool;

Проверяет эквивалентность двух областей

function FillRgn(DC, Rgn,Brush: Cardinal): Bool;

Закрашивает область кистью: DC - дескриптор канвы окна; Rgn - дескриптор области; Brush - дескриптор кисти

function FrameRgn(DC, Rgn,Brush: Cardinal; Width,Height: Integer): Bool;

Обводит (но не заполняет) область кистью высотой Height и шириной Width пикселей

function InvertRgn(DC, Rgn:Cardinal): Bool;

Инвертирует все пиксели внутри области

function OffsetRgn(DC, Rgn:Cardinal; dX, dY: Integer):Integer;

Смещает область на dX по горизонтали и на dY по вертикали

function PaintRgn(DC, Rgn:Cardinal): Bool;

Закрашивает область текущей кистью 

 

function PtInRegion(Rgn:Cardinal; X, Y: Integer):Bool;

Проверяет принадлежность точки (X,Y) области Rgn 

function RectInRegion(Rgn:Cardinal; Rect: TRect):Bool;

Проверяет принадлежность прямоугольника Rect области Rgn 

 

function SetWindowRgn(DC,Rgn: Cardina; Redraw: Bool): Integer; 

 

 

Показывает принадлежащую окну с канвой DC область Rgn вместо окна; Redraw- разрешает/запрещает перерисовку области; все не вошедшие в область части окна не показываются (см. ниже)

Параметр combineMode в функции combineRgn может значения:

rgn_and - включает в результат общие части областей; rgn_copy -копирует область Rgnl В DestRgn; RGN_DIFF - исключает из Rgnl часть, принадлежащую Rgn2; rgn_or - объединяет области; rgn_xor -включает в результат только те части областей, которые не принадлежат им одновременно. Область DestRgn уже должна существовать и иметь размеры, достаточные для размещения объединенной области. При обращении к функции createPoiygonRgn параметр Poly FillMode может иметь значения: Alternate И Winding. В первом случае заполнение многоугольника идет последовательно между двумя близлежащими его сторонами: сначала между 1-й и 2-й, затем между 2-й и 3-й и т. д. В режиме winding заполняется все внутреннее пространство области.

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

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls;

tуре

Tform1 = class(TForm)

PaintBoxl: TPaintBox;

PaintBox2: TPaintBox;

Button1: TButton;

procedure ButtonlClick(Sender: TObject);

private

{ Private declarations } public

{ Public declarations } end;

var

Form1: Tform1;

implernentation

$R *.DFM)

procedure TFormI.ButtonlClick(Sender: TObject);

var

Rgnl, Rgn2: Cardinal;

const

Points: array [1..5, 1..2] of Integer =

C(91, 0), (160, 183), (0, 70), (183, 70), (23, 183));

begin

Rgn1 := CreatePolygonRgn(Points, 5, Alternate);

Rgn2 := CreatePolygonRgn(Points, 5, Winding);

with PaintBoxl.Canvas do begin

Brush.Color := clBlack;

FillRgn(Handle, Rgnl, Brush.Handle);

end;

with PaintBox2.Canvas do begin

Brush.Color := clBlack;

FillRgn(Handle, Rgn2, Brush.Handle) ;

end;

end;

end.

Обратите внимание: прорисовка и объединение областей доступны, только если области видны в окне (для создания предыдущего рисунка нельзя, например, использовать обработчик формы OnActivate).

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

16.4.5. Классы TGraphic и TPicture

Важное место в графическом инструментарии Delphi занимают классы TGraphic и TPicture.

TGraphic - это абстрактный класс, инкапсулирующий общие свойства и методы трех своих потомков: пиктограммы (TIcon), метафайла (TMetafile) и растрового изображения (TBitmap). Общей особенностью потомков TGraphic является то, что обычно они сохраняются в файлах определенного формата. Пиктограммы представляют собой небольшие растровые изображения, снабженные специальными средствами, регулирующими их прозрачность. Для файлов пиктограмм обычно используется расширение ico. Метафайл - это изображение, построенное на графическом устройстве с помощью специальных команд, которые сохраняются в файле с расширением wmf или emf. Растровые изображения - это произвольные графические изображения в файлах со стандартным расширением bmp.

Свойства класса TGraphic:

property Empty: Boolean;

Содержит True, если с объектом не связано графическое изображение

property Height: Integer;

Содержит высоту изображения в пикселях

property Modified: Boolean;

Содержит True, если графический объект изменялся

property Palette: HPALETTE;

Содержит цветовую палитру графического объекта

property PaletteModified: Boolean;

Содержит True, если менялась цветовая палитра графического объекта

property Transparent: Boolean;

Содержит True, если объект прозрачен для фона, на котором он изображен

property Width: Integer;

Содержит ширину изображения в пикселях

Методы Класса TGraphic:

 procedure LoadFromClipooardFormat (AFormat: Word;AData: THandle; APalette:HPALETTE) ; 

 

Ищет в буфере межпрограммного обмена Clipboard зарегистрированный формат AFormat и, если формат найден, загружает из буфера изображение AData и его палитру Apalette

procedure LoadFromrile(const FileName:String) ;

Загружает изображение из файла FileName 

procedure LoadFromStream(Stream: TStream);

Загружает изображение из потока данных Stream

procedure SaveToClipboardFormat (var AFormat: Word;var AData: THandle; varAPalette: HPALETTE);

Помещает графическое изображение Adata и его цветовую палитру APalette в буфер межпрограммного обмена в формате Af ormat

procedure SaveToFile(constFileName: Strings;

Сохраняет изображение в файле FileName  

procedure SavePoStream(Stream: TStream);

Сохраняет изображение в потоке Stream

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

Свойства класса TPicture:

property Bitmap: TBitmap;

Интерпретирует графический объект как растровое изображение

property Graphic: TGraphic;

Содержит графический объект

property Height: Integer;

Содержит высоту изображения в пикселях

property Icon: TIcon;

Интерпретирует графический объект как пиктограмму

property Metafile: TMetafile;

Интерпретирует графический объект как метафайл

property width: Integer;

Содержит ширину изображения в пикселях

Методы класса TPicture:

procedure Assign(Source: TPersistent) ;

Связывает собственный графический объект Graphic с объектом Source

procedure LoadFromClipboardFormat

(AFormat: Word;AData: THandle;

APalette:HPALETTE) ; 

Ищет в буфере межпрограммного обмена Clipboard зарегистрированный формат AFormat и, если формат найден, загружает из буфера изображение AData и его палитру APalette

procedure LoadFromFile(const FileName:String);

Загружает изображение из файла FileName 

class procedure Register; ClipboardFormat(AFormat:

Word; AGraphicClass: TGraphicClass) ;

Используется для регистрации в Clipboard

нового формата изображения

 

 

class procedure Register; FileFormat (const AExtension, ADescription: String; AGraphicClass: Tgraphic Class) ;

Используется для регистрации нового файлового формата

 

class procedure RegisterFileFormatRes(const AExtension: String; ADescriptionResID: Integer; Agraphic Class: TGraphicClass);

Используется для регистрации нового формата ресурсного файла  

procedure SaveToClipboardFormat (var AFormat: Word; var AData: THandle; varAPalette: HPALETTE);

Помещает графическое изображение AData и его цветовую палитру APalette в буфер межпрограммного обмена в формате AFormat

procedure SaveToFile(const

FileName: Strings;

Сохраняет изображение в файле FileName

 

 

class function SupportsClipboardFormat(AFormat: Word): Boolean;

Возвращает True, если формат Aformat зарегистрирован в буфере межпрограммного обмена Clipboard  

class procedure UnregisterGraphicClass(AClass:TGraphicClass);

Делает недоступными любые графические объекты класса Aclass