MFC Grid manual

Custom drawing

The first step: Implementation of the ICustomDraw interface

The best place for all customizations is the ICustomDraw interface and its implementation in the CCustomDraw class. To customize the drawing, you can derive from CCustomDraw and then override the function ICustomDraw::DrawCell.
//Header file

class CMyCustomDraw : public Dapfor::GUI::CCustomDraw
{
    typedef Dapfor::GUI::CCustomDraw BaseClass;
public:
    CMyCustomDraw(bool autoDelete);

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

    ...
};

In general, implementation of this interface is quite easy. For more details about drawing, please see the Drawing workflow. The CCustomDraw::DrawCell() function is called each time, when the grid paints a cell. In the overridden function you get all information about the row, column, business object, colors, fonts, etc. You can set the appropriate appearance for each cell at any moment. Below you find the basic implementation of the CCustomDraw interface:

//Constructor
CMyCustomDraw::CMyCustomDraw(bool autoDelete) : CCustomDraw(autoDelete)
{
}

void CMyCustomDraw::DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    BaseClass::DrawCell(cell, paintContext, paintFilter);
}

Finally, let's set the instance of this interface to the grid:

//The boolean in the constructor indicates, that the grid handles the life-
//time of the interface and destroys it in its own destructor.

m_Grid.SetCustomDraw(new CMyCustomDraw(true));

How to change the font

You can change appearance by modifying the 'cell' value. For example, let's show how to modify the font:
void CMyCustomDraw::DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    //This variable should be visible by the base class
    CFont font;

    //Customize the font for each cell
    if(CFont* initialFont = cell.GetPaintInfo().GetFont())
    {
        //Get the current log font
        LOGFONT logFont;
        initialFont->GetLogFont(&logFont);

        //Change the font size for the column, identified by the 'COrder::FidProduct'.
        if ( cell.GetColumn() != 0 && 
            (cell.GetColumn()->GetFid() == COrder::FidProduct))
        {
            logFont.lfHeight = -MulDiv(12, GetDeviceCaps(paintContext.GetDC(), LOGPIXELSY), 72);
        }

        //Change the font family for other column
        if( cell.GetColumn() != 0 && 
           (cell.GetColumn()->GetFid() == COrder::FidUnitPrice))
        {
            //Change the font family
            TCHAR name[] = _T("Arial");
            memcpy(logFont.lfFaceName, name, sizeof(name));
            logFont.lfItalic = 1;
            logFont.lfWeight = 600;
        }

        //Now, let's create a new font
        font.CreateFontIndirect(&logFont);
        
        //Use the new font to draw in the cell
        cell.GetPaintInfo().SetFont(&font);
    }

    //Don't forget to call the function in the base class
    //Note that at this point the font value is visible.
    BaseClass::DrawCell(cell, paintContext, paintFilter);

    //And here, the font will be destroyed
}

How to change the foreground and background colors

The following code demonstrates how to change the text color:
//Set a custom color for all the line
void CMyCustomDraw::DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    ...
    if(cell.GetColumn() && cell.GetGridLine().GetDataObject())
    {
        UINT color = cell.GetPaintInfo().GetForeColor();
        switch(cell.GetGridLine().GetDataObject()->GetLong(COrder::FidStatus))
        {
            case COrder::Ordered:   color = RGB(11, 146, 63); break;
            case COrder::Payed:     color = RGB(106, 120, 133); break;
            case COrder::Refunded:  color = RGB(162, 125, 157); break;
            case COrder::Cancelled: color = RGB(165, 185, 21); break;
        }
        cell.GetPaintInfo().SetForeColor(color);
    }
    BaseClass::DrawCell(cell, paintContext, paintFilter);
}

How to set a custom icon in the cell

If you have an image list, it is very easy to paint a custom icon in the cell. Moreover, you can ask the grid to repaint the cell in some milliseconds and in the next painting routine, you can set a new icon. So, you can show a small 'video' in the cell.
//file MyCustomDraw.h
class CMyCustomDraw : public Dapfor::GUI::CCustomDraw
{
    CMyCustomDraw(bool autoDelete);
...
private:
    CImageList m_Bullets;
};


//file MyCustomDraw.cpp

#include "Dapfor/GUI/MsImg32.h"

//Constructor
CMyCustomDraw::CMyCustomDraw(bool autoDelete) : CCustomDraw(autoDelete)
{
    //The method CImageList::Create(...) does not permit to load 32-bit color image
    //To display full-colored images in cells of the grid, you can use the helper from the Dapfor::GUI library
    Dapfor::GUI::CMsImg32::Load32BitImage(m_Bullets, IDB_BULLETS, true, RGB(255, 255, 255));
}

void CMyCustomDraw::DrawCell(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter)
{
    //Set an image for the 'Unit price' column
    if( cell.GetColumn() && 
       (cell.GetColumn()->GetFid() == COrder::FidUnitPrice) && 
        cell.GetGridLine().GetDataObject())
    {
        cell.SetImageList(&m_Bullets);

        //Select some index in the image list
        int iconIndex = ((GetTickCount() % 2000) / 300) % 6;
        cell.SetImageId(iconIndex);
        cell.SetStretchImage(false);

        //request for the next drawing routine for the given cell in order to change the icon in few milliseconds
        cell.RequestNextPaint(30);
    }

    BaseClass::DrawCell(cell, paintContext, paintFilter);
}

Width and height adjustment

When the user double-clicks the column separator, the grid adjusts column size to the content. If you perform custom drawing, the grid can't detect the optimal size for the cell. Nevertheless, it probes the optimal size by calling ICustomDraw::GetOptimalCellWidth() and ICustomDraw::GetOptimalRowHeight() methods. When you customize the drawing, we recommend you to implement these functions too.
// Computes optimal width of a cell taking into account a font, hierarchy, text size...
int CMyCustomDraw::GetOptimalCellWidth(Dapfor::GUI::CGridCell& cell, const Dapfor::GUI::CPaintContext& paintContext, UINT paintFilter) const
{
    //This variable should be visible by the base class
    CFont font;
    
    //Customize the font for each cell
    if(CFont* initialFont = cell.GetPaintInfo().GetFont())
    {
        //Get the current font
        LOGFONT logFont;
        initialFont->GetLogFont(&logFont);
        
        //Change the font's size for the 'Product' column
        if(cell.GetColumn() != 0 && (cell.GetColumn()->GetFid() == COrder::FidProduct))
        {
            logFont.lfHeight = -MulDiv(12, GetDeviceCaps(paintContext.GetDC(), LOGPIXELSY), 72);
        }
        if(cell.GetColumn() != 0 && (cell.GetColumn()->GetFid() == COrder::FidUnitPrice))
        {
            TCHAR name[] = _T("Arial");
            memcpy(logFont.lfFaceName, name, sizeof(name));
            logFont.lfItalic = 1;
            logFont.lfWeight = 600;
        }       
        
        font.CreateFontIndirect(&logFont);
        cell.GetPaintInfo().SetFont(&font);
        
    }

    //The base class will do the computing...
    return BaseClass::GetOptimalCellWidth(cell, paintContext, paintFilter);
}

int CMyCustomDraw::GetOptimalRowHeight(CFont* font, CDC& dc) const
{
    //return the height
    return 16;
}