Click here to Skip to main content
15,881,172 members
Articles / Desktop Programming / MFC
Article

SubItem Selection in List Control

Rate me:
Please Sign up or sign in to vote.
4.62/5 (15 votes)
26 Dec 2004 123.2K   3.2K   42   8
SubItem selection in List Control.

Introduction

Muhammad Azam in his article 'SubItem Selection in List Control' showed how you can select subitem in CListCtrl by setting style LVS_OWNERDRAWFIXED and implementing DrawItem function. I'd like to show that a similar thing can be done using custom draw which is considered to be simpler.

The code

In CXListCtrl class derived from CListCtrl, I implemented NM_CUSTOMDRAW message because I wanted selected subitems to be drawn, NM_CLICK message to select subitem by mouse, and LVN_KEYDOWN to select subitem by keyboard.

  • int m_sel_row; to keep track of selected row
  • int m_sel_col; to keep track of selected column
  • BOOL m_bCustomDraw; e.g., to switch off custom draw
  • int m_nNumberOfRows; number of rows in ListCtrl
  • int m_nNumberOfCols; number of columns in ListCtrl

XListCtrl.h

//{{AFX_MSG(CXListCtrl)
  afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult);
  afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
afx_msg void custom_draw_funtion(NMHDR *pNMHDR, LRESULT *pResult);

XListCtrl.cpp

BEGIN_MESSAGE_MAP(CXListCtrl, CListCtrl)
    //{{AFX_MSG_MAP(CXListCtrl)
    ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
    ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown)
    //}}AFX_MSG_MAP
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW,custom_draw_funtion)
END_MESSAGE_MAP()
void CXListCtrl::custom_draw_funtion(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLVCUSTOMDRAW* nmcd=(NMLVCUSTOMDRAW*)pNMHDR;
    *pResult=CDRF_DODEFAULT;

    int row; 
    int col;
    switch(nmcd->nmcd.dwDrawStage)
    {
        case CDDS_PREPAINT:
            if(m_bCustomDraw) 
                *pResult=CDRF_NOTIFYITEMDRAW; 
            // else CDRF_DODEFAULT which tell windows to paint itself
            return;

        case CDDS_ITEMPREPAINT:
            *pResult=CDRF_NOTIFYSUBITEMDRAW;
            return;

        case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
        {
            *pResult=0;
            row=nmcd->nmcd.dwItemSpec;
            col=nmcd->iSubItem;

            CString str=GetItemText(row,col);

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

            if(col>0)
                GetSubItemRect(row,col,LVIR_BOUNDS,rect);
            else
                GetItemRect(row,&rect,LVIR_LABEL);

            UINT uCode=DT_LEFT;

            if(row==m_sel_row && col==m_sel_col)
            {
                COLORREF kolor=0xaa00aa;

                if(GetFocus()==this)
                    kolor=0x0000ff;

                CBrushbrush(kolor);
                pDC->FillRect(&rect,&brush);
            }
            rect.OffsetRect(5,0);
            pDC->DrawText(str,&rect,uCode);

            *pResult=CDRF_SKIPDEFAULT;

            break;
        }
    }
}
void CXListCtrl::invalidate_grid(int row, int col)
{   //I add this function to reduce flickering
    CRect r;

    if(col==0)
        GetItemRect(row,&r,LVIR_LABEL);
    else
        GetSubItemRect(row,col,LVIR_BOUNDS,r);

    InvalidateRect(&r);
}
void CXListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMITEMACTIVATE* nm=(NMITEMACTIVATE*)pNMHDR;

    invalidate_grid(m_sel_row,m_sel_col);//to clear old selection
    m_sel_row=nm->iItem;
    m_sel_col=nm->iSubItem;
    invalidate_grid(m_sel_row,m_sel_col);

    *pResult = 0;
}
void CXListCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMLVKEYDOWN* nmkd = (NMLVKEYDOWN*)pNMHDR;

    switch(nmkd->wVKey)
    {
        case VK_LEFT:
            m_sel_col--;
            if(m_sel_col<0)
                m_sel_col=0;
            invalidate_grid(m_sel_row,m_sel_col+1);
            break;
        case VK_RIGHT:
            m_sel_col++;
            if(m_sel_col>m_nNumberOfCols-1)
                m_sel_col=m_nNumberOfCols-1;
            invalidate_grid(m_sel_row,m_sel_col-1);
            break;

        case VK_UP:
            m_sel_row--;
            if(m_sel_row<0)
                m_sel_row=0;
            invalidate_grid(m_sel_row+1,m_sel_col);
            break;

        case VK_DOWN: 
            m_sel_row++;
            if(m_sel_row>m_nNumberOfRows-1)
                m_sel_row=m_nNumberOfRows-1;
            invalidate_grid(m_sel_row-1,m_sel_col);
            break;
        case VK_PRIOR:
            invalidate_grid(m_sel_row,m_sel_col);
            m_sel_row=0;
            break;
        case VK_NEXT:
            invalidate_grid(m_sel_row,m_sel_col);
            m_sel_row=m_nNumberOfRows-1;
            break;
        case VK_HOME:
            invalidate_grid(m_sel_row,m_sel_col);
            m_sel_col=0;

            if(GetKeyState(VK_CONTROL)<0)
                m_sel_row=0;

            SetItemState(m_sel_row,LVIS_FOCUSED,LVIS_FOCUSED);
            *pResult = CDRF_SKIPDEFAULT;
            invalidate_grid(m_sel_row,m_sel_col);
            return;
            break;
        case VK_END:
            invalidate_grid(m_sel_row,m_sel_col);
            m_sel_col=m_nNumberOfCols-1;
            if(GetKeyState(VK_CONTROL)<0)
                m_sel_row=m_nNumberOfRows-1;

            SetItemState(m_sel_row,LVIS_FOCUSED,LVIS_FOCUSED);

            *pResult=CDRF_SKIPDEFAULT;
            invalidate_grid(m_sel_row,m_sel_col);
            return;
    }
    *pResult = 0;
}

Points of Interest

In OnKeydown function:

  • SetItemState(m_sel_row,LVIS_FOCUSED,LVIS_FOCUSED);
  • *pResult=CDRF_SKIPDEFAULT;

change default responding CListCtrl on messages VK_HOME and VK_END.

Demo project

Demo project is a dialog based application with CListCtrl control and CXListCtrl class derived from CListCtrl.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Europe Europe
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionSimple way to accomplish subItem select Pin
mvadu18-Nov-11 15:39
mvadu18-Nov-11 15:39 
We can get same result in a much simpler way, just set the text color and background color, and return CDRF_NEWFONT. ListView will takecare of actual painting. No need to get the item area, filling rectangles or drawing text. Big Grin | :-D

C++
LRESULT listView::ListViewDrawSelection(NMLVCUSTOMDRAW &customDraw)
{
	if(m_SelectionStyle == wholeRow)
		return CDRF_DODEFAULT;

	//NMLVCUSTOMDRAW customDraw = *nmh;
	switch(customDraw.nmcd.dwDrawStage)
	{
	case CDDS_PREPAINT:
		return CDRF_NOTIFYITEMDRAW;
	
	case CDDS_ITEMPREPAINT:
		return CDRF_NOTIFYSUBITEMDRAW;

	case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
		{
			int iRow, iCol;
			iRow = customDraw.nmcd.dwItemSpec;
			iCol = customDraw.iSubItem;			
			//if this is selected subItem then chnage it to selected color
			if(m_SelCol == iCol && m_SelRow == iRow)
			{
				customDraw.clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
				customDraw.clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
				return CDRF_NEWFONT;
			}
            break;
		}
	}
	return CDRF_DODEFAULT;
};

Regards,
Adarsha

GeneralI have made changes to enable multiple selection. However I am having problems that it will not redraw it correctly Pin
MeanKhoo29-Sep-08 11:43
MeanKhoo29-Sep-08 11:43 
QuestionLVN_ITEMCHANGED issue? Pin
Maverick24-Feb-05 1:17
Maverick24-Feb-05 1:17 
AnswerRe: LVN_ITEMCHANGED issue? Pin
Piotr Szewczyk24-Feb-05 11:49
Piotr Szewczyk24-Feb-05 11:49 
GeneralRe: LVN_ITEMCHANGED issue? Pin
Maverick11-Jul-05 6:03
Maverick11-Jul-05 6:03 
GeneralRe: LVN_ITEMCHANGED issue? Pin
Michael Pauli24-Jul-07 17:09
Michael Pauli24-Jul-07 17:09 
QuestionIssues? Pin
Maverick23-Feb-05 3:51
Maverick23-Feb-05 3:51 
Generalmarvelous Pin
Muhammad Azam16-Jan-05 22:10
Muhammad Azam16-Jan-05 22:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.