b2b模式分析:可编辑子项的CListCtrl类

来源:百度文库 编辑:中财网 时间:2024/04/28 18:08:23

可编辑子项的CListCtrl类
作者:Nobita

下载源代码

关键词:可编辑子项 CListCtrl

摘 要:本文对CListCtrl控件进行了一个扩展,使它即可以编辑主项(Item),又可以编辑子项(SubItem),并尽量符合CListCtrl的操作习惯。

一、说明
大家都知道在MFC中通过给CListCtrl设置LVS_EDITLABELS属性,并且在程序中响应控件的LVN_ENDLABELEDIT消息可以修改列表控件每一行的第一项,也就是主项(Item)。代码如下:

void CEditListCtrlSampleDlg::OnEndlabeleditList1(NMHDR* pNMHDR, LRESULT* pResult){LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;// TODO: Add your control notification handler code here*pResult = TRUE;   //TRUE值表示可以修改主项,FALSE值表示不修改主项}
但是让人郁闷的是,微软留了一手,CListCtrl不支持直接修改子项(SubItem)。无奈之下只好自力更生,对CListCtrl进行扩展。>_
二、原理
通过在浩如烟海的互联网上查找资料(当然包括了大名鼎鼎的VCKBASE),发现现有的实现大都是对子项鼠标单击一次就可以编辑。但本人对CListCtrl的单击一次高亮文本,再单击一次才开始编辑的操作模式感觉比较喜欢,所以就有了这篇文章的诞生。
要想实现高亮文本也就是对文本进行着色处理,这可以通过对NM_CUSTOMDRAW消息进行处理实现,但是类向导中没有这个消息映射只能进行手工添加。
要想编辑文本则可以通过EditLabel(int nItem)成员函数以及对LVN_BEGINLABELEDIT和LVN_ENDLABELEDIT的消息处理实现。
三、实现
本文最终实现的CEditListCtrl扩展类在尽量符合CListCtrl操作步骤的情况下实现对主项及子项的可编辑。
成员变量说明:

 

int m_iItem; //主项标识符
int m_iSubItem; //子项标识符
BOOL m_bFocus; //是否绘制项文本焦点框
BOOL m_bHighLight; //是否高亮项文本
CItemEdit m_edtItemEdit; //用于子类化EditLabel函数返回的CEdit*指针

列表控件中所有项文本的绘制以及特效(焦点框、高亮)都在NM_CUSTOMDRAW消息处理中实现:

void CEditListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult){NMLVCUSTOMDRAW* pNMLVCustomDraw = (NMLVCUSTOMDRAW*)pNMHDR;// Take the default processing unless we set this to something else below.*pResult = CDRF_DODEFAULT;// First thing - check the draw stage. If it's the control's prepaint// stage, then tell Windows we want messages for every item.if (pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT){*pResult = CDRF_NOTIFYITEMDRAW;}else if (pNMLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT){// This is the notification message for an item.  We'll request// notifications before each subitem's prepaint stage.*pResult = CDRF_NOTIFYSUBITEMDRAW;}else if (pNMLVCustomDraw->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM)){//当前要绘制的主项标识符和子项标识符int iItem = (int)pNMLVCustomDraw->nmcd.dwItemSpec;int iSubItem = pNMLVCustomDraw->iSubItem;CDC* pDC = CDC::FromHandle(pNMLVCustomDraw->nmcd.hdc);CString strItemText = GetItemText(iItem, iSubItem);CRect rcItem, rcText;GetSubItemRect(iItem, iSubItem, LVIR_LABEL, rcItem);rcText = rcItem;CSize size = pDC->GetTextExtent(strItemText);if(strItemText == _T("")){size.cx = 41;}//设置文本高亮矩形rcText.left += 4;rcText.right = rcText.left + size.cx + 6;if(rcText.right > rcItem.right){rcText.right = rcItem.right;}COLORREF crOldTextColor = pDC->GetTextColor();//绘制项焦点/高亮效果if(m_bFocus){if((m_iItem == iItem) && (m_iSubItem == iSubItem)){if(m_bHighLight){pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));pDC->FillSolidRect(&rcText, ::GetSysColor(COLOR_HIGHLIGHT));}pDC->DrawFocusRect(&rcText);}}//绘制项文本rcItem.left += 6;pDC->DrawText(strItemText, &rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP);pDC->SetTextColor(crOldTextColor);*pResult = CDRF_SKIPDEFAULT;// We've painted everything.}}
单击一次文本高亮,再单击一次文本开始编辑在WM_LBUTTONDOWN消息处理中实现:
void CEditListCtrl::OnLButtonDown(UINT nFlags, CPoint point){m_bFocus = TRUE;LVHITTESTINFO  lvhit;lvhit.pt = point;int item = SubItemHitTest(&lvhit);//if (over a item/subitem)if (item != -1 && (lvhit.flags & LVHT_ONITEM)){CListCtrl::OnLButtonDown(nFlags, point);if(m_bHighLight && m_iItem == lvhit.iItem && m_iSubItem == lvhit.iSubItem){//第二次单击EditLabel(m_iItem);return;}else{//第一次单击m_iItem = lvhit.iItem;m_iSubItem = lvhit.iSubItem;m_bHighLight = TRUE;}}else{if(m_edtItemEdit.m_hWnd == NULL){//未出现文本编辑框时m_bHighLight = FALSE;}CListCtrl::OnLButtonDown(nFlags, point);}Invalidate(); //强制重绘控件}
关键的一步,对项文本进行编辑。在以上代码中当执行到EditLabel时将会产生一个编辑框,这时需要将它进行子类化处理,以控制它出现的位置。
void CEditListCtrl::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult){LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;if (m_iSubItem >= 0){ASSERT(m_iItem == pDispInfo->item.iItem);CRect  rcSubItem;GetSubItemRect( pDispInfo->item.iItem, m_iSubItem, LVIR_BOUNDS, rcSubItem);//get edit control and subclassHWND hWnd= (HWND)SendMessage(LVM_GETEDITCONTROL);ASSERT(hWnd != NULL);VERIFY(m_edtItemEdit.SubclassWindow(hWnd));//move edit control text 4 pixel to the right of org label,//as Windows does it...编辑框定位m_edtItemEdit.m_iXPos = rcSubItem.left + 4;m_edtItemEdit.SetWindowText(GetItemText(pDispInfo->item.iItem, m_iSubItem));}*pResult = 0;}void CEditListCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult){LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;LV_ITEM*plvItem = &pDispInfo->item;if (m_iSubItem >= 0){if (plvItem->pszText != NULL ){SetItemText(plvItem->iItem,m_iSubItem, plvItem->pszText);}VERIFY(m_edtItemEdit.UnsubclassWindow()!=NULL);*pResult = 0;}//编辑文本时对控件父窗口操作(如单击其它控件)引发"OnEndlabeledit"时刷新控件CRect rect;GetWindowRect(&rect);CPoint point;::GetCursorPos(&point);if(!rect.PtInRect(point)){m_iItem = -1;m_iSubItem = -1;m_bFocus = FALSE;m_bHighLight = FALSE;}}
通过以上三个步骤大体实现了本文要达到的目的,但是还不能放松。接下来还要进行一些显示细节方面的处理。这包括对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