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