Visual Studio2005 + Win CE 6.0 + WTL 8.1
WTL Application Develop Use CListViewCtrl Control With LVS_OWNERDATA Style (virtual ListCtrl)
For next wentao
Include: COwnerDraw, CCustomDraw, CImageList, Base On Dialog, WM_MEASUREITEM
Summary:开发一个车载系统的SD卡,USB中文件的查看应用模块,其中对图像,音乐,视频文件进行过滤并在ListViewCtrl控件中显示。
根据需求,整套系统要与WinCE 6.0系统默认风格区别的统一风格,所以所有文件的图标和显示内容都需要自己来Draw。
其中音乐和视频文件以LVS_REPORT方式显示,利用ImageList根据不同的文件画出不同的ICON,再显示文件名。Item被选中时,Item的背景色与文件名颜色改变。
图像文件的显示比较复杂,用LVS_ICON方式显式,需要自己画出一定大小的图像缩略图(不同格式的需自行解码为bmp格式)。选择时用一颜色框圈中图片。
由于风格的问题,还需要自己实现一个ScrollBar,并把系统默认的隐藏。
考虑到硬件发展的速度,外存设备的大小成指数上升,所以文件个数成百上千已经不足为奇,
特别是图片文件,为了实现快速查找文件并显示出来,所以用virtual list(也就是在创建
ListViewCtrl的时候添加LVS_OWNERDATA Style),因为virtual list不保存数据。
Virtual list的用法,建议认真看一遍下面的这篇Article:
http://www.codeproject.com/KB/list/virtuallist.aspx
这篇文章包函的知识点非常多,而且非常实用,虽然些没有用到,就像
LVN_ODFINDITEM消息的处理,还有check boxes的使用,等等。
不过作者对LVN_ODCACHEHINT处理讲得比较简单,其实这条消息是非常重要的,只是作者实现的功能不需要对这个消息进行处理而已。
用ListViewCtrl控件通常会与ImageList一同使用,用以显示Item的图标,我在开发中以LVS_REPORT Style过滤视频和音乐文件的时候就用到了下面这条图片List来输出。
用CImageList来输出一系列固定的图片,只需先判断文件的属性,然后指定一个Index就可以把图标Draw出来。
而我对图片文件就过滤就不这么简单,因为图片是以LVS_ICON Style显示的,每找到一张图片都把它的缩略图显示出来,这就要动态创建一张ImageList。
而我在前面讲过,virtual ListCtrl是不保存数据的,当它需要数据的时候才向我们要,我们就把准备好的数据给它,这样,我们就要处理LVN_ODCACHEHINT消息了,在这里我们可以向ListViewCtrl提供从第几条到第几条需要用到的数据。
在这次的开发中,CImageList我也是第一次用到,不难,不过用的好的话真的非常方便。我以前试过设置一个定时器,在一系列图片当中,自己计算每次截取的部分显示出来,实现一个动画闪光就效果,虽然也不会很难,但显然使用CImageList来画比较规范。
CImageList的在上面的例子中也有用到,你可以上那个网站,注册一个会员,就可以下载到源码,可以参考一下如何使用,或者看MSDN。
http://www.codeproject.com/KB/list/virtuallist.aspx
(codeproject这个网站提供非常丰富的源码下载,值得注册一个会员)
如果不用CImageList先保存Icon,直接找一个画一个,也可以,不过还是有必要创建一个CImageList与控件关联,并加载一张你将要显示的Icon大小的图,用以CListViewCtrl确定Icon的大小。这一点很重要,花了很多时间在这里,因为就算SetIconSpacing()还是无法取到Icon的大小。
在OnCreate()中,初始化CImageList:
m_ImageList.Create(IDB_FOLDER, ICON_WIDTH, 1, RGB(0, 0, 0));
SetImageList(m_ImageList, LVSIL_NORMAL);
还有一点非常重要,一定不能在OnCreate()中简单地SetMsgHandled(FALSE);一定要在一开始先初始化基类:
DefWindowProc(m_pCurrentMsg->message, m_pCurrentMsg->wParam , m_pCurrentMsg->lParam);
否则所有在OnCreate()中的对ListViewCtrl的初始化都没有一点作用!!!
要实现自己对ListViewCtrl中的Item进行绘制,无非三种方法:1.继承COwnerDraw,响应DrawItem消息,有OnDrawItem()中进行绘制。2.继承CCustomDraw,在OnPrePaint()中进行绘制。用CustomDraw来处理,要自己控制步骤,对消息的处理要十分小心,特别是返回值。3.就是在OnPaint()中老老实实地一条一条画。
要画出自己风络的Item,要解决三件事,获得当前Item的DC,RECT,ID。最重要还是ID,可以选的话,当然在DrawItem中画,来一条处理一条,在lpDrawItemStruct中,有你需要的所有信息。在CustomDraw中也差不多,而OnPaint中,它的绘图要求就一次性过来的,所以要自己计算ID。
先看一下COwnerDraw和CCustom的用法吧:
http://www.codeproject.com/KB/wtl/wtl4mfc5.aspx
如果是基于对话框的控件开发,还需要掌握DDX的知识:
http://www.codeproject.com/KB/wtl/wtl4mfc4.aspx
准备知识这样就差不多了,可以开始开发了。
先是用基于对话框的吧,用对话框作为父窗口, 这样有几个好处,首先就是资源可以统一管理,排版的工作全部都可以在OnSize()里面做,当然你也可以在资源中进行排版,不过这对于比较精细的排版有一定的困难,因为在资源中,不是以像素为单位的,而且经常会有一两个像素的误差。如果你在程序中要取客户区与屏幕的大小判断是否相等,这样就不能简单在用==来判断。
首先我们当然要创建两个类,一个是自己的ListViewCtrl,另一个是自己的ScrollBar.
class CFSListViewCtrl : public CWindowImpl<CFSListViewCtrl, CListViewCtrl>
DECLARE_WND_SUPERCLASS(L"CFSListViewCtrl", CListViewCtrl::GetWndClassName())
为什么不是这个继承呢?CFSListViewCtrl : public CListViewCtrl
因为CListViewCtrl只是一个简单的控件,在WTL中,其他控件也是一样,这样继承就没有了消息循环,没有什么作用。
在上面我们已经讲到,在CFSListViewCtrl的消息循环中我们要处理这两个消息,
REFLECTED_NOTIFY_CODE_HANDLER_EX(LVN_GETDISPINFO, OnGetdispinfo)