Почему ПоказатьВопрос сразу закрывается со значением Таймаут и как это исправить?

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

При разработке в среде 1С:Предприятие 8.3 (Управляемые формы) разработчики часто сталкиваются с ситуацией, когда немодальный метод ПоказатьВопрос не дожидается ответа пользователя. Вместо ожидаемых значений КодВозвратаДиалога.Да или КодВозвратаДиалога.Нет в процедуру обработки оповещения моментально передается КодВозвратаДиалога.Таймаут. Особенно часто это происходит внутри обработчика ОбработкаВыбора при возврате данных из формы подбора или выбора.

Разберем детально механизм возникновения этой ошибки и рассмотрим проверенные способы её устранения.

Причины автоматического сброса вопроса с кодом Таймаут

Проанализируем две ключевые причины, из-за которых платформа принудительно завершает диалог:

  1. Конфликт жизненного цикла окон при закрытии формы подбора. Когда в форме подбора пользователь совершает выбор, срабатывает стандартное закрытие дочернего окна. В этот момент клиентское приложение выполняет системную цепочку переключения фокуса и уничтожения формы. Если метод ПоказатьВопрос вызывается непосредственно в обработчике ОбработкаВыбора основной формы, открытие диалога накладывается на процесс закрытия подчиненного окна. Платформа фиксирует смену активного контекста и аварийно закрывает только что созданный диалог с результатом КодВозвратаДиалога.Таймаут.
  2. Ошибочное понимание асинхронной модели выполнения. В отличие от устаревшей модальной функции Вопрос, метод ПоказатьВопрос не останавливает выполнение исходной процедуры. Код, следующий за вызовом вопроса, выполняется мгновенно, не дожидаясь ответа пользователя. Если вся последующая логика не вынесена в отдельную процедуру оповещения, алгоритм отработает некорректно.

Способ 1. Задавать вопрос до открытия формы подбора

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

Посмотрим на пример реализации:


&НаКлиенте
Процедура КомандаОткрытьПодбор(Команда)
    Если Объект.ЗамечанияКРеквизитам.Количество() > 0 Тогда
        Оповещение = Новый ОписаниеОповещения("ПослеОтветаПередПодбором", ЭтотОбъект);
        ПоказатьВопрос(Оповещение, "Очистить текущие замечания перед подбором новых?", РежимДиалогаВопрос.ДаНет);
    Иначе
        ОткрытьФормуПодбора();
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаПередПодбором(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        Объект.ЗамечанияКРеквизитам.Очистить();
    КонецЕсли;
    ОткрытьФормуПодбора();
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьФормуПодбора()
    // Открытие формы подбора с передачей владельца
    ПараметрыФормы = Новый Структура("РежимВыбора", Истина);
    ОткрытьФорму("Справочник.Замечания.ФормаВыбора", ПараметрыФормы, Элементы.ЗамечанияКРеквизитам);
КонецПроцедуры

Способ 2. Разрыв очереди событий через обработчик ожидания

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

Рассмотрим по шагам реализацию данного подхода:


&НаКлиенте
Перем ВременныеДанныеПодбора;

&НаКлиенте
Процедура ЗамечанияКРеквизитамОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
    Если ВыбранноеЗначение.Количество() > 0 И Объект.ЗамечанияКРеквизитам.Количество() > 0 Тогда
        ВременныеДанныеПодбора = ВыбранноеЗначение;
        ПодключитьОбработчикОжидания("ЗадатьВопросПослеЗакрытияПодбора", 0.1, Истина);
    Иначе
        ЗаполнитьТабличнуюЧасть(ВыбранноеЗначение);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗадатьВопросПослеЗакрытияПодбора()
    Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект);
    ПоказатьВопрос(Оповещение, "Очистить табличную часть перед добавлением?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        Объект.ЗамечанияКРеквизитам.Очистить();
    КонецЕсли;
    
    ЗаполнитьТабличнуюЧасть(ВременныеДанныеПодбора);
    ВременныеДанныеПодбора = Неопределено;
КонецПроцедуры

&НаКлиенте
Процедура ЗаполнитьТабличнуюЧасть(ДанныеПодбора)
    Если ДанныеПодбора = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Для Каждого Подбор Из ДанныеПодбора Цикл
        Строка = Объект.ЗамечанияКРеквизитам.Добавить();
        Строка.ЗамечанияКРеквизитам = Подбор.Замечание;
    КонецЦикла;
КонецПроцедуры

Способ 3. Использование современных асинхронных конструкций Асинх и Ждать

В современных версиях платформы 1С:Предприятие (начиная с 8.3.18) рекомендуется использовать асинхронные процедуры с ключевыми словами Асинх и Ждать. Это избавляет от необходимости создавать промежуточные методы оповещения и делает код линейным и легко читаемым.

Разберем пример обработки выбора с использованием функции ВопросАсинх:


&НаКлиенте
Асинх Процедура ЗамечанияКРеквизитамОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
    Если ВыбранноеЗначение.Количество() > 0 И Объект.ЗамечанияКРеквизитам.Количество() > 0 Тогда
        Ответ = Ждать ВопросАсинх("Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);
        
        Если Ответ = КодВозвратаДиалога.Да Тогда
            Объект.ЗамечанияКРеквизитам.Очистить();
        КонецЕсли;
    КонецЕсли;
    
    Для Каждого Подбор Из ВыбранноеЗначение Цикл
        Строка = Объект.ЗамечанияКРеквизитам.Добавить();
        Строка.ЗамечанияКРеквизитам = Подбор.Замечание;
    КонецЦикла;
КонецПроцедуры

Способ 4. Использование диалогов Библиотеки Стандартных Подсистем (БСП)

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


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

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

← На главную