10,274,286 members (76,042 online)
You add a using pragma for the JHVirtualKeyboard namespace, and implement the IVirtualKeyboardInjectable interface as shown above. That interface is your contract with the virtual keyboard (VK) so they can interoperate.
Notice what we’re doing on line 14: we have an instance variable, _txtLastToHaveFocus, that serves to point to whichever text field has focus. We use this to tell the VK where to put it’s characters.
Notice also that we have three event handlers defined here. Let’s check these out.
The ContentRendered event handler:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:1d073158-6172-40ff-8890-97b81ef2e794" class="wlWriterEditableSmartContent">
We have 3 things going on here.
1. As you can see on line 5, it remembers whether the VK was up when we last ran this application. To achieve this, you simply add a boolean flag to your settings; in this case we named it IsVKUp.
2. Invoke the VK using method ShowTheVirtualKeyboard. This is placed here, within the ContentRendered handler, because at this point your Window is already rendered and the VK can know where to position itself.
3. On line 10 we set focus to the TextBox. Thus, you’ll still see the nice blinking caret in your text field, just as you would without the VK.
Here’s the ShowTheVirtualKeyboard method:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:c2e0eba6-04c0-42f0-baf8-f49f28460a25" class="wlWriterEditableSmartContent">
This is where we fire up the VK. You’re doing 2 things here.
1. By calling SaveStateToMySettings, you tell it how to save it’s state to your application settings. This is optional.
2. You call ShowOrAttachTo to launch the VK. You pass it the this pointer, so it knows who it’s owner Window is. You give it a reference to your local _virtualKeyboard instance variable, so you have a reference to it.
You need to implement the IVirtualKeyboardInjectable interface. Here’s how the demo app does this:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:a4bffb4d-0553-4d90-85e7-b957fd71df7b" class="wlWriterEditableSmartContent">
By implementing ControlToInjectInto, you tell the VK where to send the characters that your user types into it.
If you have just one text field, you would simply return that (the instance-variable for the field). Here, we have two text fields. So we instead track which one has focus using this instance-variable, and return that in this property getter.
Let’s check out something else cool..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:bfce5b33-947b-47b5-854a-f7c62f9c49b5" class="wlWriterEditableSmartContent">
If you handle the LocationChanged event of your Window thus, then the VK will move around along with your Window, like it’s stuck to it. You can still position the VK elsewhere. Normally, I include a checkbox in my app’s Options dialog to give the user the ability to turn this on or off. The MoveAlongWith is a Window extension method that we’ll get to in a bit.
Oh yeah.. the click-handler that launches it. Nothing remarkable here..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:62473db1-f00e-4c49-a472-275b7bf6d9ba" class="wlWriterEditableSmartContent">
And here is the Closing event handler:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:cf51ae2b-6dbb-469f-822a-21854eaf18db" class="wlWriterEditableSmartContent">
All this does, is save the Up/NotUp state of your VK to your application’s settings.
Tracking which field has focus:
This demo-app has two text fields so that you can see how to use your VK to serve input into more than one control.
What you need to accomplish this, is pretty simple. We added that instance variable, _txtLastToHaveFocus. This variable has to always be pointing to whichever field is currently holding the focus within your Window. How do we do this?
Simple. We hook up handlers to the GotFocus events of all the text fields, as here..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:4f73342a-0da1-493f-b9bd-d5d8ee151f20" class="wlWriterEditableSmartContent">
As a result, that variable is always pointing to the correct field.
I mentioned you need to add something to your application’s settings:
;margin:7px 7px 12px;" title="Settings" border="0" alt="Settings" align="left" src="http://designforge.files.wordpress.com/2011/01/settings2.png?w=507&h=255" width="507" height="255" />
Here you can see I put two settings. The bool setting, IsVKUp, can be whatever you want to name it. The one that specifies the keyboard layout, though, must be named “VKLayout” or else VK won’t see it.
That’s it as far as how to use this within your own app. Simple, I trust? If you’ve any questions please feel free.
Let’s talk about the design a bit.
The Design of the Virtual Keyboard
I’m quite fond of object-oriented design, especially where it saves time and yields a more elegant creation.
Here, we have keyboard layouts. These can be pretty detailed bits of information. Fifty-plus keys that change according to the language/alphabet, tooltips, and double that for the shifted/unshifted states, and then you have to treat the capital letters versus small letters, differently than the distinction between shifted/unshifted symbols. And there’re potentially hundreds of possible keyboard layouts.
On the other hand, some of these have a lot in common. And, considering the central role of the common US-std English-language keyboard, I based the root class of the hierarchy of keyboard layouts upon the English US keyboard to supply the default properties.
Thus, we have a conceptual scheme that begs of an object-oriented class hierarchy. At the top, the root class is the US keyboard layout. Everything else either inherits their stuff from that, or overrides it. Below that, at the second level, are the main variations. Russian (perhaps that should’ve really been called Cyrillic), Spanish, etc. And below that, could be further variations of those.
The economy of data arises when you consider that, e.g. the Spanish keyboard changes only a few things, but wants to leave everything else in place just as with the English US keyboard. So if we can let the Spanish keyboard inherit all the English keyboard features, it only has to override just those keys (and their tooltips, etc.) that it wants to change. The ideal OOP application.
To forge this scheme into our WPF XAML-based application that has fifty data-bound buttons, check it ..
;margin:7px 1px;" title="ClassDiagram" border="0" alt="ClassDiagram" align="left" src="http://designforge.files.wordpress.com/2011/01/classdiagram1.png?w=714&h=943" width="714" height="943" />
If you look at the KeyAssignmentSet class in Visual Studio (VS), you’ll see it has a big honkn glob of instance variables and properties. One for every key-button of the keyboard. I named them “VK_A”, “VK_OEM1”, etc. to match the Win32 definitions.
The class KeyAssignmentSet represents the US English keyboard layout.
The subclass SpanishKeyAssignmentSet, overrides only those properties it wants to change. In this, just seven does the trick. That’s a lot easier than providing for all 50-plus key definitions. Suweet.
Now, to make this work with our XAML design, merits a bit of intricacy. WPF likes to have stable view-models, to be a view of. The VK itself has a main view-model class: KeyboardViewModel. All the key-buttons have view-model objects that are members of KeyboardViewModel. Thus, to apply the appropriate KeyAssignmentSet to that view-model, we have an array of the individual key view-models, and we iterate across those assigning the information from the KeyAssignmentSet to them.
For this check out the method AssignKeys, in VirtualKeyboard.xaml.cs
In response, the WPF view updates itself automatically to reflect the new layout.
In this code I use the term “CodePoint” to refer to a spot within the Unicode standard that’s represented by a specific number, and depending upon which font you happen to be using – maps to a resulting glyph. Unicode is a wonderful resource; a massive amount of work was invested to wrest order and manageability out of the World’s chaos of languages and alphabets. Here, I use the term “shifted CodePoint” to refer to the character on the upper portion of the key button, such as the Asterisk that is often over top of the digit-8 key.
Here’s the layout with the key-names shown:
;margin:7px 7px 24px;" title="VKwKeynames100" border="0" alt="VKwKeynames100" align="left" src="http://designforge.files.wordpress.com/2011/01/vkwkeynames100_thumb.png?w=640&h=223" width="640" height="223" />
A few observations from the XAML of the VK..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:dd8d5995-b580-4b72-9c56-e1eb1822f732" class="wlWriterEditableSmartContent">
I bring in the namespace for the external utility library and assign it to the XML namespace prefix “jhlib”. Then I do the same for the JHVirtualKeyboard assembly and assign that to “vk”. Note how the syntax varies: one is an external assembly, and the other is a local assembly hence doesn’t require the “;assembly=” part. This is one of those things that if you don’t get it right, you’ll spin wheels trying to figure out why your XAML isn’t recognizing your junk in your trunk.
I set the WindowStyle property to ToolWindow, and the FontFamily to Arial Unicode MS since that seems to have pretty decent coverage of the non-Latin codepoints.
Let’s look at one of the key-button definitions:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:564c20b0-7dd5-4b67-b412-5592a6f784a7" class="wlWriterEditableSmartContent">
This one injects the section-mark (also called the “signum sectionis” if you want to sound like a jerk). All the key-buttons use this same KeyPressedCommand, with the command parameter carrying the character to inject. Most of the formatting is set in a Style, but this one overrides the Margin, Width and Height to fine-tune the appearance.
Right now, looking at this XAML in LiveWriter, the button Content shows up with the syntax for the hexadecimal 00A7 value, with the ampersand, pound, x prefix and the semicolon suffix. But at the same time, the published version I’m looking at online in FireFox, shows the actual signum sectionis instead. Hmm..
I use a style for the key-buttons so that I don’t have to type in a ton of markup..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:80f03584-e6ea-4ba8-b802-ed3cae91303d" class="wlWriterEditableSmartContent">
This Style targets all Buttons, thus it becomes the default for all Buttons within that container.
I had to tinker with and fine-tune the sizes, padding and margins to get it to look it’s best.
Note the FontFamily: “Bitstream Cyberbase, Roman”. I purchased that due to it’s codepoint-coverage.
The most interesting thing (well, to me anyway) in this Style is making the key-buttons visually float off the surface a bit, and then seem to push downward (and to the right slightly) when you click on them, to yield that cute 3D affect. As you can see from reading the XAML, the trigger fires when the IsPressed property becomes true. It reacts by setting the margin property to shift it’s position down and right, and changes the DropShadowEffect to get that 3D affect.
Here’re how the main key-buttons are defined..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:99c0a605-5769-40c3-a97e-179b0e83a7ec" class="wlWriterEditableSmartContent">
The DataContext of this Window is the KeyboardViewModel. As you can see, the Content of these Buttons is set to the Text property of the VK_1, VK_2, etc. members of KeyboardViewModel. Each of those keybuttons implement INotifyPropertyChanged to keep the GUI apprised of their values when they change. The base class BaseViewModel implements that for us.
The ToolTip property is also data-bound to the view-model. Each of those KeyViewModel objects (ie, VK_1, etc) also has a ToolTip property.
A slight complication for me was that that Text property, and the ToolTip, needed to yield different values depending upon whether the shift key was in effect.;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:f3bc8365-9b73-4074-bf83-2a396f83327c" class="wlWriterEditableSmartContent">
Here, s_domain is a static variable that refers to our KeyboardViewModel. This selects between the unshifted, and the shifted, ToolTip value.
The Text property acts similarly, except that it selects from the key’s Text property if that was explicitly set, otherwise it returns the unshifted or shifted codepoint that was assigned to that key.
Showing the VK
This merited a bit of tinkering. Probably there’s a better way to do this, but it’s what I was able to get to work. If you know of a better way, please share.;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:838391d7-ec0f-4f62-a5ca-c65b0a44bb42" class="wlWriterEditableSmartContent">
Yeah, it’s not the trivial call to ShowDialog that we’d expect, is it?
The complication is: if it was already showing, but owned by some other window, and this window (the one you’re actually trying to get to use the VK now) was launched modally, it can’t just “take ownership” of the VK Window. The only thing I could get to work, was to shut the VK down and then re-launch it.
Thus, here we tell the VK to close itself, and hook into the Closed event so that another method gets called after the VK closes. That other method, in turn, re-launches the VK.
A bit of usability testing revealed that your users, in attempting to enter their stuff using the mouse, preferred a shift-key that would reset itself. So, clicking on either of the shift keys pushes the VK into the shifted state, and then clicking on any character pops it back into the un-shifted state. But if the user delays, it resets itself after ten seconds. Here’s what does that..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:9fac6e94-0da3-4e41-86c8-979ca2e771fd" class="wlWriterEditableSmartContent">
Note that everytime your user clicks on anything within the VK, the VK’s Window gets focus. Which is not what we want. Thus we follow that with a call to SetFocusOnTarget, which tosses focus back to your text field.
Doing the actual key character injection
Here is the method that actually inserts the character into your target text-field..;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:78942caf-b89a-48d5-bd45-e8a7369043d5" class="wlWriterEditableSmartContent">
This part can merit a bit of thought. I have provided for two possible controls: a TextBox, and a RichTextBox.
I call your method ControlToInjectInto, to get a reference to what to put the character into. I try to find what it is by casting it to first one type and then another.
For either of these, I defined an extension method InsertText, to do the actual text insertion. Which was surprisingly non-trivial.
In an effort to accommodate you custom-text-box creators, I also defined an interface IInjectableControl. If you have a text field that is neither a TextBox nor a RichTextBox, if you can make your control implement this interface, it’ll still work. Otherwise, you’re going to have to modify this code to make it work for you. Well, that’s the great thing about getting a project complete with source-code. You’ll need to code for your control here, and also in the method DoBackSpace – which btw uses the built-in editing command EditingCommands.Backspace to actually do the BACKSPACE. It was actually simpler to just manipulate the text directly. But I wanted to play with the commanding approach. So I add a command-binding to this control at this point, use that to execute the Backspace operation, and leave that command-binding in place until the keyboard closes at which time we clear it.
The Extension Methods
Here’s the InsertText extension method for TextBox, which you’ll find in JLib.WPFExtensions.cs;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:63f8858c-c84d-400d-bcbf-b4eae608b511" class="wlWriterEditableSmartContent">
Yeah, looks a bit verbose.
Here’s the InsertText extension method for RichTextBox:;padding ;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:a0a165ef-f1da-4fb0-a448-1a2f83d10a11" class="wlWriterEditableSmartContent">
This took a bit of tinkering, just to get to insert a simple character. It’s not as simple as simply appending to the text: if the caret is not at the end, you have to insert at the caret and slide everything to the right (speaking in terms of array-indices of course).
So, there you have it – a working screen-based virtual keyboard created using C# and WPF. I hope this is useful for you, and that you’ll give me your thoughts and suggestions. I find WPF fun to work with: it feels so natural now that I dislike using anything else for a desktop GUI application. But there’s always new bits to learn.
James Witt Hurst
This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)
About the Author
Software Developer (Senior)
Professional, contract software developer. I help with projects, give training seminars and do on-site project consultations. BTW I'm available for short & long-term contracts.
San Jose, CA
Follow on Twitter
Comments and Discussions
General News Suggestion Question Bug Answer Joke Rant Admin
A Technical Blog, originally posted at http://designforge.wordpress.com/2011/01/06/jhvirtualkeyboard/
A design for a Virtual Keyboard using WPF. Continue reading →