Справочник по компонентам Delphi. Часть 1

ОГЛАВЛЕНИЕ

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


 

Класс TList

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

property Items[Index: Integer]: Pointer;Возвращает указатель на содержимое элемента списка с индексом Index. Это свойство является векторным свойством, принимаемым по умолчанию, и его имя можно при записи опускать (см. раздел "Свойства").
property Count: Integer;

Определяет число элементов в списке.

property Capacity: Integer; Определяет максимальное число элементов в списке. Оно может изменяться как явно — пользователем, так и при добавлении элементов в список, в том случае, когда Count>=Capacity. Максимальная емкость списка — 16380 элементов.

Управляют списком следующие методы:

function Add(Item: Pointer): Integer;Добавляет в конец списка элемент, который будет равен Item (т. е. указывать на те же данные).
function Remove(Item: Pointer): Integer;Удаляет из списка элемент, который равен Item.
procedure Insert(Index: Integer; Item: Pointer) ;Вставляет элемент, равный Item, перед элементом с индексом Index.
procedure Delete(Index: Integer);Удаляет из списка элемент с индексом Index.
procedure Clear; Очищает список, устанавливая величины Count и Capacity в 0.
procedure Exchange(Indexl, Index2: Integer);Меняет местами элементы списка с индексами Indexl и Index2.
function Expand: TList; При соблюдении равенства Count=Capacity расширяет список. При емкости списка менее пяти элементов, он по умолчанию расширяется на четыре элемента, при пяти-восьми — на восемь, более восьми — на шестнадцать.
function First: Pointer; function Last: Pointer;Возвращают значения первого п последнего (с индек­сом Count-1) элементов списка соответственно.
function IndexOf(Item: Pointer): Integer;Возвращает индекс элемента, равного Item.
procedure Move(CurIndex, Newlndex: Integer) ;Перемещает элемент списка с положения Curlndex в положение Newlndex.
procedure Pack; Упаковывает список, сдвигая элементы к началу на пустующие места.

Наконец, если приведенных методов почему-либо недостаточно, то свойство

 property List: pPointerList; pPointerList = ^TPointerList;
TPointerList = array[0..65520 div SizeOf(Pointer)] of Pointer;

возвращает указатель непосредственно на список указателен ((ко) означает, что свойство доступно только для чтения).


 

Класс TStrings

Многофункциональный класс, предназначенный для хранения текстовых строк и связанных с ними объектов (любых потомков TObject). TStrings — абстракт­ный класс; он только описывает методы работы с наборами строк и сопут­ствующих им объектов, но как именно они хранятся, на его уровне не опре­делено. Его потомки очень многочисленны; они играют основную роль в компонентах-списках (TListBox, TComboBox), редакторе (TMemo) и других. Так что вам чаще всего придется иметь дело с TStrings как со свойством одного из компонентов. В дальнейшем экземпляры этого класса и порожденных от него классов мы-будем называть наборами строк. Для создания собственных наборов строк вне компонентов предназначен потомок TStrings — TStringList, который будет рассмотрен ниже.

К строкам и объектам соответственно можно получить доступ через свойства:

 property Strings[Index: Integer]: string; property Objects[Index: Integer]: TObject; 

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

 property Count: Integer; 

Класс TStrings также предназначен для хранения пар вида 'параметр=значение', например, в файлах инициализации (.INI). Эту возможность реализует следу­ющее свойство:

 property Values[const Name: string]: string; 

При обращении к этому свойству для чтения ищется строка, содержащая подстроку (параметр) Name и символ '='. Если она найдена, возвращается то, что находится в этой строке после '='. Если нет, ValuesfName] равно пустой строке. При записи: если строка, содержащая параметр Name, найдена — ее значение после '=' заменяется новым значением, если нет — строка добавля­ется. Если существующему параметру присваивается пустая строка (Values[Name] := ";), то он удаляется из набора строк.

Методы класса приведены в таблице:

procedure BeginUpdate; procedure EndUpdate;Пара процедур, которые устанавливают и сбрасывают флаг обновления набора. Между ними, для ускорения работы, нужно заключать все операции по копи­рованию, удалению и т. д. большого количества элементов.
procedure Clear;

Осуществляет полную очистку набора.

procedure Insert(Index: Integer; const S: string);Вставляет строку S под индексом Index.

procedure Delete(Index: Integer);

Удаляет строку с индексом Index.

function IndexOf(const S: string): Integer;Возвращает индекс (номер в наборе) строки S. Если она не найдена, функция возвращает -1.
function IndexOfObject(AObject: TObject): Integer;Возвращает индекс объекта в наборе. В случае неудачи возвращает -1.
function Equals(Strings: TStrings): Boolean;Сравнивает строки вызвавшего его объекта со строками объекта Strings и возвращает True в случае равенства (сравниваются число строк и все строки попарно).
function Add(const S: string): Integer-Добавляет строку S в конец набора и в случае успеха возвращает присвоенный ей индекс (он должен быть равен значению Count до добавления строки).
function AddObject(const S: string; AObject: TObject): Integer;Добавляет строку в паре с объектом. Возвращает то же, что и метод Add.
procedure Exchange(Indexl, Index2: Integer);Меняет местами пары строка+объект с индексами Indexl и Index2.
procedure Move(Curlndex, Newlndex: Integer);Перемещает пару строка+объект с позиции Curlndex в позицию Newlndex.
procedure InsertObject(Index: Integer; const S: string; AObject: TObject);Вставляет объект AObject и соответствую­щую ему строку S в набор под индексом Index.

Шесть методов предназначены для экспорта/импорта наборов строк:

а) в поток:

procedure LoadFromStream(Stream: TStream); procedure SaveToStream(Stream: TStream); 

б) в файл (создавая поток и вызывая два предыдущих метода):

procedure LoadFrornFile (const FileName: strings-procedure SaveToFile(const FileName: string);

в) в данные в формате текстового редактора (подряд расположенные строки, оканчивающиеся парой символов CR/LF (16-ричные коды SOD/SOA)).

procedure AddScrings(Strings: TStrings! ;Добавляет в конец набора другой набор Strings.
procedure Assign!Source: T'Persisier-t l ;Уничтожает прежнее содержимое набора и подставляет вместо него Source, если источник имеет тип TStrings. В противном случае возникает исключительная ситуа­ция EConvertError.

При этом метод

function GetText: PChar; 

выгружает строки в единый массив, где они разделены парами символов CR/LF; в конце такого массива ставится нулевой байт. Размер массива не может превышать 65520 байт; поэтому строки выгружаются до тех пор, пока их суммарная длина не превосходит этого значения.

Метод

procedure SetText(Text: PChar); 

читает строки из массива Text.Строки в массиве должны быть отделены друг от друга парой символов CR/LF; допускается и один символ LF (16-ричный код $ОА). Символы с кодами 0, $lA(+) воспринимаются как конец текста. При этом прежнее содержимое набора уничтожается.


 

Класс TStringlist

Этот класс объединяет в себе свойства TStrings и TList простейшим способом — указатель на объект и соответствующая строка объединяются в запись, ука­затель на которую хранится в списке. В классе переопределены многие вирту­альные методы TStrings: Add, Clear, Delete, Exchange, IndexOf, Insert; он явля­ется полностью функциональным и вы можете создавать экземпляры TStringList в своей программе для работы с наборами строк и объектов (помимо тех, которые уже есть в компонентах).

Кроме унаследованных от TStrings, определены дополнительно полезные ме­тоды и свойства:

function Find(const S: string; var Index: Integer): Boolean;Метод ищет в наборе строку S и в случае успеха возвращает результат True, а в параметре Index — ее индекс.
property Sorted: Boolean; Свойство — признак отсортированности элементов (сортировка осуществляется через посимвольное сравнение строк). Установка Sort := True вызывает процедуру сортировки, которую можно вызвать и явно при помощи метода:
procedure Sort; Попытка добавить или вставить элемент в отсортированный список вызывает исклю­чительную ситуацию EListError; в этом случае до выполнения действия свойству Sorted нужно присвоить значение False.
property Duplicates: TDuplicates; TDuplicates = (duplgnore, dupAccept, dupError);Свойство определяет, что происходит при попытке добавить в список дубликат уже имеющейся строки: duplgnore — добав­ление игнорируется (отклоняется); dupError — добавление приводит к созда­нию исключительной ситуации EListError; dupAccept — одинаковые строки разре­шены. В этом случае при поиске в неотсор­тированном списке не определено, которая из строк будет найдена первой.
property OnChange: TNotifyEvent; property OnChanging: TNotifyEvent;Два свойства, предусмотренные для определения пользователем своей реакции на изменение данных. Событие OnChanging вызывается во многих рассмотренных выше методах до внесения первого изменения, OnChange — после последнего.

 

Класс TStream

Класс TStream не нов для библиотек фирмы Borland; он и его потомки на­зываются потоками. Со времен появления в библиотеке Turbo Vision он не претерпел существенных изменений, но теперь потоки стали обязательными составными частями там, где нужно прочитать или передать во внешний источник какую-либо информацию.

TStream "является абстрактной моделью совокупности данных, обладающей двумя свойствами — длиной Size и положением текущего элемента Position:

 property Position: Longint; (Ro) property Size: Longint; 

От TStream порождены дочерние объекты, позволяющие пользоваться мета­форой потока при работе с файлами, блоками памяти и т. п. Так, в модуле CLASSES описаны классы TMemoryStream и TFileStream.

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

 function Read(var Buffer; Count: Longint): Longint; virtual; abstract; 
function Writetconst Buffer; Count: Longint): Longint; virtual; abstract;

Метод

function Seek(0ffset: Longint; Origin: Word): Longint; virtual; abstract; 

позиционирует поток. В зависимости от значения параметра Origin новая позиция выбирается так:

0 - новая позиция равна Offset;

1 - текущая позиция смещается на Offset байт;

2 - новая позиция находится на Offset байт от конца потока.

Методы

procedure ReadBuffer(var Buffer; Count: Longint); 
procedure WriteBuffer(const Buffer; Count: Longint);

представляют собой оболочки над Read/Write, вызывающие в случае неудачи операции исключительные ситуации EReadError/EWriteError.

Метод

function CopyFromfSource: TStream; Count: Longint): Longint; 

дописывает к потоку Count байт из потока Source, начиная с его текущей позиции. Если значение Count равно нулю, то дописывается весь поток Source с его начала.

Основным отличием реализации TStream в VCL является введение ряда мето­дов, обеспечивающих чтение и запись компонентов в потоки. Они полезны на уровне разработчика новых компонентов и здесь подробно не рассматрива­ются:

 function ReadComponent(Instance: TComponent): TComponent; 
function ReadComponentRes(Instance: TComponent): TComponent;
procedure WriteComponent(Instance: TComponent);
procedure WriteComponentRes (const ResName: string; Instance: TComponent);
procedure ReadResHeader; 


 

Справочник по функциям Delphi

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

  • функции, содержащиеся в модуле SYSTEM и составляющие неотъем­лемую часть самого языка Паскаль. Они не изменялись фирмой Borland уже на протяжении многих версий, и приводить их здесь нет необ­ходимости;
  • некоторые функции, относящиеся к тем или иным компонентам и описан­ные в главе 3 вместе с ними.

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

 

Функции работы со строками

В Delphi вынужденно уживаются два стандарта работы со строковыми дан­ными. Первый из них, когда длина строки записывается в ее первом байте, традиционен для Паскаля. Ему соответствует тип данных string. Другой подразумевает, что строка заканчивается нулевым символом. Такие строки имеют тип PChar и применяются в основном при обращении к функциям API Windows.

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

Первая таблица содержит функции для работы с типом string. Все перечисленные в ней функции находятся в модуле SYSUTILS.

function NewStrtconst S: String): PString;Создает копию строки S и возвращает указатель на нее.
procedure DisposeStr(P: PString) ;Уничтожает строку, на которую указывает Р.
procedure AssignStr(var P: PString; const S: strings-Уничтожает строку, на которую указывает Р и затем присваивает ему адрес созданной копии строки S.
procedure AppendStrfvar Dest: string; const S: string);Добавляет строку S в конец строки Dest.
function Uppercase(const S: string): string;Преобразует символы 'a'..'z' в строке S к верхнему регистру.
function LowerCase(const S: string): string;Преобразует символы 'A'..'Z' в строке S к нижнему регистру.
function CompareStr(const SI, S2: string): Integer;Сравнивает две строки S1 и S2 с учетом регистра символов. Возвращаемое значение равно 0 в случае равенства строк или разности кодов пары первых несовпадающих символов.
function CompareText(const SI, S2: string): Integer;Сравнивает две строки без учета регистра сим­волов.
function AnsiUpperCase(const S: string): string;Преобразует символы в строке к верхнему регистру с учетом языкового драйвера.
function AnsiLowerCase(const S: string) : string;Преобразует символы в строке к нижнему регистру с учетом языкового драйвера.
function AnsiCompareStr(const SI, S2: string): Integer;Сравнивает две строки с использованием языкового драйвера и с учетом регистра символов.
function AnsiCompareText(const SI, S2 : string) : Integer;Сравнивает две строки с использованием языкового драйвера и без учета регистра символов.
function IsValidldent(const Ident: string): Boolean;Возвращает True, если строка Ident может служить идентификатором в программе на Object Pascal (т. е. содержит только буквы и цифры, причем первый символ — буква).
function IntToStr(Value: Longint): string;Преобразует целое число в строку.
function IntToHex(Value: Longint; Digits: Integer): s t r ing ;Преобразует целое число в строку с его шестнадцатиричным представлением.
function StrToInt(const S: string): Longint;Преобразует строку в целое число. При ошибке возникает исключительная ситуация EConvertError.
function StrToIntDef(const S: string; Default; Longint): Longint ;Работает как StrToInt, но при ошибке возвращает значение Default.
function LoadStr(Ident: Word) : string;Загружает строку с индексом Ident из ресурсов приложения.
function FmtLoadStr(Ident: Word; const Args: array of const): string;Загружает строку с индексом Ident из ресурсов приложения с форматированием (см. описание функции Format).

Следующая таблица содержит функции для работы со строками типа PChar (также находятся в модуле SYSUTILS):

function StrLIComp(Strl, Str2: PChar; MaxLen: Cardinal) : Integer;Работает как StrLComp, но без учета регистра символов.
function StrScantStr: PChar; Chr: Char) : PChar;Отыскивает первое вхождение символа Chr в строку Str и возвращает указатель на него или nil в случае отстутствия.
function StrRScanfStr: PChar; Chr: Char) : PChar;Работает как StrScan, но отыскивается последнее вхождение Chr.
function StrPos(Strl, Str2: PChar) : PChar;Отыскивает первое вхождение строки Str2 в строку Strl и возвращает указатель на нее или nil в случае отстутствия.
function StrUpperfStr: PChar) : PChar;Преобразует строку к верхнему регистру.
function StrLower(Str: PChar): PChar;Преобразует строку к нижнему регистру.
function StrPaslStr: PChar): String;Преобразует строку Str в строку типа string.
function StrAlloc(Size: Cardinal): PChar;Размещает в куче памяти новую строку размером Size и возвращает указатель на нее.
function StrBufSize(Str: PChar): Cardinal;Возвращает размер блока памяти, выделенного для строки при помощи функции StrAlloc.
function StrNewfStr: PChar): PChar ;Размещает в куче памяти копню строки Str и возвращает указатель на нее.
procedure StrDispose(Str: PChar);Уничтожает строку, размещенную при помощи StrAlloc или StrNew.
function StrLenfStr: PChar): Возвращает число символов в строке Str (без учета завершающего нулевого).
function StrEndfStr: PChar): PChar;Возвращает указатель на завершающий нулевой символ строки Str.
function StrMove(Dest, Source: PChar; Count: Cardinal): PChar;Копирует из строки Source в строку Dest ровно Count символов, причем строки могут пере­крываться.
function StrCopy(Dest, Source: PChar): PChar;Копирует Source в Dest и возвращает указатель на Dest.
function StrECopy(Dest, Source: PChar): PChar;Копирует Source в Dest и возвращает указатель на завершающий символ Dest.
function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar;Работает как StrCopy, но копирует не более MaxLen символов.
function StrPCopy(Dest: PChar; const Source: String): PChar;Копирует строку Source (типа string) в Dest и возвращает указатель на Dest.
function StrPLCopy(Dest: PChar; const Source: string; MaxLen: Cardinal): PChar;Работает как StrPCopy, но копирует не более MaxLen символов.
function StrCat(Dest, Source: PChar): PChar;Дописывает Source к концу Dest и возвращает указатель на Dest.
function StrLCatfDest, Source: PChar; MaxLen: Cardinal) : PChar;Работает как StrCat, но копирует не более MaxLen-StrLen(Dest) символов.
function StrCoirip(Strl, Str2: PChar): Integer;Сравнивает две строки (посимвольно). Возвра­щает значение: <0 — при Strl 0 — при Strl =Str2, >0 — при Strl >Str2.
function StrIComp(Strl, Str2: PChar): Integer;Работает как StrComp, но без учета регистра символов.
function StrLComp(Strl, Str2: PChar; MaxLen: Cardinal): Integer;Работает как StrComp, но сравнение происходит на протяжении не более чем MaxLen символов.
 
 

Функции работы с файлами

Эта часть библиотеки претерпела изменения в Delphi. К функциям, которые работали с файлом через файловую переменную, добавились функции, рабо­тающие с дескриптором файла. Они рассматривают файл как двоичный; с текстовыми файлами нужно работать "по старинке".

Файловые функции, описанные в модуле SYSUTILS, приведены в таблице:

function File0pen(const FileName: string; Mode: Word) : Integer;Открывает существующий FileName файл в режиме Mode (см. примеч. 1). Значение, возвра­щаемое в случае успеха, — дескриптор открытого файла. В противном случае — код ошибки DOS.
function FileCreate(const PileName: string): Integer;Создает файл с именем FileName. Возвращает то же, что и FileOpen.
function FileRead(Handle: Integer; var Buffer; Count: Longint): Longint;Считывает из файла с дескриптором Handle Count байт в буфер Buffer. Возвращает число реально прочитанных байт или -1 при ошибке.
function FileWrite(Handle: Integer; const Buffer;Записывает в файл с дескриптором Handle Count байт из буфера Buffer. Возвращает число реально записанных байт или -1 при ошибке.
function FileSeek(Handle: Integer; Offset: Longint; Origin: Integer): Longint;Позиционирует файл с дескриптором Handle в новое положение. При Origin = 1,2,3 положение смещается на Offset байт от начала файла, текущей позиции и конца файла соответственно. Возвращает новое положение или -1 при ошибке.
procedure FileClose(Handle: Закрывает файл с дескриптором Handle.
function FileAge(const Возвращает значения даты и времени создания файла или -1, если файл не существует.
function FileExists(const Возвращает True если файл FileName существует к найден.
function FindFirst(const Path: string; Attr: Integer; var SearchRec: TSearchRec): Integer;Ищет первый файл, удовлетворяющий маске поиска, заданной в Path и с атрибутами Attr (см. примеч. 2). В случае успеха заполняет запись SearchRec (см. примеч. 3) и возвращает 0, иначе возвращает код ошибки DOS.
function FindNext(var SearchRec: TSearchRec): Integer;Продолжает процесс поиска файлов, удовлетворяющих маске поиска. Параметр SearchRec должен быть заполнен при помощи FindFirst. Возвращает 0, если очередной файл найден, или код ошибки DOS. Изменяет SearchRec.
procedure FindClose(var Завершает процесс поиска файлов, удовлетворяющих маске поиска.
function FileQetDate(Handle: Integer) : Longint;Возвращает время создания файла с дескриптором Handle (в формате DOS) или -1, если дескриптор недействителен.
procedure FileSetDate(Handle: Integer; Устанавливает время создания файла с дескриптором Handle (в формате DOS).
function FileGetAttr(const FileName: string): Integer;Возвращает атрибуты (см. примеч. 2) файла с именем FileName или код ошибки DOS, если файл не найден.
function FileSetAttrtconst FileName: string; Attr:Устанавливает атрибуты файла с именем FileName.
function DeleteFile(const Уничтожает файл с именем FileName и в случае успеха возвращает True.
function RenameFile(const OldName, NewName: string): Boolean;Переименовывает файл с именем OldName в NewName и возвращает True в случае успеха.
function ChangeFileExt(const FileName, Extension: string): string;Изменяет расширение в имени файла FileName на Extension и возвращает новое значение FileName. Имя файла не изменяется.
function ExtractFilePath(const FileName: string): string;Извлекает из строки с полным именем файла FileName часть, содержащую путь к нему.
function ExtractFileName(const FileName: string): string;Извлекает из строки с полным именем файла FileName часть, содержащую его имя и расширение.
function ExtractFileExt(const FileName: string): string;Извлекает из строки с полным именем файла FileName часть, содержащую его расширение.
function ExpandFileName(const FileName: string): string;Возвращает полное имя файла FileName, добавляя при необходимости путь к нему и переводя все символы в верхний регистр.
function FileSearch(const Name, DirList: string): strings-Производит поиск файла с именем Name в группе каталогов, заданных параметром DirList. Имена каталогов должны отделяться друг от друга точкой с запятой. Возвращает в случае успеха полное имя файла или пустую строку, если файл не найден.
function DiskFree(Drive: Byte): Longint;Возвращает количество в байтах свободного места на заданном диске. Значение параметра Drive: 0 — для текущего диска, 1 — для А, 2 — для В и т. д. Если параметр неверен, функция возвращает -1.
function DiskSize(Drive: Byte): Longint;Возвращает размер диска Drive в байтах. Параметр Drive означает то же, что и в DiskFree.
function FileDateToDateTime(FileDate: Longint): TDateTime;Преобразует дату и время в формате DOS в принятый в Delphi формат TDateTime.
function DateTimeToFileDate(DateTime: TDateTime): Longint;Преобразует дату и время из формата TDateTime в формат DOS.

Примечания:

1. Допустимы следующие режимы открытия файлов:

Режим Значение

Что означает

fmOpenRead $0000

Открыть только для чтения.

fmOpenWrite $0001

Открыть только для записи.

fmOpenReadWrite $0002

Открыть для чтения и записи.

fmShareCompat $0000 Обеспечить совместимость со старой моделью доступа к файлам.
fmShareExclusive $0010

Запретить другим доступ к файлу.

fmShareDenyWrite $0020

Запретить другим запись в файл.

fmShareDenyRead $0030

Запретить другим чтение файла.

fmShareDenyNone $0040

Разрешить другим все виды доступа.

2. Файлы могут иметь следующие атрибуты:

 faReadOnly = $01; 
faHidden = $02;
faSysFile = $04;
faVolumeID = $08;
faDirectory = $10;
faArchive = $20;
faAnyFile = $3F;

3. Формат структуры TSearchRec таков:

TSearchRec = record
    Fill: array[1..21] of Byte;
    Attr: Byte;
    Time: Longint;
    Size: Longint;
    Name: string[12];
end;

Приведем типовой вариант организации групповой обработки файлов при помощи функций FindFirst, FindNext и FindClose. В нем в список добав­ляются имя и длина всех файлов в каталоге с именем CADELPHI:

var 
    SearchRec: TSearchRec;
    I : Integer;
    List : TStringList;
begin
    List := TStringList.Create;
    I := FindFirst('c:\delphi\*.*', faAnyFile, SearchRec);
    while I = 0 do
    begin
        List.Add(Format (' File %s has length %d bytes ', [SearchRec.Name, SearchRec.Size]));
        I := FindNext(SearchRec);
    end;
    FindClose(SearchRec);
    List.Free;
end;

При поиске файла наиболее вероятным является код ошибки -18 (файл не найден).

Процедура FindClose не делает ничего в 16-разрядной версии Delphi, однако будет играть роль в 32-разрядной. Употреблять ее следует из соображений переносимости приложений.

4. Ряд "старых" функций переименованы: это связано с появлением одно­именных методов у объектов. Теперь вместо Assign следует употреблять AssignFile, вместо Close — CloseFile. Старые имена поддерживаются ддя совместимости, но могут вызвать проблемы при одновременном употреб­лении с именами методов.


 

Функции форматирования строк

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

В 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 цифр. Общее число символов в представлении числа определяется полем ргес (по умолчанию 15).
f Фиксиро­ванный Элемент должен быть числом с плавающей точкой. Он преобразуется к виду -ddd.ddd. Поле ргес означает количество цифр после десятичной точки (по умолчанию 2).
д Обобщенный Элемент должен быть числом с плавающей точкой. Он пре­образуется к одному из двух вышеперечисленных видов — выбирается тот, который обеспечивает более короткую запись. Если требуемое число цифр слева от десятичной точки меньше значения поля ширины и само число больше 10" , применяется фиксированный тип. Удаляются все впереди идущие нули, десятичная точка (при возможности), а также символы, разделяющие строку на группы по три цифры.
п m Числовой ДенежныйСовпадает с типом f за исключением того, что после каждой группы из трех цифр ставятся разделительные символы:d,ddd, ddd.ddd....Элемент должен быть числом с плавающей точкой, отража­ющей денежную сумму. Способ ее представления зависит от значений глобальных констант, определенных в модуле SYSUTILS (см. ниже). В свою очередь, они зависят от на­стройки Windows на обозначения, принятые в данной стране.
Р Указатель Элемент должен быть указателем. На выходе он будет пред­ставлять из себя строку шестнадцатиричных цифр видаХХХХ: YYYY, где ХХХХ — селектор, YYYY — смещение.
s Строка Элемент может иметь тип string, PChar или быть отдель­ным символом (Char). Эта строка (или символ) вставляются вместо спецификатора формата. Если присутствует поле ргес и длина строки больше его значения, то она усекается.

Примечания:

1. Форматирующие символы можно задавать как в верхнем, так и в нижнем регистрах.

2. Тип преобразования m (денежный) тесно связан с правилами представления, принятыми в конкретной стране. Для этого в модуле SYSUTILS определены значения ряда типизированных констант, начальные значения которых бе­рутся из секции [Inti] файла WIN.INI. Вы можете изменять их значения в соответствии с вашими потребностями:

Имя и тип

Что означает

CurrencyString: string[7]; Символ (или символы), добавляемые к строке с представлением денежной суммы и обозначающие национальную денежную единицу. Это может быть, например, '$', 'DM' или 'руб'.
CurrencyFormat: Byte; Определяет способ добавления знака денежной единицы к строке. Число 1 будет преобразовано так в зависимости от значения этого параметра: '$!' при 0; '1$' при 1; '$ Г при 2; '1 $' при 3.
NegCurrFormat: Byte; Определяет способ добавления знака денежной единицы и минуса к строке в том случае, если проис­ходит преобразование отрицательного числа (к поло­жительным числам отношения не имеет). Число -1 будет преобразовано так в зависимости от значения этого параметра: '($!)' при 0; '-$!' при 1; '$-1' при 2; '$!-' при 3; '(!$)' при 4; '-!$' при 5; 'i-$' при 6; '!$-' при 7; '-1 $' при 8; '-$ Г при 9; '$ 1-' при 10.
CurrencyDecimals: Byte; Число знаков после запятой в представлении денежных сумм. Например, число 10.15 при разных значениях этого параметра отобразится так: '$10' при 0, '$10.15' при 2, '$10.1500' при 4.
ThousandSeparator: Char; Символ, разделяющий строку на группы по три циф­ры справа налево (разделитель тысяч). Применяется, в частности, в типе преобразования п.
DecimalSeparator: Char;

Символ, отделяющий дробную часть числа от целой.

Мы закончили рассмотрение символа типа преобразования. Рассмотрим осталь­ные составные части спецификатора формата.

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

Поле индекса позволяет динамически изменить последовательность извлечения аргументов из массива. Обычно аргументы извлекаются последовательно, по мере их востребования спецификаторами формата. Поле индекса означает, что следующим нужно извлечь аргумент с данным индексом. Пользуясь индексом, одни и те же аргументы могут быть использованы многократно. Например, вызов Format ( ' %s %s %0 : s %s ', [ 'Yes', 'No' ] ) Даст на выходе строку 'Yes No Yes No'.

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

Поля индекса, ширины и точности могут быть заданы напрямую или косвенно. Первый способ подразумевает явное указание значения поля (например, ' %10 . 5f '). Для косвенного задания значений нужно в соответствующих местах цифры заменить звездочками (например, '%*.*f'). В этом случае вместо звездочек будут подставлены следующие значения из списка аргументов (они обязательно должны быть целыми числами). Например, выражение

Format('Value is %*.*',[10,5, 2.718]);
эквивалентно:
Format('Value is %10.5',[2.718]);

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

procedure FmtStr(var Result: string; const Format: string; const Args: array of const);To же, что и Format, но оформлено в виде процедуры. Результат преобразо­вания возвращается в параметре Result.
function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;Форматирующая строка должна нахо­диться в параметре Format, а резуль­тирующая помещается в буфер Buffer (он должен иметь нужную длину). Функция возвращает указатель на Buffer.
function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar; const Args: array of const) : PChar;Работает как StrFmt, но длина резуль­тирующей строки не будет превышать MaxLen символов.
function FormatBuf(var Buffer; BufLen: Cardinal; const Format; FmtLen: Cardinal; const Args: array of const) : Cardinal;Форматирующая строка находится в бу­фере Format длиной FmtLen, a результирующая — в буфере Buffer длиной BufLen. Функция возвращает число реально помещенных в Buffer символов, причем оно всегда меньше или равно BufLen.
 
 

Функции преобразования чисел с плавающей точкой

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

Первый — самый простой — представлен функцией FloatToStr:

function FloatToStr(Value : Extended): string;

Число, заданное параметром Value, преобразуется в возвращаемую функцией строку. Формат преобразования соответствует типу преобразования g функции Format, причем длина выходной строки принимается равной 15 символам.

Больше возможностей для управления форматом вывода дает функция:

 function PloatToStrF(Value: Extended; Format: TFloatFormat; Precision, Digits: Integer): string; 

Здесь Value — преобразуемое значение, Format — один из предопре­деленных форматов. Хотя этот параметр имеет тип TFloatFormat, он имеет очень много общего с типами преобразований в функции Format (ссылки на них есть в предлагаемой таблице). Параметр Precision задает общее число символов в выходной строке и не должен превышать 7 для фактичес­кого параметра типа Single, 15 — для Double и 18 — для Extended. Digits — это параметр, интерпретируемый в зависимости от значения параметра Format:

ffExponent Научный формат, соответствует типу е. Precision задает общее число символов, Digits — число знаков в показателе экспоненты {0-4).
ffFixed Формат с фиксированной точкой; соответствует типу f. Precision задает общее число символов, Digits — число знаков после запятой (0-18). Если значение Precision мало для представления числа, используется научный формат.
ffGeneral Обобщенный формат, соответствует типу д (см. описание функции Format).
ffNumber Отличается от fTFixed наличием символов-разделителей тысяч (см. тип преобразования п).
ffCurrency Соответствует типу преобразования т. Параметр Digits задает число символов после десятичной точки в выходной строке (0-18).

В случае, когда в функцию переданы значения Value, соответствующие особым случаям сопроцессора ("не-число", плюс и минус бесконечность), она возвра­щает соответственно строки 'NAN', 'INF' и '-INF'.

Наконец, возможность полного управления форматом предоставляет функция FormatFloat:

 function FormatFloat(const Format: string; Value: Extended): string; 

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

0 Поле для цифры. Если форматируемая величина имеет в этой позиции цифру, то вставляется она, в противном случае вставляется 0.
#Поле для цифры. Если форматируемая величина имеет в этой позиции цифру, то вставляется она, в противном случае ничего не вставляется.
 Поле для десятичной точки. Сюда вставляется символ, определенный константой DecimalSeparator.
; Поле для разделителя тысяч. Оно означает, что группы по три цифры, считая влево от десятичной точки, будут разделяться специальным символом (он задан константой ThousandSeparator). Местоположение поля может быть произвольным.
Е+, Е-, е+, е-Признаки представления числа в научном формате. Появление любого из этих аргументов означает, что число будет преобразовано с харак­теристикой и мантиссой. Вставка нулей после такого аргумента позволяет определить ширину мантиссы. Разница между Е+, е+ и Е-, е-в том, что в первых двух случаях ставится "+" при выводе положи­тельных чисел.
'хх' "хх"Символы, заключенные в обычные или двойные кавычки, напрямую включаются в выходную строку.
; Разделяет спецификаторы формата для положительных, отрицательных и нулевых чисел.
Примечания:

1. Число всегда округляется до той точности, которую позволяет заданное программистом количество полей для размещения цифр ('0' и '#').

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

3. Символ ';' позволяет задать три разных формата вывода для чисел с разным знаком. При различном количестве форматов они применяются следующим образом:

  • один: применяется для всех чисел;
  • два: первый применяется для чисел, больших или равных нулю, второй — для отрицательных;
  • три: первьш применяется для положительных, второй — для отрицательных чисел, третий — для нуля.

Если форматы для отрицательных чисел или нуля пусты, применяется формат для положительных чисел.

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

Применение спецификатора иллюстрируется в таблице на примере преобразо­вания четырех чисел:

Спецификатор 1234 -1234 0.5 0
0 1234 -1234 1 0
0.00 1234.00 -1234.00 0.50 0.00
#.##1234 -1234 .5  
#.##0.00 1,234.00 -1,234.00 0.50 0.00
#,##0.00;(#,##0.00) 1,234.00 (1,234.00) 0.50 0.00
#,##0.00;;Zero 1,234.00 -1,234.00 0.50 Zero
О.ОООЕ+00 1.234Е+03 -1.234Е+03 5.000Е-01 О.ОООЕ+00
#.###Е-0 1.234ЕЗ -1.234ЕЗ 5Е-1 ОЕО

Две следующие функции применяют те же правила, что и рассмотренные выше функции, но отличаются параметрами:

function FloatToText(Buffer: PChar; Value: Extended; Format: TFloatFormat; Precision, Digits: Integer) : Integer;Соответствует FloatToStrF, но выходная строка помещается в буфер Buffer (без начальной длины!), а число символов в ней возвращается самой функцией.
function FloatToTextFmt(Buffer: PChar; Value: Extended; Format: PChar): Integer;Соответствует FormatFloat, но выходная строка помещается в буфер Buffer (без начальной длины!), а число символов в ней возвращается самой функцией.
Наконец, процедура:
procedure FloatToDecimal(var Result: TFloatRec; Value: Extended; Precision, Decimals: Integer);Производит подготовительный анализ преобра­зуемого числа, занося в поля записи Result различ­ные его характеристики.

Перейдем к рассмотрению функций преобразования текстовой строки в число. Их две — соответственно для строк типа string и PChar:

 function StrToPloat(const S: string): Extended; 
function TextToFloat(Buffer: PChar; var Value: Extended): Boolean;

Общие правила для передаваемой в функцию строки таковы:

  • допускаются как научный, так и фиксированный форматы;
  • в качестве десятичной точки должен выступать символ, который содержится в DecimalSeparator;
  • не допускаются символы-разделители тысяч (ThousandSeparator), а также символы обозначения денежньк единиц.

В случае ошибки преобразования функция StrToFloat генерирует исключитель­ную ситуацию EConvertError, a TextToFloat — возвращает значение False.


 

Функции работы с датами и временем

В Delphi для хранения дать! и (или) времени предусмотрен формат TDateTime, представляющий собой, на самом деле, обычное число с плавающей точкой.

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

var 
    StartTime: tDateTime;

procedure TFormI.PormCreate(Sender: TObject);
begin
    StartTime := Now;
end;

procedure TFormI.FormClick(Sender: TObject);
begin
    Labell.Caption := 'Co времени запуска прошло' + DateTimeToStr(Now - StartTime);
end;

Функции этой группы приведены в таблице:

function EncodeDate(Year, Month, Day: Word): TDateTime;Преобразует дату, заданную раздельно годом, месяцем и днем, в формат TDateTime. Если они выходят за допустимые пределы, возникает ИС EConvertError.
procedure DecodeDatefDate: TDateTime; var Year, Month, Day: Word);Преобразует дату в виде TDateTime к раздельным составляющим: году, месяцу и дню.
function EncodeTimefHour, Min, Sec, MSec: Word):TDateTime; Преобразует значение времени, заданное часом, минутой, секундой и миллисекундой в формат TDateTime.
procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word) ;Преобразует значение времени в формате TDateTime в раздельные составляющие: час, минуту, секунду и миллисекунду.
function DayOfWeektDate: TDateTime): Integer;Возвращает номер текущего дня недели от 1 (воскресенье) до 7 (суббота).
function Date: TDateTime;

Возвращает текущую дату.

function Time: TDateTime;

Возвращает текущее время.

function Now: TDateTime;

Возвращает текущие дату и время.

function DateToStr(Date: TDateTime): string;Преобразует дату Date в текстовую строку.
function TimeToStr(Time: TDateTime): string;Преобразует время из типа TDateTime в текстовую строку.
function DateTimeToStr(DateTime: TDateTime): string;Преобразует дату и время из типа TDateTime в текстовую строку.
function StrToDate(const S: string): TDateTime;Преобразует текстовую строку в дату типа TDateTime. Строка должна содержать два или три числа, разделенных символом, содержащимся в DateSeparator. Формат даты в строке определяется в соответствии со значением переменной ShortDateFormat.
function StrToTime(const S: string): TDateTime;Преобразует текстовую строку S в значение времени. Должен использоваться формат HH:NN:SS, где SS — может принимать значения 'AM' или ТМ', иначе означает секунды. Если необходимо получать время в 24-х часовой шкале, то элемент SS формата опус­кается. Если преобразование невозможно, возникает ИС EConvertError.
functionStrToDateTime(const S: string): TDateTime;Преобразует текстовую строку S в дату и время в формате MM/DD/YY HH:NN:SS (элемент SS см. выше).
function FormatDateTime(const Format: string; DateTime: TdateTime): string;Преобразует дату и время из типа TDateTime в тек­стовую строку, используя формат, задаваемый параметром Format (см. примеч. 1).
procedure DateTimeToStringfvar Result: string; const Format: string; DateTime: TDateTime);Преобразует дату и время типа TDateTime в строку Result, используя формат, передаваемый параметром Format (см. примеч. 1).
Примечания:

1. Правила задания спецификатора формата для даты и времени отлича­ются от рассмотренных выше. В первую очередь надо иметь в виду то, что функции вроде FormatDateTime оперируют над одним значением TDateTime, а спецификаторы формата извлекают из него те или иные составные части. При этом могут использоваться:

с Отображает дату, используя формат ShortDateFormat, затем отобра­жается время в формате LongTimeFormat. Время не отображается, если дробная часть переменной DateTime нулевая.
d

Отображает число месяца без нуля в левом разряде (1-31).

dd

Отображает число месяца с нулем в левом разряде (01-31).

ddd Отображает день недели в соответствии с сокращенными именами из переменной ShortDayNames.
dddd Отображает день недели в соответствии с полными именами из переменной LongDayNames.
ddddd

Отображает дату в соответствии с форматом ShortDateFormat.

dddddd

Отображает дату в соответствии с форматом LongDateFormat.

m

Отображает месяц как число без нуля в левом разряде (1-12).

mm

Отображает месяц как число с нулем в левом разряде (01-12).

irirnm Отображает месяц в соответствии с сокращенными именами из переменной ShortMonthNames.
П1ГПГПП1 Отображает месяц в соответствии с полными именами из переменной Long MonthN am es.
УУ

Отображает год двумя цифрами.

УУУУ

Отображает год четырьмя цифрами.

h

Отображает час как число без нуля в левом разряде (0-23).

hh

Отображает час как число с нулем в левом разряде (00-23).

n

Отображает минуты как число без нуля в левом разряде (0-59).

nn

Отображает минуты как число с нулем в левом разряде (00-59).

s

Отображает секунды как число без нуля в левом разряде (0-59).

ss

Отображает секунды как число с нулем в левом разряде (00-59).

t

Отображает время в соответствии с форматом ShortTimeFormat.

tt

Отображает время в соответствии с форматом LongTimeFormat.

am/pin Отображает время в 12-часовой шкале. 'AM' означает часы до полудня, 'РМ' — часы после полудня.
arnpm Отображает время в 12-часовой шкале. При этом для времени до полудня отображается содержимое переменной TimeAMString, а после полудня — TimePMString.
a/p Отображает время в 12-часовой шкале, 'а' означает часы до полудня, 'р' — часы после полудня.
/ Отображает символ — разделитель даты, содержащийся в переменной DataSeparator.
 Отображает символ — разделитель времени, содержащийся в переменной TimeSeparator.
'XX'/'XX' Символы, заключаемые в простые или двойные кавычки, отображаются как есть и не подвергаются форматированию.

Например, результатом вызова функции с параметрами

FormatDateTime (' "Совещание состоится в" dddd, dirrmim, yyyy, ' + ' "в" hh:nn', StrToDateTime('12/31/95 23:55')) ;

будет строка 'Совещание состоится в среду, 31 декабря 1995 в 23:55' (естес­твенно, если вы переопределили значения элементов массива LongMonth-Names).

2. Спецификаторы am/pm, ampm, a/p используются вместе со спецификато­рами h и hh.

3. Спецификаторы могут быть указаны как в верхнем, так и в нижнем регистрах символов — это не играет роли.

4. Начальные значения всех используемых при форматировании даты и вре­мени переменных берутся из секции [inti] файла WIN.INI.


 

Функции для работы с памятью

Для работы с памятью Delphi предоставляет следующие функции:

function AllocMemfSize: Cardinal): Pointer;SYSUTILS Выделяет блок памяти размером Size и возвращает указатель на него. Выделенная память обнуляется.
function ReAllocMem(P: Pointer; CurSize, NewSize: Cardinal): Pointer;SYSUTILS Изменяет размер блока памяти разме­ром CurSize, на который указывает Р. Если удается выделить блок размером NewSize, функция возвращает указатель на него. Содержимое старого блока переносится в новый. При этом, если CurSize>NewSize, часть содержимого отбрасывается, а если CurSizeто остаток свободного места заполняет­ся нулями.
function MemAlloc(Size: Longint): Pointer;GRAPHICS Выделяет блок размером Size. Если он меньше 64К, память выделяется в куче памяти Delphi, в противном случае — глобальная.

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

У компонента же определение конструктора на удивление лаконично, но в нем присутствует указатель на Owner:

 constructor Create(AOwner: TComponent); 

Ответ прост: Owner — тот объект, который при создании вызывает конструк­торы всех объектов, владельцем которых он является, а при уничтожении — их деструкторы. Таким образом, поместив при разработке компонент на форму, вы можете не заботиться о его создании и уничтожении в программе. Все свойства таких компонентов и все связи между ними запоминаются в файле формы .DFM и при запуске приложения создаются вновь автоматически.

 Деструктор компонента destructor Destroy; 

уничтожает все дочерние компоненты (вызывая их деструкторы) и вычеркивает себя из списка компонента-владельца.

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

Принадлежащие компоненты оформлены в виде списка, доступного через свой­ство Components. В классе TComponent предусмотрен набор методов и свойств для управления этим списком:

property Components[Index: Integer]: TComponent;Возвращает указатель на компонент с индексом в списке Index.

property ComponentCount: Integer;

function FindComponent(const AName: string): TComponent;

Возвращает число компонентов в списке.

Возвращает ссылку на компонент-потомок с заданным именем.
procedure InsertComponent(AComponent: TComponent) ;

procedure Rerr.oveCornponent (AComponent: TComponent) ;

Вставляет компонент в конец списка.
Удаляет компонент из списка.
procedure DestroyComponents; Удаляет все компоненты из списка.

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

Рассмотрим другие свойства компонента. Он может находиться в одном из множества следующих состояний:

 property ComponentState: TCornponentState; 
TComponentState= set of (csLoading, csReading, csWriting, csDestroying, csDesigning) ;

Эти состояния используются средой во время визуального проектирования, при загрузке/выгрузке компонентов из файлов ресурсов. Во время исполнения приложения множество состояний пусто. Лишь метод

 procedure Destroying; 

устанавливает в состоянии компонента флаг csDestroying и вызывает ана­логичный метод у всех потомков из списка. Он также используется в дест­рукторе компонента.

Свойство Designlnfo относится к стадии разработки (конструирования) приложения:

 property Designlnfo: Longint; 

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


 

Элементы управления

Потомком TComponent является класс TControl — элемент управления Win­dows. Все то, что видит (или может увидеть) пользователь в клиентской области вашей формы во время выполнения, порождено от класса TControl (клиентская область — вся рабочая поверхность окна Windows, исключая заголовок, полосу меню и обрамление). Таким образом, потомки TControl являются визуалъньши компонентами. Далее будем называть их, как принято в Windows,элементами управления, или, где это уместно, просто элементами.

Отметим также подвох, связанный с названием "Библиотека визуальных ком­понентов" (Visual Components Library, VCL). Называя так свою библиотеку, разработчики из фирмы Borland были абсолютно правы, так как речь идет в первую очередь о новой — визуальной — технологии программирования. Но нужно помнить, что в VCL входит множество полноправных невизуальных компонентов. Например, меню — оно, конечно, видимо на экране, но не в клиентской области; поэтому формально меню не является визуальным ком­понентом.

Большинство из свойств, которые вы будете видеть и изменять в визуальных компонентах, помещенных в Инспектор объектов, впервые описаны в классе TControl. Этот класс уже обладает "поведением" — в нем предусматривается реакция на основные события.

Объект класса TControl не является окном Windows и в силу этого не может получить фокус ввода. Однако у него обязательно имеется родительский эле­мент (см. главу 2, раздел "Наследование. Методы"), обладающий этим свой­ством и отвечающий за показ дочернего элемента и обработку некоторых поступающих ему сообщений. (Раз TControl не является окном, то он не имеет контекста устройства для рисования. Этот контекст обеспечивает ему родитель. Тип родительского элемента — TWinControl — будет рассмотрен ниже).

Обратите внимание на терминологию: Owner — это владелец компонента, а Parent — его родитель, определяемый свойством

 property Parent: TWinControl; 

Элемент управления может обрабатывать сообщения Windows. Чтобы послать собственной функции обработки элемента сообщение Windows вида Msg с параметрами WParam и LParam, можно воспользоваться методом:

 function Perform(Msg, WParam: Word; LParam: Longint): Longint; 

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

 property Caption: TCaption; TCaption= string[255]; 

либо

Text: property Text: TCaption; 

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

 function GetTextBuf(Buffer: PChar; BufSize: Integer): Integer; 
procedure SetTextBuftBuffer: PChar); function GetTextLen: Integer;

Все три метода работают, посылая в собственную функцию-обработчик со­общений объекта (через вызов метода Perform) сообщения Windows вида WM_GETTEXT, WM_SETTEXT и WM_GETTEXTLENGTH.

Курсор, который будет устанавливаться на компоненте, определен свойством:

 property Cursor: TCursor; TCursor = -32768..32767; 

В Delphi предопределены стандартные типы курсоров. Их имена:

crDefault, crNone, crArrow, crCross, crIBeam, crSize, crSizeNESW, crSizeNS, crSizeNWSE, crSizeWE, crUpArrow, crHourGlass, crDrag, crNoDrop, crHSplit, crVSplit, crMultiDrag, crSQLWait.

Этим именам соответствуют константы со значениями от 0 до -17; под этими идентификаторами все курсоры доступны в свойстве Cursors глобального объекта Screen. Можно добавить к ним собственные курсоры, загрузив их из ресурсов и присвоив положительные идентификаторы:

{$R cursors.RES} 

const
    Curl = 1;
    Cur2 = 2;

procedure TFormI. For-mCreate (Sender: TObject);
begin
    Screen.Cursors[Curl] := LoadCursor(hinstance, 'CUR_1');
    Screen.Cursors[Cur2] := LoadCursor(hinstance, 'CUR_2');
end;

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

У каждого элемента есть два свойства, отражающие его стиль и состояние. Они могут сослужить программисту хорошую службу.

Первый из них — набор флагов, управляющих поведением компонента:

 property ControlStyle: TControlStyle;
TControlStyle = set of (csAcceptsControls, csCaptureMouse, csDesignInteractive, csClickEvents, csFramed, csSetCaption, csOpaque, csDoubleClicks, csFixedWidth, csFixedHeight);

Эти флаги означают, что данный компонент имеет следующие особенности:

с sAccept sControlsМожет содержать другие (дочерние) элементы управления. Таким свойством обладают не все элементы: обладающие им называются группирующими и рассмотрены в отдельномразделе.
csCaptureMouse

Может получать сообщения от мыши.

csDesignInteractive Транслирует нажатия правой кнопки мыши в нажатия левой во время разработки.
csFramed Имеет обрамление (черным прямоугольником единичной толщины).
csSetCaption Позволяет при изменении имени менять синхронно и свойство Text (если Text не был явно переустановлен).
csOpaque Фон элемента непрозрачен. Это означает, что при пересечении нескольких компонентов на экране располо„.:мный под ним виден не будет.
csClickEvents

Воспринимает щелчки мышью.

с sDoubleC1i cks Воспринимает двойные щелчки мышью. Если этот флаг отсутствует, двойные щелчки воспринимаются как простые.
csFixedWidth, csFixedHeight Имеет фиксированную ширину или высоту соответственно. Она не изменяется при масштабировании компонента.

Набор флагов, отражающих состояние элемента, описывается свойством:

 property ControlState: TControlState;
TControlState = set of (csLButtonDown, csClicked, csPalette, csReadingState, csAlignmentNeeded, csFocusing, csCreating);
Они означают следующее:
csLButtonDown

Над элементом в данный момент нажата левая кнопка мыши.

csClicked Если элемент может воспринимать щелчки мышью, этот флаг устанавливается, пока кнопка мыши находится в нажатом состоянии.
csPalette Элемент поддерживает собственную палитру и должен получать извещения о перерисовке в необходимых случаях.
csReadingState Элемент в данный момент читается из потока.
сsAlignmentNeeded Элемент требует выравнивания относительно родительского (см. раздел "Положение, размеры и выравнивание элементов управления").
csFocusing В данный момент происходит получение элементом фокуса ввода.
csCreating Элемент создается (этот флаг в настоящий момент не задействован).

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

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

 property PopupMenu: TPopupMenu; 

Подробно о его создании и использовании рассказано в разделе, посвященном меню.


 

Положение, размеры и выравнивание элементов управления

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

 property BoundsRect: TRect; 

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

procedure SetBounds(ALeft, АТор, AWidth, AHeight: Integer);

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

(pb) property Left: Integer; J property Top: Integer; property Width: Integer; 
(Pb) property Height: Integer;

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

 (Ro) property ClientRect: TRect; 

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

 property ClientHeight: Integer; 
property ClientWidth: Integer;

Свойство ClientOrigin задает положение начала клиентской области относитель­но экрана:

 (Ro) property ClientOrigin: TPoint; 

Если же нужно связать с координатной системой экрана произвольную точку, пользуйтесь парой методов (не путать с одноименными функциями Windows API):

 function ClientToScreen(const Point: TPoint): TPoint; 
function ScreenToClient(const Point: TPoint): TPoint;

Очень важную часть работы по управлению размерами и расположением эле­ментов выполняет свойство:

 (Pb) property Align: TAlign; 

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

TAlign = (aiNone, alTop, alBottom, alLeft, alRight, alClient); aINone — выравнивание отсутствует;

alTop, alBottom, alLeft, alRight — выравнивание происходит по соответст­вующей стороне родителя;

alClient — компонент занимает все пространство клиентской области родителя.

Выравнивание гарантирует, что при изменении размеров родителя относитель­ная позиция дочернего элемента не меняется. Это свойство имеет приоритет над простым изменением положения и размеров. Если новые координаты эле­мента противоречат способу его выравнивания (например, перемещение вверх при alBottom), изменения отвергаются и элемент возвращается к первоначаль­ным координатам. Свойство Align незаменимо при организации панелей инструментов и строк состояния — они могут перемещаться и видоизменяться вместе с содержащей их формой. Для временного отключения действия свойства Align предназначены методы:

 procedure DisableAlign; procedure EnableAlign; 

Эти методы управляют возможностью выравнивания потомков данного эле-мекга, они должны вызьшаться в паре. Для восстановления выравнивания элементов в соответствии с Align есть метод:

 procedure Realign; 

В следующем примере использование методов DisableAlign и EnableAlign поз­воляет настроить выравнивание панели по тому краю формы, на который пользователь перетащит ее мышью:

procedure TFormI.FormCreate(Sender: TObject); 
begin
    Panell.Align := alBottom;
    Moving := False;
end;

procedure TFormI.PanellMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    if not Moving then
    begin
        Formi.DisableAlign;
        Moving := True;
    end;
end;

procedure TFonnl.PanellMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer)
begin
    if Moving then with Panell do
    begin
        Left := Left + X - Width div 2;
        Top := Top + Y - Height div 2;
        Panell.Caption := Format('%d,%d',[Left,Top]);
    end;
end;

procedure TFormI.PanellMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
    LastPos : TPoint;
    r0, rl, r2 : real;
begin
    if Moving then
    begin
        Moving := False;
        Panell.Caption := '';
        LastPos := Point(Panell.Left + X, Panell.Top + Y);
        if LastPos.X<=0 then
            LastPos.X := 1;
        if LastPos.X>=ClientWidth
            then LastPos.X := ClientWidth-1;
        if LastPos.Y<=0
            then LastPos.Y := 1;
        if LastPos.Y>=ClientHeight
            then LastPos.Y := ClientHeight-1;
        r0 := ClientWidth/ClientHeight;
        rl := LastPos.X/LastPos.Y;
        r2 := LastPos.X/(ClientHeight - LastPos.Y);
        with Panell do
            if rl < r0 then
                if r2 < r0 then
                    Align := alLeft
                else
                    Align := alBottom
            else
                if r2 < r0 then
                    Align := alTop
                else
                    Align := alRight;
        Formi.EnableAlign;
    end;
end;


 

Активность и видимость элементов управления

Активность элемента позволяет ему получать и обрабатывать сообщения от клавиатуры, мыши и таймера. Она определяется свойством:

 (Pb) property Enabled: Boolean; 

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

Свойство, определяющее возможность видимости элемента во время испол­нения:

(Pb) property Visible: Boolean;

Во время разработки все компоненты являются видимыми. Изменять это свой­ство непосредственно во время выполнения можно, используя два следующих метода:

 procedure Show; procedure Hide; 

Напомним, что, так как Visible является свойством, то выражение visible: =True не является простым присваиванием. Оно неявно содержит все необходимые операции для показа элемента управления. Это же касается всех остальных свойств компонентов, реакция на изменение которых должна произойти не­медленно.

Почему выше была применена формулировка "возможность видимости", а не "видимость"? Чтобы элемент был виден на экране, одного значения свойства Visible недостаточно. Нужно, чтобы видимыми были все предки элемента в иерархии. Реально видимость элемента можно узнать, пользуясь свойством:

 (Ro) property Showing: Boolean; 

Это свойство устанавливается при изменении свойства Visible. Оно доступно только для чтения. Нужно иметь в вицу, что при изменении видимости родительского элемента Showing не изменяется и может в какой-то момент не соответствовать истинному состоянию. Обновляет состояние свойства Showing для компонента метод:

 procedure UpdateControlState; 

Отрисовкой (изменением изображения на экране) элемента управления "заве­дуют" следующие методы:

procedure Invalidate; Отрисовывает компонент, вызывая функцию API InvalidateRect.
procedure update; Предусматривает дополнительные операции, необходимые Windows при отрисовке окна. Для компонентов-окон, например, в ней вызывается функция UpdateWindow.

Этот метод объединяет два предыдущих. Если элемент управления непрозрачный (имеет флаг csOpaque), занимаемый им прямоугольник предварительно очищается.

procedure Repaint;
procedure Refresh; Представляет собой вызов Repaint. Рекомендуется для вызова отрисовки.
 
 

Внутренний интерфейс Drag&Drop

Для библиотеки VCL фирмой Borland реализована собственная версия интер­фейса Drag&Drop (переводится как "перетащить"). Интерфейс этот внут­ренний — передавать и принимать можно любые управляющие элементы Del­phi внутри формы' (кроме самой формы). Он реализован без использования соответствующих функций API Windows — их нужно применять при организации общения с другими задачами путем перетаскивания.

Нажав левую кнопку мыши над элементом управления, мы можем "пере­тащить" его на любой другой элемент. С точки зрения программиста это означает, что в моменты перетаскивания и отпускания клавиши генерируются определенные события, которыми передается вся необходимая информация — указатель на перетаскиваемый объект, текущие координаты курсора и др. Получателем событий является тот элемент, на котором в данный момент находится курсор. Обработчик такого события должен сообщить системе, принимает ли данньш элемент управления "посылку" или нет. При отпускании кнопки над принимающим элементом управления генерируется еще одно или два события, в зависимости от готовности приемника.

Способ работы с этим интерфейсом в VCL определяется свойством:

 (Pb) property DragMode: TDragMode;
TDragMode = (dmManual, dmAutomatic);

Для автоматического включения механизмов, имеющихся в VCL, необходимо, чтобы свойство компонента DragMode было установлено в dmAutomatic. Это означает, что на всех стадиях перетаскивания нужные функции вызываются без участия программиста. Его задача состоит только в том, чтобы определить методы-обработчики соответствующих событий. В режиме dmManual (принимаемом по умолчанию) все необходимые вызовы функций нужно делать самому.

Рассмотрим подробнее формат обработчиков трех основных событий интер­фейса Drag&Drop.

Во время перетаскивания при перемещении курсора мыши с перетаскиваемым элементом над другим элементом управления, а также при отпускании кнопки для последнего возникает событие:

(pVl property OnDragOver: TDragOverEvent;
TDragOverEvent = procedure(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean) of object; Параметры события:
  • Sender — элемент, над которьм находится курсор;
  • Source — перетаскиваемый элемент;
  • X, Y — координаты курсора (в системе координат клиентской области Sender);
  • State — одно из трех состояний:
TDragState = (dsDragEnter, dsDragLeave, dsDragMove); 

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

  • dsDragEnter — курсор мыши появился над элементом;
  • dsDragMove — курсор мыши переместился над элементом;
  • dsDragLeave — курсор мыши ушел с элемента или была отпущена кнопка.

Обработчик этого события должен возвратить решение, примет ли данный элемент объект Source или нет, в булевой переменной Accept. Если обработчик этого события отсутствует, то элемент управления не может работать приемником, т. е. на него нельзя "перетащить".

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

 (Pb) property DragCursor: TCursor; 

Если компонент не является приемником Drag&Drop, или обработчик события OnDragOver отсутствует или возвращает False, то появляется другой курсор (по умолчанию crNoDrop).

Событие:

(Pb) property OnDragDrop: TDragDropEvent;
TDragDropEvent = procedure(Sender, Source: TObject; X, Y: Integer) of object;

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

В приведенном примере вы можете перетащить одну из геометрических фигур в вашей форме (компонент TShape) на статический текст Label 1. Для этого в Label 1 описано два метода: Label IDragOver сигнализирует о готовности принять только компоненты класса TShape, a Label IDragDrop вычисляет пло­щадь фигуры в квадратных сантиметрах в зависимости от ее вида (эллипс, прямоугольник, прямоугольник с закругленными углами).

procedure TForm1.Label1DragOver (Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); 
begin
    Accept := Source is TShape;
end;

procedure TForm1.Label1DragDrop (Sender, Source: TObject; X, Y: Integer);
var
    f : single;
    i : Integer;
begin
    with Source as TShape do
    begin
        i := Width;
        if i > Height then
            i:= Height;
        case Shape of
            stRectangle: f := Width*Height;
            stSquare: f := i * i;
            stCircle: f := Pi * i*i / 4;
            stEllipse: f := Pi * Widths-Height / 4;
            stRoundRect, stRoundSquare: begin
                    if Shape = stRoundRect then
                        f := Width*Height
                    else
                        f := i * i;
                    i := (i - Pen.Width + 1) div 4;
                    f := f - (4-Pi)*i*i;
                end;
            end;
    end;
    f := f / Sqr(Forml.PixelsPerInch / 2.54);
    Label1.Caption := FloatToStrF(f, ffFixed, 5, 2)+ ' кв.см';
end;

При завышении перетаскивания, вне зависимости от готовности приемника всегда возникает еще одно событие:

 (Pb) property OnEndDrag: TEndDragEvent;
TEndDragEvent =procedure(Sender, Target: TObject; X, Y: Integer) of object;

Его параметры идентичны описанным выше.

Для управления перетаскиванием вручную (в режиме dmManual) есть следу­ющие возможности. Начало перетаскивания происходит при вызове метода:

 procedure BeginDrag(Immediate: Boolean); 

Программист должен связать вызов этого метода с каким-либо событием в системе. (Если свойство DragMode установлено в dmAutomatic, BeginDrag вы­зывается функцией окна при нажатии левой кнопки мыши). Параметр Imme­diate определяет, когда именно возникает состояние Drag&Drop: в случае True немедленно, в случае False — после смещения мыши с нажатой левой кнопкой на 5 точек по любой из осей. Последний вариант дает возможность исполь­зовать нажатие левой кнопки и для перетаскивания, и для регистрации щелчков на элементе управления (скажем, на кнопке). В режиме dmAutomatic такой возможности нет. Метод:

 procedure DragDrop(DragObject: TObject; X, Y: Integer); dynamic; 

вызывает обработчик события OnDragDrop, а производит все завершающие действия метод

procedure EndDrag(Drop: Boolean); 

Он инициирует события OnDragDrop (при возможности приема) и OnEndDrag.

 Метод function Dragging: Boolean; 

возвращает True, если данный элемент в настоящий момент перетаскивается.


 

Ярлычки и оперативная подсказка

Совсем недавно, с легкой руки Microsoft, признание получили ярлычки с оперативной подсказкой (Hints). Если задержать курсор, например, над кноп­кой или компонентом палитры самой среды Delphi, появляется маленький прямоугольник яркого цвета (окно подсказки), в котором одной строкой ска­зано о названии этого элемента или связанном с ним действии. Delphi под­держивает механизмы создания и отображения таких ярлычков в создаваемых программах.

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

 (Pb) property ShowHint: Boolean; 

Если свойство ShowHint установлено в True, и во время выполнения курсор задержался над компонентом на некоторое время, в окне подсказки высвечива­ется текстовая строка с подсказкой:

 (Pb) property Hint: string; 

Подсказка компонента может быть пустой строкой — в этом случае система ищет в цепочке первый родительский компонент с непустой подсказкой. Если в строке Hint встречается специальный символ-разделитель '|', то часть строки до него ("короткая") передается в окно подсказки, а после ("длинная") — присваивается свойству Hint объекта Application. Ее можно использовать, например, в строке состояния внизу главной формы приложения (см. пример ниже).

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

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

 (Pb) property HintColor: TColor; 

У объекта Application значение свойства ShowHint нужно устанавливать во время выполнения, например, в обработчике OnCreate главной формы приложения. Оно является главенствующим для всей системы подсказок: если оно установлено в False, ярлычки не возникают.

Есть еще один способ получения подсказки. При смене текста в свойстве Hint, т. е. при смене текущего элемента управления, у Application возникает событие:

 (Рb) property OnHint: TNotifyEvent; 

Пример:

procedure TFormI.AppHint(Sender: TObject); 
begin
    Pane 11.Caption:= Application.Hint;
end;

procedure TFormI.FormCreate(Sender: TObject);
begin
    Application.OnHint := AppHint;
end;

В этом примере текст подсказки будет отображаться в строке состояния Panel 1 независимо от значения ShowHint у любого объекта — лишь бы этот текст был в наличии. Для этого разделяйте подсказку у элементов управления вашего приложения на две части при помощи символа '|' — краткая информация появится рядом с элементом, а более полная — в строке состояния.

У других компонентов свойство ShowHint интерпретируется системой так:

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

 (Pb) property HintPause: Integer; 

Интервал времени по умолчанию равен 800 мс. После истечения времени таймера, если мышь осталась над тем же элементом, наступает момент

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

 (Pl) property OnShowHint: TShowHintEvent;
TShowHintEvent = procedure (var HintStr: string; var CanShow: Boolean; var Hintlnfo: THintInfo) of object;

с параметрами:

  • HintStr — отображаемый текст;
  • CanShow — необходимость (возможность) появления подсказки. Если в переменной CanShow обработчик вернет False, то окно подсказки вы­свечиваться не будет;
  • Hintlnfo — структура, несущая всю информацию о том, какой элемент управления, где и как собирается показать подсказку. Ее тип:
    THintInfo = record HintControl: TControl; 
        HintPos: TPoint;
        HintMaxWidth: Integer;
        HintColor: TColor;
        CursorRect: TRect;
        CursorPos: TPoint;
    end;

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

procedure TFormI.AppShowHint(var HintStr: string; var CanShow: Boolean; var Hintlnfo: THintInfo); 
begin
    if HintStr='' then
    begin
        HintStr := Hintlnfo.HintControl.Name;
        Hintlnfo.HintColor := ciRed;
        CanShow := True;
    end;
end;

Присвоив этот метод обработчику Application.OnShowHint, установив Forml.ShowHint:=True и очистив все строки Hint, получим в качестве под­сказки имя каждого элемента.

"Спрятать" окно подсказки можно с помощью метода:

 procedure CancelHint; 

Без повторного перемещения мыши на текущий элемент оно более не возникнет.

 

Оконные элементы управления

Понятие окна Windows инкапсулировано в потомке TControl — классе TWinControl. Такой компонент получает соответствующий атрибут _ дескриптор окна, определяемый свойством:

 (Ro) property Handle: HWnd; 

С помощью этого дескриптора вы можете вызывать функции API Windows, если средств VCL вам недостаточно для решения задачи. Компоненты-потомки TWinControl — в дальнейшем будем называть оконными элементами управ­ления, а элементы управления, не имеющие дескриптора окна, — неоконными.

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

 function HandleAllocated:Boolean; procedure HandleNeeded; 

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

Важным свойством TWinControl является то, что он может содержать другие — дочерние — элементы управления. Они упорядочены в виде списка. Если быть точным, то списков на самом деле два — для неоконных и оконных дочерних элементов. Но "видны" они как один объединенный — сначала первый, потом второй. Методы и свойства для работы с этим списком приведены в таблице:

(Ro) property Controls[Index: Integer]: TControl;Содержит список дочерних элементов.

(Ro) property ControlCount: Integer;

Содержит число элементов в списке.

function ContainsControl(Control: TControl): Boolean;Проверяет наличие элемента в списке.
function ControlAtPos(const Pos: TPoint; AllowDisabled: Boolean): TControl ;Отыскивает в списке элемент, которому принадлежит заданная точка (в системе координат собственной клиентской облас­ти). Флаг AllowDisabled показывает, раз­решен ли поиск среди пассивных (свойство Enabled которых равно False) элементов.
procedure InsertControl(AControl: TControl) ;Вставляет элемент в конец списка.
procedure RemoveControl(AControl: TControl);Удаляет элемент из списка.
procedure Broadcast(var Message);Рассылает всем дочерним элементам из списка сообщение Message.

С каждым оконным компонентом можно связать контекстную помощь. Кон­текст помощи — это индекс, указывающий на определенную информацию в файле справочной системы, связанном с приложением:

 property HelpContext: THelpContext; 

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

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

Прокрутку (скроллинг) элементов на интервал DeltaX, DeltaY осуществляет метод:

 procedure ScrollBy(DeltaX, DeltaY: Integer); 

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

type 
    TMouseState = (msNormal, msDragging);

var
    OldPos, NewPos, MaxShift: TPoint;
    PMouseState : TMouseState;
   
procedure TFormI.ScrollMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    MaxShift.X := Imagel.Parent.Width - Imagel.Width;
    MaxShift.Y := Imagel.Parent.Height - Imagel.Height;
    if (MaxShift.X > 0) and (MaxShift.Y > 0) then Exit;
    FMouseState := msDragging; OldPos := Point(X, Y);
    Screen.Cursor := crDrag;
end;

procedure TFormI.ScrollMouseMove(Sender : TObject; Shift: TShiftState; X, Y: Integer);
begin
    if FMouseState = msDragging then
    begin
        NewPos := Point(X - OldPos.X, Y - OldPos.Y);
        if Imagel.Left + NewPos.X > 0 then NewPos.X := - Imagel.Left;
        if Imagel.Left + NewPos.X < MaxShift.X then NewPos.X := MaxShift.X - Imagel.Left;
        if Imagel.Top + NewPos.Y > 0 then NewPos.Y := - Imagel.Top;
        if Imagel.Top + NewPos.Y < MaxShift.Y then NewPos.Y := MaxShift.Y - Imagel.Top;
        Imagel.Parent.ScrollBy(NewPos.X, NewPos. Y);
    end;
end;

procedure TFormI.ScrollMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
    FMouseState := msNormal;
    Screen.Cursor := crDefault;
end;

Обратите внимание, что прокрутка неоконного компонента Imagel осуществ­ляется посредством вызова Image l.Parent.ScrollBy. Это свидетельствует о том, что конкретный родительский тип для этого безразличен. В примере изобра­жение помещено на панель (TPanel). Впрочем, метод ScrollBy используется также и полосами прокрутки, которые есть в компоненте TScrollingWinControl и его потомках, например, в TForm.

В VCL предусмотрена возможность написания приложений, которые будут сохранять относительный размер и положение при всех разрешениях дисплея. Более подробно эти механизмы описаны в разделе, посвященном формам; для TWinControl упомянем лишь метод

 procedure ScaleBy(M, D: Integer); 

который изменяет масштаб элемента управления в M/D раз, при этом верхний левый угол остается неподвижным. Так же изменяются и размеры всех дочерних элементов. Соответственно изменяется и масштаб шрифта (свойство Font). Флаги csFixedWidth и csFixedHeight в свойстве ControlStyle предотвращают изменение ширины или высоты.

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

 (Рb) property Ctl3D: Boolean; 

Нужно уточнить, что это свойство есть не у всех компонентов. Для части компонентов трехмерность реализована средствами VCL; другая же часть (радиокнопки, флажки и др.) требует для создания трехмерного эффекта до­ступа к библиотеке CTL3DV2.DLL.

Шрифт, которым выводится текст, связанный с элементом управления:

 property Font: TFont; 

Кисть, используемая для закрашивания рабочей области оконного элемента управления, представлена свойством:

 (Ro) property Brush: TBrush; 

Она имеет цвет, содержащийся в свойстве Color (по умолчанию clWindow). На уровне TControl оно доступно только по чтению:

property Color: TColor;

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


 

Реакция на события от мыши и клавиатуры

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

(рt) property OnMouseDown: TMouseEvent;
(Pb) property OnMouseUp: TMouseEvent;
TMouseEvent = procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object;
Параметры:
  • Sender — элемент-источник сообщения (обычно равен Self);
  • Button — идентификатор одной из кнопок;
  • TMouseButton = (mbLeft, mbRight, mbMiddle);
  • Shift — множество, которое может содержать элементы:
  • ssAlt, ssCtrl, ssShift — в зависимости от состояния этих клавиш;
  • ssLeft, ssRight, ssMiddle, ssDouble — в зависимости от нажатия кнопок мыши (ssDouble — нажать! и правая, и левая кнопки);
  • X, Y — координаты нажатия (в системе координат клиентской области получателя).

При перемещении мыши возникает событие:

 (Pb) property OnMouseMove: TMouseMoveEvent; 
TMouseMoveEvent = procedure(Sender: TObject; Shift: TShiftState; X, Y: Integer) of object;

Использование сообщений от мьшш уже встречалось в примерах, приведенных вьппе (например, см. разд. "Положение, размеры и выравнивание элементов управления").

Два события извещают о щелчке и двойном щелчке левой кнопкой мыши над компонентом:

 (pt) property OnClick: TNotifyEvent; 
(Pb) property OnDblClick: TNotifyEvent;

Отменить генерацию этих событий можно, удалив флаг csClickEvents из слова состояния элемента (ControlStyle). Для некоторых компонентов (например, кно­пок) OnClick возникает еще и при нажатии определенных клавиш на клавиатуре, а также вследствие вызова метода Click.

События, связанные с мышью, могут быть получены потомками TControl. В отличие от них, реакцию на события от клавиатуры могут иметь только оконные элементы управления ("могут", т. к. на уровне TControl и TWinControl эти события только описаны, но не опубликованы). Таким образом, есть компоненты (в том числе в Палитре компонентов), не имеющие связи с этими событиями из-за ее ненадобности. Впрочем, их меньшинство, и материал в этом разделе обобщен вполне обоснованно.

Нажатие и отпускание клавиш клавиатуры могут инициировать следующие события:

 property OnKeyDown: TKeyEvent; 
property OnKeyUp: TKeyEvent;
TKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of object;

Генерация этих событий встроена в обработчики сообщений Windows WMJCEYDOWN, WMJSYSKEYDOWN и WM_KEYUP, WM_SYSKEYUP соответст­венно. Обработчику передаются:

  • Sender — источник сообщения;
  • Shift — состояние специальных клавиш и кнопок мыши во время нажатия (отпускания);
  • Key — код нажатой клавиши, представляющий собой виртуальный код клавиши Windows (константы вида VK_XX, например, VK_F1, VK_ESCAPE и т. п.). Обратите внимание, что Key является var-параметром; т. е. его значение может быть изменено программистом.

Другое событие, возникающее вследствие нажатия клавиши:

 property OnKeyPress : TKeyPressEvent;
TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;

Это событие возникает при вводе с клавиатуры символа ASCII, т. е. оно не ге­нерируется, например, при нажатии функциональных клавиш или . Обработчик события вызывается при нажатии буквенных (в т. ч. вместе с ), цифровых клавиш, комбинаций + .. + (коды ASCII #1..#26), , , , + (код #3) и некоторых других. Также код ASCII можно сгенерировать, нажав <А11>+<десятичньш код символа> на числовой клавиатуре (Numeric Pad).

Событие OnKeyPress соответствует сообщению Windows WM_CHAR.

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

 (Pb) property KeyPreview: boolean; 

установлено в True, то сначала все три вида сообщений поступают к ее обработчикам, и только потом — к элементу управления. Если при этом в них обнулить параметр Key, то в элемент сообщение не поступит вообще. В приведенном ниже примере клавиша резервируется для изменения со­стояния формы:

procedure TFormI.FormCreate(Sender: TObject); 
begin
    KeyPreview := True;
end;

procedure TFonnl.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
    if Key = VK_F5 then
    begin
        if ssCtrl in Shift then
            WindowState := wsNormal
        else
            if Shift = [] then WindowState := wsMaximized;
        Key : = 0;
    end;
end;


 

Фокус ввода

Будучи окнами Windows, TWinControl и его потомки должны управлять фоку­сом ввода (состоянием, когда они получают и обрабатывают входные сооб­щения). Они имеют предназначенные для этого методы:

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

function Focused: Boolean- Показывает, имеет ли элемент в данный момент фокус ввода.
function CanFocus: Boolean; Возвращает True, если оконный элемент может по­лучить фокус ввода (для этого он и все его роди­тельские оконные элементы управления должны быть активными (Enabled) и видимыми).
procedure SetFocus; Запрашивает у родительской формы получение фокуса ввода.

При получении и утере фокуса оконными компонентами происходят события:

 (Pb) property OnEnter: TNotifyEvent;
(Рb) property OnExit: TNotifyEvent;

TNotifyEvent — простейшее событие — извещение, не имеющее параметров. Свойство

 (Pb) property TabStop: Boolean; 

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

 (Pb) property TabOrder: TTabOrder; 
TTabOrder = -1..32767;

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

Можно получить и весь список, содержащий иерархию дочерних элементов, имеющих табулостоп:

 procedure GetTabOrderList(List: TList) ; 

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

Два метода предназначены для управления показом перекрывающихся до­черних элементов управления:

 procedure BringToFront; procedure SendToBack; 

Метод BringToFront переносит элемент в конец родительского списка (визуа­лизация начинается с первого) и показывает его поверх остальных. SendToBack, наоборот, переносит элемент в начало и таким образом сверху окажутся все перекрывающиеся с ним. При этом элемент теряет фокус ввода (если имел).

Класс инкапсулирует шрифт Windows. В Delphi допускаются только горизон­тально расположенные шрифты. В конструкторе объекта по умолчанию принимается шрифт System цвета clWindowText размера 10 пунктов.



Привязка к родительским свойствам

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

(Pb) property ParentColor: boolean;
(Pb) property ParentCtl3D: boolean;
(Pb) property ParentFont: boolean;
(Pb) property ParentShowHint: boolean;

Все четыре свойства отвечают за наличие связи между соответствующими свойствами в родительских и дочерних элементах. Если какое-то из них у элемента установлено в True, это означает, что он наследует свойство от родителя и меняет цвет, трехмерность, шрифт или показ ярлычков вместе и вслед за ним. Но если само свойство дочернего элемента (например, Color или ParentColor) переустановить явно, то связь с родительским свойством разрьюается.


 

Графическая подсистема

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

Другой бич работы с графикой в Windows — проблема рационального исполь­зования ресурсов. Для больших программных комплексов она стала в по­следнее время нешуточной (это касается, по крайней мере, версий Windows до 3.11 включительно). Такие монстры, как Microsoft Office, потребляют во время работы львиную долю ресурсов. В Delphi ресурсами GDI незримо для пользователя "заведуют" специальные менеджеры, ведущие списки всех разме­щенных кистей, перьев, шрифтов и т. п. и управляющие их использованием.

Обязательным для любого объекта, связанного с графикой в Delphi является событие

 property OnChange: TNotifyEvent; 

Его обработчик вызывается всякий раз, когда изменились какие-то харак­теристики объекта, влияющие на его внешний вид.

В стандартном GDI основой для рисования служит дескриптор контекста устройства hDC и связанные с ним шрифт, перо и кисть. В состав VCL входят объектно-ориентированные надстройки над последними, назначением которых является удобный доступ к свойствам инструментов и прозрачная для поль­зователя обработка всех их изменений. Сначала опишем три этих класса.


 

Класс TFont

Свойства класса приведены в таблице:

property Handle: HFont;

Содержит дескриптор шрифта.

(Pb) property Name: TFontName;Содержит имя (начертание) шрифта, например, Arial.
(Pb) property Style: TFontStyles; TFontStyle = (fsBold, fsltalic, fsUnderline, fsStrikeOut); TFontStyles = set of TFontStyle;Содержит стиль (особенности начертания) шрифта: соответственно жирный, курсив, подчеркнутый и перечеркнутый.
(Pb) property Color: TColor; TColor = -(COLORENDCOLORS 4-1) .,$2FFFFFF;Определяет цвет шрифта.
(Pb) property Pitch: TFontPitch ; TFontPitch = (fpDefault, fpVariable, fpFixed) ;Определяет способ установки ширины символов шрифта. Значение fpFixed со­ответствует моноширинным шрифтам; fpVariable — шрифтам с переменной шириной символа. Установка fpDefault означает принятие того способа, который определен начертанием.
(Pb) property Height: Integer;Содержит значение высоты шрифта в пикселах.
property PixelsPerInch: Integer;Определяет число точек на дюйм. Первоначально равно числу точек на дюйм в контексте экрана. Программист не должен изменять это свойство, т. к. оно используется системой для приведения изображения на экране и принтере к одному виду.
(Pb) property Size: Integer; Содержит размер шрифта в пунктах (принято в Windows). Это свойство связано с Height соотношением.

Font.Size := -Font.Height * 72 / Font.PixelsPerInch;

 
 

Класс ТРеn

ТРеп = class(TGraphicsObject)

Инкапсулирует перо. В конструкторе по умолчанию создается непрерывное (psSolid) черное перо шириной в один пиксел. Свойства класса приведены в таблице:

property Handle: HPen;

Содержит дескриптор пера.

(Pb) property Color: TColor;

Определяет цвет пера.

(Pb) property Mode: TPenMode; TPenMode = (pmBlack, pmWhite, pmNop, pniNot, pmCopy, pmNotCopy, prnMergePenNot, pmMaskPeriNot, pmMergeNotPen, pmMaskNotPen, pmMerge, pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor) ;Содержит идентификатор одной из растровых операций, которые определяют взаимодействие пера с поверхностью. Эти операции соответствуют стандартным, определенным в Windows.
(Pb) property Style: TPenStyle; TPenStyle = (psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear, psInsideFrame) ;Определяет стиль линии, рисуемой пером. Соответствующие стили также определены в Windows.
(Pb) property Width: Integer;Содержит значение толщины пера в пик­селах.

 

Класс TBrush

TBrush = class(TGraphicsObject)

Инкапсулирует кисть. Первоначально создается белая сплошная (bsSolid) кисть. Свойства класса приведены в таблице:

property Handle: HBrush;

Содержит дескриптор кисти.

(Pb) property Color: TColor;

Определяет цвет кисти.

(Pb) property Style: TBrushStyle; TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross) ;Содержит стиль кисти (фактуру закраски).
property Bitmap: TBitmap; Содержит битовую карту, определенную пользователем для закраски поверхностей. Если это свойство определено, то Color и Style недействительны.

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


 

Класс TCanvas

 TCanvas = class(TPersistent) 

Этот класс — сердцевина графической подсистемы Delphi. Он объединяет в себе и "холст" (контекст конкретного устройства GDI), и "рабочие инстру­менты" (перо, кисть, шрифт) и даже "подмастерьев" (набор функций по рисованию типовых геометрических фигур).

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

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

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

 property Handle: HDC; 

Для рисования канва включает в себя шрифт, перо и кисть:

 (рb) property Font: TFont ; 
(Pt) property Pen: TPen;
(Рb) property Brush: TBrush;

Кроме того, можно рисовать и поточечно, получив доступ к каждому пикселу. Значение свойства

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

соответствует цвету точки с координатами (X,Y).

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

procedure Arc (XI, Yl, Х2, Y2, ХЗ, Y3, Х4, Y4: Integer) ; Метод рисует сегмент эллипса. Эллипс определяется описывающим прямоуголь­ником (X1,Y1) — (X2,Y2); его размеры должны лежать в диапазоне от 2 до 32767 точек. Начальная точка сегмента лежит на пересечении эллипса и луча, проведенного из его центра через точку (X3.Y3). Конечная точка сегмента лежит на пересечении эллипса и луча, проведенного из его центра через точку (X4.Y4). Сегмент рисуется против часовой стрелки.
procedure Chord(Xl, Yl, Х2, Y2, ХЗ, Y3, Х4, Y4: Integer);Рисует хорду и заливает отсекаемую ею часть эллипса. Эллипс, начальная и конеч­ная точки определяются, как в методе Arc.
procedure EllipsefXl, Yl, Х2, Y2: Integer) ;Рисует и закрашивает эллипс, вписанный в прямоугольник (X1.Y1) — (X2.Y2).
procedure LineTo(X, Y: Integer);Проводит линию текущим пером из текущей точки в (X,Y).
procedure MoveTo(X, Y: Integer);Перемещает текущее положение пера (свойство PenPos) в точку (X,Y).
procedure BrushCopy(const Dest: TRect; Bitmap: TBitmap; const Source: TRect; Color: TColor);Производит специальное копирование. Прямоугольник Source из битовой карты Bitmap копируется в прямоугольник Dest на канве; при этом цвет Color заменяется на цвет текущей кисти (Brush.Color). С помощью этого метода можно нарисо­вать "прозрачную" картинку. Для этого нужно выбрать соответствующий фону цвет кисти и затем заменить на него фоновый или наиболее часто встреча­ющийся цвет битовой карты (см. Bitmap. TransparentColor).
procedure CopyRect(const Dest: TRect; Canvas: TCanvas; const Source: TRect) ;Производит копирование прямоугольника Source из канвы Canvas в прямоугольник Dest в области самого объекта.
procedure FillRect(const Rect: TRect) ;Производит заливку прямоугольника (текущей кистью).
procedure FrameRectfconst Rect: TRect);Производит оконтуривание прямоуголь­ника цветом текущей кисти (без заполнения).
procedure Draw(X, Y: Integer; Graphic: TGraphic) ;Осуществляет рисование графического объекта Graphic (точнее, вызов метода его рисования) в области с верхним левым углом (X,Y).
procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);Осуществляет рисование объекта Graphic в заданном прямоугольнике Rect. Если размеры их не совпадают, Graphic масштабируется.
procedure DrawFocusRect(const Rect: TRect);Производит отрисовку прямоугольной рамки из точек (как на элементе, имеющем фокус ввода). Поскольку метод использует логическую операцию XOR (исключающее ИЛИ), повторный вызов для того же прямоугольника приводит изображение к начальному виду.
procedure FloodFilKX, Y: Integer; Color: TColor; FillStyle: TFillStyle); TFillStyle = (fsSurface, fsBorder) ;Производит заливку области текущей кистью. Процесс начинается с точки (X,Y). Если режим FillStyle равен fsSurface, то он продолжается до тех пор, пока есть соседние точки с цветом Color. В режиме fsBorder закрашивание, наоборот, прекращается при выходе на границу с цветом Color.
procedure Pie (XI, Yl, Х2, Y2, ХЗ, Y3, Х4, Y4: Integers-Рисует сектор эллипса, описываемого прямоугольником (X1,Y1) — (X2,Y2). Стороны сектора лежат на лучах, проходящих из центра эллипса через точки (X3.Y3) и (X4,Y4).
procedure Polygon(const Points: array of TPoint) ;Строит многоугольник, используя массив координат точек Points. При этом последняя точка соединяется с первой и внутренняя область закрашивается.
procedure Polyline(const Points: array of TPoint) ;Строит ломаную линию, используя массив координат точек Points.
procedure Rectangle(XI, Yl, Х2, Y2 : Integer) ;Рисует прямоугольник с верхним левым углом в (XI ,Y1) и нижним правым в (X2.Y2).
procedure RoundRect (XI, Yl, Х2, Y2, ХЗ, Y3: Integer);Рисует прямоугольник с закругленными углами. Координаты вершин — те же, что и в методе Rectangle. Закругления рисуются как сегменты эллипса с размерами осей по горизонтали и вертикали ХЗ и Y3.
function. TextHeight(const Text: string): Integer;Возвращает высоту строки Text в пикселах.
function TextWidth(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: stringi ;Производит вывод текста с отсечением. Как и в TextOut, строка Text выводится с позиции (X,Y); при этом часть текста, лежащая вне пределов прямоугольника Rect, отсекается и не будет видна.
(Ro) property ClipRect: TRect;Определяет область отсечения канвы. То, что при рисовании попадает за пределы этого прямоугольника, не будет изображено. Свойство доступно только для чтения — его значение переустанавливается системой в контексте устройства канвы.
property PenPos: TPoint; Содержит текущую позицию пера канвы (изменяется посредством метода MoveTo).

Метод

procedure Refresh; 

сбрасывает текущие шрифт, перо и кисть, заменяя их на стандартные, заимство­ванные из Windows (BLACK.PEN, HOLLOW_BRUSH, SYSTEM.FONT).

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

 property OnChange: TNotifyEvent; 
property OnChanging: TNotifyEvent;

Эти события возникают при изменениях свойств и вызовах методов TCanvas, меняющих вид канвы (то есть при любом рисовании. В MoveTo, например, они не возникают). Отличие их в том, что OnChanging вызывается до начала изменений, a OnChange — после их завершения.

Идентификатор (код) растровой операции при копировании прямоугольных блоков содержится в свойстве

 (Pb) property CopyMode: TCopyMode; 
TCopyMode = Longint;

и определяет правило сочетания пикселов, копируемых на канву, с ее текущим содержимым. При этом можно создавать разные изобразительные эффекты. В Delphi определены следующие константы кодов: cmBlackness, cmDstInvert, cmMergeCopy, cmMergePaint, cmNotSrcCopy, cmNotSrcErase, cmPatCopy, cmPatInvert, cmPatPaint, cmSrcAnd, cmSrcCopy, cmSrcErase, cmSrcInvert, cmSrcPaint, cmWhiteness.

Все они стандартно определены в Windows, и подробное их описание можно найти в документации по GDI. Значением CopyMode по умолчанию является cmSrcCopy — копирование пикселов источника поверх существующих.

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


 

Класс TGraphic

 TGraphic = class(TPersistent) 

Этот абстрактный класс является родительским для трех видов изображений, общепринятых в графике Windows — значка (класс TIcon), метафайла (класс TMetafile) и растровой картинки (класс TBitmap).

Работая над приложением в Delphi, вы никогда не будете создавать объекты класса TGraphic, но переменной этого типа вы можете присваивать указатель на любой из классов-потомков. Метод

 procedure Assign(Source: TPersistent); 

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

Загрузку и выгрузку графики в поток осуществляют методы

 procedure LoadFromStream(Stream: TStream); 
procedure SaveToStream(Stream: TStream);

а загрузку и выгрузку в файл:

procedure LoadFromFile(const Filename: string); 
procedure SaveToFile(const Filename: string);

Эти методы создают соответствующий файловый поток и затем вызывают методы LoadFromStream/SaveToStream.

Два метода осуществляют взаимодействие с буфером обмена:

 procedure LoadFromClipboardFormat(AFomat: Word; AData: THandle; APalette: HPALETTE); 
procedure SaveToCiipboardPormat(var AFormat: Word; var AData: THandle; var APalette: HPALETTE);

Здесь AFormat — используемый графический формат; AData и APalette _ данные и палитра (если она требуется). Потомок должен иметь свой формат и уметь обрабатывать данные, представленные в нем. Свойство

 (Ro) property Empty: Boolean; 

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

Высота и ширина графического объекта:

 property Height: Integer; property Width: Integer; 

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

 property Modified: Boolean; 

показывает, модифицировался ли данный графический объект. Это свойство устанавливается в True внутри обработчика события OnChange.


 

Класс TPicture

 TPicture = class(TPersistent) 

Класс-надстройка над TGraphic, точнее — над его потомками. Он содержит поле Graphic, которое может содержать TBitmap, TIcon и TMetafile. Предна­значение TPicture — управлять вызовами соответствующих методов, скрывая при этом хлопоты с определением типа и детали их реализации.

Кроме того, на уровне TPicture определены возможности по регистрации и использованию других — определенных пользователем — классов графических объектов, порожденных от TGraphic. Доступ к графическому объекту осу­ществляется посредством свойства:

 property Graphic: TGraphic; 

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

 property Bitmap: TBitmap; 
property Icon: TIcon;
property Metafile: TMetafile;

Если в поле Graphic хранился объект одного класса, а затребован — другого, то прежний объект уничтожается, а вместо него создается пустой объект нуж­ного класса. Если же вы описали свой класс (допустим, TDIB), то к его методам и свойствам следует обращаться так:

 (Graphic as TDIB).My Property := MyValue; 

Перечислим остальные методы и свойства:

procedure LoadFromFile(const Filename: scring);Анализирует расширение имени файла FileName и, если оно известно (зарегистри­ровано), то создается объект нужного класса и вызывается его метод LoadFromFile. В противном случае возникает исклю­чительная ситуация EInvalidGraphic. Стан­дартными расширениями являются .ICO, .WMF и .BMP.
procedure SaveToFile(const Filename: string);Сохраняет графику в файле, вызывая соответствующий метод объекта Graphic.
procedure LoadFromClipboardFor-r.ac iAFcr.T.at: Word; AData: T'Handle; APalette: HPALETTE) ;Если формат AFormat найден среди зарегистрированных, то AData и APalette передаются для загрузки одноименному методу соответствующего объекта. Стан­дартно зарегистрированных форматов два: битовое изображение CF BITMAP и метафайл CFMETAFILEPICT.
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THar.dle; var APalette: H?ALE"T3!;Сохраняет графику в буфере обмена, вызывая метод объекта Graphic.
procedure Assign(Source: TPersistent) ;Метод Assign переписан таким образом, чтобы присваиваемый объект мог быть класса как TPicture, так и TGraphic или любого его потомка. Кроме того, пара­метр Source может быть равен nil — в этом случае поле Graphic очищается с удалением прежнего объекта.
class function SupportsClipboardPormat(AFormat: Word) : Boolean;Метод класса возвращает True, если формат AFormat поддерживается классом TPicture (зарегистрирован в системе). Напомним, что методы класса можно вызывать через ссылку на класс, без создания экземпляра объекта.
class procedure RegisterFileFormat(const AExtension, ADescription: string; AGraphicClass: TGraphicClass); class procedure RegisterClipboardFormat(AFormat: Word; AGraphicClass: TGraphicClass) ;Предназначены для создателей новых графических классов. Они позволяют зарегистрировать формат файла и буфера обмена и связать их с созданным классом — потомком TGraphic, который умеет читать и записывать информацию в этом формате.
(^) property Width: Integer; (ко) property Height: Integer-Ширина и высота картинки. Те же, что и у Graphic.
property OnChange: TNotifyEvent;Это событие вызывается при изменениях графического объекта.

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

 Iconi.Assign(Icon2) ; 

означает, что два этих объекта разделяют теперь один значок.

Более простым является кэширование для TIcon и TMetafile, которые умеют только отображать себя и не предназначены для редактирования (создатели Delphi считают, что дескриптор графического объекта дается программисту не для того, чтобы ковыряться в нем на уровне двоичных кодов). Гораздо сложнее устроен механизм кэширования для TBitmap, который имеет свою канву для рисования.

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

Кого-то может удивить отсутствие объявленных методов рисования вроде Draw у TIcon, TMetafile и TBitmap. Такие методы есть, но они недоступны. Все рисование должно осуществляться через вызовы методов Draw и StretchDraw канвы, содержащей графику, ибо канва несет тот контекст, в котором должна осуществляться операция. Рассмотрим предопределенные графические классы.


 

Класс TMetafile

TMetafile = class(TGraphic)

Инкапсулирует свойства стандартного метафайла Windows. В нем перекрыва­ются методы Assign, LoadFromStream, SaveToStream, LoadFromClipboardFormat, SaveToClipboardFormat. В буфер обмена объект помещает свое содержимое в формате CF_METAFILEPICT. Помимо общих, класс имеет свойства:

property Handle: HMETAFILE;

Дескриптор метафайла.

property Inch: Word; Число точек на дюйм в координатной системе метафайла. Связано с установленным режимом отображения.

 

Класс TIcon

TIcon = class(TGraphic)
Инкапсулирует значок Windows.

Не пытайтесь изменить размеры значка — они постоянны (равны GetSystemMetrics(SM_CXICON) и GetSystemMetrics(SM_CYICON)), и при попытке присвоить новые значения возникает исключительная ситуация EInvalidGraphicOperation. Значок нельзя также читать и писать в буфер обмена, так как в Windows нет соответствующего формата. В этом классе перекрываются методы класса TGraphic: Assign, LoadFromStream и SaveToStream. Дополнительно также определены:

property Handle: HICON;

Дескриптор значка.

function ReleaseHandle: HICON;Метод "отдает" дескриптор — возвращает его значение, обнуляя ссылку на него в объекте.


 

Класс TBitmap

 TBitmap = class(TGraphic) 

Класс соответствует битовой карте, зависимой от устройства (Device — dependent bitmap, DDB). В нем перекрываются методы Assign, LoadFromClipboardFormat, LoadFromStream, SaveToClipboardFormat, SaveToStream. Объект взаимодействует с буфером обмена в формате CF_BITMAP.

Канва битовой карты доступна через свойство:

 (ro) property Canvas: TCanvas; 

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

Дескрипторы битовой карты и ее палитры доступны как свойства:

 property Handle: HBITMAP; 
property Palette: HPALETTE;

Два метода

function ReleaseHandle: HBITMAP; 
function ReleasePalette: HPALETTE;

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

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

 procedure Dormant ; 

— выгружает изображение в поток и уничтожает дескрипторы битовой карты и палитры,

 procedure Freelmage; 

— "освобождает" дескриптор битовой карты для дальнейшего использования и внесения изменений. Это означает, что если на данный дескриптор есть ссылки, то он дублируется; поток очищается.

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

 property Monochrome: Boolean; 

Значение True соответствует монохромной битовой карте. При его изменении происходит преобразование содержимого к требуемому виду.

"Фоновый" цвет битовой карты:

 (Ro) property TransparentColor: TColor; 

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


 

Функции для работы с графикой

В модуле GRAPHICS сосредоточен целый ряд полезных функций:

function GraphicFilter(GraphicClass: TGraphicClass): string;Эту функцию удобно использовать вместе с диалогами открытия и закрытия файла. Для заданного класса GraphicClass она вернет строку, которую сразу можно присвоить свойству Filter диалога. Например, для TBitmap она вернет строку 'Bitmaps (*.BMP)|*.BMP'.
function GraphicExtensionfGraphicClass: TGraphicClass): string;Возвращает строку, содержащую расширение, которое встречается у фай­лов в формате GraphicClass. Например, GraphicExtension(TIcon) равно строке 'ICO'.
function ColorToRGB(Color: TColor): Longint;Преобразует значение типа TColor в фор­мат RGB.
function ColorToIdent(Color: Longint; var Ident: string): Boolean; function IdentToColor(const Ident: string; var Color: Longint):Boolean;Функции взаимного преобразования цвета в строку с его названием (опреде­ленным в модуле GRAPHICS). Например, ColorToIdent(clWhite, AString) присвоит AString значение 'clWhite'. В случае неуспеха возвращается False;
function ColorToString(Color: Tcolor): string; function StringToColorfS: string): TColor;Назначение аналогично двум предыдущим функциям. При отсутствии цвета Color в списке предопределенных цветов возвращается строка с его значением в шестнадцатиричном формате. При отсутствии цвета с именем S (в том же списке) делается попытка преобразовать строку в число, в случае неудачи возникает исключительная ситуация.
procedure GetColorValues(Proc: TGetStrProc);Производит вызов определенной пользователем процедуры Proc для всех имеющихся в списке цветов. В качестве параметра в такую процедуру передается строка с именем цвета. Эта процедура используется в примере MOVLINES на прилагаемой к книге дискете.

Для преобразования битовой карты из зависимого от устройства формата DDB в независимый (DIB) предназначены две функции:

procedure GetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer; var ImageSize: Longint);Возвращает размер заголовка изображения и размер самого изображения. Значение InfoHeaderSize равно размеру структуры TBitmapInfoHeader плюс, при необходимости, размеру палитры (каждый элемент которой TRGBQuad занимает 4 байта). В ImageSize возвращается коли­чество байт, которое нужно отвести для получения изображения в формате DIB.
function GetDIBfBitmap: HBITMAP; Palette: HPALETTE; var Bitmaplnfo; var Bits): Boolean;Преобразует DDB (определенную через Bitmap и Palette) в DIB. Заголовок помещается в Bitmaplnfo, а сами данные — в Bits.
 
 

Описание компонентов VCL

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

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


 

Работа с меню

В приложениях, написанных на Delphi, могут быть реализованы меню двух основных видов:
  • Главное меню. Такое меню принадлежит форме и отображается вместе с ней под ее панелью заголовка. Если в приложении несколько форм, то для удобства можно объединять меню всех активных форм в одном.
  • Всплывающее меню. Такое меню предусмотрено почти у всех компонен­тов — элементов управления Windows. Оно возникает (всплывает) при нажатии правой кнопки мыши на этом компоненте. Предусмотрено такое меню и у формы.

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

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

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

 

Компонент TMenuItem

 TObject—>TPersistent—>TComponent—>TMenuItein 

Модуль MENUS в Палитру компонентов не входит. Этот компонент, который является основой системы меню в Delphi, вы не встретите в Палитре компонентов — он входит в состав имеющихся там компонентов TMainMenu и


 

TPopupMenu

Текст, содержащийся в пункте меню, определяется свойством:

 (Pb) property Caption: string; 

Помимо основной, он несет еще две дополнительные нагрузки. Во-первых, если в строке имеется амперсанд ('&'), то он вместе с следующим за ним символом является акселератором. Например, для строки '&File' нажатие + означает выбор этого пункта. Во-вторых, если текст состоит из одного символа '-', то этот пункт служит в меню разделителем (имеет стиль MFJSEPARATOR);

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

 (Pb) property Shortcut: TShortCut; TShortCut = Low(Word)..High(Word); 

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

 procedure Click; 

приводят к возникновению события:

(Рb) property OnClick: TNotifyEvent;

Компонент TMenuItem может различать только это событие, с которьм до­лжны быть связаны действия, ради которых вы ввели данный пункт в меню. Воспринимают это событие те пункты меню, которые в данный момент активны, что означает-установленное в True свойство:

 (Рb) property Enabled: Boolean; 

Если Enabled равно False, пункт изображается серьм цветом и не воспринимает сообщений.

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

 (Pb) property Checked: Boolean; 

отвечает за то, является ли этот пункт отмеченным.

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

 (Pb) property Break: TMenuBreak;
TMenuBreak = (mbNone, mbBreak, mbBarBreak);

призвано решить эту проблему. Если оно установлено в mbBreak, то ко­манды меню, начиная с этой, располагаются в новом — соседнем с преж­ним — столбце (при этом их принадлежность не изменяется). Если оно равно mbBarBreak, столбцы при этом разделяются вертикальной чертой. В Delphi все компоненты меню типа TMenuItem могут быть как простыми командами, так и подменю, имеющими собственный список пунктов. В случае, если у компонента дочерних подменю и пунктов нет, для него имеет смысл свойство:

 (Ro) property Command: Word; 

Прежде при написании меню нужно было выдумывать и присваивать уникаль­ный идентификатор каждому его пункту — то есть собственно код команды, который посылался в качестве параметра сообщения WM.COMMAND. Сейчас эта задача возложена на Delphi — программист не только не определяет, но может и не знать кода команды, связанного с этим пунктом меню. Система выберет уникальное значение автоматически и присвоит это значение свойству Command. Изменять его не разрешается.

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

Если для каких-то целей понадобился код команды, можно воспользоваться вышеуказанным свойством (см. пример в описании компонента TMenu).

Если у компонента TMenuItem имеются дочерние пункты, то он является подменю, имеет статус MF_POPUP и дескриптор меню Windows, доступный для чтения через свойство:

(ro)property Handle: HMENU;

Пункты меню иерархически связаны. Методы и свойства для работы с до­черними пунктами меню приведены в таблице.

(Rо) property Parent: TMenuItem;Содержит указатель на родительское подменю.
(Ro) property I terns[Index: Integer]: TMenuItem;Содержит список дочерних пунктов меню.
(Ro) property Count: Integer;Содержит количество дочерних пунктов меню.
procedure Insert(Index: Integer; Item: TMenuItem) ;Вставляет пункт Item в меню на место Index. Поскольку структура меню строго иерархическая, вставляемый пункт не должен быть частью другого меню (его свойство Parent должно быть равно nil).

procedure Delete(Index: Integer) ;

Удаляет пункт с индексом Index из меню.

function IndexOf(Item: TMenuItem): Integer;Возвращает индекс пункта Item.

procedure Add(Item: TMenuItem);

Добавляет пункт Item в конец меню.

procedure Remove(Item: TMenuItem);

Удаляет пункт Item из меню.

Если пункт меню находится в фокусе, нажатие вызовет систему помощи с контекстом, определенным в свойстве:

 (Pb) property HelpContext: THelpContext; 

Свойство (Pb)

property Grouplndex: Byte; 

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


 

Компонент TMenu

 TObject—>TPersistent->TComponent->TMenu 

Модуль MENUS В Палитру компонентов не входит. Этот компонент отсутствует в Палитре компонентов, но содержит методы и свойства, приведенные в таблице, общие для двух потомков — TMainMenu и TPopupMenu, которые туда входят.

(Ro) property Handle: HMENU;

Содержит дескриптор меню.

property WindowHandle: HWND; Содержит дескриптор окна Windows (формы или оконного элемента управления), с кото­рым связано меню.
(Ro) property Items: TMenuItem;Содержит список элементов меню. На самом верху иерархии меню есть единственный элемент (со статусом MFPOPUP), чей список и используется.
function DispatchCoinn>and(ACommand: Word): Boolean;Отыскивает пункт меню с заданной командой, в случае успеха вызывает его метод Click и возвращает True.
function DispatchPopupfAHandle: HMENU): Boolean;Работает как Di spatchCommand, но отыскивает пункт меню с дескриптором AHandle.
function Findltem(Value: Word; Kind: TFindItemKind): TMenuItem; TFindItemKind = (fkComrriand, fkHandle, fkShortCut);Возвращает указатель на объект, соответ­ствующий заданному пункту меню. Параметр Value должен содержать величину, которая интерпретируется в соответствии с одним из трех способов поиска (по команде, дескрип­тору или комбинации горячих клавиш).
function GetHelpContext(Value: Word; ByComniand: Boolean) : THelpContext;Возвращает значение контекста помощи элемента меню. Если параметр ByCommand установлен в True, Value содержит связанную с пунктом команду, в противном случае — дескриптор. Если контекст у пункта отсут­ствует (равен 0), то отыскивается ближайший родительский ненулевой контекст. Если и он не найден, возвращается 0.
function IsShortCut(var Message: TWMKey): Boolean;Определяет, какая комбинация горячих клавиш ShortCut нажата, и отыскивает соот­ветствующий пункт меню. Если пункт с таким значением ShortCut найден, и его метод Click отработал успешно, метод возвращает True.

В следующем примере метод обрабатывает сообщение Windows WM_MENUSELECT, которое возникает при перемещении фокуса между пунк­тами меню. В зависимости от типа пункта показывается его дескриптор или команда:

procedure TFormI.wmMenuSelect(var Msg :TWMMenuSelect); 
var
    Anitem : TMenuItem;
begin
    with Msg do
    begin
        if (MenuFlag and MF_POPUP <>0 ) then
        begin
            Anitem := Formi.Menu.Findltem(Idltem, fkHandle);
            if Anitemonil then
                Labell.Caption := 'Handle:'+IntToHex(Anitem.Handle,4);
            end
        else begin
            Anitem := Formi.Menu.Findltem(Idltem, fkCommand);
            if Anitemonil then Labell.Caption := 'Command:'+IntToHex(Anitem.Command,4);
        end;
    end;
    inherited;
end; 

 

Компонент TMainMenu

 TObject—>TPersistent->TComponent->TMenu->TMainMenu 

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

  • Если в приложении имеется несколько форм со своими меню, то для упрощения работы целесообразно соединить их в одно и управлять меню из главной формы.
  • Объединение меню нужно при работе с интерфейсом MDI и его подокнами.
  • Механизм объединения меню используется серверами OLE, запускаемыми по месту нахождения объекта OLE. Загружаясь, сервер дописывает осу­ществляемые им операции к меню другого приложения.

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

 (Рb) property AutoMerge: Boolean;

При этом у главного меню оно должно оставаться равным False, иначе главное меню будет вообще невидимым. Объединение будет происходить автоматически при активизации новых форм или серверов OLE. Кроме автоматического режима, объединение меню можно выполнить при вызове метода:

 procedure Merge(Menu: TMainMenu); 

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

procedure Unmerge(Menu: TMainMenu);

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

Объединение меню происходит по специальным правилам, в основе которых лежит использование группового индекса (свойства Group Index) каждого объек­та TMenuItem.

У пунктов меню одного уровня, в частности всех подменю верхнего уровня в главном меню, свойство Grouplndex является неубывающим, т. е. у после­дующего пункта групповой индекс больше либо равен индексу предыдущего. Это требование отслеживается как на этапе разработки, так и на этапе испол­нения. Например, пусть пункты меню имеют индексы О, 3, 4, 5, 6. Если вы включите пункт меню с индексом 5 между пунктами с индексами 0 и 3, то 3 и 4 будут изменены на 5. А вот изменить большее значение Х на меньшее Y, если впереди есть пункты с индексом, большим Y, невозможно. Если в этом примере попытаться изменить индекс 6 на 4, то это приведет к возникно­вению исключительной ситуации EMenuError.

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

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

К окнам интерфейса MDI все сказанное относится только при запуске приложения. Если в формах приложения со стилем fsMDIChild есть свои главные меню, то в этот момент они автоматически сольются с главным меню формы fsMDIForm независимо от состояния AutoMerge. На уровне работы с серверами OLE предусмотрены дополнительные возможности по объединению меню. Если в компонент TOLEContainer загружен объект OLE, то в конец подменю Edit обычно добавляется подменю, из которого можно вызвать функции открытия и редактирования этого объекта. После активизации сервера он может не только вставлять свои подменю в главное, но и добавлять новые пункты к уже существующим подменю.

Три метода TMainMenu используются для работы с меню OLE:

procedure PopulateOle2Menu(SharedMenu: HMenu; Groups:array of Integer; var Widths: array of Longint);
procedure GetOle2AcceleratorTable(var hAccel : THandle; var numAccels: Word; Groups: array of Integer);
procedure Set01e2MenuHandle(Handle: HMENU);

Конструктор меню Delphi поможет значительно упростить разработку меню. В нем имеются готовые шаблоны типовых подменю верхнего уровня: File, Edit и др; пример их использования см. в проекте DEMOEDIT на дискете, прилагаемой к книге. Можно также определить свои шаблоны.


Компонент TPopupMenu

TObject->TPersistent—>TComponent->TMenu->TPopupMenu

Модуль MENUS
Страница Палитры компонентов Standard

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

(Pb) property AutoPopup: Boolean;

Для вызова этого меню из программы используйте метод:

procedure Popup(X, Y: Integer);

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

(Pb) property Alignment: TPopupAlignment;
TPopupAlignment = (paLeft, paRight, paCenter) ;

В зависимости от его значения в точке щелчка появится:

  • paLeft — левый верхний угол;
  • paRight — правый верхний угол;
  • paCenter — середина верхней стороны.

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

При вызове всплывающего меню перед его показом на экране программист извещается событием:

(Pb) property OnPopup: TNotifyEvent;

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

property PopupComponent: TComponent;

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

Контекст помощи всплывающего меню задается свойством:

(Pb) property HelpContext: THelpContext;


Функции для работы с меню

Рассмотрим также описанные в модуле Menus полезные функции для управления меню.

Четыре функции предназначены для преобразования типа TShortCut, представляющего горячую комбинацию клавиш:
а) в символ+состояние специальных клавиш и обратно:

procedure ShortCutToKey(Shortcut: TShortCut; var Key: Word; var Shift: TShiftState);
function Shortcut(Key: Word; Shift: TShiftState): TShortCut;

б) в текст и обратно:

function ShortCutToText(Shortcut: TShortCut): string;
function TextToShortCut(Text: string): TShortCut;

Новые меню и их составные части можно создавать, пользуясь функциями:

function NewMenu(Owner: TComponent; const AName: string; Items: array of TMenuItem): TMainMenu;
Создает новое главное меню с именем AName и пунктами Items, которое будет принадлежать владельцу Owner.
function NewPopupMenu(Owner: TComponent; const AName: string; Alignment: TPopupAlignment; AutoPopup: Boolean; Items: array of TMenuItem): TPopupMenu; Создает новое всплывающее меню. Назначение дополнительных параметров см. описание TPopupMenu.
function NewSubMenu(const ACaption: string; hCtx: Word; const AName: string; Items: array of TMenuItem): TMenuItem; Создает новое подменю. Здесь AName — имя объекта, ACaption — его текст, hCtx — контекст системы помощи.
function NewItem(const ACaption: string; AShortCut: TShortCut; AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent; hCtx: Word; const AName: string);Создает новый объект типа TMenuItem. Параметры метода соответствуют свойствам класса.
function NewLine: TMenuItem;Создает новый элемент меню типа разделитель (TMenuItem с Caption = '-').