对于我们这些控件狂来说,窗口阴影也是一个必不可少的实现需求。虽说其没多大用,但对于增加窗口立体感来说,那是挺有帮助的。
我实现了一个类似于360界面的阴影效果,其可以支持正常窗口,也支持半透明窗口。
阴影窗口对于正常窗口和半透明窗口,有区别么?且让我慢慢写来:)
阴影窗口的实现原理,简单来讲:就是在主窗口创建时,创建一个子窗口,吸附于主窗口的底部。然后在子窗口上做一个带半透明阴影效果的描绘。
以下代码是阴影窗口在父窗口的创建代码,是不是很简单?
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
m_Shadow.Create(m_hWnd);
m_Shadow.SetShadowSize(8);
return TRUE;
}
下面是阴影窗口实现步骤:
1. 在阴影窗口创建时,只设定阴影窗口的样式为WS_VISIBLE,我们这里不能用WS_CHILD,否则阴影窗口就跑到主窗口里面去了。
// Create shadow window.
HWND Create(const HWND wndParent)
{
ATLASSERT( ::IsWindow(wndParent) );
m_hParentWnd = wndParent;
CRect rc(1, 1, 1, 1);
return CWindowImpl::Create(0, rc, NULL, WS_VISIBLE, NULL);
}
2. 在阴影窗口执行WM_CREATE消息时,修改其样式为WS_EX_LAYERED | WS_EX_TRANSPARENT,注意这两个样式都要要。WS_EX_TRANSPARENT是让窗口无法接收点击消息,你总不想你的窗口阴影可以被用户点击且激活吧:)
SetWindowLong(GWL_EXSTYLE, GetWindowLong(GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
ModifyStyleEx(WS_EX_TOPMOST, WS_EX_NOACTIVATE);
3. 与此同时,阴影窗口注册父窗口的消息处理回调函数,此举是为了获取父窗口的移动、重绘和隐藏等重要消息。因为阴影窗口要跟随着父窗口的状态改变而改变。
// Set parent window original processing.
m_OriParentProc = ::GetWindowLong(m_hParentWnd, GWL_WNDPROC);
::SetWindowLong(m_hParentWnd, GWL_WNDPROC, (LONG)ParentProc);
回调函数要做的事情很简单,吸附于父窗口之下,像个小尾巴一样:
// Get parent message.
static LRESULT CALLBACK ParentProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Find the shadow window pointer via parent window handle.
ATLASSERT( m_szShadowWindows.find(hwnd) != m_szShadowWindows.end() );
CThemedShadowWnd *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
{
pThis->ShowWindow(SW_SHOW);
}
break;
}
default:
{
break;
}
}
return pDefProc(hwnd, uMsg, wParam, lParam);
}
好了,窗口消息机制处理完了,就要处理阴影画法了,我这里用的是GDI+的画法,如果有童鞋觉得效果不够好,可以尝试多改改参数配置,以达到理想效果:
// Create shadow brush.
PathGradientBrush brShadow(m_ShadowPath.m_pPath);
Color clrShadow[3] = {Color::Transparent, Color(255, 0, 0, 0), Color(255, 0, 0, 0)};
int nCount = 3;
REAL szPos[3] = {0.0F, 0.05F, 1.0F};
brShadow.SetInterpolationColors(clrShadow, szPos, nCount);
// Draw shadow.
rcShadow.Width = rcShadow.Width - m_nShadowSize - m_nBlankArea;
rcShadow.Height = rcShadow.Height - m_nShadowSize - m_nBlankArea;
graphics.ExcludeClip(rcShadow);
graphics.FillPath(&brShadow, m_ShadowPath.m_pPath);
如果我不排除一部分阴影区域,那么透明的窗口效果将变得很难看,如下图,透明背景被阴影遮盖了,这显然不符合美学的要求。
如果你的窗口的角是椭圆的,你可能还需要增宽阴影的显示区域,那么可以用如下函数进行阴影的宽度增长:
// Set blank area right position.
void SetRightOffsetArea(const int nRightPos)
{
m_nBlankArea = nRightPos;
if (nRightPos < 0)
{
m_nBlankArea = 1;
}
}
阴影窗口免费实例代码下载:http://download.csdn.net/detail/renstarone/6267677