Winapi 一劳永逸:如何在Windows API中获得完全透明的复选框、按钮、单选按钮等,而不是黑色背景?

Winapi 一劳永逸:如何在Windows API中获得完全透明的复选框、按钮、单选按钮等,而不是黑色背景?,winapi,themes,gdi,user32,common-controls,Winapi,Themes,Gdi,User32,Common Controls,首先,如果我在这里听起来傲慢/粗鲁,我很抱歉 好吧,现在每个人都遇到了(我希望);我只是在任何地方都没有找到合适的答案。我们从一个公共控件6清单开始,然后 case WM_CTLCOLORSTATIC: if (/* window has WS_EX_TRANSPARENT */) { SetBkMode((HDC) wParam, TRANSPARENT); return (LRESULT) GetStockObject(HOLLOW_BRUSH);

首先,如果我在这里听起来傲慢/粗鲁,我很抱歉

好吧,现在每个人都遇到了(我希望);我只是在任何地方都没有找到合适的答案。我们从一个公共控件6清单开始,然后

case WM_CTLCOLORSTATIC:
    if (/* window has WS_EX_TRANSPARENT */) {
        SetBkMode((HDC) wParam, TRANSPARENT);
        return (LRESULT) GetStockObject(HOLLOW_BRUSH);
    }
并给我们的标签
WS\u EX\u透明
。它们变得透明;到现在为止,一直都还不错。现在我们必须将该样式添加到复选框中(因为复选框响应该样式,而不是出于某种原因响应
WM_CTLCOLORBTN
)。而且。。。复选框变黑了

有没有办法使它们完全透明,而不必诉诸业主抽签?我宁愿不要自己画复选框;如果主题API出现问题,我不想猜测它是否正确,或者大小是多少(将来当我在列表视图中添加自定义复选框时,我将不得不自己绘制复选框,我已经对所涉及的猜测量感到不满意)

这些复选框是在主题选项卡控件上绘制的。到目前为止,我在Windows XP中发现了五个对话框,在主题选项卡上有透明的复选框:快捷方式属性的常规选项卡、任务栏和开始菜单属性的任务栏选项卡、系统属性的系统还原选项卡、文件夹选项(单选按钮)的常规选项卡和辅助功能选项的键盘选项卡。所以这肯定是可能的!我确信Windows UI作者不必在整个操作系统中使用自定义绘图。。。我们都错过了什么

如果我需要子类,那很好(我已经有一个子类用于事件处理),但我还是不想自己画

作为奖励,按钮呢?覆盖
WM_CTLCOLORBTN
会给按钮一个黑色边框,但是我注意到上面提到的标准对话框中没有一个会让按钮的角落透明,所以呢:/

谢谢

主要是解决了这个问题:

void paintControlBackground(HWND hwnd, HDC dc)
{
    HWND parent;
    RECT r;
    POINT p;
    int saved;

    parent = GetParent(hwnd);
    if (parent == NULL)
        xpanic("error getting parent container of control in paintControlBackground()", GetLastError());
    if (GetWindowRect(hwnd, &r) == 0)
        xpanic("error getting control's window rect in paintControlBackground()", GetLastError());
    // the above is a window rect; convert to client rect
    p.x = r.left;
    p.y = r.top;
    if (ScreenToClient(parent, &p) == 0)
        xpanic("error getting client origin of control in paintControlBackground()", GetLastError());
    saved = SaveDC(dc);
    if (saved == 0)
        xpanic("error saving DC info in paintControlBackground()", GetLastError());
    if (SetWindowOrgEx(dc, p.x, p.y, NULL) == 0)
        xpanic("error moving window origin in paintControlBackground()", GetLastError());
    SendMessageW(parent, WM_PRINTCLIENT, (WPARAM) dc, PRF_CLIENT);
    if (RestoreDC(dc, saved) == 0)
        xpanic("error restoring DC info in paintControlBackground()", GetLastError());
}

    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
        if (SetBkMode((HDC) wParam, TRANSPARENT) == 0)
            xpanic("error setting transparent background mode to Labels", GetLastError());
        paintControlBackground((HWND) lParam, (HDC) wParam);
        *lResult = (LRESULT) hollowBrush;
        return TRUE;
有几个未解决的问题:

如果给定控件的直接父控件是groupbox,则此操作将不起作用;在这种情况下,您需要检查父链并沿父链走

您还需要在任何自定义容器和窗口类中实现
WM_PRINTCLIENT

这还不适用于groupbox标签;您将看到正确绘制的纹理,但groupbox线将绘制在其顶部。当我弄明白这一点并用这些信息更新这篇文章时,我会将其标记为已解决

(我看到的闪烁似乎是由程序的另一部分使用的世界垃圾收集器停止引起的,因此暂时不在我的控制范围内。希望这很快会改变。)


同时谢谢你

您必须返回已注册窗口类的
hbrBackground
元素,如下所示:

case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC: {
    wchar_t  class_Name[100];
    GetClassName(hWnd, class_Name, 100);
    WNDCLASS lpcls{};
    GetClassInfo(g_hInstance, class_Name, &lpcls);

    return  (LRESULT)lpcls.hbrBackground;
}

这个答案来得晚,但提供了一个更简单的方法

要使GroupBox的子类透明,请在GroupBox的子类proc中处理WM_CTLCOLORBTN消息

要使GroupBox的标题透明,请在GroupBox的wndproc过程的父级中处理WM_CTLCOLORBTN消息

这个链接显示了我在哪里学会的

我的代码(在groupbox的父类和groupbox的子类proc中都需要此代码):


标准Win32控件本身不支持透明性。你得把它弄糊涂
WS_EX_TRANSPARENT
不是这样。没错,但我在这里的印象是,如果我设置
WS_EX_TRANSPARENT
,它告诉窗口管理器在父窗口绘制之前不要绘制,并从
WM_CTLCOLORxxx
消息返回空心笔刷,控件将使用空心笔刷绘制,不绘制任何内容,因此显示以下内容。这是在哪里打破的?啊,我明白了,它实际上对兄弟姐妹是透明的,而不是对父母:|所以我猜复选框不能正确处理
WM_CTLCOLORSTATIC
,在这种情况下,我还能做些什么来避免自己绘制背景?我尝试了
案例WM\u ERASEKGND:return 1并在那里调用
SetBkMode()
,两者都无效。对于按钮,请尝试在您父母的处理程序中处理
WM_PRINTCLIENT
,这在过去对我很有帮助。关键是要从
WM_PAINT
复制图形代码,除非您现在将
HDC
作为消息的
wParam
提供(
HDC HDC=(HDC)wParam
而不是
HDC HDC=BeginPaint
,并且您不需要
EndPaint
)。至于复选框,我想你将需要自定义绘制它,因为我不知道任何其他解决方案…我不处理自定义绘制所有;这是所有的
DefWindowProc()
响应:这真的很有效!谢谢我一直在寻找一种使命令链接透明的方法,它奏效了。这个答案应该被接受,这是可行的,它不需要太多的代码行。
case WM_CTLCOLORSTATIC:
 case WM_CTLCOLORBTN:{
    SetBkMode((HDC) wParam, TRANSPARENT);

//if you need to get the classname
//char class_name[100];
  // GetClassName(hwnd, class_Name, 100);  
//end if you need to get the classname

    WNDCLASS lpcls{};
    GetClassInfo(hInstance,  _T("MainWindow"),&lpcls);//or use class_name
    return  (LRESULT)lpcls.hbrBackground;//works everywhere bs_groupbox
//below works in the groupbox's subclass proc but not in parent (strikes through the title)
 // return (LRESULT)GetStockObject(NULL_BRUSH);
       }