MFC Grid manual

Drawing workflow

The grid offers an interface to customize data presentation. Everything (colors, fonts, images, selection, focusing, etc.) can be customized. The grid itself draws nothing. Instead, it asks the drawing interface to do painting. Therefore, the programmer has full control of data painting. This drawing interface is called ICustomDraw. There is default implementation of this interface: CCustomDraw. If you don't set your own implementation, the grid will use the default implementation.

The best way to configure painting is to derive from CCustomDraw class and set your implementation to the grid by calling:

class CMyCustomDraw : public Dapfor::GUI::CCustomDraw
{
....
    // Called to draw a cell. 
    virtual void DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter);

}

//To set your custom drawing implementation, just call:
CMyCustomDraw* customDraw = new CMyCustomDraw(...);
m_Grid.SetCustomDraw(customDraw);

To understand how drawing works we will describe the drawing workflow. When WM_PAINT comes, the grid looks for implementation of the painting interface (or takes the default one) and then calls ICustomDraw::DrawLine function. This function is called with three parameters:

CCustomDraw default implementation doesn't do the drawing. It looks for ILinePaintFormat implementation or takes the default one implemented in CLinePaintFormat class, and forwards the call to the line paint format. The default implementation of this format draws nothing, but it takes all header's columns, constructs CGridCell object with cell dimension and appearance and calls ICustomDraw::DrawCell with these parameters.

The next step is to find ICellPaintFormat default implementation. First, CCustomDraw looks for the format in CColumn. If the column does not have format, CCellPaintFormat default implementation will be taken. Then, CCustomDraw calls ICellPaintFormat::DrawCell of the found object. Finally, the cell format performs all drawing in the specified bounds with the specified appearance.

So, ICustomDraw interface is called twice:

ICustomDraw interface is the best point to define drawing behaviour. For example at any moment you can change foreground or background colors:

void CMyCustomDraw::DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    if(cell.GetColumn() && cell.GetGridLine().GetDataObject())
    {
        //Get initial color.
        UINT color = cell.GetPaintInfo().GetForeColor();
        

        if(cell.GetColumn()->GetFid() == ...)
        {
            //Set the red color for the whole column
            cell.GetPaintInfo().SetForeColor(RGB(255, 0, 0));
        }
    }
    //Don't forget to call base class function to perform drawing with new parameters
    BaseClass::DrawCell(cell, paintContext, paintFilter);
}

Another example can give some ideas how to merge cells and draw text in the whole line:

void CMyCustomDraw::DrawLine(Dapfor::GUI::CGridLine& line, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    ...

    //Prevent from drawing text and images in cells.
    paintFilter &= paintFilter^(Dapfor::GUI::drawText|Dapfor::GUI::drawImage);

    //Draw background without text and images
    BaseClass::DrawLine(line, paintContext, paintFilter);


    //Save old parameters of CDC and set transparent mode
    int oldMode = paintContext.GetDC().GetBkMode();
    paintContext.GetDC().SetBkMode(TRANSPARENT);


    //Create a string.
    CString s(_T("This text is drawn in the whole line")); 

    //Draw text above the line 
    CRect rc(&line.GetPaintInfo().GetVisibleRect());
    paintContext.GetDC().DrawText(s, rc, DT_CENTER|DT_END_ELLIPSIS);

    //Restore the original mode
    paintContext.GetDC().SetBkMode(oldMode);
}

Note that this implementation can be easily put to ILinePaintFormat custom implementation