Export (0) Print
Expand All
Expand Minimize

Developing Stylus-Free Windows Mobile Professional Applications

4/7/2010

Larry Behrendt, Medelix Business Services, with contributions by Jim Wilson, JW Hedgehog, Inc.

January 2008

Designing your application to be easily used without a stylus can often reduce the complexity of interacting with your application and make the user more efficient overall. Similarly, as touch-screen and non-touch–screen devices continue to converge, designing your application so that users can interact with the application without a stylus dramatically increases the number of devices that can support your application. This article demonstrates how you can develop applications to reap these benefits of stylus-free design.

A download for this article will be available soon.

Windows Mobile 6 Professional

Windows Mobile 6 Classic

Windows Mobile 5.0 for Pocket PC Phone Edition

Windows Mobile 5.0 for Pocket PC

In 2005, Microsoft announced Windows Mobile 5.0, featuring enhanced support for soft-key integration and built-in keyboards. In the Windows Mobile 5.0 release announcement, Microsoft stressed that the new platform would allow its hardware partners "to develop compelling devices with improved one-handed keyboard navigation that empowers customers to access their information without a stylus."

As Microsoft predicted, the current generation of Windows Mobile devices incorporate design changes to allow for one-handed, stylus-free navigation. All of these devices incorporate soft keys, and many include full QWERTY keyboards. But while the hardware has changed to support one-handed, stylus-free navigation, the software has not kept pace. It remains difficult to create Windows Mobile software that is truly usable with only one hand. Yes, it’s easy enough to program the Windows Mobile soft keys. What’s more difficult is to design software so that all of the on-screen controls can be accessed and utilized without having to use the stylus.

In this article, we’ll look beyond the soft keys, and address the following:

  • What we mean by “one-handed operation” of mobile software
  • What standards do we have for development of one-handed software?
  • What tools does Microsoft provides for this development?
  • What are the best practices, and why should we care?

Please note that this article focuses exclusively on Windows Mobile touch screen–enabled devices such as Windows Mobile 6 Professional, Windows Mobile 6 Classic, Windows Mobile 5.0 Pocket PC, and Windows Mobile 5.0 Pocket PC Phone Edition. The lack of touch-screen support already mandates the use of stylus-free navigation for Windows Mobile 6 Standard and Windows Mobile 5.0 Smartphone devices.

We’ll be looking exclusively at techniques available in Microsoft Visual Studio® 2005 and Microsoft Visual Studio 2008 for programs targeting Windows Mobile 5.0 and Windows Mobile 6 devices. The techniques we’re demonstrating here may not work in earlier versions of Visual Studio or for programs targeting earlier Pocket PC devices.

Bb985500.note(en-us,MSDN.10).gifNote:
Just a reminder of the new device naming conventions Windows Mobile 6 introduces. The following table shows the relationship between the new and old names along with their corresponding touch screen and phone support capabilities.

New Name Old Name Touch Screen Phone

Windows Mobile Professional

Pocket PC Phone Edition

Yes

Yes

Windows Mobile Classic

Pocket PC

Yes

No

Windows Mobile Standard

Smartphone

No

Yes

Let’s start with a basic question. Why do we care about “one-handed operation” of mobile software? What’s wrong with requiring our customers to use the stylus? In desktop computing, we have to use a mouse or other pointing device—why avoid the use of a pointing device on a mobile computer?

One answer is that mobile computing has changed. Two years ago, nearly all mobile computing devices were classic PDAs, in the mold of the original Palm Pilot. The original PDAs were strictly point-and-click devices. Today, most mobile devices are “convergent” devices, combining a PDA with a cell phone (and an MP3 player, and a GPS device, and so forth). Some of these devices have keyboards, and some do not. As devices continue to converge, incorporating a stylus-free design in all of your applications provides you with a greater opportunity because devices both with and without touch-screen support are able to run your application.

In my experience, people view these new devices as "smart" cell phones, and they expect to use these devices the way they used their old cell phones—if possible, with one hand, and without using a stylus. Most people hold their phone in one hand, and dial phone numbers by pressing the phone keys with the thumb of that same hand. This allows us to operate the phone with (for example) our left hand, while using our right hand to hold something else: a pen, a steering wheel, whatever.

I'm a fan of the Palm Treo devices built for Windows Mobile. Like many of the latest Windows Mobile devices, the Treo has a built-in keyboard and is designed for one-handed operation. For example, a Treo user can look up a contact with one hand by clicking the contact's initials on the Treo's keyboard. As a Treo user, I have grown to like this kind of one-handed functionality, and I now look to perform many other tasks on the Treo in a one-handed way.

Bb985500.note(en-us,MSDN.10).gifNote:
Although locating a contact by the contact's initials is a specific feature of the Treo, all Windows Mobile 5.0 and Windows Mobile 6 devices allow you to locate a contact by typing just a portion of the contact's name.

Many Windows Mobile 5.0 and Windows Mobile 6 devices feature keyboards, and if we want to encourage our users to take advantage of their keyboards, we have to make it possible to operate our mobile software without the stylus. In this respect, mobile computing is different from desktop computing. On a desktop computer, one can easily switch between the keyboard and the pointing device (mouse); this is not the case with a mobile device. You can’t really type with the hand that’s holding the stylus, and you don’t want to continually remove the stylus from the device and replace the stylus when it’s time to use the keyboard. Mobile computing is pretty much an “either/or” proposition: during any mobile computing session, you’re either going to use the stylus or the keyboard, but not both. If you require your user to grab the stylus, you will have temporarily disabled the user’s ability to utilize the keyboard.

So, before we get into the implementation details of one-handed navigation, let's sum up. Why is it important to add one-handed navigation support to our mobile applications? Because we want to capitalize on the ease and convenience provided by the latest and greatest mobile devices on the market—those devices that incorporate keyboards and cell phones. Even more importantly, we want to meet the expectations of our customers—and increasingly, our customers expect to use mobile software like they use their cell phones, with one hand.

Bb985500.note(en-us,MSDN.10).gifNote:
With a topic as complex as one-handed navigation, the ideas and apparent best practices are constantly evolving. In order to keep the ideas discussed in this article as fresh as possible, you should regularly visit the Windows Mobile Dev Center Adapt Your App page, which is regularly updated with information and ideas around maximizing application usability by designing your applications to use adaptable features such as stylus.

Now that we’ve established that one-handed navigation is important, we need to figure out what we mean, exactly, by one-handed navigation.

The concept of one-handed navigation integrates two separate but related principles:

  1. Our users should be able to navigate through mobile programs with one hand—shifting focus from control to control—by using the Action key (sometimes also referred to as the joystick or the navigation key) integrated in the bottom center of every Windows Mobile device. In addition, if a keyboard is available on the Windows Mobile device, the user should be able to navigate through mobile programs by pressing the Tab key or the SHIFT+Tab key combination.
  2. Once a control has focus, our users should be able to utilize the control with one hand (again, primarily relying on the Action key) without having to utilize the Windows Mobile stylus to point and click.
Bb985500.note(en-us,MSDN.10).gifNote:
Much like navigating desktop computer applications without a mouse, navigating mobile applications without a stylus should logically follow the tab order to identify the next and previous controls. The tab order should behave cyclically, so that a user can tab forward and backward between controls.

These principles are simple and basic, and leave a great deal to the imagination of the programmer. Do we move forward through the program by using the Down Action key, the Right Action key, or both? And how exactly do we make our controls usable in a one-handed way? Certainly, someone must have written standards to govern how one-handed navigation should work. So, we search the Web for these standards, and we find…

… nothing. Well, almost nothing. There’s Microsoft’s Designed for Windows Mobile 5.0 Software Application Handbook for Pocket PC and Designed for Windows Mobile 6 Professional Software Application Handbook. They specify that we’re supposed to support the left and right soft keys, with the menu mapped to the right soft key and the left key used for a “common” action. Not much guidance there. This discussion of Tab Order and Focus provides a little more information but still fails to provide substantial guidance.

Palm has provided a bit more detail in its Designed for Palm Products Program Test Guide. Palm tells us that the application “must enable the Action key for one-handed operation” and that the button “should function as expected within the application with no errors, freezes or crashes.” Nothing controversial there! Palm goes on to state that users should be able to open pop-up lists with the center Select button, and that the up and down buttons should navigate within any list. That is good information, but hardly everything we need to know to implement one-handed navigation.

If we’re going to discover standards applicable to one-handed navigation, we’ll need to explore how one-handed navigation is implemented in standard Windows Mobile applications, such as Calendar, Contacts, and Microsoft Office Word Mobile. I encourage you to perform your own exploration to see if the standards you discover are the same as what I've found.

I’ll outline the standards that seem to be “out there” in a moment. First, a caveat. As I stated earlier, there is no “hard” or published standard available for one-handed navigation. This means, to me at least, that we programmers have some leeway to implement one-handed navigation in the way we think best for a given application. Later in this article, we’ll develop an application demonstrating one-handed navigation, and I’ll show you one example where I would deviate from the way one-handed navigation is typically implemented.

Here's my best effort to summarize the existing de facto standard for implementing one-handed, stylus-free navigation in Windows Mobile programs.

Although there are many caveats, Table 1 shows the general behavior of the Action key and keyboard in application navigation.

Table 1. General behavior of the Action key and keyboard in application navigation

Action Key

Right

Moves focus to the next control

If the control contains modifiable text, may be used to move the cursor within the control; see the text box description for more details

Action Key

Left

Moves focus to the previous control in tab-order

If the control contains modifiable text, may be used to move the cursor within the control; see the text box description for more details

Action Key

Down

Moves focus to the next control in tab-order

If the control contains a list of values, may be used to move the selection highlight within the list; see the combo box and list-style control descriptions for details

Action Key

Up

Moves focus to the previous control in tab-order

If the control contains a list of values, may be used to move the selection highlight within the list; see the combo box and list-style control descriptions for details

Action Key

Center

Activates click event

If the control has a concept of expanded and collapsed, may expand and collapse the control; see the combo box description for details

Keyboard

Tab

Moves focus to the next control in tab-order

Keyboard

Shift+Tab

Moves focus to the previous control in tab-order

Keyboard

Enter

Activates click event

Button and Check Box Controls

Normal (push-style) buttons, radio buttons, and check boxes are relatively simple from a navigation standpoint. As you can see in Table 2, these controls conform to the general rules of navigation with the exception of exposing a click event.

Table 2. Navigation behavior of the button control

Action Key

Right

Moves focus to the next control in tab-order

Action Key

Left

Moves focus to the previous control in tab-order

Action Key

Down

Moves focus to the next control in tab-order

Action Key

Up

Moves focus to the previous control in tab-order

Action Key

Center

Activates click event

Keyboard

Right

Moves focus to the next control in tab-order

Keyboard

Left

Moves focus to the previous control in tab-order

Keyboard

Down

Activates click event

List-Style Controls

List-style controls such as list boxes and list views are also relatively simple. Although list-style control navigation behavior is somewhat different from other controls, the behavior is very natural, as shown in Table 3.

Table 3. The navigational behavior of the list-style controls

Action Key

Right

Closes the drop-down list and moves focus to the next control preserving the current selection

Action Key

Left

Closes the drop-down list and moves focus to the previous control preserving the current selection

Action Key

Down

Moves the selection to the next item in the list; if the last item in the list is already selected, then there is no effect

Action Key

Up

Moves the selection to the previous item in the list; if the first item in the list is already selected, then there is no effect

Action Key

Center

Activates the click event

Keyboard

Tab

Same as right action key

Keyboard

Shift+Tab

Same as left action key

Keyboard

Enter

Same as center action key

Text Box Controls

Text boxes are a bit complex because they have some special behaviors. First, text boxes have three different states: no text, text with no characters selected, and text with one or more characters selected. Each state affects the navigational behavior of the text box.

In addition to having multiple states, text boxes behave differently when receiving focus depending on whether the text box contains existing text. If a text box contains existing text, then the text is selected and the cursor is positioned at the end of the text; otherwise, the cursor is simply placed at the beginning of the text box.

Table 4 shows the navigational behavior of text boxes and how the behavior differs for each state.

Table 4. Navigational behavior of the text box control

Action Key

Right

No Text: Moves focus to the next control

With Text: Moves cursor one character to the right; if cursor is right of the last character, move focus to the next control

With Selected Text: Unselect the text and move the cursor one character to the right of the last selected character; if selection includes the right-most character in the text box, position the cursor at the end of the text

Action Key

Left

No Text: Moves focus to the previous control

With Text: Moves cursor one character to the left; if cursor is left of the first character, move focus to the previous control

With Selected Text: Unselect the text and move the cursor one character to the left of the first selected character; if selection includes the left-most character in the text box, position the cursor at the beginning of the text

Action Key

Down

Moves focus to the next control

Action Key

Up

Moves focus to the previous control

Action Key

Center

No effect

Keyboard

Tab

Moves focus to the next control

Keyboard

Shift+Tab

Moves focus to the previous control

Keyboard

Enter

No effect

Combo Box Controls

Like the text box, the behavior of the combo box varies depending on the current state of the combo box. In the case of the combo box, the state is affected by whether the combo box drop-down list is opened or closed as shown in Table 5.

Table 5. Navigation behavior of the combo box control

Action Key

Right

Closes the drop-down list and moves focus to the next control without modifying the combo box control's selection

Action Key

Left

Closes the drop-down list and moves focus to the previous control without modifying the combo box control's selection

Action Key

Down

Closed: Moves focus to the next control

Opened: Moves the selection to the next item in the drop-down list; if the last item in the drop-down list is already selected, then there is no effect

Action Key

Up

Closed: Moves focus to the previous control

Opened: Moves the selection to the previous item in the drop-down list; if the first item in the drop-down list is already selected, then there is no effect

Action Key

Center

Closed: Opens the drop-down list and highlights the current selection

Opened: Closes the drop-down list making the highlighted item in the drop-down list the current selection

Keyboard

Tab

Same as right action key

Keyboard

Shift+Tab

Same as left action key

Keyboard

Enter

Same as center action key

Tab Controls

Tab controls present a special challenge because the tab control has a tab order relative to the other controls on the tab control's parent control, each tab in a tab control has a tab order relative to one another, and the child controls within each tab also have a tab order relative to the other child controls within that tab. Table 6 summarizes the navigation behavior of the tab control.

Table 6. Navigation behavior of the tab control

Action Key

Right

Moves focus to the next tab; if on last tab

Action Key

Left

Moves focus to the previous tab; if on first tab

Action Key

Down

Moves focus to the tab's child control that is first in the tab order

Action Key

Up

Moves focus to the tab's child control that is last in the tab order

Action Key

Center

No effect

Keyboard

Tab

Same as Down Action key

Keyboard

Shift+Tab

Same as Up Action key

Keyboard

Enter

No effect

Although this list of control navigational behaviors isn't exhaustive, it does provide a pretty good picture of the different types of navigational behaviors. From the controls in this list, we should be able to infer the navigational behavior of most other controls.

Now that we have a good picture of the de facto standards for one-handed navigation, we’re ready to explore how to implement these standards in our Visual Studio 2005 and Visual Studio 2008 Windows Mobile programs. Let’s set up a sample program, and see how many of these standards are implemented by Visual Studio out of the box.

Let’s develop a quick example program that we’ll use to investigate how to best develop software for one-handed, stylus-free use. Our sample program is designed as a pocket collection of famous quotations. We’ll call our program “Anjou’s Familiar Quotations.” You'll find two versions of the program as part of this article's downloads. The first version is the program with the default navigation behaviors as created by Visual Studio. The second version is the completed program with additional navigation behaviors added as described throughout this article.

Our program will be set up on two forms: a simple form with a few controls on it, and a more complicated form using a tab control with two tabs. The program will not be fully functional—we’ll take the program just as far as necessary to demonstrate our navigation techniques.

Let’s start with the more complicated form first, the one with two tabs. The two tabs look like those shown in Figure 1.

Bb985500.65693533-8dae-4fd7-bd51-b0b156bcaf81(en-us,MSDN.10).gif

Figure 1. Form1’s two tabs as they appear in the Visual Studio design view

The form as shown uses the View tab to display our saved quotations. The user can filter quotations by language and by author (we won’t actually implement these filters in this demonstration). The quotations saved in the system are selected from the Quote drop-down box. Because some of these quotations are too long to fit within the width of the drop-down box, we’ve added a read-only text box to display the complete quotation. The user can click the Edit button to edit a saved quotation. For purposes of this demonstration, the Edit button will merely toggle the read-only status of the Quote text box, allowing the text in the text box to be changed (but not actually allowing the edits to be saved in the program).

The Add tab is present to allow users to add famous quotations to the system. The user selects the language of the quotation (either English or French) from radio buttons at the top of the form, then types in the quotation and the author’s name. The user can also select a date for the quotation, using the date-time picker (you could imagine that a user might want to find quotations associated with a date such as September 11, 2001). When done, the user would save the quotation by clicking the Save button. Our sample program won’t actually save the quotation, because we don’t need to implement this functionality in order to test our program’s ability to handle one-handed navigation.

The other form contains all of the same controls as the View tab on the first form but does not include a tab control. With these two forms, we can demonstrate how to apply our one-handed navigation techniques to forms both with and without a tab control.

Next, let’s try our hand at some one-handed navigation. Start up our sample program in Visual Studio on a Pocket PC emulator, and try navigating through the program using the Tab key on your desktop computer keyboard. This will simulate what your users would experience by pressing the Tab key on their Pocket PC keyboard (assuming, of course, that they have a keyboard on their Pocket PCs). Alternatively, this will simulate the user’s clicking on the Tab key on a Pocket PC soft input panel.

In the process of testing one-handed navigation using the Tab key, we notice two things:

  1. First, the form passes focus to controls that probably should not have focus. One example of such a control is the multiline text box on the View tab. This text box is read-only—it’s there only to display the full text of a quotation selected in the Quote drop-down box. Setting the control's TabStop property to false prevents the control from receiving focus. However, if you remember, we’ve set up the Edit button to toggle the read-only state of the multiline text box. The text box’s TabStop property should be toggled as well, which is achieved by changing the code for the Edit button’s Click event to read like the following:
    if (this.textBox1.ReadOnly) 
    { 
        textBox1.ReadOnly = false; 
        textBox1.TabStop = true; 
    } 
    else 
    { 
        textBox1.ReadOnly = true; 
        textBox1.TabStop = false; 
    } 
    
    
  2. Next, the focus may not always move through the controls in a logical order. The initial navigation order depends on the order in which you add controls to the form. For example, you might place the buttons on the form prior to placing the combo boxes; therefore, the buttons receive focus before the combo boxes.

The first thing to do is adjust the tab order of the controls so that when your users employ one-handed navigation techniques, the focus will move through the form in a logical order. On Form1, you’ll probably be OK with giving the initial focus to the tab control, but you should then make certain that the next control to receive focus is the top-most control on the form, and that one-handed navigation shifts focus to controls from top to bottom and from left to right.

Using Visual Studio, you can change the navigation order by manually changing the TabIndex property of each control so that the numerical order of these indexes corresponds to the order in which each control should receive focus. Better yet, you can use a special View in Visual Studio to see the TabIndex for each of your controls. In Visual Studio design view, make certain that the form is the active control, select View on the menu bar, and then select Tab Order.

The Tab Order view makes it easy to see the TabIndex for each control. When viewing the form in the Tab Order view, you can modify the TabIndex of the controls by simply clicking each control in the same order that you would like them to appear in the tab order.

Figure 2 shows the simpler of the two forms, Form2, as it appears in the Tab Order view.

Bb985500.8796c824-37b7-49c4-8ce3-686dfdbc9cff(en-us,MSDN.10).gif

Figure 2. Form2 displayed in the Tab Order view

Figure 3 shows the more complex form, Form1, in Tab Order view.

Bb985500.177305a0-2b62-4e6e-a52d-38c932c50d46(en-us,MSDN.10).gif

Figure 3. Form1 displayed in the Tab Order view

The Tab Order view is more complicated for Form2 than for Form1, because the tab control on Form2 creates a number of parent-child relationships. The form itself is the top-level parent. Next in the parent-child hierarchy is the tab control. The tab control has two children: the View tab and the Add tab. The various controls on the two tabs—the text boxes, drop-down boxes, and so forth—are children of the View tab and the Add tab. As you can see, by using a tab control, we have set up three levels of child controls on our main form.

Once we understand that the tab control introduces three levels of “children,” we can understand why the Tab Order view of our main form displays each control’s TabIndex as it does. The 0 that each tab displays in the blue box in the upper-left corner represents the tab order of the tab control itself within the form. The TabIndex displayed in the Tab Order view for the other controls has at least three numbers and two decimal points. The first number in each blue box represents the tab control's TabIndex on the form, and the second number represents each tab page's TabIndex within the tab control; for our Form1, the View tab is first in the tab order and the Add tab is second. The number that affects the tab order of the child controls within each tab is the third number; this is the TabIndex property for the control in question. We can change the TabIndex here, just like we did with our simpler form (Form2), by clicking the blue box until the desired TabIndex is displayed in the blue box.

(The Tab Order view is only useful for setting the TabIndex properties. Once you’ve set your TabIndex properties in Visual Studio, remember to turn off the Tab Order view by going back to the menu bar and clicking View, Tab Order.)

All controls have TabIndex and TabStop properties but several controls such as the label control and panel control do not expose these properties to Visual Studio and therefore, cannot have their TabIndex property updated by Visual Studio's Tab Order view. The fact that all controls have a TabIndex property explains the somewhat unexpected four-part tab index of the two radio buttons. The panel control is a child of the Add tab and the two check boxes are children of the panel control. As a child of the Add tab, the third digit, 0 in this case, represents the panel control's TabIndex within the tab page. The fourth digit then represents each radio button's TabIndex within the panel control. So even though the panel control isn't a tab stop and does not display its own TabIndex, it is part of the parent-child hierarchy and does affect the order of the radio buttons relative to the other controls within the tab page.

Please note that where you have a group of radio buttons, as we do in the sample program, one of the radio buttons in the group must be in the checked state or you cannot navigate to any of the radio buttons. Once one of the radio buttons is set to the checked state, either programmatically or by using a stylus, you can then navigate to the radio buttons normally.

You can verify this behavior by doing a simple experiment with our sample application. Make certain that within the panel control on the Add tab, the English radio button has a TabIndex of 0, that the French radio button has a TabIndex of 1, and that both radio buttons have their Checked properties set to false. Run the sample program on your Pocket PC emulator in Visual Studio, and click the Add tab. Notice that neither of the radio buttons are checked. Now, navigate through the controls on the form using the Tab key on your keyboard. Notice that it’s now impossible to navigate to the radio buttons. Now click one of the radio buttons, then try again to navigate to each control on the form using the Tab key. You’ll now be able to pass focus to the radio buttons without any problem.

This leads us to the lesson we want to stress in this article: the default behavior of one-handed navigation is hard to predict, so you’re going to need to test to make certain that your programs work the way you expect them to work.

Let’s pause for a moment. We’ve optimized the TabIndex order for the controls in our sample program, and we’re able (more or less) to navigate from control to control using the Tab key on our keyboards. In my experience, this is where most programmers stop. Why stop here? Because the steps we’ve looked at up until now are relatively easy to implement. Besides, the Tab key is the principal method we use in desktop and Internet programming to allow users to navigate from control to control via the keyboard—it might not seem necessary to do anything more in our Windows Mobile programs. And the tools Microsoft has given us to implement one-handed navigation—the Tab Order view, the TabIndex property, and the TabStop property—are clearly designed for use with the Tab key.

Microsoft’s programming tools for one-handed navigation may be designed around use of the Tab key, but I personally believe that the Tab key is not the best tool available for one-handed navigation. Why? Because many Windows Mobile devices lack a keyboard. Other devices may have a keyboard that slides out from underneath the device, meaning that the keyboard is not always available to the user. Still other devices have an always-available keyboard, but no Tab key (the Palm Treo is an example of a keyboard without a Tab key).

Bb985500.note(en-us,MSDN.10).gifNote:
The code in this article is provided to demonstrate one possible way to implement one-handed, stylus-free navigation support. You will no doubt have other ideas of how to achieve similar results and in many cases you will likely find better ways to do so.

But all Windows Mobile devices have an Action key, usually located on the bottom center of the device. The Action key differs in design from device to device, but always allows the user to simulate pressing a keyboard left arrow, right arrow, up arrow, down arrow, and Enter key. This is the primary tool our users will employ for one-handed navigation.

Let’s start with our sample program, and see how the Action key works out of the box with the common controls we’ve used in our program. Start the program in your Windows Mobile emulator, and this time try to navigate each screen using the Action key on the emulator. As always, the program opens with initial focus given to the Edit tab. By clicking the right-side of the Action key, you can shift focus to the Add tab, and then move from the Add tab back to the Edit tab by clicking the left side of the Action key. That’s the extent of the functionality—there’s no way at the moment to use the Action key to move focus away from the tabs and to the controls on each tabbed page.

Try clicking on some of the controls in our sample program, then try using the Action key to shift focus to another control. For the most part, you’ll find that this doesn’t work: You cannot shift focus away from the drop-down boxes, the text boxes, or the list box using the Action key. You can use the Action key to shift focus from one radio button to the other, but you can’t use the Action key to shift focus from a radio button to a control outside of the panel containing the radio buttons.

In fact, only the button controls in our sample program seem to respond to the Action key to permit one-handed navigation from one control to the other. But if you look carefully, you’ll discover that even the button controls fail to fully cooperate with our efforts to implement one-handed, stylus-free navigation. As a test, click the Copy button on the Edit tab in our sample program, and then press the desktop computer's Tab key. Notice how the Tab key correctly shifts the focus from the Edit button to the Edit tab. Now, click the Edit button again to give this button focus, and this time click the bottom of the Action key. Note that the focus shifts this time, not to the Edit tab, but to the language drop-down box! What is going on here?

Clearly, we’re going to have to write some code to get the navigation control to work the way we want it to work.

Before we look at some coding techniques for the Action key, let’s look at what we can do inside of a control using the Action key. Here, the news is not so grim. If we give focus to a text box that has some text in it, then we can use the Action key to move the cursor through the text in the control. That’s not everything we want to accomplish inside of this control, but it’s a start. Similarly, if we give focus to a drop-down box, we can click the center of the Action key to open and close the drop-down box, and we can use the other Action key's directions to change the selected item in the combo box. In similar fashion, if we give focus to our list box on the Add tab, the Action key can be used to select different items in the list. We can use the Action key to shift focus from one radio button to the other, and one tab to the other.

Let’s take a look at what we can do with the Action key when the date-time control on the Add tab is given focus. Here, we should be pleased to see that Microsoft makes full use of the Action key. We can use the Action key's left or right actions to change which portion of the date (month, day, or year) is highlighted, and we can increase or decrease the selected item by using the Action key's up or down actions. We cannot use the Action key to display the date-time picker’s calendar view (the calendar that becomes visible when you click the down arrow at the right of the control), or to navigate away from the date-time picker, but the Action key does give us some good functionality within the date-time picker. We may want to enhance this functionality, but we can appreciate that this functionality exists from the outset.

Now that we’ve seen how much and how little has been provided to us by Microsoft, let’s explore some techniques we might try to enhance the use of the Action key in our sample program.

Form-Level Techniques

If you’re like me, you’ve probably accidentally clicked the Action key in Visual Studio design view. If you’ve done this, you’ve seen that the following code is automatically added to your form:

private void Form1_KeyDown(object sender, KeyEventArgs e) 
{ 
    if ((e.KeyCode == System.Windows.Forms.Keys.Up)) 
    { 
        // Up 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Down)) 
    { 
        // Down 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
    { 
        // Left 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
    { 
        // Right 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Enter)) 
    { 
        // Enter 
    } 
} 

The preceding code handles the five possible actions that a user can perform with the Action key. Better yet, it captures these actions at the form level, meaning that this code comes into play regardless of which control has the current focus on the form. This looks like it could be a useful place to start coding! Next, we need a method we can call to shift focus from one control to the other.

Luckily, Microsoft has provided us with such a method—the SelectNextControl method. This is a very powerful method for our purposes. The method takes five parameters:

  • The control where we’ll begin our search
  • A bool to indicate whether we’re moving forward or backwards from the base control
  • A bool to indicate whether we’ll ignore controls that have their TabStop properties set to false
  • A bool to indicate whether we should include nested child controls
  • A bool to indicate whether we should continue searching from the first control in the tab order after the last control has been reached

To utilize the SelectNextControl method, we’ll need to determine which control on our form has the current focus. This is easy to do in the full version of .NET, where we can look at the ActiveControl property of the current form. Sadly, this property is not available in the .NET Compact Framework. (Unfortunately, we’re going to encounter a number of properties and methods excluded from the .NET Compact Framework that would be useful in setting up one-handed navigation.) So, we’ll need to adopt a different approach. We can take advantage of the fact that each control is contained in a collection on the form—we can loop through this collection to see at any moment which control has the current focus. Then we can specify this control as the first parameter in the SelectNextControl method, and use the method to move focus to the next (or preceding) control.

Let’s see how this works, using our less complicated form (Form2) as our initial model. Open this form in design view, and then click the Action key. This should open up the code for Form2, and add a Form2_KeyDown event handler to the form. Modify the code for this form, so that it reads as follows:

private void Form2_KeyDown(object sender, KeyEventArgs e) 
{ 
    if ((e.KeyCode == System.Windows.Forms.Keys.Up)) 
    { 
        // Up 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Down)) 
    { 
        for (int i = 0; i < this.Controls.Count; i++) 
        { 
            if (this.Controls[i].Focused) 
            { 
                this.SelectNextControl(this.Controls[i], true, true, true, true); 
                break; 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
    { 
        // Left 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
    { 
        // Right 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Enter)) 
    { 
        // Enter 
    } 
} 

Note that once we locate the control with the current focus, we use the SelectNextControl method to move focus to the next control and then execute a break instruction. The break instruction exits the for loop once we’ve found the control with current focus, and moves focus to the next control. This is essential for our code, because we only want to run the SelectNextControl method once for every Action key press. If we didn't break from the loop, we might inadvertently advance the focus several times on the single Action key press.

Modify the code in Program.cs to make Form2 the startup form, run the program in your Pocket PC emulator, and confirm that you can navigate through the simple form using the Action key's down action. The Action key's down action passes focus from drop-down boxes to the buttons and back again, ignoring the multiline text box unless the ReadOnly property of the text box has been set to false. Ignore for the moment the fact that navigating through the form changes the values displayed in the drop-down boxes—we’ll take care of that problem in due time. Hooray! That looks like a big step in implementing one-handed, stylus-free navigation in Visual Studio. Time to celebrate yet?

Not yet. The code is not performing as well as it appears to perform. You can see this for yourself if you create code for the Action key's up action to go with our code for the Action key's down action. The only difference between the code for the Action key's up and down actions is in the second parameter for the SelectNextControl method: This parameter is set to true to cause focus to move forwards, and to false to cause focus to move backwards. Modify your Form2 code so that it reads like the following code:

private void Form2_KeyDown(object sender, KeyEventArgs e) 
{ 
    if ((e.KeyCode == System.Windows.Forms.Keys.Up)) 
    { 
        for (int i = 0; i < this.Controls.Count; i++) 
        { 
            if (this.Controls[i].Focused) 
            { 
                this.SelectNextControl(this.Controls[i], false, true, true, true); 
                break; 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Down)) 
    { 
        for (int i = 0; i < this.Controls.Count; i++) 
        { 
            if (this.Controls[i].Focused) 
            { 
                this.SelectNextControl(this.Controls[i], true, true, true, true); 
                break; 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
    { 
        // Left 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
    { 
        // Right 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Enter)) 
    { 
        // Enter 
    } 
} 

Run the code in the Windows Mobile emulator, and this time try to navigate through the form using the Action key's up action. All is well until you try to navigate from the Edit button, where you’ll pass focus to the multiline text box. How did this happen? The multiline text box is not set up as a tab stop, and we’ve set up the parameters for our SelectNextControl method so that we only pass focus to tab stop controls. What is going on here?

If you experiment a little bit, you’ll discover that the buttons on our sample form ignore the SelectNextControl method altogether. No matter what code you try, the Action key's down action will pass focus from a button control forward to the next control (in order of TabIndex) capable of receiving focus, regardless of the TabStop setting for that control. The Action key's up action will act in exactly the same way, but in reverse TabIndex order. If you remember our earlier discussion, this is the default behavior for the button control: The action key's down action always passes focus from the button control to the next control (in order of TabIndex), and the Action key's up action always passes focus from the button control to the previous control on the form.

So, our button controls fail to heed the instructions we’ve set up in our Form2_KeyDown method. You may have also noticed that our text box also ignores these instructions: Once the text box takes program focus, it holds onto this focus, paying no attention to our SelectNextControl method.

(Finally, if you ran the current code on our more complicated form, the one with the tab control, you would notice that you could not return focus to these tabs by using the Down navigation button.)

What we need here is a simple fix, a magic bullet to solve all of these problems. Surprisingly, there is such a quick fix: add the line “e.Handled = true” after each call to the SelectNextControl method, so that your code reads as follows:

private void Form2_KeyDown(object sender, KeyEventArgs e) 
{ 
    if ((e.KeyCode == System.Windows.Forms.Keys.Up)) 
    { 
        for (int i = 0; i < this.Controls.Count; i++) 
        { 
            if (this.Controls[i].Focused) 
            { 
                this.SelectNextControl(this.Controls[i], false, true, true, true); 
                e.Handled = true; 
                break; 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Down)) 
    { 
        for (int i = 0; i < this.Controls.Count; i++) 
        { 
            if (this.Controls[i].Focused) 
            { 
                this.SelectNextControl(this.Controls[i], true, true, true, true); 
                e.Handled = true; 
                break; 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
    { 
        // Left 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
    { 
        // Right 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Enter)) 
    { 
        // Enter 
    } 
} 

Run your code in the emulator. Success! The problems we noted earlier are all fixed. We’ve even fixed the other problem we mentioned earlier, that navigating through our form changed the values displayed in the drop-down boxes on our form. Cool! The instruction e.Handled = true has saved the day.

When you set e.Handled = true, you’re telling the Form class' default KeyDown event handler that your program has handled the KeyDown event and that you do not want the Form class to perform the default KeyDown event handling. Using e.Handled = true allows you to limit the KeyDown event handling to only that code that you specifically implement. This is a powerful and useful instruction, one that we’re going to use more than once before we finish writing our code.

Our next task is to take the navigation code we used for our simple form, and modify it for use in our more complicated form. The code must be modified, because (as we discussed earlier) the only control in the Form1 controls collection is the tab control itself – we’ll need to reach down to the controls collection on our two tabs in order to make our navigation work. This means that we must loop through two collections of controls: the collection on the tab control (representing our two tabbed pages), and the collection on each tab.

Bb985500.note(en-us,MSDN.10).gifNote:
There is actually a third Controls collection, the one on Form1 itself. In this case though, we don't need to work with the Form1 Controls collection because we're taking advantage of our a priori knowledge that the Form1 class has only one control—the tab control—and that we are working with the tab control directly.

I won’t bore you with a detailed discussion of how to set up the nested loops. Other than the addition of (1) instructions for navigating the tab control, and (2) a second for loop to examine the collection of controls on each tabbed page, the code for each tab page is the same as the code we used in Form2. Open Form1 in design view, and then click the Action key. This should open up the code for Form1, and add a Form1_KeyDown event handler to the form. Modify the code for this form, so that it reads as follows:

private void Form1_KeyDown(object sender, KeyEventArgs e) 
{ 
    if ((e.KeyCode == System.Windows.Forms.Keys.Up)) 
    { 
        if (this.tabControl1.Focused) 
            this.SelectNextControl(this.tabControl1, false, true, true, true); 
        else 
        { 
            for (int i = 0; i < this.tabControl1.Controls.Count; i++) 
            { 
                if (this.tabControl1.SelectedIndex == i) 
                { 
                    for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++) 
                    { 
                        if (this.tabControl1.Controls[0].Controls[j].Focused) 
                        { 
                            this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true); 
                            e.Handled = true; 
                            break; 
                        } 
                    } 
                    break; 
                } 
            }         } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Down)) 
    { 
        if (this.tabControl1.Focused) 
            this.SelectNextControl(this.tabControl1, true, true, true, true); 
        else 
        { 
            for (int i = 0; i < this.tabControl1.Controls.Count; i++) 
            { 
                if (this.tabControl1.SelectedIndex == i) 
                { 
                    for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++) 
                    { 
                        if (this.tabControl1.Controls[0].Controls[j].Focused) 
                        { 
                            this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true); 
                            e.Handled = true; 
                            break; 
                        } 
                    } 
                    break; 
                } 
            } 
        } 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
    { 
        // Left 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
    { 
        // Right 
    } 
    if ((e.KeyCode == System.Windows.Forms.Keys.Enter)) 
    { 
        // Enter 
    } 
} 

Test this code in the Windows Mobile emulator – remember to change the startup form for the sample program from Form2 to Form1. You should be able to navigate backwards and forwards through the controls on both tabs, using the Action Keys up and down actions.

We’re left with one remaining problem: We still can’t navigate successfully to and from the radio buttons on the Add tab. The problem appears to be with the panel control that contains the radio buttons, so the logical next step is to eliminate the panel control while retaining the radio buttons. Go ahead and do this in the sample program: In form view, switch to the Add tab, drag the two radio buttons out of the panel control, delete the panel control, and reposition the buttons. Make sure that one of the buttons has its Checked property set to true. You may also need to open the Add tab in Tab Order view and re-order the controls so that their TabIndex properties increase as you move down the form. The revised Add tab should look like that shown in Figure 4:

Bb985500.note(en-us,MSDN.10).gifNote:
If moving controls outside of their parent control as we did with the radio buttons is not an option, you can modify the code that locates the active control so that the code also searches within any container controls, such as a Panel control, instead of searching only those controls that are placed directly on the Form or Tab control.
Bb985500.fae107a0-83b2-4def-b5e5-437b786fee91(en-us,MSDN.10).gif

Figure 4. Form containing a tab control with multiple tabs displayed in Visual Studio's tab view

Run the program and try navigating through the controls on the Add tab, using the Tab key, and also the Action key's up and down actions. Now everything works! You can use the TAB key or the Action key's up and down actions to shift focus to and from the selected radio button, and you can use the Action key's left and right actions to change the selected radio button. Whew!

Control-Level Techniques

Up to this point, we have had good success using the KeyDown event and the SelectNextControl method on a form level. Why not stop here, pat ourselves on the back, and proclaim our sample program to be a complete realization of one-handed navigation?

Well, truth is, we’re about half-way there. We can now shift focus to every control in our sample program, using one-handed, stylus-free techniques. But we still want to fully utilize each control in a one-handed, stylus-free way, once the control receives focus.

Remember the rules we set forth at the beginning of this article? We want to do things like open our controls using the Enter key, or select text in a text box by shifting focus to the text box. The Action key's left and right actions are supposed to navigate away from list boxes and combo boxes, not change the item selected in these controls. The truth is, the program as currently implemented gives us some ability to utilize our controls in a one-handed way, but this ability is not yet complete.

We’ll spend a little bit of time talking about control-level techniques for three of our control types in our sample program: text boxes, combo boxes, and the date-time picker. The techniques we use to manipulate these controls should be applicable to the code you’ll write to handle most other types of controls. I’ll leave it to you to figure out how to write control-level techniques for list boxes, list views, tab controls, and the like.

Text Boxes

Take a moment to review the rules we set forth earlier for one-handed navigation of text boxes. (1) When you navigate to a text box using one-handed techniques, the text in the text box should be selected. (2) The Action key's up and down actions are supposed to navigate away from a text box in all events, but (3) the behavior of the Action key's left and right actions is supposed to depend on whether there is text in the text box, and the position of the cursor in this text. At the moment, the text boxes in our sample program satisfy criteria (2), but criteria (1) and (3) are not implemented.

How should we implement the automatic selection of text in a text box? Ideally, we would include the code for this automatic selection in our Form1_KeyDown method, along with all of our other navigation code. While this can probably be done, the code for doing so is complicated: It requires us to determine not only the type of object we are navigating away from, but also the type of object to which we are navigating. For example, if we’re using the Add tab of our sample program, when we navigate forward from the radio buttons, we’d need to check if the next control is a text box (which it is), then select the text in that text box. However, we’d have to make this check upon each navigation up and down in our program, even though we have only three text boxes in the program. That seems wasteful to me. It seems more efficient to run our automatic text selection code only when it is a text box that is about to receive focus—that way, the automatic text selection code would only run when we need it to run.

So… we've decided to implement the automatic selection of text in our text boxes by wiring a GotFocus event for these text boxes that selects all of the text in the text box each time the text box receives focus. The code we want to run when this event is fired would look like the following:

this.textBox1.SelectAll() 

So long as we stick to one-handed, stylus-free navigation, this code works just fine. But remember, our code needs to work well with both one-handed, stylus-free navigation and conventional stylus-based navigation. And if you experiment, you’ll find that wiring the SelectAll method to the GotFocus event can produce bizarre results when you use the stylus for navigation. If you set up a text box the way we’ve just described, and if there’s text in the text box, clicking the stylus in the middle of the text causes the SelectAll method to select part of the text but not all of it. You end up with a selection that looks something like that shown in Figure 5:

Bb985500.0b4657dc-5bc6-4522-8eea-b073dbe6c5ce(en-us,MSDN.10).gif

Figure 5. Result of selecting a text box using a stylus when the control forces a SelectAll on GotFocus

The text before your cursor is selected, and the text after your cursor is not selected. No one wants to see that.

How do you solve this problem? The key is to investigate where the cursor gets positioned when a text box receives focus. If the text box receives focus by means of a stylus click, then the cursor will be located at the point where the stylus touches the screen. But if the text box receives focus by means of one-handed navigation, then in most cases the cursor will be located at the beginning or at the end of the text. So we can test for the cursor location in our function, and select text only if the cursor is at the text beginning or text end. To implement this test, substitute the following code for the code you created for the GotFocus events for the three text boxes on Form1 (substituting the correct name of the text box control for the name shown in the code below):

if (this.textBox1.SelectionStart == 0 || this.textBox1.SelectionStart == this.textBox1.Text.Length) 
this.textBox1.SelectAll(); 

Next, let’s tackle the operation of the Action key's left and right actions. Remember, if there’s no text in a text box, then the Action key's left and right actions are supposed to navigate away from the text box. If there is text in the text box, then the Action key's left and right actions are supposed to move the cursor left and right through the text, navigating away from the text box only if the cursor is at the beginning or end of the text. How are we supposed to make this work?

Go back and take a look at the code we’ve written so far for the Form1_KeyDown function. We have not yet written any code for the Keys.Left and Keys.Right portions of the code. Also, the code we have written (for Keys.Up and Keys.Down) makes no distinction between types of controls. The code we now need to write must be aware of the type of control that has the current focus—for example, we only want to apply our text box rules when it is a text box that has the focus.

For this purpose, we’ll take advantage of the GetType method that’s available to determine the type of any control. The Name property of the GetType method returns a string identifying whether the control is a “TextBox,” a “ComboBox,” or some other type of control. With this method, we can apply different coding instructions depending on the type of control that has the current focus.

Modify the Keys.Left and Keys.Right if statements currently in the Form1_KeysDown function, as shown in the following code:

if ((e.KeyCode == System.Windows.Forms.Keys.Left)) 
{ 
    if (!this.tabControl1.Focused) 
    { 
        for (int i = 0; i < this.tabControl1.Controls.Count; i++) 
        { 
            if (this.tabControl1.SelectedIndex == i) 
            { 
                for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++) 
                { 
                    if (this.tabControl1.Controls[0].Controls[j].Focused) 
                    { 
                        switch (this.tabControl1.Controls[0].Controls[j].GetType().Name) 
                        { 
                            case "TextBox": 
                            // add text box code 
                            break; 
                            case "ComboBox": 
                            // add Combo box code 
                            break; 
                            case "ListBox": 
                            // add list box code 
                            break; 
                        } 
                        break; 
                    } 
                } 
                break; 
            } 
        } 
    } 
} 
if ((e.KeyCode == System.Windows.Forms.Keys.Right)) 
{ 
    if (!this.tabControl1.Focused) 
    { 
        for (int i = 0; i < this.tabControl1.Controls.Count; i++) 
        { 
            if (this.tabControl1.SelectedIndex == i) 
            { 
                for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++) 
                { 
                    if (this.tabControl1.Controls[0].Controls[j].Focused) 
                    { 
                        switch (this.tabControl1.Controls[0].Controls[j].GetType().Name) 
                        { 
                            case "TextBox": 
                            // add text box code 
                            break; 
                            case "ComboBox": 
                            // add Combo box code 
                            break; 
                            case "ListBox": 
                            // add list box code 
                            break; 
                        } 
                        break; 
                    } 
                } 
                break; 
            } 
        } 
    } 
} 

With this structure in place, we can now consider the code we want to run when a text box has focus. What we want to do is navigate away from the text box if no text is selected, the cursor is at the beginning or the end of the text, and the user selects the Action keys left or right actions. We can use the SelectedText and SelectionStart properties of the text box to determine the selected text and the cursor position… only at this point, we do not have a reference to the text box object. We only have a reference to a control object that happens to be a text box. So, we first need to cast our control object into a text box object, and then we can use the properties of the text box object to determine if we should navigate away from the text box control.

The code we need for our Keys.Left text box code is the following:

TextBox _tBox = (TextBox)this.tabControl1.Controls[0].Controls[j]; 
if (_tBox.SelectedText == "" && _tBox.SelectionStart == 0) 
{ 
    this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true); 
    e.Handled = true; 
} 
break;
 

And our Keys.Right text box code is the following:

TextBox _tBox = (TextBox)this.tabControl1.Controls[0].Controls[j]; 
if (_tBox.SelectedText == "" && _tBox.SelectionStart == _tBox.Text.Length) 
{ 
    this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true); 
    e.Handled = true; 
} 
break; 

Note that we only needed to provide code for the event where the cursor is at the beginning or end of the text in the text box, and where we wanted to move focus to a different control. We did not need to write code to cover when the cursor is supposed to move backwards or forwards through the text. The text box provides this functionality by default.

Combo Boxes

Let’s quickly review how one-handed navigation should work for combo boxes. Clicking the Enter button should open or close the combo box. If the box is closed, then the directional navigation buttons should move focus to the previous or next control. If the combo box is open, then the Action key's up and down actions should change the selected item, and the Action key's left and right actions should save the selected item and move to the previous or next control.

Note that in our sample program, the combo box control handles the Enter button in the desired manner, but the other Action key behaviors do not satisfy our specifications. The Action key's up and down actions always navigate away from the combo box—we only expect to see such navigation when the combo box is closed. The Action key's left and right actions always change the combo box’s selected item, even when the combo box is closed. According to our specifications, it should be possible to change the selected item only if the combo box is open, and then only with the Action key's up and down actions.

But these specifications are not written in stone. In our application, it’s useful to be able to change the selected quotation in the Quote combo box without actually opening the combo box. If open, the Quote combo box obscures the full text of the quotation as shown in Figure 6.

Bb985500.ba844019-39d2-49e1-8c75-c58b14957d2b(en-us,MSDN.10).gif

Figure 6. Text box obscured by the expanded combo box

So for this application, we’ll bend the rules a little bit, as Microsoft seems to be inclined to allow us to do. We’ll follow the specifications, except that we’ll allow the Action key's left and right actions to change the combo box selected item when the combo box is closed. Admittedly, this might confuse our users, as it reverses the functions performed by the Action key's up and down actions and the Action key's left and right actions, depending on whether the combo box is open or closed (closed box: left/right changes selected item, up/down navigates to another control; open box: left/right navigates to another control, up/down changes selected item). We could revise this specification so that, for example, the Action key's up and down actions always changed the combo box selected item, regardless of whether the combo box was open or closed. However, this change would interfere with the code we’ve written earlier, which consistently allows the user to navigate from control to control using the Action key's up and down actions.

Ultimately, when it comes to implementing one-handed navigation for our more complicated controls, we have to make design choices and design compromises. After all, the Action key only gives us five options to work with! You may not agree with the choice I’ve made for combo boxes in our sample application, but the important point is that you’re required to make a choice. You should follow the specifications detailed earlier in this article whenever you can, and deviate from these specifications only when it makes sense to do so.

OK. Now that we’ve settled on how we’d like our combo boxes to behave in our sample application, let’s turn our focus back to the application code. Before we make any changes to our code, we should realize that our sample program already has most of the functionality we want to implement. We can open a combo box by giving the box focus and using the Enter key. We can change the selected item in a closed combo box by using the Action key's left and right actions, and we can navigate away from a closed combo box by using the Action key's up and down actions. The only functionality we’re lacking is when the combo box is open. For open combo boxes, (1) we want the Action key's up and down actions to change the selected item, and not to navigate away from the box, and (2) we want the Action key's left and right actions to navigate away from the combo box.

So… the first thing we need to be able to do is determine whether a combo box is open or closed. While .NET provides a DroppedDown property for combo boxes, this property is not available in .NET Compact Framework. So we’re forced to rely on Windows Messaging to determine the state of our combo boxes. The following code will do the trick:

const int CB_GETDROPPEDSTATE = 0x157;
ComboBox _cBox = [the combo box control we want to target];
Message comboBoxMessage = Message.Create(_cBox.Handle, CB_GETDROPPEDSTATE, IntPtr.Zero, IntPtr.Zero);
MessageWindow.SendMessage(ref comboBoxMessage);
// if isDropped is true, then the combo box is open
bool isDropped = comboBoxMessage.Result != IntPtr.Zero;

This code sets up a bool, isDropped, that will tell us whether a particular combo box is open or closed. Whether the combo box is open or closed is determined by simply sending the Win32® Windows Message, CB_GETDROPPEDSTATE, to the combo box. You’ll need to add a reference in your sample program to "Microsoft.WindowsCE.Forms" in order for this code to work.

Let’s test the code on the Action key's down action. At the moment, the Action key's down action always navigates away from the control having current focus. Let’s modify the code for this action so that if a combo box has the current focus, we navigate away from the combo box only if it is closed. This will require us to use the Windows Messaging code shown earlier, plus the ability to distinguish between types of controls that we demonstrated earlier for the text box control. Add the using Microsoft.WindowsCE.Forms statement to the form in your sample code, and then modify the existing code for the Keys.Up and Keys.Down KeyDown events with the following:

            if ((e.KeyCode == System.Windows.Forms.Keys.Up))
            {
                if (this.tabControl1.Focused)
                    this.SelectNextControl(this.tabControl1, false, true, true, true);
                else
                {
                    for (int i = 0; i < this.tabControl1.Controls.Count; i++)
                    {
                        if (this.tabControl1.SelectedIndex == i)
                        {
                            for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++)
                            {
                                if (this.tabControl1.Controls[0].Controls[j].Focused)
                                {
                                    switch (this.tabControl1.Controls[0].Controls[j].GetType().Name)
                                    {
                                        case "ComboBox":
                                            ComboBox _cBox = (ComboBox)this.tabControl1.Controls[0].Controls[j];
                                            Message comboBoxMessage = Message.Create(_cBox.Handle, 0x0157, IntPtr.Zero, IntPtr.Zero);
                                            MessageWindow.SendMessage(ref comboBoxMessage);
                                            bool isDropped = comboBoxMessage.Result != IntPtr.Zero;
                                            if (!isDropped)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        default:
                                            this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true);
                                            e.Handled = true;
                                            break;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Down))
            {
                if (this.tabControl1.Focused)
                    this.SelectNextControl(this.tabControl1, true, true, true, true);
                else
                {
                    for (int i = 0; i < this.tabControl1.Controls.Count; i++)
                    {
                        if (this.tabControl1.SelectedIndex == i)
                        {
                            for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++)
                            {
                                if (this.tabControl1.Controls[0].Controls[j].Focused)
                                {
                                    switch (this.tabControl1.Controls[0].Controls[j].GetType().Name)
                                    {
                                        case "ComboBox":
                                            ComboBox _cBox = (ComboBox)this.tabControl1.Controls[0].Controls[j];
                                            Message comboBoxMessage = Message.Create(_cBox.Handle, 0x0157, IntPtr.Zero, IntPtr.Zero);
                                            MessageWindow.SendMessage(ref comboBoxMessage);
                                            bool isDropped = comboBoxMessage.Result != IntPtr.Zero;
                                            if (!isDropped)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        default:
                                            this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true);
                                            e.Handled = true; 
                                            break;
                                    }
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }

Run this code on your Windows Mobile emulator. You’ll see that when a combo box has focus, the Action key's up and down actions only navigate to the next control if the combo box is closed. That’s good! Next, let's change the function of the Action key's left and right actions, so that they'll navigate to the next control if the combo box is open. Modify the existing code for the Keys.Left and Keys.Right KeyDown events with the following:


            if ((e.KeyCode == System.Windows.Forms.Keys.Left))
            {
                if (!this.tabControl1.Focused)
                {
                    for (int i = 0; i < this.tabControl1.Controls.Count; i++)
                    {
                        if (this.tabControl1.SelectedIndex == i)
                        {
                            for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++)
                            {
                                if (this.tabControl1.Controls[0].Controls[j].Focused)
                                {
                                    switch (this.tabControl1.Controls[0].Controls[j].GetType().Name)
                                    {
                                        case "TextBox":
                                            TextBox _tBox = (TextBox)this.tabControl1.Controls[0].Controls[j];
                                            if (_tBox.SelectedText == "" && _tBox.SelectionStart == 0)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        case "ComboBox":
                                            ComboBox _cBox = (ComboBox)this.tabControl1.Controls[0].Controls[j];
                                            Message comboBoxMessage = Message.Create(_cBox.Handle, 0x0157, IntPtr.Zero, IntPtr.Zero);
                                            MessageWindow.SendMessage(ref comboBoxMessage);
                                            bool isDropped = comboBoxMessage.Result != IntPtr.Zero;
                                            if (isDropped)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], false, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        case "ListBox":
                                            // add list box code 
                                            break;
                                    }
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Right))
            {
                if (!this.tabControl1.Focused)
                {
                    for (int i = 0; i < this.tabControl1.Controls.Count; i++)
                    {
                        if (this.tabControl1.SelectedIndex == i)
                        {
                            for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++)
                            {
                                if (this.tabControl1.Controls[0].Controls[j].Focused)
                                {
                                    switch (this.tabControl1.Controls[0].Controls[j].GetType().Name)
                                    {
                                        case "TextBox":
                                            TextBox _tBox = (TextBox)this.tabControl1.Controls[0].Controls[j];
                                            if (_tBox.SelectedText == "" && _tBox.SelectionStart == _tBox.Text.Length)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        case "ComboBox":
                                            ComboBox _cBox = (ComboBox)this.tabControl1.Controls[0].Controls[j];
                                            Message comboBoxMessage = Message.Create(_cBox.Handle, 0x0157, IntPtr.Zero, IntPtr.Zero);
                                            MessageWindow.SendMessage(ref comboBoxMessage);
                                            bool isDropped = comboBoxMessage.Result != IntPtr.Zero;
                                            if (isDropped)
                                            {
                                                this.SelectNextControl(this.tabControl1.Controls[0].Controls[j], true, true, true, true);
                                                e.Handled = true;
                                            }
                                            break;
                                        case "ListBox":
                                            // add list box code 
                                            break;
                                    }
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }

Hooray! With a little bit of help from Windows Messaging, we were able to implement our planned one-handed navigation of the combo box control. We’ll finish this article by looking at one last control—the DateTimePicker.

DateTimePicker

The DateTimePicker is a great example of a control that forces programmers to make difficult design choices when it comes to one-handed, stylus-free navigation. As we noted earlier, the DateTimePicker does a good job out of the box of supporting one-handed, stylus-free navigation: The Action key's left and right actions can be used to change which portion of the displayed date (month, day, or year) is selected at any time, and the Action key's up and down actions can be used to change the selected value. Unfortunately, this configuration does not permit the user to navigate away from the DateTimePicker by using the Action key.

Our code adopts a different approach: The code we've already added to our sample program allows the user to navigate away from the DateTimePicker by using the Action key's up and down actions (our code has not affected the Action key's left and right actions; these actions will continue to change the portion of the date that is selected). Because we're using the Action key's up and down actions for navigation, we can no longer use these actions to change the value selected in the DateTimePicker control. Of course, if our users have a numeric keyboard available to them on their devices, they can type a new day or month value in place of the selected value… so maybe the approach we’ve taken in our control represents a sensible compromise. Or maybe you’d prefer to use the Action key's left and right actions to advance (or roll back) the date shown in the control by a single day. If so, I’ll leave it to you to develop the requisite code. The point is, you’ll need to exercise your judgment as a programmer to adopt the one-handed navigation behavior that best suits your program.

As noted earlier, the DateTimePicker does nothing to support the Action key's enter action. It would be useful if the Action key's enter action opened the calendar portion of the DateTimePicker control—our users might then use the Action key's left, right, up, and down actions to change the date displayed on the calendar, then select the Action key's enter action to close the calendar and save the date. Once again, we face the situation where the methods we need to drop down and close up the DateTimePicker are not exposed in the .NET Compact Framework. So, we could adopt an approach similar to the one we used for combo boxes, and use Windows Messaging to open and close our DateTimePicker (for example, see the article at http://blog.opennetcf.org/ayakhnin/SearchView.aspx?q=datetimepicker).

However, this time let’s try something different: We’ll simulate a mouse click on the down arrow to the far right side of the DateTimePicker, the arrow you click to open the calendar. This can prove to be a handy technique to have in the arsenal when you’re programming in the .NET Compact Framework. To make this work, add a using System.Runtime.InteropServices statement to Form1, and then add the following enum declaration and p/invoke code near the beginning of Form1 (for example, after the form constructor):

enum MouseFlags
{
    LeftDown = 0x0002,
    LeftUp = 0x0004,
    Absolute = 0x8000,
}
[DllImport("coredll.dll", SetLastError = true)]
static extern void mouse_event(MouseFlags dwFlags, int dx, int dy, int dwData, UIntPtr dwExtraInfo);

Finally, add the following code to the Keys.Enter portion of the Form1_KeysDown function:

if (!this.tabControl1.Focused)
{
    for (int i = 0; i < this.tabControl1.Controls.Count; i++)
    {
        if (this.tabControl1.SelectedIndex == i)
        {
            for (int j = 0; j < this.tabControl1.Controls[0].Controls.Count; j++)
            {
                if (this.tabControl1.Controls[0].Controls[j].Focused &&
this.tabControl1.Controls[0].Controls[j].GetType().Name == "DateTimePicker")
                {
                    Control _ctl = this.tabControl1.Controls[0].Controls[j];
                    Point p = this.PointToScreen(new Point(_ctl.Right - 5, _ctl.Bottom - 5));
                    int m1 = (int)(65535 / Screen.PrimaryScreen.Bounds.Width);
                    int m2 = (int)(65535 / Screen.PrimaryScreen.Bounds.Height);
                    int x = m1 * p.X;
                    int y = m2 * p.Y;
                    mouse_event(MouseFlags.Absolute | MouseFlags.LeftDown, x, y, 0, UIntPtr.Zero);
                    mouse_event(MouseFlags.Absolute | MouseFlags.LeftUp, x, y, 0, UIntPtr.Zero);
                    e.Handled = true;
                    break;
                }
            }
            break;
        }
    }
}

Bb985500.note(en-us,MSDN.10).gifNote:
I'd like to point to Daniel Moth’s article at http://www.danielmoth.com/Blog/2004/11/mouseevent.html, which I adopted for this mouse-clicking simulation code.

Run your sample program in the Windows Mobile emulator. When the DateTimePicker has focus, selecting the Action key's enter action simulates a mouse click at point "p," 5 pixels to the left and 5 pixels above the bottom right corner of the control. This causes the calendar to appear and disappear.

This is as far as we’ll take one-handed, stylus-free navigation in this demonstration.

At the outset, we noted that there’s not much out there guiding our implementation of one-handed navigation in Windows Mobile programming. Hopefully, this article will help fill the vacuum. There’s doubtless a lot more that can be written on this subject: Some of the questions I’ve raised can be answered, some additional code can be provided for navigating controls that I did not address, and hopefully my code can be improved upon by others.

I also hope that this article points out the need for the .NET Compact Framework and Visual Studio to provide better support for one-handed, stylus-free navigation. It shouldn’t be this difficult to utilize the Tab key and the Action key in a Windows Mobile program!

In implementing our own one-handed navigation support, we encountered a number of properties and methods that are necessary when implementing one-handed navigation. I understand that the .NET Compact Framework must maintain a small memory footprint, but one-handed navigation needs to be considered of first-class importance. We need to have access to the properties and methods that enable us to more easily incorporate one-handed navigation in our programs. We should not have to resort to tricks such as those available using Windows Messaging or p/invoking mouse clicks.

This being said, it will not be too difficult for programmers to utilize the KeysDown method I demonstrated in this article, and add a great deal of one-handed, stylus-free functionality to their mobile programs.

See Also

Author Bio

Larry Behrendt is the Chief Executive Officer of Medelix Business Services LLC (www.medelix.com), a California company providing mobile computing solutions in the health care industry. Medelix’s flagship product, Pocket Anesthesia Pro, is the leading medical charge capture solution in the field of anesthesia. Since its inception, Larry’s company has worked extensively with Microsoft technology—from embedded Microsoft Visual Basic® through Visual Studio 2005. Larry is also an attorney with more than 20 years experience in the areas of technology and finance. You can reach Larry at dev@medelix.com.

Jim Wilson is president of JW Hedgehog, Inc. (http://www.jwhh.com) a New Hampshire–based consulting firm specializing in solutions, content creation, and mentoring for the Windows Mobile platform. Jim has worked extensively with the .NET Framework and .NET Compact Framework since the original beta release of each; he has over 20 years experience in the software industry including more than 14 years experience with relational database programming including SQL Server and SQL Server Compact Edition. Jim writes frequently for MSDN and has developed mobility curriculums for two of the industry’s leading technology training organizations, DevelopMentor and PluralSight. Jim speaks regularly at Tech Ed, the Professional Developer's Conference (PDC), VSLive, and the Mobility & Embedded DevCon. You'll find Jim online at http://pluralsight.com/blogs/jimw.

Show:
© 2014 Microsoft