При разработке интерфейсов на обычных формах 1С (платформа 8.2 / 8.3 в режиме обычного приложения, часто встречающаяся в таких конфигурациях, как «Управление производственным предприятием 1.3» или «Управление торговлей 10.3») программисты часто сталкиваются с необходимостью построения динамических таблиц. Типичный пример — вывод шахматки или кросс-таблицы, где количество и названия колонок (например, даты, склады или контрагенты) заранее неизвестны и определяются только в момент выполнения запроса.
В процессе реализации этого механизма легко совершить архитектурные ошибки, которые могут привести к падению производительности интерфейса или неработоспособности кода. В этой статье мы подробно разберем, как правильно динамически создавать колонки в табличном поле, как без ошибок заполнять их данными, а также рассмотрим подводные камни, связанные с производительностью и интерактивной работой пользователей.
Рассмотрим типичную ошибку, с которой часто сталкиваются начинающие разработчики. Попытка записать значение напрямую в коллекцию колонок выглядит следующим образом:
// Ошибочный подход
СводнаяТаблица.Колонки["На_" + ИмяКолонки].Значение = ВыборкаСумм.СуммаОстатка;
Такой код неизбежно вызовет ошибку выполнения. Разберем причину. Объект КолонкаТаблицыЗначений (или колонка табличного поля) отвечает исключительно за структуру и визуальное представление данных (тип данных, ширина, заголовок, формат отображения). Сами данные в колонках не хранятся.
Данные в 1С всегда хранятся в строках таблицы. Колонка лишь указывает, к какому полю строки мы обращаемся. Соответственно, чтобы записать значение в определенную динамическую колонку, нам сначала нужно получить или создать строку таблицы значений, а затем обратиться к свойству этой строки по имени колонки.
Проанализируем правильный порядок действий для решения этой задачи. Процесс состоит из трех основных шагов:
ТаблицаЗначений).ТабличноеПоле.Рассмотрим практический пример кода, реализующий эту логику:
// Шаг 1. Подготовка типов данных для динамических колонок
КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Любой);
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
ЧисловойТип = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧисла);
// Шаг 2. Обход выборки данных и динамическое создание структуры колонок
Пока ВыборкаСумм.Следующий() Цикл
ИмяКолонки = "На_" + СтрЗаменить(Строка(Формат(ВыборкаСумм.ДатаИсполненияЗаказа, "ДЛФ=Д")), ".", "_");
// Проверяем, существует ли уже колонка в Таблице Значений
Если СводнаяТаблица.Колонки.Найти(ИмяКолонки) = Неопределено Тогда
// Добавляем колонку в структуру данных
СводнаяТаблица.Колонки.Добавить(ИмяКолонки, ЧисловойТип, "Остаток на " + Формат(ВыборкаСумм.ДатаИсполненияЗаказа, "ДЛФ=Д"));
КонецЕсли;
// Шаг 3. Заполнение строки данными
// Ищем существующую строку по ключевым полям (например, по Региону)
ПараметрыОтбора = Новый Структура("Регион", ВыборкаСумм.Регион);
НайденныеСтроки = СводнаяТаблица.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
ТекущаяСтрока = НайденныеСтроки[0];
Иначе
ТекущаяСтрока = СводнаяТаблица.Добавить();
ТекущаяСтрока.Регион = ВыборкаСумм.Region;
КонецЕсли;
// Записываем значение в динамическую колонку строки!
ТекущаяСтрока[ИмяКолонки] = ВыборкаСумм.СуммаОстатка;
КонецЦикла;
// Шаг 4. Однократное обновление визуального интерфейса на форме
ЭлементыФормы.СводнаяТаблица.СоздатьКолонки();
Обратите внимание на положение метода СоздатьКолонки(). В ошибочных вариантах реализации этот метод часто вызывают внутри цикла добавления записей. Это серьезная ошибка проектирования.
Метод СоздатьКолонки() заставляет платформу полностью перестроить графическое отображение элемента управления на форме, пересчитать ширину всех столбцов и перерисовать интерфейс. Если ваша выборка содержит сотни или тысячи записей, вызов этого метода на каждой итерации цикла приведет к зависанию интерфейса программы на длительное время.
Золотое правило: любые манипуляции со структурой отображения (интерфейсом) должны выполняться строго после завершения всех расчетных процедур и циклов заполнения данных. Вызывайте СоздатьКолонки() только один раз в самом конце процедуры.
Использование метода СоздатьКолонки() — самый простой, но не всегда лучший способ. У него есть существенный недостаток: он полностью сбрасывает все ручные настройки колонок табличного поля, выполненные в конфигураторе или ранее в коде (ширину столбцов, видимость, доступность, заголовки, положение текста). Более того, по умолчанию все колонки создаются как текстовые поля без возможности редактирования.
Если вам требуется, чтобы пользователь мог редактировать значения в динамических колонках или если вам нужно задать красивое оформление, следует отказаться от автоматического метода и привязывать колонки вручную. Рассмотрим, как это сделать программно:
// 1. Добавляем колонку в источник данных (Таблицу Значений)
ИмяКолонки = "На_ДинамическаяКолонка";
СводнаяТаблица.Колонки.Добавить(ИмяКолонки, ЧисловойТип);
// 2. Программно добавляем колонку в визуальный элемент формы (ТабличноеПоле)
НоваяКолонкаТП = ЭлементыФормы.СводнаяТаблица.Колонки.Добавить(ИмяКолонки);
НоваяКолонкаТП.Заголовок = "Динамические данные";
НоваяКолонкаТП.Ширина = 15;
// 3. Подводный камень: Сначала устанавливаем элемент управления, только потом привязываем данные!
НоваяКолонкаТП.УстановитьЭлементУправления(Тип("ПолеВвода"));
НоваяКолонкаТП.Данные = ИмяКолонки; // Связываем колонку ТП с колонкой ТЗ
// Теперь колонка доступна для интерактивного редактирования пользователем
НоваяКолонкаТП.РежимРедактирования = РежимРедактированияКолонкиТабличногоПоля.ВходПриВводе;
Соблюдение этой последовательности крайне важно. Если вы попытаетесь сначала присвоить свойство Данные, а только потом вызвать УстановитьЭлементУправления(), платформа может заблокировать ячейки динамической колонки для ввода данных.
Важно понимать ограничения платформы 1С при работе с объектами базы данных. Напрямую динамически изменять состав колонок у табличной части документа (например, ЭтотОбъект.Товары) в режиме предприятия нельзя. Структура табличных частей жестко задана в метаданных конфигурации.
Если перед вами стоит задача реализовать динамическую таблицу для ввода данных в документе, используйте следующий обходной путь:
ТабличноеПоле, источником данных для которого будет являться временная ТаблицаЗначений (реквизит формы).Если вам требуется заполнить динамическую колонку уже готовым набором данных (например, полученным из массива), не обязательно использовать построчный обход в цикле. Мы можем существенно повысить скорость работы кода, используя специализированные методы платформы 1С.
Например, метод ЗагрузитьКолонку() позволяет в одну строку заполнить всю вертикаль таблицы значений из готового массива, что работает в разы быстрее классического цикла:
// Быстрое заполнение всей колонки данными из массива значений
МассивЗначений = Новый Массив; // Предварительно заполненный массив данных
СводнаяТаблица.ЗагрузитьКолонку(МассивЗначений, "ИмяДинамическойКолонки");
Также помните, что для построения сложных аналитических отчетов с динамическими колонками (кросс-таблиц) в обычных формах гораздо эффективнее использовать объект ПостроительЗапроса или Систему Компоновки Данных (СКД) с выводом результатов в сводную таблицу. Это избавит вас от ручной работы со структурой табличных полей.