Browse DevX
Sign up for e-mail newsletters from DevX


Introduction to Tablet PC Development : Page 2

Development for Tablet PCs requires a skill set similar to development for regular PCs, plus an understanding of digital Ink collection, management, and analysis. Developing for Tablet PCs is surprisingly easy and powerful at the same time.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Options for Ink Collection and Rendering
There are several options available for collecting Ink. For one, the Tablet PC SDK ships with a number of controls such as the InkEdit control, which you can simply drop on a form. The InkEdit control provides a textbox that works for standard keyboard text entry as well as handwritten Ink entry, which can be stored as Ink or converted to regular text automatically. The InkEdit control is really a specialized version of the RichTextBox control.

Similarly, there also is an InkPicture control, which is a specialized version of the Picture control. As you probably guessed, this control can display a picture, but it allows the user to Ink-annotate over it.

Figure 2. Adding Ink References: Add the Microsoft.Ink namespace (Microsoft Tablet PC API) to your project references.
These controls provide a quick way to add Ink functionality to your applications. However, the user experience provided (especially by the InkEdit control) is not particularly good. These controls should probably be seen as examples more than production controls. For real-life use, a more sophisticated approach is warranted and it is readily available to us in a variety of ways.

The standard approach is the use of InkCollector and InkOverlay classes. These classes can be attached to any regular control, which turns the control into an Ink-enabled control. To see these classes at work, create a new Windows Forms application/project in the .NET language of your choice. Add the Microsoft Tablet PC API assembly to your project's assembly references (see Figure 2). Then, drop a control of your choice onto the form. In my example, I use a Panel control with white background to simulate a white writing area. Then, instantiate an InkCollector object and attach it to the panel. To do so, you first have to create a field on the form that can hold the instance. In C#, this can be done like this:

public class Form1 : System.Windows.Forms.Form { private Microsoft.Ink.InkCollector collector; // default form code continues here...

The Visual Basic version is similar:

Public Class Form1 Inherits System.Windows.Forms.Form Private collector As Microsoft.Ink.InkCollector ' default form code continues here...

Then, in the constructor of the form, instantiate the InkCollector object and assign it to the control you want to Ink-enable. Any control will do, such as a panel, a textbox, or even the entire window. In my example, I attach the collector to the panel control. Here's the C# version:

public Form1() { InitializeComponent(); this.collector = new Microsoft.Ink.InkCollector(this.panel1); this.collector.Enabled = true; }

Figure 3. Simple Ink Application: The figure shows a simple Ink-enabled application using a Panel and an InkCollector class.
Once again, the Visual Basic .NET version is very similar:

Public Sub New() MyBase.New() InitializeComponent() Me.collector = _ New Microsoft.Ink.InkCollector(Me.Panel1) Me.collector.Enabled = True End Sub

That's it! Your first Ink-enabled Windows application is complete. Figure 3 shows the result.

Many Ink-enabled applications use this as the basis for all Ink features. The InkCollector class provides a great deal of functionality. It collects Ink, stores it in an Ink object, and also renders Ink and makes sure it is always refreshed properly when it needs to be. Note that the rendering of the Ink is of very high quality, as the rendering engine applies techniques such as Bezier smoothing (making the line less jagged and more like a smooth stroke) and anti aliasing.

Using Ink collectors, it's also easy to change Ink attributes, such as Ink color. The following C# code snippet applies red as the default color for all future Ink input:

collector.DefaultDrawingAttributes.Color = Color.Red;

Remove the semi-colon at the end, and you have Visual Basic .NET's version of the same operation.

The Ink information collected in the Ink object attached to the InkCollector also provides access to a very powerful feature: Ink recognition. The following C# code snippet shows how to turn handwriting into regular text:

MessageBox.Show( this.collector.Ink.Strokes.ToString());

Here's the Visual Basic .NET version:


It is really as easy as that! Of course, if you wanted to, you could get a lot more information from the recognizer, such as alternate options, confidence levels, and much more. You might also want to recognize shapes or spatial meaning in Ink (such as annotations). Or perhaps you want to perform recognition asynchronously on a background thread.

The Ink object is where all the Ink information is stored. Ink is broken into individual strokes, and each stroke is made up of points. Using this object, developers can manage Ink in memory. For instance, Ink can easily be saved to a file (C#):

byte[] inkBytes = this.collector.Ink.Save(); FileStream strm = File.Open(@"c:\test.isf", FileMode.Create); strm.Write(inkBytes,0,inkBytes.Length); strm.Close();

Here is the Visual Basic version:

Dim inkBytes As Byte() = Me.collector.Ink.Save() Dim strm As FileStream = _ File.Open("c:\test.isf", FileMode.Create) strm.Write(inkBytes, 0, inkBytes.Length) strm.Close()

The key line in this code snippet is the one that calls Ink.Save(). Save() automatically serializes all Ink into Ink-serialized format and returns it as a byte array. You can then choose to store that byte array wherever you want, such as in a file (as in this example) or in a database.

Loading Ink is a very similar affair, except for one little gotcha: Ink objects have a Load() method. That method can reliably load Ink only into empty Ink objects. It is therefore important to create a new Ink object and call its Load() method, rather than calling the same method on an existing object. Here is a working example in C#:

FileStream strm = File.OpenRead(@"C:\test.isf"); FileInfo info = new FileInfo(@"C:\test.isf"); byte[] inkBytes = new byte[info.Length]; strm.Read(inkBytes,0,(int)info.Length); strm.Close(); Ink newInk = new Ink(); newInk.Load(inkBytes); this.collector.Enabled = false; this.collector.Ink = newInk; this.collector.Enabled = true;

And the Visual Basic .NET version:

Dim strm As FileStream = _ File.OpenRead("C:\test.isf") Dim info As FileInfo = New FileInfo("C:\test.isf") Dim inkBytes(info.Length) As Byte strm.Read(inkBytes, 0, info.Length) strm.Close() Dim newInk As New Microsoft.Ink.Ink newInk.Load(inkBytes) Me.collector.Enabled = False Me. collector.Ink = newInk Me.collector.Enabled = True

Note that it is important to disable the collector/overlay before attaching a new Ink object; otherwise, an error is thrown.

For more information on storing Ink, see the "Ink-Enabled Database Applications" article in this issue.

Thanks for your registration, follow us on our social networks to keep up-to-date