Как изменить заголовок колонки в таблице 1С, чтобы форма не "прыгала"?

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке на управляемых формах 1С часто возникает задача динамически изменять интерфейс в зависимости от действий пользователя. Одна из таких задач — изменение заголовков колонок таблицы при переходе между строками. Например, если в колонке отображается значение субконто, логично в ее заголовке показывать наименование этого субконто, которое может быть разным для разных счетов.

Однако при реализации этой логики в событии ПриАктивизацииСтроки разработчики сталкиваются с неприятным побочным эффектом: таблица начинает "прыгать" или "мерцать". Активная строка смещается, фокус теряется, что вызывает сильный дискомфорт у пользователей. Разберем, почему это происходит и как этого избежать.

Выясняем причину: почему таблица "прыгает"

Проблема кроется в архитектуре клиент-серверного взаимодействия платформы 1С. Когда вы на клиенте пытаетесь изменить свойство элемента формы, которое влияет на ее структуру (например, Заголовок, Видимость, Доступность), платформа может инициировать контекстный серверный вызов.

Проанализируем ситуацию по шагам:

  1. Пользователь кликает на новую строку в таблице.
  2. Срабатывает клиентское событие ПриАктивизацииСтроки.
  3. Внутри обработчика выполняется код, который меняет заголовок колонки, например: Элементы.СписокСубконто.Заголовок = "Новый заголовок".
  4. Платформа определяет это изменение как существенное и отправляет на сервер запрос для обновления представления формы.
  5. Сервер обрабатывает запрос и возвращает клиенту новое описание формы.
  6. Клиент получает это описание и полностью перерисовывает форму или, как минимум, ее значительную часть, включая проблемную таблицу.

Именно эта полная перерисовка и воспринимается как "прыжок". Позиция прокрутки может сброситься, а сама платформа может повторно вызвать событие ПриАктивизацииСтроки, что в некоторых случаях приводит к бесконечному циклу.

Важный момент: поведение платформы может меняться от версии к версии. Начиная с версии 8.3.7 и в более поздних релизах, многие операции по изменению свойств элементов были оптимизированы и перестали вызывать серверный вызов. Однако полностью полагаться на это не стоит, так как в сложных формах или при определенных условиях старое поведение может сохраняться. Поэтому лучше использовать методы, которые гарантированно не вызывают перерисовку.

Решение 1: Использование свойства "ПутьКДаннымЗаголовка" (рекомендуемый способ)

Это самый правильный, производительный и элегантный способ решения проблемы. Его суть заключается в том, чтобы не менять свойство элемента формы напрямую, а привязать заголовок к отдельному реквизиту формы. Изменение значения реквизита — легковесная операция, которая не вызывает перерисовки.

Разберем по шагам:

  1. Создаем реквизит формы. В дереве реквизитов формы создайте новый реквизит, например, ЗаголовокКолонкиДебет с типом Строка. Создайте аналогичный реквизит для колонки кредита, если это необходимо (ЗаголовокКолонкиКредит).
  2. Связываем колонку с реквизитом. В дереве элементов формы выберите колонку таблицы, заголовок которой нужно менять. В палитре свойств найдите свойство ПутьКДаннымЗаголовка (TitleDataPath).
  3. Указываем путь. Нажмите на кнопку выбора в этом свойстве и укажите путь к вашему новому реквизиту формы (ЗаголовокКолонкиДебет).
  4. Модифицируем код. Теперь в обработчике события ПриАктивизацииСтроки вместо изменения свойства элемента формы нужно изменять значение созданного реквизита.

Посмотрим на пример кода. Вместо такого кода, который вызывает проблему:


&НаСервере
Процедура ОтображениеЗаголовков(ТекущаяСтрока)
    // ... логика получения наименования субконто ...
    ЭлементДебета.Заголовок = ОписаниеСчетаДт_["С" + СокрЛП(Позиция)] + " (Дт)";
    ЭлементКредита.Заголовок = ОписаниеСчетаКт_["С" + СокрЛП(Позиция)] + " (Кт)";
КонецПроцедуры

&НаКлиенте
Процедура УслугиПриАктивизацииСтроки(Элемент)
    ОтображениеЗаголовков(Элементы.Услуги.ТекущиеДанные);
КонецПроцедуры

Используем новый подход, работающий полностью на клиенте и без серверных вызовов:


// В реквизитах формы добавлены:
// ЗаголовокКолонкиДебет (Строка)
// ЗаголовокКолонкиКредит (Строка)
//
// У колонок "СубконтоДебет" и "СубконтоКредит" в свойстве "ПутьКДаннымЗаголовка"
// указаны эти реквизиты соответственно.

&НаКлиенте
Процедура УслугиПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.Услуги.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        // Если строка пустая, можно установить заголовки по умолчанию
        ЗаголовокКолонкиДебет = "Субконто (Дт)";
        ЗаголовокКолонкиКредит = "Субконто (Кт)";
        Возврат;
    КонецЕсли;

    // Предположим, что вся необходимая информация для заголовков
    // уже есть на клиенте или была получена ранее.
    // Если ее нет, то нужно будет сделать один серверный вызов для получения данных,
    // а не для изменения интерфейса.
    
    // Вместо Элементы.УслугиДебет.Заголовок = ...
    ЗаголовокКолонкиДебет = ПолучитьНаименованиеСубконтоДт(ТекущиеДанные) + " (Дт)";

    // Вместо Элементы.УслугиКредит.Заголовок = ...
    ЗаголовокКолонкиКредит = ПолучитьНаименованиеСубконтоКт(ТекущиеДанные) + " (Кт)";
КонецПроцедуры

Этот метод решает проблему полностью. Таблица перестанет "прыгать", так как платформа просто обновит текст в заголовке в соответствии со значением связанного реквизита, не перерисовывая всю форму.

Решение 2: Проверка на необходимость изменения

Если по каким-то причинам вы не можете использовать предыдущий метод, можно минимизировать количество перерисовок. Идея проста: менять заголовок только тогда, когда он действительно должен быть изменен.

Например, если пользователь перемещается между несколькими строками, для которых заголовок должен быть одинаковым, нет смысла выполнять присваивание на каждой строке. Добавим простую проверку.


&НаКлиенте
Процедура УслугиПриАктивизацииСтроки(Элемент)
    // ... код для получения нового заголовка
    НовыйЗаголовокДт = ПолучитьНаименованиеСубконтоДт(Элементы.Услуги.ТекущиеДанные) + " (Дт)";

    // Проверяем, отличается ли новый заголовок от текущего
    Если Элементы.УслугиДебет.Заголовок <> НовыйЗаголовокДт Тогда
        // Вызываем перерисовку только при реальном изменении
        Элементы.УслугиДебет.Заголовок = НовыйЗаголовокДт;
    КонецЕсли;

    // Аналогично для кредита
    НовыйЗаголовокКт = ПолучитьНаименованиеСубконтоКт(Элементы.Услуги.ТекущиеДанные) + " (Кт)";
    Если Элементы.УслугиКредит.Заголовок <> НовыйЗаголовокКт Тогда
        Элементы.УслугиКредит.Заголовок = НовыйЗаголовокКт;
    КонецЕсли;
КонецПроцедуры

Важно: этот способ не устраняет проблему, а лишь уменьшает частоту ее проявления. "Прыжок" все равно произойдет в момент, когда пользователь перейдет на строку, требующую смены заголовка. Этот метод можно рассматривать как временное решение или оптимизацию.

Решение 3: Предотвращение рекурсивного вызова

Как упоминалось ранее, перерисовка формы может вызвать повторное срабатывание события ПриАктивизацииСтроки. Чтобы избежать зацикливания, можно использовать реквизит формы в качестве флага, который будет хранить информацию об уже обработанной строке.

  1. Создайте на форме реквизит, например, ИдентификаторОбработаннойСтроки. Тип данных должен позволять уникально идентифицировать строку (например, УникальныйИдентификатор, Число или тип ссылки на объект).
  2. В начале обработчика ПриАктивизацииСтроки проверяйте, не совпадает ли идентификатор текущей строки со значением в этом реквизите. Если совпадает — значит, обработка уже была, и нужно выйти из процедуры.
  3. В конце обработчика сохраняйте идентификатор текущей строки в этот реквизит.

// В реквизитах формы: ИдентификаторОбработаннойСтроки (например, тип Ссылка)

&НаКлиенте
Процедура УслугиПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элементы.Услуги.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Проверка, чтобы избежать повторной обработки той же строки
    Если ИдентификаторОбработаннойСтроки = ТекущиеДанные.Ссылка Тогда
        Возврат;
    КонецЕсли;

    // ----- Основная логика процедуры -----
    // Используем Решение 1 или Решение 2 для смены заголовка
    ЗаголовокКолонкиДебет = ПолучитьНаименованиеСубконтоДт(ТекущиеДанные) + " (Дт)";
    // ... и т.д.
    // ---------------------------------

    // Запоминаем, что эту строку мы уже обработали
    ИдентификаторОбработаннойСтроки = ТекущиеДанные.Ссылка;
КонецПроцедуры

Эта техника является хорошей практикой защитного программирования и полезна в любых сложных обработчиках события ПриАктивизацииСтроки, даже если они не меняют структуру формы.

Подводя итог, для решения проблемы "прыгающей" таблицы при смене заголовков настоятельно рекомендуется использовать связывание заголовка колонки с реквизитом формы через свойство ПутьКДаннымЗаголовка. Это наиболее чистое, надежное и производительное решение, которое соответствует идеологии управляемых форм 1С.

← На главную