[翻译]-Windows CE 程序设计 (3rd 版)--5.2 公共控件(十)

      菜单条                                       翻译:tellmenow

菜单条控件是在Pocket PC 2000中开始引入的。从外观上看,菜单条同命令条的不同之处在于它是位于窗口底部而不是顶部。但是,对程序员来说,菜单条有许多不同的编程接口。因为Pocket PC的流行以及OEM厂商希望能够创建同Pocket PC软件兼容的嵌入式系统,所以从Windows CE.NET 4.2开始,现在菜单条同Windows CE的嵌入式版本一起发布。

菜单条是一个精巧复杂的控件,这使得它不适合手工编程。菜单条控件的设计者似乎也有意通过代码向导和资源编辑器来完成菜单条控件的大部分编码和资源产生。虽然这是大部分Windows程序员编码的方式,但了解菜单条控件实际上是如何工作的依然是很重要的,尤其是当工具不能完全符合工作需要的时候。出于这个原因,在本节,我将在基本的API级别上描述菜单条。因此,我可以准确的描述出希望的控件外观,特别是用资源的方式来描述。对书中后面的例子,当我在例子中使用菜单条时,我将用代码向导来产生菜单条上的菜单。

在一头钻进去编写菜单条之前,我想简单讲一下如何设计控件。在许多方面,菜单条都和Windows CE系统中使用的其它命令条控件不同。首先,菜单条上的菜单不是作为一个整体来管理的。相反,虽然菜单被定义成一个单一资源,但是菜单条将菜单作为一系列独立的子菜单来管理。

当菜单条上对应的按钮被点中时,每个子菜单都被当作一个弹出菜单显示在适当的位置上。所以,从这个意义上讲,同命令条相比,菜单条更像是一个工具条。

用户很少能看到菜单条和命令条之间的差别,因为菜单按钮是按期望的方式,彼此相邻的摆放在菜单条的最左边的。然而,对于程序员来说,理解差异的重点在于理解如何管理和操纵菜单条。

和命令条的另一个不同点在于,菜单条并不是创建它的窗口的子窗口。菜单条控件实际上是系统创建的一个弹出窗口,并放在屏幕底部而已。创建菜单的窗口可以通过覆盖菜单条来将其隐藏起来。菜单条的部分可以被绘制到它的拥有者的上层。为了避免这种情况,应用程序必须调整窗口尺寸来为桌面上菜单条留出空间。应用程序同菜单条之间的这种纠缠,就是为什么使用菜单条控件的应用程序要手工调整其主窗口的原因。

图5-5展示了Pocket PC上的菜单条,图5-6显示了在嵌入式系统中运行同样的应用程序时的界面。将要讨论的两个菜单条外观上微小差异还是存在的。
图5-5(略):Pocket PC设备上的菜单条。
图5-5(略):嵌入式系统上的菜单条。

Pocket PC上的菜单条在控件最左边包含了软键盘(SIP)按钮。在嵌入式设备上,SIP按钮在任务条上,而不在菜单条上。作为SIP按钮的替代,嵌入式设备上的菜单条有一个[关闭]按钮,同Pocket PC相比,在嵌入式设备屏幕顶部的导航条上有一个轻巧的[最小化]按钮。最后,即使是拥有同样菜单条资源的非常相似的应用程序,在Pocket PC版的菜单条最左边有一个叫[新建]([NEW])的菜单。这是shell的一个扩展,但嵌入式设备并不支持。因为缺乏这个Shell支持,所以即使用来创建菜单条的资源中指定了[新建]菜单,菜单条不能创建[新建]菜单。

菜单条在Pocket PC和嵌入式设备中的另一个不同是菜单条的高度。因为菜单条高度随系统的不同而不同,所以必须编程来判断菜单条的高度。老一些的Pocket PC应用程序,包括本书前几版中的程序,都是假设菜单条是26像素高。既然现在菜单条控件用在各种系统上,那么这个假设就不成立了。一种计算菜单条高度的方式是用菜单条句柄作为参数来调用GetWindowRect函数。下面的代码是在WM_CREATE消息处理函数中,菜单条创建后就计算高度。

RECT rectMB;
GetWindowRect (hwndMenuBar, &rectMB);
nMBHeight = (rectMB.bottom - rectMB.top);

创建菜单条                                                                                                    翻译:tellmenow
要创建菜单条,可以调用BOOL SHCreateMenuBar (SHMENUBARINFO *pmb);函数中唯一的参数是一个SHMENUBARINFO结构的的地址,该结构定义如下:
typedef struct tagSHMENUBARINFO {
DWORD cbSize;
HWND hwndParent;
DWORD dwFlags;
UINT nToolBarId;
HINSTANCE hInstRes;
int nBmpId;
int cBmpImages;
HWND hwndMB;
COLORREF clrBk;
}SHMENUBARINFO;

必须使用SHMENUBARINFO结构的尺寸来设置cbSize域。hwndParent设置为创建菜单条的窗口句柄。dwFlags则设置成三个标志的组合:
SHCMBF_EMPTYBAR 用来创建没有菜单的菜单条
SHCMBF_HIDDEN 创建一个菜单条,初始状态为隐藏
SHCMBF_HIDESIPBUTTON 创建一个菜单,其右边没有SIP按钮
SHCMBF_COLORBK 指出clrBk域中包含有效的颜色,在填充菜单条背景时使用。
SHCMBF_HMENU 指出资源是菜单资源,而不是菜单条资源

除非指定了SHCMBF_EMPTYBAR标志,否则你必须将nToolBarId域设置成描述菜单条中菜单和按钮结构的资源。除非使用了SHCMBF_HMENU标志,否则该资源并不是简单的菜单资源。该资源是一个普通资源数据快和菜单资源的组合,用来一起描述菜单条上的菜单以及按钮位置。我将在本节后面描述该资源。

接下来是hInstRes域,应该设置成包含菜单条资源的模块的实例句柄。随后的两个域,nBmpId和cBmpImages,描述了用于定义菜单条上按钮外观的位图图片。如果菜单条上有图形按钮,则可以将nBmpId设置成位图资源ID。该位图应该是16像素高,位图中的每个图片应该是16像素宽。所以如果位图中有三个图片,那么位图应该是48像素宽,16像素高。cBmpImages域设置成位图中图片的数量。为了更有艺术性,请参考最新的应用程序指导,看看如何将位图同其它部分搭配的更恰当。

如果菜单条创建成功,SHCReateMenuBar函数返回TRUE,这时候,hwndMB域中将包含菜单条句柄。您需要保存这个窗口句柄,因为一旦创建完成后,就没有其它方式可以确定菜单条句柄了。

菜单条资源                                                 翻译:tellmenow
如前所述,菜单条在很多方面都像一个工具条控件。查看菜单条使用的资源,会看到这两个对象的一些差别是在于外观上。图5-7显示了一个简单菜单条。
图5-7(略):一个打开了编辑菜单(Edit)的简单菜单条。

当创建菜单条时,SHMENUBARINFO结构中的nToolBarId域被适当的设置,因为nToolBarID标识的资源不是菜单资源,而是一个用于菜单条控件的定制资源。为了创建图5-7所展示的菜单条,资源编辑器将在.RC文件中创建下面的文本:
///
// Data
//
IDM_MENU SHMENUBAR MOVEABLE PURE
BEGIN
    IDM_MENU, 4,
    I_IMAGENONE, IDM_SHAREDNEW, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDS_SHNEW,
    0, NOMENU,
    I_IMAGENONE, ID_EDIT, TBSTATE_ENABLED,
    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_EDIT, 0, 0,
    I_IMAGENONE, IDM_MAIN_COMMAND1, TBSTATE_ENABLED,
    TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP, 0, 1,
    0, ID_BACKBTN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, ID_BACKBTN, 2,
END
  
///
// Menu bar
//
IDM_MENU MENU DISCARDABLE
BEGIN
    POPUP "Edit"
    BEGIN
        MENUITEM "Cut",                         ID_EDIT_CUT
        MENUITEM "Copy",                        ID_EDIT_COPY
        MENUITEM "Paste",                       ID_EDIT_PASTE
    END
    POPUP "Tools"
    BEGIN
        MENUITEM "About",                       IDM_HELP_ABOUT
        MENUITEM "Options",                     ID_TOOLS_OPTIONS
    END
END

大多数情况下,不需要准确的知道资源编辑器在资源中放置了什么资源。然而,有必要了解格式,这样可以容易的更改使用了菜单条的应用程序,也可以在碰到使用的设备上资源编辑器不能创建菜单条控件的时候,来方便使用。资源实际上就是对工具条上按钮的描述。下面的代码提供了前述数据的格式:
IDM_MENU SHMENUBAR MOVEABLE PURE
BEGIN
    IDM_MENU, 4,
  
    I_IMAGENONE, IDM_SHAREDNEW,     TBSTATE_ENABLED,
      TBSTYLE_AUTOSIZE,                    IDS_SHNEW,    0,         NOMENU,
  
    I_IMAGENONE, ID_EDIT,           TBSTATE_ENABLED,
      TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_EDIT, 0,         0,
  
    I_IMAGENONE, IDM_MAIN_COMMAND1, TBSTATE_ENABLED,
      TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP,     0,         1,
  
    0,           ID_BACKBTN,        TBSTATE_ENABLED,
      TBSTYLE_AUTOSIZE,                    0,           ID_BACKBTN, 2,
END

资源文件中的第一行中,IDM_MENU是资源ID,SHMENUBAR是资源类型,MOVEABLE和PURE是资源标志。IDM_MENU作为ID需要传递到SHCreateMenuBar的SHMENUBARINFO 结构中。资源类型SHMENUBAR实际上被向导定义成了RCDATA,资源编译器将它理解成一个由应用程序使用的简单资源数据块。这一点很重要,因为SHMENUBAR并没有被定义在Pocket PC 的包含文件(include files)中,只有使用Pocket PC 应用向导(AppWizard)来创建菜单条资源时才会包含它。所以,对于非向导产生的菜单条资源文件,需要加入以下内容到RC文件中:#define SHMENUBAR RCDATA

BEGIN/END块中第一行数据是:IDM_MENU, 4。这一行定义了菜单资源,用于创建菜单条上单独的弹出菜单。数字4表示该项在SHMENUBAR资源中的编号。每一项要么是菜单条上的弹出菜单,要么就是一个按钮。

因为书本印刷格式的原因,导致大家看到的前面的资源描述中每项的描述都被折成了两行。让我们看一下资源中的最后一项--回退(Back)按钮项:
0,    ID_BACKBTN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, ID_BACKBTN, 2,

对该行进行垂直折行,加入注释后,资源描述如下:

0,                            // Bitmap index
ID_BACKBTN,                   // WM_COMMAND ID value
TBSTATE_ENABLED,              // Initial state of "button"
TBSTYLE_AUTOSIZE,             // Style of "button"
0,                            // String resource ID of text label
ID_BACKBTN,                   // String resource ID of tooltip
2,                            // Submenu index

菜单条上该项的图片在位图数组中的索引就包含在第一个域中。如果该项没有位图,就设置成I_IMAGENONE。在上面的例子中,使用的图片是位图数组中的第一个。下一个域包含该项的ID值。对按钮来说,该值就是当按钮被点压时随WM_COMMAND消息发送到父窗口的那个ID值。对于菜单来说,当查询子菜单句柄的时候可以用这个ID来标识子菜单。因为shell在菜单条中会使用自己的ID集合,所以应用程序不应该使用小于100的值。这条规则一样适用于菜单、按钮以及字符串资源ID。

菜单条使用两个预定义的菜单项ID:IDM_SHAREDNEW 和 IDM_SHAREDNEWDEFAULT。这两个ID会添加一个New(新建)菜单项,用来显示其它应用程序注册的菜单项。两个ID的区别是,简单的点一下菜单项,IDM_SHAREDNEWDEFAULT就会显示一个新的菜单项。而使用IDM_SHAREDNEW,则将New菜单变成了一个带下拉箭头的按钮。在New按钮上点击,会发送WM_COMMAND消息到父窗口,指出应该创建一个新文档。在箭头上点击则显示新菜单自身。对于非Pocket PC系统,只有当shell为系统提供New菜单支持的时候,New菜单才会显示在菜单条上,否则,预定义的新菜单项ID将被忽略。

接下来的两个域是按钮/根菜单项的初始状态和风格。状态域使用的是工具条的状态标志来进行描述的,例如TBSTATE_ENABLED 和TBSTATE_CHECKED。对于菜单,状态几乎总是TBSTATE_ENABLED。风格域也是使用工具条风格标志来描述的,例如用于按钮的TBSTYLE_BUTTON,用于菜单项的TBSTYLE_DROPDOWN。有文本而不是位图的项以及包含位图的项通常也会设置TBSTYLE_AUTOSIZE 来告诉菜单条调整按钮的尺寸来适应菜单项中的文本。

下一个域是菜单项文本的字符串资源ID。文字和第一个域中指定的图片是并排摆放的。在上面的例子中,该项只是一个简单的位图按钮,所以没有指定字符串资源。对菜单项来说,是字符串资源来标记菜单,而不是在菜单资源中指定的子菜单名。如果需要的话,您可以使用7个预定义的字符串ID,它们作为自解释型常量被定义在Aygshell.h文件中。
#define IDS_SHNEW           1
#define IDS_SHEDIT          2
#define IDS_SHTOOLS         3
#define IDS_SHVIEW          4
#define IDS_SHFILE          5
#define IDS_SHGO            6
#define IDS_SHFAVORITES     7
#define IDS_SHOPEN          8
如果您需要一个不同的文本,那么您的应用程序必须用字符串资源定义文本,并将ID传递到这个域中。下一个域是工具提示信息(tool tip)域。同样,您必须使用字符串资源ID来填充这个域。

最后一个域规定了子菜单,当用户点这个项,就会弹出来。只有风格域包含TBSTYLE_DROPDOWN标志(表示该项附加了一个菜单),这个子菜单值才是有效的。这个值代表子菜单的菜单资源索引。本节提到的例子中有两个子菜单:编辑(Eidt)菜单,包含了Cut,Copy和Paste三个菜单项;工具(Tools)菜单,包含About和选项(Options)两个菜单项。 按钮上显示的文字是来自菜单条资源中,而不是来自菜单资源。例如,

可以按下面的代码来修改菜单资源,而不用改变菜单条中的文本。
///
// Menu bar
//
IDM_MENU MENU DISCARDABLE
BEGIN
    POPUP "Cat"
    BEGIN
        MENUITEM "Cut",                         ID_EDIT_CUT
        MENUITEM "Copy",                        ID_EDIT_COPY
        MENUITEM "Paste",                       ID_EDIT_PASTE
    END
    POPUP "Dog"
    BEGIN
        MENUITEM "About",                       IDM_HELP_ABOUT
        MENUITEM "Options",                     ID_TOOLS_OPTIONS
    END
END
现在,根菜单名字是Cat和Dog,而不是Edit和Options。因为菜单条从菜单条项中取名字,而不是从菜单资源中取,所以这个改变并没有影响应用程序。

对菜单条的长篇描述是让您了解基础知识。只有在很少见的情况下,才需要您手工操纵资源。当然,这些知识很容易使用的。

使用菜单条                                                                                翻译:tellmenow
一旦创建了菜单条,可能还需要配置它。虽然菜单条看上去和命令条不同,但它同样是建立在工具条基础上的。所以尽管您不希望菜单条的行为总是和命令条一样,但您可以一些命令条的函数和工具条的消息。例如,公共控件的一个很方便的特性是它们包含一系列位图,用于工具条上的按钮。所以您不必自己创建位图,这样可能会创建一个非标准图象,您可以使用系统提供的图像,用于剪切、复制和粘贴。

在菜单条中使用公共控件位图
要使用系统提供的位图,只要简单的加它们到菜单条中即可,就如同加它们到公共控件条一样。菜单条被创建的时候,在结构SHMENUBARINFO中指定的任何位图将被加到菜单条中。所以,如果有一个包含三个图象的位图,并要加到标准图象集中,那么“剪切(cut)”位图图象应该指定为STD_CUT+3。在下面的代码片段中,将创建一个菜单条,并且将标准图象集加到其中。
if(!SHCreateMenuBar(&mbi))
   return NULL;
CommandBar_AddBitmap(mbi.hwndMB, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, STD_PRINT, 16,16);
在菜单条资源里的菜单项中指定正确的索引,是使用这些图象的最简单方法。菜单条项资源中的第一个域就是位图图象索引。只要位按钮位图设置位图索引即可。

使用菜单条菜单
有时应用程序需要操纵菜单条,比如设置/清楚检查标志(check mark)或者设置菜单项的生效/失效等。标准函数集(例如CheckMenuItem)用在菜单条上来修改菜单。需要获得菜单句柄,这样就可以修改菜单项了。菜单条提供三个消息用于获得/设置菜单句柄:SHCMBM_GETMENU、SHCMBM_GETSUBMENU和SHCMBM_SETSUBMENU。可以发送SHCMBM_GETMENU和SHCMBM_GETSUBMENU消息到菜单条来查询菜单句柄或指定的子菜单。下面演示了如何使用SHCMBM_GETMENU来查询根菜单句柄。
hMenu = (HMENU) SendMessage (hwndMenuBar, SHCMBM_GETMENU, 0, 0);
可以使用这个菜单句柄来修改菜单条上显示的任意菜单项。要查询绑定到菜单条指定项的子菜单,可以使用SHCMBM_GETSUBMENU,例如
hSubMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_GETSUBMENU, 0, ID_VIEWMENU);
参数lParam被设置成菜单条上指定按钮的ID,本例中,菜单句柄绑定到ID是ID_VIEWMENU的按钮上。

要改变菜单条上特性按钮的菜单,可以使用SHCMBM_SETMENU消息,将参数wParam设置为按钮ID,参数lParam设置成新的菜单句柄,例如
hOldMenu = (HMENU)SendMessage (hwndMenuBar, SHCMBM_SETMENU, ID_VIEWMENU, (LPARAM)hNewMenu);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值