Windows Mobile 开发识别屏幕方向的应用程序
编写目的:
现在Windows Mobile的屏幕分辨率支持非常多。最新的Windows Mobile6.5支持
240*240,240*320,240*400,320*320,480*480,480*640,480*800,480*854。当我们开发的窗体程序想适应如此多的分辨率时,往往要为一个对话框建立多个对话框模板。最常用的方法就是用RelayoutDialog。来重新layout对话框。这样使我们陷入了不断的摆放控件,然后编译查看效果。往往为了支持,方屏、肖像模式、风景画模式要建立三个模板。此文的目的就是用代码的方式来Layout窗体,使支持不同的分辨率。
更改屏幕方向:引MSDN
通常,屏幕方向仅在用户使用“屏幕方向”控制面板,或按下某硬件按钮来旋转屏幕时才改变。
此外,应用程序也可通过 ChangeDisplaySettingsEx 函数以编程方式更改屏幕方向。该函数使用方法见 MSDN 中“Rotating the Content of the Screen”(旋转屏幕内容的方向)一文。例如,如果使用的应用程序只能在竖向模式下操作,则可通过编程改变屏幕方向。但是,如果屏幕方向的改变很突然,通过编程改变屏幕方向会让用户搞不清楚。应用程序应始终要求用户在旋转屏幕前确认显示模式。
为了在竖向模式下显示,将 dmDisplayOrientation 字段中的 DEVMODE 结构设置为 DMDO_0。对于右手横向模式,使用 DMDO_270。对于左手横向模式,使用 DMDO_90
应用程序窗口如何识别屏幕方向:引MSDN
屏幕方向如果改变,或当“输入面板”出现时,应用程序的所有全屏顶层窗口都要适应新的取向。
注意: 如果窗口的上、左、右坐标在工作区域边界之上或之外,该窗口被认为是全屏窗口。工作区域是标题栏下的整个屏幕区域。顶层窗口是无父窗口的窗口,即有一个 NULL 父窗口。
如果窗口大小改变,窗口将收到 WM_SIZE 通知。WM_SIZE 消息的 lParam 参数的低位字指定了客户端区域的新宽度,高位字指定了客户端区域的新高度。应用程序应识别窗口大小的改变,并相应地更新窗口布局。此外,也应重新确定所包含任何子窗口的布局。
如果应用程序没有全屏窗口,它收不到 WM_SIZE 通知。相反,它应在 wParam 参数设置为 SETTINGCHANGE_RESET 时监听 WM_SETTINGCHANGE消息。
注意: 如果应用程序有顶层窗口,或使用 SHHandleWMSettingChange、SHInitDialog 和 SHFullScreen 方法创建了窗口,它会同时收到 WM_SIZE 和 WM_SETTINGCHANGE 消息。但是,如果应用程序创建了子窗口,子窗口收不到 WM_SIZE 消息,即使子窗口是全屏窗口。
以下 WindowProc 模板的代码示例利用了 WM_SIZE 和 WM_SETTINGCHANGE 消息。
switch (uMessage)
{
case WM_SIZE:
// 重新计算所有子窗口的布局;重新设置
// 列出视图和编辑框的大小,重新确定按钮、
// 静态文字和其他控件的位置。
break;
case WM_SETTINGCHANGE:
if (SETTINGCHANGE_RESET == wParam) {
// 屏幕方向改变。此时
// 执行 WM_SIZE 不能执行的处理,
// 如重新调整全屏子窗口的大小,对
// 顶层窗口调用 MoveWindow 等等。
// 如果不需要处理 WM_SETTINGCHANGE 消息,可以
// 忽略它。
}
break;
}
开发识别屏幕方向的应用程序
我们先来看下微软的“今日”设置界面。
上面这两个图分别是肖像模式和风景画模式的UI布局。如果用DRA中的RelayoutDialog来实现,我们起码需要两个对话框模板。代码如下
WM_SIZE:
DRA::RelayoutDialog(
g_hInst,
hDlg,
DRA::GetDisplayMode() != DRA::Portrait ? MAKEINTRESOURCE(IDD_TODAY_WIDE) : MAKEINTRESOURCE(IDD_TODAY));
如果要你放到方屏的机器上,你又要添加一个对话框模板。并要修改相应的代码。
WM_SIZE:
{
UINT uIDDialog= IDD_TODAY_WIDE;
Switch(DRA::GetDisplayMode())
{
DRA::RelayoutDialog(
g_hInst,
hDlg,
DRA::GetDisplayMode() == DRA::Portrait ? MAKEINTRESOURCE(IDD_TODAY) : (DRA::GetDisplayMode() == DRA::Square? MAKEINTRESOURCE(IDD_ TODAY_SQUARE):MAKEINTRESOURCE(IDD_TODAY_WIDE));
}
用RelayoutDialog来开发支持屏幕方向应用十分的麻烦。那么有没有通过代码实现,用一个对话框模板就可以实现的呢。
我们可以来分析下上面的两个Layout好的对话框。可以发现右边的三个按钮”上移”、“下移”、“选项”都是靠右的。而最下面的Checkbox和Combox。是下靠其的。然后中间留出的位置给list view来摆放。上面的对话框从肖像模式到风景画模式可以这样改变。
向右对于控件实现如下:
1. 记录要移动控件的位置
2. 找出除list view,最left和最right的控件。标记为MinLeft,MaxRight
3. 计算最right的控件移动右边的位移。并保存为MoveX. MoveX =(rcClient.right - nMargin) - MaxRight.right. nMargin为边缘空白的距离,默认设置为8个像素。
4. 保存最left控件到list控件的距离,并保存,这样保证list控件到其他控件的相对位置不变。cListMargin = MinLeft.left - rcPrimary.right;
5. 根据计算出的MoveX,把所有控件向右移动MoveX(除list).
6. 计算list控件的宽度。MinLeft.left - cListMargin + MoveX - rcPrimary.left
7. 高度不变,左上顶点左边不变。用MoveWindow改变宽度。
同样的,向下对其也是同样的道理。
之前在网上看到过一个CScreenlib的类,是用来Layout窗体的。我在其基础上扩展了三个比较实用的接口
static void DockControl(HWND hwndDlg, UINT nIDAffectedCtl, DockType nType = dtFill);
static void OptimizeWidth(HWND hwndDlg, int cAffectedCtls, UINT nIDAffectedCtl, ...);
static void OptimizeHeight(HWND hwndDlg, UINT nIDAffectedCtl);
static void AlignControls(HWND hwndDlg, AlignType nType, int cAffectedCtls, UINT nIDFixedCtl, UINT nIDAffectedCtl, ...);
static void MakeSameSize(HWND hwndDlg, SizeType nType, int cAffectedCtls, UINT nIDFixedCtl, UINT nIDAffectedCtl, ...);
static void OptimizeListRight(HWND hwndDlg, UINT nIDPrimaryCtl, int cControlCount, ...);
static void OptimizeListBottom(HWND hwndDlg, UINT nIDPrimaryCtl, int cControlCount, ...);
static void AlignFixControls(HWND hwndDlg, AlignType nType, UINT nIDPrimaryCtl, int cControlCount, ...);
下面来介绍下各个函数的使用方法。
DockControl: 自动拉伸
OptimizeWidth: 自动把控件拉伸到右边
OptimizeHeight:把控件拉伸到底部
AlignControls:其他控件根据nIDFixedCtl的位置向左、向右、向下、向上对其。
MakeSameSize:使其他跟nIDFixedCtl一样大小
关于上面这几个函数的使用,见MSDN视频
下面介绍下
OptimizeListRight、OptimizeListBottom和AlignFixControls
OptimizeListRight把一组控件和list控件向右靠其
我们在VS2005中layout一个如下对话框。
。
用OptimizeListRight来对其到右边。nIDPrimaryCtl为列表控件,cControlCount为其他控件个数。在WM_SIZE中添加
CScreenLib::OptimizeListRight(hDlg, IDC_LIST, 2, IDC_SEND, IDC_DELETE);
这样实现了控件的又靠其。但是发现下面两个按钮没显示出来。我们使用OptimizeListBottom来进行按钮的下对其。
在WM_SIZE中添加
CScreenLib::OptimizeListRight(hDlg, IDC_LIST, 2, IDC_SEND, IDC_DELETE);
CScreenLib::OptimizeListBottom(hDlg, IDC_LIST, 3, IDC_CHECK, IDC_LEFT, IDC_RIGHT);
运行结果如下:
这样图标已经下对其了。但是”向右”按钮没有和删除按钮右对其。接口AlignFixControls
把一组控件对其到某个控件,nIDPrimaryCtl为目标控件。这组控件间的相对位置不变,如果是向右对其,则把这组控件最右的控件对其到目标控件,然后移动其他控件,并保持这组控件相对位置不变。
在WM_SIZE中添加
CScreenLib::OptimizeListRight(hDlg, IDC_LIST, 2, IDC_SEND, IDC_DELETE);
CScreenLib::OptimizeListBottom(hDlg, IDC_LIST, 3, IDC_CHECK, IDC_LEFT, IDC_RIGHT);
CScreenLib::AlignFixControls(hDlg, CScreenLib::atRight,IDC_DELETE, 2,IDC_LEFT, IDC_RIGHT);
运行效果如下:
CScreenlib在