一、引言
列表控件主要用来以各种方式显示一组数据记录供用户进行各种操作,是最常见的控件之一。WindowsXP资源管理器中的“查看”菜单下的 “图标|平铺|列表|详细信息”就是一个非常典型的应用。MFC中的CListCtrl类提供了对列表控件操作的基本方法,包括插入一个新的项目InsertItem、删除一个项目DeleteItem、排序项目SortItems等,但CListCtrl类不支持对项目的直接编辑,只能用SetItem或者SetItemText来输入数据,使用起来非常不方便。下面介绍在VC++6.0中以CListCtrl类为基类构建可直接编辑的列表控件类的方法,并提供一个可运行的实例。
二、实现方法
以在列表控件中实现CEditBox的直接编辑为例,介绍构建可直接编辑的列表控件类CEditListCtrl的方法:
在CEditListCtrl类中设置一个指向编辑框控件的指针m_edit,当列表控件中的一个子项目被两次不连续的单击后,使用m_edit在该子项目处创建一个编辑框,编辑框的大小与子项目的大小一致。当向编辑框中输入数据后,单击列表控件,便可将编辑框中的数据写回对应的子项目,同时释放m_edit所占用的空间。
当列表控件中的项目通过滚动条滚动时,某一子项目恰好处于编辑状态,则此时该子项目所对应的编辑框也要跟着滚动。为此,在CEditListCtrl类中添加WM_HSCROLL和 WM_VSCROLL消息处理函数,实现编辑框控件和其所对应的子项目的同步滚动。
在使用列表控件显示数据时,往往只需要编辑部分列中的数据,有些列的数据则不需要进行编辑。为此,在CEditListCtrl类中添加一个数据成员BOOL *m_isedit,m_isedit是一个数组,用于标志某一列是否可以进行直接编辑,若m_isedit[i]为TRUE,则第i列可以进行直接编辑,否则,反之。CEditListCtrl类中新增的成员函数SetEditColomn用于设置m_isedit的值。
以上方法也可用于在列表控件中实现ComboBox、DropdownList、CheckBox、PushButton等的直接编辑。
三、CEditListCtrl类的定义和实现
在VC++6.0中创建一个基于对话框的工程,点击菜单“插入->类”,在弹出的对话框中设置基类为CLlistCtrl,派生类为CEditListCtrl。在CEditListCtrl类中定义四个数据成员:
CEdit * m_edit;//指向编辑框,初值为NULL
BOOL * m_isedit;//允许进行编辑的列,初值为NULL
int m_item;//当前编辑的行号,初值为-1
int m_subitem;//当前编辑的列号,初值为-1
定义一个常量ID_LISTEDIT作为m_edit所指向的编辑框的ID号。在CEditListCtrl中添加NM_CLICK的消息处理函数,实现代码如下:
void CEditListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
if(!m_isedit) return;
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(!m_edit)
{
m_subitem=pNMListView->iSubItem;
if(pNMListView->iItem!=m_item)
{
m_item=pNMListView->iItem;//标志被单击的项目
return;
}
}
if(!m_isedit[m_subitem])//若当前列不允许直接编辑,则返回
return;
RECT m_itemrect,m_r;
GetItemRect(m_item ,&m_itemrect,2);
GetItemRect(0 ,&m_r,2);
int m_height=m_itemrect.bottom -m_itemrect.top ;
int x=m_r.left ,y=m_r.top,cx,cy;//(x,y,cx,cy)为编辑框显示的位置
for(int i=0;i< m_item;i++)
y+=m_height;
cy=y+m_height;
for(i=0;i<m_subitem;i++)
x+=GetColumnWidth(i);
cx=x+GetColumnWidth(i);
if(m_edit)//若编辑框已存在。
{
CString s1;
s1.Format ("%d %d %d %d",x,y,cx,cy);
m_edit->MoveWindow(x,y,cx-x,cy-y);//移动到当前子项目的位置。
Invalidate();//刷新屏幕。
return;
}
//若编辑框不存在,则创建编辑框,并在当前子项目处显示编辑框。
CRect rect(x,y,cx,cy);
m_edit=new CEdit();
m_edit->Create (WS_CHILD|WS_VISIBLE|WS_BORDER,rect,this,ID_LISTEDIT);
CString str=GetItemText (pNMListView->iItem,pNMListView->iSubItem);
m_edit->UpdateData(0);
m_edit->SetWindowText(str);
DWORD dwSel = m_edit->GetSel();
m_edit->SetSel(HIWORD(dwSel), -1);
m_edit->ShowWindow (SW_SHOW);//显示编辑框。
m_edit->SetFocus ();
*pResult = 0;
}
在CEditListCtrl中添加NM_SETFOCUS的消息处理函数,实现代码如下:
void CEditListCtrl::OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult)
{
if(m_edit)
{//将编辑框中的数据写回对应的子项目中
UpdateData( );
CString str;
m_edit->GetWindowText(str);
SetItemText(m_item,m_subitem,str);
delete m_edit;
m_edit=NULL;
}
*pResult = 0;
}
添加WM_HSCROLL和 WM_VSCROLL的消息处理函数,实现代码如下:
void CEditListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{//水平滚动时,移动已显示的编辑框。
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
LRESULT* pResult=new LRESULT;
if(m_edit) OnClick((NMHDR*)this,pResult) ;
}
void CEditListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{//垂直滚动时,移动已显示的编辑框。
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
LRESULT* pResult=new LRESULT;
if(m_edit)
{
RECT m_itemrect,m_headrect;
GetItemRect(m_item ,&m_itemrect,2);
GetHeaderCtrl()->GetWindowRect(&m_headrect);
if(m_itemrect.top<m_headrect.bottom-m_headrect.top)
{
RECT m_rect;
m_edit->GetWindowRect(&m_rect);
m_edit->MoveWindow(m_rect.left,-(m_rect.bottom-m_rect.top),m_rect.right,0);
}
else
{
OnClick((NMHDR*)this,pResult) ;
}
}
}
添加SetEditColomn函数,实现代码如下:
void CEditListCtrl::SetEditColomn(int col,BOOL edit)
{//设置允许直接进行编辑的列
if(!m_isedit)
{
int len=GetHeaderCtrl()->GetItemCount();
m_isedit=new BOOL[len];
for(int i=0;i<len;i++)//初始化m_isedit。
m_isedit[i]=FALSE;
}
m_isedit[col]=edit;
}
重载DestroyWindow()函数,实现代码如下:
BOOL CEditListCtrl::DestroyWindow()
{
delete [] m_isedit;
return CListCtrl::DestroyWindow();
}
四、CEditListCtrl类的测试
在上述工程的主对话框中添加一个ListCtrl控件,设置控件的显示方式为LVS_REPORT方式,给该控件定义一个CEditListCtrl类型的对象m_list,再在对话框的初始化函数中添加初始化代码:
m_list.InsertColumn (0,"学号", LVCFMT_LEFT,50);
m_list.InsertColumn (1,"姓名", LVCFMT_LEFT,90);
m_list.InsertColumn (2,"年龄", LVCFMT_LEFT,50);
m_list.InsertItem (0,"1");
m_list.SetItemText (0,1,"王明");
m_list.SetItemText (0,2,"23");
m_list.InsertItem (1,"2");
m_list.SetItemText (1,1,"赵可");
m_list.SetItemText (1,2,"20");
m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT);
m_list.SetEditColomn (0,TRUE);//允许第0列可直接编辑
m_list.SetEditColomn (1,TRUE); //允许第1列可直接编辑
m_list.SetEditColomn (2,TRUE); //允许第2列可直接编辑
本文中的编码在Window2000中调试通过,运行结果:当两次不连续地单击列表控件中的一个子项目后,该子项目便呈现编辑状态,如下图所示。向编辑框中输入数据后单击列表控件,便完成对子项目的编辑。
程序运行效果
五、结语
本文介绍了在VC++6.0下以CListCtrl类为基类,构建可直接编辑的列表控件类的方法,非常方便地实现了对列表控件的直接编辑。本文介绍的方法也可用于实现对树型控件的直接编辑。