Click here to Skip to main content
13,048,805 members (78,636 online)
Click here to Skip to main content
Add your own
alternative version

Stats

5.4K views
661 downloads
8 bookmarked
Posted 22 Feb 2017

Touchscreen GDI+ Drawn Keyboard

, 10 Jul 2017
Rate this:
Please Sign up or sign in to vote.
A custom drawn keyboard usercontrol and TextBox with keyboard popup

Introduction

Recently, I’ve had a need for a professional looking virtual keyboard control for my WinForm project. I’ve wanted a control that suits the interface of my application. Online, I found plenty of keyboards, but many of them had considerable shortcomings, such as customization absence, inconvenient settings, slow speed, impossibility to send pressed key to an inactive window. So I decided to make own on-screen keyboard which has many options to operate with customization. Along the way, I’ve implemented not only a keyboard, but also a TextBox with keyboard's picker.

Background

The VirtualKeyBoard derives from the System.Windows.Forms.Control and in order to extend its implementation, you must to be familiar with GDI+ programming. The control works as emulator and lets input text into the currently focused entry field. It allows users to change keyboard design, to manipulate with such properties as colors for different elements, font for buttons, and also you can hide/unhide different buttons areas.

The TextBoxWithKeyboard is the extended standard System.Windows.Forms.TextBox control. The component shows implemented virtual keyboard under TextBox and hides it when the user clicks somewhere away from the keyboard.

Using the Code

To use the VirtualKeyboard as given here, you simply need to download the given VirtualKeyboard.DLL and include it into your Visual Studio Project. If you want to use TextBoxWithKeyboard control, you must to attach VirtualKeyboard.dll and TextBoxKeyboard.dll into your application or you can attach TextBoxKeyboard.csproj project to your solution. Also it will be required to change visual design of popup keyboard, you'll have to modify it manually, the keyboard can be found on the OnScreenKeyboardForm.cs form.

By default, virtual keyboard sends messages to the currently active window, but when you click with the finger (on the touchscreen) or the mouse another application (Notepad, Word etc.), becomes active window within clicked application. If you want prevent it, you can investigate "OSK.exe" window style through "SPY++". The window has extended styles WS_EX_NOACTIVATE and WS_EX_TOPMOST. They prevent the window from becoming active and stealing input focus. These styles quite easily can be used by overriding CreataParams.

const uint WS_EX_NOACTIVATE = 0x08000000;
const uint WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
    get
    {
        CreateParams baseParams = base.CreateParams;
        baseParams.ExStyle |= (int)(WS_EX_NOACTIVATE | WS_EX_TOPMOST);
        return baseParams;
    }
}

Such trick doesn't allow to enter field values on another Windows form which was created within your .NET Visual Studio application. There are many ways to transfer data from one Windows form to another. I found two different methods to achieve this.

The first way is the thread pool. Just call Application.Run from another thread and display the specified form with keyboard.

... 
ThreadPool.QueueUserWorkItem(KeyboardLoop);
... 
private void KeyboardLoop(object state) 
{ 
    Application.Run(new KeyboradExampleForm() { });
}

The second way which I can suggest, to override CreataParams for form with keyboard.

const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
const int WS_CHILD = 0x40000000;
const int WS_BORDER = 0x00800000;
const int WS_DLGFRAME = 0x00400000;
const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;

const int WS_SYSMENU = 0x00080000;
const int WS_MAXIMIZEBOX = 0x00010000;
const int WS_MINIMIZEBOX = 0x00020000;
private const int WS_THICKFRAME = 0x00040000;
private const int WS_SIZEBOX = WS_THICKFRAME;

protected override CreateParams CreateParams
{
    get
    {
        CreateParams ret = base.CreateParams;
        ret.Style = WS_CAPTION |
            WS_SIZEBOX | WS_SYSMENU |
            WS_MINIMIZEBOX |
            WS_MAXIMIZEBOX | WS_CHILD;

        ret.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOPMOST);
        StartPosition = FormStartPosition.CenterScreen;
        return ret;
    }
}

Implementation

The class VirtualKeyBoard has code to create a list of keyboard buttons List<VirtualKbButton>. Each keyboard button is the VirtualKeyBoard.cs class with such properties as rectangle, text for button, name of button. The component uses these properties to paint the keyboard buttons by their coordinates. In order to populate the list of keyboard keys I used tuples with text for buttons. The tuples are separated by rows. Example of the first row:

public List<Tuple<string, string>> FirstRowShort()
 {
     return new List<Tuple<string, string>>
    {
        new Tuple<string, string>("~", "`"),
        new Tuple<string, string>("!", "1"),
        new Tuple<string, string>("@", "2"),
...
    };
 }

By default, the component uses predefined English keyboard layout. You can create custom layouts represented by XML file and apply to the keyboard. In order to get predefined layout , use the method GetDefaultLayout(), which returns XDocument. Using as basis this XML scheme, you can create and save your own layout. This approach can help use different languages.

<KeyboardLayout>
  <Row>
    <RowNumber>1</RowNumber>
    <Buttons>
      <Text>
        <UpText>~</UpText>
        <BottomText>`</BottomText>
      </Text>
      <Text>
        <UpText>!</UpText>
        <BottomText>1</BottomText>
      </Text>
...
    </Buttons>
  </Row>
...
</KeyboardLayout>

The created XML document can be applied with LayoutSettings property.

With SendKeys method, the keyboard component simulates key pressed events. By this method, each letter can be handled by whatever control currently has the focus.

SendKeys.Send("a"); //letter 'a' pressed 
SendKeys.Send("~"); //symbol '~' pressed

To send such symbols as '{', '}', '+, '^', '%', '~', '(', ')', it is necessary to use curly braces.

SendKeys.Send("{%}"); //symbol '%' pressed
SendKeys.Send("{+}"); //symbol '+' pressed

As the many of special keys (ENTER, TAB, DELETE, etc.) don't have corresponding alphabetic designations, they have special mnemonic names. Using curly braces, it is possible to emulate these keys:

SendKeys.Send("{ENTER}");
SendKeys.Send("{ESC}");

The exception is made for SHIFT, CTRL, ALT keys. They have special symbolic names: '+' for Shift , '^' for Ctrl, '%' for Alt. Such designations help make different keys combinations.

SendKeys.Send("^c"); // Ctrl + C
SendKeys.Send("^%s"); // Ctrl + Alt + S
SendKeys.Send("%{F4}"); // Alt + F4

At this moment, the VirtualKeyBoard can handle the following keys combinations:

  • Ctrl + Alt + Shift + Any Letter or Any Function Command (F1 - F12)
  • Ctrl + Alt + Any Letter or Any Function Command
  • Ctrl + Any Letter or Any Function Command
  • Alt + Any Letter or Any Function Command

The TextBoxWithKeyboard class uses IMessageFilter interface for mouse clicks. It checks to see whether a mouse event is raised outside the popup form, which contains VirtualKeyBoard control.

private static KeyboardFilter kf = null;

...

if (kf == null)
{
    kf = new KeyboardFilter();
    Application.AddMessageFilter(kf);
}

...

private class KeyboardFilter : IMessageFilter
{
    public delegate void LeftButtonDown();
    public event LeftButtonDown MouseDown;

    public delegate void KeyPressUp(IntPtr target);
    public event KeyPressUp KeyUp;
    private const int WM_KEYUP = 0x101;
    private const int WM_LBUTTONDOWN = 0x201;

    bool IMessageFilter.PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            // raises KeyUp() event 
            case WM_KEYUP:
                if (KeyUp != null)
                {
                    KeyUp(m.HWnd);
                }
                break;

            // raises MouseDown() event
            case WM_LBUTTONDOWN:
                if (MouseDown != null)
                {
                    MouseDown();
                }
                break;
        }
        return false;
    }
}

TextBoxWithKeyboard can display either numeric or standard keypad, depending on control's properties.

History

  • 22nd February, 2017: Initial post
  • 10th June, 2017: Added functions to apply custom XML keyboard layout
  • 11th June, 2017: Fixed bug, the keyboard had been sending null value during literal key pressed

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Yury Yuhno
Software Developer Axon Cable
Latvia Latvia
No Biography provided

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170713.1 | Last Updated 10 Jul 2017
Article Copyright 2017 by Yury Yuhno
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid