Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Accepting Signatures from Hand-Held Devices

0.00/5 (No votes)
26 Dec 2007 3  
An article on drawing graphics on a hand-held device touch screen.

Introduction

This article is a great example of how code-share via sites like CodeProject could give rise to exciting results. While working on my first hand-held project (a Point of Sale application), I happened to go through leonardosalvatore's article on a GPS tracer application, which proved to be a big help for one of the challenges that I faced - how to take a customer's signature for invoicing? And, how to save and retrieve that signature data when needed?

How it works

There are three actions that a user can perform:

  • Draw on the rectangular screen area (which I call 'canvas' in the code documentation) with the help of the stylus (or mouse in the case of the device emulator)
  • Save a drawing (can be any weird one other than a signature) in a file for later retrieval
  • Load a file containing the drawing (OK! From now on, I will use only one word - the signature) which was saved previously as a single-line string (of points to be drawn in a specific format)

Requirements

For this sample, I used a hand-held device with Windows CE .NET (4.2) and .NET Compact Framework 2.0. You can test it with the help of an emulator in Visual Studio 2005. I tested this application on PSION TECKLOGIX Workabout PRO.

Getting started

I have included a project which should have everything you need to get started. You can use this utility both as a standalone and as a part of your own device application to capture (and, of course, to show) signature data. The code-snippets for both the cases are also provided.

Using the code

The application is made up of two actors:

  • Plotter: This is the main class that does all kinds of drawing jobs: draw, save, load the signatures.
  • FrmSignPad: It's the only Windows UI form of the application working as a client of the aforementioned class (Plotter.cs); it has an Options menu with Clear and Save actions; also, when the application detects a .sign file in the default \signs directory, it populates this menu with all those signature names so that the user can open and redraw the signature for display and/or edit.

The user is advised to clear the screen (i.e., click on Clear menu item) before writing the signature; it makes the drawing area (called canvas) look visible.

Note that I am not explaining the details of the main menu programming; I can add it to my future updates if people find it problematic.

Initializing things

It initializes the application; as you can see, it's able to run on 240px X 320px devices. Once the main actor m_SignPlotter is created in the form constructor, it is ready for user actions for writing a signature; i.e., the FrmSignPad_MouseMove and FrmSignPad_MouseUp events:

public partial class FrmSignPad : Form
{
    private Plotter m_SignPlotter;
    ...
#region BASIC DRAWING CODE

    //load the Sign Pad passing this form's Graphics 
    //to be used for drawing tasks
    public FrmSignPad()
    {
        InitializeComponent();
        Graphics m_graphics= this.CreateGraphics();
        //based on m_graphics, create & setup the plotter object
        m_SignPlotter = new Plotter(m_graphics, 0, 30, 240, 300);
        ...
    }

#endregion

But before we move on to examine these events, let's see how the Plotter instance (m_SignPlotter) gets initialized:

public class Plotter
{
#region STATIC/NON-STATIC VARIABLES
    //list of points for the current sign-part
    private List<Point> m_listPoints;

    //comma-delimited x,y co-ordinates for all the points of signature
    private StringBuilder m_strPoints = new StringBuilder();

    //grafix canvas of the form which uses this plotter
    private Graphics m_gfxCanvas;

    //rectangular area which m_gfxCanvas uses for its clip region
    private Rectangle m_rectClip;

    //background colorizing tool for the m_gfxCanvas
    static private Brush m_brushBg = new SolidBrush(Color.LightBlue);

    //pen used to draw points as lines
    static private Pen m_penLine = new Pen(Color.MediumBlue);

#endregion

#region BASIC DRAWING CODE

    //the only constructor for the calling form
    //params: 

    //Graphics gfx: the caller form's grafix
    //int x, int y: the drawing canvas location co-ordinates
    //int width, int height: the drawing canvas dimensions

    public Plotter(Graphics gfx, int x, int y, int width, int height)
    {
        //initialize the plotter's members
        m_listPoints = new List<Point>();
        m_gfxCanvas = gfx;
        m_rectClip = new Rectangle(x, y, width, height);
        m_gfxCanvas.Clip = new Region(m_rectClip);

        //draw the rectangular canvas as the plotter's signature pad
        m_gfxCanvas.FillRectangle(m_brushBg, m_rectClip);
    }

As you can see, I've tried to make the code look self-explanatory by sprinkling in-line comments all over; also, I have divided my code into three regions:

  • VARIABLES: Static/non-static variables of FrmSignPad and Plotter
  • BASIC DRAWING CODE: All you need to draw a signature on the form-screen
  • ADDITIONAL SAVE/LOAD FUNCTIONALITY: A bit complex part of the program which deals with saving the signature data for reuse - as a .sign file, or a list of Point objects, or a single-line string

Basic drawing code

It's better to look at these parts one by one; let's understand the basic drawing logic first:

  • The FrmSignPad constructor calls the Plotter constructor and creates a rectangular region for drawing (m_rectClip) using the Brush object m_brushBg
  • As the user moves the mouse or stylus on the screen, we collect its position co-ordinates, save them, and draw lines representing this move (called a SignPart)

Here's is how it goes:

//a user draws with the mouse or hand-held device stylus
private void FrmSignPad_MouseMove(object sender, MouseEventArgs e)
{

    if (m_SignPlotter != null)//this hardly possible
    {

    //capture the mouse position co-ordinates;
    //make up a point mapping that position
    //and pass it on to the plotter to for drawing
    Point aPoint = new Point();
    aPoint.X = e.X;
    aPoint.Y = e.Y;
    m_SignPlotter.ReDraw(aPoint);
    }
}

The plotter's ReDraw(Point aPoint) method first adds the point of mouse position in the list of points (m_listPoints), and calls Draw() which does the main job of drawing the signature lines:

//the main plotter which adds up all the points sent by FrmSignPad_MouseMove;
//and draws all the currently held points in m_listPoints onto the canvas
public void ReDraw(Point aPoint){
    m_listPoints.Add(aPoint);
    Draw();
}

//called by ReDraw AND the load functionality given below
public void Draw()
{
    float xTo = 0;
    float xFrom = 0;
    float yTo = 0;
    float yFrom = 0;

    if (m_listPoints.Count < 2)
    //can't draw 1 point only; coz, only lines are drawn here
        return;

    for (int i = 1; i < m_listPoints.Count; i++)
    {
        //co-ordinates of starting point
        xFrom = m_listPoints[i - 1].X;
        yFrom = m_listPoints[i - 1].Y;

        //co-ordinates of ending point
        xTo = m_listPoints[i].X;
        yTo = m_listPoints[i].Y;

        //draw a line segment
        m_gfxCanvas.DrawLine(m_penLine, (int)xTo, 
                   (int)yTo, (int)xFrom, (int)yFrom);
    }
}

As a SignPart is drawn, the user moves up the mouse (or stylus), which signals our application the end of drawing one SignPart so that the plotter clears the list of points because we keep only the current SignPart's points in m_listPoints:

//save away the drawing data until now; and get ready 
//for another sign-part (i.e. drawing via FrmSignPad_MouseMove)
private void FrmSignPad_MouseUp(object sender, MouseEventArgs e)
{
    m_SignPlotter.SaveAndClearPoints();
}

//appends currently held points to the string of all points 
//(i.e. m_strPoints) and clears the cashe (m_listPoints)
//called when mouse up OR when one sign-part is to be stored as string
public void SaveAndClearPoints()
{
    ...

    //when done, clear the list and for the next points' addition 
    //(i.e. FrmSignPad_MouseMove results)
    m_listPoints.Clear();
}

As far as drawing a signature is concerned, that's all there's to it. But surely, this won't be enough for our business applications because we would want to somehow save that signature data for record-keeping (say, in order to be able to further process a transaction). The SignPad application provides you with methods which either help you save away the signature data in a file or a database, or pass on to your device application as a continuous list of Point objects so that it can be plotted with the help of some third party printing API for generating invoice print-outs with that signature (e.g., I used fieldsoftware.com's printing SDK for that very purpose).

Saving signature data for reuse

Each SignPart co-ordinates are maintained in two storage variables:

  • m_listPoint: which holds only the current SignPart Point objects
  • m_strPoints: which maintains all the SignParts' points in this format: x1,y1,x2,y2,x3,y3;x1,y1,x2,y2;x1,y1,x2,y2,x3 (each SignPart is delimited by a ';')

This is the string which we can save in a file or in a database. Also, to pass this points data on to any printing program, the Plotter class has a read-only property (Points) which makes up a list of all the points recorded during the signature writing; have a look at the SaveAndClearPoints() method of Plotter; every time FrmSignPad_MouseUp is called, it appends the current SignPart points to m_strPoints in this string format before it clears m_listPoint for the next FrmSignPad_MouseMove calls (i.e., next SignPart's points data collection).

//appends currently held points to the string of all points 
//(i.e. m_strPoints) and clears the cashe (m_listPoints)
//called when mouse up OR when one sign-part is to be stored as string
public void SaveAndClearPoints()
{
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < m_listPoints.Count; i++)
    {
    sb.Append(m_listPoints[i].X + "," + 
              m_listPoints[i].Y + ",");
    }
    string strCoordinates = sb.ToString().Substring(0, 
           sb.ToString().Length - 1);//trim of last ','
    //all sign-parts would be separated by ';', 
    //e.g. 12,334,13,34;122,23,34,45;etc...
    this.m_strPoints.Append(strCoordinates + ";");
    //when done, clear the list and for the next points' addition
    //(i.e. FrmSignPad_MouseMove results)
    m_listPoints.Clear();
}

Hence, to save a signature data, we just write that formatted string in a file by calling Plotter's ToString() method:

//persists the current drawing in a file as a single-line string
public void SaveSign(string file)
{
    StreamWriter sw = new StreamWriter(file);
    sw.WriteLine(this.ToString());
    sw.Flush();
    sw.Close();
}

Loading a signature data for reuse

Finally! You would want to retrieve a saved signature for display or printing purpose (from a disk file or a database).

Now! This part of the code is a bit tricky. Our saved signatures are single-line strings; but how to re-draw those SignParts when there is no FrmSignPad_MouseMove and FrmSignPad_MouseUp runtime environment present? For that, I have devised another version of the ReDraw method which gets the signature string, splits it, and draws all the SignParts one by one looking as if it were being drawn by an invisible user:

//gets single-line string data from a .sign file
public void OpenSign(string file)
{
    StreamReader sr = new StreamReader(file);

    string signString="";
    if(!sr.EndOfStream)
    signString= sr.ReadLine();

    if (signString != "")
        ReDraw(signString);

    sr.Close();
}

//a worker method for OpenSign(string file)
//draws the saved signature on the canvas
private void ReDraw(string signString)
{
    this.Clear();
    //sign pad must have no pending or old drawing

    string[] signParts_asStr = signString.Split(';');
    //collect all sign-parts as strings

    foreach (string signPart in signParts_asStr)
    {
        //collect all x\y numbers as strings
        string[] arrPoints = signPart.Split(',');
        for (int i = 1; i < arrPoints.Length; )
        {
            string strX = arrPoints[i - 1];
            string strY = arrPoints[i];
            i = i + 2;
            Point p = new Point(Convert.ToInt32(strX), 
                                Convert.ToInt32(strY));
            this.m_listPoints.Add(p);
        }

        // done with this signPart? draw it! store it!
        // and clear the cashe for next signPart
        Draw();
        SaveAndClearPoints();
    }// do the same for next signPart if any
    //signature is re-drawn and ready for edit
}

Points of interest

I daresay this effort takes a lot of stuff from leonardosalvatore's article but I want to demonstrate that sharing your knowledge and expertise not only solves somebody's problems but also opens doors to new contributors (like myself) who further extend this knowledge-share to make software development so enticing for other developers. It's my first project on the great CodeProject website, so please contact me if you have any doubts or proposals. I'm always available for collaboration.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here