在我设置视觉样式后,常规控件未使用WM_CTLCOLORSTATIC处理程序正确绘制

时间:2014-03-20 09:01:15

标签: c++ winapi

引言及相关资料:

我通过资源编辑器创建了2个对话框。由于我使用 Microsoft Visual Studio Express版,我必须下载free resource editor来创建它们。在我的计划中,我Visual Styles启用了这样:

#include <commctrl.h>

#pragma comment( lib, "comctl32.lib")

#pragma comment( linker, "/manifestdependency:\"type='win32' \
        name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
        processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
        language='*'\"")

据我所知,复选框单选按钮组合框获取WM_CTLCOLORSTATIC消息,用于绘制文本

这是我为第一个和第二个对话框编码的

case WM_CTLCOLORSTATIC:
    {
        SetBkMode( (HDC)wParam, TRANSPARENT );
        SetTextColor( (HDC)wParam, RGB( 0, 0, 0 ) );
        return (INT_PTR)( (HBRUSH)GetStockObject(NULL_BRUSH) );
    }

我希望这些控件具有透明的文本背景和文本的黑色。

问题:

Windows XP 上,这是第一个对话框的结果图像:

enter image description here

组框有蓝色文字和棕色边框,而复选框的所有内容都是黑色。在 Windows 7 上,启动相同的程序后,我得到了这个:

enter image description here

此处,组框复选框具有正确的文本颜色,但复选框的背景的边框组框是错误的。在我的对话框中,我在 Windows 7 上都有静态控件它们被正确绘制 Windows XP

我为此做了什么:

我浏览了SO档案,但是找不到任何我可以用来修改WM_CTLCOLORSTATIC处理程序的内容。

我找到了一个解决方法,从这些控件中删除 视觉样式,因此它可以实现所需的结果,但我需要保留视觉样式< / em>并使文本的背景透明,因此这个解决方案不能满足我。

在浏览 Visual Styles参考并进行一些实验后,我找到了单选按钮复选框的解决方法(但不是分组框),代码如下:

case WM_CTLCOLORSTATIC:
    if( (HWND)lParam == GetDlgItem( hwnd, IDC_RADIO1 ) ) 
    {
        RECT r;
        GetClientRect( hwnd, &r );
        DrawThemeParentBackground( (HWND)lParam, (HDC)wParam, &r );
    }
    else
    {
        SetTextColor( (HDC)wParam, RGB( 0, 0, 0 ) );
        SetBkMode( (HDC)wParam, TRANSPARENT );
    }
    return (INT_PTR)( (HBRUSH)GetStockObject(NULL_BRUSH) );

尽管如此,我已经&#34;撞墙了#34;有了这个:

在我的对话框中有一个 treeview ,一旦我选择了节点并按下空格键(或任何其他关键字)对话框的背景位图获取在我的静态控件之上。

在我评论DrawThemeParentBackground()之后,重新编译并重新启动程序一切正常(当我选择树的节点并按空格键)但是我是&#34;在第一个方向&#34;。

问题:

  1. 如何修改我的WM_CTLCOLORSTATIC处理程序以解决我的问题?

  2. 如果无法解决上述问题,我可以使用NM_CUSTOMDRAW获得所需的效果吗?

  3. 注:

    我认为我必须使用group box绘制GDI。如果是这种情况,我也会接受这个解决方案,因为我主要担心的是checkboxradio button

    使用提交的示例项目进行编辑:

    根据要求,我正在提交SSCCE。要创建项目,请按以下步骤操作:

    1。)在Visual Studio中创建默认的Win32项目。

    2。)在stdafx.h复制/粘贴以下#include <windows.h>下面的指令:

    #include <commctrl.h>
    #include <Uxtheme.h>
    
    #pragma comment( linker, "/manifestdependency:\"type='win32' \ 
                        name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                        processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                        language='*'\"")
    
    #pragma comment( lib, "comctl32.lib")
    #pragma comment( lib,"Msimg32.lib")    // needed for GradientFill(...) API
    #pragma comment( lib, "UxTheme.lib")
    

    3。)在资源编辑器中,通过添加2 单选按钮,2 复选框来更改关于对话框包围它们的组框(参见上面的图片也看我的意思)并添加 treeview 控件(此控件将&#34;触发&#34;问题)。

    4.。)在主cpp文件中,添加帮助函数以绘制自定义背景:

    void GradientTriangle( HDC MemDC, 
        LONG x1, LONG y1, 
        LONG x2, LONG y2, 
        LONG x3, LONG y3, 
        COLORREF top, COLORREF bottom )
    {
        TRIVERTEX vertex[3];
    
        vertex[0].x     = x1;
        vertex[0].y     = y1;
        vertex[0].Red   = GetRValue(bottom) << 8;
        vertex[0].Green = GetGValue(bottom) << 8;
        vertex[0].Blue  = GetBValue(bottom) << 8;
        vertex[0].Alpha = 0x0000;
    
        vertex[1].x     = x3;
        vertex[1].y     = y3; 
        vertex[1].Red   = GetRValue(bottom) << 8;
        vertex[1].Green = GetGValue(bottom) << 8;
        vertex[1].Blue  = GetBValue(bottom) << 8;
        vertex[1].Alpha = 0x0000;
    
        vertex[2].x     = x2;
        vertex[2].y     = y2;
        vertex[2].Red   = GetRValue(top) << 8;
        vertex[2].Green = GetGValue(top) << 8;
        vertex[2].Blue  = GetBValue(top) << 8;
        vertex[2].Alpha = 0x0000;
    
        // Create a GRADIENT_TRIANGLE structure that
        // references the TRIVERTEX vertices.
    
        GRADIENT_TRIANGLE gTriangle;
    
        gTriangle.Vertex1 = 0;
        gTriangle.Vertex2 = 1;
        gTriangle.Vertex3 = 2;
    
        // Draw a shaded triangle.
    
        GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);
    }
    

    5.)在common controls中发起_tWinMain

    // initialize common controls
    
    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_STANDARD_CLASSES ;
    InitCommonControlsEx(&iccex);
    

    6。)在WM_INITDIALOG中的树视图中插入一些项目:

    case WM_INITDIALOG:
        {
            HWND TreeView = GetDlgItem( hDlg, IDC_TREE1 );
    
            // add root item
    
            TVINSERTSTRUCT tvis = {0};
    
            tvis.item.mask = TVIF_TEXT;
            tvis.item.pszText = L"This is root item";
            tvis.hInsertAfter = TVI_LAST;
            tvis.hParent = TVI_ROOT;
    
            HTREEITEM hRootItem = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
                TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );
    
            // add firts subitem for the hTreeItem
    
            memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );
    
            tvis.item.mask = TVIF_TEXT;
            tvis.item.pszText = L"This is first subitem";
            tvis.hInsertAfter = TVI_LAST;
            tvis.hParent = hRootItem;
    
            HTREEITEM hTreeSubItem1 = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
                TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );
    
            // now we insert second subitem for hRootItem
    
            memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );
    
            tvis.item.mask = TVIF_TEXT | TVIF_STATE; // added extra flag
            tvis.item.pszText = L"This is second subitem";
            tvis.hInsertAfter = TVI_LAST;
            tvis.hParent = hRootItem;
    
            HTREEITEM hTreeSubItem2 = reinterpret_cast<HTREEITEM>( SendMessage( TreeView , 
                TVM_INSERTITEM, 0, reinterpret_cast<LPARAM>( &tvis ) ) );
        }
        return (INT_PTR)TRUE;
    

    7。)绘制对话框的自定义背景:

    case WM_ERASEBKGND:
        {
            RECT r;
            GetClientRect( hDlg, &r );
    
            GradientTriangle( (HDC)wParam, r.right, r.bottom - r.top, 
                r.left, r.bottom - r.top,
                r.left, r.top,
                RGB( 0x0, 0x0, 0xFF ), RGB( 0xFF, 0xFF, 0x0 ) );
    
            GradientTriangle( (HDC)wParam, r.right, r.bottom - r.top, 
                r.right, r.top,
                r.left, r.top, 
                RGB( 0xFF, 0x0, 0x0 ), RGB( 0x0, 0xFF, 0x0 ) );
        }
        return TRUE;
    

    8。)添加WM_CTLCOLORSTATIC的处理程序:

    case WM_CTLCOLORSTATIC:
        if( ( (HWND)lParam == GetDlgItem( hDlg, IDC_RADIO1 ) )       
            || ( (HWND)lParam == GetDlgItem( hDlg, IDC_CHECK1 ) ) ) 
        {
            RECT r;
            GetClientRect( hDlg, &r );
            DrawThemeParentBackground( (HWND)lParam, (HDC)wParam, &r );
        }
        else
        {
            SetTextColor( (HDC)wParam, RGB( 0, 0, 0 ) );
            SetBkMode( (HDC)wParam, TRANSPARENT );
        }
        return (INT_PTR)( (HBRUSH)GetStockObject(NULL_BRUSH) );
    

    这将是创建演示问题的最小程序所需的所有步骤和信息。现在运行应用程序并观察差异:

    带有IDC_RADIO1

    单选按钮和带有IDC_CHECK1复选框将被正确绘制(带有黑色的透明文本),而其他复选框单选按钮不会。此外,在您单击树视图并按空格键后,您将看到对话框背景的问题。

    编辑结束

    谢谢。

    最好的问候。

2 个答案:

答案 0 :(得分:3)

我已经制作了你的对话框,并在xp和7中运行它,一切都很顺利。你应该做的是以下几点:

在内存中创建一个位图并在那里完成所有绘图。这应该是对话框背景。

HDC hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcMemDialogBackground = CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, dialogWidth, dialogHeight);
hBitmapOld = SelectObject(hdcMemDialogBackground , hBitmap);
DeleteDC(hdc);

创建单选按钮和复选框使用&#34;按钮&#34;

的类不是静态的
WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX
WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON

样式。

在对话框中包括

WS_CLIPCHILDREN

式。这将解决空间按下时控件重叠的背景。

您不应在WM_ERASEBKGND消息中绘制对话框的背景,而是WM_PAINT

case WM_PAINT:
        RECT r, rt;
        GetUpdateRect(hDlg, &rt, false);

        GetClientRect( hDlg, &r );

        GradientTriangle( hdcMemDialogBackground, r.right, r.bottom - r.top, 
        r.left, r.bottom - r.top,
        r.left, r.top,
        RGB( 0x0, 0x0, 0xFF ), RGB( 0xFF, 0xFF, 0x0 ) );

        GradientTriangle( hdcMemDialogBackground, r.right, r.bottom - r.top, 
        r.right, r.top,
        r.left, r.top, 
        RGB( 0xFF, 0x0, 0x0 ), RGB( 0x0, 0xFF, 0x0 ) );

        hdc = BeginPaint(hDlg, &ps);

        BitBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, hdcMemDialogBackground, rt.left, rt.top, SRCCOPY);

        EndPaint(hDlg, &ps);

        break;

最后透明的背景,在WM_NOTIFY

case WM_NOTIFY:
    NMHDR *nmr;
    NMCUSTOMDRAW *nmcd;

    nmr = (NMHDR *)lParam;
    nmcd = (NMCUSTOMDRAW *)lParam;

    if(nmr->idFrom == IDC_RADIO1 && nmr->code == NM_CUSTOMDRAW){
        if(nmcd->dwDrawStage == CDDS_PREERASE){
            BitBlt(nmcd->hdc, 0, 0, radioButton1Width, radioButton1Height, hdcMemDialogBackground, radioButton1PosX, radioButton1PosY, SRCCOPY);

            return CDRF_SKIPDEFAULT;
        }
    }

    if(nmr->idFrom == IDC_RADIO2 && nmr->code == NM_CUSTOMDRAW){
        //the same
    }

    if(nmr->idFrom == IDC_CHECK1 && nmr->code == NM_CUSTOMDRAW){
        //the same
    }

    if(nmr->idFrom == IDC_CHECK2 && nmr->code == NM_CUSTOMDRAW){
        //the same
    }

    break;

最后处理你的资源:

SelectObject(hdcMemDialogBackground, hBitmapOld);
DeleteObject(hBitmap);
hBitmap = NULL;
DeleteDC(hdcMemDialogBackground);
hdcMemDialogBackground= NULL;

修改

POINT pt;
RECT rt;
GetClientRect(hwndRadioButton1, &rt);
pt.x = 0;
pt.y = 0;
ClientToScreen(hwndRadioButton1, &pt);
ScreenToClient(hDlg, &pt);

BitBlt(nmcd->hdc, 0, 0, rt.right, rt.bottom, hdcMemDialogBackground, pt.x, pt.y, SRCCOPY);

EDIT2(用于调整大小)

更改WM_PAINT:

case WM_PAINT:
    GetUpdateRect(hDlg, &rt, false);
    hdc = BeginPaint(hDlg, &ps);

    BitBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, hdcMemDialogBackground, rt.left, rt.top, SRCCOPY);

    EndPaint(hDlg, &ps);

    break;

在WM_SIZE中:

case WM_SIZE:
    r.left = 0;
    r.top = 0;
    r.right = LOWORD(lParam);;
    r.bottom = HIWORD(lParam);

    GradientTriangle(hdcMemDialogBackground, r.right, r.bottom - r.top, 
                r.left, r.bottom - r.top,
                r.left, r.top,
                RGB( 0x0, 0x0, 0xFF ), RGB( 0xFF, 0xFF, 0x0 ) );

    GradientTriangle(hdcMemDialogBackground, r.right, r.bottom - r.top, 
                r.right, r.top,
                r.left, r.top, 
                RGB( 0xFF, 0x0, 0x0 ), RGB( 0x0, 0xFF, 0x0 ) );

    InvalidateRect(hwndRadioButton1, NULL, false);
    InvalidateRect(hwndRadioButton2, NULL, false);
    InvalidateRect(hwndCheck11, NULL, false);
    InvalidateRect(hwndCheck2, NULL, false);
    InvalidateRect(hDlg, NULL, false);

    break;

要调整大小,您需要创建最大值hdcMemDialogBackground 尺寸对话框将具有例如1280x1024或1680x1050或任何其他。

多数民众赞成。

瓦尔特

答案 1 :(得分:2)

WM_CTLCOLORSTATIC处理程序中,您只能更改静态控件的BG。

按钮,单选按钮和检查按钮是相同的控制按钮。要更改BG,您需要处理WM_CTLCOLORBTN。可以找到一个示例here