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