【轉載】CMenu自繪---鉤子---去除邊框

       使用默認的CMenu菜單類或者繼承CMenu實現的菜單擴展類,在顯示的時候最外層都會有邊框出現,或者說是具備3D外觀(菜單陰影不算),當改變菜單背景色或者須要加個邊框線時就會看上去很不美觀。看過不少菜單的自定義實現類,通常能夠有兩種方式來實現外框的移除。
       第一種方法就是:自定義窗口,徹底模擬菜單的實現,自給自足,卻是可以徹底知足開發須要,不過實現的複雜讓人頭痛,此處略過不提。線程

       下面介紹第二種比較簡單直接的方法:安裝鉤子,在菜單建立時就改變其窗口屬性。其實菜單應該也算是一個窗口類,不過實在是無從得知到底在哪建立的窗口,因此下下鉤子,過程卻是明瞭許多。
 實現以下:code

先在cpp前面申明一下:繼承

        static HHOOK g_hook=NULL; // 全局鉤子
        static LRESULT WINAPI CallWndProc(int, WPARAM, LPARAM); // 安裝的鉤子的窗口過程
        static LRESULT WINAPI MenuWndProc(HWND, UINT, WPARAM, LPARAM); // 用來處理菜單的窗口過程開發

而後是鉤子的實現:it

/////////////////////////////////////////////////////////////////////////////
// 若是須要去除菜單的外部邊框,須要經過安裝鉤子,設置外框屬性並改變菜單大小
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); 
}ast

// 處理菜單的窗口過程
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);   
 }   
}  方法

/////////////////////////////////////////////////////////////static

最後是調用:
下鉤子須要調用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);
 }
}

 這樣子,就搞定菜單的邊框了,最後要記得,若是安裝了鉤子,須要卸載掉。

相關文章
相關標籤/搜索