• 【转载】CMenu自绘---钩子---去除边框


           使用默认的CMenu菜单类或者继承CMenu实现的菜单扩展类,在显示的时候最外层都会有边框出现,或者说是具有3D外观(菜单阴影不算),当改变菜单背景色或者需要加个边框线时就会看上去很不美观。看过很多菜单的自定义实现类,一般可以有两种方式来实现外框的移除。
           第一种方法就是:自定义窗口,完全模拟菜单的实现,自给自足,倒是能够完全满足开发需要,不过实现的复杂让人头痛,此处略过不提。

           下面介绍第二种比较简单直接的方法:安装钩子,在菜单创建时就改变其窗口属性。其实菜单应该也算是一个窗口类,不过实在是无从得知到底在哪创建的窗口,所以下下钩子,过程倒是明了许多。
     实现如下:

    先在cpp前面申明一下:

            static HHOOK g_hook=NULL; // 全局钩子
            static LRESULT WINAPI CallWndProc(int, WPARAM, LPARAM); // 安装的钩子的窗口过程
            static LRESULT WINAPI MenuWndProc(HWND, UINT, WPARAM, LPARAM); // 用来处理菜单的窗口过程

    然后是钩子的实现:

    /////////////////////////////////////////////////////////////////////////////
    // 如果需要去除菜单的外部边框,需要通过安装钩子,设置外框属性并改变菜单大小
    WNDPROC oldWndProc = NULL; // 用来保存被替换的窗口过程
    LRESULT WINAPI CallWndProc(int code, WPARAM wParam, LPARAM lParam)
    {
     CWPSTRUCT* pStruct = (CWPSTRUCT*)lParam;
     while (code == HC_ACTION)
     {
      HWND hWnd = pStruct->hwnd;
      // 捕捉创建消息WM_CREATE,后面筛选为是否是菜单的创建
      if ( pStruct->message != WM_CREATE)
       break;
      TCHAR sClassName[10];
      int Count = ::GetClassName(hWnd, sClassName, sizeof(sClassName)/sizeof(sClassName[0]));
      // 检查是否菜单窗口,#32768为菜单类名
      if ( Count != 6 ||  _tcscmp(sClassName, _T("#32768")) != 0 )   
       break;
      
      WNDPROC lastWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);   
      if (lastWndProc != MenuWndProc)   
      {   
       // 替换菜单窗口过程  
       SetWindowLong(hWnd, GWL_WNDPROC, (long)MenuWndProc);   
       // 保留原有的窗口过程   
       oldWndProc = lastWndProc;   
      }
      break;   
     }

     return CallNextHookEx((HHOOK)WH_CALLWNDPROC, code, wParam, lParam); 
    }

    // 处理菜单的窗口过程
    LRESULT WINAPI MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {   
     LRESULT lResult;
     switch (message)   
     {      
     case WM_CREATE:
      {   
       // 首先要去掉菜单窗口的一些扩展风格
       // 包括:WS_BORDER、WS_EX_DLGMODALFRAME、WS_EX_WINDOWEDGE
       lResult = CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);   
       DWORD dwStyle = ::GetWindowLong(hWnd,   GWL_STYLE);   
       DWORD dwNewStyle = (dwStyle & ~WS_BORDER);
       ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);   
       DWORD dwExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
       DWORD dwNewExStyle = (dwExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE));   
       ::SetWindowLong(hWnd, GWL_EXSTYLE, dwNewExStyle);   
       return lResult; 
      }   
     case   WM_PRINT: // 此处阻止非客户区地绘制
       return CallWindowProc( oldWndProc, hWnd, WM_PRINTCLIENT, wParam, lParam);

     case   WM_WINDOWPOSCHANGING:   
      {   
       // 最后,由于我们在MeasureItem里指定了菜单大小,而系统会自动替菜单加边框,
       // 因此必须去掉此部分额外地尺寸,将菜单大小改小
       LPWINDOWPOS lpPos = (LPWINDOWPOS)lParam;   
       lpPos->cx -= 2*GetSystemMetrics(SM_CXBORDER)+4;   
       lpPos->cy -= 2*GetSystemMetrics(SM_CYBORDER)+4;
       lResult = CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);  
       return 0;
      }   
     case   WM_GETICON:
      return 0;    
     default:   
      return  CallWindowProc( oldWndProc, hWnd, message, wParam, lParam);   
     }   
    }  

    /////////////////////////////////////////////////////////////

    最后是调用:
    下钩子需要调用SetWindowsHookEx,参数包括主窗口的实例句柄theApp.m_hInstance,可以在调用时作为参数传入,同时也设一个参数作为是否安装钩子的标识,以便在退出时判断是否需要卸载钩子(UnhookWindowsHookEx(g_hook))。示例如下:

    void CSkinMenu::RemoveMenuBorder(HINSTANCE hInst, BOOL bRemove /* = TRUE */)
    {
     m_bRemoveBorder = bRemove; // 标识

     // 需要移除边框时,要安装钩子
     if (m_bRemoveBorder)
     {
      DWORD id = ::GetCurrentThreadId(); // 获取当前线程的ID
      g_hook = SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc,hInst,id);
     }
    }


     这样子,就搞定菜单的边框了,最后要记得,如果安装了钩子,需要卸载掉。

  • 相关阅读:
    博客园安卓客户端合仔茶版本V4.0震撼发布
    提示功能的检索框
    .net 玩自动化浏览器
    《表单篇》DataBase之大数据量经验总结
    自定义表主键
    一次网络程序Debug过程
    关于.NET下开源及商业图像处理(PSD)组件
    利用反射从程序集dll中动态调用方法
    Linux内核源码分析方法
    wcf基础教程之 契约(合同)Contract
  • 原文地址:https://www.cnblogs.com/doudongchun/p/3699700.html
Copyright © 2020-2023  润新知