c++ - 对话框不正确地重绘其背景

标签 c++ winapi gdi

我有一个对话框,它应该有一个自定义图像作为背景。

我没有位图或任何其他格式的整个图像,所以我必须从头开始绘制。

我使用了一个icon(截图左下角的男性),一个EMF文件(下面截图中的 map ),剩下的包括绿色渐变画笔、浅灰色阴影画笔和文本的组合 - 所有这些都是使用 GDI 绘制的。我的绘图结果如下所示(屏幕截图也包含控件):

enter image description here

当我将对话框向左 移动然后将其移回其原始位置时,我遇到的问题就出现了。下一个屏幕截图的图片底部说明了发生的工件:

enter image description here

对话框是无模式对话框,我在WM_CTLCOLORDIALOG 中绘制整个图像,之后返回NULL_BRUSH

这里是WM_CTLCOLORDIALOG处理程序的相关代码片段(请注意,我直接在DC上绘制,没有双缓冲。原因是这个是一个快速测试代码,很快就出错了):

case WM_CTLCOLORDLG:
    {   
        RECT rect; // dialog's client rectangle

        GetClientRect( hwnd, &rect );

        // ligh gray brush for hatched brush

        HBRUSH hbPozadina = CreateSolidBrush( RGB( 242, 242, 242 ) );

        FillRect( (HDC)wParam, &rect, hbPozadina );

        // cleanup

        DeleteObject( hbPozadina );

        // draw grid "manualy"

        LOGBRUSH lbPozadina;

        HGDIOBJ hPenPozadina = NULL, hOldPenPozadina;

        lbPozadina.lbColor = RGB( 255, 255, 255 );
        lbPozadina.lbHatch = 0;
        lbPozadina.lbStyle = BS_SOLID;

        hPenPozadina = ExtCreatePen( PS_COSMETIC | PS_SOLID, 1, &lbPozadina, 0, NULL); 

        hOldPenPozadina = SelectObject((HDC)wParam, hPenPozadina);

        // draw vertical lines

        for( int i = rect.left + 12; i< rect.right; i += 12)
        {
            MoveToEx((HDC)wParam, i, rect.top, NULL );

            LineTo((HDC)wParam, i, rect.bottom - rect.top + 1 );
        }

        // draw horizontal lines

        for( int i = rect.top + 12; i< rect.bottom; i += 12)
        {
            MoveToEx((HDC)wParam, rect.left, i, NULL );

            LineTo((HDC)wParam, rect.right - rect.left + 1, i );
        }

        //clean up

        SelectObject((HDC)wParam, hOldPenPozadina);

        DeleteObject(hPenPozadina);

        // draw metafile of the map

        HENHMETAFILE hemf = GetEnhMetaFile( L".\\resources\\KartaDlg.emf" );
        ENHMETAHEADER emh; 
        GetEnhMetaFileHeader( hemf, sizeof(emh), &emh ); 

        // remove the "status bar" from the calculation

        RECT r;

        r.top = rect.top;
        r.bottom = rect.bottom - 30;
        r.left = rect.left;
        r.right = rect.right;

        // calculate rescaled metafile

        UINT o_height = emh.rclFrame.bottom - emh.rclFrame.top, 
            o_width =  emh.rclFrame.right - emh.rclFrame.left;

        float scale = 0.5;

        scale = (float)( r.right - r.left ) / o_width;

        if( (float)( r.bottom - r.top ) / o_height  <  scale )
            scale = (float)( r.bottom - r.top ) / o_height;

        int marginX = ( r.right - r.left ) - (int)( o_width * scale );
        int marginY = ( r.bottom - r.top ) - (int)( o_height * scale );

        marginX /= 2;
        marginY /= 2;

        r.left = r.left + marginX;
        r.right = r.right - marginX;
        r.top = r.top + marginY;
        r.bottom = r.bottom - marginY;

        // Draw the picture.  

        PlayEnhMetaFile( (HDC)wParam, hemf, &r ); 

        // Release the metafile handle.  

        DeleteEnhMetaFile(hemf); 

        // this function draws green gradient and icon

        drawFooter( (HDC)wParam, rect, 
            RGB( 0x48, 0xAC, 0xC6), RGB( 0x31, 0x83, 0x99 ) ); 

        //========= draw right text in status bar =============//

        SetBkMode( (HDC)wParam, TRANSPARENT );

        SIZE sBaner; // needed for proper positioning

        HFONT hf, hfOld;

        long lfHeight;

        lfHeight = -MulDiv( 8, GetDeviceCaps( (HDC)wParam, LOGPIXELSY), 72 );

        hf = CreateFont( lfHeight, 0, 0, 0, FW_BOLD, TRUE, 
            0, 0, 0, 0, 0, 0, 0, L"Arial Black" );

        hfOld = (HFONT)SelectObject( (HDC)wParam, hf ); // needed for proper cleanup

        GetTextExtentPoint32( (HDC)wParam, 
            L"ЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ",
            wcslen(L"ЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ"), 
            &sBaner );

        // position it properly

        r.bottom = rect.bottom;
        r.right = rect.left + sBaner.cx + 30;
        r.left = rect.left + 30;
        r.top = rect.bottom - rect.top - 30;

        // draw it 

        DrawTextEx( (HDC)wParam, 
            L"РУДАРСКО ГЕОЛОШКИ ФАКУЛТЕТ\nЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ",
            wcslen(L"РУДАРСКО ГЕОЛОШКИ ФАКУЛТЕТ\nЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ"), 
            &r, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_WORDBREAK, 0 );

        SelectObject( (HDC)wParam, hfOld ); // proper cleanup

        DeleteObject( hf );

        //============== right text in the status bar ==================//

        lfHeight = -MulDiv( 10, GetDeviceCaps( (HDC)wParam, LOGPIXELSY), 72 );

        hf = CreateFont( lfHeight, 0, 0, 0, FW_BOLD, TRUE, 
            0, 0, 0, 0, 0, 0, 0, L"Arial" );

        hfOld = (HFONT)SelectObject( (HDC)wParam, hf ); // needed for proper cleanup

        GetTextExtentPoint32( (HDC)wParam, 
            L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана",
            wcslen(L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана"), 
            &sBaner );

        // position it properly

        r.bottom = rect.bottom;
        r.right = rect.right - 10;
        r.left = rect.right - rect.left - sBaner.cx - 10;
        r.top = rect.bottom - rect.top - sBaner.cy;

        // draw it 

        DrawTextEx( (HDC)wParam, 
            L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана",
            wcslen(L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана"), 
            &r, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_WORDBREAK | DT_NOPREFIX, 0 );

        // perform proper cleanup

        SelectObject( (HDC)wParam, hfOld );

        DeleteObject(hf);
    }
    return (INT_PTR)GetStockObject(NULL_BRUSH);

为了使这个更加完整,我提交了 drawFooter 函数及其辅助函数:

// Fills triangle with gradient brush

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     = x2;
    vertex[1].y     = y2;
    vertex[1].Red   = GetRValue(top) << 8;
    vertex[1].Green = GetGValue(top) << 8;
    vertex[1].Blue  = GetBValue(top) << 8;
    vertex[1].Alpha = 0x0000;

    vertex[2].x     = x3;
    vertex[2].y     = y3; 
    vertex[2].Red   = GetRValue(bottom) << 8;
    vertex[2].Green = GetGValue(bottom) << 8;
    vertex[2].Blue  = GetBValue(bottom) << 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);
}

// draw the window's footer ( "status bar" )

void drawFooter( HDC MemDC, RECT r, COLORREF top, COLORREF bottom )
{
    // bottom triangle

    GradientTriangle( MemDC, 
        r.right, r.bottom, 
        ( r.right - r.left ) / 2,
        r.bottom - r.top - 15,
        r.left, 
        r.bottom, 
        top, bottom );

    // upper triangle

    GradientTriangle( MemDC, 
        r.right, r.bottom - r.top - 30, 
        ( r.right - r.left ) / 2, 
        r.bottom - r.top - 15,
        r.left, 
        r.bottom - r.top - 30, 
        top, bottom );

    // left triangle

    GradientTriangle( MemDC, 
        r.left, r.bottom, 
        ( r.right - r.left ) / 2, 
        r.bottom - r.top - 15,
        r.left, 
        r.bottom - r.top - 30, 
        top, bottom );

    // right triangle

    GradientTriangle( MemDC, 
        r.right, 
        r.bottom - r.top - 30,
        ( r.right - r.left ) / 2,
        r.bottom - r.top - 15, 
        r.right, 
        r.bottom, 
        top, bottom );

    // draw icon

    DrawIconEx( MemDC, r.left, r.bottom - r.top - 30, 
        hiAdmin, // a global variable for icon
        30, 30, NULL, NULL, DI_NORMAL );
}

视觉样式已启用 - 这可能很重要,我不知道。

我没有处理 WM_ERASEBKGND 也没有处理 WM_SIZEWM_MOVE(对话框不能调整大小。)。我已经尝试过但没有帮助(为 WM_ERASEBKGND 返回 TRUE,为 WM_SIZE 返回 InvalidateRect WM_MOVE)。我在互联网上找不到任何可以帮助我的东西。

问题:如何更改我的代码以修复我遇到的错误?

最佳答案

您正在滥用 WM_CTLCOLORDLG 消息。它旨在提供一种简单的方法来更改对话框的背景颜色,而不是自定义绘制它。

您应该在那里返回 NULL_BRUSH,或者甚至完全忽略该消息,并在 WM_ERASEBKGND 中绘制背景。

或者甚至更好,您可以忽略 WM_ERASEBKGND 并在 WM_PAINT 中进行绘画,就像任何其他窗口一样。

更新:在下面的一些评论之后,问题似乎是调用 GradientFill() 三角形时顶点的顺序。即:

  • 顶点顺时针排列:绘制三角形。
  • 顶点逆时针排列:不绘制三角形。

或者也许是相反的方式,我永远也说不准......

无论如何,仍然存在一个谜,即为什么有时它无论顺序如何都有效,而有时它只适用于特定的顺序。此外,这是否记录在任何地方?

我猜测这可能是驱动程序/2D 加速问题...所以这将取决于 DC 是在显示器上还是在内存中,但这很难说。

关于c++ - 对话框不正确地重绘其背景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21117252/

相关文章:

c++ - 如何使动态字符串在控制台中使用 UTF-8?

c++ - 如何使用 winsock 在 C++ 应用程序中实现安全套接字通信?

c++ - 如何找到字体的宽度?

c++ - 为什么 C 风格的类型转换可以工作,但 reinterpret_cast 不能?

java - Java、C 和 C++ 中循环变量的类型限制

c++ - GNU MAKE - 从我自己的 Makefile 创建 getoptpp 库或为 c++ 建议命令行选项库

c++ - bitblt 在 Windows 10 版本 1703 (15063.138) 上失败

c++ - C++中的浏览器帮助对象

c# - 在内核空间模拟鼠标点击

c++ - 如何将 GDI 8-bpp 索引位图转换为 RGB 位图?