使用默认的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);
}
}
这样子,就搞定菜单的边框了,最后要记得,如果安装了钩子,需要卸载掉。
【转载】CMenu自绘---钩子---去除边框,码迷,mamicode.com
原文地址:http://www.cnblogs.com/doudongchun/p/3699700.html