Как ограничить нажатие кнопки в 1С и защитить систему от двойного срабатывания?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

Разберем классическую ситуацию, с которой регулярно сталкиваются разработчики и администраторы систем на платформе 1С:Предприятие 8 (особенно в конфигурациях на обычных формах, таких как «Управление торговлей 10.3»). Пользователь открывает обработку, заполняет параметры, нажимает кнопку «Выполнить» и... ничего визуально не происходит в первую секунду. Пользователь думает, что программа «зависла» или клик не сработал, и начинает судорожно нажимать на кнопку еще несколько раз.

В результате тяжелая транзакция или ресурсоемкий алгоритм запускается параллельно или последовательно несколько раз. Это приводит к блокировкам базы данных, дублированию проводок, созданию лишних документов и критическим ошибкам в учете. В этой статье мы подробно рассмотрим, как решить эту проблему профессионально, предотвратить повторные клики и сделать интерфейс отзывчивым и безопасным.

Способ 1. Простая блокировка доступности кнопки

Самый очевидный способ избежать повторного нажатия — сделать кнопку недоступной сразу после того, как пользователь кликнул по ней в первый раз. Для этого в коде обработчика нажатия кнопки мы обращаемся к ее свойству Доступность и устанавливаем его в значение Ложь.

Рассмотрим пример реализации этого подхода для обычных форм:


Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    // Делаем кнопку недоступной для клика
    ЭлементыФормы.КнопкаВыполнить.Доступность = Ложь;
    
    // Выполняем полезную работу
    ВыполнитьОбработкуДанных();
    
КонецПроцедуры

Несмотря на простоту, у этого метода есть существенный недостаток. Если процедура ВыполнитьОбработкуДанных() выполняется очень быстро, пользователь не успеет нажать кнопку дважды. Но если обработка «тяжелая» и занимает более 0.5–1 секунды, операционная система может зафиксировать повторные клики пользователя еще до того, как платформа 1С успеет перерисовать интерфейс и визуально заблокировать элемент. Эти «клики» попадут в очередь сообщений операционной системы и последовательно выполнятся 1С, что приведет к повторному запуску процедуры.

Способ 2. Защита от «дребезга» кликов с помощью флага-переменной

Чтобы полностью исключить двойное срабатывание при задержках отрисовки интерфейса, нам необходимо использовать программный флаг состояния на уровне модуля формы. Этот подход гарантирует, что даже если очередь сообщений Windows пришлет платформе три клика подряд, выполнен будет только первый из них.

Разберем по шагам, как написать такой защищенный обработчик:

  1. Объявим переменную модуля формы, которая будет хранить состояние процесса выполнения.
  2. В начале обработчика нажатия кнопки проверим эту переменную. Если она уже установлена в значение Истина, мы мгновенно прерываем выполнение алгоритма с помощью оператора Возврат.
  3. Если флаг равен Ложь, сразу переводим его в Истина, блокируем кнопку и принудительно обновляем форму с помощью метода ЭтаФорма.Обновить(), чтобы интерфейс мгновенно среагировал на изменения.
  4. Обязательно оборачиваем выполнение основного кода в конструкцию Попытка...Исключение, чтобы в случае непредвиденного сбоя или ошибки вернуть кнопке доступность.

Посмотрим на практический пример реализации:


Перем ПроцессВыполненияАктивен; // Переменная модуля формы

Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    // Шаг 1. Проверяем, не запущен ли уже процесс выполнения
    Если ПроцессВыполненияАктивен = Истина Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Блокируем повторный вход в процедуру
    ПроцессВыполненияАктивен = Истина;
    ЭлементыФормы.КнопкаВыполнить.Доступность = Ложь;
    
    // Шаг 3. Принудительно обновляем отображение формы, чтобы кнопка сразу стала серой
    ЭтаФорма.Обновить();
    
    // Шаг 4. Безопасное выполнение алгоритма
    Попытка
        
        ВыполнитьОбработкуДанных(); // Здесь выполняется основной тяжелый код
        
        // Если после выполнения операции форму закрывать не нужно,
        // и мы хотим разрешить повторный запуск (например, после изменения параметров)
        ПроцессВыполненияАктивен = Ложь;
        ЭлементыФормы.КнопкаВыполнить.Доступность = Истина;
        
    Исключение
        
        // Шаг 5. Обработка ошибок: возвращаем все в исходное состояние
        ПроцессВыполненияАктивен = Ложь;
        ЭлементыФормы.КнопкаВыполнить.Доступность = Истина;
        
        // Выводим сообщение об ошибке пользователю
        ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
        
    КонецПопытки;
    
КонецПроцедуры

// Инициализируем флаг при открытии модуля
ПроцессВыполненияАктивен = Ложь;

Способ 3. Управление фокусом ввода (Текущий элемент)

При работе в обычных формах 1С существует важный нюанс: если мы отключаем доступность элемента управления (в нашем случае — кнопки), на котором в данный момент находится фокус ввода (активный элемент формы), система может повести себя непредсказуемо. Фокус ввода может хаотично прыгнуть на первое попавшееся поле или, в редких случаях на старых релизах платформы, вызвать визуальный сбой интерфейса.

Профессиональный подход требует перед отключением доступности программно перенести фокус на другой нейтральный элемент формы (например, на надпись, поле ввода параметров или табличную часть).

Проанализируем пример кода с переносом фокуса:


Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    // Переносим фокус на поле ввода параметров, чтобы избежать «зависания» фокуса на кнопке
    ЭтаФорма.ТекущийЭлемент = ЭлементыФормы.ПолеВводаОрганизация;
    
    // Теперь безопасно отключаем доступность кнопки
    ЭлементыФормы.КнопкаВыполнить.Доступность = Ложь;
    ЭтаФорма.Обновить();
    
    ВыполнитьОбработкуДанных();
    
КонецПроцедуры

Способ 4. Специфика кнопок в Командных панелях

В типовых конфигурациях 1С (таких как «Управление торговлей 10.3») кнопки выполнения действий крайне редко располагаются на форме как самостоятельные элементы. Чаще всего они размещаются внутри специального контейнера — КоманднаяПанель. Если у такой панели включено свойство Автозаполнение, прямое обращение к кнопке через имя может не сработать.

Для корректного отключения кнопки в командной панели необходимо обращаться к ней через коллекцию кнопок этой панели:


// Пример отключения кнопки "Выполнить" в командной панели "ДействияФормы"
ЭлементыФормы.ДействияФормы.Кнопки.Выполнить.Доступность = Ложь;

Если кнопка находится внутри выпадающего подменю командной панели, обращение к ней будет выглядеть несколько сложнее, поскольку нам потребуется пройти по иерархии коллекции кнопок. Всегда проверяйте структуру вашей командной панели в конфигураторе перед написанием кода обращения к ней.

Способ 5. Использование фоновых заданий для длительных операций

Если ваша кнопка запускает по-настоящему длительный процесс (формирование тяжелого отчета, проведение документов за месяц, закрытие периода), блокировать интерфейс в основном потоке — крайне плохая практика. Экран пользователя «повиснет», операционная система присвоит окну 1С статус «Не отвечает», и испуганный пользователь может просто завершить работу программы через диспетчер задач, прервав транзакцию на середине и разрушив базу данных.

Правильным решением для длительных процессов является запуск асинхронного фонового задания на сервере:

  1. При нажатии на кнопку мы запускаем фоновое задание методом ФоновыеЗадания.Выполнить().
  2. Сразу делаем кнопку недоступной, чтобы пользователь не нажал ее снова.
  3. Подключаем обработчик ожидания через метод ПодключитьОбработчикОжидания() с интервалом в 1–2 секунды.
  4. Обработчик ожидания периодически проверяет статус выполнения фонового задания. Пока задание выполняется, кнопка остается недоступной, а на форме можно крутить красивую анимацию или показывать прогресс-бар.
  5. По завершении задания (успешном или с ошибкой) мы отключаем обработчик ожидания и возвращаем кнопке доступность (или закрываем форму).

Рассмотрим пример реализации такой схемы в модуле формы:


Перем УникальныйИдентификаторЗадания;

Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    ЭлементыФормы.КнопкаВыполнить.Доступность = Ложь;
    ЭтаФорма.ТекущийЭлемент = ЭлементыФормы.ПолеВводаОрганизация;
    
    // Запускаем длительную процедуру в фоновом задании на сервере
    ПараметрыЗапуска = Новый Массив;
    ПараметрыЗапуска.Добавить(Параметр1);
    
    ФоновоеЗадание = ФоновыеЗадания.Выполнить("МойОбщийМодульСервер.ВыполнитьДлительнуюОперацию", ПараметрыЗапуска);
    УникальныйИдентификаторЗадания = ФоновоеЗадание.УникальныйИдентификатор;
    
    // Подключаем ежесекундную проверку состояния задания
    ПодключитьОбработчикОжидания("ПроверитьСтатусВыполненияЗадания", 1);
    
КонецПроцедуры

Процедура ПроверитьСтатусВыполненияЗадания() Экспорт
    
    Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(УникальныйИдентификаторЗадания);
    
    Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
        // Задание все еще выполняется на сервере, ничего не делаем, ждем следующего тика
        Возврат;
    КонецЕсли;
    
    // Если мы дошли сюда, задание завершило свою работу
    ОтключитьОбработчикОжидания("ПроверитьСтатусВыполненияЗадания");
    
    Если Задание.Состояние = СостояниеФоновогоЗадания.Выполнено Тогда
        Предупреждение("Обработка данных успешно завершена!");
    Иначе
        Предупреждение("В процессе выполнения операции произошла ошибка!");
        ЭлементыФормы.КнопкаВыполнить.Доступность = Истина;
    КонецЕсли;
    
КонецПроцедуры

Способ 6. Использование свойства кнопки «Изменяет данные»

В платформе 1С у кнопок есть крайне полезное стандартное свойство — ИзменяетДанные. На него редко обращают внимание начинающие разработчики, однако оно играет ключевую роль при проектировании интерфейсов документов.

Если у кнопки на форме установлено свойство ИзменяетДанные = Истина, платформа автоматически связывает ее доступность с режимом работы формы:

Используйте эти профессиональные практики при проектировании форм в 1С, и ваши пользователи никогда не столкнутся с ошибками двойного проведения данных или зависанием интерфейса!

← На главную