使用默認的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);
}
}
這樣子,就搞定菜單的邊框了,最後要記得,若是安裝了鉤子,須要卸載掉。