Форма в режиме диалога
Окно диалога предназначено для приостановки выполнения программы (процедуры или функции) с целью получения данных от пользователя, на основании которых будет продолжено выполнение программы. В общем случае существует два варианта диалога программы и пользователя. В первом варианте программа задаёт вопрос, а пользователь отвечает, нажимая на кнопки в окне сообщения (рис. 1), которое вызывается с помощью функции MsgBox. В другом случае от пользователя необходимо получить какие-нибудь данные. Для этого используется окно ввода – функция InputBox (рис. 2).
Второй вариант выглядит несколько ограниченным, т.к. иногда пользователю нужны какие-нибудь подсказки для того, чтобы ввести правильные данные. Например, выбрать данные из выпадающего списка, источником строк которого служит справочная таблица. Тогда в качестве диалогового окна можно использовать любую форму, если открыть её в режиме диалога: DoCmd.OpenForm FormName:=strFormName, WindowMode:=acDialog. Это самый простой способ получить диалоговое окно средствами системы. При этом выполнение процедуры, из которой вызывается форма в режиме диалога, приостанавливается, пока эта форма видна на экране. Именно видна. Для продолжения выполнения процедуры надо закрыть (с помощью команды DoCmd.Close) или скрыть форму (присвоив свойству Visible формы значение False). Форма в режиме диалога всё-таки имеет один недостаток: эту форму после её открытия нельзя «настроить». Например, вы хотите использовать диалоговую форму выбора из выпадающего списка и для выбора измерения количества товара, и для выбора категорий товара, и ещё для чего угодно. Если использовать вызов формы в режиме диалога, то для каждого диалога выбора потребуется своя форма. И эти формы друг от друга будут отличаться по сути лишь несколькими символами в источнике строк для списка, да в подписи самого списка.
Экземпляр формы
Форма с кодом является модулем класса с графическим интерфейсом. Так же, как можно создать любое количество экземпляров класса, можно создать любое количество экземпляров и формы. Например, у нас есть диалоговая форма выбора "DialogChoose” (рис. 3). У неё есть некий код (обработка нажатий кнопок), который находится в модуле формы. В обозревателе объектов проекта VBA приложения можно увидеть модуль класса этой формы, который будет называться "Form_DialogChoose” (рис. 4). Чтобы создать экземпляр этой формы нам понадобится объектная переменная типа формы (Form). Экземпляр создаётся с помощью ключевого слова New. Вновь созданная форма будет в точности как исходная, только она останется невидимой. Чтобы сделать эту форму видимой, надо присвоить свойству Visible этой формы значение True. Но созданный таким образом экземпляр будет существовать, пока на него есть ссылка. Т.е. объектная переменная, хранящая ссылку должна быть либо статической, либо глобальной, либо общей переменной модуля. Либо можно создать ссылку на эту объектную переменную в самой форме и присваивать ей значение при загрузке формы. Таким образом, получаем следующий код модуля для создания экземпляра формы (листинг 1): Public frmDialogChoose as Form Public Sub ShowDialogChoose() Set frmDialogChoose = New Form_DialogChoose frmDialogChoose.Visible = True End Sub
Для организации формы диалога нам не потребуется создавать несколько экземпляров одной формы одновременно. Ведь сам смысл диалоговой формы заключается в том, что работа приостанавливается, пока пользователь не завершит диалог. Поэтому такой вариант мы даже не будем рассматривать, хотя это можно организовать с помощью массива или коллекции для хранения ссылок на экземпляры формы.
Диалоговая форма как экземпляр
Исходя из всего вышеизложенного, можно создать класс диалоговой формы, который будет управлять отображением диалоговой формы (её «настройкой») в зависимости от значения некоторых свойств этого класса, а также обработкой действий пользователя. Для обработки действий пользователя может потребоваться перехват событий диалоговой формы. Некий абстрактный класс (шаблон класса) для управления диалоговой формой будет выглядеть следующим образом (листинг 2): Option Compare Database Option Explicit 'класс для управления диалоговой формой Private WithEvents mfrmDialog As Form 'диалоговая форма с событиями
Public Sub ShowDialog() 'открытие диалоговой формы для выбора Set mfrmDialog = Form_DialogChoose With mfrmDialog .OnClose = "[Event Procedure]" 'объявление обработки события для перехвата .Modal = True 'модальная форма – диалог 'другая настройка .Visible = True 'отобразить форму End With End Sub Private Sub mfrmDialog_Close() 'обработка события закрытия диалоговой формы End Sub
В таком случае ссылку на экземпляр этого класса надо сделать в виде общей переменной модуля. Или ссылку сделать в модуле самой формы и присваивать ей значение при настройке формы.
Реализация
Итак, рассмотрим вариант реализации работы такой диалоговой формы под управлением класса.
Диалоговая форма
Для реализации потребуется разработка самой формы под названием, например DialogForm. Данная форма должна иметь свой программный модуль, иначе нельзя будет обработать событие этой формы. Если эта диалоговая форма будет иметь, как обычные диалоговые формы, кнопки "OK” и "Отмена”, то на события нажатия этих кнопок, нужно закрывать форму, а также сделать что-нибудь, чтобы отличать одно событие от другого. Самое простое, можно создать общую переменную в модуле формы (Public mlngValue as Long), значение которой будет зависеть от нажатой кнопки.
Управляющий класс
Создадим модуль класса под названием, например, clsDialog. Данный класс будет полностью управлять диалоговой формой и реагировать на выбор пользователя, обрабатывая событие закрытия диалоговой формы. Это очень важный момент, так как в обычном случае, при использовании простого вызова формы в режиме диалога, реакция на выбор пользователя обрабатывается в той же процедуре, где происходит и вызов формы диалога. В нашем же случае, эта обработка переносится в модуль класса управления диалоговой формой. Модуль класса clsDialog минимально должен содержать код, представленный в листинге 2 (см. раздел «Диалоговая форма как экземпляр»).
В процедуре обработки закрытия формы диалога mfrmDialog_Close следует определить каков же был выбор пользователя. Это поможет сделать общая переменная формы (mlngValue), которая принимает значение в зависимости от того, какую кнопку нажал пользователь. Далее в зависимости от выбора пользователя необходимо выполнить действия на этот выбор. Обычно это занесение какого-либо значения в таблицу и (или) занесение значения в вызвавшую этот диалог форму. Для определения названия таблицы, названия поля или название элемента управления вызвавшей этот диалог формы можно применять специальные свойства класса управления. И при создании экземпляра класса перед вызовом метода ShowDialog присвоить этим свойствам необходимые значения.
Связываем всё вместе
Создадим ещё один модуль, но уже не класса, а обычный под названием, например basDialog. В этом модуле объявим общую переменную типа clsDialog и создадим процедуру для вызова и настройки формы диалога. Причём настройка формы будет осуществляться путём задания значений свойствам экземпляра класса (переменной mclsDialog), управляющего диалоговой формой. Поэтому при проектировании класса clsDialog необходимо все эти свойства предусмотреть. И потом значения для всех этих свойств необходимо передать в процедуру вызова диалоговой формы (листинг 3):
Public mclsDialog as clsDialog
Public Sub ShowDialogForm(ctlForReturn as Control, Parametr as Long)
Set mclsDialog = New clsDialog
With mclsDialog
‘передача управляющему классу параметров
.CtlReturn = ctlForReturn ‘ элемент управления, куда записать значение
‘передача управляющему классу параметров для настройки
.Parametr = Parametr
‘вызов формы диалога
.ShowDialog
End With
End Sub Всё предварительная настройка завершена.
Как это работает?
А работает всё предельно просто. Где и когда нужно вызывается процедура вызова диалоговой формы, например, в обработчике события нажатия кнопки (листинг 4):
Private Sub btnDialog_Click()
ShowDialogForm Me.ctlUserValue, 1
End Sub
И всё! А всё остальное делает уже класс, управляющей этой диалоговой формой.
Резюме
Скажите, что слишком много работы для организации какой-то диалоговой формы? Мы ведь речь ведём не о простой диалоговой формы на один диалог, а о настраиваемой диалоговой форме. То есть эту форму можно использовать для нескольких однотипных случаев. Так, если случаев три, то при обычном подходе пришлось бы разработать три очень сильно похожие диалоговые формы (один раз разработать одну, создать две копии, правильно отредактировать копии). Для вызова этих форм надо было писать один и тот же код с небольшими вариантами. Опять-таки один раз разработать, создать две копии, правильно отредактировать.
В случае с настраиваемой диалоговой формой. Форма будет одна. Плюс один класс для управления. Плюс одна процедура для вызова диалоговой формы. Что даёт нам возможность в будущем увеличить количество диалоговых форм такого типа, не добавляя практически ни одной новой формы, а лишь передавая в процедуру вызова другие значения параметров (конечно, при условии достаточно хорошо продуманного класса диалоговых форм или не слишком сложных диалоговых форм)!
Есть ещё один способ организации настраиваемой диалоговой формы – создание экземпляра формы, настройка, вывод на экран, ожидание закрытия (или скрытия) формы с помощью бесконечного цикла и встроенной функции VBA DoEvents, которая позволяет обрабатывать текущие события Access. Этот способ широко описан в специальной литературе. Предложенный в данной статье способ организации настраиваемой диалоговой формы на основе экземпляра формы не использует бесконечный цикл. Что, на мой взгляд, является хоть и небольшим, но преимуществом.
|