• 菜单编写(VC)


    目录

    菜单在 .rc 文件中的格式
    加载/卸载菜单
    菜单常用的操作
    创建弹出菜单
    菜单加速键
    MFC下菜单消息路由

    (本章节中例子都是用 VS2005 编译调试的)


    菜单在 .rc 文件中的格式

    .rc 中的菜单格式

    虽然现在微软的编译器中都会自动生成好用的 rc 资源但是还是可以了解下它内部代码的意义.

    这里是不太建议直接在 .rc 文件中修改菜单因为修改了.rc 文件后还需在其他文件中修改对应地方,否则在编译中会报错.所以还是建议在编译器的资源管理器中修改对话框.

    格式:

    menuID MENU [,载入特性选项]
    {
    菜单项列表
    }

    说明:

    • menuID: 菜单资源标识
    • MEMU: 关键字
    • 载入特性: 
      • DISCARDABLE 当不再需要菜单时候菜单可丢弃
      • FIXED 将菜单保存在内存中固定位置
      • LOADONCALL 需要时加载菜单
      • MOVEABLE 菜单在内存中可移动
      • PRELOAD 立即加载菜单
    • 菜单项列表:
      • 弹出菜单/子菜单(POPUP)
        • 格式:
          • POPUP"子菜单名"[,选项]
            BEGIN
            …(菜单项成员)
            END
        • 说明:
          • POPUP:  关键字
          • 子菜单名:  "子菜单的名字&热键"
          • BEGIN:  子菜单中菜单项开始的标识
          • 选项:
            • MENUBARBREAK   菜单项纵向分隔标识
            • CHECKED   显示选中标识
            • INACTIVE   禁止一个菜单项
            • GRAYED   禁止一个菜单项并使其显示灰色
          • 菜单项成员:   子菜单或菜单项(定义如下所示)
          • END:   子菜单中菜单项结束的标识
      • 菜单项(MENUITEM)
        • 格式:  MENUITEM "菜单项名",菜单项标识符(ID)[,选项]
        • 说明:
          • MENUITEM:   关键字
          • 菜单项名:   "菜单项名字&热键"
          • 选项:
            • MENUBARBREAK   菜单项纵向分隔标识
            • CHECKED   显示选中标识
            • INACTIVE   禁止一个菜单项
            • GRAYED   禁止一个菜单项并使其显示灰色 

    菜单组成部分

    • 主菜单栏
    • 下拉式菜单框
    • 菜单项热键标识
    • 菜单项加速键标识
    • 菜单项分割线(占据菜单索引)

    加载/卸载菜单

    加载菜单

    在 win32 界面程序中加载菜单有以下几种方式:

    • 在窗口类设计时候进行加载
      在定义 WNDCLASS 时对成员 lpszMenuName 赋予相对应的值
    • 在创建窗口时候进行加载
    • 动态加载菜单

    代码示例:

    .rc 资源内容

    IDR_MENU1 MENU 
    BEGIN
        POPUP "菜单1"
        BEGIN
            POPUP "子菜单1.1"
            BEGIN
                MENUITEM "菜单项1.1.1",                    ID_40001
                MENUITEM "菜单项1.2.1",                    ID_40002
            END
            MENUITEM "菜单项1.2",                      ID_40003
            MENUITEM SEPARATOR
            MENUITEM "菜单项1.3",                      ID_40004
            MENUITEM "菜单项1.4",                      ID_40005
        END
        POPUP "菜单2"
        BEGIN
            MENUITEM "菜单项2.1",                      ID_40006
            MENUITEM "菜单项2.2",                      ID_40007
        END
    END

    加载菜单:

    • 第一种加载方式(类设计时):
      WNDCLASS wndclass;
      
      ....
      wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
      //这里省略了窗体类创建时需要填写的其他信息.
    • 第二种加载方式(窗体创建时):
      HMENU hmenu;
      WNDCLASS wndclass;
      
      ....
      wndclass.lpszMenuName=NULL;
      //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作
      
      //加载菜单到菜单句柄中
      hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
      //在创建窗体时候载入菜单
      hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
          CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);
    • 第三种加载方式(窗体创建后):
      HMENU hmenu;
      WNDCLASS wndclass;
      
      ....
      wndclass.lpszMenuName=NULL;
      //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作
      
      //创建窗体
      hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
          CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
      
      //加载菜单到菜单句柄中
      hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
      //动态的加载菜单到窗体中去
      SetMenu(hwnd,hmenu);

    程序源码:

    View Code
    #include<windows.h>
    #include"resource.h"
    
    LRESULT CALLBACK textprom(
      HWND hwnd,      // handle to window
      UINT uMsg,      // message identifier
      WPARAM wParam,  // first message parameter
      LPARAM lParam   // second message parameter
    );
    
    int WINAPI WinMain(  HINSTANCE hInstance,  // handle to current instance
      HINSTANCE hPrevInstance,  // handle to previous instance
      LPSTR lpCmdLine,      // pointer to command line
      int nCmdShow          // show state of window
      )
    {
        WNDCLASS wndclass;
        HWND hwnd;
        HMENU hmenu;
        MSG msg;
    
        //设计窗口类
        wndclass.cbClsExtra=0;
        wndclass.cbWndExtra=0;
        wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
        wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
        wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
        wndclass.hInstance=hInstance;
        wndclass.lpfnWndProc=textprom;
        wndclass.lpszClassName="text";
        wndclass.lpszMenuName=NULL;
        //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
        wndclass.style=CS_HREDRAW | CS_VREDRAW;
        
        //注册窗口类
        if(!RegisterClass(&wndclass))
        {
            MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
        }
    
        //创建无菜单资源的窗口窗口
        hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,
            500,300,NULL,NULL,hInstance,NULL);
        
        /*
        //载入菜单资源
        hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
        //创建有菜单资源的窗口
        hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
            CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/
        
        //载入菜单资源,并在窗口加载菜单资源
        hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
        SetMenu(hwnd,hmenu);
    
        //显示更新窗口
        ShowWindow(hwnd,nCmdShow);
        UpdateWindow(hwnd);
    
        //消息循环
        while(GetMessage(&msg,NULL,0,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return msg.wParam;
    }
    
    LRESULT CALLBACK textprom(
      HWND hwnd,      // handle to window
      UINT uMsg,      // message identifier
      WPARAM wParam,  // first message parameter
      LPARAM lParam   // second message parameter
    )
    {
        switch(uMsg)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        }
        return DefWindowProc(hwnd,uMsg,wParam,lParam);
    }

    运行结果:

    卸载菜单

    步骤:

    代码示例:

    HMENU hmenu;
    switch(uMsg)
    {
    //添加鼠标右键单击事件响应处理,即卸载菜单
    case WM_LBUTTONDOWN:
        hmenu = GetMenu(hwnd);
        if(hmenu != NULL)
        {
            SetMenu(hwnd,NULL);
            DestroyMenu(hmenu);
        }
        break;
    
    .....

    运行结果:

     

    单击鼠标右键后


    菜单常用的操作

    菜单项常用操作 

    • 设置默认菜单项  SetMenuDefaultItem
    • 禁止或激活菜单项  EnableMenuItem
    • 设置默认菜单项  SetMenuDefaultItem
    • 修改菜单项  ModifyMenu
    • 设置或或取消菜单项选择标志  CheckMenuItem
    • 获得菜单项的 ID     GetMenuItemID 
    • 获得菜单项的标志位    GetMenuState
    • 获得菜单项的字符串     GetMenuString 

    菜单常用操作

    • 获取子菜单句柄  GetSubMenu
    • 获得窗口主菜单句柄  GetMenu
    • 动态创建空的弹出菜单  CreateMenu
    • 销毁动态创建空的弹出菜单    DestroyMenu 
    • 删除菜单项  DeleteMenu
    • 在菜单中插入菜单项  InsertMenu
    • 在菜单尾部增加菜单项  AppendMenu
    • 获得弹出菜单的菜单子项的数目    GetMenuItemCounte

    菜单索引

    在编写菜单操作前还需了解个很重要的概念,即了解菜单的结构,只有了解了这个结构,才能找到对应的菜单,子菜单或菜单项进行对应的操作.因为在操作菜单时需要获得对应的菜单句柄(如在上图子菜单1.1进行插入菜单项1.3.1操作时要获得子菜单1.1句柄),或操作菜单项时要获得对应的子菜单项的菜单句柄(如让上图菜单项1.1.1禁用时候需要获得子菜单1.1的句柄).然而在获取子菜单句柄时需要子菜单索引,所以对索引号的一些规则必须要有一定的了解.

    简单规则如下:

      • 菜单索引基于0开始;
      • 分隔符也算一个菜单项,所以他也占据一个索引号

    即如下图所示:

    两个简单样例:

    • 在上图子菜单1.1进行插入菜单项1.3.1操作
      //在程序源码的显示更新窗口
      ShowWindow(hwnd,nCmdShow);
      UpdateWindow(hwnd);
      
      //在程序源码的显示更新窗口后插入
      //插入菜单项
      AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");
      程序运行结果:
        
    • 让上图菜单项1.1.1禁用:
       
      //在程序源码的显示更新窗口
      ShowWindow(hwnd,nCmdShow);
      UpdateWindow(hwnd);
      
      //在程序源码的显示更新窗口后插入
      //插入菜单项
      AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");
      //禁用菜单项
      EnableMenuItem(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),0,MF_BYPOSITION | MF_GRAYED);
      运行结果:

    创建弹出菜单

    步骤:

    • 载入菜单资源
      (用 GetSubMenu,非 LoadMenu 或 getMenu,因为后两种获得的菜单句柄都是主菜单的句柄,而主菜单句柄不适合用 TrackPopupMenu 显示弹出菜单,若用的是主菜单句柄作为弹出菜单句柄时候效果如下图所示)
    • 调用 TrackPopupMenu 显示弹出菜单

    流程图如下:

    代码示例:

    .rc 资源内容

    View Code
    /***********************************************/
    //主菜单
    IDR_MENU1 MENU 
    BEGIN
        POPUP "菜单1"
        BEGIN
            POPUP "子菜单1.1"
            BEGIN
                MENUITEM "菜单项1.1.1",                    ID_40001
                MENUITEM "菜单项1.2.1",                    ID_40002
            END
            MENUITEM "菜单项1.2",                      ID_40003
            MENUITEM SEPARATOR
            MENUITEM "菜单项1.3",                      ID_40004
            MENUITEM "菜单项1.4",                      ID_40005
        END
        POPUP "菜单2"
        BEGIN
            MENUITEM "菜单项2.1",                      ID_40006
            MENUITEM "菜单项2.2",                      ID_40007
        END
    END
    /***********************************************/
    //弹出菜单
    IDR_MENU2 MENU 
    BEGIN
        POPUP "弹出菜单"
        BEGIN
            MENUITEM "弹出菜单项1.1",                    ID_A_40012
            MENUITEM "弹出菜单项1.2",                    ID_A_40013
        END
    END

    加载弹出菜单

    hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);

    显示弹出菜单

    POINT p;
    switch(uMsg)
    {
    //添加鼠标左键单击事件响应处理,即显示弹出菜单
    case WM_RBUTTONDOWN:
        p.x=LOWORD(lParam); 
        p.y=HIWORD(lParam);
        //将窗口坐标转换成屏幕坐标
        ClientToScreen(hwnd,&p); 
        TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
        break;
    
    .....

     程序运行结果(在鼠标右键单击后):


    菜单加速键

     .rc 中的菜单格式

    格式:

    • 加速键ID  ACCELERATORS
      BEGIN
      键名,命令ID [,类型] [,选项]
      …  
      END

     说明:

    • 加速键ID:  一个字符串或者是1~65535之间的数字
    • ACCELERATORS:   关键字
    • BEGIN:   关键字,表示加速键列表的开始
    • 键名:   表示加速键对应的按钮,可以有3种方式定义
      • “^字母”:表示Ctrl键加上字母键.
      • “字母”:表示字母,这时类型必须指明是VIRTKEY
      • 数值:表示ASCII码为该数值的字母,这时类型必须指明为ASCII
    • 命令ID:   按下加速键后,Windows向程序发送的命令ID.如果想把加速键和菜单项关联起来,这里就是要关联期间项的命令ID
    • 类型:   用来指定键的定义方式,可以是 VIRTKEY 和 ASCII,分别用来表示“键名”字段定义的是虚拟键还是ASCII码
    • 选项:   可以是 Alt, Control 或 Shift 中的单个或多个,如果指定多个,则中间用逗号隔开,表示加速键是按键加上这些控制键的组合键.这些选项只能在类型是VIRTKEY的情况下才能使用
    • END 关键字,表示加速键列表的结束

    编写菜单资源加速键

    编写步骤:

    • 加载菜单加速键资源:   LoadAccelerators
    • 修改消息循环:  (即在消息循环中先把消息派送给转换菜单加速键,然后在派送给转换消息最后分配消息,如下图所示)

    代码样例(为菜单项1.4增加快捷键 Crt+Alt+K):

    .rc资源:

    IDR_ACCELERATOR1 ACCELERATORS 
    BEGIN
        "K",            ID_40009,               VIRTKEY, CONTROL, ALT, NOINVERT
    END

     加载菜单加速资源:

    HACCEL haccel;
    haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));

     更改消息循环:

    while(GetMessage(&msg,NULL,0,0))
    {
        if(!TranslateAccelerator(hwnd,haccel,&msg))
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }
    }

     添加菜单项1.4 响应事件:

    switch(uMsg)
    {
    //添加鼠标左键单击事件响应处理,即卸载对话框
    case WM_RBUTTONDOWN:
        p.x=LOWORD(lParam); 
        p.y=HIWORD(lParam);
        //将窗口坐标转换成屏幕坐标
        ClientToScreen(hwnd,&p); 
        TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
        break;
    //添加菜单响应事件
    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            //添加菜单项1.4响应事件
        case ID_40009:
            MessageBox(hwnd,"success!","test",MB_OK);
            break;
        }
        break;
    
    .....

    程序源码:

    View Code
    #include<windows.h>
    #include"resource.h"
    
    HMENU hmenuPop;//弹出菜单句柄
    
    LRESULT CALLBACK textprom(
        HWND hwnd,      // handle to window
        UINT uMsg,      // message identifier
        WPARAM wParam,  // first message parameter
        LPARAM lParam   // second message parameter
    );
    
    int WINAPI WinMain(
        HINSTANCE hInstance,  // handle to current instance
        HINSTANCE hPrevInstance,  // handle to previous instance
        LPSTR lpCmdLine,      // pointer to command line
        int nCmdShow          // show state of window
    )
    {
        WNDCLASS wndclass;
        HWND hwnd;
        HMENU hmenu;
        MSG msg;
        HACCEL haccel;
    
        //设计窗口类
        wndclass.cbClsExtra=0;
        wndclass.cbWndExtra=0;
        wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
        wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
        wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
        wndclass.hInstance=hInstance;
        wndclass.lpfnWndProc=textprom;
        wndclass.lpszClassName="text";
        wndclass.lpszMenuName=NULL;
        //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
        wndclass.style=CS_HREDRAW | CS_VREDRAW;
    
        //注册窗口类
        if(!RegisterClass(&wndclass))
            MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
    
        //创建无菜单资源的窗口窗口
        hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,500,300,NULL,NULL,hInstance,NULL);
    
        /*
        //载入菜单资源
        hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
        //创建有菜单资源的窗口
        hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
        CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/
    
        //载入菜单资源,并在窗口加载菜单资源
        hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
        SetMenu(hwnd,hmenu);
    
        //载入弹出菜单资源
        hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);
        //hmenuPop=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2));
    
        //显示更新窗口
        ShowWindow(hwnd,nCmdShow);
        UpdateWindow(hwnd);
    
        //加载菜单加速资源
        haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));
    
        //消息循环
        while(GetMessage(&msg,NULL,0,0))
        {
            if(!TranslateAccelerator(hwnd,haccel,&msg))
            {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        }
    
        return msg.wParam;
    }
    
    LRESULT CALLBACK textprom(
        HWND hwnd,      // handle to window
        UINT uMsg,      // message identifier
        WPARAM wParam,  // first message parameter
        LPARAM lParam   // second message parameter
    )
    {
        POINT p;
        switch(uMsg)
        {
        //添加鼠标左键单击事件响应处理,即卸载对话框
        case WM_RBUTTONDOWN:
            p.x=LOWORD(lParam); 
            p.y=HIWORD(lParam);
            //将窗口坐标转换成屏幕坐标
            ClientToScreen(hwnd,&p); 
            TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
            break;
        //添加菜响应事件
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
            //添加菜单项1.4响应事件
            case ID_40009:
                MessageBox(hwnd,"success!","test",MB_OK);
                break;
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        }
        return DefWindowProc(hwnd,uMsg,wParam,lParam);
    }

    运行结果(在单击菜单项1.4或者按下Ctrl+Alt+K的组合键时):


    MFC下菜单消息路由

    菜单消息路由

    Cview -> CDocument -> CFrameWnd -> CWinApp

    设置自己的菜单消息机制

    MFC为菜单提供了一种命令更新机制,所以程序运行时,根据此机制去判断哪个菜单可以使用,哪个菜单不能使用,然后显示其相应的状态.默认情况下,所有菜单项的更新都是由MFC的命令更新机制完成的,如果我们想自己更改菜单的状态,那就必须把m_bAutoMenuEnable变量设置为false,之后我们自己对菜单项状态更新才能起作用

    MFC的菜单消息机制

    但是利用MFC编程时候,菜单项状态的维护依赖于CN_UPDATE_COMMAND_UI消息,MFC在后台要做的工作是:当要显示菜单时,操作系统发出WM_INITMENUPOPUP消息,然后由程序窗口基类如CFrameWnd接管,它会创建一个CCmdUI对象,并与程序的第一个菜单项相关联,调用该对象的一个成员函数DoUpdate(),这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有一个指向CCmdUI对象的指针.这时,系统会自动判断是否存在一个ON_UPDATE_COMMAND_UI宏去捕获这个菜单项消息.如果找到这样一个宏,就会调用相应的消息相应函数进行处理,在这个函数中,可以利用传递进来的CCmdUI对象去调用相应的函数,使该激活或禁用该菜单项.当更新王第一菜单项后,同一个CCmdUI对象就设置为与第二个菜单项相关联,依此顺序进行,知道完成所有菜单项的处理.

  • 相关阅读:
    《Java TCP/IP Socket 编程 》读书笔记之十一:深入剖析socket——TCP套接字的生命周期
    c++实现二分查找
    hadoop序列化机制与java序列化机制对比
    C、C++中“*”操作符和“后++”操作符的优先级
    poj2774之最长公共子串
    Python之美[从菜鸟到高手]--urlparse源码分析
    (程序员面试题)字符串处理之寻找最大不重复子串
    hdu 4782 Beautiful Soupz
    教程Xcode 下编译发布与提交App到AppStore
    云端的ABAP Restful服务开发
  • 原文地址:https://www.cnblogs.com/kzang/p/2746070.html
Copyright © 2020-2023  润新知