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