天天看点

Windows Mobile 开发识别屏幕方向的应用程序

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;

      }

开发识别屏幕方向的应用程序

    我们先来看下微软的“今日”设置界面。

Windows Mobile 开发识别屏幕方向的应用程序
Windows Mobile 开发识别屏幕方向的应用程序

上面这两个图分别是肖像模式和风景画模式的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一个如下对话框。

Windows Mobile 开发识别屏幕方向的应用程序

用OptimizeListRight来对其到右边。nIDPrimaryCtl为列表控件,cControlCount为其他控件个数。在WM_SIZE中添加

CScreenLib::OptimizeListRight(hDlg, IDC_LIST, 2, IDC_SEND, IDC_DELETE);

Windows Mobile 开发识别屏幕方向的应用程序
Windows Mobile 开发识别屏幕方向的应用程序

这样实现了控件的又靠其。但是发现下面两个按钮没显示出来。我们使用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);

运行结果如下:

Windows Mobile 开发识别屏幕方向的应用程序
Windows Mobile 开发识别屏幕方向的应用程序

这样图标已经下对其了。但是”向右”按钮没有和删除按钮右对其。接口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);

运行效果如下:

Windows Mobile 开发识别屏幕方向的应用程序
Windows Mobile 开发识别屏幕方向的应用程序

CScreenlib在

http://download.csdn.net/source/1630242