Welcome to TechNet Blogs Sign in | Join | Help

We’ve made some fairly large changes to the ink object model in Avalon.  It differs now from our COM / WinForms Ink object model in several key ways that I’d like to discuss.  In this post, I’ll talk about our decision to remove the Microsoft.Ink.Ink object.

At a high level, there were two main reasons for the changes

1)  Feedback on our COM / WinForms OM

2) Our Avalon inking OM is pure managed code all the way down and it gave us the opportunity to make sure it fits well into the .NET platform

Our existing COM / WinForms platform is largely unmanaged COM code in InkObj.dll, with a thin managed wrapper in Microsoft.Ink.dll.  This strategy was a good one as it helped us make our OM available to a wide range of languages: VB6, C++/COM, C#, VB.NET, etc.  Avalon on the other hand is almost entirely managed code and is not available from VB6, C++ / COM. 

One of the major differences between the Avalon and COM / WinForms inking platform is that that COM / WinForms exposes an ‘Ink’ object that can be thought of as a DOM or database of Stroke data while there is no ‘Ink’ object in Avalon and Strokes own their own data. 

Let’s start by looking at the legacy inking ink platform.

Each collection of Strokes can be thought of as a recordset of Stroke objects.  Thus, to delete a Stroke from the Ink object, you must call Ink.DeleteStroke(Stroke) instead of simply Ink.Strokes.Remove(Stroke).  If you delete the underlying Stroke data from the Ink object, there can still be outstanding Strokes ‘recordsets’ in memory with a reference to a Stroke object that represents the deleted Stroke data.  Microsoft.Ink.Stroke exposes a ‘Deleted’ property to indicate this situation exists.

Each Stroke was identified by its ID property instead of object equality.  Why?  Because of the DOM / Recordset model.  There can be multiple Stroke objects that represent the same underlying data in the Ink object.  The Ink object assigned unique monotonically increasing integers to each Stroke data and it could ensure that there were no conflicts since it owned the data.

The COM / WinForms inking platform was designed with Win32/COM memory management in mind, while the Avalon ink platform was designed for a purely managed environment.  Instead of an Ink object that is a DOM or database of Stroke data, each Stroke owns its own data and is garbage collected when no longer referenced. 

In Avalon, when a Stroke is no longer referenced by any StrokeCollections, it is simply GC’d.  Note that in this model, Stroke.ID and Stroke.Deleted are not needed (and not possible).

The latest Avalon bits are now available (September CTP).  Tablet Avalon integration is included in the Avalon assemblies for all platforms and largely represents the APIs we will ship with.  There are only a few very minor tweaks we are making right now that will be in the next CTP.

To download, go to http://msdn.microsoft.com/windowsvista/getthebeta/default.aspx and click on the 'Download the WinFX Runtime Components – September CTP' link.


To start off with…


When you collect Strokes in an InkCanvas, the appearance of each Stroke is controlled by two things:

1)      The sampling points of the Stroke (or spine)

2)      The DrawingAttributes of the Stroke

A good way to think of this is that at each sampling point, there is a shape around the point:

Each shape is connected:

If the ink is being collected with a Stylus, and there is pressure information present, we scale the shape at each sampling point based on the pressure:

And then we fill the outer contour and render the stroke.

To control the shape for each sampling point (and ultimately how the Stroke appears), we have a class named DrawingAttributes.  You can find it as properties on two objects:

1)      Stroke.DrawingAttributes – Controls what each Stroke looks like

2)      InkCanvas.DefaultDrawingAttributes – Controls the DrawingAttributes that each new Stroke collected by the InkCanvas will have.  Since DrawingAttributes is a reference type, we clone the InkCanvas.DefaultDrawingAttributes for each new Stroke.  If we didn’t, all Strokes would change appearance when any Stroke.DrawingAttributes instance was altered.

Here are the properties that DrawingAttributes has, and what each controls:

1)      Color – The color of the Stroke.  We use this to create a SolidColorBrush to fill the Stroke with when we render it to the MIL (Avalon Media Integration Layer)

2)      IgnorePressure – A boolean that instructs us to ignore pressure if present.  If this is set to false, we will scale the shape around each sampling point based on the pressure collected at the sampling point.

3)      StylusTip – The shape of the sampling points.  We support Rectangle and Ellipse.

4)      StylusTipTransform – A Matrix that allows you to rotate / shear / scale the shape of the sampling points.

5)      Height – The height of the shape around the sampling points. 

6)      Width – The width of the shape around the sampling points.

7)      FitToCurve – Set this to true if you would like us to apply Bezier smoothing to the Stroke.

8)      IsHighlighter – A boolean that instructs us to treat this Stroke in a special way by applying a transparency that doesn’t blend with other IsHighlighter Strokes of the same color.  In our COM \ WinForms platform, which rendered via GDI \ GDI+, this was controlled by the DrawingAttributes.RasterOperation property, but the MIL does not support GDI raster ops.

Understanding InkCanvas, EditingModes and DrawingAttributes enables you to do a lot with our platform.  In upcoming blogs, I plan on talking about:

1)      The difference between our Avalon and COM \ WinForms object model’s and why we made changes

2)      How to do recognition of your Avalon Strokes

3)      How to do custom rendering of your Avalon Strokes

And more.  Stay tuned…

Going a little deeper into InkCanvas


InkCanvas not only collects and renders ink, but it also allows editing of that ink, as well as any elements hosted in the InkCanvas.  I wanted to talk about two very important properties on InkCanvas that control editing:  InkCanvas.EditingMode and InkCanvas.EditingModeInverted


Both properties are of type InkCanvasEditingMode:


    public enum InkCanvasEditingMode


        None = 0,









InkCanvas.EditingMode controls the editing mode that InkCanvas uses for the tip of the Stylus, InkCanvas.EditingModeInverted controls the editing mode for the back of the Stylus (the eraser).  By default, EditingMode is set to Ink, and EditingModeInverted is set to EraseByStroke.  While our COM \ WinForms InkOverlay and InkPicture had an EditingMode property, they did not have an EditingModeInverted property.  A common question developers had was how to implement back of Stylus erase; we’ve added the EditingModeInverted property to InkCanvas to make this work out of the box.


These properties are settable in XAML, so you can change the defaults as you like:


<Grid xmlns=http://schemas.microsoft.com/winfx/avalon/2005>

 <InkCanvas EditingMode=”InkAndGesture” EditingModeInverted=”EraseByStroke”/>



This is what each InkCanvasEditingMode value does:

  • None: Instructs the InkCanvas to not perform any editing operations.  The InkCanvas will simply behave like a normal element.

  • Ink: Ink is collected for the Stylus or Mouse.  Any time a Stroke is collected (a series of down, optional move and up of the Stylus or Mouse), the InkCanvas.StrokeCollected event will be raised and the Stroke will be added to the InkCanvas’s StrokeCollection (available from the InkCanvas.Strokes property)

  • GestureOnly: Ink is collected, but after each Stroke, it is passed to the gesture recognizer (mshwgst.dll) if present and the Gesture event is raised.  The event args for the Gesture event describe the gesture recognition results.  Once the event fires, the Stroke is removed from the InkCanvas.

  • InkAndGesture: A combination of the above two modes.  The Stroke is first evaluated to see if it is a gesture.  If it is, it is removed from the InkCanvas.  If not, it is left on the InkCanvas.

  • Select: The InkCanvas will select Strokes and or child elements either by tapping on them, or by selecting them via lasso selection.

  • EraseByPoint: Point erase mode.  You can control the size of the eraser via the EraserShape property.

  • EraseByStroke: Stroke erase mode.  When the eraser comes in contact with a Stroke, it is removed.


That’s all for now.  Next time… using InkCanvas.DefaultDrawingAttributes to control how new Strokes look when added to the InkCanvas.

Tablet PC has had platform technologies for COM \ WinForms developers dating back to our first release in 2001.  Over the last few years, the Tablet team has been busy developing a new Tablet client platform for Avalon.  We’ve integrated our code directly into the Avalon assemblies, which makes inking available anywhere Avalon is.  I’m the development lead for the team and I started this blog to talk about what we’ve done in the Tablet Avalon platform, and why.

To start off, if you haven’t already, install the May CTP of Avalon / CLR:

Avalon / Indigo

You can also install the Tablet PC SDK to get support for a stylus on an integrated or external digitizer.  If you don’t install this, you can still ink with your mouse
Tablet PC SDK 1.7

The Avalon / Indigo link also has links to Visual Studio and the WinFX SDK.  Once you have installed those, adding ink to your application is as simple as this:

<Grid xmlns=http://schemas.microsoft.com/winfx/avalon/2005>

You can type that directly into the XAMLPAD tool that is included with the WinFX SDK, drag your mouse (or stylus) across the InkCanvas to get ink!

InkCanvas is similar to the InkPicture control that is part of the Tablet COM \ WinForms platform.

The difference is that InkCanvas can host more than an image; in fact, it can host anything an Avalon Canvas can.