Ink in WPF -- Using TextInputPanel for Text Input
Posted
Wednesday, October 01, 2008 12:08 PM
by
Nathan Zaugg
WPF has built-in controls for ink, but is missing much of the API that is available in the TabletPC SDK 1.7. As a result, unless you are doing the most basic and mundane ink you will be required to use this library. One of those basic things that are missing is the ability to open the TextInputPanel in an WPF application.
The TextInputPanel is a tool that ships with certain versions of Vista (in previous versions of windows it was called a Pen Input Panel) that enables handwriting recognition and an on-screen keyboard. It makes text input into a text box easy when the user is operating with a Tablet PC. The problem is that the .NET assembly only supports hwnd-based controls and WPF controls (thankfully) are not hwnd-based.
So while trying to figure this out, and off the clock from the project I was on, I made a class that helped bridge the gap between WPF and the TextInputPanel.
Before you try to use this you will need to do two things.
First you need to download and install the TabletPC SDK 1.7 (if you haven't already) and download my class (Source / Binary)
Second you need to start the Tablet PC Input Panel if you on a regular PC. To do this go to:
Start -> All Programs -> Accessories -> Tablet PC -> Tablet PC Input Panel
To use the class as a binary application, simply add the DLL as a reference. Once that is done you will need to create an instance of the class for each text box you wish to have ink input (shown below). IMPORTANT: THIS SHOULD BE DONE ON THE WINDOW LOAD EVENT! IF YOU PUT THIS CODE IN THE CONSTRUCTOR IT WILL NOT WORK!
<?xml version="1.0" encoding="utf-8"?>
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestApp"
Title="Ink Input App"
Loaded="Window_Loaded">
<Grid>
<TextBox Name="tbTest" Height="30" Width="300" />
</Grid>
protected InkInputPanel pnltbTestInk;
...
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Enable the Ink Input Panel
pnltbTestInk= new InkInputPanel(tbTest, true);
}
The code above will display the TextInputPanel anytime the textbox has focus and will go away when it looses focus. This is the simplest way to use the class. If you are doing something more complex like making the input panel appear when a button is clicked you will have some slightly different code.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Enable the Ink Input Panel
pnltbTestInk = new InkInputPanel(tbTest, false); // False for AutoShow
}
private void InputFieldButton_Click(object sender, RoutedEventArgs e) {
tbTest.Focus();
pnltbTestInk.Show();
}
protected void tbTest_LostFocus(object sender, RoutedEventArgs e) {
pnltbTestInk.Hide();
}
Just make sure that you focus the text box before you call show on the Text Input Panel.
Basically the class I created uses the COM object exposed by the SDK rather than the Microsoft.Ink.dll assembly. The COM object is called "Microsoft PenInputPanel 1.7" and is found at C:\Program Files\Common Files\Microsoft Shared\ink\tiptsf.dll
If you are able to make use of my class or have questions or feedback, please leave a comment!
UPDATE
I received a number of emails indicating that using this code for multiple text boxes didn't work (it threw a COM exception). I have modified the code to fix this issue and have uploaded it. Because of a COM limitation, there is a change in how you use the API. The biggest change is that you only need one instance of the class and you can attach multiple text boxes to this one instance.
Example:
protected InkInputPanel pnltbTest;
...
private void Window_Loaded(object sender, RoutedEventArgs e) {
pnltbTest = new InkInputPanel(tbTest, true);
pnltbTest.AddAttachedTextBox(tbTest1, true);
pnltbTest.AddAttachedTextBox(tbTest2, true);
pnltbTest.AddAttachedTextBox(tbTest3, true);
}
if you are going to hide and show the panel explictly, then you will need to use one of the indexers available on the InkInputPanel class.
Example:
protected InkInputPanel pnltbTestInk;
...
private void Window_Loaded(object sender, RoutedEventArgs e) {
// Enable the Ink Input Panel
// Pass false for AutoShow
pnltbTestInk = new InkInputPanel(tbTest, false);
}
private void InputFieldButton_Click(object sender, RoutedEventArgs e) {
tbTest.Focus();
pnltbTestInk[tbTest].Show();
}
protected void tbTest_LostFocus(object sender, RoutedEventArgs e) {
pnltbTestInk[tbTest].Hide();
}
Links