Click here to Skip to main content
15,861,168 members
Articles / Multimedia / GDI+

Digitizer interface in C# using VBTablet

Rate me:
Please Sign up or sign in to vote.
4.28/5 (12 votes)
4 Dec 2009CPOL3 min read 61.3K   2.4K   25   13
Digitzer device interface using VBTablet in C#.

Image 1

Image 2

Image 3

Introduction

This article demonstrates the following things:

  1. Interfacing a Digitizer Tablet (Wintab Compatible Digitizer) in C#.
  2. Handling events raised by the digitizer and capturing the data.
  3. Using the VBTablet COM vomponent in C# for interfacing a Digitizer.

The VBTablet component is used for interfacing digitizer devices; this component can be downloaded from: http://www.sourceforge.org/projects/vbtablet/. More details are available at: http://www.greenreaper.co.uk/ which is the developer's website.

The VBTablet is a COM component written in VB (most probably). The interface in VB.NET is available on the 'SourceForge' link, but C# based code is not available. I am right now working on the digitizer, hence I have tried this interface in C# and am posting here.

One thing you should note is that you must have a digitizer tablet to test this code. I am using WACOM Intuos4(4X3). You can get the details about this hardware at http://www.wacom.com/intuos/.

If you are developing a graphic designer application, this can be a starting step. This article also demonstrates the use of the COM component in C#.

Background

Install the device driver and connect the hardware. To understand this code, you should have a basic idea of COM and delegates. Here, we connect the digitizer to the PC, and the digitizer is interfaced as a VBTablet object in C# code. You need to add a reference to the VBTablet.dll file which contains all the definitions.

Using the code

We have to add a reference to this namespace:

using VBTablet;

Next, we create an object for the Digitizer; the class Tablet is defined in the VBTablet namespace.

C#
public Tablet Digitizer;

The digitizer is connected in the main form load event. First, the digitizer is initialized. The Tablet object (Digitizer) will probe the hardware and make the info available. We get the context, pressure, max X , max Y etc., here. Using this component, we can even read the Z coordinate of the pen tip (in 3D). Next, we define the delegate for handling the event for the Tablet object (Digitizer).

C#
Digitizer.PacketArrival += new Tablet.PacketArrivalEventHandler(PacketArrival); 

This is the delegate which handles the packet arrival event. It occurs when the pen tip touches the surface of the digitizer.

C#
private void MainFrm_Load(object sender, EventArgs e)
{
    try
    {
        // On Error GoTo errorload
        Digitizer = new Tablet(); //Actually create the tablet object
        sldGranularity.Value = 2; //Set packet granularity value
        //that uses a tablet attribute. Remember not everyone has _your_ tablet.
        Digitizer.UnavailableIsError = false;

        //Tablet.hWnd = frmMain.DefInstance.Handle
        prgX.Maximum = Digitizer.Context.OutputExtentX - Digitizer.Context.OutputOriginX;
        prgY.Maximum = Digitizer.Context.OutputExtentY - Digitizer.Context.OutputOriginY;
        prgZ.Maximum = 255;

        prgPressure.Maximum = (int)Digitizer.Device.NormalPressure.get_Max(true);
        prgTangentPressure.Maximum = (int)Digitizer.Device.TangentPressure.get_Max(true);

        // The Delegates for Digitizer Event Handling
        Digitizer.ContextClosed += 
          new VBTablet.Tablet.ContextClosedEventHandler(ContextClosed);
        Digitizer.ContextOpened += 
          new VBTablet.Tablet.ContextOpenedEventHandler(ContextOpened);
        Digitizer.ContextRepositioned += 
           new Tablet.ContextRepositionedEventHandler(ContextRepositioned);
        Digitizer.ContextUpdated += new Tablet.ContextUpdatedEventHandler(ContextUpdated);
        Digitizer.CursorChange += new Tablet.CursorChangeEventHandler(CursorChange);
        Digitizer.InfoChange += new Tablet.InfoChangeEventHandler(InfoChange);
        Digitizer.ProximityChange += new Tablet.ProximityChangeEventHandler(ProximityChange);
        Digitizer.PacketArrival += new Tablet.PacketArrivalEventHandler(PacketArrival);
    }
    catch (Exception  ex)
    {
        MessageBox.Show(ex.Message + ". Please Connect the Digitizer Device First !!");
    }
}

Before using the tablet, you have to enable it. This is done by first pressing the Connect button and then the Enable button. The Connect button will attach the context of the Digitizer to the picture box of the main form, through:

C#
IntPtr Hwnd;
Hwnd = this.Handle;

private void cmdConnect_Click(object sender, EventArgs e)
{
    IntPtr Hwnd;
    bool IsDigitizingContext=false;
    string ContextID = "FirstContext";
    Connect.Enabled = false;
    Disconnect.Enabled = true;
    Enable.Enabled = true;
    Disable.Enabled = false;
    chkDigitise.Enabled = true;//Enable Digitize Mode
    Hwnd = this.Handle;
    Digitizer.hWnd = Hwnd;
    Digitizer.AddContext(ContextID, ref IsDigitizingContext);
    Digitizer.SelectContext(ref ContextID);
    Digitizer.Connected = true;
    Digitizer.Context.QueueSize = 32;//Set queue size to a reasonable value

    Deviceinfo();
}

private void Enable_Click(object sender, EventArgs e)
{
    Disable.Enabled = true;
    Enable.Enabled = false;
    InContext();//Call Incontext
    //Check Following Code
   
    Digitizer.Context.TrackingMode = true;
    Digitizer.Context.Enabled = true;
    return;
}

The packet coming from the digitizer has X, Y, Z, Pressure, Azimuth, Altitude, and Cursor information. We get this info in the packet arrival event. The event handler updates the main form display; we are also using some GDI functions for displaying data in a picture box.

C#
public void PacketArrival(ref IntPtr ContextHandle, 
         ref int Cursor_Renamed, ref int X , ref int Y , ref int Z, 
         ref int Buttons , ref int Pressure , ref int TangentPressure, 
         ref int Azimuth, ref int Altitude, ref int Twist, ref int Pitch, 
         ref int Roll , ref int Yaw,ref int PacketSerial, ref int PacketTim) 
{
    //Show current stats. Note that it's a good idea not to update
    //if not necessary - 100+ updates a second can really hurt performance
    
    tmpl = System.Math.Abs(X);

    if( tmpl != prgX.Value)
    {
        if( tmpl <= prgX.Maximum )
            prgX.Value = tmpl;
    }

    tmpl = System.Math.Abs(Y);

    if( tmpl != prgY.Value)
    {
        if( tmpl <= prgY.Maximum)
            prgY.Value = tmpl;
    }

    tmpl = System.Math.Abs(Z);
    if( tmpl != prgZ.Value)
    {
        if( tmpl <= prgZ.Maximum)
            prgZ.Value = tmpl;
    }
    tmpl = System.Math.Abs(Pressure);
    if( tmpl != prgPressure.Value)
        prgPressure.Value = tmpl;
    tmpl = System.Math.Abs(TangentPressure);
    if( tmpl != prgTangentPressure.Value)
        prgTangentPressure.Value = tmpl;

    if( Convert.ToInt32(lblX.Text)!= X )
        lblX.Text = X.ToString();
    if( Convert.ToInt32(lblY.Text)!= Y )
        lblY.Text = Y.ToString();
    if( Convert.ToInt32(lblZ.Text)!= Z )
        lblZ.Text = Z.ToString();
    if( Convert.ToInt32(lblCursor.Text)!= Cursor_Renamed )
        lblCursor.Text = Cursor_Renamed.ToString();
    if( Convert.ToInt32(lblPressure.Text)!= Pressure )
        lblPressure.Text = Pressure.ToString();
    if( Convert.ToInt32(lblTangentPressure.Text)!= TangentPressure )
        lblTangentPressure.Text = TangentPressure.ToString();
    if( Convert.ToInt32(lblAzimuth.Text)!= Azimuth )
        lblAzimuth.Text = Azimuth.ToString();
    if( Convert.ToInt32(lblAltitude.Text)!= Altitude )
        lblAltitude.Text = Altitude.ToString();
    if( Convert.ToInt32(lblTwist.Text)!= Twist )
        lblTwist.Text = Twist.ToString();
    if( Convert.ToInt32(lblPitch.Text)!= Pitch )
        lblPitch.Text = Twist.ToString();
    if( Convert.ToInt32(lblRoll.Text)!= Roll )
        lblRoll.Text = Roll.ToString();
    if( Convert.ToInt32(lblYaw.Text)!= Yaw )
        lblYaw.Text = Yaw.ToString();
            
    Pen ppen=new Pen (Color.Red,1);
    Graphics Gr;
    if (Pressure > 0) // 'catch normalpressure and button 1
    {
        if (Digitizer.Context.CursorIsInverted)
        {
            ppen.Color= Color.Red;
            picDraw.Refresh();
        }
        else
        {
            ppen.Color = Color.Blue;
        }
        tmpl = (int)Digitizer.Device.NormalPressure.get_Max(true);
        RectWidth = (int)((Pressure / (float)tmpl) * 100);
        if ((RectWidth >= 0) & (RectWidth <= 20))
            ppen.Color = Color.LawnGreen;
        else if ((RectWidth >= 21) & (RectWidth <= 40))
            ppen.Color = Color.Blue;
        else if((RectWidth >= 41) & (RectWidth <= 100))
            ppen.Color = Color.Red;
        
        try
        {
            Gr = picDraw.CreateGraphics();
            Gr.DrawLine(ppen, X, picDraw.Height - Y, 
                        Xold,picDraw.Height - Yold + 1);
            // Gr.DrawEllipse(ppen, X, picDraw.Height - Y, 1, 1);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message.ToString(), "Error", 
                   MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
    }
    //   Application.DoEvents(); // Poor Data Capture & Rendering
    Xold=X;
    Yold=Y;
};

The device details are given by the following function:

C#
private void Deviceinfo()
{
    lblDeviceMaxPktRate.Text = "Maximal Update Packet Rate (Packets/sec): " + 
                               Digitizer.Device.MaxPktRate.ToString();
    lblDeviceMargins.Text = "Device Context Margins (x, y, z): " + 
        Digitizer.Device.Margins.X.ToString() + ", " + 
        Digitizer.Device.Margins.Y.ToString() + ", " + 
        Digitizer.Device.Margins.Z.ToString(); ;
    lblDeviceNormalPressure.Text = "Normal Pressure (Min, Max, Resolution, Units): " + 
        Digitizer.Device.NormalPressure.get_Min(true).ToString() + 
        ", " + Digitizer.Device.NormalPressure.get_Max(true).ToString() + 
        " , " + Digitizer.Device.NormalPressure.Resolution.ToString() + 
        " , " + Digitizer.Device.NormalPressure.Units;
    lblDevicePnPID.Text = "Plug-and-Play device ID: " + 
        Digitizer.Device.PnPID.ToString();
    lblDeviceX.Text = "X capabilities (Min, Max, Resolution, Units): " + 
        Digitizer.Device.X.get_Min(true).ToString()+ ", " + 
        Digitizer.Device.X.get_Max(true).ToString() +" ," + 
        Digitizer.Device.X.Resolution.ToString() + " ," + Digitizer.Device.X.Units;
    lblDeviceY.Text = "Y capabilities (Min, Max, Resolution, Units): " + 
        Digitizer.Device.Y.get_Min(true).ToString() + ", " + 
        Digitizer.Device.Y.get_Max(true).ToString() + " ," + 
        Digitizer.Device.Y.Resolution.ToString() + " ," + 
        Digitizer.Device.Y.Units;
    lblDeviceZ.Text = "Z capabilities (Min, Max, Resolution, Units): " + 
        Digitizer.Device.Z.get_Min(true).ToString() + ", " + 
        Digitizer.Device.Z.get_Max(true).ToString() + " ," + 
        Digitizer.Device.Z.Resolution.ToString() + " ," + 
        Digitizer.Device.Z.Units;
    lblDeviceAzimuth.Text = "Azimuth capabilities (Min, Max, Resolution, Units): " + 
        Digitizer.Device.Azimuth.get_Min(true).ToString() + ", " + 
        Digitizer.Device.Azimuth.get_Max(true).ToString() + " ," + 
        Digitizer.Device.Azimuth.Resolution.ToString() + " ," + 
        Digitizer.Device.Azimuth.Units;
    lblDevName.Text = " Device Name : " + Digitizer.Device.GetType().ToString();
    
    lblStatusContexts.Text = "Number of Contexts open : " + 
                             Digitizer.Status.OpenContexts.ToString();
    lblStatusSysContexts.Text = "Number of System Contexts open : " + 
                                Digitizer.Status.OpenSysContexts.ToString();
    lblStatusMaxRate.Text = "Maximum Packet Rate in use (packets/sec): " + 
                            Digitizer.Status.MaxCurrentPktRate.ToString();
    lblStatusMgrHandles.Text = "Number of Manager Handles open : " + 
                               Digitizer.Status.OpenMgrHandles.ToString();
    lblExtensionTag.Text = "Extension ID: " + Digitizer.Extension.ID.ToString();
    lblExtensionAbsSize.Text = "Size of Extension in a Packet (Absolute Mode): " + 
                               Digitizer.Extension.AbsoluteSize.ToString();
    lblExtensionRelSize.Text = "Size of Extension in a Packet (Relative Mode): " + 
                               Digitizer.Extension.RelativeSize.ToString();
    lblExtensionMask.Text = "Extension Or-Mask: " + 
                            Digitizer.Extension.OrMask.MaskValue.ToString();
}

Points of interest

The VBTablet demo was in VB.NET, no C# version was available. This code demonstrates the C# version of implementation of VBTablet. This can be used with any Wintab compatible Digitizer. I have successfully tested it on a Wacom Intuos4.

Thanks

Thanks to 'L Laurence "GreenReaper" Parry', Developer of VBTablet.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
QuestionThis is really helpfull Pin
Josef Meile11-Oct-17 6:48
Josef Meile11-Oct-17 6:48 
QuestionNice article Pin
JPais12-Nov-15 7:10
JPais12-Nov-15 7:10 
Questiondigitizer plus sdk Pin
jawad ch21-Sep-14 21:09
jawad ch21-Sep-14 21:09 
Questiondigitizer interface Pin
Member 1042452925-Jul-14 1:41
Member 1042452925-Jul-14 1:41 
Questionsave picture Pin
Member 826372018-Mar-13 0:16
Member 826372018-Mar-13 0:16 
AnswerRe: save picture Pin
kidoucorp3-May-13 15:39
kidoucorp3-May-13 15:39 
QuestionDigitizer interface Pin
Member 826372016-Mar-13 23:54
Member 826372016-Mar-13 23:54 
QuestionCursor Pin
Member 172834714-Oct-10 22:13
Member 172834714-Oct-10 22:13 
GeneralProblema con Windows 7 (64 bits) Pin
nixsus20-May-10 12:43
nixsus20-May-10 12:43 
Problem with Windows 7 (64 bit)

Hello, I'm using a Wacom Bamboo tablet. In windows XP works fine, but windows 7 lost packets. I spend with your code, also with other codes. It may be a driver bug. Wacom in the part of said faq for developers that can saturate the packet queue. So should increase but I still have problems. I tried to release the queue but I'm not getting done. You've tested your code in Windows 7 or Vista?

There is another library called wintabdotnet same thing happens.

Thanks
GeneralRe: Problema con Windows 7 (64 bits) Pin
Dr. Vinayak Ashok Bharadi1-Jul-10 2:29
Dr. Vinayak Ashok Bharadi1-Jul-10 2:29 
GeneralGreat work! Pin
rahulphilips18-Apr-10 19:39
rahulphilips18-Apr-10 19:39 
GeneralNice Work Pin
Md. Marufuzzaman17-Dec-09 18:38
professionalMd. Marufuzzaman17-Dec-09 18:38 
GeneralYEAH!!! X| Pin
DIEGO FELDNER17-Dec-09 8:06
DIEGO FELDNER17-Dec-09 8:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.