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