夜雨竹林
落叶无声
作为MFC中最常用的控件之一,CListCtrl功能的使用,修改及扩展是一个很深,很广泛的问题。在学习的过程中我尽量留下笔记,贴在这里和大家共享,欢迎大家挑错。

CListCtrl应用在作表格上最多,所以一般研究的功能都是Report view。
下面的程序实现了一些ListCtrl的加强功能。如果你遇到这个问题想找一个现成的解决方法,你可以下载下面的小程序包。
效果大概如下图:

这个增强的listctrl控件支持CheckBox和彩色字体。同时可以选择当鼠标选中一个格子的时候是全行显示被选中,还是只有这个格子显示被选中。上图中只有当前的格子被选中。
对程序稍作修改可以调整背景颜色等等。
如果想在表格中显示其它控件,只需要按照下面说的方法加入两三个函数就可以了。

下面讲一下怎么应用这个小程序:

这是一个演示增强控件CListReport的程序,如果你要用CListReport这个控件,需要把在Shared Classes里面的六个文件加到你的project中。6个文件所在目录的深度需要一样,否则在include的语句中你需要修改以下路径。

在你的Dialog或者FormView里加上想要用的List Control。在class wizard里给list control加控制变量,variable type里就会有CListCtrl和CListReport两个选后者,然后把List Control的Proprty中View设成Report,然后选Owner draw fixed。就可以了。(如果由于一些原因你在class wizard中找不到CListReport,可以直接加成CListCtrl类,然后在自己的.h中将其改成CListReport,然后在.h最前面 写 #include "路径/ListReport.h")


CListReport的默认显示方式是全行选中,就是当你选中一个格子的时候全行被hightlight,这个和CListCtrl的默认风格一样。想改成选中格子就只加重这个格子,在你的Dialog或FormView的析构函数中初始话风格变量:
CListDemoDlg::CListDemoDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CListDemoDlg::IDD, pParent)
    , m_listDemo(ListReportDefinition::intCellSelect)

用以下方法在表格中进行添加。
在你的主程序中用下面的方法调用: 假设变量名叫m_listDemo
CReportCtrl m_listDemo
在header中加一列:     m_listDemo.AddColumn("Column 0", 0);   //第一个参数是列的文字,第二个参数是列号。
在header中加一列,并指定其宽度为200:    m_listDemo.AddColumn("Column 0", 0, 200);

在第0行第0列加Text "Row 0, Col 000",红色字体,粗体字    
m_listDemo.AddItem(0, 0, "Row 0, Col 000", CListItemText::GenerateProperty(true, RGB(255,  0,  0)))
字太长的时候item会显示成...(如图)。
在第0行第1列加Text "Row 0, Col 100",绿色字体,非粗体字    
    m_listDemo.AddItem(0, 1, "Row 0, Col 100", CListItemText::GenerateProperty(false, RGB(0, 255,  0)));
在第0行第2列加Check Box,初始状态为选中。  
    m_listDemo.AddItem(0, 2, "1", CListItemCheckBox::GenerateProperty());


基本上直接使用的函数就这些了。如果你想作进一步修改
1。改写DrawItem()函数,可以将颜色背景颜色之类的进行调整。
2。如果除了彩色字体和checkbox你还想加入别的类,例如图片类。你只需要作以下几个步骤:
     生成一个普通的C++ class,从CListItem继承下来(#include "路径/ListItem.h",或者直接写在ListItem.h/.cpp中)。
     CListItem是显示没个小格子的基类。对于你的contructor而言,你可以定义格子的风格参数。例如CListItemText中定义了是否 是粗体字,是什么颜色等等。CListItemCheckBox没有任何附加的功能,contructor就是空的。这里我用CSV格式来将所有的参数一 次性输入,然后在画图的时候再将其展开。每个函数的GenerateProperty()就是实现这个功能的。
    好,假设你已经定义好了所有风格,或者没有风格,下面需要写的就是如何显示它了。实现DrawRect(CDC &dc, CRect rcItem, string sText, bool bSelected)函数。    其中提供了dc,格子的位置,格子的内容以及当前是否被选中,干什么都够了。
    显示已经作完了。如果你想实现点击功能,需要写ClickItem和DbClickItem这两个功能。注意在List中如果点的很快,响应将是 double click,所以像CheckBox,必须重载ClickItem和DbClickItem这两个功能,而不能只重载ClickItem参数中第一个 pParent是这个List。另外两个是行和列。

需要注意的问题是,这个ListReport控件使用了ON_WM_LBUTTONDOWN和ON_WM_LBUTTONDBLCLK两个事件,那么其parent就无法获得这两个事件了。作为补偿,这里增加了两个user message:
#define WM_LISTREPORT_LBUTTONDOWN    WM_USER+1201
#define WM_LISTREPORT_LBUTTONDBLCLK    WM_USER+1202
你的主Dialog可以像处理ON_WM_LBUTTONDOWN和ON_WM_LBUTTONDBLCLK一样处理这两个事件,我没有在 PostMessage里面加任何参数,因为好像没太多必要。找到鼠标的位置然后hittest一下就得到所有信息了。如果有什么其它需要的希望大家提 醒。

尚存问题:目前为止发现的问题是有时候连续选中一行的不同列的时候反应比较慢,能长达半秒钟。主要问题大概是ListCtrl本身把这一行来回draw了好几遍。我的DrawItem里面需要更仔细的判断以提高速度。

功能讲完了。制作的原理大概就是在鼠标左键按下的时候记下鼠标的位置,重载DrawItem函数,根据鼠标位置决定文字的背景颜色。程序应该很好懂,有什么问题欢迎大家指正。

其它ListCtrl的常见问题:
问题1:如何左击list的列的时候如何获得当前的列号?
    ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST, OnLvnColumnclickList)

void CDlgLists::OnLvnColumnclickList(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    int intSelectedColumn = pNMLV->iSubItem;

这里intSelectedColumn就是当前选中的列好了。

这个事件也可以在list内管理,而不在Dialog中管理。看具体的处理事件是什么了。一般的概念就是谁来处理这个事件就有谁管理。例如我的list上 有30行,我想用ListCtrl自身的sort()来重新排序,那么这个事件应该由list管理。如果希望sort有其Dialog实现,就应该在 Dialog中管理。我个人更加倾向于在Dialog中管理。因为list本身能作的事情太有限了。例如我要求sort()结束后在下面的状态栏里显示 Sort by 哪一列,这个很小的附加功能对于list管理的方法而言就很不舒服了。另外如果list显示的是一个巨大的数据库中的某一页,我想sort()的时候当然 是对整个数据库sort()。list控件的sort()对此无能为力。
posted on 2009-09-10 09:24  夜雨竹林  阅读(948)  评论(0编辑  收藏  举报