Easy InkCanvas in Winforms for capturing signatures
Posted by anoriginalidea on April 18, 2008
In WPF there’s an interesting control called “InkCanvas” that can be used to capture signatures and the like in Tablet PC applications.
Recently I created a prototype for a windows forms application. I did consider using WPF, but the overhead seemed unnecessary considering the simplicity of the requirement.
I am aware there’s a specific SDK for Tablet PC that provides winforms controls for the requirement, but I thought it might be fun to create my own. As it turns out, it was pretty easy.
The requirement was to have a signature panel which could be used to collect a bitmap that would be attached to an entity to act as an authorisation.
Signature Panel
In the prototype I decided to create the signature panel as a standalone usercontrol called it, strangely enough “SignaturePanel”.
It uses Mousedown and MouseUp events to determine stylus pressure and mousemove for drawing. Like the WPF control it keeps a collection of strokes internally.
Unlike the wpf control, I just decided to expose a “SignatureImage” property to allow a developer to retrieve a bitmap. This is rendered from the strokes in realtime.
I assume it works for the Tablet PC, but I don’t know for sure.
Write your signature here
An additional feature of this control is a label which tells the user the last time the signature was last updated. I thought the user may find this helpful.
If the user hasn’t written anything then it just reads “Write your signature here”.
The user control consists of a picturebox called “inkPanel” and a button for resetting the content:
Here’s the code:
Public Class SignaturePanel
Private moCurrentWriting As New List(Of Point) Private Sub inkPanel_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseMove Private Sub inkPanel_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseUp moCurrentWriting = New List(Of Point) Private Sub inkPanel_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles inkPanel.Paint
mRenderSignature(e.Graphics)
Dim lsText As String = "" e.Graphics.DrawString(lsText, Me.Font, Brushes.Black, (inkPanel.Width / 2) - (e.Graphics.MeasureString(lsText, Me.Font).Width / 2), (inkPanel.Height / 2) - (e.Graphics.MeasureString(lsText, Me.Font).Height / 2)) <System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)> _ Return loBitmap End Class
Private moRememberInk As New List(Of List(Of Point))
Private mbPenDown As Boolean = False
Private Sub inkPanel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseDown
mbPenDown = True
End Sub
If mbPenDown Then
moCurrentWriting.Add(e.Location)
mdLastSignatureUpdate = Now
Me.Refresh()
End If
End Sub
If moCurrentWriting.Count > 2 Then
moRememberInk.Add(moCurrentWriting)
End If
mbPenDown = False
End Sub
If Not Me.DesignMode Then
If mdLastSignatureUpdate.HasValue Then
lsText = "Signature last updated " & Me.LastSignatureUpdate.ToLongTimeString
End If
e.Graphics.DrawString(lsText, Me.Font, Brushes.Black, 5, inkPanel.Height - 20)
Else
lsText = "... Write your signature here ..."
End If
End Sub
Private Sub mRenderSignature(ByVal g As Graphics)
Using loPen As New Pen(Color.Black)
loPen.Width = 2
For Each loLines As List(Of Point) In moRememberInk
g.DrawLines(loPen, loLines.ToArray)
Next
If moCurrentWriting.Count > 1 Then
g.DrawLines(loPen, moCurrentWriting.ToArray)
End If
End Using
End Sub
Private Sub cmdClearInk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClearInk.Click
moRememberInk.Clear()
inkPanel.Refresh()
End Sub
Private mdLastSignatureUpdate As Nullable(Of Date) = Nothing
<System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)> _
Public Property LastSignatureUpdate() As Date
Get
Return mdLastSignatureUpdate
End Get
Set(ByVal value As Date)
mdLastSignatureUpdate = value
End Set
End Property
Public ReadOnly Property SignatureImage() As Image
Get
Dim loBitmap As New Bitmap(Me.Width, Me.Height)
Using loGfx As Graphics = Graphics.FromImage(loBitmap)
mRenderSignature(loGfx)
End Using
End Get
End Property
If anyone’s interested in a standalone sample project, post the request to this blog and I’ll see what I can do.
Possible Uses
It may be interesting to use this code as:
- Signature recognition
- “Mud map” style sketching for business applications
- A basis for a drawing program
Of course, if you’re using WPF, use InkPanel. But if you don’t want the overhead, maybe this will be the foundation of a lightweight solution.


Loren Heiny said
You’ll get a bunch of features with the WPF InkCanvas which you might want to check out, such as “smart” erase, selection (which you don’t need in your case), and more.
Also, if you’re running Vista, you probably have the Tablet bits (including handwriting recognition) on your machine–you just need an external digitizer, such as a Wacom tablet, to “get to them.” InkCanvas will work OK with a mouse though.
Loren Heiny said
By the way, you may want to check out the ink support in Silverlight 2. Your ink-enabled drawing program would be extra cool there.
anoriginalidea said
Thanks for the info.
I have used WPF’s InkCanvas in the past for other things.
In this particular case, WPF’s overhead is not really warranted (perhaps), so I wanted to see how hard it would be to do this in Winforms.
Haichen Dong said
It looks nice, but how to save the image to a file. I try to save it, but the image always black.
This the code I used to save the file:
Dim bm As Bitmap = SignatureImage ‘ inkPanel.Image
‘ Save the picture as a bitmap, JPEG, and GIF.
bm.Save(file_name & “bmp”, System.Drawing.Imaging.ImageFormat.Bmp)
bm.Save(file_name & “jpg”, _
System.Drawing.Imaging.ImageFormat.Jpeg)
bm.Save(file_name & “gif”, _
System.Drawing.Imaging.ImageFormat.Gif)
anoriginalidea said
Hi,
I’ve read about this happening in some circumstances.
The usual suggestion is try saving as a “png”.
For more information, take a look at this thread:
http://forums.msdn.microsoft.com/en-US/vbgeneral/thread/8b2d3715-32a4-4c31-be42-a20bf6042cb1/