При разработке интерфейсов для планшетов и сенсорных терминалов на базе 1С 7.7 программисты часто сталкиваются с тем, что привычные методы эмуляции клавиатуры, такие как SendKeys через WScript.Shell, работают нестабильно. Особенно остро эта проблема проявляется в формах обработок: при нажатии пальцем на кнопку событие отрабатывает, кнопка визуально нажимается, но переключение окон (комбинация Ctrl+Tab) не происходит. Это связано с особенностями обработки системных сообщений WM_POINTER и WM_TOUCH в Windows, которые «перехватывают» фокус ввода иначе, чем обычный клик мышью.
В этой статье мы подробно рассмотрим, как реализовать надежное циклическое переключение между открытыми окнами, используя возможности внешней компоненты Formex. Мы разберем, почему стандартные методы подводят, и напишем программный код для управления фокусом окон.
Проанализируем ситуацию: когда пользователь нажимает на экран планшета, операционная система генерирует последовательность событий. В 1С 7.7 форма обработки (объект Обработка) имеет специфический цикл обработки сообщений. Эмуляция нажатия SendKeys("^({TAB})") отправляет команду в активное окно. Однако в момент касания сенсорного экрана фокус может на долю секунды «залипнуть» на самом элементе управления или уйти в системный слой обработки жестов. В результате команда уходит в пустоту, так как ни одно окно не готово принять клавиатурный ввод в этот микроинтервал времени.
Для решения этой задачи нам необходимо отказаться от эмуляции клавиатуры и перейти к прямому управлению объектами окон через программный интерфейс Formex.
Первым делом нам нужно понять, какие окна сейчас открыты в сеансе пользователя. Для этого в Formex существует объект Сервис и его метод СписокВсехОткрытыхФорм(). Этот метод возвращает таблицу значений, содержащую контексты всех активных окон.
Рассмотрим пример функции, которая подготавливает список окон для переключения:
Процедура ПереключитьОкноНаСледующее()
Сервис = СоздатьОбъект("Сервис");
ТЗ_Окна = Сервис.СписокВсехОткрытыхФорм();
// ТЗ_Окна содержит колонки: "Заголовок", "Контекст", "Вид"
КоличествоОкон = ТЗ_Окна.КоличествоСтрок();
Если КоличествоОкон < 2 Тогда
Возврат; // Переключаться некуда
КонецЕсли;
// Определяем текущее активное окно
АктивныйКонтекст = Сервис.АктивноеОкно();
НомерТекущейСтроки = 0;
ТЗ_Окна.ВыбратьСтроки();
Пока ТЗ_Окна.ПолучитьСтроку() = 1 Цикл
Если ТЗ_Окна.Контекст = АктивныйКонтекст Тогда
НомерТекущейСтроки = ТЗ_Окна.НомерСтроки;
Прервать;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Выясним, какое окно должно стать следующим. Если текущее окно — последнее в списке, нам нужно вернуться в начало. Если мы не смогли определить текущее окно (например, фокус был на рабочем столе программы), выберем первое доступное.
Продолжим наш код, добавив логику выбора следующего индекса:
Если НомерТекущейСтроки = 0 Тогда
// Если фокус потерян, берем первое окно
СледующийНомер = 1;
ИначеЕсли НомерТекущейСтроки = КоличествоОкон Тогда
// Если мы на последнем окне, переходим к первому
СледующийНомер = 1;
Иначе
// Просто берем следующее по списку
СледующийНомер = НомерТекущейСтроки + 1;
КонецЕсли;
ТЗ_Окна.ПолучитьСтрокуПоНомеру(СледующийНомер);
КонтекстСледующегоОкна = ТЗ_Окна.Контекст;
Теперь самое важное — вывести выбранное окно на передний план и передать ему фокус ввода. Для этого мы будем использовать метод НаПереднийПлан(), который доступен для объектов типа «Окно» в Formex.
Важный нюанс для планшетов: просто вывести окно на передний план иногда недостаточно. Нужно принудительно заставить систему считать его активным. Рассмотрим финальную часть реализации:
// Получаем объект окна из контекста формы
Окно = СоздатьОбъект("Окна");
// В Formex можно работать с окном напрямую через контекст
Попытка
КонтекстСледующегоОкна.Форма.НаПереднийПлан();
// Для надежности на сенсорных экранах используем ВзятьВФокус
Сервис.ВзятьВФокус(КонтекстСледующегоОкна);
Исключение
// Если форма была закрыта в процессе, просто вызываем процедуру еще раз
ПереключитьОкноНаСледующее();
КонецПопытки;
КонецПроцедуры
Разберем еще одну интересную ситуацию. Иногда прямое управление из одного окна другим может блокироваться (например, если одна из форм модальная). В этом случае можно применить механизм ВнешнееСобытие.
Логика работы следующая:
Сервис.ВнешнееСобытие().ПриВнешнемСобытии.НаПереднийПлан() сама для себя.Пример кода отправки события:
Процедура НажатиеКнопкиПереключения()
Сервис = СоздатьОбъект("Сервис");
// Отправляем источнику "Система", событие "Переключить", данные - имя следующего окна
Сервис.ВнешнееСобытие("System", "SwitchWindow", "Next");
КонецПроцедуры
Поскольку мы обсуждаем сенсорные экраны, важно упомянуть масштабирование интерфейса. При переключении окон на устройствах с высоким разрешением (High DPI) элементы управления могут выглядеть мелкими. В сообщениях форума предлагалось использовать Класс Привязки и динамическое изменение шрифтов.
Проанализируем, как Formex помогает в этом: вы можете программно менять размер шрифта при открытии формы, используя метод УстановитьШрифт(). Это позволит сделать кнопки переключения окон («Домой», «Следующее окно») крупными и удобными для нажатия пальцем, независимо от физического размера дисплея.
Резюме: Для стабильной работы интерфейса на планшетах в среде 1С 7.7 следует избегать методов WScript.Shell. Использование таблицы открытых форм через Сервис.СписокВсехОткрытыхФорм() в сочетании с методом НаПереднийПлан() гарантирует, что переключение произойдет мгновенно и без потерь фокуса, что критически важно для оперативной работы пользователей.