Книга знаний

1С:Предприятие / Приемы программирования / Внешние компоненты

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.

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика