使用CEF使父窗口透明并使子窗口不透明

5
在Windows上,我正在尝试使用CEF(Chromium嵌入式框架)创建一个窗口应用程序,其中父窗口为透明,其子窗口为不透明(我想要在子窗口中拥有圆角和指向状态栏的箭头)。类似于:

enter image description here

我尝试使用 SetLayeredWindowAttributes 使父窗口透明,但这也会使子窗口透明。在Windows上有没有办法实现这一点?


如果您愿意牺牲一些性能(这可能取决于您的应用程序),则可以使用离屏渲染将外星控件作为浏览器视图放入您的实际窗口中。 - hiitiger
1个回答

2
SetLayeredWindowAttributes需要一个透明度的颜色。确保透明颜色没有被子窗口使用。你可以选择一个随机的颜色,例如RGB(255, 0, 254)并假设子窗口没有使用它。
如果你无法控制子窗口,并且无法确定它可能使用什么颜色,那么SetWindowRgn是创建非矩形窗口的另一种选择。
下面的示例显示如何设置区域,使其四个角是圆形的,并在顶部有一个三角形。
你可以使用GDI+来获得更灵活的绘制区域的方法,并进行抗锯齿处理,使边框看起来更加平滑。
#include <Windows.h>

int triangle_height = 30;
int corner_size = 20;
int caption_height = 60;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
    case WM_CREATE:
    {
        //create a child button for testing
        CreateWindowW(L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE,
            10, caption_height + 10, 100, 30,
            hwnd, HMENU(100), GetModuleHandle(NULL), NULL);

        RECT rc;
        GetClientRect(hwnd, &rc);

        //create a triangle region
        int w = rc.right;
        int h = rc.bottom;
        int z = triangle_height;
        POINT pt[3];
        pt[0] = { w / 2, 0 };
        pt[1] = { w / 2 - z, z };
        pt[2] = { w / 2 + z, z };
        HRGN htri = CreatePolygonRgn(pt, 3, WINDING);

        //create a round rectangle region
        HRGN hrgn = CreateRoundRectRgn(0, z, w, h - z, corner_size, corner_size);

        //combine the triangle with round rectangle
        CombineRgn(hrgn, htri, hrgn, RGN_OR);

        //set the new region
        SetWindowRgn(hwnd, hrgn, TRUE);

        DeleteObject(htri);
        DeleteObject(hrgn);
        return 0;
    }

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc = ps.rcPaint;

        //we don't have a standard title bar, paint one here:
        rc.bottom = caption_height;
        SetDCBrushColor(hdc, RGB(80, 80, 80));
        FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));

        //paint the background
        rc = ps.rcPaint;
        rc.top = caption_height;
        SetDCBrushColor(hdc, RGB(240, 240, 240));
        FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));

        //use FrameRgn to paint a border around the region
        HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
        GetWindowRgn(hwnd, hrgn);
        SetDCBrushColor(hdc, RGB(128, 128, 128));
        FrameRgn(hdc, hrgn, (HBRUSH)GetStockObject(DC_BRUSH), 1, 1);
        DeleteObject(hrgn);

        EndPaint(hwnd, &ps);

        return 0;
    }

    case WM_NCHITTEST:
    {
        //we don't have a standard title-bar
        //respond to our custome title-bar manually:
        POINT pt;
        GetCursorPos(&pt);
        ScreenToClient(hwnd, &pt);
        if(pt.y < caption_height)
            return HTCAPTION;
        break;
    }

    case WM_COMMAND:
        if(HIWORD(wparam) == BN_CLICKED)
            if(LOWORD(wparam) == 100)
                SendMessage(hwnd, WM_CLOSE, 0, 0);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wparam, lparam);
}

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
    WNDCLASSEXW wcex = { sizeof(wcex) };
    wcex.style = CS_DROPSHADOW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = NULL;
    wcex.lpszClassName = L"classname";
    RegisterClassExW(&wcex);

    CreateWindowW(wcex.lpszClassName, L"Test", WS_VISIBLE | WS_POPUP,
        200, 200, 600, 400, 0, 0, hInstance, 0);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

嗨Barmak:你的解决方案有效。我按原样测试了它,看到了带箭头的圆角框。我花了一些时间确保它也适用于CEF窗口的父窗口。最终,那也行得通。谢谢你。我会给你赏金! - trungdinhtrong
欢迎 @trungdinhtrong。顺便说一下,您可以添加[winapi]标签来吸引更多关注这类问题。更新:使用FrameRgn在区域周围添加了一个细边框。 - Barmak Shemirani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接