转自:http://www.vckbase.com/index.php/wv/1548
- 文章概要:
- 本文对CListCtrl控件进行了一个扩展,使它即可以编辑主项(Item),又可以编辑子项(SubItem),并尽量符合CListCtrl的操作习惯。
-
一、说明
大家都知道在MFC中通过给CListCtrl设置LVS_EDITLABELS属性,并且在程序中响应控件的LVN_ENDLABELEDIT消息可以修改列表控件每一行的第一项,也就是主项(Item)。代码如下:
1.voidCEditListCtrlSampleDlg::OnEndlabeleditList1(NMHDR* pNMHDR,LRESULT* pResult)2.{3.LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;4.// TODO: Add your control notification handler code here5.6.*pResult = TRUE;//TRUE值表示可以修改主项,FALSE值表示不修改主项7.}但是让人郁闷的是,微软留了一手,CListCtrl不支持直接修改子项(SubItem)。无奈之下只好自力更生,对CListCtrl进行扩展。>_
二、原理
通过在浩如烟海的互联网上查找资料(当然包括了大名鼎鼎的VCKBASE),发现现有的实现大都是对子项鼠标单击一次就可以编辑。但本人对CListCtrl的单击一次高亮文本,再单击一次才开始编辑的操作模式感觉比较喜欢,所以就有了这篇文章的诞生。
要想实现高亮文本也就是对文本进行着色处理,这可以通过对NM_CUSTOMDRAW消息进行处理实现,但是类向导中没有这个消息映射只能进行手工添加。要想编辑文本则可以通过EditLabel(int nItem)成员函数以及对LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT的消息处理实现。
三、实现
本文最终实现的CEditListCtrl扩展类在尽量符合CListCtrl操作步骤的情况下实现对主项及子项的可编辑。
成员变量说明:
1.intm_iItem;//主项标识符2.intm_iSubItem;//子项标识符3.BOOLm_bFocus;//是否绘制项文本焦点框4.BOOLm_bHighLight;//是否高亮项文本5.CItemEdit m_edtItemEdit;//用于子类化EditLabel函数返回的CEdit*指针列表控件中所有项文本的绘制以及特效(焦点框、高亮)都在NM_CUSTOMDRAW消息处理中实现:
01.voidCEditListCtrl::OnCustomDraw(NMHDR* pNMHDR,LRESULT* pResult)02.{03.NMLVCUSTOMDRAW* pNMLVCustomDraw = (NMLVCUSTOMDRAW*)pNMHDR;04.05.// Take the default processing unless we set this to something else below.06.*pResult = CDRF_DODEFAULT;07.08.// First thing - check the draw stage. If it's the control's prepaint09.// stage, then tell Windows we want messages for every item.10.11.if(pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)12.{13.*pResult = CDRF_NOTIFYITEMDRAW;14.}15.elseif(pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)16.{17.// This is the notification message for an item. We'll request18.// notifications before each subitem's prepaint stage.19.*pResult = CDRF_NOTIFYSUBITEMDRAW;20.}21.elseif(pNMLVCustomDraw->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))22.{23.//当前要绘制的主项标识符和子项标识符24.intiItem = (int)pNMLVCustomDraw->nmcd.dwItemSpec;25.intiSubItem = pNMLVCustomDraw->iSubItem;26.27.CDC* pDC = CDC::FromHandle(pNMLVCustomDraw->nmcd.hdc);28.29.CString strItemText = GetItemText(iItem, iSubItem);30.CRect rcItem, rcText;31.GetSubItemRect(iItem, iSubItem, LVIR_LABEL, rcItem);32.rcText = rcItem;33.34.CSize size = pDC->GetTextExtent(strItemText);35.if(strItemText == _T(""))36.{37.size.cx = 41;38.}39.40.//设置文本高亮矩形41.rcText.left += 4;42.rcText.right = rcText.left + size.cx + 6;43.if(rcText.right > rcItem.right)44.{45.rcText.right = rcItem.right;46.}47.48.COLORREFcrOldTextColor = pDC->GetTextColor();49.50.//绘制项焦点/高亮效果51.if(m_bFocus)52.{53.if((m_iItem == iItem) && (m_iSubItem == iSubItem))54.{55.if(m_bHighLight)56.{57.pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));58.pDC->FillSolidRect(&rcText, ::GetSysColor(COLOR_HIGHLIGHT));59.}60.pDC->DrawFocusRect(&rcText);61.}62.}63.64.//绘制项文本65.rcItem.left += 6;66.pDC->DrawText(strItemText, &rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP);67.68.pDC->SetTextColor(crOldTextColor);69.*pResult = CDRF_SKIPDEFAULT;// We've painted everything.70.}71.}单击一次文本高亮,再单击一次文本开始编辑在WM_LBUTTONDOWN消息处理中实现:
01.voidCEditListCtrl::OnLButtonDown(UINTnFlags, CPoint point)02.{03.m_bFocus = TRUE;04.LVHITTESTINFO lvhit;05.lvhit.pt = point;06.intitem = SubItemHitTest(&lvhit);07.08.//if (over a item/subitem)09.if(item != -1 && (lvhit.flags & LVHT_ONITEM))10.{11.CListCtrl::OnLButtonDown(nFlags, point);12.13.if(m_bHighLight && m_iItem == lvhit.iItem && m_iSubItem == lvhit.iSubItem)14.{15.//第二次单击16.EditLabel(m_iItem);17.return;18.}19.else20.{21.//第一次单击22.m_iItem = lvhit.iItem;23.m_iSubItem = lvhit.iSubItem;24.m_bHighLight = TRUE;25.}26.}27.else28.{29.if(m_edtItemEdit.m_hWnd == NULL)30.{31.//未出现文本编辑框时32.m_bHighLight = FALSE;33.}34.35.CListCtrl::OnLButtonDown(nFlags, point);36.}37.38.Invalidate();//强制重绘控件39.}关键的一步,对项文本进行编辑。在以上代码中当执行到EditLabel时将会产生一个编辑框,这时需要将它进行子类化处理,以控制它出现的位置。
01.voidCEditListCtrl::OnBeginlabeledit(NMHDR* pNMHDR,LRESULT* pResult)02.{03.LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;04.05.if(m_iSubItem >= 0)06.{07.ASSERT(m_iItem == pDispInfo->item.iItem);08.CRect rcSubItem;09.GetSubItemRect( pDispInfo->item.iItem, m_iSubItem, LVIR_BOUNDS, rcSubItem);10.11.//get edit control and subclass12.HWNDhWnd= (HWND)SendMessage(LVM_GETEDITCONTROL);13.ASSERT(hWnd != NULL);14.VERIFY(m_edtItemEdit.SubclassWindow(hWnd));15.16.//move edit control text 4 pixel to the right of org label,17.//as Windows does it...编辑框定位18.m_edtItemEdit.m_iXPos = rcSubItem.left + 4;19.m_edtItemEdit.SetWindowText(GetItemText(pDispInfo->item.iItem, m_iSubItem));20.}21.22.*pResult = 0;23.}24.25.voidCEditListCtrl::OnEndlabeledit(NMHDR* pNMHDR,LRESULT* pResult)26.{27.LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;28.LV_ITEM *plvItem = &pDispInfo->item;29.30.if(m_iSubItem >= 0)31.{32.if(plvItem->pszText != NULL )33.{34.SetItemText(plvItem->iItem,m_iSubItem, plvItem->pszText);35.}36.37.VERIFY(m_edtItemEdit.UnsubclassWindow()!=NULL);38.*pResult = 0;39.}40.41.//编辑文本时对控件父窗口操作(如单击其它控件)引发"OnEndlabeledit"时刷新控件42.CRect rect;43.GetWindowRect(&rect);44.CPoint point;45.::GetCursorPos(&point);46.if(!rect.PtInRect(point))47.{48.m_iItem = -1;49.m_iSubItem = -1;50.m_bFocus = FALSE;51.m_bHighLight = FALSE;52.}53.}通过以上三个步骤大体实现了本文要达到的目的,但是还不能放松。接下来还要进行一些显示细节方面的处理。这包括对WM_PAINT、WM_SETFOCUS和WM_KILLFOCUS消息的处理,限于篇幅,就不进行细讲了,有兴趣的朋友可以查看本文提供的源代码。最后实现的效果如下图所示:

四、参考资料
1. Simplified Subitem Editing
http://www.codeguru.com/cpp/controls/listview/editingitemsandsubitem/article.php/c4175/
2. 可设置单元格颜色的ClistCtrl类
http://www.vckbase.com/document/viewdoc/?id=891
本文介绍如何扩展CListCtrl控件以实现主项和子项的编辑功能,通过响应LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT消息完成编辑流程。
592

被折叠的 条评论
为什么被折叠?



