专题1: 如何使CListCtrl完全可编辑? 1. 背景 : 我们知道如果CListCtrl是报表样式,那么CListCtrl所提供的编辑功能只局限于第一列.也就是说只有第一列可编辑.这样显然无法满足一般数据库的要求.我们想要每个子项都能编辑. 2. 思路 : CEdit是一个很好的可控制编辑控件.如何把CEdit和我们的CListCtrl联系起来?一种很好的想法是------一般我们如果想编辑某一项,那么就应该去双击.双击以后就让CEdit在那里显示,当然要把大小调整和子项表格一样.如果CEdit失去了焦点,表示修改完毕,那么立即更改子项的数据,同时让CEdit隐藏.因为每次只能编辑一项,所以只需要一个CEdit就够了. 3. 方法: (1) 首先从CListCtrl派生一个类,其他已经有的变量或者函数设置我已经介绍,如果不清楚的读者,可以去参考”基础篇”. (2) 有一点可以肯定,我们必须响应双击事件: void Cmylist::OnLButtonDblClk(UINT nFlags, CPoint point) { int index;//行号 int colnum;//列号 GetWindowRect(r);//稍后说明 GetParent()->ScreenToClient(r);//稍后说明 if((index=HitTestEx(point,&colnum))!=-1) EditSubItem(index,colnum); CListCtrl::OnLButtonDblClk(nFlags, point); } 其中HitTestEx是用来求出双击点所在的行列号,如果行号不为-1,那么就调用函数EditSubItem. 这个函数会根据行列号求出该子项具体坐标,方便CEdit调整位置. (3) 如何求出行列号?行号是很好求出来的 ,但是列号就不是很简单了,必须详细判断. int Cmylist::HitTestEx(CPoint &point, int *pcolumn) { int columnNum=0; //获取页面内首行索引号,不一定是0,要考虑滚动条的情况 int row=GetTopIndex(); // GetCountPerPage()获取在页面内行的总数 int bottom=row+this->GetCountPerPage(); // 防止超出范围 if(bottom>this->GetItemCount()) bottom=GetItemCount(); //获取列的总数 int ncolumncount=this->GetHeaderCtrl()->GetItemCount(); //可以肯定双击点肯定在页面内,因此从页面首行索引号开始判断 for(;row<=bottom;++row) { CRect rect; //求出行的rect GetItemRect(row,&rect,LVIR_BOUNDS); //点是否在行的矩形内 if(rect.PtInRect(point)) //如果点在行的矩形内,求出点在哪一列 for(columnNum=0;columnNum<ncolumncount;columnNum++) { //求出列的宽度 int colwidth=this->GetColumnWidth(columnNum); if(point.x>=rect.left&&point.x<=(rect.left+colwidth)) { *pcolumn=columnNum; return row; } rect.left+=colwidth; } } return -1; } 当然上面那种方法有点复杂,是完全从头开始判断.其实我们可以先利用CListCtrl提供的函数求出行号,再求列号,这样稍微简单点 int Cmylist::HitTestEx(CPoint &point, int *pcolumn) { int columnNum=0; int row=HitTest(point);//求出行号 int ncolumncount=this->GetHeaderCtrl()->GetItemCount(); (2007.1.3更新) LVHITTESTINFO Info; Info.pt=point; this->SubItemHitTest(&Info); *pcolumn=Info.iSubItem; if(*pcolumn>=0&&*pcolumn<ncolumncount) return row; else return -1; /* int ncolumncount=this->GetHeaderCtrl()->GetItemCount(); CRect rect; GetItemRect(row,&rect,LVIR_BOUNDS); if(rect.PtInRect(point)) for(columnNum=0;columnNum<ncolumncount;columnNum++) { int colwidth=this->GetColumnWidth(columnNum); if(point.x>=rect.left&&point.x<=(rect.left+colwidth)) { *pcolumn=columnNum; return row; } rect.left+=colwidth; }*/ } (4) 求出具体CEdit移动坐标 int Cmylist::Item_X(int row, int column,CRect& rect_X) { int offset=0; for(int i=0;i<column;i++) offset+=GetColumnWidth(i); CRect rect; GetItemRect(row,rect,LVIR_BOUNDS); //注意水平滚动条的影响,如果已经移动了水平滚动条,可能left为0,或者超出总大小 if(offset+rect.left<0||offset+rect.left>client_rect.right) { CSize size; //offset肯定为正,如果出现了rect.left为负 if(offset+rect.left>0) size.cx=- (offset+rect.left); else size.cx=offset+rect.left; size.cy=0;//垂直不用管 //如果某一列的一半在滚动条左边,一半在右边,就再次调整滚动条的位置. Scroll(size); rect.left - =size.cx; } rect.left+=offset+2; rect.right=rect.left+GetColumnWidth(column)-2; //bottom和top不用管 rect_X=rect; return rect.right; } (5) 移动CEdit void Cmylist::EditSubItem(int Item, int Column) { CRect rect; //求出行列所在rect this->Item_X(Item,Column,rect); EditCellShow(rect,Item,Column,r); } void Cmylist::EditCellShow(CRect rect, int Item, int Column,CRect r) { //还记得r吗?在开始的双击函数OnLButtonDblClk中,它是CListCtrl在父窗口中的位置 rect.left+=r.left; rect.top+=r.top+2; rect.right+=r.left; rect.bottom+=r.top+2; //pedit是CEdit对象的指针,提供接口,只要在程序中让pedit指向一个对象即可 pedit->MoveWindow(rect,TRUE); pedit->ShowWindow(TRUE); pedit->SetFocus(); } ^_^!这样就完成了.效果还可以.当然你还要去响应CEdit失去焦点和得到焦点的事件.这个就不是我的任务了,因为每个人的要求不一样啊! 看看我的效果!

评论