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