我正在开发一个透明的树视图控件。为了实现透明度,我已将子类化为树,并覆盖了WM_PAINT
(在我的WM_ERASEBKGND
处理程序中,我只返回TRUE
。滚动,鼠标滚轮和其他相关消息得到妥善处理)。
为了使树的背景透明,我使用以下算法(基于this CodeGuru 文章):
让树在内存DC中执行默认绘制(保存在memDC
中)。
在另一个内存DC中获取父级的背景(保存在finalDC
中)。
映射树和父坐标,这样我就可以获取父级背景位图的正确部分。
使用TransparentBlt
和树的背景颜色(TransparentBlt( finalDC, ... , memDC, ... );
)合并这两个图像。
在父窗口中,我实现了WM_PRINTCLIENT
,因此我可以通过简单的::SendMessage( GetParent(hwnd), WM_PRINTCLIENT, (WPARAM)finalDC, (LPARAM)(PRF_CLIENT) );
调用将其背景复制到内存DC(步骤2)。我得到的结果在 Windows XP 和 Windows7 上都是正确的:
我通过::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );
调用获取树的默认位图(步骤1)。在这里,结果在 Windows XP 和 Windows7 上都是正确的:
在 Windows XP 上(我不知道为什么上传的图片会丢失复选框,一切在我的计算机上看起来都很好):
在 Windows7 上(我不知道为什么上传的图片遗漏了复选框,一切在我的电脑上都很好):
然而,在TransparentBlt()
通话后,最终图片未正确绘制:
在 Windows XP上复选框是问题 - >
在 Windows7 上,一些白色留在字母中 - >
这些图片是将位图从设备上下文导出到文件中的结果(我已修改this code以实现此目的)。
以下是WM_PAINT
的代码段:
case WM_PAINT:
{
// usual stuff
PAINTSTRUCT ps;
RECT rcClient = {0};
GetClientRect( hwnd, &rcClient );
HDC hdc = BeginPaint( hwnd, &ps );
// create helper memory DCs
HDC memDC = CreateCompatibleDC(hdc), finalDC = CreateCompatibleDC(hdc);
// create helper bitmaps
HBITMAP memBmp, // default tree's paint
finalBmp, // parent's background image
bmpOld, bmpOldFinal; // needed for cleanup
memBmp = CreateCompatibleBitmap( hdc,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top );
bmpOld = (HBITMAP)SelectObject( memDC, memBmp );
// map parent and child rectangles
RECT rcParent;
GetClientRect( GetParent(hwnd), &rcParent );
// upper left corners of the treeview, parent window
POINT ptTreeUL, ptParentUL;
// map tree's coordinates
ptTreeUL.x = rcClient.left;
ptTreeUL.y = rcClient.top;
ClientToScreen( hwnd, &ptTreeUL );
// map parent's coordinates
ptParentUL.x = rcParent.left;
ptParentUL.y = rcParent.top;
ScreenToClient( GetParent(hwnd), &ptParentUL );
/********* get parent's background image *******/
finalBmp = CreateCompatibleBitmap( hdc,
rcParent.right - rcParent.left,
rcParent.bottom - rcParent.top );
bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );
::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC,
(LPARAM)(PRF_CLIENT) );
/********* capture default tree image *********/
::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );
// get tree's background color
COLORREF clrMask = TreeView_GetBkColor(hwnd);
if( clrMask == -1 ) // this means tree uses default system color
clrMask = ::GetSysColor(COLOR_WINDOW);
/**** combine tree's default image with parent's background ****/
/**** so we can erase default background with parent's background ****/
TransparentBlt( finalDC,
ptParentUL.x + ptTreeUL.x,
ptParentUL.y + ptTreeUL.y,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
memDC,
0, 0,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
clrMask );
// draw the result into tree's DC
BitBlt( hdc,
0, 0,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
finalDC,
ptParentUL.x + ptTreeUL.x,
ptParentUL.y + ptTreeUL.y, SRCCOPY);
// cleanup
SelectObject( memDC, bmpOld );
DeleteDC( memDC );
DeleteObject( memBmp );
SelectObject( finalDC, bmpOldFinal );
DeleteDC( finalDC );
DeleteObject( finalBmp );
EndPaint( hwnd, &ps );
}
return 0L;
如何将默认树位图与父背景正确组合,实现透明度而不会产生视觉伪影?
我能够通过抛弃TransparentBlt()
并自己做事来解决复选框问题。
ClearType字体仍然是个问题,工件仍然存在。面具创建是个问题。成员 arx 表示,背景组合对此负责。如果我可以创建适当的面具,那么我的问题就会得到解决。
以下是整个子类过程:
LRESULT CALLBACK TreeProc( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
// handle messages that paint tree without WM_PAINT
case WM_TIMER: // handles autoscrolling when item is partially visible
case TVM_DELETEITEM:
case TVM_INSERTITEM:
case WM_MOUSEWHEEL:
case WM_HSCROLL:
case WM_VSCROLL:
{
::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0 );
LRESULT lres = ::DefSubclassProc( hwnd, message, wParam, lParam );
::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0 );
::RedrawWindow( hwnd, NULL, NULL,
RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW );
return lres;
}
case WM_PAINT:
{
// usual stuff
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwnd, &ps );
// get client coordinates of parent and tree window
RECT rcClient = {0}, rcParent = {0};
GetClientRect( hwnd, &rcClient );
GetClientRect( GetParent(hwnd), &rcParent );
// create helper DCs and bitmaps
HDC memDC = CreateCompatibleDC(hdc), //default tree paint
finalDC = CreateCompatibleDC(hdc), // parent's WM_PRINTCLIENT
maskDC = CreateCompatibleDC(hdc); // DC that holds monochrome mask
HBITMAP memBmp, // default tree's paint
finalBmp, // parent's background image
maskBmp, // monochrome mask
bmpOld, bmpOldFinal, bmpOldMask; // needed for cleanup
memBmp = CreateCompatibleBitmap( hdc, rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top );
bmpOld = (HBITMAP)SelectObject( memDC, memBmp );
/****** get parent's background image *******/
finalBmp = CreateCompatibleBitmap( hdc, rcParent.right - rcParent.left,
rcParent.bottom - rcParent.top );
bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );
::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC,
(LPARAM)(PRF_CLIENT) );
/****** capture default tree image *********/
::SendMessage( hwnd, WM_PRINTCLIENT,(WPARAM)memDC, (LPARAM)(PRF_CLIENT) );
/********** create monochrome mask *******/
// get tree's background color
COLORREF clrMask = TreeView_GetBkColor(hwnd);
if( clrMask == -1 )
clrMask = ::GetSysColor(COLOR_WINDOW);
maskBmp = CreateBitmap( rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top, 1, 1, NULL );
bmpOldMask = (HBITMAP)SelectObject( maskDC, maskBmp );
SetBkColor( memDC, clrMask ); // this color becomes white, all others black
BitBlt( maskDC, 0, 0, rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top, memDC, 0, 0, SRCCOPY );
/***** map tree's coordinates to parent window *****/
POINT ptTreeUL;
ptTreeUL.x = rcClient.left;
ptTreeUL.y = rcClient.top;
ClientToScreen( hwnd, &ptTreeUL );
ScreenToClient( GetParent(hwnd), &ptTreeUL );
/***** creating transparent background ********/
// mask the original image
SetBkColor( memDC, RGB( 0, 0, 0 ) );
SetTextColor( memDC, RGB( 255, 255, 255 ) );
BitBlt( memDC, 0, 0,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
maskDC, 0, 0, SRCAND );
// create transparent treeview image
SetBkColor( finalDC, RGB ( 255, 255, 255 ) );
SetTextColor( finalDC, RGB ( 0, 0, 0 ) );
BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
maskDC, 0, 0, SRCAND );
BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
memDC, 0, 0, SRCPAINT );
// display the result
BitBlt( hdc, 0, 0,
rcClient.right - rcClient.left,
rcClient.bottom - rcClient.top,
finalDC, ptTreeUL.x, ptTreeUL.y, SRCCOPY );
/***************** cleanup ******************/
SelectObject( memDC, bmpOld );
DeleteDC( memDC );
DeleteObject( memBmp );
SelectObject( finalDC, bmpOldFinal );
DeleteDC( finalDC );
DeleteObject( finalBmp );
SelectObject( maskDC, bmpOldMask );
DeleteDC( maskDC );
DeleteObject( maskBmp );
EndPaint( hwnd, &ps );
}
return 0L;
case WM_ERASEBKGND:
return TRUE;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, TreeProc, 0 );
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
在父窗口中绘制来自WM_PAINT
的图片以回复WM_PRINTCLIENT
(请记住使用(HDC)wParam
代替BeginPaint
,并使用return (LRESULT)0;
)。我画了一个渐变,如上图所示。
在父窗口中,您必须在WM_NOTIFY
处理程序中添加以下内容:
case WM_NOTIFY:
{
if( ((LPNMHDR)lParam)->code == TVN_SELCHANGING )
{
InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, NULL, FALSE );
break;
}
if( ((LPNMHDR)lParam)->code == TVN_ITEMEXPANDING )
{
InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, NULL, FALSE );
break;
}
}
break;
只有字体仍有待修复。
答案 0 :(得分:3)
在Windows 7上,使用消除锯齿对文本进行平滑处理。 Windows不会在纯色背景上绘制黑色文本的像素,它会在黑色和背景颜色之间进行混合。
TransparentBlt
将单一纯色视为透明色。因此,它不会将文本的抗锯齿边缘视为透明,因此这些边缘在最终位图中可见。
要解决此问题,您可以选择禁用了抗锯齿的字体,但显然这会为您提供更多块状文本。
XP上的问题是复选框的角落与背景颜色相同。您可以通过将背景更改为不会发生碰撞的颜色(例如品红色)来解决此问题。