Collecting Ink

The Windows Presentation Foundation platform collects digital ink as a core part of its functionality. This topic discusses methods for collection of ink in Windows Presentation Foundation (WPF).

Prerequisites

To use the following examples, you must first install Microsoft Visual Studio 2005 and the Windows SDK. You should also understand how to write applications for the WPF. For more information about getting started with WPF, see Getting Started with Windows Presentation Foundation.

Using the InkCanvas Element

The InkCanvas element provides the easiest way to collect ink in WPF. The InkCanvas element is similar to the InkOverlay object from the Tablet PC SDK 1.7 and earlier versions.

Use an InkCanvas element to receive and display ink input. You commonly input ink through the use of a stylus, which interacts with a digitizer to produce ink strokes. In addition, a mouse can be used in place of a stylus. The created strokes are represented as Stroke objects, and they can be manipulated both programmatically and by user input. The InkCanvas enables users to select, modify, or delete an existing Stroke.

By using XAML, you can set up ink collection as easily as adding an InkCanvas element to your tree. The following example adds an InkCanvas to a default WPF project created in Microsoft Visual Studio 2005.

<Grid>
  <InkCanvas/>
</Grid>

The InkCanvas element can also contain child elements, making it possible to add ink annotation capabilities to almost any type of XAML element. For example, to add inking capabilities to a text element, simply make it a child of an InkCanvas.

<InkCanvas>
  <TextBlock>Show text here.</TextBlock>
</InkCanvas>

Adding support for marking up an image with ink is just as easy.

<InkCanvas>
  <Image Source="myPicture.jpg"/>
</InkCanvas>

InkCollection Modes

The InkCanvas provides support for various input modes through its EditingMode property.

Manipulating Ink

The InkCanvas provides support for many ink editing operations. For example, InkCanvas supports back-of-pen erase with no additional code needed to add the functionality to the element.

Selection

Setting selection mode is as simple as setting the InkCanvasEditingMode property to Select. The following code sets the editing mode based on the value of a CheckBox:

' Set the selection mode based on a checkbox 
If CBool(cbSelectionMode.IsChecked) Then
    theInkCanvas.EditingMode = InkCanvasEditingMode.Select 
Else
    theInkCanvas.EditingMode = InkCanvasEditingMode.Ink
End If
// Set the selection mode based on a checkbox 
if ((bool)cbSelectionMode.IsChecked)
{
    theInkCanvas.EditingMode = InkCanvasEditingMode.Select;
}
else
{
    theInkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}

DrawingAttributes

Use the DrawingAttributes property to change the appearance of ink strokes. For instance, the Color member of DrawingAttributes sets the color of the rendered Stroke. The following example changes the color of the selected strokes to red.

' Get the selected strokes from the InkCanvas 
Dim selection As StrokeCollection = theInkCanvas.GetSelectedStrokes()

' Check to see if any strokes are actually selected 
If selection.Count > 0 Then 
    ' Change the color of each stroke in the collection to red 
    Dim stroke As System.Windows.Ink.Stroke
    For Each stroke In  selection
        stroke.DrawingAttributes.Color = System.Windows.Media.Colors.Red
    Next stroke
End If
// Get the selected strokes from the InkCanvas
StrokeCollection selection = theInkCanvas.GetSelectedStrokes();

// Check to see if any strokes are actually selected 
if (selection.Count > 0)
{
    // Change the color of each stroke in the collection to red 
    foreach (System.Windows.Ink.Stroke stroke in selection)
    {
        stroke.DrawingAttributes.Color = System.Windows.Media.Colors.Red;
    }
}

DefaultDrawingAttributes

The DefaultDrawingAttributes property provides access to properties such as the height, width, and color of the strokes to be created in an InkCanvas. Once you change the DefaultDrawingAttributes, all future strokes entered into the InkCanvas are rendered with the new property values.

In addition to modifying the DefaultDrawingAttributes in the code-behind file, you can use XAML syntax for specifying DefaultDrawingAttributes properties. The following XAML code demonstrates how to set the Color property. To use this code, create a new WPF project called "HelloInkCanvas" in Visual Studio 2005. Replace the code in the Window1.xaml file with the following code.

<Window x:Class="Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Ink="clr-namespace:System.Windows.Ink;assembly=PresentationCore"
    Title="Hello, InkCanvas!" Height="300" Width="300"
    >
  <Grid>
    <InkCanvas Name="inkCanvas1" Background="Ivory">
      <InkCanvas.DefaultDrawingAttributes>
        <Ink:DrawingAttributes xmlns:ink="system-windows-ink" Color="Red" Width="5" />
      </InkCanvas.DefaultDrawingAttributes>

    </InkCanvas>

    <!-- This stack panel of buttons is a sibling to InkCanvas (not a child) but overlapping it, 
         higher in z-order, so that ink is collected and rendered behind -->
    <StackPanel Name="buttonBar" VerticalAlignment="Top" Height="26" Orientation="Horizontal" Margin="5">
      <Button Click="Ink">Ink</Button>
      <Button Click="Highlight">Highlight</Button>
      <Button Click="EraseStroke">EraseStroke</Button>
      <Button Click="Select">Select</Button>
    </StackPanel>
  </Grid>
</Window>
<Window x:Class="HelloInkCanvas.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Ink="clr-namespace:System.Windows.Ink;assembly=PresentationCore"
    Title="Hello, InkCanvas!" Height="300" Width="300"
    >
  <Grid>
    <InkCanvas Name="inkCanvas1" Background="Ivory">
      <InkCanvas.DefaultDrawingAttributes>
        <Ink:DrawingAttributes xmlns:ink="system-windows-ink" Color="Red" Width="5" />
      </InkCanvas.DefaultDrawingAttributes>
    </InkCanvas>

    <!-- This stack panel of buttons is a sibling to InkCanvas (not a child) but overlapping it, 
         higher in z-order, so that ink is collected and rendered behind -->
    <StackPanel Name="buttonBar" VerticalAlignment="Top" Height="26" Orientation="Horizontal" Margin="5">
      <Button Click="Ink">Ink</Button>
      <Button Click="Highlight">Highlight</Button>
      <Button Click="EraseStroke">EraseStroke</Button>
      <Button Click="Select">Select</Button>
    </StackPanel>
  </Grid>
</Window>

Next, add the following button event handlers to the code behind file, inside the Window1 class.

' Set the EditingMode to ink input. 
Private Sub Ink(ByVal sender As Object, ByVal e As RoutedEventArgs) 

    inkCanvas1.EditingMode = InkCanvasEditingMode.Ink

    ' Set the DefaultDrawingAttributes for a red pen.
    inkCanvas1.DefaultDrawingAttributes.Color = Colors.Red
    inkCanvas1.DefaultDrawingAttributes.IsHighlighter = False
    inkCanvas1.DefaultDrawingAttributes.Height = 2

End Sub 'Ink

' Set the EditingMode to highlighter input. 
Private Sub Highlight(ByVal sender As Object, ByVal e As RoutedEventArgs) 

    inkCanvas1.EditingMode = InkCanvasEditingMode.Ink

    ' Set the DefaultDrawingAttributes for a highlighter pen.
    inkCanvas1.DefaultDrawingAttributes.Color = Colors.Yellow
    inkCanvas1.DefaultDrawingAttributes.IsHighlighter = True
    inkCanvas1.DefaultDrawingAttributes.Height = 25

End Sub 'Highlight


' Set the EditingMode to erase by stroke. 
Private Sub EraseStroke(ByVal sender As Object, ByVal e As RoutedEventArgs) 

    inkCanvas1.EditingMode = InkCanvasEditingMode.EraseByStroke

End Sub 'EraseStroke

' Set the EditingMode to selection. 
Private Sub [Select](ByVal sender As Object, ByVal e As RoutedEventArgs) 

    inkCanvas1.EditingMode = InkCanvasEditingMode.Select 

End Sub 'Select
// Set the EditingMode to ink input. 
private void Ink(object sender, RoutedEventArgs e)
{
    inkCanvas1.EditingMode = InkCanvasEditingMode.Ink;

    // Set the DefaultDrawingAttributes for a red pen.
    inkCanvas1.DefaultDrawingAttributes.Color = Colors.Red;
    inkCanvas1.DefaultDrawingAttributes.IsHighlighter = false;
    inkCanvas1.DefaultDrawingAttributes.Height = 2;
}

// Set the EditingMode to highlighter input. 
private void Highlight(object sender, RoutedEventArgs e)
{
    inkCanvas1.EditingMode = InkCanvasEditingMode.Ink;

    // Set the DefaultDrawingAttributes for a highlighter pen.
    inkCanvas1.DefaultDrawingAttributes.Color = Colors.Yellow;
    inkCanvas1.DefaultDrawingAttributes.IsHighlighter = true;
    inkCanvas1.DefaultDrawingAttributes.Height = 25;
}

// Set the EditingMode to erase by stroke. 
private void EraseStroke(object sender, RoutedEventArgs e)
{
    inkCanvas1.EditingMode = InkCanvasEditingMode.EraseByStroke;
}

// Set the EditingMode to selection. 
private void Select(object sender, RoutedEventArgs e)
{
    inkCanvas1.EditingMode = InkCanvasEditingMode.Select;
}

After copying this code, press F5 in Microsoft Visual Studio 2005 to run the program in the debugger.

Notice how the StackPanel places the buttons on top of the InkCanvas. If you try to ink over the top of the buttons, the InkCanvas collects and renders the ink behind the buttons. This is because the buttons are siblings of the InkCanvas as opposed to children. Also, the buttons are higher in the z-order, so the ink is rendered behind them.

See Also

Reference

DrawingAttributes

DefaultDrawingAttributes

System.Windows.Ink