当前位置 : 首页 » 文章分类 :  开发  »  MFC可编辑的列表控件的实现

MFC可编辑的列表控件的实现

一、引言

列表控件主要用来以各种方式显示一组数据记录供用户进行各种操作,是最常见的控件之一。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

在构造函数CEditListCtrl::CEditListCtrl()中进行初值的设定:

m_edit = NULL;//编辑框的指针,初始为NULL,表示无编辑框
m_isedit = NULL;//标志哪些列可编辑的标志维数组,初始为NULL
m_item = -1;//当前编辑的行号,初值为-1
m_subitem = -1;//当前编辑的列号,初值为-1

定义一个常量ID_LISTEDIT作为m_edit所指向的编辑框的ID号:
在Resource.h中,

#define ID_LISTEDIT   1100   //自己给可编辑列表定义一个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_HSCROLLWM_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方式,ID为:IDC_LIST_EDIT(一定要和前面定义的常量ID对应)给该控件定义一个CEditListCtrl类型的对象m_list,记得要在主对话框中加入#include "EditListCtrl.h"
再在对话框的初始化函数中添加初始化代码:

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中调试通过,运行结果:当两次不连续地单击列表控件中的一个子项目后,该子项目便呈现编辑状态,如下图所示。向编辑框中输入数据后单击列表控件,便完成对子项目的编辑。

上一篇 MFC工具栏中插入分隔线最简单的方法

下一篇 绫辻行人《杀人暗黑馆》读后感

阅读
评论
1.7k
阅读预计6分钟
创建日期 2012-07-02
修改日期 2017-07-15
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论