100 компонентов общего назначения библиотеки Delphi5
Форма (а) и окно в процессе выполнения (б) вашего приложения


Этот инструмент очень удобен во многих случаях, когда вы не очень точно помните последовательность перечисления параметров какой-нибудь функции, или когда не уверены в имени какого-то свойства. Но иногда, когда вы хорошо освоитесь с Delphi, эти подсказки, может быть, начнут вас раздражать. Тогда вы можете отключить Знатока Кода, выполнив команду Tools | Editor Options и на странице Code Insight выключив опцию Code Completion. В Delphi 4 аналогичные действия можно сделать командой Tools | Environment Options.
Итак, ваше приложение готово. Можете откомпилировать и выполнить его (команда Run | Run). После недолгой компиляции перед вами появится окно вашего приложения. Нажав в нем кнопку Пуск, вы увидите указанную вами строку текста (Рисунок 1.4 б). Можете попробовать различные манипуляции с окном: перемещение его, изменение размеров его рамки курсором мыши, свертывание и развертывание. В заключение закройте приложение, щелкнув на кнопке в его правом верхнем углу.
Теперь посмотрите код программы, который создала с вашей помощью Delphi. Этот код содержится в двух файлах — головном файле программы с расширением .dpr и в файле модуля с расширением .pas. Головной файл .dpr — один на весь проект, а файлов модулей столько, сколько форм вы ввели в свой проект. Добавление в проект новой формы вы можете осуществить командой File | New Form, а удалить форму из проекта можно командой Project | Remove from Project.
Текст головного файла программы вы, как правило, не видите и не трогаете, полностью доверяя его ведение Delphi. А честь написания текстов модулей вы делите с Delphi. Delphi создает заготовку модуля, включает в нее описание формы и всех компонентов, которые вы размещаете на форме, обеспечивает связь с библиотекой. А вы пишете в основном обработчики событий компонентов. При необходимости вводите также в текст различные переменные, константы, описания типов, вспомогательные функции и процедуры. Поэтому надо представлять, как выглядит текст модуля и в какие его места что можно добавлять.
Ниже приведен текст модуля описанного выше простого примера, который вы можете увидеть в окне Редактора Кода. Подробные комментарии в этом тексте поясняют, куда и что в этот код вы можете добавлять. unit Unit1; interface // Открытый интерфейс модуля {Список подключаемых модулей} uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; {Объявление класса формы} type TForm1 = class(TForm) Label1: TLabel; Button1: TButton; procedure Button1Click (Sender: TObject); private // Закрытый раздел класса { Private declarations } {Сюда могут помещаться объявления переменных, функций и процедур, включаемых в класс формы, но не доступных для других модулей} public // Открытый раздел класса { Public declarations } {Сюда могут помещаться объявления переменных, функций и процедур, включаемых в класс формы и доступных для других модулей} end; var Form1: TForm1; {Сюда могут помещаться объявления типов, констант, переменных, функций и процедур, к которым будет доступ из других модулей, но которые не включаются в класс формы} implementation // Реализация модуля {$R *.DFM} {Сюда могут помещаться предложения uses, объявления типов, констант, переменных, к которым не будет доступа из других модулей. Тут же должны быть реализации всех объявленных в разделе interface функций и процедур, а также могут быть реализации любых дополнительных, не объявленных ранее функций и процедур.} procedure TForm1.Button1Click(Sender: TObject); begin Label1.Caption := 'Это мое приложение Delphi!'; end; end.
Модуль начинается с ключевого слова unit, после которого пишется имя модуля. Оно совпадает с именем файла, в которым вы сохранили свой модуль. По умолчанию для первого модуля имя равно Unit1, для второго Unit2 — и т.д.
Текст модуля состоит из двух основных разделов: interface — открытый интерфейс модуля, и implementation — реализация модуля. Все, что помещается непосредственно в раздел interface (типы, переменные, константы, функции, процедуры), может быть использовано другими модулями программы. Все, что помещается в раздел implementation — внутреннее дело модуля. Внешние модули не могут видеть типы, переменные, константы, функции и процедуры, размещенные в разделе реализации.
В разделе interface после предложения uses, содержащего список подключаемых библиотечных модулей, вы можете видеть объявление класса вашей формы, подготовленное Delphi. Имя класса вашей формы — TForm1. В класс включены те объекты которые вы разместили на форме — метка Label1 и кнопка Button1. Кроме того в класс включено объявление того обработчика щелчка на кнопке, который вы создали — процедуры Button1Click.
В классе предусмотрено также два раздела: private — закрытый раздел класса, и public — открытый раздел класса. То, что вы или Delphi объявите в разделе public, будет доступно для других классов и модулей. То, что объявлено в разделе private, доступно только в пределах данного модуля.
После завершения объявления класса формы вы можете видеть строки var Form1: TForm1
Это объявляется переменная Form1 класса TForm1, т.е. объявляется ваша форма как объекта класса TForm1.
Затем следует раздел реализации implementation, который начинается с директивы компилятора, смысл которой, вообще говоря, знать не обязательно. Надо только следить, чтобы случайно не стереть эту директиву. Далее в разделе implementation вы можете видеть реализацию объявленной в классе процедуры Button1Click с единственным оператором, который вы ввели в программу сами.
В раздел implementation вы можете сами включать помимо обработчиков событий любые объявления глобальных переменных, констант, типов, и описания функций и процедур. Если вы хотите из данного модуля получить доступ к другому разработанному вами модулю (другой форме), то в раздел implementation вам надо включить оператор uses, в котором указать файл модуля, доступ к которому вы хотите получить. Например, предложение uses Unit2, Unit3; обеспечит вам доступ ко всем элементам, объявленным в интерфейсе модулей Unit2 и Unit3.
в дальнейшем мы будем использовать
Интегрированная Среда Разработки (Integrated Development Environment — IDE, в дальнейшем мы будем использовать для нее аббревиатуру ИСР) — это среда, в которой есть все необходимое для проектирования, запуска и тестирования приложений и где все нацелено на облегчение процесса создания программ. ИСР интегрирует в себе редактор кодов, отладчик, инструментальные панели, редактор изображений, инструментарий баз данных — все, с чем приходится работать.Запустите Delphi с помощью меню Windows Пуск | Программы. Когда вы щелкнете на пиктограмме Delphi, перед вами откроется основное окно Интегрированной Среды Разработки (см. Рисунок 1.1). Для версий Delphi, отличных от Delphi 5, окно выглядит несколько иначе, но в основных чертах окна ИСР всех версий Delphi одинаковы.
Окно Редактора Кода без встроенного окна Code Explorer (а) и со встроенным окном (б)


В заголовке окна Редактора Кода отображается имя текущего файла, того, с текстом которого вы работаете. В верхней части окна вы можете видеть также закладки или ярлычки, указывающие текущую страницу. Приложения Delphi могут использовать много исходных файлов и закладки помогают вам переходить от одного из них к другому.
В окно Редактора Кода, как и в другие окна Delphi, встроена контекстная справка. Чтобы получить справку по какому-то слову кода (ключевому слову, написанному имени функции и т.п.) достаточно установить курсор на это слово и нажать клавишу F1. Вам будет показана соответствующая тема справки.
Следующим важнейшим элементом среды разработки является Инспектор Объектов (Object Inspector) — см. левое окно на Рисунок 1.1. Он обеспечивает простой и удобный интерфейс для изменения свойств объектов Delphi и управления событиями, на которые реагирует объект. Но прежде, чем рассказывать о нем, надо сказать несколько слов о принципах объектно-ориентированного программирования, на которых основан современный подход к созданию прикладных программ.
В современном представлении программа — это система объектов. Каждый объект характеризуется набором свойств. Свойство — это некоторые данные плюс процедуры их чтения и записи в объект. Эти процедуры называются методами и часто работают незримо для пользователя. Пусть некоторый объект имеет свойство А и вы пишете в программе оператор А := А * 10 (символы «:=» используются в языке Object Pascal для присваивания переменной, указанной в левой части оператора, значения, равного выражению в правой части). В действительности в этом случае программа вызывает метод чтения значения А, умножает это значение на 10, а затем вызывает метод записи значения в А, и передает в него вычисленную величину А * 10. Эти методы невидимы для пользователя. Но помимо них каждый объект обладает еще рядом методов — функций и процедур, оперирующих со свойствами объекта. Так что более полно объект можно характеризовать как совокупность свойств и методов. Перенеся объект на форму и нажав клавишу F1 вы можете увидеть во встроенной справке Delphi все его свойства (propeties) и методы (methods).
Помимо методов и свойств любой компонент характеризуется набором событий, на которое он может реагировать. Под событиями прежде всего понимаются действия пользователя: щелчок мыши, перемещение курсора, нажатие кнопок мыши или клавиш. Но и сами объекты тоже могут генерировать различные события. В объекте могут предусматриваться обработчики тех или иных событий, воспринимаемых данным компонентом. Фактически к написанию этих обработчиков и сводится программирование приложений. В обработчиках программист описывает, как должны реагировать компоненты на соответствующие события.
Завершив на этом краткий экскурс в объектно-ориентированное программирование вернемся к рассмотрению Инспектора Объектов. Окно Инспектора Объектов (рис, 1.3) имеет две страницы.
В верхней части окна ИСР

Ниже полосы главного меню расположены две инструментальные панели. Левая панель содержит два ряда быстрых кнопок, дублирующих некоторые наиболее часто используемые команды меню. Правая панель содержит палитру компонентов библиотеки визуальных компонентов (Visual Component Library — VCL). Именно эта библиотека и составляет основной предмет рассмотрения данной книги. В дальнейшем мы для краткости будем называть библиотеку визуальных компонентов просто библиотекой, благо это ближе к истине, так как в ней содержатся и визуальные (видимые пользователю), и невизуальные компоненты (они явным образом не видны пользователю). Палитра компонентов содержит ряд страниц, закладки которых видны в ее верхней части.
В основном поле окна вы можете видеть слева окно Инспектора Объектов (Object Inspector), с помощью которого вы в дальнейшем будете задавать свойства компонентов и обработчики событий. Правее вы можете видеть окно пустой формы, готовой для переноса на нее компонентов. Под ним расположено окно Редактора Кодов. Обычно оно при первом взгляде на экран невидимо, так как его размер равен размеру формы и окно Редактора Кодов полностью перекрывается окном формы. На Рисунок 1.1 это окно немного сдвинуто и выглядывает из под окна формы.
Подробные сведения о всех разделах меню не входят в задачу данной книги. Этому посвящена отдельная книга серии «Все о Delphi». Отметим только несколько основных команд, которые используются при разработке прикладных программ в среде Delphi (в дальнейшем изложении для краткости будем называть прикладные программы просто приложениями). Создание нового проекта приложения начинается с команды File | New Application. По этой команде открывается новый проект приложения с пустой формой, как вы можете это видеть на Рисунок 1.1. Сохранить на диске готовый проект или его заготовку можно командой File | Save Project As или File | Save All. Удобно также для сохранения использовать быстрые кнопки — третью или четвертую слева в верхнем ряду на Рисунок 1.1. Открыть ранее сохраненный проект можно командой File | Open или File | Open Project (вторая слева быстрая кнопка в верхнем ряду на Рисунок 1.1). Но если вы недавно работали с этим проектом, то удобнее воспользоваться командой File | Reopen или кнопочкой справа от быстрой кнопки Open (см. Рисунок 1.1). Эта команда дает вам возможность быстро выбрать проект из числа тех, с которыми вы работали последнее время.
Для компиляции и запуска на выполнение приложения надо выполнить команду Run | Run (быстрая кнопка с зеленой стрелкой, пятая в нижнем ряду на Рисунок 1.1).
Ограничимся описанием только этих основных команд. Подробное описание всех разделов меню вы можете найти во встроенной справке Delphi, которая вызывается или из меню Help, или нажатием клавиши F1.
Палитра компонентов — витрина библиотеки визуальных компонентов (Visual Component Library — VCL). Подробное описание палитры и методики работы с ней будет дано в главе 2, а компонентам, отображаемым в палитре, посвящена вся книга. Поэтому пока ограничимся только кратким рассмотрением техники переноса компонентов из палитры на форму. Чтобы осуществить эту операцию, надо открыть соответствующую страницу библиотеки и указать курсором мыши необходимый компонент. Затем надо сделать щелчок мышью в нужном месте формы и компонент разместится там. В дальнейшем вы можете отбуксировать его в любое другое место.
Форма, на которой размещаются компоненты, является основой почти всех приложений Delphi. Ее можно понимать как типичное окно Windows. Она имеет те же свойства, что присущи другим окнам Windows 95/98: управляющее меню в верхнем левом углу, полосу заголовка, занимающую верхнюю часть, кнопки развертывания, свертывания и закрытия окна в верхнем правом углу. Форма является контейнером (родителем — Parent)размещенных на ней компонентов. Впрочем, компоненты могут размещаться не непосредственно на форме, а в других компонентах — панелях. Тогда родителем по отношению к этим компонентам выступает соответствующая панель. Понятие родительского компонента в дальнейшем не раз будет использоваться в этой книге.
Одной из наиболее важных частей среды Delphi является окно Редактора Кода. Его вы можете видеть на Рисунок 1.1, а в укрупненном виде оно показано на Рисунок 1.2 а. В действительности, если вы откроете в первый раз это окно в Delphi 5, оно может выглядеть несколько иначе (рис 1.2 б) и включать в себя слева еще одно встроенное окно — окно Исследователя Кода Code Explorer. Это окно в данной книге мы рассматривать не будем. В большинстве случаев вы просто можете закрыть его, щелкнув на кнопке с крестиком в его правом верхнем углу. Чтобы оно вообще больше не появлялось, выполните команду Tools | Environment Options и в открывшемся диалоговом окне на странице Explorer выключите опцию Automatically show Explorer (показывать автоматически окно Code Explorer).
Основы разработки прикладных программ в Delphi
В этой небольшой вступительной главе излагаются краткие сведения о работе со средой разработки Delphi 5 и о структуре объектно-ориентированных прикладных программ. Те, кто уже работал с Delphi, могут со спокойной совестью пропустить эту главу. А тем, кто только начинает знакомство с Delphi 5, эту главу обязательно надо внимательно прочитать, чтобы спокойно воспринимать используемую далее терминологию и не испытывать трудностей с построением приложений, тестирующих различные компоненты. Для более глубокого освоения интегрированной среды разработки я рекомендую обеим категориям читателей приобрести первую книгу серии «Все о Delphi» — «Интегрированная среда разработки Delphi», в которой приводятся исчерпывающие сведения, необходимые для серьезного и продуктивного ее применения, и кроме того содержится краткий начальный курс обучения.Программный доступ к свойствам и методам объектов
Рассмотрим теперь, как получить из программы доступ к свойствам и методам объектов. Если вас интересует какое-то свойство объекта, то ссылка на него осуществляется в следующем формате: <имя объекта>.<имя свойства>После имени объекта пишется без пробела символ точки, а затем так же без пробела пишется имя свойства. Например, ссылка на свойство Caption метки Label1 осуществляется записью Label1.Caption (см. в тексте примера предыдущего раздела процедуру TForm1.Button1Click).
Иногда свойство объекта является в свою очередь объектом. Тогда в обращении к этому свойству указывается через точки вся цепочка предшествующих объектов. Например, метки имеют свойство Font — шрифт, которое в свою очередь является объектом. У этого объекта имеется множество свойств, в частности, свойство Color — цвет шрифта. Чтобы сослаться на цвет шрифта метки Label1, надо написать Label1.Font.Color. Это означает: свойство Color объекта Font, принадлежащего объекту Label1. Например, оператор Label1.Font.Color:=clRed; сделает надпись метки Label1 красной.
Аналогичная нотация с точкой используется и для доступа к методам объекта. Например, для метки, как и для большинства других объектов, определен метод Free, который уничтожает метку и освобождает занимаемую ею память. Если вы в какой-то момент решили уничтожить метку Label1 в своем приложении, то вы можете написать оператор Label1.Free;
Теперь посмотрим, чем различаются константы, переменные, функции и процедуры, включенные и не включенные в описание класса.
Если в приложении создается только один объект данного класса (в нашем примере — только один объект формы класса TForm1), то различие в основном чисто внешнее. Для процедур, объявленных в классе, в их описании в разделе implementation к имени процедуры должна добавляться ссылка на класс. В нашем примере имя процедуры Button1Click в описании этой процедуры в разделе implementation заменяется на TForm1.Button1Click. Для процедур, объявленных вне класса, такой замены не требуется.
Обращение к переменным и процедурам, описанным внутри и вне класса, из процедур, описанных вне класса, различается. К переменным и процедурам, описанным вне класса, обращение происходит просто по их именам, а к переменным и процедурам, описанным в классе, через имя объекта класса. Например, если вы вне класса хотите описать какую-то процедуру, изменяющую надпись метки Label1, вы должны обратиться к ее свойству Caption следующим образом: Form1.Label1.Caption. Только через ссылку на объект Form1 внешние по отношению к классу процедуры могут получить доступ ко всему, объявленному в классе.
Все эти ссылки на объект не требуются в процедурах, объявленных в классе. Поэтому в процедуре TForm1.Button1Click ссылка на объект Label1 не содержит дополнительной ссылки на объект формы.
Если в приложении создается несколько объектов одного класса, например, несколько форм класса TForm1, то проявляются более принципиальные различия между переменными, описанными внутри и вне класса. Переменные вне класса так и остаются в одном экземпляре. А переменные, описанные в классе, тиражируются столько раз, сколько объектов данного класса создано. Т.е. в каждом объекте класса TForm1 будут свои переменные, описанные в классе, и их значения никак не будут связаны друг с другом. Таким образом, в переменную, описанную внутри класса, можно заносить какую-то информацию, индивидуальную для каждого объекта данного класса. А переменная, описанная в модуле вне описания класса, может хранить только одно значение,
Теперь остановимся на вопросе об областях видимости элементов программы — констант, переменных, функций и процедур, т.е. о связи места их объявления в программе и места их использования. Частично этот вопрос мы уже затрагивали в предыдущем разделе, не упоминая о самом понятии область видимости.
Видимость отдельных элементов, размещенных в различных частях модуля, поясняется подробными комментариями в тексте модуля, приведенном в . Поэтому ограничимся только некоторым подведением итогов.
Страница свойств (а) и страница событий (б) Инспектора Объектов


Если щелкнуть на некоторых свойствах, например, на свойстве Color (цвет), то справа от имени свойства откроется окно выпадающего списка. Нажав в нем на кнопочку со стрелкой вниз, вы можете увидеть список возможных значений свойства (см. Рисунок 1.3 а). Например, смените значение свойства Color с принятого по умолчанию clBtnFace (цвет поверхности кнопок) на clWindow (цвет окна). Вы увидите, что поверхность формы изменит свой цвет.
Рядом с некоторыми свойствами вы можете видеть знак плюс (см., например, свойство BorderIcons на Рисунок 1.3 а). Это означает, что данное свойство является объектом, который в свою очередь имеет ряд свойств.
Найдите, например, свойство Font (шрифт). Рядом с ним вы увидите знак плюс. Щелкните на этом плюсе или сделайте двойной щелчок на свойстве Font. Вы увидите, что откроется таблица таких свойств, как Color (цвет), Height (высота), Name (имя шрифта) и др. Среди них вы увидите свойство Style (стиль), около которого тоже имеется знак плюс. Щелчок на этом плюсе или двойной щелчок на этом свойстве раскроет дополнительный список подсвойств, в котором вы можете, например, установить в true свойство fsBold (жирный). Кстати, для смены true на false и обратно в подобных булевых свойствах не обязательно выбирать значение из выпадающего списка. Достаточно сделать двойной щелчок на значения свойства, и оно изменится. После того, как вы просмотрели или изменили подсвойства, вы можете опять сделать двойной щелчок на головном свойстве или щелчок на знаке минус около него, и список подсвойств свернется.
Страница событий (Events) составляет вторую часть Инспектора Объектов (см. Рисунок 1.3 б). На ней указаны все события, на которые может реагировать выбранный объект. Например, если вам надо выполнить какие-то действия в момент создания формы (обычно это различные операции настройки), то вы должны выделить событие OnCreate. Рядом с именем этого события откроется окно с выпадающим списком. Если вы уже написали в своем приложении какие-то обработчики событий и хотите при событии OnCreate использовать один из них, вы можете выбрать необходимый обработчик из выпадающего списка. Если же вам надо написать новый обработчик, то сделайте двойной щелчок на пустом окне списка.
Вы попадете в окно Редактора Кода, в котором увидите текст: procedure TForm1.FormCreate(Sender: TObject); begin end;
Курсор будет расположен в пустой строке между ключевыми словами begin и end. Увиденный вами код — это заготовка обработчика события, которую автоматически сделала Delphi. Вам остается только в промежутке между begin и end написать необходимые операторы.
Если вы сделали эти операции, то вернитесь в Инспектор Объектов, выделите в нем, например, событие OnActivate и нажмите в нем кнопку выпадающего списка. Вы увидите в нем введенный вами ранее обработчик события OnCreate (этот момент изображен на Рисунок 1.3 б). Если вам надо использовать тот же самый обработчик и в событии OnActivate, просто выберите его из списка. Таким образом вы можете избежать дублирования в программе одних и тех же фрагментов кода.
Пользуясь Инспектором Объектов вы можете получить контекстную справку по свойствам или событиям. Для этого выделите в окне Инспектора Объектов интересующее вас свойство или событие и нажмите клавишу F1.
В Delphi 5 в Инспектор Объектов введена возможность фильтрации свойств и событий и возможность группировать их по категориям. Для того, чтобы воспользоваться этими возможностями, щелкните в окне Инспектора Объектов правой кнопкой мыши. Во всплывшем меню вы можете выбрать раздел View. Вам будет показан ряд категорий свойств и событий, около каждой из которых имеется индикатор. Вы можете включить индикаторы только у некоторых категорий и тогда в Инспекторе Объектов вы увидите события и свойства только указанных категорий. Выбор раздела Toggle переключит видимость разделов: те, которые были видимы станут невидимы и наоборот.
В том же меню, всплывающем при щелчке правой кнопкой мыши, вы можете выбрать раздел Arrange и в нем установить одну из двух возможностей: by Name — упорядочить свойства и события по алфавитной последовательности их имен, или by Category — упорядочить их по категориям. При этом некоторые свойства могут попасть одновременно в несколько категорий. Но это не имеет значения: вы можете менять их значения в любой категории и они синхронно изменятся во всех категориях.
Структура кода модуля
Рассмотрим теперь, как выглядят тексты разрабатываемых прикладных программ. Создайте простенькое приложение, в котором при щелчке пользователя на кнопке в окне появлялась бы какая-нибудь надпись. Выполните для этого последовательно следующие шаги.Диалоговое окно задания информации о шаблоне

Настройка палитры компонентов
В предыдущих разделах описаны страницы палитры компонентов по умолчанию. Но палитру компонентов можно перестраивать, перемещать компоненты с одной страницы на другую, вообще убирать не используемые вами компоненты с палитры, изменять последовательность страниц, перемещая в конец палитры те страницы, к которым вы редко или вообще никогда не обращаетесь. Можно также создавать новые страницы. Можно включать на страницы свои собственные шаблоны компонентов или разработанные вами новые компоненты.Вызвать настройку палитры компонентов можно щелчком правой кнопки мыши на палитре и выбором команды Properties в Delphi 5 и 4 или Configure в более ранних версиях из всплывшего меню. Можно выполнить для этого команду Component | Configure Palette. Можно также выполнить команду Tools | Environment Options в Delphi 3 — 5 или Options | Environment в Delphi 1. При таком вызове настройки надо перейти в открывшемся диалоговом окне на страницу Palette. Вид окна настройки палитры компонентов представлен на Рисунок 2.2.
Опции окна позволяют работать со страницами палитры. Для этого надо перейти в окно Pages и нажать кнопку Add, чтобы добавить новую страницу (см. страницу Мои шаблоны на Рисунок 2.2), кнопку Rename, чтобы переименовать страницу, кнопку Delete, чтобы удалить страницу (она должна быть к этому моменту пустой), кнопки Move Up или Move Down, чтобы изменить последовательность страниц в палитре.
Некоторые итоги
В данной главе мы рассмотрели работу с библиотекой компонентов. Последующие главы книги посвящены обзору различных компонентов, хранящихся в библиотеке. При этом тематика отдельных глав будет определяться не страницами палитры, а функциям, выполняемыми компонентами. Это позволит сравнивать компоненты, близкие по своему функциональному назначению, видеть их достоинства и недостатки и вырабатывать рекомендации по их применению.Окно настройки палитры компонентов

Организация библиотеки компонентов
Библиотека визуальных компонентов (Visual Component Library — VCL) Delphi содержит множество предопределенных типов компонентов, из которых пользователь может строить свою прикладную программу. Витрину библиотеки — палитру компонентов, вы видите расположенной справа в полосе инструментальных панелей интегрированной среды разработки Delphi. На этой палитре вы можете выделить курсором мыши нужный вам компонент и перенести его на форму. Палитра библиотеки в Delphi 5 приведена на Рисунок 2.1.Палитра компонентов

Чтобы перенести компонент на форму, надо открыть соответствующую страницу библиотеки и указать курсором мыши необходимый компонент. При этом кнопка-указатель

Есть и другой способ поместить компонент на форму — достаточно сделать двойной щелчок на пиктограмме компонента, и он автоматически разместится в центре вашей формы. Если вы выбрали компонент, а затем изменили ваше намерение размещать его, вам достаточно нажать кнопку указателя. Это прервет процесс размещения компонента и программа вернется в нормальный режим, в котором вы можете выбирать другой компонент или выполнять какую-то команду.
Имена компонентов, соответствующих той или иной пиктограмме, вы можете узнать из ярлычка, появляющегося, если вы задержите над этой пиктограммой курсор мыши. Если вы выберете в палитре компонент и нажмете клавишу F1, то вам будет показана справка по типу данного компонента. Тут надо сразу сделать одно замечание. Имена на ярлычках выглядят, например, так: MainMenu, Button и т.д. Однако, в Delphi все имена классов в действительности начинаются с символа «Т», например, TMainMenu, TButton. Под такими именами вы можете найти описания соответствующих компонентов во встроенной в Delphi справочной системе.
Палитра имеет ряд страниц, на которых скомпонованы пиктограммы всех компонентов, предопределенных в Delphi. По умолчанию в палитре Delphi 5 имеются страницы:
Примеры со страницы ActiveX также не документированы. Но если вы перенесете соответствующий компонент на форму и щелкнете на нем правой кнопкой мыши, то во всплывшем меню можете выбрать команду Property и некоторые другие, которые отобразят диалоговые окна, помогающие задать необходимые свойства компонента.
Многие из компонентов страниц Samples и ActiveX надо рассматривать скорее именно как примеры создания компонентов. Их полезно изучить, но для практического использования в приложениях многие из них не очень приспособлены.
В версиях Delphi, отличных от Delphi 5, страницы палитры несколько иные. Особенно отличается от других палитра в Delphi 1. Многие компоненты, которые имеются в Delphi 1 на страницах Standard, Additional и других, перенесены в последующих версиях на страницу Win 3.1 и заменены их более совершенными аналогами (см. таблицу 2.1). Компоненты страницы Win 3.1 сохраняются в 32-разрядных версиях для обеспечения совместимости при переводе приложений из Delphi 1.0 в более поздние версии. В 32-разрядных приложениях компоненты данной страницы применять не рекомендуется.
Таблица 2.1. Соответствие компонентов страницы Win 3.1 и новых 32-разрядных компонентов
Пример меню с инструментальной панелью и стандартными диалогами

И это все. Ваш шаблон появится в библиотеке. Вы можете убедиться в этом, посмотрев на указанную вами страницу библиотеки. Попробуйте провести подобный эксперимент с компонентами любого приложения. Например, с элементарным приложением, рассмотренным в (Рисунок 1.4) и содержащим всего два компонента: кнопку и метку, в которую при щелчке на кнопке пишется какой-то текст. Оформите эти компоненты как шаблон и занесите в библиотеку. Затем откройте новое приложение и перенесите на форму из библиотеки несколько экземпляров шаблона. В вашем проекте появится несколько кнопок и несколько меток, причем каждая кнопка будет управлять своей меткой. Поскольку метки будут иметь разные имена, то, посмотрев код модуля, вы увидите, что Delphi учла это: операторы обработчиков щелчков на разных кнопках различаются именами кнопок.
Создание и запись в библиотеку шаблонов компонентов и групп компонентов
Вы можете использовать страницы библиотеки для включения в них каких-то разработанных вами шаблонов компонентов или совокупностей компонентов. В последующих главах будут рассмотрены различные компоненты. В каждом из них в процессе проектирования задается некоторое множество значений свойств и пишутся обработчики событий. Причем очень часто компоненты с одинаковыми свойствами и с одинаковыми или почти одинаковыми обработчиками кочуют из приложения в приложение. Например, стандартные меню с одними и теми же разделами имеются в очень многих приложениях. Причем меню должны быть именно стандартными, с одними и теми же надписями в разделах, с одними и теми же «горячими» клавишами и т.п. Такая стандартизация — одно из основных требований, предъявляемых к приложениям Windows. Кроме того каждое меню по современным требованиям должно сопровождаться инструментальной панелью с быстрыми кнопками, дублирующими основные разделы меню. Эти быстрые кнопки должны иметь привычные пользователю пиктограммы, должны сопровождаться всплывающими ярлычками подсказок. Наконец, любое меню, имеющее разделы работы с файлами должно вызывать стандартные диалоги Windows открытия и закрытия файлов.Все это легко создать в Delphi, но для этого надо перенести на форму ряд компонентов (см. пример на Рисунок 2.3): MainMenu — главное меню (см. ), инструментальную панель ToolBar (см. ) и создать на ней множество быстрых кнопок, различные стандартные диалоги (см. ), список изображений для кнопок и разделов меню ImageList (см. ), диспетчер событий ActionList (см. ). В каждом из этих компонентов надо задать различные свойства, связать компоненты друг с другом, написать обработчики событий. Это немалая работа. И обидно, если начиная в дальнейшем похожие приложения придется выполнять ее снова и снова. Вот тут и могут помочь шаблоны, внесенные вами в библиотеку компонентов.
Страница ActiveX
Страница ActiveX содержит примеры компонентов ActiveX. Поскольку это всего лишь примеры, они снабжены в Delphi лишь минимальной документацией и во встроенной справке сведения о них отсутствуют. Но если вы перенесете соответствующий компонент на форму и щелкнете на нем правой кнопкой мыши, то во всплывшем меню можете выбрать команду Property и некоторые другие, которые отобразят диалоговые окна, помогающие задать необходимые свойства компонента.

диаграммы и графики

орфографический контроль

страницы Excel

диаграммы

диаграммы и графики
* В Delphi 5 этот компонент отсутствует. Поэтому на изображении страницы из палитры компонентов, взятом из Delphi 5, компонента Graph нет. По-видимому, автор писал большую часть книги для Delphi 4 а затем автоматически перенес текст под 5-ю версию. - Примечание разработчика электронной версии.
Страница Additional
Страница является дополнением страницы Standard и содержит ряд часто используемых компонентов общего назначения

кнопка с графикой

кнопка с фиксацией

маскированный ввод

таблица строк

таблица рисунков

изображение

формы

рамка

окно с прокруткой

список с флажками

разделитель панелей

метка с бордюром

инструментальная панель

события приложения

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

«Открыть файл»

«Сохранить файл как...»

«Открыть рисунок»

«Сохранить рисунок как...»

«Шрифты»

«Цвет»

«Печать»

«Установка принтера»

«Найти»

«Заменить»
Страница QReport
Страница QReport содержит компоненты, используемые при генерации отчетов.

отчет

детали

полоса текста

полоса

дочерняя полоса

группировка

метка

текст из базы данных

математические выражения

системные данные

многострочный текст

тексты с математическими выражениями

многострочный текст RTF

многострочный текст RTF базы данных

форма

изображение

изображение из базы данных

составной отчет

предварительный просмотр

фильтр текста

разделитель

фильтр HTML

диаграммы, графики
Страница Samples
Страница Samples содержит примеры компонентов. Поскольку это всего лишь примеры, они снабжены в Delphi лишь минимальной документацией и во встроенной справке сведения о них отсутствуют. Однако, исходные тексты примеров со страницы Samples имеются в каталоге ...\Source\Samples. Вы можете их просмотреть и понять, как построены эти примеры и как ими пользоваться.

индикатор хода процесса

таблица цветов

кнопка-счетчик

окно редактирования со счетчиком

дерево каталогов

календарь

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

фрейм

главное меню

всплывающее меню

метка

окно редактирования

многострочное окно редактирования

командная кнопка

контрольный индикатор с флажком

радиокнопка

окно списка

редактируемый описок

линейка прокрутки

групповое окно

группа радиокнопок

панель

список событий
Страница System
Страница System содержит компоненты, позволяющие использовать системные средства Windows.

таймер

окно для рисования

аудио и видео плеер

контейнер OLE

диалог с сервером DDE

данные, передаваемые серверу DDE

диалог с клиентом DDE

данные, передаваемые клиенту DDE
содержит компоненты, которые широко
Страница Win3. 1 содержит компоненты, которые широко используются Delphi 1, но в более поздних версиях Delphi заменены новыми. Компоненты этой страницы сохраняются в 32-разрядных версиях для обеспечения совместимости при переводе приложений из Delphi 1.0 в более поздние версии. В 32-разрядных приложениях компоненты данной страницы применять не следует.

блокнот с закладками

окно дерева

многостраничная форма

пачка страниц

заголовок

список файлов

структура каталогов

список дисков

список фильтров

создание списка данных

создание редактируемого списка данных
Страница Win32 содержит компоненты общего
Страница Win32 содержит компоненты общего назначения, позволяющие разрабатывать приложения в стиле Windows 95/98 и NT 4.x. Некоторые из этих компонентов аналогичны имеющимся на странице Win3.1.

страница с закладкой

многостраничное окно

список изображений

окно редактирования в формате RTF

ползунок

отображение хода процесса

кнопка-счетчик

«горячие» клавиши

воспроизведение немых клипов

ввод дат и времени

ввод дат

дерево

списки

заголовок

полоса состояния

инструментальная панель

инструментальная перестраиваемая панель

прокрутка страниц
Страницы библиотеки компонентов
В этом разделе приведены те страницы библиотеки компонентов, которые рассматриваются в данной книге. Книга посвящена компонентам общего назначения и поэтому в ней не затрагиваются страницы компонентов, используемых для работы с базами данных и для работы с Интернет. Не рассматриваются также серверы СОМ. Иначе говоря, из перечисленных в разделе 2.1 страниц не рассматриваются страницы Data Access, Data Controls, ADO, Inferbase, Midas, InternetExpress, Internet, FastNet, Decision Cube, Servers. Рассмотрение этих страниц требует серьезного обсуждения вопросов, связанных с базами данных и Интернет, которое не укладывается в допустимый объем данной книги. По той же причине в данной книге не рассмотрены компоненты, связанные с OLE и DDE. Все эти компоненты будут рассмотрены в отдельных книгах серии «Все о Delphi», посвященных работе с базами данных и Интернет и распределенным приложениям.Диалоговое окно проектирования компонента F1Book

Рассказывать подробно о работе с окном проектирования компонента F1Book невозможно из-за ограничения на объем данной книги. Те, кто знаком с Excel, без труда смогут в этом окне ориентироваться. К тому же в нем имеется встроенная справка, вызываемая командой меню Help или клавишей F1.
Щелкнув правой кнопкой мыши на компоненте F1Book, вы можете выбрать еще одну команду — Properties. В появившемся при этом диалоговом окне (Рисунок 3.16) вы можете, в частности, задать опции, определяющие, что будет видно или не видно в таблице при работе приложения: заголовки строк и столбцов (Row Heading и Column Heading), сетка (Gridlines), формулы вычислений (Formulas) и т.п.
Font — шрифт отображения текста в компонентах
Во всех компонентах ввода и отображения текстовой и цифровой информации шрифт текста, его размер, стиль (жирный, курсив и т.п.) определяются свойством Font. Это свойство в свою очередь является объектом типа TFont, который имеет множество подсвойств, устанавливаемых в процессе проектирования или программно во время выполнения приложения. Основные свойства шрифта следующие:Если система не может найти шрифта с заданной комбинацией свойств Name, Charset, Pitch и Size, Windows использует другой, близкий по характеристикам шрифт.
Основное свойство шрифта — Name. Если заданный именем Name шрифт недоступен в системе, Windows заменит его другим шрифтом. По умолчанию для всех компонентов Delphi, начиная с Delphi 2, задается имя шрифта MS Sans Serif и размер 8. Delphi 1 задает имя шрифта System и размер 10. Можно рекомендовать без особой нужды не изменять Name, так как заданные по умолчанию шрифты есть на любом компьютере с Windows, а другой шрифт может отсутствовать на компьютере пользователя вашей программы.
Свойства Size и Height определяют размер шрифта. Свойство Size определяет его в кеглях (пунктах, принятых в Windows), a свойство Height — в пикселях. Если значение Size задано отрицательным, то в размер входит верхний пиксель каждой строки. Если значение Size задано положительным, то этот пиксель не учитывается.
Значение Size связано со свойствами Height и PixelsPerInch (число пикселей на дюйм) соотношением: Font.Size = -Font.Height * 72 / Font.PixelsPerInch
Из соотношения, в частности, видно, что задание положительного значения Size ведет к отрицательному значению Height и наоборот.
Свойство Pitch обычно имеет значение fpDefault, при котором ширина устанавливается равной по умолчанию, т.е. описанной в шрифте заданного вида Name. Свойство может принимать также значения fpFixed — установка одинаковой ширины всех символов и fpVariable — установка различной ширины символов. Задание значения отличного от fpDefault заставляет Windows искать наилучший способ удовлетворить всем заданным характеристикам шрифта. Иногда это может привести к замене шрифта на шрифт другого, близкого вида, а иногда может вообще не повлиять на шрифт — все зависит от конкретного вида шрифта и даже от его версии.
Свойство Charset определяет набор символов шрифта. Каждый вид шрифта, определяемый его именем, поддерживает один или более наборов символов. Какие именно значения Charset поддерживает тот или иной шрифт можно установить из документации на него или экспериментальным путем, в частности, с помощью приведенного далее в этом разделе тестового приложения. Для шрифтов, поддерживающих несколько наборов символов, важно правильно задать Charset.
В Delphi предопределено много констант, соответствующих стандартным наборам символов. Большинство из них, относящихся к японскому, корейскому, китайскому и другим языкам, вряд ли представляют интерес для наших читателей. Поэтому отметим только одно значение — 204, обозначаемое также константой RUSSIAN_CHARSET, которое соответствует символам кириллицы.
По умолчанию в объектах типа TFont задается значение Charset, равное 1 или DEFAULT_CHARSET. При этом шрифт выбирается только по его имени Name и размеру Size. Если описанный шрифт недоступен в системе, то Windows заменит его другим шрифтом. Для имен шрифтов, принятых в Delphi по умолчанию, это обычно нормальный вариант. Но в ряде случаев полезно для отображения русских текстов с другими шрифтами заменить это значение на RUSSIAN_CHARSET. Это позволит отобразить символы кириллицы для тех шрифтов, для которых при DEFAULT_CHARSET символы кириллицы не отображаются нормально.
Свойство Style, задающее стиль, представляет собой множество или пустое, или содержащее одно или более из возможных значений. Ниже приведены примеры операторов, устанавливающих стиль шрифта: Label1.Font.Style := [ ]; {Обычный стиль} Label1.Font.Style := [fsBold]; {Полужирный} Label1.Font.Style := [fsBold, fsItalic]; {Полужирный курсив}
Значения свойств объекта Font можно присваивать по отдельности, как это сделано в приведенных выше операторах для свойства Style. Но чаще они задаются все сразу методом Assign, который записывает значения всех свойств одного объекта в другой. Пусть, например, на форме имеется компонент Memo1 (см. ), в котором расположен некоторый текст, компонент FontDialog1 — диалог выбора шрифта (см. ), и меню с разделом выбора шрифта, названным MFont. Для того, чтобы пользователь мог выбрать имя и атрибуты шрифта текста, отображаемого в Memo1, в обработчик события OnClick раздела меню MFont надо вставить оператор: if (FontDialog1.Execute) then Memo1.Font.Assign(FontDialog1.Font);
Если пользователь сменил атрибуты в диалоговом окне выбора шрифта, то метод FontDialog1.Execute (см. ) возвращает true и атрибуты шрифта компонента Memo1 устанавливаются равными выбранным пользователем.
Для того, чтобы продемонстрировать доступные в системе шрифты и исследовать влияние на них свойств Pitch и Charset, можно построить тестовое приложение, показанное на Рисунок 3.1 В нем используются компоненты MainMenu, Button, ComboBox, SpinEdit и Memo, которые будут рассмотрены позднее в разделах , , , и . Поэтому те, кто не знаком с этими компонентами, могут пока пропустить данный пример и вернуться к нему позднее.
Компоненты отображения иерархических данных — TreeView, Outline, ListView
Компоненты TreeView и Outline служат для отображения иерархических данных в виде дерева (см. пример на Рисунок 3.18), в котором пользователь может выбрать нужный ему узел или узлы. Иерархическая информация может быть самой разной: структура некоторого предприятия, структура документации учреждения, структура отчета и т.п. С каждым узлом дерева могут быть связаны некоторые данные.Возможности компонента TreeView несколько шире, чем компонента Outline. К тому же TreeView — 32-разрядный компонент, a Outline — 16-разрядный. Поэтому Outline целесообразно использовать только в приложениях, создаваемых с помощью Delphi 1.
Основным свойством TreeView, содержащим информацию об узлах дерева, является Items. Доступ к информации об отдельных узлах осуществляется через этот индексный список узлов. Например, TreeView1.Items[1] — это узел дерева с индексом 1 (второй узел дерева). Каждый узел является объектом типа TTreeNodes, обладающим своими свойствами и методами.
Во время проектирования формирование дерева осуществляется в окне редактора узлов дерева, представленном на Рисунок 3.20. Это окно вызывается двойным щелчком на компоненте TreeView или нажатием кнопки с многоточием около свойства Items в окне Инспектора Объектов.
Компоненты выбора из списков — ListBox, CheckListBox и ComboBox
Пример компонентов, рассмотренных в данном разделе и обеспечивающих выбор из списка, приведен на Рисунок 3.10.Многострочные окна редактирования Memo и RichEdit
Компоненты Memo и RichEdit (см. пример на Рисунок 3.7) являются окнами редактирования многострочного текста. Они так же, как и окно Edit, снабжены многими функциями, свойственными большинству редакторов. В них предусмотрены типичные комбинации «горячих» клавиш: Ctrl-C — копирование выделенного текста в буфер обмена Clipboard (команда Copy), Ctrl-X — вырезание выделенного текста в буфер Clipboard (команда Cut), Ctrl-V — вставка текста из буфера Clipboard в позицию курсора (команда Paste), Ctrl-Z — отмена последней команды редактирования.Обеспечение синтаксически правильного ввода текстовых и цифровых данных
Мы рассмотрели различные компоненты ввода информации. Теперь остановимся на проблеме сокращения числа возможных ошибок пользователя при вводе текстовых и числовых данных. Это очень серьезная проблема, особенно при построении приложений, рассчитанных на массового и не очень квалифицированного пользователя. Частично безошибочного ввода можно добиться за счет маскирования, описанного при рассмотрении компонента MaskEdit. Однако, и при маскированном вводе пользователь может ошибиться в синтаксисе, в результате чего будет генерироваться исключение. Еще лучше использовать, если возможно, выбор с помощью компонентов типа ListBox, ComboBox, StringGrid, DateTimePicker, SpinEdit и т.п. Это гарантирует, если не правильный в смысловом плане, то, по крайней мере, синтаксически правильный ввод. В качестве примера на Рисунок 3.17 приведены два варианта ввода информации о сотрудниках некоей организации. Слева ввод осуществляется с помощью компонентов Edit и пользователь может сделать любые ошибки.Справа ввод осуществляется с помощью компонентов ComboBox, SpinEdit, MaskEdit и DateTimePicker. ComboBox со значением свойства Style равным csDropDownList не допускает редактирования и, значит, пользователь просто не может указать неправильный отдел. SpinEdit гарантирует синтаксически правильное указание стажа работы, а его свойства MinValue и MaxValue определяют реальные пределы вводимого числа (например, число лет от 1 до 50). Аналогично компонент MaskEdit гарантирует синтаксически правильный ввод номера телефона, а компонент DateTimePicker гарантирует (даже без использования выпадающего календаря) синтаксически правильный ввод даты рождения и ее допустимые пределы (например, от 1930 г. до 1980 г.).
Окна редактирования Edit и MaskEdit
На Рисунок 3.5 вы можете увидеть примеры окон редактирования. Внешнее оформление окон редактирования определяется свойством BorderStyle, влияние которого на вид компонента вы можете увидеть на том же рисунке.Окно редактирования списков строк

Весь текст, представленный одной строкой типа String, внутри которой используются разделители типа символов возврата каретки и перевода строки, содержится в свойстве Text.
Доступ к отдельной строке текста вы можете получить с помощью свойства Strings[Index: Integer]. Индексы, как и везде в Delphi, начинаются с 0. Так что Memo1.Lines.Strings[0] — это текст первой строки. Учтите, что если окно редактирования изменяется в размерах при работе с приложением и свойство WordWrap = true, то индексы строк будут изменяться при переносах строк, так что в этих случаях индекс мало о чем говорит.
Свойство только для чтения Count указывает число строк в тексте.
Для очистки текста в окне надо выполнить процедуру Clear. Этот метод относится к самому окну, а не к его свойству Lines.
Для занесения новой строки в конец текста окна редактирования можно воспользоваться методами Add или Append свойства Lines. Для загрузки текста из файла применяется метод LoadFromFile. Сохранение текста в фале осуществляется методом SaveToFile.
Пусть, например, в вашем приложении имеется окно редактирования Edit1, в котором пользователь вводит имя сотрудника, и есть кнопка, при щелчке на которой в окно Memo1 должна занестись шапка характеристики этого сотрудника, после чего пользователь может заполнить текст характеристики.
Обработчик щелчка на кнопке может иметь вид: Memo1.Clear; Memo1.Lines.Add ('Х А Р А К Т Е Р И С Т И К А'); Memo1.Lines.Add('Сотрудник '+Edit1.Text) ;. Memo1.SetFocus;
Загрузка в окно Memo1 текста из файла (например, хранящейся в файле характеристики сотрудника) может осуществляться командой Memo1.Lines.LoadFromFile ('text.txt');
Сохранение текста в файле может осуществляться командой Memo1.Lines.SaveToFile('text.txt');
Свойство SelStart компонентов Memo и RichEdit указывает позицию курсора в тексте или начало выделенного пользователем текста. Свойство CaretPos указывает на запись, поле X которой содержит индекс символа в строке, перед которым расположен курсор, а поле Y — индекс строки, в которой находится курсор (встроенная справка Delphi утверждает другое — что свойство CaretPos содержит координаты курсора в пикселях; но, к счастью, это не так). Таким образом, учитывая, что индексы начинаются с 0, значения Memo1.CaretPos.Y+1 и Memо1.CaretPos.X+1 определяют соответственно номер строки и символа в ней, перед которым расположен курсор. В редакторе на Рисунок 3.8 именно эти значения (только не для Memo, а для RichEdit) использованы, чтобы отображать в строке состояния (см. ) позицию курсора.
Окно редактора масок с загруженными файлами стандартных масок: американским (а) и российским (6)


На Рисунок 3.6 б вы можете видеть его в работе, а на Рисунок 3.5 вы можете видеть ввод в окна с масками телефона и даты.
Рассмотрим примеры масок. В приведенном выше файле маска для ввода номера телефона имеет вид: !\(999\) 000-00-00;0;_ В этой маске символ 9 означает, что в соответствующей позиции может быть только цифра. Символ 0 означает, что в данной позиции должна быть цифра. Символ подчеркивания в конце маски будет заполнять пустые позиции. Таким образом, пользователю для ввода в окне будет отображен шаблон (см. Рисунок 3.5): (___)___-__-__
Поскольку второй раздел маски равен 0, то при чтении введенных пользователем значений свойства EditText и Text будут различаться. Свойство EditText для примера Рисунок 3.5 будет равно (095) 123-45-67, а свойство Text будет равно 0951234567. Если второй раздел маски сделать равным 1, то значения обоих свойств будут равны (095) 123-45-67.
Рассмотрим еще пример. Если с помощью EditMask надо ввести, например, целое число без знака, состоящее не более, чем из двух цифр, можно задать маску 99;0; . Если число обязательно должно быть двузначным, то маска должна иметь вид 00;0; .
Окно редактора списка объектов компонента ListView

Основное свойство компонента, описывающее состав отображаемой информации — Items. Во время проектирования оно может быть установлено специальным редактором (Рисунок 3.21), вызываемом щелчком на кнопке с многоточием рядом с этим свойством в окне Инспектора Объектов. Оно похоже на окно, описанное для компонента TreeView (Рисунок 3.20). Точно так же в нем задаются новые узлы кнопкой New Item и дочерние узлы — кнопкой New SubItem. Только смысл дочерних узлов другой: это информация, которая появляется только в режиме vsReport — в виде таблицы.
Для каждого узла задается свойство Caption — надпись, появляющаяся около пиктограммы. Для дочерних узлов это свойство соответствует надписи, появляющейся, в ячейках таблицы в режиме vsReport.
Свойство Image Index определяет индекс пиктограммы. Индекс соответствует спискам изображений, хранящимся в отдельных компонентах ImageList (см. ). Указания на эти компоненты вы можете задать в свойствах LargeImages для режима vsIcon и SmallImages для режимов vsSmallIcon, vsList и vsReport. Индексы начинаются с 0. Если вы укажете индекс -1 (значение по умолчанию), пиктограммы изображаться не будут. Свойство State Index в панели Item Properties позволяет добавить вторую пиктограмму в данный объект. Подобная пиктограмма может просто служить дополнительной характеристикой объекта. Индекс, указываемый как State Index, соответствует списку изображений, хранящихся в отдельном компоненте ImageList, указанном в свойстве StateImages компонента ListView.
Свойство Columns определяет список заголовков таблицы в режиме vsReport при свойстве ShowColumnHeaders (показать заголовки), установленном в true. Свойство Columns можно задать в процессе проектирования специальным редактором заголовков, вызываемом двойным щелчком на компоненте ListView или щелчком на кнопке с многоточием рядом со свойством Columns в окне Инспектора Объектов. Редактор заголовков уже (см. Рисунок 3.19).
Свойство Checkboxes определяет отображение индикатора с флажком около каждого элемента списка. Индикаторы можно устанавливать программно или их может изменять пользователь во время выполнения. Тогда узнать программно, установлен ли индикатор в некотором элементе Items[i], можно проверкой его свойства Checked. Например: for i:=0 to ListView1.Items.Count-1 do if ListView1.Items[i].Checked then ...;
Свойства HotTrack и HotTrackStyles определяют появление выделения при перемещении курсора над элементом списка и стиль этого выделения. Свойство HoverTime (только в Delphi 5) задает в миллисекундах задержку появления такого выделения.
Свойство списка Selected определяет выделенный пользователем элемент списка.
Свойство DragMode определяет режим перетаскивания элементов списка. Если вы хотите разрешить пользователю перетаскивать пиктограммы по площади списка в режимах vsIcon и vsSmallIcon, вам надо сделать следующее:
Метод Arrange: procedure Arrange(Code: TListArrangement); позволяет упорядочить пиктограммы в режимах vsIcon и vsSmallIcon. Параметр Code определяет способ упорядочивания:
Это свойство определяет рабочую область (прямоугольную рамку), в которой осуществляется выравнивание пиктограмм в режимах vsIcon и vsSmallIcon. Свойство WorkAreas представляет собой индексированный список, аналогичный Items, но совершенно независимый от него. Если WorkAreas — пустой список (ни одна область в него не добавлена), то упорядочивание пиктограмм производится в пределах всей клиентской области ListView. Добавление новой рабочей области осуществляется методом Add. Свойство рабочей области Rect типа TRect (см. ) определяет границы области. Свойство Color определяет цвет рамки, обрамляющей область. Свойство DisplayName определяет подпись под рамкой. И рамка, и подпись отображаются только в случае, если свойство списка ShowWorkAreas установлено в true.
Пример операторов, создающих в площади списка две рабочие области: ListView1.WorkAreas.Add; ListView1.WorkAreas[0].Rect := Rect(0,0, ListView1.ClientWidth div 4, ListView1.ClientHeight div 2); ListView1.WorkAreas.Items[0].DispiayName := 'Область 0'; ListView1.WorkAreas.Items[0].Color := clRed; ListView1.WorkAreas.Add; ListView1.WorkAreas[1].Rect := Rect(ListView1.ClientWidth div 2, 0, (ListView1.ClientWidth div 4)*3, ListView1.ClientHeight div 2); ListView1.WorkAreas.Items[1].DisplayName := 'Область 1';
Упорядочивание пиктограмм происходит в пределах той области, в которой они находятся. Если вы разрешили пользователю перетаскивать пиктограммы, как было описано ранее, то вид выравнивания будет зависеть от расположения пиктограмм. Если одна или несколько из них расположены в пределах одной области, а другие размещаются вне рабочих областей, то при вызове метода Arrange все они расположатся в пределах той области, в которой была хоть одна пиктограмма. Если же пиктограммы были расположены в нескольких рабочих областях, то они будет упорядочиваться в пределах областей их размещения.
Способ упорядочивания определяется соответствующим заданием свойства SortType, которое уже рассматривалось нами для компонента TreeView.
Окно редактора узлов дерева компонента TreeView

Кнопка New SubItem (новый дочерний узел) позволяет добавить в дерево дочерний узел. Он будет расположен на уровень ниже уровня того узла, который выделен курсором в момент щелчка на кнопке New SubItem.
Кнопка Delete (удалить) удаляет выделенный узел дерева. Кнопка Load позволяет загрузить структуру дерева из файла. Файл, хранящий структуру дерева — это обычный текстовый файл, содержащий тексты узлов. Уровни узлов обозначаются отступами. Например, файл дерева, изображенного на Рисунок 3.18 и 3.20, может иметь вид: производство цех 1 цех 2 цех 3 управление администрация бухгалтерия
Для каждого нового узла дерева можно указать ряд свойств в панели Item Properties окна на Рисунок 3.20. Это прежде всего свойство Text — надпись, появляющаяся в дереве около данного узла. Свойства Image Index и Selected Index определяют индекс пиктограммы, отображаемой для узла, который соответственно не выделен и выделен пользователем в данный момент. Эти индексы соответствуют списку изображений, хранящихся в отдельном компоненте ImageList (см. ). Указание на этот компонент вы можете задать в свойстве Images компонента TreeView. Индексы начинаются с 0. Если вы укажете индекс -1 (значение по умолчанию), пиктограммы изображаться не будут. Последнее свойство — State Index в панели Item Properties позволяет добавить вторую пиктограмму в данный узел, не зависящую от состояния узла. Подобная пиктограмма может просто служить дополнителъной характеристикой узла. Индекс, указываемый как State Index, соответствует списку изображений, хранящихся в отдельном компоненте ImageList, указанном в свойстве StateImages компонента TreeView.
Мы рассмотрели формирование дерева в процессе проектирования. Однако, дерево можно формировать или перестраивать и во время выполнения приложения. Для этого служит ряд методов объектов типа TTreeNodes. Следующие методы позволяют вставлять в дерево новые узлы:
Дерево может быть сколь угодно разветвленным. Например, следующие операторы добавляют дочерние узлы «бригада 1» и «бригада 2» в сформированный ранее узел «цех 1»: TreeView1.Items.AddChild(TreeView1.Items.Item[1], 'бригада 1'); TreeView1.Items.AddChild(TreeView1.Items.Item[l], 'бригада 2');
Текст, связанный с некоторым узлом, можно найти с помощью его свойства Text. Например, TreeView1.Items.Item[1].Text — это надпись «цех 1».
С каждым узлом может быть связан некоторый объект. Добавление таких узлов осуществляется методами AddObject, AddObjectFirst, InsertObject, AddChildObject, AddChildObjectFirst, аналогичными приведенным выше, но содержащими в качестве параметра еще указатель на объект:
Для удаления узлов имеется два метода: Clear, очищающий все дерево, и Delete(Node: TTreeNode), удаляющий указанный узел Node и все его узлы — потомки. Например, оператор TreeView1.Items.Clear; удалит в нашем примере все узлы, а оператор TreeView1.Items.Delete(TreeView1.Items.Item[1]); удалит узел «цех 1» и его дочерние узлы (если они имеются).
При удалении узлов, связанных с объектами, сами эти объекты не удаляются.
Реорганизация дерева, связанная с созданием или удалением многих узлов, может вызывать неприятное мерцание изображения. Избежать этого можно с помощью методов BeginUpdate и EndUpdate. Первый из них запрещает перерисовку дерева, а второй — разрешает. Таким образом, изменение структуры дерева может осуществляться по следующей схеме: TreeView1.Items.BeginUpdate; <операторы изменения дерева> TreeView1.Items.EndUpdate;
Если метод BeginUpdate применен подряд несколько раз, то перерисовка дерева произойдет только после того, как столько же раз будет применен метод EndUpdate.
Среди свойств узлов следует отметить Count — число узлов, управляемых данным, т.е. дочерних узлов, их дочерних узлов и т.п. Если значение Count узла равно нулю, значит у узла нет дочерних узлов, т.е. он является листом дерева.
Вернемся к свойствам компонента TreeView. Важным свойством компонента TreeView является Selected. Это свойство указывает узел, который выделен пользователем. Пользуясь этим свойством можно запрограммировать операции, которые надо выполнить для выбранного пользователем узла. Если ни один узел не выбран, значение Selected равно nil. При выделении пользователем нового узла происходят события OnChanging (перед изменением выделения). В обработчик события передаются параметры Node: TTreeNode — узел, который выделен в данный момент, и var AllowChange: Boolean — разрешение на перенос выделения. Если в обработчике задать AllowChange = false, то переключение выделения не произойдет.
У компонента TreeView имеется свойство RightClickSelect, разрешающее (при значении равном true) выделение узла щелчком как левой, так и правой кнопкой мыши.
Ряд событий компонента TreeView связан с развертыванием и свертыванием узлов. При развертывании узла происходят события OnExpanding (перед развертыванием) и OnExpanded (после развертывания). В обработчики обоих событий передается параметр Node: TTreeNode — развертываемый узел. Кроме того в обработчик OnExpanding передается параметр var AllowExpansion: Boolean, который можно задать равным false, если желательно запретить развертывание. При свертывании узла происходят события OnCollapsing (перед свертыванием) и OnCollapsed (после свертывания). Так же, как и в событиях, связанных с развертыванием, в обработчики передается параметр Node: TTreeNode — свертываемый узел, а в обработчик OnCollapsing дополнительно передается параметр var AllowCollapse: Boolean, разрешающий или запрещающий свертывание.
Свойство ReadOnly компонента TreeView позволяет запретить пользователю редактировать отображаемые данные. Если редактирование разрешено, то при редактировании возникают события OnEditing и OnEdited, аналогичные рассмотренным ранее (в обработчике OnEditing параметр var AllowEdit: Boolean позволяет запретить редактирование).
Ряд свойств компонента TreeView: ShowButtons, ShowLines, ShowRoot отвечают за изображение дерева и позволяют отображать или убирать из него кнопки, показывающие раскрытия узла, линии, связывающие узлы, и корневой узел. Поэкспериментируйте с этими свойствами и увидите, как они влияют на изображение.
Свойство SortType позволяет автоматически сортировать ветви и узлы дерева. По умолчанию это свойство равно stNone, что означает, что дерево не сортируется. Если установить SortType равным stText, то узлы будут автоматически сортироваться по алфавиту. Возможно также проводить сортировку по связанным с узлами объектам Data (значение SortType равно stData), одновременно по тексту и объектам Data (значение SortType равно stBoth) или любым иным способом. Для использования этих возможностей сортировки надо написать обработчик события OnСompare, в который передаются, в частности, параметры Node1 и Node2 — сравниваемые узлы, и по ссылке передается целый параметр Compare, в который надо заносить результат сравнения: отрицательное число, если узел Node1 должен располагаться ранее Node2, 0, если эти узлы считаются эквивалентными, и положительное число, если узел Node1 должен располагаться в дереве после Node2. Например, следующий оператор в обработчике события OnCompare обеспечивает обратный алфавитный порядок расположения узлов: Compare := - AnsiCompareText(Node1.Text, Node2.Text);
События OnCompare наступают после задания любого значения SortType, отличного от stNone, и при изменении пользователем свойств узла (например, при редактировании им надписи узла), если значение SortType не равно stNone. После сортировки первоначальная последовательность узлов в дереве теряется. Поэтому последующее задание SortType = stNone не восстанавливает начальное расположение узлов, но исключает дальнейшую генерацию событий OnCompare, т.е. автоматическую перестановку узлов, например, при редактировании их надписей. Если же требуется изменить характер сортировки или провести сортировку с учетом новых созданных узлов, то надо сначала задать значение SortType = stNone, а затем задать любое значение SortType, отличное от stNone. При этом будут сгенерированы новые обращения к обработчику событий OnCompare.
Имеются и другие возможности сортировки. Например, метод AlphaSort обеспечивает алфавитную последовательность узлов независимо от значения SortType, но при отсутствии обработчика событий OnCompare (если обработчик есть, то при выполнении метода AlphaSort происходит обращение к этому обработчику). Отличие метода AlphaSort от задания значения SortType = stText заключается в том, что изменение надписей узлов приводит к автоматической пересортировке дерева только при SortType = stText.
Мы рассмотрели основные возможности компонента TreeView. Компонент Outline похож на него. Структура дерева тоже содержится в свойстве Items и доступ к отдельным узлам также осуществляется через этот индексный список узлов. Но индексы начинаются с 1. Например, Outline1.Items[1] — это узел дерева с индексом 1 (первый узел дерева). Свойство Items имеет тип TOutlineNode. Его свойства и методы отличаются от свойств и методов типа узлов в TreeView. И заполняется структура дерева иначе: через свойство Lines типа TStrings. Редактор этого свойства можно вызвать, щелкнув на кнопке с многоточием около свойства Lines в окне Инспектора Объектов. Вы попадете в окно, в котором можете записать тексты всех узлов, делая отступы, чтобы выделить уровни узлов. Текст должен выглядеть так, как выше описывалось представление структуры в текстовом файле.
Пиктограммы, сопровождающие изображения узлов, задаются параметрами PictureOpen (пиктограмма развернутого узла), PictureClosed (пиктограмма свернутого узла), PictureMinus (пиктограмма символа «-» около развернутого узла, имеющего наследников), PicturePlus (пиктограмма символа «+» узла, имеющего наследников, но не развернутого), PictureLeaf (пиктограмма узла, не имеющего наследников — листа дерева). Основное отличие пиктограмм компонента Outline заключается в том, что они одинаковы для всех узлов одного типа, тогда как в TreeView можно задавать пиктограммы индивидуально для каждого узла.
Программно изменять структуру дерева можно с помощью методов Add, AddObject (добавление узла в дерево), Insert, InsertObject (вставка узла в заданную позицию), AddChild, AddChildObject (вставка дочернего узла), Delete (удаление узла).
Индекс выделенного пользователем узла можно определить через свойство SelectedItem. Если SelectedItem = 0, значит ни один узел не выделен. Текст выделенного узла определяется свойством Text: например, OutLine1.Items[Outline1.SelectedItem].Text
Впрочем, тот же самый текст даст и выражение OutLine1.Lines[Outline1.SelectedItem-1]
В заключение коротко рассмотрим компонент ListView. Он позволяет отображать в стиле Windows 95/98 данные в виде списков, таблиц, крупных и мелких пиктограмм. С подобным отображением все вы сталкиваетесь, раскрывая папки Windows.
Стиль отображения информации определяется свойством ViewStyle, которое может устанавливаться в процессе проектирования или программно во время выполнения. Свойство может принимать значения: vsIcon — крупные значки, vsSmallIcon — мелкие значки, vsList — список, vsReport — таблица. Что означает каждое из этих значений вы можете посмотреть в любой папке Windows на рабочем столе.
Окно редактора заголовков

Свойство AllowClick, равное по умолчанию true, определяет поведение секции как кнопки при щелчке пользователя на ней. В этом случае при щелчке генерируется событие OnSectionСlick компонента HeaderControl, в обработчик которого и надо вставить операторы, выполняющие необходимые действия.
Свойство Style может иметь значение hsText — в этом случае в заголовке отображается значение свойства Text, или hsOwnerDraw — в этом случае отображается то, что рисуется непосредственно на канве операторами, записанными в обработчике события OnDrawSection компонента HeaderControl.
Компонент Header обладает существенно меньшими возможностями, чем HeaderControl. В нем свойство Sections имеет тип TStrings и содержит только тексты заголовков, не позволяя регулировать пределы изменения ширины секций, их функционирование как кнопок и т.д. Таким образом, Header имеет смысл использовать только в Delphi 1.
Отображение текста в надписях компонентов Label, StaticText и Panel
Для отображения различных надписей на форме используются в основном компоненты Label, StaticText (появившийся только в Delphi 3) и Panel. Первые два из этих компонентов — метки, специально предназначенные для отображения текстов. Основное назначение панели Panel другое: компоновка компонентов в окне формы. Однако, панель можно использовать и для вывода текстов.Примеры вывода текста в компоненты приведены на Рисунок 3.4.
Тексты, отображаемые в перечисленных компонентах, определяются значением их свойства Caption. Его можно устанавливать в процессе проектирования или задавать и изменять программно во время выполнения приложения. Например: Label1.Caption := 'Новый текст';
Если требуется отобразить числовую информацию, можно воспользоваться функциями FloatToStr и IntToStr, переводящими соответственно числа с плавающей запятой и целые в строку. Для формирования текста, состоящего из нескольких фрагментов, можно использовать операцию «+», которая для строк означает их склеивание (конкатенацию). Например, если в программе имеется целая переменная I, отображающая число сотрудников некоторой организации, то вывести в метку Label1 информацию об этом можно оператором: Label1.Caption := 'Число сотрудников: '+IntToStr(I);
Во всех компонентах цвет фона определяется свойством Color, а цвет надписи — подсвойством Color свойства Font. Например, в большинстве меток (кроме верхней) на Рисунок 3.4 а и в правых метках на Рисунок 3.4 б задан цвет фона равным clWhite — белый. Если цвет специально не задавать, то цвет фона обычно сливается с цветом контейнера, содержащего метку, так что фон просто не заметен.
Для метки Label цвет и шрифт — единственно доступные элементы оформления надписи. Компоненты StaticText и Panel имеют кроме того свойство BorderStyle, определяющее рамку текста — бордюр. На Рисунок 3.4 б вы можете видеть влияние бордюра на вид метки StaticText. При стиле sbsNone метка StaticText по виду не отличается от метки Label. Вероятно, если уж использовать бордюр, то наиболее приятный стиль sbsSunken.
Перечень компонентов ввода и отображения чисел, дат и времени
В библиотеке визуальных компонентов Delphi существует ряд компонентов, позволяющих вводить, отображать и редактировать числа, даты и время. Конечно, с подобной информацией можно обращаться и просто как с текстовой, используя компоненты, описанные в разделе 3.3. Но это не удобно, так как не гарантирует от ошибок при вводе. В таблице 3.2 приведен перечень специализированных, компонентов ввода и отображения чисел, дат и времени с краткими характеристиками и указанием основных параметров, содержащих отображаемый или вводимый текст. В этой таблице не указаны аналогичные элементы отображения и редактирования текстов, содержащихся в базах данных, так как их рассмотрение выходит за рамки данной книги. Таблица 3.2. Компоненты ввода и отображения чисел, дат и времениграмма

(кнопка-счетчик)

(кнопка-счетчик с окном редактирования)

(окно ввода дат и времени)

(окно ввода дат)

(календарь на указанный месяц)

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

(метка)

(метка с бордюром)

(панель)

(окно редактирования)

(окно маскированного редактирования)

(многострочное окно редактирования)

(многострочное окно редактирования в формате RTF)

(окно списка)

(список с индикаторами)

(редактируемый список)

(таблица строк)
В последующих разделах мы рассмотрим возможности и методику применения этих компонентов.
Перечень компонентов
Ниже будут рассмотрены компоненты секционированного отображения текстов (заголовков) и компоненты отображения иерархических данных. Перечень этих компонентов дается в таблице 3.3. Таблица 3.3. Компоненты отображения секционированных текстов и иерархических данныхграмма

(заголовок)

(заголовок)

(окно дерева данных)

(список данных в стиле Windows 95)

(окно дерева данных)
На Рисунок 3.18 приведены примеры перечисленных в таблице компонентов.
Приложение с компонентом F1Book

Пример компонента StringGrid

Основные свойства компонента, определяющие отображаемый текст:
Свойства ColCount и RowCount определяют соответственно число столбцов и строк, свойства FixedCols и FixedRows — число фиксированных, не прокручиваемых столбцов и строк. Цвет фона фиксированных ячеек определяется свойством FixedColor. Свойства LeftCol и TopRow определяют соответственно индексы первого видимого на экране в данный момент прокручиваемого столбца и первой видимой прокручиваемой строки.
Свойство ScrollBars определяет наличие в таблице полос прокрутки. Причем полосы прокрутки появляются и исчезают автоматически в зависимости от того, помещается таблица в соответствующий размер, или нет.
Свойство Options является множеством, определяющим многие свойства таблицы: наличие разделительных вертикальных и горизонтальных линий в фиксированных (goFixedVertLine и goFixedHorzLine) и не фиксированных (goVertLine и goHorzLine) ячейках, возможность для пользователя изменять с помощью мыши размеры столбцов и строк (goColSizing и goRowSizing), перемещать столбцы и строки (goColMoving и goRowMoving) и многое другое. Важным элементом в свойстве Options является goEditing — возможность редактировать содержимое таблицы.
В основном компонент StringGrid используется для выбора пользователем каких-то значений, отображенных в ячейках. Свойства Col и Row показывают индексы столбца и колонки выделенной ячейки. Возможно также выделение пользователем множества ячеек, строк и столбцов.
Среди множества событий компонента StringGrid следует отметить событие OnSelectСell, возникающее в момент выбора пользователем ячейки. В обработчик этого события передаются целые параметры ACol и ARow — столбец и строка выделенной ячейки, и булев параметр CanSelect — допустимость выбора. Параметр CanSelect можно использовать для запрета выделения ячейки, задав его значение false. А параметры ACol и ARow могут использоваться для какой-то реакции программы на выделение пользователя. Например, оператор Label1.Caption:='Выбрана ячейка ' +IntToStr(ARow)+':'+IntToStr(ACol); выдаст в метку Label1 номер выбранной ячейки. А оператор Label1.Caption :=StringGrid1.Cells [ACol, ARow]; выведет в ту же метку текст выделенной ячейки. Конечно, в реальном приложения задача заключается не в том, чтобы вывести подобные тексты при выборе пользователем той или иной ячейки, а в том, чтобы сделать нечто более полезное.
Пример компонентов UpDown и SpinEdit

Основное свойство компонента UpDown — Associate, связывающее кнопки со стрелками с одним из оконных компонентов, обычно с Edit. Чтобы опробовать компонент UpDown, перенесите на форму его и окно редактирования Edit, расположив Edit там, где это требуется, а UpDown — в любом месте формы. Далее в выпадающем списке свойства Associate компонента UpDown выберите Edit1. Компонент UpDown немедленно переместится к Edit и как бы сольется с ним.
Свойство AlignButton компонента UpDown, которое может принимать значения udLeft или udRight, определяет, слева или справа от окна будут размещаться кнопки. Свойство Orientation, которое может принимать значения udHorizontal или udVertical, определяет, расположатся ли кнопки по вертикали (одна под другой — см. левый компонент на Рисунок 3.12) или по горизонтали (одна рядом с другой — см. правый компонент на Рисунок 3.12). Свойство ArrowKeys определяет, будут ли управлять компонентом клавиши клавиатуры со стрелками. Свойство Thousands определяет наличие или отсутствие разделительного пробела между каждыми тремя цифрами разрядов вводимого числа.
Свойства Min и Мах компонента UpDown задают соответственно минимальное и максимальное значения чисел, свойство Increment задает приращение числа при каждом нажатии на кнопку. Свойство Position определяет текущее значение числа. Это свойство можно читать, чтобы узнать, какое число задал пользователь. Его можно задать во время проектирования в диапазоне Min — Мах. Тогда это будет значение числа по умолчанию, отображаемое в окне в начале выполнения приложения.
Свойство Wrap определяет, как ведет себя компонент при достижении максимального или минимального значений. Если Wrap = false, то при увеличении или уменьшении числа до максимального или минимального значения это число фиксируется на предельном значении и нажатие кнопки, пытающейся увеличить максимальное число или уменьшить минимальное, ни к чему не приводит. Если же Wrap = true, то попытка превысить максимальное число приводит к его сбросу на минимальное значение. Аналогично, попытка уменьшить минимальное число приводит к его сбросу на максимальное значение. Т.е. изменение чисел «закольцовывается».
Если в компоненте Edit, связанном с UpDown, не задать ReadOnly равным true, то пользователь сможет редактировать число, не пользуясь кнопками со стрелками. Это удобно, если требуемое число далеко от указанного по умолчанию, а шаг приращения Increment в UpDown мал. Но тут проявляется серьезный недостаток компонента UpDown: ничто не мешает пользователю ввести по ошибке не цифры, а какие-то другие символы. Чтобы избавиться от этого недостатка, можно использовать прием, описанный в , который не дает возможность пользователю ввести в окно редактирования какие-то символы, кроме цифр. Но лучше для этих целей использовать компонент SpinEdit.
Свойства компонента SpinEdit похожи на рассмотренные, только имеют другие имена: свойства Min, Max, Position называются соответственно MinValue, MaxValue, Value. В целом компонент SpinEdit во многих отношениях удобнее простого сочетания UpDown и Edit. Так что, если не требуются какие-то из описанных выше дополнительных возможностей UpDown (нестандартное расположение кнопок, «закольцовывание» изменений и т.п.), то можно рекомендовать пользоваться компонентом SpinEdit.
Пример компонентов выбора из списков

Основное свойство обоих компонентов, содержащее список строк, — Items, имеющее рассмотренный тип TStrings. Заполнить его во время проектирования можно, нажав кнопку с многоточием около этого свойства в окне Инспектора Объектов. Во время выполнения работать с этим свойством можно, пользуясь свойствами и методами класса TStrings (см. ) — Clear, Add и другими.
В компоненте ListBox имеется свойство MultiSelect, разрешающее пользователю множественный выбор в списке (на Рисунок 3.10 это свойство установлено в true в среднем верхнем списке). Если MultiSelect = false (значение по умолчанию), то пользователь может выбрать только один элемент списка. В этом случае можно узнать индекс выбранной строки из свойства ItemIndex, доступного только во время выполнения. Если ни одна строка не выбрана, то ItemIndex = -1. Начальное значение ItemIndex невозможно задать во время проектирования. По умолчанию ItemIndex = -1. Это означает, что ни один элемент списка не выбран. Если вы хотите задать этому свойству какое-то другое значение, т.е. установить выбор по умолчанию, который будет показан в момент начала работы приложения, то сделать это можно, например, в обработчике события OnCreate формы, введя в него оператор вида ListBox1.ItemIndex:=0;
Если допускается множественный выбор (MultiSelect = true), то значение ItemIndex соответствует тому элементу списка, который находится в фокусе. При множественном выборе проверить, выбран ли данный элемент, можно проверив свойство Selected[Index: Integer] типа Boolean.
На способ множественного выбора при MultiSelect = true влияет еще свойство ExtendedSelect. Если ExtendedSelect = true, то пользователь может выделить интервал элементов, выделив один из них, затем нажав клавишу Shift и переведя курсор к другому элементу. Выделить не прилегающие друг к другу элементы пользователь может, если будет удерживать во время выбора нажатой клавишу Ctrl. Если же ExtendedSelect = false, то клавиши Shift и Ctrl при выборе не работают.
Свойство Columns определяет число столбцов, в которых будет отображаться список, если он не помещается целиком в окне компонента ListBox (в среднем верхнем списке на Рисунок 3.10 свойство Columns равно 2).
Свойство Sorted позволяет упорядочить список по алфавиту. При Sorted = true новые строки в список добавляются не в конец, а по алфавиту.
Свойство Style, установленное в lbStandard (значение по умолчанию) соответствует списку строк. Другие значения Style позволяют отображать в списке не только текст, но и изображения.
Имеется еще один компонент, очень похожий на ListBox — это список с индикаторами CheckListBox. Выглядит он так же, как ListBox (средний нижний список на Рисунок 3.10), но около каждой строки имеется индикатор, который пользователь может переключать. Индикаторы можно переключать и программно, если список используется для вывода данных и необходимо в нем отметить какую-то характеристику каждого объекта, например, наличие товара данного наименования на складе.
Все свойства, характеризующие компонент CheckListBox как список, аналогичны ListBox, за исключением свойств, определяющих множественный выбор. Эти свойства компоненту CheckListBox не нужны, поскольку в нем множественный выбор можно осуществлять установкой индикаторов. Свойства компонента CheckListBox, связанные с индикаторами, будут рассмотрены в .
Рассмотрим теперь компонент ComboBox. Стиль изображения этого компонента определяется свойством Style, которое может принимать следующие основные значения:
Если в окне проводилось редактирование данных, то ItemIndex = -1. По этому признаку можно определить, что редактирование проводилось.
Свойство MaxLength определяет максимальное число символов, которые пользователь может ввести в окно редактирования.
Если MaxLength = 0, то число вводимых символов не ограничено.
Как и в компоненте ListBox, свойство Sorted позволяет упорядочить список по алфавиту. При Sorted = true новые строки в список добавляются не в конец, а по алфавиту.
Пример редактора на основе компонента RichEdit

В Delphi 1 аналогичное окно имеет еще кнопки, позволяющие загрузить текст из файла или сохранить его в файле.
Примеры компонентов Memo и RichEdit

Компонент RichEdit работает с текстом в обогащенном формате RTF. При желании изменить атрибуты вновь вводимого фрагмента текста вы можете задать свойство SelAttributes. Это свойство типа TTextAttributes, которое в свою очередь имеет подсвойства: Color (цвет), Name (имя шрифта), Size (размер), Style (стиль) и ряд других. Например, введите на форму компонент RichEdit, диалог выбора шрифта FontDialog (см. ) и кнопку Button, которая позволит пользователю менять атрибуты текста. В обработчик щелчка кнопки можно было бы ввести текст: if FontDialog1.Execute then with RichEdit1.SelAttributes do begin Color:=FontDialog1.Font.Color; Name:=FontDialog1.Font.Name; Size:=FontDialog1.Font.Size; Style:=FontDialog1.Font.Style; end; RichEdit1.SetFocus;
В приведенном коде присваивается поочередно значение каждого свойства. Но этот текст можно кардинально сократить, воспользовавшись тем, что объекты SelAttributes и Font совместимы по типу. Поэтому можно присвоить сразу все свойства одного объекта другому: if FontDialog1.Execute then RichEdit1.SelAttributes.Assign(FontDialog1.Font); RichEdit1.SetFocus;
Запустите приложение и увидите, что вы можете менять атрибуты текста, выполняя отдельные фрагменты различными шрифтами, размерами, цветами, стилями. Устанавливаемые атрибуты влияют на выделенный текст или, если ничего не выделено, то на атрибуты нового текста, вводимого начиная с текущей позиции курсора (позиция курсора определяется свойством SelStart).
В компоненте имеется также свойство DefAttributes, содержащее атрибуты по умолчанию. Эти атрибуты действуют до того момента, когда изменяются атрибуты в свойстве SelAttributes.
Но значения атрибутов в DefAttributes сохраняются и в любой момент эти значения могут быть методом Assign присвоены атрибутам свойства SelAttributes, чтобы вернуться к прежнему стилю.
Свойство DefAttributes доступно только во время выполнения. Поэтому его атрибуты при необходимости можно задавать, например, в обработчике события OnCreate.
За выравнивание, отступы и т.д. в пределах текущего абзаца отвечает свойство Paragraph типа TParaAttributes. Этот тип в свою очередь имеет ряд свойств:
Следующий оператор приведет к тому, что текущий абзац будет отображаться как список, т.е. с маркерами: RichEdit1.Paragraph.Numbering:=nsBullet;
Уничтожение списка в текущем абзаце осуществляется оператором RichEdit1.Paragraph.Numbering:=nsNone;
В целом, если с помощью компонента ActionList (см. ) определено некоторое действие ввода и уничтожения списка, названное ABullet, то операторы обработки соответствующего действия могут иметь вид: if (ABullet.Checked) then RichEdit1.Paragraph.Numbering:=nsNone else RichEdit1.Paragraph.Numbering :=nsBullet; ABullet.Checked:=not ABullet.Checked;
Они обеспечивают переключение соответствующей быстрой кнопки и раздела меню из нажатого состояния (отмеченного) в ненажатое с соответствующим изменением вида текущего абзаца.
Свойства TabCount и Tab имеют смысл при вводе текста только при значении свойства компонента WantTabs = true. Это свойство разрешает пользователю вводить в текст символ табуляции. Если WantTabs = false, то нажатие пользователем клавиши табуляции просто переключит фокус на очередной компонент и символ табуляции в текст не введется.
Мы рассмотрели основные отличия Memo и RichEdit. Теперь остановимся на общих свойствах этих окон редактирования.
Свойства Alignment и WordWrap имеют тот же смысл, что, например, в метках, и определяют выравнивание текста и допустимость переноса длинных строк. Установка свойства ReadOnly в true задает текст только для чтения. Свойство MaxLength определяет максимальную длину вводимого текста. Если MaxLength = 0, то длина текста не ограничена. Свойства WantReturns и WantTab определяют допустимость ввода пользователем в текст символов перевода строки и табуляции.
Свойство ScrollBars определяет наличие полос прокрутка текста в окне. По умолчанию ScrollBars = ssNone, что означает их отсутствие. Пользователь может в этом случае перемещаться по тексту только с помощью курсора. Можно задать свойству ScrollBars значения ssHorizontal, ssVertical или ssBoth, что будет соответственно означать наличие горизонтальной, вертикальной или обеих полос прокрутки.
В качестве примера на Рисунок 3.8 приведен пример текстового редактора, использующего описанные выше свойства компонента RichEdit. Текст в окне редактора частично поясняет атрибуты шрифта, использованные при его написании.
Примеры компонентов отображения дат и времени

При вводе дат можно задать свойство DateMode равным dmComboBox — наличие выпадающего календаря, или равным dmUpDown — наличие кнопок увеличения и уменьшения (см. средний компонент DateTimePicker на Рисунок 3.13), напоминающих те, которые используются в описанных ранее компонентах UpDown и SpinEdit. Только в данном случае пользователь может независимо устанавливать с помощью кнопок число, месяц и год. Формат представления дат определяется свойством DateFormat, которое может принимать значения dfShort — краткий формат (например, 01.12.99), или dfLong — полный формат (например, 1 декабря 1999 г.).
Значение даты по умолчанию можно задать в Инспекторе Объектов через свойство Date. Это же свойство читается для определения заданной пользователем даты. При чтении Date надо учитывать тип этого свойства — TDateTime, представляющий собой число с плавающей запятой, целая часть которого содержит число дней, отсчитанное от некоторого начала календаря, а дробная часть равна части 24-часового дня, т.е. характеризует время и не относится к дате. Для 32-разрядных версий Delphi за начало календаря принята дата 12/30/1899 00 часов. В Delphi 1 за начало отсчета принят год 1, т.е. для перевода даты Delphi 1 в дату последующих версий Delphi надо вычесть из даты число 693594.
Для преобразования значения свойства Date в строку можно воспользоваться функцией DateToStr. Например, оператор Memol.Lines.Add('Дата: ' + DateToStr (DateTimePicker1.Date)); добавит в окно Memo1 строку вида «Дата: 01.12.98».
При вводе дат можно задать значения свойств MaxDate и MinDate, определяющих соответственно максимальную и минимальную дату, которую может задать пользователь.
В режиме ввода времени dtkTime введенное пользователем значение можно найти в свойстве Time, тип которого — тот же рассмотренный выше TDateTime. Преобразовать время в строку можно функцией TimeToStr.
Компонент MonthCalendar похож на компонент DateTimePicker, работающий в режиме ввода дат. Правда, в компоненте MonthCalendar предусмотрены некоторые дополнительные возможности: можно допустить множественный выбор дат в некотором диапазоне (свойство MultiSelect), можно указывать в календаре номера недель с начала года (свойство WeekNumbers), перестраивать календарь, задавая первый день каждой недели (свойство FirstDayOfWeek) и т.п. Для некоторых офисных приложений все это достаточно удобно.
Компонент Calendar представляет собой менее красочный и более обыденно оформленный календарь на один месяц. Вместо свойства Date в нем предусмотрены отдельные свойства Year -год, Month — месяц, Day — день. Все это целые числа, с которыми иногда удобнее иметь дело, чем с типом TDateTime. Перед отображением на экране или в процессе проектирования надо задать значения Month и Year, чтобы компонент отобразил календарь на указанный месяц указанного года. Впрочем, если вам надо иметь календарь на текущий месяц, надо установить в true значение свойства UseCurrentDate (установлено по умолчанию). В этом случае по умолчанию будет показан календарь на текущий месяц с выделенным в нем текущим днем. Свойство StartOfWeek задает день, с которого начинается неделя. По умолчанию задано 0 — воскресенье, как это принято в западных календарях. Но для нас все-таки как-то привычнее начинать неделю с рабочего дня — понедельника. Так что желательно задать StartOfWeek = 1.
Примеры окон редактирования

Окна редактирования снабжены многими функциями, свойственными большинству редакторов. Например, в них предусмотрены типичные комбинации «горячих» клавиш: Ctrl-C — копирование выделенного текста в буфер обмена Clipboard (команда Copy), Ctrl-X — вырезание выделенного текста в буфер Clipboard (команда Cut), Ctrl-V — вставка текста из буфера Clipboard в позицию курсора (команда Paste), Ctrl-Z — отмена последней команды редактирования. Правда, пользователи часто не догадываются об этих возможностях редактирования. Так что полезно напоминать им об этом соответствующими подсказками.
Свойство AutoSelect определяет, будет ли автоматически выделяться весь текст при передаче фокуса в окно редактирования. Его имеет смысл задавать равным true в случаях, когда при переключении в данное окно пользователь будет скорее заменять текущий текст, чем исправлять его. Имеются также свойства только времени выполнения SelLength, SelStart, SelText, определяющие соответственно длину выделенного текста, позицию перед первым символом выделенного текста и сам выделенный текст. Например, если в окне имеется текст «выделение текста» и в нем пользователь выделил слово «текста», то SelLength = 6, SelStart = 10 и SelText = 'текста'. Если выделенного текста нет, то свойство SelStart просто определяет текущее положение курсора.
Окна редактирования можно использовать и просто как компоненты отображения текста. Для этого надо установить в true их свойство ReadOnly и целесообразно установить AutoSelect в false. В этом случае пользователь не сможет изменять отображаемый текст и окно редактирования становится подобным меткам, рассмотренным в . Но имеются и определенные отличия. Во-первых, окна редактирования оформлены несколько иначе (сравните Рисунок 3.4 и 3.5.). А главное — окна редактирования могут вмещать текст, превышающий их длину. В этом случае пользователь может прокручивать этот текст, перемещая курсор в окне. Такими особенностями не обладает ни одна метка.
При использовании окон редактирования для вывода, ввода и редактирования чисел необходимо использовать функции взаимного преобразования строк и чисел. Для вывода это описанные при рассмотрении функции FloatToStr и IntToStr. При вводе это функции StrToFloat — преобразование строки в значение с плавающей запятой, и StrToInt — преобразование строки в целое значение. Если вводимый текст не соответствует числу (например, содержит недопустимые символы), то функции преобразования генерируют исключение EConvertError. Поэтому в программе необходимо предусмотреть обработку этого исключения. Например: var A: integer; try A := StrToInt(Edit1.Text); ...{операторы, использующие переменную А} except on EConvertError do ShowMessage('Вы ввели ошибочное число; повторите ввод');
Этот код обеспечивает сообщение пользователю об ошибке ввода и предотвращает ошибочные вычисления. Впрочем, это не лучший вариант предотвратить ошибочный ввод, поскольку пользователь узнает о своей ошибке только после того, как программа пытается использовать введенные данные. Лучше, если пользователь просто не сможет ввести неправильные символы. Например, если вы хотите, чтобы пользователь мог вводить в окно редактирования Edit только цифры и символ точки, вы можете в обработчик события OnKeyPress этого компонента вставить оператор: if not (Key in ['0'..'9', ',']) then Key := #0;
Этот оператор подменит все символы, кроме цифр и запятой, нулевым символом, который не занесется в текст окна Edit.
Свойство MaxLength определяет максимальную длину вводимого текста. Если MaxLength = 0, то длина текста не ограничена. В противном случае значение MaxLength указывает максимальное число символов, которое может ввести пользователь.
Свойство Modified, доступное только во время выполнения, показывает, проводилось ли редактирование текста в окне. Если вы хотите использовать это свойство, то в момент начала работы пользователя с текстом Modified надо установить в false. Тогда при последующем обращения к этому свойству можно по его значению (true или false) установить, было или не было произведено редактирование.
Свойство PasswordChar позволяет превращать окно редактирования в окно ввода пароля. По умолчанию значение PasswordChar равно #0 — нулевому символу. В этом случае это обычное окно редактирования. Но если в свойстве указать иной символ (например, символ звездочки «*»), то при вводе пользователем текста в окне будут появляться именно эти символы, а не те, которые вводит пользователь (см. Рисунок 3.5). Тем самым обеспечивается секретность ввода пароля.
Компонент MaskEdit отличается от Edit тем, что в нем можно задать строку маски в свойстве EditMask. Маска состоит из трех разделов, между которыми ставится точка с запятой (;). В первом разделе — шаблоне записываются специальным образом символы (см. таблицу 3.2), которые можно вводить в каждой позиции, и символы, добавляемые самой маской; во втором разделе записывается 1 или 0 в зависимости от того, надо или нет, чтобы символы, добавляемые маской, включались в свойство Text компонента; в третьем разделе указывается символ, используемый для обозначения позиций, в которых еще не осуществлен ввод. Прочитать результат ввода можно или в свойстве Text, которое в зависимости от вида второго раздела маски включает или не включает в себя символы маски, или в свойстве EditText, содержащем введенный текст вместе с символами маски. Таблица 3.2. Символы шаблона маски
В редакторе масок окно Sample Masks содержит наименования стандартных масок и примеры ввода с их помощью. В окно Input Mask надо ввести маску. Если вы выбираете одну из стандартных масок, то окно Input Mask автоматически заполняется и вы можете, если хотите, отредактировать эту маску.
Окно Character for Blanks определяет символ, используемый для обозначения позиций, в которых еще не осуществлен ввод (третий раздел маски). Индикатор Save Literal Characters определяет второй раздел маски: установлен, если второй раздел равен 1, и не установлен, если второй раздел равен 0.
Примеры вывода текста методом TextOut

Если в приведенном примере заменить оператор TextRect на TextRect(Rect(X1-5, Y1-5, X1+TextWidth(s)+5, Y1+TextHeight(s)+5), X1, Y1, s); то текст будет выводится полностью в красной прямоугольной области, на 5 пикселей отступающей во все стороны от текста. Именно этим отступом, делающим надпись более красивой, этот оператор отличается от всех предыдущих более простых операторов. Примеры вывода текста методом TextRect показаны на Рисунок 3.3. Вверху относительно короткий текст выровнен по середине прямоугольной рамки, в которой он рисуется. Ниже в аналогичной по размерам рамке помещен более длинный текст: «Это очень длинный текст». Вы видите, что текст урезан по размерам рамки. Внизу вы видите вывод, сделанный оператором, аналогичным приведенному выше и обеспечивающим зазор в 5 пикселей во все стороны от надписи любой длины. Очевидно, что это наиболее удачный вариант.
Примеры вывода текста методом TextRect

Примеры вывода текста в компоненты Label (а), StaticText (б) и Panel (в)



Размещение всех рассматриваемых компонентов на форме определяется, в частности, свойствами Тор (координата верхнего края), Left (координата левого края), Height (высота), Width (ширина). Имеются также свойства, определяющие изменение размера компонента при изменении пользователем во время выполнения приложения размеров окна. Это общие свойства всех оконных компонентов: Align (выравнивание компонента по всей верхней, левой, правой, нижней частям контейнера или по всей его клиентской области), Anchors (привязка сторон компонента к сторонам контейнера), Constraints (ограничения допустимых изменений размеров). Учтите, что использование в свойстве Anchors одновременной привязки компонента к противоположным сторонам контейнера приведет к растягиванию или сжатию компонента при изменении пользователем размеров окна. При этом в метках в ряде случаев наблюдаются неприятности, связанные с перемещением надписей (если они, например, выровнены по центру). Чтобы избежать этих неприятностей, надо в обработку события формы OnResize вставить операторы, перерисовывающие компоненты методом Repaint, например: StaticText1.Repaint;
Размер меток Label и StaticText определяется также свойством AutoSize. Если это свойство установлено в true, то вертикальный и горизонтальный размеры компонента определяются размером надписи. Если же AutoSize равно false, то выравнивание текста внутри компонента определяется свойством Alignment, которое позволяет выравнивать текст по левому краю, правому краю или центру клиентской области метки. В панели Panel также имеется свойство AutoSize, но оно не относится к размерам надписи Caption. Однако, свойство выравнивания Alignment работает и для панели.
В метке Label имеется свойство Wordwrap — допустимость переноса слов длинной надписи, превышающей длину компонента, на новую строчку. Чтобы такой перенос мог осуществляться, надо установить свойство WordWrap в true, свойство AutoSize в false (чтобы размер компонента не определялся размером надписи) и сделать высоту компонента такой, чтобы в нем могло поместиться несколько строк (см. пример правой нижней метки на Рисунок 3.4 а). Если WordWrap не установлено в true при AutoSize равном false, то длинный текст, не помещающийся в рамке метки, просто обрезается (см. пример левой нижней метки на Рисунок 3.4 а).
В метке StaticText перенос длинного текста осуществляется автоматически, если значение AutoSize установлено в false и размер компонента достаточен для размещения нескольких строк. Для того, чтобы в StaticText осуществлялся перенос при изменении пользователем размеров окна, надо осуществлять описанную выше перерисовку компонента методом Repaint в обработчике события формы OnResize.
В панели размещение надписи в нескольких строках невозможно.
Можно отметить еще одно свойство меток Label и StaticText, превращающее их в некоторое подобие управляющих элементов. Это свойство FocusControl — фокусируемый компонент. Если в свойстве метки Caption поместить перед одним из символов символ амперсант «&», то символ, перед которым поставлен амперсант, отображается в надписи метки подчеркнутым (сам амперсант вообще не отображается). Если после этого обратиться к свойству метки FocusControl, то из выпадающего списка можно выбрать элемент, на который будет переключаться фокус, если пользователь нажмет клавиши ускоренного доступа: клавишу Alt + подчеркнутый символ. Подобные клавиши ускоренного доступа предусмотрены в управляющих элементах: разделах меню (см. ) и кнопках (см. ). Благодаря свойству FocusControl метки могут обеспечить клавишами ускоренного доступа иные элементы, например, окна редактирования (см. ), в которых такие клавиши не предусмотрены. Только для того, чтобы клавиши ускоренного доступа в метках срабатывали, необходимо установить свойство ShowAccelChar этих меток в true.
Для отображения текстовой информации, и даже с дополнительной возможностью прокрутки длинных текстов, можно использовать также Edit и MaskEdit в режиме Readonly.
Секционированное отображение текстов — компоненты HeaderControl и Header
Компоненты заголовков HeaderControl и Header можно рассматривать в какой-то степени как частный случай таблицы строк, состоящей всего из одной строки. По умолчанию свойство Align в HeaderControl задано равным alTop, что обеспечивает размещение компонента вверху окна формы. Но это свойство можно изменить, например, на alNone и разместить компонент в любом необходимом месте. Заголовок состоит из ряда секций, причем пользователь во время выполнения приложения может изменять ширину отдельных секций с помощью мыши.Основное свойство компонента HeaderControl — Sections. Оно является списком объектов типа THeaderSection, каждый из которых описывает одну секцию заголовка. Свойство Sections можно задать во время проектирования, нажав кнопку с многоточием рядом с этим свойством в Инспекторе Объектов или просто сделав двойной щелчок на компоненте HeaderControl. В обоих случаях перед вами откроется окно редактора заголовков, представленное на Рисунок 3.19. Левая быстрая кнопка позволяет добавить новую секцию в заголовок. Следующая быстрая кнопка позволяет удалить секцию. Кнопки со стрелкой позволяют изменять последовательность секций.
Страницы Excel — компонент F1Book
Очень интересным компонентом является F1Book на странице ActiveX. Этот компонент позволяет встроить в ваше приложение таблицы типа Excel (Рисунок 3.14), которые пользователь может заполнять соответствующими числами, а компонент будет производить по заданным формулам вычисления и тут же отображать их результаты в указанных ячейках. В таблицу можно встроить диаграммы и графики различных типов. И все изменения, вносимые пользователем в данные таблицы, немедленно будут отображаться в диаграммах. Таким образом вы можете включать в свое приложение различные бланки смет, счетов, ведомостей, с которыми будет работать пользователь, различные таблицы, производящие статистические или технические расчеты и т.п.Таблица строк — компонент StringGrid
Компонент StringGrid (см. пример на Рисунок 3.11) представляет собой таблицу, содержащую строки. Данные таблицы могут быть только для чтения или редактируемыми. Таблица может иметь полосы прокрутки, причем заданное число первых строк и столбцов может быть фиксированным и не прокручиваться. Таким образом, можно задать заголовки столбцов и строк, постоянно присутствующие в окне компонента. Каждой ячейке таблицы может быть поставлен в соответствие некоторый объект.Тестовое приложение свойств шрифта

Расположение всех компонентов может примерно соответствовать приведенному на Рисунок 3.1. Далее надо написать обработчики событий: события OnCreate при созданий формы (FormCreate), выбора раздела меню (MFontClick), изменений в списке CBName (CBNameClick) и в списке CBPitch (CBPitchChange) и щелчка на кнопке Button1 (Button1Click). Ниже приведен текст всех этих обработчиков. procedure TForm1.FormCreate(Sender: TObject); var i: integer; begin // Загрузка в CBName всех шрифтов системы for i := 0 to Screen.Fonts.Count - 1 do CBName.Items.Add(Screen.Fonts[i]); CBName.ItemIndex := 0; CBPitch.ItemIndex := 0; // Загрузка в Memo1 имени первого шрифта Memo1.Font.Name := CBName.Items[CBName.ItemIndex]; end; procedure TForm1.MFontClick(Sender: TObject); begin // Задание диалогу текущих атрибутов шрифта Memo1 FontDialog1.Font.Assign(Memo1.Font); if (FontDialog1.Execute) then begin // Задание атрибутов шрифта, выбранных в диалоге пользователем Memo1.Font.Assign(FontDialog1.Font); CBName.Text:=Memo1.Font.Name; SpinEdit1.Value:=FontDialog1.Font.CharSet; end end; procedure TForm1.CBNameChange(Sender: TObject); begin // Изменение свойства Name Memo1.Font.Name := CBName.Items[CBName.ItemIndex]; end; procedure TForm1.CBPitchChange(Sender: TObject); begin // Изменение свойства Pitch case CBPitch.ItemIndex of 0: Memo1.Font.Pitch := fpDefault; 1: Memo1.Font.Pitch := fpFixed; 2: Memo1.Font.Pitch := fpVariable; end; end; procedure TForm1.Button1Click(Sender: TObject); begin // Изменение свойства CharSet Memo1.Font.CharSet := SpinEdit1.Value; end;
Комментарии в тексте поясняют отдельные операции. Запустите приложение и исследуйте шрифты, зарегистрированные в системе, и влияние на них свойств Pitch и Charset.
Ввод и отображение целых чисел — компоненты UpDown и SpinEdit
В Delphi имеются специализированные компоненты, обеспечивающие ввод целых чисел — UpDown и SpinEdit (см. пример на Рисунок 3.12).Ввод и отображение дат и времени – компоненты DateTimePicker, MonthCalendar, Calendar
Примеры компонентов ввода и отображение дат и времени приведены на Рисунок 3.13.Вывод текста на канву Canvas
В последующих разделах будут рассмотрены специализированные компоненты отображения и ввода текстовой и цифровой информации. Но помимо этих компонентов отображать текстовые надписи можно непосредственно на свойстве Canvas (канва, холст) любого компонента, имеющего это свойство, в частности, непосредственно на форме.При выводе текста на канву фон надписи определяется свойством канвы Brush — кисть. Это свойство является в свою очередь объектом, который может хранить в своем свойстве Style некий шаблон заполняющей штриховки. Но по умолчанию этот шаблон соответствует сплошной закраске фона. А свойство кисти Color задает цвет фона. Атрибуты выводимого текста определяются рассмотренным в свойством канвы Font.
Вывод текста на канву может осуществляться методом TextOut, объявленным следующим образом: procedure TextOut(X, Y: Integer; const Text: string); Процедура TextOut пишет строку текста Text на канве, начиная с позиции с координатами (X, Y). Если цвет кисти Brush в момент вывода текста отличается от того, которым закрашена канва, то текст получится выведенным в цветной прямоугольной рамке. Но ее размеры будут точно равны размерам надписи. Если требуется более красивая рамка с отступом от текста, следует применять другой метод — TextRect. Этот метод определен следующим образом: procedure TextRect (Rect :TRect; X, Y: Integer; const Text: string);
Процедура TextRect пишет строку текста Text на канве, начиная с позиции с координатами (X, Y) — это левый верхний угол надписи. Параметр Rect типа TRect определяет прямоугольную рамку, в которую вписывается текст. Тип TRect определен следующим образом: TRect = record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint) ; end;
Координаты задаются и как четыре целых числа, определяющих координаты в пикселях левой (Left), верхней (Тор), правой (Right) и нижней (Bottom) сторон прямоугольника, и как две точки типа TPoint, представляющие собой координаты левого верхнего и правого нижнего углов: type TPoint = record X: Longint; Y: Longint; end;
Началом координат обычно считается левый верхний угол экрана или окна.
При выводе текста методом TextRect часть текста, не помещающаяся в прямоугольную область Rect, усекается.
Надписи и в методе TextOut, и в методе TextRect делаются в соответствии с текущими установками шрифта Font. Пространство внутри области Rect в методе TextRect и фон надписи в методе TextOut закрашиваются текущей кистью Brush.
Для выравнивания позиции текста на канве и для задания красивой рамки в методе TextRect можно использовать методы, дающие высоту и длину выводимого текста в пикселях: методы TextWidth, TextHeight и TextExtent. Функция TextWidth: function TextWidth (const Text: string): Integer; возвращает длину в пикселях текста Text, который предполагается написать на канве текущим шрифтом. Функция TextHeight: function TextHeight(const Text: string): Integer; возвращает высоту в пикселях текста Text. Применение методов TextWidth и TextHeight позволяет перед выводом текста на канву определить размер надписи и расположить ее и другие элементы изображения наилучшим образом.
Имеется еще метод TextExtent, определенный следующим образом: type TSize = record сх: Longint; су: Longint; end; function TextExtent (const Text: string): TSize; Метод возвращает одновременно и высоту, и длину текста. Метод TextWidth возвращает то же, что TextExtent(Text).cx, а метод TextHeight возвращает то же, что TextExtent(Text).cy.
Рассмотрим примеры применения всего этого на практике. Оператор Form1.Canvas.TextOut(10, 10, s); выводит текст, хранящийся в строковой переменной s, на канву формы Form1, начиная с позиции (10, 10). Если форма Form1 является той, в обработчике которой написан этот оператор, то ссылку на Form1 можно не делать: Canvas.TextOut(10, 10, s) ; Оператор with Canvas do TextOut((ClientWidth - TextWidth(s)) div 2, TextHeight(s), s) ; выводит текст на канву текущей формы, выравнивая его при любом шрифте по середине ширины канвы (ширина определяется свойством ClientWidth) и отступив одну строчку сверху. То же самое делает и оператор with Canvas do TextOut((ClientWidth - TextExtent(s).cx) div 2, TextExtent(s).cy, s); который вместо методов TextWidth и TextHeight использует метод TextExtent.
Оператор with Canvas do TextOut((ClientWidth - TextWidth(s)) div 2, (ClientHeight - TextHeight(s)) div 2, s); выводит текст в середину формы и по высоте и по ширине.
Примеры вывода текста методом TextOut приведены на Рисунок 3.2. Основной недостаток такого вывода заключается в том, что рамка текста получается без зазоров. Такой вывод выглядит некрасиво. Вероятно, вывод этой функцией оправдан только в случае, если цвет фона (свойство Canvas.Brush.Color) совпадает с цветом поверхности компонента, на канву которого выводится текст. В этом случае он просто появится без рамки.
Анимация копирования файла

Воспроизводить фрагмент клипа можно и методом Play, который определен следующим образом: procedure Play(FromFrame, ToFrame: Word; Count: Integer);
Метод воспроизводит заданную последовательность кадров клипа от FromFrame до ToFrame включительно и воспроизведение повторяется Count раз. Если FromFrame = 1, то воспроизведение начинается с первого кадра. Значение ToFrame должно быть не меньше FromFrame и не больше значения, определяемого свойством FrameCount (свойство только для чтения), указывающим полное число кадров в клипе. Если Count = 0, то воспроизведение повторяется до тех пор, пока не будет выполнен метод Stop.
Выполнение Play идентично заданию StartFrame равным FromFrame, StopFrame равным ToFrame, Repetitions равным Count и последующей установке Active в true.
В компоненте Animate предусмотрены события OnClose, OnOpen, OnStart и OnStop, генерируемые соответственно в моменты закрытия и открытия компонента, начала и окончания воспроизведения.
Давайте теперь построим тестовое приложение, показывающее возможности компонента Animate. Установите в том приложении, которое вы уже начали, свойство Visible компонента Animate в false. Это надо для того, чтобы изображение возникало только тогда, когда произойдет соответствующее событие: копирование файлов, поиск файлов и т.п. В тестовом приложении мы будем имитировать начало и окончание события, которое должно сопровождаться мультипликацией, нажатиями кнопок запуска и останова воспроизведения. Поэтому верните значение свойства Repetitions в 0, чтобы воспроизведение длилось до окончания события. Свойство Active установите в false. Полезно также установить свойство AutoSize в false, а свойство Center в true, чтобы изображение всегда появлялось в центре экрана.
А теперь добавьте в приложение 3 кнопки (Рисунок 4.22). Первая из них (назовите ее ВWind) будет начинать процесс воспроизведения поочередно всех стандартных клипов Windows. Вторая кнопка (назовите ее BStop) пусть завершает воспроизведение очередного клипа. А третью кнопку (назовите ее BFile) введем для того, чтобы показать, что компонент может воспроизводить изображения из заданного файла .avi. Чтобы пользователь мог выбрать файл изображения, добавьте на форму компонент OpenDialog (см. ) и задайте его фильтр (свойство Filter) равным видео *.avi *.avi
Теперь все приготовления закончены и осталось только написать обработчики событий. Код обработчиков может иметь вид:
Демонстрация возможностей компонента Animate

Обработчик события OnClick кнопки BStop останавливает воспроизведение методом Stop.
Обработчик события OnStop компонента Animate1 увеличивает счетчик на 1, в зависимости от значения счетчика загружает в компонент соответствующий клип Windows и активизирует компонент. Если все клипы уже воспроизведены, то компонент делается невидимым.
Обработчик события OnClick кнопки BFile загружает в компонент видео файл, выбранный пользователем.
Выполните приложение и проверьте его в работе. В качестве видео файла можете использовать файл ...\Demos\Coolstuf\Cool.avi, поставляемый с примерами Delphi (на Рисунок 4.22 изображен момент воспроизведения именно этого файла).
Форма приложения Рисунок 4.8 с занесенными в нее условными данными

Выйдите из Редактора Диаграмм, выделите в вашем приложении нижний компонент Chart и повторите для него задание свойств с помощью Редактора Диаграмм. В данном случае вам надо будет задать две серии, если хотите отображать на графике две кривые, и выбрать тип диаграммы Line. Поскольку речь идет о графиках, вы можете воспользоваться закладками Axis и Walls для задания координатных характеристик осей и трехмерных граней графика.
На этом проектирование внешнего вида приложения завершается. Осталось написать код, задающий данные, которые вы хотите отображать. Для тестового приложения давайте зададим в круговой диаграмме просто некоторые константные данные, а в графиках — функции синус и косинус.
Для задания отображаемых значений надо использовать методы серий Series. Остановимся только на трех основных методах.
Метод Clear очищает серию от занесенных ранее данных.
Метод Add: Add(Const AValue: Double; Const ALabel: String; AColor: TColor) позволяет добавить в диаграмму новую точку. Параметр AValue соответствует добавляемому значению, параметр ALabel — название, которое будет отображаться на диаграмме и в легенде, AColor — цвет. Параметр ALabel — не обязательный, его можно задать пустым: ''.
Метод AddXY: AddXY(Const AXValue, AYValue: Double; Const ALabel: String; AColor: TColor) позволяет добавить новую точку в график функции. Параметры AXValue и AYValue соответствуют аргументу и функции. Параметры ALabel и AColor те же, что и в методе Add.
Таким образом, процедура, обеспечивающая загрузку данных в нашем примере, может иметь вид: const A1=155; A2=251; A3=203; A4=404; var i: word; begin With Series1 do begin Clear; Add(A1, 'Цех 1', clYellow); Add(A2, 'Цех 2', clBlue); Add(A3, 'Цех 3', clRed); Add(A4, 'Цех 4', clPurple); end; Series2.Clear; Series3.Clear; for i:=0 to 100 do begin Series2.AddXY(0.02*Pi*i, sin(0.02*Pi*i), '', clRed); Series3.AddXY(0.02*Pi*i, cos(0.02*Pi*i), '', clBlue); end; end;
Эту процедуру можно включить в обработку щелчка какой-нибудь кнопки, в команду меню или просто в событие OnCreate формы. Операторы Clear нужны, если в процессе работы приложения вы собираетесь обновлять данные. Без этих операторов повторное выполнение методов Add и AddXY только добавит новые точки, не удалив прежние.
Если вы предусмотрели, например, для данных, отображаемых в диаграмме, две серии Series1 и Series4 разных видов — Pie и Bar, то можете ввести процедуру, изменяющую по требованию пользователя тип диаграммы. Эту процедуру можно ввести в событие OnClick какой-нибудь кнопки, в команду меню или, например, просто в обработку щелчка на компоненте Chart. Для того, чтобы загрузить данные в Series4 и сделать эту диаграмму в первый момент невидимой, можно вставить в конце приведенной ранее процедуры операторы Series4.Assign(Series1); Series4.Active:=false;
Первый из этих операторов переписывает данные, помещенные в Series1, в серию Series4. А второй оператор делает невидимой серию Series4. Смена типа диаграммы осуществляет процедура Series1.Active := not Series1.Active; Series4.Active := not Series4.Active;
На Рисунок 4.8 б вы можете видеть результат переключения пользователя на другой вид диаграммы.
График синусоиды, построенный по пикселям (а) и линиями (б)


Свойство Style определяет вид линии. Это свойство может принимать следующие значения:
Давайте попробуем нарисовать пером график синуса из предыдущего примера. В данном случае обработчик события формы OnPaint может иметь вид: procedure TForm1.FormPaint(Sender: TObject); var X,Y: real; // координаты функции PX,PY: longint; // координаты пикселей begin Color:=clWhite; Canvas.MoveTo(0,ClientHeight div 2); for PX:=0 to ClientWidth do begin {X - аргумент графика, соответствующий пикселю с координатой РХ} X := PX*4*Pi/ClientWidth; Y := Sin(X); {PY — координата пикселя, соответствующая координате Y} PY := trunc(ClientHeight - (Y+1)*ClientHeight/2); {Проводится линия на графике} Canvas.LineTo(PX,PY); end; end;
Результат работы приложения в этом варианте вы можете видеть на Рисунок 4.1 б. Как видите, качество графика существенно улучшилось.
Перо может рисовать не только прямые линии, но и фигуры. Полный список методов канвы, использующих перо, см. во встроенной справке Delphi. А пока в качестве примера приведем только один из них — Ellipse, который рисует эллипс или окружность. Он объявлен как procedure Ellipse(X1, Y1, Х2, Y2: Integer); где параметры X1, Х2, Y1, Y2 определяют координаты прямоугольника, описывающего эллипс или окружность. Например, оператор Canvas.Ellipse(10, 40, 20, 50); нарисует окружность с диаметром 10 и с координатами центра (15, 45).
Фигуры в общем случае рисуются не пустыми, а закрашенными с помощью свойства канвы Brush — кисть. Свойство Brush является объектом, имеющим в свою очередь ряд свойств. Свойство Color определяет цвет заполнения. Свойство Style определяет шаблон заполнения (штриховку). По умолчанию значение Style равно bsSolid, что означает сплошное закрашивание цветом Color.
У пера Pen имеется еще одно свойство, которое мы пока не рассматривали. Это свойство — Mode (режим). По умолчанию значение Mode = pmCopy. Это означает, что линии проводятся цветом, заданным в свойстве Color. Но возможны и другие режимы, в которых учитывается не только цвет Color, но и цвет соответствующих пикселей фона. Наиболее интересным из этих режимов является режим pmNotXor — сложение с фоном по инверсному исключающему ИЛИ. Если задан этот режим, то повторное рисование той же фигуры на том же месте канвы убирает ранее нарисованное изображение и восстанавливает цвета пикселей, которые были до первого изображения фигуры.
Эту особенность режима pmNotXor можно использовать для создания простенькой анимации. Достаточно нарисовать нечто, затем стереть нарисованное, перерисовать немного измененным — и рисунок будет представляться ожившим.
Попробуйте сделать сами простенькую мультипликацию — движущуюся окружность. Начните новое приложение и в раздел implementation вставьте объявление var X,Y: integer;
Тем самым вы введете глобальные переменные X и Y — текущие координаты изображения.
В событие формы OnPaint вставьте операторы Canvas.Brush.Color := clWhite; Color := clWhite; Canvas.Pen.Mode := pmNotXor;
Первый из этих операторов задает белый цвет кисти Brush. Значит ваша окружность будет закрашена внутри белым цветом. Второй оператор задает белый цвет фона поверхности формы. Третий оператор устанавливает режим пера pmNotXor, который позволит вам стирать прежнее изображение прежде, чем нарисовать новое.
Даже самая простая мультипликация нуждается в синхронизации. Иначе скорость движения будет определяться быстродействием компьютера. Поэтому перенесите на форму компонент Timer — таймер со страницы System. Этот компонент описан в . Можете посмотреть там его подробное описание. А пока задайте его свойство Interval равным, например, 30 (это время выдержки в миллисекундах, но реальное время выдержки будет больше — см. раздел 5.7) и установите свойство Enabled равным false (это означает, что таймер не будет запускаться автоматически в момент запуска приложения).
В обработчик события этого компонента OnTimer вставьте операторы // Стирание прежнего изображения Canvas.Ellipse(Х-5, Y, X+5, Y-1Q); Inc(X); // Рисование нового изображения Canvas.Ellipse(Х-5, Y, X+5, Y-10); // Останов при достижении конца формы if (X >= ClientWidth-20) then Timer1.Enabled := false;
Первый из этих операторов рисует окружность в том месте, где она была нарисована ранее, т.е. стирает прежнее изображение. Далее увеличивается на единицу функцией Inc текущая координата X и изображение окружности рисуется в новой позиции. Последний оператор останавливает изображение у края формы.
Теперь перенесите на форму кнопку Button и в обработчик щелчка на ней поместите операторы Х:=10; Y:=100; Canvas.Ellipse(X-5, Y, X+5, Y-10); Timer1.Enabled:=true;
Первые два оператора задают начальные координаты окружности. Третий оператор рисует окружность в ее начальном положении, а четвертый — запускает таймер.
Оттранслируйте приложение, запустите его на выполнение, щелкните на кнопке. Вы увидите изображение окружности, перемещающееся по форме слева направо. А дальше уж подключите вашу фантазию и преобразуйте это не слишком интересное приложение во что-нибудь более увлекательное.
На канве можно отображать не только программно создаваемые изображения, но и изображения, хранящиеся в графических файлах. Только сама канва не имеет метода загрузки изображения из файла. Поэтому загружать файл надо в какой-нибудь другой графический объект, способный воспринимать информацию графических файлов. А затем переписывать изображение из этого объекта на канву с помощью метода канвы Draw. Его описание: procedure Draw(X, Y: Integer; Graphic: TGraphic);
Здесь параметры Х и Y определяют координаты левого верхнего угла размещения изображения на канве, a Graphic — объект, хранящий информацию. В качестве такого объекта может выступать, например, объект типа TBitMap, предназначенный для хранения битовых матриц. Давайте посмотрим, как все это выглядит на практике.
Откройте новое приложение, перенесите на форму компонент OpenPictureDialog со страницы Dialogs (это компонент диалога открытия графических файлов — см. ) и кнопку Button. Разместите OpenPictureDialog в любом месте формы, так как этот компонент невизуальный, а кнопку разместите внизу формы. В обработчик щелчка на кнопке занесите код: procedure TForm1.Button1Click(Sender: TObject); var BitMap:TBitMap; begin // Выбор пользователем графического файла if OpenPictureDialog1.Execute then begin // Создание объекта BitMap типа TBitMap BitMap:=TBitMap.Create; // Загрузка в BitMap выбранного графического файла BitMap.LoadFromFile(OpenPictureDialog1.FileName); // Перенос изображения на канву формы Canvas.Draw(10, 10, BitMap); // Уничтожение объекта BitMap BitMap.Free; end; end;
Этот код создает временный объект типа TBitMap с именем BitMap. Затем вызывается диалог открытия графического файла OpenPictureDialog1 и, если пользователь выбрал файл, то он загружается в BitMap методом LoadFromFile. Затем методом Draw загруженное изображение копируется на канву в область, с координатами левого верхнего угла (10, 10). После этого временный объект BitMap уничтожается.
Запустите ваше приложение и щелкните на его кнопке. Вы увидите, что можете загрузить любой графический файл типа .bmp и он отобразится на канве формы (см. Рисунок 4.2 а). Графические файлы вы можете найти в каталоге Images. В Delphi 5 и 4 он обычно расположен в каталоге ...\program files\Common Files\Borland Shared. В Delphi 3 он расположен в каталоге ...\program files\Borland\Delphi 3, а в Delphi 1 — в каталоге Delphi 16. В каталоге Images имеется, в частности, подкаталог \Images\Splash\16Color\, в котором хранится файл, загруженный в примере Рисунок 4.2.
Изображение на канве графического файла (а) и его стирание (б) при перекрытии другим окном


Вы видите, что если окно какого-то другого приложения перекрывает на время окно вашего приложения, то изображение, нарисованное на канве формы, портится. Посмотрим, как можно устранить этот недостаток.
Если окно было перекрыто и изображение испортилось, операционная система сообщает приложению, что в окружении что-то изменилось и что приложение должно предпринять соответствующие действия. Как только требуется обновление окна, для него генерируется событие OnPaint. В обработчике этого события (в нашем случае события формы) нужно перерисовать изображение.
Перерисовка может производиться разными способами в зависимости от приложения. В нашем примере можно было бы вынести объявление переменной BitMap (оператор var BitMap: TBitMap) за пределы приведенной выше процедуры, т.е. сделать эту переменную глобальной, разместив непосредственно в разделе implementation. Оператор BitMap.Free можно было бы перенести в обработчик события формы OnDestroy, происходящего в момент закрытия приложения. Тогда в течение всего времени выполнения вашего приложения вы будете иметь копию картинки в компоненте BitMap и вам достаточно ввести в обработчик события OnPaint формы всего один оператор: Canvas.Draw(10, 10, BitMap);
Сделайте это, и увидите, что изображение на форме не портится при любых перекрытиях окон.
Помимо рассмотренного метода Draw канва имеет еще метод копирования CopyRect: procedure CopyRect(Dest: TRect; Canvas: TCanvas; Source: TRect);
Метод копирует указанную параметром Source область изображения в канве источника изображения Canvas в указанную параметром Dest область данной канвы. Тип TRect, характеризующий прямоугольные области Source и Dest, уже описывался в . Например, оператор Canvas.CopyRect(MyRect2, Bitmap.Canvas, MyRect1); копирует на канву формы в область MyRect2 изображение из области MyRect1 канвы компонента Bitmap.
Копирование методом CopyRect производится в режиме, установленном свойством CopyMode. По умолчанию это свойство имеет значение cmSrcCopy, что означает просто замену изображения, содержащегося ранее в области Dest, на копируемое изображение. Другие возможные значения CopyMode позволяют комбинировать изображения, но их рассмотрение выходит за рамки данной книги.
Этими основными сведениями о выводе графической информации на канву мы ограничимся. В были сообщены сведения о выводе на канву текста. В целом же канва — сложный объект, обладающий еще многими свойствами и методами. Но это требует развернутого обсуждения, выходящего за рамки данной книги. В следующей книге серии «Все о Delphi» эти вопросы будут рассмотрены подробнее.
Изображение в компоненте Image битовой матрицы (а) и пиктограммы (6)


Для пиктограмм можно было бы использовать оператор Image1.Picture.Icon.LoadFromFile( OpenPictureDialog1.FileName); а для метафайлов — оператор Image1.Picture.Metafile.LoadFromFile( OpenPictureDialog1.FileName); или Image1.Picture.Graphic.LoadFromFile( OpenPictureDialog1.FileName) ;
Но во всех этих случаях, если формат файла не совпадет с предполагаемым, возникнет ошибка. Аналогично работает и метод SaveToFile с тем отличием, что примененный к Picture или к Picture.Graphic он сохраняет в файле изображение любого формата. Например, если вы дополните свое приложение диалогом SavePictureDialog (см. ), введете в меню раздел Сохранить как и в его обработчик поместите оператор if SavePictureDialog1.Execute then Image1.Picture.SaveToFile(SavePictureDialog1.FileName); то пользователь получит возможность сохранить изображение любого формата в файле с новым именем. Только при этом, чтобы не возникало в дальнейшем путаницы, расширение сохраняемого файла все-таки должно соответствовать формату сохраняемого изображения.
Абсолютно идентично для изображений любого формата будет работать программа, если оператор сохранения вы замените на Image1.Picture.Graphic.SaveToFile( SavePictrureDialog1.FileName); использующий свойство Picture.Graphic. А если вам известен формат хранимого в компоненте Image изображения, то вы можете применить метод SaveToFile к свойствам Picture.Bitmap, Picture.Icon и Picture.Metafile.
Для всех рассмотренных объектов Picture, Picture.Bitmap, Picture.Icon и Picture.Metafile определены методы присваивания значений объектов: procedure Assign(Source: TPersistent);
Однако, для BitMap, Icon и Metafile присваивать можно только значения однородных объектов: соответственно битовых матриц, пиктограмм, метафайлов. При попытке присвоить значения разнородных объектов генерируется исключение ЕConvertError. Объект Picture — универсальный, ему можно присваивать значения объектов любых из остальных трех классов. А значение Picture можно присваивать только тому объекту, тип которого совпадает с типом объекта, хранящегося в нем.
Метод Assign можно использовать и для обмена изображениями с буфером Clipboard. Например, оператор Clipboard.Assign(Image1.Picture) ; занесет в буфер обмена изображение, хранящееся в Image1. Аналогично оператор Image1.Picture.Assign(Clipboard) ; прочитает в Image1 изображение, находящееся в буфере обмена. Причем это может быть любое изображение и даже текст.
Надо только не забыть при работе с буфером обмена вставить в оператор uses вашего модуля ссылку на модуль Clipbrd. Автоматически Delphi эту ссылку не вставляет.
Возвращаясь к свойствам компонента Image, можно отметить один недостаток, присущий нашему тестовому приложению, приведенному на Рисунок 4.5. При загрузке разных изображений размер окна приложения может оказаться или слишком маленьким, и тогда вы увидите только часть изображения, или слишком большим, и тогда изображение будет некрасиво размещено в левом верхнем углу формы, оставляя много пустого пространства. Этот недостаток можно устранить, если воспользоваться свойствами Height (высота) и Width (ширина) компонента Image. При свойстве AutoSize установленном в true размеры Image автоматически устанавливаются равными размерам загруженного изображения. И этими размерами можно воспользоваться для соответствующего изменения размеров формы. Например, приведенный ранее код загрузки изображения из файла можно заменить на следующий: if OpenPictureDialog1.Execute then begin Image1.Picture.LoadFromFile( OpenPictureDialog1.FileName); Form1.ClientHeight := Image1.Height+10; Image1.Top := Form1.ClientRect.Top + (Form1.ClientHeight - Image1.Height) div 2; Form1.ClientWidth := Image1.Width+10; Image1.Left := Form1.ClientRect.Left + (Form1.ClientWidth - Image1.Width) div 2; end;
В этом коде размеры клиентской области формы устанавливаются несколько больше размеров компонента Image1, которые в свою очередь адаптируются к размеру картинки благодаря свойству AutoSize. Внесите эти исправления в свое приложение, выполните его и увидите, что форма стала автоматически адаптироваться к размерам загруженного изображения.
Компонент Chart
Теперь рассмотрим компонент Chart. Этот компонент позволяет строить различные диаграммы и графики, которые выглядят очень эффектно (Рисунок 4.8). Компонент Chart имеет множество свойств, методов, событий, так что если все их рассматривать, то этому пришлось бы посвятить целую главу. Поэтому ограничимся рассмотрением только основных характеристик Chart. А с остальными вы можете ознакомиться во встроенной справке Delphi или просто опробовать их, экспериментируя с диаграммами.Компонент Shape
Компонент Shape только условно может быть отнесен к средствам отображения графической информации, поскольку просто представляет собой различные геометрические фигуры, соответствующим образом заштрихованные. Основное свойство этого компонента — Shape (форма), которое может принимать значения:Компоненты Image и PaintBox
Компоненты Image и PaintBox представляют собой некоторую ограниченную поверхность с канвой, на которую можно заносить изображения, как это описано в . При этом компонент PaintBox, собственно говоря, не дает ничего нового по сравнению с рисованием на канве формы. Рисование на PaintBox вместо формы не имеет никаких преимуществ, кроме, может быть, некоторого облегчения в расположении одного или нескольких рисунков в площади окна.Компонент Image много богаче по своим возможностям и удобнее, чем PaintBox. Даже при использовании описанных в приемов рисования на канве компонент Image имеет существенное преимущество: в нем не приходится думать о перерисовке изображения, испорченного из-за перекрытия данного окна другими. Все, связанное с обработкой событий OnPaint, рассмотренных в разделе 4.2, в Image осуществляется автоматически. Кроме того с помощью Image проще, чем при непосредственном рисовании на канве формы, расположить в окне приложения несколько изображений и управлять ими. При этом отпадает необходимость сложных и нудных расчетов координат канвы формы, обеспечивающих требуемое взаимное расположение изображений, т.е. в полной мере проявляются преимущества визуального программирования. Так что, вероятно, во всех случаях лучше работать с канвой Image, чем с канвой формы.
Но помимо этих возможностей у компонента Image имеются свойства, позволяющие работать с различными типами графических файлов.
Delphi поддерживает три типа файлов — битовые матрицы, пиктограммы и метафайлы. Все три типа файлов хранят изображения; различие заключается лишь в способе их хранения внутри файлов и в средствах доступа к ним. Битовая матрица (файл с расширением .bmp) отображает цвет каждого пикселя в изображении. При этом информация хранится таким образом, что любой компьютер может отобразить изображение с разрешающей способностью и количеством цветов, соответствующими его конфигурации.
Пиктограммы (файлы с расширением .ico) — это маленькие битовые матрицы. Они повсеместно используются для обозначения значков приложений, в быстрых кнопках, в пунктах меню, в различных списках. Способ хранения изображений в пиктограммах схож с хранением информации в битовых матрицах, но имеются и различия. В частности, пиктограмму невозможно масштабировать, она сохраняет тот размер, в котором была создана.
Метафайлы (Metafiles) хранят не последовательность битов, из которых состоит изображение, а информацию о способе создания картинки. Они хранят последовательности команд рисования, которые и могут быть повторены при воссоздании изображения. Это делает такие файлы, как правило, более компактными, чем битовые матрицы.
Компонент Image позволяет отображать информацию, содержащуюся в графических файлах всех указанных типов. Для этого служит его свойство Picture — объект типа TPicture.
Компоненты страницы ActiveX — F1Book, Chartfx, Graph
К компонентам, отображающим графическую информацию, может быть отнесен рассмотренный в компонент F1Book со страницы библиотеки ActiveX. На приводившемся в этом разделе Рисунок 3.14 показано, как можно использовать этот компонент для отображения диаграмм и графиков данных, занесенных в таблицу.Еще одним интересным компонентом, отображающим графическую информацию, является Chartfx со страницы библиотеки ActiveX. Он представляет собой законченный редактор диаграмм со встроенной инструментальной панелью (Рисунок 4.12). Нажимая кнопки инструментальной панели пользователь может задавать новые данные (вторая кнопка справа на Рисунок 4.12 и выбор опции Data Editor), изменять тип диаграммы, сохранять диаграмму в файле с расширением .chf или загружать ее из аналогичного файла, копировать диаграмму в буфер обмена Clipboard и таким образом включать ее в другие документы (например, в документы Word) и т.п.
Методы компонентов, обеспечивающие печать
Ряд компонентов, описанных в предыдущих разделах, имеют методы, обеспечивающие печать хранящихся в них данных. Например, компонент RichEdit (см. ) имеет метод Print, позволяющий печатать хранящийся в нем текст. В этот метод передается единственный параметр типа строки, назначение которого заключается только в том, что при просмотре в Windows очереди печатаемых заданий принтера эта строка появляется как имя задания. Например, оператор RichEdit1.Print('Печать RichEdit1'); обеспечивает печать текста компонента RichEdit1, причем задание на печать получает имя «Печать RichEdit1».Печать воспроизводит все заданные особенности форматирования. Перенос строк и разбиение текста на страницы производится автоматически. Длина строк никак не связана с размерами компонента RichEdit, содержащего этот текст.
Компонент Chart (см. ) также имеет метод Print, обеспечивающий печать отображаемого в компоненте графика или диаграммы. Предварительно может быть выполнен метод PrintPortrait, задающий книжную (вертикальную) ориентацию бумаги, или метод PrintLandscape, задающий альбомную (горизонтальную) ориентацию. Масштабировать размер печатаемого графика можно, вызвав предварительно метод PrintRect: procedure PrintRect(const R: TRect); в котором параметр R определяет размер области принтера, в которой осуществляется печать.
Компонент Chartfx (см. ) имеет быструю кнопку печати (пятая слева в инструментальной панели Рисунок 4.12), с помощью которой пользователь в любой момент может напечатать текущий график или диаграмму.
Не только многие компоненты, но и сами формы имеют метод Print, который печатает клиентскую область формы. При этом полоса заголовка формы и полоса главного меню не печатается. Таким образом, можно включить в приложение форму, в которой пользователь во время выполнения размещает необходимые для печати результаты. Если имя этой формы Form2, то ее печать может выполняться оператором Form2.Print;
Свойство формы PrintScale определяет опции масштабирования изображения при печати. Возможные значения PrintScale:
Окна ввода выражений компонента



Когда вы ввели все аргументы, щелкните на ОК и вернетесь в основное окно Рисунок 4.17 а, в котором будет сформировано требуемое выражение. Щелкните в нем на OK и сформированное выражение занесется в свойство Expression.
Компонент QRSysData позволяет отображать в отчете системные данные. Его основное свойство Data, которое может принимать следующие значения:
Давайте в качестве примера использования всех этих компонентов рассмотрим создание простого отчета вида, представленного на Рисунок 4.16. Он содержит некоторый текст и рисунки, имеет нижние колонтитулы, в которых печатается название отчета и номер страницы. Кроме того, мы хотим, чтобы на первой странице над нижним колонтитулом было написано «МОСКВА» и дата создания отчета.
Свойство HasTitle можно устанавливать в true, а можно не устанавливать, поскольку различие между заголовком отчета и его полосой детализации проявляться в нашем случае не будет. Дело в том, что при работе с базами данных полоса детализации может печататься многократно и тогда надо отдельно выделить полосу заголовка, которая должна печататься только один раз. А в нашем случае полоса детализации будет печататься один раз, так что она не отличается от заголовка.
Если вы установили в true свойства HasDetail и HasPageFooter, то на поле компонента QuickRep появятся слабо видимые полосы с надписями «Detail» и «Page Footer».
Теперь на страницу надо добавить строку с текстом «МОСКВА» и датой создания отчета. Но если мы разместим ее под компонентом QRRichText, то в отчете она окажется непосредственно под текстом. А мы хотим разместить ее внизу страницы. Поэтому нам надо ввести в отчет еще одну полосу.
Это не лучший вариант, так как при этом будет отображаться день, месяц и год. Если хотите, чтобы отображались только месяц и год, используйте вместо QRSysData компонент QRExpr, в котором можно, как показано выше, задать отображение даты без указания дня.
Осталось внести информацию в нижний колонтитул страницы.
Проектирование отчета завершено. Щелкните правой кнопкой мыши на компоненте QuickRep и из всплывшего меню выберите команду Preview. Перед вами откроется окно предварительного просмотра, в котором вы увидите свой отчет. Подберите размещение компонентов и размеры шрифтов в них так, чтобы все выглядело прилично.
Конечно, подобный отчет совсем не интересен, так как он весь создан в процессе проектирования. К тому же вы никак не использовали возможностей форматирования текста в компоненте QRRichText. Поэтому текст выглядит некрасиво. Давайте теперь создадим форму (Рисунок 4.18), которая управляла бы построением, просмотром и печатью этого отчета.
Окно диалога открытия графического файла

Вернемся к рассмотрению свойств компонента Image.
Если установить свойство AutoSize в true, то размер компонента Image будет автоматически подгоняться под размер помещенной в него картинки. Если же свойство AutoSize установлено в false, то изображение может не поместиться в компонент или, наоборот, площадь компонента может оказаться много больше площади изображения.
Другое свойство — Stretch позволяет подгонять не компонент под размер рисунка, а рисунок под размер компонента. Установите AutoSize в false, растяните или сожмите размер компонента Image и установите Stretch в true. Вы увидите, что рисунок займет всю площадь компонента, но поскольку вряд ли реально установить размеры Image точно пропорциональными размеру рисунка, то изображение исказится. Устанавливать Stretch в true может иметь смысл только для каких-то узоров, но не для картинок. Свойство Stretch не действует на изображения пиктограмм, которые не могут изменять своих размеров.
Свойство — Center, установленное в true, центрирует изображение на площади Image, если размер компонента больше размера рисунка.
Рассмотрим еще одно свойство — Transparent (прозрачность). Если Transparent равно true, то изображение в Image становится прозрачным. Это можно использовать для наложения изображений друг на друга. Поместите на форму второй компонент Image и загрузите в него другую картинку. Только постарайтесь взять какую-нибудь мало заполненную, контурную картинку. Можете, например, взять картинку из числа помещаемых обычно на кнопки, например, стрелку (файл ...\program files\common files\borland shared\images\buttons\arrow1l.bmp). Передвиньте ваши Image так, чтобы они перекрывали друг друга, и в верхнем компоненте установите Transparent равным true. Вы увидите, что верхняя картинка перестала заслонять нижнюю. Одно из возможных применений этого свойства — наложение на картинку надписей, выполненных в виде битовой матрицы. Эти надписи можно сделать с помощью встроенной в Delphi программы Image Editor.
Учтите, что свойство Transparent действует только на битовые матрицы. При этом прозрачным (т.е. заменяемым на цвет расположенного под ним изображения) делается по умолчанию цвет левого нижнего пикселя битовой матрицы.
Мы рассмотрели загрузку изображения из файла в процессе проектирования. Но свойство Picture позволяет также легко организовать обмен с графическими файлами любых типов в процессе выполнения приложения. Чтоб пояснить технику такого обмена, надо сначала подробнее рассмотреть свойство Picture.
Это свойство является объектом, который имеет в свою очередь подсвойства, указывающие на хранящийся графический объект. Если в Picture хранится битовая матрица, на нее указывает свойство Picture.Bitmap. Если хранится пиктограмма, на нее указывает свойство Picture.Icon. На хранящийся метафайл указывает свойство Picture.Metafile. Наконец, на графический объект произвольного типа указывает свойство Picture.Graphic.
Объект Picture и его свойства Bitmap, Icon, Metafile и Graphic имеют методы файлового чтения и записи LoadFromFile и SaveToFile: procedure LoadFromFile(const FileName: string); procedure SaveToFile(const FileName: string);
Для свойств Picture.Bitmap, Picture.Icon и Picture.Metafile формат файла должен соответствовать классу объекта: битовой матрице, пиктограмме, метафайлу. При чтении файла в свойство Picture.Graphiс файл должен иметь формат метафайла. А для самого объекта Picture методы чтения и записи автоматически подстраиваются под тип файла. Поясним это на примере.
Давайте построим приложение, аналогичное рассмотренному в разделе 4.2 примеру просмотра графических файлов. Для разнообразия можно организовать управление им не кнопкой Button, а меню. Поместите на форму компонент Image. Растяните его или задайте его свойство Align равным alClient, чтобы он занял всю клиентскую область формы. Перенесите на форму компонент диалога открытия графического файла OpenPictureDialog (см. ). Поместите также на форму компонент главного меню MainMenu (см. ) и задайте в нем один раздел — Файл. В обработчике этого раздела напишите оператор if(OpenPictureDialog1.Execute) then Image1.Picture.LoadFromFile( OpenPictureDialog1.FileName);
Этот оператор вызовет диалог открытия графического файла (см. Рисунок 4.4) и загрузит в компонент Image1 изображение из выбранного пользователем файла (см. Рисунок 4.5). Причем файл может быть любого типа: битовая матрица, пиктограмма или метафайл.
Окно формы, управляющей подготовкой отчета

Теперь осталось связать друг с другом две формы и написать небольшие команды управления.
Окно Picture Editor

Окно задания свойств компонента Chartfx: страницы Data Values (а) и Шрифты (б)


Отображение графики на канве Canvas
Канва Canvas не является компонентом, так что, строго говоря, она не должна бы рассматриваться в рамках данной книги. Но поскольку многие компоненты, в частности, формы, имеют канву и канва предоставляет возможность выводить различную графическую информацию, то некоторые начальные сведения о канве все-таки целесообразно дать.Канва представляет собой область компонента, на которой можно рисовать или отображать готовые изображения. Она содержит свойства и методы, существенно упрощающие графику Delphi. Все сложные взаимодействия с системой спрятаны для пользователя, так что рисовать в Delphi может человек, совершенно не искушенный в машинной графике.
Каждая точка канвы имеет координаты X и Y. Система координат канвы, как и везде в Delphi, имеет началом левый верхний угол канвы. Координата X возрастает при перемещении слева направо, а координата Y — при перемещении сверху вниз. Координаты измеряются в пикселях. Пиксель — это наименьший элемент поверхности рисунка, с которым можно манипулировать. Важнейшее свойство пикселя — его цвет.
Канва имеет свойство Pixels. Это свойство представляет собой двумерный массив, который отвечает за цвета канвы. Например, Canvas.Pixels[10,20] соответствует цвету пикселя, 10-го слева и 20-го сверху. С массивом пикселей можно обращаться как с любым свойством: изменять цвет, задавая пикселю новое значение, или определять его цвет по хранящемуся в нем значению. Например, Canvas.Pixels[10,20] := 0 или Canvas.Pixels[10,20] := clBlack — это задание пикселю черного цвета.
Свойство Pixels можно использовать для рисования на канве. Давайте попробуем нарисовать по пикселям график синусоиды на канве формы. Для этого в обработчик события формы OnPaint (прорисовка) можно вставить следующий код: procedure TForm1.FormPaint(Sender: TObject); var X,Y: real; // координаты функции PX,PY: longint; // координаты пикселей begin Color := clWhite; for PX := 0 to ClientWidth do begin {X — аргумент графика, соответствующий пикселю с координатой РХ} X := PX*4*Pi/ClientWidth; Y:=Sin(X); {PY — координата пикселя, соответствующая координате Y} PY:=trunc(ClientHeight - (Y+1)*ClientHeight/2); {Устанавливается черный цвет выбранного пикселя (О яркости)} Canvas.Pixels [PX, PY] := 0; end; end;
Выполните это тестовое приложение и вы увидите результат, представленный на Рисунок 4.1 а. График синусоиды получился, хотя и не очень хороший, т.к. разбивается на отдельные точки — пиксели.
Отображение хода выполнения длительных операций — компоненты ProgressBar и Gauge
Рассмотрим компоненты ProgressBar со страницы библиотеки Win32 и Gauge со страницы Samples, предназначенные для отображения в стиле Windows 95/98 хода процессов, занимающих заметное время, например, копирования больших файлов, настройку приложения, установку приложения на компьютере и т.п. Пример возможных вариантов отображения хода процесса компонентами ProgressBar и Gauge приведен на Рисунок 4.20.Панель компонента MediaPlayer

Тип устройства мультимедиа, с которым работает медиа-плеер, определяется его свойством DeviceType. Если устройство мультимедиа хранит объект воспроизведения в файле, то имя файла задается свойством FileName. По умолчанию свойство DeviceType имеет значение dtAutoSelect. Это означает, что медиа-плеер пытается определить тип устройства исходя из расширения имени файла FileName.
Еще одно свойство MediaPlayer — AutoOpen. Если оно установлено в true, то медиа-плеер пытается открыть устройство, указанное свойством DeviceType, автоматически во время своего создания в процессе выполнения приложения.
Воспроизведение видео информации по умолчанию производится в окно, которое создает само открытое устройство мультимедиа. Однако это можно изменить, если в свойстве Display указать оконный элемент, в котором должно отображаться изображение. Это может быть, например, форма или панель. Можно также задать в свойстве DisplayRect типа TRect (свойство только времени выполнения) прямоугольную область этого окна, в которую должно выводиться изображение. Для задания свойства DisplayRect можно использовать функцию Rect. Однако, в данном свойстве использование типа TRect не совсем обычно. Первые две координаты, как и обычно, задают положение левого верхнего угла изображения. А два следующих числа задают ширину и высоту изображения, а не координаты правого нижнего угла. Например, оператор MediaPlayer1.DisplayRect := Rect(10, 10, 200, 200); задает для вывода область с координатами левого верхнего угла (10, 10), длиной и шириной, равными 200.
В компоненте MediaPlayer определены события OnClick и OnNotify. Первое из них происходит при выборе пользователем одной из кнопок медиа-плеера и определено как type TMPBtnType=(btPlay, btPause, btStop, btNext, btPrev, btStep, btBack, btRecord, btEject); procedure(Sender: TObject; Button:TMPBtnType; var DoDefault: Boolean);
Параметр Button указывает выбранную кнопку. Параметр DoDefault, передаваемый как var, определяет выполнение (при значении true по умолчанию) или отказ от выполнения стандартного метода, соответствующего выбранной кнопке.
Событие OnNotify происходит после возвращения очередного метода, если свойство медиа-плеера Notify было установлено в true. Способ возврата любого метода медиа-плеера определяется свойством Wait. Если установить Wait равным false, то возвращение управления в приложение происходит сразу после вызова метода, не дожидаясь завершения его выполнения. Таким образом, задав Notify равным true и Wait равным false, можно обеспечить немедленный возврат в приложение и отображение пользователю текущего состояния объекта мультимедиа.
Свойства Notify и Wait действуют только на один очередной метод. Поэтому их значения надо каждый раз восстанавливать в обработчиках событий OnClick или OnNotify.
В обработчиках событий можно читать свойство Mode, характеризующее текущее состояние устройства мультимедиа. Можно также читать и устанавливать ряд свойств, характеризующих размер воспроизводимого файла и текущую позицию в нем.
Вот, собственно, в конспективном виде основная информация о компоненте MediaPlayer. А теперь попробуйте все это на практике. Простое и в то же время мощное приложение можно сделать очень легко. Начните новый проект и перенесите на форму компоненты MediaPlayer, MainMenu и OpenDialog. В фильтре компонента OpenDialog можно, например, задать:
В компоненте MediaPlayer при желании можно указать имя файла FileName, открываемого в момент начала выполнения приложения. Тогда надо установить свойство AutoOpen в true. Впрочем, это, конечно, не обязательно.
Вот и все. Можете выполнять свое приложение и наслаждаться музыкой или фильмами (если, конечно, все вопросы, связанные с настройкой мультимедиа на вашем компьютере решены).
Чтобы все-таки использовать какие-то события компонента MediaPlayer, давайте немного усложним приложение. Введем в него три метки (Рисунок 4.24), в которых будем отображать имя воспроизводимого файла, состояние открытого устройства мультимедиа и последнюю вызванную операцию.
Печать с помощью объекта Printer
В Delphi имеется класс печатающих объектов TPrinter, который обеспечивает печать текстов, изображений и других объектов, расположенных на его канве — Canvas (см. разделы и ). На канве объекта типа TPrinter могут размещаться и тексты, и изображения.Модуль Delphi, именуемый Printers, содержит переменную Printer, являющуюся объектом типа TPrinter. Эта переменная эквивалентна невизуальному компоненту, только отсутствующему в палитре библиотеки. Чтобы использовать Printer, надо добавить модуль Printers в оператор uses вашей программы. Автоматически он не добавляется.
Рассмотрим некоторые свойства и методы объекта типа TPrinter.
Предположим, вы хотите напечатать текст, используя печатающий объект. Вы можете написать код вида: Printer.BeginDoc; Printer.Canvas.ТехtOut(10, 10, 'Я печатаю через объект Printer'); Printer.EndDoc;
Этот код вызывает печать на канве принтера текста «Я печaтаю через объект Printer», начиная с десятого пикселя слева и десятого сверху. BeginDoc запускает задание на печать. Текст посылается на канву с помощью метода TextOut объекта Canvas. Метод EndDoc вызывает печать текста и останавливает задание на печать.
Если вы хотите напечатать изображение, хранящееся в компоненте Image1 (см. ), это можно сделать операторами: Printer.BeginDoc; with Image1.Picture.BitMap do Printer.Canvas.CopyRect(Rect(0, 0, Height, Width), Canvas, Rect(0, 0, Height, Width)); Printer.EndDoc;
При печати текста объект Printer не производит автоматического переноса строк и разбиения текста на страницы. Поэтому печать длинных текстов с помощью объекта Printer требует достаточно сложного программирования. Проще это делать, например, загрузкой текста в компонент RichEdit (см. ) и выполнением его метода Print (см. ). Другой возможный вариант — использование описанной в следующем разделе системы QuickReport.
Перечень компонентов отображения графической информации
Для отображения графической информации в библиотеке Delphi предусмотрены компоненты, список которых дан в таблице 4.1. Таблица 4.1. Компоненты отображения графической информацииграмма

(изображение)

(окно для рисования)

(таблица рисунков)

(диаграммы и графики)
Кроме того, отображать и вводить графическую информацию можно на поверхности любого оконного компонента, имеющего свойство Canvas — канва.
Подготовка и печать отчетов с
На странице QReport палитры компонентов Delphi имеется множество элементов (см. таблицу 4.2), предназначенных для подготовки и печати отчетов с помощью системы QuickReport. Таблица 4.2. Компоненты системы QuickReportграмма

(отчет)

(детали)

(полоса текста)

(полоса)

(дочерняя полоса)

(группировка)

(метка)

(текст из базы данных)

(математические выражения)

(системные данные)

(многострочный текст)

(тексты с математическими выражениями)

(многострочный текст RTF)

(многострочный текст RTF базы данных)

(форма)

(изображение)

(изображение из базы данных)

(составной отчет)

(предварительный просмотр)

(фильтр текста)

(разделитель)

(фильтр HTML)

(диаграммы, графики)
В данном разделе мы коротко рассмотрим принципы создания отчетов с помощью QuickReport. Конечно, рассмотреть в деталях все компоненты QuickReport невозможно — это потребовало бы отвести под эту тему целую главу, а, возможно, и несколько. К тому же отчеты обычно строятся для того, чтобы распечатывать информацию из баз данных. А рассмотрение компонентов, связанных с данными, выходит за рамки данной книги. Поэтому ограничимся только самыми общими принципами построения отчетов, не связанных с базами данных.
QuickReport — это система, позволяющая визуально проектировать отчеты и связывать их с кодом приложения. QuickReport генерирует отчеты, представляемые в виде полос различных типов. Именно для размещения в отчете полос предназначены такие компоненты, как QRSubDetail, QRStringsBand, QRBand, QRChildBand, QRGroup. Однако, понятие полосы тесно связано с отображением информации из баз данных. С этим же связаны такие компоненты, как QRDBTest, QRDBRichText, QRDBImage. Все они в совокупности позволяют строить отчеты, в которых автоматически просматриваются базы данных и отображается их текущее состояние. Но в данном разделе мы сосредоточимся на основном компоненте QuickRep и на компонентах, не связанных с данными. При этом можно строить только простые отчеты, содержащие тексты и рисунки, как показано на Рисунок 4.16.
Предварительный просмотр отчета

Приложение на основе компонента Chartfx

Свойство Series на странице Data Values (Рисунок 4.13 а) диалога (в Инспекторе Объектов это свойство названо NSeries) обозначает число серий данных (на Рисунок 4.12 равно 2). Свойство Points на той же странице диалога (в Инспекторе Объектов оно названо NValues) обозначает число значений по оси аргументов (на Рисунок 4.12 равно 4). Страница диалога Elements позволяет задать какие-то характерные уровни (опции Value — Text, на Рисунок 4.12 отмечен уровень 40 с текстом «Минимально допустимый запас»), выделить цветом какие-то полосы уровней (опции From — То — Color, на Рисунок 4.12 выделены полосы 0 — 20 и 20 — 40), задать текст в строке состояния (опции ID — Width — Text). Прочие свойства позволяют задать тексты вверху диаграммы, внизу, слева, справа, задать координатные сетки и многое другое. Следует обратить внимание на выбор шрифтов на странице Шрифты (Рисунок 4.13 б). Шрифты, естественно, надо выбрать такие, которые содержат символы кириллицы. При выборе шрифтов надо сначала в выпадающем списке Свойство выбрать надпись, для которой указывается шрифт (например, TopFont — шрифт надписи над диаграммой), а затем в окнах Шрифт, Начертание, Размер установить атрибуты шрифта. Подобную процедуру надо повторить для всех надписей.
Приложение на основе компонента Graph

* В Delphi 5 компонент Graph отсутствует. По-видимому, автор писал большую часть книги для Delphi 4 а затем автоматически перенес текст под 5-ю версию. - Примечание разработчика электронной версии.
Приложение универсального проигрывателя при воспроизведении видео файла

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

Пример отчета

Компонент QuickRep имеет ряд свойств, определяющих характеристики печати отчета:
Компонент QuickRep имеет два основных метода: Preview — предварительный просмотр, и Print — печать. Предварительный просмотр и даже печать отчета можно осуществлять и в процессе проектирования. Для этого надо щелкнуть правой кнопкой мыши на компоненте QuickRep и из всплывшего меню выбрать команду Preview. Перед вами откроется окно предварительного просмотра, в котором, в частности, имеется кнопка печати.
Компоненты QRLabel, QRMemo, QRRichText, QRShape, QRImage, размещаемые на полосах отчета, являются аналогами обычных компонентов — Label, Memo, RichEdit, Shape, Image, которые уже рассматривались ранее. Основной особенностью соответствующих компонентов QuickReport является их способность печататься в тех полосах отчета, в которых они размещены. Компоненты имеют два свойства, отсутствующих в обычных компонентах: Frame и Size.
Свойство Frame имеет ряд подсвойств, определяющих рамку вокруг компонента: Color — цвет, Style — стиль, Width — ширина, DrawBottom, DrawLeft, DrawRight, DrawTop — определяют наличие рамки соответственно внизу, слева, справа и вверху компонента.
Свойство Size имеет подсвойства, определяющие размер и место размещения компонента при печати. Все определяется в единицах измерения, заданных свойством Units компонента QuickRep.
Некоторые компоненты имеют свойство AlignToBand — выравнивание в полосе. Если это свойство установить в true, то компонент будет выровнен по краю полосы, заданному свойством Alignment: taLeftJustify — влево, taCenter — по центру, taRightJustify — вправо.
Остановимся теперь на свойствах отдельных компонентов. Интересное свойство имеет компонент QRRichText — ParentRichEdit. Это свойство указывает на обычный компонент RichEdit, текст из которого автоматически передается в компонент QRRichText. Вы можете, например, ввести в свое приложение компонент RichEdit и предоставить пользователю возможность вводить в него нужный текст, форматируя соответствующим образом слова и абзацы. Этот текст будет автоматически переноситься в отчет — в размещенный в нем компонент QRRichText. Впрочем, и в другие компоненты QuickReport можно программно заносить тексты и изображения, задаваемые пользователем. Например, вы можете ввести в свое приложение компонент Edit и предоставить пользователю возможность вводить в него нужный текст. А перед предварительным просмотром или печатью отчета вы можете перенести этот текст, например, в метку QRLabel, оператором QRLabel1.Caption:=Edit1.Text;
Компонент QRExpr позволяет отображать в отчете результаты каких-то вычислений. Основное свойство этого компонента — Expression. Нажав на кнопку с многоточием около этого свойства в Инспекторе Объектов, вы попадете в окно, представленное на Рисунок 4.17 а. В этом окне вы можете ввести выражение, результаты которого должны отображаться в отчете на месте расположения компонента QRExpr. Элементами выражения могут быть поля базы данных (Database field), такие переменные (Variable), как текущее время, дата, номер страницы и др., функции (Function). Кнопка Validate позволяет вам проверить синтаксическую правильность введенного выражения и посмотреть результат вычисления. Поэкспериментируйте с этим компонентом и вы легко поймете принципы работы с ним.
Пусть, например, вы хотите отобразить текущую дату, включая месяц и год, но не включая числа. Обычно дата отображается в формате «дд.мм.гг». Значит вам надо убрать из этого формата первые три символа, отображающие день. Это можно сделать с помощью функции копирования фрагмента строки COPY(DATE, 4, 5), в которой первый аргумент указывает строку, второй — индекс первого копируемого символа, третий — число копируемых символов. В приведенном примере DATE — функция, возвращающая текущую дату в виде строки.
Пример отображения хода процесса компонентами ProgressВar и Gauge

Можно поступать иначе: задать сначала значение максимальной величины равным Count, а затем в ходе процесса задавать позицию равной Current. Например: Gauge1.MaxValue := Count; Gauge1.Progress := Current;
Компонент ProgressBar имеет два метода, которыми тоже можно воспользоваться для отображения процесса: StepBy(Delta: Integer) — увеличение позиции на заданную величину Delta и StepIt — увеличение позиции на один шаг, величина которого задается свойством Step.
Пример приложения с диаграммами


Разместите один или два (если захотите воспроизвести Рисунок 4.8) компонента Chart на форме и посмотрите открывшиеся в Инспекторе Объектов свойства. Приведем пояснения некоторых из них.
Если вы хотите попробовать воспроизвести приложение, показанное на Рисунок 4.8, сделайте двойной щелчок на верхнем компоненте Chart. Вы попадете в окно Редактора Диаграмм (Рисунок 4.9) на страницу Chart, которая имеет несколько закладок. Прежде всего вас будет интересовать на ней закладка Series. Щелкните на кнопке Add — добавить серию. Вы попадете в окно (Рисунок 4.10), в котором вы можете выбрать тип диаграммы или графика. В данном случае выберите Pie — круговую диаграмму. Воспользовавшись закладкой Titles вы можете задать заголовок диаграммы, закладка Legend позволяет задать параметры отображения легенды диаграммы (списка обозначений) или вообще убрать ее с экрана, закладка Panel определяет вид панели, на которой отображается диаграмма, закладка 3D дает вам возможность изменить внешний вид вашей диаграммы: наклон, сдвиг, толщину и т.д.
Когда вы работаете с Редактором Диаграмм и выбрали тип диаграммы, в компонентах Chart на вашей форме отображается ее вид с занесенными в нее условными данными (см. Рисунок 4.11).
Пример таблицы DrawGrid

Компоненты DrawGrid и StringGrid имеют канву Canvas, на которой можно размещать изображения методами, изложенными в . Имеется метод CellRect, который возвращает область канвы, отведенную под заданную ячейку. Этот метод определен как function CellRect (ACol, ARow: Longint): TRect; где ACol и ARow — индексы столбца и строка, начинающиеся с 0, на пересечении которых расположена ячейка. Возвращаемая этой функцией область является областью канвы, в которой можно рисовать необходимое изображение. Например, оператор DrawGrid1.Canvas.CopyRect(DrawGrid1.CellRect(1, 1), BitMap.Canvas, Rect(0, 0, BitMap.Height, BitMap.Width)); копирует методом CopyRect (см. ) в ячейку (1, 1) таблицы DrawGrid1 изображение из компонента BitMap. Эта ячейка является второй слева и второй сверху в таблице, поскольку индексы начинаются с 0. Учтите, что если размеры ячейки меньше, чем размер копируемого изображения, то в ячейке появится только левая верхняя часть картинки.
Изображение на канве компонентов DrawGrid и StringGrid, как и на канве любого компонента, подвержено стиранию при перекрытии окна приложения другими окнами или, например, при сворачивании приложения. Поэтому необходимо принимать меры, описанные в , чтобы с помощью обработчика событий OnPaint восстанавливать испорченное изображение. Это делает компонент DrawGrid не слишком удобным для использования.
Все свойства и события, позволяющие определить выбранную пользователем ячейку таблицы, были рассмотрены в . Там же вы найдете описание свойств, отвечающих за внешний вид и допустимость перестройки пользователем таблицы во время выполнения приложения.
Примеры компонента Shape

* В книге нет главы 10. Вероятно, автор планировал поместить в этой главе справочные материалы по рассмотренным в книге объектам Дельфи. - Примечание разработчика электронной версии.
Редактор отображаемых данных

Таблицы изображений — компоненты DrawGrid и StringGrid
Компонент DrawGrid используется для создания в приложении таблицы, которая может содержать графические изображения (см. пример на Рисунок 4.6). Этот компонент подобен компоненту StringGrid (см. ), поскольку последний является производным от DrawGrid. Поэтому в DrawGrid присутствуют все свойства, методы, события компонента StringGrid, кроме относящихся к тексту, т.е. кроме свойств Cells, Cols, Rows, Objects. С этой точки зрения компонент StringGrid обладает существенно большими возможностями, чем DrawGrid, поскольку он может хранить в ячейках и изображения, и тексты. А если вы захотите внести текст в какие-то ячейки DrawGrid, то вам надо будет использовать для этого методы вывода текста на канву (см. ), что не очень удобно.Универсальный проигрыватель MediaPlayer
В Delphi (начиная с Delphi 2) имеется компонент MediaPlayer — универсальный проигрыватель аудио- и видео-информации. Этот медиа-плеер расположен на странице System библиотеки компонентов.Компонент можно использовать в двух режимах. Во-первых, можно предоставить пользователю возможность управлять воспроизведением информации с помощью кнопочного интерфейса, напоминающего панель управления различными проигрывателями. Во-вторых, можно сделать сам компонент невидимым и управлять воспроизведением информации с помощью его методов.
Пользовательский интерфейс медиа-плеера представлен на Рисунок 4.23. Он имеет ряд кнопок, управляемых мышью или клавишей пробела и клавишами со стрелками.
Воспроизведение немых видео клипов — компонент Animate
Теперь рассмотрим способ воспроизведения в приложении Delphi стандартных мультипликаций Windows и файлов .avi — клипов без звукового сопровождения. Это позволяет сделать компонент Animate, расположенный на странице Win32 библиотеки.Компонент Animate позволяет воспроизводить на форме стандартные видео клипы Windows (типа копирования файлов, поиска файлов и т.п.) и немые видео файлы .avi — Audio Video Interleaved. Эти файлы представляют собой последовательность кадров битовых матриц. Они могут содержать и звуковую дорожку, но компонент Animate воспроизводит только немые клипы AVI.
Откройте новое приложение, перенесите на форму компонент Animate и познакомьтесь с ним.
Воспроизводимое им изображение задается одним из двух свойств: FileName или CommonAVI. Первое из этих свойств, как ясно из его названия, позволяет в процессе проектирования или программно задать имя воспроизводимого файла. А свойство CommonAVI позволяет воспроизводить стандартные мультипликации Windows. Это свойство объявлено следующим образом: type TCommonAVI = (aviNone, aviFindFolder, aviFindFile, aviFindComputer, aviCopyFiles, aviCopyFile, aviRecycleFlle, aviEmptyRecycle, aviDeleteFile); property CommonAVI: TCommonAVI;
Тип TCommonAVI определяет множество предопределенных в Windows мультипликаций типа копирования файлов, поиска файлов, удаления файлов и т.п. Что означает каждое значение вы увидите из тестового приложения, которое построите чуть позже.
А пока установите значение CommonAVI, например, равным aviCopyFile. Это соответствует стандартному изображению копирования файла. Соответствующий начальный рисунок немедленно появится на вашей форме. Свойство Repetitions компонента Animate задает число повторений воспроизведения клипа. Если оно равно 0 (значение по умолчанию), то воспроизведение повторяется вновь и вновь до тех пор, пока не будет выполнен метод Stop. При выполнении этого метода генерируется событие OnStop, которое можно использовать, например, чтобы стереть изображение — сделать его невидимым.
Если же свойство Repetitions задать большим нуля, оно определит число повторений клипа. Задайте его, например, равным 3. А теперь установите свойство Active компонента Animate в true. Вы увидите (Рисунок 4.21), что еще в процессе проектирования ваше приложение заработает. Изображение оживет и клип будет повторен 3 раза.
Выбор типа диаграммы в Редакторе Диаграмм

Страница Series, также имеющая ряд закладок, дает вам возможность выбрать дополнительные характеристики отображения серии. В частности, для круговой диаграммы на закладке Format полезно включить опцию Circled Pie, которая обеспечит при любом размере компонента Chart отображение диаграммы в виде круга. На закладке Marks кнопки группы Style определяют, что будет написано на ярлычках, относящихся к отдельным сегментам диаграммы: Value — значение, Percent — проценты, Label — названия данных и т.д. В примере Рисунок 4.8 включена кнопка Percent, a на закладке General установлен шаблон процентов, обеспечивающий отображение только целых значений.
Группы радиокнопок — компоненты RadioGroup, RadioButton и GroupBox
Радиокнопки образуют группы взаимосвязанных индикаторов, из которых обычно может быть выбран только один. Они используются для выбора пользователем одной из нескольких взаимоисключающих альтернатив, например, отдела, в котором работает сотрудник, или пола сотрудника. Впрочем, радиокнопки могут использоваться и для отображения аналогичных данных. В этом случае управление кнопками осуществляется программно. Несколько примеров организации размещения радиокнопок вы можете увидеть на Рисунок 5.1 в .Начнем рассмотрение радиокнопок с компонента RadioGroup — панели группы радиокнопок. Это панель, которая может содержать регулярно расположенные столбцами и строками радиокнопки. Надпись в левом верхнем углу панели (см. Рисунок 5.1) определяется свойством Caption. А надписи кнопок и их количество определяются свойством Items, имеющим тип TStrings. Щелкнув на кнопке с многоточием около этого свойства в окне Инспектора Объектов, вы попадете в редактор списков строк, который уже рассматривался нами в (Рисунок 3.9). В нем вы можете занести надписи, которые хотите видеть около кнопок, по одной в строке. Сколько строчек вы запишете — столько и будет кнопок. Например, для компонента RadioGroup в верхней правой части формы Рисунок 5.1 свойство Items имеет вид: бухгалтерия администрация цех 1 цех 2 цех 3 цех 4
Кнопки, появившиеся в панели после задания значений Items, можно разместить в несколько столбцов (не более 17), задав свойство Columns. По умолчанию Columns = 1, т.е. кнопки размещаются друг под другом.
Определить, какую из кнопок выбрал пользователь, можно по свойству ItemIndex, которое показывает индекс выбранной кнопки. Индексы, как всегда в Delphi, начинаются с 0. По умолчанию ItemIndex = -1, что означает отсутствие выбранной кнопки. Если вы хотите, чтобы в момент начала выполнения приложения какая-то из кнопок была выбрана (это практически всегда необходимо), то надо установить соответствующее значение ItemIndex во время проектирования. Если вы используете радиокнопки не для ввода, а для отображения данных, устанавливать значение ItemIndex можно программно во время выполнения приложения.
Компонент RadioGroup очень удобен, но не свободен от некоторых недостатков. Его хорошо использовать, если надписи кнопок имеют примерно одинаковую длину и если число кнопок в каждом столбце (при размещении их в нескольких столбцах) одинаково. Посмотрите, например, Рисунок 5.1 из . Группа радиокнопок в нижнем левом углу формы имеет нормальный вид. А группа аналогичных радиокнопок в верхней правой части формы выглядит плохо: она занимает слишком много места, которое пропадает в пустую. Связано это с тем, что длина надписей у кнопок первого столбца превышает длину надписей у остальных кнопок. A RadioGroup при размещении кнопок ориентируется на надпись максимальной длины. Еще хуже выглядела бы эта группа, если бы число кнопок было, например, равно 5.
В подобных случаях желательно нерегулярное расположение кнопок. Такую возможность дают компоненты RadioButton, сгруппированные панелью GroupBox. Панель GroupBox выглядит на форме так же, как RadioGroup (см. Рисунок 5.1), и надпись в ее верхнем левом углу также определяется свойством Caption. Эта панель сама по себе пустая. Ее назначение — служить контейнером для других управляющих элементов, в частности, для радиокнопок RadioButton. Отдельная радиокнопка RadioButton особого смысла не имеет, хотя и может служить индикатором, включаемым и выключаемым пользователем. Но в качестве индикаторов обычно используются другие компоненты — CheckBox. А радиокнопки имеют смысл, когда они взаимодействуют друг с другом в группе. Эта группа и объединяется единым контейнером, обычно панелью GroupBox.
Рассмотрим свойства радиокнопки RadioButton. Свойство Caption содержит надпись, появляющуюся около кнопки. Значение свойства Alignment определяет, с какой стороны от кнопки появится надпись: taLeftJustify — слева, taRightJustify — справа (это значение принято по умолчанию). Свойство Checked определяет, выбрана ли данная кнопка пользователем, или нет. Поскольку в начале выполнения приложения обычно надо, чтобы одна из кнопок группы была выбрана по умолчанию, ее свойство Checked надо установить в true в процессе проектирования. Если вы поэкспериментируете, то заметите, что и во время проектирования можно установить в true значение Checked только у одной кнопки из группы.
Размещение кнопок RadioButton в панели GroupBox, как можно видеть из Рисунок 5.1 , дает большую свободу по сравнению с компонентом RadioGroup и позволяет разместить кнопки не регулярно.
Радиокнопки RadioButton могут размещаться не только в панели GroupBox, но и в любой панели другого типа, а также непосредственно на форме. Группа взаимосвязанных кнопок в этих случаях определяется тем оконным компонентом, который содержит кнопки. В частности, для радиокнопок, размещенных непосредственно на форме, контейнером является сама форма. Таким образом, все кнопки, размещенных непосредственно на форме, работают как единая группа, т.е. только в одной из этих кнопок можно установить значение Checked в true.
Индикаторы CheckBox и CheckListBox
Индикаторы с флажком CheckBox (см. Рисунок 5.1 ) используются в приложениях в основном для того, чтобы пользователь мог включать и выключать какие-то опции, или для индикации состояния. При каждом щелчке пользователя на индикаторе его состояние изменяется, проходя в общем случае последовательно через три значения: выделение (появление черной галочки), промежуточное (серое окно индикатора и серая галочка) и не выделенное (пустое окно индикатора). Этим трем состояниям соответствуют три значения свойства компонента State: cbChecked, cbGrayed, cbUnchecked. Впрочем, эти три состояния допускаются только при значении другого свойства AllowGrayed равном true. Если же AllowGrayed = false (значение по умолчанию), то допускается только два состояния: выделенное и не выделенное. И State, и AllowGrayed можно устанавливать во время проектирования или программно во время выполнения.Промежуточное состояние обычно используется, если индикатор применяется для отображения какой-то характеристики объекта. Например, если индикатор призван показать, какой регистр использовался при написании какого-то фрагмента текста, то в случае, если весь текст написан в верхнем регистре индикатор может принимать выделенное состояние, если в нижнем — не выделенное, а если использовались оба регистра — промежуточное.
Проверять состояние индикатора можно не только по значению State, но и по значению свойства Checked. Если Checked равно true, то индикатор выбран, т.е. State = cbChecked. Если Checked равно false, то State равно cbUnchecked или cbGrayed. Установка Checked в true во время проектирования или выполнения автоматически переключает State в cbChecked.
Как и в радиокнопке, в индикаторе CheckBox надпись задается свойством Caption, а ее размещение по отношению к индикатору — свойством Alignment.
Еще один компонент, имеющий индикаторы — список CheckListBox. Это аналог рассмотренного в компонента ListBox, но около каждой строки списка имеется индикатор, состояние которого пользователь может изменять. Вы можете увидеть, как выглядит список CheckListBox, на Рисунок 3.10 в разделе 3.3.5 (компонент расположен на рисунке в центре внизу).
Свойства, общие у CheckListBox и ListBox, мы рассматривать не будем, так как все, характеризующее этот компонент как список, рассмотрено в разделе 3.3.5. А состояния индикаторов определяют два свойства: State и Checked. Оба эти свойства можно рассматривать как индексированные массивы, каждый элемент которого соответствует индексу строки. Эти свойства можно устанавливать программно или читать, определяя установки пользователя. Например, операторы CheckListBox1.Checked[1] := true; CheckListBox1.State[2] := cbGrayed; устанавливают индикатор второй строки списка CheckListBox1 в состояние выбранного, а индикатор третьей строки — в промежуточное состояние (вспомним, что индексы начинаются с 0). Оператор for i:=0 to CheckListBox1.Items.Count - 1 do if CheckListBox1.Checked[i] then ... проверяет состояние всех индикаторов списка, и для выбранных пользователем строк осуществляет какие-то действия (в приведенном операторе на месте этих действий просто поставлено многоточие).
В компоненте CheckListBox имеется также событие OnClickCheck, возникающее при каждом изменении пользователем состояния индикатора. Его можно использовать для обработки результатов изменения.
Кнопка с фиксацией SpeedButton
Кнопки SpeedButton имеют возможность отображения пиктограмм и могут использоваться как обычные управляющие кнопки или как кнопки с фиксацией нажатого состояния (см. на Рисунок 5.1). Обычно они используются в качестве быстрых кнопок, дублирующих различные команды меню, и в инструментальных панелях, в которых требуется фиксация нажатого состояния.У кнопок SpeedButton, как и у других кнопок, имеется свойство Caption — надпись, но в этих кнопках оно обычно оставляется пустым, так как вместо надписи используется пиктограмма.
Изображение на кнопке задается свойством Glyph точно так же, как описано в для кнопок BitBtn. И точно так же свойство NumGlyphs определяет число используемых пиктограмм, свойства Layout и Margin определяют расположение изображения, а свойство Spacing — расстояние между изображением и надписью (если, конечно, вы все-таки хотите использовать надпись на кнопке).
Особенностью кнопок SpeedButton являются свойства GroupIndex (индекс группы), AllowAllUp (разрешение отжатого состояния всех кнопок группы) и Down (исходное состояние — нажатое). Если GroupIndex = 0, то кнопка ведет себя так же, как Button и BitBtn. При нажатии пользователем кнопки она погружается, а при отпускании возвращается в нормальное состояние. В этом случае свойства AllowAllUp и Down не влияют на поведение кнопки.
Если Grouplndex > 0 и AllowAllUp = true, то кнопка при щелчке пользователя на ней погружается и остается в нажатом состоянии. При повторном щелчке пользователя на кнопке она освобождается и переходит в нормальное состояние (именно для того, чтобы освобождение кнопки состоялось, необходимо задать AllowAllUp = true). Если свойство Down во время проектирования установлено равным true, то исходное состояние кнопки — нажатое.
Если есть несколько кнопок, имеющих одинаковое ненулевое значение GroupIndex, то они образуют группу взаимосвязанных кнопок из которых нажатой может быть только одна. Если одна кнопка находится в нажатом состоянии и пользователь щелкает на другой, то первая кнопка освобождается, а вторая фиксируется в нажатом состоянии. Поведение нажатой кнопки при щелчке на ней зависит от значения свойства AllowAllUp. Если оно равно true, то кнопка освободится, поскольку в этом случае возможно состояние, когда все кнопки группы отжаты. Если же AllowAllUp равно false, то щелчок на нажатой кнопке не приведет к изменению вида кнопки. Впрочем, и в этом случае, как и при любом щелчке на кнопке, возникает событие OnClick, которое может быть обработано.
Состояние кнопки во время выполнения можно определить по значению свойства Down: если значение равно true, то кнопка нажата. Во время события OnClick значение Down уже равно тому состоянию, которое примет кнопка в результате щелчка на ней.
Общая характеристика
В данной главе будут рассмотрены такие управляющие элементы, как кнопки, индикаторы и некоторые другие. В таблице 5.1 приведен перечень этих элементов с краткими характеристиками. В этой таблице не указаны аналогичные элементы, связанные с базами данных, так как их рассмотрение выходит за рамки данной книги. Кнопка UpDown уже была рассмотрена в .На Рисунок 5.1 показаны примеры приведенных в таблице 5.1 компонентов. Таблица 5.1. Кнопки, индикаторы и иные управляющие элементы
грамма

(командная кнопка)

(кнопка с графикой)

(кнопка с фиксацией и графикой)

(группа радиокнопок)

(радиокнопка)

(групповое окно)

(кнопка-счетчик)

(контрольный индикатор с флажком)

(список с индикаторами)

(ползунок)

(линейка прокрутки)

(таймер)
В целом можно сказать, что в качестве управляющих кнопок используются обычно Button или, если желательно иметь на кнопке пиктограмму — BitBtn. Кнопки SpeedButton применяются в качестве быстрых кнопок и для построения инструментальных панелей, в которых требуется фиксация нажатого состояния. Компонент RadioGroup применяется для формирования группы регулярно размещенных радиокнопок, из которых в любой момент времени может быть включена только одна. Если по различным соображениям регулярное размещение радиокнопок нежелательно, то используются компоненты RadioButton, размещенные желательным образом в панели GroupBox. Наконец, для введения в приложение различных опций, которые пользователь может включать и выключать, используются индикаторы Checkbox.
Окно редактора пиктограммы

Файл изображения для кнопки может содержать до четырех изображений пиктограмм размера 16x16. Самое левое соответствует отжатой кнопке. Второе слева соответствует недоступной кнопке, когда ее свойство Enabled равно false. Третье слева изображение используется при нажатии пользователя на кнопку при ее включении. Четвертое слева изображение используется в кнопках с фиксацией SpeedButton, о которых будет сказано позднее, для изображения кнопки в нажатом состоянии. Большинство изображений для кнопок использует две пиктограммы. Число пиктограмм вы можете узнать из свойства кнопки NumGlyphs, которое после загрузки изображения покажет вам число пиктограмм в нем.
Расположение изображения и надписи на кнопке определяется свойствами Margin, Layout и Spacing. Если свойство Margin равно -1 (значение по умолчанию), то изображение и надпись размещаются в центре кнопки. При этом положение изображения но отношению к надписи определяется свойством Layout, которое может принимать значения: blGlyphLeft (слева, это значение принято по умолчанию), blGlyphRight (справа), blGlyphТор (вверху), blGlyphBottom (внизу). Если же Margin > 0, то в зависимости от значения Layout изображение и надпись смещаются к той или иной кромке кнопки, отступая от нее на число пикселей, заданное значением Margin.
Свойство Spacing задает число пикселей, разделяющих изображение и надпись на поверхности кнопки. По умолчанию Spacing = 4. Если задать Spacing = 0, изображение и надпись будут размещены вплотную друг к другу. Если задать Spacing = -1, то текст появится посередине между изображением и краем кнопки.
Еще одно свойство BitBtn — свойство Kind определяет тип кнопки. По умолчанию значение этого свойства равно bkCustom — заказная. Но можно установить и множество других предопределенных типов: bkOK, bkCancel, bkHelp, bkYes, bkNo, bkClose, bkAbort, bkRetry, bkIgnore, bkAll. В этих типах уже сделаны соответствующие надписи, введены пиктограммы, заданы еще некоторые свойства. Обычно все-таки лучше ими не пользоваться. Во-первых, надписи все равно надо переводить на русский язык. Во-вторых, предопределенные рисунки обычно выбиваются из общего стиля конкретного приложения. И главное — предопределение некоторых свойств, не учтенных вами, может иногда приводить к странным результатам работы. Уж лучше использовать заказные кнопки и самому устанавливать в них все необходимые свойства.
Ползунки и полосы прокрутки — компоненты TrackBar и ScrollBar
Компонент TrackBar представляет собой элемент управления в виде ползунка, который пользователь может перемещать курсором мыши или клавишами во время выполнения. Таким образом, пользователь может управлять какими-то процессами: громкостью звука, размером изображения и т.п. На Рисунок 5.3 приведены различные формы отображения ползунка. Как видно из рисунка, он может располагаться горизонтально, вертикально, иметь шкалу с различных сторон, иметь какой-то выделенный диапазон шкалы.Различные варианты ползунков

Свойство Position — целое, значение которого может изменяться в пределах, задаваемых свойствами Min и Мах. По умолчанию Min = 0, Мах = 10, так что Position может принимать только 11 значений — от 0 до 10. Если задать большее значение Мах, соответственно увеличится количество возможных значений Position в диапазоне Min — Мах.
Свойство Orientation определяет ориентацию ползунка: trHorizontal — горизонтальная, trVertical — вертикальная.
Свойство TickMarks указывает размещение шкалы относительно компонента и может принимать значения: tmBottomRight — снизу или справа в зависимости от ориентации компонента (верхний и правый компоненты на Рисунок 5.3), tmTopLeft — сверху или слева в зависимости от ориентации компонента (нижний компонент на Рисунок 5.3), tmBoth — с обеих сторон (средний компонент на Рисунок 5.3).
Свойство TickStyle определяет способ изображения шкалы. Оно может принимать значения: tsAuto — автоматическая прорисовка шкалы, tsNone — отсутствие шкалы, tsManual — программное рисование шкалы с помощью метода SetTick(Value: Integer), который помещает метку шкалы в позицию, соответствующую величине Value. Метки, соответствующие началу и концу шкалы автоматически размещаются и в случае TickStyle = tsManual.
При TickStyle = tsAuto частота меток шкалы определяется свойством Frequency. Это свойство задает, сколько возможных значений Position лежит между метками. Например, если Frequency = 2, то метки будут соответствовать только каждому второму возможному значению позиции (такое значение Frequency задано в верхнем компоненте на Рисунок 5.3).
Свойства LineSize и PageSize определяют, насколько смещается ползунок, если пользователь управляет им с помощью соответственно клавиш со стрелками или клавишами PageUp и PageDown.
Свойства SelStart и SelEnd позволяют визуально выделить на шкале некоторый диапазон (см. средний компонент на Рисунок 5.3), который о чем-то говорит пользователю, например, рекомендуемый диапазон значений. При этом ничто не мешает пользователю выйти за пределы этого диапазона.
Похож на ползунок по своим функциям и компонент ScrollBar, хотя выглядит он иначе и предназначен по замыслу для других целей. Этот компонент представляет собой стандартную линейку прокрутки Windows. Однако, он может использоваться и для целей прокрутки (впрочем, многие оконные компоненты Delphi имеют собственные полосы прокрутки), и для управления, подобного компоненту TrackBar.
Основные свойства ScrollBar — Position, Min и Мах те же, что у компонента TrackBar. Свойство Kind, определяющее горизонтальное или вертикальное расположение полосы и принимающее соответственно значения sbHorizontal или sbVertical, аналогично свойству Orientation компонента TrackBar.
Имеются два свойства, отсутствующие у TrackBar: SmallChange и LargeChange. Они определяют соответственно «малый» сдвиг при щелчке на кнопке в конце полосы или нажатии клавиши со стрелкой, и «большой» сдвиг при перемещении на страницу щелчком рядом с бегунком или нажатии клавиш PageUp или PageDown.
Событие, соответствующее перемещению пользователем бегунка полосы прокрутки — OnScroll. В процедуру обработчика этого события передается по ссылке параметр ScrollPos — позиция бегунка, которую можно читать, но можно и изменять, и передается параметр ScrollCode, характеризующий вид перемещения бегунка. Этот параметр может иметь значения:
Таймер — компонент Timer
Компонент Timer позволяет задавать в приложении интервалы времени. Таймер находит многочисленные применения: синхронизация мультипликации, закрытие каких-то окон, с которыми пользователь долгое время не работает, включение хранителя экрана или закрытие связей с удаленным сервером при отсутствии действий пользователя, регулярный опрос каких-то источников информации, задание времени на ответ в обучающих программах — все это множество задач, в которых требуется задавать интервалы времени, решается с помощью таймера.Таймер — невизуальный компонент, который может размещаться в любом месте формы. Он имеет два свойства, позволяющие им управлять: Interval — интервал времени в миллисекундах и Enabled — доступность. Свойство Interval задает период срабатывания таймера. Через заданный интервал времени после предыдущего срабатывания, или после программной установки свойства Interval, или после запуска приложения, если значение Interval установлено во время проектирования, таймер срабатывает, вызывая событие OnTimer. В обработчике этого события записываются необходимые операции.
Если задать Interval = 0 или Enabled = false, то таймер перестает работать. Чтобы запустить отсчет времени надо или задать Enabled = true, если установлено положительное значение Interval, или задать положительное значение Interval, если Enabled = true.
Например, если требуется, чтобы через 5 секунд после запуска приложения закрылась форма — заставка, отображающая логотип приложения, на ней надо разместить таймер, задать в нем интервал Interval = 5000, а в обработчик события OnTimer вставить оператор Close, закрывающий окно формы.
Если необходимо в некоторой процедуре запустить таймер, который отсчитал бы заданный интервал, например, 5 секунд, после чего надо выполнить некоторые операции и отключить таймер, это можно сделать следующим образом. При проектировании таймер делается доступным (Enabled = true), но свойство Interval задается равным 0. Таймер не будет работать, пока в момент, когда нужно запустить таймер, не выполнится оператор Timer1.Interval := 5000;
Через 5 секунд после этого наступит событие OnTimer. В его обработчике надо задать оператор Timer1.Interval := 0; который отключит таймер, после чего можно выполнять требуемые операции.
Другой эквивалентный способ решения задачи — использование свойства Enabled. В время проектирования задается значение Interval = 5000 и значение Enabled = false. В момент, когда надо запустить таймер выполняется оператор Timer1.Enabled := true;
В обработчик события OnTimer, которое наступит через 5 секунд после запуска таймера, можно вставить оператор Timer1.Enabled:=false; который отключит таймер.
Таймер точно выдерживает заданные интервалы Interval, если они достаточно велики — сотни и тысячи миллисекунд. Если же задавать интервалы длительностью десятки или единицы миллисекунд, то реальные интервалы времени оказываются заметно больше вследствие различных накладных расходов, связанных с вызовами функций и иными вычислительными аспектами.
Управляющие кнопки Button и BitBtn
Простейшей и, пожалуй, наиболее часто используемой кнопкой является кнопка Button (см. на Рисунок 5.1 в верхнем левом углу формы), расположенная на странице библиотеки Standard. Реже используется кнопка BitBtn (см. на Рисунок 5.1 под кнопкой Button), отличающаяся, прежде всего, возможностью отобразить на ее поверхности изображение. Большинство свойств, методов и событий у этих видов кнопок одинаковы.Основное с точки зрения внешнего вида свойство кнопки — Caption (надпись). В надписях кнопок можно предусматривать использование клавиш ускоренного доступа, выделяя для этого один из символов надписи. Перед символом, который должен соответствовать клавише ускоренного доступа, ставится символ амперсанта «&». Этот символ не появляется в надписи, а следующий за ним символ оказывается подчеркнутым. Тогда пользователь может вместо щелчка на кнопке нажать в любой момент клавишу Alt совместно с клавишей выделенного символа.
Например, если в вашем приложении имеется кнопка выполнения какой-то операции, вы можете задать ее свойство Caption равным «&Выполнить». На кнопке эта надпись будет иметь вид «Выполнить». И если пользователь нажмет клавиши Alt-В, то это будет эквивалентно щелчку на кнопке.
Основное событие любой кнопки — OnClick, возникающее при щелчке на ней. Именно в обработчике этого события записываются операторы, которые должны выполняться при щелчке пользователя на кнопке. Помимо этого есть еще ряд событий, связанных с различными манипуляциями клавишами и кнопками мыши.
Свойство Cancel, если его установить в true, определяет, что нажатие пользователем клавиши Esc будет эквивалентно нажатию на данную кнопку. Это свойство целесообразно задавать равным true для кнопок «Отменить» в различных диалоговых окнах, чтобы можно было выйти из диалога, нажав на эту кнопку или нажав клавишу Esc.
Свойство Default, если его установить в true, определяет, что нажатие пользователем клавиши ввода Enter будет эквивалентно нажатию на данную кнопку, даже если данная кнопка в этот момент не находится в фокусе. Правда, если в момент нажатия Enter в фокусе находится другая кнопка, то все-таки сработает именно кнопка в фокусе.
Еще одно свойство — ModalResult используется в модальных формах, рассмотрение которых выходит за рамки данной книги. В обычных приложениях значение этого свойства должно быть равно mrNone.
Из методов, присущих кнопкам, имеет смысл отметить один — Click. Выполнение этого метода эквивалентно щелчку на кнопке, т.е. вызывает событие кнопки OnClick. Этим можно воспользоваться, чтобы продублировать какими-то другими действиями пользователя щелчок на кнопке. Пусть, например, вы хотите, чтобы при нажатии пользователем клавиши с символом «С» или «с» в любой момент работы с приложением выполнялись операции, предусмотренные в обработчике события OnClick кнопки Button1. Поскольку неизвестно, какой компонент будет находиться в фокусе в момент этого события, надо перехватить его на уровне формы. Такой перехват осуществляется, если установить свойство формы KeyPreview в true. Тогда в обработчике события формы OnKeyPress можно написать оператор if (key='C' or key='c') then Button1.Click;
Если пользователь ввел символ «С» или «с», то в результате будет выполнен обработчик щелчка кнопки Button1.
Все сказанное выше в равной степени относится и к Button, и к BitBtn. Рассмотрим теперь особенности кнопки с пиктограммой BitBtn. Изображение на этой кнопке задается свойством Glyph. При нажатии кнопки с многоточием в строке свойства Glyph в Инспекторе Объектов вызывается окно, представленное на Рисунок 5.2. Нажав в нем кнопку Load вы перейдете в обычное окно открытия файла рисунка и можете выбрать файл битовой матрицы .bmp, содержащий желаемое изображение. В частности, с Delphi поставляется большое количество изображений для кнопок. Они расположены в каталоге \Images\Buttons, а сам каталог Images в Delphi 5 и 4 расположен в каталоге \program files\common files\borland shared, а в других версиях Delphi — в каталоге \program files\borland\delphi... .
Главное меню — компонент MainMenu
В Delphi имеется два компонента, представляющие меню: MainMenu — главное меню, и PopupMenu — всплывающее меню. Оба компонента расположены на странице Standard. Эти компоненты имеют много общего. Начнем рассмотрение с компонента MainMenu.Это невизуальный компонент, т.е. место его размещения на форме в процессе проектирования не имеет никакого значения для пользователя — он все равно увидит не сам компонент, а только меню, сгенерированное им.
Обычно на форму помещается один компонент MainMenu. В этом случае его имя автоматически заносится в свойство формы Menu. Но можно поместить на форму и несколько компонентов MainMenu с разными наборами разделов, соответствующими различным режимам работы приложения. В этом случае во время проектирования свойству Menu формы присваивается ссылка на один из этих компонентов. А в процессе выполнения в нужные моменты это свойство можно изменять, меняя соответственно состав главного меню приложения.
Основное свойство компонента — Items. Его заполнение производится с помощью Конструктора Меню, вызываемого двойным щелчком на компоненте MainMenu или нажатием кнопки с многоточием рядом со свойством Items в окне Инспектора Объектов. В результате откроется окно, вид которого представлен на Рисунок 6.1. В этом окне вы можете спроектировать все меню. На Рисунок 6.2 показано в работе то меню, которое соответствует проектируемому на Рисунок 6.1.
Горячие клавиши — компонент HotKey
Компонент HotKey, расположенный в библиотеке на странице Win32, является вспомогательным, обеспечивающим возможность задания самим пользователем горячих клавиш, определяющих быстрый доступ к разделам меню. К тому же этот компонент позволяет задать такие сочетания горячих клавиш, которые не предусмотрены в выпадающем списке свойства разделов меню ShortCut.Компонент HotKey внешне выглядит как обычное окно редактирования Edit. Но если в него входит пользователь, то оно переводит нажимаемые им клавиши в тип TShortCut, хранящий комбинацию горячих клавиш. Например, если пользователь нажимает клавиши Ctrl-ф, то в окне HotKey появится текст «Ctrl + ф».
Основное свойство компонента — HotKey, равное по умолчанию комбинации клавиш Alt-А. Это свойство можно прочесть и присвоить свойству ShortCut какого-то раздела меню. Например, оператор MOpen.ShortCut := HotKey1.HotKey; присваивает разделу меню с именем MOpen комбинацию клавиш, заданную в компоненте HotKey1.
Свойство Modifiers указывает модификатор — вспомогательную клавишу, нажимаемую перед символьной. Это свойство является множеством, которое может включать значения hkShift, hkCtrl, hkAlt, hkExt, что соответствует клавишам Shift, Ctrl, Alt, Extra. По умолчанию Modifiers =[hkAlt]. Если вы хотите, например, задать вместо этого значения в качестве модификатора клавишу Ctrl, вы должны выполнить оператор: HotKey1.Modifiers: = [hkCtrl];
Свойство InvalidKeys задает недопустимые клавиши или их комбинации. Это свойство является множеством, которое может включать значения hcNone, hcShift, hcCtrl, hcAlt, hcShiftCtrl, hcShiftAlt, hcCtrlAlt, hcShiftCtrlAlt, что соответствует отсутствию модификатора и клавишам Shift, Ctrl, Alt, Shift-Ctrl, Shift-Alt, Ctrl-Alt, Shift-Ctrl-Alt.
Если вы хотите задать программно значение свойства HotKey, то можете это сделать, например, операторами HotKey1.HotKey := ord('F'); HotKey1.Modifiers := [hkAlt];
Эти операторы зададут комбинацию горячих клавиш Alt-F.
В заключение приведем пример использования компонента HotKey и настройки горячих клавиш меню в процессе выполнения приложения.
Пусть у вас есть главная форма приложения, содержащая компонент MainMenu и пусть вы хотите ввести команду настройки, позволяющую пользователю изменить установленные для разделов меню горячие клавиши. Для упрощения задачи будем считать, что меню, сконструированное в MainMenu:
Эти предположения сделаны просто для того, чтобы упростить код и не использовать функции, не описанные в данной книге.
Начните новое приложение, разместите на форме компонент MainMenu и сконструируйте с его помощью любое меню, удовлетворяющее перечисленным требованиям. Задайте каким-то из разделов меню быстрые клавиши. Один из разделов меню должен называться Настройка и при выборе его мы хотим предоставить пользователю вспомогательную форму для настройки быстрых клавиш.
Добавьте в приложение еще одну форму (команда File | New Form). Эта форма будет вспомогательной. В обработчик команды Настройка главной формы вставьте оператор Form2.ShowModal;
Этот оператор покажет пользователю окно вспомогательной формы как модальное — т.е. пользователь не сможет вернуться в главную форму, пока не закроет вспомогательную. Чтобы компилятор понял этот оператор, надо в модуль главной формы Unit1 вставить оператор uses, ссылающийся на модуль вспомогательной формы Unit2. Можете сделать это вручную или, перейдя в окне Редактора Кода в модуль Unit1, выполните команду File | Use Unit и укажите, что хотите связаться с модулем Unit2. Поскольку из модуля Unit2 надо будет видеть меню модуля Unit1, то аналогичным образом введите и обратную связь — свяжите модуль Unit2 c Unit1.
Теперь давайте спроектируем вспомогательную форму. Она может иметь вид, представленный на Рисунок 6.6. На ней расположено два списка ListBox (см. ): ListBox1, в котором отображаются заголовки меню, и ListBox2, в котором отображаются разделы меню, соответствующие выбранному заголовку. В нижней части формы расположен компонент HotKey и кнопка Button, которая фиксирует в меню сделанный пользователем выбор и закрывает форму. В компоненте HotKey надо стереть с помощью Инспектора Объектов свойство HotKey, которое содержит некоторое значение по умолчанию.
Контекстное всплывающее меню — компонент PopupMenu
Контекстное меню привязано к конкретным компонентам. Оно всплывает, если во время, когда данный компонент в фокусе, пользователь щелкнет правой кнопкой мыши. Обычно в контекстное меню включают те команды главного меню, которые в первую очередь могут потребоваться при работе с данным компонентом.Контекстному меню соответствует компонент PopupMenu. Поскольку в приложении может быть несколько контекстных меню, то и компонентов PopupMenu может быть несколько. Оконные компоненты: панели, окна редактирования, а также метки и др. имеют свойство PopupMenu, которое по умолчанию пусто, но куда можно поместить имя того компонента PopupMenu, с которым будет связан данный компонент.
Формирование контекстного всплывающего меню производится с помощью Конструктора Меню, вызываемого двойным щелчком на PopupMenu, точно так же, как это делалось для . Обратим только внимание на возможность упрощения этой работы. Поскольку разделы контекстного меню обычно повторяют некоторые разделы уже сформированного главного меню, то можно обойтись копированием соответствующих разделов. Для этого, войдя в Конструктор Меню из компонента PopupMenu, щелкните правой кнопкой мыши и из всплывшего меню выберите команду Select Menu (выбрать меню). Вам будет предложено диалоговое окно, в котором вы можете перейти в главное меню. В нем вы можете выделить нужный вам раздел или разделы (при нажатой клавише Shift выделяются разделы в заданном диапазоне, при нажатой клавише Ctrl можно выделить совокупность разделов, не являющихся соседними). Затем выполните копирование их в буфер обмена, нажав клавиши Ctrl-C. После этого опять щелкните правой кнопкой мыши, выберите команду Select Menu и вернитесь в контекстное меню. Укажите курсором место, в которое хотите вставить скопированные разделы, и нажмите клавиши чтения из буфера обмена — Ctrl-V. Разделы меню вместе со всеми их свойствами будут скопированы в создаваемое вами контекстное меню.
В остальном работа с PopupMenu не отличается от работы с MainMenu. Только не возникает вопросов объединения меню разных форм: контекстные меню не объединяются.
Меню «Окно» в приложении MDI со списком открытых документов

Одним из безусловных требований, предъявляемых к меню приложений для Windows, является стандартизация меню и их разделов. Этому помогает команда Save As Template... в контекстном меню, всплывающем при щелчке правой кнопкой мыши в окне Конструктора Меню. Эта команда вызывает диалог, представленный на Рисунок 6.5. В этом диалоге вы можете в верхнем окне указать описание (заголовок), под которым хотите сохранить ваше меню. Впоследствии в любом вашем новом приложении вы можете загрузить этот шаблон в меню, выбирая из всплывающего меню в окне Конструктора Меню команду Insert From Template....
Окно постройки горячих клавиш во время выполнения

При событии OnShow формы Form2 происходит загрузка списка ListBox1 заголовками меню. Цикл загрузки перебирает индексы от 0 до Form1.MamMenu1.Items.Count-1. Это значение на 1 меньше значения MainMenu1.Items.Count, которое равно числу элементов в свойстве MainMenu1.Items.
При щелчке пользователя на списке ListBox1 происходит загрузка списка ListBox2. При этом к соответствующим разделам меню получается доступ с помощью выражения Form1.MainMenu1.Items[ListBox1.ItemIndex].Items[i]. В этом выражении Form1.MainMenu1.Items[ListBox1.ItemIndex] — элемент головного раздела меню, выбранного пользователем в ListBox1. Каждый такой раздел можно рассматривать как элемент массива меню и в то же время он сам является массивом разделов второго уровня. Поэтому его свойство Items[i] указывает на подраздел с индексом i.
При щелчке пользователя на списке ListBox2 происходит загрузка компонента HotKey1 символами горячих клавиш выбранного пользователем раздела. Если раздел не имеет горячих клавиш, то в окне HotKey1 отображается текст «Нет». Далее пользователь может войти в окно HotKey1 и нажать сочетание клавиш, которое он хочет назначить выбранному им разделу меню. Обработка щелчка на кнопке фиксирует это сочетание в разделе меню и закрывает вспомогательную форму.
Опробуйте это приложение в работе и вам станет яснее механизм работы с разделами меню и с быстрыми клавишами.
Окно сохранения шаблона разработанного меню

Пример меню с разбиением нa столбцы

Еще одним свойством, позволяющим вводить маркеры в разделы меню, является RadioItem. Это свойство, установленное в true, определяет, что данный раздел должен работать в режиме радиокнопки совместно с другими разделами, имеющими то же значение свойства GroupIndex. По умолчанию значение GroupIndex равно 0. Но можно задать его большим нуля и тогда, если имеется несколько разделов с одинаковым значением GroupIndex и с RadioItem = true, то в них могут появляться маркеры флажков, причем только в одном из них (на Рисунок 6.1, 6.2 свойство RadioItem установлено в true в разделах Шаблон 1 и Шаблон 2, имеющих одинаковое значение GroupIndex). Если вы зададите программно в одном из этих разделов Checked = true, то в остальных разделах Checked автоматически сбросится в false. Впрочем, установка Checked = true лежит на программе; эта установка может выполняться аналогично приведенному выше оператору.
Описанные маркеры флажков в режиме радиокнопок и в обычном режиме используются для разделов меню, представляющих собой различные опции, взаимоисключающие или совместимые.
Для каждого раздела могут быть установлены во время проектирования или программно во время выполнения свойства Enabled (доступен) и Visible (видимый). Если установить Enabled = false, то раздел будет изображаться серой надписью и не будет реагировать на щелчок пользователя. Если же задать Visible = false, то раздел вообще не будет виден, а остальные разделы сомкнутся, заняв место невидимого. Свойства Enabled и Visible используются для того, чтобы изменять состав доступных пользователю разделов в зависимости от режима работы приложения.
Начиная с Delphi 4 предусмотрена возможность ввода в разделы меню изображений. За это ответственны свойства разделов Bitmap и ImageIndex. Первое из них позволяет непосредственно ввести изображение в раздел, выбрав его из указанного вами файла. Второе позволяет указать индекс изображения, хранящегося во внешнем компоненте ImageList (см. ). Указание на этот компонент вы можете задать в свойстве Images компонента MainMenu. Индексы начинаются с 0. Если вы укажете индекс -1 (значение по умолчанию), изображения не будет.
Мы рассмотрели все основные свойства объектов, соответствующих разделам меню. Основное событие раздела — OnClick, возникающее при щелчке пользователя на разделе или при нажатии «горячих» клавиш быстрого доступа.
Рассмотрим теперь вопросы объединения главных меню вторичных форм с меню главной формы. Речь идет о приложениях с несколькими формами, в которых и главная, в вспомогательные формы имеют свои главные меню — компоненты MainMenu. Конечно, пользователю неудобно работать одновременно с несколькими окнами, каждое из которых имеет свое меню. Обычно надо, чтобы эти меню сливались в одно меню главной формы.
Приложения с несколькими формами могут быть двух видов: приложения с интерфейсом множества документов — так называемые MDI приложения, и обычные приложения с главной и вспомогательными формами. Типичными примерами приложений MDI являются программы Word и Excel. Рассмотрение особенностей этих видов приложений выходит за рамки данной книги. Сейчас нас интересует только один вопрос: как объединяются меню различных форм. В MDI приложениях меню дочерних форм всегда объединяются с меню родительской формы. А в приложениях с несколькими формами наличие или отсутствие объединения определяется свойством AutoMerge компонентов ТМаinMenu. Если требуется, чтобы меню вторичных форм объединялись с меню главной формы, то в каждой такой вторичной форме надо установить AutoMerge в true. При этом свойство AutoMerge главной формы должно оставаться в false.
Способ объединения меню определяется свойством разделов GroupIndex. По умолчанию все разделы меню имеют одинаковое значение GroupIndex, равное нулю. Если требуется объединение меню, то разделам надо задать неубывающие номера свойств GroupIndex. Тогда, если разделы встраиваемого меню имеют те же значения GroupIndex, что и какие-то разделы меню основной формы, то эти разделы заменяют соответствующие разделы основного меню. В противном случае разделы вспомогательного меню встраиваются между элементами основного меню в соответствии с номерами GroupIndex. Если встраиваемый раздел имеет GroupIndex меньший, чем любой из разделов основного меню, то разделы встраиваются в начало.
Тогда в момент, когда активизируется вторая форма, в первой появляется меню со структурой:
Пусть, например, в основной и вторичной формах структуры меню имеет следующие значения GroupIndex: Форма 1 Форма 2 2 - 4 1 - 3 | | | | 2 4 1 3 | | | 2 4 1
Тогда в момент, когда активизируется вторая форма, в первой появляется меню со структурой: 1 - 2 - 3 - 4 | | | | 1 2 3 4 | | | 1 2 4
В этом примере отсутствовали разделы, имеющие в обеих формах одинаковые значения GroupIndex. Если бы такие были, то при активизации второй формы соответствующие разделы ее меню заменили бы аналогичные разделы первой формы.
Если в меню имеются разделы, работающие как радиокнопки, то нельзя забывать, что их взаимодействие также определяется свойствами GroupIndex.
Теперь остановимся на одном из вопросов, связанных с меню в упоминавшихся выше приложениях MDI. В них пользователь может открывать сколько ему требуется окон документов. Обычно в подобных приложениях имеется меню Окно (см. Рисунок 6.4), которое содержит такие разделы, как Новое, Упорядочить и т.п. Последним идет обычно список открытых окон документов, в который заносятся названия открытых пользователем окон. Выбирая в этом списке, пользователь может переключаться между окнами документов.
Результат конструирования меню

При выборе нового раздела вы увидите в Инспекторе Объектов множество свойств данного раздела. Дело в том, что каждый раздел меню, т.е. каждый элемент свойства Items, является объектом типа TMenuItem, обладающим своими свойствами, методами, событиями.
Свойство Caption обозначает надпись раздела. Заполнение этого свойства подчиняется тем же правилам, что и заполнение аналогичного свойства в кнопках (см. ), включая использование символа амперсанта для обозначения клавиш быстрого доступа. Если вы в качестве значения Caption очередного раздела введете символ минус «-», то вместо раздела в меню появится разделитель (см. на Рисунок 6.1 и 6.2 разделители после разделов Сохранить как, Настройка принтера и Опции).
Свойство Name задает имя объекта, соответствующего разделу меню. Очень полезно давать этим объектам осмысленные имена, так как иначе вы скоро запутаетесь в ничего не говорящих именах типа N21. Куда понятнее имена типа MFile, MOpen, MSave и т.п.
Свойство Shortcut определяет клавиши быстрого доступа к разделу меню — «горячие» клавиши, с помощью которых пользователь, даже не заходя в меню, может в любой момент вызвать выполнение процедуры, связанной с данным разделом. Чтобы определить клавиши быстрого доступа, надо открыть выпадающий список свойства Shortcut в окне Инспектора Объектов и выбрать из него нужную комбинацию клавиш. Эта комбинация появится в строке раздела меню (см. команду Сохранить... на Рисунок 6.1, 6.2). В рассказано о некоторых дополнительных возможностях задания комбинаций горячих клавиш.
Свойство Default определяет, является ли данный раздел разделом по умолчанию своего подменю, т.е. разделом, выполняемым при двойном щелчке пользователя на родительском разделе. Подменю может содержать только один раздел по умолчанию, выделяемый жирным шрифтом (ем. раздел Открыть на Рисунок 6.1, 6.2).
Свойство Break используется в длинных меню, чтобы разбить список разделов на несколько столбцов. Возможные значение Break: mbNone — отсутствие разбиения меню (это значение принято по умолчанию), mbBarBreak и mbBreak — в меню вводится новый столбец разделов, отделенный от предыдущего полосой (mbBarBreak) или пробелами (mbBreak). На Рисунок 6.3 показан пример, в котором в разделе 1-3 установлено значение Break = mbBreak, а в разделе 1-5 — Break = mbBarBreak.
Фрейм выбора файла

В обработчик события OnShowHint компонента ApplicationEvents занесите оператор: if HintInfo.HintControl = Edit1 then begin HintStr := Edit1.Text; ApplicationEvents1.CancelDispatch; end;
Этот оператор в момент, когда должен отображаться ярлычок, проверяет, не является ли источником этого события (HintInfo.HintControl) окно редактирования Edit1. Если да, то текст ярлычка (HintStr) подменяется текстом, содержащимся в окне редактирования и принимаются меры (метод CancelDispatch), чтобы это событие не обрабатывалось другими компонентами ApplicationEvents, которые могут присутствовать в приложении. Пояснение всех этих операций см. в .
Теперь введите в модуль фрейма глобальную переменную FileName типа string, в которой будет отображаться выбранный файл. В обработчик щелчка на кнопке введите оператор if OpenDialog1.Execute then begin Edit1.Text := OpenDialog1.FileName; FileName := OpenDialog1.FileName; end; который вызывает диалог открытия файла и помещает в окно редактирования Edit1 и в переменную FileName имя файла, выбранного пользователем, вместе с путем к нему.
В обработчик события OnExit компонента Edit1 поместите оператор FileName := Edit1.Text; заносящий в переменную FileName имя файла, если пользователь не пользовался диалогом, а просто написал в окне имя файла.
Программирование фрейма закончено. Теперь создайте тестовую программу, использующую этот фрейм. Предположим, что вам нужно разместить на форме два фрагмента, описанных вами во фрейме. Перейдите в основной модуль вашего приложения и разместите на форме так, как вы уже делали, два объекта вашего фрейма (Рисунок 7.12 а).
Фреймы
В Delphi 5 введен новый компонент, который помогает поддерживать стилистическое единство приложения. Это Frame — фрейм. Он представляет собой нечто среднее между панелью и формой. С формой его роднит то, что он:С панелью фрейм роднит то, что он:
Таким образом, фрейм — это панель, т.е. некий фрагмент окна приложения, но способный переноситься на разные формы, в разные приложения и допускающий использование преимуществ наследования.
Начать проектирование нового фрейма можно командой File | New Frame или командой File | New и выбором пиктограммы Frame на странице New окна Депозитария. В обоих случаях перед вами откроется окно фрейма, подобное окну формы, а в Редакторе Кода вы увидите текст заготовки модуля фрейма: unit Unit2; Interface // Открытый интерфейс фрейма {Список подключаемых модулей} uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; {Объявление класса фрейма} type TFrame2 = class(TFrame) {Сюда Delphi помещает объявления компонентов, размещаемых на фрейме. Не добавляйте сюда ничего вручную} private // Закрытый раздел класса {Private declarations} {Сюда могут помещаться объявления переменных, функций и процедур, включаемых в класс фрейма, но не доступных для других модулей} public // Открытый раздел класса {Public declarations} {Сюда могут помещаться объявления переменных, функций и процедур, включаемых в класс фрейма и доступных для других модулей} end; {Сюда могут помещаться объявления типов, констант, переменных, функций и процедур, к которым будет доступ из других модулей, но которые не включаются в класс фрейма. Они будут едины для всех объектов фреймов} implementation // Реализация модуля {$R *.DFM} {Сюда могут помещаться предложения uses, объявления типов, констант, переменных, к которым не будет доступа из других модулей. Они будут едины для всех объектов фреймов. Тут же должны быть реализации всех объявленных в разделе interface функций и процедур, а также могут быть реализации любых дополнительных, не объявленных ранее функций и процедур} end.
Комментарии в приведенном тексте поясняют, куда и что можно помещать в модуле. Те переменные, объявления которых вы поместите в объявление класса, будут индивидуальны для каждого объекта фрейма. Объявления имеют обычный для класса вид. Например: A: integer;
Переменные, объявления которых вы поместите вне объявления класса, будут едины для всех объектов фрейма. Они объявляются как обычные переменные. Например: var A: integer;
На фрейм вы можете так же, как на форму, переносить и размещать любые компоненты, устанавливать их свойства, писать обработчики их событий и т.п.
Давайте создадим чисто тестовый фрейм, чтобы на его примере продемонстрировать проектирование фрейма, его использование, доступ к различным его элементам и наследование свойств.
Начните новое приложение и выполните команду File | New Frame. Перенесите на фрейм групповую панель GroupBox (см. ). Перенесите на панель метку Label и три кнопки Button. Разместите все эти компоненты примерно так, как показано на Рисунок 7.9, изменив соответственно их надписи (Caption) и назвав кнопки соответственно BSetup, BInc, BShow.
Иллюстрация различных вариантов панели PageControl

Каждая создаваемая вами страница является объектом типа TTabSheet. Это панель, на которой можно размещать любые управляющие компоненты, окна редактирования и т.п. После того, как вы создадите несколько страниц, выделите одну из них, щелкнув в ее середине, и посмотрите ее свойства в Инспекторе Объектов. Страница имеет следующие основные свойства:
Рассмотрим теперь компонент TabControl. Внешне этот компонент выглядит так же, как PageControl, и имеет много тех же свойств: Style, MultiLine, TabPosition, TabHeight, TabWidth, Images, ScrollOpposite, тех же событий: OnChanging и OnChange. Но принципиальное отличие его от PageControl заключается в том, что TabControl не имеет множества панелей (страниц). Компонент представляет собой одну страницу с управляющим элементом типа кнопки со многими положениями. И надо написать соответствующие обработчики событий OnChanging и OnChange, чтобы определить, что именно должно происходить на панели при переключениях закладок пользователем. У компонента имеется еще одно свойство — MultySelect, позволяющее множественный выбор закладок. Если это свойство установлено в true, то в обработчиках событий надо описать реакцию на такой выбор пользователя.
Число закладок и их надписи определяются свойством Tabs типа TStrings. В нем вы можете задать надписи закладок. Сколько строчек надписей вы укажете, столько будет закладок. Текущее состояние переключателя определяется свойством TabIndex. Вы можете установить его в процессе проектирования, чтобы определить исходное состояние переключателя. А затем в обработчиках событий OnChanging и OnChange можете читать это свойство, чтобы определить, что именно выбрал пользователь.
Применять компонент TabControl имеет смысл в тех приложениях, в которых нужен многопозиционный переключатель. Вы можете, конечно, имитировать с помощью TabControl поведение, аналогичное компоненту PageControl. Для этого достаточно, например, расположить в пределах TabControl две закрывающие друг друга панели и в обработчик события OnChange вставить оператор: if TabControl1.TabIndex = 0 then Panel2.Visible := false else Panel2.Visible := true;
Если Panel2 — верхняя панель, то при выборе первой закладки (TabIndex = 0) она будет делаться невидимой и под ней будет проступать нижняя панель.
Но подобная имитация PageControl не имеет смысла, так как проще использовать сам компонент PageControl. A TabControl надо применять, если требуются какие-то перестроения в рамках одной панели.
Теперь коротко остановимся на компонентах TabSet, TabbedNoteBook и NoteBook. Эти компоненты применяются в Delphi 1 и не рекомендуются для применения в 32-разрядных приложениях.
Компонент TabbedNoteBook является аналогом многостраничной панели PageControl. Только многие одинаковые у этих панелей свойства называются по-разному. Основное свойство — Pages, определяющее число страниц и надписи закладок. Свойство ActivePage определяет надпись активной страницы. Свойство PageIndex определяет индекс активной страницы (0 — первая страница). Так что узнать, какая страница активна, можно или по значению ActivePage, или по PageIndex.
В обработчик события OnChange, происходящего при переключении пользователем страницы, передается параметр NewTab, равный индексу новой страницы, и AllowChange — разрешение переключения. Для запрета переключения можно в обработчике задать AllowChange = false.
Рассмотренный компонент TabbedNoteBook является как бы соединением двух компонентов: пачки панелей (страниц) NoteBook и набора закладок TabSet. Эти два компонента могут использоваться и раздельно. Компонент TabSet во многом аналогичен рассмотренному ранее 32-разрядному компоненту TabControl. Это многопозиционный управляющий элемент, который сам по себе не имеет никакой панели. Его основное свойство — Tabs типа TStrings. Задавая строки этого свойства вы тем самым определяете число закладок и их надписи. Свойства StartMargin и EndMargin определяют поля — расстояния крайних закладок от краев компонента. Сами закладки всегда направлены вниз. Поэтому компонент TabSet надо располагать внизу управляемого им компонента. Свойство AutoScroll определяет появление кнопок при большом количестве закладок, которые позволяют пользователю прокручивать полосу закладок, как это делается в компонентах PageControl и TabControl при MultiLine = false. Индекс выбранной закладки определяется свойством TabIndex, значение которого можно устанавливать и можно читать в обработчике события OnChange, происходящего при смене пользователем закладки и идентичного такому событию в компоненте TabbedNoteBook.
Компонент NoteBook является пачкой панелей, имена и количество которых определяются свойством Pages, как в компоненте TabbedNoteBook. Индекс выбранной страницы определяется свойством PageIndex. В этом компоненте отсутствует управляющий элемент — закладки. Так что страницы можно переключать какими-то кнопками, переключать их в зависимости от действий пользователя, в зависимости от отображаемых данных и т.п. Компоненты NoteBook и TabSet могут быть, конечно, объединены программно в компонент, аналогичный TabbedNoteBook. Для этого достаточно в обработчик события OnChange компонента TabSet вставить оператор Notebook1.PageIndex := NewTab;
Но подобное использование этих компонентов вряд ли целесообразно: уж лучше использовать непосредственно TabbedNoteBook.
Инструментальные панели — компоненты ToolBar и PageScroller
Как уже говорилось выше, инструментальные панели можно создавать и не прибегая к специальным компонентам. Можно поместить на форму простейшую панель Panel, разместить на ней быстрые кнопки SpeedButton и панель готова. Остается только написать для кнопок соответствующий код. Но специализированные компоненты, которые мы рассмотрим в этом разделе, дают, конечно, дополнительные возможности для построения инструментальных панелей.Начнем рассмотрение компонентов, которые используются для построения различных инструментальных панелей, с компонента ToolBar. Пример панели, построенной на основе этого компонента, приведен на Рисунок 7.4.
Многостраничные панели — компоненты TabControl, PageControl, TabSet, TabbedNoteBook, NoteBook
Многостраничные панели позволяют экономить пространство окна приложения, размещая на одном и том же месте страницы разного содержания. На Рисунок 7.3 показаны различные формы отображения многостраничного компонента PageControl. Начнем рассмотрение многостраничных панелей именно с этого компонента.Общая характеристика
Панели являются контейнерами, служащими для объединения других управляющих элементов. Они могут выполнять как чисто декоративные функции, зрительно объединяя компоненты, связанные друг с другом по назначению, так и функции управления, организуя совместную работу своих дочерних компонентов. Примером этого была рассмотренная в организация работы группы радиокнопок.В таблице 7.1 приведен перечень панелей, обслуживающих их компонентов и компонентов внешнего оформления, включенных в библиотеку Delphi 5. Таблица 7.1 Панели и обслуживающие их компоненты
грамма

(групповое окно)

(панель)

(рамка)

(окно с прокруткой)

(разделитель панелей)

(инстументальная панель)

(страница с закладкой)

(многостраничное окно)

(полоса состояния)

(инструментальная панель)

(инструментальная перестраиваемая панель)

(прокрутка страниц)

(блокнот с закладками)

(многостраничная форма)

(пачка страниц))

(фрейм)
Окно New Items с включенным новым фреймом

При включенной кнопке Inherit вы получите в своем проекте фрейм, наследующий размещенному в Депозитарии. Это значит, что если вы что-то измените во фрейме, хранящемся в Депозитарии, то это отразится при перекомпиляции во всех проектах, которые наследуют этот фрейм. Однако, изменения в наследуемых фреймах никак не скажутся на свойствах фрейма, хранящегося в Депозитарии.
При включенной кнопке Use вы получите режим использования. В этом случае в ваш проект включится сам фрейм, хранящийся в Депозитарии. Значит любое изменение свойств фрейма, сделанное в вашем проекте, отразится и на хранящемся в Депозитарии фрейме, и на всех проектах, наследующих или использующих этот фрейм (при их перекомпиляции).
Таким образом, режим Inherit целесообразно использовать для всех модулей вашего проекта, а режим Use — для изменения базового фрейма. Тогда усовершенствование вами базового фрейма будет синхронно сказываться на всех модулях проекта при их перекомпиляции.
Введенный в Delphi 5 компонент фрейм благодаря использованию наследования позволяет обеспечить единство стилистических решений не только внутри приложения, но и в рамках серии разрабатываемых вами приложений. Вам достаточно один раз разработать какие-то часто применяемые фреймы, включить их в Депозитарий, а затем вы можете использовать их многократно во всех своих проектах.
Панели общего назначения — компоненты Panel, GroupBox, Bevel, ScrollBox, Splitter
На Рисунок 7.1 приведен пример, демонстрирующий вид таких панелей, как Panel, GroupBox, Bevel, ScrollBox. В левой части формы размещены компоненты Panel с различными значениями параметров. С этих компонентов мы и начнем рассмотрение панелей.Панели с разделителем


Откомпилируйте приложение, запустите его на выполнение и убедитесь (Рисунок 7.2), что, потянув курсором мыши за границу раздела между панелями, вы можете перемещать эту границу.
Закройте приложение и посмотрите в Инспекторе объектов свойства компонента Splitter. Свойство ResizeStyle определяет поведение разделителя при перемещении его пользователем. По умолчанию это свойство равно rsPattern. Это означает (как вы могли видеть в эксперименте), что пока пользователь тянет курсором мыши границу, сам разделитель не перемещается и панели тоже остаются прежних размеров. Перемещается только шаблон линии, указывая место намечаемого перемещения границы. Лишь после того, как пользователь отпустит кнопку мыши, разделитель переместится и панели изменят свои размеры. Практически такая же картина наблюдается, если установить ResizeStyle = rsLine. При ResizeStyle = rsUpdate в процессе перетаскивания границы пользователем разделитель тоже перемещается и размеры панелей все время меняются. Это, может быть, удобно, если пользователь хочет установить размер панели таким, чтобы на ней была видна какая-то конкретная область. Но так как процесс перетаскивания в этом случае сопровождается постоянной перерисовкой панелей, наблюдается неприятное мерцание изображения. Так что этот режим можно рекомендовать только в очень редких случаях. Если установить ResizeStyle = rsNone, то в процессе перетаскивания границы не перемещается ни сама граница, ни изображающая ее линия. Вряд ли это удобно пользователю, так что я не могу представить случая, когда было бы выгодно использовать этот режим.
Свойство MinSize компонента Splitter устанавливает минимальный размер в пикселях обеих панелей, между которыми зажат разделитель. Задание такого минимального размера необходимо, чтобы при перемещениях границы панель не сжалась бы до нулевого размера или до такой величины, при которой на ней исчезли бы какие-то необходимые для работы элементы управления. К сожалению, как вы сами можете убедиться, экспериментируя с компонентом Splitter, свойство MinSize не всегда срабатывает верно. К тому же это свойство относится к обеим панелям, граница между которыми перемещается, а в ряде случаев желательно раздельно установить различные минимальные размеры одной и другой панели. Это проще (и надежнее) сделать, задав в панелях соответствующие значения свойства Constraints. Например, если вы в описанном выше тестовом приложении зададите в свойстве Constraints в панели Panel2 значение MinWidth = 50, а в панели Panel3 MinWidth = 100, то именно только до таких размеров в процессе выполнения приложения пользователь сможет изменять эти панели.
Компонент Splitter имеет событие OnMoved, которое наступает после конца перемещения границы. В обработчике этого события можно предусмотреть, если необходимо, упорядочение размещения компонентов на панелях, размеры которых изменились: переместить какие-то метки, изменить размеры компонентов и т.д.
Панель GroupBox не имеет таких широких возможностей задания различных стилей оформления, как Panel. Но она имеет встроенную рамку с надписью (см. Рисунок 7.1), которая обычно используется для выделения на форме группы функционально объединенных компонентов. Никаких особых свойств, отличных от уже рассмотренных, панель GroupBox не имеет.
Компонент Bevel формально не является панелью, он не может служить контейнером для компонентов. Например, с помощью Bevel нельзя сгруппировать радиокнопки. Однако, чисто зрительно компонент Bevel может использоваться как подобие панели. На Рисунок 7.1 в правой нижней части окна представлены различные варианты оформления Bevel.
Стиль отображения Bevel определяется свойством Style, которое может принимать значения bsLowered — утопленный, и bsRaised — приподнятый. А контур компонента определяется свойством Shape, которое может принимать значения: bsBox — прямоугольник, bsFrame — рамка, bsSpacer — пунктирная рамка, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine — соответственной верхняя, нижняя, левая и правая линии. В зависимости от значения Style линии могут быть утопленными или выступающими. Все перечисленные варианты приведены на Рисунок 7.1.
Остановимся теперь на компоненте ScrollBox — панели с прокруткой. Этот компонент предназначен для создания области, в которой могут размещаться компоненты, занимающие площадь большую, чем сам ScrollBox. Например, компонент ScrollBox можно использовать для размещения длинных текстовых строк или больших инструментальных панелей, которые исходя из соображений экономии площади окна нецелесообразно отображать целиком. В примере Рисунок 7.1 в ScrollBox помещена панель с надписью: «Это ScrollBox, содержащая панель с длинной надписью». В пределах ScrollBox видна только часть этой панели. Если размеры ScrollBox меньше, чем размещенные компоненты, то появляются полосы прокрутки, которые позволяют пользователю перемещаться по всем размещенным в ScrollBox компонентам.
Разместить в пределах небольшой области ScrollBox большие компоненты или много компонентов, занимающих в сумме большую площадь, можно в процессе проектирования, например, с помощью такого приема. Увеличьте временно размер ScrollBox так, чтобы в этом компоненте поместилось все, что вы хотите разместить. Проведите необходимое размещение. А затем сократите размеры ScrollBox до требуемых.
Свойство BorderStyle определяет стиль рамки компонента ScrollBox. Свойство AutoScroll позволяет задать автоматическое появление необходимых полос прокрутки, если размер размещенных компонентов превышает размер области по горизонтали, вертикали или в обоих измерениях. Если по каким-то соображениям это нежелательно, вы можете сами управлять появлением горизонтальной и вертикальной полос с помощью свойств HorzScrollBar и VertScrollBar соответственно. Но в этом случае вы должны сами задавать ряд свойств полосы прокрутки и, прежде всего, Range — размер в пикселях прокручиваемой области. Значение перемещения при однократном нажатии пользователем кнопки прокрутки может рассчитываться компонентом автоматически исходя из размеров области и окна, если свойство полосы прокрутки Smooth установлено в true. В противном случае вы должны задать величину единичного перемещения в свойстве Increment.
Перестраиваемая панель на


Перестраиваемые панели — компоненты CoolBar и ControlBar
Перестраиваемые панели являются дальнейшим развитием инструментальных панелей. Только в перестраиваемых панелях сами инструментальные панели обычно являются компонентами более сложных образований. Примером перестраиваемой панели может служить панель ИСР Delphi 5, включающая в себя ряд более мелких панелей быстрых кнопок и палитру компонентов. Пользователь может настраивать их, изменять местоположение панелей и т. п.Начнем рассмотрение с компонента CoolBar. Он позволяет строить перестраиваемые панели, состоящие из полос (bands). В полосы могут включаться инструментальные панели ToolBar и любые другие оконные компоненты: окна редактирования, панели и т.п. Каждый из этих компонентов автоматически снабжается средствами перемещения его пользователем в пределах окна CoolBar. В полосы могут вставляться и не оконные компоненты, например, метки. Но они не будут перемещаемыми.
Опробуйте в работе этот компонент. Поместите его на форму. Перенесите на него другие компоненты, например, ToolBar и Edit. Когда вы размещаете на CoolBar очередной компонент, ему отводится отдельная полоса и он растягивается на всю ширину CoolBar. Около каждого компонента появляется слева полоска, за которую компонент можно перемещать. Например, взявшись за эту полоску вы можете переместить полосу вместе с ее компонентом в тот ряд, где уже имеется другой компонент. Тогда они расположатся в ряд один левее другого (см. пример на Рисунок 7.6).
Полоса состояния StatusBar
Компонент StatusBar представляет собой ряд панелей, отображающих полосу состояния в стиле Windows. Обычно эта полоса размещается внизу формы.Свойство SimplePanel определяет, включает ли полоса состояния одну или множество панелей. Если SimplePanel = true, то вся полоса состояния представляет собой единственную панель, текст которой задается свойством SimpleText. Если же SimplePanel = false, то полоса состояния является набором панелей, задаваемых свойством Panels. В этом случае свойство SizeGrip определяет, может ли пользователь изменять размеры панелей в процессе выполнения приложения.
Каждая панель полосы состояния является объектом типа TStatusPanels. Свойства панелей вы можете задавать специальным редактором наборов. С этим инструментом вы уже имели дело в разделах и при редактировании заголовков и полос. Вызвать редактор можно тремя способами: из Инспектора Объектов кнопкой с многоточием около свойства Panels, двойным щелчком на компоненте StatusBar или из контекстного меню, выбрав команду Panels Editor. В окне редактора вы можете перемещаться по панелям, добавлять новые или уничтожать существующие. При перемещении по панелям в окне Инспектора Объектов вы будете видеть их свойства.
Основное свойство каждой панели — Text, в который заносится отображаемый в панели текст. Его можно занести в процессе проектирования, а затем можно изменять программно во время выполнения. Другое существенное свойство панели — Width (ширина).
Программный доступ к текстам отдельных панелей можно осуществлять двумя способами: через индексированное свойство Panels или через его индексированное подсвойство Items. Например, два следующих оператора дадут идентичный результат: StatusBar1.Panels[0].Text := 'текст 1'; или StatusBar1.Panels.Items[0].Text := 'текст 1';
Оба они напечатают текст «текст 1» в первой панели.
Количество панелей полосы состояния можно определить из подсвойства Count свойства Panels. Например, следующий оператор очищает тексты всех панелей: for i := 0 to StatusBar1.Panels.Count - 1 do StatusBar1.Panels[i].Text := '';
На Рисунок 7.8 приведен пример текстового редактора на основе компонента RichEdit, содержащий полосу состояния. В ее первой панели отображается номер строки и символа, перед которым находится курсор, во второй — отображается, модифицирован текст в окне, или нет. В третьей панели отображается подсказка о назначении компонента, над которым в данный момент расположен курсор мыши.
Приложения с двумя фреймами выбора файла: его форма (а) и приложение во время выполнения (б)


Вы разработали достаточно полезный фрейм и хотели бы его сохранить для использования в будущих приложениях. Это легко сделать, внеся его в Депозитарий. Щелкните на своем фрейме правой кнопкой мыши и выберите из всплывшего меню раздел Add To Repository. Перед вами откроется окно, представленное на Рисунок 7.13. В верхнем его окне редактирования Title вы должны написать название вашего фрейма — подпись под его пиктограммой при входе в Депозитарий. В следующем окне — Description можете написать более развернутое пояснение. Его может увидеть пользователь, войдя в Депозитарий, щелкнув правой кнопкой мыши и выбрав во всплывшем меню форму отображения View Details. В выпадающем списке Page вы можете выбрать страницу Депозитария, на которой хотите разместить пиктограмму своего фрейма. Впрочем, вы можете указать и новую страницу с новым заголовком (Мои формы на Рисунок 7.13). В результате она появится в Депозитарии.
В окне Author вы можете указать сведения о себе как об авторе. Наконец, если стандартная пиктограмма вас не устраивает, вы можете выбрать другую, щелкнув на кнопке Browse.... После выполнения всех этих процедур щелкните на кнопке OK и ваш фрейм окажется включенным в Депозитарий.
Теперь вы можете использовать его в последующих ваших приложениях. Для этого вам надо будет выполнить команду File | New и в открывшемся диалоговом окне New Items отыскать ваш фрейм (Рисунок 7.14).
Пример фрейма

В модуле введены переменные:
Введенные в модуль обработчики щелков на кнопках обеспечивают сброс всех переменных на 1 (процедура TFrame2.BSetupClick), увеличение всех переменных на 1 (процедура TFrame2.BIncClick), отображение текущего состояния переменных (процедура TFrame2.BShowClick).
Теперь давайте разместим несколько экземпляров фрейма на форме. Перейдите в основную форму приложения и выберите в палитре компонентов Frame (первая кнопка на странице Standard). Появится диалоговое окно, в котором будет спрашиваться, какой фрейм вы хотите разместить на форме. Выберите ваш фрейм Frame2 и он появится на форме. Можете отбуксировать его, как обычный компонент, в нужное место. Повторите эту операцию еще раз и разместите на форме второй фрейм (Рисунок 7.10). Добавьте кнопку и метку, задав ее свойство Align равным alTop и свойство Alignment равным taCenter.
Вы получили форму, содержащую два объекта — фрейма. Можете изменить какие-то свойства объектов. Например, изменить надписи (Caption) групповых панелей GroupBox (см. Рисунок 7.10 а).
Пример инструментальной панели и ее прокрутки компонентом PageScroller

Свойство Margin компонента PageScroller определяет размер полей в пикселях, которые оставляются между краем окна PageScroller и прокручиваемым компонентом. По умолчанию эти поля равны нулю, но надо задать свойству Margin некоторое положительное значение. Иначе края прокручиваемого компонента могут быть плохо видны.
Свойство AutoScroll определяет, должна ли прокрутка осуществляться автоматически, как только курсор мыши пройдет над кнопкой прокрутки. Опробуйте режим автоматической прокрутки экспериментально. На мой взгляд лучше оставлять значение AutoScroll равным false, поскольку такая автоматическая прокрутка не очень удобна пользователю.
Пример инструментальной панели ToolBar

Занесение компонентов на панель ToolBar можно, в принципе, осуществлять обычным способом — переносом их из палитры компонентов. Но для занесения кнопок имеется и более простой вариант. Щелкните на ToolBar правой кнопкой мыши и выберите из всплывшего меню команду New Button. На форме появится очередная кнопка — объект типа TToolButton. Это не совсем обычная кнопка, так как в дальнейшем вы увидите, что внешне она может не походить на кнопку. Ее вид и поведение определяется ее свойством Style, которое по умолчанию равно tbsButton — кнопка. Другие возможные стили мы рассмотрим позднее. А как кнопка этот объект очень похож на кнопку SpeedButton. Только изображение на кнопке определяется не свойством Glyph, а свойством ImageIndex. Оно определяет индекс изображения, хранящегося во внешнем компоненте ImageList (см. ). Указание на этот компонент может задаваться такими свойствами компонента ToolBar, как Images, DisabledImages (указывает на список изображений кнопок в недоступном состоянии) и HotImages (указывает на список изображений кнопок в моменты, когда над ними перемещается курсор мыши).
Свойство MenuItem позволяет задать раздел главного или контекстного меню (см. разделы и ), который дублируется данной кнопкой. При установке этого свойства, если в соответствующем разделе меню было задано изображение и установлен текст подсказок (свойство Hint), то это же изображение появится на кнопке и тот же текст появится в свойстве Hint кнопки. Передадутся из раздела меню в кнопку также значения свойств Enabled (доступность) и Visible (видимость). Правда, все это передастся в кнопку только в момент установки свойства MenuItem. Если в процессе дальнейшего проектирования вы измените соответствующие свойства раздела меню, это не отразится на свойствах кнопки. Но если вы сотрете значение MenuItem, а потом установите его снова, то в кнопке зафиксируются новые значения свойств раздела меню.
Свойство Wrap, установленное в true, приводит к тому, что после этой кнопки ряд кнопок на панели прерывается и следующие кнопки размещаются в следующем ряду.
Теперь вернемся к свойству Style, задающему стиль кнопки. Значение Style = tbsCheck определяет, что после щелчка пользователя на кнопке она остается в нажатом состоянии. Повторный щелчок на кнопке возвращает ее в отжатое состояние. Поведение такой кнопки подобно кнопкам SpeedButton и определяется аналогичными свойствами AllowAllUp и Down (см. ). Если при этом в нескольких кнопках установлено свойство Grouped = true, то эти кнопки образуют группу, из которой только одна кнопка может находиться в нажатом состоянии.
Значение Style = tbsDropDown соответствует кнопке в виде выпадающего списка. Этот стиль удобен для воспроизведения выпадающего меню. Если для подобной кнопки задать в качестве свойства MenuItem головной раздел меню, то в выпадающем списке автоматически будут появляться разделы выпадающего меню. В примере Рисунок 7.4 стиль tbsDropDown имеет четвертая слева кнопка. В ней в качестве свойства MenuItem задан раздел Опции из меню, рассмотренного в и представленного на Рисунок 6.2. При Style = tbsDropDown можно вместо свойства MenuItem задать свойство DropDownMenu, определяющее контекстное меню (компонент PopupMenu), которое будет отображаться в выпадающем списке.
Значение Style = tbsSeparator приводит к появлению разделителя, позволяющего отделить друг от друга кнопки разных функциональных групп. Значение Style = tbsDivider приводит к появлению разделителя другого типа — в виде вертикальной линии. Разделитель можно ввести и из контекстного меню ToolBar, выбрав команду New Separator.
Свойство кнопки Indeterminate задает ее третье состояние — не нажатая и не отпущенная. Это свойство можно устанавливать в true во время выполнения, если в данном режиме кнопка не доступна.
Свойство Marked выделяет кнопку.
Мы рассмотрели занесение на панель кнопок. Но в инструментальных панелях нередко используются и выпадающие списки. Например, для задания размера шрифта. Не представляет труда перенести на панель ToolBar такие компоненты, как ComboBox (это изображено на Рисунок 7.4), SpinEdit и др.
Из общих свойств компонента ToolBar следует еще отметить ButtonHeight и ButtonWidth — высота и ширина кнопок в пикселях, и Wrapable — автоматический перенос кнопок в следующий ряд панели, если они не помещаются в предыдущем. Такой перенос осуществляется и во время проектирования, и во время выполнения при изменении пользователем размеров панели.
Свойства, определяющие вид панели ToolBar: BorderWidth — ширина бордюра, EdgeInner и EdgeOuter — стиль изображения внутренней и внешней части панели (утопленный или выступающий), EdgeBorders — определяет изображение отдельных сторон панели (левой, правой, верхней, нижней).
Мы рассмотрели построение инструментальной панели на основе компонента ToolBar. Но полоса может быть очень длинной и не помещаться в отведенном ей месте формы. Примером является палитра компонентов Delphi. В этих случаях может помочь компонент PageScroller, обеспечивающий прокрутку панели. Собственно говоря, PageScroller может прокручивать любой компонент, не обязательно панель ToolBar. Например, он может прокручивать какую-то панель вместе с размещенными на ней компонентами. В этом отношении он напоминает рассмотренный в компонент ScrollBox. Но есть и заметные различия между этими двумя компонентами: PageScroller прокручивает только один компонент и только в одном направлении — горизонтальном или вертикальном. Да и оформление управления прокруткой у PageScroller не похоже на полосы прокрутки в ScrollBox.
Пример применения компонента PageScroller показан на Рисунок 7.5. Это тот же пример, что и на Рисунок 7.4. В верхней части окна показана та же инструментальная панель, что и на рис, 7.4, а ниже показана идентичная панель, но заключенная в небольшое окно PageScroller и снабженная кнопкой прокрутки.
Пример использования фреймов; форма (а) и приложение в работе (б)


Теперь давайте напишем обработчик щелчка на кнопке главной формы. Прежде всего взгляните на текст модуля этой формы. Вы увидите, что в нем в описании класса формы появились две строки: Frame21: TFrame2; Frame22: TFrame2;
Это объявления объектов фреймов. Все компоненты, размещенные на фреймах, напрямую из модуля формы не видны. Доступ к ним можно получить только через объекты Frame21 и Frame22. Имена компонентов, размещенных во фреймах, локальные. Несмотря на то, что во фреймах имеются кнопки с именами BShow, вы можете назвать тем же именем кнопку на форме.
Поместите в обработчик щелчка на этой кнопке оператор Label1.Caption := 'В(Frame21)='+IntToStr(Frame21.B) + ' B(Frame22)='+IntToStr(Frame22.B) + ' C=' + IntToStr(C);
Он отображает в метке Label1 значения переменных В объектов фреймов и значение переменной С класса фрейма. Значения переменных А и D отобразить невозможно, поскольку эти переменные недоступны из внешних модулей. Если вы попытаетесъ отобразить их значения, компилятор выдаст сообщение об ошибке.
Сохраните ваше приложение, оттранслируйте его и выполните. Манипулируя кнопками вы легко сможете убедиться (см. Рисунок 7.10 б), что переменные А и В независимы для каждого фрейма, а переменные С и D одинаковы. Точнее оба фрейма оперируют с одними и теми же переменными С и D.
Рассмотренный фрейм не имел никакого практического значения. Давайте построим более полезный пример. Во многих диалогах при установке различных опций фигурирует фрагмент, фрейм которого показан на Рисунок 7.11. Фрагмент включает в себя панель GroupBox, окно редактирования, в котором пользователь может написать имя файла, и кнопку Обзор, которая позволяет выбрать файл в стандартном диалоге Windows открытия файла. Если путь к файлу длинный, то полное имя файла с путем может не помещаться в окне редактирования. Поэтому полезно для него предусмотреть всплывающее окно, которое отображало бы полное имя файла вместе с путем и всплывало бы, если пользователь задержал над ним курсор мыши.
Давайте построим подобный фрейм и опробуем его в работе. Начните новое приложение и выполните команду File | New Frame. Перенесите на фрейм групповую панель GroupBox. Перенесите в эту панель окно редактирования Edit, кнопку Button, диалог OpenDialog (см. ) и компонент ApplicationEvents — перехватчик событий приложения (см. ). Расположите компоненты примерно так, как показано на Рисунок 7.11.
Пример панелей общего назначения

Одним из назначений панелей является также группирование таких управляющих элементов, как RadioButton — радиокнопки (см. ). Все радиокнопки, расположенные на панели, работают как согласованная группа: в любой момент может быть выбрана только одна из них. Аналогично согласованной группой работают и расположенные на панели быстрые кнопки SpeedButton (см. ), если они имеют одинаковое значение свойства GroupIndex. В то же время SpeedButton, расположенные на разных панелях или на панели и форме, не образуют связанной группы даже при одинаковом значении GroupIndex.
Внешний вид панели Panel определяется совокупностью параметров BevelInner — стиль внутренней части панели, BevelOuter — стиль внешней части панели, BevelWidth — ширина внешней части панели, BorderStyle — стиль бордюра, BorderWidth — ширина бордюра. Результат сочетания некоторых значений этих параметров показан на Рисунок 7.1. Верхняя панель соответствует значениям параметров по умолчанию. Нижняя панель соответствует случаю, когда не определен стиль ни одной из областей панели (значения всех параметров равны None), В этом случае сама панель никак не выделяется на форме. Видна только надпись на ней (свойство Caption), если надпись задана, и, конечно, видны те компоненты, которые размещаются на панели.
Если вы строите приложение, в котором разрешаете пользователю изменять размер окна, надо позаботиться о том, чтобы синхронно с этим изменялись и размеры панелей. В уже коротко говорилось о свойствах Align, Anchors и Constraints, которые позволяют решить эту задачу. Но в ряде приложений этого мало. В зависимости от ситуации какие-то панели в приложении могут оказаться временно перегруженными отображаемыми данными, а другие в значительной степени пустыми. В этих случаях полезно предоставить пользователю возможность перемещать границы, разделяющие различные панели, изменяя их относительные размеры. Пример такой возможности вы можете увидеть в программе Windows «Проводник».
В библиотеке Delphi имеется специальный компонент — Splitter, который позволяет легко осуществить это. Рассмотрим это на примере. Пусть вы хотите иметь в приложении форму, содержащую три панели, которые располагаются так, как показано на Рисунок 7.2. При изменении пользователем размеров окна панель Panel1 должна в любом случае занимать всю нижнюю часть окна, не увеличиваясь в высоту. Панель Panel2 должна занимать левую часть окна, изменяя при изменении размеров окна свою высоту, но не изменяя ширину. А панель Panel3 должна занимать всю оставшуюся часть окна. Кроме того мы хотим обеспечить пользователю возможность изменять положение границы между панелями Panel2 и Panel3, расширяя одну из этих панелей и соответственно сжимая другую.
Исходя из наших требований свойство Align надо установить у панели Panel1 в alBottom (выравнивание по нижнему краю окна), у панели Panel2 — в аlLeft (выравнивание по левому краю окна), у панели Panel3 — в alClient (выравнивание по всей оставшейся части клиентской области окна). А между Panel2 и Panel3 надо разместить Splitter — разделитель.
Пример перестраиваемой панели на основе компонента CoolBar

Свойство Text задает текст, который может появиться в начале соответствующей полосы. Это свойство можно оставлять пустым. А можно и задать его — надписи «Панель 1», «Панель 2», «Окно 1», «Окно 2» на Рисунок 7.6 заданы именно этим свойством.
Вместо свойства Text (или наряду с ним) можно задать свойство ImageIndex — индекс списка изображений ImageList (см. ), ссылка на который задается свойством Images. Указанные таким образом изображения появятся в начале соответствующих полос (см. верхние полосы на Рисунок 7.6).
Свойства MinHeight и MinWidth определяют минимальную высоту и ширину полосы при перестроениях пользователем полос панели.
Свойство FixedSize определяет, фиксирован ли размер данной полосы или он может изменяться пользователем. По умолчанию для всех полос FixedSize = false, т.е. все полосы перестраиваются. Но при желаний размеры некоторых полос можно зафиксировать, задав для них FixedSize = true.
Для компонента CoolBar в целом, помимо обычных для других панелей свойств, надо обратить внимание на свойство BandMaximize. Оно определяет действие, которым пользователь может установить максимальный размер полосы, не перетаскивая ее границу: bmNone — такое действие не предусмотрено, bmClick — щелчком мыши, bmDblClick — двойным щелчком. Наиболее целесообразно, по-видимому, задавать значения bmDblClick или bmNone, поскольку значение bmClick приводит к резкому перестроению полос даже при случайном щелчке мыши.
Свойство FixedOrder, если его установить в true, не разрешит пользователю в процессе перемещений полос изменять их последовательность. Вероятно, такое задание лучше, чем значение по умолчанию, равное false, поскольку чрезмерная свобода для пользователя способна его запутать.
Свойство Vertical указывает вертикальное или горизонтальное расположение полос. По умолчанию Vertical = false, что соответствует горизонтальным полосам.
Запустите свое тестовое приложение, если вы его построили, и опробуйте его в работе. Вы увидите, как легко пользователь может перестраивать панель.
Еще большую свободу перестроений дает пользователю компонент ControlBar, который появился в Delphi 4. Только оформление панели несколько отличается от CoolBar и кроме того в ней может широко применяться техника перетаскивания и встраивания Drag&Doc, широко используемая в Delphi (начиная с Delphi 4), в частности, используемая в инструментальных панелях среды проектирования Delphi.
Поместите на форму компонент ControlBar и перенесите на него несколько компонентов, например, инструментальных панелей ToolBar и окон редактирования Edit. Вы увидите (см. Рисунок 7.7), что каждый компонент, попадая на ControlBar, получает полосу захвата, свойственную технологии Drag&Doc.
Установите у компонентов, размещенных на ControlBar, свойство DragMode = dmAutomatic и DragKind = dkDock. Это означает автоматическое выполнение операций Drag&Doc.
Свойства компонента ControlBar RowSize и RowSnap определяют процедуру встраивания. Свойство RowSize задает размеры полос, в которые могут встраиваться компоненты, a RowSnap определяет захват полосами встраиваемых компонентов. Свойство AutoDrag определяет, можно (при значении true), или нельзя простым перетаскиванием вынести полосу за пределы ControlBar.
Пример редактора RichEdit с полосой состояния

Эти операторы заполняют первые две панели полосы состояния. Занесение в строку состояния подсказок в целом выходит за рамки данной книги. Но, поскольку, говоря о полосе состояния, было бы неправильно обойти этот вопрос, ниже без особых пояснений описывается, как это можно сделать.
В Delphi 5 это делается очень просто с помощью компонента ApplicationEvents. Этот компонент помещается на форму и в обработчик его события OnHint (см. подробнее в ) заносится оператор, который для приведенного на Рисунок 7.8 примера имеет вид: StatusBar1.Panels[2].Text := Application.Hint;
Он отображает в панели свойство приложения Application.Hint, а в это свойство автоматически переносится вторая часть текста подсказки (свойство Hint — о нем подробнее сказано ниже) того компонента, над которым перемещается курсор мыши.
В предыдущих версиях Delphi это оформляется более громоздко. Аналогичный оператор надо занести в обработчик события OnHint переменной Application (см. ). Этот обработчик вам надо создать вручную. Пусть вы назвали его DisplayHint.
Прототип этой функции DisplayHint можно внести в описание класса формы. Кроме того надо указать приложению на эту функцию как на обработчик события OnHint. Это можно сделать, например, задав в обработчике события OnCreate формы оператор: Application.OnHint := DisplayHint;
В итоге текст вашего модуля может иметь вид (приводится с сокращениями): interface type TForm1 = class(TForm) StatusBar1: TStatusBar; procedure FormCreate(Sender: TObject); procedure DisplayHint(Sender: TObject); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.DisplayHint(Sender: TObject); begin StatusBar1.Panels[2].Text := Application.Hint; end; procedure TForm1.FormCreate (Sender: TObject); begin Application.OnHint := DisplayHint; end;
Как видим, все это существенно более громоздко, чем указанные выше возможности, реализованные в Delphi 5.
Чтобы все это работало, надо в свойствах Hint тех компонентов, пояснения которых вы хотите отображать в панели состояния, написать соответствующие тексты. Если вы к тому же хотите, чтобы у компонента появлялся ярлычок с короткими подсказками при задержке пользователем мыши над этим компонентом, то текст в свойстве Hint должен состоять из двух частей, разделенных символом вертикальной черты «|». Например, в свойстве Hint быстрой кнопки, соответствующей разделу меню Сохранить можно ввести текст: «Сохранить|Сохранение документа в файле». Первая часть этого текста будет появляться в ярлычке кнопки, если ее свойство ShowHint (показать подсказку) установлено в true. А вторую часть описанные выше операторы будут отображать в строке состояния при перемещении курсора мыши над кнопкой, независимо от значения ее свойства ShowHint.
Диалог в старом стиле при включенной опции ofOldStyleDialog и выключенной опции ofHideReadOnly

Можно также написать обработчики событий OnFolderChange — изменение каталога, OnSelectionChange — изменение имени файла, OnTypeChange — изменение типа файла. В этих обработчиках вы можете предусмотреть какие-то сообщения пользователю.
Теперь приведем примеры использования диалогов OpenDialog и SaveDialog. Пусть ваше приложение включает окно редактирования Memo1, в которое по команде меню Открыть вы хотите загружать текстовый файл, а после каких-то изменений, сделанных пользователем, — сохранять по команде Сохранить текст в том же файле, а по команде Сохранить как... — в файле с другим именем.
Введите на форму компоненты — диалоги OpenDialog и SaveDialog. Предположим, что вы оставили их имена по умолчанию — OpenDialog1 и SaveDialog1. Поскольку после чтения файла вам надо запомнить его имя, чтобы знать под каким именем потом его сохранять, вы можете определить для этого имени переменную, назвав ее, например, FName: var FName: string;
Тогда обработка команды Открыть может сводиться к следующему оператору: if OpenDialog1.Execute then begin FName := OpenDialog1.FileName; Memo1.Lines.LoadFromFile(FName); end;
Этот оператор вызывает диалог, проверяет, выбрал ли пользователь файл (если выбрал, то функция Execute возвращает true), после чего имя выбранного файла (OpenDialog1.FileName) сохраняется в переменной FName и файл загружается в текст Memo1 методом LoadFromFile.
Обработка команды Сохранить выполняется оператором Memo1.Lines.SaveToFile(FName);
В данном случае нет необходимости обращаться к какому-то диалогу, поскольку имя файла известно: оно хранится в переменной FName.
Обработка команды Сохранить как... выполняется операторами: SaveDialog1.FileName := FName; if SaveDialog1.Execute then begin FName := SaveDialog1.FileName; Memo1.Lines.SaveToFile(FName); end;
Первый из этих операторов присваивает свойству FileName компонента SaveDialog1 запомненное имя файла. Это имя по умолчанию будет предложено пользователю при открытии диалога Сохранить как.... Следующий оператор открывает диалог и, если пользователь выбрал в нем файл, запоминает новое имя файла и сохраняет в файле с этим именем текст компонента Memo1.
Мы рассмотрели диалоги открытия и сохранения файлов произвольного типа. Начиная с Delphi 3 в библиотеке имеются специализированные диалоги открытия и закрытия графических файлов: OpenPictureDialog и SavePictureDialog. Диалоговые окна, открываемые этими файлами, приведены на Рисунок 8.5 и 8.6. От окон, открываемых компонентами OpenDialog и SaveDialog (Рисунок 8.1, 8.2), они отличаются удобной возможностью просматривать изображения в процессе выбора файла.
Диалог выбора цвета — компонент ColorDialog
Компонент ColorDialog вызывает диалоговое окно выбора цвета, представленное на Рисунок 8.9. В нем пользователь может выбрать цвет из базовой палитры или, нажав кнопку Определить цвет, раскрыть дополнительную панель (на Рисунок 8.9 она раскрыта), позволяющую синтезировать цвет, отличный от базовых. Синтезированный цвет можно добавить кнопкой Добавить в набор в палитру дополнительных цветов на левой панели и использовать его в дальнейшем.Диалог выбора шрифта — компонент FontDialog
Компонент FontDialog вызывает диалоговое окно выбора атрибутов шрифта, представленное на Рисунок 8.8. В нем пользователь может выбрать имя шрифта, его стиль (начертание), размер и другие атрибуты.Диалоги открытия и сохранения
Компоненты OpenDialog — диалог «Открыть файл» и SaveDialog — диалог «Сохранить файл как...», пожалуй, используются чаще всего, в большинстве приложений. Примеры открываемых ими диалоговых окон приведены на Рисунок 8.1 и 8.2.Диалоги печати и установки принтера — компоненты PrintDialog и PrinterSetupDialog
Компонент PrintDialog вызывает диалоговое окно печати, представленное на Рисунок 8.10. В нем пользователь может выбрать принтер и установить его свойства, указать число копий и последователъность их печати, печатать в файл или непосредственно на принтер, выбрать печатаемые страницы или печатать только выделенный фрагмент.Диалоги поиска и замены текста — компоненты FindDialog и ReplaceDialog
Компоненты FindDialog и ReplaceDialog, вызывающие диалоги поиска и замены фрагментов текста (Рисунок 8.12 и 8.13), очень похожи и имеют одинаковые свойства, кроме одного, задающего заменяющий текст в компоненте ReplaceDialog. Такое сходство не удивительно, поскольку ReplaceDialog — производный класс от FindDialog.Диалоговое окно настройки печати

Рассмотренные ранее диалоговые компоненты возвращали одно свойство — имя файла, цвет, или один объект — Font, содержащий множество свойств. В отличие от них компонент PrintDialog возвращает ряд свойств, характеризующих выбранные пользователем установки. Это следующие свойства:
Диалоговое окно сохранения файла изображения

Диалоговое окно сохранения файла

Типы искомых файлов, появляющиеся в диалоге в выпадающем списке Тип файла (Рисунок 8.1, 8.2), задаются свойством Filter. В процессе проектирования это свойство проще всего задать с помощью редактора фильтров, который вызывается нажатием кнопки с многоточием около имени этого свойства в Инспекторе Объектов. При этом открывается окно редактора, вид которого представлен на Рисунок 8.3. В его левой панели Filter Name вы записываете тот текст, который увидит пользователь в выпадающем списке Тип файла диалога. А в правой панели Filter записываются разделенные точками с запятой шаблоны фильтра. В примере Рисунок 8.3 задано два фильтра: текстовых файлов с расширениями .txt и .doc и любых файлов с шаблоном *.*.
Диалоговое окно выбора атрибутов шрифта

Свойства MaxFontSize и MinFontSize устанавливают ограничения на максимальный и минимальный размеры шрифта. Если значения этих свойств равны 0 (по умолчанию), то никакие ограничения на размер не накладываются. Если же значения свойств заданы (обычно это целесообразно делать исходя из размеров компонента приложения, для которого выбирается шрифт), то в списке Размер диалогового окна (см. Рисунок 8.8) появляются только размеры, укладывающиеся в заданный диапазон. При попытке пользователя задать недопустимый размер ему будет выдано предупреждение вида «Размер должен лежать в интервале ...» и выбор пользователя отменится. Свойства MaxFontSize и MinFontSize действуют только при включенной опции fdLimitSize (см. ниже).
Свойство Device определяет, из какого списка возможных шрифтов будет предложен выбор в диалоговом окне: fdScreen — из списка экрана (по умолчанию), fdPrinter — из списка принтера, fdBoth — из обоих.
Свойство Options содержит множество опций:
Если установить опцию fdApplyButton, то при нажатии пользователем кнопки Применить возникает событие OnApply, в обработчике которого вы можете написать код, который применит выбранные пользователем атрибуты, не закрывая диалогового окна.
Приведем примеры применения компонента FontDialog. Пусть ваше приложение включает окно редактирования Memo1, шрифт в котором пользователь может выбирать командой меню Шрифт. Вы ввели в приложение компонент FontDialog, имя которого по умолчанию FontDialog1. Тогда обработчик команды Шрифт может иметь вид: if FontDialog1.Execute then Memo1.Font.Assign(FontDialog1.Font);
Приведенный оператор вызывает диалог выбора атрибутов шрифта и, если пользователь произвел выбор, то значения всех выбранных атрибутов, содержащиеся в свойстве FontDialog1.Font, присваиваются атрибутам окна редактирования, содержащимся в свойстве Memo1.Font. Шрифт в окне редактирования немедленно изменится.
Если вы установите в компоненте FontDialog1 опцию fdApplyButton, то можете написать обработчик события OnApply: Memo1.Font.Assign(FontDialog1.Font);
Тогда пользователь может наблюдать изменения в окне Memo1, нажимая в диалоговом окне кнопку Применить и не прерывая диалога. Это очень удобно, так как позволяет пользователю правильно подобрать атрибуты шрифта.
Если в качестве окна редактирования в вашем приложении вы используете RichEdit, то можете предоставить пользователю выбирать атрибуты шрифта для выделенного фрагмента текста или для вновь вводимого текста. Тогда выполнение команды меню Шрифт может осуществляться операторами: if FontDialog1.Execute then RichEdit1.SelAttributes.Assign(FontDialog1.Font);
Вы можете разрешить пользователю изменять шрифт не только отдельных компонентов, но и всех компонентов и надписей на форме. Это осуществляется оператором: if FontDialog1.Execute then Font.Assign(FontDialog1.Font);
В этом операторе свойство Font без ссылки на компонент подразумевает шрифт формы.
Диалоговое окно выбора цвета

Имена цветов задаются от ColorA (первый цвет) до ColorP (шестнадцатый, последний). Например, строка ColorA=808022 задает первый заказной цвет. При задании цвета 2 младших разряда описывают интенсивность красного цвета, следующие 2 — зеленого, старшие — синего.
Свойство Options содержит множество следующих опций:
Приведем пример применения компонента ColorDialog. Если вы хотите, чтобы пользователь мог задать цвет какого-то объекта, например, цвет фона компонента Memo1, то это можно реализовать оператором if ColorDialog1.Execute then Memo1.Color := ColorDialog1.Color;
Диалоговое окно замены фрагмента текста

Поиск заданного фрагмента легко проводить, пользуясь функцией Object Pascal Pos, которая определена в модуле System следующим образом: function Pos(Substr: string; S: string): Byte; где S — строка, в которой ищется фрагмент текста, a Substr — искомый фрагмент. Функция возвращает позицию первого символа первого вхождения искомого фрагмента в строку. Если Substr в S не найден, возвращается 0.
Для организации поиска нам потребуется еще две функции: Сору и AnsiLowerCase. Первая из них определена как: function Copy(S: string; Index, Count: Integer): string;
Она возвращает фрагмент строки S, начинающийся с позиции Index и содержащий число символов, не превышающее Count. Функция AnsiLowerCase, определенная как function AnsiLowerCase(const S: string): string; возвращает строку символов S, переведенную в нижний регистр.
Теперь мы можем рассмотреть пример организации поиска. Пусть в вашем приложении имеется компонент Memo1 и при выборе раздела меню MFind вы хотите организовать поиск в тексте, содержащемся в Memo1. Для упрощения задачи исключим опцию поиска только целых слов и опцию поиска вверх от положения курсора.
Программа, реализующая поиск, может иметь следующий вид:
var SPos: integer; procedure TForm1.MFindClick(Sender: TObject); begin {запоминание позиции курсора} SPos := Memo1.SelStart; with FindDialog1 do begin {начальное значение текста поиска — текст, выделенный в Memo1} FindText := Memo1.SelText; {позиционирование окна диалога внизу Memo1} Position := Point(Form1.Left, Form1.Top + Memo1.Top + Memo1.Height); {удаление из диалога кнопок «Вверх», «Вниз», «Только слово целиком»} Options := Options + [frHideUpDown, frHideWholeWord]; {выполнение} Execute; end; end; procedure TForm1.FindDialog1Find(Sender: TObject); begin with FindDialog1 do begin if frMatchCase in Options {поиск с учетом регистра} then Memo1.SelStart := Pos(FindText, Copy(Memo1.Lines.Text, SPos + 1, Length(Memo1.Lines.Text))) + Spos - 1 {поиск без учета регистра} else Memo1.SelStart := Pos(AnsiLowerCase(FindText), AnsiLowerCase(Copy(Memo1.Lines.Text, SPos + 1, Length(Memo1.Lines.Text)))) + Spos - 1; if Memo1.SelStart >= Spos then begin {выделение найденного текста} Memo1.SelLength := Length(FindText); {изменение начальной позиции поиска} SPos := Memo1.SelStart + Memo1.SelLength + 1; end else if MessageDlg( 'Текст "'+FindText+'" не найден. Продолжать диалог?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then CloseDialog; end; Memo1.SetFocus; end;
В программе вводится переменная SPos, сохраняющая позицию, начиная с которой надо проводить поиск.
Процедура MFindClick вызывает диалог, процедура FindDialog1Find обеспечивает поиск с учетом или без учета регистра в зависимости от флага frMatchCase. После нахождения очередного вхождения искомого текста этот текст выделяется в окне Memo1 и управление передается этому окну редактирования. Затем при нажатии пользователем в диалоговом окне кнопки Найти далее, поиск продолжается в оставшейся части текста. Если искомый текст не найден, делается запрос пользователю о продолжении диалога. Если пользователь не ответил на этот запрос положительно, то диалог закрывается методом CloseDialog.
В дополнение к приведенному тексту полезно в обработчики событий OnClick и OnKeyUp компонента Memo1 ввести операторы SPos := Memo1.SelStart;
Это позволяет пользователю во время диалога изменить положение курсора в окне Memo1. Это новое положение сохранится в переменной SPos и будет использовано при продолжении поиска.
При реализации команды Заменить приведенные выше процедуры можно оставить теми же самыми, заменив в них FindDialog1 на ReplaceDialog1. Дополнительно можно написать процедуру обработки события OnReplace компонента ReplaceDialog1: procedure TForm1.ReplaceDialog1Replace(Sender: TObject); begin if Memo1.SelText <> '' then Memo1.SelText := ReplaceDialog1.ReplaceText; if frReplaceAll in ReplaceDialog1.Options then ReplaceDialog1Find(Self); end;
Этот код производит замену выделенного текста и, если пользователь нажал кнопку Заменить все, то продолжается поиск вызовом уже имеющейся процедуры поиска ReplaceDialog1Find*. Если же пользователь нажал кнопку Заменить, то производится только одна замена и для продолжения поиска пользователь должен нажать кнопку Найти далее.
* Предлагаемый автором алгоритм принажатии на кнопку Заменить все заменяет только одно значение и находит следующее. На наш взгляд такия действия более логично было бы задать кнопке Заменить, а для Заменить все организовать цикл. Причем такой цикл проще осуществить в процедуре ReplaceDialog1Find. В приведенном ниже коде кроме того введена локальная переменная ss, так как свойству SelStart нельзя присваивать отрицательные значения.
procedure TForm1.ReplaceDialog1Find(Sender: TObject); var ss: integer; last: Boolean; st: string; begin with ReplaceDialog1 do begin if (frFindNext in Options) then {изменение начальной позиции поиска} SPos := Memo1.SelStart + Memo1.SelLength + 1; last := not (frReplaceAll in Options); repeat if frMatchCase in Options {поиск с учетом регистра} then ss := Pos(FindText, Copy(Memo1.Lines.Text, SPos + 1, Length(Memo1.Lines.Text))) + Spos - 1 {поиск без учета регистра} else ss := Pos(AnsiLowerCase(FindText), AnsiLowerCase(Copy(Memo1.Lines.Text, SPos + 1, Length(Memo1.Lines.Text)))) + Spos - 1; if ss >= Spos then begin {выделение найденного текста} Memo1.SelStart := ss; Memo1.SelLength := Length(FindText); if (frReplaceAll in Options) then begin {замена} Memo1.SelText := ReplaceDialog1.ReplaceText; {изменение начальной позиции поиска} SPos := Memo1.SelStart + Memo1.SelLength + 1; end; end else begin if (frReplaceAll in Options) or (frReplace in Options) then st := 'Замена "' + FindText + '" на "' + ReplaceText + '" закончена' else st := 'Текст "' + FindText + '" не найден'; if MessageDlg(st + '. Продолжать диалог?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then CloseDialog; last:=true; end; until last; end; end; procedure TForm1.ReplaceDialog1Replace(Sender: TObject); begin if (frReplace in ReplaceDialog1.Options) and (Memo1.SelText <> '') then Memo1.SelText := ReplaceDialog1.ReplaceText; ReplaceDialog1Find(Self); end; - Примечание разработчика электронной версии.
Фрагменты диалогов — компоненты
Помимо законченных диалогов работы с файлами, в Delphi имеется ряд компонентов, представляющих собой фрагменты диалогов: выпадающие списки дисков (драйверов) — DriveComboBox и фильтров (масок) файлов — FilterComboBox, списки каталогов — DirectoryListBox и файлов — FileListBox, дерево каталогов — DirectoryOutline. Внешний вид этих компонентов вы можете увидеть на Рисунок 8.7. Компоненты работы с файловой системой облегчают вам создание собственных диалоговых окон, что нередко требуется. Например, вы можете захотеть включить в диалоговое окно отображение каких-то характеристик файлов (размера, даты создания и т.д.) или оперативный просмотр содержания текстовых файлов. Тогда вам очень пригодятся готовые компоненты работы с файлами. Правда, все они, кроме DirectoryOutline, предназначены в основном для Delphi 1 и в последующих версиях Delphi расположены на странице Win3.1 палитры компонентов. Это значит, что они не рекомендуются для 32-разрядных приложений. Но, во-первых, у вас остается компонент DirectoryOutline. Кроме того, никто не запрещает вам все-таки использовать и остальные компоненты в любых приложениях Delphi. И, наконец, если уж вы хотите четко следовать рекомендации не использовать первые четыре фрагмента диалогов в 32-разрядных приложениях, вы можете разработать свои аналогичные компоненты, используя обычный компонент ComboBox.Компоненты — фрагменты диалогов

Свойство TextCase задает регистр отображения: tcUpperCase — в верхнем регистре, tcLowerCase — в нижнем.
Связать компонент DriveComboBox со списком каталогов, отображаемых компонентом DirectoryListBox, можно во время проектирования через свойство DirList компонента DriveComboBox. Это свойство может указывать на компонент типа DirectoryListBox. Можно обеспечить связь этих двух типов компонентов и программно, включив в обработчик события OnChange компонента DriveComboBox оператор DirectoryListBox1.Drive := DriveComboBox1.Drive;
Этот оператор задает имя диска, выбранное пользователем в компоненте DriveComboBox1, свойству Drive списка каталогов DirectoryListBox1.
Аналогичным оператором можно обеспечить связь компонента DriveComboBox с деревом каталогов и файлов в компоненте DirectoryOutline: DirectoryOutline1.Drive := DriveComboBox1.Drive;
Рассмотрим теперь выпадающий список фильтров — компонент FilterComboBox. Его основное свойство — Filter, которое задается так же, как в описанных ранее диалогах. К отдельным частям фильтра — тексту и маске, можно получить доступ через свойства Text и Mask соответственно. Связь компонента со списком файлов типа TFileListBox можно установить, задав свойство FileList.
Компонент DirectoryListBox отображает список каталогов диска, заданного свойством Drive. Значение этого свойства можно установить программно во время выполнения. Как уже говорилось выше, связь этого свойства с выбранным пользователем диском в компоненте DriveComboBox устанавливается или программно, или с помощью свойства DirectoryListBox компонента DriveComboBox.
Связь списка каталогов с компонентом типа TFileListBox, отображающим список файлов, осуществляется с помощью свойства FileList. Можно также использовать результаты выбора пользователем каталога, читая свойство Directory в обработчике события OnChange.
С компонентом DirectoryListBox можно также связать метку типа Label. В этой метке будет отображаться путь к текущему каталогу. Если путь не умещается в метке, он автоматически отображается в сокращенном виде (см. Рисунок 8.7) с помощью функции MinimizeName. Метка, отображающая каталог, указывается в свойстве DirLabel.
Список файлов содержится в компоненте FileListBox. Его свойства Drive, Directory и Mask определяют соответственно диск, каталог и маску файлов. Эти свойства можно устанавливать программно или связывая описанным ранее способом компонент FileListBox с компонентами DriveComboBox, DirectoryListBox и FilterComboBox. Свойство FileType позволяет включать в список не все файлы, а только те, которые имеют соответствующие атрибуты. Свойство FileType представляет собой множество, указывающее типы включаемых файлов. Элементы этого множества могут иметь значения: ftReadOnly — только для чтения, ftHidden — невидимые, ftSystem — системные, ftVoluimeID — обозначения дисков, ftDirectory — каталоги, ftArchive — архивные, ftNormal — не имеющие особых атрибутов.
Свойство ShowGlyphs разрешает или исключает показ пиктограмм файлов (в примере Рисунок 8.7 это свойство = true).
Свойство MultiSelect разрешает выбор нескольких файлов.
Основное свойство, в котором можно прочитать имя выбранного пользователем файла — FileName.
Со списком файлов может быть связано окно редактирования Edit, в котором отображается выбранный файл (см. окно над списком файлов на Рисунок 8.7). На этот список указывает устанавливаемое во время проектирования свойство FileEdit.
Теперь рассмотрим компонент DirectoryOutline, содержащий дерево каталогов. В этом компоненте значение диска устанавливается свойством Drive. Текущий каталог, выбранный пользователем, можно прочитать в свойстве Directory. Свойство TextCase определяет стиль отображения имен каталогов: tcLowerCase — преобразование к нижнему регистру, tcUpperCase — к верхнему, tcAsIs — без преобразования (этот режим использован в примере Рисунок 8.7). Остальные свойства идентичны компоненту OutLine, на основе которого построен данный пример. Вы можете найти исходный текст этого примера в каталоге ...\source\samples.
Общая характеристика компонентов-диалогов
В приложениях часто приходится выполнять стандартные действия: открывать и сохранять файлы, задавать атрибуты шрифтов, выбирать цвета палитры, производить контекстный поиск и замену и т.п.Разработчики Delphi позаботились о том, чтобы включить в библиотеку простые для использования компоненты, реализующие соответствующие диалоговые окна. Они размещены на странице Dialogs. В таблице 8.1 приведен перечень этих диалогов. Таблица 8.1. Системные диалоги и их фрагменты
грамма

«Открыть файл»

«Сохранить файл»

«Открыть рисунок»

«Сохранить рисунок»

«Шрифты»

«Цвет»

«Печать»

«Установка принтера»

«Найти»

«Заменить»

(список файлов)

(структура каталогов)

(список дисков)

(список фильтров)

(дерево каталогов)
Последние четыре компонента в таблице 8.1 являются не законченными диалогами, а их фрагментами, позволяющими строить свои собственные диалоговые окна.
Все диалоги являются невизуальными компонентами, так что место их размещения на форме не имеет значения. При обращении к этим компонентам вызываются стандартные диалоги, вид которых зависит от версии Windows и настройки системы. Так что при запуске одного и того же приложения на компьютерах с разными системами диалоги будут выглядеть по-разному. Например, при русифицированной версии Windows все их надписи будут русскими, а при англоязычной версии надписи будут на английском языке.
Основной метод, которым производится обращение к любому диалогу, — Execute. Эта функция открывает диалоговое окно и, если пользователь произвел в нем какой-то выбор, то функция возвращает true. При этом в свойствах компонента-диалога запоминается выбор пользователя, который можно прочитать и использовать в дальнейших операциях. Если же пользователь в диалоге нажал кнопку Отмена или клавишу Esc, то функция Execute возвращает false. Поэтому стандартное обращение к диалогу имеет вид: if <имя компонента-диалога>.Execute then <операторы, использующие выбор пользователя>;
Окно редактора фильтров

В этой строке тексты и шаблоны разделяются вертикальными линиями. В аналогичном виде, если требуется, можно задавать свойство Filter программно во время выполнения приложения.
Свойство FilterIndex определяет номер фильтра, который будет по умолчанию показан пользователю в момент открытия диалога. Например, значение FilterIndex = 1 задает по умолчанию первый фильтр.
Свойство InitialDir определяет начальный каталог, который будет открыт в момент начала работы пользователя с диалогом. Если значение этого свойства не задано, то открывается текущий каталог или тот, который был открыт при последнем обращении пользователя к соответствующему диалогу в процессе выполнения данного приложения.
Свойство DefaultExt определяет значение расширения файла по умолчанию. Если значение этого свойства не задано, пользователь должен указать в диалоге полное имя файла с расширением. Если же задать значение DefaultExt, то пользователь может писать в диалоге имя без расширения. В этом случае будет принято заданное расширение.
Свойство Title позволяет вам задать заголовок диалогового окна. Если это свойство не задано, окно открывается с заголовком, определенным в системе (например, «Открытие файла» в окне на Рисунок 8.1). Но вы можете задать и свой заголовок, подсказывающий пользователю ожидаемые действия. Например, «Укажите имя открываемого файла».
Свойство Options определяет условия выбора файла. Множество опций, которые вы можете установить программно или во время проектирования, включает:
Если вы разрешаете с помощью опции ofAllowMultiSelect множественный выбор файлов, то список выбранных файлов можно прочитать в свойстве Files типа TStrings.
В приведенной таблице даны опции, используемые в 32-разрядных версиях Delphi. В Delphi 1 диалоговое окно имеет вид, представленный на Рисунок 8.4. Аналогичный вид имеет диалог и в 32-разрядных версиях Delphi при включении опции ofOldStyleDialog. В примере Рисунок 8.4 диалог открыт с заданным значением свойства Title и заданный текст отображается в заголовке окна. Кроме того, в этом примере выключена опция ofHideReadOnly (в Delphi 1 она выключена по умолчанию), что привело к появлению индикатора «Только чтение».
Диалоговое окно, отображаемое методом Application.MessageBox

Диспетчеризация событий — компоненты, связанные с ActionList
Начиная с Delphi 4 появился инструментарий, который, не добавляя никаких принципиально новых возможностей, позволяет систематизировать и упорядочить разработку объектно-ориентированных приложений. К тому же грамотное его использование позволяет сэкономить немало времени при проектировании. Для организации диспетчеризации событий введены следующие понятия.Действие (action) — реализация некоторого поведения, являющегося реакцией на поступок пользователя, такой, как щелчок на кнопке или на разделе меню — инициаторе действия. Разработан класс TAction и ряд его наследников, реализующих многие стандартные действия, характерные для приложений Windows.
Список действий — компонент типа ActionList, содержащий предусмотренные в приложении действия. Это интерфейс разработчика, упорядочивающий его работу с действиями в процессе проектирования.
Редактор связей — объект типа TActionLink, который поддерживает связь между действиями и инициаторами действий. Редактор связей определяет, какое действие должно выполняться для данного инициатора.
Цель действия — объект, в котором отражается результат действия. Это может быть окно отображения или редактирования текста, набор данных и т.п.
В начале проектирования приложения разработчик должен представить себе список тех действий, которые может осуществлять пользователь. Конечно, в процессе проектирования этот список будет пополняться и изменяться, но некоторое начальное приближение очень полезно продумать заранее. Практическая реализация составленного вами списка действий может начинаться с переноса на проектируемую форму невизуального компонента ActionList, расположенного на странице библиотеки Standard. Сделав на этом компоненте двойной щелчок, вы попадаете в редактор действий (Рисунок 9.1), позволяющий вводить и упорядочивать действия.
Объект экрана — Screen
В приложении Delphi автоматически создается глобальный объект Screen (экран) типа TScreen, свойства которого определяются из информации Windows о мониторе, на котором запускается приложение. Вы можете в любом приложении использовать, например, такие параметры объекта Screen, как Height — высота экрана и Width — его ширина. Так как вы используете в процессе проектирования один тип монитора, а приложение в дальнейшем может работать на мониторе другого типа, то не исключено, например, что ваша форма не поместится на экране или наоборот — будет слишком маленького размера для данного монитора. Чтобы избежать этих неприятностей, можно автоматически масштабировать свою форму, вводя, например, в обработчик ее события OnCreate код: Form1.Width := Screen.Width div 2; Form1.Height := Screen.Height div 2;Этот код задает размеры формы равными половине соответствующих размеров экрана.
Разрешающую способность экрана можно определить, воспользовавшись свойством PixelsPerInch, указывающим количество пикселей экрана на дюйм в вертикальном направлении. Это справедливо именно для вертикального направления, поскольку во многих мониторах коэффициенты масштабирования по горизонтали и вертикали различаются.
Screen имеет свойство Forms[I], содержащее список форм, отображаемых в данный момент на экране, и свойство FormCount, отражающее количество таких форм. Вы можете использовать это свойство, например, для того, чтобы гарантировать, что на данном типе монитора размеры ни одной формы не превысят размеров экрана. Соответствующий код может выглядеть так: with Screen do for I := 0 to FormCount - 1 do begin if Forms[I].Height > Height then Forms[I].Height := Height; if Forms[I].Width > Width then Forms[I].Width := Width; end;
Размеры форм, превышающие размер экрана, урезаются этим кодом.
В приведенных примерах надо, конечно, предусмотреть, чтобы при изменении размеров формы адекватно изменялось и расположение компонентов на ее поверхности.
Еще одно полезное свойство объекта Screen — Fonts (шрифты). Это свойство типа TStrings содержит список шрифтов, доступных на данном компьютере (свойство только для чтения). Его можно использовать в приложении, чтобы проверять, имеется ли на компьютере тот или иной шрифт, используемый в приложении. Если нет — то можно или дать пользователю соответствующее предупреждение, или сменить шрифт в приложении на один из доступных, или дать пользователю возможность самому выбрать соответствующий шрифт. Например, вы можете поместить в вашем приложении компонент списка TComboBox и при событии формы OnCreate загрузить его доступными шрифтами с помощью операторов: ComboBox1.Items := Screen.Fonts; ComboBox1.ItemIndex := 0;
Тогда в нужный момент пользователь может выбрать подходящий шрифт из списка. Пример использования свойства Screen.Fonts приведен в .
В Delphi 5 в тип TScreen введено два новых свойства: HintFont и MenuFont типа TFont. Они хранят установленные в Windows шрифты отображения ярлычков и разделов меню.
Свойство Cursor объекта Screen определяет вид курсора. Если это свойство равно crDefault, то вид курсора при перемещении над компонентами определяется установленными в них свойствами Cursor. Но если свойство Cursor объекта Screen отлично от crDefault, то соответствующие свойства компонентов отменяются и курсор имеет глобальный вид, заданный в Screen. Этим можно воспользоваться для такой частой задачи, как изменение курсора на форму «песочные часы» во время выполнения каких-то длинных операций. Это можно сделать следующим образом: Screen.Cursor := crHourGlass; try <выполнение требуемых длинных операций> finally Screen.Cursor := crDefault; end;
При успешном или аварийном окончании длинных операций курсор в любом случае возвращается в значение по умолчанию. Если в приложении в какие-то отрезки времени используется отличный от crDefault глобальный вид курсора, то приведенный код можно изменить, чтобы по окончании длинных операций восстановить прежнее глобальное значение: var OldCursor: TCursor; begin OldCursor := Screen.Cursor; Screen.Cursor := crHourGlass; try <выполнение требуемых долгих операций> finally Screen.Cursor := OldCursor; end;
С помощью Screen можно получить доступ к активной в текущий момент форме вашего приложения через свойство ActiveForm. Если в данный момент пользователь переключился с вашего приложения на какое-то другое и, следовательно, ни одна форма вашего приложения не активна, то ActiveForm указывает на форму, которая станет активной, когда пользователь вернется к вашему приложению. В момент переключения фокуса с одной вашей формы на другую, генерируется событие OnActiveFormChange.
Аналогично с помощью свойства ActiveControl можно получить доступ к активному в данный момент оконному компоненту на активной форме. При смене фокуса генерируется событие OnActiveControlChange.
Начиная с Delphi 4 предусмотрена возможность разработки мультиэкранных приложений, работающих одновременно с множеством мониторов. При этом приложение может решать, какие формы и диалоги надо отображать на том или ином мониторе. Свойства различных мониторов, используемых в таком приложении, можно найти с помощью свойства Screen.Monitors[I], где I — индекс монитора. Индекс 0 относится к первичному монитору. Свойство Screen.Monitors[I] является списком объектов типа TMonitor, содержащих информацию о конкретных мониторах.
Среди свойств объектов типа TMonitor имеются Height — высота и Width — ширина экрана монитора. Кроме того имеются свойства Left и Тор. Эти свойства означают следующее. Все доступное экранное пространство можно представить себе разбитым на экраны отдельных мониторов, размещающихся слева направо и сверху вниз. Соответственно свойства Left и Тор определяют координаты левого верхнего угла экрана монитора в этом логическом экранном пространстве. Объекты типа TMonitor имеют также свойство MonitorNum — номер монитора, представляющий собой его индекс в свойстве Screen.Monitors[I].
Для управления тем, на каком мониторе должна появляться та или иная форма, служит свойство формы DefaultMonitor. Это свойство может принимать значения:
Общая характеристика
Имеется несколько компонентов, которые сами по себе не выполняют никаких действий, но помогают организовать взаимодействие компонентов в приложении и обмен информацией между приложениями. Это компоненты, представленные в таблице 9.1. Компоненты ActionList и ImageList организуют управление внутри приложения и позволяют сделать его код более четким и понятным, отвечающим принципам объектно-ориентированного программирования.Компоненты OLEContainer, DDEClientConv, DDEClientItem и TDDEServerItem организуют взаимодействие одновременно выполняемых приложений по двум различным технологиям: OLE и DDE. Вопросы взаимодействия приложений требуют развернутых пояснений, выходящих за рамки данной книги. Поэтому компоненты, связанные с технологиями OLE и DDE, рассматриваться далее не будут.
К компонентам, организующим управление приложением, можно также отнести объекты Application — приложение и Screen — экран. Эти объекты отсутствуют в палитре компонентов, но всегда создаются автоматически в каждом приложении. Некоторые характеристики этих объектов также будут рассмотрены в данной главе. Таблица 9.1. Компоненты организации управления приложением
грамма

(список событий)

(список изображений)

(события приложения)

(контейнер OLE)

(диалог с сервером DDE)

(данные, передаваемые серверу DDE)

(данные, передаваемые клиенту DDE)
Окно редактора действий

Окно редактора списков изображений

Как видно из Рисунок 9.3, каждое загруженное в список изображение получает индекс. Именно на эти индексы впоследствии вы можете ссылаться в соответствующих свойствах разделов меню, списков, кнопок и т.д., когда вам надо загрузить в них то или иное изображение. Изменить последовательность изображений в списке вы можете просто перетащив изображение мышью на новое место.
В редакторе списков изображений вы можете, выделив то или иное изображение, установить его свойства: Transparent Color и Fill Color. Но это можно делать только в том сеансе работы с редактором списков изображений, в котором загружено данное изображение. Для изображений, загруженных в предыдущих сеансах, изменение этих свойств невозможно.
Свойство Transparent Color определяет цвет, который используется в маске для прозрачного рисования изображения. По умолчанию это цвет левого нижнего пикселя изображения. Для пиктограмм данное свойство устанавливается в clNone, поскольку пиктограммы уже маскированы.
Свойство Fill Color определяет цвет, используемый для заполнения пустого пространства при перемещении и центрировании изображения. Для пиктограмм данное свойство устанавливается в clNone.
Группа радиокнопок Options определяет способ размещения изображения битовой матрицы с размерами, не соответствующими размерам, принятым в списке:
Окно выбора стандартных действий

Можно для каждого или некоторых действий указать свойство ImageIndex, которое является индексом (начиная с 0) изображения, соответствующего данному действию, в отдельном компоненте списка изображений ImageList (см. ). Этот индекс передастся в дальнейшем компонентам, связанным с данным событием — разделам меню, кнопкам. Если в свойстве Images компонента ActionList указать имя списка, размещенного на форме и заполненного изображениями, то эти изображения появятся также в окне редактора действий (Рисунок 9.1).
Свойство Category (категория) не имеет отношения к выполнению приложения. Задание категории просто позволяет в процессе проектирования сгруппировать действия по их назначению. Вы можете для каждого действия выбрать категорию из выпадающего списка, или написать имя новой категории и отнести к ней какие-то действия. Например, на Рисунок 9.1 видна введенная пользователем категория Файлы, к которой отнесены действия, связанные с меню Файл.
На странице событий Инспектора Объектов для каждого действия определено три события: OnExecute, OnUpdate и OnHint.
Событие OnExecute возникает в момент, когда пользователь инициализировал действие, например, щелкнув на компоненте (разделе меню, кнопке), связанном с данным действием. Обработчик этого события должен содержать процедуру, реализующую данное действие. Например, обработчик события OnExecute действия Exit может в простейшем случае иметь вид procedure TForm1.ExitExecute(Sender: TObject); begin Close; end; а в более сложных случаях может содержать проверку возможности закрыть приложение, запросы пользователю и т.д. Одним из преимуществ использования действий является то, что заголовки обработчиков приобретают осмысленный характер и код становится более прозрачным. Действительно, гораздо понятнее заголовок ExitExecute, чем, например, Button7Click или N14Click (попробуйте найти в вашем большом приложении, где эта кнопка Button7 или раздел меню N14). В результате вы избавляетесь от необходимости давать осмысленные имена кнопкам и разделам меню, т.е. облегчаете свою работу с компонентами.
Событие OnUpdate периодически возникает в промежутках между действиями. Возникновение этих событий прекращается только во время реализации события или во время, когда пользователь ничего не делает и компьютер находится в состоянии ожидания действий. Обработчик события OnUpdate может содержать какие-то настройки, подготовку ожидаемых дальнейших действий или выполнение каких-то фоновых операций.
Событие OnHint возникает в момент, когда на экране отображается ярлычок подсказки в результате того, что пользователь задержал курсор мыши над компонентом, инициализирующим событие.
Наличие в объекте действия событий OnUpdate и OnHint обогащает ваши возможности по проектированию приложения. Без этого объекта подобные события отсутствуют и их при необходимости приходится моделировать более сложными приемами.
Связь объектов действий с конкретными инициализаторами действий — управляющими элементами типа кнопок, разделов меню и т.д., осуществляется через свойство Action, имеющееся у всех управляющих элементов. Поместите на вашу форму кнопку, и вы увидите в Инспекторе Объектов это свойство. Раскройте его выпадающий список и выберите из него действие, которое вами было предварительно описано. Вы сможете заметить, что после этого в кнопку перенесутся такие свойства объекта действия, как Caption, Hint и др., и что в ее событие OnClick подставится обработчик, предусмотренный вами для данного действия. Если далее вы введете в приложение меню с разделом, соответствующим тому же действию, и укажете в нем то же значение свойства Action, то свойства и обработчики событий объекта действия будут перенесены и на этот раздел меню. Вам не придется повторно задавать значение Hint, Caption и др.
Подобная связь между свойствами объекта действия и свойствами управляющих элементов выполняется классом TActionLink и его наследниками. Передаются в компоненты такие свойства действия, как Caption, Checked, Enabled, HelpContext, Hint, ImageIndex, Shortcut, Visible. Впрочем, в любом компоненте разработчик может изменить переданное в него свойство. Обратной связи TActionLink с компонентами нет, так что эти изменения будут локальными и не отразятся на других компонентах. Если же требуется изменить свойства всех связанных с одним действием компонентов, надо изменять свойство объекта действия. Это облегчает программное управление компонентами, связанными с одним и тем же действием. Например, если в какой-то момент вам надо сделать недоступными (или, наоборот, доступными) два компонента — кнопку Button3 и раздел меню N5, связанные с одним событием (назовем его объект Do), то при отсутствии централизованной диспетчеризации событий через ActionList вам пришлось бы писать два оператора: Button3.Enabled := false; N5.Enabled := false; а при наличии объекта Do — всего один: Do.Enabled := false;
Дело не только в экономии кода, но и в его прозрачности, понятности его как для вас самих, так и для тех, кому придется, может быть, в дальнейшем сопровождать ваше приложение.
Приложение — компонент ApplicationEvents и объект Application
В каждом приложении автоматически создается объект Application типа TApplication — приложение. Этот компонент отсутствует в палитре библиотеки, вероятно, только потому, что он всегда один в приложении. Application имеет ряд свойств, методов, событий, характеризующих приложение в целом.Рассмотрим сначала некоторые свойства Application. Булево свойство Active (только для чтения) характеризует активность приложения. Оно равно true, если форма приложения находится в фокусе. Если же пользователь переключился на работу с другим приложением, свойство Active равно false.
Свойство ExeName является строкой, содержащей имя выполняемого файла с полным путем к нему. Это свойство удобно использовать, чтобы определять каталог, из которого запущено приложение и который может содержать другие файлы (настройки, документы, базы данных и т.п.), связанные с приложением. Выражение ExtractFilePath(Application.ExeName) дает этот каталог. Обычно свойство ExeName тождественно функции ParamStr(0), возвращающей нулевой параметр командной строки — имя файла с путем.
Свойство Title определяет строку, которая появляется около пиктограммы свернутого приложения. Если это свойство не изменяется во время выполнения, то оно равно опции Title, задаваемой во время проектирования на странице Application окна опций проекта (команда Project | Options). Свойство может изменяться программно, например, изменяя надпись в зависимости от режима работы приложения.
Свойство MainForm типа TForm определяет главную форму приложения. Булево свойство ShowMainForm определяет, должна ли главная форма быть видимой в момент запуска приложения на выполнение. По умолчанию оно равно true, что обеспечивает видимость главной формы в момент начала работы приложения. Если же установить в головном файле проекта Application.ShowMainForm равным false до вызова метода Application.Run и если при этом свойство Visible главной формы тоже равно false, то главная форма в первый момент будет невидимой.
Свойство HelpFile указывает файл справки, который используется в приложении в данный момент как файл по умолчанию. Если это свойство не изменяется во время выполнения, то оно равно опции Help File, задаваемой во время проектирования на странице Application окна опций проекта (команда Project | Options). Свойство можно изменять программно, назначая в зависимости от режима работы приложения тот или иной файл справки.
Ряд свойств объекта Application определяет ярлычки подсказок компонентов приложения. Свойство Hint содержит текст подсказки Hint того визуального компонента или раздела меню, над которым в данный момент перемещается курсор мыши. Смена этого свойства происходит в момент события OnHint, которое будет рассмотрено позднее. Во время этого события текст подсказки переносится из свойства Hint компонента, на который переместился курсор мыши, в свойство Hint объекта Application. Свойство Application.Hint можно использовать для отображения этой подсказки или для установки и отображения в полосе состояния текста, характеризующего текущий режим приложения.
Свойство HintColor типа TColor определяет цвет фона окна ярлычка. По умолчанию это цвет clInfoBk, но его значение можно изменять программно. Свойство HintPause определяет задержку появления ярлычка в миллисекундах после переноса курсора мыши на очередной компонент (по умолчанию 500 миллисекунд или половина секунды). Свойство HintHidePause аналогичным образом определяет интервал времени, после которого ярлычок становится невидимым (по умолчанию 2500 миллисекунд или две с половиной секунды). Свойство HintShortPause определяет аналогичным образом задержку перед появлением нового ярлычка, если в данный момент отображается другой ярлычок (по умолчанию 50 миллисекунд). Это свойство позволяет предотвратить неприятное мерцание, если пользователь быстро перемещает курсор мыши над разными компонентами.
Теперь остановимся на некоторых методах объекта Application. Методы Initialize — инициализация проекта, и Run — запуск выполнения приложения, включаются в каждый проект автоматически — вы можете это увидеть в головном файле проекта, если выполните команду Project | View Source. Там же вы можете увидеть применение метода создания форм CreateForm для всех автоматически создаваемых форм проекта. Если же в вашим проекте есть форма, которая исключена из списка автоматически создаваемых (команда Project | Options и соответствующая установка на странице Forms), то когда эта форма вам потребуется, вы должны будете вызвать этот метод: procedure CreateForm(FormClass: TFormClass; var Reference); где FormClass — класс создаваемой формы, Reference — ссылка на создаваемый объект (его имя). Например: Application.CreateForm(TForm2, Form2);
Метод Terminate завершает выполнение приложения. Если вам надо завершить приложение из главной формы, то вместо метода Application.Terminate вы можете использовать метод Close главной формы. Но если вам надо закрыть приложение из какой-то вторичной формы, например, из диалога, то надо применять метод Application.Terminate.
Метод Minimize сворачивает приложение, помещая его пиктограмму в полосу задач Windows.
Ряд методов связан с работой со справочными файлами. Выше уже говорилось о свойстве HelpFile, указывающем текущий файл справки. Метод HelpContext: function HelpContext(Context: THelpContext): Boolean; вызывает переход в файл справки на тему с идентификатором Context. Это идентификатор, который при проектировании справки поставлен в соответствие некоторой теме. Метод HelpJump: function HelpJump(const JumpID: string): Boolean; выполняет аналогичные действия, но его параметр JumpID — не идентификатор темы, а имя соответствующей темы в файле справки, задаваемое в нем сноской #.
Метод HelpCommand: function HelpCommand(Command: Word; Data: Longint): Boolean; позволяет выполнить указанную параметром Command команду API WinHelp с параметром Data. Метод генерирует событие OnHelp активной формы или приложения, а затем выполняет указанную команду WinHelp. Полный список команд WinHelp вы можете найти в теме WinHelp справочного файла win32.hlp, расположенного в каталоге ...\Program Files\Common Files\Borland Shared\MSHelp. Приведем только некоторые из них. Команда HELP_CONTENTS с параметром 0 отображает окно Содержание справки. Команда HELP_INDEX с параметром 0 отображает окно Указатель справки. Команда HELP_CONTEXT с параметром, равным идентификатору темы, отображает тему с заданным идентификатором (это тождественно рассмотренному ранее методу HelpContext). Команда HELP_CONTEXTPOPUP с параметром, равным идентификатору темы, делает то же самое, но отображает тему во всплывающем окне.
В классе TApplication имеется еще немало методов, но часть из них используется в явном виде очень редко (вы можете посмотреть их во встроенной справке Delphi), а часть будет рассмотрена ниже при обсуждении событий объекта Application. Хотелось бы только обратить внимание читателя на очень полезный метод MessageBox, позволяющий вызывать диалоговое окно с указанным текстом, указанным заголовком и русскими надписями на кнопках (в русифицированных версиях Windows). Это наиболее удачный полностью русифицируемый стандартный диалог, но его рассмотрение, к сожалению, выходит за рамки данной книги. См. о нем в следующих книгах серии «Все о Delphi».
В классе TApplication определено множество событий, которые очень полезны для организации приложения. Ранее для использования этих событий было необходимо вводить соответствующие обработчики и указывать на них объекту Application специальными операторами. В Delphi 5 введен компонент ApplicationEvents, существенно облегчивший эту задачу. Этот компонент перехватывает события объекта Application и, следовательно, обработчики этих событий теперь можно писать как обработчики событий невизуального компонента ApplicationEvents. На каждой форме приложения можно разместить свой компонент ApplicationEvents. События объекта Application будут передаваться всем этим компонентам. Если вы хотите, чтобы событие передавалось прежде всего какому-то одному из них, примените к нему метод Activate, который поставит его в начало очереди компонентов ApplicationEvents. Если же вы при этом не хотите, чтобы другие компоненты ApplicationEvents получали события, примените к привилегированному компоненту метод CancelDispatch. Тогда после обработки события в данном компоненте другие компоненты ApplicationEvents вообще не будут реагировать на эти события.
Во многие обработчики событий компонента ApplicationEvents передается по ссылке параметр Handled. По умолчанию его значение равно false. Если вы обработали соответствующее событие и не хотите, чтобы оно далее обрабатывалось другими компонентами ApplicationEvents, надо в обработчике установить Handled = true. Если же вы оставите Handled = false, то событие будут пытаться обрабатывать другие компоненты ApplicationEvents (если они есть). Если событие так и останется необработанным, то его будет пытаться обработать активный компонент, а если не обработает — то активная форма. Предотвратить обработку события другими компонентами можно, используя описанный ранее метод CancelDispatch.
Ниже приведена таблица событий компонента ApplicationEvents с их краткими описаниями.
Приведем примеры использования событий компонента ApplicationEvents.
Обработчик события OnHint: procedure TForm1.ApplicationEvents1Hint( Sender: TObject); begin StatusBar1.SimpleText := Application.Hint; end; отображает в полосе состояния StatusBar1 (см. ) вторую часть свойства Hint любого компонента, в котором определено это свойство и над которым перемещается курсор мыши. Отображение происходит независимо от значения свойства ShowHint компонента.
Обработчик события OnShowHint: procedure TForm1.ApplicationEvents1ShowHint( var HintStr: String; var CanShow: Boolean; var HintInfo: THintInfo); begin if (HintInfo.HintControl.ClassName = 'TEdit') then begin HintStr := (HintInfo.HintControl as TEdit).Text; ApplicationEvents1.CancelDispatch; end; end; подменяет для окон редактирования типа TEdit текст ярлычков на текст, содержащийся в этих окнах редактирования. Такой прием позволяет пользователю, подведя курсор мыши к окну редактирования, увидеть во всплывающем окне полный текст, который может не быть виден обычным образом, если он длинный и не помещается целиком в окне редактирования. Обработчик события OnHelp: function TForm1.ApplicationEvents1Help(Command: Word; Data: Integer; var CallHelp: Boolean): Boolean; begin if (Command = HELP_CONTEXT) and (Datathen begin Application.HelpCommand(HELP_CONTEXTPOPUP, Data); CallHelp := false; end; Result := true; end; обеспечивает отображение всех контекстных справок с номерами идентификаторов тем, меньшими 10, во всплывающем окне, не вызывая при этом WinHelp. Это дает возможность отобразить многострочные (в отличие от ярлычков) всплывающие окна, поясняющие назначение тех или иных элементов приложения. Обработчик события OnShortCut: procedure TForm1.ApplicationEvents1ShortCut( var Msg: TWMKey; var Handled: Boolean); begin if Msg.CharCode = Ord('Q') then if Application.MessageBox( 'Действительно хотите завершить работу?', 'Подтвердите завершение', MB_YESNOCANCEL + MB_ICONQUESTION) = IDYES then Application.Terminate; end; перехватывает нажатие пользователем клавиш и, если нажата клавиша с символом «Q» (в любом регистре и независимо от установки русского или английского языка), то пользователю методом Application.MessageBox предлагается диалоговое окно, изображенное на Рисунок 9.4.
Список изображений — компонент ImageList
Компонент ImageList представляет собой набор изображений одинаковых размеров, на которые можно ссылаться по индексам, начинающимся с 0. Во многих рассмотренных ранее компонентах (меню, списках и др.) встречались свойства, представляющие собой ссылки на компонент ImageList. Этот компонент позволяет организовать эффективное и экономное управление множеством пиктограмм и битовых матриц. Он может включать в себя монохромные битовые матрицы, содержащие маски для отображения прозрачности рисуемых изображений.Изображения в компонент TImageList могут быть загружены в процессе проектирования с помощью редактора списков изображений. Окно редактора, представленное на Рисунок 9.3, вызывается двойным щелчком на компоненте TImageList или щелчком правой кнопки мыши и выбором команды контекстного меню ImageList Editor.
Программирование: Языки - Технологии - Разработка
- Программирование
- Технологии программирования
- Разработка программ
- Работа с данными
- Методы программирования
- IDE интерфейс
- Графический интерфейс
- Программирование интерфейсов
- Отладка программ
- Тестирование программ
- Программирование на Delphi
- Программирование в ActionScript
- Assembler
- Basic
- Pascal
- Perl
- VBA
- VRML
- XML
- Ada
- Lisp
- Python
- UML
- Форт
- Языки программирования