Переход платформы «1С:Предприятие 8» на управляемые формы принес разработчикам множество преимуществ, но вместе с тем и добавил неочевидных на первый взгляд нюансов. Одна из самых частых проблем, с которой сталкиваются программисты (особенно те, кто ранее работал с обычными формами) — это попытка напрямую манипулировать таблицей значений на форме так же, как и в коде модулей объектов.
В этой статье мы подробно разберем, почему привычный код вроде ТрудозатратыСотрудника.Колонки.Количество() вызывает ошибку в управляемых формах, выясним фундаментальные различия между объектами отображения данных и самими данными, а также рассмотрим практические и производительные способы получения структуры колонок и их программного добавления.
Давайте проанализируем ситуацию. В обычных формах табличное поле на форме напрямую связывалось с объектом ТаблицаЗначений. Мы могли легко написать ЭлементыФормы.ТабличноеПоле1.Колонки.Количество() и получить точный результат. В управляемом приложении архитектура взаимодействия клиента и сервера была полностью пересмотрена в целях оптимизации трафика.
Когда вы создаете таблицу на управляемой форме, платформа разделяет ее на два абсолютно разных объекта, которые часто имеют одинаковые имена, что и запутывает разработчиков:
ДанныеФормыКоллекция. Его основная задача — хранить данные в максимально «легковесном» формате для передачи между клиентом и сервером. К нему обращаются напрямую по имени, например, ТрудозатратыСотрудника.ТаблицаФормы. Его задача — отображать данные пользователю на экране. К нему обращаются через коллекцию элементов: Элементы.ТрудозатратыСотрудника.Тип ДанныеФормыКоллекция оптимизирован для работы на клиенте. Он представляет собой, упрощенно говоря, массив структур. У этого типа в управляемом интерфейсе принципиально отсутствует свойство Колонки. Именно поэтому попытка вызвать обращение к колонкам напрямую через реквизит формы приводит к ошибке выполнения.
Если перед нами стоит задача узнать количество колонок или их имена на стороне сервера, у нас есть несколько путей. Рассмотрим их подробнее.
Самый простой, но не всегда оптимальный способ — это преобразование реквизита формы в полноценную прикладную таблицу значений с помощью встроенного метода формы. Посмотрим на пример кода:
&НаСервере
Процедура ПолучитьКолонкиЧерезПреобразование()
// Преобразуем коллекцию данных формы в реальную Таблицу Значений
ТЗ = РеквизитФормыВЗначение("ТрудозатратыСотрудника");
КоличествоКолонок = ТЗ.Колонки.Количество();
Сообщить("Количество колонок в таблице: " + КоличествоКолонок);
КонецПроцедуры
Важное примечание: Метод РеквизитФормыВЗначение является достаточно ресурсоемким, так как он заново создает в памяти объект прикладного типа и копирует туда все данные. Если ваша таблица содержит тысячи строк, такое преобразование может вызвать заметную задержку.
Для оптимизации работы мы можем использовать другие, более легковесные методы типа ДанныеФормыКоллекция:
КолКолонок = ТрудозатратыСотрудника.Выгрузить().Колонки.Количество();
&НаСервере
Функция ПолучитьКоличествоКолонокОптимально()
ПустаяТЗ = РеквизитФормыВЗначение("ТрудозатратыСотрудника").ВыгрузитьКолонки();
Возврат ПустаяТЗ.Колонки.Количество();
КонецФункции
На клиенте (в директиве &НаКлиенте) ситуация усложняется: объект ТаблицаЗначений там недоступен в принципе, а у объекта ДанныеФормыКоллекция нет методов Выгрузить() или ВыгрузитьКолонки(). Как же быть?
Мы можем пойти по пути анализа визуальных элементов формы. Обратимся к свойству отображения нашей таблицы:
&НаКлиенте
Процедура ОпределитьКоличествоКолонокНаКлиенте()
КоличествоВизуальныхКолонок = Элементы.ТрудозатратыСотрудника.ПодчиненныеЭлементы.Количество();
ПоказатьПредупреждение(, "Элементов в таблице на форме: " + КоличествоВизуальныхКолонок);
КонецПроцедуры
Внимание! У этого способа есть критические ограничения:
ПодчиненныеЭлементы содержит не физические колонки данных, а элементы управления на форме. Если вы сгруппировали несколько колонок в визуальную группу (тип ГруппаКолонок), платформа посчитает эту группу как один элемент.Видимость установлено в Ложь), они все равно будут присутствовать в коллекции подчиненных элементов, но если колонки исключены из состава видимости через настройки прав пользователей, поведение может отличаться.Если вам на клиенте регулярно требуется знать точный состав реквизитов таблицы, хорошей практикой является сохранение их списка (например, массива имен колонок) в служебный реквизит формы еще на этапе ее создания на сервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Сохраняем схему колонок в Параметры или в фиксированный массив для доступа на клиенте
МассивКолонок = Новый Массив;
ПустаяТЗ = РеквизитФормыВЗначение("ТрудозатратыСотрудника").ВыгрузитьКолонки();
Для Каждого Колонка Из ПустаяТЗ.Колонки Цикл
МассивКолонок.Добавить(Колонка.Имя);
КонецЦикла;
// Сохраняем в невидимый реквизит формы (например, строкой или через временное хранилище)
СписокКолонокСтрокой = СтрСоединить(МассивКолонок, ",");
КонецПроцедуры
Добавление новой колонки в таблицу на управляемой форме — это комплексный двухэтапный процесс. Мы не можем просто добавить колонку в элемент отображения, предварительно не создав под нее данные в реквизите формы, и наоборот.
Разберем по шагам правильный алгоритм программного добавления колонок:
ИзменитьРеквизиты(), чтобы добавить новое поле данных в структуру нашего реквизита ТрудозатратыСотрудника.ПолеФормы и связываем его путь к данным с новым реквизитом.Рассмотрим детальный практический пример реализации этого алгоритма на сервере:
&НаСервере
Процедура ДобавитьКолонкуНаСервере(ИмяНовойКолонки, ЗаголовокКолонки, ТипЗначенияКолонки)
// Шаг 1. Описываем новый реквизит формы.
// Параметры конструктора РеквизитФормы: Имя, Тип, ПутьКРодителю, Заголовок, СохраняемыеДанные
НовыйРеквизит = Новый РеквизитФормы(
ИмяНовойКолонки,
ТипЗначенияКолонки,
"ТрудозатратыСотрудника",
ЗаголовокКолонки
);
// Помещаем его в массив для добавления
ДобавляемыеРеквизиты = Новый Массив;
ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
// Применяем изменения структуры формы.
// Внимание: метод ИзменитьРеквизиты() ресурсоемкий, вызывайте его один раз для пакета изменений!
ИзменитьРеквизиты(ДобавляемыеРеквизиты);
// Шаг 2. Создаем визуальный элемент на форме для отображения созданного реквизита.
РодительТаблица = Элементы.ТрудозатратыСотрудника;
ИмяЭлементаКолонки = "ТрудозатратыСотрудника" + ИмяНовойКолонки;
НоваяКолонкаФормы = Элементы.Добавить(ИмяЭлементаКолонки, Тип("ПолеФормы"), РодительТаблица);
НоваяКолонкаФормы.Вид = ВидПоляФормы.ПолеВвода;
// Связываем элемент формы с созданным ранее путем к данным
НоваяКолонкаФормы.ПутьКДанным = "ТрудозатратыСотрудника." + ИмяНовойКолонки;
НоваяКолонкаФормы.Заголовок = ЗаголовокКолонки;
КонецПроцедуры
Проанализируем важный аспект производительности при динамическом изменении формы. Метод ИзменитьРеквизиты() заставляет платформу полностью перекомпилировать контекст формы. Если вам нужно добавить сразу 10 колонок, ни в коем случае не вызывайте этот метод в цикле.
Правильный подход заключается в накоплении всех добавляемых и удаляемых реквизитов в массивы и вызове метода ИзменитьРеквизиты() ровно один раз в конце процедуры. Это убережет ваше приложение от серьезных зависаний интерфейса.
Подводя итог нашего разбора, сформулируем основные правила работы с таблицами на управляемых формах 1С:
Колонки напрямую через имя таблицы формы на клиенте. Помните, что на форме вы работаете с типом ДанныеФормыКоллекция.РеквизитФормыВЗначение("ИмяТаблицы").ВыгрузитьКолонки(). Это сохранит оперативную память сервера.ПодчиненныеЭлементы элемента формы, но помните о группах колонок и настройках видимости.ИзменитьРеквизиты, затем Элемент через Элементы.Добавить) и группируйте серверные вызовы для обеспечения высокой скорости работы интерфейса.