Один из способов адаптации настольных приложений под сенсорный экран

    Поработав некоторое время с Windows 8 на планшете и ультрабуке с сенсорным экраном, я столкнулся с одной интересной особенностью. При работе с полями ввода в настольных (desktop) приложениях экранная клавиатура не появляется автоматически, приходится каждый раз вызывать путем касания значка в системном лотке, а потом закрывать нажатием на кнопку закрытия окна.
    В Windows UI приложениях такой проблемы нет, клавиатура автоматически появляется, как только поле ввода получает фокус и исчезает, когда в ней отпадает необходимость — например, элемент управления потерял фокус ввода, либо пользователь начал вводить текст с «железной» клавиатуры.
    Так как я не только пользователь, но и разработчик, то подумал: а что если перенести это поведение экранной клавиатуры и в свои настольные приложения? Задача поставлена, ищем решение.
    Поздравляю хабрачитателей с приближающимися праздниками! Всем, дочитавшим до конца — готовый к использованию код в подарок! :)

    Задача номер 1 — отобразить клавиатуру.


    Tablet PC Input Panel — это приложение, входящее в состав стандартной поставки Windows 8, которое, собственно и отображает экранную клавиатуру. Запустим его и посмотрим, что получится. После запуска приложения видим, что на экране появилась клавиатура. Следовательно для отображения клавиатуры реализуем программный запуск.

    TCHAR kbdPath[MAX_PATH] = {0};
    
    ExpandEnvironmentStrings(_T("%CommonProgramW6432%"), kbdPath, _countof(kbdPath));
    wcscat_s(kbdPath, _countof(kbdPath), _T("\\microsoft shared\\ink\\tabtip.exe"));
    
    ShellExecute(NULL, _T("open"), kbdPath, NULL, NULL, SW_SHOWNORMAL);
    


    Если выполнить этот код на экране появится экранная клавиатура, то чего и добивались.

    Задача номер 2 — скрыть клавиатуру


    После того как пользователь нажал Enter или поле ввода потеряло фокус неплохо было бы скрыть клавиатуру, чтобы ему не пришлось делать это самостоятельно. Сделать это еще проще, так как клавиатура отображается в окне то реализовать это можно путем программного закрытия или сворачивания окна. Но для начала его необходимо как-то найти.

    Я запустил клавиатуру и проверил свойства окна с помощью утилиты Spy++, входящей в состав Visual Studio и нашел то, что меня интересовало, а именно класс окна IPTip_Main_Window. Остальное дело техники, используя имя класса находим окно и сворачиваем его, путем отправки соответствующего сообщения.

    HWND kbd = ::FindWindow(_T("IPTip_Main_Window"), NULL);
    
    if(kbd != NULL)
    {
    	::PostMessage(kbd, WM_SYSCOMMAND, SC_CLOSE, 0);
    }
    


    Задача номер 3 — развернуть во всю ширину экрана, свернуть клавиатуру


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

    DWORD msgSwitchTo = ::RegisterWindowMessage(_T("IPTipDockButtonPressed"));
    
    HWND kbd = ::FindWindow(_T("IPTip_Main_Window"), NULL);
    
    if(kbd != NULL)
    {
    	::PostMessage(kbd, msgSwitchTo, 0x891, 0);
    }
    

    Также не плохо было бы понимать, в каком состоянии находится клавиатура. Здесь я решил поступить просто — определить ширину экрана, далее ширину клавиатуры и сравнить два этих значения

    HWND kbd = ::FindWindow(_T("IPTip_Main_Window"), NULL);
    
    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    
    RECT touchWindow = {0};
    
    if(GetWindowRect(kbd, &touchWindow) == false)
    {
    	return false;
    }
    
    int touchWidth = touchWindow.right - touchWindow.left;
    
    return (screenWidth <= touchWidth);
    

    Для простоты использования в своих проектах я обернул все полученные функции в класс CTouchKeyboard

    class CTouchKeyboard
    {
    public:
    	static bool IsVisible();
    	static bool Show();
    	static bool Hide();
    	static bool Dock();
    	static bool IsDocked();
    
    private:
    	static HWND FindKeyboardWindow();
    	static bool PostMessageToKeyboard(DWORD msg, WPARAM wParam, LPARAM lParam);
    };
    


    Как использовать в приложениях


    При работе с текстовыми полями пользователь ожидает, что переходе в поле появится экранная клавиатура. Как это реализовать? При получении фокуса ввода текстовым полем родительское окно получает уведомление EN_SETFOCUS, соответственно добавляем обработчик и в обработчике этого уведомления вызываем

    CTouchKeyboard::Show(); 
    

    При потере фокуса ввода родительское окно получает уведомление EN_KILLFOCUS, соответственно там будет вызываться

    CTouchKeyboard::Hide();  
    

    Собираем, запускаем, наслаждаемся результатом — если выбрать текcтовое поле клавиатура появляется, нажимаем клавишу Tab — клавиатура исчезает.



    Пример использования и исходники доступны здесь.
    Intel
    151,00
    Компания
    Поделиться публикацией

    Комментарии 16

      +4
      не делайте так пожалуйста. бесит.
      я тыкаю пальцем в поле ввода, чтобы перевести на него фокус и тут всплывает это чудо на пол экрана. потом я набираю текст с железной клавы и оно пропадает.
      лучше показывать клаву только при клике в поле которое уже находится в фокусе. тогда все будут счастливы.
        +1
        «А может просто сбрить усы?»
        Я бы предложил автору добавлять такое поведение только если нет обычной клавиатуры.
          0
          Я думал над вариантом с обнаружением «железной» клавиатуры. А если это трансформер и он находится в режиме планшета?
            0
            Как уже написал выше, сделайте по клику на поле ввода, если он уже в фокусе.
        +5
        Это не адаптация, это выдача костыля.
          0
          Предложите более удобный вариант.
            –3
            Переписать гуй с нуля.
              +1
              Переписывать придется не только GUI, но и весь код, если идет речь о портировании с десктопа в Windows Store. Оно, конечно, правильнее, но в некоторых случаях не реализуемо, если речь идет не о RSS ридере, а, например, о приложении использующем API не доступный в WinRT
          0
          Если уж на то пошло, то можно использовать примерно такой подход чтобы определить видимость каретки в своем (или даже чужом) приложении и автоматически показывать или скрывать клавиатуру.
          А вообще да, лучше показывать клавиатуру не на смену фокуса, а на событие от прикосновения к экрану (проверкой на MOUSEEVENTF_FROMTOUCH флажок). Хотя такие вещи все-равно должны решаться системой, а не отдельно взятым приложением.
            0
            С тачем отличная идея, если еще предусмотреть вариант, когда фокус изменяется Tab-ом. Есть над чем подумать…
            0
            простите, а где вы в 12 студии взяли Spy++? у меня в web/c# express'ах этой тулзы нет.
            Зато я её отлично помню по nnCron, и называлась она так же.
              0
              В профессиональной редакции он есть. Видимо, в бесплатный Express не включается.
              0
              Лучше дать пользователю выбор и подсказку, нарисовать кнопку с клавиатурой рядом с полем. Нет железной клавиатуры — вызову кнопкой, есть — просто напечатаю как всегда.
                0
                Хорошее предложение, но опять же это может раздражать, особенно когда я перехожу из поля в поле Tab-ом или завершая ввод через Enter.

                После прочтения всех комментариев я понял, что стоит искать компромисс…
                  +1
                  Компромисс — в этике и праве разрешение некой конфликтной ситуации путём взаимных уступок.

                  Уступки — это невыгодны обоим сторонам, и что-то от первоначального будет потеряно.
                  Стоит искать такой метод, который приведет к консенсусу.
                    0
                    Согласие есть продукт при полном непротивлении сторон :)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое