透明树视图控件 [英] Transparent tree view control

查看:27
本文介绍了透明树视图控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用纯 Win32 API 创建一个具有透明背景的树视图.

I want to create a tree view with transparent background, using pure Win32 API.

从 MSDN 阅读文档后,我能够成功创建它,但没有说明如何使其透明.Google 也无济于事,因为示例在 MFC 中,并且它们不会创建透明背景,而是通过使用 TreeView_SetBkColor API 或使用 TVM_SETBKCOLOR 消息来更改树的颜色.

After reading documentation from MSDN I was able to successfully create it, but there is nothing saying on how to make it transparent. Google doesn't help either, since examples are in MFC, and they do not create transparent background, but rather change tree's color by using TreeView_SetBkColor API or with TVM_SETBKCOLOR message.

举个例子,我创建了如下所示的窗口:

Just for an example, I have created window like this one below:

并且我添加了树视图作为子窗口,如下所示:

And I have added tree view as a child window like this:

我的问题是:如何使树的背景透明,以便可以看到它背后的图片?

My question is: How do I make tree's background transparent so the picture behind it can be seen ?

如果其他人有更好的答案/建议,请发布,但此时我会接受乔尔的解决方案.

If anybody else has a better answer/suggestion, please post it, but at this point I will accept Joel's solution.

推荐答案

你应该停止删除和重新添加这个问题.

You should probably stop deleting and re-adding this question.

这是我能想到的最好的.我再怎么强调都不为过,这段代码是多么容易被破解,以及它有多容易被破解.然而,这将我的努力与乔纳森的评论结合到了某种工作中.它会闪烁,而且很丑,但它或多或少会满足要求.

// 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) 处绘制的,这将仅按原样工作.正如图片所示,看起来非常糟糕.

As I mentioned before, the hDCMem contains the original pre-stretched bitmap and has to be accessible to the subclassed WndProc. This will only work as-is if the original bitmap is drawn at (0,0) in the parent. And as the image shows it's pretty awful to look at.

它还存在一些其他缺陷(可能比我在此处列出的更多):

It also has a few other flaws (and there are probably more than I've listed here):

  1. 它假设树视图总是用纯色背景绘制.正如按钮所示,Microsoft 可能会随心所欲地更改它们的外观,因此自定义风险由您自己承担.

  1. It assumes tree views are always drawn with a solid background color. As buttons have shown, Microsoft may change their appearance at whim, so customize at your own risk.

它假设您知道导致树视图重绘的所有消息.这不是一个好的假设.即使现在是真的,也没有什么可以阻止它在树视图控件的一些未来更新中被破坏,仅仅是因为这没有记录在案,并且它正在使用不属于应用程序的代码 - 内置树视图 WndProc.

It assumes you know all the messages that cause repaints on a tree view. This is not a good assumption. Even if it's true now, there's nothing to prevent it breaking with some future update to the tree view control, simply because this isn't documented to work and it's playing around with code that doesn't belong to the application - the built-in tree view WndProc.

即使这是一个很好的方法,在每个 WM_PAINT 上重新创建位图和内存 DC 可能也不是一个好主意.

Even if this is a good way to do this, it probably isn't a good idea to recreate the bitmaps and memory DCs on every WM_PAINT.

这篇关于透明树视图控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆