So, how do you create a virtual, on screen keyboard ? Creating a form/usercontrol with buttons or some other control(s) on it to represent the keyboard is relatively simple. What's difficult is knowing where to send the key data when the control is clicked, because when you click on the virtual keyboard it tends to get focus, taking it away from the input field that needs the data.
Googling the problem I found several samples that simply raised an event from the on screen keyboard form/control, but that still leaves you with the issue of passing on the key data to some unknown edit field. Most of these samples assumed the keyboard control was either on the same form as the field receiving the key data, or that a reference to the control could be provided, which is fine if you only want to deal with forms within your own application, but sucks otherwise.
A few other samples worked by using API's to obtain the handle of the control that currently has focus before the keyboard form/control is activated. When the buttons on the virtual keyboard are clicked, the key data is sent to the 'remembered' window handle and then the original window is refocused using API's, or the window is refocused first and then SendKeys is used. This doesn't work very well though. If the virtual keyboard window overlaps the client window then you get 'flickering' as the focus changes between the windows. Also it seems some key data just gets 'lost' if you hit buttons on the virtual keyboard quickly, although I'm not sure why.
In the end, thanks to two http://www.codeproject.com/ articles and another Google search, as well as some trial and error on my part, I managed to find the 3 magic things you must do to make an on screen keyboard work. These are;
- Show your virtual keyboard form without 'activation', so it doesn't get focus when the keyboard is shown.
- Intercept the WM_MOUSEACTIVATE message and return MA_NOACTIVATE as the result.
- Most important of all - you must NOT use controls that can get focus to represent your buttons.
So, how exactly do you build the virtual keyboard ?
I recommend you grab a copy of FoxholeWilly's keyboard control from CodeProject at http://www.codeproject.com/cs/miscctrl/touchscreenkeyboard.asp
This gives you a nice looking, resizable, keyboard. It also supports both alphabetical and QWERTY layouts, as well as a 'kids' version. It doesn't have the Numeric Keypad like I wanted, but with a little image editing and some tweaking of the code it's easy enough to add.
Unfortunately, this control doesn't take care of points 1 and 2 from before, or actually pass on the key data for you. To do this, place the control on a host form and connect the control's
UserKeyPressed event to an event handler. In that event handler place the following line of code;
SendKeys.Send(e.KeyboardKeyPressed);
Because FoxholeWilly's control already provides a SendKeys formatted string as an event argument, that's the only line of code needed to pass on the key data.
Now, to prevent the form receiving focus or being activated when it's shown, simply override the CreateParams property on the host form, like this;
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle = createParams.ExStyle & WS_EX_NOACTIVATE;
return createParams;
}
}
Finally, you need to prevent the form getting focus or being activated when it or the keyboard control are clicked on. This is as easy as adding the following code to the host form;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATE = 0x0003;
protected override void WndProc(ref Message m)
{
//If we're being activated because the mouse clicked on us...
if (m.Msg == WM_MOUSEACTIVATE)
{
//Then refuse to be activated, but allow the click event to pass through (don't use MA_NOACTIVATEEAT)
m.Result = (IntPtr)MA_NOACTIVATE;
}
else
base.WndProc(ref m);
}
This code actually came from a comment posted by Dirk Moshage, on Randy More's CodeProject article (http://www.codeproject.com/samples/onscreenkeyboard.asp) about creating an on screen keyboard. However, because Randy's keyboard project uses button controls to represent the buttons on screen, most people found the above code didn't work properly because the child windows (buttons) still got activated, and then activated the parent anyway. In our case, neither FoxholeWilly's keyboard control or the picturebox it uses to show the keyboard can get focus, which means the WM_MOUSEACTIVATE code actually works the way it's intended.
That's all there is to it. Once you have your host form and control configured, you can run the project. Simply put keyboard focus in any control on any window, and use the mouse (or your touch screen) to click on your virtual keyboard. As you click each virtual button, the appropriate key stroke is sent to the control that currently has focus, and the focus remains where it is.
Of course there's a lot more you can do to improve your keyboard, such as adding a sound when keys are clicked on or implementing 'type rate' so keystrokes are sent repeatedly while the mouse button is held down over a particular key. For a basic keyboard though, you should have all you need.
30 comments:
I found today this post. THANKSSSSSS. You are saving my life ;)
No problem... glad it helped. I get so few readers I've been thinking of tearing the blog down, but if it actually helped someone, maybe I'll keep it.
Thanks for the comment.
Thank you very much for this information! I was about to make a numeric keypad for the company I work for and you saved me lots of trouble.
Your very welcome, I'm glad it helped !
Thank you very much Sir. It is very helpful for my POS application.
Thanks a lot...
:)
by
Pavan kumar.
Great Post! This was just the solution that I needed for a kiosk project I am working on. Thanks for taking the time to post this solution and for keeping the blog up. Thanks also for linking to FoxholeWilly's post, his keyboard gave my project the finished look I needed.
Thanks for the comment, it's always nice to know when you've helped somebody.
Thanks a lot man. You're my hero!
This seems to be my most popular page :) Glad to know it's helped someone else !
This is great help, but not sure how to do this using WPF? I have a WPF keyboard and want to make it do the same thing as your Forms keyboard. There is no 'CreateParams' in WPF... ideas??
This is a great post and has helped me understand some of the mechanic's behind creating a on screen Keyboard. The problem I am having is that the on screen keyboard that I created is made using WPF. How do you set the WS_EX_NOACTIVATE flag on an WPF window? Ideas?? Thanks in advance!
Hi Oregon Ted,
That's an interesting point. Unfortunately I haven't done a lot of work in WPF and so I don't know the answer, but I'll do some research and if I figure it out I'll let you know.
Hi Oregon Ted,
Try this link;
http://rhizohm.net/irhetoric/blog/9/default.aspx
It has information on how to show a WPF window without it 'activating'.
This is a great post and it saved me a lot of time. Here is the equivalent for use in VB.NET.
Thank you so much....
Private Const WS_EX_NOACTIVATE As Integer = &H8000000
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
'Dim SecPerm As New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)
'SecPerm.Demand()
' Extend the CreateParams property of the Button class.
Dim cp As System.Windows.Forms.CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or WS_EX_NOACTIVATE ' BS_ICON value
Return cp
End Get
End Property
Private Const WM_MOUSEACTIVATE As Integer = &H21
Private Const MA_NOACTIVATE As Integer = &H3
Dim maNoactivate As IntPtr = New IntPtr(MA_NOACTIVATE)
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If (m.Msg = WM_MOUSEACTIVATE) Then
m.Result = maNoactivate
Else
MyBase.WndProc(m)
End If
End Sub
Thanks for that Jeremey, and I'm glad the post helped you out.
Good afternoon Yort,
I need a Numerical Keypad on a touch screen exactly like you needed. I have spent a few hours fooling around with the c# source but cannot have it to work. Any chance you would have such a keyboard you would be willing to share the source ?
Hi Jean-Pierre,
At the moment the code I have is woven into the device struture for the POS system I work on (since that's the only place we needed it), and has some unnecessary code from your point of view. I could try to seperate it and post a working version, but it might be quicker to help you with your problem(s).
Where exactly are you getting stuck ?
Thanx Yort,
I hadn't notice an error in my source. I copiedfor lines from the article but there is an error in the line "createParams.ExStyle = createParams.ExStyle WS_EX_NOACTIVATE;"; an equal sign is missing. It should read "createParams.ExStyle = createParams.ExStyle = WS_EX_NOACTIVATE;"
Now my problem is that it refuse to runs on Windows XP imbedded on a thin client because it only has framework 1.1 and can't compile lower then 2.0
I need an XP machine in order to compile.
Thanx again
J.P.
Hi Jean-Pierre,
Thanks for that... I believe the missing character is actually an & (must have been stripped out by the posting tool) as the code is supposed to be setting a particular bit in the ExStyle property value. I have updated the post now.
It's a shame you're having problems with Windows XP Embedded. I believe it is possible to build your own image that contains .Net 2.0 using (Win XP Embedded) Platform builder, but I'm not very familiar with doing so and I don't know if you're building your own images or using one provided by your IHV.
Good luck.
thanx very much for your contribution article.
i am trying to do a software component wich shows up a touch screen keyboard.
i would like to iterate all keys in the currently attached(configured) keyboard, but all solutions i found on this site and on the internet use fixed layouts or a renge of fixed layouts
i would like to know wich keys-keycodes are available on the current locale keyboard.
could you adress me to some point ?
thank you
Hi Yort,
sure that,You don't know me and I also don't know you. But today your discussion about On-screen keyboards is very useful for me.
Thank you very very much!!!
Dear Yort,
My name is Hien - vietnamese
I'm coding on-screen keyboard.
My keyboard have TopMost's property is true.
when program show new modal dialog.
I can't click on my keyboard.
How to click on key button when program is showing different dialog.
Plz help me!!! Thank for your read.
Dear Yort,
My name is sugia-vietnamese
I'm coding on-screen keyboard.
My keyboard have TopMost's property is true.
When i show new modal dialog.
I can't click on my keyboard.
How to click on key of my keyboard when program is showing modal dialog.
Plz help me!!!
Thank you.
Hi Hien,
Unfortunately, what you describe makes sense. A 'modal' dialog prevents accesing any other windows in the same process, so you won't be able to use the keyboard.
The only solutions I know of are;
1. Do not use a modal dialog.
2. Put your keyboard in a seperate exe/application, so it can still be accessed while the rest of your application is showing a modal dialog.
Hi,
Unfortunately I do not know how to detect which keys are available on the current keyboard. There may be something in the Win32 API to do with locale, but possibly not.
If you can figure out which keys exist, then it should be simple enough to just draw the keys using GDI instructions (DrawRectable or FillRectangle of the Graphics class will help here), and then the mouse handling is just a matter of catching the click event and checking where the pointer was on screen when the button was pressed. When you draw your buttons, keep a track of the location and size of each one, and then when you get a click you can enumerate them all until you find one that 'contains' the point where the mouse was clicked.
Hi! After reading your post you made me wonder how the Label is set to not receive focus. I dug into Reflector and found that I could create my own Button control with the only difference being that in the constructor I call:
this.SetStyle(ControlStyles.Selectable, false);
That prevents the button from receiving focus. I was then able to create my keyboard using real buttons, which presented a much better user response than clicking an image. I hope this helps!
Hi Mark,
Thanks for your comment. Yes, any non-focusable control will do, and you can build your own using the (protected) SetStyle method of a control.
An excellent tip, thanks.
Hi Yort thanks for the great info.
I followed all your instrutions to a T and everything seems to have went well till I actually tried to use the keyboard.
For some reason the only keys that do work are the caps lock and the shift keys.
Any ideas?
Hi,
I'm sorry you're having trouble, but that's not a symptom I've seen or that I can think of a cause for. Perhaps if I could see your code I could help more.
Have you had any luck trying to debug the code with a debugger ? What actually gets executed and what is the result ?
Post a Comment