При разработке нестандартных интерфейсов или интеграции 1С с внешним оборудованием (например, микроконтроллерами, станками, портами передачи данных) возникает задача непрерывной отправки сигналов всё то время, пока пользователь удерживает кнопку мыши нажатой. Как только кнопка отпускается, процесс должен сразу остановиться.
Проанализируем поведение стандартных элементов платформы. У обычной кнопки в управляемых формах 1С доступно только событие Нажатие. Это событие генерируется в момент завершения клика — то есть когда клавиша мыши уже отпущена над элементом. Событий прямого зажатия и отпускания (аналогов MouseDown и MouseUp) для стандартных кнопок в платформе не предусмотрено. Разберем рабочие варианты реализации данной функциональности.
Наиболее гибкий и наглядный способ реализовать удержание кнопки — разместить на форме элемент типа ПолеHTMLДокумента. Внутри встроенного веб-документа мы можем отрисовать кнопку и назначить стандартные обработчики событий мыши onmousedown и onmouseup.
Разберем по шагам, как настроить такую схему:
Строка (например, ТекстHTML) и вынесем его на форму как ПолеHTMLДокумента.ПриСозданииНаСервере или ПриОткрытии сформируем HTML-разметку с кнопкой и скриптом JavaScript.setInterval, а при отпускании очищать через clearInterval. Оповещение платформы 1С о событиях можно выполнять через смену URL или вызов встроенных объектов взаимодействия.Посмотрим на пример формирования HTML-разметки:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ТекстHTML =
"<!DOCTYPE html>" +
"<html>" +
"<head>" +
"<meta charset='utf-8'/>" +
"<style>" +
" .hold-btn {" +
" padding: 10px 20px;" +
" font-size: 14px;" +
" cursor: pointer;" +
" user-select: none;" +
" }" +
"</style>" +
"</head>" +
"<body>" +
" <button class='hold-btn' id='controlBtn'>Удерживайте для передачи</button>" +
" <script>" +
" var timerId = null;" +
" var btn = document.getElementById('controlBtn');" +
" " +
" function startAction() {" +
" if (timerId !== null) return;" +
" document.location.href = 'v8action://start';" +
" timerId = setInterval(function() {" +
" document.location.href = 'v8action://tick';" +
" }, 1000);" +
" }" +
" " +
" function stopAction() {" +
" if (timerId !== null) {" +
" clearInterval(timerId);" +
" timerId = null;" +
" document.location.href = 'v8action://stop';" +
" }" +
" }" +
" " +
" btn.addEventListener('mousedown', startAction);" +
" btn.addEventListener('mouseup', stopAction);" +
" btn.addEventListener('mouseleave', stopAction);" +
" </script>" +
"</body>" +
"</html>";
КонецПроцедуры
Обратите внимание на обработку граничных ситуаций: если пользователь зажмет кнопку, а затем уведет курсор мыши за пределы элемента и там отпустит клавишу, событие mouseup на самой кнопке не сработает. Чтобы исключить зависание процесса, мы обязательно подключаем обработчик mouseleave (или используем механизм setPointerCapture при событиях указателя pointerdown/pointerup).
Теперь рассмотрим, как перехватить команды в модуле управляемой формы 1С с помощью события ПриНажатии поля HTML-документа:
&НаКлиенте
Процедура ПолеHTMLПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
АдресПерехода = ДанныеСобытия.Href;
Если СтрНайти(АдресПерехода, "v8action://start") > 0 Тогда
СтандартнаяОбработка = Ложь;
// Действие при первом зажатии кнопки
ИначеЕсли СтрНайти(АдресПерехода, "v8action://tick") > 0 Тогда
СтандартнаяОбработка = Ложь;
ВыполнитьИтерациюПередачиДанных();
ИначеЕсли СтрНайти(АдресПерехода, "v8action://stop") > 0 Тогда
СтандартнаяОбработка = Ложь;
// Завершение передачи данных
КонецЕсли;
КонецПроцедуры
Если задача позволяет изменить пользовательский сценарий и не требовать непрерывного физического удержания клавиши мыши, наиболее простым и надежным решением внутри 1С будет кнопка с фиксацией состояния (триггер / переключатель).
Разберем логику работы такого интерфейса:
РежимПередачиАктивен с типом Булево.Истина, а кнопка визуально помечается как нажатая (свойство Пометка кнопки). Подключается периодический вызов процедуры через метод ПодключитьОбработчикОжидания с интервалом от 1 секунды.Ложь, снимает пометку с кнопки и останавливает таймер с помощью метода ОтключитьОбработчикОжидания.Посмотрим на пример реализации кода:
&НаКлиенте
Процедура КомандаПереключения(Команда)
РежимПередачиАктивен = Не РежимПередачиАктивен;
Элементы.КомандаПереключения.Пометка = РежимПередачиАктивен;
Если РежимПередачиАктивен Тогда
// Запускаем периодическую запись данных раз в 1 секунду
ПодключитьОбработчикОжидания("ПериодическаяЗаписьДанных", 1);
ПериодическаяЗаписьДанных();
Иначе
ОтключитьОбработчикОжидания("ПериодическаяЗаписьДанных");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПериодическаяЗаписьДанных() Экспорт
// Передача пакета данных или запись в файл
ЗаписатьДанныеВЦелевойИсточник();
КонецПроцедуры
Такой подход полностью построен на штатных механизмах платформы без внедрения HTML-кода. Однако следует учитывать, что метод ПодключитьОбработчикОжидания работает в основном потоке интерфейса и имеет минимальную дискретность в 1 секунду.
Если требуется управлять внешними контроллерами или оборудованием с высокой точностью (миллисекунды) без задержек пользовательского интерфейса, оптимальным архитектурным решением является разработка внешней компоненты (Native API).
Компонента позволяет вынести циклический обмен с портом или запись в файл в отдельный системный поток. В этом случае 1С только подает высокоуровневые команды на старт и остановку потока, а сам интерфейс платформы не блокируется и не зависит от задержек файловой системы или COM-порта.