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