Разработчики управляемых форм в 1С:Предприятии 8.3 иногда сталкиваются с крайне неприятной проблемой: при выполнении вполне стандартного кода по изменению видимости или доступности элементов интерфейса платформа неожиданно закрывается с критической ошибкой. Операционная система при этом выдает стандартное окно с сообщением о том, что работа программы прекращена, и Windows осуществляет поиск способа устранения этой ошибки.
Особенно часто это происходит в версиях платформы начиная с 8.3.14. В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем внутренние механизмы работы платформы и рассмотрим четыре надежных способа обхода этой системной ошибки.
Для того чтобы понять природу сбоя, обратимся к типовому сценарию программного кода, который приводит к вылету системы. Проанализируем пример, когда у нас есть поле ввода (например, выбор клиента со типом СправочникСсылка.Клиенты). Разработчик хочет реализовать следующую логику: если введенного клиента нет в базе данных, программа должна спросить пользователя о создании нового контрагента, программно переключить шаг интерфейса и скрыть/показать определенные элементы формы.
Посмотрим на исходный код обработчиков событий:
&НаКлиенте
Процедура КлиентОкончаниеВводаТекста(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, СтандартнаяОбработка)
Если Клиент.Пустая() Тогда
Ответ = Вопрос("Данного клиента нет в базе. Создать нового?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
КлиентФИО = Текст;
Переключатель = 1;
ПереключательПриИзменении(Неопределено);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПереключательПриИзменении(Элемент)
СтруктураЗаголовков = ПолучитьЗаголовкиФормы(); // Вызов сервера
Если Переключатель = 0 Тогда
Элементы.Шаг12.Видимость = Ложь;
Заголовок = СтруктураЗаголовков.Заголовок11;
Элементы.Шаг11.Видимость = Истина;
ИначеЕсли Переключатель = 1 Тогда
Элементы.Шаг11.Видимость = Ложь; // Точка сбоя!
Заголовок = СтруктураЗаголовков.Заголовок12;
// ... дальнейшая серверная логика заполнения данных ...
Элементы.Шаг12.Видимость = Истина;
КонецЕсли;
КонецПроцедуры
На первый взгляд код выглядит логичным, однако в нем кроется фундаментальное нарушение правил работы клиент-серверного взаимодействия управляемого интерфейса 1С. Выделим три ключевые причины, по которым этот код заставляет платформу аварийно завершать работу:
ОкончаниеВводаТекста выполняется в интерактивном режиме ожидания ввода. В соответствии со встроенной справкой платформы (Синтаксис-Помощником), в обработчике этого события строго запрещено использовать контекстные серверные вызовы формы (методы с директивой &НаСервере). Видимость для элемента формы, на котором в данный момент сфокусирован ввод (или который находится в процессе завершения редактирования), платформа инициирует немедленный пересчет геометрии всей формы. Пытаясь перестроить интерфейс в тот момент, когда поле ввода еще не завершило передачу данных и не потеряло фокус, движок отрисовки (UI-поток тонкого клиента) уходит в бесконечную рекурсию или обращается к некорректным адресам памяти. Это вызывает ошибку нарушения доступа (Access Violation) на уровне операционной системы.Вопрос() блокирует выполнение потока в момент ввода текста. Это нарушает стандартную последовательность системных событий фокуса ввода и усугубляет нестабильность платформы.Рассмотрим подробнее четыре практических способа решения этой проблемы, которые позволят сделать ваш код стабильным и безопасным.
---
Самый надежный способ предотвратить падение платформы — это разорвать непрерывный цикл выполнения интерфейсных событий. Мы можем отложить выполнение тяжелой логики переключения видимости и серверных вызовов. Для этого отлично подходит встроенный механизм платформы ПодключитьОбработчикОжидания.
Идея заключается в том, чтобы в самом событии ОкончаниеВводаТекста лишь зафиксировать намерение изменить интерфейс, а саму процедуру переключения вызвать через микроскопическую паузу (например, 0.1 секунды). За это время поле ввода успеет корректно завершить свою работу, передать данные в фоновом режиме и безопасно освободить системные ресурсы.
Реализуем этот подход на практике. Изменим наш код следующим образом:
&НаКлиенте
Процедура КлиентОкончаниеВводаТекста(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, СтандартнаяОбработка)
Если Клиент.Пустая() Тогда
// Используем асинхронный вызов вопроса для предотвращения блокировки UI-потока
Оповещение = Новый ОписаниеОповещения("ПослеВопросаОСозданииКлиента", ЭтотОбъект, Текст);
ПоказатьВопрос(Оповещение, "Данного клиента нет в базе. Создать нового?", РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопросаОСозданииКлиента(РезультатВыбора, ДополнительныеПараметры) Экспорт
Если РезультатВыбора = КодВозвратаДиалога.Да Тогда
КлиентФИО = ДополнительныеПараметры; // Переносим текст в реквизит формы
Переключатель = 1;
// Подключаем однократный обработчик ожидания с задержкой 0.1 секунды
ПодключитьОбработчикОжидания("ВыполнитьПереключениеИнтерфейса", 0.1, Истина);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьПереключениеИнтерфейса() Экспорт
// Данный метод вызывается вне контекста ввода текста. Платформа находится в состоянии покоя.
// Теперь мы можем безопасно вызывать серверные методы и менять видимость любых элементов.
ПереключательПриИзменении(Неопределено);
КонецПроцедуры
Этот простой шаг полностью решает проблему "вылетов", так как в момент изменения свойства Видимость активное поле ввода уже завершило свою работу и не конфликтует со структурой графического интерфейса формы.
---
Если специфика вашей задачи требует частого и динамического переключения экранов (например, при реализации пошагового мастера ввода данных "Мастер настройки" или "Помощник создания"), постоянное изменение свойств Видимость = Истина/Ложь у большого количества элементов перегружает систему отрисовки. Платформа вынуждена каждый раз полностью пересчитывать положение всех соседних элементов формы.
Значительно более производительным и безопасным решением является использование панели страниц со скрытыми закладками:
ГруппаШаги).ОтображениеЗакладок в значение Не отображать.СтраницаШаг1 и СтраницаШаг2).Теперь вместо небезопасного изменения видимости отдельных элементов вы можете просто переключать активную страницу одной строкой кода:
&НаКлиенте
Процедура ПереключитьНаВторойШаг()
// Этот код работает мгновенно и не приводит к перестроению дерева элементов
Элементы.ГруппаШаги.ТекущаяСтраница = Элементы.СтраницаШаг2;
КонецПроцедуры
При таком подходе элементы формы всегда остаются "видимыми" с точки зрения платформы, но пользователь видит только ту страницу, которая активна в данный момент. Это полностью исключает возникновение системных ошибок отрисовки.
---
В некоторых случаях интерфейсные задачи можно решить без физического скрытия полей. Если дизайн-система вашего прикладного решения допускает отображение неактивных полей в сером (заблокированном) цвете, настоятельно рекомендуется заменить управление свойством Видимость на свойство Доступность.
Давайте посмотрим на разницу в поведении платформы:
// Небезопасно в интерактивных обработчиках: вызывает полный пересчет геометрии формы
Элементы.ИмяЭлемента.Видимость = Ложь;
// Безопасно: элемент остается на своем месте, меняется лишь его визуальное состояние блокировки
Элементы.ИмяЭлемента.Доступность = Ложь;
Поскольку при изменении доступности координаты элементов формы не меняются, платформа не выполняет сложных расчетов позиционирования и не обращается к графической подсистеме операционной системы для перерисовки дерева контейнеров. Это гарантирует стабильность работы даже на устаревших релизах платформы 1С.
---
Проанализируем архитектуру событий поля ввода. Событие ОкончаниеВводаТекста предназначено исключительно для предобработки строки, которую пользователь набрал на клавиатуре перед тем, как система попытается найти этот объект в базе данных. Выполнять в нем тяжелую бизнес-логику (создавать элементы справочников, открывать новые формы, менять интерфейс) — это архитектурная ошибка.
Если вам нужно отреагировать на то, что пользователь выбрал или ввел определенное значение, гораздо правильнее использовать событие ПриИзменении. В этот момент ввод данных уже завершен, значение записано в реквизит формы, а фокус ввода может быть безопасно передан другому элементу.
Рассмотрим пример правильного распределения логики:
&НаКлиенте
Процедура КлиентПриИзменении(Элемент)
// Значение реквизита "Клиент" уже заполнено и проверено системой
Если Не ЗначениеЗаполнено(Клиент) Тогда
Возврат;
КонецЕсли;
// Здесь мы можем безопасно вызывать любые процедуры переключения видимости
ПереключитьИнтерфейсПодКлиента();
КонецПроцедуры
Аварийное завершение работы платформы при изменении видимости — это следствие конфликта между активным состоянием фокуса ввода элемента и мгновенным требованием платформы перерисовать интерфейс. Чтобы избежать подобных ошибок в своей практике, придерживайтесь простых правил:
ОкончаниеВводаТекста, АвтоПодбор или Очистка.ПодключитьОбработчикОжидания.ГруппаСтраницы).Доступность, если это не портит эргономику формы.Применение этих подходов сделает ваши управляемые формы надежными, быстрыми и застрахует пользователей от внезапной потери несохраненных данных из-за вылетов программы.