您可能应该停止删除并重新添加此问题。
编辑:这是我能想到的最好的。我不能足够强调这段代码是什么黑客,以及它是多么容易被破解。然而,这将我的努力与乔纳森的评论结合在一起,形成了某种作品。它闪烁,而且很丑陋,但它或多或少地满足了要求。
// Making these globals is bad practice, but I'm not trying to show perfect
// practice here.
HDC hDCMem; // memory DC for background bitmap
HBITMAP hBmp; // the background bitmap
WNDPROC oldTreeWndProc; // old WndProc for tree view
HWND hWndTree; // HWND of the tree view
// Subclassed tree view WndProc
LRESULT CALLBACK TreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_VSCROLL:
case WM_HSCROLL:
case WM_MOUSEWHEEL:
case WM_KEYDOWN:
case TVM_INSERTITEM:
case TVM_DELETEITEM:
case TVM_SELECTITEM:
case TVM_EXPAND:
case TVM_ENSUREVISIBLE:
{
// For a whole bunch of messages that might cause repainting apart
// from WM_PAINT, let the tree view process the message then
// invalidate the window. This is a brittle hack and will break as
// soon as tree views handle some other kind of message that isn't
// included in the list above. Fundamentally, tree views just don't
// seem to support this kind of transparency.
//
// If you use this in production, expect to get bug reports about
// weird background artifacts when somebody scrolls the window
// some way you didn't think of or that didn't exist at the time
// the code was written.
LRESULT result =
CallWindowProc(oldTreeWndProc, hWnd, msg, wParam, lParam);
InvalidateRect(hWnd, NULL, TRUE);
return result;
}
case WM_PAINT:
{
::CallWindowProc(oldTreeWndProc, hWnd, msg, wParam, lParam);
COLORREF treeBGColor = SendMessage(hWnd, TVM_GETBKCOLOR, 0, 0);
// This shouldn't return -1 because it should have been set in the
// parent WndProc to an explicit color.
assert(treeBGColor != ((COLORREF)(-1)));
HDC hdc = GetDC(hWnd);
RECT rect;
GetWindowRect(hWnd, &rect);
HWND hWndParent = GetParent(hWnd);
POINT pt;
pt.x = rect.left;
pt.y = rect.top;
ScreenToClient(hWndParent, &pt);
rect.left = pt.x;
rect.top = pt.y;
pt.x = rect.right;
pt.y = rect.bottom;
ScreenToClient(hWndParent, &pt);
rect.right = pt.x;
rect.bottom = pt.y;
int cx = rect.right - rect.left;
int cy = rect.bottom - rect.top;
HDC hdcMemTree = ::CreateCompatibleDC(hdc);
HBITMAP hComposite = ::CreateCompatibleBitmap(hDCMem, cx, cy);
hComposite = (HBITMAP)SelectObject(hdcMemTree, hComposite);
// Blt the background bitmap to the tree view memory DC
BitBlt(
hdcMemTree, 0, 0, cx, cy, hDCMem, rect.left, rect.top, SRCCOPY);
// TransparentBlt what the tree view drew for itself into the tree
// view memory DC (this part overlays the tree view window onto the
// background).
TransparentBlt(
hdcMemTree, 0, 0, cx, cy, hdc, 0, 0, cx, cy, treeBGColor);
// Blt the memory DC back to the screen with the composite image.
BitBlt(hdc, 0, 0, cx, cy, hdcMemTree, 0, 0, SRCCOPY);
hComposite = (HBITMAP)SelectObject(hdcMemTree, hComposite);
DeleteObject(hComposite);
DeleteDC(hdcMemTree);
ReleaseDC(hWnd, hdc);
}
return 0;
}
return ::CallWindowProc(oldTreeWndProc, hWnd, msg, wParam, lParam);
}
// Main window WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_CREATE:
{
HDC hDCDisplay = GetDC(NULL);
hDCMem = CreateCompatibleDC(hDCDisplay);
ReleaseDC(NULL, hDCDisplay);
// This code loads the bitmap from a file. You will need to replace it with
// something that copies your image into the memory DC at the right size.
hBmp = (HBITMAP)LoadImage(
NULL, _T("Test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBmp == NULL)
{
MessageBox(hWnd, _T("Failed to load bitmap"), _T("Error"), MB_OK);
}
hBmp = (HBITMAP)::SelectObject(hDCMem, hBmp);
hWndTree = CreateWindowEx(
0,
WC_TREEVIEW,
_T(""),
WS_CHILD | WS_BORDER | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hWnd,
(HMENU)10000,
NULL,
0);
if (hWndTree == NULL)
{
MessageBox(NULL, _T("Failed to make tree view"), _T("Error"), MB_OK);
}
oldTreeWndProc = (WNDPROC)SetWindowLongPtr(
hWndTree, GWLP_WNDPROC, (LONG_PTR)TreeWndProc);
// Make sure the background color for the tree view is not the
// same as any of the selected colors so that selections don't
// get messed up by transparency. If this feels like a hack,
// that's because it is.
COLORREF selectedBGColor = GetSysColor(COLOR_HIGHLIGHT);
COLORREF selectedFGColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
COLORREF treeBGColor = (selectedBGColor + 1) % 0x00ffffff;
if (treeBGColor == selectedFGColor)
{
treeBGColor = (selectedFGColor + 1) % 0x00ffffff;
}
SendMessage(hWndTree, TVM_SETBKCOLOR, 0, treeBGColor);
// Add a bunch of dummy items to the tree view just for testing.
TVINSERTSTRUCT tvis;
::ZeroMemory(&tvis, sizeof(tvis));
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_TEXT;
tvis.hParent = TVI_ROOT;
TCHAR buffer[10];
for (int i = 0; i < 20; ++i)
{
_stprintf(buffer, _T("Item %d"), i);
tvis.item.pszText = buffer;
tvis.item.cchTextMax = _tcslen(buffer);
SendMessage(hWndTree, TVM_INSERTITEM, 0, (LPARAM)&tvis);
}
}
return 0;
// Leaving the WM_CTLCOLOREDIT stuff in here to show how that would
// seem to work. I tried it, and it doesn't really work all that well.
// Initially, the background shows through, but when you scroll the
// window, it doesn't redraw the background. It just seems to do a
// a ScrollWindow call and blts the background upward. Also, the
// background of the tree view items stayed white even with the code
// to change the background mode to TRANSPARENT.
//case WM_CTLCOLOREDIT:
// {
// HDC hdcCtrl = GET_WM_CTLCOLOR_HDC(wParam, lParam, message);
// HWND hWndCtrl = GET_WM_CTLCOLOR_HWND(wParam, lParam, message);
// if (hWndCtrl != hWndTree)
// {
// return DefWindowProc(hWnd, message, wParam, lParam);
// }
// SetTextColor(hdcCtrl, RGB(0, 0, 0));
// SetBkColor(hdcCtrl, RGB(0xff, 0xff, 0xff));
// SetBkMode(hdcCtrl, TRANSPARENT);
// RECT rect;
// GetWindowRect(hWndCtrl, &rect);
// POINT pt;
// pt.x = rect.left;
// pt.y = rect.top;
// ScreenToClient(hWnd, &pt);
// rect.left = pt.x;
// rect.top = pt.y;
// pt.x = rect.right;
// pt.y = rect.bottom;
// ScreenToClient(hWnd, &pt);
// rect.right = pt.x;
// rect.bottom = pt.y;
// int cx = rect.right - rect.left;
// int cy = rect.bottom - rect.top;
// BitBlt(hdcCtrl, 0, 0, cx, cy, hDCMem, rect.left, rect.top, SRCCOPY);
// return (LRESULT)GetStockObject(NULL_BRUSH);
// }
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// 960 x 540 is the size of the image I used for testing. Adjust for
// your own image.
::BitBlt(hdc, 0, 0, 960, 540, hDCMem, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
// Positioning the tree view somewhere on the parent that is not the
// upper left corner.
MoveWindow(hWndTree, 20, 20, 100, 100, TRUE);
break;
case WM_DESTROY:
hBmp = (HBITMAP)::SelectObject(hDCMem, hBmp);
::DeleteObject(hBmp);
::DeleteDC(hDCMem);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
正如我之前提到的,hDCMem
包含原始的预拉伸位图,并且必须可供子类访问WndProc
。如果原始位图在父级中绘制在 (0,0) 处,这只会按原样工作。如图所示,看起来非常糟糕。
它还有一些其他缺陷(可能比我在这里列出的更多):
它假定树视图始终使用纯色背景颜色绘制。如按钮所示,Microsoft 可能会随心所欲地更改其外观,因此您需要自担风险进行自定义。
它假定您知道导致树视图重绘的所有消息。这不是一个好的假设。即使现在是真的,也没有什么可以阻止它在未来对树视图控件的更新中破坏,这仅仅是因为没有记录它可以工作并且它正在使用不属于应用程序的代码 - 内置树视图WndProc
。
即使这是执行此操作的好方法,在每个WM_PAINT
.