对话框不正确地重新绘制其背景

时间:2014-01-14 15:26:02

标签: c++ winapi gdi

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

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

我使用icon(屏幕截图左下角的男性),一个EMF文件(下面屏幕截图中的地图),其余部分由绿色渐变画笔组成,浅灰色阴影画笔和文字 - 全部使用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_ERASEBKGNDWM_SIZEWM_MOVE对话框无法调整大小。)。我尝试了但它没有帮助(TRUE返回WM_ERASEBKGNDInvalidateRectWM_SIZE返回WM_MOVE。我没有在互联网上找到任何帮助我的东西。

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

3 个答案:

答案 0 :(得分:1)

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

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

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

更新:在下面的几条评论之后,问题似乎是调用GradientFill()三角形中顶点的顺序。那就是:

  • 顶点按顺时针方向列出:绘制三角形。
  • 顶点按逆时针方向列出:未绘制三角形。

或许是另一种方式,我永远无法分辨......

无论如何,仍然有一个神秘的问题,为什么有时它无论顺序如何都有效,为什么有时它只适用于特定的顺序。而且,这在任何地方都有记录吗?

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

答案 1 :(得分:0)

你正在处理错误的信息。

您不应在WM_CTLCOLORDLG消息上绘制任何内容。只需将您的绘画代码移动到WM_ERASEBKGND处理程序。

答案 2 :(得分:0)

行。你的下一个问题是你正在重新绘制完整的窗口。请注意,窗口的左侧部分( 需要更新)被绘制好。问题是窗口的主要(右)部分,从左侧移动窗口后实际上并不需要更新。

WM_ERASEBKGND处理程序中,您可以调用GetUpdateRect()来获取需要更新的矩形。

如果您不想更改代码,可以至少拨打电话,例如在绘制任何内容之前IntersectClipRect() API。这会将所有绘画剪辑到该矩形。