Tablet PC: Getting started with InkAnalysis – intro to parsing

Last time:

We took a look at asynchronous analysis and handwriting recognition using the new InkAnalysis API (Part of the Windows Vista SDK for RC1). This time, we’ll extend that code to include parsing (layout analysis of ink) results.

Q: What is parsing?

A: Parsing can be broken down into a couple basic categories: 1. Writing/Drawing classification of ink –and- 2. Categorization and relation of ink (Ex: These strokes make up a drawing that enclose those strokes that make up a word)

Example of a drawing containing a word:


Here’s a breakdown of what we’ll do to modify the asynchronous analysis application to incorporate parsing results: (In the ResultsUpdated event handler)

-          Call InkAnalyzer.FindNodesOfType() to obtain a ContextNodeCollection of InkWord objects

-          Call InkAnalyzer.FindNodesOfType() to obtain a ContextNodeCollection of InkDrawing objects

-          Build up a string that outputs the recognition for each of the words and each of the drawings, and display that string to the user

Example Code:


// Basic Ink enabled Windows Forms application with

// handwriting recognition using InkAnalyzer

// Gavin Gear -

// 09/2006


using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Ink; // The managed Tablet PC API

namespace BasicInkApplication


    public partial class BasicInkApplication : Form


        // The InkOverlay that we'll attach to our Form

        private InkOverlay inkOverlay;

        private InkAnalyzer inkAnalyzer;

        public BasicInkApplication()



            // Create an InkOverlay object that's attached to the Form

            this.inkOverlay = new InkOverlay(this);

            // Enable the InkOverlay (default is Enabled == false)

            this.inkOverlay.Enabled = true;

            this.inkOverlay.Stroke += new InkCollectorStrokeEventHandler(inkOverlay_Stroke);

            // Create a new InkAnalyzer

            // - Associate with the InkOverlay's Ink object

            // - Send the Form "this" as the synchronizing object

            this.inkAnalyzer = new InkAnalyzer(this.inkOverlay.Ink, this);

            // Hook up to the InkAnalyzer.ResultsUpdated event

            // in order to be notified when BackgroundAnalyze() finishes

            this.inkAnalyzer.ResultsUpdated += new


            // The InkOverlay needs to be disposed due to unmanaged resources

            // used by the InkOverlay

            this.FormClosing += new FormClosingEventHandler(BasicInkApplication_FormClosing);


        void inkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)


            // We have a new stroke, add it to the InkAnalyzer



        void BasicInkApplication_FormClosing(object sender, FormClosingEventArgs e)



            this.inkAnalyzer.Dispose(); // Free the unmanaged resources


        private void buttonRecognize_Click(object sender, EventArgs e)


            // Kick off asynchronous analysis



        void inkAnalyzer_ResultsUpdated(object sender, ResultsUpdatedEventArgs e)


            // Build up a summary of the parsing structure

            // this is essentially a hierarchical collection

      // of elements called ContextNodes which can represent things

            // like Drawings, Paragraphs, Lines, Words, and even relationships

            // (ContextLinks) like connectors, containers, etc

            // In this case, we'll keep things simple by just showing the user drawings

            // and words

            ContextNodeCollection words =


            ContextNodeCollection drawings =


            string summary = "Words:" + Environment.NewLine;

            foreach (InkWordNode node in words)


                summary += "\t" + node.GetRecognizedString() + Environment.NewLine;


            summary += Environment.NewLine + "Drawings:" + Environment.NewLine;

            foreach (InkDrawingNode node in drawings)


                summary += "\t" + node.GetShapeName() + Environment.NewLine;


            MessageBox.Show(summary, "Parsing Results");





So that’s a basic into to parsing. This is a simple example, but gives you a basic idea of what parsing is, and how to get started with it. We’ll look at parsing in greater detail in subsequent posts.

See Ya,