Почему вызов функции Вопрос меняет модифицированность обычной формы в 1С 8.3?

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

При разработке в среде 1С:Предприятие 8.3 на обычных формах (толстый клиент) разработчики нередко сталкиваются со странным поведением: после программной записи объекта и вызова стандартного модального окна с вопросом форма внезапно помечается как измененная (Модифицированность = Истина). В результате при закрытии формы или продолжении работы платформа выдает предупреждение: «Данные были изменены. Сохранить изменения?».

Разберем детально механику работы платформы, выясним причину такого поведения и рассмотрим надежные способы решения проблемы.

В чем заключается проблема

Рассмотрим типовой сценарий: в процедуре формы выполняется запись объекта через ЗаписатьВФорме(), после чего в цикле происходит взаимодействие с внешним оборудованием (например, печать чека через драйвер ККТ). Если лента закончилась, программа задает вопрос пользователю с помощью метода Вопрос():


ЗаписатьВФорме();
Пока Драйвер.ПечатьКопииПоследнегоЧека() <> 0 Цикл
    Если Драйвер.Результат = -3807 Тогда
        // До вызова модального диалога форма не модифицирована
        Ответ = Вопрос("Кончилась чековая лента. Заправьте и нажмите Ок.", РежимДиалогаВопрос.ОК);
        // Сразу после возврата из Вопрос() значение ЭтаФорма.Модифицированность() становится Истина
        ЗаписатьВФорме();
        Продолжить;
    КонецЕсли;
    Сообщить(СтрШаблон("При печати копии чека произошла ошибка с кодом %1 %2",
        Драйвер.Результат, Драйвер.ОписаниеРезультата));
    Прервать;
КонецЦикла;

Пошаговая отладка показывает, что до строки с вызовом Вопрос() метод Модифицированность() возвращает Ложь, а непосредственно после закрытия модального окна — Истина. Никаких прямых присвоений переменным объекта в этой части кода нет.

Причина: обработчик ОбновлениеОтображения и цикл обработки сообщений

Проанализируем скрытые механизмы платформы 1С при работе в толстом клиенте:

  1. Вложенный цикл сообщений Windows: Когда платформа вызывает модальные методы (Вопрос, Предупреждение, ОткрытьФормуМодально), основной поток приостанавливает выполнение текущего алгоритма и запускает внутренний цикл обработки сообщений операционной системы.
  2. Событие перерисовки формы: При переходе приложения в состояние ожидания ответа пользователя (Idle) или при смене активности окон платформа принудительно инициирует системное событие ОбновлениеОтображения().
  3. Неявная модификация реквизитов: Если в модуле формы определена процедура ОбновлениеОтображения() (или вызываемые из нее вспомогательные функции), и внутри нее происходит изменение значений реквизитов основного объекта (ДокументОбъект, СправочникОбъект), форма автоматически взводит флаг Модифицированность = Истина.
  4. Потеря фокуса элементами ввода: Открытие модального диалога приводит к тому, что активный элемент управления теряет фокус. Если в этот момент редактировалось табличное поле или поле ввода, срабатывают обработчики фиксации данных (ПриИзменении, ОкончаниеВводаТекста), что также может пометить форму как измененную.

Способы устранения проблемы

Разберем по шагам, как локализовать и устранить нежелательную установку модифицированности формы.

1. Ревизия процедуры ОбновлениеОтображения

Главное правило разработки обычных форм в 1С гласит: в обработчике ОбновлениеОтображения() разрешено управлять только элементами пользовательского интерфейса (свойствами Доступность, Видимость, ТолькоПросмотр, ЦветФона и т. д.).

Проверим код обработчика. Если в нем выполняются расчеты, их результат следует выводить в несвязанные реквизиты формы, а не в поля объекта:


Процедура ОбновлениеОтображения()
    // НЕПРАВИЛЬНО: изменение реквизита объекта взводит Модифицированность
    // ЭтотОбъект.СуммаДокумента = РассчитатьИтог();

    // ПРАВИЛЬНО: управление элементами формы
    ЭлементыФормы.КнопкаПечать.Доступность = Не ТолькоПросмотр;
    ЭлементыФормы.НадписьСтатус.Заголовок  = ПолучитьТекстСтатуса();
КонецПроцедуры

2. Использование флага блокировки обработки отображения

Если в процедуре ОбновлениеОтображения() содержится сложная логика, которую нельзя быстро переписать, применим флаг блокировки на уровне модуля формы:


Перем мБлокировкаОбновления;

Процедура ОбновлениеОтображения()
    Если мБлокировкаОбновления = Истина Тогда
        Возврат;
    КонецЕсли;

    // Выполнение обновлений интерфейса
    ОбновитьСостояниеКнопок();
КонецПроцедуры

Перед началом длительных операций или вызовов модальных окон взводим блокировку, а после завершения — снимаем ее:


мБлокировкаОбновления = Истина;
Попытка
    Ответ = Вопрос("Кончилась чековая лента. Заправьте и нажмите Ок.", РежимДиалогаВопрос.ОК);
Исключение
    мБлокировкаОбновления = Ложь;
    ВызватьИсключение;
КонецПопытки;
мБлокировкаОбновления = Ложь;

3. Сохранение и восстановление флага Модифицированность

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


ТекущееСостояние = ЭтаФорма.Модифицированность();
Ответ = Вопрос("Кончилась чековая лента. Заправьте и нажмите Ок.", РежимДиалогаВопрос.ОК);
ЭтаФорма.Модифицированность = ТекущееСостояние;

Особенности диагностики через отладчик

Выясним, как быстро найти место, в котором форма помечается модифицированной:

  1. Замер производительности платформы суммирует время работы строк, но не отображает полную последовательность вызовов при срабатывании системных окон.
  2. Для точного поиска установим точку останова в самом начале процедуры ОбновлениеОтображения(), а также в процедурах ПриИзменении() всех элементов формы.
  3. Добавим выражение ЭтаФорма.Модифицированность() в табло отладчика и выполним пошаговое прохождение кода, чтобы увидеть конкретную строку, изменяющую состояние объекта.
← На главную