Как сохранить фокус и положение прокрутки в Таблице Формы 1С при изменении ячеек

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

При разработке интерфейсов на управляемых формах 1С программисты часто сталкиваются с неприятным поведением динамических таблиц (элементов типа ТаблицаФормы). Представим типичную ситуацию: на форме расположена широкая таблица со множеством колонок (например, табель учета рабочего времени на 31 день, шахматка продаж или матрица распределения бюджета). Пользователь прокручивает таблицу вправо, меняет значение в крайней правой колонке, нажимает Enter — и в этот момент горизонтальный скролл мгновенно «отлетает» в самое начало (влево), заставляя пользователя снова и снова вручную прокручивать таблицу обратно. Такое поведение вызывает крайнее раздражение и замедляет работу.

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

Выясняем причину: почему «слетает» горизонтальный скролл?

Основная причина сброса горизонтальной прокрутки и фокуса — это полная или частичная перерисовка (обновление отображения) элементов формы на клиенте после возврата из серверного вызова. Давайте проанализируем цепочку событий, которая приводит к этой проблеме:

  1. Пользователь изменяет значение в ячейке.
  2. Срабатывает событие ячейки или самой таблицы (например, ПриИзменении).
  3. В коде обработчика происходит вызов серверной процедуры с контекстом формы (директива &НаСервере).
  4. Платформа передает состояние всей формы (контекст) на сервер, выполняет код и возвращает измененный контекст обратно на клиент.
  5. Получив обновленный контекст, клиентское приложение 1С понимает, что данные формы изменились, и инициирует обновление графического интерфейса. При этом дерево элементов формы перестраивается, а визуальное положение прокрутки в табличном поле сбрасывается в исходное (крайнее левое) положение.

Аналогичный эффект вызывают:

Рассмотрим детально проверенные методы борьбы с этой проблемой.

Способ 1. Переход на внеконтекстные вызовы (Рекомендуемый подход)

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

Вместо директивы &НаСервере мы должны использовать &НаСервереБезКонтекста. При этом необходимые данные строки передаются на сервер в виде структуры, там обрабатываются, возвращаются на клиент и точечно записываются в текущую строку таблицы.

Рассмотрим этот процесс на практическом примере. Допустим, у нас есть таблица «Задачи», в которой при изменении исполняемого дня нужно пересчитать показатели.


&НаКлиенте
Процедура ТаблицаЗадачДеньПриИзменении(Элемент)
    
    // Получаем текущую строку таблицы
    ТекущаяСтрока = Элементы.ТаблицаЗадач.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 1: Формируем легкую структуру только с нужными параметрами
    ПараметрыДляРасчета = Новый Структура;
    ПараметрыДляРасчета.Вставить("Исполнитель", ТекущаяСтрока.Исполнитель);
    ПараметрыДляРасчета.Вставить("ВыбранныйДень", ТекущаяСтрока.День);
    ПараметрыДляРасчета.Вставить("Ставка", ТекущаяСтрока.Ставка);
    
    // Шаг 2: Вызываем быстрый внеконтекстный серверный метод
    РезультатРасчета = РассчитатьПоказателиНаСервереБезКонтекста(ПараметрыДляРасчета);
    
    // Шаг 3: Точечно заполняем измененные реквизиты прямо на клиенте
    ЗаполнитьЗначенияСвойств(ТекущаяСтрока, РезультатРасчета);
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция РассчитатьПоказателиНаСервереБезКонтекста(Знач ПараметрыРасчета)
    
    Результат = Новый Структура;
    // Производим необходимые вычисления на сервере
    Коэффициент = 1;
    Если ПараметрыРасчета.ВыбранныйДень = "Суббота" Или ПараметрыРасчета.ВыбранныйДень = "Воскресенье" Тогда
        Коэффициент = 2; // Выходной тариф
    КонецЕсли;
    
    Результат.Вставить("Сумма", ПараметрыРасчета.Ставка * Коэффициент);
    Результат.Вставить("Обработано", Истина);
    
    Возврат Результат;
    
КонецФункции

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

Способ 2. Программный возврат фокуса через обработчик ожидания

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

Если мы просто установим свойство ТекущийЭлемент после серверного вызова, ячейка выделится, но прокрутка формы все равно может остаться в левом положении, скрыв выделенную область из зоны видимости. Чтобы заставить платформу аппаратно прокрутить таблицу к нужной ячейке, мы должны принудительно активизировать ее и войти в режим редактирования с микрозадержкой. Для этого идеально подходит однократный обработчик ожидания.

Разберем реализацию этого механизма по шагам:


&НаКлиенте
Перем ИмяТекущейКолонки; // Переменная модуля для сохранения имени колонки

&НаКлиенте
Процедура ТаблицаЗадачПриИзменении(Элемент)
    
    // Шаг 1: Запоминаем имя колонки, которую редактирует пользователь
    ТекущийЭлементФормы = Элементы.ТаблицаЗадач.ТекущийЭлемент;
    Если ТекущийЭлементФормы <> Неопределено Тогда
        ИмяТекущейКолонки = ТекущийЭлементФормы.Имя;
    КонецЕсли;
    
    // Шаг 2: Выполняем неизбежный контекстный вызов сервера
    ВыполнитьСложныеРасчетыНаСервере();
    
    // Шаг 3: Восстанавливаем фокус на элементе (но скролл еще может не успеть отработать)
    Если Не ПустаяСтрока(ИмяТекущейКолонки) Тогда
        Элементы.ТаблицаЗадач.ТекущийЭлемент = Элементы[ИмяТекущейКолонки];
    КонецЕсли;
    
    // Шаг 4: Подключаем однократный обработчик ожидания с минимальной задержкой
    ПодключитьОбработчикОжидания("ПодключитьФокусКЯчейкеПослеОбновления", 0.1, Истина);
    
КонецПроцедуры

&НаСервере
Процедура ВыполнитьСложныеРасчетыНаСервере()
    // Контекстный метод: происходит изменение реквизитов объекта, итогов формы и т.д.
    Объект.ИтогСумма = Объект.ТаблицаЗадач.Итог("Сумма");
КонецПроцедуры

&НаКлиенте
Процедура ПодключитьФокусКЯчейкеПослеОбновления() Экспорт
    
    // Шаг 5: Принудительно заставляем платформу спозиционировать скролл на выбранной ячейке
    Попытка
        Элементы.ТаблицаЗадач.ИзменитьСтроку();
    Исключение
        // Исключение может возникнуть, если строка заблокирована или недоступна для редактирования
    КонецПопытки;
    
КонецПроцедуры

Важная деталь: Задержка в 0.1 секунды практически незаметна глазу пользователя, но она дает платформе необходимое время на завершение полной отрисовки интерфейса на клиенте, после чего метод ИзменитьСтроку() безошибочно фокусирует экран на редактируемом поле.

Способ 3. Оптимизация динамического условного оформления

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

Если в вашей таблице много колонок, проанализируйте правила условного оформления:

Способ 4. Изменение эргономики формы (Альтернативный вариант)

Если колонок действительно критически много, стоит задуматься о юзабилити формы в целом. Горизонтальная прокрутка сама по себе усложняет восприятие информации пользователем. Мы можем применить альтернативный паттерн проектирования интерфейсов, который называется Панелью детального редактирования.

Рассмотрим структуру такого интерфейса:

  1. На форме располагается табличная часть с минимальным количеством основных колонок (например: «Сотрудник», «Подразделение», «Отработано дней всего»). Она легко помещается на любом мониторе без горизонтального скролла.
  2. Ниже или справа от таблицы размещается группа обычных полей ввода (например, 31 отдельное поле ввода для каждого дня месяца, аккуратно сгруппированное в виде календаря), которые связаны с реквизитами текущей строки таблицы.
  3. Когда пользователь кликает по нужной строке в таблице, под ней автоматически отображаются детальные данные. Пользователь вносит изменения в эти поля.

Такой подход полностью исключает проблему прокрутки таблицы, так как все манипуляции с данными происходят в фиксированных полях ввода на самой форме.

Резюме

Для устранения проблемы со сбросом фокуса и прокрутки в ТаблицаФормы придерживайтесь следующих правил:

  1. Всегда отдавайте приоритет разработке через &НаСервереБезКонтекста. Это ускоряет работу формы и сохраняет положение интерфейса.
  2. Если контекстный серверный вызов необходим, возвращайте фокус и позицию прокрутки программно через метод ИзменитьСтроку(), вызванный в обработчике ожидания с минимальным интервалом.
  3. Минимизируйте влияние условного оформления на скроллируемые части таблицы.
← На главную