通常使用::SetWindowRgn来设置不规则region,从而实现不规则窗体,然而有时希望实现带alpha通道的位图来作为窗口,所以使用layer window的UpdateLayeredWindow方法来实现
首先应该在窗体初始化的时候设置设置Extention式样
SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
然后,应该考虑在合适的时候(如OnPaint)中填充一个MemoryDC,并Update到layered窗口
// 获取含有alpha通道的位图 HBITMAP hbmp; Gdiplus::Bitmap bmp(L"aaa.png"); bmp.GetHBITMAP(Gdiplus::Color(0,0,0,0),&hbmp); // 创建MemoryDC CPaintDC dc(this); CDC dcMemory; dcMemory.CreateCompatibleDC(&dc); HGDIOBJ oldBmp = dcMemory.SelectObject(hbmp); // 这里的代码只是为了在MemoryDC上绘制些东西,或者在这里绘制子控件 SYSTEMTIME SysTime; CString StrCurTime; ::GetLocalTime(&SysTime); StrCurTime.Format(_T("%d/%d/%d %d:%d:%d"), SysTime.wYear,SysTime.wMonth,SysTime.wDay,SysTime.wHour,SysTime.wMinute,SysTime.wSecond); Gdiplus::Graphics grap(dcMemory.GetSafeHdc()); Gdiplus::SolidBrush brush(Gdiplus::Color(255,255,255,0)); Gdiplus::FontFamily fontFamily(L"Arial"); Gdiplus::Font font(&fontFamily, 12, Gdiplus::FontStyleBold, Gdiplus::UnitPoint); grap.DrawString(StrCurTime, StrCurTime.GetLength(), &font, Gdiplus::PointF(250,200), NULL, &brush); // 获取屏幕DC HDC hdcScreen = ::GetDC(NULL); // 设置alpha融合规则 BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 255; blend.AlphaFormat = AC_SRC_ALPHA; // 更新layered窗口 CRect rectWindow; GetWindowRect(&rectWindow); ::UpdateLayeredWindow( m_hWnd, hdcScreen, &rectWindow.TopLeft(), &rectWindow.Size(), dcMemory.GetSafeHdc(), &CPoint(0,0), RGB(0, 0, 0), &blend, ULW_ALPHA); // 注意:在xp下,ULW_ALPHA | ULW_COLORKEY同时使用会无法显示窗口 // 适当的清理 dcMemory.SelectObject(oldBmp); ::DeleteObject(hbmp); ::ReleaseDC(NULL, hdcScreen);
1)这里使用的Gdi+,所以必须在程序启动/销毁时,进行Gdi+必要的初始化和释放
2)UpdateLayeredWindow不支持显示子控件,如果要显示子控件,则要使用::SetLayeredWindowAttributes,但这样做就只能设置opacity,而不能使用per pixel alpha channel了,参考:http://stackoverflow.com/questions/7981322/strategy-for-creating-a-layered-window-with-child-windows-controls,目前也不知有没有什么更好的解决方案
3)使用Gdiplus::FontStyleRegular字体渲染,会导致字体变成透明,有个简单的解决方案是将字体笔刷的alpha改为254就可以了