SubItem Selection in List Control






4.62/5 (14 votes)
Dec 27, 2004

124599

3176
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 rowint m_sel_col;
to keep track of selected columnBOOL m_bCustomDraw;
e.g., to switch off custom drawint m_nNumberOfRows;
number of rows inListCtrl
int m_nNumberOfCols;
number of columns inListCtrl
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
.