MobileUsing the CE Ink Control

Using the CE Ink Control

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Inking implementations aren’t very consistent across CE platforms. Unlike the other programs written in CE, you can’t count on the CE Ink control working anywhere except on the platform for which it was written. (That’s why this example project is set up to target the emulator.) That’s the bad news. Now for the good news—If you simply wish to collect handwritten input, store it, and redisplay it, the RichInk control makes inking data fairly easy to acquire and handle. Though details differ among the CE platforms, the basic strategy is consistent:

  • Include inkx.lib in the linker specification of your project settings
  • Load the common controls
  • Specifically initialize the ink control
  • Communicate with the control using the messages IM_GETDATALEN, IM_GETDATA and IM_SETDATA
  • The PalmtopInk Example Running Under the CE Device Emulator:

    Using the Rich Ink Control to Accept Handwriting

First Steps With the RichInk Control

When you add rich ink control support to your project, make sure that you remember to add the inkx.lib to the link specifications in your project. Also, you’ll need the inking header files:

#include <Inkx.h>
#include <RichInk.h>

You must initialize the inking facility after the common controls are loaded with a call to InitInkX() and before you attempt to create an ink control. We do this initialization in InitInstance():

    hInst = hInstance;
    InitCommonControls();
    
//initialize the inking facility before creating the control
    InitInkX();

Now we’ll create an ink control by passing a special window class constant to CreateWindow(). Notice that this is the only window we create in InitInstance(), which means that it is the main frame window for our application. The window procedure for the class WC_INKX ( the window class that implements the rich ink control ) exists outside the scope of our application code. We must substitute a window procedure of our own, or we won’t be able to create application specific behaviors for the rich ink control. We do this by subclassing the rich ink control’s window procedure.

//We create a main window of class WC_INKX
hwndMain = CreateWindow(WC_INKX, 
                        NULL, 
                        WS_VISIBLE,
                        0,0,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL, NULL, hInstance, NULL );

if ( !hwndMain )        
{
    return FALSE;
}

We have to subclass the ink control’s window procedure to implement app specific behavior, so we set the new window procedure, InkProc(), with a call to SetWindowLong():

fnOldInkControl = (WNDPROC) SetWindowLong (hwndMain, 
                                           GWL_WNDPROC, 
                                           (DWORD) InkProc);

Now we need to update the ink control’s menu with some application specific menu items. Unfortunately, this code is both messy and brittle. First, we call GetInkCtrlMenu():

//Search until we find the handle of the InkControl window menu.
HMENU   hMenu = GetInkCtrlMenu(hwndMain);

GetInkCtrlMenu() needs a handle to the command bar that contains the menu, so we call GetInkCtrlCommandBar():

HWND hwndCB = GetInkCtrlCommandBar(hwndInk);

Basically, now we have to sort through all of the ink control’s child windows, and find one with the class “CBFRAME”. We use the helper function IsClass() to retrieve the window class name strings for purposes of our comparison.

// Find the Menu handle in the Ink control
hwndCurrrent = GetWindow(hwndInk,GW_CHILD);

do
{
    if(IsClass(hwndCurrrent, TEXT("CBFRAME")))    
       break;
} while (hwndCurrrent = GetWindow(hwndCurrrent, GW_HWNDNEXT));

If we are successful in finding the menu band handle, things get rational again. We append the application specific menu items, one for each ink control message we’ll be handling in our subclass procedure.

//Append user defined menu items
HMENU    hMenuPop = GetSubMenu (hMenu, 2);
AppendMenu (hMenuPop, MF_SEPARATOR, NULL, NULL);  
AppendMenu (hMenuPop, MF_STRING, IDM_INIT_CTRL, TEXT("Init Ctrl"));
AppendMenu (hMenuPop, MF_STRING, IDM_GET_DATA, TEXT("Get Data"));
AppendMenu (hMenuPop, MF_STRING, IDM_SET_DATA, TEXT("Set Data"));

Window Subclassing

If you examine the parameters to CreateWindow(), you’ll notice that the first parameter identifies the window’s class. This parameter gives the system a way to connect a window to a stored data structure which defines a particular window’s critical attributes. In many of the examples in this book, we supplied this data by initializing a WNDCLASS structure, and calling RegisterClass(). Here’s the typedef for the WNDCLASS structure:

typedef struct _WNDCLASS { 
          UINT    style; 
          WNDPROC lpfnWndProc; 
          int     cbClsExtra; 
          int     cbWndExtra; 
          HANDLE  hInstance; 
          HICON   hIcon; 
          HCURSOR hCursor; 
          HBRUSH  hbrBackground; 
          LPCTSTR lpszMenuName; 
          LPCTSTR lpszClassName; 
          } WNDCLASS; 

Notice the second member, lpfnWndProc. This member specifies the address of the main window procedure for this window class. The Windows operating system passes messages and notifications for a given window to the procedure at this address. Long ago, a clever person discovered that if you wanted to change the behavior of a window in small ways, you could replace this address of the original window procedure with a different one. We do this with the function call SetWindowLong(), which sets a new window procedure address, and returns the old one, which we save in a global variable.

The new procedure then receives “first crack” at message processing, allowing it to handle those of particular interest, and pass the rest of the messages to the original window procedure, using its save address. This strategy is called subclassing a window procedure.


Downloads

Download source code: Ink.zip – 3 kb.

About the Author

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.

In addition to writing for Developer.com, she has written several books including Making Win 32 Applications Mobile.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories