zoukankan      html  css  js  c++  java
  • 如何把菜单栏和标题栏合为一体

    你会发现,现在越来越多的桌面应用程序将菜单栏和标题栏合为一体。要实现这种效果,一般有两种方案:

    1. 将菜单绘制到标题栏上
    2. 移除标题栏,把菜单栏当标题用

    众所周知,标题栏和菜单栏都是典型的非客户区,而在Windows平台上,非客户区的自绘对很多程序猿来说,真的是痛苦不堪啊。因此方案一自然而然就被淘汰了。既然如此,那么如何用方案二实现我们要的效果呢?

    这里,先把“效果”简单说下:应用程序的界面上,标题栏和菜单栏在同一个区域显示,用户点击“菜单区域”,弹出对应的菜单;用户点击其他空白区域,如同点击了标题栏 (表述不清,不过我想大家都理解了吧……)。

    方案二里,实际上包含了两个要点:

    1. 移除标题栏
    2. 用菜单栏模拟标题栏

    如何移除标题栏,这个简单,移除WS_CAPTION窗口风格即可。不过要注意的是,不要忘记调用SetWindowPos并传入SWP_FRAMECHANGED标志位。那么如何让菜单栏模拟标题栏的功能呢?说到这里,要先补充一点基础知识。

    在Windows上,你如何知道鼠标所在的点位于当前窗口的哪一个部分?这要借助一个消息:WM_NCHITTEST。通过这个消息,我们知道一个窗口被细分出了20多个区域,其中有两个就是我们现在感兴趣的,HTMENU和HTCAPTION。现在想法来了,在自己处理WM_NCHITTEST消息时,如果能把系统返回的部分HTMENU转化成HTCAPTION,是不是就能达到模拟标题栏的效果了呢?事实告诉我们,基本上是达到了。代码如下:

       1: UINT Cls_OnNCHitTest(HWND hwnd, int x, int y)
    
       2: {
    
       3:     UINT uNcHitResult = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc);
    
       4:  
    
       5:     if (HTMENU == uNcHitResult)
    
       6:     {
    
       7:         HMENU hMenu = GetMenu(hwnd);
    
       8:         int nMenuItemCount = GetMenuItemCount(hMenu);
    
       9:  
    
      10:         RECT rcTheLastMenu = { 0, 0, 0, 0 };
    
      11:         if (GetMenuItemRect(hwnd, hMenu, nMenuItemCount - 1, &rcTheLastMenu))
    
      12:         {
    
      13:             // 当点击在菜单空白区域时,模拟成标题栏行为
    
      14:             if (rcTheLastMenu.right < x)
    
      15:             {
    
      16:                 uNcHitResult = HTCAPTION;
    
      17:             }
    
      18:         }
    
      19:     }
    
      20:  
    
      21:     return uNcHitResult;
    
      22: }
    

    经过这样的处理后,单击/双击菜单栏空白区域,以及拖拽等都好像是在操作标题栏一样。

    写完这些代码之后,你会发现几个问题:

    1. 右键单击菜单栏空白区域看不到系统菜单
    2. 最大化后全屏了,窗口覆盖了任务栏

    所以说,这种模拟方式只是基本实现了功能,还没有完全达到要求。现在我们来一步一步解决这些问题。先看系统菜单。

    要解决系统菜单的问题,首先要知道怎么获得系统菜单。这个可以通过GetSystemMenu的方式来实现。

    接下来,在什么时候弹出菜单?这个问题也简单,一般我们选用WM_NCRBUTTONUP。怎么弹的问题交给TrackPopupMenuEx。代码如下:

       1: void Cls_OnNCRButtonUp(HWND hwnd, int x, int y, UINT codeHitTest)
    
       2: {
    
       3:     if (HTCAPTION == codeHitTest)
    
       4:     {
    
       5:         HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
    
       6:         int nMenuAlign = GetSystemMetrics(SM_MENUDROPALIGNMENT);
    
       7:  
    
       8:         // 特别注意TPM_RETURNCMD,我们需要知道用户到底点中了哪一个系统菜单项
    
       9:         BOOL bRes = TrackPopupMenuEx(hSysMenu, nMenuAlign | TPM_RETURNCMD, x, y, hwnd, NULL);
    
      10:         return FORWARD_WM_SYSCOMMAND(hwnd, bRes, x, y, DefWindowProc);
    
      11:     }
    
      12:  
    
      13:     FORWARD_WM_NCRBUTTONUP(hwnd, x, y, codeHitTest, DefWindowProc);
    
      14: }
    

    到目前为止,我们看似已经把菜单的问题全部解决了。但事实上,问题还是有一点的。系统菜单项的状态好像有点问题。要解决这个问题,我们要借助WM_INITMENU消息,通过当前窗口的最大化或最小化状态,挑战对应的菜单项状态。请看代码:

       1: void Cls_OnInitMenu(HWND hwnd, HMENU hMenu)
    
       2: {
    
       3:     if (GetSystemMenu(hwnd, FALSE) == hMenu)
    
       4:     {
    
       5:         UINT uSysCmds[] = {SC_SIZE, SC_MOVE, SC_MINIMIZE, SC_MAXIMIZE, SC_CLOSE, SC_RESTORE};
    
       6:         if (IsIconic(hwnd))
    
       7:         {
    
       8:             for (int i = 0; i < _countof(uSysCmds); ++i)
    
       9:             {
    
      10:                 switch (uSysCmds[i])
    
      11:                 {
    
      12:                 case SC_MAXIMIZE:
    
      13:                 case SC_RESTORE:
    
      14:                 case SC_CLOSE:
    
      15:                     EnableMenuItem(hMenu, uSysCmds[i], MF_ENABLED);
    
      16:                     break;
    
      17:  
    
      18:                 default:
    
      19:                     EnableMenuItem(hMenu, uSysCmds[i], MF_DISABLED | MF_GRAYED);
    
      20:                     break;
    
      21:                 }
    
      22:             }
    
      23:         }
    
      24:         else if (IsZoomed(hwnd))
    
      25:         {
    
      26:             for (int i = 0; i < _countof(uSysCmds); ++i)
    
      27:             {
    
      28:                 switch (uSysCmds[i])
    
      29:                 {
    
      30:                 case SC_RESTORE:
    
      31:                 case SC_MINIMIZE:
    
      32:                 case SC_CLOSE:
    
      33:                     EnableMenuItem(hMenu, uSysCmds[i], MF_ENABLED);
    
      34:                     break;
    
      35:  
    
      36:                 default:
    
      37:                     EnableMenuItem(hMenu, uSysCmds[i], MF_DISABLED | MF_GRAYED);
    
      38:                     break;
    
      39:                 }
    
      40:             }
    
      41:         }
    
      42:         else
    
      43:         {
    
      44:             for (int i = 0; i < _countof(uSysCmds); ++i)
    
      45:             {
    
      46:                 switch (uSysCmds[i])
    
      47:                 {
    
      48:                 case SC_RESTORE:
    
      49:                     EnableMenuItem(hMenu, uSysCmds[i], MF_DISABLED | MF_GRAYED);
    
      50:                     break;
    
      51:  
    
      52:                 default:
    
      53:                     EnableMenuItem(hMenu, uSysCmds[i], MF_ENABLED);
    
      54:                     break;
    
      55:                 }
    
      56:             }
    
      57:         }
    
      58:     }
    
      59:     else
    
      60:     {
    
      61:         FORWARD_WM_INITMENU(hwnd, hMenu, DefWindowProc);
    
      62:     }
    
      63: }
    

    大功告成啦。唉,好像还有点不对,第一次弹出的系统菜单状态好像有问题,再弹出来,好像就对了,咋回事?这要怪GetSystemMenu了。要解决这个问题,只要在没有显示系统菜单前就先调用下GetSystemMenu做一份拷贝先。

    至此,系统菜单的问题应该全部都解决了。下面就是要解决全屏的问题了。

    未完。。。。。

  • 相关阅读:
    db2中的常用命令及使用方法
    互联网服务应用协议设计
    Zookeeper整理(一)- 写操作产生事件,写操作与Watcher对应关系
    实现自己的连接池(一)
    SMP、NUMA、MPP体系结构介绍
    随手备忘 ubuntu12.04 lts 安装gcc 4.8
    认真体会 结构体中的零长度数组
    由内存池实现总结内存对齐问题
    认真理解 oom killer 备忘
    仔细体会 epoll中的et lt模式
  • 原文地址:https://www.cnblogs.com/wpcockroach/p/2441487.html
Copyright © 2011-2022 走看看