|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionOne of the most challenging aspects about being a developer for Windows Mobile is the fact that Windows Mobile is not a single platform. Over the past decade, Microsoft has layered multiple user interface shells over the core Windows CE Operating System, and today, two contenders still remain. The first, formerly named Pocket PC, and now, Windows Mobile Classic or Windows Mobile Professional, is a touch-screen based design where a stylus or finger is the primary input device. The second, formerly named SmartPhone and now Windows Mobile Standard, uses only the keypad and navigation buttons. In recent years, Microsoft has worked hard to converge the two user interfaces. They have even blurred the marketing distinctions with the Windows Mobile Classic/Standard/Professional naming scheme (so much so that the rest of this article refers to the two species as touchscreen and non-touchscreen). However, for the developer, differences certainly still exist. Foremost, Microsoft has different logo certification requirements for the two platforms. In fact, each has their own PDF document outlining specific behavior which must be implemented. Obtaining logo certification for an application is a good idea, because it lets customers know that the application is robust and that it follows standards that will ease deployment and training. Even more important, if an application works with restricted low level system calls, it must obtain a Microsoft Mobile2Market privileged signature. To get a privileged signature, logo certification is now a requirement, not an option! This article shows how to abstract some of the most common issues a developer will encounter when creating a native code application that must be logo certified for each platform. The routines demonstrated by the demo application are included in a single source file, should work for Windows Mobile 5.0 and higher, and can easily be added to any project. BackgroundCreating source code that compiles on multiple different systems is called cross platform development. Most code, if it is generic enough, will compile on any system. But in some cases, code will need to be specifically targeted to a certain platform. Usually, this involves inserting the Using the CodeThe SIP (Or Lack Thereof)Many touchscreen devices lack a keyboard, and only have the screen available as a way to enter text. To solve this problem, all touchscreen devices offer a Soft Input Panel (SIP). The user can then enter text using the SIP. The application user interface residing behind the SIP should be notified about the limited screen real estate now available, so that it can resize itself as best as it can. Implementing the above is one of the requirements for Windows Mobile Professional logo certification, as stated in the Designed for Windows Mobile 6 Professional Software Application Handbook:
Handling the SIP and resizing any dialog affected by it involves just a few system calls. All of them revolve around the void
LH_SIPCreate(HWND hwnd,SHACTIVATEINFO *psai)
{
#if WIN32_PLATFORM_PSPC
SIPINFO si;
int cx,cy;
// Initialize the shell activate info structure.
memset(psai, 0, sizeof (SHACTIVATEINFO));
psai->cbSize = sizeof (SHACTIVATEINFO);
memset(&si, 0, sizeof(si));
si.cbSize = sizeof(si);
SHSipInfo(SPI_GETSIPINFO, 0, (PVOID) &si, FALSE);
cx = si.rcVisibleDesktop.right - si.rcVisibleDesktop.left;
cy = si.rcVisibleDesktop.bottom - si.rcVisibleDesktop.top;
// If the SIP is not shown, or it is showing but not docked, the
// desktop rect does not include the height of the menu bar.
if (!(si.fdwFlags & SIPF_ON) ||
((si.fdwFlags & SIPF_ON) && !(si.fdwFlags & SIPF_DOCKED)))
{
RECT rcMenu;
HWND hwndMenuBar;
hwndMenuBar = SHFindMenuBar(hwnd);
if(hwndMenuBar!=NULL)
{
GetWindowRect(hwndMenuBar,&rcMenu);
cy -= (rcMenu.bottom-rcMenu.top);
}
}
SetWindowPos(hwnd, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER);
#endif // WIN32_PLATFORM_PSPC
}
In void
LH_SIPActivate(HWND hwnd,WPARAM wParam,LPARAM lParam,SHACTIVATEINFO *psai)
{
#if WIN32_PLATFORM_PSPC
SHHandleWMActivate(hwnd, wParam, lParam, psai, FALSE);
#endif // WIN32_PLATFORM_PSPC
}
void
LH_SIPSettingChange(HWND hwnd,WPARAM wParam,LPARAM lParam,SHACTIVATEINFO *psai)
{
#if WIN32_PLATFORM_PSPC
SHHandleWMSettingChange(hwnd, wParam, lParam, psai);
#endif // WIN32_PLATFORM_PSPC
}
After that, on Of course, non-touchscreen devices don't have SIPs--there is no way to tap on the panel. Instead, these devices have full keyboards, or at least a number pad that can enter alphanumeric characters. The question, then, is how to write clean code that will deal with both SIP and non-SIP devices. This is done by bracketing platform specific code with
The image on the left shows what happens when the SIP is not handled correctly. When docked, the SIP overlays the application's user interface, obscuring the edit control. This looks ugly, appears unprofessional, and would not satisfy the SIP logo requirement. On the other hand, the image on the right shows what happens when the application responds correctly. Here, the sample application processes the The Mysterious Back ButtonThe back button performs very different operations depending on which platform you are using. For touchscreen devices, the button sends the application to the rear. For non-touchscreen devices, the back button is actually a delete key in some cases. The proper use of the back button is a requirement for Windows Mobile 6 Standard logo certification. Thus, it is important for a developer to handle this correctly, as stated in the Designed for Windows Mobile 6 Standard Software Application Handbook:
Ironically, in order to achieve this standard functionality for non-touchscreen devices, we will actually have to override the way the back button works! If the dialog contains an edit control, we need the button to work as a delete key. If it doesn't have an edit control, the back button should behave normally, and either send the application to the rear or close a sub-dialog. The function void
LH_BackKeyBehavior(HWND hwnd,BOOL bHasEditControl)
{
#if WIN32_PLATFORM_WFSP
LPARAM lparam;
HWND hwndMenuBar;
hwndMenuBar = SHFindMenuBar(hwnd);
if(hwndMenuBar!=NULL)
{
if(bHasEditControl)
lparam = MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
SHMBOF_NODEFAULT | SHMBOF_NOTIFY);
else
lparam = MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY, 0);
SendMessage(hwndMenuBar, SHCMBM_OVERRIDEKEY, VK_TBACK, lparam);
}
#endif // WIN32_PLATFORM_WFSP
}
In void
LH_BackKeyHotKey(HWND hwnd,UINT uMessage,WPARAM wParam,LPARAM lParam)
{
#if WIN32_PLATFORM_WFSP
if(HIWORD(lParam) == VK_TBACK)
SHSendBackToFocusWindow(uMessage, wParam, lParam);
#endif // WIN32_PLATFORM_WFSP
}
Touchscreen devices, of course, do not honor this deletion behavior. The back key is always used to close dialogs or send applications to the rear. While maintaining the
In the image on the left, there is an edit control visible. According to the logo certification guidelines, the back button should be able to delete in this case. For the image on the right, the edit control has been disabled and hidden. Without any edit controls, the back button should perform its default behavior. For the main screen of an application, that behavior is to force the application to the back. Spinners or Combo Boxes?Complicating life for the Windows Mobile developer is the fact that not all common controls are acceptable for logo certification on both platforms. One of the biggest differences is regarding combo boxes. On touchscreen devices, combo boxes are A-OK. Clicking on one brings up a dropdown list and the user can select an item. However, on non-touchscreen devices, spinners are preferred. Instead of a combo box, the user is presented with a single line containing the selected entry. Alongside are left and right arrows which allow the user to scroll through alternate selections. If the user clicks on the entry field, a full screen listbox is displayed, allowing the user to see all the choices at once. Non-touchscreen devices do support combo boxes. However, the Windows Mobile 6 Standard logo certification guidelines do not allow their use, as stated in the Designed for Windows Mobile 6 Standard Software Application Handbook:
If desktop code is being ported to Windows Mobile non-touchscreen devices, it most likely depends on combo boxes. The same is true for code being brought over from Windows Mobile touchscreen devices. Rewriting that code to use spinners instead would be a lot of work, and might risk breaking the existing implementation. How best to abstract this behavior? Subclassing! void
LH_InitSpinCombo(HWND hWnd)
{
#if WIN32_PLATFORM_WFSP
PSPINNERCOMBO psc;
RECT rc;
psc=(PSPINNERCOMBO)malloc(sizeof(SPINNERCOMBO));
if(psc!=NULL)
{
memset(psc,0x00,sizeof(SPINNERCOMBO));
SetProp(hWnd, TEXT("SpinComboData"), psc);
psc->origCombo = (WNDPROC)SetWindowLong (hWnd, GWL_WNDPROC,
(LONG_PTR)SubclassComboProc);
GetWindowRect(hWnd,&rc);
MapWindowPoints (NULL, GetParent(hWnd), (LPPOINT)&rc, 2);
psc->hwndSpin = CreateWindow (TEXT("listbox"), NULL,
WS_VISIBLE | WS_TABSTOP | LBS_NOTIFY,
rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
GetParent(hWnd), (HMENU)GetDlgCtrlID(hWnd), NULL, NULL);
// Put spinner in the proper tab order after the original combobox
SetWindowPos(psc->hwndSpin,hWnd,0,0,0,0,
SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
psc->hwndUpDown = CreateWindow (UPDOWN_CLASS, NULL,
WS_VISIBLE | UDS_HORZ | UDS_ALIGNRIGHT | UDS_ARROWKEYS |
UDS_SETBUDDYINT | UDS_WRAP | UDS_EXPANDABLE,
0, 0, 0, 0, GetParent(hWnd), NULL, NULL, NULL);
SendMessage (psc->hwndUpDown, UDM_SETBUDDY, (WPARAM)psc->hwndSpin, 0);
psc->hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
SendMessage(psc->hwndSpin,WM_SETFONT,(WPARAM)psc->hFont,0);
EnableWindow(hWnd,FALSE);
ShowWindow(hWnd,SW_HIDE);
}
#endif // WIN32_PLATFORM_WFSP
}
Instead of worrying about combo boxes on non-touchscreen devices, a spinner control can be created that will sit on top of the hidden combo box and intercept all of its communication. In LRESULT CALLBACK
SubclassComboProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PSPINNERCOMBO psc;
WNDPROC origCombo;
psc = (PSPINNERCOMBO)GetProp(hWnd, TEXT("SpinComboData"));
origCombo=psc->origCombo;
switch (message)
{
case CB_ADDSTRING:
return SendMessage(psc->hwndSpin,LB_ADDSTRING,wParam,lParam);
case CB_SETITEMDATA:
return SendMessage(psc->hwndSpin,LB_SETITEMDATA,wParam,lParam);
case CB_GETITEMDATA:
return SendMessage(psc->hwndSpin,LB_GETITEMDATA,wParam,lParam);
case CB_SETCURSEL:
return SendMessage(psc->hwndSpin,LB_SETCURSEL,wParam,lParam);
case CB_GETCURSEL:
return SendMessage(psc->hwndSpin,LB_GETCURSEL,wParam,lParam);
case WM_SIZE:
{
RECT rc;
GetWindowRect(hWnd,&rc);
MapWindowPoints (NULL, GetParent(hWnd), (LPPOINT)&rc, 2);
MoveWindow(psc->hwndSpin,rc.left,rc.top,
rc.right-rc.left,rc.bottom-rc.top,TRUE);
SendMessage (psc->hwndUpDown, UDM_SETBUDDY, (WPARAM)psc->hwndSpin, 0);
break;
}
case WM_DESTROY:
{
SetWindowLong (hWnd, GWL_WNDPROC, (LONG_PTR)psc->origCombo);
free(psc);
break;
}
}
return CallWindowProc (origCombo, hWnd, message, wParam, lParam);
}
The image on the left shows a non-touchscreen device running with code using a combo box. This implementation does not follow the spinner rule from the Windows Mobile 6 Standard Software Application Handbook, and would not pass logo certification. The image on the right is the same code, only with the Putting it All TogetherThis demo shows how to use all of the above code to write an application that satisfies the logo certification requirements for either platform. On touchscreen devices, the SIP properly resizes the application window. On non-touchscreen devices, the back button is correctly enabled when the edit dialog is displayed. Finally, spinner controls appear on non-touchscreen devices, without any changes to the underlying combo box based code. BOOL CALLBACK
DlgProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
LOGODATA *ld;
ld=(LOGODATA *)GetWindowLong (hWnd, GWL_USERDATA);
switch (wMsg)
{
case WM_INITDIALOG:
{
SHMENUBARINFO mbi;
SHINITDLGINFO shidi;
SetWindowLong(hWnd,GWL_USERDATA,lParam);
ld=(LOGODATA *)lParam;
ld->hwndEdit=GetDlgItem(hWnd,IDC_EDIT1);
ld->hwndCombo=GetDlgItem(hWnd,IDC_COMBO1);
...
LH_InitSpinCombo(ld->hwndCombo);
SendMessage (ld->hwndCombo, CB_ADDSTRING, 0,
(LPARAM)TEXT("Edit Control Disabled"));
ld->iEnable=SendMessage (ld->hwndCombo, CB_ADDSTRING, 0,
(LPARAM)TEXT("Edit Control Enabled"));
SendMessage (ld->hwndCombo, CB_SETCURSEL,
(WPARAM)ld->iEnable, (LPARAM)0);
LH_SIPCreate(hWnd,&(ld->sai));
LH_BackKeyBehavior(hWnd,TRUE); // There is an edit control
return TRUE;
}
case WM_ACTIVATE:
LH_SIPActivate(hWnd, wParam, lParam, &(ld->sai));
break;
case WM_SETTINGCHANGE:
LH_SIPSettingChange(hWnd, wParam, lParam, &(ld->sai));
break;
case WM_HOTKEY:
LH_BackKeyHotKey(hWnd,wMsg,wParam,lParam);
break;
case WM_SIZE:
OnSize(hWnd,ld);
break;
case WM_COMMAND:
{
switch (LOWORD (wParam))
{
case IDC_COMBO1:
{
if(HIWORD(wParam)==CBN_SELCHANGE)
{
BOOL bShowEdit;
bShowEdit=(SendMessage(ld->hwndCombo,CB_GETCURSEL,0,0)==ld->iEnable);
EnableWindow(ld->hwndEdit,bShowEdit?TRUE:FALSE);
ShowWindow(ld->hwndEdit,bShowEdit?SW_SHOW:SW_HIDE);
LH_BackKeyBehavior(hWnd,bShowEdit);
}
break;
}
case IDC_EXIT: // From our menubar
EndDialog(hWnd, TRUE);
break;
case IDCANCEL: // Via Smartphone Back Button
SHNavigateBack();
break;
}
break;
}
}
return FALSE;
}
In Whenever there is a change in the combo box/spinner selection, the edit control needs to be updated with its new status, and the back key must be properly configured depending on that status. Inside ConclusionAdding the six helper functions outlined here to an application will allow it to satisfy all of the logo certification issues discussed in this document. Because these functions abstract the platform specific operations, the rest of the hosting application's code does not need to worry about platform idiosyncrasies. Best of all, the hosting application should be able to pass logo certification on both platforms with a minimum of modifications. References
History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||