Немодальные диалоги
Немодальный диалог в отличие от модального допускает
переключение на другие немодальные окна клиента ASB (мышью или клавишами Num+ и
Num*). Поддержан в версии
14.263.110 на основе класса DIALOG.
Немодальный диалог следует создавать так.
- Создать экземпляр класса DIALOG, добавить в него элементы управления.
- Поместить созданный экземпляр DIALOG в перманентную переменную, чтобы
диалог пережил создавший его программный комплекс.
- Присвоить свойству DIALOG.OnProcessUserInput
ссылку на процедуру обслуживания немодального диалога, причем до того, как
диалог станет видимым (после - запрещено).
- Если нужно, чтобы при показе немодальный диалог был размещен ниже всех
видимых окон по оси Z, сбросить флаг DIALOG.ShowAtTheTop.
По умолчанию флаг поднят, немодальный диалог размещается выше всех видимых
окон.
- Сделать диалог видимым, т.е. поднять флаг
DIALOG.Visible
и вызвать метод DIALOG.Redraw.
Это можно сделать только из
программного комплекса, запущенного опциями
CALL, CallBP или
RunProg. Предыдущие действия можно было
выполнить из любого программного комплекса, в том числе из обработчиков
оконных событий и NTF.
- Завершить программный комплекс.
До завершения диалог фактически не является немодальным.
Для вызова процедуры обслуживания немодального диалога используется тот же
механизм, что и для выполнения опции CALL из меню,
поэтому процедура обслуживания должна быть размещена в головном модуле. Назначение процедуры
обслуживания - обрабатывать события диалога и его элементов управления, т.е.
вызывать DIALOG.Wait,
и, при необходимости, перерисовывать диалог по таймауту. Процедура обслуживания
должна выглядеть приблизительно так.
PROCEDURE OnProcessUserInput(dlg: DIALOG);
BEGIN
IF dlg.Active THEN
WHILE dlg.Visible & ~dlg.ExternalUserInputReceived DO
(* обновление вида диалога *)
dlg.Wait(10); (* 10 секунд - условно *)
END;
ELSE
dlg.Wait(0); (* для фонового вызова DIALOG.OnSize вследствие изменения размеров главного окна *)
END;
END {OnProcessUserInput};
Цикл, в котором вызывается DIALOG.Wait, очень похож на тот, что
пишется для модального диалога. Разница в дополнительной проверке флагового
свойства DIALOG.ExternalUserInputReceived.
Поднятый флаг означает, что в очереди ввода присутствует событие, не относящееся
к немодальному диалогу, например Num+ для переключения на следующее
открытое окно, или клик в пустое место главного окна для поднятия главного меню,
или Alt+F9 для выбора набора палитр, или NTF-программа при помощи
LIKEMENU поместила опции в очередь команд. Если флаг
поднят, процедура обслуживания должна завершиться, чтобы дать системе обработать
событие.
Флаговое свойство DIALOG.Active
поднято, когда окно немодального диалога активно. Пока "активное окно" можно
понимать как верхнее, точное определение будет приведено
в следующем разделе. Ветка ~DIALOG.Active написана
для вызова обработчика DIALOG.OnSize
(изнутри DIALOG.Wait) для неактивного окна немодального диалога вследствие
изменения размеров главного окна. Такой вызов поддержан начиная с версии
14.263.130.
Если перерисовывать диалог не нужно, не нужен и цикл; процедуру
обслуживания можно упростить до приведенного ниже вида.
PROCEDURE OnProcessUserInput(dlg: DIALOG);
BEGIN
IF dlg.Active THEN
dlg.Wait();
ELSE
dlg.Wait(0);
END;
END {OnProcessUserInput};
А если учесть, что для неактивного немодального диалога метод DIALOG.Wait
возвращает управление без ожидания, процедура обслуживания будет выглядеть еще
короче.
PROCEDURE OnProcessUserInput(dlg: DIALOG);
BEGIN
dlg.Wait();
END {OnProcessUserInput};
Нетривиальный момент: нажатие Esc не только прерывает три
вышеприведенных варианта процедуры обслуживания, но и скрывает немодальный
диалог. Дело в том, что в случае неудачного завершения процедуры обслуживания
(необработанное исключение) диалог скрывается автоматически. А нажатие Esc
в диалоге именно выбрасывает исключение 301 класса
Exc_TermByUser.
По завершении программного комплекса, сделавшего видимым немодальный
диалог(и), будет автоматически создан(ы) процесс(ы) обслуживания немодального диалога,
подобный процессам Edit, View,
ShowWin, Survey и т.п.
- Размещение созданного процесса обслуживания в списке процессов, а равно
размещение окна в списке открытых окон ("Ctrl+Shift+Alt+F9 - Список открытых
окон") зависит от значения флагового свойства DIALOG.ShowAtTheTop
на момент показа немодального диалога.
- Если флаг поднят, созданный процесс обслуживания помещается в начало
списка процессов, т.е. получит управление немедленно, а окно помещается в
начало списка видимых окон.
- Если флаг сброшен, созданный процесс обслуживания помещается в конец
списка процессов, т.е. получит управление после завершения всех остальных
процессов, или в случае активации окна пользователем, а окно помещается в
конец списка видимых окон.
- Если программный комплекс создал несколько немодальных диалогов,
порядок соответствующих процессов в списке процессов и окон в списке окон
зависит от порядка вызовов метода DIALOG.Redraw,
который сделал диалог видимым. Среди диалогов с поднятым флагом
DIALOG.ShowAtTheTop последний показанный будет верхним. Среди диалогов со
сброшенным флагом DIALOG.ShowAtTheTop последний показанный будет нижним.
- Стек контекстов процесса немодального диалога состоит из контекстов Global и ModelessDialog.
Описанный в v32.mnu контекст ModelessDialog
содержит меню окна: "Предыдущее", "Следующее", "Список открытых окон", "Цвета".
- Процесс немодального диалога завершится, когда диалог станет невидимым.
Если впоследствии немодальный диалог снова станет видимым, для
него вновь будет создан процесс немодального диалога.
Процесс обслуживания немодального диалога в цикле выполняет следующее.
- Вызывает процедуру обслуживания немодального диалога.
- Если процедура обслуживания завершилась неудачно (необработанным
исключением), немодальный диалог автоматически скрывается, чтобы избежать
зависания клиента ASB.
- Проверяет видимость немодального
диалога. Если диалог невидим, процесс завершается.
- Немодальный диалог видим, значит из процедуры обслуживания вышли по
флагу DIALOG.ExternalUserInputReceived, чтобы дать системе обработать
событие ввода, не относящееся к немодальному диалогу. Процесс получает
событие ввода (опцию) и анализирует ее. Здесь в частности может быть
открыто меню.
- Если не требуется переключение на другой процесс, процесс немодального
диалога обрабатывает опцию и возвращается в начало цикла на вызов
обслуживающей процедуры. Собственно, процесс немодального диалога в
состоянии обработать только опции Cancel и
CloseWin (скрытием диалога).
Прочие опции игнорируются.
- Если требуется переключение на другой процесс, предварительно
выполняется валидация редактируемого поля (если есть), т.е. строка
преобразуется в значение, а затем вызывается обработчик EDIT.OnSuccessfulChange.
- После валидации (успешной или неуспешной) снова проверяется
видимость немодального диалога. Если невидим, процесс завершается.
- Если валидация прошла успешно (или ее вовсе не было), происходит
переключение на другой процесс. В противном случае процесс возвращается в
начало цикла - на вызов процедуры обслуживания.
Теперь можно уточнить понятие активного немодального диалога. Немодальный диалог является
активным, пока его процесс обслуживания является верхним процессом,
исключая промежутки времени, когда поднято меню, или открыто немодальное
сообщение/форма, дочернее к немодальному диалогу.
Вызов процедуры обслуживания отличается рядом особенностей.
- Вызов процедуры обслуживания невозможно отключить при помощи
Ctrl+F5, опция ASLTogl на него не
действует.
- Для активного немодального диалога отлаживаются только входы в
процедуры-обработчики диалога и его контролов. Собственно процедуру
обслуживания отладить невозможно, поскольку переключение в окно отладчика и
обратно порождает события инактивации/активации главного окна. Уровень
отладки (Ctrl+Alt+F5): "Отладка всех
программ, включая запрещенные к отладке".
- В случае неудачного завершения процедуры обслуживания (необработанным
исключением) очередь команд не
очищается.
- Для немодального диалога процедуру KBWAIT
вызвать можно, но кнопка WaitInt срабатывать не будет.
- Функция MAINTHREADTRNCOUNT не учитывает
транзакцию программы обслуживания немодального диалога.
Если необходимо регулярно обновлять вид неактивного немодального диалога,
программист может включить фоновые вызовы процедуры обслуживания по таймеру.
Свойство DIALOG.InactiveTimeout задает интервал в секундах между фоновыми
вызовами. По умолчанию свойство содержит пустое значение, т.е. фоновых вызовов
нет. Если свойство непусто - процедура будет вызываться, но не чаще одного раза в
2.5 секунды.
Подобно NTF-программам фоновый вызов процедуры обслуживания происходит в
случайный момент, в том числе во время работы другой программы. Соответственно,
для него действуют почти те же ограничения, что и для NTF-программ.
- Фоновый вызов процедуры обслуживания должен завершиться как можно
быстрее. Прервать его при помощи Esc невозможно.
Если он зависнет (например, в бесконечном цикле),
зависнет сессия клиента ASB. Если будет работать
долго, пользователь ощутит неприятные задержки.
- Во встроенном пространстве имен отсутствует группа процедур для работы с
нерегулярными таблицами БД.
- Процедуре обслуживания предоставляются не
перманентные переменные, а их
копии, снятые в момент старта программы. Т.е. модифицировать перманентные
переменные можно, но бессмысленно.
- Автоматически устанавливается режим подавления сообщений
MSG_SUPPRESSION_ALL. Но, в отличие от NTF, в случае завершения программы
необработанным исключением, связанное с исключением подавленное сообщение
будет показано.
- Очередной фоновый вызов процедуры обслуживания не будет выполнен до тех
пор, пока не завершится предыдущий.
- Отладка фоновых вызовов процедуры обслуживания включается (Ctrl+Alt+F5)
на уровне "Отладка всех программ, включая запрещенные к отладке".
С учетом фоновых вызовов процедура обслуживания должна выглядеть так.
PROCEDURE OnProcessUserInput(dlg: DIALOG);
BEGIN
IF dlg.Active THEN (* диалог активен *)
WHILE dlg.Visible & ~dlg.ExternalUserInputReceived DO
(* обновление вида диалога *)
dlg.Wait(10); (* 10 секунд - условно *)
END;
ELSE (* фоновый вызов *)
(* обновление вида диалога *)
dlg.Wait(0);
END;
END {OnProcessUserInput};
В объектной модели видеосистемы немодальному диалогу соответствует тип окна
WINDOW.Type_ModelessDialog и
класс окна MODELESS_DIALOG_WINDOW.
Экземпляр MODELESS_DIALOG_WINDOW может присутствовать в коллекции
APP.VisibleWindows, также на него может ссылаться
свойство
APP.TopWindow. Свойство
WINDOW.Id
можно использовать для активации немодального диалога опцией
GoToWin.
Свойства WINDOW.Width и WINDOW.Depth
(размеры окна в знакоместах) для немодального диалога не имеют смысла и возвращают 0.