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