Кирилл Сурков Александр Вальвачев, Дмитрий Сурков, Юрий Четырько дата публикации 12-05-2006 07:16 урок из цикла:
Глава 9. Окна диалога. Часть I
Все мы любим иногда поболтать. Это человеческое свойство передалось программам, и они частенько у вас что-то спрашивают, а вы им что-то отвечаете, иногда невпопад. "Беседа", правда, идет текстом, а не голосом. Так вот, разговор между программой и пользователем называется диалогом. Организация диалога — важнейшая часть любой программы. Ваша прямая обязанность сделать этот диалог приятным. По форме диалог прост — появляется окно с некоторым сообщением, полем для ввода вашего ответа и кнопкой OK. Вы внимательно читаете сообщение, набираете строку-ответ и нажимаете кнопку OK. Вот и все. Создатели среды Delphi предусмотрели все возможные типы диалогов и создали для вас ряд великолепных "домашних заготовок".
В основе диалога между пользователем и компьютером лежит окно диалога (dialog box) — форма, содержащая компоненты для ввода данных: кнопки, текстовые поля, флажки, переключатели, списки и др. С помощью этих компонентов пользователь просматривает и вводит данные. В среде Delphi окно диалога создается на основе обычной формы.
Окна диалога могут работать в одном из двух режимов, монопольном (иногда говорят модальном, от англ. modal) и немонопольном (немодальном, от англ. modeless).
Монопольное окно диалога не дает пользователю возможности переключиться на другие окна программы до тех пор, пока работа с ним не будет завершена. Сразу заметим, что это не мешает пользователю переключаться на другие программы, например, с помощью панели задач Windows или нажатием комбинации клавиш Alt+Tab. Большинство окон диалога работает в монопольном режиме.
Немонопольные окна диалога предоставляют пользователю свободу выбора, позволяя вводить данные сразу в нескольких окнах.
Простейшим примером окна диалога является окно About ("О программе"). Как правило, оно открывается по команде меню Help | About... , работает в монопольном режиме и служит лишь для информирования пользователя. В предыдущей главе мы рассматривали программу PicView, там как раз не достает окна About. Исправим это упущение и на практике познакомимся с созданием простейших окон диалога.
Шаг 1. Запустите среду Delphi и откройте проект PictureViewer. Добавьте в главное меню пункт Help (программный идентификатор HelpMenuItem) с командой About... (программный идентификатор AboutMenuItem). По команде About... (рисунок 9.1) будет вызываться окно диалога About, которое мы дальше разработаем.
Рисунок 9.1. Пункт меню для вызова окна About
Шаг 2. Добавьте в проект новую форму, переименуйте ее в AboutForm и сохраните модуль под именем About.pas. Придайте форме нужные размеры и установите ее заголовок (свойство Caption) в значение About Picture Viewer. Далее сделаем из этой формы окно диалога.
Шаг 3. Обычная форма имеет много "фитюлек", которые совсем не нужны окну диалога, например раздвижную границу, меню управления окном, кнопки сворачивания и разворачивания окна. Чтобы их убрать, установите свойство BorderStyle в значение bsDialog (рисунок 9.2).
Рисунок 9.2. Превращение формы в окно диалога
Результат сделанного изменения проявится только во время работы программы и будет выражаться в следующем:
- у пользователя не будет возможности изменить размеры формы;
- у формы не будет кнопок сворачивания и разворачивания;
- в управляющем меню будут лишь два пункта: Move и Close.
Шаг 4. Большинство монопольных окон диалога появляются в центре экрана. За это у формы отвечает свойство Position. Изначально оно равно poDesigned — форма появляется точно в том же месте, где она находится во время разработки. Чтобы центрировать форму на экране, установите свойство Position в значение poScreenCenter. Заметим, что другие значения свойства Position позволяют центрировать форму относительно главной формы, относительно формы-владельца или вообще не центрировать (см. параграф 7.3.4).
С формой разобрались, займемся компонентами. Окно About обычно содержит красивый рисунок, название программного продукта, замечания по поводу авторских прав или что-нибудь в этом роде и, конечно же, кнопку OK. Начнем с того, что добавим в форму кнопку.
Разрабатывая окно диалога, прежде всего необходимо обеспечить для пользователя возможность завершения диалога по окончание ввода данных. Вот тут как раз и нужны кнопки (buttons). В простейшем окне диалога, каким является окно About, достаточно всего одной кнопки (она обычно называется OK или Close). В более сложных окнах может понадобиться несколько кнопок. Например, в том случае, когда окно диалога принимает от пользователя данные, в него помещают кнопки OK и Cancel, которые позволяют пользователю подтвердить или отменить результат диалога.
Обычная кнопка создается с помощью компонента Button, расположенного в палитре компонентов на вкладке Standard.
Рисунок 9.3. Компонент Button
Характерные свойства этого компонента кратко описаны в таблице 9.1.
Свойство | Описание |
Action | Команда, хранящаяся в компоненте ActionList и выполняемая при нажатии кнопки. |
Cancel | Если равно значению True, то кнопка срабатывает при нажатии клавиши Esc. |
Caption | Текст на кнопке. |
Default | Если равно значению True, то кнопка срабатывает при нажатии клавиши Enter. Исключением является ситуация, когда в окне диалога активна другая кнопка — в этом случае срабатывает она. |
ModalResult | При нажатии кнопки значение этого свойства копируется в одноименное свойство формы, что приводит к закрытию монопольного окна диалога. Однако для этого значение свойства должно отличаться от mrNone. |
WordWrap | Если равно значению True, то работает перенос слов. |
|
Таблица 9.1. Важнейшие свойства компонента Button
Текст на кнопке определяется значением свойства Caption. В тексте может присутствовать символ &, который не пишется на кнопке, а обеспечивает подчеркивание следующего за ним символа. Нажатие подчеркнутого символа на клавиатуре в комбинации с клавишей Alt вызывает срабатывание кнопки. Например, если свойство Caption содержит строку &Yes, то на кнопке будет написано Yes, и для нажатия кнопки можно воспользоваться комбинацией клавиш Alt+Y.
Пользователи, привыкшие работать с клавиатурой, знают, что одна из кнопок в окне диалога срабатывает при нажатии клавиши Enter. Это происходит при условии, что кнопка содержит значение True в свойстве Default. Такая кнопка, как правило, содержит текст OK и внешне отличается от остальных наличием жирной рамки.
Одна из кнопок окна диалога может срабатывать при нажатии клавиши Esc. Это происходит, если кнопка содержит значение True в свойстве Cancel. Как правило, такая кнопка подписывается текстом "Отмена" (Cancel).
Когда пользователь нажимает кнопку, в компоненте Button происходит событие OnClick. В обработчике этого события вы можете завершить диалог. Завершение немонопольного диалога выполняется вызовом метода Close у объекта формы. Завершение монопольного окна диалога выполняется установкой ненулевого значения в свойстве ModalResult формы. Впрочем, без этого можно обойтись, если воспользоваться свойством ModalResult компонента Button (мы не ошиблись, это свойство имеет и форма, и кнопка). Свойство ModalResult компонента Button устанавливается в окне свойств и по умолчанию равно значению mrNone. Если вы выберите другое значение, то нажатие кнопки будет вызывать автоматическое завершение диалога с копированием этого значения в свойство ModalResult формы. Анализируя свойство ModalResult после завершения диалога, программа узнает, какую кнопку нажал пользователь и в соответствии с этим направляет работу программы в нужное русло.
Помните, что свойство ModalResult работает только в монопольных окнах диалога.
Шаг 5. Перейдем теперь от теории к практике и создадим в нашем окне About кнопку OK (рисунок 9.4). Для этого выберите в палитре компонентов компонент Button, опустите его в форму и установите его свойства следующим образом:
Cancel = True
Caption = OK
Default = True
ModalResult = mrOk
Рисунок 9.4. Кнопка OK в окне About
Каждый из нас в душе художник. Поэтому рано или поздно стандартные невзрачные кнопки, содержащие лишь "голый" текст, перестанут вам нравиться. Появится естественное желание их как-то приукрасить. В этом случае мы советуем вам вместо компонента Button воспользоваться компонентом BitBtn. Он расположен в палитре компонентов на вкладке Additional.
Рисунок 9.5. Компонент BitBtn
Компонент BitBtn обладает теми же возможностями, что и компонент Button, но кроме текста может содержать значок, который придает кнопке более привлекательный вид. По сравнению с компонентом Button компонент BitBtn имеет некоторые новые свойства, которые отражены в таблице 9.2.
Свойство | Описание |
Glyph | Значок на кнопке. |
NumGlyphs | Количество вариантов значка. Компонент делит рисунок Glyph по горизонтали на заданное количество значков и рисует один из них в зависимости от состояния кнопки. |
Layout | Положение значка относительно текста: blGlyphLeft — слева, blGlyphRight — справа, blGlyphTop — сверху, blGlyphBottom — снизу. |
Margin | Расстояние от границы кнопки до значка. Если оно равно -1, то значок вместе с текстом центрируются на кнопке. |
Spacing | Расстояние от значка до текста. Если оно равно -1, то текст центрируется между значком и границей кнопки. |
Kind | Задает кнопку стандартного вида. Упрощает создание таких стандартных кнопок, как OK, Cancel, Yes, No, Close, Abort, Retry, Ignore, All, Help. |
|
Таблица 9.2. Характерные свойства компонента BitBtn
Заметим, что кнопка, содержащая значок, принимает наиболее красивый вид, если свойство Margin равно значению 4, а свойство Spacing равно значению 1.
С помощью компонента BitBtn стандартные кнопки OK, Cancel, Yes, No, Close, Abort, Retry, Ignore, All и Help создаются проще, чем при использовании компонента Button. Для этого в свойстве Kind достаточно выбрать одно из значений, приведенных в таблице 9.3.
Значение | Вид кнопки | Результат установки значения | Пояснения |
bkOK | | Caption = 'OK' Default = True ModalResult = mrOK | Кнопка, подтверждающая ввод данных. |
bkCancel | | Caption = 'Cancel' Cancel = True ModalResult = mrCancel | Кнопка, отменяющая ввод данных. |
bkYes | | Caption = '&Yes' Default = True ModalResult = mrYes | Кнопка для положительного ответа на вопрос. |
bkNo | | Caption = '&No' Cancel = True ModalResult = mrNo | Кнопка для отрицательного ответа на вопрос. |
bkAll | | Caption = '&All' ModalResult = mrAll | Кнопка для положительного ответа на все вопросы. |
bkAbort | | Caption = 'Abort' ModalResult = mrAbort | Кнопка для прерывания операции. |
bkRetry | | Caption = '&Retry' ModalResult = mrRetry | Кнопка для повторения операции. |
bkIgnore | | Caption = '&Ignore' ModalResult = mrIgnore | Кнопка для игнорирования произошедших изменений и продолжения начатой операции. |
bkHelp | | Caption = '&Help' | Кнопка для вызова справочника. Вы можете вызывать справочник в обработчике события OnClick или возложить эту работу на среду Delphi, установив у формы свойство HelpContext в нужное значение. |
bkClose | | Caption = '&Close' | Кнопка, закрывающая форму. |
bkCustom | Любой | Любой | Кнопка для ваших собственных целей. |
|
Таблица 9.3. Значения свойства Kind компонента BitBtn
Например, если вам нужна кнопка OK, установите свойство Kind в значение bkOK. В результате на кнопке появится зеленая "галочка" и текст "OK", свойство Default получит значение True и свойство ModalResult получит значение mrOK.
Хорошее приложение должно быть не только функционально, но и эстетично. Как говорится, хорошее блюдо должно быть красиво подано. Это относится и к нашему замечательному приложению, и конечно к его окну About. А что может лучше украсить окно About, чем яркий, запоминающийся рисунок.
Шаг 6. Как вы уже знаете, рисунок создается с помощью компонента Image, расположенного в палитре компонентов на вкладке Additinal. Выберите этот компонент и поместите его в форму AboutForm.
Шаг 7. Установите свойство AutoSize в значение True, чтобы компонент автоматически подгонял свои размеры под размеры рисунка, и установите свойство Transparent в значение True, чтобы рисунок отображался с прозрачным фоном.
Рисунок 9.6. Прозрачный фон для рисунка задается установкой свойства Transparent в значение True
Шаг 8. Чтобы установить рисунок, перейдите к свойству Picture и нажатием кнопки с многоточием откройте окно Picture Editor. Это окно должно быть вам уже знакомо. Загрузите файл Athena.bmp из папки "C:\Program Files\Common Files\Borland Shared\Images\Splash\16Color". Рисунок появится в форме (рисунок 9.7).
Рисунок 9.7. В компоненте Image загружен рисунок
Сейчас мы подошли к созданию текстовых надписей в окне About. Они будут сообщать пользователю о названии программы и средствах ее разработки. Для нашей учебной задачи этого достаточно, в реальных приложениях окно About может дополнительно содержать замечание об авторских правах, серийный номер и другую регистрационную или лицензионную информацию.
Следуя традиции, напишем название программы большими жирными буквами, а название средства разработки — обычным мелким шрифтом. Для этого воспользуемся компонентом Label, который находится в палитре компонентов на вкладке Standard.
Рисунок 9.8. Компонент Label
Характерные свойства компонента Label кратко описаны в таблице 9.4.
Свойство | Описание |
Align | Способ выравнивания в пределах содержащего компонента. |
Alignment | Выравнивание текста в пределах компонента: taLeftJustify — прижат к левой границе, taRightJustify — прижат к правой границе, taCenter — центрирован. |
AutoSize | Если равно значению True, то размеры компонента автоматически подгоняются по ширине и высоте текста. |
Caption | Текст надписи. С помощью символа & в тексте может быть задана "горячая" клавиша. |
FocusControl | Компонент формы, получающий фокус ввода при нажатии "горячей" клавиши. |
ShowAccelChar | Если равно значению True, то записанный в тексте символ & транслируется в подчеркивание следующего за ним символа. Подчеркнутый символ используется в комбинации с Alt как "горячая" клавиша. |
Transparent | Если равно значению True, то фон надписи является прозрачным. Прозрачный фон полезен при наложении надписи на рисунок. |
WordWrap | Если равно значению True, то работает перенос слов. |
|
Таблица 9.4. Важнейшие свойства компонента Label
Компонент Label отображает нередактируемый текст, хранящийся в свойстве Caption. Текст выравнивается в пределах компонента одним из трех способов: по левому краю, по правому краю или по центру. Способ выравнивания определяется свойством Alignment. Если текст надписи слишком велик, можно организовать его вывод в несколько строк (с переносом слов). Для этого достаточно установить свойство WordWrap в значение True. Еще одна удобная возможность — автоматическая подгонка размеров компонента по ширине и высоте текста. Она контролируется свойством AutoSize и по умолчанию включена.
Фон надписи можно сделать прозрачным, установив свойство Transparent в значение True. Такую надпись можно вынести наверх графического изображения, в результате получится текст на фоне рисунка.
С помощью компонента Label часто создаются подсказки к другим компонентам, в частности к полям ввода. При этом свойство Caption содержит не только текст, но и "горячую" клавишу, при выборе которой активизируется связанный с надписью компонент. Активизируемый компонент указывается в свойстве FocusControl.
Шаг 9. Вспомним, что компонент Label понадобился нам для того, чтобы сделать необходимые надписи в окне About. Опустите в форму первую надпись справа от изображения и установите ее свойства следующим образом:
WordWrap = True
Caption = Picture Viewer
Font Color = clNavy
Font Name = Times New Roman
Font Size = 20
Font Style = [fsBold]
Рисунок 9.9. Надпись выполнена с помощью компонента Label
Шаг 10. После этого поместите в форму еще один компонент Label с текстом "Developed in Delphi" в свойстве Caption.
Окно диалога почти готово, но для полного ажура не хватает одной мелочи — рельефной канавки вокруг рисунка и надписей (это придаст окну законченность). Для решения подобных задач служит компонент Bevel, расположенный в палитре компонентов на вкладке Additional.
Рисунок 9.10. Компонент Bevel
Шаг 11. Поместите в форму компонент Bevel, придайте ему нужные размеры и положение, после чего установите свойство Shape в значение bsFrame.
Рисунок 9.11. Компонент Bevel в форме
Рельефные канавки удобно создавать с помощью компонента Bevel. Однако компонент Bevel не может быть контейнером для других компонентов, а следовательно, перемещение рамки не вызывает перемещение компонентов, находящихся внутри нее. Если нужна не просто рамка, а контейнер с рамкой, то пользуются рельефной панелью — компонентом Panel (вкладка Standard панели инструментов).
Рисунок 9.12. Компонент Panel
Отличительные свойства компонента Panel сведены в таблицу 9.5.
Свойство | Описание |
Align | Способ выравнивания панели в пределах владельца. |
BevelInner | Внутренний скос рельефной рамки: bvNone — скос отсутствует, bvLowered — скос внутрь, bvRaised — скос наружу; bvSpace — скос заменяется отступом. |
BevelOuter | Внешний скос рельефной рамки: bvNone — скос отсутствует, bvLowered — скос внутрь, bvRaised — скос наружу; bvSpace — скос заменяется отступом. |
BevelWidth | Ширина скосов рельефной рамки. |
BorderWidth | Расстояние в пикселях между внутренним и внешним скосами. |
BorderStyle | Определяет, имеет ли панель рамку. |
Caption | Текст на панели. |
DockSite | Определяет, используется ли панель для стыковки других компонентов. |
FullRepaint | Свойство сохранено для совместимости с предыдущими версиями библиотеки VCL. Оно не влияет на способ перерисовки компонента. |
Locked | Если равно False, то OLE-серверу разрешено заменить панель на свою панель инструментов. Если равно True и панель выравнена по какой-нибудь стороне формы, то она остается нетронутой при активизации OLE-сервера по месту. |
UseDockManager | Включает режим автоматического размещения стыкуемых компонентов на панели. Компоненты стыкуются методом деления панели по горизонтали и вертикали. Если свойство равно значению False, то программист должен сам позаботиться о размерах и местоположении стыкуемых компонентов, определив обработчики событий OnGetSiteInfo и OnDockDrop. |
OnCanResize | Происходит при попытке изменить размеры панели. Запрос на изменение размеров может исходить от пользователя. Устанавливая значение параметра Resize, обработчик события OnCanResize может разрешить или запретить действительное изменение размеров панели. |
OnConstrainedResize | Происходит при изменении размеров панели и позволяет на лету изменять ее минимальные и максимальные размеры. |
OnGetSiteInfo | Происходит, когда у компонента запрашивается место для стыковки. |
|
Таблица 9.5. Важнейшие свойства компонента Panel
Шаг 12. Уберите компонент Bevel из формы и поместите на его место компонент Panel. Откорректируйте его положение и размеры и установите свойства следующим образом (рисунок 9.13):
Caption = <пусто>
BevelInner = bvRaised
BevelOuter = bvLowered
Рисунок 9.13. Компонент Panel заменил в форме компонент Bevel
Шаг 13. С помощью окна Object TreeView перенесите компоненты Image1, Label1 и Label2 на панель Panel1 (рисунок 9.14).
Рисунок 9.14. Компоненты Image1, Label1 и Label2 переносятся на панель Panel1
Теперь рельефная рамка заменена рельефной панелью и при ее перемещении перемещаются все надписи и рисунок (рисунок 9.15).
Рисунок 9.15. Компоненты Image1, Label1 и Label2 — теперь на панели Panel1
Будьте аккуратны при удалении панели! Вместе с ней всегда удаляются внутренние компоненты.
Визуальное проектирование окна About закончено, осталось обеспечить его вызов при выборе пользователем команды Help | About... главного меню. Для этого нужно сделать следующее:
- создать команду AboutAction в компоненте ActionList и связать ее с пунктом меню About...;
- в модуле MainUnit подключить модуль AboutUnit. Это обеспечит доступ к форме AboutForm из главной формы PictureForm;
- создать обработчик события OnExecute команды AboutAction и обеспечить в нем монопольное выполнение диалога.
Шаг 14. Как реализовать первый пункт плана вы уже знаете, поэтому мы не будем на нем останавливаться, и сразу перейдем ко второму пункту плана. Активизируйте PictureForm, а затем выберите в меню File команду Use Unit... . На экране появится окно (рисунок 9.16):
Рисунок 9.16. С помощью окна Use Unit модуль About подключается в модуле Main
Выберите в этом окне модуль AboutUnit и щелкните на кнопке OK. Модуль AboutUnit, содержащий определение формы AboutForm, подключится в модуле MainUnit, содержащем определение главной формы PictureForm. Чтобы в этом убедиться, перейдите к редактору кода. В разделе implementation модуля MainUnit вы обнаружите строку
uses AboutUnit;
Ее можно было бы набрать и вручную, но мы решили продемонстрировать вам еще одну возможность визуальной среды программирования.
Шаг 15. Выполните теперь второй пункт плана — создайте обработчик события OnExecute для команды AboutAction компонента ActionList:
procedure TPictureForm.AboutActionExecute(Sender: TObject);
begin
AboutForm.ShowModal;
end;
|
|
Метод ShowModal запускает окно диалога в монопольном режиме и возвращает управление только после его завершения. В нашем примере метод вызывается как процедура, но в действительности это функция, которая возвращает код завершения диалога. Код берется у формы из свойства ModalResult. Напомним, что для выполнения диалога в немонопольном режиме его нужно вызвать с помощью метода Show, а не метода ShowModal.
Шаг 16. Выполните компиляцию и запустите программу, затем проверьте работу новоиспеченного окна диалога, выполнив команду меню Help | About... (рисунок 9.17).
Рисунок 9.17. Диалоговое окно в работе
Все работает правильно, остается только выяснить один вопрос: где, когда и кем конструируется объект AboutForm. Ведь никаких усилий мы для этого не предпринимали, и тем не менее использовали объект при вызове метода ShowModal как уже существующий.
Объект AboutForm создается при запуске программы и существует на протяжении всей ее работы. В этом можно убедиться, заглянув в главный файл программы с помощью команды меню View | Project Source. В главном программном блоке вы найдете оператор:
Application.CreateForm(TAboutForm, AboutForm);
Он-то и обеспечивает "автоматическое" создание объекта формы. Это удобно, но имеет и отрицательные стороны, так как память, выделенная объекту остается занятой даже тогда, когда форма невидима, т.е. до запуска диалога и после его завершения. Если программа имеет одну-две формы, это не так важно, а если много? В этом случае от автоматического создания всех форм, кроме главной, лучше отказаться.
Шаг 17. Чтобы исключить автоматическое создание формы AboutForm, откройте окно Project Options и на вкладке Forms отбуксируйте элемент AboutForm из списка Auto-create forms в список Available forms (рисунок 9.18):
Рисунок 9.18. Форма AboutForm исключена из списка автоматически создаваемых форм
В результате среда Delphi удалит приведенный выше оператор из главного файла программы, переложив заботу о создании формы на ваши плечи.
Шаг 18. Разумеется, что после сделанных действий метод AboutItemClick, из которого вызывается окно диалога, нужно переписать:
procedure TPictureForm.AboutActionExecute(Sender: TObject);
begin
AboutForm := TAboutForm.Create(Self);
try
AboutForm.ShowModal;
finally
AboutForm.Free;
end;
end;
|
|
Ну вот, теперь ресурс оперативной памяти используется более рационально. Кстати, обратите внимание, как обеспечивается защита объекта AboutForm от исключительных ситуаций, которые могут возникнуть в период его работы (это конечно маловероятно, но чего в этой жизни не бывает!). Если объект AboutForm успешно создается, то благодаря оператору try...finally...end он всегда корректно освобождается, даже в случае возникновения исключительной ситуации.
Обсуждение материала нет сообщений |