http://www.programfan.com/blog/article.asp?id=21881
CListCtrl学习笔记(2)---中级篇(1)(2006-12-24 14:53:00)
专题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失去焦点和得到焦点的事件.这个就不是我的任务了,因为每个人的要求不一样啊!
看看我的效果!

阅读(2085) | 评论(4)| 复制链接
版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!
本文介绍了如何通过自定义CListCtrl派生类实现列表视图的完全可编辑功能,即不仅限于第一列可编辑,而是所有列均可进行编辑操作。通过双击事件触发编辑模式,并使用CEdit控件来完成具体的编辑工作。
1464







评论
评论人: zzy 发布时间: 2007-9-2 11:56:00-
r 是怎么定义的?
评论人: 男人 发布时间: 2007-3-20 14:33:00-
多谢楼主!我编程时正好想实现这个功能,祝愿你能写出更多精彩的文章!
评论人: flychj 发布时间: 2007-1-2 20:08:00-
这玩意好!妙!
评论人: fly 发布时间: 2007-1-2 13:55:00-
其实DATAGRID可以代替你写的这个控件!但是自己写的控件用起来也就更灵活,也更方便我们的编程!你写的这个控件用处是很大的,很多地方都可以用![佩服!我得好好学学!!