Introduction to User Interface Controls in a Windows Mobile-based Smartphone Application Using C++ and Visual Studio 2005

 

John Kennedy
Microsoft Corporation

June 2005

Applies to:
   Microsoft Visual Studio 2005
   Windows Mobile version 5.0
   Microsoft Visual C++
   Microsoft Windows

Summary: This article discusses the use of Visual Studio 2005 to develop native applications in Visual C++ for Windows Mobile 5.0 -based devices, with particular attention to dialog boxes and controls. It is aimed at developers who are new to mobile devices, but who have experience with the Microsoft Windows operating system. (15 printed pages). You can also view a video of this article.

Contents

Introduction
Creating Your Project
Creating a Dialog Box
Creating Controls
Responding to Controls
Closing Dialog Boxes
Adding Smartphone Softkeys
Conclusion

Introduction

With the release of Visual Studio 2005, smart device programmers now use the same development tools for their Pocket PC and Smartphone applications as they do for their desktop computer projects.

If you have been developing applications by using C# or Microsoft Visual Basic .NET for Windows Mobile-based devices, it's likely that you appreciate the Rapid Application Development (RAD) tools environment. It's extremely convenient to double-click a button control that you dragged from the Toolbox, and, as a result, the tool inserted the handler code into your application.

For C++ developers, the process is not quite as automatic. The C++ developer demands flexibility. What comes with flexibility, however, is extra coding that is required for creating and handling controls.

This article describes a basic application that creates a dialog box and some controls. Then, it focuses briefly on the code that is required to make use of the dialog box and controls. The article also touches on creating softkeys for the Smartphone platform.

Creating Your Project

As you know, programming a Windows Mobile-based device is very similar to programming a desktop computer Windows-based application. Your program usually has a window that displays information, such as alien spaceships or entries from a database. Controls such as buttons, text boxes and menus enable the user to interact with the program. You can add logic for moving the spaceships or processing the database contents, and there you have it — an application.

When starting a C++-based application, the key is flexibility. C++ makes it easy to develop an application in a multitude of ways. The path you take is up to you. Assume that you are using the New Project wizard in Visual Studio 2005 for Pocket PC or Smartphone, and you want to develop an application with a standard user interface (UI). The wizard creates a skeleton application for you that includes enough code to create a window and add a basic menu. On the Pocket PC, you also get an About dialog box. After that, you're on your own.

If you compile and run the skeleton code, the application displays a window — and that's about it. Adding controls to this window is probably one of the first things you will choose to do. If you've never tried to add controls in C++, it can be confusing. By default, C++ does not offer a pretty graphical editor for your controls. You can manually create the information that the program needs to display a control by creating a resource file. A simpler way to start is not with a button or textbox control, but by creating a dialog box.

It might seem counterintuitive to start your program by creating a second dialog box, but it's by far the simplest way to proceed. Here's why: after you create a dialog box, you can use the Visual Studio 2005 Resource Editor to drag controls and automatically manage resources. It is not quite as automatic as it would be in C# or Visual Basic .NET, but it's better than manually editing resource files in a text editor. The user won't know that you have created an extra dialog box because the new dialog box fills the screen and behaves as though it is the only dialog box in your application.

In this example, you will create a Windows Mobile-based application by using the Windows Mobile 5.0 SDK for Smartphone in Visual Studio 2005. The procedure for a Pocket PC-based application is similar: the Pocket PC has extra controls that will show up automatically in the Toolbox.

Creating a Dialog Box

After you have created your project, you can create the dialog box in many ways. Visual Studio 2005 is nothing if not flexible. One method is to use the Resource View.

To create a dialog box by using the Resource View

  1. On the View menu, select Resource View .

  2. In the treeview (the upper-most element), right-click your project.

  3. On the shortcut menu, click Add, and then click Resource.

  4. A dialog box should appear similar to the one as shown in the following illustration.

  5. Click here for larger image

    Click on thumbnail for larger image

  6. Expand the Dialog node, and then click the dialog box window style that is most relevant to the device display that your application is targeting.

  7. For the sake of this example, choose IDD_SMARTPHONE_PORTRAIT.

  8. Click New to create the dialog box.

Visual Studio 2005 opens the Resource Editor. If you open the Toolbox, the Visual Studio 2005 Toolbox and Dialog Editor will look similar to Figure 1.

Figure 1. Toolbox and Dialog Editor

Tip   If you have a large enough monitor so that the toolbox does not obscure the Dialog Editor, pinning the Toolbox in place is a good idea. You can pin the Toolbox in place by clicking the drawing pin icon (which looks like a pushpin) in the Toolbox title bar. Try it, if you haven't already.

If you are a mobile device programming aficionado, the sight of the new controls in the Toolbox that you can drag onto your new dialog box may be overwhelmingly exciting. I will assume you have calmed down and are ready to continue.

Creating Controls

Now for the fun part — placing and arranging the controls that make up your UI. The first application is simple. It consists of text displayed by a read-only edit control, and a check box, as shown in Figure 2. When the user selects the check box, the text changes.

Figure 2. Controls on the Dialog Editor

Figure 2 shows the controls have been dragged onto the dialog box, and the TODO text was removed.

Notice how the Properties view, which is located by default in the lower-right area of the Visual Studio development environment, changes depending on which control you select. You will change the existing caption property of the check box in the Properties pane and the read-only and border status of the edit box, as shown in Figure 3.

Figure 3. Changing the Border status in the Properties pane

At this point, the graphical design of the application is complete. Next, you will resort to writing code to implement the following tasks:

  • When the program starts, it displays the new dialog box.
  • When the user selects the check box control, the text in the edit box changes.

To achieve these tasks, you will write code in C++ that opens the new dialog box. Then, you will write the callback function to handle the various Windows Messages that are associated with it. Some of the messages will originate from the check box control and will be used to change the text.

The best place to open the new dialog box is in the handler for the default window's WM_CREATE message. At this point in the program, the default window has been created and it's safe to perform your own actions. The program will have access to the resource file that you created to store your controls using the Resource Editor, because you will add the following line.

#include "resource.h"

In Visual Studio 2005, resources are tracked separately for individual programs. For this reason, you will use resourcesp.h because you are creating this example for the Smartphone.

The start of the program's main .cpp file, therefore, looks like the following.

#include "stdafx.h"
#include "TestControlSP2.h"
#include <windows.h>
#include <commctrl.h>
#include "resourcesp.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE            g_hInst;         // current instance
HWND                 g_hWndMenuBar;         // Menu bar handle

// Forward declarations of functions included in this code module:
ATOM             MyRegisterClass(HINSTANCE, LPTSTR);
BOOL             InitInstance(HINSTANCE, int);
LRESULT CALLBACK     WndProc(HWND, UINT, WPARAM, LPARAM);

// Callback for the dialog
LRESULT CALLBACK     MyDialogProc(HWND, UINT, WPARAM, LPARAM);

Notice that the last line is the forward declaration for the dialog box's MyDialogProc callback function,. The following code example illustrates a simple implementation of the MyDialogProc function.

LRESULT CALLBACK MyDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) 
    {

         case WM_INITDIALOG:
             return true;
    }
    return false;
}

The following code example illustrates the code in the WM_CREATE handler that creates the dialog box.

DialogBox(g_hInst, MAKEINTRESOURCE(IDD_SMARTPHONE_PORTRAIT), hWnd,  (DLGPROC)MyDialogProc);

This call requires the program's global instance handle, the resource that describes the dialog box, which is converted into the correct format by using the MAKEINTRESOURCE macro, the handle to the current window, and the callback function that will handle the dialog box messages.

If you were to add this call to the code that the wizard created, it would compile and run. Then, you would see the dialog box appear as shown in Figure 4.

Click here for larger image

Figure 4. The dialog box appears on the device. Click on thumbnail for larger image.

Notice that the dialog box does not fill the screen. You can see the home screen behind the dialog box. This is sub-optimal, as they say around these parts. To make the dialog fit the screen properly, a call to the SHInitDialog function is required. This call makes the callback function appear as shown in the following code example.

LRESULT CALLBACK MyDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{

     SHINITDLGINFO shido;
       
    switch (message) 
    {

         case WM_INITDIALOG:
             ZeroMemory(&shido, sizeof(shido));
             shido.dwMask = SHIDIM_FLAGS;
             shido.dwFlags = SHIDIF_SIZEDLGFULLSCREEN;
             shido.hDlg = hDlg;
             if (!SHInitDialog(&shido))
                 return false;
             return true;
            
            break;
     }
    return false;
}

By adding this code, the application appears as shown in Figure 5.

Click here for larger image

Figure 5. The application appears on the device. Click on thumbnail for larger image.

Responding to Controls

The first half of the programming work is done: getting the dialog box and controls to appear. Now, you can turn you attention to the task of responding to the controls. The task is to detect when the user has selected the check box and to update the text in the edit box accordingly.

Controls send and receive messages, and so the program needs to handle a message from the check box and then send an update message to the edit control. The following code example will accomplish that task.

case WM_COMMAND:

wmId    = LOWORD(wParam); 
       wmEvent = HIWORD(wParam); 
          
       switch (wmId)
       {
            case IDC_CHECK1:    // This message is sent by the check // box.
         if (wmEvent == BN_CLICKED)    // And it's the click message.
         {
         // Get current state of the check box.
         if (SendDlgItemMessage(hDlg,IDC_CHECK1,BM_GETCHECK,0,0)==1)
             // It's now checked.
SendDlgItemMessage(hDlg,IDC_EDIT1,WM_SETTEXT,0,(LPARAM)L"Check box is ON");
         else
             // It's now unchecked.
             SendDlgItemMessage(hDlg,IDC_EDIT1,WM_SETTEXT,0,(LPARAM)L"Check box is OFF");
}
            
       break;
   } 

In the previous code, when a message arrives from the check box, IDC_CHECK1, the program tests to see if the received message (BN_CLICKED) implies that the user has selected the check box. If it is such a message, the code then queries the check box for its current state. If the current state is a 1, that means the box is selected and the edit box is given the message, Check box is ON to display. If the current state is 0, that means the box is not selected and an alternative message is sent.

In a more detailed program, you might program the application differently. For example, if your dialog box has a collection of check boxes that enables the user to select different options, you might wait until the dialog box is closed until the code polls all of the check boxes for their current states.

Closing Dialog Boxes

The dialog box that you've been focusing on is the main window of your application. Therefore, the user should never close it. If this dialog box were a supplementary window, such as one that contained options for the user to select, it would need to close when the user had finished selecting options.

On a Pocket PC, that would mean responding to the ID_OK message that is generated when the user taps the OK button in the upper-right portion of the screen, or by adding an OK or Cancel button. Writing code that responds to buttons is similar to writing code that responds to check boxes. The following code example handles a button click, and then closes the dialog box as a result.

case IDC_BUTTON1:
     if (wmEvent == BN_CLICKED)
     {
         EndDialog(hDlg,0);
         return false;
     }

Although you can add buttons to a Smartphone user interface with Visual Studio 2005, you can also achieve this interaction by using the softkeys at the bottom of every window to provide OK and Cancel options.

Adding Smartphone Softkeys

Every Windows Mobile-based Smartphone has two softkeys, the function of which varies from program to program. The usability design guidelines suggest that you use the left softkey for a single action, such as OK or Done, and that you use the right softkey for bringing up a menu. For sake of simplicity, this article focuses on how to assign OK and Cancel to the softkeys — exactly the functions you need if you are trying to close a dialog box containing options.

The softkeys are a menu bar control, although frequently they don't open a menu; they simply act as buttons. What is the difference between a menu bar with one item and a button? The answer is that they do not differ very much, which is a benefit for the task that you currently want to achieve.

Unfortunately, it's currently not possible to use the graphical Resource Editor to create the menus. You must manually edit the Resource Editor and enter the necessary information yourself, but it can seem a bit tricky to get it to work Here's what you need to do.

To manually create the softkey menu bar controls

First, you will add new values to the resource header file (for example, resourcesp.h). The best way to add values is to open the Resource Symbols dialog box.

  1. Right-click the resources .rc file in the Resource View, and then click Resource Symbols.
  2. Click New, as shown in the following illustration, and then type the following names:
IDR_MENU1         
IDS_MENU_OK     
IDS_MENU_CANCEL     
IDM_MENU_OK           
IDM_MENU_CANCEL    


Click on thumbnail for larger image.

The default values that are shown in the dialog box are fine to leave as is. The first symbol that you typed, IDR_MENU1, will refer to the menu bar you are creating. You will use the second two symbols for strings that will be assigned the words OK and Cancel. The last two symbols are a reference for the messages that the softkeys will return to the program.

Now it's time to edit the resource file. Unless the resource file fails to compile, you probably won't see the raw contents of a resource file, however you can right-click the .rc file in the Solution Explorer and click View Code from the pop-up menu to see the file as text. As you change between views, Visual Studio will occasionally warn you that the .rc file is open and will ask if it is OK to close it. It almost invariably is OK, so you should ignore this message.

The best place to enter the menu bar information is the .rc2 file that the New Project wizard creates for you. This resource file is specifically designed for entering information about resources that haven't been created by Visual Studio programmatically. (The format is the same as the SHMENUBAR structure created by eMbedded Visual C++.)

Here is what you need to include:

STRINGTABLE 
BEGIN
    IDS_MENU_OK             "Ok"
    IDS_MENU_CANCEL         "Cancel"
END

IDR_MENU1 RCDATA
BEGIN
    0,
    2,
    I_IMAGENONE, IDM_MENU_OK, TBSTATE_ENABLED, 
        TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, IDS_MENU_OK, 0, 
        NOMENU,
    
    I_IMAGENONE, IDM_MENU_CANCEL, TBSTATE_ENABLED, 
       TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, IDS_MENU_CANCEL, 0, 
       NOMENU,

END

The first section assigns strings to the symbols you defined earlier. The second section includes a definition of the menu bar, which can be understood according to the following table.

IDR_MENU1            The name of the menu resource
BEGIN                The start of a resource block.
0                This value would point to a submenu if one was defined.
2                The number of menus items (2 = OK and Cancel).
I_IMAGENONE            A flag that means that no images exist in this menu.
IDM_MENU_OK            The message that this menu item will create.
TBSTATE_ENABLED        The menu is active.
TBSTYLE_BUTTON        The menu acts like a button, not a pop-up menu.
TBSTYLE_AUTOSIZE        The text is sized to fit.
IDS_MENU_OK            The string to display.
0                If this menu was a pop-up, the index to the submenu.
NOMENU            A flag meaning that no submenu exists.
…
END                    The end of a resource block.

Now that the menu bar resource has been entered, you can compile your program and see it run, as shown in Figure 6.

Click here for larger image

Figure 6. The program runs on the device. Click on thumbnail for larger image.

You still need to write the code that responds when the user presses the softkeys. This is another simple case statement in the message handler of our callback, and it might look like the following code example.

case IDM_MENU_OK:
    EndDialog(hDlg,1);
        return false;
        break;

    case IDM_MENU_CANCEL:
        EndDialog(hDlg,0);
        return false;
        break;

Notice the 0 and 1 values in the EndDialog method. These values are returned by the call to DialogBox. These return values allow you to take actions depending on whether the user selects OK or Cancel, for example.

Conclusion

Visual Studio 2005 provides the mobile device developer with many controls. Most of these controls work in a very similar way to their Windows-based desktop computer counterparts. Hopefully you'll feel encouraged to try to write applications for Windows Mobile-based devices. You can even use the emulator that ships with the device SDKs if you don't have a physical device.