Многие разработчики и пользователи 1С:Предприятие 8.3 (особенно на релизах платформы 8.3.23, 8.3.24 и выше) сталкиваются с крайне неприятной ситуацией. При попытке быстро набрать текст в строке поиска динамического списка (например, в справочнике «Номенклатура» или «Контрагенты»), курсор внезапно «прыгает», текст стирается, а ввод начинается заново. Создается впечатление, что программа «не успевает» за пользователем и сбрасывает его действия.
В этой статье мы подробно разберем, почему возникает этот эффект «гонки состояний», проанализируем внутренние механизмы платформы и рассмотрим несколько эффективных способов решения проблемы, включая программные «костыли» и правильную архитектурную переработку формы.
Для начала проанализируем ситуацию с точки зрения работы платформы. В современных версиях 1С поиск в динамических списках работает асинхронно. Это означает, что при вводе каждого символа платформа не блокирует интерфейс, а отправляет фоновый запрос на сервер для фильтрации данных. Как только данные отфильтрованы, список на форме обновляется.
Проблема возникает в тот момент, когда в динамическом списке определен обработчик события ПриАктивизацииСтроки. Рассмотрим цепочку событий, которая приводит к ошибке:
ПриАктивизацииСтроки.СписокСтрокаПоиска и возвращается на саму таблицу. В результате ввод прерывается, а следующий символ может либо не напечататься, либо стереть предыдущее значение из-за особенностей фокуса.Самый простой и быстрый способ избежать нежелательного обновления формы — это добавить проверку того, где в данный момент находится курсор пользователя. Если пользователь активно печатает в строке поиска, нам, скорее всего, не нужно выполнять тяжелые операции по обновлению остатков или цен прямо в этот микросекундный интервал.
Рассмотрим пример кода, который необходимо добавить в самое начало обработчика ПриАктивизацииСтроки на клиенте:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Проверяем, не находится ли фокус в строке поиска
Если ЭтаФорма.ТекущийЭлемент.Имя = "СписокСтрокаПоиска" Тогда
Возврат;
КонецЕсли;
// Далее идет основной код обработки активизации строки
ОбновитьДополнительныеДанныеНаФорме();
КонецПроцедуры
Разберем этот пример. Системное имя элемента строки поиска в управляемых формах обычно соответствует шаблону ИмяСпискаСтрокаПоиска. Проверяя свойство ТекущийЭлемент, мы блокируем выполнение кода, вызывающего перерисовку, пока пользователь взаимодействует с поисковым полем. Это позволяет платформе спокойно завершить асинхронные процессы фильтрации без прерывания ввода.
Если логика в ПриАктивизацииСтроки критически важна и должна выполняться всегда, использование простого Возврат может привести к тому, что данные на форме будут неактуальными. В этом случае проанализируем более элегантное решение — использование метода ПодключитьОбработчикОжидания.
Этот метод позволяет «отложить» выполнение тяжелого кода на доли секунды. Этого времени платформе достаточно, чтобы завершить все процессы отрисовки поиска. Посмотрим на реализацию:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Отключаем предыдущий вызов, если пользователь быстро перемещается по строкам
ОтключитьОбработчикОжидания("ВыполнитьОбновлениеДанных");
// Запускаем небольшую задержку (например, 0.2 секунды)
// Это позволит строке поиска «проскочить» момент обновления без потери фокуса
ПодключитьОбработчикОжидания("ВыполнитьОбновлениеДанных", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьОбновлениеДанных()
// Здесь выполняем все серверные вызовы и обновление интерфейса
ОбновитьПоказателиОбъектаСервер();
КонецПроцедуры
Использование этого метода значительно повышает «отзывчивость» интерфейса. Пользователь может быстро листать список или вводить текст, и серверные вызовы не будут наслаиваться друг на друга, вызывая фризы и сброс фокуса.
Проанализируем еще одну важную деталь. Одной из причин сброса фокуса является контекстный серверный вызов. Когда мы вызываем процедуру &НаСервере, на сервер передается все состояние формы (данные всех реквизитов). После возврата управления платформа пытается синхронизировать состояние элементов управления.
Для решения проблемы с поиском рекомендуется:
&НаСервереБезКонтекста там, где это возможно.Ссылка текущей строки) вместо передачи всей формы.Видимость, Доступность и Заголовок для элементов, не связанных напрямую с выбранной строкой, внутри обработчика активизации.Посмотрим на пример оптимизированного вызова:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Используем неконтекстный вызов для получения данных
ДопИнфо = ПолучитьДанныеСерверБезКонтекста(ТекущиеДанные.Ссылка);
// Обновляем только конкретный реквизит формы, что меньше влияет на фокус
Объект.ИнформационнаяНадпись = ДопИнфо;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеСерверБезКонтекста(ВыбраннаяСсылка)
// Логика получения остатков или цен
Возврат "Остаток на складе: " + Справочники.Номенклатура.ПолучитьОстаток(ВыбраннаяСсылка);
КонецФункции
Стоит отметить, что в некоторых релизах платформы (например, ветка 8.3.24) наблюдается странное поведение даже в самом Конфигураторе. При поиске по текстам модулей или в окне «Перейти к строке» фокус может самопроизвольно перескакивать в основное окно кода. Это подтвержденная особенность (баг) определенных релизов, которая лечится только обновлением платформы на более стабильный патч.
Однако для пользовательских форм в 99% случаев виноват именно избыточный код в ПриАктивизацииСтроки. Если вы используете типовые решения (например, продукты фирмы «Рарус» или сложные конфигурации от 1С), проверьте, не добавлены ли там расширения, которые вносят свои изменения в логику активации строки.
Для устранения проблемы «прыгающего» поиска выполним следующие шаги:
ПриАктивизацииСтроки.Если ЭтаФорма.ТекущийЭлемент.Имя = "СписокСтрокаПоиска" Тогда Возврат; для мгновенного исправления.Соблюдение этих правил позволит сделать работу пользователей в тяжелых справочниках комфортной и избавит от раздражающих сбоев при поиске номенклатуры или контрагентов.