RoundRect在透明背景上?

时间:2019-06-19 05:38:28

标签: winapi gdi ownerdrawn

我想使用RoundRectWM_DRAWITEM中为所有者绘制控件绘制一个填充的圆角矩形。一切正常,但由于某种原因,将圆角之外的区域设置为白色(就像先清除正方形然后画出圆角矩形一样)。我希望它不要触及该区域,而是将其视为透明的。怎么做?

TIA !!

2 个答案:

答案 0 :(得分:2)

Windows默认为矩形。绘制它们时,必须绘制整个矩形。也就是说,所有者至少可以通过三种方式将按钮绘制为圆形矩形:

简单方法:将圆形矩形按钮外部的区域涂成与其父级背景颜色相同的颜色

老派的方法:使用SetWindowRgn(...)使所有者绘制的按钮具有一个圆形的矩形裁剪区域。下面包含执行此操作的代码。这项技术的问题在于您无法对圆形矩形的边界进行抗锯齿处理。

新方法:由于可以使用WS_EX_LAYERED扩展样式创建Windows 8子窗口。这应该使您只需将窗口绘制为圆形矩形即可。但是,由于(我认为)您需要包括一个清单,该清单将您的可执行文件的受支持操作系统设置为Windows 8或更高版本,否则情况就变得复杂了,否则对CreateWindowEx(WS_EX_LAYERED,...)的调用只会失败(至少对我有帮助)。不幸的是,我无法显示如何将清单包含在我的头顶上。

然而,下面是非常基本的代码,展示了窗口区域的方式。您可以通过使用GDI +进行绘制并设置抗锯齿的图形来解决此问题,也可以使用位图填充而不是使用GDI / GDI +调用进行绘制。

// RoundRectButton.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "RoundRectButton.h"

#define BTN_ID 101

HINSTANCE g_instance = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    g_instance = hInstance;

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
    wc.lpszClassName = L"owner_draw_btn";

    if (!RegisterClass(&wc))
        return -1;

    if (!CreateWindow(wc.lpszClassName, L"foobar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL))
        return -1;

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

    return 0;
}

LRESULT HandleDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam, int corner_wd, int corner_hgt, 
        COLORREF unclicked_color, COLORREF clicked_color)
{
    auto* dis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
    if (dis->CtlType != ODT_BUTTON)
        return 0;

    COLORREF fill_color = (dis->itemState & ODS_SELECTED) ? clicked_color : unclicked_color;

    auto rect = &dis->rcItem;
    //DrawFrameControl(dis->hDC, rect, DFC_BUTTON, style);
    HPEN pen = CreatePen(PS_SOLID, 6, RGB(0, 0, 0));
    HPEN old_pen = (HPEN) SelectObject(dis->hDC, pen);
    HBRUSH brush = CreateSolidBrush(fill_color);
    HBRUSH old_brush = (HBRUSH) SelectObject(dis->hDC, brush);

    RoundRect(dis->hDC, rect->left, rect->top, rect->right, rect->bottom, corner_wd, corner_hgt);

    SelectObject(dis->hDC, old_pen);
    SelectObject(dis->hDC, old_brush);

    DeleteObject(pen);
    DeleteObject(brush);

    TCHAR text[512];
    auto n = GetWindowText(dis->hwndItem, text, 512);
    SetBkMode(dis->hDC, TRANSPARENT);
    DrawText(dis->hDC, text, n, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);

    return 0;
}

HWND CreateRoundRectButton(HWND parent, int x, int y, int wd, int hgt, int corner_wd, int corner_hgt, int id)
{
    HWND button = CreateWindow(
        L"button", L"foobar",
        BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
        x, y, wd, hgt, parent,
        (HMENU) id,
        g_instance,
        0
    );
    SetWindowRgn(button, CreateRoundRectRgn(0, 0, wd, hgt, corner_wd, corner_hgt), TRUE);
    return button;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND button = NULL;
    DWORD err;
    switch (message)
    {
    case WM_CREATE:
        button = CreateRoundRectButton(hWnd, 15, 15, 150, 35, 15, 15, BTN_ID);
        return 0;

    case WM_DRAWITEM:
        return HandleDrawItem(hWnd, wParam, lParam, 15, 15, RGB(230,230,230), RGB(255,255,255));

    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

答案 1 :(得分:0)

由于这是一个按钮控件,因此似乎可以正常工作(我试图添加注释,但无法设置其格式)。

case WM_CTLCOLORBTN:
{
  if ((HWND)lparam==GetDlgItem(hwnd, IDC_BUTTON)) {
    return (LRESULT) ::GetStockObject(NULL_BRUSH);
  }
  break;
}