首页 > 代码库 > CLISTCTRL2
CLISTCTRL2
回顾:
刚刚写完,因为是分期写的,所以最初想好好做一下的文章格式半途而废了~说的也许会有点啰嗦,但是所有的基础用到的技术细节应该都用到了。
如果还有什么疑问,请回复留言,我会尽力解答。
如果有错误,请指正。
这里所有使用到的内容在VS2010中均能正常运行。
可运行程序的截图:
CListCtrl控件的创建
如果一个CListCtrl控件对象和dialog上的控件已经绑定,那么可以跳过这个这一步,因为绑定了的对象已经完成了创建。
但是如果要在dialog上凭空创建出来一个列表控件,那么就需要了解CListCtrl类的成员函数:Create
先看这个函数的原型:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
第一个参数是用来声明这个列表的风格,具体风格如下 :
- LVS_ALIGNLEFT Specifies that items are left-aligned in icon and small icon view.
指定条款是小图标且左对齐 - LVS_ALIGNTOP Specifies that items are aligned with the top of the control in icon and small icon view.
指定条款是顶端对其且为小图标 - LVS_AUTOARRANGE Specifies that icons are automatically kept arranged in icon view and small icon view.
指定图标自动保存在图标视图中,且为小图标视图 - LVS_EDITLABELS Allows item text to be edited in place. The parent window must process the LVN_ENDLABELEDITnotification message.
允许条目适当的编辑,父窗口必须指定LVN_ENDLABELEDIT消息 - LVS_ICON Specifies icon view.
指定图标视图 - LVS_LIST Specifies list view.
指定列表视图 - LVS_NOCOLUMNHEADER Specifies that a column header is not displayed in report view. By default, columns have headers in report view.
指明列的头结点不在报告视图中显示,默认时,列的头结点会在报告中显示出来 - LVS_NOLABELWRAP Displays item text on a single line in icon view. By default, item text can wrap in icon view.
在图标视图中单独的显示出来。默认情况下,项文本能和图标同时显示 - LVS_NOSCROLL Disables scrolling. All items must be within the client area.
关闭滚动条,全部的条目必须在客户区内显示 - LVS_NOSORTHEADER Specifies that column headers do not work like buttons. This style is useful if clicking a column header in report view does not carry out an action, such as sorting.
表明列头不会像一个按钮那样。这个选项在report风格下如果不打算让其实现一些功能会很有用,比如点击后排序 - LVS_OWNERDRAWFIXED Enables the owner window to paint items in report view. The list view control sends a WM_DRAWITEM message to paint each item; it does not send separate messages for each subitem. The itemData member of theDRAWITEMSTRUCT structure contains the item data for the specified list view item.
这个我貌似用不到……不翻译了 - LVS_REPORT Specifies report view.
指明报告窗口 - LVS_SHAREIMAGELISTS Specifies that the control does not take ownership of the image lists assigned to it (that is, it does not destroy the image lists when it is destroyed). This style enables the same image lists to be used with multiple list view controls.
指明这个控件并不获取图像列表的所有权。这就意味着,在这个对象析构的时候斌不会析构图像列表。这个风格允许同样的图像列表用于多个列表控件 - LVS_SHOWSELALWAYS Always show the selection, if any, even if the control does not have the focus.
总是显示被选中的选项,即便窗口未被锁定 - LVS_SINGLESEL Allows only one item at a time to be selected. By default, multiple items can be selected.
一次只能选择一个选项,默认的情况下,能同时选择多个条目 - LVS_SMALLICON Specifies small icon view.
指定小图标视图 - LVS_SORTASCENDING Sorts items based on item text in ascending order.
按照递增进行排序 - LVS_SORTDESCENDING Sorts items based on item text in descending order.
- 按照递减排序
一下翻译这么多,有点累,看来我的英语还是太差,不然怎么会觉得累~
第二个参数:rect
这个参数就是你所创建的CListCtrl的位置,以及大小。不再做过多介绍了
第三个参数:pParentWnd
这个参数用来指定父窗口,一般是一个CDialog对象指针,需要注意的是,千万不能是NULL。
第四个参数:nID
控件ID,这个不用多说了,每个看控件都有的。
CListCtrl风格修改
但是如果你已经创建好了一个ListCtrl控件,那么你为其声明了一个CListCtrl控件,那么上面的Create函数你就可以跳过去了~
但是,风格总是要设置的,所以在这里,有一个函数可以调用,专门用来设置风格~
LONG SetWindowLong( HWND hWnd, // handle to window int nIndex, // offset of value to set LONG dwNewLong // new value );
CListCtrl的窗口句柄是public的,能直接获取。
第二个参数添加:GWL_STYLE,目的就是为了设置新的风格。
第三个参数就是风格的组合了
这个函数的具体内容请看MSDN,我这就不再多说了~
CListView的扩展风格
CListCtrl可以设置的风格:
LVS_EX_GRIDLINES //绘制表格
LVS_EX_SUBITEMIMAGES
LVS_EX_CHECKBOXES //带复选框
LVS_EX_TRACKSELECT //自动换行
LVS_EX_HEADERDRAGDROP
LVS_EX_FULLROWSELECT //选择整行
LVS_EX_ONECLICKACTIVATE//单击激活
LVS_EX_TWOCLICKACTIVATE//双击激活
LVS_EX_FLATSB//扁平滚动条
LVS_EX_REGIONAL
LVS_EX_INFOTIP
LVS_EX_UNDERLINEHOT
LVS_EX_UNDERLINECOLD
LVS_EX_MULTIWORKAREAS//多工作区
以上风格设置可以使用下面的CListCtrl成员函数来设置:
DWORD SetExtendedStyle( DWORD dwNewStyle );
现在有代码如下:
1 void CMyRisingDlg::ListCtrlInitial() 2 { 3 CRect lc_rect; 4 DWORD dwStyle = ::GetWindowLong(m_LCTable.m_hWnd, GWL_STYLE); 5 dwStyle &= ~(LVS_TYPEMASK); 6 dwStyle &= ~(LVS_EDITLABELS); 7 SetWindowLong(m_LCTable.m_hWnd, GWL_STYLE,dwStyle |LVS_REPORT | LVS_NOLABELWRAP | LVS_SHOWSELALWAYS); 8 m_LCTable.SetExtendedStyle(LVS_EX_ONECLICKACTIVATE|LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); 9 m_LCTable.GetWindowRect(&lc_rect); 10 DWORD dwListLength = lc_rect.Width() / 3; 11 m_imagelist.Create(16, 16, ILC_COLORDDB|ILC_MASK, 0, 1); 12 13 m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON1)); 14 m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON2)); 15 m_LCTable.SetImageList(&m_imagelist, LVSIL_SMALL); 16 m_LCTable.InsertColumn(0, _T("Name"), LVCFMT_LEFT | LVCFMT_IMAGE, dwListLength); 17 m_LCTable.InsertColumn(1, _T("PathName"), LVCFMT_LEFT, dwListLength); 18 m_LCTable.InsertColumn(2, _T("Create Time"), LVCFMT_LEFT, dwListLength + 3); 19 20 m_LCTable.InsertItem(0, LPSTR_TEXTCALLBACK);21 22 }
很显然,这个函数的功能是初始化一个ListCtrl。从代码里面能够看出来,在初始化上,其实主要的就是风格设置。然后最一行的函数调用是为了做虚拟列表,CListCtrl的虚拟列表内容我会在新的随笔中介绍。
上面的几个比较主要的成员函数调用,SetExtendedStyle函数就不再多说了,可以参考样式以及上面的各种风格,或者直接去看MSDN。
然后下面的SetImageList是为了给列表上主要的首子项添加上文件图标,正在做,还未完成。不过上面的程序界面截图中能看到首列有图标,那个只是目前应付一下的~
接下来要比较重点的介绍InsertClumn函数:
这个函数是用来完成列的插入的。或者说,建立了一个索引,或者链表头。由此函数调之后才能调用InsertItem函数,否则是无效的,函数刚一调用,估计就直接返回个0了。
这个函数看下MSDN的介绍:
int InsertColumn( int nCol, const LVCOLUMN* pColumn );
int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth= -1, int nSubItem = -1 );
Return Value
The index of the new column if successful or -1 otherwise.
这个函数有两种形式,但是我只介绍第二种,原因为啥你应该很清楚~。这里第一个参数就是列号了,不多说。第二个参数是列名,也不用太罗嗦,第三个是格式,需要简单说一下。
nFormat参数有一下的几种形式:LVCFMT_LEFT, LVCFMT_RIGHT, or LVCFMT_CENTER.另外还有LVCFMT_IMAGE,默认情况下是从左往右的,也就是我们的书写顺序。另外需要注意的是,其实这个参数一般来讲的话都是直接使用LEFT参数的,原因就是如果你使用了center或者right参数,会直接导致一个后果:就是这列的全部内容,也都是对应的居中或者置右。另外如果使用了LVCFMT_IMAGE参数,那么就会在最左端添加图片的基础上,默认为LEFT风格,这点还是需要注意的。
目前除了InsertItem函数之外,其余的基础操作设置就差不多了,至于其他的一些操作我等会也还需要再看~
然后接下来讲下如何添加首项的图片。
我觉得添加这个图片的目的主要还是为了提供更好的用户体验。因为如果有图片的话,对大多数用户来说会提供一个很便利的途径来知道这个文件的类型属性。毕竟大多数的用户是不会去根据文件后缀来判断这个文件到底是干什么的~
废话不多说,先看一个类:CImageList.
接着贴MSDN:
An “image list” is a collection of same-sized images, each of which can be referred to by its zero-based index. Image lists are used to efficiently manage large sets of icons or bitmaps. All images in an image list are contained in a single, wide bitmap in screen device format. An image list may also include a monochrome bitmap that contains masks used to draw images transparently (icon style). The Microsoft Win32 application programming interface (API) provides image list functions that enable you to draw images, create and destroy image lists, add and remove images, replace images, merge images, and drag images.
大意如下:
一个图像列表是同样大小图像的集合,每个成员都能够通过以0为起始的目录来访问(其实就是顺序表访问)。图像列表控件能够用来有效的管理大数量的图标或位图。在图像列表中的所有图片都在屏幕设备格式中包含一个单独的,宽(这里不知道怎么翻译)的位图。一个图像列表控件也可能包含一个包含绘制透明图像任务的单色位图(这里不是很确定)。
后面的就不翻译了,没什么用。
上面我贴的那个函数里CImageList的使用也比较明了,简单介绍下:
对象创建完之后并不需要立即初始化,还不错。然后首先要调用其中的Create函数,就是创建一个初始化一个列表并付给对象,MSDN基本就这个意思。这里再贴一下函数原型:
BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );
BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2, int nImage2, int dx,int dy );
BOOL Create( CImageList* pImageList );
Return Value
Nonzero if successful; otherwise 0.
很显然,在我的程序里面调用的函数是其中的第一个,所以我这就只介绍第一个了~前两个参数很明显,就是这个图片的尺寸。我在这里的建议还是,提供16*16的图片比较合适。我曾经想过做8*8的,结果,太小了……实在不合适。这个尺寸是我做列表比较推荐的。第三个参数很显然就是个标志位,用来设置风格的。
具体风格如下:
ILC_COLOR | Use the default behavior if none of the other ILC_COLOR* flags is specified. Typically, the default is ILC_COLOR4; but for older display drivers, the default is ILC_COLORDDB. |
ILC_COLOR4 | Use a 4-bit (16 color) device-independent bitmap (DIB) section as the bitmap for the image list. |
ILC_COLOR8 | Use an 8-bit DIB section. The colors used for the color table are the same colors as the halftone palette. |
ILC_COLOR16 | Use a 16-bit (32/64k color) DIB section. |
ILC_COLOR24 | Use a 24-bit DIB section. |
ILC_COLOR32 | Use a 32-bit DIB section. |
ILC_COLORDDB | Use a device-dependent bitmap. |
ILC_MASK | Uses a mask. The image list contains two bitmaps, one of which is a monochrome bitmap used as a mask. If this value is not included, the image list contains only one bitmap. |
我上面的函数在这里使用的是后两个参数的组合。DDB的意思是设备相关位图,与之相反的就是大名鼎鼎的DIB了,不过这里没有。我的MSDN版本比较老,是给VC6.0做的,有兴趣的可以去MSDN网站上看看,新版的CImageList有没有提供DIB的。
不过我觉得,似乎我的那个代码里,ILC-MASK参数貌似没什么用处,回头删掉。
然后nInitial参数,就是Imagelist对象中初始化后就包含的图像数目,MSDN:Number of images that the image list initially contains.
最后一个参数:nGrow 原文:Number of images by which the image list can grow when the system needs to resize the list to make room for new images. This parameter represents the number of new images the resized image list can contain.
其实这个参数倒不是很重要,但是,能牵扯到效率问题。如果每次新添加一个图片就要申请一块新的内存,是不是有点效率太低了?所以不如一次申请多个,这样效率更高一下些。但是如果你的列表一般只需要10个,但是你每次resize之后会变成100个,就造成了浪费。所以还是需要注意一下的,但是小列表设置为5以内的数应该就行了。
这个函数介绍完了,就要接着讲添加的问题。不过这个还是比较简单的。可以直接调用add成员函数。
int Add( CBitmap* pbmImage, CBitmap* pbmMask );
int Add( CBitmap* pbmImage, COLORREF crMask );
int Add( HICON hIcon );
Return Value
Zero-based index of the first new image if successful; otherwise – 1.
这里提供了三个函数,前两个都是和Bitmap相关的,暂且不谈,只谈第三个。
第三个函数,很简单,只要一个图标资源的句柄就可以了。获取这个句柄资源,在MFC中有一种很简单的方法:直接调用App类的LoadIcon函数。
AfxGetApp()->LoadIcon(IDI_ICON1)
里面直接添加上工程中建立好的图标资源ID就好了。
不过这个成员函数只限CWinApp类的对象使用。另外一种方法是调用SDK的函数,其中hInstance就是句柄,这里应该直接添加app的m_hwnd,应该是的,我还没试过。第二参数要使用MAKEINTRESOURCE宏转一下就可以了。
接下来就是如何将图片绑定到列表头部了。
在两个对象都创建完成后,只要调用CListCtrl函数的一个成员函数就可以了:
CImageList* SetImageList( CImageList* pImageList, int nImageList );
Return Value
A pointer to the previous image list.
Parameters
pImageList
Pointer to the image list to assign.
nImageList
Type of image list. It can be one of these values:
- LVSIL_NORMAL Image list with large icons.
- LVSIL_SMALL Image list with small icons.
- LVSIL_STATE Image list with state images.
不用说太多,我这里第二个参数直接使用的LVSIL_SMALL选项,毕竟16*16的图片绝对不大。
然后这样就实现了绑定,但是此时你需要知道,虽然两个对象绑定了,但是ImageList对象依旧能够独立于ListCtrl进行修改操作。
那么绑定了之后呢?还是要看InsertItem函数。
InsertItem函数形式有好几个,这里需要使用这种形式:
int InsertItem( int nItem, LPCTSTR lpszItem, int nImage );
这个形式的函数专门添加了nImage这个参数,目的就是为了实现列表的添加。
nImage的选择是对应ImageList中的图像寻址功能,如果在ImageList中添加好了图片,那么InsertItem函数就会根据这个参数的值取寻找对应的图片,然后直接将其添加到列表中。
虚拟列表我会在新的随笔中介绍,并会介绍一下我的思考过程。2
CLISTCTRL2