Предупреждения, сообщения и диалоги во встроенном языке 1С
Содержание
Самый короткий способ показать предупреждение пользователю во встроенном языке 1С — вызвать Предупреждение("Текст"). Это блокирующее модальное окно с кнопкой «ОК». Но в тонком и веб-клиенте модальные вызовы по умолчанию запрещены, поэтому в современных конфигурациях вместо него используют асинхронный ПоказатьПредупреждение() или конструкцию Ждать ПредупреждениеАсинх().
Кроме окна с «ОК», предупреждение можно оформить как вопрос с кнопками Вопрос(), как привязанное к полю сообщение СообщениеПользователю, как всплывающее оповещение в углу или как собственную форму. Ниже — все рабочие варианты с кодом: когда какой уместен и как не поймать ошибку модальности.
Классическое окно с кнопкой «ОК»: функция Предупреждение()
Функция Предупреждение() выводит модальное окно с текстом и единственной кнопкой «ОК». Пока пользователь её не нажмёт, выполнение кода стоит на месте.
Предупреждение("Сохраните документ перед закрытием.");
У функции есть ещё два параметра — таймаут автозакрытия (в секундах) и заголовок окна:
Предупреждение("Сеанс завершится через 10 секунд.", 10, "Внимание");
Если таймаут равен нулю (значение по умолчанию), окно ждёт нажатия сколько угодно. При положительном значении окно закроется само по истечении срока — это удобно для информационных сообщений, которые не должны «ловить» пользователя.
Предупреждение() — синхронный модальный вызов. В тонком и веб-клиенте при свойстве конфигурации «Режим использования модальности» = «Не использовать» такой вызов вызовет ошибку компиляции или выполнения. Для новых конфигураций используйте асинхронные методы из следующего раздела.
Проверить режим можно так: откройте в Конфигураторе свойства корня конфигурации и найдите свойство Режим использования модальности. Три варианта — «Использовать», «Использовать с предупреждениями» и «Не использовать». В типовых конфигурациях на управляемых формах стоит «Не использовать».
Неблокирующие вызовы: ПоказатьПредупреждение() и Ждать
Асинхронная замена — процедура ПоказатьПредупреждение(). Она не останавливает код: окно показывается, управление сразу возвращается, а то, что должно выполниться после нажатия «ОК», выносится в отдельную процедуру-обработчик оповещения.
&НаКлиенте
Процедура ПроверитьИПродолжить(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеПредупреждения", ЭтотОбъект);
ПоказатьПредупреждение(Оповещение, "Операция завершена.");
КонецПроцедуры
&НаКлиенте
Процедура ПослеПредупреждения(ДополнительныеПараметры) Экспорт
// Этот код выполнится после того, как пользователь нажмёт «ОК».
КонецПроцедуры
Объект ОписаниеОповещения хранит имя процедуры-обработчика и модуль, где она лежит. Третьим параметром в него можно передать произвольные данные — они придут в ДополнительныеПараметры.
В платформе 8.3.18 и новее появился более читаемый вариант — асинхронные методы с ключевыми словами Асинх и Ждать. Код после ожидания пишется прямо под вызовом, без отдельной процедуры:
&НаКлиенте
Асинх Процедура ПроверитьИПродолжить(Команда)
Ждать ПредупреждениеАсинх("Операция завершена.");
// Код продолжается здесь после нажатия «ОК».
КонецПроцедуры
Процедура-обработчик должна удовлетворять трём условиям, иначе платформа не сможет её вызвать: она лежит в том же модуле, что указан вторым параметром ОписаниеОповещения (ЭтотОбъект — модуль текущей формы), помечена Экспорт и имеет директиву компиляции &НаКлиенте. Несовпадение имени процедуры в строке и в объявлении — самая частая причина, почему «после окна ничего не происходит».
Если первый параметр ОписаниеОповещения передать как Неопределено, окно просто покажется, а продолжения не будет — это допустимо, когда после закрытия ничего делать не нужно.
Диалог с кнопками: Вопрос() и ПоказатьВопрос()
Когда нужно не просто уведомить, а получить ответ, подойдёт Вопрос(). Набор кнопок задаёт системное перечисление РежимДиалогаВопрос, а результат возвращается значением КодВозвратаДиалога.
Ответ = Вопрос("Провести документ перед закрытием?", РежимДиалогаВопрос.ДаНетОтмена);
Если Ответ = КодВозвратаДиалога.Да Тогда
// провести
ИначеЕсли Ответ = КодВозвратаДиалога.Нет Тогда
// закрыть без проведения
Иначе
// отмена — ничего не делаем
КонецЕсли;
Основные наборы кнопок:
- 🔹
ДаНет— две кнопки для бинарного выбора; - 🔹
ДаНетОтмена— ещё и возможность передумать; - 🔹
ОКОтмена— подтверждение действия; - 🔹
ПовторитьОтмена— после неудачной операции предложить повтор.
Асинхронный аналог — ПоказатьВопрос(). Результат приходит первым параметром обработчика:
&НаКлиенте
Процедура СпроситьИЗакрыть(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Закрыть без сохранения?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопроса(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Модифицированность = Ложь;
Закрыть();
КонецЕсли;
КонецПроцедуры
У Вопрос() есть дополнительные параметры: таймаут автозакрытия, кнопка по умолчанию (на которой стоит фокус) и заголовок окна. Для опасных операций ставьте по умолчанию безопасный вариант, чтобы случайный Enter не запустил удаление:
Ответ = Вопрос("Удалить документ без возможности отмены?",
РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет, "Подтверждение");
В версиях 8.3.18+ есть и ВопросАсинх(), который возвращает КодВозвратаДиалога через Ждать — без отдельной процедуры обработки.
Сообщения без остановки работы: Сообщить() и СообщениеПользователю
Модальное окно уместно не всегда: если нужно просто вывести текст в служебную область, лучше подходит объект СообщениеПользователю. Он не блокирует интерфейс и умеет привязываться к полю формы.
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен контрагент.";
Сообщение.Поле = "Контрагент";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Свойство Поле указывает реквизит, рядом с которым 1С подсветит сообщение и подставит красную рамку. Для табличной части путь к полю записывают с индексом строки — например, Товары[2].Количество. Это стандартный приём в процедуре ОбработкаПроверкиЗаполнения() объекта.
Старая глобальная функция Сообщить("Текст") тоже выводит строку в ту же область, но она признана устаревшей и привязку к полю не поддерживает. В новом коде вместо неё применяют СообщениеПользователю.
Канонический случай этого объекта — проверка заполнения в модуле объекта. В процедуре ОбработкаПроверкиЗаполнения() каждое нарушение выводят отдельным сообщением с указанием поля, а чтобы платформа не сформировала своё стандартное сообщение, реквизит исключают из массива проверяемых:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Выберите контрагента.";
Сообщение.Поле = "Контрагент";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Для строки табличной части или элемента списка вместо (или вместе с) Поле заполняют свойство КлючДанных ссылкой на нужный объект — тогда по клику на сообщение платформа откроет именно его.
Сообщения, выведенные таким способом из серверного кода при записи объекта, автоматически попадут в окно сообщений формы, из которой шла запись.
Всплывающие оповещения в углу экрана
Когда действие прошло успешно и прерывать пользователя незачем, подойдёт ПоказатьОповещениеПользователя() — небольшая всплывающая плашка в правом нижнем углу окна 1С, которая гаснет сама.
ПоказатьОповещениеПользователя(
"Документ записан: " + Объект.Номер,
ПолучитьНавигационнуюСсылку(Объект.Ссылка),
"Нажмите, чтобы открыть",
,
СтатусОповещенияПользователя.Информация);
Параметры по порядку: текст, навигационная ссылка (куда перейти по клику), пояснение, картинка и статус. Со статусом СтатусОповещенияПользователя.Важное оповещение не исчезает автоматически — пользователь закроет его сам. Последним параметром можно передать ключ уникальности: оповещения с одинаковым ключом не дублируются, а обновляют уже показанное — это спасает, когда уведомление формируется в цикле.
Отдельно про «системные уведомления Windows»: встроенный язык 1С своего API к центру уведомлений операционной системы не имеет. Всплывающие оповещения показываются средствами самой платформы внутри окна приложения, а не в системном трее. Если нужна именно интеграция с уведомлениями ОС, это решается внешней компонентой — штатной функции для этого в платформе нет, поэтому не рассчитывайте на неё в типовом коде.
Своя форма вместо стандартного окна
Стандартные окна не настраиваются: нельзя добавить третью кнопку с произвольной надписью, поле ввода или картинку. Когда этого мало, делают отдельную общую форму и открывают её как модальный диалог.
&НаКлиенте
Процедура ПоказатьДиалог(Команда)
Параметры = Новый Структура("Текст", "Удалить помеченные объекты?");
Оповещение = Новый ОписаниеОповещения("ПослеДиалога", ЭтотОбъект);
ОткрытьФорму("ОбщаяФорма.ДиалогПодтверждения", Параметры, ЭтотОбъект,
, , , Оповещение, РежимОткрытияОкна.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура ПослеДиалога(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Истина Тогда
// пользователь подтвердил
КонецЕсли;
КонецПроцедуры
Режим БлокироватьОкноВладельца делает окно модальным по отношению к форме-владельцу, но не ломается в веб-клиенте — в отличие от устаревшего ОткрытьФормуМодально(). Внутри своей формы кнопки возвращают результат через Закрыть(ЗначениеВозврата), и он приходит в обработчик оповещения.
Чтобы диалог вернул не просто «да / нет», а введённые значения, кнопки формы закрывают её структурой. Вызывающий код разбирает её в обработчике:
// В модуле формы диалога, по кнопке «Подтвердить»:
Результат = Новый Структура("Подтверждено, Комментарий", Истина, Комментарий);
Закрыть(Результат);
// В обработчике оповещения:
Если ТипЗнч(Результат) = Тип("Структура") И Результат.Подтверждено Тогда
ТекстПричины = Результат.Комментарий;
КонецЕсли;
Такой подход оправдан, когда диалог должен собрать данные: например, выбрать причину списания и ввести комментарий одной формой. Если же пользователь закрыл окно крестиком, в обработчик придёт Неопределено — этот случай всегда проверяйте перед обращением к полям результата.
Предупреждения в фоновых заданиях
В фоновом задании интерфейса нет — ни одно из окон (Предупреждение, Вопрос, открытие формы) там не сработает, потому что некому нажимать кнопки. Это частая ошибка: код, который прекрасно работал на форме, в фоне падает или молча ничего не делает.
Что можно вместо этого:
- Писать ход работы в журнал регистрации (см. следующий раздел) — это надёжнее всего.
- Накапливать сообщения через
СообщениеПользователю— после завершения задания их забирают функциейПолучитьСообщенияПользователю(Истина)и показывают пользователю уже в основном сеансе. - Возвращать результат и текст через параметр-структуру, если фоновое задание запущено из вашего кода и вы сами разбираете его итог.
// В теле фонового задания:
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не загружено 3 строки из файла.";
Сообщение.Сообщить();
Не пытайтесь «достучаться» до пользователя из фона модальным окном. Сеанс фонового задания изолирован, и попытка показать диалог либо вызовет исключение, либо зависнет без эффекта.
Запись предупреждений в журнал регистрации
Если предупреждение важно сохранить для разбора — а не только показать на секунду, — его пишут в журнал регистрации процедурой ЗаписьЖурналаРегистрации().
ЗаписьЖурналаРегистрации(
"ЗагрузкаДанных.Проверка",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Пропущено 3 строки: не найден контрагент.");
Параметры: имя события (произвольная строка, по которой потом фильтруют), уровень важности, метаданные, ссылка на данные и текст комментария. Уровни задаёт перечисление УровеньЖурналаРегистрации: Информация, Предупреждение, Ошибка, Отказ.
Посмотреть записанное можно в Конфигураторе через меню Администрирование → Журнал регистрации, а в режиме 1С:Предприятие — в разделе администрирования соответствующей конфигурации. В журнале удобно отбирать записи по имени события и по уровню: так легко собрать все предупреждения конкретной обработки за день.
Сравнение способов по задаче:
| Способ | Блокирует работу | Виден в фоне | Привязка к полю |
|---|---|---|---|
Предупреждение() | да | нет | нет |
ПоказатьПредупреждение() | нет | нет | нет |
Вопрос() | да | нет | нет |
СообщениеПользователю | нет | частично | да |
ПоказатьОповещениеПользователя() | нет | нет | нет |
ЗаписьЖурналаРегистрации() | нет | да | нет |
Частые вопросы
Почему Предупреждение() выдаёт ошибку «использование модальных вызовов запрещено»?
ПоказатьПредупреждение() с обработчиком оповещения или на Ждать ПредупреждениеАсинх().Как сделать предупреждение с кнопками «Да» и «Нет»?
Вопрос("Текст", РежимДиалогаВопрос.ДаНет) и сравните результат с КодВозвратаДиалога.Да. В асинхронном коде — ПоказатьВопрос() или ВопросАсинх().Можно ли показать предупреждение из серверной процедуры?
СообщениеПользователю, а окна и вопросы вызывают из клиентских процедур формы.Чем всплывающее оповещение отличается от окна Предупреждение()?
ПоказатьОповещениеПользователя() не прерывает работу и гаснет само через несколько секунд, а Предупреждение() держит фокус до нажатия «ОК». Первое — для успешных уведомлений, второе — когда пользователь обязан прочитать.Как задать своё время автозакрытия окна?
Предупреждение() и ПоказатьПредупреждение() есть параметр таймаута в секундах. Например, Предупреждение("Готово", 5) закроется через 5 секунд, даже если «ОК» не нажали.