Export (0) Print
Expand All
Add Support for Digital Ink to Your Windows Applications
Power to the Pen: The Pen is Mightier with GDI+ and the Tablet PC Real-Time Stylus
Expand Minimize

Using CustomStrokes, Renderer, and ExtendedProperties to Manipulate Ink

 

By Leszynski Group, Inc.

July 2003

Applies to:
   Microsoft® Windows® XP Tablet PC Edition
   Microsoft® Visual Studio® .NET

Summary: This paper discusses using ink controls for the Microsoft Windows XP Tablet PC Edition operating system. We will discuss the CustomStrokes collection, which is a stroke property of the Ink object. We will also cover selective rendering using the Renderer object, as well as how to show and hide ink using ExtendedProperties. This discussion covers practical application, implementation, and potential benefits.

This document is intended for developers creating ink-based applications. Code samples were created using Microsoft Visual Basic .NET and C#, and the Microsoft Tablet PC Platform SDK 1.0. (17 printed pages)

Contents

Introduction
Using the CustomStrokes Collection to Logically Group Strokes
What are CustomStrokes?
Collections to Consider
Creating Collections
Examples: Tracking User Strokes
Example: Storing Recognition Results
Selective Rendering Using the Renderer Object
What is the Renderer?
Example: Using Selective Rendering to Redraw Ink
Showing and Hiding Ink Based on ExtendedProperties
Example: Using ExtendedProperties to Store Timestamps
Conclusion

Introduction

A stroke is a data set based on a single pen down/pen move/pen up sequence. Stroke input goes into the ink collector and is stored and manipulated using the Ink object. In addition to offering common methods and properties to manage strokes, the Ink object allows you to create collections of stroke objects.

CustomStrokes is a built-in collection used to group similar or different strokes. Once you have grouped strokes, you can use the Renderer object to selectively render ink from the collection, or perform text recognition against the stroke collection instead of on a stroke-by-stroke basis. You can also show or hide the collection using ExtendedProperties of the ExtendedProperty object.

Using the CustomStrokes Collection to Logically Group Strokes

Tracking individual strokes by their ID or by extended attributes can be tedious and requires more code than tracking strokes in collections. In addition to making it easier for developers to group together similar or different strokes, collections offer functionality that helps condense and reuse code.

Creating a CustomStrokes collection is easy and, once created, it can be used, ignored, or persisted. CustomStrokes helps you better use the Ink object to:

  • Track user(s) based on color, username, or device
  • Group different ink colors (purple and red)
  • Group strokes by screen region
  • Copy/paste by stroke collection

What are CustomStrokes?

The CustomStrokes collection is a property of the Ink object that indexes Strokes collections by assigning each a unique name. Since this unique name is the only difference between a Strokes collection and a CustomStrokes collection, you can use custom collections the same way you would use a Strokes collection. CustomStrokes simply extends the grouping functionality of existing Strokes collections.

You can categorize a stroke collection by any string name as long as you can persist the name. CustomStrokes collections are simply references to ink data, and are not the actual data itself.

Collections to Consider

You will generally use CustomStrokes to store strokes with similar meaning or context. Examples of strokes that you may want to group include:

  • All the strokes drawn by the same cursor, or pen
  • The strokes in an Ink object that correspond to a word or paragraph
  • All the strokes that intersect a given region
  • All strokes written in a specified time frame

You can also store strokes that have differing qualities. For example, you may want to identify individual users or groups of users based on color metadata. You can store all green and blue ink in one collection to represent Team1 and all red and yellow ink in another collection for Team2.

In addition, you can use drawing attributes as grouping criteria, you can create your own stroke properties (see the following ExtendedProperties discussion), or you can base the collection on any environment variable, such as:

  • Domain
  • User name
  • Directory
  • Time stamp
  • Location
  • Width

Environment variables, such as Pressure or Angle, that a Tablet PC is capable of transmitting are hardware specific. Check with the hardware documentation or the manufacturer for a list of available variables.

Creating Collections

Creating and adding to a CustomStrokes collection is as easy. You simply group strokes as they are created. Use the Stroke event to identify strokes that should go into the collection.

There are two ways to populate a custom stroke collection:

  • Add a Strokes collection to a new custom stroke collection:
//C#
myInkCollector.Ink.CustomStrokes.Add(theName, theNamedStrokes);

'VB
myInkCollector.Ink.CustomStrokes.Add(theName, theNamedStrokes)

  • Add a Stroke to an already existing custom stroke collection:
//C#
myInkCollector.Ink.CustomStrokes[theName].Add(theStroke);

'VB
myInkCollector.Ink.CustomStrokes(theName).Add(theStroke)

Note that a CustomStrokes collection is limited to a single Ink object. To store information across multiple Ink objects, use ExtendedProperties as discussed later in this article.

A CustomStrokes collection can be persisted by using the Save method of its Ink object.

Examples: Tracking User Strokes

The next two examples utilize the CustomStrokes collection.

Bob is presenting a new user interface at the team meeting. Each attendee will use a Tablet PC to view the presentation. The attendees can write suggestions on their Tablet PCs and have their writing appear on the overhead screen as well. Bob wants the ability to single out writing on a per-user basis so that he can show only suggestions that have received approval from the team. This is made possible by laying an InkCollector object over the presentation window and using the CustomStrokes collection to group ink strokes per user.

Let's assume there are only two attendees and they have agreed to use red and black as their respective ink colors. Bob can track the different colored strokes they draw with ink by placing each color in a different CustomStrokes collection.

Bob stores each stroke in the CustomStrokes collection when the InkCollector fires the Stroke event upon completion of a stroke. First, he instantiates an InkCollector object to contain the strokes:

//C#
this.myInkCollector = new InkCollector(this.Handle);
this.myInkCollector.Enabled = true;

'VB
Private WithEvents myInkCollector As Microsoft.Ink.InkCollector
myInkCollector = New Microsoft.Ink.InkCollector(Me.Handle)
myInkCollector.Enabled = True

Because the CustomStrokes collection is indexed by name, Bob sets a String variable as the name of the default color:

//C#
private string NameCollection = "black";

'VB
Private NameCollection As String = "black"

Finally, Bob instantiates the two CustomStrokes collections by creating locally-scoped, empty Strokes collections, then assigns them to their respective CustomStrokes indices:

//C#
private Strokes tmpBlack;
private Strokes tmpRed;

tmpBlack = myInkCollector.Ink.CreateStrokes();
tmpRed = myInkCollector.Ink.CreateStrokes();
myInkCollector.Ink.CustomStrokes.Add("black", tmpBlack);
myInkCollector.Ink.CustomStrokes.Add("red", tmpRed);

'VB
Dim tmpBlack As Microsoft.Ink.Strokes
Dim tmpRed As Microsoft.Ink.Strokes

tmpBlack = myInkCollector.Ink.CreateStrokes
tmpRed = myInkCollector.Ink.CreateStrokes
myInkCollector.Ink.CustomStrokes.Add("black", tmpBlack)
myInkCollector.Ink.CustomStrokes.Add("red", tmpRed)

The object variables will go out of scope, but the "black" and "red" CustomStrokes collections remain. To use them, Bob executes the following code when the user changes the ink color to red:

//C#
myInkCollector.DefaultDrawingAttributes.Color = System.Drawing.Color.Red;
NameCollection = "red";
//And likewise for black
myInkCollector.DefaultDrawingAttributes.Color = System.Drawing.Color.Black;
NameCollection = "black";

'VB
myInkCollector.DefaultDrawingAttributes.Color = Drawing.Color.Red
NameCollection = "red"
' And likewise for black
myInkCollector.DefaultDrawingAttributes.Color = Drawing.Color.Black
NameCollection = "black"

Bob adds a single stroke to the CustomStrokes collection each time a Stroke is completed on the InkCollector. He does this by referencing the Strokes by index and by using the Add method:

//C#
protected void myInkCollector_Stroke(object sender, 
Microsoft.Ink.InkCollectorStrokeEventArgs e)
{
  myInkCollector.Ink.CustomStrokes[NameCollection].Add(e.Stroke);
}

'VB
Private Sub myInkCollector_Stroke(ByVal sender As Object, 
ByVal e As InkCollectorStrokeEventArgs) Handles myInkCollector.Stroke

    myInkCollector.Ink.CustomStrokes(NameCollection).Add(e.Stroke)
End Sub

Now myInkCollector.Ink.CustomStrokes("black") contains all of the black-colored ink strokes, and myInkCollector.Ink.CustomStrokes("red") contains all of the red-colored ink strokes.

To empty the collection, Bob uses the ClearAll method. To actually delete the strokes from the InkCollector he uses the following code:

//C#
myInkCollector.Ink.DeleteStrokes(myInkCollector.Ink.CustomStrokes
[NameCollection]);

'VB
myInkCollector.Ink.DeleteStrokes(myInkCollector.Ink.CustomStrokes
(NameCollection))

A second example using the CustomStrokes collection:

Tina wants to offer a timed test to her long-distance learning students. For the first hour the answers are worth full credit. All answers after that are worth only half credit. Tina can send the tests by e-mail and her students can take the test on Tablet PCs. Because she can't be physically present to time the students, she needs a way to differentiate when answers are given. She would like to display full-credit answers in blue and half-credit answers in red to more easily assess the test score.

Utilizing the CustomStrokes collection allows Tina to group ink strokes by their time stamps. She can use this collection to change the color of ink strokes for answers given during the first hour. She decides to create two collections that are named BeforeTimesUp and AfterTimesUp.

As in the previous example with Bob, Tina first instantiates the variables:

//C#
Microsoft.Ink.InkCollector myInkCollector = new 
Microsoft.Ink.InkCollector(this.Handle);
myInkCollector.Enabled = true;

'VB
Private WithEvents myInkCollector As Microsoft.Ink.InkCollector
    myInkCollector = New Microsoft.Ink.InkCollector(Me.Handle)
    myInkCollector.Enabled = True

Next, the test period timer is set for one hour and started:

//C#
private System.Timers.Timer TimerForTest = new System.Timers.Timer(3600000);
TimerForTest.Enabled = true;

'VB
Private WithEvents TimerForTest As New Timers.Timer(3600000)
TimerForTest.Enabled = True

In the timer's Elapsed event, Tina creates the custom stroke collections and changes the ink's drawing color:

//C#
myInkCollector.Ink.CustomStrokes.Add("BeforeTimesUp", _
myInkCollector.Ink.Strokes);
TimerForTest.Enabled = false;
myInkCollector.DefaultDrawingAttributes.Color = Color.Blue;
MessageBox.Show("Time's up!");
Strokes tmpStrokes = myInkCollector.Ink.CreateStrokes();
myInkCollector.Ink.CustomStrokes.Add("AfterTimesUp", tmpStrokes);

'VB
MyInkCollector.Ink.CustomStrokes.Add("BeforeTimesUp", _
MyInkCollector.Ink.Strokes)
TimerForTest.Enabled = False
MyInkCollector.DefaultDrawingAttributes.Color = Color.Blue
MsgBox("Time's Up!")
Dim tmpStrokes As Strokes
tmpStrokes = MyInkCollector.Ink.CreateStrokes
MyInkCollector.Ink.CustomStrokes.Add("AfterTimesUp", tmpStrokes)

Finally, in the InkCollector's Stroke event, she handles each stroke drawn after the hour elapses:

//C#
if (!TimerForTest.Enabled)
{
    myInkCollector.Ink.CustomStrokes["AfterTimesUp"].Add(e.Stroke);
}

'VB
If Not TimerForTest.Enabled Then
    myInkCollector.Ink.CustomStrokes("AfterTimesUp").Add(e.Stroke)
End If

Example: Storing Recognition Results

The results of recognizing handwritten ink are returned in a RecognitionResult object. System performance may suffer if recognition results are automatically assigned to every collection of strokes. Therefore, the results are not attached to a collection of strokes by default; you must call the SetResultOnStrokes method to assign recognition results to a collection of strokes.

Once you assign recognition results to a collection of strokes, you can then store the strokes in a CustomStrokes collection. These custom strokes, as well as the RecognitionResult, can be persisted and retrieved for later use.

In the previous timed-test example, Tina could allow text recognition to occur on the written answers and then check the results against a database of correct answers. She does this by using the RecognitionResult object and its methods to assign results to a collection of strokes. Then, she again uses the CustomStrokes collection to automate scoring based on the correct answers.

First, Tina creates two new collections called FullCreditCorrectAnswers and HalfCreditCorrectAnswers. All correct answers from the BeforeTimesUp collection will populate the FullCreditCorrectAnswers collection, while all correct answers from the AfterTimesUp collection will populate the HalfCreditCorrectAnswers collection. Then, Tina initializes the recognizer's strokes and assigns them to the context:

//C#
InkCollector myInkCollector;
Strokes theStrokes;
RecognizerContext theRecoContext;
RecognitionResult theRecoResult;

theStrokes = myInkCollector.Ink.Strokes;
theRecoContext = new RecognizerContext();
theRecoContext.Strokes = theStrokes;
 
'VB
Dim MyInkCollector As InkCollector
Dim TheStrokes As Strokes
Dim TheRecoContext As RecognizerContext
Dim TheRecoResult As RecognizerContext

TheStrokes = MyInkCollector.Ink.Strokes
TheRecoContext = New RecognizerContext()
TheRecoContext.Strokes = TheStrokes

On the RecognitionWithAlternates event, Tina saves the RecognitionResult data, copies it to strokes, and then puts the strokes in a custom strokes collection. Note how Tina uses the ToString function in the Strokes collection to isolate what is recognized as text:

//C#
theRecoResult = e.Result;
theRecoResult.SetResultOnStrokes();
theInkCollector.Ink.CustomStrokes.Add(theRecoResult.ToString, 
theRecoContext.Strokes);

'VB
TheRecoResult = e.Result
TheRecoResult.SetResultOnStrokes()
MyInkCollector.Ink.CustomStrokes.Add(TheRecoResult.ToString, _
TheRecoContext.Strokes)

Selective Rendering Using the Renderer Object

The Renderer object is useful for displaying ink in different colors or widths. You can also use Renderer to anti-alias, or smooth strokes.

What is the Renderer?

You can use Renderer to change the attributes, such as color, of existing ink strokes, or you can use it to render only ink that appears on a specified area of a page. You can also take drawn strokes and place them in another context. For example, you can take a written signature from an InkPicture control and place it in a document.

The Renderer contains two important functions:

  • InkSpaceToPixel. This function takes a point from "ink space" coordinates, and converts it to a point on the actual form (or user control) canvas. This conversion is necessary because the ink space coordinates are much finer than screen coordinates.
  • DrawStroke. This function takes a stroke from the Ink object and draws it on a form or user control. An example where this might be useful would be the Print Preview feature where the ink must be redrawn.

Example: Using Selective Rendering to Redraw Ink

This example shows how to use the Renderer object to turn to blue any ink on the left-hand side of the form.

In C#

protected void myInkCollector_Stroke(object sender, 
Microsoft.Ink.InkCollectorStrokeEventArgs e)
{
    Point pt;
    for (int i = 0; i < e.Stroke.BezierPoints.Length - 1; i++)
    {
        pt = e.Stroke.BezierPoints[i];
        myInkCollector.Renderer.InkSpaceToPixel
(this.CreateGraphics(),ref pt);
        if (pt.X > this.Width /2) return;
    }

    e.Stroke.DrawingAttributes.Color = Color.Blue;
    Rectangle Rect = 
e.Stroke.GetBoundingBox(Microsoft.Ink.BoundingBoxMode.Default);
    Point pointTopLeft = new Point(Rect.Left, Rect.Top);
    Point pointBottomRight = new Point(Rect.Left, Rect.Top);
    pointBottomRight.Offset(Rect.Width, Rect.Height);
    myInkCollector.Renderer.InkSpaceToPixel(this.CreateGraphics(), 
ref pointTopLeft);
    myInkCollector.Renderer.InkSpaceToPixel(this.CreateGraphics(), 
ref pointBottomRight);

    Rectangle convertedRect = new Rectangle(pointTopLeft.X, 
pointTopLeft.Y, pointBottomRight.X - pointTopLeft.X, 
pointBottomRight.Y - pointTopLeft.Y);
    myInkCollector.Renderer.Draw(this.CreateGraphics(), e.Stroke);

    //invalidate the rectangle to get rid of the old black stroke 
that was just drawn
    this.Invalidate(convertedRect);
    this.Update();
}

In VB .NET

Private WithEvents myInkCollector As Microsoft.Ink.InkCollector

Private Sub myInkCollector_Stroke(ByVal sender As Object, 
ByVal e As Microsoft.Ink.InkCollectorStrokeEventArgs) 
Handles myInkCollector.Stroke

    ' Fired after an ink stroke is drawn
    Dim i As Integer
    Dim pt As Point
    For i = 0 To e.Stroke.BezierPoints.Length - 1
        pt = e.Stroke.BezierPoints(i)
        myInkCollector.Renderer.InkSpaceToPixel(Me.CreateGraphics, pt)
        ' Check to see where the stroke has been drawn
        If pt.X > Me.Width / 2 Then Exit Sub
    Next

    e.Stroke.DrawingAttributes.Color = Drawing.Color.Blue
    Dim Rect As Rectangle = 
e.Stroke.GetBoundingBox(Microsoft.Ink.BoundingBoxMode.Default)
    Dim pointTopLeft As New Point(Rect.Left, Rect.Top)
    Dim pointBottomRight As New Point(Rect.Left, Rect.Top)

    pointBottomRight.Offset(Rect.Width, Rect.Height)
    myInkCollector.Renderer.InkSpaceToPixel(Me.CreateGraphics, 
pointTopLeft)
    myInkCollector.Renderer.InkSpaceToPixel(Me.CreateGraphics, 
pointBottomRight)

    Dim ConvertedRect As New Rectangle(pointTopLeft.X, 
pointTopLeft.Y, pointBottomRight.X - pointTopLeft.X, 
pointBottomRight.Y - pointTopLeft.Y)

    myInkCollector.Renderer.Draw(Me.CreateGraphics, e.Stroke)
    ' Invalidate the rectangle to get rid of the old black 
stroke that was just drawn
    Me.Invalidate(ConvertedRect)
    Me.Update()
End Sub

Showing and Hiding Ink Based on ExtendedProperties

Because ExtendedProperties offers a method for capturing data that is otherwise not tracked, you can use them to more easily manipulate ink. For example, you can identify strokes by the creator and hide or show the remarks in a document edited by several people. Or you can track strokes by time stamps, as in the previous timed exam example. ExtendedProperties can also help track previous ink color for cases where the user highlights writing, but the original ink color may need to be rendered.

What are ExtendedProperties?

The ExtendedProperties property is simply a collection of ExtendedProperty objects. The ExtendedProperty object allows you to add your own data to a variety of objects within the Tablet PC object model. The name of the extended property is expressed as a globally unique identifier (GUID).

ExtendedProperties are essentially application-specific data that are stored along with a stroke. The properties used can be in whatever manner the application requires. ExtendedProperties offers a mechanism for tracking data that is otherwise not included in the properties listed for the ExtendedProperty object.

Because they contain information about a single stroke, ExtendedProperties must be created for each stroke, unlike the CustomStrokes collection. However, ExtendedProperties can cross multiple ink objects while CustomStrokes cannot. This can make using ExtendedProperties more powerful. For example, if you need to count strokes across several Ink objects, you can use ExtendedProperties and increment it. If you serialize a stroke, all of its properties, including the ExtendedProperties, are persisted.

Example: Using ExtendedProperties to Store Timestamps

The following example uses the Stroke event handler to store a timestamp as a custom property of each stroke. This example could be applied to the previous timed exam example, where the strokes drawn after a certain time could be hidden.

Each stroke drawn on the form will have a timestamp stored in its ExtendedProperties, and when one of two form buttons is pressed, either the strokes formed at an even or odd time interval will be hidden and the rest will be turned to blue.

In C#

using Microsoft.Ink;
    private Guid TimestampGUID = new 
Guid("{00000000-0000-0000-0000-000000000002}");
    private int mTime = DateTime.Now.Second;
    private InkCollector myInkCollector;

private void myInkCollector_Stroke(object sender, 
Microsoft.Ink.InkCollectorStrokeEventArgs e)
{
    e.Stroke.ExtendedProperties.Add(TimestampGUID, 
DateTime.Now.Second - mTime);
}

private void cmdEven_Click(object sender, System.EventArgs e)
{
    foreach(Stroke myStroke in myInkCollector.Ink.Strokes)
    {
    if (myStroke.ExtendedProperties.DoesPropertyExist(TimestampGUID))
        TurnStrokeColor(myStroke, System.Drawing.Color.Blue);
    else
        TurnStrokeColor(myStroke, System.Drawing.Color.Black);
    }
    this.Update();
}

private void TurnStrokeColor(Stroke myStroke, System.Drawing.Color c)
{
    myStroke.DrawingAttributes.Color = c;
    Rectangle Rect = 
myStroke.GetBoundingBox(Microsoft.Ink.BoundingBoxMode.Default);
    Point pointTopLeft = new Point(Rect.Left, Rect.Top);
    Point pointBottomRight = new Point(Rect.Left, Rect.Top);
    pointBottomRight.Offset(Rect.Left, Rect.Top);
    myInkCollector.Renderer.InkSpaceToPixel(this.CreateGraphics(), 
ref pointTopLeft);
    myInkCollector.Renderer.InkSpaceToPixel(this.CreateGraphics(), 
ref pointBottomRight);

    Rectangle ConvertedRect = new Rectangle(pointTopLeft.X, 
pointTopLeft.Y, pointBottomRight.X - pointTopLeft.X, 
pointBottomRight.Y - pointTopLeft.Y);
    myInkCollector.Renderer.Draw(this.CreateGraphics(), myStroke);

    this.Invalidate(ConvertedRect);

}

private void cmdOdd_Click(object sender, System.EventArgs e)
{
    foreach(Stroke myStroke in myInkCollector.Ink.Strokes)
    {
        if 
(myStroke.ExtendedProperties.DoesPropertyExist(TimestampGUID))
            if (((int)myStroke.ExtendedProperties[TimestampGUID].Data % 
2) ==1)
            TurnStrokeColor(myStroke, this.BackColor);
        else
            TurnStrokeColor(myStroke, Color.Blue);
    }
    this.Update();
}

In VB .NET

Imports Microsoft.Ink

Private TimestampGUID As New 
Guid("{00000000-0000-0000-0000-000000000002}")
Private mTime As Integer = Now.Second
Private WithEvents myInkCollector As InkCollector

Private Sub myInkCollector_Stroke(ByVal sender As Object, 
ByVal e As Microsoft.Ink.InkCollectorStrokeEventArgs) 
Handles myInkCollector.Stroke

    e.Stroke.ExtendedProperties.Add(TimestampGUID, Now.Second - mTime)
End Sub

Private Sub cmdEven_Click(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles cmdEven.Click

    Dim myStroke As Stroke
    For Each myStroke In myInkCollector.Ink.Strokes
        If myStroke.ExtendedProperties.DoesPropertyExist(TimestampGUID) 
Then
            If myStroke.ExtendedProperties(TimestampGUID).Data > 3600 
Then
                TurnStrokeColor(s, Drawing.Color.Blue)
            Else
                TurnStrokeColor(s, Me.BackColor)
            End If
        End If
    Next
    Me.Update()
End Sub

Private Sub TurnStrokeColor(ByVal myStroke As Stroke, ByVal c As Color)
    myStroke.DrawingAttributes.Color = c
    Dim Rect As Rectangle = 
myStroke.GetBoundingBox(Microsoft.Ink.BoundingBoxMode.Default)
    Dim pointTopLeft As New Point(Rect.Left, Rect.Top)
    Dim pointBottomRight As New Point(Rect.Left, Rect.Top)

    pointBottomRight.Offset(Rect.Width, Rect.Height)
    myInkCollector.Renderer.InkSpaceToPixel(Me.CreateGraphics, 
pointTopLeft)
    myInkCollector.Renderer.InkSpaceToPixel(Me.CreateGraphics, 
pointBottomRight)

    Dim ConvertedRect As New Rectangle(pointTopLeft.X, 
pointTopLeft.Y, pointBottomRight.X - pointTopLeft.X, 
pointBottomRight.Y - pointTopLeft.Y)

    myInkCollector.Renderer.Draw(Me.CreateGraphics, s)
    Me.Invalidate(ConvertedRect)

End Sub

Private Sub cmdOdd_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdOdd.Click
    Dim myStroke As Stroke
    For Each myStroke In myInkCollector.Ink.Strokes
        If myStroke.ExtendedProperties.DoesPropertyExist(TimestampGUID) 
Then
            If myStroke.ExtendedProperties(TimestampGUID).Data Mod 2 
Then
                TurnStrokeColor(myStroke, Me.BackColor)
            Else
                TurnStrokeColor(myStroke, Drawing.Color.Blue)
            End If
        End If
    Next
    Me.Update()
End Sub

Conclusion

The main benefit of CustomStrokes, Renderer, and ExtendedProperties is the ability of each to help you manipulate ink. The CustomStrokes collection provides you with more flexibility in how you group Strokes while presenting a usage model that is like Strokes collections. Renderer allows you to display ink differently, to anti-alias strokes, and to draw strokes in a context different from that of the original strokes. ExtendedProperties offers a means for allowing you to define and capture data items that are otherwise not inherently tracked.

About Leszynski Group Inc.

Leszynski Group develops line-of-business applications and retail software built around databases, XML, and digital ink using Microsoft .NET architecture. Leszynski Group was the first solution provider to develop and ship ink-based applications for the Tablet PC.

Show:
© 2014 Microsoft