窗口阴影效果
背景:
win7系统中可以在设置窗口底部有阴影效果,这样使得窗口看起来更有立体感。如果我们自定义窗口,没有用到系统默认的边框,这时阴影效果也随着没有了。我们需要在这样的窗口上加上阴影效果。
方法:
1)创建一个WS_EX_TRANSPARENT样式的窗口,该类窗口具有鼠标穿透的效果(这样的效果也可以用SetWindowRgn实现,但是我们这里比较特殊,只能使用前者)。
2)将窗口绘制成阴影,这个可以用GDI+来实现。
3)跟随目标窗口,这里需要处理这些消息:show、hide、minimize、maximize、paint、resize。这里采用的做法是hook目标窗口的窗口处理函数。这比编写代码是接受目标窗口发送来的消息更加方便,而且对于目标窗口来说阴影窗口是透明的。
实现步骤:
1)首先在窗口类中加入我们定义的阴影类对象:
CShadowWndBase m_shadowWnd;
2)我们在OnCreate(OnInitDlg)中将目标窗口的窗口句柄传给阴影窗口类对象,然后创建阴影窗口:
m_shadowWnd.SetShadowWnd(*this);
m_shadowWnd.Create(NULL,_T(""),WS_OVERLAPPEDWINDOW,CRect(0,0,0,0),GetDesktopWindow(),0);阴影窗口创建过程还有一个工作要做,就是要hook目标窗口的窗口处理函数,注意这里需要保存原来目标窗口的处理函数地址,因为我们会在新处理函数中调用旧函数
int CShadowWndBase::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;ASSERT(m_hWndShadow!=NULL);
// TODO: 在此添加您专用的创建代码
m_szShadowWindows.insert( make_pair(m_hWndShadow, this) );ModifyStyle(WS_MAXIMIZEBOX | WS_SIZEBOX | WS_CAPTION | WS_DLGFRAME, 0, 0);
::SetWindowLong(*this,GWL_EXSTYLE, GetWindowLong(*this,GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
ModifyStyleEx(WS_EX_TOPMOST, WS_EX_NOACTIVATE);// Set parent window original processing.
m_OriParentProc = ::GetWindowLong(m_hWndShadow, GWL_WNDPROC);
::SetWindowLong(m_hWndShadow, GWL_WNDPROC, (LONG)ShadowProc);return 0;
}3)
这里还有一些问题,新处理函数是静态函数,无法访问非静态成员变量;另外还要考虑到另外的情况,可能同一进程中有多个窗口使用了阴影窗口类,我们也需要记录目标窗口和阴影窗口的对应关系。只需要定义一个类静态变量(其作用在这里就相当于一个全局变量):
static std::map<HWND, CShadowWndBase*> m_szShadowWindows;
4)这样对于目标窗口的一些消息就可以直接处理,然后再传给默认函数。下面是新处理函数:
LRESULT CALLBACK CShadowWndBase::ShadowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// Find the shadow window pointer via parent window handle.
ASSERT( m_szShadowWindows.find(hwnd) != m_szShadowWindows.end() );
CShadowWndBase *pThis = m_szShadowWindows[hwnd];
WNDPROC pDefProc = (WNDPROC)pThis->m_OriParentProc;switch(uMsg)
{
case WM_ERASEBKGND:
case WM_PAINT:
case WM_MOVE:
case WM_ACTIVATE:
case WM_NCACTIVATE:{
if (::IsWindowVisible(hwnd)){
pThis->AdjustWindowPos();
}
break;
}
case WM_DESTROY:{
// Destroy the shadow window.
pThis->DestroyWindow();
break;
}
case WM_NCDESTROY:{
// Remove shadow window from map.
m_szShadowWindows.erase(hwnd);
break;
}
case WM_SHOWWINDOW:{
// the window is being hidden
if (!wParam){
pThis->ShowWindow(SW_HIDE);
}
else{
CRect rect;
pThis->GetWindowRect(rect);
pThis->ShowWindow(SW_SHOW);
}
break;
}
default:
break;
}return pDefProc(hwnd, uMsg, wParam, lParam);//注意最后将消息都给原来的处理函数,让其保持原状(至少是外表上的)。
}5)窗口在检测到目标窗口尺寸或位置改变时需要相应的改变:
void CShadowWndBase::AdjustWindowPos()
{
CRect rcParent;::GetWindowRect(m_hWndShadow, &rcParent);
rcParent.InflateRect(m_nShadowSize, m_nShadowSize);
rcParent.MoveToXY(rcParent.left+2,rcParent.top+2);
::SetWindowPos(*this, m_hWndShadow, rcParent.left,rcParent.top,rcParent.Width(), rcParent.Height(), SWP_NOACTIVATE);GetClientRect(m_rcClient);
// If window was resized, redraw the shadow.
if (m_bResize == TRUE)
{
m_bResize = FALSE;
DrawShadow();
}
}6)利用GDI+绘制阴影效果:
void CShadowWndBase::DrawShadow()
{// Create shadow path.
Rect rcShadow(m_rcClient.left, m_rcClient.top, m_rcClient.Width(), m_rcClient.Height());
m_ShadowPath.CreateRoundRect(rcShadow, 18);Bitmap bitmap(rcShadow.Width, rcShadow.Height);
Graphics graphics(&bitmap);// Create shadow brush.
PathGradientBrush brShadow(m_ShadowPath.m_pPath);
Color clrShadow[] = {Color::Transparent, Color(255, 0, 0, 0)};
int nCount = 2;REAL szPos[] = {0.0F, 1.0F};
brShadow.SetInterpolationColors(clrShadow, szPos, nCount);
brShadow.SetFocusScales((REAL)(rcShadow.Width-6*m_nShadowSize)/(rcShadow.Width), (REAL)(rcShadow.Height-6*m_nShadowSize)/(rcShadow.Height));
// Draw shadow.
rcShadow.Inflate(-m_nShadowSize-6,-m_nShadowSize-6);
graphics.ExcludeClip(rcShadow);
graphics.FillPath(&brShadow, m_ShadowPath.m_pPath);// Update layered window.
HBITMAP hBitMap;
m_pBitmap.GetHBITMAP((ARGB)Color::Black, &hBitMap);
CBitmap bitmap;
bitmap.Attach(hBitMap);CClientDC dc(this);
CDC dcCompat;
dcCompat.Attach(::CreateCompatibleDC(NULL));
dcCompat.SelectObject(bitmap);BITMAP bmpInfo = {0};
bitmap.GetBitmap(&bmpInfo);
CSize size(bmpInfo.bmWidth, bmpInfo.bmHeight);
CRect rcWindow;
GetWindowRect(&rcWindow);BLENDFUNCTION bf = { AC_SRC_OVER, 0, 128, AC_SRC_ALPHA };
CPoint ptWnd(rcWindow.left, rcWindow.top);
CPoint ptSource(0, 0);::UpdateLayeredWindow(m_hWnd, dc.m_hDC, &ptWnd, &size,
dcCompat.m_hDC, &ptSource, 0, &bf, ULW_ALPHA);
}