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