要完成本文练习内容,您需要:
| • |
Microsoft eMbedded Visual C++ 4.0 Service Pack 3 |
| • |
|
| • |
可用于 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. |
|
|
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. |
|
|
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 示例。
|
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);
|
第四,您需要移动箭头(该箭头表明线索是向下的还是横向的)。
| • |
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. |
|
|
3. |
|
|
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。 |
|
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 资源添加到程序中。
|
1. |
在 Insert 菜单上,单击 Resource。 |
|
2. |
|
|
3. |
|
|
4. |
|
|
5. |
在“Resources”窗格中,右键单击新的资源,然后单击 Properties。 |
|
6. |
将该项重命名为 "HI_RES_AWARE"(包括引号)。(如果省略引号,则 HI_RES_AWARE 将在 resource.h 中被错误地定义为数值,并且您将需要从 resource.h 中删除该行。) |
|
7. |
|
接下来,您需要切换到 192-DPI 仿真程序图像,即 WWE Pocket PC 2003 SE VGA 仿真程序。
在 WWE PPC 2003 SE VGA 仿真程序上运行 Crossword 示例
|
1. |
在 Tools 菜单上,单击 Configure Platform Manager。 |
|
2. |
|
|
3. |
为新设备键入名称(例如,Pocket PC 2003 SE VGA Emulator)。 |
|
4. |
|
|
5. |
将 Transport 更改为 TCP/IP Transport for Windows CE。 |
|
6. |
确保 Startup Server 为 Emulator Startup Server。 |
|
7. |
|
|
8. |
在 Image 列表中,选择 WWE PPC 2003 SE VGA。 |
|
9. |
|
|
10. |
从 Windows CE Configuration 工具栏中选择新设备。 |
|
11. |
|
|
12. |
|
调整硬编码的常数、背景、“Enter”按钮、提示区域以及对话框
首先,该应用程序中存在许多未缩放的常数。您将使用 SCALEX 和 SCALEY 宏来将 96-DPI 坐标调整到任意的屏幕分辨率。
|
1. |
将调用添加到 InitInstance 顶部的 HIDPI_InitScaling 之中。 |
|
2. |
将未缩放常数周围的 SCALEX 和 SCALEY 宏添加到 CroswordSample.cpp 中的所有以下函数:
| • |
InitInstance:修改两个 CreateWindow 调用。(不要缩放 CreateWindow 的参数,它用来创建主应用程序窗口)。 |
| • |
CreateCrossword:修改 CreateCompatibleBitmap 和 RECT r 声明。请注意,当 r.right 设置为等于 r.left + 17 时,此处的 17 等于单元格大小(16 像素)加上边框(1 像素)。在本例中,您将缩放单元格区域(而不是边框),因此该代码应该如下所示。 r.left = SCALEX(16) * x;
r.top = SCALEY(16) * y;
r.right = r.left + SCALEX(16) + 1;
r.bottom = r.top + SCALEY(16) + 1;
|
| • |
DrawHint:请记住,对于 DrawText 调用,常数 15 等于单元格大小减去边框(类似于上面的例子)。不要忘记,还必须缩放 InvalidateRect 调用。 |
| • |
OnLButtonDown:修改所接收的坐标和 InvalidateRect。 |
| • |
OnPaint:修改 TransparentImage、PolyLine、Rectangle r、DrawText 的边距,以及 Polygon。(请记住,16 代表单元格宽度,1 代表边框。)Polygon 调用有点困难,它用来绘制向下和横向箭头 — 请确保缩放所有常数。 |
| • |
OnMessage:修改 WM_SIZE 处理程序。请记住,您只需缩放常数。nWidth 值已经能够识别 HIDPI,因为已从操作系统接收该值。因此,SCALEX(nWidth - 57) 是错误的;正确的表达式为 nWidth - SCALEX(57)。 |
| • |
InWideMode:HIDPI 横向模式中的垂直像素大于 320。 | |
|
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”按钮需要放大并重定位,以更好地适合屏幕。
| • |
在 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);
|
| • |
在 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 消息。
| • |
在每个 STM_SETIMAGE 调用前,通过添加以下代码,使用 HIDPI_StretchBitmap(在 UIHelper.H 中进行定义)来缩放位图。 |
| • |
HIDPI_StretchBitmap(&hBMP, SCALEX(320), SCALEY(320), 1, 1); |
最后,在 ToolsOptions2.cpp 中,WM_MEASUREITEM 处理程序包含一个硬编码的常数,该常数表示列表框中每个元素的高度(需要垂直缩放)。
|
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); |
小结
祝贺您!您已经成功地将应用程序转换为能够识别横向和高分辨率的应用程序。
附录 A:安装指导
下载该工具包以后,您需要检验计算机是否已按正确的顺序安装了正确的应用程序。如果未按指定的顺序安装应用程序,则该练习可能不会正确进行。
|
1. |
单击 Start,指向 Programs,再指向 MobileApplicationDevelopmentToolkit,然后单击 Index.html。 |
|
2. |
在左侧的导航面板,单击 Developer Tools and SDKs。 |
|
3. |
遵循 Developer Tools and SDKs 页上的指导。 | |