A Virtual Keyboard for your WPF apps

Keywords: WPF, Silverlight, keyboard, Unicode, XAML

GermanLayout

Sometimes having a software-based ‘virtual keyboard’ for your desktop applications can be a help.  You can provide a way for your users to enter characters that they don’t have on their physical keyboard. You can also circumvent a stealthy keylogger by entering passwords this way. And for writing letters or posts to readers in other languages, it can be a huge timesaver.

Thus, the VirtualKeyboard project. You can get the complete Visual Studio solution from the CodePlex project here. I built it using Visual Studio 2013, however projects for earlier versions of Visual Studio (and targeting earlier versions of the .NET Framework) are included.

RussianLayout

This was a fun project done to do in Windows Presentation Foundation (WPF) – and I thought it’d provide a useful article to y’all because of the range of design techniques it uses. WPF yielded a few side-bennies like the fact that you can resize this Window and all the keys resize themselves appropriately. That was a freebee, by virtue of the Grid control.  Of course, I consider WPF to be a delight to design with; it’s the premier GUI-design platform out there and I have great respect for it’s creators. That said, this project does represent a significant amount of work. Let me rephrase that: a lot amount of work, to get it right. And then to boil it down to it’s essentials to the fairly compact application you see before you. I’m sharing this with you, to save you time on your projects and to illustrate techniques. Please give your feedback and contribute your own improvements so we can evolve this to make it even more useful. If you send me your suggestions, I’ll edit them into the code upon the next revision and cite you as the contributor. Multiple minds are always superior to just one.

We’ll walk through it’s design, have a little fun, and provide a link so you can download the entire demo application and source-code so you can put it to immediate use.

The VirtualKeyboard in operation:

It’s a non-modal dialog window, actually a WPF Window, which, if you integrate it into your application – allows you to provide a way for your user to invoke it and click on the key-buttons to insert characters into your application text fields.

DemoAppWithHands2

I’ve provided it with several language modes as a starter, and a brilliant gentleman named Iyengar Sesharaman did a beautiful job contributing two additional language sets – Sanskrit and Tamil – plus enhanced the code! My immediate need was for a memorization flash-card program to learn various language terms as in Arabic or Russian, so that’s what I created first. It also has French, German, and Spanish, which only actually add a few locale-specific characters. Perhaps you can tackle implementing a Mandarin keyboard and send to me to be included – I’d love to see!

You select the keyboard-layout via the ComboBox at the lower-right corner. Changing the language causes the keyboard to automatically update itself.

I found most of the keyboard-layout information on wikipedia and the like. I have to warn you though: that’s a good way to get utterly distracted and wind up delving into the historical underpinnings of ligatures and linguistics and whatever else catches your roving eye. But anyway – I strongly advocate for simplification via standardization. Hence, I try to select the one layout that is most appropriate as a standard for a given language/culture, but not go so far as to bastardize the standard US keyboard form itself. I can see that for some languages, that is not going to be an easy balance to choose. That arrangement should be chosen (by my reasoning) to most resemble what a native speaker would most likely be used to, within the confines of the standard keyboard layout. We are not selecting a Culture/Language for the GUI, after all. That’s a different topic altogether.

Anyway..

ColorfulFrog_wAs a further aid in typing, along the bottom edge, are thirteen extra typographic symbols you can type. I’ve needed these a lot.

In the demo-app pictured above you can see that there are two text fields. One is a TextBox, the other a RichTextBox. The virtual keyboard (VK) works the same with either one. Whichever text field has the current focus, that’s the one who receives whatever is typed into the VK.

The keyboard has provisions for saving it’s state, in that the next time you invoke it it’ll preset to the last language you used, and it’ll also remember whether you had it up whence you last exited your application, so it can auto-launch itself next time.

As a further convenience, just for kicks n giggles – when you move your application around on the screen, this keyboard Window can move in sync with it.

The key ToolTips are a convenience to the user, to help identify what the key is. I was tempted to insert the entire history of each glyph in there. However, this application/tool is for helping the person of the current Culture, type characters that happen to come from a foreign alphabet (relative to that user). Thus the ToolTips are in English (by default) to identify what those glyphs are. For this application, those ToolTips can be important.

When you click on the CAPS LOCK key, all the letters shift to uppercase. It stays in this state until you click CAPS LOCK again.

When you click on the SHIFT LOCK key, you have access to the shifted-state key symbols that you see on the keycaps, in addition to capital letters. This is the same as a physical keyboard does. In this case, after a ten-second timeout it drops back down to the unshifted state.

The Control and Alt keys do nothing. I just left them there so it looks more like a standard (US English) keyboard. But you can put those to use for something like the dead-letter keys that certain keyboards use languages. Or just remove them.

The Platform

This I originally created using Visual Studio 2010, on Windows 7 x64, and targeted the .NET Framework 4. Now I do my development using Visual Studio 2013 on Windows 8.1.  I did not test it on earlier versions.  I’m a believer in letting the vendors evolve our tools and taking advantage of the latest versions. If you’re one of those still stuck on version 1.0, because you’re so ultra-conservative – all I can say is: go away!  I have a separate library that this and all my other projects use, but I pared it down for this project download to just the bare minimum that it uses and included it with your source download. That ‘common’ base-level library is BaseLib, which I use for both desktop apps and web apps. Another library, BaseLibWpf, contains the WPF-specific common facilities; I separated that out because I didn’t want to be deploying desktop-specific assemblies with my online applications.

I set the configuration to target “Any CPU”. And that took a bit of tinkering. I’m unsure why. It kept flipping itself back to x64. When VS 2010 on Windows x64 suddenly seems to not be recognizing certain of your projects, check the build configuration. If one is x86 and the other x64, they will definitely cap your swagger! It can take several attempts before the setting sticks. (update: I haven’t seen that behavior as yet, with VS 2013).

I’m sure you want to know how to put it to use within your own program. Then we’ll chat about a few interesting aspects of it’s design, and then finally walk through the code itself.

Using it for your own application

The easiest way to tell how to use the VK in your own design, is to show you a simple demo-app that does just that. Let’s mosey through it in three steps.

Using it within your own Application is easy. The demo app that comes with your download is a pared-down WPF application that has a simple TextBox to receive the characters, and a Button for launching the virtual keyboard. And.. I added a 2nd text control, a RichTextBox, to illustrate using the VK for multiple input fields.

Here’s the XAML for the demo-app:

<Window x:Class=“JhVirtualKeyboardDemoApp.MainWindow”
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml&#8221;
Title=“JhVirtualKeyboard Demo Application, by James W. Hurst” Height=“247” Width=“359” Background=”{StaticResource dialogBackgroundBrush}>
<DockPanel LastChildFill=“True”>
<StackPanel Orientation=“Horizontal” HorizontalAlignment=“Right” DockPanel.Dock=“Bottom” Margin=“0,8,10,8”>
<Button Name=“btnVK” Margin=“5,0” Width=“96” Click=“btnVK_Click”>Virtual Keyboard</Button>
<Button Name=“btnClose” Margin=“5,0” Width=“80” IsCancel=“True” Click=“btnClose_Click”>Exit</Button>
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=“Auto”/>
<RowDefinition Height=“*”/>
<RowDefinition Height=“Auto”/>
<RowDefinition Height=“*”/>
<RowDefinition Height=“Auto”/>
<RowDefinition Height=“Auto”/>
</Grid.RowDefinitions>
<Label Name=“lblTextBox” Margin=“25,0,0,0” Padding=“5,5,5,0”>TextBox:</Label>
<TextBox Name=“txtBox” Margin=“20,0,20,10” FontSize=“16” Grid.Row=“1” GotFocus=“txtBox_GotFocus” ToolTip=”{Binding Path=ToolTipForTextBox} IsEnabled=”{Binding Path=IsEnabled} ToolTipService.ShowOnDisabled=“True” />
<Label Name=“lblRichTextBox” Margin=“25,0,0,0” Padding=“5,5,5,0” Grid.Row=“2”>RichTextBox:</Label>
<RichTextBox Name=“txtrichBox” Margin=“20,0,20,5” Grid.Row=“3” GotFocus=“txtrichBox_GotFocus” ToolTip=”{Binding Path=ToolTipForRichTextBox} IsEnabled=”{Binding Path=IsEnabled} ToolTipService.ShowOnDisabled=“True” />
<CheckBox Name=“ckbxEnable” Margin=“20,10,0,0” Grid.Row=“4” Content=“Enable the text controls” IsChecked=”{Binding Path=IsEnabled} />
</Grid>
</DockPanel>
</Window>

Mainly there’s a TextBox and a RichTextBox. The DockPanel on line 1 is there simply as a convenience for sticking the buttons along the bottom. I’ve been in the habit of throwing the DockPanel in there everytime I create a new Window. Note that when you set a Label over another field, if you want it to get quite close you have to set it’s bottom-padding to zero as well as it’s bottom-margin. I leave the other sides at the default padding value of 5.  In some cases I’ve had to use a negative bottom-margin to get it to nudge as near as I wanted. Tried that same method with a girl at the theatre, but that didn’t have positive results.

The two buttons have click-handlers which serve to launch the virtual keyboard, and to close the application.

I use a certain coding formatting standard in all our projects, which I’ve detailed here.

Let’s check out the code-behind for this MainWindow. Here’s the constructor..

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using Hurst.VirtualKeyboard;
  5. namespace Hurst.VirtualKeyboardDemoApp
  6. {
  7.     ///<summary>
  8.     /// The code-behind logic for our MainWindow.
  9.     ///</summary>
  10.     public partial class MainWindow : Window, IVirtualKeyboardInjectable
  11.     {
  12.         // The constructor
  13.         public MainWindow()
  14.         {
  15.             InitializeComponent();
  16.             _viewModel = new ViewModel();
  17.             // Remember which text field has the current focus.
  18.             _txtLastToHaveFocus = txtBox;
  19.             // Set up our event handlers.
  20.             this.ContentRendered += OnContentRendered;
  21.             this.LocationChanged += OnLocationChanged;
  22.             this.Closing += new System.ComponentModel.CancelEventHandler(OnClosing);
  23.             // Set the DataContext to our view-model.
  24.             DataContext = _viewModel;
  25.         }

You add a using pragma for the VirtualKeyboard 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 20: 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:

void OnContentRendered(object sender, EventArgs e)
{
// If the Virtual Keyboard was up (being shown) when this application was last closed,
// show it now.
if (Properties.Settings.Default.IsVKUp)
{
ShowTheVirtualKeyboard();
}
// Put the initial focus on our first text field.
txtBox.Focus();
}

We have 3 things going on here.

1. As you can see on the first line within the method, 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 the final line of code 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:

  1. public void ShowTheVirtualKeyboard()
  2. {
  3.     // (Optional) Enable it to remember which language it was set to last time, so that it can preset itself to that this time also.
  4.     VirtualKeyboard.SaveStateToMySettings(Properties.Settings.Default);
  5.     // Show the keyboard.
  6.     VirtualKeyboard.ShowOrAttachTo(this, ref _virtualKeyboard);
  7.     // (Optional) Call this for each text field for which you want the virtual keyboard to also remap your physical keyboard.
  8.     _virtualKeyboard.HandleKeysForControl(txtBox);
  9.     _virtualKeyboard.HandleKeysForControl(txtrichBox);
  10. }

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:

public System.Windows.Controls.Control ControlToInjectInto
{
get { return _txtLastToHaveFocus; }
}

By implementing the ControlToInjectInto property, 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 fun..

///<summary>
/// Handle the LocationChanged event, which is raised whenever the application window is moved.
///</summary>
void OnLocationChanged(object sender, EventArgs e)
{
// Do this if, when your user moves the application window around the screen,
// you want the Virtual Keyboard to move along with it.
if (_virtualKeyboard != null)
{
_virtualKeyboard.MoveAlongWith();
}
}

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 itself relative to the application by manipulating it directly. Normally, I include a checkbox in my app’s Options dialog to give the user the ability to turn this on or off.  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..

///<summary>
/// Handle the event that happens when the user clicks on the Show Virtual Keyboard button.
///</summary>
private void btnVK_Click(object sender, RoutedEventArgs e)
{
ShowTheVirtualKeyboard();
}

And here is the Closing event handler:

///<summary>
/// Handle the Closing event.
///</summary>
void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
bool isVirtualKeyboardUp = _virtualKeyboard != null && VirtualKeyboard.IsUp;
Properties.Settings.Default.IsVKUp = isVirtualKeyboardUp;
Properties.Settings.Default.Save();
}

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..

private void txtBox_GotFocus(object sender, RoutedEventArgs e)
{
// Remember that the plain TextBox was the last to receive focus.
_txtLastToHaveFocus = txtBox;
}private void txtrichBox_GotFocus(object sender, RoutedEventArgs e)
{
// Remember that the RichTextBox was the last to receive focus.
_txtLastToHaveFocus = txtrichBox;
}

As a result, that variable is always pointing to the correct field.

Settings:

I mentioned you need to add something to your application’s settings:

Settings

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. Checkit..

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 ..

ClassDiagram

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 (hugging myself..).

Now, to make this work with our XAML design, merits a bit of intricacy. WPF likes to have stable view-models, to thus 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:

VKwKeynames100

A few observations from the XAML of the VK..

xmlns:baseLibWpf=“clr-namespace:Hurst.BaseLibWpf;assembly=Hurst.BaseLibWpf”
xmlns:vk=“clr-namespace:Hurst.VirtualKeyboard”
baseLibWpf:WindowSettings.SaveSize=“True”

I bring in the namespace for the external utility library and assign it to the XML namespace prefix “baseLibWpf”. Then I do the same for the VirtualKeyboard 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 a bound property on our view-model that hopefully yields a font that renders our layout nicely. Arial Unicode MS is one common font that seems to have pretty decent coverage of the non-Latin codepoints. But it doesn’t hurt to have some fun seeking out lovely fonts for this.

Let’s look at one of the key-button definitions:

            <Button Name=“btnSectionMark”
Command=“vk:VirtualKeyboard.KeyPressedCommand”
CommandParameter=“§”
Grid.Column=“2”
Height=“24”
Margin=“6,0,2,1”
ToolTip=“Section sign, or signum sectionis”
Width=“25”>§</Button>

This one injects the section-mark (also called the “signum sectionis” if you want to sound uppity). 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..

<Style TargetType=”{x:Type Button}>
<Setter Property=“Width” Value=“Auto”/>
<Setter Property=“Height” Value=“Auto”/>
<Setter Property=“FontSize” Value=“18”/>
<Style.Triggers>
<Trigger Property=“IsPressed” Value=“False”>
<!– This is here because we’re setting it slightly differently when the button is being pressed. –>
<Setter Property=“Margin” Value=“0,0,2,1”/>
<Setter Property=“Effect”>
<Setter.Value>
<DropShadowEffect Direction=“315” Opacity=“0.7”/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property=“IsPressed” Value=“True”>
<Setter Property=“Foreground” Value=”{StaticResource brushBlue}/>
<!– Shift the button downward and to the right slightly, to give the affect of being pushed inward. –>
<Setter Property=“Margin” Value=“2,1,0,0”/>
<Setter Property=“Effect”>
<Setter.Value>
<DropShadowEffect Direction=“135” Opacity=“0.5” ShadowDepth=“2”/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

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 right.

In the first version I had set the FontFamily to “Bitstream Cyberbase, Roman”. I purchased that due to it’s codepoint-coverage. But I found that different fonts are merited for different languages, so I made that a bindable property within the view-model.

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 to the right, and changes the DropShadowEffect to get that 3D affect.

Here’re how the main key-buttons are defined..

<Button Name=“btnVK_Oem3” Grid.Column=“0” ToolTip=”{Binding Path=VK_Oem3.ToolTip}>
<Button.Content>
<TextBlock Name=“txtblkVK_Oem3”>
<Run Text=”{Binding Path=VK_Oem3.Text, Mode=OneWay}/>
</TextBlock>
</Button.Content>
</Button>
<Button Name=“btnVK_1” Grid.Column=“1” ToolTip=”{Binding Path=VK_1.ToolTip}>
<Button.Content>
<TextBlock Name=“txtblkVK_1”>
<Run Text=”{Binding Path=VK_1.Text, Mode=OneWay}/>
</TextBlock>
</Button.Content>
</Button>
<Button Name=“btnVK_2” Grid.Column=“2” ToolTip=”{Binding Path=VK_2.ToolTip}>
<Button.Content>
<TextBlock Name=“txtblkVK_2”>
<Run Text=”{Binding Path=VK_2.TextShifted, Mode=OneWay}/>
<Run Text=”{Binding Path=VK_2.TextUnshiftedNL, Mode=OneWay}/>
<Run Text=”{Binding Path=VK_2.TextAltGr, Mode=OneWay} Foreground=”{Binding Path=ColorForAltGr}/>
</TextBlock>
</Button.Content>
</Button>

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 ViewModel 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.

Here is how that is implemented within the KeyViewModel class …

#region ToolTip
///<summary>
/// Get the ToolTip, whether it corresponds to the unshifted, shifted, or Alt codepoints.
///</summary>
public virtual string ToolTip
{
get
{
if ((s_domain.IsShiftLock
|| (_isLetter && s_domain.IsCapsLock)) && !String.IsNullOrEmpty(_shiftedToolTip))
{
return _shiftedToolTip;
}
else if (s_domain.IsInAltGrState && !String.IsNullOrEmpty(_altToolTip))
{
return _altToolTip;
}
else
{
return _toolTip;
}
}
}public string UnshiftedToolTip
{
get { return _toolTip; }
set
{
if (value != _toolTip)
{
_toolTip = value;
Notify(“ToolTip”);
}
}
}public string ShiftedToolTip
{
get { return _shiftedToolTip; }
set { _shiftedToolTip = value; }
}public string AltGrToolTip
{
get { return _altToolTip; }
set { _altToolTip = value; }
}
#endregion

Here, _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

Stretching hard to get to where we want to be

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.

The exception-handling is something that you’ll want to adapt to your specific application; what I use here is just one example of a system-wide method.

Here’s the code…


///<summary>
/// Either create and show, or else re-attach to (if it’s already up) the virtual keyboard.
/// To be called from the desired target-Window that wants to use it.
///</summary>
///<param name=“targetWindow”>The Window that wants to be the owner of the virtual-keyboard Window and the target of it’s output</param>
///<param name=“myPointerToIt”>The targetWindow’s own instance-variable that will point to the virtual keyboard object</param>
public static void ShowOrAttachTo(IVirtualKeyboardInjectable targetWindow, ref VirtualKeyboard myPointerToIt)
{
try
{
_desiredTargetWindow = targetWindow;
// Evidently, for modal Windows I can’t share user-focus with another Window unless I first close and then recreate it.
// A shame. Seems like a waste of time. But I don’t know of a work-around to it (yet).
if (IsUp)
{
Console.WriteLine(“VirtualKeyboard: re-attaching to a different Window.”);
VirtualKeyboard.The.Closed += new EventHandler(OnTheKeyboardClosed);
VirtualKeyboard.The.Close();
myPointerToIt = null;
}
else
{
myPointerToIt = ShowIt(targetWindow);
}
}
catch (Exception x)
{
Console.WriteLine(“in VirtualKeyboard.ShowOrAttachTo: “ + x.Message);
IInterlocution inter = Application.Current as IInterlocution;
if (inter != null)
{
inter.NotifyUserOfError(“Well, now this is embarrassing.”, “in VirtualKeyboard.ShowOrAttachTo.”, x);
}
}
}

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..

///<summary>
/// Flip the state of the SHIFT key-button.
///</summary>
private void PutIntoShiftState(bool bShiftLock)
{
ClearTimer();
// Set the shift-lock state.
_domain.IsShiftLock = bShiftLock;
UpdateTheKeyBindings();// If we’re turning Shiftlock on, give that a 10-second timeout before it resets by itself.
if (bShiftLock)
{
// If we’re going into shift-lock state,
// regard the AltGr state as mutually-exclusive and turn that off.
_domain.IsInAltGrState = false;
// Set the reset-timer.
_resetTimer = new DispatcherTimer(TimeSpan.FromSeconds(10),
DispatcherPriority.ApplicationIdle,
new EventHandler(OnResetTimerTick),
this.Dispatcher);
}
SetFocusOnTarget();
}

Note that every time 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..

#region Inject
///<summary>
/// Type the given string into whatever the target TextBox (or similar control) is.
///</summary>
///<param name=“sWhat”></param>
public void Inject(string sWhat)
{
if (TargetWindow != null)
{
((Window)TargetWindow).Focus();
TextBox txtTarget = TargetWindow.ControlToInjectInto as TextBox;
if (txtTarget != null)
{
if (txtTarget.IsEnabled && !txtTarget.IsReadOnly)
{
if (_domain.IsFlowDirectionLeftToRight
|| (!_domain.IsFlowDirectionLeftToRight && !_domain.IsForcingLeftToRight))
{
txtTarget.InsertText(sWhat);
}
else  // the FlowDirection is backwards, but the user wants to force it to insert LeftToRight.
{
txtTarget.InsertTextLeftToRight(sWhat);
}
MakeKeyPressSound();
}
else
{
if (!txtTarget.IsEnabled)
{
SetStatusTextTo(“Cannot type into disabled field”);
}
else
{
SetStatusTextTo(“Cannot type into readonly field”);
}
}
}
else
{
RichTextBox richTextBox = TargetWindow.ControlToInjectInto as RichTextBox;
if (richTextBox != null)
{
if (richTextBox.IsEnabled && !richTextBox.IsReadOnly)
{
richTextBox.InsertText(sWhat);
MakeKeyPressSound();
}
else
{
if (!txtTarget.IsEnabled)
{
SetStatusTextTo(“Cannot type into disabled field”);
}
else
{
SetStatusTextTo(“Cannot type into readonly field”);
}
}
}
else // let’s hope it’s an IInjectableControl
{
IInjectableControl targetControl = TargetWindow.ControlToInjectInto
as IInjectableControl;
if (targetControl != null)
{
targetControl.InsertText(sWhat);
MakeKeyPressSound();
}
}
//else   // if you have other text-entry controls such as a rich-text box, include them here.
//{
//    FsWpfControls.FsRichTextBox.FsRichTextBox txtrTarget
//     = TargetWindow.ControlToInjectInto as FsWpfControls.FsRichTextBox.FsRichTextBox;
//    if (txtrTarget != null)
//    {
//        txtrTarget.InsertThis(sWhat);
//    }
}
}
}///<summary>
/// Inject the character represented by the given “key” switch view-model, into whatever the target TextBox is.
///</summary>
///<param name=“vm”>The KeyViewModel that carries the information of what to inject</param>
public void Inject(KeyViewModel vm)
{
// The Tag would’ve been another way to associate the view-model with the button.
if (vm != null)
{
if (vm == _domain.EnterKey)
{
Inject(Environment.NewLine);
}
else
{
Inject(vm.CodePoint.ToString());
}
}
// If we were in Shift-mode, reset that now.
_domain.IsShiftLock = false;
ClearTimer();
}
#endregion

This part can merit a bit of thought. Out-of-the-box, I have provided for two possible controls: a TextBox, and a RichTextBox.

I make a call to 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, isn’t it? 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 BaseLibWpf.WPFExtensions.cs

///<summary>
/// Insert the given text into this TextBox at the current CaretIndex, and replacing any already-selected text.
///</summary>
///<param name=“textbox”>The TextBox to insert the new text into</param>
///<param name=“sTextToInsert”>The text to insert into this TextBox</param>
public static void InsertText(this System.Windows.Controls.TextBox textbox, string sTextToInsert)
{
int iCaretIndex = textbox.CaretIndex;
int iOriginalSelectionLength = textbox.SelectionLength;
textbox.SelectedText = sTextToInsert;
if (iOriginalSelectionLength > 0)
{
textbox.SelectionLength = 0;
}
textbox.CaretIndex = iCaretIndex + 1;
}

Yeah, looks a bit verbose.

Here’s the InsertText extension method for RichTextBox:

///<summary>
/// Insert the given text into this RichTextBox at the current CaretPosition, replacing any already-selected text.
///</summary>
///<param name=“richTextBox”>The RichTextBox to insert the new text into</param>
///<param name=“sTextToInsert”>The text to insert into this RichTextBox</param>
public static void InsertText(this System.Windows.Controls.RichTextBox richTextBox, string sTextToInsert)
{
if (!String.IsNullOrEmpty(sTextToInsert))
{
richTextBox.BeginChange();
if (richTextBox.Selection.Text != string.Empty)
{
richTextBox.Selection.Text = string.Empty;
}
TextPointer tp = richTextBox.CaretPosition.GetPositionAtOffset(0, LogicalDirection.Forward);
richTextBox.CaretPosition.InsertTextInRun(sTextToInsert);
richTextBox.CaretPosition = tp;
richTextBox.EndChange();
Keyboard.Focus(richTextBox);
}
}

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).

Conclusion

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. Next up – a Mac and Linux implementation.

This project was introduced as an article on The Code Project.

This project is also published on CodePlex, which is also a convenient place for you to participate in discussion on this project.

James Witt Hurst

Advertisements

About James W Hurst

a professional software designer since the beginning days of the desktop cptr and uC-controlled avionics, I today am focusing on Java, Swift, C# and F# for building mobile and desktop and online applications under Android, Xamarin.Forms, iOS, WPF, and ASP.NET-MVC along with the requisite HTML/CSS/JavaScript/Ajax for web applications. My database expertise has covered a panoply of different database-engines and modeling approaches, and my main area of academic interest is Artificial Intelligence and vision.
This entry was posted in C#, CodeProject, silverlight, Software Design, The .NET Framework, WPF and tagged , . Bookmark the permalink.

37 Responses to A Virtual Keyboard for your WPF apps

  1. Yeangshing says:

    Hi James,
    I really interested on your article about virtual keyboard WPF for Windows 7. I would like to develop for my own use i.e. customize to support Chinese Cangjie input method and Arabic IME. However it tooks me too long to convert Visual Studio 2010 solution into version 2008 from the source you given. I will appreciate if you can give me the copy of version 2008 for .NET 3.5. Thanks :-)

    By the way I can share with you what I have hosted at google code, http://code.google.com/p/jawiweb/. This is an WPF project writen by myself to automate some Arabic drawing which integrate with svg. Just a piece of small project for myself purpose.

    Regards,
    –yeangshing

  2. James Hurst says:

    Hi Yeangshing, let me see what I can do. I guess I’ll create a virtual machine with VS 2008 and recreate the solution on that, and post it for you.
    I’m curious about your project that you included that link to. What does that do exactly? What do you mean by automate Arabic drawing? Those characters look.. beautiful. That’s an interesting project. Nice graphics work! And yes, of course — if you’d like to create a Chinese and Arabic keyboard layouts and share them – that would be great. Do you know both languages?! I’m fascinated with both myself, and intend to learn both, but I’ll be a year yet trying get fluent with Russian before moving on to the next one. And, what does “Cangjie” mean, is that the name of a keyboard layout that is used in China?
    Thanks for your comment and for sharing that. James

  3. Balaji says:

    Hi,
    Very nice artical ,but can you send me in .net2008 because I can’t convert into 2008

    you can send me code or dll to my email id pdvbalaji@gmail.com

    I am waiting for your replay

    Thank you

    balaji

  4. Oguz Tugrul says:

    Hello,

    I need the solution for VS2008, I will be appreciated if you can send it to me as soon as possible. E-mail: oguz_tugrul@hotmail.com

    Thank you…

  5. Beau Brownlee says:

    Hi, I really like this implementation of a virtual keyboard but i’m having trouble implementing it. I basically am trying to just re-implement your demo code and I copied everything exactly from this article into a WPF application (vs 2010 etc). The virtual keyboard pops up but the window is black/blank. Have you run into this?

  6. Mihail788 says:

    !!!
    . , , , , , , , , .
    grau.ru

  7. jyyou says:

    if you don’t mind, Can i Get the solution For VS2008,
    It’s the Program What i need, but now i’m using VS2008
    Would you Send me a 2008 Solution?, I’m living in Asia And Not Very good at English… Thank you for your Help or Not
    Email:flowersnakes@nate.com

  8. vikram says:

    can u send me the source code for vs 2010 and vs 2008
    at

    vikrambarthwal@yahoo.com

  9. malakool says:

    Really interesting article virtual keyboard , same sort of thing i want to do in C# web application VS2008 , so could you help me and if possible then post your article in VS2008 also.

    Thanks

  10. iris says:

    Hi, James:
    I like your code of Virtual Keyboard and want to use it in my application.
    My app is WPF, with several TextBoxs in different pages or viewers.
    If I do NOT do this:
    MyWindow : Window, IIVirtualKeyboardInjectable
    Is there another way MyApp can use the Virtual Keyboard dll / code?
    Thanks;
    Iris;

  11. Mind Swiper says:

    Hej I’ve tried to Download the code for the VS2010 solution but when I try to open and build it it still need som JhLib and it’s not included in the downloaded rar,

  12. Sharda says:

    Hi James,

    I have downloaded the source code for VS 2010 from CodePlex and tried to build it. It’s not building and looking for JHLib which is not present in source folder.

    I downloaded the sourcecode from codeproject which has the JHLib. Is both the source code same.

    Regards,
    Sharda.

  13. Noam gabay says:

    Hii. First of all , this is very powerful control! Thanks for sharing it!
    How can I add languages to the keyboard? Is there a Hebrew keyboard subclass that had been already developed?
    If so, please send me a link to the source code.
    Gabay.noam@gmail.com

    Thanks
    Noam gabay

  14. Whats up very nice blog!! Man .. Excellent .. Superb .. I will bookmark your website and take the feeds also?I’m glad to find a lot of helpful information here within the post, we’d like develop extra techniques in this regard, thank you for sharing. . . . . .

  15. sudhir says:

    can you send me virtual keyboard code in VS2010 for c# windows application on my email id sudhirkolhe1020@gmail.com

  16. naren says:

    It was cool article but I didn’t understand how the GUI comes in vb2010

  17. That is very interesting, You are an overly professional blogger. I’ve joined your feed and look forward to in quest of more of your wonderful post. Additionally, I’ve shared your site in my social networks

  18. Av says:

    I’m trying to understand your code:
    1- SL version enable only from codeplex link, but other two links doesnt contain it.
    2- All 3 download sources doesn’t contain the same code? even for .NET 4 only, each source has it’s own last update date.
    3- This article doesn’t usable for SL at all
    4- About feature -show vk for some control- why you use interfaces(IVirtualKeyboardInjectable)? it means that i need to realize this interface in me own control class for some base Control to enable this feature isnt it? What about behaviors for control? If il need to reuse such behavior as static option for some Control i can make Attached DP in the style for that control, but if i’l realize interface, it means that i’l need to do another style for last ChildControl – is that really what you want?
    5- the structure of your projects in solutions very dirty.
    if you interested in changes with Behaviors i can sent recomposed version to you

    • Hello Av, thank you for your feedback. I was diving heavily into SL before hearing that that technology was being dead-ended by Microsoft, so I didn’t finish implementing that. I guess I neglected to remove all references to it – I’ll attend to that with the next update. Yes, I don’t necessarily update everything at once. That’s one benefit of having separate assemblies. And yes – if you use IVirtualKeybaordInjectable then that is you’ll have to do (implement that interface). Alternatively, yes – you could use behaviors. Actually, now that you mention it – your ideas seems potentially pretty good. If you’d like to send me your solution I’d love to check it out, and perhaps we can together update this post with your ideas? Feel free to email me. jh

  19. I read once that when someone asked the great American poet Frost if he reads himself as
    a teacher, he stated: I am not a teacher, I’m an
    awakener. I feel this blog is the same since it awakens
    me, and makes me think.

  20. Len Vanerstrom says:

    James:

    I’m trying to have a look at your keyboard but the sourcecode download appears to be missing a “jhLib” project. Where can I download that component from?

    Len

    • Hello Len, that was an older base-library that I had used for it. I just now uploaded a new version of the complete project. That old jhLib, is now renamed to BaseLib and BaseLibWpf, and is slimmed-down. Please let me know if you have any issues with it – thanks. James Hurst

  21. LemsOmime says:

    Ils vous proposent cette fois le meilleur clavier de la planete pour etudier l’arabe en toute securité et chance , il peut s’homogeniser avec plusieurs moteurs de recherche telle que ask et lycos , elle ont la possibilité chercher sur dailymotion avec un seul click de la souris , apprenez l’arabe avec nous avec le meilleur plaisir de la langue arabe et la calligraphie arabe elle est de splendide qualité et tres belle a voir et a toucher avec le nez clavier arabe de egypt ou uae ou en allemagne aussi il y a les arabes qui souhaitent etudier l’arabe mais ne trouvent pas les materiels adequats de le faire a moindre cout , on sait bien que les francais sont tres riches et ne lachent pas le blé dans les choses importantes mais si c’etait la viande ou la glace la ils sont les derniers n’en parlant pas de KFC , allez n’oubliez pas la tradition de vos ancetres avec le clavier arabe de la planete terre guide touristique maroc

  22. Silvio says:

    Hi James
    For some reason i can’t download the current release at codeproject.com.
    The zip file VirtualKeyborad.zip is not valid :-(
    Can you help me, please?

  23. Sohaib says:

    Hi james I want to use it for all applications not for my single application. Please let me know how to achieve it that way. I appreciate your asap response as I have to integrate it in my Clients machine.
    Thanks

  24. Jan says:

    Hello James,
    I really like your solution but sadly the ParseLib_Vs2010 is missing and the ParsingValueConverters in the BaseLibWpf_Vs2010 is missing too. Is there a way you could reupload them?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s