Tablet PC Platform Independence

 

Dr. Neil Roodyn
Independent contractor, https://www.Roodyn.com

August, 2004

Applies to:
   Microsoft® Windows® XP Tablet PC Edition 2005
   Application Deployment

Summary: Describes a number of strategies for deploying an application across Windows XP and Windows XP Tablet PC Edition. Details and code examples illustrate single application deployment for multiple platforms. This article assumes you have Microsoft Visual Studio® .NET 2003 and the Windows XP Tablet PC Edition Development Kit 1.7 installed on your development computer. You must have a Tablet PC to test the features specific to Tablet PC; however, you do not need a Tablet PC to complete the exercises or create the application.

Contents

Introduction
Different Strategies
The Common Strategy
Exercise Time
   Getting Started
   Is this a Tablet PC?
   Enabling Controls
   Printing
Putting It All Together
Biography

Introduction

Over the last year I helped a number of teams to use the features of the Tablet PC operating system in their applications. Several of the teams I've worked with wanted to ship the same application to multiple types of computers: desktops, laptops, and Tablet PCs. This makes a lot of sense, as the Tablet PC runs a full version of the Windows XP Professional operating system with extra features to support the Tablet PC hardware. I expect if you are reading this, then like the teams I have worked with, you want your application to use the features of the Tablet PC if they are available. Through a series of hands-on exercise we are going to learn how our code can detect if Tablet PC hardware is available and if so, take advantage of the additional features.

Different Strategies

Before we get into writing any code I want to think through some possible strategies we can take to enable us to build one assembly that runs across the different platforms. Once we detect if the device we are running on is a Tablet PC we really only have three choices:

  1. The If then Add strategy. This strategy sets a flag to indicate if the application is running on a Tablet PC. Each time we encounter an area of the code in which we want add Tablet-specific features we check this flag. If the flag is set we call the code to add the Tablet PC features. This is easy to implement but it can be confusing for the end user if the interface doesn't clearly change to distinguish that it is able to accept Ink as an input.

  2. The If-Else Strategy. This strategy is similar to the If then Add strategy except that we only show the user interface features for the platform that the application is running on. In this strategy, all the areas of the code that can have Ink input have an if-else condition.

    If (RunningOnTablet)
        Display Tablet Interface
    Else 
        Display Non Tablet Interface
    

    This is not much harder to implement than the first strategy but it provides a much better user interface for the end user.

  3. Alternate forms strategy. With this strategy you need to create two sets of forms: one set that runs on the Tablet PC and the other set that runs on a standard Windows XP computer. While this might seem like a lot of work, you can use form inheritance to lighten the load. This creates a much more object oriented pattern for your code. You still need to make the decision as to which form to load up through checking a flag that gets set when the application loads up. This method has the advantage of creating cleaner code, and if you stick with the principle of never duplicating code then it can help to separate the business logic from the user interface. The disadvantage of this approach is that it creates a larger assembly file.

The Common Strategy

Whichever strategy you decide is best for your project (and they are all valid) you will need to consider some aspect of how the common language runtime (CLR) works. We want to have some code paths that run only on the Tablet PC and those paths would not only fail to run on a platform other than Windows XP Tablet PC Edition, but they may also fail to compile. Remember that the Microsoft intermediate language (MSIL) you create when you build a managed assembly is compiled by a just-in-time (JIT) compiler before it gets run. If our application is not running on a Tablet PC we don't want the code that is specific to Tablet PC to even get compiled. We can force this to happen by extracting all of our Tablet-specific code into methods that only get called when running on the Tablet PC. Here is an example of what not to do.

public InitializeComponents()
{
    .
    .
    .
    if (tabletEnabled)
    {
        inkOverlay = new Microsoft.Ink.InkOverlay(picCheck.Handle);
        inkOverlay.Enabled = true;
    }
}

This is the right way to do it. If the application doesn't run on a Tablet PC then the InitializeTabletComponents method never gets called. Therefore, the method doesn't get compiled unless it runs on a Tablet PC.

public void InitializeComponents()
{
    .
    .
    .
    if (tabletEnabled)
    {
        InitializeTabletComponents();
    }
}

protected void InitializeTabletComponents()
{
    inkOverlay = new Microsoft.Ink.InkOverlay(picCheck.Handle);
    inkOverlay.Enabled = true;
}

Exercise Time

Through the following exercise we are going to develop a small application that allows the user to create payment checks based on a template provided by their bank. A customer on a computer other than a Tablet PC will be able to enter the payee and amounts with a keyboard, whereas a user with a Tablet PC will be able to write the payee, amount, and sign the check, all by using a pen.

This example uses the second strategy (If–Else) and the common strategy.

Getting Started

In this first exercise we start by creating a new C# Windows Forms project to build up on it throughout the rest of the exercises in this article.

  1. Create a new C# Windows Application project called CheckWriter, as shown in Figure 1.

    Figure 1. Create a new project

  2. Add a reference to the Tablet PC Development Kit 1.7. In the Solution Explorer window, right-click the References folder in the Project, and then click Add Reference. On the .NET tab in the Add Reference dialog box, on the components list, select Microsoft Tablet PC API, version 1.7.xxxx.x. Click Select, and then click OK. The last step is shown in Figure 2.

    Figure 2. Add a reference to the Tablet PC API

  3. Change the name of the generated Form class from Form1 to CheckForm.

  4. In the Design View of the CheckForm project, add a PictureBox control to the form. In the properties pane for the PictureBox, change the Name property from pictureBox1 to picCheck. Change the Dock property to Fill by clicking the central area in the drop-down box that appears when you select the Dock property. Finally, set the Image property to something that resembles a check. I have added an image for fictional bank check on my website at https://Tablet.Roodyn.com/Samples.aspx. Your form should now look something like Figure 3.

    Figure 3. The Check Writer Form with PictureBox control

  5. Next, put three TextBox controls on the form: one each for the payee, written amount, and numeric amount. Change the Name property for these controls to Payee, Amount and NumAmount respectively, as illustrated in Figure 4.

    Figure 4. Add the TextBox controls

  6. Compile and run the application. We now have a form with a check we can fill in.

Is this a Tablet PC?

Now, we can put in place the code that will determine if we run this application for a Tablet PC or for another type of computer.

  1. In the CheckForm class we will add a method called IsRunningOnTablet. This method calls the Win32 API GetSystemMetrics to discover if the device is a Tablet PC. We will then store this result in a member variable, so we don't have to make the API call each time we need to know if the application is running on a Tablet PC.

    using System.Runtime.InteropServices;
    .
    .
    .
    [DllImport("user32.dll")]
    private static extern int GetSystemMetrics(int nIndex);
    // System metric constant for Windows XP Tablet PC Edition
    private const int SM_TABLETPC = 86;
    private readonly bool tabletEnabled;
    
    protected bool IsRunningOnTablet()
    {
        return (GetSystemMetrics(SM_TABLETPC) != 0);
    }
    
  2. We will call this method from the constructor of the CheckForm class.

    public CheckForm()
    {
        tabletEnabled = IsRunningOnTablet();
    
        InitializeComponent();
    }
    

Enabling Controls

Now that we know if we are running on a Tablet PC, we can determine which way we want the form to look. If we are not running on a Tablet PC we want the form to be pretty much the way we've designed it so that the user can enter the values in the TextBox controls. On the other hand, if a user has a Tablet PC they won't need the TextBox controls, so we should not enable them. Instead, we should allow the user to write on the check.

  1. Start by setting up the PictureBox to accept ink input if the application is running on a Tablet PC. We'll need the InkOverlay object elsewhere in our class, so we'll make it a member variable of the class. Remember, the common strategy we discussed earlier means we will have to create a separate method.

    private InkOverlay inkOverlay;
    public CheckForm()
    {
        tabletEnabled = IsRunningOnTablet();
        InitializeComponent();
    
        if (tabletEnabled)
        {
            InitializeTabletComponents();
        }
    }
    
    protected void InitializeTabletComponents()
    {
        inkOverlay = new Microsoft.Ink.InkOverlay(picCheck.Handle);
        inkOverlay.Enabled = true;
    }
    
  2. Next, we are going to do something we're not supposed to do. We are going to edit the InitializeComponnent method. The comments tell us not to modify the contents of the method, but it's just code right? As long as we know what we are doing we should be just fine.

    We are going to extract the initialization of the controls we want only in a non-Tablet PC application. We will put this code in a separate method called InitializeNonTabletControls.

    private void InitializeNonTabletComponents()
    {
        this.Payee = new System.Windows.Forms.TextBox();
        this.Amount = new System.Windows.Forms.TextBox();
        this.NumAmount = new System.Windows.Forms.TextBox();
        // 
        // Payee
        // 
        this.Payee.Location = new System.Drawing.Point(176, 112);
        this.Payee.Name = "Payee";
        this.Payee.Size = new System.Drawing.Size(360, 20);
        this.Payee.TabIndex = 1;
        this.Payee.Text = "<Payee>";
        // 
        // Amount
        // 
        this.Amount.Location = new System.Drawing.Point(224, 168);
        this.Amount.Name = "Amount";
        this.Amount.Size = new System.Drawing.Size(392, 20);
        this.Amount.TabIndex = 2;
        this.Amount.Text = "<Amount>";
        // 
        // NumAmount
        // 
        this.NumAmount.Location = new System.Drawing.Point(672, 240);
        this.NumAmount.Name = "NumAmount";
        this.NumAmount.Size = new System.Drawing.Size(96, 20);
        this.NumAmount.TabIndex = 3;
        this.NumAmount.Text = "<NumAmount>";
    
        this.Controls.Add(this.NumAmount);
        this.Controls.Add(this.Amount);
        this.Controls.Add(this.Payee);
    
        this.Payee.BringToFront();
        this.Amount.BringToFront();
        this.NumAmount.BringToFront();
    
    }
    
  3. We will call this method in the constructor of our CheckForm class.

    public CheckForm()
    {
        InitializeComponent();
    
        tabletEnabled = IsRunningOnTablet();
    
        if (tabletEnabled)
        {
            InitializeTabletComponents();
        }
        else
        {
            InitializeNonTabletComponents();
        }
    
    }
    
  4. Compile and run this application. On a Tablet PC you should be able to write directly on the check. If you run it on a computer that is not a Tablet PC, you can enter the values in the TextBox controls.

Printing

  1. Let's now try to use the data entered to actually do something. For this little application we will just print out the entered fields. If this is text in the text box then we will print that on the check, if it is Ink that has been handwritten we will want to print that out instead. Don't forget the common strategy applies here so we will create a separate method for printing the Ink. This method will only get called if the application is running on a Tablet PC.

    private PrintDocument prnDoc;
    
    private void PrintInk(Graphics g)
    {
        inkOverlay.Renderer.Draw(g,
        inkOverlay.Ink.Strokes);
    }
    
    private void prnDoc_PrintPage(object sender, 
        PrintPageEventArgs e)
    {
        e.Graphics.DrawImage(picCheck.Image, 0, 0);
    
        if (tabletEnabled)
        {
            PrintInk(e.Graphics);
        }
        else
        {
            int yOffset = 20; 
            int xOffset = 5;
    
            Brush brsh = Brushes.Blue;
    
            e.Graphics.DrawString(Payee.Text,
                Payee.Font,brsh,
                (Payee.Left+2) + xOffset,
                yOffset+ Payee.Top);
            e.Graphics.DrawString(Amount.Text,Amount.Font,
                brsh,(Amount.Left+2) + xOffset,
                yOffset + Amount.Top);
            e.Graphics.DrawString(NumAmount.Text,NumAmount.Font,
                brsh,(NumAmount.Left+2) + xOffset,
                yOffset + NumAmount.Top );
        }
    }
    
  2. We also need to add a button to the form to allow the user to print it out.

    private void printBtn_Click(object sender, System.EventArgs e)
    {
        prnDoc = new PrintDocument();
        prnDoc.PrintPage +=
            new PrintPageEventHandler(prnDoc_PrintPage);
        prnDoc.Print();
    }
    
  3. Run this and test it. The printing might not be very accurate, but it works as a starting point to show you how to use the Ink versus text input.

Putting It All Together

You should now have a good idea of how to develop a managed Windows Form application that can run on both the traditional Windows platform and Windows XP Tablet PC Edition. As the Tablet PC becomes more widely adopted, usage will require many Windows applications to support the features of the Tablet PC.

There are some extra things you could do now. You could detect if the pen is in range and then change the interface accordingly. This way even a Tablet PC user can use the application in a traditional manner when they have their Tablet docked or are using their device as a laptop.

Biography

English born, Dr. Neil travels the world working with software companies. He loves Australia, where he spends the summer enjoying the Sydney lifestyle and helping software development teams get more productive. Dr. Neil spends his other summer each year flying between northern Europe and the USA, working with software teams and writing about his experiences. Neil brings his business and technical skills to the companies he works with to ensure he has happy customers.

You can find out more from his website https://www.Roodyn.com or you can email Dr. Neil at Neil@Roodyn.com.