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