如何为 Pocket PC 开发能够识别方向和 DPI 的应用程序
Microsoft
适用于:
Microsoft® eMbedded Visual C++® version 4.0
Windows Mobile™ 2003 Second Edition software for Pocket PCs
摘要:本练习旨在让您了解,如何将现有应用程序转换成能够识别横向和屏幕分辨率的应用程序。完成该练习大约要花一个半小时。
本页内容
简介 | |
第一部分:修复识别方向的问题 | |
第二部分:修复识别 DPI 的问题 | |
小结 | |
附录 A:安装指导 |
要完成该实验,您需要:
• | Microsoft eMbedded Visual C++ 4.0 Service Pack 3 |
• | Pocket PC 软件开发工具包 (SDK) |
• | 可用于 Windows Mobile 2003 Second Edition 的开发人员资源 |
• | Pocket PC 的 Windows Mobile 2003 Second Edition 仿真程序映像 |
注要使该练习能够正确进行,必须按顺序安装这些应用程序。如果您已在计算机上安装了这些应用程序,则需要卸载它们,然后按该顺序重新安装。有关详细信息,请参见本文中的附录 A。
简介
该自定步调的练习演示了如何将现有应用程序转换成能够识别横向和屏幕分辨率的应用程序。Crossword 示例 — 一个包含许多特征(代表实际应用程序)的字谜游戏,包括所有者描述的控件和屏幕外内存缓冲区 — 该 Crossword 示例包含在 Program Files/Developer Resources for Windows Mobile 2003 Second Edition/Samples/Pocket PC/Win32/Crossword 文件夹中。为了防止无关的详细信息导致代码混乱,Crossword 示例的许多功能并未实现。该字谜本身是虚构的,没有答案。
第一部分:修复识别方向的问题
这部分练习的目标为,使 Crossword 示例能够识别横向和纵向模式间的方向变化。默认情况下,示例文件位于 Program Files/Developer Resources for Windows Mobile 2003 Second Edition/Samples/Pocket PC/Win32/Crossword 文件夹。有关对横向模式进行编码的详细信息,请参阅 Developing Screen Orientation-Aware Applications。
在这部分练习中,您将执行以下任务:
• | 使用 Pocket PC 的 Windows Mobile 2003 Second Edition 仿真程序映像打开 Crossword 示例 |
• | 调整背景、文本框区域、提示区域及工具命令对话框 |
在这部分练习中,有些图是缩略图。您可以单击缩略图以查看大图像。
使用 Pocket PC 的 Windows Mobile 2003 Second Edition 仿真程序映像打开 Crossword 示例
在该任务中,您将打开并运行 Crossword 示例,以便了解项目,并了解移植您自己的应用程序时可能遇到的问题类型。
要使用 Pocket PC 的 Windows Mobile 2003 Second Edition 仿真程序映像,需要配置平台管理器 (Platform Manager)
1. | 启动 Microsoft eMbedded Visual C++ 4.0 版本。 |
2. | 在 Tools 菜单上,单击 Configure Platform Manager。 |
3. | 展开 Pocket PC 2003,然后单击 Add Device。 |
4. | 键入 Pocket PC 2003 SE Emulator 作为新设备的名称。 |
5. | 单击 Properties。 |
6. | 将 Transport 更改为 TCP/IP Transport for Windows CE。 |
7. | 确保 Startup Server 为 Emulator Startup Server。 |
8. | 然后转到 Emulator Startup Server,单击 Configure。 |
9. | 在 Image 列表中,选择 WWE PPC 2003 SE。 |
10. | 在每个对话框上单击 OK,关闭所有三个对话框。 |
11. | 从 BaseSample 文件夹打开工作区 CrosswordSample.vcw。 |
12. | 在 Windows CE Configuration 工具栏上,选择 Win32 (WCE emulator) Debug 作为活动配置,并选择 Pocket PC 2003 SE Emulator 作为默认设备。 |
在配置完平台管理器以使用 Pocket PC 的 Windows Mobile 2003 Second Edition 仿真程序映像以后,您需要运行 Crossword 示例。
运行 Crossword 示例
1. | 按 F5 键运行该示例。如果出现任何 Find Local Modules 对话框,单击 Cancel。 |
2. | 细读源代码,以便了解该示例是如何实现的。 |
3. | 在仿真程序中,按 F2 键以观察它是如何处理纵向/横向这两个方向的变化的。 |
4. | 请注意以下问题,如下图所示。 |
• | 背景在屏幕的右侧被剪切。 |
• | 应该扩大文本框区域以填充屏幕的长度。 |
• | 未显示提示区域。 |
• | Tools 命令对话框被剪切,并且该对话框需要滚动条。 |
调整背景、文本框区域、提示区域及工具命令对话框
在该任务中,您将修复在前一任务观察到的问题。
首先,背景图像 (Background1.bmp) 的大小只有 240 _ 320 像素,您需要将它替换为位于 LandscapeAware 文件夹中的 320 _ 320 版本的图像。
扩展背景
1. | 在 LandscapeAware 文件夹中,复制 Background1.bmp。 |
2. | 将 Background1.bmp 粘贴到 Crossword/BaseSample 文件夹。 |
3. | 在 OnPaint 处理程序中,将现有的 BitBlt 语句替换为以下代码。 |
4. | BitBlt(hDC, 0, 0, 320, 320, hMemDC, 0, 0, SRCCOPY); |
其次,您需要扩大文本框区域以填充屏幕的长度。当屏幕方向从纵向改为横向时,您会接收到 WM_SIZE 消息。
扩大文本框区域
• | 在 CrosswordSample.cpp 中,将下面的 WM_SIZE 处理程序添加到 WndProc。 case WM_SIZE: { HWND hEditBox = GetDlgItem(hWnd, IDC_MAIN_EDIT_BOX); HWND hEnterButton = GetDlgItem(hWnd, IDC_MAIN_ENTER_BUTTON); INT nWidth = LOWORD(lParam); MoveWindow(hEditBox, 8, 4, nWidth - 70, 20, TRUE); MoveWindow(hEnterButton, nWidth - 57, 4, 50, 20, TRUE); } break; |
第三,需要将提示区域移到屏幕的一侧。该移动需要您添加一个函数,用它来检测是否应该以宽模式 (wide mode) 显示 Crossword 示例。只要是少于 320 垂直像素,您就可以使应用程序使用宽模式。
将提示区域移到屏幕的一侧
1. | 添加以下函数。 BOOL InWideMode() { int height = GetSystemMetrics(SM_CYSCREEN); return (height < 320) ? TRUE : FALSE; } |
2. | 在 OnPaint 中(绘制提示区域的位置),删除声明 RECT r 的行,然后将其替换为以下代码行。 RECT rTallMode = { 25, 200, 230, 245 }; RECT rWideMode = { 240, 43, 311, 185 }; RECT& r = InWideMode() ? rWideMode : rTallMode; |
3. | 用以下代码替换 OnLButtonDown 中现有的 RECT.r 声明,因为单击鼠标左键时提示区域是无效的。 RECT rTallMode = { 0, 189, 240, 320 }; RECT rWideMode = { 240, 26, 320, 240 }; InvalidateRect(hWnd, InWideMode() ? &rWideMode : &rTallMode, FALSE); |
第四,您需要移动箭头(该箭头表明线索是向下的还是横向的)。
移动向下/横向箭头
• | 在 OnPaint 中,添加以下代码。 rgArrow[i].x += (InWideMode() ? 280 : 5); rgArrow[i].y += (InWideMode() ? 26 : 200); |
以下三个程序举例说明了如何完整地显示 Tools 命令对话框。
重新布局对话框
• | 向 LandscapeAware 文件夹中的项目添加 UIHelper.CPP、UIHelper.H 和 shguim.h。(UIHelper.CPP 和 UIHelper.H 提供您将要使用的 helper 函数 RelayoutDialog。) |
您需要为设备创建对话框模板,使其用于宽模式。
创建用于宽模式的对话框模板
1. | 创建一个新的对话框。(在 Insert 菜单上,单击 Resource,选择 Resource type 列表中的 Dialog,然后单击 New)。 |
2. | 删除默认的 OK 和 Cancel 按钮。 |
3. | 右键单击对话框,然后单击 Properties。 |
4. | 在 ID 框中,键入 IDD_TOOLS_OPTIONS_1_WIDE。 |
5. | 通过拖动右下角至适当大小,将对话框的大小调整到 187 _ 91 DLU。(这些尺寸大概就是横向模式中对话框的大小。) |
6. | 在右侧“Resource”窗格中,双击 IDD_TOOLS_OPTIONS_1。 |
7. | 按 CTRL+A,选择所有项,然后按 CTRL+C,将这些项复制到剪贴板。 |
8. | 打开 IDD_TOOLS_OPTIONS_1_WIDE,然后按 CTRL+V,粘贴这些项。 |
9. | 调整这些项的大小并重新排列,以使其适合新的对话框,如下图所示。
您可以发现,通过暂时放大对话框的大小,能够更轻松地重新排列这些项,随意移动这些项以后再将对话框的大小调整到 187 _ 91 DLU。 |
10. | 对于 IDD_TOOLS_OPTIONS_1_WIDE,将 ID 为 IDC_STATIC 的三个对话框控件重命名为:IDC_STATIC_1、IDC_STATIC_2 和 IDC_STATIC_3。所有这些 ID 必须都是唯一的,这样 RelayoutDialog 才能识别纵向模式的哪些控件对应于横向模式的哪些控件。 |
11. | 重复 IDD_TOOLS_OPTIONS_1 的第 10 步,确保为每个控件提供与其对应的横向控件相同的 ID。 |
12. | 重复 IDD_TOOLS_OPTIONS_2 的第 1 到 11 步,确保将 IDD_TOOLS_OPTIONS_2 和 IDD_TOOLS_OPTIONS_2_WIDE 的 IDC_STATIC 控件重命名为 IDC_STATIC_1。 |
将 WM_SIZE 处理程序添加到对话框
1. | 在 CrosswordSample.cpp、ToolsOptions1.cpp 和 ToolsOptions2.cpp 中,添加以下语句。 #include UIHelper.H In ToolsOptions1.cpp, add the following WM_SIZE handler: case WM_SIZE: { RelayoutDialog(g_hInst, hDlg, InWideMode() ? MAKEINTRESOURCE(IDD_TOOLS_OPTIONS_1_WIDE) : MAKEINTRESOURCE(IDD_TOOLS_OPTIONS_1)); } return TRUE; |
2. | 在 ToolsOptions2.cpp 中,添加以下 WM_SIZE 处理程序。 case WM_SIZE: { RelayoutDialog(g_hInst, hDlg, InWideMode() ? MAKEINTRESOURCE(IDD_TOOLS_OPTIONS_2_WIDE) : MAKEINTRESOURCE(IDD_TOOLS_OPTIONS_2)); } return TRUE; |
3. | 在 ToolsAbout.cpp 中,添加以下 WM_SIZE 处理程序(在这种情况下,您可以手动处理 About 对话框模板,而无需使用 RelayoutDialog)。 case WM_SIZE: { INT nWidth = LOWORD(lParam); INT nHeight = HIWORD(lParam); HWND hWnd = GetDlgItem(hDlg, IDC_STATIC_1); RECT rWnd; RECT rDlg; GetWindowRect(hWnd, &rWnd); GetWindowRect(hDlg, &rDlg); OffsetRect(&rWnd, -rDlg.left, -rDlg.top); MoveWindow(hWnd, rWnd.left, rWnd.top, nWidth - rWnd.left - 8, nHeight - rWnd.top - 8, TRUE); } return TRUE; |
4. | 在 CrosswordSample.h 中,添加 Function Prototypes 部分中的下列行。 BOOL InWideMode(); |
5. | 验证整个应用程序是否处理了横向/纵向这两个方向的变化。 |
第二部分:修复识别 DPI 的问题
这部分练习的目标是,将 Crossword 示例转换成能够识别屏幕分辨率的变化。该练习是建立在第一部分练习所完成的工作之上的。有关对高分辨率设备进行编码的详细信息,请参阅 Developing DPI-Aware Applications。
在这部分练习中,您将执行以下任务:
• | 使用 WWE PPC 2003 SE VGA 仿真程序打开 Crossword 示例 |
• | 调整硬编码的常数、背景、“Enter”按钮、提示区域以及对话框 |
在这部分练习中,有些图是缩略图。您可以单击缩略图以查看大图像。
使用 WWE PPC 2003 SE VGA 仿真程序打开 Crossword 示例
在该任务中,您将禁用 HIDPI 仿真,并构建和运行 Crossword 示例,以便了解项目,并了解移植您自己的应用程序时可能遇到的问题类型。
默认情况下,您的应用程序接收 HIDPI 仿真。要将其关闭,请将 HIDPI_RES_AWARE 资源添加到程序中。
禁用 HIDPI 仿真
1. | 在 Insert 菜单上,单击 Resource。 |
2. | 单击 Custom。 |
3. | 键入 CEUX 作为资源类型。 |
4. | 将资源数据设置为 01 00。 |
5. | 在“Resources”窗格中,右键单击新的资源,然后单击 Properties。 |
6. | 将该项重命名为 "HI_RES_AWARE"(包括引号)。(如果省略引号,则 HI_RES_AWARE 将在 resource.h 中被错误地定义为数值,并且您将需要从 resource.h 中删除该行。) |
7. | 清除 External file 复选框。 |
接下来,您需要切换到 192-DPI 仿真程序图像,即 WWE Pocket PC 2003 SE VGA 仿真程序。
在 WWE PPC 2003 SE VGA 仿真程序上运行 Crossword 示例
1. | 在 Tools 菜单上,单击 Configure Platform Manager。 | ||||||||||||||
2. | 单击 Add Device。 | ||||||||||||||
3. | 为新设备键入名称(例如,Pocket PC 2003 SE VGA Emulator)。 | ||||||||||||||
4. | 单击 Properties。 | ||||||||||||||
5. | 将 Transport 更改为 TCP/IP Transport for Windows CE。 | ||||||||||||||
6. | 确保 Startup Server 为 Emulator Startup Server。 | ||||||||||||||
7. | 单击 Configure。 | ||||||||||||||
8. | 在 Image 列表中,选择 WWE PPC 2003 SE VGA。 | ||||||||||||||
9. | 在每个对话框上单击 OK,关闭所有三个对话框。 | ||||||||||||||
10. | 从 Windows CE Configuration 工具栏中选择新设备。 | ||||||||||||||
11. | 按 F5 键运行该应用程序。您应该看到下图。
| ||||||||||||||
12. | 观察以下问题:
|
调整硬编码的常数、背景、“Enter”按钮、提示区域以及对话框
在该任务中,您将修复在前一任务中观察到的问题。
首先,该应用程序中存在许多未缩放的常数。您将使用 SCALEX 和 SCALEY 宏来将 96-DPI 坐标调整到任意的屏幕分辨率。
缩放硬编码的常数
1. | 将调用添加到 InitInstance 顶部的 HIDPI_InitScaling 之中。 | ||||||||||||||
2. | 将未缩放常数周围的 SCALEX 和 SCALEY 宏添加到 CroswordSample.cpp 中的所有以下函数:
| ||||||||||||||
3. | 运行 Crossword 示例,以便查看某些问题是否已修复,如下图所示。
|
其次,您需要通过导入 192-DPI 位图背景来扩展背景图像的屏幕长度。
扩展背景图像
1. | 在 Insert 菜单上,单击 Resource 和 Import,定位到 HidpiAware 文件夹,然后选择 Background1_hidpi.bmp。 |
2. | 在 Properties 选项卡上,将文件重命名为 IDB_BACKGROUND_1_HIDPI。当屏幕分辨率为 192 DPI 或更高时,您将使用该位图。 |
3. | 在 OnPaint 中,将绘制背景的代码替换为以下代码。请注意 StretchBlt 的用法。 int nImageSize = g_HIDPI_LogPixelsX >= 192 ? 640 : 320; HBITMAP hBMP = LoadBitmap(g_hInst, g_HIDPI_LogPixelsX >= 192 ? MAKEINTRESOURCE(IDB_BACKGROUND_1_HIDPI) : MAKEINTRESOURCE(IDB_BACKGROUND_1)); HDC hMemDC = CreateCompatibleDC(hDC); HBITMAP hOldBMP = (HBITMAP)SelectObject(hMemDC, hBMP); StretchBlt(hDC, 0, 0, SCALEX(320), SCALEY(320), hMemDC, 0, 0, nImageSize, nImageSize, SRCCOPY); SelectObject(hMemDC, hOldBMP); DeleteObject(hBMP); DeleteDC(hMemDC); |
第三,“Enter”按钮需要放大并重定位,以更好地适合屏幕。
放大“Enter”按钮
• | 在 InitInstance 中,调用 HIDPI_ImageList_LoadImage(在 UIHelper.H 中进行定义)来缩放图像,而不要调用 ImageList_LoadImage。 g_hImageList = HIDPI_ImageList_LoadImage( g_hInst, MAKEINTRESOURCE(IDB_ENTERBTN), SCALEX(46), 0, CLR_NONE, IMAGE_BITMAP, 0); |
重定位“Enter”按钮
• | 在 WM_DRAWITEM 处理程序中,使用 BF_ADJUST 查找正确的位置以绘制 ImageList。 case WM_DRAWITEM: { LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam; DrawEdge(lpDis->hDC, &lpDis->rcItem, (lpDis->itemState & ODS_SELECTED) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT | BF_ADJUST); ImageList_Draw(g_hImageList, (lpDis->itemState & ODS_SELECTED) ? 0 : 1, lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.top, ILD_NORMAL); } break; |
第四,需要在 HIDPI 设备的屏幕上更容易地看到分隔线和提示区域的边框。
增强分隔线和提示区域的边框
1. | 通过添加以下代码,使分隔线(和提示区域的边框)的宽度为两像素。 HPEN hNewPen = CreatePen(PS_SOLID, SCALEX(1), RGB(0, 0, 0)); HPEN hOldPen = (HPEN)SelectObject(hDC, hNewPen); POINT line[] = { {SCALEX(0), SCALEY(188)}, {SCALEX(240), SCALEY(188)} }; Polyline(hDC, line, 2); // Erase the hint area using a pattern brush. |
2. | 两个 DeleteObject 调用后,添加下列行。 SelectObject(hDC, hOldPen); DeleteObject(hNewPen); |
第五,提示区域内的文本大小需要根据设备进行更改。您可以使用在 shguim.h 中定义的 SHGetUIMetrics 来查询设备的字体大小(用户可以使用“Screen”控制面板来调整该字体大小)。
更改提示区域的文本大小
1. | 生成一个创建字体的函数。 void CreateHintFont() { if (g_hFont) { DeleteObject(g_hFont); } DWORD dwRequired; LONG dwFontSize = 12; SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL, &dwFontSize, sizeof(dwFontSize), &dwRequired); LOGFONT lf; memset(&lf, 0, sizeof(lf)); _tcscpy(lf.lfFaceName, _T("Courier New")); lf.lfHeight = -dwFontSize; lf.lfWeight = FW_NORMAL; g_hFont = CreateFontIndirect(&lf); } |
2. | 在 InitInstance 中,删除 CreateFontIndirect 调用,然后将其替换为对刚才生成的 CreateHintFont 函数的调用。 当该字体大小更改时,还需要通知 Crossword 示例。 |
3. | 靠近 Crossword.CPP 的顶部,声明 WM_SH_UIMETRIC_CHANGE 作为全局变量。 |
4. | 在 InitInstance 的顶部,通过调用 RegisterWindowMessage 来初始化 WM_SH_UIMETRIC_CHANGE。 UINT WM_SH_UIMETRIC_CHANGE; // define this as a global. : : WM_SH_UIMETRIC_CHANGE = RegisterWindowMessage(SH_UIMETRIC_CHANGE); //When you receive this window message in WndProc, recreate the hint //font: if (message == WM_SH_UIMETRIC_CHANGE) { CreateHintFont(); } |
5. | 运行 Crossword 示例,以便查看是否已修复了更多的问题,如下图所示。
|
第六,必须修复“Background Picker”对话框。在 ToolsOptions1.cpp 中,从两个位置向“Background Picker”预览控件发送 STM_SETIMAGE 消息。
修复“Background Picker”对话框
• | 在每个 STM_SETIMAGE 调用前,通过添加以下代码,使用 HIDPI_StretchBitmap(在 UIHelper.H 中进行定义)来缩放位图。 |
• | HIDPI_StretchBitmap(&hBMP, SCALEX(320), SCALEY(320), 1, 1); |
最后,在 ToolsOptions2.cpp 中,WM_MEASUREITEM 处理程序包含一个硬编码的常数,该常数表示列表框中每个元素的高度(需要垂直缩放)。
修复“Font Picker”对话框
1. | 用以下代码修改代码行 lpmis->itemHeight = 40。 lpmis->itemHeight = SCALEY(40); 在 WM_INITDIALOG 处理程序中还有一个硬编码的 15,它指的是滚动条的宽度。 |
2. | 使用 GetSystemMetrics 来获得垂直滚动条的真正宽度。 |
3. | 用以下代码替换代码行 lvColumn.cx = r.right – r.left – 15;。 lvColumn.cx = r.right - r.left – GetSystemMetrics(SM_CXVSCROLL) – 2 * GetSystemMetrics(SM_CYBORDER); |
小结
祝贺您!您已经成功地将应用程序转换为能够识别横向和高分辨率的应用程序。
在本练习中,您执行了以下任务:
• | 修复识别方向的问题 |
• | 修复识别 DPI 的问题 |