可编辑子项的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 subclass
		HWND 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值