При работе с отчетами в 1С на Системе Компоновки Данных (СКД) часто возникает задача динамической установки ширины колонок. Цель — обеспечить оптимальное отображение данных, чтобы содержимое ячеек и заголовки не обрезались, а колонки не занимали излишнее пространство. Особенно это актуально, когда значения в колонках имеют разную длину, а заголовки могут быть длиннее содержимого или даже охватывать несколько колонок. Давайте подробно рассмотрим, как можно реализовать такое требование заказчика.
Основное требование, которое мы будем разбирать, заключается в следующем:
R3C3:R3C4), что усложняет расчет.Пользователи часто хотят, чтобы отчеты выглядели "красиво" и "читаемо" без необходимости ручной настройки ширины колонок (например, скрывать пустые столбцы при отсутствии данных). Однако динамическая установка ширины имеет свои подводные камни, например, при изменении шрифтов или стилей пользователем, что может сбить все автоматические настройки. Поэтому важно выбирать подход, обеспечивающий необходимую гибкость и устойчивость к внешним факторам.
Один из первоначальных способов взаимодействия с представлением отчета в СКД — это использование макетов. Перейдите на закладку "Макеты" в настройках СКД. Здесь мы можем создавать и редактировать пользовательские макеты для различных областей отчета (шапка, подвал, данные, группировки). Макеты предоставляют некоторый уровень контроля над внешним видом отчета.
В макетах мы можем:
Задать фиксированную ширину: В макете таблицы (например, макет области "Макет таблицы" или "Макет заголовка колонок") мы можем вручную настроить ширину для каждой колонки. Для этого выделите нужную колонку или группу колонок в макете и в окне свойств установите значение свойства "Ширина". Этот подход подходит, если длина данных более-менее предсказуема и не меняется кардинально от отчета к отчету. Однако, это не решает задачу динамического автоподбора ширины по фактическому содержимому после выполнения запроса, так как значения задаются статически.
Использовать условное оформление (ограниченно): В расширенных настройках отчета (доступных через кнопку с изображением гаечного ключа, обычно называемой "Настройка", или через меню "Все действия" -> "Изменить вариант" -> вкладка "Условное оформление") можно задать минимальную и максимальную ширину для определенных полей. Например, мы можем указать, что для поля "Номенклатура" минимальная ширина должна быть 10 единиц, а максимальная — 50. Это немного гибче, чем полностью фиксированная ширина в макете, поскольку позволяет задавать границы, но это также не позволяет динамически рассчитывать оптимальную ширину на основе фактического содержимого, которое получилось после выполнения запроса. Значения задаются фиксированными числами.
Для настройки условного оформления:
Этот метод полезен для установки базовых ограничений или специфичных правил для определенных данных (также можно использовать инструменты для расширенной настройки СКД на стороне пользователя), но он не является полноценным решением для автоматической подстройки по самому длинному содержимому всех колонок отчета.
Как мы видим, стандартные настройки макетов и условного оформления в СКД дают ограниченные возможности для динамической установки ширины колонок по содержимому. Для более сложной и точной логики нам потребуется программная обработка результата компоновки данных.
Этот подход является наиболее гибким и позволяет реализовать практически любую логику определения ширины колонок, включая учет длины заголовков и объединенных ячеек. Для подготовки сложных структур полезно использовать современную Консоль запросов и отчетов — для этого подойдёт Консоль запросов и компоновки данных для разработчиков. Суть метода заключается в том, что мы сначала формируем отчет, а затем программно анализируем его содержимое в полученном табличном документе и корректируем ширину колонок уже после их вывода.
Обычно это делается в обработчике события формы отчета, например, в процедуре, которая вызывается при нажатии кнопки "Сформировать" (перед этим полезно отладить запросы в специализированной консоли). Мы используем объекты КомпоновщикМакетаКомпоновкиДанных для получения макета, ПроцессорКомпоновкиДанных для получения результата и ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент для вывода в табличный документ.
// Пример получения табличного документа (упрощенный фрагмент кода)
// Этот код обычно находится в обработчике события кнопки "Сформировать" на форме отчета
// или в функции, вызываемой для формирования отчета.
Перем КомпоновщикМакета;
Перем ПроцессорКомпоновки;
Перем ПроцессорВывода;
Перем ТабличныйДокумент;
// Инициализация компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// Получение настроек компоновщика данных (КомпоновщикНастроек)
// КомпоновщикНастроек обычно является реквизитом формы отчета типа КомпоновщикНастроекКомпоновкиДанных
НастройкиКомпоновки = КомпоновщикНастроек.ПолучитьНастройки();
// Выполнение компоновки макета
МакетКомпоновки = КомпоновщикМакета.Выполнить(НастройкиКомпоновки, ДанныеРасшифровки); // ДанныеРасшифровки - опциональный параметр
// Инициализация процессора компоновки данных
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
// Создание нового табличного документа для вывода результата
ТабличныйДокумент = Новый ТабличныйДокумент;
// Инициализация процессора вывода результата в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);
// Вывод результата компоновки в табличный документ
// Здесь может быть цикл по элементам результата ПроцессораКомпоновки,
// но для простоты мы выводим весь результат сразу.
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// После вывода, мы можем вызвать нашу процедуру для расчета ширины
РассчитатьИУстановитьШиринуКолонок(ТабличныйДокумент);
// Обычно ТабличныйДокумент затем отображается пользователю
// ЭлементыФормы.ПолеТабличногоДокумента.Вывести(ТабличныйДокумент);
Более удобным и типовым местом для выполнения этой логики является обработчик события ПриКомпоновкеРезультата основного модуля объекта отчета. В нем мы можем получить доступ к уже сформированному табличному документу (параметр ДокументРезультат) непосредственно перед его показом пользователю.
// В модуле объекта отчета (например, Отчет.МойОтчет.МодульОбъекта)
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Если мы хотим полностью контролировать вывод и не использовать стандартные механизмы СКД
// для дальнейшей обработки (например, для сохранения в файл), можем отключить СтандартнаяОбработка.
// В нашем случае, мы просто модифицируем уже сформированный ДокументРезультат.
// СтандартнаяОбработка = Ложь;
// ... стандартный код компоновки, если он есть, до вызова нашей процедуры ...
// После того как ДокументРезультат заполнен системой, вызываем нашу процедуру
РассчитатьИУстановитьШиринуКолонок(ДокументРезультат);
// Если СтандартнаяОбработка = Истина, то система продолжит обработку ДокументРезультат
// (например, его вывод на форму или сохранение).
// Если СтандартнаяОбработка = Ложь, то мы должны сами управлять выводом.
КонецПроцедуры
Основная логика будет заключаться в итерации по колонкам и строкам табличного документа для определения максимальной длины текста. Мы создадим вспомогательную процедуру РассчитатьИУстановитьШиринуКолонок.
Процедура РассчитатьИУстановитьШиринуКолонок(ТабДок)
Если ТабДок = Неопределено Тогда
Возврат;
КонецЕсли;
// Коэффициент для перевода длины строки в символах в ширину колонки ТабличногоДокумента.
// Этот коэффициент сильно зависит от используемого шрифта, его размера и масштаба отображения в 1С.
// Значение 1.5 из сообщения форума - это хорошая отправная точка, но может потребоваться подбор.
// Чем меньше коэффициент, тем шире будут колонки для одной и той же длины текста.
КонстКоэффициентШирины = 1.2;
// Можно использовать другой коэффициент для заголовков, если они используют другой шрифт или стиль.
МаксимальныеШириныКолонок = Новый Массив; // Для хранения максимальной ширины для каждой колонки (в символах)
// Инициализируем массив максимальных ширин нулями для каждой колонки
Для Инд = 0 По ТабДок.КоличествоКолонок - 1 Цикл
МаксимальныеШириныКолонок.Добавить(0);
КонецЦикла;
// --- Первый проход: Определяем максимальную ширину по содержимому ячеек данных ---
// Перебираем все строки и все колонки табличного документа.
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
Для НомерКолонки = 1 По ТабДок.ШиринаТаблицы Цикл
// Получаем область текущей ячейки
ОбластьЯчеек = ТабДок.Область(НомерСтроки, НомерКолонки);
// Проверяем, есть ли текст в ячейке
Если ОбластьЯчеек.Текст <> Неопределено Тогда
// Получаем длину текста. Важно учесть переносы строк, если они есть.
// Для текста с переносами, нас интересует самая длинная подстрока.
ДлинаТекста = 0;
Если СтрНайти(ОбластьЯчеек.Текст, Символы.ПС) > 0 Тогда
// Если есть переносы, разбиваем на строки и ищем самую длинную
МассивСтрок = СтрРазделить(ОбластьЯчеек.Текст, Символы.ПС);
Для Каждого Стр Из МассивСтрок Цикл
ДлинаТекста = Макс(ДлинаТекста, СтрДлина(Стр));
КонецЦикла;
Иначе
// Если переносов нет, берем всю длину текста
ДлинаТекста = СтрДлина(ОбластьЯчеек.Текст);
КонецЕсли;
// Обновляем максимальную ширину для данной колонки, если текущая длина больше
ТекущаяМаксимальнаяШирина = МаксимальныеШириныКолонок[НомерКолонки - 1];
МаксимальныеШириныКолонок[НомерКолонки - 1] = Макс(ТекущаяМаксимальнаяШирина, ДлинаТекста);
КонецЕсли;
КонецЦикла;
КонецЦикла;
// --- Второй проход: Учитываем заголовки, особенно объединенные ---
// Это самая сложная часть, особенно когда заголовки объединены по горизонтали.
// Заголовки отчета могут располагаться в разных областях: Шапка, Заголовок, ЗаголовокКолонки,
// но чаще всего они находятся в одной из первых нескольких строк табличного документа.
// Предполагаем, что заголовки находятся в строках с 1 по 3. Можно увеличить этот диапазон.
Для НомерСтрокиШапки = 1 По 3 Цикл
Если ТабДок.ВысотаТаблицы < НомерСтрокиШапки Тогда
Продолжить; // Пропускаем, если строк меньше, чем мы пытаемся проверить
КонецЕсли;
Для НомерКолонки = 1 По ТабДок.ШиринаТаблицы Цикл
ОбластьЯчеек = ТабДок.Область(НомерСтрокиШапки, НомерКолонки);
Если ОбластьЯчеек.Текст <> Неопределено И НЕ ПустаяСтрока(ОбластьЯчеек.Текст) Тогда
ДлинаТекстаЗаголовка = 0;
// Аналогично, учитываем переносы строк в заголовке
Если СтрНайти(ОбластьЯчеек.Текст, Символы.ПС) > 0 Тогда
МассивСтрок = СтрРазделить(ОбластьЯчеек.Текст, Символы.ПС);
Для Каждого Стр Из МассивСтрок Цикл
ДлинаТекстаЗаголовка = Макс(ДлинаТекстаЗаголовка, СтрДлина(Стр));
КонецЦикла;
Иначе
ДлинаТекстаЗаголовка = СтрДлина(ОбластьЯчеек.Текст);
КонецЕсли;
// Проверяем, является ли текущая ячейка частью объединенной области по горизонтали
Если ОбластьЯчеек.ОбъединениеГоризонтальное > 1 Тогда
// Это значит, что один заголовок "захватывает" несколько колонок.
// Общая длина заголовка должна быть распределена между этими колонками.
// Мы должны убедиться, что суммарная ширина всех объединенных колонок
// достаточна для отображения всего текста заголовка.
// Вычисляем общую длину, требуемую для заголовка
ТребуемаяСуммарнаяДлина = ДлинаТекстаЗаголовка;
// Распределяем эту длину поровну между объединенными колонками.
// Если ТребуемаяСуммарнаяДлина мала, все равно даем минимум 1 символ на колонку.
ДлинаНаКолонку = Цел(ТребуемаяСуммарнаяДлина / ОбластьЯчеек.ОбъединениеГоризонтальное);
Если ДлинаНаКолонку = 0 Тогда ДлинаНаКолонку = 1; КонецЕсли;
Для ИндКолонки = НомерКолонки По НомерКолонки + ОбластьЯчеек.ОбъединениеГоризонтальное - 1 Цикл
Если ИндКолонки <= ТабДок.ШиринаТаблицы Тогда
ТекущаяМаксимальнаяШирина = МаксимальныеШириныКолонок[ИндКолонки - 1];
// Учитываем распределенную длину заголовка как минимум для каждой из объединенных колонок
МаксимальныеШириныКолонок[ИндКолонки - 1] = Макс(ТекущаяМаксимальнаяШирина, ДлинаНаКолонку);
КонецЕсли;
КонецЦикла;
// Перемещаем указатель НомерКолонки, чтобы не обрабатывать повторно уже учтенные части объединенной ячейки
НомерКолонки = НомерКолонки + ОбластьЯчеек.ОбъединениеГоризонтальное - 1;
Продолжить; // Переходим к следующей ячейке после объединенной
Иначе
// Это обычный заголовок (не объединенный по горизонтали)
ТекущаяМаксимальнаяШирина = МаксимальныеШириныКолонок[НомерКолонки - 1];
МаксимальныеШириныКолонок[НомерКолонки - 1] = Макс(ТекущаяМаксимальнаяШирина, ДлинаТекстаЗаголовка);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
// --- Установка рассчитанной ширины колонок в ТабличномДокументе ---
Для НомерКолонки = 1 По ТабДок.ШиринаТаблицы Цикл
ДлинаВСимволах = МаксимальныеШириныКолонок[НомерКолонки - 1];
// Устанавливаем минимальную ширину для колонки, чтобы она не стала слишком узкой
// (например, чтобы помещался значок сортировки или просто выглядела читаемо)
ДлинаВСимволах = Макс(ДлинаВСимволах, 5); // Минимум 5 символов
// Переводим длину в символах в единицы ширины ТабличногоДокумента
НоваяШирина = Цел(ДлинаВСимволах / КонстКоэффициентШирины);
// Устанавливаем ширину для всей колонки в табличном документе
ТабДок.Область(1, НомерКолонки, ТабДок.ВысотаТаблицы, НомерКолонки).ШиринаКолонки = НоваяШирина;
КонецЦикла;
КонецПроцедуры
Когда мы устанавливаем ширину отдельных колонок, важно убедиться, что объединенные заголовки продолжают корректно отображаться. Стандартный вывод СКД обычно сохраняет информацию об объединении ячеек. Если СКД уже вывела объединенные заголовки (что происходит в большинстве случаев, если они настроены в макете), то они будут продолжать выглядеть объединенными. Наша процедура лишь корректирует ширину отдельных колонок, составляющих эту объединенную область.
Однако, если по какой-то причине нам нужно программно создать объединение ячеек в уже сформированном табличном документе (например, если мы перестраиваем структуру отчета или добавляем собственные заголовки), мы можем использовать метод ОбъединитьЯчейки() объекта ТабличныйДокумент.
// Пример программного объединения ячеек (если СКД не сделала это сама, или нужно новое объединение)
// Предположим, нужно объединить ячейки с 3-й по 4-ю колонки в 3-й строке
// для заголовка, который мы добавили программно.
ТабДок.Область(3, 3, 3, 4).ОбъединитьЯчейки();
// Или, если заголовок занимает несколько строк:
// ТабДок.Область(1, 1, 2, 1).ОбъединитьЯчейки(); // Объединить ячейки 1-1, 2-1
Важно помнить, что если вы программно изменяете структуру или форматирование табличного документа, который был создан СКД, вы должны быть осторожны, чтобы не нарушить стандартное отображение и возможность расшифровки данных, предоставляемых системой компоновки.
При реализации автоматической установки ширины колонок нам необходимо учитывать несколько важных моментов, которые могут повлиять на корректность работы и удобство использования отчета.
Коэффициент ширины (КонстКоэффициентШирины): Значение 1.2 (или 1.5, как было предложено в обсуждении) является эмпирическим. Фактический коэффициент, преобразующий длину строки в символах в ширину колонки ТабличногоДокумента (которая измеряется в неких условных единицах), зависит от множества факторов: используемого шрифта, его размера, масштаба отображения в 1С (системные настройки и настройки пользователя), а также разрешения экрана. Вам, возможно, потребуется экспериментально подобрать это значение для вашей конфигурации и предпочтений пользователей. Различные шрифты (например, моноширинные и пропорциональные) требуют разных коэффициентов.
Пользовательские настройки (шрифты, стили): Как было отмечено в сообщении 4, изменение пользователем шрифта или стиля в отчете (через меню "Вид" -> "Изменить шрифт" или "Изменить стиль") может полностью сбить все ваши тщательно рассчитанные ширины. Это критический момент. Если заказчик действительно не будет менять настройки (что бывает редко в реальной жизни), это упрощает задачу. Но обычно пользователи имеют такую возможность. Решением может быть:
В некоторых случаях, лучше всего либо полностью оставить автоматическую ширину (которая в СКД работает по неким внутренним алгоритмам, но может быть не идеальной для всех случаев), либо задать свою максимальную ширину, чтобы избежать выхода за пределы экрана и некрасивых "растягиваний" колонок.
Производительность: Программный перебор всех ячеек большого отчета (сотни строк, десятки колонок) может сказаться на производительности, увеличивая время формирования отчета. Для очень больших отчетов стоит рассмотреть оптимизации, например, ограничение количества строк для анализа (например, только первые 100 строк) или кэширование уже рассчитанных ширин для типовых колонок. Также можно анализировать только видимые колонки, если это применимо.
Сохранение настроек: Изменения ширины, сделанные программно, не сохраняются автоматически в вариантах отчета, созданных пользователем. Если пользователю нужно, чтобы эти настройки сохранялись при следующем открытии отчета, потребуется дополнительная логика по сохранению и загрузке пользовательских настроек (например, в регистре сведений) для каждого варианта отчета. Это довольно сложная задача, если требуется высокая детализация.
Типовые конфигурации и ИТС: В примерах ИТС (Информационно-технологическое сопровождение) часто можно найти реализации процедур для автоматического расчета ширины колонок (например, процедура РасчетШириныКолонок в различных общих модулях отчетов). Мы рекомендуем изучить эти готовые, проверенные решения, которые могут служить отправной точкой или источником идей для вашей реализации.
Ручная подстройка (для разработчика): Для быстрой проверки или тонкой настройки во время разработки, вы можете выделить группу колонок в табличном документе (который открыт на форме) и, удерживая клавишу Ctrl, двойным щелчком по правой границе одной из выделенных колонок установить минимальную ширину по тексту. Это очень полезный трюк для быстрого анализа необходимой ширины для конкретного содержимого.
Реализация автоматической установки ширины колонок в отчетах СКД по содержимому и заголовкам, особенно с учетом объединенных ячеек, является выполнимой задачей, но требует программного подхода. Мы рассмотрели, как это можно сделать, используя постобработку табличного документа и написание собственной процедуры для расчета оптимальной ширины. Всегда помните о компромиссах между идеальным отображением, производительностью и устойчивостью к пользовательским изменениям. Тщательный подбор коэффициента ширины и учет особенностей заголовков позволит создать удобные и читаемые отчеты.