Мы, как разработчики на платформе 1С:Предприятие, часто сталкиваемся с необходимостью динамически изменять структуру управляемой формы. Например, может потребоваться добавлять новые реквизиты в зависимости от данных, загруженных из базы, или пользовательских настроек. В такой ситуации возникает закономерный вопрос: как получить доступ к этим программно созданным реквизитам, чтобы присвоить им значения или управлять их поведением? И не менее важный вопрос: как избежать снижения производительности формы при добавлении большого количества таких элементов? Давайте рассмотрим эту задачу подробно, проанализируем возможные решения и выясним оптимальные подходы.
Основной вопрос, который возникает при динамическом добавлении реквизитов на управляемую форму, это как к ним обратиться после их создания. Если бы реквизит был добавлен статически в конфигураторе, мы могли бы использовать прямое обращение по имени, например, ЭтаФорма.МойРеквизит. Однако, когда имя реквизита формируется программно, например, в цикле, такой подход неприменим, поскольку точное имя каждого реквизита неизвестно на этапе компиляции.
Для решения этой задачи в 1С:Предприятии предусмотрен специальный синтаксис, позволяющий обращаться к реквизитам формы по их строковому имени. Этот механизм аналогичен доступу к элементам коллекции по ключу и использует квадратные скобки.
Предположим, мы добавляем реквизиты с именами типа "Сообщение1", "Сообщение2" и так далее. После того как мы добавили их с помощью процедуры ИзменитьРеквизиты, мы можем обратиться к каждому из них следующим образом:
// Пример добавления реквизитов на серверной процедуре (например, ПриСозданииНаСервере)
// 1. Подготавливаем массив для добавляемых реквизитов
МассивДобавляемыхРеквизитов = Новый Массив;
Счетчик = 0; // Инициализируем счетчик для уникальных имен
// 2. В цикле собираем информацию о будущих реквизитах и создаем объекты РеквизитФормы
Для каждого СтрокаЗначений из ТаблицаЗначений Цикл
Счетчик = Счетчик + 1;
ИмяНовогоРеквизита = "Сообщение" + Счетчик;
// Создаем объект РеквизитФормы с динамическим именем и нужным типом (например, ФорматированныйДокумент)
НовоеСообщение = Новый РеквизитФормы(ИмяНовогоРеквизита, Новый ОписаниеТипов("ФорматированныйДокумент"));
МассивДобавляемыхРеквизитов.Добавить(НовоеСообщение);
КонецЦикла;
// 3. Вызываем ИзменитьРеквизиты один раз, после того как собрали все реквизиты в массив.
// Это критически важно для производительности.
Если МассивДобавляемыхРеквизитов.Количество() > 0 Тогда
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
КонецЕсли;
// 4. Теперь, когда реквизиты добавлены в структуру формы, мы можем присваивать им значения.
// Снова проходим по нашим данным или используем уже существующий контекст.
Счетчик = 0;
Для каждого СтрокаЗначений из ТаблицаЗначений Цикл
Счетчик = Счетчик + 1;
ИмяНовогоРеквизита = "Сообщение" + Счетчик;
// Обращаемся к реквизиту по строковому имени через квадратные скобки
ЭтаФорма[ИмяНовогоРеквизита] = СтрокаЗначений.СодержимоеСообщения;
// Предполагаем, что СтрокаЗначений.СодержимоеСообщения имеет тип ФорматированныйДокумент
КонецЦикла;
Здесь ЭтаФорма представляет собой текущий объект управляемой формы. Использование ЭтаФорма[ИмяНовогоРеквизита] позволяет динамически собрать имя реквизита и затем получить к нему доступ для присвоения значения. Важно отметить, что этот синтаксис доступен сразу после выполнения процедуры ИзменитьРеквизиты в серверном контексте формы. Некоторые разработчики могут задаться вопросом о возможности использования ЭтотОбъект.ВашПрограммноДобавленныйРеквизит, но это некорректно, поскольку ЭтотОбъект относится к основному объекту данных формы (если форма является формой объекта), а не к реквизитам самой формы, добавленным через ИзменитьРеквизиты.
Одной из важных проблем, возникающих при массовом программном добавлении реквизитов, является потенциальное снижение производительности формы. Рассмотрим несколько ключевых моментов, которые помогут нам избежать "тормозов" и оптимизировать работу:
ИзменитьРеквизиты:
Процедура ИзменитьРеквизиты является достаточно ресурсоемкой, так как она вызывает обновление структуры формы. Критически важно собирать все добавляемые или удаляемые реквизиты в один массив и вызывать ИзменитьРеквизиты только один раз за операцию, а не в цикле. Многократные вызовы этой процедуры внутри цикла значительно замедлят работу формы, так как каждый вызов инициирует внутренние процессы перестроения формы.
// НЕПРАВИЛЬНО (вызов ИзменитьРеквизиты в цикле):
// Счетчик = 0;
// Для каждого СтрокаЗначений из ТаблицаЗначений Цикл
// Счетчик = Счетчик + 1;
// НовоеСообщение = Новый РеквизитФормы("Сообщение"+Счетчик, Новый ОписаниеТипов("ФорматированныйДокумент"));
// МассивДляИзменения = Новый Массив; // Создание нового массива каждый раз
// МассивДляИзменения.Добавить(НовоеСообщение);
// ИзменитьРеквизиты(МассивДляИзменения); // МНОГОКРАТНЫЙ ВЫЗОВ - ПЛОХАЯ ПРОИЗВОДИТЕЛЬНОСТЬ
// КонецЦикла;
// ПРАВИЛЬНО (один вызов ИзменитьРеквизиты после сбора всех реквизитов):
МассивДобавляемыхРеквизитов = Новый Массив;
Для каждого СтрокаЗначений из ТаблицаЗначений Цикл
// ... создаем НовоеСообщение ... (как в примере выше)
МассивДобавляемыхРеквизитов.Добавить(НовоеСообщение);
КонецЦикла;
Если МассивДобавляемыхРеквизитов.Количество() > 0 Тогда
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов); // ЕДИНСТВЕННЫЙ ВЫЗОВ - ОПТИМАЛЬНО
КонецЕсли;
Даже при однократном вызове ИзменитьРеквизиты, если мы добавляем очень большое количество реквизитов (сотни и более), это может сказаться на времени открытия и отклика формы. Каждый реквизит требует соответствующих элементов управления на форме для отображения (например, текстовое поле, поле форматированного документа и т.д.), что увеличивает объем данных для отрисовки в браузере или тонком клиенте.
Особенно это актуально для реквизитов сложного типа, таких как ФорматированныйДокумент. Отображение большого числа полей с форматированным текстом может быть очень ресурсоемким, поскольку каждый такой элемент может содержать значительный объем данных и требует сложной отрисовки. Автор исходной темы столкнулся именно с этой проблемой, когда пытался отобразить много ФорматированныйДокумент, и в конечном итоге отказался от такого подхода из-за тормозов.
Если мы подозреваем проблемы с производительностью, всегда необходимо использовать встроенные инструменты замера производительности 1С:Предприятия. Они позволяют точно определить, какие участки кода занимают больше всего времени выполнения. Это поможет нам локализовать "узкие места" и принять обоснованные решения по оптимизации, а не действовать наугад.
Как показывает практика, сам по себе вызов ИзменитьРеквизиты для небольшого числа реквизитов занимает относительно небольшую долю общего времени открытия формы (например, 3-5%). Однако, если речь идет о сотнях реквизитов, суммарное время на их создание и, главное, отрисовку соответствующих элементов управления может стать критическим. Замеры помогут нам отличить проблемы с добавлением реквизитов от проблем с отрисовкой элементов или с работой другого кода.
В исходной задаче обсуждалось отображение множества элементов типа ФорматированныйДокумент. Если добавление отдельного реквизита для каждого такого элемента приводит к неприемлемым "тормозам", нам следует рассмотреть следующие альтернативные решения, которые часто оказываются более эффективными:
Вместо того чтобы создавать отдельный реквизит и соответствующий элемент управления для каждого форматированного документа, мы можем разместить на форме один элемент типа ПолеФорматированногоДокумента (или ПолеHTMLДокумента, если содержимое можно представить в HTML). Когда пользователь выбирает строку в таблице значений, динамическом списке или другом списке, мы присваиваем содержимое выбранного элемента этому единственному полю. Таким образом, у нас на форме всегда присутствует только один сложный элемент для отрисовки, что значительно экономит ресурсы и улучшает скорость отклика.
// Пример:
// У нас есть реквизит формы "ТаблицаСообщений" типа ТаблицаЗначений,
// содержащий колонку "Сообщение" типа ФорматированныйДокумент.
// На форме создан элемент "ПолеДетальногоСообщения" с видом "Поле форматированного документа"
// и связанный с реквизитом формы (например, просто для инициализации).
&НаКлиенте
Процедура ТаблицаСообщенийПриАктивизацииСтроки(Элемент)
Если Элемент.ТекущиеДанные <> Неопределено Тогда
// При активации строки, присваиваем содержимое колонки "Сообщение"
// нашему единственному элементу ПолеДетальногоСообщения
ЭтаФорма.Элементы.ПолеДетальногоСообщения.Значение = Элемент.ТекущиеДанные.Сообщение;
Иначе
// Если строка не выбрана, очищаем поле
ЭтаФорма.Элементы.ПолеДетальногоСообщения.Значение = Новый ФорматированныйДокумент();
КонецЕсли;
КонецПроцедуры;
Этот подход особенно эффективен, когда нам нужно отображать детали выбранной записи, а не все записи сразу. Это было одно из решений, к которому пришел автор темы, отказавшись от массовой генерации реквизитов.
Хотя напрямую поместить ФорматированныйДокумент в колонку табличного поля управляемой формы нельзя, мы можем использовать табличное поле с источником данных типа ДинамическийСписок или ТаблицаЗначений для отображения списка элементов. Для доступа к форматированному содержимому можно рассмотреть следующие варианты, хотя они не всегда удовлетворяют всем требованиям к отображению:
ФорматированныйДокумент), это может быть опцией. Однако это потребует предварительного преобразования ФорматированныйДокумент в HTML.
ПолеФорматированногоДокумента, куда и будет загружаться полное содержимое ФорматированныйДокумент для выбранной строки. Это позволяет эффективно управлять ресурсами, загружая сложный элемент только по требованию.
Если нам действительно нужно отобразить очень много форматированных документов, и их количество не позволяет создать все элементы сразу, мы можем использовать принцип ленивой загрузки (Lazy Loading). Например, отображать только первые N документов, а остальные подгружать по мере прокрутки табличного поля или по требованию пользователя. Это сложнее в реализации, так как требует дополнительной логики для управления видимостью и подгрузкой данных, но позволяет эффективно управлять нагрузкой на форму и сервер.
В ходе нашего анализа мы выяснили, что для обращения к программно добавленным реквизитам управляемой формы в 1С 8.3 следует использовать синтаксис ЭтаФорма[ИмяРеквизитаСтрокой]. Это позволяет динамически формировать имена реквизитов и успешно с ними работать в серверном контексте. При этом крайне важно вызывать процедуру ИзменитьРеквизиты только один раз после формирования полного списка добавляемых реквизитов, чтобы минимизировать накладные расходы и избежать проблем с производительностью.
Если мы сталкиваемся с проблемами производительности при массовом добавлении реквизитов, особенно таких сложных типов как ФорматированныйДокумент, следует рассмотреть альтернативные подходы к отображению данных. Использование одного центрального элемента для вывода деталей выбранной записи или применение принципов ленивой загрузки могут значительно улучшить пользовательский опыт и скорость работы формы. И всегда помните: для точной диагностики "узких мест" в вашем коде необходимо использовать инструменты замера производительности 1С:Предприятия.