FormEx: обработчики и события клавиатуры: полезные трюкиВ статье описаны приемы работы с клавиатурой при помощи внешней компоненты FormEx.
Реализовано перемещение между реквизитами формы посредством клавиш-стрелок, циклическое переключение перечисления Булево по нажатию клавиши Пробел, быстрый ввод реквизита ссылочного типа по первым буквам наименования. | | Автор статьи: romix | Редакторы: Волшебник Последняя редакция №4 от 18.02.06 | История URL: http://kb.mista.ru/article.php?id=69 | |
Ключевые слова: FormEx, клавиатура, событие, горячая клавиша, автозавершение, перехват нажатий на клавиши, событие нажатия, активизировать поле ввода, при помощи стрелок
Если вы были в крупном магазине, то могли обратить внимание на программное обеспечение, с которым работают их сотрудники: менеджеры торгового зала и кассиры.
Скорее всего, это будет интерфейс, написанный под MS-DOS! Спортмастер, Эльдорадо… Родина низких цен. Буржуи умеют считать деньги? Или у них в "буржуинии" принято работать в текстовых интерфейсах? Но разгадка подобной страсти многих торговых предприятий к, казалось бы, антикварным операционным системам может быть и в другом.
Когда количество документов превышает определенный порог, или требуется быстрая реакция продавца или менеджера перед клиентом, который выбирает товар, или же собирается очередь покупателей, то не может быть и речи о работе с мышью – кассир или оператор при массовом вводе документов работает только с клавиатурой.
Исключения, в виде государственных организаций, собирающих толпы народа (ЖЭК, налоговая…), где операторы, разумеется, плавно работают с мышью и только с мышью, лишь подтверждают общее правило.
Попробуем и мы поработать с клавиатурой в 1С:Предприятие 7.7.
Использование клавиш-стрелок
Первое, что бросается в глаза (или бросается, но не в глаза) – клавиши-стрелки, которыми интуитивно хочется переходить между полями, работают только в пределах поля. Чтобы изменить это их предназначение, нам потребуется обрабатывать нажатия клавиш клавиатуры, для чего мы можем воспользоваться внешней компонентой FormEx (автор – Алексей Федоров, иначе известный как АльФ).
Загрузим внешнюю компоненту из глобального модуля:
///////////////////////////////////////////////////////////////////
//Предопределенная процедура
Процедура ПриНачалеРаботыСистемы()
//…
имяф=КаталогИБ()+"dll\FormEx\FormEx.dll";
Если ЗагрузитьВнешнююКомпоненту(имяф)=0 Тогда
Сообщить("Ошибка при загрузке внешней компоненты: "+имяф);
Сообщить("Возможно, нет указанного файла или недостаточно прав доступа.");
КонецЕсли;
//…
КонецПроцедуры
Теперь в модулях формы доступна предопределенная процедура ПриНажатииКнопкиКлавиатуры(), в которую поступают пользовательские нажатия на клавиши.
///////////////////////////////////////////////////////////////////////
Процедура ПриНажатииКнопкиКлавиатуры(прм_КодКлавиши, прм_Alt, прм_Shift, прм_Ctrl, прм_Символ, прм_ФСО)
//Сообщить("Код клавиши: "+прм_КодКлавиши);
КонецПроцедуры
Небольшой эксперимент показывает нам, что коды клавиш-стрелок:
40 – стрелка вниз
38 – стрелка вверх
39 – стрелка вправо
37 – стрелка влево
Казалось бы, нет ничего проще – перехватить нажатие этих (или других) клавиш и, в зависимости от текущего реквизита формы, активизировать соседние поля. Однако, не все так просто: метод Активизировать("ИмяПоля"); не всегда срабатывает в обработчике клавиатуры!
Один из способов это побороть – присвоить параметру прм_ФСО («флаг стандартной обработки») значение 0.
Для проверки я создал тестовый документ с полями ТестБулево, ТестСправочник, ТестЧисло, ТестСтрока и ТестНеограниченнаяСтрока с полями соответствующего типа
(булево – перечисление со значениями Да и Нет).
///////////////////////////////////////////////////////////////////////
//Предопределенная процедура FormEx
Процедура ПриНажатииКнопкиКлавиатуры(прм_КодКлавиши,прм_Alt,прм_Shift,прм_Ctrl,прм_Символ,прм_ФСО)
чКодКлавиши=прм_КодКлавиши;
стрА=Форма.АктивныйЭлемент();
Если чКодКлавиши=40 Тогда //Стрелка вниз
Если стрА="НомерДок" Тогда
Активизировать("ТестБулево");
ИначеЕсли стрА="ТестБулево" Тогда
Активизировать("ТестЧисло");
ИначеЕсли стрА="ТестЧисло" Тогда
Активизировать("ТестНеограниченнаяСтрока");
ИначеЕсли стрА="ТестНеограниченнаяСтрока" Тогда
Активизировать("ф_ОК");
ИначеЕсли стрА="ДатаДок" Тогда
Активизировать("ТестСправочник");
ИначеЕсли стрА="ТестСправочник" Тогда
Активизировать("ТестСтрока");
ИначеЕсли стрА="ТестСтрока" Тогда
Активизировать("ТестНеограниченнаяСтрока");
КонецЕсли;
прм_ФСО=0;
ИначеЕсли чКодКлавиши=38 Тогда //Стрелка вверх
Если стрА="ТестБулево" Тогда
Активизировать("НомерДок");
ИначеЕсли стрА="ТестЧисло" Тогда
Активизировать("ТестБулево");
ИначеЕсли стрА="ТестНеограниченнаяСтрока" Тогда
Активизировать("ТестЧисло");
ИначеЕсли стрА="ф_ОК" Тогда
Активизировать("ТестНеограниченнаяСтрока");
ИначеЕсли стрА="ТестСправочник" Тогда
Активизировать("ДатаДок");
ИначеЕсли стрА="ТестСтрока" Тогда
Активизировать("ТестСправочник");
КонецЕсли;
прм_ФСО=0;
ИначеЕсли чКодКлавиши=39 Тогда //Стрелка вправо
Если стрА="НомерДок" Тогда
Активизировать("ДатаДок");
ИначеЕсли стрА="ТестБулево" Тогда
Активизировать("ТестСправочник");
ИначеЕсли стрА="ТестЧисло" Тогда
Активизировать("ТестСтрока");
КонецЕсли;
прм_ФСО=0;
ИначеЕсли чКодКлавиши=37 Тогда //Стрелка влево
Если стрА="ДатаДок" Тогда
Активизировать("НомерДок");
ИначеЕсли стрА="ТестСправочник" Тогда
Активизировать("ТестБулево");
ИначеЕсли стрА="ТестСтрока" Тогда
Активизировать("ТестЧисло");
КонецЕсли;
прм_ФСО=0;
КонецЕсли;
КонецПроцедуры
Конечно, такие сложности ни к чему, если поля расположены линейно (например, в одну колонку): по нажатию стрелки можно просто сымитировать нажатие Tab или Shift-Tab:
wsh=СоздатьОбъект("WScript.Shell");
wsh.SendKeys("{TAB}"); //клавиша табуляции
wsh.SendKeys("+{TAB}"); //Shift-TAB
В пределах поля пользователь уже не сможет использовать курсор без мыши – но на практике это почти никогда не нужно (особенно при наличии сочетания клавиш Ctrl-Стрелки, которое работает при переходе между словами). Обеспечим в обработчике нажатия клавиши такую возможность, вставив в его начало следующий код:
Если прм_Ctrl=1 Тогда
Возврат;
КонецЕсли;
Теперь, пользователь может перемещаться в поле ввода (с точностью до слова), удерживая клавишу Ctrl.
Переключение булевых полей
Усложним нашу задачу: сделаем так, чтобы булево поле (тип – Перечисление.Булево, значения Да и Нет) можно было переключать по нажатию клавиши Пробел (циклическое переключение), а также клавиш Д и Н (в любой раскладке).
Почему не флажок и не два состояния? Пользователь может забыть установить флажок, и это необходимо контролировать. Например, на некоем предприятии есть параметр «Канистра требует покраски». Если вместо трех состояний «Да-Нет-Неопределенно» использовать галочку с двумя состояниями, то угадайте, что будет означать снятая галочка? Третье – неопределенное – состояние переключателя, таким образом, добавляет ясности в заполненный пользователем документ.
Чтобы реализовать удобный для массового ввода переключатель булева типа, впишем в наш обработчик примерно такой код:
Если стрА="ТестБулево" Тогда
Если чКодКлавиши=32 Тогда //Пробел
Если ТестБулево=Перечисление.Булево.Да Тогда
ТестБулево=Перечисление.Булево.Нет;
ИначеЕсли ТестБулево=Перечисление.Булево.Нет Тогда
ТестБулево="";
Иначе
ТестБулево=Перечисление.Булево.Да;
КонецЕсли;
ИначеЕсли чКодКлавиши=76 Тогда //Д
ТестБулево=Перечисление.Булево.Да;
ИначеЕсли чКодКлавиши=89 Тогда //Н
ТестБулево=Перечисление.Булево.Нет;
КонецЕсли;
КонецЕсли;
Я уже слышу возмущенные возгласы: этот код не работает! И действительно – он не работает. Почему он не работает в обработчике нажатия клавиши, я не знаю, но обычный способ обхода подобных «тупиковых» ситуаций, которые иногда встречаются в 1С – сгенерировать «событие», и уже в его обработчике (где блокировки уже не действуют) произвести требуемое изменение.
Есть несколько способов сгенерировать событие – от использования внешней компоненты (где генерация события – штатная и документированная возможность) и до имитации закрытия формы – Форма.Закрыть() с СтатусВозврата(0) в обработчике ПриЗакрытии().
(Возможно, трюк с закрытием принадлежит Рупору – точное его авторство я не помню).
Если это кажется вам сложным – не думайте об этом, а просто воспользуйтесь возможностью, которая появилась в последних версиях FormEx (у меня версия 2.0.1.1) – метод ВнешнееСобытие объекта Сервис.
Сервис = СоздатьОбъект("Сервис");
Сервис.ВнешнееСобытие("FormEx","НажатиеКлавиши",""+прм_КодКлавиши)
Таким образом, мы генерируем событие. Три параметра – абсолютно произвольные текстовые строки, но вообще-то они называются «Источник», «Событие» и «Данные».
В обработчике события формы (предопределенной процедуре) поймаем это событие.
///////////////////////////////////////////////////////////////////////
//Предопределенная процедура
Процедура ОбработкаВнешнегоСобытия(прм_Источник, прм_Событие, прм_Данные)
//…
КонецПроцедуры
Почему этого обработчика нет в синтаксис-помощнике? Он актуален только для внешних компонент, и документирован в источнике под названием «Технология создания внешних компонент», который продается фирмой 1С за отдельную плату (в размере 500 рублей).
Вот как этот обработчик может выглядеть на практике:
///////////////////////////////////////////////////////////////////////
//Предопределенная процедура
Процедура ОбработкаВнешнегоСобытия(прм_Источник, прм_Событие, прм_Данные)
стрА=Форма.АктивныйЭлемент();
Если прм_Событие="НажатиеКлавиши" Тогда
чКодКлавиши=0+прм_Данные;
Если стрА="ТестБулево" Тогда
Если чКодКлавиши=32 Тогда //Пробел
Если ТестБулево=Перечисление.Булево.Да Тогда
ТестБулево=Перечисление.Булево.Нет;
ИначеЕсли ТестБулево=Перечисление.Булево.Нет Тогда
ТестБулево="";
Иначе
ТестБулево=Перечисление.Булево.Да;
КонецЕсли;
ИначеЕсли чКодКлавиши=76 Тогда //Д
ТестБулево=Перечисление.Булево.Да;
ИначеЕсли чКодКлавиши=89 Тогда //Н
ТестБулево=Перечисление.Булево.Нет;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры // ОбработкаВнешнегоСобытия
Если активно поле с именем ТестБулево, то нажатие пробела производит циклическое переключение Да-Нет-Пусто, нажатие Д активизирует значение Да, а нажатие Н – значение Нет.
Вот как все удобно – пользователю не надо хвататься за мышь (подобно тому, как д-р Геббельс при слове «культура» хватался за пистолет). Конечно, вы вряд ли будете переделывать типовую конфигурацию, чтобы бухгалтеру было удобнее работать (потому что при следующем обновлении изменения исчезнут). Однако, в нетиповых конфигурациях, если пользователь вводит, скажем, по 250 одинаковых (например, производственных) документов в день, то даже небольшое улучшение интерфейса существенно облегчит его работу. Как показывает практика, на многие годы вперед.
Ввод элемента справочника по первым буквам наименования
Для реквизита ТестСправочник я назначил тип: Справочник.Номенклатура. Активизировав этот реквизит, интуитивно хочется начать вводить название товара.
Поэтому, в наш обработчик нажатия клавиши мы добавим генерацию события (без события трюк почему-то не работает):
Если стрА="ТестСправочник" Тогда
Если (ПустоеЗначение(прм_Символ)=0) Тогда
Сервис.ВнешнееСобытие("FormEx","ВводСимвола",прм_Символ)
КонецЕсли;
КонецЕсли;
А в обработчик события – ветвь условия:
ИначеЕсли прм_Событие="ВводСимвола" Тогда
Если стрА="ТестСправочник" Тогда
Если ПустаяСтрока(прм_Данные)=0 Тогда
wsh=СоздатьОбъект("WScript.Shell");
wsh.SendKeys("{F4}");//Имитируем нажатие F4
wsh.SendKeys("{TAB}");//Имитируем нажатие TAB
wsh.SendKeys(прм_Данные);//Имитируем нажатие буквы
КонецЕсли;
КонецЕсли;
Мы назвали событие «ВводСимвола», чтобы отличить его от предыдущего примера, и передать значение клавиши вместо ее кода.
Обработчик события имитирует (средствами объекта WScript.Shell ) нажатия клавиши F4, Tab (чтобы сразу перескочить на поле Наименование справочника) и нажатие введенной буквы. Пользователю сразу откроется справочник с активизированной первой буквой наименования товара (не надо вручную жать F4). Предположим, что я набрал букву К. На эту букву у меня есть несколько товаров. Предположим, мне надо выбрать товар Кофе «Арабика». Поэтому я продолжаю набирать первые буквы названия товара – «коф». После выбора нужной позиции я нажимаю Enter. Таким образом, уставшая мышь может немного отдохнуть, а пользователь – быстрее и удобнее ввести нужные сведения в документ.
Этот режим ввода реализован в 1С:Предприятие 8.0, поэтому данный трюк можно рассматривать как дешевое «подручное средство», если переход на 8 версию платформы по какой-то причине (например, по причине необходимости переписывать все программные модули «с нуля») представляется невозможным или маловероятным.
Автозавершение в выпадающих списках значений
Предположим, что на форме есть выпадающий список, который необходимо заполнять по первым буквам.
тт.Открыть(КаталогИБ()+"Улицы.txt");
Для й= 1 По тт.КоличествоСтрок() Цикл
стр=тт.ПолучитьСтроку(й);
ТестСписок.ДобавитьЗначение(стр);
КонецЦикла;
Чтобы включить этот режим, я использую метод ПерехватитьСписокЗначений(), доступный в FormEx.
АтрФормы = СоздатьОбъект("АтрибутФормы");
АтрФормы.УстановитьАтрибут(Форма,"ТестСписок");
АтрФормы.ПерехватитьСписокЗначений();
Этот метод работает только после окончательного открытия формы, поэтому я выполняю это действие в обработчике события.
Код проверен в 1С:Предприятие 7.7, 25 релиз.
Внешняя компонента FormEx – версии 2.0.1.1.
Тестовая конфигурация для скачивания – http://x-romix.narod.ru/Fakir3.rar
(217 КБ, скачивать левой кнопкой мыши).
В конфигурации откойте пункт меню Журналы – Тест клавиатуры.
Конфигурация уже содержит внешнюю компоненту FormEx. Первый запуск конфигурации необходимо произвести под правами администратора или привилегированного пользователя, чтобы внешняя компонента вписала себя в реестр системы Windows.
|