Почему не работает сброс свойства ЭтаФорма.Модифицированность в Ложь при открытии формы в 1С и как это исправить?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Разработчики конфигураций на платформе «1С:Предприятие» (особенно при работе с обычными формами в толстом клиенте) часто сталкиваются со следующей ситуацией. При открытии документа в его табличные части программно подтягиваются справочные данные из внешних источников (например, из регистров сведений или других документов). Поскольку эти данные не предназначены для постоянного хранения в самом документе, а нужны лишь для удобства пользователя, программист пытается сбросить признак изменения формы, прописывая в конце процедуры ПриОткрытии следующую строку:

ЭтаФорма.Модифицированность = Ложь;

Однако при запуске программы этот код не дает ожидаемого результата. Звездочка в заголовке формы (символизирующая наличие изменений) все равно появляется, а при попытке закрыть документ без каких-либо действий пользователя система настойчиво предлагает сохранить изменения. Давайте подробно разберем, почему так происходит, как устроена логика платформы «1С:Предприятие» и какими способами можно решить эту проблему (от быстрых обходных путей до правильного проектирования архитектуры данных).

---

Выясняем причину: почему сброс флага модифицированности затирается платформой

Проанализируем детально поведение платформы при инициализации и открытии формы. Когда выполняется процедура ПриОткрытии(), мы можем отладчиком проверить значение свойства ЭтаФорма.Модифицированность. Действительно, после выполнения строки ЭтаФорма.Модифицированность = Ложь; флаг успешно сбрасывается в Ложь. Однако сразу после выхода из процедуры управление возвращается платформе 1С, и флаг снова принимает значение Истина. Происходит это по следующим причинам:

  1. Разделение модифицированности формы и объекта. Когда мы программно добавляем строки в табличную часть документа через ДокументОбъект (например, Объект.РеквизитыЗаказа.Добавить()), мы физически модифицируем сам прикладной объект в оперативной памяти компьютера. У объекта взводится внутреннее свойство модифицированности. На этапе завершения открытия формы платформа выполняет автоматическую синхронизацию данных формы с прикладным объектом. Видя, что объект изменен (метод ЭтотОбъект.Модифицированность() возвращает значение Истина), платформа принудительно устанавливает ЭтаФорма.Модифицированность = Истина уже после того, как отработал весь наш код в событии ПриОткрытии.
  2. Влияние элементов управления и их событий. На форме документа располагаются табличные поля, отображающие измененные табличные части. У этих элементов управления по умолчанию включено свойство «Изменяет данные». В процессе окончательной отрисовки интерфейса (которая происходит уже после завершения ПриОткрытии) срабатывают внутренние механизмы обновления строк табличного поля. Если у табличного поля определены обработчики событий (например, ПриВыводеСтроки, ПриПолученииДанных или даже ПриАктивизацииЯчейки), любое обращение к данным в них может спровоцировать повторную блокировку объекта и автоматическое взведение флага модифицированности формы.

Таким образом, прямое присвоение значения Ложь в обработчике открытия формы не способно перекрыть автоматические алгоритмы платформы по синхронизации данных формы и объекта.

---

Вариант 1. Архитектурно правильное решение — использование реквизита формы вместо табличной части объекта

Если данные в табличной части не должны записываться в базу данных вместе с документом (так как они динамически считываются из регистра сведений), то хранить их в метаданных документа в виде табличной части — это серьезная архитектурная ошибка. Правильнее всего полностью отказаться от использования физической табличной части документа для отображения этой информации.

Рассмотрим по шагам, как перевести отображение данных на независимый реквизит формы:

  1. Откроем форму документа в конфигураторе.
  2. В окне свойств формы перейдем на вкладку «Реквизиты» и создадим новый реквизит формы (например, с именем ТаблицаПараметров) и типом значения ТаблицаЗначений.
  3. Добавим в созданную таблицу значений колонки, которые нам необходимо отображать пользователю (например, Реквизит, Значение, Код, ТипПоля).
  4. Перенесем созданный реквизит ТаблицаПараметров на рабочую область формы в виде элемента управления ТабличноеПоле.
  5. В свойствах этого табличного поля убедимся, что его данные связаны именно с реквизитом формы ТаблицаПараметров, а не с объектом документа.

Теперь напишем код заполнения нашей таблицы на форме при открытии. Поскольку реквизиты формы не относятся к данным прикладного объекта, их программное изменение не будет приводить к модификации самого документа:


Процедура ПриОткрытии()
    
    // Заполняем реквизит формы данными из регистра сведений
    ЗаполнитьТаблицуПараметровНаФорме();
    
    // Теперь свойство модифицированности формы останется в значении Ложь, 
    // так как сам ДокументОбъект мы никак не затрагивали в памяти.
    
КонецПроцедуры

Процедура ЗаполнитьТаблицуПараметровНаФорме()
    
    ТаблицаПараметров.Очистить();
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	БЗ_ЗначенияРеквизитов.Реквизит КАК Реквизит,
        |	БЗ_ЗначенияРеквизитов.Значение КАК Значение,
        |	БЗ_ЗначенияРеквизитов.Код КАК Код,
        |	БЗ_ЗначенияРеквизитов.ТипРеквизита КАК ТипПоля
        |ИЗ
        |	РегистрСведений.БЗ_ЗначенияРеквизитов КАК БЗ_ЗначенияРеквизитов
        |ГДЕ
        |	БЗ_ЗначенияРеквизитов.Заказ = &Заказ";
        
    Запрос.УстановитьПараметр("Заказ", ЭтотОбъект.Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        // Загружаем данные напрямую в реквизит формы (ТаблицуЗначений)
        ТаблицаПараметров.Загрузить(РезультатЗапроса.Выгрузить());
    КонецЕсли;
    
КонецПроцедуры

Если пользователю разрешено редактировать эту таблицу на форме, и при этом документ все-таки должен становиться грязным (модифицированным), мы можем легко отследить интерактивные действия пользователя через события табличной части. Для этого достаточно определить обработчик события ПриИзменении для нашего табличного поля на форме:


Процедура ТаблицаПараметровПриИзменении(Элемент)
    
    // Если пользователь вручную отредактировал строку,
    // мы принудительно взводим модифицированность формы
    ЭтаФорма.Модифицированность = Истина;
    
КонецПроцедуры

При таком подходе мы полностью избавляемся от "звездочки" при простом просмотре документа, сохраняя при этом возможность отслеживать реальные изменения данных пользователем.

---

Вариант 2. Быстрый обходной путь через обработчик ожидания

Если конфигурация находится на поддержке, имеет сложную структуру связей, или у вас нет времени на перепроектирование метаданных и замену табличных частей на реквизиты формы, можно применить обходной прием (так называемый «костыль»). Его суть заключается в использовании асинхронного вызова через обработчик ожидания.

Мы подключаем однократный обработчик ожидания с минимальной задержкой в самом конце процедуры ПриОткрытии. Платформа сначала завершит все процедуры открытия формы, отрисует интерфейс, выполнит синхронизацию модифицированности с объектом, и только после этого выполнит процедуру нашего обработчика ожидания, в которой мы гарантированно сбросим модифицированность в Ложь.

Посмотрим, как выглядит программная реализация данного метода:


Процедура ПриОткрытии()
    
    // Выполняем заполнение табличных частей документа
    ОбновимПараметры(); 
    
    // Подключаем однократный обработчик ожидания (интервал 0.1 секунды)
    ПодключитьОбработчикОжидания("СброситьМодифицированностьПослеОткрытия", 0.1, Истина);
    
КонецПроцедуры

Процедура СброситьМодифицированностьПослеОткрытия() Экспорт
    
    // Данная процедура выполнится уже после того, как форма полностью откроется
    // и платформа завершит все свои внутренние обработчики синхронизации.
    ЭтаФорма.Модифицированность = Ложь;
    
КонецПроцедуры

Важное примечание: Процедура, вызываемая обработчиком ожидания, обязательно должна быть объявлена с ключевым словом Экспорт, иначе платформа не сможет получить к ней доступ из глобального контекста.

---

Вариант 3. Использование свойства элемента управления «Изменяет данные»

Если модифицированность формы продолжает взводиться из-за интерактивного обновления строк на экране, проанализируем настройки элементов управления. У каждого табличного поля на форме есть специализированное свойство.

Разберем по шагам, как его отключить:

  1. Откроем форму документа в конфигураторе и выделим проблемное табличное поле (например, связанное с ТЧ РеквизитыЗаказа).
  2. Откроем палитру свойств этого элемента управления (сочетание клавиш Alt + Enter).
  3. Найдем в списке свойство «Изменяет данные» (оно находится в категории «Данные»).
  4. Снимем галочку напротив этого свойства (установим значение в Ложь).

Когда это свойство отключено, любые программные манипуляции со строками в этой таблице или вызовы связанных с ней интерфейсных событий при выводе данных на экран не будут автоматически переводить форму в состояние модифицированности.

---

Резюме и рекомендации

Подводя итоги, сделаем важные выводы для качественной разработки в среде «1С:Предприятие»:

← На главную