How to: Erase Ink on a Custom Control
The IncrementalStrokeHitTester determines whether the currently drawn stroke intersects another stroke. This is useful for creating a control that enables a user to erase parts of a stroke, the way a user can on an InkCanvas when the EditingMode is set to EraseByPoint.
Example
The following example creates a custom control that enables the user to erase parts of strokes. This example creates a control that contains ink when it is initialized. To create a control that collects ink, see Creating an Ink Input Control.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.IO;
// This control initializes with ink already on it and allows the
// user to erase the ink with the tablet pen or mouse.
public class InkEraser : Label
{
IncrementalStrokeHitTester eraseTester;
InkPresenter presenter;
string strokesString =
@"ALwHAxdIEETLgIgERYQBGwIAJAFGhAEbAgAkAQUBOBkgMgkA9P8CAekiOkUzCQD4n"
+ "wIBWiA6RTgIAP4DAAAAgH8RAACAPx8JEQAAAAAAAPA/CiUHh/A6N4HR0AivFX8Vs"
+ "IfsiuyLSaIeDSLwabiHm0GgUDi+KZkACjsQh/A9t4IC5VJpfLhaIxyxXIh7Dncnh"
+ "+6e7qODwoERlPAw8EpGoJAgh61IKjCYXBYDA4DAIHF67KIHAAojB4fwMteBn+RKB"
+ "lziaIfwWTeCwePqbM8WgIeeQCDQOFRvcIAKNA+H8B8XgQHkUbjQTTnGuaZns3l4h"
+ "/DWt4a0YKBBOA94D6HRCAiGnp5CS8LExMLB1tOgYIAKUBOH8KnXhU7lMold+tcbi"
+ "kChkqu2EtPYxp9bmYCH8HDHg4ZhMIwRyMHH+4Jt8nleX8c0/D/AkYwxJGiHkkQgM"
+ "Ch9CqcFhMDQCBwWAwuR2eAACmgdh/EpF4lA6XMUfhMXgMHgVDxBFpRKpZII5EINA"
+ "OA64M+J4Lw1CIoAh/B2x4PS4bQodAopEI5IJBki4waEx2Qy+dy+ayHgleEmmHH8c"
+ "e3MZOCGw5TWd3CwsHAwMCgRAEAgElKwOHZKBApaGIfxezeL0uN02N8IzwaGEpNIJ"
+ "ZxHnELyOj0GfyuU6FgmhplIgIfwYgeDHeaI1vjOtZgcHgHAYb9hUCgEFgsPm1xnM"
+ "ZkYhsnYJgZeZh4uAgCgnSBIOJv4OAgwCmkgh/GrR41X4dGoRJL9EKra5HKY7IZ3C"
+ "4fj/M06olSoU8kkehUbh8jkMdCH8IJXhAXhMCk8JuNlmNyh0YiEumUwn2wMRxyHw"
+ "2TzWmzeb02OzGKxMITwIhnrjzbb44zRhGEKRhCM4zrr6sQKXRWH8kuXkmPj0DiXC"
+ "gcJbC9HZZgkKgUG4bLh3YrwJHAYw2CAh/CiN4Tq7DOZr4BB/AFtdOWW5P2h1Wkzv"
+ "l4+YwqXf8d5fZ7ih51QKbB4LQrLAYDBIDABA4BO4nAICApvIIfy4BeXA2DRSrQlL"
+ "oHHsYQ/KMXlsvl8rn8Xkcdg+G9NVaUWimUDYk9Ah/BoF4M0YBCqZPYqk8dwLf7hD"
+ "YNBJFLKBNqZTqNubWshl9VoM1reFYZYQEBGUsDAwKEjYuDQKBgICBgCAgIOAg4nI"
+ "8OACloSh/BFl4Gf/IOt6FXfF8F4ToPCZzlPwP4+B+DHmQO847rfDeCcG8eKh/EZV"
+ "4i9eZt8A9nUF8VzxaUe5grl7YrPaHfpRKJNx4yHmUuj1vicwmMBEAjUVgKB61A=";
public InkEraser()
{
presenter = new InkPresenter();
this.Content = presenter;
// Create a StrokeCollection the string and add it to
StrokeCollectionConverter converter =
new StrokeCollectionConverter();
if (converter.CanConvertFrom(typeof(string)))
{
StrokeCollection newStrokes =
converter.ConvertFrom(strokesString) as StrokeCollection;
presenter.Strokes.Clear();
presenter.Strokes.Add(newStrokes);
}
}
protected override void OnStylusDown(StylusDownEventArgs e)
{
base.OnStylusDown(e);
StylusPointCollection points = e.GetStylusPoints(this);
InitializeEraserHitTester(points);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (e.StylusDevice != null)
{
return;
}
Point pt = e.GetPosition(this);
StylusPointCollection collectedPoints = new StylusPointCollection(new Point[] { pt });
InitializeEraserHitTester(collectedPoints);
}
// Prepare to collect stylus packets. Get the
// IncrementalHitTester from the InkPresenter's
// StrokeCollection and subscribe to its StrokeHitChanged event.
private void InitializeEraserHitTester(StylusPointCollection points)
{
EllipseStylusShape eraserTip = new EllipseStylusShape(3, 3, 0);
eraseTester =
presenter.Strokes.GetIncrementalStrokeHitTester(eraserTip);
eraseTester.StrokeHit += new StrokeHitEventHandler(eraseTester_StrokeHit);
eraseTester.AddPoints(points);
}
protected override void OnStylusMove(StylusEventArgs e)
{
StylusPointCollection points = e.GetStylusPoints(this);
AddPointsToEraserHitTester(points);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.StylusDevice != null)
{
return;
}
if (e.LeftButton == MouseButtonState.Released)
{
return;
}
Point pt = e.GetPosition(this);
StylusPointCollection collectedPoints = new StylusPointCollection(new Point[] { pt });
AddPointsToEraserHitTester(collectedPoints);
}
// Collect the StylusPackets as the stylus moves.
private void AddPointsToEraserHitTester(StylusPointCollection points)
{
if (eraseTester.IsValid)
{
eraseTester.AddPoints(points);
}
}
// Unsubscribe from the StrokeHitChanged event when the
// user lifts the stylus.
protected override void OnStylusUp(StylusEventArgs e)
{
StylusPointCollection points = e.GetStylusPoints(this);
StopEraseHitTesting(points);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (e.StylusDevice != null)
{
return;
}
Point pt = e.GetPosition(this);
StylusPointCollection collectedPoints = new StylusPointCollection(new Point[] { pt });
StopEraseHitTesting(collectedPoints);
}
private void StopEraseHitTesting(StylusPointCollection points)
{
eraseTester.AddPoints(points);
eraseTester.StrokeHit -= new
StrokeHitEventHandler(eraseTester_StrokeHit);
eraseTester.EndHitTesting();
}
// When the stylus intersects a stroke, erase that part of
// the stroke. When the stylus dissects a stoke, the
// Stroke.Erase method returns a StrokeCollection that contains
// the two new strokes.
void eraseTester_StrokeHit(object sender,
StrokeHitEventArgs args)
{
StrokeCollection eraseResult =
args.GetPointEraseResults();
StrokeCollection strokesToReplace = new StrokeCollection();
strokesToReplace.Add(args.HitStroke);
// Replace the old stroke with the new one.
if (eraseResult.Count > 0)
{
presenter.Strokes.Replace(strokesToReplace, eraseResult);
}
else
{
presenter.Strokes.Remove(strokesToReplace);
}
}
}
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Ink
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.IO
' This control initializes with ink already on it and allows the
' user to erase the ink with the tablet pen or mouse.
Public Class InkEraser
Inherits Label
Private eraseTester As IncrementalStrokeHitTester
Private presenter As InkPresenter
' The base-64 encoded string that contains ink data
' in ink serialized format (ISF).
Private strokesString As String = _
"ALwHAxdIEETLgIgERYQBGwIAJAFGhAEbAgAkAQUBOBkgMgkA9P8CAekiOkUzCQD4n" _
& "wIBWiA6RTgIAP4DAAAAgH8RAACAPx8JEQAAAAAAAPA/CiUHh/A6N4HR0AivFX8Vs" _
& "IfsiuyLSaIeDSLwabiHm0GgUDi+KZkACjsQh/A9t4IC5VJpfLhaIxyxXIh7Dncnh" _
& "+6e7qODwoERlPAw8EpGoJAgh61IKjCYXBYDA4DAIHF67KIHAAojB4fwMteBn+RKB" _
& "lziaIfwWTeCwePqbM8WgIeeQCDQOFRvcIAKNA+H8B8XgQHkUbjQTTnGuaZns3l4h" _
& "/DWt4a0YKBBOA94D6HRCAiGnp5CS8LExMLB1tOgYIAKUBOH8KnXhU7lMold+tcbi" _
& "kChkqu2EtPYxp9bmYCH8HDHg4ZhMIwRyMHH+4Jt8nleX8c0/D/AkYwxJGiHkkQgM" _
& "Ch9CqcFhMDQCBwWAwuR2eAACmgdh/EpF4lA6XMUfhMXgMHgVDxBFpRKpZII5EINA" _
& "OA64M+J4Lw1CIoAh/B2x4PS4bQodAopEI5IJBki4waEx2Qy+dy+ayHgleEmmHH8c" _
& "e3MZOCGw5TWd3CwsHAwMCgRAEAgElKwOHZKBApaGIfxezeL0uN02N8IzwaGEpNIJ" _
& "ZxHnELyOj0GfyuU6FgmhplIgIfwYgeDHeaI1vjOtZgcHgHAYb9hUCgEFgsPm1xnM" _
& "ZkYhsnYJgZeZh4uAgCgnSBIOJv4OAgwCmkgh/GrR41X4dGoRJL9EKra5HKY7IZ3C" _
& "4fj/M06olSoU8kkehUbh8jkMdCH8IJXhAXhMCk8JuNlmNyh0YiEumUwn2wMRxyHw" _
& "2TzWmzeb02OzGKxMITwIhnrjzbb44zRhGEKRhCM4zrr6sQKXRWH8kuXkmPj0DiXC" _
& "gcJbC9HZZgkKgUG4bLh3YrwJHAYw2CAh/CiN4Tq7DOZr4BB/AFtdOWW5P2h1Wkzv" _
& "l4+YwqXf8d5fZ7ih51QKbB4LQrLAYDBIDABA4BO4nAICApvIIfy4BeXA2DRSrQlL" _
& "oHHsYQ/KMXlsvl8rn8Xkcdg+G9NVaUWimUDYk9Ah/BoF4M0YBCqZPYqk8dwLf7hD" _
& "YNBJFLKBNqZTqNubWshl9VoM1reFYZYQEBGUsDAwKEjYuDQKBgICBgCAgIOAg4nI" _
& "8OACloSh/BFl4Gf/IOt6FXfF8F4ToPCZzlPwP4+B+DHmQO847rfDeCcG8eKh/EZV" _
& "4i9eZt8A9nUF8VzxaUe5grl7YrPaHfpRKJNx4yHmUuj1vicwmMBEAjUVgKB61A="
Public Sub New()
presenter = New InkPresenter()
Me.Content = presenter
' Create a StrokeCollection the string and add it to
Dim converter As New StrokeCollectionConverter()
If converter.CanConvertFrom(GetType(String)) Then
Dim newStrokes As StrokeCollection = converter.ConvertFrom(strokesString)
presenter.Strokes.Clear()
presenter.Strokes.Add(newStrokes)
End If
End Sub 'New
Protected Overrides Sub OnStylusDown(ByVal e As StylusDownEventArgs)
MyBase.OnStylusDown(e)
Dim points As StylusPointCollection = e.GetStylusPoints(Me)
InitializeEraserHitTester(points)
End Sub 'OnStylusDown
Protected Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseLeftButtonDown(e)
If Not (e.StylusDevice Is Nothing) Then
Return
End If
Dim pt As Point = e.GetPosition(Me)
Dim collectedPoints As New StylusPointCollection(New Point() {pt})
InitializeEraserHitTester(collectedPoints)
End Sub 'OnMouseLeftButtonDown
' Get the IncrementalHitTester from the InkPresenter's
' StrokeCollection and subscribe to its StrokeHitChanged event.
Private Sub InitializeEraserHitTester(ByVal points As StylusPointCollection)
Dim eraserTip As New EllipseStylusShape(3, 3, 0)
eraseTester = presenter.Strokes.GetIncrementalStrokeHitTester(eraserTip)
AddHandler eraseTester.StrokeHit, AddressOf eraseTester_StrokeHit
eraseTester.AddPoints(points)
End Sub 'InitializeEraserHitTester
Protected Overrides Sub OnStylusMove(ByVal e As StylusEventArgs)
Dim points As StylusPointCollection = e.GetStylusPoints(Me)
AddPointsToEraserHitTester(points)
End Sub 'OnStylusMove
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
MyBase.OnMouseMove(e)
If Not (e.StylusDevice Is Nothing) Then
Return
End If
If e.LeftButton = MouseButtonState.Released Then
Return
End If
Dim pt As Point = e.GetPosition(Me)
Dim collectedPoints As New StylusPointCollection(New Point() {pt})
AddPointsToEraserHitTester(collectedPoints)
End Sub 'OnMouseMove
' Collect the StylusPackets as the stylus moves.
Private Sub AddPointsToEraserHitTester(ByVal points As StylusPointCollection)
If eraseTester.IsValid Then
eraseTester.AddPoints(points)
End If
End Sub 'AddPointsToEraserHitTester
' Unsubscribe from the StrokeHitChanged event when the
' user lifts the stylus.
Protected Overrides Sub OnStylusUp(ByVal e As StylusEventArgs)
Dim points As StylusPointCollection = e.GetStylusPoints(Me)
StopEraseHitTesting(points)
End Sub 'OnStylusUp
Protected Overrides Sub OnMouseLeftButtonUp(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseLeftButtonUp(e)
If Not (e.StylusDevice Is Nothing) Then
Return
End If
Dim pt As Point = e.GetPosition(Me)
Dim collectedPoints As New StylusPointCollection(New Point() {pt})
StopEraseHitTesting(collectedPoints)
End Sub 'OnMouseLeftButtonUp
Private Sub StopEraseHitTesting(ByVal points As StylusPointCollection)
eraseTester.AddPoints(points)
RemoveHandler eraseTester.StrokeHit, AddressOf eraseTester_StrokeHit
eraseTester.EndHitTesting()
End Sub 'StopEraseHitTesting
' When the stylus intersects a stroke, erase that part of
' the stroke. When the stylus dissects a stoke, the
' Stroke.Erase method returns a StrokeCollection that contains
' the two new strokes.
Sub eraseTester_StrokeHit(ByVal sender As Object, ByVal args As StrokeHitEventArgs)
Dim eraseResult As StrokeCollection = args.GetPointEraseResults()
Dim strokesToReplace As New StrokeCollection()
strokesToReplace.Add(args.HitStroke)
' Replace the old stroke with the new one.
If eraseResult.Count > 0 Then
presenter.Strokes.Replace(strokesToReplace, eraseResult)
Else
presenter.Strokes.Remove(strokesToReplace)
End If
End Sub 'eraseTester_StrokeHit
End Class 'InkEraser