おそらく、この質問を削除して再度追加するのはやめるべきです。
編集済み:これは私が思いついた最高のものです。このコードがどのようなハックであり、どれほど簡単に解読できるかをいくら強調してもしきれません。ただし、これは私の努力とジョナサンのコメントを組み合わせて、ある種の作品にしています。ちらつき、醜いですが、多かれ少なかれ要求されたことを実行します。
// 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
。
これが良い方法であるとしても、ビットマップとメモリ DC を毎回WM_PAINT
.