WIN32汇编语言教程:第05章 使用资源 · 5.1 菜单和加速键(6)

4. 菜单项的修改

在程序的运行中也可以动态修改菜单项,包括添加、删除和修改操作,这些操作是通过几个API函数来完成的:

invoke AppendMenu,hMenu,uFlags,uIDNewItem,lpNewItem           ;添加菜单项
invoke InsertMenu,hMenu,uPosition,uFlags,uIDNewItem,lpNewItem ;插入菜单项
invoke ModifyMenu,hMenu,uPosition,uFlags,uIDNewItem,lpNewItem ;修改菜单项
invoke DeleteMenu,hMenu,uPosition,uFlags                      ;删除菜单项
invoke RemoveMenu,hMenu,uPosition,uFlags                      ;删除菜单项

其中AppendMenu用来在一个菜单的最后添加菜单项,InsertMenu则在中间插入菜单项,ModifyMenu可以修改一个菜单项的文字,DeleteMenu和RemoveMenu则可以删除一个菜单项。

这些函数中的参数都是雷同的,hMenu参数指要操作的菜单句柄;uPosition用来定位要操作的菜单项,定位的方法有两种:用命令ID定位或用位置索引,用哪一种方法取决于后面的uFlags参数,当uFlags为MF_BYCOMMAND时,uPostion为菜单项的命令ID,而当uFlags为MF_BYPOSITION的时候,uPostion表示菜单项的位置索引,索引是从0开始的,也就是说第一个菜单项的索引值为0。

AppendMenu函数总是在菜单的最后添加新的菜单项,所以不需要uPostion参数。

对于AppendMenu和InsertMenu,会有一个新的菜单项产生,uIDNewItem就表示这个新菜单项的命令ID,lpNewItem指向新菜单项的文字字符串,ModifyMenu函数可以修改一个菜单项的命令ID或文字字符串,所以也有uIDNewItem和lpNewItem参数。而用来删除菜单项的DeleteMenu和RemoveMenu显然用不着uIDNewItem和lpNewItem参数。

uFlags参数除了指定MF_BYCOMMAND还是MF_BYPOSITION外,还可以组合指定菜单项的其他属性,如MF_CHECKED,MF_DISABLED,MF_ENABLED,MF_GRAYED,MF_MENUBARBREAK,MF_MENUBREAK,MF_SEPARATOR和MF_UNCHECKED等,从其字面上就可以看出这些属性的含义。

DeleteMenu和RemoveMenu的不同之处在于对popup菜单项的处理。当它们用于popup属性的菜单项时,DeleteMenu不仅删除菜单项,而且将这个popup菜单项的所有子项目全部删除,这样,这个popup菜单就不能在别的地方继续使用;而RemoveMenu仅从菜单中移去这个popup菜单项,整个popup菜单在内存中还是存在的。以Menu.asm程序为例,按鼠标右键弹出的菜单实际上是主菜单中的“查看”菜单项,假如用DeleteMenu删除主菜单中的“查看”项目,那么按右键也就弹不出菜单了,而用RemoveMenu删除主菜单中的“查看”项目,按鼠标右键仍然可以弹出菜单。对于非popup属性的菜单项,DeleteMenu和RemoveMenu的效果是同样的。

5. 使用系统菜单

系统菜单指按下了标题栏图标后弹出的菜单,和窗口菜单不同,选中系统菜单的菜单项后,Windows向窗口发送的是WM_SYSCOMMAND消息而非WM_COMMAND消息。默认的系统菜单中已经有“还原”、“移动”、“大小”、“最大化”、“最小化”和“关闭”等菜单项,这些菜单项的命令ID已经预定义为SC_RESTORE,SC_MOVE,SC_SIZE,SC_MAXIMIZE,SC_MINIMIZE和SC_CLOSE等,如果读者要自己处理它们,可以在WM_SYSCOMMAND消息中建立一个比较分支对它们进行处理,一般在程序中并不自己处理WM_SYSCOMMAND消息,而是交给DefWindowProc处理。

如何在系统菜单中添加自己的菜单项呢?方法就是使用上面介绍的AppendMenu(当然也可以用InsertMenu),在添加前必须用GetSystemMenu函数首先获取系统菜单的句柄。例子程序在窗口初始化的时候在系统菜单尾添加了一个分隔线和两个菜单项:“帮助主题”和“关于本程序”:

.if eax == WM_CREATE
    ...
    invoke GetSystemMenu,hWnd,FALSE
    mov    @hSysMenu,eax
    invoke AppendMenu,@hSysMenu,MF_SEPARATOR,0,NULL
    invoke AppendMenu,@hSysMenu,0,IDM_HELP,offset szMenuHelp
    invoke AppendMenu,@hSysMenu,0,IDM_ABOUT,offset szMenuAbout

在窗口过程中处理系统菜单消息的分支结构为:

.elseif eax == WM_SYSCOMMAND
       mov    eax,wParam
       .if    ax == 自定义ID1
              ...
       .elseif ax == 自定义ID2
               ...
       .else
               invoke DefWindowProc,hWnd,uMsg,wParam,lParam
               ret
       .endif

和处理WM_COMMAND消息不同的是,在WM_SYSCOMMAND消息中处理了自定义的菜单命令ID后,必须把其他命令ID交给DefWindowProc处理,并直接把返回值返回给Windows,不然的话会发现窗口不能移动,不能关闭,不能最小化......因为它相当于屏蔽了所有SC_RESTORE,SC_MOVE,SC_SIZE,SC_MAXIMIZE,SC_MINIMIZE和SC_CLOSE等消息的处理。

6. 右键弹出菜单

例子程序的一个功能是当用户在窗口客户区按下鼠标右键的时候弹出一个菜单,这个功能是用TrackPopupMenu函数实现的。TrackPopupMenu函数的用法:

invoke TrackPopupMenu,hMenu,uFlags,x,y,nReserved,hWnd,lpRect

这个函数本身很简单,执行后在参数指定的x,y位置弹出一个属于hWnd窗口(也就是说WM_COMMAND消息发到这个窗口)的菜单,菜单句柄是hMenu。函数中的坐标是以整个屏幕左上角为基准的,所以弹出菜单的位置不一定在窗口的客户区内,它可以是屏幕的任何一个地方。

uFlags参数指定一些和位置相关的选项,它可以是PM_CENTERALIGN,TPM_LEFTALIGN或TPM_RIGHTALIGN三者之一,表示(x,y)坐标是代表弹出菜单位置的中间、左上角还是右上角,一般的习惯是使用TPM_LEFTALIGN,这样菜单会在鼠标点击处的右边弹出。uFlags中同时还可以指定用鼠标左键还是右键选定菜单项,定义值可以是TPM_LEFTBUTTON或TPM_RIGHTBUTTON,如果选择TPM_RIGHTBUTTON的话,对在菜单项上面按鼠标左键是没有反应的。

lpRect指向一个RECT结构,用来指定一个区域,当菜单弹出后,在这个区域外单击鼠标,菜单才会消失,如果这个参数指定为NULL的话,在菜单之外单击鼠标,菜单就会消失。

在使用TrackPopupMenu之前,有几个准备工作是要做的:为了在客户区中按下鼠标右键弹出菜单,我们当然要处理鼠标右键消息,也就是说在WM_RBUTTONDOWN消息中调用TrackPopupMenu函数,一般的习惯是在鼠标按下的地方弹出菜单,所以还要首先获取鼠标光标的位置,然后在此位置弹出菜单。

要获取鼠标位置,可以用GetCursorPos函数:

invoke GetCursorPos,lpPoint

参数lpPoint指向一个POINT数据结构,这个结构只有两个字段:

POINT STRUCT
x DWORD ?
y DWORD ?
POINT ENDS

该结构用来表示一个点的(x,y)坐标,GetCursorPos将当前的鼠标位置返回到这个结构中,程序中的相关代码是:

local  @stPos:POINT   ;首先定义一个POINT结构
...
invoke GetCursorPos,addr @stPos      ;获取鼠标位置
invoke TrackPopupMenu,hSubMenu,\
      TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL

用GetCursorPos获取的鼠标位置是一个POINT结构,但TrackPopupMenu输入坐标的方法是用x,y两个参数,而不是一个POINT结构,所以要用结构中的两个字段@stPos.x和@stPos.y分别输入。

使用TrackPopupMenu时要注意的是,弹出的菜单句柄必须是popup类型的,而在资源文件中定义并且可以用LoadMenu函数装入的菜单并不是popup类型的,popup菜单(如例子中的“文件”与“查看”等)只能在第二层中才能定义,在程序中用GetSubMenu得到的第二层子菜单的句柄才是popup类型的。GetSubMenu函数的用法是:

invoke GetSubMenu,hMenu,nPos
.if    eax
        mov hSubMenu,eax
.endif

nPos参数指定要获取的菜单的位置索引,GetSubMenu的返回值是获取的子菜单句柄。

例子用invoke GetSubMenu,hMenu,1取得第二个子菜单(“文件”子菜单为0,“查看”子菜单为1,......)的句柄,然后在TrackPopupMenu中使用,这个菜单句柄就是主菜单中的“查看”菜单,所以按鼠标右键弹出的菜单和下拉菜单中的“查看”菜单是一模一样的。

上页:第05章 使用资源 · 5.1 菜单和加速键(5) 下页:第05章 使用资源 · 5.1 菜单和加速键(7)

第05章 使用资源

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号