Создаем диалог-мастер
Оформил: DeeCo
Автор: Николай Мазуркин
В данной статье рассматривается способ создания мастера (Wizard) с
использованием средств Delphi и стандартной библиотеки VCL, без установки и
использования каких-либо дополнительных компонентов или средств.
Введение
В палитре современных средств для строительства программных интерфейсов
особое место занимают интерфейсные диалоги типа "мастер" (в оригинале, на
английском, Wizard - "фокусник, волшебник"). Данные элементы широко применяются
как самой операционной системой Windows (особенно последние версии Windows 2000,
Windows XP), так и многими программами различных производителей программного
обеспечения. Во многих случаях использование мастера стало стандартом, например,
в программах установки программного обеспечения.
Что же такое мастер? Можно определить мастер, как самоконфигурирующийся
диалог управления определенным процессом, который разбивает настройку процесса
на несколько этапов с возможностью ветвления, проводит пользователя через эти
этапы, указывая только необходимые параметры и избавляя от необходимости тратить
внимание на элементы управления, не участвующие в текущем этапе.
Достоинствами мастера перед альтернативными решениями, вроде многостраничного
диалога настройки являются следующие факторы.
- Пошаговый процесс настройки облегчает понимание сути настраиваемого
процесса.
- Мастер показывает только необходимые в данный момент элементы управления,
что снижает риск ошибки ввода информации.
- Мастер, благодаря по-этапному разбитию настроек и малому количеству
элементов управления на каждом этапе, позволяет ввести большое количество
справочных надписей, рисунков и элементов разбивки, что облегчает понимание и
восприятие процесса пользователем.
Пример мастера приведен на следующем рисунке. Это тестовый мастер, который мы
будем разрабатывать в рамках данной статьи.
Практически любой мастер состоит из следующих элементов.
- "Рекламная" область, которая может располагаться либо слева, либо сверху.
Обычно в этой области выводится красивое рекламное изображение, поясняющее
назначение данного мастера, либо рекламирующего логотип фирмы-производителя.
Расположение этой области сверху более эргономично, так как позволяет вывести
еще название текущей странички и краткое пояснение к ней. Такое расположение
стало применяется не так давно и выглядит более современным.
- Область редактирования. Содержит элементы редактирования текущего этапа
настройки.
- Кнопки "Назад" и "Вперед". Позволяют перемещаться по этапам настройки
процесса.
- Кнопка "Отмена". Позволяет в любой момент покинуть диалог настройки.
Представим себе, что мы разрабатываем финансовую программу, результатом
которой является таблица с большим количествов записей. Нам необходимо сохранить
эту таблицу в формате, указанном пользователем. Формат может быть выбран из
множества HTML, XML или TXT. Также у каждого формата существуют свои,
специфические настройки.
Решим эту задачу с помощью разработки специального мастера, который позволит
пользователю выбрать необходимый формат, указать настройки, и выбрать файл для
сохранения отчета.
Схема мастера
Прежде чем приступать к кодированию, необходимо продумать структуру будующего
мастера: количество и назначение страниц, порядок перехода и конфигурации. Это
облегчит работу на последующих этапах, в том числе и на этапе кодирования.
Еще лучше нарисовать всю структуру на бумаге или в графическом редакторе типа
Visio. А те кто знаком с графическим языком UML, наверняка предпочтут
проработать структуру мастера в виде полноценной Statechart диаграммы в
редакторе типа Rational Rose. Подобная визуальная проработка структуры позволит
продумать и учесть множество нюансов.
Структура нашего мастера показана на следующем рисунке. Блоками обозначены
странички, реализующие этапы настройки. В блоках указано программное имя
странички (имя компонента), а цифрой в квадратных скобках показан индекс
странички среди всех страничек мастера (подробнее о программной реализации
мастера мы поговорим далее). Стрелками показаны переходы между страничками по
нажатию кнопок "Назад" и "Вперед". Для тех же, кто понимает язык моделирования
UML, можно предложить более детальную схему.
Видно, что мастер состоит из семи страничек. Назначение каждой из страничек
следующее.
- tsWelcome - стартовая страничка. При запуске мастера пользователь
видит именно ее. На этой страничке нет элементов управления, только два
текстовых блока. В первом текстовом блоке описывается назначение и порядок
работы данного мастера. Во втором тестовом блоке описываются основные принципы
работы с мастерами вообще: назначение кнопок "Назад", "Вперед" и "Отмена".
Цель этой странички - позволить пользователю психологически расслабиться и
обрести уверенность в своих действиях. В принципе, данную страничку можно
полностью убрать и дать пользователю возможность сразу приступить к работе.
Однако, по мнению автора, наличие такой странички делает любой мастер более
"дружественным" и "вежливым" по отношению к пользователю.
- tsFormat - страничка выбора формата экспорта. После этой странички
наступает ветвление, и переход на следующую страничку происходит в
соответствии с выбранным форматом.
- tsFormatHTML - настройка опций при экспортировании отчета в HTML
формате.
- tsFormatXML - настройка опций при экспортировании отчета в XML
формате.
- tsFormatTXT - настройка опций при экспортировании отчета в TXT
формате.
- tsProgress - страничка экспортирования. На данной страничке
собственно и происходит экспортирование отчета в соответствии с выбранным
форматом. На страничке располагается журнал операций, который позволяет
проследить прогресс выполнения экспортирования (если отчет очень большой и
экспортирование длится более 1-2 секунд), а также позволяет увидеть всю
информацию о возникающих ошибках. При успешном экспортировании управление
передается на следующую страничку.
- tsFinish - страничка, информирующая пользователя об успешном
окончании процесса экспортирования. На данной страничке пользователь модет
либо завершить работу с мастером, либо экспортировать отчет еще раз, но уже в
другом формате (обратите внимание куда ведет стрелка по событию OnBackward).
Будем считать, что мы уже разработали содержание, дизайн и текстовое
наполнение каждой из страничек.
Устройство мастера
Итак, схема мастера уже полностью разработана и нам нужно приступить к
непосредственному кодированию.
Прежде чем приступить к рассмотрению вопросов, связанных с кодированием
мастера, скачайте и откройте тестовый проект, содержащий полную реализацию
проектируемого нами мастера.
<<<
Исходный код проекта диалога-мастера
В режиме редактирования наш мастер выглядит следующим образом.
Настройка основных элементов мастера показана зеленым цветом. С остальными
настройками и расположением элементов вы можете ознакомится сами, используя
Object Inspector.
В качестве контейнера для страниц мы будем использовать элемент TPageControl.
Для того чтобы убрать выпуклую рамку вокруг элемента переведем его в режим
tsButtons. Заголовки страничек позволят нам быстро выбирать нужную страничку для
редактирования, однако они будут лишними в рабочем режиме. Чтобы убрать эти
заголовки, при создании формы установим у всех элементов TTabSheet свойство
TabVisible равным False. Для того чтобы дизайн мастера в рабочем режиме не
сильно отличался от дизайна в режиме редактирования, установим минимальную
высоту закладок.
В "рекламной" области установим два текстовых элемента, один из которых будет
отображать название текущей странички, а другой - краткое пояснение к ней. Чтобы
не умножать сущности, название странички будем хранить прямо в ней, в свойстве
TTabSheet.Caption, а краткое пояснение в свойстве TTabSheet.Hint (но при этом
TTabSheet.ShowHint = False).
Код мастера
По сути, любой мастер - это конечный автомат или state machine, в чистом
виде. У него есть внутренние состояние, которое может быть в одном из нескольких
положений (один из этапов или страничек). Также есть два события - OnForward и
OnBackward, которые переводят этот автомат из одного состояния в другое, в
зависимости от дополнительных внутренних настроек или опций. При этом переход
определяется исключительно текущим положением и текущими настройками.
Такой подход позволяет прекрасно и понятно описать работу мастера с помощью
средств UML, а также анализировать и реализовывать работу мастера с помощью уже
готового математического аппарата. Однако, мы не будем залезать в теоретические
дебри и реализуем мастер простым способом.
Основной код мастера состоит из трех процедур, являющихся обязательными для
каждого мастера.
Процедура ShowPage
Процедура ShowPage активизирует указанную страницу, настраивает кнопки и
внешний вид диалога в соответствии с указанной страницей. Эта процедура также
копирует текст названия страницы и комментарий к ней из свойств
TTabSheet.Caption и TTabSheet.Hint в текстовые поля "рекламной" области.
Можно дополнять и расширять эту процедуру. Например, настраивать
дополнительные кнопки, выводить каждый раз новый логотип или информационную
картинку в соответствии с выбранной страничкой, проигрывать звуковые файлы и
т.д.
////////////////////////////////////////////////////////////////////////////////
// Показываем требуемую страничку
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.ShowPage(PageIndex: Integer);
begin
// Проверка индекса на допустимость
if (PageIndex < 0) or (pcWizard.PageCount < = PageIndex) then
Exit;
// Установка странички
pcWizard.ActivePageIndex := PageIndex;
// Установка параметров заголовка
lblCaption.Caption := pcWizard.ActivePage.Caption;
lblHint.Caption := pcWizard.ActivePage.Hint;
// Настраиваем кнопку "Назад"
btnBackward.Visible := PageIndex <
>
tsWelcome.PageIndex;
// Настраиваем кнопку "Вперед"
if PageIndex = tsFinish.PageIndex then
btnForward.Caption := 'Готово'
else
btnForward.Caption := 'Вперед >';
// Настраиваем кнопку "Отмена"
btnCancel.Enabled := PageIndex <
>
tsFinish.PageIndex;
end;
Обработчик OnForward
Обработчик события OnForward срабатывает каждый раз при нажатии кнопки
"Вперед". И, если процедура ShowPage определяет, как выглядит мастер на
каждой страничке, то обработчик события OnForward определяетю, что
происходит при переходе от одной странички вперед к другой.
По умолчанию, обработчик заменяет текущую страничку следующей, не производя
при этом никаких других действий. Однако, для того чтобы реализовать логику
нашего мастера, мы должны каждый раз проверять какая страничка находится в
данный момент на экране, и какую страничку мы должны выставить следующей.
Обратите внимание, что при уходе с определенных страничек происходит проверка
их содержимого на корректность. И, если введенные пользователем данные на
страничке некорректны, перехода не происходит.
////////////////////////////////////////////////////////////////////////////////
// Передвигаемся вперед
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.OnForward(Sender: TObject);
begin
// Это страница выбора формата?
if pcWizard.ActivePage = tsFormat then
begin
// Показываем соответствующую страницу
if rbtnChooseHTML.Checked then
ShowPage(tsFormatHTML.PageIndex);
if rbtnChooseXML.Checked then
ShowPage(tsFormatXML.PageIndex);
if rbtnChooseTXT.Checked then
ShowPage(tsFormatTXT.PageIndex);
Exit;
end;
// Это формат HTML?
if pcWizard.ActivePage = tsFormatHTML then
begin
// Проверка имени файла
if Trim(edtExportHTML.Text) = '' then
begin
edtExportHTML.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это формат XML?
if pcWizard.ActivePage = tsFormatXML then
begin
// Проверка имени файла
if Trim(edtExportXML.Text) = '' then
begin
edtExportXML.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это формат TXT?
if pcWizard.ActivePage = tsFormatTXT then
begin
// Проверка имени файла
if Trim(edtExportTXT.Text) = '' then
begin
edtExportTXT.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это страница экспорта?
if pcWizard.ActivePage = tsProgress then
begin
// Выполняем экспортирование
if rbtnChooseHTML.Checked then
if not ExportHTML then
Exit;
if rbtnChooseXML.Checked then
if not ExportXML then
Exit;
if rbtnChooseTXT.Checked then
if not ExportTXT then
Exit;
// Переходи на финальную страницу
ShowPage(tsFinish.PageIndex);
Exit;
end;
// Это финальная страница?
if pcWizard.ActivePage = tsFinish then
begin
ModalResult := mrOK;
Exit;
end;
// Действие по умолчанию - следующая страница
ShowPage(pcWizard.ActivePageIndex + 1);
end;
Обработчик OnBackward
Обработчик события OnBackward срабатывает каждый раз при нажатии на кнопку
"Назад" и, по назначению, аналогичен обработчику OnForward. Данный обработчик
определяет, что происходит при уходе со странички в обратном направлении.
////////////////////////////////////////////////////////////////////////////////
// Передвигаемся назад
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.OnBackward(Sender: TObject);
begin
// Это страница экспорта?
if pcWizard.ActivePage = tsProgress then
begin
// Показываем соответствующую страницу
if rbtnChooseHTML.Checked then
ShowPage(tsFormatHTML.PageIndex);
if rbtnChooseXML.Checked then
ShowPage(tsFormatXML.PageIndex);
if rbtnChooseTXT.Checked then
ShowPage(tsFormatTXT.PageIndex);
Exit;
end;
// Это финальная страница?
if pcWizard.ActivePage = tsFinish then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат HTML?
if pcWizard.ActivePage = tsFormatHTML then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат XML?
if pcWizard.ActivePage = tsFormatXML then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат TXT?
if pcWizard.ActivePage = tsFormatTXT then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Действие по умолчанию - предыдущая страница
ShowPage(pcWizard.ActivePageIndex - 1);
end;
Дополнительные советы
Мастер является мощным инструментом реализации настроек сложных процессов и
процедур, однако само по себе создание мастера не приводит автоматически к
успеху - любой мастер должен быть продуманным, а его использование -
оправданным.
Вот несколько дополнительных советов.
- А нужен ли вообще мастер в вашем случае? Возможно, что настройки процесса
просты, а элементы управления гармонично умещаются в одном диалоге.
- Достигайте компромиса между количеством страничек в мастере и числом
управляющих элементов на каждой страничке.
- Продумайте систему переходов между страничками в прямом и обратном
направлении. Лучше, если вы нарисуете визуальную диаграмму переходов.
- Размещайте на страничках больше информационных текстовых элементов - это
поможет пользователю разобраться в процессе, не прибегая к вызову справки.
- Создавайте в мастере стартовую и финальную страницы. Стартовая страница
проинформирует пользователя о сути процесса, а финальная - об успешном (или
неуспешном) окончании процесса.
- Пропорции сторон диалога мастера могут точно или приближенно
соответствовать "золотому" сечению - это создаст благоприятный визуальный
эффект.
- Дизайн каждой странички мастера должен быть тщательно проработан.
- Не забывайте о порядке перехода фокуса между элементами как каждой
странички, так и мастера в целом.
|