При разработке интерфейсов ввода данных часто возникает задача жесткого контроля: не выпускать пользователя из поля ввода до тех пор, пока введенная информация не пройдет валидацию. Разберем типичную ситуацию: оператор вводит значение, срабатывает обработчик ПриИзменении, алгоритм находит ошибку, выдает предупреждение и возвращает курсор обратно в элемент управления. Однако если после этого пользователь ничего не исправил и снова нажал клавишу перехода или кликнул мышью в другое место формы, платформа 1С беспрепятственно позволяет покинуть поле. В этой статье мы подробно проанализируем поведение событийной модели 1С и рассмотрим надежные способы решения данной задачи.
Выясним причину такого поведения платформы. В отличие от многих других сред разработки, в 1С:Предприятии концептуально отсутствует стандартное событие потери фокуса элементом (аналог onBlur или LostFocus). Архитектура платформы ориентирована на фиксацию изменений бизнес-данных, а не на детальный трекинг перемещения указателя мыши или курсора по элементам формы.
Рассмотрим подробнее два ключевых события элемента управления ПолеВвода:
Если в поле возникла ошибка, мы вернули фокус назад через установку свойства ЭтаФорма.ТекущийЭлемент, но пользователь не нажал ни одной клавиши внутри поля, внутренний буфер платформы считается немодифицированным. При повторной попытке перехода платформа видит, что значение не менялось, и просто переводит фокус на другой элемент, полностью игнорируя повторный вызов процедур ПриИзменении и ОкончаниеВводаТекста.
Посмотрим на обходной путь, предложенный непосредственно в обсуждении проблемы. Если платформа требует обязательного изменения текста для повторного вызова события, можно сымитировать нажатие клавиш операционной системы с помощью объекта WScript.Shell сразу после возврата фокуса в поле ввода.
Разберем по шагам логику этого решения:
SendKeys переводим каретку в конец строки, добавляем пробел и тут же удаляем его клавишей Backspace.
Shell = Новый COMОбъект("WScript.Shell");
Если НЕ ПроверитьСтрокуКомбинации(Комбинация1, ТекстОшибки) Тогда
Предупреждение(ТекстОшибки);
ЭтаФорма.ТекущийЭлемент = ЭлементыФормы.Комбинация1;
Shell.SendKeys("{END}");
Shell.SendKeys(" ");
Shell.SendKeys("{BACKSPACE}");
Возврат;
КонецЕсли;
В результате такой манипуляции итоговый текст в поле остается прежним, но флаг модификации внутреннего буфера взводится заново. При следующей попытке покинуть поле событие ПриИзменении гарантированно сработает повторно.
Важное ограничение метода: Использование WScript.Shell жестко привязывает решение к операционной системе Windows и может вызывать непредвиденные сбои, если в момент отправки нажатий пользователь переключит активное окно (нажатия клавиш могут уйти в стороннее приложение).
Проанализируем более надежный и полностью штатный способ, не требующий внешних COM-объектов. Поскольку перехватить уход с текущего элемента напрямую невозможно, мы можем перехватить получение фокуса всеми остальными элементами формы.
В обычных формах элементы управления (кнопки, другие поля ввода, табличные части) поддерживают событие ПриВходе. Разберем реализацию этого алгоритма:
ПриВходе.Посмотрим на пример реализации модуля формы:
Процедура СоседнийЭлементПриВходе(Элемент)
// Проверяем корректность критически важного поля
Если НЕ ПроверитьКорректность(Комбинация1) Тогда
Предупреждение("Поле 'Комбинация 1' заполнено некорректно! Завершите ввод.");
ЭтаФорма.ТекущийЭлемент = ЭлементыФормы.Комбинация1;
КонецЕсли;
КонецПроцедуры
Такой подход гарантирует, что пользователь не сможет продолжить работу с формой, нажать кнопку отправки или перейти к следующему полю, пока предыдущее заполнено с ошибками.
Еще один распространенный интерфейсный паттерн, исключающий необходимость «ловить» фокус, — последовательное открытие доступа к элементам. Если оператор заполняет цепочку полей, логично запретить взаимодействие со следующими шагами до успешного прохождения текущего.
Разберем по шагам, как организовать такую логику:
ЭлементыФормы.Комбинация2.Доступность = Ложь;.ПриИзменении первого поля выполняем валидацию.ЭлементыФормы.Комбинация2.Доступность = Истина; и переводим на него фокус.Данная схема исключает человеческий фактор: оператор физически не сможет покинуть поле для редактирования других реквизитов, пока не введет валидное значение либо не очистит строку полностью.
В ситуациях, когда форма содержит сложную структуру элементов и назначать событие ПриВходе каждому элементу трудоемко, можно задействовать периодическую проверку активного элемента через метод ПодключитьОбработчикОжидания.
Рассмотрим структуру данного подхода:
Перем ПредыдущийЭлемент;
Процедура ПриОткрытии()
ПредыдущийЭлемент = ЭлементыФормы.Комбинация1;
ПодключитьОбработчикОжидания("ПроверитьСменуФокуса", 1);
КонецПроцедуры
Процедура ПроверитьСменуФокуса()
Текущий = ЭтаФорма.ТекущийЭлемент;
Если ПредыдущийЭлемент = ЭлементыФормы.Комбинация1 И Текущий <> ЭлементыФормы.Комбинация1 Тогда
Если НЕ ПроверитьКорректность(Комбинация1) Тогда
Предупреждение("Исправьте значение в поле 'Комбинация 1'!");
ЭтаФорма.ТекущийЭлемент = ЭлементыФормы.Комбинация1;
Возврат;
КонецЕсли;
КонецЕсли;
ПредыдущийЭлемент = Текущий;
КонецПроцедуры
Особенности реализации: Следует учитывать, что минимальный интервал вызова обработчика ожидания в платформе 8.2 составляет 1 секунду. Это может приводить к небольшой визуальной задержке возврата фокуса, поэтому данный метод уступает прямому перехвату через ПриВходе или управлению доступностью элементов формы.