При разработке интерфейсов на управляемых формах 1С мы периодически сталкиваемся с неприятной особенностью: после ввода данных в поле и попытке кликнуть мышкой на следующий элемент управления, фокус не переходит с первого раза. Требуется сделать «лишний клик», чтобы активировать нужное поле ввода. При этом, если заканчивать ввод нажатием клавиши Enter, переход на следующий элемент отрабатывает корректно и мгновенно. Давайте разберем эту ситуацию по шагам и выясним причину такого поведения системы, а также рассмотрим рабочие способы решения данной проблемы.
Чаще всего подобная проблема возникает при работе с программно генерируемыми полями ввода или выбора, для которых назначен обработчик события ПриИзменении. Экспериментальным путем легко выяснить, что сбой фокуса происходит из-за вызова процедур с директивой &НаСервере внутри обработчика изменения. Даже если серверная процедура абсолютно пустая, сам факт контекстного вызова ломает естественный пользовательский поток.
Техническая причина кроется в механизмах отрисовки и обновления интерфейса платформы (особенно ярко это начало проявляться с релизов 8.3.14 - 8.3.15). Когда из события ПриИзменении вызывается контекстная серверная процедура, платформа 1С собирает все данные формы, отправляет их на сервер, а по возвращении на клиент полностью перерисовывает (обновляет) состояние элементов формы. В этот микромомент прерывается UI-поток: система просто игнорирует клик пользователя по другому полю, так как форма была принудительно обновлена.
Рассмотрим подробнее самый правильный и архитектурно верный способ исправления этой ошибки. Нам необходимо избавиться от полной передачи контекста формы на сервер при каждом изменении реквизита.
Для этого процедуру, которая обрабатывает изменение данных, нужно вызывать не с директивой &НаСервере, а с директивой &НаСервереБезКонтекста. При вызове внеконтекстной процедуры платформа не гоняет весь объем данных формы на сервер и обратно. Следовательно, жесткого обновления формы не происходит, и клиент 1С нормально завершает действие мыши — фокус переходит куда нужно с первого раза.
Важное архитектурное правило: В идеальном коде 1С с директивой &НаСервере должны определяться только предопределенные обработчики событий самой формы (например, ПриСозданииНаСервере). Все остальные серверные вызовы, особенно из событий элементов, желательно делать внеконтекстными, передавая в них только нужные параметры.
Пример правильного вызова:
&НаКлиенте
Процедура ДинамическоеПолеПриИзменении(Элемент)
// Получаем необходимые данные из текущих элементов
ТекущееЗначение = ЭтаФорма.ДанныеПоля;
// Вызываем внеконтекстную процедуру, передавая только то, что нужно
Результат = ОбработатьИзменениеНаСервереБезКонтекста(ТекущееЗначение);
// Обновляем данные на клиенте
ЭтаФорма.РезультатПоля = Результат;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОбработатьИзменениеНаСервереБезКонтекста(Значение)
// Выполняем логику без тяжелого контекста формы
Возврат Значение * 2; // Условная логика
КонецФункции
Да, переход на &НаСервереБезКонтекста может потребовать серьезного пересмотра логики (особенно если в обработке задействованы таблицы значений или деревья), но это гарантированно решит проблему с фокусом и ускорит работу формы в целом.
Посмотрим на примеры, когда переписать логику под внеконтекстный вызов слишком сложно или трудозатратно (например, если нужно плотно работать с множеством динамических реквизитов всей формы сразу). В таких случаях мы можем применить обходные маневры.
ПриИзменении на клиенте мы не вызываем серверный код напрямую. Вместо этого мы используем метод ПодключитьОбработчикОжидания с минимальной задержкой (например, 0.1 секунды). Это позволяет платформе сначала корректно отработать клик пользователя, перевести фокус, а уже через мгновение асинхронно выполнить серверный код.
&НаКлиенте
Процедура РеквизитПриИзменении(Элемент)
ПодключитьОбработчикОжидания("ОтложеннаяОбработкаИзменения", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОтложеннаяОбработкаИзменения()
ОбработкаИзмененияНаСервере();
КонецПроцедуры
ТекущийЭлемент.
&НаКлиенте
Процедура Поле1ПриИзменении(Элемент)
ВыполнитьТяжелыйКодНаСервере();
ЭтаФорма.ТекущийЭлемент = Элементы.Поле2; // Принудительно ставим фокус
КонецПроцедуры
ОбновлениеТекстаРедактирования со значением ОбновлениеТекстаРедактирования.ПриИзмененииЗначения иногда предотвращает агрессивный сброс состояния элемента при обновлении формы. Установить это свойство можно как в конфигураторе, так и программно при генерации элемента.
Подводя итог: если вы столкнулись с проблемой лишнего клика, в первую очередь постарайтесь оптимизировать код, заменив вызовы &НаСервере на &НаСервереБезКонтекста. Это не только исправит досадный баг интерфейса, но и сделает ваше приложение более быстрым и отзывчивым для пользователей.