LKB
LKB

Reputation: 1040

MFC CListCtrl CustomDraw() event handler code getting stuck

I'm currently experiencing a problem with my OnNMCustomdrawlistctrlvalues() event handler being called non-stop once I've painted a CListCtrl row red (code gets stuck), causing my application to freeze once I try to perform another event, such as clicking a button.

I have the following code:

void CSPID_FQA_Test_ClientDlg::OnNMCustomdrawlistctrlvalues(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    int itemCnt = 0;
    CString text;
    RECT rc;

    switch(lpLVCustomDraw->nmcd.dwDrawStage)
    {
    case CDDS_ITEMPREPAINT:
    case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
        //get each row text for 2nd column
        itemCnt = mListCtrl.GetItemCount();
        for (int i = 0; i < itemCnt; i++)
        {
            text = mListCtrl.GetItemText(i, 2);
            if (text.Compare("No") == 0)
            {
                if (i == (lpLVCustomDraw->nmcd.dwItemSpec))
                {
                    lpLVCustomDraw->clrTextBk = RGB(255,50,50);
                    mListCtrl.GetItemRect(i,&rc,LVIR_BOUNDS);
                    mListCtrl.InvalidateRect(&rc, 0);
                }
            }
        }
        break;
    default: break;
    }

    *pResult = 0;
    *pResult |= CDRF_NOTIFYITEMDRAW;
    *pResult |= CDRF_NOTIFYSUBITEMDRAW;
    *pResult |= CDRF_NOTIFYPOSTPAINT;
}

Which results in:

result1

Within the CListCtrl, if I scroll to an area which isn't painted red, my application is fine.

I'm not too sure how to stop this from happening... maybe I need an alternative way of dynamically painting a row red?


EDIT: I have changed my code to the below, but am experiencing this issue (only 2nd and 3rd column being coloured, but when I redraw the list control i.e. scroll down then back up, all columns are then drawn):

result2

void CSPID_FQA_Test_ClientDlg::OnNMCustomdrawlistctrlvalues(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    int itemCnt = 0;
    CString text;
    RECT rc;

    CDC* pDC = CDC::FromHandle (lpLVCustomDraw->nmcd.hdc);

    switch(lpLVCustomDraw->nmcd.dwDrawStage)
    {
    case CDDS_ITEMPOSTPAINT:
    //case CDDS_ITEMPREPAINT:
    //case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
        //get each row text for 2nd column
        itemCnt = mListCtrl.GetItemCount();
        for (int i = 0; i < itemCnt; i++)
        {
            text = mListCtrl.GetItemText(i, 2);
            if (text.Compare("No") == 0)
            {
                if (i == (lpLVCustomDraw->nmcd.dwItemSpec))
                {
                    //lpLVCustomDraw->clrTextBk = RGB(255,50,50);
                    mListCtrl.GetItemRect(i,&rc,LVIR_BOUNDS);
                    pDC->FillSolidRect (&rc, RGB (0, 255, 0));

                    //mListCtrl.InvalidateRect(&rc, 0);
                }
            }
        }
        break;
    default: break;
    }

    *pResult = 0;
    *pResult |= CDRF_NOTIFYITEMDRAW;
    *pResult |= CDRF_NOTIFYSUBITEMDRAW;
    *pResult |= CDRF_NOTIFYPOSTPAINT;
}

EDIT2: I changed to the below code, but am still getting the same issue:

LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
CString text;
RECT rc;

CDC* pDC = CDC::FromHandle (lpLVCustomDraw->nmcd.hdc);

switch(lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_ITEMPOSTPAINT:

    mListCtrl.GetItemRect(lpLVCustomDraw->nmcd.dwItemSpec,&rc,LVIR_BOUNDS);
    text = mListCtrl.GetItemText(lpLVCustomDraw->nmcd.dwItemSpec, 2);

    if (text.Compare("No") == 0)
    {
        pDC->FillSolidRect (&rc, RGB(0, 0, 255));
    }

    break;
default: break;
}

result3

Upvotes: 1

Views: 4086

Answers (2)

mfc
mfc

Reputation: 565

Try this:

void ColorListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMLVCUSTOMDRAW *pNMLVCUSTOMDRAW = (NMLVCUSTOMDRAW *) pNMHDR;
    *pResult = CDRF_DODEFAULT;
    switch (pNMLVCUSTOMDRAW->nmcd.dwDrawStage)
    {   case CDDS_PREPAINT:
            *pResult |= CDRF_NOTIFYITEMDRAW;
            break;
        case CDDS_ITEMPREPAINT:
            // compare text in column 2
            // set background to special color if matches
            INT nItem = pNMLVCUSTOMDRAW->nmcd.dwItemSpec;
            CString strText = GetItemText(nItem, 2);
            if (strText==_T("No"))  pNMLVCUSTOMDRAW->clrTextBk = RGB(255,0,0);
            break;
    }
}

Upvotes: 1

rrirower
rrirower

Reputation: 4590

I think your design is slightly off by using CDDS_ITEMPREPAINT. I use custom painting to highlight a row in a CListCtrl to the color yellow. I do this by leveraging CDDS_ITEMPOSTPAINT and using the FillSolidRect and DrawText methods of the CDC. As Mark alluded to, some simple experimenting (with what I've posted in this answer) should help.

Edit: Using CDDS_ITEMPOSTPAINT, you can get the device context by

CDC*  pDC = CDC::FromHandle (pNMLVCD->nmcd.hdc);

Getting the row corodinates that you want to paint can be done by

GetItemRect (row, &rect, LVIR_BOUNDS);

Rendering the row color is done by

pDC->FillSolidRect (&rect, RGB (0, 255, 0));

And, lastly, draw each column text by

//  draw each column's text into the corresponding CRect.
for (col=0; col<nCols; ++col)
    {
    GetSubItemRect (row, col, LVIR_BOUNDS, rect);
    rect.left += 6;
    CString text = pRow->GetColumnText (col);
    pDC->DrawText (text, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    }

You'll need to make the necessary adjustments to the above code to meet your needs.

Upvotes: 1

Related Questions