При разработке сложных аналитических отчетов в системе 1С:Предприятие программисты часто сталкиваются с задачей вывода нескольких последовательных таблиц в один табличный документ. Естественное желание пользователя в данном сценарии — чтобы при прокрутке отчета шапка текущей активной таблицы оставалась зафиксированной вверху экрана до тех пор, пока ее не сменит шапка следующей таблицы.
Стандартный объект платформы ТабличныйДокумент имеет жесткое архитектурное ограничение: свойства ФиксацияСверху и ФиксацияСлева задаются глобально для всего документа и могут принимать только одно значение (индекс строки или колонки). Платформа «из коробки» не умеет автоматически сдвигать область фиксации при переходе от одной таблицы к другой внутри единого поля табличного документа.
Тем не менее решить эту задачу возможно. Давайте подробно рассмотрим и проанализируем основные практические способы обхода этого платформенного ограничения — от динамического пересчета строк на клиенте до современных подходов с использованием веб-технологий.
Этот способ основан на интерактивном отслеживании положения фокуса (курсора) пользователя на форме отчета. Идея заключается в том, чтобы во время формирования отчета на сервере запомнить номера строк начала каждой таблицы, а затем на клиенте динамически переключать свойство ФиксацияСверху табличного документа.
Разберем по шагам, как реализовать этот алгоритм.
Шаг 1. Перехват компоновки отчета и сбор координат таблиц
В модуле объекта отчета (или в модуле формы при ручном выводе) переопределим процедуру компоновки. Нам понадобится специальная служебная таблица (например, выгруженная в реквизит формы ТаблицаФиксации с колонками НомерСтрокиНачала и НомерСтрокиШапки), в которую мы будем записывать координаты начала каждой таблицы отчета.
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.НачатьВывод();
// Очищаем таблицу фиксации на форме (доступна через реквизиты)
ТаблицаФиксации.Очистить();
Пока Истина Цикл
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
Если ЭлементРезультата = Неопределено Тогда
Прервать;
КонецЕсли;
ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
// Анализируем макеты выводимых секций (например, "Макет2" - первая таблица, "Макет5" - вторая)
Если ЭлементРезультата.ЗначенияПараметров.Количество() > 0 Тогда
Если ЭлементРезультата.Макет = "Макет2" Тогда
НоваяСтрока = ТаблицаФиксации.Добавить();
НоваяСтрока.НомерСтрокиНачала = ДокументРезультат.ВысотаТаблицы;
НоваяСтрока.НомерСтрокиШапки = ДокументРезультат.ВысотаТаблицы;
ИначеЕсли ЭлементРезультата.Макет = "Макет5" Тогда
НоваяСтрока = ТаблицаФиксации.Добавить();
НоваяСтрока.НомерСтрокиНачала = ДокументРезультат.ВысотаТаблицы;
// Запоминаем строку шапки для второй таблицы
НоваяСтрока.НомерСтрокиШапки = ДокументРезультат.ВысотаТаблицы;
КонецЕсли;
КонецЕсли;
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод();
// Установим начальную фиксацию по первой таблице
Если ТаблицаФиксации.Количество() > 0 Тогда
ДокументРезультат.ФиксацияСверху = ТаблицаФиксации[0].НомерСтрокиШапки;
КонецЕсли;
КонецПроцедуры
Шаг 2. Обработка события на клиенте
Теперь перейдем в модуль управляемой формы отчета. Для элемента формы типа Поле табличного документа (например, с именем Результат) добавим обработчик события ПриАктивизацииОбласти. Нам нужно определять, в области какой таблицы сейчас находится курсор пользователя, и динамически сдвигать фиксацию.
&НаКлиенте
Процедура РезультатПриАктивизацииОбласти(Элемент)
ТекущаяОбласть = Элемент.ТекущаяОбласть;
Если ТекущаяОбласть = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущийВерх = ТекущаяОбласть.Верх;
НоваяФиксация = 0;
// Обходим таблицу границ в обратном порядке, чтобы найти текущий активный раздел
КоличествоСтрок = ТаблицаФиксации.Количество();
Для Индекс = 1 По КоличествоСтрок Цикл
ОбратныйИндекс = КоличествоСтрок - Индекс;
СтрокаГраницы = ТаблицаФиксации[ОбратныйИндекс];
Если ТекущийВерх >= СтрокаГраницы.НомерСтрокиНачала Тогда
НоваяФиксация = СтрокаГраницы.НомерСтрокиШапки;
Прервать;
КонецЕсли;
КонецЦикла;
// Применяем фиксацию только если она изменилась, чтобы избежать лишнего мерцания экрана
Если НоваяФиксация > 0 И Элемент.ФиксацияСверху <> НоваяФиксация Тогда
Элемент.ФиксацияСверху = НоваяФиксация;
КонецЕсли;
КонецПроцедуры
Критический нюанс метода: Данное решение отлично работает при навигации по отчету с помощью клавиатуры (стрелками, клавишами PageUp, PageDown, клавишей Enter) или при кликах мышью по ячейкам табличного документа. Однако событие ПриАктивизацииОбласти не срабатывает, если пользователь просто тащит бегунок вертикальной полосы прокрутки (скроллбара) мышью, так как при этом фокус ввода (активная ячейка) не перемещается.
Если требуется получить абсолютно плавное, бесшовное и современное поведение фиксации шапок при любом способе прокрутки (включая колесико мыши и перетаскивание скроллбара), стандартный ТабличныйДокумент лучше заменить на Поле HTML-документа.
В веб-стандартах существует нативное свойство CSS position: sticky. Элемент с таким позиционированием ведет себя как относительный (relative), пока не достигает заданной границы (например, верхнего края контейнера), после чего он «прилипает» к экрану и становится фиксированным (fixed). Когда снизу поднимается следующий блок с аналогичным липким элементом, он естественным образом «выталкивает» предыдущий.
Проанализируем алгоритм реализации этого подхода:
ТабличныйДокумент.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.HTML).<tr> или ячейки <td> внутри шапки), и внедряем в их стили CSS-свойство position: sticky; top: 0; background-color: #ffffff; z-index: 999;.Посмотрим на пример CSS-кода, который автоматически решает нашу задачу на уровне движка рендеринга:
/* Стиль для «липких» шапок таблиц в HTML-отчете */
.sticky-header {
position: -webkit-sticky; /* Поддержка старых версий WebKit */
position: sticky;
top: 0;
z-index: 10;
background-color: #f2f2f2; /* Фон обязателен, иначе сквозь шапку будут просвечивать данные */
border-bottom: 2px solid #a0a0a0;
}
Этот метод визуально выглядит безупречно и не требует никаких обработчиков ожидания или клиентских событий. Каждая последующая таблица сама мягко сменяет шапку предыдущей при прокрутке.
Если переходить на HTML-отображение не хочется, а нестабильная прокрутка в первом решении не устраивает пользователей, рассмотрим изменение архитектуры самой формы отчета. Вместо вывода нескольких таблиц «простыней» в один элемент формы, можно применить один из следующих интерфейсных приемов:
ФиксацияСверху. Каждая таблица имеет свою полосу прокрутки, что исключает путаницу.Если ваша основная задача заключается не в интерактивном просмотре отчета на мониторе компьютера, а в обеспечении читаемости при выводе на печать (или сохранении в PDF), то в платформе 1С предусмотрен специализированный штатный механизм.
Объект ТабличныйДокумент содержит свойство ПовторятьПриПечатиСтроки. Мы можем программно назначить для каждого печатного листа свои строки повторения. Перед выводом секций каждой новой таблицы мы можем динамически переопределять эту область:
// Пример динамической настройки областей печати для вывода
ДокументРезультат.ПовторятьПриПечатиСтроки = ДокументРезультат.Область("R1C1:R3C10");
// При выводе второй таблицы мы перезаписываем свойство на новые строки:
ДокументРезультат.ПовторятьПриПечатиСтроки = ДокументРезультат.Область("R150C1:R152C10");
При отправке такого документа на принтер платформа автоматически продублирует актуальную шапку текущей таблицы вверху каждого листа, на который она не поместилась целиком.
Мы выяснили причину ограничения платформы 1С и подробно разобрали возможные пути обхода. Выбор конкретного метода зависит от приоритетов вашего проекта: