win32 菜单


最重要的用户界面

从编程的角度来分类:

  • 静态菜单:在菜单资源编译器中预先编辑好的
  • 动态菜单:在程序运行过程中通过代码生成
  • 快捷菜单:前两种菜单的组合,在菜单编译器中预先编辑好,然后在程序运行过程中动态显示

对于菜单而言,可以理解为一个二维数组,每一个二维数组的元素理解为一个可以嵌套的子菜单。

菜单里面的每一个菜单项都有两个最基本的要素:菜单项名字,该菜单项唯一的标识ID

VS中菜单在资源管理器的Menu的资源项下

选中项目可以通过添加资源来添加资源

如果自己新建了菜单,可以通过注册窗口的时候添加自己的菜单

//如果想在window里面去绘制一个窗口,必须先注册一个窗口类
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    //WNDCLASS的扩展,如果尾部出现EX,表示此种类型的扩展
    WNDCLASSEXW wcex;  //结构变量
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;  
    wcex.cbClsExtra     = 0; 
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance; 
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTWIN32)); 
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    //改成自己设计的菜单
    wcex.lpszMenuName   = MAKEINTRESOURCEW(MY_MENU); //窗口的菜单是哪个资源
    wcex.lpszClassName  = szWindowClass; 
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassExW(&wcex);
}

菜单的响应

在WinProc中响应菜单

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 在此处响应自己制作的菜单
            switch (wmId)
            {
            //ID_FILE_OPEN是自己创建的菜单ID
            case ID_FILE_OPEN:
				MessageBox(hWnd,_"打开了自己的菜单",0,0);
                break;
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

静态菜单

菜单静态编辑通过VS中资源编辑器可以自己编辑,然后通过相关代码来实现静态菜单的功能。

可以通过 &+F (F为对应的按键)来实现快捷键alt+F 键。

通过代码实现菜单的加载

//加载菜单
LoadMenu(
    _In_opt_ HINSTANCE hInstance,   //加载菜单的窗口句柄
    _In_ LPCWSTR lpMenuName);       //已经建好的静态菜单ID
//通过创建窗口的时候加载菜单
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
    hInst = hInstance; // 将实例句柄存储在全局变量中 
    hWnd = FindWindow(szWindowClass,nullptr); //查找窗口函数
    //如果窗口已经有了,就不再创建,返回false
    if(hWnd)
    {
        return false;
    }
    //MY_MENU为自己在VS中已经编辑好的菜单资源ID
    HMENU my_menu =LoadMenu(hInst,MAKEINTRESOURCEW(MY_MENU));
    
    hWnd = CreateWindowW(szWindowClass,  
                             szTitle,
                             WS_OVERLAPPEDWINDOW, 
      						 CW_USEDEFAULT,
                             0, 
                             CW_USEDEFAULT,
                             0,的
                             nullptr, 
                             my_menu, //这里加入自己编辑好的菜单
                             hInstance,
                             nullptr);
	if (!hWnd)
	{
		return FALSE;
	}

	ShowWindow(hWnd, nCmdShow); //显示窗口
	UpdateWindow(hWnd);         //更新窗口

	return TRUE;
}

动态菜单

通过代码来创建全新的菜单,并加载到窗口中

菜单相关函数

//创建菜单
CreateMenu(
    VOID);
//尾部追加菜单
AppendMenu(
    _In_ HMENU hMenu,           //追加的菜单句柄
    _In_ UINT uFlags,           //标识,菜单的风格
    _In_ UINT_PTR uIDNewItem,   //添加新菜单项的ID号
    _In_opt_ LPCWSTR lpNewItem);//新菜单项的名字(字符串)

//对应位置插入菜单,第二个参数将受到第三个参数的影响
InsertMenu(
    _In_ HMENU hMenu,             //追加的菜单句柄
    _In_ UINT uPosition,          //插入的位置
    _In_ UINT uFlags,             //标识,菜单的风格
    _In_ UINT_PTR uIDNewItem,     //添加新菜单项的ID号
    _In_opt_ LPCWSTR lpNewItem);  //新菜单项的名字(字符串)

//删除菜单,很少删除菜单项,因为只是单纯删除菜单项,菜单的ID号还存在
DeleteMenu(
    _In_ HMENU hMenu,             //删除的菜单句柄
    _In_ UINT uPosition,          //删除的菜单的位置
    _In_ UINT uFlags);            //基于菜单的风格删除

//激活菜单
EnableMenuItem(
    _In_ HMENU hMenu,          //激活的菜单的句柄
    _In_ UINT uIDEnableItem,   //菜单的ID
    _In_ UINT uEnable);        //
// MF_GRAYED  变灰  MF_ENABLED 激活

//设置更新窗口
SetMenu(
    _In_ HWND hWnd,          //菜单所属的窗口句柄
    _In_opt_ HMENU hMenu);   //对应的菜单句柄

//菜单风格 MF开头
MF_BYCOMMAND  基于菜单ID
MF_BYPOSITION 基于下标位置
MF_CHECKED    选中
MF_ENABLED    激活
MF_UNENABLED  取消激活
MF_HILITE
MF_POPUP      弹出式菜单

菜单实现,代码如下

菜单的新建不能放在UpdateWindow(hWnd); //更新窗口之后,会出现BUG

如果在UpdateWindow(hWnd); 之后,需要更新一下菜单SetMenu(hWnd,my_menu);

//自己新建菜单
HMENU my_menu = NULL;  //菜单句柄
//创建菜单
HMENU my_menu_1 = CreateMenu(); //创建一个空菜单,即空白菜
my_menu = CreateMenu(); //再次创建一个空白菜单单

//空白菜单添加菜单项
//my_menu_1添加
AppendMenu(my_menu_1,0,5001,_T("新建(&N)"));
AppendMenu(my_menu_1,0,5002,_T("打开(&F)"));
AppendMenu(my_menu_1,0,5003,_T("保存(&S)"));
AppendMenu(my_menu_1,0,5004,_T("关闭"));
//把my_menu理解为二维数组,my_menu_1理解为其中的一个一维数组
//my_menu_1当成一个菜单嵌套进my_menu中
//注意:在弹出式菜单中,不需要id,把一个菜单句柄当作ID来使用,那么这个弹出式菜单弹出之后就是该菜单句柄所指的菜单
//尾部追加菜单,行追加
AppendMenu(my_menu,       //菜单句柄
          MF_POPUP,       //菜单风格,弹出式菜单
          (UINT)my_menu_1,//菜单ID,把my_menu_1做一个UINT强转,当成菜单ID
          _T("文件(&F)")); //菜单的名字,&F 响应快捷键

HMENU my_menu_2 = CreateMenu(); //创建一个空菜单,即空白菜
//my_menu_2添加
AppendMenu(my_menu_2,MF_CHECKED,6001,_T("撤销(&Z)"));
AppendMenu(my_menu_2,0,6002,_T("剪切(&X)"));
AppendMenu(my_menu_2,0,6003,_T("复制(&C)"));
AppendMenu(my_menu_2,0,6004,_T("删除"));

//插入菜单,一般习惯基于下标来插入
InsertMenu(my_menu,                        //菜单句柄
           0,                             //插入的位置
           MF_BYPOSITION | MF_POPUP,        //菜单风格,弹出式菜单
           (UINT)my_menu_2, //菜单ID,把my_menu_1做一个UINT强转,当成菜单ID
           _T("编辑(&E)"));  //菜单的名字,&F 响应快捷键

//一般情况下不删除菜单,让菜单变灰即可MF_BYCOMMAND基于ID MF_GRAYED变灰
EnableMenuItem(my_menu_2,6001,MF_BYCOMMAND | MF_GRAYED); //菜单变灰,非激活
EnableMenuItem(my_menu_2,6001,MF_BYCOMMAND | MF_ENABLED); //菜单再次激活

//在hWnd这个句柄所在的窗口里面重新更新一下菜单
SetMenu(hWnd,my_menu);

hWnd = CreateWindowW(szWindowClass,  
                             szTitle,
                             WS_OVERLAPPEDWINDOW, 
      						 CW_USEDEFAULT,
                             0, 
                             CW_USEDEFAULT,
                             0,的
                             nullptr, 
                             my_menu, //这里加入自己编辑好的菜单
                             hInstance,
                             nullptr);

if (!hWnd)
{
	return FALSE;
}

ShowWindow(hWnd, nCmdShow); //显示窗口
UpdateWindow(hWnd);         //更新窗口

return TRUE;


快捷菜单

首先在VS中新建一个菜单,或者自己通过代码实现一个菜单,菜单暂时不显示,通过鼠标右键点击弹出菜单。

动态菜单弹出函数

TrackPopupMenu(
    _In_ HMENU hMenu,               //要显示的菜单的句柄
    _In_ UINT uFlags,               //菜单风格,对齐的方式
    _In_ int x,                     //鼠标点击的x坐标
    _In_ int y,                     //鼠标点击的y坐标
    _Reserved_ int nReserved,       //默认给0
    _In_ HWND hWnd,                 //显示菜单的窗口句柄
    _Reserved_ CONST RECT *prcRect);//显示在哪个矩形里面

//参数uFlags
TPM_CENTERALIGN   //居中
TPM_LEFTALIGN     //左对齐
TPM_RUGHTALIGN    //右对齐
TPM_TOPALIGN      //右对齐  

//得到子菜单
GetSubMenu(
    _In_ HMENU hMenu,   //得到的是哪个菜单的子菜单的菜单句柄
    _In_ int nPos);     //

    
//把窗口区的坐标转换成到屏幕的坐标
ClientToScreen(
    _In_ HWND hWnd,          //对应的窗口句柄
    _Inout_ LPPOINT lpPoint);//Point结构体,用来存放x,y坐标

代码逻辑如下

//全局菜单句柄
HMENU g_popMenu;
//WndProc消息处理函数中
switch (message)
{
    //创建窗口时候加载菜单
    case WM_CREATE: 
        {
            //MY_MENU为已经创建好的静态菜单,或者是自己写好的菜单
            g_popMenu = LoadMenu(hInst,MAKEINTRESOURCEW(MY_MENU))
        }
        break;
    //鼠标的左键按下,显示菜单
    case WM_RBUTTONDOWN: 
    {
        Point pt;
		pt.x = LOWORD(lParam);
        //y取高两位2字节
        pt.y = HIWORD(lParam);
        //把窗口区的坐标转换成到屏幕的坐标
        ClientToScreen(hWnd,&pt);
        //得到子菜单
        HMENU popMenu1 = GetSubMenu(g_popMenu,0);
        //显示弹出式菜单,要注意相对坐标,绝对坐标
        TrackPopupMenu(popMenu1,  //要显示的菜单的句柄
                      TPM_LEFTALIGN | TPM_TOPALIGN
                      pt.x,       //鼠标点击的坐标,相对于屏幕的位置
                      pt.y,            
                      0,
                      hWnd,       //显示的窗口的句柄
                      nullptr);
    }
        break;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值