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

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

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

Важное различие: Табличная часть и Табличное поле

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

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

---

Шаг 1. Динамическое добавление колонок в Обычных формах (ОФ)

Рассмотрим ситуацию, когда у нас на форме есть табличное поле с именем Таблица, а его источником данных (реквизитом формы) является Таблица Значений с аналогичным именем. Если мы просто добавим колонку в элементы формы, данные отображаться не будут, и платформа выдаст ошибку при попытке записать туда значения. Нам необходимо синхронно добавить колонку как в сам источник данных, так и в элемент управления на форме.

Давайте разберем по шагам правильный алгоритм создания колонок:


// Очищаем табличное поле и ТЗ перед перестроением (если применимо)
Таблица.Очистить();

НомерКолонки = 0;
Для каждого СтрокаСклад из СписокСкладов Цикл
    НомерКолонки = НомерКолонки + 1;
    ИмяКолонки = "ОстатокСклад_" + Строка(НомерКолонки);
    ЗаголовокКолонки = "Остаток (" + СокрЛП(СтрокаСклад.Значение) + ")";
    
    // 1. Сначала добавляем колонку в источник данных (Таблицу Значений)
    Таблица.Колонки.Добавить(ИмяКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
    
    // 2. Теперь добавляем колонку визуально в Табличное Поле формы
    НоваяКолонка = ЭлементыФормы.Таблица.Колонки.Добавить(ИмяКолонки, ЗаголовокКолонки);
    
    // 3. Связываем визуальную колонку с реквизитом источника данных
    НоваяКолонка.Данные = ИмяКолонки;
    
    // 4. Настраиваем элемент управления для возможности редактирования или отображения
    НоваяКолонка.УстановитьЭлементУправления(Тип("ПолеВвода"));
    НоваяКолонка.ЭлементУправления.ТипЗначения = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3));
КонецЦикла;

В данном примере мы используем метод Добавить для создания колонки в коллекции Колонки таблицы значений, а затем дублируем это действие в коллекции Колонки табличного поля формы. Установка свойства Данные связывает визуальный элемент с физическим полем в памяти базы данных.

---

Шаг 2. Программная привязка события «ПриИзменении» в Обычных формах

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

При реализации этого механизма разработчики часто совершают две ошибки:

  1. Передают в метод УстановитьДействие строковое имя процедуры вместо специального объекта Действие. Это вызывает ошибку «Несоответствие типов».
  2. Процедура-обработчик объявляется без параметров. Это приводит к ошибке «Процедура, указанная в качестве обработчика события, имеет неверное количество параметров».

Давайте посмотрим на пример правильной привязки события:


// В цикле создания колонок добавляем назначение действия:
ДействиеПриИзменении = Новый Действие("ДинамическийПриИзмененииОстатка");
НоваяКолонка.ЭлементУправления.УстановитьДействие("ПриИзменении", ДействиеПриИзменении);

Теперь в модуле формы необходимо определить саму процедуру-обработчик. Она обязательно должна иметь один входящий параметр (сам элемент управления, который инициировал событие) и иметь ключевое слово Экспорт:


Процедура ДинамическийПриИзмененииОстатка(Элемент) Экспорт
    // Получаем текущую строку табличного поля через ТекущиеДанные
    ТекущаяСтрока = ЭлементыФормы.Таблица.ТекущиеДанные;
    
    Если ТекущаяСтрока <> Неопределено Тогда
        // Выясняем имя измененной колонки
        ИмяКолонки = Элемент.Имя;
        НовоеЗначение = ТекущаяСтрока[ИмяКолонки];
        
        // Здесь мы можем реализовать любую бизнес-логику, например, расчет общей суммы
        Сообщить("В колонке " + ИмяКолонки + " изменено значение на: " + Строка(НовоеЗначение));
    КонецЕсли;
КонецПроцедуры

---

Шаг 3. Как правильно очищать динамические колонки

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

Проанализируем пример процедуры очистки динамических колонок. Для этого рекомендуется хранить список созданных имен колонок в отдельном массиве (например, в переменной модуля формы МассивДинамическихКолонок):


Процедура ОчиститьДинамическиеКолонки()
    // Проверяем, есть ли созданные ранее колонки
    Если МассивДинамическихКолонок = Неопределено Тогда
        МассивДинамическихКолонок = Новый Массив;
        Возврат;
    КонецЕсли;
    
    Для Каждого ИмяКолонки Из МассивДинамическихКолонок Цикл
        // 1. Удаляем колонку из элемента формы (Табличного поля)
        КолонкаТП = ЭлементыФормы.Таблица.Колонки.Найти(ИмяКолонки);
        Если КолонкаТП <> Неопределено Тогда
            ЭлементыФормы.Таблица.Колонки.Удалить(КолонкаТП);
        КонецЕсли;
        
        // 2. Удаляем колонку из источника данных (Таблицы Значений)
        Если Таблица.Колонки.Найти(ИмяКолонки) <> Неопределено Тогда
            Таблица.Колонки.Удалить(ИмяКолонки);
        КонецЕсли;
    КонецЦикла;
    
    // Очищаем массив учета динамических колонок
    МассивДинамическихКолонок.Очистить();
КонецПроцедуры

---

Шаг 4. Современное решение задачи на Управляемых формах (УФ)

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

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


&НаСервере
Процедура ДобавитьКолонкиНаСервере(СписокСкладов)
    
    // Массив для регистрации новых колонок реквизитов формы
    ДобавляемыеРеквизиты = Новый Массив;
    УдаляемыеРеквизиты = Новый Массив;
    
    // Перед добавлением нужно удалить старые динамические колонки, если они были созданы
    // Для этого предварительно можно сохранять их список в реквизите формы или находить по префиксу
    Для Каждого Реквизит Из ЭтаФорма.ПолучитьРеквизиты("ТаблицаДанных") Цикл
        Если Найти(Реквизит.Имя, "ДинамическийОстаток_") > 0 Тогда
            УдаляемыеРеквизиты.Добавить("ТаблицаДанных." + Реквизит.Имя);
            
            // Также находим и удаляем визуальный элемент на форме
            ЭлементФормы = Элементы.Найти("ТаблицаДанных_" + Реквизит.Iмя);
            Если ЭлементФормы <> Неопределено Тогда
                Элементы.Удалить(ЭлементФормы);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    // Применяем удаление старых реквизитов
    Если УдаляемыеРеквизиты.Количество() > 0 Тогда
        ЭтаФорма.ИзменитьРеквизиты(, УдаляемыеРеквизиты);
    EndIf;
    
    // Формируем новые колонки на основании выбранных складов
    Для Каждого Склад Из СписокСкладов Цикл
        // Создаем уникальное безопасное имя для реквизита формы
        ИмяРеквизита = "ДинамическийОстаток_" + Склад.УникальныйИдентификатор();
        НовыйРеквизит = Новый РеквизитФормы(ИмяРеквизита, Новый ОписаниеТипов("Число"), "ТаблицаДанных", "Остаток (" + Склад + ")");
        ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
    КонецЦикла;
    
    // Добавляем новые реквизиты в структуру формы
    ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
    
    // Создаем визуальные элементы (колонки таблицы) на управляемой форме
    Для Каждого Реквизит Из ДобавляемыеРеквизиты Цикл
        ИмяЭлемента = "ТаблицаДанных_" + Реквизит.Имя;
        НовыйЭлемент = Элементы.Добавить(ИмяЭлемента, Тип("ПолеФормы"), Элементы.ТаблицаДанных);
        НовыйЭлемент.Вид = ПолеФормыВид.ПолеВвода;
        НовыйЭлемент.ПутьКДанным = "ТаблицаДанных." + Реквизит.Имя;
        
        // Установка действия в Управляемых формах выполняется через передачу имени процедуры строкой
        НовыйЭлемент.УстановитьДействие("ПриИзменении", "Подключаемый_ПриИзмененииДинамическогоОстатка");
    КонецЦикла;

КонецПроцедуры

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


&НаКлиенте
Процедура Подключаемый_ПриИзмененииДинамическогоОстатка(Элемент)
    ТекущиеДанные = Элементы.ТаблицаДанных.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        // Получаем имя измененного реквизита из пути к данным элемента
        ИмяРеквизита = Элемент.ПутьКДанным; // Вернет например "ТаблицаДанных.ДинамическийОстаток_УИД"
        НовоеЗначение = ТекущиеДанные[Элемент.Имя]; 
        
        // Ваша логика обработки изменений на клиенте...
    КонецЕсли;
КонецПроцедуры

---

Альтернативный вариант: Управление видимостью заранее созданных колонок

Если в вашей бизнес-логике есть разумный лимит на количество выводимых складов (например, не более 5 одновременно), существует более простой и надежный способ реализации. Мы можем заранее, в конфигураторе, создать максимальное количество колонок (например, Остаток1, Остаток2 ... Остаток5) и жестко прописать для них обработчики событий.

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


// Пример управления видимостью для Обычных Форм:
ЭлементыФормы.Таблица.Колонки.Остаток1.Видимость = Истина;
ЭлементыФормы.Таблица.Колонки.Остаток1.ТекстШапки = "Склад Основной";

Преимущества данного подхода:

Недостатки:

Выбор конкретного решения всегда остается за разработчиком. Для динамических отчетов и сложных АРМ с непредсказуемым количеством объектов лучше использовать первый вариант с программной генерацией колонок, а для простых и фиксированных задач отлично подойдет вариант с управлением видимостью.

← На главную