Программирование на языке Delphi. Глава 8. Меню, строка состояния и панель инструментов

Источник: RSDN
Вальвачев, Сурков K., Сурков Д., Четырько

предыдущая статья серии

Исходные тексты примера к статье

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

8.1. Меню

8.1.1. Идея меню

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

Различают два типа меню:

  • главное меню формы;
  • контекстное меню формы или компонента.

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

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

Для создания главного и контекстного меню среда Delphi имеет два разных компонента: MainMenu и PopupMenu. Заполнение этих компонентов пунктами меню происходит одинаково, но результат будет разным. В первом случае мы получим стандартную строку главного меню, а во втором - окно контекстного меню.

8.1.2. Главное меню

Шаг 1. Приступая к практической работе, создайте новое приложение. Как это сделать, вы уже знаете. Поскольку в качестве примера мы решили разработать приложение для просмотра графических файлов, давайте назовем форму PictureForm (значение свойства Name), и дадим ей заголовок Picture Viewer (значение свойства Caption).

Шаг 2. Теперь сохраните модуль формы и весь проект, выполнив команду меню File / Save All. Модуль назовите MainUnit.pas, а файл проекта - PictureViewer.dpr. Вот теперь можно приступать к изучению и меню и всего остального.

Отображение в форме главного меню (main menu) обеспечивает компонент MainMenu, расположенный в палитре компонентов на вкладке Standard (рисунок 8.1). Поместите этот компонент на форму и дайте ему имя MainMenu (значение свойства Name).


Рисунок 8.1. Компонент MainMenu

Компонент MainMenu имеет небогатый набор свойств, подробно мы на них останавливаться не будем, а обозначим лишь самые важные (таблица 8.1):

Свойство Описание
AutoHotKeys Значение maAutomatic избавляет программиста от необходимости назначать пунктам меню "горячие" клавиши (с помощью специального символа & в тексте пунктов); компонент автоматически подбирает "горячие" клавиши. Значение maManual требует, чтобы "горячие" клавиши назначил программист (см. параграф 8.1.3).
AutoLineReduction Если равно значению maAutomatic, то при отображении меню идущие подряд пункты-разделители рисуются как один разделитель, а пункты-разделители, находящиеся в начале или конце меню вообще не показываются. Свойство AutoLineReduction применяется при программном добавлении и удалении пунктов меню, чтобы избежать нежелательных явлений вроде повторяющихся и повисших разделительных линий. Если свойство AutoLineReduction равно значению maManual, то все пункты меню рисуются как есть.
AutoMerge Определяет, сливается ли главное меню вторичной формы с главным меню главной формы. Способ слияния определяется значением свойства GroupIndex каждого пункта меню верхнего уровня.
Images Список значков, отображаемых рядом с пунктами меню. Свойство Images используется совместно со свойством ImageIndex компонентов MenuItem (см. параграф 8.1.12).
Items Массив пунктов меню.
OwnerDraw Если равно значению True, то каждый пункт меню получает возможность участвовать в процессе своего отображения при помощи специальных событий OnMeasureItem и OnDrawItem. Событие OnMeasureItem происходит в пункте меню, когда рассчитываются размеры пункта. Событие OnDrawItem происходит в пункте меню, когда пункт рисуется на экране. Если свойство OwnerDraw равно значению False, то пункты меню имеют стандартный вид и события OnMeasureItem и OnDrawItem не происходят.
OnChange Происходит при изменении структуры меню.
Таблица 8.1. Важнейшие свойства и события компонента MainMenu

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

Пока в меню нет пунктов, нет и самого меню. Добавление новых пунктов выполняется в специальном окне - дизайнере меню (Menu Designer).

8.1.3. Дизайнер меню

Вызов дизайнера меню осуществляется с помощью команды Menu Designer…, которая находится в контекстном меню компонента MainMenu (рисунок 8.2).


Рисунок 8.2. Вызов дизайнера меню (Menu Designer)

Шаг 3. Выберите показанную на рисунке команду Menu Designer… и на экране появится окно с заголовком PictureForm.MainMenu. Это и есть дизайнер меню.


Рисунок 8.3. Дизайнер меню (Menu Designer)

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

Шаг 4. Сейчас строка главного меню состоит из одного безымянного пункта. Дайте этому пункту программный идентификатор FileMenuItem (значение свойства Name) и заголовок &File (значение свойства Caption). Символ & обеспечивает подчеркивание следующего за ним символа при отображении текста, поэтому пункт меню будет виден как File (рисунок 8.4). Подчеркнутая буква используется в комбинации с клавишей Alt для быстрого выбора пункта меню и называется горячей клавишей . В данном случае активизация пункта File будет происходить по комбинации клавиш Alt+F. Заметим, что в некоторых версиях операционной системы Windows «горячие» клавиши подчеркиваются только после нажатия клавиши Alt.


Рисунок 8.4. Текст пункта меню

Бывает очень утомительно назначать пунктам меню горячие клавиши. К тому же приходится заботиться о том, чтобы горячие клавиши не дублировались в нескольких пунктах. К счастью в компоненте MainMenu существует свойство AutoHotKeys. Если оно установлено в значение maAutomatic, то подбор горячих клавиш выполняется автоматически. С этого момента мы будем пользоваться этой возможностью, поэтому удалите символ амперсанта (&) из свойства Caption компонента FileMenuItem и убедитесь, что в компоненте MainMenu свойство AutoHotKeys установлено в значение maAutomatic.

Шаг 5. Сейчас под пунктом File нужно создать подчиненное меню со списком команд. Для этого просто щелкните в дизайнере меню на пункте File, среда Delphi все сделает за вас. Под пунктом File появится пустая ячейка - заготовка первого пункта выпадающего списка. Выберите этот пункт с помощью мыши и дайте ему программный идентификатор OpenMenuItem (свойство Name), а в свойстве Caption впишите текст Open... . Вместо пустой ячейки появится текст Open... и пустая ячейка переместится ниже.

Шаг 6. Действуя по аналогии, добавьте еще три пункта: Save As... , Close и Exit. В программе они должны называться SaveAsMenuItem, CloseMenuItem и ExitMenuItem соответственно.


Рисунок 8.5. Пункты в меню File

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

Согласитесь, что добавление новых пунктов сделано в среде Delphi очень удобно. Но для создания полноценного меню, одной этой возможности явно недостаточно - нужны средства вставки и удаления пунктов, создания вложенных меню и прочие. Поэтому в дизайнере меню для каждого отдельно взятого пункта предусмотрено контекстное меню с необходимым набором команд (рисунок 8.6 и таблица 8.2).


Рисунок 8.6. Контекстные команды в дизайнере меню

Команда Описание
Insert Вставляет новый пункт.
Delete Удаляет выбранный пункт.
Create Submenu Создает в позиции пункта подчиненное меню.
Select Menu Предлагает выбрать для работы другой компонент меню.
Save As Template Сохраняет текущую структуру меню в списке шаблонов.
Insert From Template Вставляет меню из списка шаблонов.
Delete Templates Удаляет шаблон(ы) меню.
Insert From Resource Вставляет меню из файла с описанием меню (расширение MNU) или из стандартного файла ресурсов (расширение RC).
Таблица 8.2. Контекстные команды в дизайнере меню

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

8.1.4. Пункты меню

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

Свойство Описание
Action Задает так называемую команду, которая будет выполняться при выборе данного пунтка меню. Весь список команд содержится в компоненте ActionList (см. параграф 8.6).
AutoCheck Если равно значению True, то выбор пункта меню автоматически приводит к изменению значения свойства Checked на противоположное. Если равно значению False, то изменением свойства Checked управляет программист.
AutoHotkeys Значение maAutomatic избавляет программиста от необходимости назначать пункту меню "горячую" клавишу (с помощью специального символа & в тексте пункта); компонент автоматически подбирает "горячую" клавишу. Значение maManual требует, чтобы "горячую" клавишу назначил программист (см. параграф 8.1.3). Значение maParent показывает, что способ назначения горячей клавиши определяется "родительским" компонентом MainMenu.
AutoLineReduction Если равно значению maAutomatic, то при отображении меню подряд идущие пункты-разделители рисуются как один разделитель, а пункты-разделители, находящиеся в начале или конце меню вообще не показываются. Свойство AutoLineReduction применяется при программном добавлении и удалении пунктов меню, чтобы избежать нежелательных явлений вроде повторяющихся и повисших разделительных линий. Если свойство AutoLineReduction равно значению maManual, то все пункты меню отображаются как есть. Если свойство равно значению maParent, то способ определяется "родительским" компонентом (например, MainMenu).
Bitmap Значок, который отображается рядом с текстом пункта меню. Если для данного пункта меню указан индекс значка с помощью свойство ImageIndex, то значение свойства Bitmap игнорируется. Значки в пунктах меню более подробно расмотрены в параграфе 8.1.12.
Break Если равно mbBreak или mbBarBreak, то пункт меню начинает новый столбец. Значение mbBarBreak обеспечивает отделение нового столбца от предыдущего вертикальной чертой.
Caption Текст пункта меню.
Checked Если равно значению True, то пункт меню содержит метку в виде "птички".
Default Значение True говорит о том, что выбор пункта меню можно осуществить двойным щелчком "родительского" пункта меню.
Enabled Определяет, доступен ли пользователю данный пункт меню.
GroupIndex Работает по-разному в зависимости от того, находится пункт в подчиненном меню или в строке главного меню. Пункты подчиненного меню с одинаковым положительным значением GroupIndex согласовано переключают между собой метку - установка у одного пункта свойства Checked в значение True снимает метку с другого пункта.В MDI-формах свойство GroupIndex работает по-другому. Пункты главного меню, находящиеся в дочерней форме MDI, сливаются с пунктами главного меню обрамляющей формы MDI при активизации дочерней формы. При этом если в строке главного меню обрамляющей формы существуют пункты с таким же значением свойства GroupIndex, то новый пункт со своим списком пунктов полностью заменяет старый; в противном случае новый пункт со своим списком пунктов вставляется в строку главного меню. Более подробно слияние меню изложено в справочнике среды Delphi.
Hint Краткая подсказка для пользователя, отображаемая в строке состояния.
ImageIndex Номер значка в списке Images компонента MainMenu. Значок отображается рядом с текстом пункта меню (см. параграф 8.1.12). Отрицательное значение свойства ImageIndex говорит о том, что для пункта меню значок не задан. Свойство ImageIndex имеет приоритет над свойством Bitmap.
RadioItem Если равно значению True, то метка имеет вид жирной точки.
ShortCut Комбинация клавиш для выполнения команды, не открывая меню.
SubMenuImages Список значков, отображаемых рядом с пунктами подчиненного меню. Свойство SubMenuImages используется совместно со свойством ImageIndex компонентов MenuItem (см. параграф 8.1.12).
Visible Определяет, виден ли пользователю пункт меню.
OnAdvancedDrawItem Происходит при рисовании отдельно взятого пункта меню на экране. Событие происходит только в том случае, если соответствующий компонент меню (MainMenu или PopupMenu) содержит значение True в свойстве OwnerDraw. Предоставляет более широкие возможности по сравнению с событием OnDrawItem.
OnClick Происходит при выборе пункта меню пользователем.
OnDrawItem Происходит при рисовании отдельно взятого пункта меню на экране. Событие происходит только в том случае, если соответствующий компонент меню (MainMenu или PopupMenu) содержит значение True в свойстве OwnerDraw.
OnMeasureItem Происходит при расчете размеров отдельно взятого пункта меню перед его рисованием на экране. Событие происходит только в том случае, если соответствующий компонент меню (MainMenu или PopupMenu) содержит значение True в свойстве OwnerDraw.
Таблица 8.3. Важнейшие свойства и события компонента MenuItem

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


Рисунок 8.7. Пункты меню в программном коде

8.1.5. Разделительные линии

Шаг 7. Логически связанные между собой команды принято отделять от других команд горизонтальной линией. Например, пункт Exit хорошо бы отделить от остальных (рисунок 8.8). Для этого вставьте новый пункт и запишите в значении свойства Caption символ минуса (-).


Рисунок 8.8. Разделительная линия в меню

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

8.1.6. Комбинации клавиш

Некоторым пунктам меню назначают комбинации клавиш (shortcut), чтобы выполнять команды, не открывая меню. Они ускоряют работу с приложением и популярны среди опытных пользователей. Названия комбинаций клавиш отображаются справа от текста соответствующих пунктов. Например, во многих программах команде меню File / Open... назначается комбинация клавиш Ctrl+O.

Шаг 8. Чтобы назначить пункту комбинацию клавиш, активизируйте пункт в дизайнере меню, перейдите к окну свойств и выберите в списке значений свойства ShortCut требуемую комбинацию клавиш (рисунок 8.9). Если ее там нет, то введите название комбинации клавиш вручную.


Рисунок 8.9. Комбинация клавиш для активизации пункта меню

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

8.1.7. Обработка команд меню

В первом приближении меню готово и вам наверняка не терпится его опробовать. Давайте реализуем закрытие формы по команде Exit. Решение этой задачи сводится к обработке события OnClick компонента ExitMenuItem. Это событие возникает при выборе пользователем в меню пункта Exit.

Шаг 9. Итак, активизируйте в дизайнере меню пункт Exit и выберите в окне свойств вкладку Events. Теперь сделайте двойной щелчок мышью на значении события OnClick (рисунок 8.10).


Рисунок 8.10. Создание обработчика команды меню

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

procedure TPictureForm.ExitMenuItemClick(Sender: TObject);
begin
  Close;
end;

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

Проверим, работает ли меню. Выполните компиляцию и запустите проект. На экране появится форма со строкой меню под заголовком. Выбор в меню любой команды кроме Exit безрезультатен. По команде Exit окно закроется и приложение завершится (рисунок 8.11).


Рисунок 8.11. Проверка работы команды Exit

8.1.8. Пункты-переключатели

Во многих программах существуют пункты меню, которые работают как переключатели. Если вы еще не сообразили, о чем идет речь, посмотрите на рисунок 8.12. В нашей программе переключателями удобно сделать пункты меню, отвечающие за отображение панели инструментов и строки состояния. Установка флажка щелчком пункта-переключателя показывает панель инструментов (или строку состояния), а снятие флажка - прячет. Рассмотрим, как программируется такое поведение.

Шаг 10. В строке главного меню создайте выпадающее меню View с пунктами Toolbar (программное имя ToolBarMenuItem) и Status bar (программное имя StatusBarMenuItem). Установите в последних двух пунктах свойство Checked в значение True. В пунктах меню появятся метки (рисунок 8.12).


Рисунок 8.12. Пункты-переключатели в меню

Шаг 11. В ответ на выбор пользователем пунктов Toolbar и Status bar будем переключать флажок. Вы уже знаете, как определить обработчик события OnClick для пункта меню, поэтому сразу приведем то, что вы должны получить:

procedure TPictureForm.ToolBarMenuItemClick(Sender: TObject);
begin
  // Спрятать или показать панель инструментов
  ToolBarMenuItem.Checked := not ToolBarMenuItem.Checked;
end;

procedure TPictureForm.StatusBarMenuItemClick(Sender: TObject);
begin
  // Спрятать или показать строку состояния
  StatusBarMenuItem.Checked := not StatusBarMenuItem.Checked;
end;

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

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

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

Шаг 12. Добавьте в меню View три пункта: Half Size (программное имя HalfSizeMenuItem), Normal Size (программное имя NormalSizeMenuItem) и Double Size (программное имя DoubleSizeMenuItem), отделив их от остальных пунктов меню с помощью разделительной линии.

Шаг 13. Объедините только что созданные пункты меню в одну согласовано работающую группу. Для этого у всех пунктов установите одинаковое ненулевое значение свойства GroupIndex (например, 1). Кроме того, установите для всех этих пунктов свойство RadioItem в значение True, чтобы метка имела вид жирной точки. Один из пунктов (например, Normal Size) переведите во включенное состояние, установив его свойство Checked в значение True (рисунок 8.13).


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

Шаг 14. Чтобы привести в действие механизм переключения пунктов, определите в них следующие обработчики события OnClick:

procedure TPictureForm.HalfSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок половинного размера
  HalfSizeMenuItem.Checked := True;
end;

procedure TPictureForm.NormalSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок нормального размера
  NormalSizeMenuItem.Checked := True;
end;

procedure TPictureForm.DoubleSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок двойного размера
  DoubleSizeMenuItem.Checked := True;
end;

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

8.1.10. Недоступные пункты меню

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

Шаг 15. В нашем примере логично было бы сделать недоступными пункты Save As..., Close, а также Half Size, Normal Size и Double Size, когда нет открытого графического файла. Для этого в каждом из указанных пунктов меню установите свойство Enabled в значение False (рисунок 8.14).


Рисунок 8.14. Недоступные пункты меню

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

type
  TPictureForm = class(TForm)
    ...
  private
    { Private declarations }
    procedure EnableCommands(Enable: Boolean);
  end;

...

procedure TPictureForm.EnableCommands(Enable: Boolean);
begin
  SaveAsMenuItem.Enabled := Enable;
  CloseMenuItem.Enabled := Enable;
  HalfSizeMenuItem.Enabled := Enable;
  NormalSizeMenuItem.Enabled := Enable;
  DoubleSizeMenuItem.Enabled := Enable;
end;

Параметр Enable данного метода определяет, в какое состояние перевести пункты меню: доступны - True или недоступны - False.

Шаг 17. Создайте обработчики команд Open... и Close. Как вы понимаете, в обработчик команды меню Open... следует поместить вызов метода EnableCommands с параметром True, а в обработчик команды Close - вызов метода EnableCommands с параметром False:

procedure TPictureForm.OpenMenuItemClick(Sender: TObject);
begin
  // Открыть рисунок и разрешить команды
  EnableCommands(True);
end;

procedure TPictureForm.CloseMenuItemClick(Sender: TObject);
begin
  // Закрыть рисунок и запретить команды
  EnableCommands(False);
end;

Выполните компиляцию и запустите программу. Посмотрите, как изменилось ее меню (рисунок 8.15).


Рисунок 8.15. Меню работающей программы. Некоторые пункты недоступны.

В самом начале работы отдельные пункты выпадающих меню File и View недоступны. Они становятся доступными после выбора команды File / Open..., а после выбора команды File / Close - снова недоступными.

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

8.1.11. Контекстное меню

Контекстное (вспомогательное) меню представлено в среде Delphi компонентом PopupMenu (рисунок 8.16). Отыщите его в палитре компонентов на вкладке Standard и поместите на форму. Дайте новому компоненту имя PopupMenu.


Рисунок 8.16. Компонент PopupMenu

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

Свойство Описание
Alignment Определяет место появления меню относительно указателя мыши: paLeft - левый верхний угол меню совпадает с позицией курсора мыши; paCenter - середина верхнего края меню совпадает с позицией курсора мыши; paRight - правый верхний угол меню совпадает с позицией курсора мыши.
AutoHotkeys Значение maAutomatic избавляет программиста от необходимости назначать пунктам меню "горячие" клавиши (с помощью специального символа & в тексте пунктов); компонент автоматически подбирает "горячие" клавиши. Значение maManual требует, чтобы "горячие" клавиши назначил программист (см. параграф 8.1.3).
AutoLineReduction Если равно значению maAutomatic, то при отображении меню подряд идущие пункты-разделители рисуются как один разделитель, а пункты-разделители, находящиеся в начале или конце меню вообще не показываются. Свойство AutoLineReduction применяется при программном добавлении и удалении пунктов меню, чтобы избежать нежелательных явлений вроде повторяющихся и повисших разделительных линий. Если свойство AutoLineReduction равно значению maManual, то все пункты меню отображаются как есть.
AutoPopup Если равно значению True, то меню появляется автоматически по нажатию правой кнопки мыши. Если равно значению False, то меню необходимо отображать программно.
Images Список значков, отображаемых рядом с пунктами меню. Свойство Images используется совместно со свойством ImageIndex компонентов MenuItem (см. параграф 8.1.12).
Items Обеспечивает нумерованный доступ к пунктам меню.
MenuAnimation Набор флажков, определяющих способ появления меню на экране: maLeftToRight - слева направо, maRightToLeft - справа налево, maTopToBottom - сверху вниз, maBottomToTop - снизу вверх, maNone - мгновенное отображение. Чтобы флажки начали работать, запустите программу настройки экрана (Start->Settings->Control Panel->Display) и на вкладке Effects выберите способ появления меню и подсказок - Scroll Effect.
OwnerDraw Если равно значению True, то каждый пункт меню получает возможность участвовать в процессе своего отображения при помощи специальных событий OnMeasureItem и OnDrawItem. Событие OnMeasureItem происходит в пункте меню, когда расчитываются размеры пункта. Событие OnDrawItem происходит в пункте меню, когда пункт рисуется на экране. Если свойство OwnerDraw равно значению False, то пукнты меню имеют стандартный вид и события OnMeasureItem и OnDrawItem не происходят.
TrackButton Кнопка мыши для выбора пункта меню: tbLeftButton - левая кнопка, tbRightButton - еще и правая кнопка.
OnChange Происходит при изменении структуры меню.
OnPopup Происходит при вызове меню пользователем.
Таблица 8.4. Важнейшие свойства и события компонента PopupMenu

Шаг 18. Контекстное меню наполняется пунктами, как и главное меню, в дизайнере меню. Двойным щелчком мыши на компоненте PopupMenu откройте окно констурктора меню и, используя уже известные вам приемы, добавьте в меню пункты Half Size (с идентификатором HalfSizePopupItem), Normal Size (с идентификатором NormalSizePopupItem) and Double Size (с идентификатором DoubleSizePopupItem). Во всех пунктах контекстного меню установите следующие свойства:

  • Enabled = False
  • GroupIndex = 1
  • RadioItem = True

Кроме этого пометьте пункт Normal Size, установив в нем свойство Checked в значение True. Таким образом, команды всплывающего меню дублируют некоторые команды главного меню, обеспечивая пользователю дополнительные удобства (рисунок 8.17).


Рисунок 8.17. Команды контекстного меню

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

Шаг 19. Активизируйте в дизайнере меню пункт Half Size и выберите в окне свойств вкладку Events. Выберите обработчик HalfSizeMenuItemClick из раскрывающегося списка события OnClick. То же самое проделайте с пунктами Normal Size и Double Size, установив для них обработчики NormalSizeMenuItemClick и DoubleSizeMenuItemClick соответственно (рисунок 8.18).


Рисунок 8.18. Установка обработчиков команд контекстного меню

Шаг 20. Для синхронной работы главного и контекстного меню нужно еще подправить некоторые обработчики:

procedure TPictureForm.HalfSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок половинного размера
  HalfSizeMenuItem.Checked := True;
  HalfSizePopupItem.Checked := True;
end;

procedure TPictureForm.NormalSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок нормального размера
  NormalSizeMenuItem.Checked := True;
  NormalSizePopupItem.Checked := True;
end;

procedure TPictureForm.DoubleSizeMenuItemClick(Sender: TObject);
begin
  // Показать рисунок двойного размера
  DoubleSizeMenuItem.Checked := True;
  DoubleSizePopupItem.Checked := True;
end;

procedure TPictureForm.EnableCommands(Enable: Boolean);
begin
  SaveAsMenuItem.Enabled := Enable;
  CloseMenuItem.Enabled := Enable;
  HalfSizeMenuItem.Enabled := Enable;
  HalfSizePopupItem.Enabled := Enable;
  NormalSizeMenuItem.Enabled := Enable;
  NormalSizePopupItem.Enabled := Enable;
  DoubleSizeMenuItem.Enabled := Enable;
  DoubleSizePopupItem.Enabled := Enable;
end;

Шаг 21. Контекстное меню готово, осталось сделать так, чтобы оно вызывалось по щелчку правой кнопки мыши на форме. Нет ничего проще - активизируйте форму и запишите в значении свойства PopupMenu имя разработанного ранее контекстного меню - PopupMenu. Вы можете ввести это значение с клавиатуры или выбрать из раскрывающегося списка (рисунок 8.19).


Рисунок 8.19. Привязка контекстного меню к форме

Готово, выполните компиляцию и запустите программу. Нажатие правой кнопки мыши в окне приложения вызовет появление контекстного меню. Все его пункты окажутся недоступными. Чтобы пункты контекстного меню заработали, выполните команду главного меню File / Open. После этого проверьте, что контекстное меню работает синхронно с главным меню.

8.1.12. Значки в пунктах меню

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

Шаг 22. Вызовите дизайнер меню для компонента MainMenu формы PictureForm. Выберите пункт File / Open и перейдите к свойству Bitmap в окне свойств (рисунок 8.20).


Рисунок 8.20. Свойство Bitmap пункта меню

Шаг 23. Установка значения свойства Bitmap осуществляется с помощью уже знакомого вам окна Picture Editor (рисунок 8.21), вызываемого нажатием кнопки с многоточием в поле свойства. В этом окне нажмите кнопку Load... и выберите файл Open.bmp из коллекции рисунков на компакт-диске (каталог \Images).


Рисунок 8.21. Окно Picture Editor

Наконец, закройте диалоговое окно с помощью кнопки OK. Результат показан на рисунке 8.22.


Рисунок 8.22. Пункт Open имеет значок

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

Как вы уже могли догадаться из названия компонента ImageList, он предназначен для хранения заранее подготовленного списка значков (в общем случае, любых рисунков). Другие компоненты берут значки из этого списка по номеру. Поскольку для хранения номера требуется гораздо меньше памяти, чем для хранения самого значка, то при использовании одного и того же значка в нескольких компонентах, достигается значительная экономия памяти. Да и управлять значками с помощью компонента ImageList тоже очень удобно. Если изменить значок в списке, то он автоматически изменится во всех компонентах, которые его используют. Существует лишь одно ограничение при использовании компонента ImageList - все хранимые значки должны иметь одинаковые размеры.

Свойство Описание
Width, Height Ширина и высота рисунков в списке.
AllocBy Приращение массива. Когда массив полностью заполнен и делается попытка добавить новый рисунок, размер массива увеличивается на AllocBy элементов. Используется для оптимизации скорости добавления элементов и занимаемой ими памяти.
BkColor Цвет фона. Этим цветом запоняются те части рисунков, которые должны быть прозрачными. Значение clNone оставляет фон прозрачным.
BlendColor Цвет, которым подсвечиваются рисунки. Наличие подсветки и ее яркость зависит от значения свойства DrawingStyle.
DrawingStyle Способ отображения рисунков:dsFocus - легкая (25%) подсветка цветом BlendColor;dsSelected - сильная (50%) подсветка цветом BlendColor;dsNormal - подсветка отсутствует, цвет фона берется из свойства BkColor. Если свойство BkColor содержит значение clNone, то фон прозрачный.dsTransparent - подсветка отсутствует, цвет фона прозрачный независимо от значения свойства BkColor.Значение этого свойства может игнорироваться стандартными компонентами. Оно полезно, если рисунки из списка отображаются программно с помощью метода Draw.
ImageType Выбирает между отображением рисунков (значение tiImage) и их масок (значение tiMask).Значение этого свойства может игнорироваться стандартными компонентами. Оно полезно, если рисунки из списка отображаются программно с помощью метода Draw.
Masked Если равно значению True, то при добавлении рисунка в список для него создается специальная маска. Маска описывает фоновые пиксели рисунка и используется при выводе рисунка на экран. Наличие маски позволяет манипулировать фоном рисунка с помощью свойств BkColor и DrawingStyle. Если свойство Masked равно значению False, то рисунок помещается в список без маски и всегда рисуется как есть. В этом случае свойство BkColor игнорируется, а значение dsTrasnparent в свойстве DrawingStyle не производит эффекта.
ShareImages Если равно значению False, то при уничтожении компонента уничтожается также соответствующий объект операционной системы Windows, который скрыт внутри компонента ImageList. Если равно значению True, то при уничтожении компонента связанный с ним объект операционной системы не уничтожается, что позволяет использовать этот объект за пределами библиотеки VCL.
OnChange Происходит при любом изменении списка.
Таблица 8.5. Основные свойства и события компонента ImageList

Воспользуемся компонентом ImageList для хранения значков в нашей программе. Найдите его в палитре компонентов на вкладке Win32 (рисунок 8.23).


Рисунок 8.23. Компонент ImageList

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

Шаг 24. Редактирование списка рисунков осуществляется в специальном окне. Вызовите его с помощью команды ImageList Editor…, находящейся в контекстном меню компонента ImageList (рисунок 8.24).


Рисунок 8.24. Вызов окна для редактирования списка значков

Шаг 25. В открывшемся окне (рисунок 8.25) нажмите кнопку Add... и выберите несколько файлов из коллекции рисунков на компакт-диске (каталог \Images).


Рисунок 8.25. Окно, в котором редактируется список значков

В этом окне параметры Transparent Color, Fill Color и Options нуждаются в пояснении. Параметр Transparent Color - это цвет пикселей, которые становятся прозрачными. Параметры Options и Fill Color используются, если размеры рисунка не совпадают с размерами, указанными в свойствах Width и Height компонента ImageList. Параметр Options принимает следующие значения: Crop - заполнить лишние пиксели цветом Fill Color (либо отсечь правую нижнюю часть рисунка, если рисунок превышает размеры); Stretch - растянуть/сжать рисунок до принятых размеров; Center - центрировать рисунок, заполнив оставшуюся область цветом Fill Color.

Закройте диалоговое окно с помощью кнопки OK.

Шаг 26. Список значков мы подготовили. Теперь нужно указать нашему компоненту MainMenu, откуда брать значки для своих пунктов. Выделите компонент MainMenu на форме и перейдите к свойству Images в окне свойств. Из списка значений свойства Images выберите элемент ImageList.

Шаг 27. Теперь осталось указать пунктам меню номера соответствующих им значков. Для этого вызовите дизайнер меню для компонента MainMenu. Как это сделать, вы уже знаете. Далее выберите пункт Open... (программный идентификатор OpenMenuItem), перейдите к свойству ImageIndex и выберите из раскрывающегося списка подходящий значок (рисунок 8.26).


Рисунок 8.26. Установка значка для пункта меню

Аналогично установите номера соответствующих значков для пунктов Save As…, Half Size, Normal Size и Double Size. Не пугайтесь внешнего вида значков в недоступных пунктах меню. Они, как и текст, отображаются блеклыми.

На рисунке 8.27 показана форма после добавления значков.


Рисунок 8.27. В меню добавлены значки

Шаг 28. Теперь установим значки для пунктов контекстного меню нашей формы. Активизируйте форму PictureForm и выберите на ней компонент PopupMenu. Затем в окне свойств перейдите к свойству Images и из списка значений этого свойства выберите элемент ImageList. После этого вызовите дизайнер меню у компонента PopupMenu, и аналогично тому, как вы это делали в главном меню, назначьте номера подходящих значков для пунктов Half Size, Normal Size и Double Size контекстного меню.

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

Шаг 29. Кстати, мы совсем забыли про значок, все еще хранящийся в свойстве Bitmap компонента OpenMenuItem. Сейчас в нем нет необходимости, поскольку реально используется значок, заданный с помощью свойства ImageIndex. Чтобы не держать в программе ненужные данные, удалите значок из свойства Bitmap. Для этого вызовите дизайнер меню для компонента MainMenu и выберите пункт Open.... Далее в окне свойств перейдите к свойству Bitmap и нажмите клавишу Del, после чего нажмите клавишу Enter.

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

8.2. Полноценное приложение для просмотра графических файлов

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

  • организовать выбор файла по командам меню Open... и Save As... ;
  • реализовать загрузку и отображение рисунка.

Первая задача решается с помощью стандартных диалоговых компонентов OpenDialog и SaveDialog, вторая - с помощью специального компонента Image.

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

Шаг 30. Диалоговые окна для выбора открываемого или сохраняемого файла организуются с помощью компонентов OpenDialog и SaveDialog (рисунок 8.29). Найдите их в палитре компонентов на вкладке Dialogs и поместите на форму. Первый компонент назовите OpenDialog, а второй - SaveDialog.


Рисунок 8.28. Компоненты OpenDialog и SaveDialog

Характерные свойства этих компонентов кратко описаны в таблице 8.6.

Свойство Описание
DefaultExt Расширение, которое добавляется к имени файла, если пользователь его не указал.
FileName Имя выбранного файла.
Filter Фильтры имени файла.
FilterIndex Номер активного фильтра.
InitialDir Начальный каталог, открываемый при первом появлении окна диалога.
Options Параметры, определяющие внешний вид и поведение окна диалога. (см. таблицу 8.7).
OptionsEx Дополнительные параметры, определяющие внешний вид и поведение окна диалога (см. таблицу 8.7).
Title Заголовок окна диалога. Если значение свойства не указано, то заголовок будет стандартным - Open (Открыть) или Save (Сохранить) в зависимости от типа компонента.
OnCanClose Происходит, когда пользователь пытается закрыть окно диалога. Позволяет выполнить дополнительные проверки и отменить закрытие окна при необходимости.
OnClose Происходит непосредственно перед закрытием формы после события OnCanClose.
OnFolderChange Происходит, если пользователь переходит в другой каталог.
OnIncludeItem Происходит при добавлении каждого файла в список отображаемых в окне файлов. Позволяет выполнять дополнительную фильтрацию файлов.
OnSelectionChange Происходит при смене выделенного файла или списка файлов.
OnShow Происходит непосредственно перед отображением окна диалога на экране.
OnTypeChange Происходит, когда пользователь выбирает фильтр.
Таблица 8.6. Важнейшие свойства и события компонентов OpenDialog и SaveDialog.

Компоненты OpenDialog и SaveDialog очень схожи между собой, оба являются объектно-ориентированными оболочками стандартных диалоговых окон Windows: Open и Save. На следующем рисунке показано окно Open (рисунок 8.29).


Рисунок 8.29. Стандартное окно Open для выбора открываемого файла

Приблизительный сценарий работы с каждым из компонентов OpenDialog и SaveDialog таков. Компонент помещается на форму и конфигурируется для выбора тех или иных файлов. По команде меню Open... или Save As... у соответствующего компонента вызывается метод Execute. Он вызывает диалог и возвращает значение True, если пользователь выбрал файл. Полный маршрут к файлу запоминается в свойстве FileName. Ход дальнейших действий зависит от прикладной задачи и, как правило, включает или чтение, или запись файла, в зависимости от обрабатываемой команды меню.

Придерживаясь написанного сценария, приспособим компоненты OpenDialog и SaveDialog для выбора графических файлов, поддерживаемых нашей программой. Чтобы пользователь мог просматривать файлы выборочно (какого-то одного типа) в диалоговых блоках имеется набор фильтров, оформленный в виде раскрывающегося списка с подписью Files of type (см. рис. выше). Исходные данные для этого списка устанавливаются в свойстве Filter. Номер активного в данный момент фильтра записывается в свойстве FilterIndex.

Шаг 31. Приступим к формированию списка фильтров. Активизируйте на форме компонент OpenDialog и в окне свойств выберите свойство Filter (рисунок 8.30). Щелчком кнопки с многоточием откройте редактор фильтров - окно Filter Editor (рисунок 8.31).


Рисунок 8.30. Нажатие кнопки с многоточием вызывает редактор фильтров

Окно Filter Editor представляет собой список с двумя столбцами. В левой колонке вводится текст, отображаемый в раскрывающемся списке Files of type окна диалога. В правом столбце через точку с запятой записываются маски, на основании которых выполняется фильтрация файлов.

Шаг 32. Установите в компоненте OpenDialog фильтры, как показано на рисунке 8.31.


Рисунок 8.31. Окно для редактирования фильтров - Filter Editor

Шаг 33. Аналогичным образом установите фильтры в компоненте SaveDialog. Самый простой и быстрый способ в данном случае - скопировать текст свойства Filter из компонента OpenDialog в компонент SaveDialog через буфер обмена (результат показан на рисунке 8.32):


Рисунок 8.32. Фильтры для окна Save скопированы из окна Open

Компоненты OpenDialog и SaveDialog имеют большое количество булевских параметров, организованных в виде составных свойств Options и OptionsEx. Эти параметры влияют на то, как окно диалога выглядит и работает. Их смысл поясняет таблица 8.7.

Параметр Описание
ofReadOnly Если равно True, то переключатель Read-only в окне диалога включен.
ofOverwritePrompt Если равно True, то пользователю выдается предупреждение при попытке сохранить файл с именем, которое уже существует.
ofHideReadOnly Если равно True, то переключатель Read-only отсутствует в окне диалога.
ofNoChangeDir Если равно True, то пользователь не сможет сменить каталог в окне диалога.
ofShowHelp Если равно True, то в окне диалога присутствует кнопка Help.
ofNoValidate Если равно True, то пользователь может вводить в имени файла любые символы, даже недопустимые.
ofAllowMultiSelect Если равно True, то пользователь может выделить сразу несколько файлов.
ofExtensionDifferent Этот параметр устанавливается после завершения диалога, если расширение в имени файла отличается от начального расширения.
ofPathMustExist Если равно True, то пользователь не сможет ввести для файла несуществующий маршрут.
ofFileMustExist Если равно True, то пользователь не сможет ввести имя несуществующего файла.
ofCreatePrompt Если равно True и пользователь вводит имя несуществующего файла, то пользователю задается вопрос, желает ли он создать новый файл с таким именем.
ofShareAware Если равно True, то ошибки одновременного доступа к файлу со стороны нескольких приложений игнорируются.
ofNoReadOnlyReturn Если равно True, то пользователь не сможет ввести файл с атрибутом read-only (только для чтения).
ofNoTestFileCreate Если равно True, то проверка на возможность записи в каталог не выполняется.
ofNoNetworkButton Если равно True, то кнопка Network отсутствует в окне диалога. Этот параметр работает только в паре с параметром ofOldStyleDialog.
ofNoLongNames Если равно True, то длинные имена файлов запрещены.
ofOldStyleDialog Если равно True, то окно диалога отображается в старом стиле Windows 3.1.
ofNoDereferenceLinks Если равно True, то ярлыки к каталогам трактуются как обычные файлы. В противном случае они трактуются как каталоги.
ofEnableIncludeNotify Если равно True, то при формировании списка отображаемых файлов происходит событие OnIncludeItem (для каждого файла). В обработчике этого события обычно выполняется дополнительная фильтрация файлов.
ofEnableSizing Если равно значению True, то пользователь имеет возможность изменять размеры окна диалога.
ofDontAddToRecent Если равно значению True, то файл не помещается в список последних открытых файлов.
ofShowHidden Если равно True, то в окне показываются скрытые файлы (файлы с атрибутом Hidden).
ofExNoPlaceBar Если равно True, то боковая панель не показывается в окне диалога. Флажок ofExNoPlaceBar относится к свойству OptionsEx.
Таблица 8.7. Параметры компонентов OpenDialog и SaveDialog

Шаг 34. В нашем простом примере ограничимся тем, что установим в компоненте SaveDialog параметр ofOverwritePrompt в значение True (см. табл. 6.6).

Заметим, что проверить работу компонентов OpenDialog и SaveDialog можно с помощью команды Test Dialog. Она находится в контекстном меню значка компонента в форме.

8.2.2. Отображение рисунков

Шаг 35. Ну вот, диалоговые компоненты настроены. Теперь нужен компонент, обеспечивающий отображение рисунков различных форматов. Такой компонент в среде Delphi есть, он называется Image и находится в палитре компонентов на вкладке Additional (рисунок 8.33). Выберите его из палитры и поместите на форму. Назовите новый компонент Image, а свойствам Left и Top установите значение 0.


Рисунок 8.33. Компонент Image

Характерные свойства компонента Image кратко описаны в таблице 8.8.

Свойство Описание
AutoSize Если равно значению True, то размеры компонента автоматически подгоняются под размеры рисунка.
Center Центрирует рисунок в пределах компонента.
IncrementalDisplay Обеспечивает постепенное (по мере загрузки) отображение больших рисунков. Используется для устранения эффекта блокировки пользовательского ввода во время отображения рисунка.
Picture Cодержит рисунок, отображаемый в области компонента. Свойство Picture является объектом класса TPicture и может хранить точечный рисунок (bitmap), метафайл (metafile), значок (icon).
Proportional Если равно значению True, то при масштабировании сохраняется пропорция между вертикальным и горизонтальным размерами рисунка.
Stretch Если равно значению True, то рисунок масштабируется так, чтобы его размеры совпадали с размерами компонента. Масштабирование выполняется только для точечных рисунков и метафайлов.
Transparent Если равно значению True, то фон рисунков становится прозрачным. Эффект появляется только после установки свойства Picture.
OnProgress Происходит по мере выполнения длительных операций, например во время загрузки больших рисунков.
Таблица 8.8. Основные свойства компонента Image

Компонент Image позволяет отображать рисунки разных форматов: точечные рисунки (BMP), значки (ICO), метафайлы (WMF, EMF). Сам рисунок хранится в свойстве Picture.

Шаг 36. Размеры установленного рисунка могут не совпадать с текущими размерами компонента. В этом случае лишняя часть изображения отсекается. Чтобы подогнать размеры компонента под размеры рисунка установите свойство AutoSize в значение True (рисунок 8.34). После этого при каждой установке свойства Picture размеры компонента (но не рисунка) будут изменяться автоматически.


Рисунок 8.34. Свойство AutoSize в компоненте Image установлено в значение True

Бывает и обратная ситуация, когда нужно подогнать размеры рисунка под заданные размеры компонента. Для этого свойство Stretch устанавливается в значение True, а AutoSize - в значение False. Масштабирование целесообразно применять только для векторных изображений; для точечных рисунков оно не всегда дает приятный результат - начинает сказываться точечная природа изображения.

Сейчас компонент Image находится на своем месте и подготовлен к работе (свойство AutoSize имеет значение True). Рассмотрим, как осуществляется загрузка и сохранение рисунка по командам меню Open... и Save As... .

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

procedure TPictureForm.OpenMenuItemClick(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    Image.Picture.LoadFromFile(OpenDialog.FileName);
    EnableCommands(True);
    NormalSizeMenuItem.Click;
  end;
end;

В данном обработчике обратите внимание на вызов метода Click у компонента NormalSizeItem. Он имитирует выбор пункта меню Normal Size, чтобы сразу после загрузки рисунок имел нормальный размер.

Шаг 38. Пункт меню Save As... еще не имеет обработчика события OnClick, поэтому вам придется его создать (напомним, что это делается в окне свойств на вкладке Events). Обработка команды Save As... состоит в вызове стандартного диалогового окна Save с последующем сохранением рисунка в файле:

procedure TPictureForm.SaveAsMenuItemClick(Sender: TObject);
begin
  if SaveDialog.Execute then
    Image.Picture.SaveToFile(SaveDialog.FileName);
end;

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

procedure TPictureForm.CloseMenuItemClick(Sender: TObject);
begin
  with Image do
  begin
    Picture := nil;
    Width := 0;
    Height := 0;
  end;
  NormalSizeMenuItem.Click;
  EnableCommands(False);
end;

Шаг 40. Еще остались незавершенными обработчики команд меню Half Size, Normal Size и Double Size, которые тоже нужно доработать. С ними вы легко разберетесь:

procedure TPictureForm.HalfSizeMenuItemClick(Sender: TObject);
begin
  HalfSizeMenuItem.Checked := True;
  HalfSizePopupItem.Checked := True;
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width div 2;
    Height := Picture.Height div 2;
    Stretch := True;
  end;
end;

procedure TPictureForm.NormalSizeMenuItemClick(Sender: TObject);
begin
  NormalSizeMenuItem.Checked := True;
  NormalSizePopupItem.Checked := True;
  Image.AutoSize := True; // восстановить нормальные размеры компонента
end;

procedure TPictureForm.DoubleSizeMenuItemClick(Sender: TObject);
begin
  DoubleSizeMenuItem.Checked := True;
  DoubleSizePopupItem.Checked := True;
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width * 2;
    Height := Picture.Height * 2;
    Stretch := True;
  end;
end;

В первом приближении программа для просмотра графических файлов готова. Выполните компиляцию программы и проверьте ее работоспособность. Например, откройте файл Chemical.bmp из стандартной коллекции изображений среды Delphi (C:\Program Files\Common Files\Borland Shared\Images\Splash\256Color). Вашему взору предстанет следующая картина (рисунок 8.35):


Рисунок 8.35. Программа для просмотра графических файлов в работе

Внимание! В каталоге C:\Program Files\Common Files\Borland Shared\Images вы найдете для своих приложений много полезных и красивых точечных рисунков, значков, курсоров. Если вы еще не исследовали этот каталог, то сделайте это с помощью своей программы.

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

8.3. Строка состояния

8.3.1. Создание строки состояния

Строка состояния (status bar) - это панель в нижней части окна, предназначенная для вывода вспомогательной информации: параметров документа, с которым работает пользователь, подсказок к пунктам меню и др. В среде Delphi она организуется с помощью компонента StatusBar, расположенного в палитре компонентов на вкладке Win32 (рисунок 8.36).

Шаг 41. Поместите компонент на форму и дайте ему имя StatusBar.


Рисунок 8.36. Компонент StatusBar

Таблица 8.9 знакомит вас с основными свойствами компонента StatusBar. Когда вы изучите компонент, она пригодится вам в качестве справочника, а сейчас просто окиньте ее взглядом и двигайтесь дальше.

Свойство Описание
Action Задает так называемую команду, которая будет выполняться по щелчку на строке состояния. Весь список команд содержится в компоненте ActionList (см. параграф 8.6).
Align Способ выравнивания строки состояния в пределах содержащего компонента (например, формы или панели).
AutoHint Если равно значению True, то текст строки состояния автоматически устанавливается равным текущей всплывающей подсказке.
BorderWidth Величина отступа от границ компонента до границ информационных панелей.
Panels Информационные панели, отображаемые на строке состояния.
SimplePanel Если равно значению True, то вместо информационных панелей на строке состояния отображается одна простая строка текста, хранящаяся в свойстве SimpleText.
SimpleText Строка текста, отображаемая в строке состояния, когда свойство SimplePanel равно значению True.
SizeGrip Если равно значению True, то в правом нижнем углу строки состояния рисуется "гармошка", работающая как размерная рамка.
UseSystemFont Если равно значению True, то используется стандартный шрифт системы.
OnCreatePanelClass Позволяет создавать свои собственные панели на базе класса TStatusPanel.
OnDrawPanel Происходит при рисовании панели, если свойство Style содержит значение psOwnerDraw.
Таблица 8.9. Важнейшие свойства и события компонента StatusBar

Как только вы добавили на форму строку состояния, она тут же прижалась к нижнему краю формы и растянулась по всей ее ширине (см. рис. ниже). Какая сообразительная! А ну-ка изменим ширину формы. Ба! Строка состояния тоже корректирует свою ширину и всегда занимает всю нижнюю часть формы (рисунок 8.37).


Рисунок 8.37. Строка состояния автоматически прижимается к нижнему краю формы

Такое поведение обеспечивает свойство Align, которое в компоненте StatusBar изначально содержит значение alBottom. Свойство Align есть во многих визуальных компонентах. С его помощью вы можете заставить компонент подгонять свои размеры и положение при изменении размеров своего владельца (формы или компонента, на котором он находится). Возможные значения свойства Align описаны в таблице 8.10.

Значение Описание
alNone Позиция и размеры компонента остаются неизменными в пределах владельца.
alTop Компонент прижимается к верхнему краю владельца и растягивается по всей его ширине.
alBottom Компонент прижимается к нижнему краю владельца и растягивается по всей его ширине.
alLeft Компонент прижимается к левому краю владельца и растягивается по всей его высоте.
alRight Компонент прижимается к правому краю владельца и растягивается по всей его высоте.
alClient Компонент подгоняется под размеры владельца.
Таблица 8.10. Значения свойства Align

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

Шаг 42. Вернемся к примеру и приспособим строку состояния для отображения размеров рисунка и имени файла, в котором рисунок хранится на диске. С этой целью разделим строку состояния на две информационные панели. Перейдите к окну свойств и в поле Panels щелкните кнопку с многоточием (либо в контекстном меню строки состояния выберите пункт Panels Editor…). Откроется специальное окно с заголовком Editing StatusBar.Panels для создания панелей в строке состояния (рисунок 8.38).


Рисунок 8.38. Окно для создания панелей в строке состояния

Шаг 43. В этом окне создаются, редактируются и удаляются панели строки состояния. Оно работает в паре с окном свойств, в котором настраиваются свойства отдельно взятой панели строки состояния. Нажатием кнопки Add New создайте первую панель и установите ее свойства так, чтобы она получилась шириной 70 пикселей (Width = 70), продавленной (Bevel = pbLowered) и с центрированным текстом (Alignment = taCenter). См. рисунок 8.39.


Рисунок 8.39. В строке состояния создана панель

В этой панели будут отображаться размеры рисунка. Аналогично создайте вторую панель (рисунок 8.40) неограниченной ширины (Width = -1), продавленной (Bevel = pbLowered) и с прижатым влево текстом (Alignment = taLeftJustify). В ней будет отображаться имя файла.


Рисунок 8.40. В строке состояния создана еще одна панель

После этого закройте окно Editing StatusBar.Panels.

Строка состояния создана и сейчас рассмотрим, как вывести в ней текст. Доступ к панелям обеспечивает свойство Panels. Оно содержит массив Items, элементами которого являются объекты-панели. Каждая панель имеет свойство Text, в котором хранится отображаемый на панели текст. Итак, установка содержимого строки состояния в нашем примере будет выглядеть так:

StatusBar.Panels.Items[0].Text :=
  Format('%d x %d', [Image.Picture.Width, Image.Picture.Height]);
StatusBar.Panels.Items[1].Text := OpenDialog.FileName;

Учитывая, что массив Items выступает главным свойством объекта Panels, эти операторы можно записать короче:

StatusBar.Panels[0].Text :=
  Format('%d x %d', [Image.Picture.Width, Image.Picture.Height]);
StatusBar.Panels[1].Text := OpenDialog.FileName;

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

Шаг 44. Обновление строки состояния удобно оформить в виде метода формы:

procedure TPictureForm.UpdateStatusBar;
begin
  if Image.Width <> 0 then 
  begin
    StatusBar.Panels[0].Text := Format('%d x %d',
      [Image.Picture.Width, Image.Picture.Height]);
    StatusBar.Panels[1].Text := OpenDialog.FileName;
  end
  else // в компоненте Image нет рисунка
  begin
    StatusBar.Panels[0].Text := '';
    StatusBar.Panels[1].Text := '';
  end;
end;

Шаг 45. Вызовы метода UpdateStatusBar поместите в обработчики команд меню Open... и Close.

procedure TPictureForm.OpenMenuItemClick(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    Image.Picture.LoadFromFile(OpenDialog.FileName);
    EnableCommands(True);
    NormalSizeMenuItem.Click;
  end;
  UpdateStatusBar;
end;

procedure TPictureForm.CloseMenuItemClick(Sender: TObject);
begin
  with Image do
  begin
    Picture := nil;
    Width := 0;
    Height := 0;
  end;
  NormalSizeMenuItem.Click;
  EnableCommands(False);
  UpdateStatusBar;
end;

Наконец выполните компиляцию приложения и проверьте, что строка состояния работает. Например, откройте файл Chemical.bmp, расположенный по маршруту C:\Program Files\Common Files\Borland Shared\Images\Splash\256Color. В строке состояния отобразятся размеры рисунка и путь к файлу.


Рисунок 8.41. Программа для просмотра графических файлов теперь имеет строку состояния

8.3.2. Подсказки в строке состояния

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

Для того чтобы вы могли получить строку состояния с описанной выше логикой работы, в компоненте StatusBar предусмотрен режим отображения простого текста. Его обеспечивает булевское свойство SimplePanel. По умолчанию оно равно значению False и в строке состояния отображаются панели объекта Panels. Если установить свойство SimplePanel в значение True, то в строке состояния будет отображаться текст, хранящийся в свойстве SimpleText. Итак, задача состоит в том, чтобы при активизации меню записывать подсказку выбранного пункта в свойстве SimpleText и, в том случае если подсказка содержит текст, устанавливать свойство SimplePanel в значение True. Для решения этой задачи вы должны представлять механизм работы подсказок. Его суть состоит в следующем.

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

Объект Application не виден в окне свойств, но вы можете получить доступ к его событиям на этапе проектирования. Для этого в форму помещается специальный компонент ApplicationEvents, который вы найдете в палитре компонентов на вкладке Additional (рисунок 8.42).


Рисунок 8.42. Компонент ApplicationEvents

Шаг 46. Поместите на форму компонент ApplicationEvents. Дайте ему имя ApplicationEvents. Обратите внимание, что у этого компонента всего два свойства: Name и Tag. Это не удивительно, так как основное назначение компонента - представить события объекта Application (таблица 8.11).

Событие Описание
OnActionExecute Происходит при выполнении любой команды в компоненте ActionList (см. параграф 8.6).
OnActionUpdate Происходит во время простоя программы для обновления состояния команд в компоненте ActionList (см. параграф 8.6).
OnActivate Происходит, когда приложение получает активность, т.е. когда пользователь переключается на него с другого приложения.
OnDeactivate Происходит, когда приложение теряет активность, т.е. когда пользователь переключается с него на другое приложения.
OnException Происходит, когда в приложении возникает необработанная исключительная ситуация. По умолчанию обработчик этого события вызывает метод ShowException для отображения окна сообщений с пояснением причины ошибки. Вы можете изменить реакцию на событиеOnException, переписав его обработчик.
OnHelp Происходит, когда пользователь вызывает справку.
OnHint Происходит, когда курсор мыши наводится на компонент, содержащий всплывающую подсказку.
OnIdle Периодически происходит во время простоя программы.
OnMessage Происходит при получении программой сообщения операционной системы Windows.
OnMinimize Происходит, когда пользователь сворачивает приложение.
OnRestore Происходит, когда пользователь восстанавливает свернутое приложение.
OnSettingChange Происходит при изменении настроек операционной системы, например, настроек экрана или региональных настроек.
OnShortCut Происходит при нажатии клавиш на клавиатуре (еще до того, как в форме происходит событие OnKeyDown).
OnShowHint Происходит непосредственно перед появлением любой всплывающей подсказки.
Таблица 8.11. События компонента ApplicationEvents

Шаг 47. В окне свойств переключитесь на вкладку Events, найдите событие OnHint и создайте следующий обработчик:

procedure TPictureForm.ApplicationEventsHint(Sender: TObject);
begin
  with StatusBar do
  begin
    SimpleText := Application.Hint;
    SimplePanel := SimpleText <> '';
  end;
end;

Шаг 48. Теперь в свойстве Hint каждого пункта меню впишите угодную вам строку-подсказку (рисунок 8.43).


Рисунок 8.43. Подсказка для пункта меню

Шаг 49. Выполните компиляцию и запустите программу. Проверьте работу механизма подсказок в строке состояния (рисунок 8.44).


Рисунок 8.44. Программа для просмотра графических файлов теперь показывает подсказки для пунктов меню в строке состояния

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

8.4. Прокрутка

8.4.1. Прокрутка рабочей области формы

На практике часто бывает, что отображаемая информация не умещается на форме целиком (даже если форма раскрыта на весь экран). Например, в нашем примере можно загрузить рисунок, размеры которого превосходят размеры формы (и даже всего экрана) в несколько раз. Лучшее, что можно предпринять в таком случае, - это организовать прокрутку (scrolling) рисунка внутри формы.

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

Форма имеет встроенную поддержку прокрутки, благодаря чему реализуется просмотр содержимого формы при любом изменении ее размеров. Когда размеры или координаты компонентов превышают размеры формы, форма создает полосы прокрутки и пользователь получает возможность прокручивать изображение. Встроенные в форму полосы прокрутки представлены составными свойствами HorzScrollBar (горизонтальная полоса прокрутки) и VertScrollBar (вертикальная полоса прокрутки). Они кратко описаны в таблице 8.12.

Свойство Описание
ButtonSize Размер кнопок со стрелками.
Increment Величина "информативной строки" в пикселах.
Margin Минимальный отступ прокручиваемых элементов от края области прокрутки.
Position Позиция бегунка на полосе прокрутки.
Range Виртуальный размер области прокрутки.
Size Ширина полосы прокрутки.
Smooth Значение True указывает, что значение свойства Increment должно автоматически рассчитываться компонентом.
Style Стиль полосы прокрутки: ssRegular - обычный рельефный, ssFlat - плоский, ssHotTrack - плоский с подсветкой при наведении указателя мыши.
ThumbSize Размер бегунка.
Tracking Если равно значению True, то прокрутка изображения происходит по мере передвижения бегунка.
Visible Определяет, видна ли полоса прокрутки пользователю.
Таблица 8.12. Составные свойства HorzScrollBar и VertScrollBar

Наибольший интерес представляют вложенные свойства Tracking и Increment. Установка булевского свойства Tracking в значение True обеспечивает прокрутку изображения по мере передвижения бегунка с помощью мыши. Свойство Increment задает величину "информативной строки" в пикселях. Уменьшив это значение до 1, вы получите более плавную прокрутку.

8.4.2. Отдельная область прокрутки

Как ни крути, а форма не позволяет организовать прокрутку в отдельной своей части. Например, в приложении для просмотра графических файлов хотелось бы организовать прокрутку рисунка, но так, чтобы строка состояния в прокрутке не участвовала. Форма этого сделать не позволяет. Здесь на помощь приходит компонент ScrollBox, представляющий собой отдельную область прокрутки . Он расположен в палитре компонентов на вкладке Additional (рисунок 8.45).


Рисунок 8.45. Компонент ScrollBox

Таблица 8.13 содержит краткую характеристику его отличительных свойств.

Свойство Описание
Align Способ выравнивания области прокрутки в пределах владельца.
AutoScroll Если равно значению True, полосы прокрутки появляются и скрываются автоматически по мере необходимости.
AutoSize Режим автоматического изменения размеров области прокрутки в зависимости от размеров и положения внутренних компонентов.
BevelEdges Вложенные свойства beLeft, beTop, beRight и beBottom определяют видимость соответственно левой, верхней, правой и нижней сторон рельефной рамки.
BevelInner Внутренний скос рельефной рамки: bvNone - скос отсутствует, bvLowered - скос внутрь, bvRaised - скос наружу; bvSpace - скос заменяется отступом.
BevelKind Вид рельефной рамки: bkNone - рамки нет, bkTile - рамка с четкими скосами, bkSoft - рамка со сглаженными скосами, bkFlat - плоская рамка (без скосов).
BevelOuter Внешний скос рельефной рамки: bvNone - скос отсутствует, bvLowered - скос внутрь, bvRaised - скос наружу; bvSpace - скос заменяется отступом.
BevelWidth Ширина скосов рельефной рамки.
BorderStyle Определяет, имеет ли область прокрутки рамку.
DockSite Определяет, используется ли область прокрутки для стыковки других компонентов.
HorzScrollBar Определяет параметры и поведение горизонтальной полосы прокрутки (см. табл. 6.10).
VertScrollBar Определяет параметры и поведение вертикальной полосы прокрутки (см. табл. 6.10).
OnGetSiteInfo Происходит, когда у компонента запрашивается место для стыковки.
Таблица 8.13. Важнейшие свойства компонента ScrollBox

Компонент ScrollBox служит контейнером для других компонентов и обеспечивает их прокрутку внутри себя. Давайте поместим на него рисунок (компонент Image), а область прокрутки расположим между меню и строкой состояния. В результате большие рисунки будут прокручиваться уже не формой, а компонентом ScrollBox и строка состояния останется на своем месте, прижатой к нижнему краю формы.

Шаг 50. Выделите на форме компонент Image и временно удалите его в буфер (команда меню Edit / Cut). Теперь опустите на форму компонент ScrollBox, выбрав его из палитры компонентов. Назовите новый компонент ScrollBox и подгоните его под всю незанятую область формы, установив свойство Align в значение alClient (рисунок 8.46).


Рисунок 8.46. Свойство Align обеспечивает подгонку компонента под размеры контейнера

Шаг 51. А сейчас переключитесь на форму (так, чтобы компонент ScrollBox остался выделенным) и вставьте из буфера обмена компонент Image (команда меню Edit / Paste). Убедитесь, что он находится в левом верхнем углу области прокрутки.

Готово. Выполните компиляцию и запустите приложение, загрузите в него какой-нибудь рисунок из каталога C:\Program Files\Common Files\Borland Shared\Images\Splash\256Color. Увеличивая и уменьшая окно, понаблюдайте за тем, как появляются и исчезают полосы прокрутки между меню и строкой состояния (рисунок 8.47). Обратите внимание, что величина бегунков на полосах прокрутки зависит от соотношения видимой части и всего изображения. Это работает компонент ScrollBox. Правда, здорово! А самое главное - быстро и без единой строчки кода.


Рисунок 8.47. Программа для просмотра графических файлов теперь умеет прокручивать не уместившееся внутри окна изображение

8.4.3. Полосы прокрутки

Коль уж речь зашла о прокрутке, сделаем небольшое отступление и скажем пару слов о компоненте ScrollBar. Вы, наверное, еще раньше заметили его в палитре компонентов на вкладке Standard и сейчас не совсем понимаете, для чего он нужен (рисунок 8.48).


Рисунок 8.48. Компонент ScrollBar

ScrollBar - это отдельная полоса прокрутки без области прокрутки. Ее согласованная работа с другими компонентами обеспечивается программистом. Для этого в компоненте ScrollBar предусмотрено событие OnScroll, в ответ на которое и нужно выполнять необходимые действия. Должны вам сообщить, что компонент ScrollBar не имеет никакого отношения ни к форме, ни к компоненту ScrollBox. И вообще, он используется редко. Авторы этой книги будут вам признательны, если вы сообщите им о применении компонента ScrollBar в реальной задаче.

Следуя традиции данной книги, мы приводим табличное описание свойств компонента (таблица 8.14).

Свойство Описание
Kind Вид полосы прокрутки: горизонтальная или вертикальная.
LargeChange Величина "информативной страницы".
Min, Max Начальная и конечная виртуальные позиции на полосе прокрутки.
Position Позиция бегунка на полосе прокрутки.
SmallChange Величина "информативной строки".
OnChange Происходит при изменении значения свойства Position. Если значение свойства Position изменяется при перемещении пользователем бегунка, то событие OnChange происходит сразу после события OnScroll.
OnScroll Происходит при перемещении бегунка.
Таблица 8.14. Важнейшие свойства и события компонента ScrollBar

Рисунок 8.49 наглядно поясняет смысл свойств LargeChange и SmallChange.


Рисунок 8.49. Свойства LargeChange и SmallChange применяются при расчете величины прокрутки

Ну вот вы и разобрались с прокруткой. Уверены, что вам понравилось, как она реализована в среде Delphi. Действительно, компонентное программирование. Взял компонент ScrollBox, поместил на форму, набросал в него других компонентов - и готово. А теперь пора засучить рукава, ибо вас ждет самая увлекательная часть этой главы - проектирование панели инструментов.

8.5. Панель инструментов

Панель инструментов (toolbar) - это расположенная под главным меню панель с кнопками, раскрывающимися списками, переключателями и другими компонентами. Компоненты панели инструментов, как правило, дублируют часто используемые команды меню.

8.5.1. Панель

Для создания панели инструментов в среде Delphi существует компонент ToolBar, расположенный в палитре компонентов на вкладке Win32.


Рисунок 8.50. Компонент ToolBar

Шаг 52. Активизируйте форму и поместите на нее компонент ToolBar. Дайте новому компоненту имя ToolBar (рисунок 8.51).


Рисунок 8.51. Панель инструментов оказалась в области прокрутки

Внимательный читатель, наверное, уже обратил внимание, что компонент ToolBar попал в область прокрутки (внутрь компонента ScrollBox), и поэтому будет прокручиваться вместе с рисунком. Нам нужно вынести компонент ToolBar из области прокрутки и поместить его прямо в форму. Для этого воспользуемся окном Object TreeView.

Шаг 53. Перейдите к окну Object TreeView и найдите в нем компонент ToolBar. Захватите его с помощью мыши и перетащите к элементу PictureForm в этом же окне (рисунок 8.52).


Рисунок 8.52. Буксировка в окне Object TreeView позволяет быстро перенести компонент с одной панели на другую

Теперь компонент ToolBar находится именно там, где нужно (рисунок 8.53):


Рисунок 8.53. Панель инструментов вынесена за пределы области прокрутки

Между прочим, если вы сразу хотите поместить компонент на другой компонент, закрытый от вашего взора, выберите первый компонент в палитре компоненте и щелкните второй компонент в окне Object TreeView (рисунок 8.54):


Рисунок 8.54. Размещение компонента сразу внутри нужного компонента с помощью окна Object TreeView

Шаг 54. В окне свойств установите свойство AutoSize в значение True. После этого панель инструментов будет автоматически подгонять свои размеры в зависимости от размеров и количества размещенных на ней компонентов.

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

Свойство Описание
AutoSize Если равно значению True, то панель автоматически изменяет свою высоту в зависимости от размеров размещенных на ней компонентов.
BorderWidth Величина отступа от границ компонента до кнопок.
ButtonWidth, ButtonHeight Ширина и высота кнопок на панели инструментов.
Customizable Если равно значению True, то пользователь во время работы программы имеет возможность управлять расположением кнопок на панели инструментов. Удерживая клавишу Shift пользователь может захватить кнопку и перенести ее на нужное место, а двойным щелчком панели инструментов (но не ее кнопок!), пользователь может вызвать специальное окно настройки.
DisabledImages Список значков, отображаемых на недоступных кнопках. Свойство DisabledImages используется совместно со свойством ImageIndex компонента ToolButton.
DockSite Определяет, используется ли панель инструментов для стыковки других компонентов.
EdgeBorders Вложенные свойства ebLeft, ebTop, ebRight и ebBottom определяют видимость соответственно левой, верхней, правой и нижней сторон рельефной рамки.
EdgeInnter Внутренний скос рельефной рамки: esNone - скос отсутствует, esLowered - скос внутрь, esRaised - скос наружу.
EdgeOuter Внешний скос рельефной рамки: esNone - скос отсутствует, esLowered - скос внутрь, esRaised - скос наружу.
Flat Если равно значению True, то все кнопки, находящиеся на панели инструментов, не имеют рельефных границ. Рельефные границы появляются при наведении указателя мыши на кнопку.
HideClippedButtons Если равно значению True, то кнопки, не уместившиеся на панели целиком, не показываются вообще.
HotImages Список значков, которые отображаются на кнопках при наведении на них указателя мыши. Свойство HotImages используется совместно со свойством ImageIndex компонента ToolButton.
Images Список значков, которые отображаются на кнопках. Свойство Images используется совместно со свойством ImageIndex компонента ToolButton.
Indent Отступ от края панели до первой кнопки.
List Если равно значению True, то надписи на кнопках отображаются справа от значков. Иначе надписи отображаются под значками.
Menu Ссылка на компонент MainMenu. Установка значения этого свойства приводит к тому, что панель инстурментов выглядит как строка главного меню.
ShowCaptions Определяет, отображаются ли надписи на кнопках. Если установлено значение False, то на кнопках отображаются только значки.
ShowHint Разрешает (значение True) или запрещает (значение False) показ высплывающих подсказок для кнопок панели инструментов.
Transparent Если равно значению True, то фон панели инструментов является прозрачным.
Wrapable Включает автоматический перенос невместившихся кнопок панели инструментов на новую строку. Если равно значению False, то перенос кнопок регулируется с помощью свойства Wrap компонента ToolButton.
OnAdvancedCustomDraw Происходит до и после рисования панели инструментов на экране.
OnAdvancedCustomDrawButton Происходит до и после рисования каждой кнопки панели инструментов на экране.
OnCustomDraw Происходит при рисовании панели инструментов на экране.
OnCustomDrawButton Происходит при рисовании каждой кнопки панели инструментов на экране.
OnCustomizeAdded Происходит, когда пользователь добавляет кнопку на панель с помощью окна настройки панели инструментов.
OnCustomizeCanDelete Происходит, когда пользователь пытается убрать кнопку с помощи окна настройки панели инструментов.
OnCustomizeCanInsert Происходит, когда пользователь пытается добавить кнопку с помощи окна настройки панели инструментов.
OnCustomized Происходит по окончании любого изменения на панели инструментов.
OnCustomizeDelete Происходит, когда пользователь убирает кнопку с панели при помощи окна настройки панели инструментов.
OnCustomizeNewButton Используется для формирования списка кнопок, доступных для добавления на панель инструментов. В этом обработчике, как правило, динамически создаются и возвращаются через параметр Button объекты класса TToolButton (или производных классов).
OnCustomizeReset Происходит при нажатии пользователем кнопки Reset в окне настройки панели инструментов.
OnCustomizing Происходит при вызове пользователем окна настройки панели инструментов и при нажатии кнопки Reset в этом окне.
OnGetSiteInfo Происходит, когда у компонента запрашивается место для стыковки.
Таблица 8.15. Важнейшие свойства и события компонента ToolBar

8.5.2. Кнопки

Кнопки панели инструментов представлены компонентами ToolButton. Не пытайтесь найти компонент ToolButton в палитре компонентов. Его там нет, поскольку он не является самостоятельным компонентом, а создается и управляется из компонента ToolBar.

Шаг 55. Для добавления кнопки вызовите контекстное меню компонента ToolBar и выберите команду New Button (рисунок 8.55).


Рисунок 8.55. Создание кнопки на панели инструментов с помощью команды New Button контекстного меню

На панели инструментов появится кнопка, свойства которой будут тут же показаны в окне свойств. Дайте компоненту имя OpenToolButton.

Шаг 56. Аналогичным образом создайте еще четыре кнопки с программными идентификаторами SaveAsToolButton, HalfSizeToolButton, NormalSizeToolButton и DoubleSizeToolButton (рисунок 8.56).


Рисунок 8.56. Все необходимые кнопки созданы, но для них еще не заданы значки

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

Свойство Описание
Action Команда, хранящаяся в компоненте ActionList и выполняемая при нажатии кнопки (см. параграф 8.6).
AllowAllUp Разрешает всем кнопкам одной группы находиться в отжатом состоянии.
AutoSize Включает режим автоматического подбора размеров кнопки в зависимости от размеров значка и надписи.
Caption Надпись на кнопке.
Down Если равно True, то кнопка рисуется нажатой.
DropDownMenu Выпадающее меню, которое появляется при нажатии кнопки. Это свойство используется, если свойство Style содержит значение tbdDropDown.
Grouped Определяет, принадлежит ли кнопка группе взаимоисключающих переключателей. Сгруппированными считаются расположенные рядом кнопки со значением True в свойстве Grouped и значением tbdCheck в свойстве Style.
ImageIndex Номер значка в списке Images компонента ToolBar.
Indeterminate Если равно True, то кнопка имеет неопределенное состояние и рисуется поблекшей.
Marked Если равно значению True, то кнопка подсвечивается цветом выделенных элементов (стандартно - синим цветом).
MenuItem Пункт меню, с которым ассоциирована кнопка. При установке этого свойства из соответствующего пункта меню копируются значения наиболее важных свойств и событий, например Caption, ImageIndex, Enabled, Hint, OnClick.
Style Тип кнопки: tbsButton - обычная кнопка, tbsCheck - кнопка-переключатель, tbsDivider - разделитель в виде вертикальной черты, tbsDropDown - выпадающий список, tbsSeparator - разделитель в виде вертикальной черты или пробела в зависимости от значения свойства Flat компонента ToolBar.
Wrap Обеспечивает перенос последующих кнопок на новую строку.
Таблица 8.16. Важнейшие свойства компонента ToolButton

8.5.3. Значки на кнопках

Главным атрибутом кнопки является значок. Он определяется значением свойства ImageIndex (номер значка в списке Images компонента ToolBar). Установим на кнопках значки, воспользовавшись ранее созданным списком ImageList.

Шаг 57. Выделите на форме компонент ToolBar, перейдите к окну свойств и установите свойству Images значение ImageList (рисунок 8.57).


Рисунок 8.57. На кнопках панели инструментов появились значки

Вот здорово! На всех кнопках появились разные значки, хотя мы не устанавливали свойство ImageIndex ни в одной из кнопок. Это произошло потому, что компонент ToolBar сделал это за нас, назначив каждой кнопке номер значка в соответствии с очередностью добавления кнопок на панель.

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

8.5.4. Надписи на кнопках

Шаг 58. Кнопка может содержать надпись рядом со значком. Текст надписи устанавливается в свойстве Caption. Сначала он не виден и, чтобы его показать, задайте в компоненте ToolBar свойству ShowCaptions значение True (рисунок 8.58).


Рисунок 8.58. Кнопки панели инструментов могут содержать надписи (свойство ShowCaptions равно True)

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

Шаг 59. Подправим надписи. В окне свойств переключите свойство List в значение True (рисунок 8.59).


Рисунок 8.59. Надписи на кнопках располагаются справа от значков (свойство List равно True)

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

Шаг 60. Теперь измените надписи на кнопках (свойство Caption), чтобы получить панель инструментов, показанную на рисунке 8.60.


Рисунок 8.60. Кнопкам заданы правильные надписи

Шаг 61. Чтобы панель инструментов выглядела лучше, подгоним размеры кнопок под размеры надписей. Для этого воспользуемся свойством AutoSize, которое есть у каждой кнопки. При установке этого свойства применим технику группового редактирования компонентов.

Шаг 62. Выделите щелчком мыши первую кнопку, а затем, удерживая клавишу Shift, выделите щелчками мыши все остальные кнопки. В окне свойств произойдут следующие изменения:

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

Перейдите к окну свойств и установите свойство AutoSize в значение True (рисунок 8.61).


Рисунок 8.61. Применение техники группового редактирования при установке во всех кнопках свойства AutoSize в значение True

Теперь выполните компиляцию и запустите программу. Результат представлен на рисунке 8.62.


Рисунок 8.62. Программа для просмотра графических файлов имеет панель инструментов, однако кнопки пока еще не работают

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

Шаг 63. Закройте приложение и вы вернетесь в среду Delphi. Выделите на форме компонент ToolBar и переключите его свойство Flat в значение True. Теперь снова запустите программу и полюбуйтесь на результат (рисунок 8.63).


Рисунок 8.63. Кнопки панели инструментов получили современный «плоский» вид

Кнопки приобретают рельефный вид только при наведении на них указателя мыши.

8.5.5. Разделительные линии

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

Шаг 64. В нашем примере логично отделить группу кнопок, отвечающих за размеры рисунка (Half Size, Normal Size и Double Size), от кнопок Open и Save As. Для этого вызовите контекстное меню панели инструментов и выберите команду New Separator (рисунок 8.64).


Рисунок 8.64. Создание разделительной линии на панели инструментов с помощью команды New Separator контекстного меню

На панель инструментов будет добавлен новый компонент, имеющий вид вертикальной черты. С помощью мыши отбуксируйте его на место между кнопками Save As и Half Size (рисунок 8.65):


Рисунок 8.65. Буксировка разделительной линии на место между кнопками Save As и Half Size

Выполните компиляцию и запустите программу. Результат представлен на рисунке 8.66.


Рисунок 8.66. Кнопки на панели инструментов внешне сгруппированы по назначению

Напоследок заметим, что разделительная линия представлена обычным компонентом ToolButton. То, какой вид имеет этот компонент (кнопка или разделительная линия), определяется свойством Style. Это свойство имеет много значений, которые перечислены в таблице 8.16.

8.5.6. Кнопки-переключатели

Кнопки панели инструментов могут работать как переключатели, «залипая» при нажатии. Для того, чтобы кнопка была переключателем, ее свойство Style должно содержать значение tbsCheck. Состояние кнопки (нажата она или нет) определяется значением свойства Down.

Кнопки-переключатели могут работать согласовано, т.е. включение одной из них означает выключение остальных. Именно так должны работать кнопки выбора масштаба отображения рисунка. Согласованная работа кнопок обеспечивается не так, как согласованная работа пунктов меню. Кнопки панели инструментов не имеют свойства GroupIndex, они группируются по другому принципу. Сгруппированными считаются расположенные рядом кнопки, у которых свойство Grouped равно значению True и свойство Style равно значению tbsCheck.

Шаг 65. Сгруппируйте кнопки Half Size, Normal Size и Double Size. Они уже находятся рядом друг с другом, поэтому просто установите их свойства Grouped и Style как показано на рисунке 8.67.


Рисунок 8.67. Кнопки Half Size, Normal Size и Double Size сгруппированы в трехпозиционный переключатель

У кнопки Normal Size установите свойство Down в значение True, предварительно убрав выделение кнопок Half Size и Normal Size (рисунок 8.68).


Рисунок 8.68. Начальное положение трехпозиционного переключателя - Normal Size

Выполните компиляцию и запустите программу. Проверьте, что кнопки Half Size, Normal Size и Double Size работают как трехпозиционный переключатель (рисунок 8.69).


Рисунок 8.69. Кнопки-переключатели в работе

Шаг 66. Завершая дизайн панели инструментов, сделайте кнопки Save As, Half Size, Normal Size и Double Size недоступными, установив у них свойство Enabled в значение False (рисунок 8.70). Эти кнопки будут оставаться недоступными, пока пользователь не откроет какой-нибудь рисунок.


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

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

8.5.7. Обработка нажатий кнопок

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

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

procedure TPictureForm.HalfSizeMenuItemClick(Sender: TObject);
begin
  HalfSizeToolButton.Down := True; // кнопка согласуется с пунктом меню
  ...
end;

procedure TPictureForm.NormalSizeMenuItemClick(Sender: TObject);
begin
  NormalSizeToolButton.Down := True; // кнопка согласуется с пунктом меню
  ...
end;

procedure TPictureForm.DoubleSizeMenuItemClick(Sender: TObject);
begin
  DoubleSizeToolButton.Down := True; // кнопка согласуется с пунктом меню
  ...
end;

Шаг 68. Чтобы кнопки становились доступными или недоступными в зависимости от того, открыт рисунок или нет, подправьте метод EnableCommands:

procedure TPictureForm.EnableCommands(Enable: Boolean);
begin
  ...
  SaveAsToolButton.Enabled := Enable;
  HalfSizeToolButton.Enabled := Enable;
  NormalSizeToolButton.Enabled := Enable;
  DoubleSizeToolButton.Enabled := Enable;
end;

Вроде бы все. После компиляции и запуска программы вы получите работающую панель инструментов (рисунок 8.71). Нажмите кнопку Open и выберите рисунок. Когда рисунок откроется, все остальные кнопки станут доступными. Понажимайте кнопки, отвечающие за масштаб, и убедитесь, что они работают согласовано с командами меню.


Рисунок 8.71. Программа для просмотра графических файлов получила работоспособную панель инструментов

Панель инструментов работоспособна, но в ней не хватает одной мелочи - подсказок к кнопкам.

8.5.8. Подсказки к кнопкам

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


Рисунок 8.72. Всплывающая подсказка и подсказка в строке состояния

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

<всплывающая подсказка>/<подсказка в строке состояния>

Всплывающая подсказка отделяется вертикальной чертой от подсказки для строки состояния.

Если в программе есть строка состояния, то при попадании указателя мыши на визуальный компонент в ней автоматически отображается поясняющий текст, записанный справа от символа вертикальной черты. Это легко проверить. Впишите в свойстве Hint кнопки Open текст "Open an existing file.../Open an existing file...". После запуска программы вы обнаружите, что подсказка для кнопки работает точно так же, как и подсказка к пункту меню.

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

Шаг 69. В каждом визуальном компоненте существует булевское свойство ShowHint, определяющее, появляется подсказка, или нет (рисунок 8.73). Его значение может устанавливаться напрямую, а может копироваться из содержащего компонента (например, из формы). Копирование значения происходит тогда, когда вспомогательное свойство ParentShowHint установлено в True. Таким образом, появлением подсказок можно управлять из содержащего компонента. Этой возможностью мы воспользуемся в нашей задаче. Принимая во внимание, что во всех компонентах свойство ParentShowHint изначально равно True, просто установите в компоненте ToolBar (владелец кнопок) свойство ShowHint в значение True. В результате во всех кнопках свойство ShowHint тоже получит значение True и это заставит их отображать свои подсказки.


Рисунок 8.73. Свойство ShowHint управляет отображением всплывающих подсказок

Шаг 70. Впишите для каждой кнопки текст подсказки (свойство Hint). Как вы знаете, свойство Hint может содержать сразу две подсказки: всплывающую подсказку и подсказку в строке состояния (они разделяются символом вертикальной черты). Если вторая кажется вам лишней, просто не набирайте ее, но символ вертикальной черты поставьте (рисунок 8.74).


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

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

Давайте придадим приложению завершенность и доработаем обработчики команд меню View / Toolbar и View / Status bar.

8.5.9. Управление видимостью панели кнопок

Шаг 71. Обработка команд View / Toolbar и View / Status bar выполняется на удивление просто - у соответствующей панели изменяется значение булевского свойства Visible на противоположное. При этом панель исчезает или появляется в окне вместе с изменением значения свойства. Проще не придумаешь! Окончательный вариант обработчиков будет следующим:

procedure TPictureForm.ToolBarMenuItemClick(Sender: TObject);
begin
  ToolBar.Visible := not ToolBar.Visible;
  ToolBarMenuItem.Checked := not ToolBarMenuItem.Checked;
end;

procedure TPictureForm.StatusBarMenuItemClick(Sender: TObject);
begin
  StatusBar.Visible := not StatusBar.Visible;
  StatusBarMenuItem.Checked := not StatusBarMenuItem.Checked;
end;

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

8.6. Список команд

Часто одна и та же команда дублируется в разных местах пользовательского интерфейса: и в главном меню, и в контекстном меню, и на панели инструментов. Например, команды управления масштабом рисунка присутствуют во всех перечисленных местах программы Picture Viewer. Это очень удобно для пользователя, но добавляет работы программисту, поскольку изменение команды в одном месте требует таких же изменений во всех других местах. На помощь приходит компонент ActionList, который централизованно управляет всеми командами пользовательского интерфейса. Рассмотрим его использование.

8.6.1. Создание списка команд

Отыщите в палитре компонентов на вкладке Standart компонент ActionList и добавьте его в форму (рисунок 8.75).


Рисунок 8.75. Компонент ActionList

Дайте компоненту имя ActionList (рисунок 8.76).


Рисунок 8.76. Компонент ActionList на форме

Ознакомьтесь со свойствами компонента ActionList в таблице 8.17.

Свойство Описание
Images Список значков, отображаемых в компонентах, использующих команды. Свойство Images используется совместно со свойством ImageIndex компонентов Action.
State Позволяет временно запретить выполнение всех команд списка. Возможные значения:
asNormal - команды работают в обычном режиме и доступность каждой команды определяется значением свойства Enabled в соответствующем компоненте Action;
asSuspended - все команды недоступны, но при этом не затрагиваются свойства Enabled в компонентах Action;
asSuspendedEnabled - все команды недоступны, но имеют обычный вид благодаря тому, что свойство Enabled каждого компонента Action устанавливается в значение True.
OnChange Происходит при модификации команд в списке. Однако не происходит при создании и уничтожении команд.
OnExecute Происходит при выполнении команды.
OnStateChange Происходит при изменении свойства State. Следует учитывать, что из-за ошибки в библиотеке VCL событие не происходит при переводе свойства State в значение asSuspended.
OnUpdate Происходит при выполнении команды и периодически во время простоя программы. Позволяет отслеживать и изменять состояния команд.
Таблица 8.17. Важнейшие свойства и события компонента ActionList

Шаг 72. Создание списка команд начнем с команды Open. В контекстном меню компонента ActionList выберите команду Action List Editor… (рисунок 8.77).


Рисунок 8.77. Вызов списка команд из контекстного меню компонента ActionList

Перед вами откроется окно команд (рисунок 8.78).


Рисунок 8.78. Окно команд компонента ActionList

Окно команд работает в паре с окном свойств. Создание и удаление команд осуществляется в окне команд, а свойства отдельно взятой команды устанавливаются в окне свойств (рисунок 8.78).

Шаг 73. Щелчком на первой кнопке добавьте в список новую команду. Свойства команды немедленно появятся в окне свойств.

8.6.2. Команды

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

Свойство Описание
AutoCheck Если равно значению True, то выполнение команды (вызов метода Execute) автоматически приводит к изменению значения свойства Checked на противоположное. Если равно значению False, то изменением состояния свойства Checked управляет сам программист.
Caption Заголовок команды.
Category Категория команды.
Checked Если равно значению True, то команда считается выбранной. В этом случае связанные с командой пунткы меню содержат метку, а кнопки имеют вдавленный вид.
Enabled Определяет, доступна ли команда пользователю.
GroupIndex Команды с одинаковым положительным значением GroupIndex согласовано переключают свойство Checked - установка у одной команды свойства Checked в значение True приводит к устновке его в значение False в других командах.
ImageIndex Номер значка в списке Images компонента ActionList. Значок отображается рядом с текстом пункта меню (см. параграф 8.1.12). Отрицательное значение свойства ImageIndex говорит о том, что для пункта меню значок не задан. Свойство ImageIndex имеет приоритет над свойством Bitmap.
SecondaryShortCuts Дополнительные комбинации клавиш.
ShortCut Комбинация клавиш для выполнения команды.
Visible Определяет, видна ли пользователю команда.
OnExecute Происходит при выполнении команды.
OnHint Происходит в момент появления всплывающей подсказки.
OnUpdate Происходит при выполнении команды и периодически во время простоя программы. Позволяет отслеживать и изменять состояние команды.
Таблица 8.18. Важнейшие свойства и события компонента Action

Шаг 74. Перейдем к настройке команды, созданной на предыдущем шаге. Дайте команде имя OpenAction, в свойстве Caption впишите текст Open... и в свойстве ShortCut выберите значение Ctrl+O (рисунок 8.79).


Рисунок 8.79. Для команды Open задана комбинация клавиш Ctrl+O

Команда может иметь значок. Он определяется значением свойства ImageIndex (номер значка в списке Images компонента ActionList). Прежде чем выбрать значение для свойства ImageIndex, нужно указать список значков компоненту ActionList.

Шаг 75. Выделите на форме компонент ActionList и перейдите к окну свойств. Выберите в свойстве Images значение ImageList (рисунок 8.80).


Рисунок 8.80. Для компонента ActionList задается компонент ImageList со списком значков

Шаг 76. А теперь установим компоненту OpenAction соответствующий значок. Перейдите к окну команд и выделите команду OpenAction. Затем в окне свойств отыщите свойство ImageIndex и выберите значок с номером 0 (рисунок 8.81).


Рисунок 8.81. Для команды Open задан значок с индексом 0

Шаг 77. С командой OpenAction мы разобрались, теперь самостоятельно создайте команды SaveAsAction, CloseAction, ExitAction, ToolBarAction, StatusBarAction, HalfSizeAction, NormalSizeAction, DoubleSizeAction (рисунок 8.82) с соответствующими заголовками (свойство Caption).


Рисунок 8.82. Полный список команд для программы

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

8.6.3. Привязка команд

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

Шаг 78. Привязку команд начнем с кнопки Open на панели инструментов. Выделите ее и в раскрывающемся списке свойства Action выберите значение OpenAction (рисунок 8.83).


Рисунок 8.83. К кнопке OpenToolButton привязывается команда OpenAction

Обратите внимание, что надпись на кнопке изменилась. Это результат копирования значения свойства Caption из компонента OpenAction в компонент OpenToolButton.

Шаг 79. Аналогичным образом привяжите команду OpenAction к пункту Open… главного меню (рисунок 8.84).


Рисунок 8.84. К пункту меню OpenMenuItem привязывается команда OpenAction

Проверим, что у нас получилось. Выполните компиляцию и запустите программу (рисунок 8.85).


Рисунок 8.85. При запуске программы команда Open оказалась недоступна

Странно: и кнопка Open… на панели инструментов, и пункт Open… в главном меню недоступны. Это объясняется отсутствием у компонента OpenAction обработчика события OnExecute. Им сейчас и займемся.

8.6.4. Реакция на команды

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

Шаг 80. Определим в компоненте OpenAction обработчик события OnExecute. Обратитесь к контекстному меню компонента ActionList и вызовите окно команд. В этом окне выберите команду OpenAction, после чего в окне свойств выберите вкладку Events. Теперь сделайте двойной щелчок мыши на значении события OnExecute. Среда Delphi создаст заготовку для будущего обработчика:

procedure TPictureForm.OpenActionExecute(Sender: TObject);
begin

end;

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

procedure TPictureForm.OpenMenuItemClick(Sender: TObject);
begin
end;
...
procedure TPictureForm.OpenActionExecute(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    Image.Picture.LoadFromFile(OpenDialog.FileName);
    EnableCommands(True);
    NormalSizeAction.Execute; // Вместо NormalSizeMenuItem.Click;
  end;
  UpdateStatusBar;
end;

Сохраните проект; пустой метод OpenMenuItemClick будет автоматически удален из исходного текста.

Обратите внимание, что компонент Action автоматически подменяет обработчики OnClick в связанных с ним компонентах. Поэтому если вы перейдете к окну свойств и посмотрите на событие OnClick в компоненте OpenMenuItem, то обнаружите там метод OpenActionExecute (обработчик события OnExecute компонента OpenAction).

Выполните компиляцию и запустите программу. Команда Open снова доступна пользователю (рисунок 8.86).


Рисунок 8.86. Команда Open опять доступна пользователю (компонент OpenAction обрабатывает событие OnExecute)

Закрыв программу, вернитесь к проекту в среде Delphi, чтобы продолжить настройку оставшихся команд.

Шаг 81. Обойдите все пункты меню (не забудьте про контекстное меню) и кнопки панели инструментов и установите в каждом из них свойство Action в соответствующее значение. Попутно значения некоторых других свойств тоже изменятся, например свойство Enabled получит значение True. Пусть вас это не беспокоит, так и должно быть (рисунок 8.87).


Рисунок 8.87. Все пункты меню и кнопки панели инструментов привязаны к командам

Восстановим правильную логику работы кнопок и пунктов меню.

Шаг 82. Сделайте недоступной команду SaveAsAction, установив ее свойство Enabled в значение False. Одновременно кнопка и пункт меню Save As... станут недоступными (рисунок 8.88).


Рисунок 8.88. Команда Save As отключена до тех пор, пока пользователь не откроет какой-нибудь графический файл

Шаг 83. Создайте для компонента SaveAsAction обработчик события OnExecute и перенесите код метода SaveAsMenuItemClick в только что созданный метод SaveAsActionExecute:

procedure TPictureForm.SaveAsMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.SaveAsActionExecute(Sender: TObject);
begin
  if SaveDialog.Execute then
    Image.Picture.SaveToFile(SaveDialog.FileName);
end;

Шаг 84. Доработку команды SaveAsAction мы закончили и теперь по аналогии доработаем команды ExitAction и CloseAction:

procedure TPictureForm.ExitMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.CloseMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.ExitActionExecute(Sender: TObject);
begin
  Close;
end;

procedure TPictureForm.CloseActionExecute(Sender: TObject);
begin
  with Image do
  begin
    Picture := nil;
    Width := 0;
    Height := 0;
  end;
  NormalSizeAction.Execute; // Вместо NormalSizeMenuItem.Click; 
  EnableCommands(False);
  UpdateStatusBar;
end;

Шаг 85. Теперь настало время команд ToolBarAction и StatusBarAction:

procedure TPictureForm.ToolBarMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.StatusBarMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.ToolBarActionExecute(Sender: TObject);
begin
  ToolBarAction.Checked := not ToolBarAction.Checked;
  ToolBar.Visible := not ToolBar.Visible;
end;

procedure TPictureForm.StatusBarActionExecute(Sender: TObject);
begin
  StatusBarAction.Checked := not StatusBarAction.Checked;
  StatusBar.Visible := not StatusBar.Visible;
end;

Теперь восстановим логику работы команд, отвечающих за масштаб рисунка.

Шаг 86. Вернитесь к окну редактирования списка команд и выделите команду HalfSizeAction. После этого нажмите клавишу Ctrl и, удерживая ее, выделите команды NormalSizeAction и DoubleSizeAction. Перейдите к окну свойств и установите свойство GroupIndex в значение 1 (рисунок 8.89).


Рисунок 8.89. Группировка команд с помощью свойства GroupIndex

Шаг 87. Свойство Checked компонента NormalSizeAction установите в значение True - при запуске программы рисунок не масштабируется (рисунок 8.90).


Рисунок 8.90. Начальное значение для переключателя масштаба - Normal Size

Шаг 88. Установите свойство Enabled команд HalfSizeAction, NormalSizeAction и DoubleSizeAction в значение False - при запуске программы рисунок еще не загружен, поэтому команды переключения масштаба должны быть недоступны (рисунок 8.91).


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

Шаг 89. Теперь создадим обработчики команд HalfSizeAction, NormalSizeAction и DoubleSizeAction. Для каждой команды определите обработчик события OnExecute и перенесите код из уже имеющихся методов:

procedure TPictureForm.HalfSizeMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.NormalSizeMenuItemClick(Sender: TObject);
begin
end;

procedure TPictureForm.DoubleSizeMenuItemClick(Sender: TObject);
begin
end;

...

procedure TPictureForm.HalfSizeActionExecute(Sender: TObject);
begin
  HalfSizeToolButton.Down := True; 
  HalfSizeMenuItem.Checked := True;
  HalfSizePopupItem.Checked := True;
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width div 2;
    Height := Picture.Height div 2;
    Stretch := True;
  end;
end;

procedure TPictureForm.NormalSizeActionExecute(Sender: TObject);
begin
  NormalSizeToolButton.Down := True; 
  NormalSizeMenuItem.Checked := True;
  NormalSizePopupItem.Checked := True;
  Image.AutoSize := True; 
end;

procedure TPictureForm.DoubleSizeActionExecute(Sender: TObject);
begin
  DoubleSizeToolButton.Down := True; 
  DoubleSizeMenuItem.Checked := True;
  DoubleSizePopupItem.Checked := True;
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width * 2;
    Height := Picture.Height * 2;
    Stretch := True;
  end;
end;

Шаг 90. Обработчики можно упростить за счет управления состоянием пунктов меню и кнопок через компоненты Action, т.е. первые три оператора каждого обработчика заменяются на один оператор:

procedure TPictureForm.HalfSizeActionExecute(Sender: TObject);
begin
  HalfSizeAction.Checked := True; 
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width div 2;
    Height := Picture.Height div 2;
    Stretch := True;
  end;
end;

procedure TPictureForm.NormalSizeActionExecute(Sender: TObject);
begin
  NormalSizeAction.Checked := True; 
  Image.AutoSize := True; 
end;

procedure TPictureForm.DoubleSizeActionExecute(Sender: TObject);
begin
  DoubleSizeAction.Checked := True; 
  with Image do
  begin
    AutoSize := False;
    Width := Picture.Width * 2; 
    Height := Picture.Height * 2;
    Stretch := True;
  end;
end;

А теперь воспользуемся свойством AutoCheck компонентов Action, чтобы избавиться от необходимости программно переключать метку в пунктах Toolbar и Status bar главного меню. Когда свойство AutoCheck равно True, то при выполнении команды свойство Checked автоматически меняет свое значение на противоположное. Это отражается на связанных с командой пунктах меню и кнопках-переключателях.

Шаг 91. У команд ToolBarAction и StatusBarAction установите свойства AutoCheck и Checked в значение True.

Шаг 92. Подправьте методы ToolBarActionExecute и StatusBarActionExecute:

procedure TPictureForm.ToolBarActionExecute(Sender: TObject);
begin
  ToolBar.Visible := ToolBarAction.Checked;
end;

procedure TPictureForm.StatusBarActionExecute(Sender: TObject);
begin
  StatusBar.Visible := StatusBarAction.Checked;
end;

8.6.5. Управление состоянием команд

Компонент ActionList имеет удобный механизм управления состоянием команд (например, доступна/недоступна). После выполнения очередной команды и во время простоя программы в компоненте возникает событие OnUpdate. Реакцией на это событие может быть изменение состояния отдельных команд, например переключение в них свойства Enabled. Напомним, что сейчас для этих целей используется метод EnableCommand, вызываемый при открытии и закрытии файла. Избавимся от него.

Шаг 93. Выделите на форме компонент ActionList, и в окне свойств на вкладке Events отыщите событие OnExecute. Двойным щелчком мыши создайте обработчик:

procedure TPictureForm.ActionListUpdate(Action: TBasicAction;
  var Handled: Boolean);
var
  NonEmpty: Boolean;
begin
  NonEmpty := Image.Picture.Graphic <> nil;
  SaveAsAction.Enabled := NonEmpty;
  CloseMenuItem.Enabled := NonEmpty;
  HalfSizeAction.Enabled := NonEmpty;
  NormalSizeAction.Enabled := NonEmpty;
  DoubleSizeAction.Enabled := NonEmpty;
  Handled := True;
end;

Шаг 94. Удалите метод EnableCommands и обращения к нему из методов OpenActionExecute и CloseActionExecute. Вот, что должно получиться:

procedure TPictureForm.OpenActionExecute(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    Image.Picture.LoadFromFile(OpenDialog.FileName);
    NormalSizeAction.Execute;
  end;
  UpdateStatusBar;
end;

procedure TPictureForm.CloseActionExecute(Sender: TObject);
begin
  with Image do
  begin
    Picture.Graphic := nil;
    Width := 0;
    Height := 0;
  end;
  NormalSizeAction.Execute;
  UpdateStatusBar;
end;

Шаг 95. Программа полностью готова, выполните компиляцию и запустите ее. Наслаждайтесь результатами своего труда, просматривая рисунки на жестком диске (рисунок 8.92).


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

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

8.7. Итоги

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

А.Н. Вальвачев
К.А. Сурков
Д.А. Сурков
Ю.М. Четырько


Страница сайта http://185.71.96.61
Оригинал находится по адресу http://185.71.96.61/home.asp?artId=7582