In my most recent article, I prepped you for developing real-world Windows CE applications. In this article, I’ll move forward to writing a full-fledged Windows CE configuration application.
I chose to implement a configuration application because I wanted to start off simple but still wanted you to understand how to write something more complicated than “Hello World.” The application consists of property sheets and pages, commonly known as the tabbed control. It has custom icons and tabs and can save and load preferences to and from the registry.
One key goal of the application was to remain faithful to the Microsoft user interface in the Settings folder. I took great care to make sure the fonts and layout look exactly like Microsoft applications, as this familiar appearance improves usability. Commercial developers should make it a priority to provide a seamless, integrated look for their Windows CE applications.
Grab the code
You can download the code for this example here.
The class structure
The entire code base is a mere few thousand lines, divided up into five files. I decided to implement the application using Microsoft Foundation Classes (MFC), since it was readily available on the PDA and would allow me to write fairly clean and simple code. It also allowed me to architect an application that would be easily extensible, as Figure A shows.
- · CCNETConfig: This is the main thread of the application. It starts off, sets up the registry, and associates CPageTab1, CPageTab2, and CPageTab3 objects with the CPropSheet object. It then creates a modal dialog using the property sheet. CCNETConfig is a singleton.
- · CPropertySheet: The CPropertySheet class is basically empty. MFC handles switching the property pages as you click on different tabs. CPropertySheet is a singleton.
- · CPageTab: The CPageTab classes contain code that is related to each respective tab. They perform the saving, loading, and validation of all the data present on that property page. CPageTab has a many-to-one relationship with CPropertySheet.
The user interface
By default, Embedded Tools uploads the application to the \Windows\Start Menu directory. If you upload it to the \Windows\Start Menu\Settings directory, then the icon will appear in the Settings folder. Figure B shows the application in the Programs folder.
|Our application in the Programs menu|
The configuration application has three pages, as Figure C shows. CPageTab1 handles tasks related to CNET’s news.com mobile updating service. CPageTab2 handles tasks related to preferences, and CPageTab3 handles tasks related to signing onto cnet.com.
At this point, the application does not contain any functionality. A programmer could easily move the code base towards a configuration for a wireless 802.11b driver or a Bluetooth driver configuration application.
|Property pages and their respective attributes|
Each class has its list of private member variables that maintain the status of the settings via MFC’s DDX, Do Data Exchange. I execute the UpdateData(FALSE) method during the OnInitDialog function for each of these property pages and save the values to the registry along with validating them with UpdateData(TRUE) during the OnKillActive event.
Saving the information in the registry every time the OnKillActive event occurs, even if the application terminated in the middle, would maintain the settings. Loading the settings via OnInitDialog, only once per application lifetime, keeps the code efficient.
I created the three property sheets in Figure D using the Resource Editor. The title bar follows the design of all the other applications in the Settings folder. The text below the title bar uses the same font and color as the other applications in the folder.
The line below that text was drawn using a 1-bit bitmap in a paint program and applied as a picture. The c|net logo was a 4-bit bitmap, applied as a picture. Apart from that, I made liberal use of check boxes, static text controls, edit boxes, radio buttons, and combo boxes, but I steered clear of using custom controls.
Customizing the font and style of the static text box
To get the proper font for the static text box that displays CNET Configuration, I implemented the method WindowProc in the CPageTab classes. I handled the message WM_CTLCOLORSTATIC and then replaced the current font and text color such that the static text box rendered the text accurately.
WM_CTLCOLORSTATIC passes the hDC and the hWnd of the static control about to be redrawn. I use those variables as arguments to the function in Listing A.
As you can see, I used a static variable to create the font once on the first occasion and reused the pointer to that font for all subsequent redraws. If you would prefer not to override the color and typeface of the font, you simply need not override the WindowProc method of the PropertyPages. All the rest of the code will function perfectly.
Rather than recreate the font using the proper typeface, I decided it would be more portable and OS friendly to get the system font and modify it to look like the other Windows CE Settings applications. The font is a navy blue, and using the same design decisions as above, I found that it was equivalent in color to the active border of a window.
Creating a PropertySheet with PropertyPages
You might be wondering how I created the property sheet and attached the pages along with it. I simply used the MFC ClassWizard to create four independent classes, one of them deriving from CPropertySheet and the other three deriving from CPropertyPage. In the CWinApp::InitInstance method, I added the pages to the property sheet, as shown in Listing B.
I instantiated the objects from the derivation of CPropertySheet and CPropertyPage and not from the CPropertySheet and CPropertyPage classes themselves.
I initialized all of the variables on the stack and simply associated the pages to the sheet. After that I called the DoModal method on the property sheet that handles all the GUI interaction with the user.
When InitInstance returns FALSE, MFC does not execute the Windows message pump. I could have created a modeless dialog box by executing CPropertySheet::Create() instead of CPropertySheet::DoModal(). If I did that, I would have to return TRUE so that the Windows message pump could handle the messages of normal user interaction. If I created a modeless box and returned FALSE, the application would exit.
Accessing the registry
I mentioned that in order to save and load information from the registry, I implemented the function OnInitDialog and OnKillActive for the CPageTab set of classes. The code in Listing C illustrates the implementation.
MFC trivializes access to the registry by automatically creating a root key for the application and then letting you employ user-friendly functions such as GetProfileInt, GetProfileString, WriteProfileInt, and WriteProfileString.
Figure E shows how the registry entries relate to the options presented in the GUI. I used Windows CE Remote Registry Editor in EMbedded Tools 3.0 to view the registry on the remote device.
|Keys are saved under HKEY_CURRENT_USER\Software\CNET Networks, Inc\CNETConfig\etc…|
You should also notice how the keys are saved under HKEY_CURRENT_USER\Software\CNET Networks, Inc\CNETConfig\etc…
When using MFC, traditionally the programmer uses
- · WriteProfileInt
- · WriteProfileString
- · GetProfileInt
- · GetProfileString
to store or retrieve program settings from the registry. The above functions access the registry at the following location:
HKEY_CURRENT_USER\Software\<company name>\<application name>\<section name>\<value name>.
The company name was set using SetRegistryKey. The application name was taken internally by the Visual C++ compiler, but can be overridden. The section names (News.Com, Preferences, and Sign In) and value names were taken as parameters by the above four profile functions.
You should notice that throughout the code in this article, TEXT appears in parentheses around all the strings. The reason for this is that Windows CE is implemented using Unicode exclusively. Unicode is essentially using 16 bits per character instead of 8 bits per character. This takes up considerably more space for all the strings but allows for one global operating system that supports all the languages of the world.
I could have alternatively used L”” instead of TEXT(“”), but the method I used is more portable, as it compiles differently depending on the OS. L”” signifies a Unicode string explicitly.
With the help of some remarkable libraries provided with the compiler, I was able to implement a configuration application with the ability to load and save from the registry using a small amount of code. The code is robust, efficient, and fairly extensible. I also spent a great deal of time attempting to match the GUI to the standard Windows look and feel to improve the user experience.
Next, I’ll be moving on to an application that will allow you to work with the object store database in a Windows CE environment.
More Windows CE
Do you think the Windows CE series is valuable? Post a comment below or drop us an e-mail.