Customizing the Windows CE .NET User Interface, Part 1
April 4, 2002
Recently, while preparing for a talk at an Embedded event, a bullet point in my presentation seemed to beg for elaboration: the skinnable UI for Microsoft® Windows® CE .NET. I had talked to this point at a number of conferences, but had spent little time playing with the code to produce a customized UI. Since I had a few moments in the speaker lounge (and also an article to write), it seemed a good time to take a look at how the UI can be customized for Windows CE. On my wish list: customized screen colors, Windows controls that don't look like they belong to Windows, an OK/Close button in the non-client area that looks like a smiley face, a replacement shell (perhaps based on Microsoft® Internet Explorer 5.5 browser), and a shiny bike with basket and bell for Christmas...
Okay, so on with the article; let's see what's lurking beneath the hood of the Windows CE UI.
Windows CE .NET provides the ability to create a custom user interface for your embedded design. Previous versions of Windows CE provided the ability to replace the standard shell with a custom application (ideal for building POS terminals, ATM machines, or other 'fixed function' devices) or with the Internet browser. Applications running within a custom shell on previous versions of Windows CE would still have the Windows 'look and feel'; this has now changed with Windows CE .NET.
There are a number of options for customizing the user interface of a Windows CE .NET device. These include:
- Using Microsoft Internet Explorer as the shell of the device. This is an extremely flexible option. Building highly customized user interfaces in HTML (and DHTML) is quick, simple, and flexible, and this user interface can make use of bitmaps, animation, Dynamic HTML. (See the DHTML Dude MSDN column to get some idea of the power that DHTML can bring to an HTML page.)
- Creating a stand-alone application to replace the standard shell. This is similar in concept to using the Internet Explorer browser, and can be a good option for a single-function device, such as a point-of-sale terminal. It's possible to change the appearance of controls within an application by subclassing the control, which is achieved through GetWindowLong( ) and SetWindowLong( ) API's. (Feel free to check out any of the number of articles on MSDN that discuss subclassing.)
- Altering the appearance of some of the user interface components within the operating system. This is achieved through modifying bitmaps and code.
Microsoft provides two "skins" with Windows CE .NET: Windows 95 look and Windows XP look. These provide the respective Windows look for common controls, window controls, and non-client area. The "skin" files are located in the following folder: ... \WINCE400\PUBLIC\COMMON\OAK\DRIVERS\SKINNABLEUI. The code provided in the skinnableui folder can be altered (since it's in the 'Public' tree) and shipped with your device, providing a completely custom appearance for your device.
Take a look in the skinnableui folder. There are two sets of user interface components: GWE/COMMCTRL (Windows 95 appearance) and GWEXP/COMMCTRLXP (Windows XP).
Here's how we choose which of the 'skins' is applied to our device:
- Build an operating system image using the default catalog options; this will give a Windows 95 appearance.
- Select the Windows XP skin from the catalog. This can be found in the following folder: Core O/S/Display Based Devices/Shell and User Interface/User Interface/Customizable UI/Windows XP-like sample skin. This component can be added to a project by dragging from the catalog, or by setting the SYSGEN_XPSKIN environment variable.
How do these skins differ? Let's take a look at some of the UI differences. We can then take a look at how to further customize the Windows CE .NET user interface.
Figure 1. Windows 95 appearance, using the standard skin
Figure 2. Windows XP appearance, using the Windows XP-like sample skin
Let's now examine how we modify the Windows CE .NET user interface. This includes:
- Changing the user interface colors.
- Modifying user interface bitmaps.
- Modifying Windows CE .NET controls appearance.
- Modifying standard UI dialogs (for instance, Startupwhich can be used to show device configuration or password screen, Out of Memory, and Touch Screen Calibration).
- Using Internet Explorer as the shell of our device.
Changing the User Interface Colors
One of the quickest and simplest ways to modify the appearance of a device is to modify some or all of the user interface colors. This is achieved through the registry. Note that the default system colors are not visible in the devices registry. To set colors to something other than the default system colors, we need to override the color map in the registry. The easiest way to do this is to build an operating system image using Platform Builder, download the image to either the emulation environment or to a reference board, use the control panel to modify the color scheme to match the colors you require for your device, and then use the remote registry editor to take a snapshot of the updated colors from the registry—after all, who wants to spend time guessing RGB values? The color map can be found in the following registry location: HKEY_LOCAL_MACHINE\SYSTEM\GWE\SysColor.
If we were to modify the system color map on a device (or emulator) and use Remote Registry Editor to extract a .REG file, here's how the extracted .REG file might look. Once we've extracted the registry information we can then add this to our Project.REG file and rebuild our platform to take advantage of the new color scheme.
[HKEY_LOCAL_MACHINE\SYSTEM\GWE] "SysColor"=hex:E0,E0,E0,00, 00,80,80,00, 80,80,FF,00, 80,80,80,00, C0,C0,C0,00, FF,FF,FF,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, FF,FF,00,00, C0,C0,C0,00, C0,C0,C0,00, 80,80,80,00, 00,00,FF,00, 00,00,FF,00, C0,C0,C0,00, 80,80,80,00, 80,80,80,00, 00,00,00,00, C0,C0,C0,00, FF,FF,FF,00, 00,00,00,00, DF,DF,DF,00, 00,00,00,00, FF,FF,E1,00
The .REG file contents directly map to the following colors on the device (note that each color is represented by 4 hex digits).
Note that COLOR_BTNFACE is used throughout the Windows XP-like user interface as the color for caption bars and the like.
Modifying User Interface Bitmaps
Another way to get a UI facelift is to alter some of the standard UI bitmaps.The bitmaps contain the toolbar button images used in standard Windows CE .NET applications, such as Microsoft® Windows Explorer. The toolbar bitmaps can be found in the following folder: \WINCE400\public\common\OAK\FILES.
- Stdsm.bmp: Windows 95-like toolbar bitmaps.
- Stdsmxp.bmp: Windows XP-like toolbar bitmaps.
- Viewsm.bmp: Windows 95 'file' toolbar bitmaps.
- Viewsmxp.bmp: Windows XP 'file' toolbar bitmaps.
So how about the 'Close' button on an applications caption bar? Can we also modify how this looks? Yes, absolutely. Let's look at how to change the Windows XP look.
This isn't as simple as it first seems. There are two parts to the Close button. The first is the background bitmap, which can be found in C:\wince400\public\common\OAK\DRIVERS\SKINNABLEUI\GWEXP\GCACHEVIEWXP\RES—the file is Closebutton.bmp. This contains the button background in up, down, selected, and disabled form. So how does the white "X" get added to the button?
Right, lets roll up them sleeves and dig into some of the skinnableui source code… Perhaps the first stop on the tour of the code is the initialization function for the XP skin code. The code is wrapped into a class called CacheView_t. The initialization function is called Init( ). The CacheView_t::Init( ) function is located in SKINNABLEUI\GWEXP\GCACHEVIEWXP\gcacheviewxp.cpp. Let's take a look at some of the code. This is where the GDI object cache is setup.
// close button HBITMAP hbmCloseButton = NULL; hbmCloseButton = LoadBitmapW_I (hInstance, \ MAKEINTRESOURCE(GWES_CLOSEBUTTON)); ASSERT (hbmCloseButton); g_cacheview.hdcCloseButton = Gdi::CreateCompatibleDC_I(NULL); ASSERT(g_cacheview.hdcCloseButton); Gdi::SelectObject_I(g_cacheview.hdcCloseButton, \ (HGDIOBJ)hbmCloseButton);
Here's what's happening: We're loading a bitmap resource "GWES_CLOSEBUTTON" and selecting this into a cached device context
g_cacheview.hdcCloseButton, so that this is ready to use later. GWES_CLOSEBUTTON is defined in gcacheviewxp.res as follows:
GWES_CLOSEBUTTON BITMAP res\\CloseButton.bmp
The bitmap is getting loaded from SKINABLEUI\GWEXP\GCACHEVIEWXP\RES.
So, we've loaded the background bitmap. We now need to figure out where
g_cacheview.hdcCloseButton is getting used. Since the caption bar is part of the non-client area, it's a safe bet that we will find the code we're looking for in the following folder: SKINNABLEUI\GWEXP\NCLIENTVIEWXP. Let's take a look at nclientviewxp.cpp.
Below is part of the DrawClose( ) function. We can clearly see the call to DrawCaptionButton( ) passing in the cached hdcCloseButton. (I've skipped some lines that aren't interesting for this part of the article.) We can then see the calls to SelectObject_I(hdc, hNewBrush), which selects a white brush into the device context. We then call DrawDiagonalLine( ) twice to place the cross onto the close button. This can, of course, be easily replaced with whatever background bitmap and foreground text/figure you want.
DrawCaptionButton(hdc, lprc, wControlState, g_cacheview.hdcCloseButton); // … skip some lines… hOldBrush = (HBRUSH)Gdi::SelectObject_I(hdc, hNewBrush); DrawDiagonalLine(hdc, lprc, 1, 2, 0); DrawDiagonalLine(hdc, lprc, -1, 2, 0);
Modifying the Appearance of Windows CE .NET Controls
Ok, so we've looked at changing colors and replacing some of the standard bitmaps. These are quick and effective ways of changing the appearance of a device. Now comes the interesting bit: altering the drawing code for some of the standard windows and controls. Here's a list of the items within Windows CE .NET that support skinning:
- Scroll bar
- Button—radio, check, push
- Combo box (edit and list)
- List box
- Static controls
- Group box
- Caption bar/window border
- Command band/command bar
- Header control
- Trackbar (slider)
- Progress bar
Skinning of the above items requires modification to source code (and in some cases, modifying bitmaps).
We have two options for creating a new skin for Windows CE .NET. The first option is to make a copy of the contents of either C:\wince400\public\common\OAK\DRIVERS\SKINNABLEUI (GWE & COMMCTRL) or (GWEXP & COMMCTRLXP), and create a new skin project folder, perhaps called MYGWE and MYCOMMCTRL. We will also need to modify the DIRS file in C:\wince400\public\common\OAK\DRIVERS\SKINNABLEUI to include my new project files into the build. Then we need to replace the standard operating system build components with my new components. This is achieved through setting the SYSGEN_REPLACESKIN=1 environment variable (using Platform/Settings/Environment).
Lets examine the use of these environment variables. Here's a section of cesysgen.bat (part of the build system). We can see the first line checks for SYSGEN_REPLACESKIN. If this is set, then we skip including the standard Windows 95 and Windows XP UI components. If this is not set, then we look for SYSGEN_XPSKIN being set, and if it is, then we include the Windows XP skin components. If SYSGEN_XPSKIN is not set, then we include the Windows 95 skin components. Simple.
REM //XP or 9X UI if "%SYSGEN_REPLACESKIN%"=="1" goto ReplaceSkin if "%SYSGEN_XPSKIN%"=="1" set GWE4_COMPONENTS=%GWE4_COMPONENTS% sbcmnviewxp nclientviewxp gcacheviewxp btnctlviewxp stcctlviewxp cmbctlviewxp lbctlviewxp if not "%SYSGEN_XPSKIN%"=="1" set GWE4_COMPONENTS=%GWE4_COMPONENTS% sbcmnview nclientview gcacheview btnctlview stcctlview cmbctlview lbctlview :ReplaceSkin
To replace the default component with the component you customize, you must modify your cesysgen.bat file, and add a line similar to the following, which lists the skinning replacement libraries:
set GWE4_COMPONENTS=%GWE4_COMPONENTS% sbcmnview nclientview gcacheview btnctlview stcctlview cmbctlview lbctlview
The cesysgen.bat file can be found in the following folder: \WINCE400\public\%PROJECT_NAME%\WINCE400\%TARGETNAME%\oak\MISC. For an emulator project I'm working on, the cesysgen.bat file would be found here: \WINCE400\public\UIDemo\WINCE400\Emulator\oak\MISC.
We now understand how to remove the existing UI skin libraries (SYSGEN_REPLACESKIN) and how to add our own libraries to the build (cesysgen.bat). Let's now take a look at some of the Windows XP skin source code.
In this example, we could easily replace the 'button' focus rectangle drawing code with a solid rectangle. The Windows XP 'button' drawing code can be found in the following folder: \WINCE400\public\common\OAK\DRIVERS\SKINNABLEUI\GWEXP\BTNCTLVIEWXP. The file we're interested in is buttonviewxp.cpp. Search for
DrawFocusRect_I. This is in the ButtonView_t::DrawText( ) function. We could replace the DrawFocusRect_I function call with a call to Gdi::Rectangle_I( ) to get a solid 'focus' rectangle. I'm sure you can think of many other ways to change the appearance of some of the Windows CE .NET user interface controls.
And then we're back to bitmaps—for example, changing the look of a scrollbar is as simple as using the Windows Paint program. The following folder contains the Scrollbar bitmaps: \WINCE400\public\common\oak\drivers\skinnableui\gwexp\gcacheviewxp\res\. Take a look at ScrollThumb.bmp and ScrollShaft.bmp; these are the bitmaps for vertical and horizontal 'thumbs,' and the horizontal and vertical 'shaft.' Simply edit the bitmaps, rebuild the operating system, and you have a whole new look.
Modifying Standard UI Dialogs (Startup, Out of Memory, Touch Screen Calibration)
Windows CE .NET contains a number of standard user interface components that can be OEM customized. These include the Startup UI, the Out-of-Memory UI, and the Touch-Screen-Calibration UI.
We may have specific setup/configuration options for our device that can be handled through the Startup UI (this is typically used to prompt for a password, and display user information). The Out-of-Memory UI component can be modified to prompt a user in a specific manner, or to work with headless devices, perhaps creating a log file or sending a message across the network to alert a system administrator. (Having a UI component isn't much use on headless devices!) Touch Screen Calibration is self-explanatory.
So how do we replace/modify these components?
This requires some modification of cesysgen.bat (see Modifying the Appearance of Windows CE .NET Controls above for the location of this file), and the setting of environment variables. Here's how:
First we need to remove the ComponentName (oomui, startui, calibrui) from cesysgen.bat for each component being replaced. Below is an extract from cesysgen.bat. We can see the oomui component highlighted; remove this from cesysgen (unless you are modifying the existing code).
set GWE4_COMPONENTS=%GWE4_COMPONENTS% startup oomui oom startui cmbctl cdlctl edctl imgctl lbctl scbctl
Use GWES_REPLACE to replace components with .res files, and GWES_REPLACECOMPONENT to replace a component without a .res file.
We then need to set the environment variable GWES_REPLACE with the name of the module we are inserting into the build process to replace (in this case) oomui. The GWES_REPLACE environment variable can be set using Platform/Settings/Environment in the Platform Builder IDE.
We could, for example set GWES_REPLACE=myoomui—I know you are asking how the build system knows where to get this file. That's simple. The build system will pick up the .lib file from the following folder: C:\WINCE400\public\common\OAK\LIB\%PROCESSOR\DEBUG/RETAIL.
That's all there is to it. In this month's column, we've taken a look at how to change the user interface colors using the registry, modify user interface bitmaps for common dialogs, and modify Windows CE .NET controls appearance. I'm sure the creative side of you is just itching to get started with building customized Windows CE user interface components. We'd certainly be interested in seeing any samples you produce. As always, let us know if you'd be interested in getting more information in this area, or have suggestions for additional articles.
But wait, there's more… Next month we will take a look at how to replace the standard shell and create a flexible user interface using the Internet Explorer 5.5 browser. This is an interesting shell solution, since we can quickly build a device user interface using HTML, DHTML, and Microsoft ActiveX® Controls.
Steve Maillet is the Founder and Senior Consultant for Entelechy Consulting. Steve has provided training and has developed Windows CE solutions for clients since 1997, when CE was first introduced. Steve is a frequent contributor to the Microsoft Windows CE development newsgroups. When he's not at his computer burning up the keys, Steve can be found jumping out of airplanes at the nearest drop zone.