В процессе разработки интерфейсов на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с задачей оптимизации юзабилити. Одно из частых требований пользователей — минимизация работы с мышью и обеспечение возможности быстрого перемещения по форме с помощью клавиатуры. Представьте стандартный сценарий: пользователь нажимает кнопку на форме документа (например, «Заполнить» или «Рассчитать»), после чего ему необходимо сразу же продолжить работу со строками таблицы значений (ТЗ), перемещаясь по ним с помощью стрелок на клавиатуре. Для этого фокус ввода (курсор) должен мгновенно и автоматически переместиться с нажатой кнопки на саму таблицу значений.
Однако при попытке реализовать это стандартным вызовом метода Активизировать() разработчики обнаруживают, что фокус упрямо остается на кнопке. В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем внутренние механизмы платформы 1С 7.7 и рассмотрим несколько эффективных способов решения — от классических «обходных путей» до изящных современных решений.
Давайте разберем физику процесса обработки событий в системе 1С 7.7. Когда пользователь нажимает на кнопку формы, управление передается связанной с ней процедуре модуля. В этот момент элемент диалога «Кнопка» захватывает фокус ввода. Платформа построена таким образом, что пока выполняется код процедуры кнопки, фокус жестко заблокирован за ней.
Даже если внутри процедуры мы напишем:
Активизировать("ТаблицаТоваров");
платформа добросовестно попытается выполнить эту команду в процессе выполнения кода, но в момент завершения процедуры кнопки (когда управление возвращается операционной системе) встроенный интерфейсный поток 1С 7.7 принудительно возвращает фокус на элемент, который инициировал событие — то есть на нашу кнопку. В итоге визуально фокус так и остается на кнопке, а строка таблицы значений может лишь кратковременно «моргнуть» или выделиться цветом, но управлять ею с клавиатуры по-прежнему невозможно.
Кроме того, метод Активизировать() имеет свои особенности документирования. Он требует точного указания идентификатора элемента диалога. Но для таблиц значений передача самого идентификатора ТЗ не всегда приводит к активации ввода в ячейках. Давайте рассмотрим рабочие альтернативы, которые позволяют обойти это системное ограничение.
---
Это классический, проверенный годами «хак» разработчиков 1С 7.7, ставший стандартом де-факто для обхода ограничений платформы. Идея заключается в том, чтобы заставить форму инициировать процесс закрытия. При вызове метода закрытия платформе приходится полностью перестраивать стек фокусов ввода и освобождать кнопку. В предопределенной процедуре ПриЗакрытии() мы перехватываем это событие, отменяем само закрытие формы с помощью функции СтатусВозврата(0), но перед этим передаем фокус на нужный элемент. В этот момент кнопка уже «отпустила» фокус, и активация таблицы значений проходит успешно.
Давайте по шагам разберем программную реализацию этого метода. Нам понадобится глобальная переменная модуля формы для управления состоянием закрытия.
Шаг 1. Объявление переменной и инициализация в модуле формы:
Перем КонтрольЗакрытия; // Переменная-флаг для отслеживания фиктивного закрытия
Шаг 2. Описание процедуры инициализации при открытии формы:
Процедура ПриОткрытии()
// Устанавливаем начальное значение флага
КонтрольЗакрытия = 0;
КонецПроцедуры
Шаг 3. Код процедуры кнопки, которая должна передать фокус на ТЗ:
Процедура ВыполнитьОбработкуИПерейти()
// ... Здесь выполняется ваш основной рабочий код кнопки ...
// Например, заполнение или расчет Таблицы Значений
// Включаем флаг фиктивного закрытия
КонтрольЗакрытия = 1;
// Инициируем закрытие формы программно
Форма.Закрыть(0);
КонецПроцедуры
Шаг 4. Перехват события в предопределенной процедуре ПриЗакрытии():
Процедура ПриЗакрытии()
Если КонтрольЗакрытия = 1 Тогда
// Сбрасываем флаг, чтобы при повторном реальном закрытии не зациклиться
КонтрольЗакрытия = 0;
// Активизируем нужную таблицу значений по ее идентификатору на форме
Активизировать("ТаблицаТоваров");
// Отменяем закрытие формы! Форма остается открытой, но фокус уже перенесен
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецПроцедуры
Этот способ работает со 100% гарантией в любой конфигурации без использования внешних компонент. Однако он имеет один эстетический недостаток: код модуля размазывается по разным процедурам, и появляется необходимость использовать глобальную переменную формы.
---
Очень часто метод Активизировать("ИмяТЗ") отрабатывает некорректно, потому что платформа ожидает указания конкретного поля ввода или колонки интерактивного элемента. Оказывается, 1С 7.7 гораздо лучше реагирует на команду активации, если передавать в нее идентификатор конкретной колонки таблицы значений.
Давайте проанализируем следующий пример кода. Предположим, у нашей таблицы значений ТаблицаТоваров есть колонка с идентификатором Товар. Вместо активации всей таблицы целиком, мы можем точечно направить фокус на эту колонку:
Процедура ПриНажатииКнопки()
// Активируем конкретную колонку таблицы значений
Активизировать("Товар", 0);
КонецПроцедуры
Второй параметр 0 в методе Активизировать() крайне важен. Он указывает системе, нужно ли сразу входить в режим редактирования ячейки. Значение 0 просто устанавливает курсор на ячейку (подсвечивает строку), что позволяет пользователю сразу начать перемещение стрелками «Вверх»/«Вниз» по строкам ТЗ.
Если требуется вернуть фокус именно на ту колонку, которая была активна ранее (до того как пользователь кликнул на кнопку), мы можем динамически считать ее имя с помощью свойства формы:
Процедура ПриНажатииКнопки()
ТекущаяКолонкаТЗ = Форма.ТекущаяКолонка();
Если ПустаяСтрока(ТекущаяКолонкаТЗ) = 0 Тогда
Активизировать(ТекущаяКолонкаТЗ, 0);
КонецЕсли;
КонецПроцедуры
---
Если мы хотим избежать сложной логики с фиктивным закрытием формы в процедуре ПриЗакрытии(), мы можем прибегнуть к красивому штатному механизму асинхронного вызова процедур — локальному таймеру формы.
Идея метода: процедура кнопки завершает свою работу, полностью освобождая фокус ввода. За мгновение до завершения она регистрирует однократный вызов процедуры активации через таймер. Спустя заданный интервал времени (минимальный шаг встроенного таймера 1С 7.7 составляет 1 секунду) система запускает процедуру фокуса, когда кнопка уже гарантированно не держит фокус.
Рассмотрим пошаговую реализацию этого элегантного подхода:
Шаг 1. Код процедуры при нажатии кнопки:
Процедура ПриНажатииКнопки()
// Выполняем полезную работу
// ...
// Запускаем локальный таймер формы.
// Процедура "УстановитьФокусНаТЗ" выполнится автоматически через 1 секунду
Форма.ОбработкаОжидания("УстановитьФокусНаТЗ", 1);
КонецПроцедуры
Шаг 2. Создание вызываемой процедуры в модуле формы:
Процедура УстановитьФокусНаТЗ()
// Важно! Первым делом отключаем таймер ожидания, чтобы процедура не вызывалась циклически
Форма.ОбработкаОжидания("УстановитьФокусНаТЗ", 0);
// Теперь фокус свободен, активизируем колонку таблицы значений
Активизировать("Товар", 0);
КонецПроцедуры
Единственным минусом этого штатного подхода является визуальная задержка в 1 секунду (меньший интервал стандартная ОбработкаОжидания задать не позволяет). Если для бизнес-процесса такая задержка критична, то лучше использовать другие методы.
---
Еще один оригинальный и быстрый способ передачи фокуса — программная имитация нажатия клавиши Tab на клавиатуре. Данный способ идеально подходит, если на форме вашей задачи таблица значений расположена в порядке обхода (настройки последовательности переходов по клавише Tab) сразу за нажимаемой кнопкой.
Для реализации этого метода мы обратимся к возможностям операционной системы Windows через ActiveX-объект WScript.Shell. Посмотрите на пример реализации:
Процедура ПриНажатииКнопки()
// Выполняем действия по кнопке
// ...
// Создаем объект для работы со сценариями Windows
Шелл = СоздатьОбъект("WScript.Shell");
// Посылаем сигнал нажатия клавиши TAB в активное окно приложения
Шелл.SendKeys("{TAB}");
КонецПроцедуры
В момент выполнения строки SendKeys операционная система генерирует виртуальное нажатие клавиши Tab, фокус ввода автоматически перепрыгивает на следующий элемент формы (которым у нас настроена таблица значений). Переход происходит мгновенно, без задержек и без использования сложных схем перехвата закрытия формы.
---
Если в вашей конфигурации допускается использование внешних компонент расширения интерфейса (что является стандартом для большинства зрелых систем на платформе 1С 7.7), то наилучшим решением будет использование библиотеки FormEx.
Компонента FormEx предоставляет разработчику объект РасширениеФормы, который умеет работать с миллисекундными интервалами ожидания. С его помощью мы можем реализовать асинхронный перенос фокуса без видимой секундной задержки:
Процедура ПриНажатииКнопки()
// Создаем расширение формы средствами FormEx
РФормы = СоздатьОбъект("РасширениеФормы");
// Запускаем сверхбыструю обработку ожидания (например, на 50 миллисекунд)
РФормы.ОбработкаОжидания("УстановитьФокусНаТЗ", 50);
КонецПроцедуры
Пользователь даже не заметит задержки в 50 миллисекунд: фокус перепрыгнет на таблицу значений визуально в то же мгновение, когда кнопка вернется в исходное (не нажатое) состояние.
---
Давайте сопоставим все рассмотренные нами методы, чтобы вы могли выбрать наиболее подходящий под архитектуру вашего решения:
Мы рекомендуем начать решение задачи со Способа 2 (активация конкретной колонки). Если поведение формы под воздействием других обработчиков сбрасывает фокус обратно — смело внедряйте проверенный временем Способ 1 (фиктивное закрытие формы), который гарантированно решит проблему в любой конфигурации 1С 7.7.