赞
踩
1.基本原理
在列表控件上实现可编辑功能的原理非常简单,借助一个编辑框控件即可达到目的。具体步骤如下:①从 CListCtrl派生一个子类,并拦截某个意味着进入编辑状态的消息,获取需要编辑的数据项的相关信息。所拦截的消息通常选择鼠标消息(例如双击),这样更容易确定数据项在列表控件中的位置(行号、列号)及其所占的区域。②将一个编辑框控件移动到待编辑数据项所在的区域上,装入待编辑的数据并显示出来,供用户进行修改。③编辑结束后将修改后的数据返回给列表控件,让其在对应的子项上显示新的数据。
2.实现过程
1)在VC 6.0中,新建一个基于对话框的项目,名称:Exam02。
2)编辑对话框资源,删除IDOK 按钮和静态标签;保留IDCANCEL按钮,将其标题改为“退出”;添加一个列表控件,将其显示风格改为report。利用类向导为列表控件添加一个关联变量m_list(Type:CListCtrl)。在CExam02Dlg::OnInitDialog函数中添加如下代码:
m_list.InsertColumn(0,_T("1"),LVCFMT_LEFT,100);
m_list.InsertColumn(1,_T("2"),LVCFMT_LEFT,100);
m_list.InsertColumn(2,_T("3"),LVCFMT_LEFT,100);
m_list.InsertColumn(3,_T("4"),LVCFMT_LEFT,100);
m_list.InsertItem(0,_T("123"));
m_list.SetItemText(0,1,_T("c"));
m_list.SetItemText(0,2,_T("d"));
m_list.SetItemText(0,3,_T("e"));
m_list.InsertItem(1,_T("456"));
m_list.SetItemText(1,1,_T("f"));
m_list.SetItemText(1,2,_T("g"));
m_list.SetItemText(1,3,_T("h"));
m_list.InsertItem(2,_T("789"));
m_list.SetItemText(2,1,_T("i"));
m_list.SetItemText(2,2,_T("j"));
m_list.SetItemText(2,3,_T("k"));
m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT );
如果在此时运行程序,则显示一个普通的列表控件,不具备编辑功能(如下图所示)。
3)添加一个类:CEditListCtrl,继承自CListCtrl。
注释掉 EditListCtrl.cpp文件中的 #include "Exam02.h"。该指令是类向导自动生成的,而CEditListCtrl类的实现并不依赖它。如不注掉它,将该类用于其他项目时,会无法编译。
在 Exam02Dlg.h的头部添加:#include "EditListCtrl.h";将CListCtrl m_list;语句替换成CEditListCtrl m_list;(该操作将列表控件资源与CEditListCtrl类关联起来,效果与椭圆形按钮实现过程的步骤4相同)。
此时程序的执行效果与步骤2是完全一样的。但控制列表控件行为的类已经换成CEditListCtrl了。接下来只需要对CEditListCtrl进行修改,就可以改变列表控件的行为了。
4)添加一个类:CItemEdit,继承自CEdit。注意,虽然这个类单独生成一样可以使用,但其主要作用就是为 CEditListCtrl类服务。考虑到使用的方便性,将其放在CEditListCtrl的类定义文件中更为合适。
具体方法如下:在生成新类 的对话框中,点击“Change”按钮(如左下图),在弹出的“Change Files”对话框中(如右下图所示),分别将头文件和实现文件指向editlistctrl.h和editlistctrl.cpp。
5)实现列表控件对鼠标双击事件的响应——编辑框的显示功能
在CEditListCtrl类中添加如下一个私有成员变量:
在其构造函数中添加:
m_edit.m_hWnd = NULL;
添加一个私有成员函数ShowEdit,用于在待编辑区域显示一个编辑框。函数声明如下:
下面为该函数的实现代码:
void CEditListCtrl::ShowEdit(BOOL bShow, int nItem, int nIndex, CRect rc)
{
|ES_WANTRETURN|WS_BORDER,CRect(0,0,0,0),this,IDC_EDIT);
m_edit.SetSel(-1);
m_edit.SetCtrlData(MAKEWPARAM(nIndex,nItem));
}
void CEditListCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
//获得子项所在区域,存入rcCtrl
}
编译后执行,双击列表控件上某个子项,该处就会显示出一个编辑框,其中显示的数据与对应位置上的数据项相同(如左上图所示)。而且,该数据已经被全部选中(高亮显示),用户可以对其进行更改,只是新的数据无法在列表控件上显示。
它还有一个小问题:当在列表控件的其他点击时,被双击过的编辑框不消失。下一步来解决这个问题。
6)在 CEditListCtrl类中添加NM_CLICK消息的响应函数,隐藏编辑框的显示。
代码如下:
void CEditListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
}
7)实现编辑完成后列表控件上的数据显示
用户所作的编辑是在编辑框控件中进行的,若想让编辑后的结果在列表控件上正确显示,则需要向列表控件传递新的数据。在两个控件间传递数据的方法非常简单,调用源控件的GetWindowText函数将数据放在一个字符串类对象中,再调用目标控件的SetItemText函数将字串中内容显示出来即可。但是,我们还要考虑下面两个问题:数据何时传递,是否更新。用户在编辑框中进行数据编辑时,一般是以按下回车键或者鼠标点击到其他地方(编辑框失去焦点)作为编辑完成的确认,显然此时是传递数据的合适时机;如果在编辑过程中,用户不想对原来的数据作出更改,则会按下ESCAPE键以示放弃,也意味着要退出编辑状态,但不应对数据作出更新。上述三种事件的发生,列表控件是无法自动感知的,因此我们需要给它发送一个消息,它才能及时处理数据更新的问题。另外,还需要让列表控件知道它要更新哪个子项的数据。
在 EditListCtrl.h的头部添加一个自定义消息:
#define WM_USER_EDIT_END
为CEditListCtrl类添 加一个成员函数:
LRESULT OnEditEnd(WPARAM wParam,LPARAM lParam = FALSE);
【上述操作是添加自定义消息响应函数的一般方法。MFC无法自动建立自定义消息映射,所以需要手工完成。自定义消息不能与系统消息相冲突,一般选用WM_USER之后的某个数值。自定义消息的处理函数本质上也是类的成员函数,因此可以采用添加成员函数的方法生成。将其声明语句移到 DECLARE_MESSAGE_MAP()宏之前并添加afx_msg前缀只是为了增加程序的可读性,不这样做也不会影响程的功能。关键一步是 OnMessage宏的插入,它将自定义消息及其响应函数添加到消息列表之中。如果不执行该操作,则自定义消息不会被MFC的消息网络接收和处理。另外该宏必须放在BEGIN_MESSAGE_MAP宏和END_MESSAGE_MAP宏之间,否则编译无法通过。】
在CItemEdit类中添加两个私有变量:
实现代码如下:
void CItemEdit::SetCtrlData(DWORD dwData)
{
}
DWORD CItemEdit::GetCtrlData()
{
}
添加两个消息处理函数OnSetFocus和 OnKillFocus:
void CItemEdit::OnSetFocus(CWnd* pOldWnd)
{
}
void CItemEdit::OnKillFocus(CWnd* pNewWnd)
{
}
填写OnEditEnd函数的代码如下:
LRESULT CEditListCtrl::OnEditEnd(WPARAM wParam, LPARAM lParam)
{
}
但是,当我们在编辑框中输入回车或Esc键时,整个对话框却退出了。为什么呢,还没有对这两个键盘消息进行拦截。
改 写CItemEdit类的虚拟成员函数:PreTranslateMessage,添加如下代码:
BOOL CItemEdit::PreTranslateMessage(MSG* pMsg)
{
}
至此,可编辑列表控件的基本功能就已经实现了。但是当用户需要对列表中的多个数据作出修改时,依次双击需要修改的字段就太麻烦了。能否像Eexel表格一样,通过按下某些控制键来实现行列之间的快速跳转呢?
3.强化功能
根据习惯,这里采用Tab键跳转到下一字段(如果到行尾,则跳到下一行的第一个字段);Shift+Tab键跳转到上一字段(如过到行头则跳到上一行的行尾);Ctrl+Tab键跳转到下一行的同一字段(到最后一行则跳回第一行)。实现原理则是捕获按键消息。具体实现过程如下:
1)
BOOL CEditListCtrl::PreTranslateMessage(MSG* pMsg)
{
}
2)添加一个私有成员函数Key_Shift,添加代码如下:
void CEditListCtrl::Key_Shift(int &nItem, int &nSub)
{
}
这样就实现了Tab及Shift键的跳转功能,同理易于实现Ctrl+Tab的功能。
3)添加一个私有成员函数Key_Ctrl,添加代码如下:
BOOL CEditListCtrl::Key_Ctrl(int &nItem, int &nSub)
{
}
同时在CEditListCtrl::PreTranslateMessage函数的 Key_Shift(nItem,nSub);语句之前添加
4.该类的使用
生成的可编辑列表控件类CEditListCtrl,其使用方法与CListCtrl类基本相同。由于相关的代码都包含在EditListCtrl.h和EditListCtrl.cpp文件之中,只需要将这两个文件拷贝到引用它的工程目录并加入到工程,然后在使用它的类中包含EditListCtrl.h即可使用。对于对话框或FormView等允许在资源编辑器中添加列表控件的窗口类来说,首先需要按常规将列表控件资源与CListCtrl变量进行关联,然后将该变量声明语句中的CListCtrl改成CEditListCtrl即可。对于一般的窗口类(例如 View类),则需要声明一个CEditListCtrl类型的成员变量,再利用其Create成员函数动态创建。下图为在View类的客户区中,动态创建可编辑列表控件的实例。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。