Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
We introduce you to the benefits of building composite applications with the Composite Application Guidance for WPF from Microsoft patterns & practices.

By Glenn Block (September 2008)
ADO.NET Data Services provide Web-accessible endpoints that allow you to filter, sort, shape, and page data without having to build that functionality yourself.

By Shawn Wildermuth (September 2008)
See how routed events and routed commands in Windows Presentation Foundation form the basis for communication between the parts of your UI.

By Brian Noyes (September 2008)
Technology changes at a lightening-fast pace. This month Howard Dierking considers how the rapid changes affect developer priorities and magazine focus.

By Howard Dierking (September 2008)
More ...
Popular Articles
ADO.NET Data Services provide Web-accessible endpoints that allow you to filter, sort, shape, and page data without having to build that functionality yourself.

By Shawn Wildermuth (September 2008)
Here we present a rundown of the various language paradigms of CLR-based languages via short language introductions and code samples.

By Joel Pobar (May 2008)
Learn how to automate custom SharePoint application deployments, use the SharePoint API, and avoid the hassle of custom site definitions.

By E. Wilansky, P. Olszewski, and R. Sneddon (May 2008)
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
More ...
Read the Blog
SQL Server 2008 supports a new data type, HierarchyID, that helps solve some of the problems in modeling and querying hier­archical information. In the September 2008 issue of MSDN Magazine, Kent Tegels introduces you to the ...
Read more!
Many people using SharePoint technologies don't realize that there is auditing support built directly into the Windows SharePoint Services (WSS) 3.0 platform. In the September 2008 issue of MSDN Magazine, Ted Pattison walks you through a ...
Read more!
The September 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Hierarchy ID: Model ...
Read more!
Silverlight 2 features a rich and robust control model that is the basis for the controls included in the platform and for third-party control packages. You can also use this control model to build controls of your own. In the August 2008 issue of MSDN Magazine, Jeff Prosise describes how to ...
Read more!
In the August 2008 issue of MSDN Magazine, Matt Milner covers several topics regarding development with Windows Workflow Foundation, some that are intended to address specific reader questions, such as how to safely share a persistence database ...
Read more!
LINQ is a powerful tool enabling quick filtering data based on a standard query language. It can tear through a structured set of data using a simple and straightforward syntax. In the August 2008 issue of MSDN Magazine, Jared Parsons demonstrates a ...
Read more!
More ...
Mobility
Optimize Your Pocket PC Development with the .NET Compact Framework
Dave Edson and John Socha-Leialoha

This article discusses:
  • Tips for making programming in the .NET Compact Framework environment easier
  • Tricks for making your Pocket PC-based apps run faster
  • Important .NET Compact Framework classes
  • .NET Compact Framework windowing issues
This article uses the following technologies:
.NET Compact Framework, Win32, C#
Programmers get away with a lot today. With .NET, at last there is a framework that allows for really easy application development. Memory is managed with so little effort from the developer that we can easily abuse it. All the neat controls, forms, windowing systems, and GDI+ capabilities make it so easy. Easy, that is, until you have to write for the Pocket PC, a 200MHz platform with achingly slow graphics (read: no acceleration), no hard drive, and only 32MB of RAM, of which you can really only use about 2 or 3MB. Add to the hardware limitations the sinking feeling you get when you dive into the .NET Compact Framework and discover that some of your favorite features are not implemented. Suddenly your dream of writing that squeaky clean C# killer app turns into a nightmare.
Don't despair. The .NET Compact Framework really can be used to write great code and great applications. As long as you take a few things into consideration and are willing to bend a rule or two, you can have your performance cake and eat it too. We have written many applications using C# for the Pocket PC, resulting in polished, slick apps that are small and fast.
This article is divided into two main sections: goodies and optimization. In the goodies part, we will go over some neat tricks that make our lives as programmers easier, using the .NET Compact Framework. In the optimization section, we will discuss techniques to increase performance, decrease load time, and decrease memory footprints. Sample code will be provided to help you turn your app into something small and fast.

Essential Goodies and Our Test Bench
When you fire up Visual Studio® .NET to build our initial application, you'll need to make sure to add these lines of code in the main form's constructor, right after the call to InitializeComponent:
#if DEBUG
    MinimizeBox = false;
#else
    MinimizeBox = true;
#endif
This lets you actually close the app rather than just minimizing it, which can come in very handy when testing outside the debugger. The next important task is to unearth the full power of the device for which you are programming.
The .NET Compact Framework is certainly true to its name—it sure is compact. Thankfully, interop services are included, and as a result you can call any of the Win32® CE APIs on the device. Since many of these APIs require a window handle, you need to get one. Until version 2.0 of .NET Compact Framework is released (which resurrects the Handle property), use these lines of code to get the hWnd:
control.Capture = true;
IntPtr hwnd = Win32.GetCapture();
control.Capture = false;
See the sidebar "The Windows CE Namespace" for the Win32 class.
Now that the Win32 API is back in your grasp, there is another very handy use of that hWnd. You can subclass your own window with a Win32 DLL written with eMbedded Visual C++® 4.0 and play hardball. But there is something to keep in mind: the interop services do not keep a DLL loaded in memory. Deep inside the bowels of P/Invoke is a LoadLibrary/FreeLibrary pair. If you want your DLL to subclass your app (or, excuse me, any app on the system), that DLL needs to stay loaded in memory until you are ready to let it go. There is a nice solution to this problem: just call LoadLibrary on the DLL when you start, and then call FreeLibrary on the DLL when you are finished. The reference count will be bumped up a notch, and P/Invoke's LoadLibrary/FreeLibrary pair will become benign.
Figure 1 MSDN Test Bench 
A lot of different topics are going to be covered in this article. We have written a Test Bench program (shown in Figure 1) that integrates many of the concepts discussed. It incorporates the Win32 class covered in the sidebar "The Windows CE Namespace" as well as the BaseControl class discussed later in this article.

Bad, Wicked Dialogs
Users and developers unaware of dialog behavior in the .NET Compact Framework can end up in La-La Land when they open a modal dialog. Dialogs in the .NET Compact Framework aren't really dialogs in the normal sense. Rather, they're simply new forms that appear on top of existing forms, but don't return control to the caller until you close the form. If you use code like this to display your dialog
  BadDialog test = new BadDialog();
   test.ShowDialog();
the dialog will appear as a top-level form. When the user switches away from your program, the fun begins. Eventually they want to get back to your app, and they go to the System | Settings | Memory | Running Programs page, where they see both your main app and the dialog box listed. If the user clicks on the top-level app instead of the dialog, the main app window will be displayed, but will be inactive as it is waiting for the dialog to be closed.
To see this happen, click the Bad Dialog button on the Test Bench application. Now go into the list of running programs from the Settings screen and activate the Test Bench application (see Figure 2). If you tap on any of the buttons, they'll just beep, as this window is disabled. You can't even close it. So your users respond in the only way they know how: a clicking fit (actually, a tapping fit on a mobile device). The way out of this pickle is to go back to the list of running programs and activate the Bad Dialog item. Just to pour salt into the wound, once you close the dialog, another application may pop up, since the z-order of the windows could have been changed while you were switching from program to program.
Figure 2 Confusing Information 
To fix these problems, as well as a few others related to the existence of multiple forms, we created a Dialogs class with a static member called ShowDialog. Here's what it does:
  • Hides the parent form in the Running Programs List by setting its caption to an empty string.
  • Sets the caption of the dialog to the same name used by the parent form, preserving the name you see in the Running Program List, no matter which dialog is topmost.
  • Traps the Activated and Closed events of the new dialog so you can keep track of which form is the "topmost" form.
  • Calls ShowDialog for the dialog.
  • Activates and brings the "topmost" form to the top whenever any of the "parent" forms is activated.
This whole process sometimes results in another application being visible momentarily when you close a modal dialog, but other than that, everything works as a user would expect. You can see how this enhanced version of ShowDialog works by clicking on the Dialog Fix button in the Test Bench application. Notice that the caption on the dialog is the same as the main form, and only one form shows up in the Running Programs List.

Chilling Out the Context Menu
Tap and hold on a typical Pocket PC-based application, and you get those dancing dots followed by a context menu. To the user, it's divine. To the programmer, it's annoying because you don't receive the MouseDown event. This means that you don't have a chance to do some pre-context menu operations, such as disallowing the context menu in the first place or changing a selection. Getting around this behavior involves playing a few tricks on the runtime. Normally, you would just set the ContextMenu property of a control to associate it with a context menu for when a user taps and holds. In our solution, we don't set the ContextMenu property of the control; instead we mimic the code built into the Control class to handle the MouseDown event and display the menu. To make this as transparent as possible for the programmer, we developed a new base control class cleverly named BaseControl. It encapsulates a lot of the behavior we want available to our custom controls. See Figure 3 for a short list of these features. The key to banishing the default behavior and replacing it with our own is to override the ContextMenu property. This way the Control.ContextMenu property is still null, while BaseControl.ContextMenu contains the information we need.
Figure 4 shows the four methods of BaseControl that get the work done: OnMouseDown, CheckContext, ShowContext, and RecognizeGesture. Your own custom control can override the OnMouseDown method if you need to change a selection or do some other action. The CheckContext method calls OnBeforeContextPopup, which in turn fires a BeforeContextPopup event. The event handler then has a chance to change or cancel the context menu, or do other application-specific things. The RecognizeGesture method does the actual work of making the dancing dots appear. ShowContext displays the context menu.
Figure 5 Context Menu Trick 
On the Test Bench program, tap the Context Menu button and you will see the screen shown in Figure 5. The top grey box control is a normally created control. Tap on it and the listbox underneath will show the relevant events. As explained, if you tap and hold, no mouse down event happens. The lower half of the screen uses a control that employs our tricks. This control behaves just like the normal control, except you get that oh-so-important mouse down event at the very moment the user touches the screen.

Designable Custom Controls
Designing custom controls for the .NET Compact Framework is not quite as easy as it is for the desktop. It would be nice to be able to use the designer to create a custom control, and then use the new control while designing your application. On the desktop this all works seamlessly because the code behind the desktop System.Windows.Form.Control object has all the designer support code built in. Microsoft.WindowsCE.Forms.Control doesn't have this code because it would eat up memory.
There is a little trick you can do to make this happen, however. When designing a new custom control, start with a form instead of a control. Now you can use all the designer tools to build your custom control. Of course, there is a price for this, but it isn't too high: when using the designer for your application (not the control), you cannot add your custom control to the designer's toolbox because you've created a form, not a control. Therefore, you will need to create an instance of the "control," using code like this:
MyControl = new MyCustomControl();
Controls.Add(MyControl);
   MyControl.Location = new Point(x, y);
   MyControl.Size = new Size(dx, dy);
   MyControl.Visible = true;
Notice that we're setting both the location and the size. When you are using a form as a control, the .NET Compact Framework will automatically set its size as appropriate for using the full screen. To get around this, you'll have to set the size yourself.
There is one little problem, however. In some cases, adding a custom control that is actually a form will cause extra flicker in the caption at the top of the screen, as it first changes to show the caption from the new form and then changes back. Getting around this involves undoing our trick. We need to change the base class back to a control again! To let the control flip-flop on whether it wants to be a form or a control, you can add a conditional compilation constant and modify the class definition for each custom control you create based on this form trick.
Figure 6 DESIGN Conditional Constant 
Figure 6 shows the conditional constant called DESIGN, which is only set in the debug version of the compilation. Modify the class definition for your custom control (form) to look something like this:
   // DESIGN is only set in Debug builds
   #if DESIGN
       public class MyCustomControl : 
          System.Windows.Forms.Form
   #else
       public class MyCustomControl : 
          System.Windows.Forms.Control
   #endif
Figure 7 Custom Control 
If the DESIGN constant isn't defined (which it shouldn't be for the release mode), your custom control will be compiled as a control instead of as a form. You can keep the DESIGN constant defined for debug mode and test in that mode. But if you want to, you can always remove it for testing your code, then add it back in so you can design the form again. If you remove and add it, you might need to recompile your application before Visual Studio recognizes again that it can design the form.
In the Test Bench application, tapping on the Custom Control button brings up a dialog with the custom control in it (see Figure 7). If you go to the source code for the Test Bench, you will see the MyCustomControl class in the Tests folder, which you are able to edit from the designer (see Figure 8).
Figure 8 MyCustomControl 

Optimization
Writing small, fast code is always a good idea, but it's critical when writing for the .NET Compact Framework. You just don't have a whole lot of memory or speed available. At design time, you need to think about what code will be executed and how your data structures are going to be used. Since memory is at a premium, it's important to avoid making copies of data. And most importantly, your code should only create data when it is needed, and discard it when finished.
Another issue to take into consideration is when to use .NET code and when to use Win32 code. There are times when it is much faster and easier to use Win32 code (really!). You will be glad to know that most of the time it is easier to use .NET code. Note, though, that we are not going to encourage you to return to the age of stone knives and bearskins just to eke a little bit better performance from your applications.
In a nutshell, the .NET Compact Framework rocks when it comes to memory management, event handling, object-oriented programming, user input, internationalization, and file I/O. The places where the .NET Compact Framework has the biggest challenges are graphics, window management, and resource management. Enormous improvements in speed and functionality may be gained by abandoning the Graphics object and going back to the Win32 GDI. When it comes to window management, .NET Compact Framework code can be tweaked to improve performance immensely. It just takes a long time to create windows. If you have a tab control with many controls across many pages, it can take eons for the tab control to initialize. This is because each and every control on every page is created at load time. Later on, we will present a replacement for the tab control class that is mainly .NET code with a sprinkling of Win32 calls that results in a tab control that loads blisteringly fast and dynamically loads or discards nonvisible pages.
One way to time your application's performance is with the Environment.TickCount property, as shown here:
   int t = Environment.TickCount;
   // <do your stuff here>
   t = Environment.TickCount - t;
Of course, nothing is that easy. Since a .NET executable is actually composed of Microsoft® intermediate language (MSIL) code, there is a just-in-time (JIT) compiler involved. The first time your function is called, it may take more time than in subsequent calls. Keep this in mind when running your tests. Also note that the emulator and the actual device can give very different opinions on the amount of time it takes to get a task done. Therefore, you will need to run all of your benchmarking tests on the actual device and not through the debugger.
Our first example harkens back to a May 1995 Microsoft Systems Journal article entitled "Grab Bag of Gotchas and Goodies for Programming in Windows." ExtTextOut is still, after all these years, the fastest way to splat text onto the screen. It can also draw with a transparent background, which labels in the .NET Compact Framework do not support (just try to put a label over a PictureBox and you'll see).
Figure 9 GDI Speed Test 
On the Test Bench program, run the GDI Speed Test. The first time you run the test, the JIT compiler will taint the results. On our device, the conventional .NET approach using DrawString took 51ms, while using the GDI method ExtTextOut took only 22ms. If you take the time to run the test a second time, you will see a tremendous improvement in speed, as shown in Figure 9.

Getting Resourceful
When it comes time to add images and text to your application, it's a good idea to use resources instead of hand-coding the text or using external files for images. Visual Studio .NET 2003 has all the tools you need to create string resources, but it lacks built-in support for managing image resources. For that you need to turn either to the ResEditor sample application that ships with Visual Studio .NET or to a third-party application. Our favorite is a free program called Resourcer for .NET that is available at www.aisto.com/roeder/dotnet. It's simple to use and comes with source code, so you can change it if you like.
The Test Bench program loads a bitmap resource into the form when you click the Resources button. Test Bench contains a class called MyResources that makes using these resources much easier. This is all you need to do to load an image:
m_image = MyResources.Icons.LoadBitmap("resTest");
You can create a string resource file simply by using the Add New Item dialog box to add a new assembly resource file. To group the resources in one place, we created a file called strings.resx in a folder called Resources. Double-click on the strings.resx file and you'll see a screen like the one shown in Figure 10. To load a string, you just do the following:
string s = MyResources.Strings["id"];
The Windows CE Namespace
To aid in writing code using the .NET Compact Framework, we've created a library of Win32 wrapper classes and .NET Compact Framework utility classes, shown in Figure A.
The current implementation of fonts in the .NET Compact Framework doesn't work very well. A glaring example of this is that you simply can't create an eight-point font on some devices. Our FontCache class takes all the pain away from fonts. It has one public method: CreateFont. There are three overloads of this method, as shown here:
FontGdi CreateFont(string Family, float Size, FontStyle Style)
FontGdi CreateFont(
    string Family, float Size, FontStyle Style, bool forceClearType)
FontGdi CreateFont(Font fromFont)
You have the option of forcing ClearType fonts even if ClearType is not selected in the Settings dialog of your device. In addition, when you ask for a font of a certain size, you get it. As you can guess, FontCache is a cache of fonts. That means that if you call CreateFont twice with the same parameters, only one instance of the actual font is created in memory. Tap the Font Test button in the Test Bench application and you will see fonts created via the Framework on the top, and fonts created with FontCache on the bottom.
The Gdi class is quite powerful. Its methods are shown in Figure B. If you use the properties of the Gdi object to change pens, brushes, and so on, then you do not need to worry about the whole SelectObject shenanigans from Win32 days. Typical usage of the Gdi object is done in the Paint event handler. The FontCache class is utilized in this example:
private void MainForm_Paint(
    object sender, System.Windows.Forms.PaintEventArgs e)
{
    using ( Gdi gdi = new Gdi (this))
    {
        gdi.TextColor = Color.Red;
        gdi.Font = FontCache.CreateFont ( "Tahoma", 12, FontStyle.Regular );
        gdi.ExtTextOut ( 100, 100, "Wardrobe Malfunction" );
    }
}

As we mentioned before, using image resources requires the use of an external resource editor. If you're using Resourcer, you can select Add File from the Add menu, enter the name of the image resource to be added, then select the file. If you run the sample application and click on the Resource Test button, you'll see both a string and an image loaded from the resource file.
Figure 10 Adding a String Resource File 
If you are writing a commercial application, there are many reasons to put your resources into a separate DLL. The most important is that you can offer various skins. If you can offer users a variety of appearances for the application, you can increase your potential customer base. We won't go into details of how to write a skinnable application, but we will assume that strings, bitmaps, and positional information all need to go into a DLL that can easily be swapped out with another that contains a different skin.
Dynamically loading a .NET DLL with the .NET Compact Framework and getting the resources out of it is not pleasant. Even if it were, you would be fast asleep by the time your resources were loaded. There is also an annoying behavior with .NET resources: if you load an image from a .NET resource, and then call Dispose, the image is gone for good. If you try to load it again from the .NET resource, the resource loader will give you back a valid Image object that won't draw itself. The only way to get that image to recache itself is to create a new instance of the resource loader (which takes time). So let's just walk away from those resources altogether and start from scratch.
Before we talk about loading bitmaps, we need to talk about rendering them. The Win32 BitBlt function still rules the world when it comes to speed—as long as you are using a device-dependent bitmap (DDB), that is. If you try to Blt out to a device-independent bitmap (DIB), you take a huge speed hit.
Inside the system directory of your Pocket PC device is a DLL that decompresses all sorts of image formats: JPG, PNG, and GIF, just to name a few. We take advantage of this functionality in a C++ DLL we wrote that can load an image resource from a DLL and return to us the information as a DDB. The DLL has two exposed functions:
HBITMAP LoadBitmapFromFilename (HDC hdc, LPCTSTR szFileName);
HBITMAP LoadBitmapFromResource (
    HDC hdc, DWORD dwResourceID, LPCTSTR pcszClass, HMODULE hModule);
The first parameter for both of these functions is a handle to device context (HDC). We will use our eponymously named Win32 class to get the HDC of the current form, and pass that into these functions. The functions then use the HDC passed in to figure out how to return a DDB to your application. The first function is easy. The second function can be cleared up with some code snippets. Here is the code in the RC file of a C++ resource DLL:
1001    PNG    DISCARDABLE    "msdn.png"
And here is the code that would load this into your application:
[DllImport("ImageLoader.dll", CharSet=CharSet.Unicode)]
public static extern IntPtr LoadBitmapFromResource(
    IntPtr hdc, uint dwResourceID, string pcszClass, IntPtr hModule);
...
hInstance = Win32.LoadLibary ( stringDLLName );
if (hInstance != IntPtr.Zero)
{
    IntPtr hdc = Win32.GetDC ( this.Handle );
    hbmp = LoadBitmapFromResource ( hdc, 1001,  
           "PNG", hInstance );
    Win32.ReleaseDC ( hwnd, hdc );
    Win32.FreeLibrary ( stringDLLName );
}
You will find that this loads a bitmap quite quickly. And since the JPG, PNG, and GIF formats are supported, your resource DLLs can be quite small. In addition to images, you can also use the Win32.LoadString method to stuff language-specific information or even positional information as strings.
Figure 11 Resource Test 
In our Test Bench application, tap the Resource Test button to see a comparison (see Figure 11). In the Resource Test screen, tap the Load button to load a bitmap from a resource DLL using the code you just saw and using a normal .NET resource call. Each piece of code loads the resource, draws it, and then discards it. The first time you run the test, you will see a stunning difference in speed. Subsequent runs are not as impressive, yet there is still a difference. For example, the next run we did resulted in a load time of 30ms for the .NET Compact Framework and 7ms for our DLL.

Load Only What You Need; Chuck What You Don't
Good optimization starts with good memory management. Our favorite method in programming .NET is Dispose. Since we do not live in the blissful world of endless memory, it is important when writing your code to think about the resources it uses. For each and every form, you want to add code for when it is activated and deactivated, as shown in the following:
protected override void OnActivated(EventArgs e)
{
    base.OnActivated (e);
}

protected override void OnDeactivate(EventArgs e)
{
    base.OnDeactivate (e);
}
In these functions, you have to be sure to load and discard anything that isn't critical to the application. This is where we load and discard our bitmaps.
The same mentality goes for objects that you create and use. Any time you can free up objects when you are not using them any more, you should let them go. And everything should be created on the fly unless it is absolutely necessary for the app's well-being. This includes the forms themselves. Forms and the controls in them take up memory. When you close down a form, dispose of the form as well.
The using keyword in C# is an excellent aid for keeping you on the disposal track. Take a look at this example:
using ( MyClass thing = new MyClass ())
{
    // Do something interesting here
}
Inside the curly braces the thing object is valid. Once program control leaves the braces, the Dispose method of thing is automatically called and any resources used by the instance of MyClass are released (although the garbage collector will still be responsible for releasing the memory occupied by the thing itself). Of course, your MyClass object must support the IDisposable interface for this to work.
Solid application architecture makes this scenario easier to implement. One paradigm of programming is known as the Mediator Model. In a nutshell, apps written in accordance with the Mediator Model are arranged so that no object knows of any objects other than those directly underneath it. Objects have no idea about their parents, and they have no idea about siblings. Communication with parent objects is accomplished solely via events, and communication with direct descendants is accomplished using methods. Keeping true to this model makes it very easy to prune off entire sections of an object tree dynamically when they are not used, and makes your memory management easier. For example, let's say your application creates an object A which itself creates and uses an object B, which in turn creates and uses an object called C.
When that part of your application is no longer needed, the top-level object A simply calls B.Dispose. B.Dispose then calls the Dispose method of the C object. As a result, all of the resources from A down are removed. If you end up reusing this code in other apps, or you move it around in your current app, as long as you stick to the rules of the Mediator Model, your initialization and cleanup of objects will be robust.
In addition to keeping the overall memory footprint small, another reason to load only what you need is to improve application load time. Every unnecessary form, bitmap, or database loaded and cached away makes for a longer load time. Since Windows® CE is an instant-on type of operating system, consumers will expect the same from their applications.
Threads are your friend when it comes to reducing load time. You can use them to build up nonessential structures while the main app is doing essential activities, such as checking for registration, logging in, displaying a splash screen, or letting the user browse a main screen. If your program has many features available from a toolbar or menu, you could load the optional features in a thread, and grow or enable the menu or toolbar to show more features as the background thread is finished loading.

Putting It All Together—the TabControlEx Class
Now it's time to put together a lot of the concepts we've discussed. TabControl in the .NET Compact Framework is packed with problems. The biggest problem is that all of the pages and their controls are loaded at startup. This means that there is a long load time before the tab control comes up, and lots of memory is used. We've written a new class, which we cleverly call TabControlEx. It's a tab control built from scratch that allows for pages to be loaded only when the user clicks on the tab. TabControlEx also allows for the page to be discarded when the user clicks on another tab. The pages will automatically resize if the Input Panel appears or disappears. There are also some other nice goodies, such as better control over the visuals. See Figure 12 for a list of the properties and methods associated with the TabControlEx class.
The biggest difference with TagControlEx is that you don't create your pages and add them to a collection. Instead, you add the types of pages to the collection:
m_tab.TabPages.Add("Tab Text", typeof(MyPage));
The TabControlEx class has a private method called LoadPage that uses this type information to create an instance of the control. Inside the LoadPage method is the code that is responsible for creating the actual object:
private Control LoadPage(Type type, TabPageEx tab) {
    ...
    ConstructorInfo constructor = type.GetConstructor(new Type[] {});
    Control page = (Control) constructor.Invoke(null);
    ...
    return page;
}
Using reflection to get access to the default constructor and invoking it is an elegant means of creating different kinds of objects on the fly with one piece of code.
The LoadPage method contains some code that attempts to get an IPage interface (see Figure 13) from the control, and then calls the InitPage method:
IPage p = page as IPage;
if (p != null) p.InitPage(this);
You would implement the IPage interface for two reasons. First, you might want to do some initialization once your control is loaded that requires communicating with the rest of the application. The InitPage method has a single parameter, a pointer to the TabControlEx object. If your page needs to communicate with other pages on the tab, you can use the TabPages collection to find the other control if it's loaded. You can also register to receive the PageLoadedEventHandler event so you can interact with other controls after they're loaded. Finally, you could create a subclass of TabControlEx that has some extra properties or methods that your pages will use to interact with the rest of the application.
Second, not all pages will want to be resized when the input panel becomes visible. If this is the case, you can have the CanResize property of the IPage interface return false. The default behavior, resizing your page, will happen if you return true for your implementation of this property.
Sometimes you might want to know when your page becomes active or inactive so you can, for example, update values based on other changes in the application. The TabControl in the .NET Compact Framework doesn't provide an easy way to get such notification. You can get this notification very easily in your page control by implementing the IPageActivate interface (see Figure 13), which contains just two methods: Activated and Deactivated. These methods will be called only when the specific instance of your control is activated or deactivated. You can get the same information for all pages, instead of just one, using the TabControlEx's SelectedIndexChanged event. Generally when a page needs to know when it becomes active or inactive, using IPageActivate is easier. But when you want to monitor all pages from your application, SelectedIndexChanged would definitely be better.
In our Test Bench application, tapping on the Tab Control button brings up the tab control test dialog. The constructor for the TabTest object is shown in Figure 14.

Adding Toolbar Buttons to TabControlEx
The final optional interface for your tab pages provides something that is really useful, but is difficult to implement using the tab control in the .NET Compact Framework. Let's say you have a menu bar and you want to have some toolbars that are specific to each page of your tab control. With our TabControlEx it's really simple. You'll need to add an instance of MyToolbar to your form instead of Toolbar. MyToolbar is a subclass of Toolbar that supports the concepts of toolbar sections, which are toolbar buttons that can be shown or hidden as a group. The IPageToolbar interface in Figure 13 has a single method, which returns a list of toolbar sections. Figure 15 shows the code from our Test Bench sample application that sets up a couple of toolbar icons on the screen.
The code in the IPageToolbar.Sections method creates a new toolbar section if this is the first time that this method is called. We then add two icons to this section and return it. LoadIcon is just a helper function, as shown here:
private Icon LoadIcon(string name)
{
    string path = Path.Combine(Utility.HomePath, name);
    usin(Stream stream = File.OpenRead(path))
    {
        return new Icon(stream);
    }
}
LoadIcon gets the Utility.HomePath property, which is a one-liner that wraps up this tough-to-remember tangle of characters:
public static string HomePath
{
    get 
    { 
        return Path.GetDirectoryName(
            Assembly.GetExecutingAssembly().GetName().CodeBase); 
    }
}
Run the Test Bench sample and choose the Toolbar Test button. You can jump back and forth between the two tabs and see how the toolbar buttons appear only for the Test page.
You may have noticed that we used the same control, MyCustomControl, for this test with a toolbar and the Custom Control test, which did not use a toolbar. The IPageToolbar.Sections method returns null if the UseToolbar property of MyCustomControl is false, which is the default. To set this to true for the toolbar test, we use a technique we mentioned earlier for communicating with pages when they're first loaded. The constructor of the ToolbarTest form is identical to the TabTest form, except for the extra code added at the end of the constructor:
public ToolbarTest()
{
    ...
    tab.PageLoaded += new 
        TabControlEx.PageLoadedEventHandler(tab_PageLoaded);
    
    // Create an instance of our toolbar needed to allow 
    // each page to have its own toolbar buttons.
    MyToolbar toolbar = new MyToolbar();
    Controls.Add(toolbar);
}
First, we register to receive the PageLoadedEventHandler event of the tab control, which allows us to set properties of the control immediately after it's been loaded. We'll use this event to set the UserToolbar property to true:
private void tab_PageLoaded(object sender, PageLoadedEventArgs e)
{
    MyCustomControl control = e.Page as MyCustomControl;
    if (control != null) control.UseToolbar = true;
}
After creating the event handler, we add an instance of MyToolbar to the form, which is a subclass of the Toolbar class that supports toolbar sections.

Conclusion
Writing applications for the .NET Compact Framework is a wonderful experience. The applications are small enough that shareware developers can once again write something on their own and sell it online to make a little bit of money. But the simplicity and small capacity of mobile devices present new challenges to programmers. Without awesome power, you have awesome responsibility. Strive from the get-go to design code that is fast, tight, and highly, highly disposable.

Dave Edson is currently wearing the hats of massage therapist, pinball machine czar, and Software Architect for Potala Software, LLC. He lives in Seattle with his wife and two kids. Every possible spare moment is spent snow skiing or wakeboarding.

John Socha-Leialoha is the Chief Software Architect for Potala Software, LLC. His past achievements include writing the Norton Commander. He lives in Seattle with his wife and son. Every possible spare moment is spent building ridiculously small, yet terrifically accurate scale models for his model railroading hobby/business.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.