При разработке интерфейсов на управляемых формах 1С:Предприятие 8.3 программисты часто сталкиваются с неприятным визуальным эффектом: при выборе строки в табличной части или динамическом списке вся таблица внезапно «прыгает», позиционируя активную строку ровно по центру видимой области. Это сбивает пользователя, особенно если он привык быстро просматривать список сверху вниз.
В этой статье мы подробно разберем причины такого поведения платформы, проанализируем механизмы перерисовки интерфейса и найдем несколько способов решения проблемы, от простых до профессиональных.
Основная причина кроется в механизме, который называется Layout Reflow (перестроение геометрии формы). Когда в обработчике события ПриАктивизацииСтроки происходит изменение свойств элементов формы, платформа инициирует пересчет размеров и положений всех виджетов.
Рассмотрим ситуацию: у нас есть декорация типа «Надпись» (Элементы.ИнфоНадпись), и мы меняем ее текст при переходе по строкам. Платформа видит, что заголовок изменился. Поскольку текст может быть разной длины или занимать несколько строк, система обязана пересчитать, не изменится ли высота или ширина этой надписи, и не нужно ли из-за этого подвинуть соседние элементы (включая саму таблицу).
Во время этого глобального пересчета фокус находится на таблице. Чтобы гарантировать, что активная строка не исчезнет из поля зрения (например, если таблица уменьшится в размере), платформа применяет алгоритм принудительного позиционирования. В версии 8.3.12 и некоторых других этот алгоритм часто срабатывает избыточно, помещая строку в самый центр формы.
Проанализируем типичную ошибку начинающего разработчика. Попытка получить данные для надписи напрямую с сервера внутри события ПриАктивизацииСтроки:
&НаКлиенте
Процедура ЗапасыПриАктивизацииСтроки(Элемент)
// ОПАСНО: Прямой вызов сервера в этом событии
Элементы.ИнформацияОНоменклатуре.Заголовок = ПолучитьИнформациюНаСервере(Элементы.Запасы.ТекущиеДанные.Номенклатура);
КонецПроцедуры
Согласно справке 1С (Синтаксис-Помощник), в обработчике данного события нельзя использовать серверные методы формы с директивой &НаСервере. Любой контекстный серверный вызов приводит к передаче всего состояния формы на сервер и обратно. Это не только замедляет интерфейс («фризы» при перемещении по таблице), но и гарантированно вызывает полный сброс визуального состояния элементов управления, что и провоцирует «прыжки» строк.
Это самый эффективный и рекомендуемый способ, который помог автору темы. Суть заключается в разнице между изменением свойств элемента и изменением данных реквизита.
Когда мы меняем свойство Заголовок у объекта Декорация, платформа воспринимает это как изменение структуры оформления. Но если мы свяжем элемент формы с реквизитом, ситуация меняется.
Пошаговый алгоритм решения:
ТекстИнформации, тип Строка).Поле надписи. Это сделает его визуально неотличимым от обычной декорации.Посмотрим на пример правильного кода:
&НаКлиенте
Процедура ЗапасыПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элементы.Запасы.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// МЕНЯЕМ ДАННЫЕ, А НЕ СВОЙСТВО ЭЛЕМЕНТА
ТекстИнформации = ТекущиеДанные.КэшированнаяИнформация;
КонецЕсли;
КонецПроцедуры
Почему это работает? Платформа оптимизирована для частого обновления данных. Изменение значения реквизита, отображаемого в Поле надписи, вызывает локальное обновление только этого конкретного поля. Геометрия формы при этом не пересчитывается, и таблица остается в покое.
Если информации для вывода много или её получение всё же требует обращения к серверу (хотя этого стоит избегать), используем «стандартный костыль» опытных разработчиков — ПодключитьОбработчикОжидания.
Это позволяет разнести во времени момент активации строки и момент обновления интерфейса. Если пользователь быстро «пролистывает» таблицу стрелками, интерфейс не будет судорожно дергаться. Обновление произойдет только тогда, когда пользователь задержится на строке более чем на 0.1–0.2 секунды.
Рассмотрим реализацию:
&НаКлиенте
Процедура ЗапасыПриАктивизацииСтроки(Элемент)
// Отменяем предыдущее задание, если пользователь нажал стрелку быстро
ОтключитьОбработчикОжидания("ОбновитьИнфоПанель");
// Запускаем обновление с минимальной задержкой
ПодключитьОбработчикОжидания("ОбновитьИнфоПанель", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьИнфоПанель()
ТекущиеДанные = Элементы.Запасы.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// Здесь выполняется обновление реквизита или заголовка
ТекстИнформации = "Данные по номенклатуре: " + ТекущиеДанные.Номенклатура;
КонецЕсли;
КонецПроцедуры
Поскольку процедура ОбновитьИнфоПанель вызывается вне контекста события ПриАктивизацииСтроки, платформа гораздо спокойнее реагирует на изменения интерфейса, и центрирование строки происходит значительно реже или исчезает вовсе.
Иногда прыжки вызваны тем, что при изменении текста надпись меняет свою высоту (например, текст перескочил на вторую строку). Чтобы предотвратить это, проанализируем настройки оформления элемента:
Высота для элемента надписи (например, 2 или 3 строки).РастягиватьПоГоризонтали = Ложь.МаксимальнаяШирина.Если размер контейнера, в котором находится текст, остается неизменным вне зависимости от его содержания, 1С не видит причин для полного перестроения (Layout) формы, что помогает избежать нежелательной прокрутки таблицы.
Выясним, какой метод выбрать. Если вам нужно просто выводить текст из текущей строки — используйте Решение №1 (связка с реквизитом формы). Это наиболее «чистый» способ с точки зрения архитектуры управляемого приложения. Если же данные нужно вычислять или подтягивать из базы — используйте Решение №2 с обработчиком ожидания, чтобы сохранить отзывчивость интерфейса и комфорт пользователя.
Помните, что работа с событием ПриАктивизацииСтроки требует особой осторожности: это одно из самых часто вызываемых событий, и любая неоптимальность в нем мгновенно портит впечатление от работы с программой.