列表控件可以看作是功能增強的ListBox,它提供了四種風(fēng)格,而且可以同時顯示一列的多中屬性值。MFC中使用CListCtrl類來封裝列表控件的各種操作。通過調(diào)用
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );創(chuàng)建一個窗口,dwStyle中可以使用以下一些列表控件的專用風(fēng)格:
LVS_ICON ,LVS_SMALLICON ,LVS_LIST, LVS_REPORT 這四種風(fēng)格決定控件的外觀,同時只可以選擇其中一種,分別對應(yīng):大圖標顯示,小圖標顯示,列表顯示,詳細報表顯示
LVS_EDITLABELS 結(jié)點的顯示字符可以被編輯,對于報表風(fēng)格來講可編輯的只為第一列。
LVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結(jié)點
LVS_SINGLESEL 同時只能選中列表中一項
首先你需要設(shè)置列表控件所使用的ImageList,如果你使用大圖標顯示風(fēng)格,你就需要以如下形式調(diào)用:
CImageList* SetImageList( CImageList* pImageList, LVSIL_NORMAL);
如果使用其它三種風(fēng)格顯示而不想顯示圖標你可以不進行任何設(shè)置,否則需要以如下形式調(diào)用:
CImageList* SetImageList( CImageList* pImageList, LVSIL_SMALL);
int InsertItem( int nItem, LPCTSTR lpszItem ); 插入行
nItem:指明插入位置
lpszItem:為顯示字符。
除LVS_REPORT風(fēng)格外其他三種風(fēng)格都只需要直接調(diào)用InsertItem就可以了,但如果使用報表風(fēng)格就必須先設(shè)置列表控件中的列信息。
int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat , int nWidth, int nSubItem); 插入列
iCol:為列的位置,從零開始
lpszColumnHeading:為顯示的列名
nFormat:為顯示對齊方式
nWidth:為顯示寬度
nSubItem:為分配給該列的列索引。
BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );設(shè)置每列的顯示字符
nItem:為行位置
nSubItem:為列位置
lpszText:為顯示字符
下面的代碼演示了如何設(shè)置多列并插入數(shù)據(jù):
m_list.SetImageList(&m_listSmall,LVSIL_SMALL);//設(shè)置ImageList
m_list.InsertColumn(0,\"Col 1\",LVCFMT_LEFT,300,0);//設(shè)置列
m_list.InsertColumn(1,\"Col 2\",LVCFMT_LEFT,300,1);
m_list.InsertColumn(2,\"Col 3\",LVCFMT_LEFT,300,2);
m_list.InsertItem(0,\"Item 1_1\");//插入行
m_list.SetItemText(0,1,\"Item 1_2\");//設(shè)置該行的不同列的顯示字符
m_list.SetItemText(0,2,\"Item 1_3\")
COLORREF GetTextColor( )/BOOL SetTextColor( COLORREF cr ):用于得到/設(shè)置顯示的字符顏色。
COLORREF GetTextBkColor( )/BOOL SetTextBkColor( COLORREF cr ):用于得到/設(shè)置顯示的背景顏色。
void SetItemCount( int iCount ):用于得到添加進列表中項的數(shù)量。
BOOL DeleteItem(int nItem):用于刪除某一項 [Page]
BOOL DeleteAllItems( ):將刪除所有項。
BOOL SetBkImage(HBITMAP hbm, BOOL fTile , int xOffsetPercent, int yOffsetPercent):用于設(shè)置背景位圖。
CString GetItemText( int nItem, int nSubItem ):用于得到某項的顯示字符。
列表控件的消息映射同樣使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode為通知代碼,id為產(chǎn)生該消息的窗口ID,memberFxn為處理函數(shù),函數(shù)的原型如同void OnXXXList(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR為一數(shù)據(jù)結(jié)構(gòu),在具體使用時需要轉(zhuǎn)換成其他類型的結(jié)構(gòu)。對于列表控件可能取值和對應(yīng)的數(shù)據(jù)結(jié)構(gòu)為:
LVN_BEGINLABELEDIT 在開始某項編輯字符時發(fā)送,所用結(jié)構(gòu):
NMLVDISPINFO
LVN_ENDLABELEDIT 在結(jié)束某項編輯字符時發(fā)送,所用結(jié)構(gòu):
NMLVDISPINFO
LVN_GETDISPINFO 在需要得到某項信息時發(fā)送,(如得到某項的顯示字符)所用結(jié)構(gòu):NMLVDISPINFO
關(guān)于ON_NOTIFY有很多內(nèi)容,將在以后的內(nèi)容中進行詳細講解。
關(guān)于動態(tài)提供結(jié)點所顯示的字符:首先你在項時需要指明lpszItem參數(shù)為:
LPSTR_TEXTCALLBACK。在控件顯示該結(jié)點時會通過發(fā)送TVN_GETDISPINFO來取得所需要的字符,在處理該消息時先將參數(shù)pNMHDR轉(zhuǎn)換為
LPNMLVDISPINFO,然后填充其中item.pszText。通過item中的iItem,iSubItem可以知道當前顯示的為那一項。下面的代碼演示了這種方法:
char szOut[8][3]={\"No.1\",\"No.2\",\"No.3\"};
//添加結(jié)點
m_list.InsertItem(LPSTR_TEXTCALLBACK,...)
m_list.InsertItem(LPSTR_TEXTCALLBACK,...)
//處理消息
void CParentWnd::OnGetDispInfoList(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR;
pLVDI->item.pszText=szOut[pTVDI->item.iItem];
//通過iItem得到需要顯示的字符在數(shù)組中的位置
*pResult = 0;
}
關(guān)于編輯某項的顯示字符:(在報表風(fēng)格中只對第一列有效)首先需要設(shè)置列表控件的LVS_EDITLABELS風(fēng)格,在開始編輯時該控件將會發(fā)送
LVN_BEGINLABELEDIT,你可以通過在處理函數(shù)中返回TRUE來取消接下來的編輯,在編輯完成后會發(fā)送LVN_ENDLABELEDIT,在處理該消息時需要將參數(shù)
pNMHDR轉(zhuǎn)換為LPNMLVDISPINFO,然后通過其中的item.pszText得到編輯后的字符,并重置顯示字符。如果編輯在中途中取消該變量為NULL。下面的代碼說明如何處理這些消息:
0282 設(shè)置ListControl控件的顯示風(fēng)格
ListControl控件可在窗體中管理和顯示列表項??煽刂屏斜韮?nèi)容的顯示方式,能夠以圖標和表格的形式顯示數(shù)據(jù)。打開ListControl控件的屬性窗口,在Styles選項卡中的View屬性中可以設(shè)置顯示風(fēng)格,包括:Icon表示圖標視圖;Small Icon表示小圖標視圖;List表示列表視圖;Report表示報表視圖。
0283 為ListControl控件設(shè)置列標題
在使用ListControl控件Report顯示風(fēng)格時,需要設(shè)置列標題信息,否則不能向控件中添加數(shù)據(jù)信息,編輯列標題需要使用InsertColumn方法。該方法的語法如下:
int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT,int nWidth = -1, int nSubItem = -1 );
參數(shù)說明如下。
l nCol:標識新列的索引。
l lpszColumnHeading:標識列標題。
l nFormat:標識列的對齊方式。
l nWidth:標識列寬度。
l nSubItem:標識關(guān)聯(lián)當前列的子視圖項索引。
程序代碼如下:
m_Grid.InsertColumn(0,"姓名",LVCFMT_LEFT,150,0);
m_Grid.InsertColumn(1,"聯(lián)系電話",LVCFMT_LEFT,150,1);
0284 為ListControl控件添加行
在ListControl控件中添加信息時不能直接向控件中添加列信息,需要先為控件添加行,使用InsertItem方法,該方法用于向ListControl控件中添加行。語法如下:
int InsertItem( int nItem, LPCTSTR lpszItem );
參數(shù)說明如下。
l nItem:表示被插入的行索引。
l lpszItem:表示行文本。
程序代碼如下:
m_Grid.InsertItem(0,"");
0285 為ListControl控件添加列
通過SetItemText方法可以為任意行的任意列添加數(shù)據(jù),該方法用于設(shè)置ListControl控件中的文本。語法如下:
BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );
參數(shù)說明如下。
l nItem:標識行索引。
l nSubItem:標識列索引。
l lpszText:標識設(shè)置的顯示文本。
程序代碼如下:
m_Grid.SetItemText(0,0,"周X");
m_Grid.SetItemText(0,1,"12345XXXXXX");
0286 設(shè)置ListControl控件的擴展風(fēng)格
在使用ListControl控件的Report顯示風(fēng)格時,需要設(shè)置一些常用的擴展風(fēng)格。如圖5.33所示。

圖5.33 設(shè)置ListControl控件的擴展風(fēng)格
程序代碼如下:
m_Grid.SetExtendedStyle(
LVS_EX_FLATSB //扁平風(fēng)格滾動條
|LVS_EX_FULLROWSELECT //允許整行選中
|LVS_EX_HEADERDRAGDROP //允許標題拖曳
|LVS_EX_ONECLICKACTIVATE //高亮顯示
|LVS_EX_GRIDLINES //畫出網(wǎng)格線
);
m_Grid.InsertColumn(0,"姓名",LVCFMT_LEFT,150,0);
m_Grid.InsertColumn(1,"聯(lián)系電話",LVCFMT_LEFT,150,1);
m_Grid.InsertItem(0,"");
m_Grid.SetItemText(0,0,"周X");
m_Grid.SetItemText(0,1,"12345XXXXXX");
m_Grid.InsertItem(1,"");
m_Grid.SetItemText(1,0,"諸葛X");
m_Grid.SetItemText(1,1,"67890XXXXXX");
0287 設(shè)置ListControl控件數(shù)據(jù)的排列順序
通過ListControl控件的屬性窗口可以設(shè)置控件中數(shù)據(jù)的排列順序,在Styles選項卡中的Sort屬性中可以對控件中的數(shù)據(jù)進行排序,包括:None表示不進行排序;Ascending表示升序排列;Decending表示降序排列。
0288 單擊ListControl控件列標題進行排序
在使用ListControl控件的Report顯示風(fēng)格時,要實現(xiàn)單擊列標題進行排序需要在控件的LVN_COLUMNCLICK消息的處理函數(shù)中添加SortItems函數(shù),SortItems函數(shù)實現(xiàn)了對列表項排序的功能,語法如下:
BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData );
參數(shù)說明如下。
l pfnCompare:排序時所使用的回調(diào)函數(shù),如果不進行排序就讓回調(diào)函數(shù)返回-1。
l dwData:指定列表控件對象指針。
程序代碼如下:
void CCompositorDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
m_Grid.SortItems(Compositor,(DWORD)reinterpret_cast<DWORD>(this));
*pResult = 0;
}
int CALLBACK CCompositorDlg::Compositor(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListCtrl* pListCtrl=reinterpret_cast<CListCtrl*>(lParamSort);
return 1;
}
0289 具有熱點效果的視圖控件
在使用列表視圖控件時,如果列表視圖以表格形式顯示數(shù)據(jù),并且包含有LVS_EX_TWOCLICKACTIVATE擴展風(fēng)格,那么,當鼠標移動到某一行時,整行數(shù)據(jù)會出現(xiàn)熱點效果。如何將某一個單元格設(shè)置為熱點效果呢?本例實現(xiàn)了該功能,效果如圖5.34所示。
本例是通過繪制列表視圖控件實現(xiàn)的。首先從CListCtrl派生一個子類,然后添加ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW,OnCustomDraw)消息映射實現(xiàn)CListCtrl的繪制。最后處理鼠標移動時的事件,判斷鼠標在移動過程中,單元格是否發(fā)生了改變,如果發(fā)生了改變,重繪窗口。程序主要代碼如下:
BOOL CCustomView::OnCustomDraw(NMHDR *pNotifyStruct, LRESULT *lResult)
{
NMLVCUSTOMDRAW * nmDraw = (NMLVCUSTOMDRAW *)pNotifyStruct;
NMCUSTOMDRAW & pDraw = nmDraw->nmcd;
UINT flag = pDraw.uItemState;
switch (pDraw.dwDrawStage)
{
case CDDS_PREPAINT :
{
*lResult =CDRF_NOTIFYITEMDRAW;
break;
}
case CDDS_ITEMPREPAINT :
{
*lResult =CDRF_NOTIFYSUBITEMDRAW ;
break;
}
case (CDDS_ITEMPREPAINT | CDDS_SUBITEM) :
{
int row = pDraw.dwItemSpec;
int col = nmDraw->iSubItem;
CPoint point;
GetCursorPos(&point);
ScreenToClient(&point);
CRect rect,secRC;
this->GetSubItemRect(row,col,LVIR_BOUNDS ,rect);
int cWidth = this->GetColumnWidth(0); //獲取第一列的寬度
nmDraw->clrText = m_FontColor;
if (col !=0)
{
if (rect.PtInRect(point))
nmDraw->clrText = m_HotFontColor;
}
else
{
secRC.CopyRect(CRect(0,rect.top,cWidth,rect.bottom));
if (secRC.PtInRect(point))
nmDraw->clrText = m_HotFontColor;
}
break;
}
default:
{
*lResult = CDRF_DODEFAULT;
break;
}
}
return TRUE;
}
void CCustomView::OnMouseMove(UINT nFlags, CPoint point)
{
CListCtrl::OnMouseMove(nFlags, point);
LVHITTESTINFO pos;
pos.pt = point;
pos.flags = LVHT_ABOVE;
int curcol = -1;
int currow = -1;
if (this->SubItemHitTest(&pos)>=0)
{
curcol = pos.iSubItem;
currow = pos.iItem;
if (m_PreCol ==-1)
m_PreCol = curcol;
if (m_PreRow== -1)
m_PreRow = currow;
if (currow==0 && m_Valid == FALSE )
{
m_Valid = TRUE;
CRect rect;
GetSubItemRect(pos.iItem,pos.iSubItem,LVIR_BOUNDS ,rect);
int cWidth = rect.right;
if (curcol==0)
{
cWidth = this->GetColumnWidth(0); //獲取第一列的寬度
}
RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));
GetSubItemRect(m_PreRow,m_PreCol,LVIR_BOUNDS ,rect);
cWidth = rect.right;
if (m_PreCol==0)
{
cWidth = this->GetColumnWidth(0); //獲取第一列的寬度
}
RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));
}
else if (m_PreCol != curcol ||(m_PreRow != currow)) //用戶移動鼠標時列發(fā)生了改變//或行發(fā)生了改變
{
CRect rect;
GetSubItemRect(pos.iItem,pos.iSubItem,LVIR_BOUNDS ,rect);
int cWidth = rect.right;
if (curcol==0)
{
cWidth = this->GetColumnWidth(0); //獲取第一列的寬度
}
RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));
GetSubItemRect(m_PreRow,m_PreCol,LVIR_BOUNDS ,rect);
cWidth = rect.right;
if (m_PreCol==0)
{
cWidth = this->GetColumnWidth(0); //獲取第一列的寬度
}
RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));
}
m_PreCol = curcol;
m_PreRow = currow;
}
}
0290 具有背景的列表視圖控件
在使用列表視圖控件時,默認情況下,列表視圖控件具有白色的背景,如果能夠在列表視圖中以圖片為背景,界面效果會好很多。本例實現(xiàn)了一個具有背景的列表視圖控件,如圖5.35所示。

圖5.35 具有背景的列表視圖控件
有些用戶可能認為只要從CListCtrl派生一個子類,然后在WM_PAINT消息處理函數(shù)中繪制一幅圖片就可以了。但是,這樣會導(dǎo)致列表視圖中的數(shù)據(jù)被背景圖片覆蓋。其實,實現(xiàn)具有背景的列表視圖控件并不復(fù)雜,首先在程序初始化時調(diào)用AfxOleInit()函數(shù)初始化Com;然后調(diào)用CListCtrl的SetBkImage方法設(shè)置背景位圖;最后調(diào)用SetTextBkColor方法將文本背景透明。程序主要代碼如下:
m_List.SetBkImage("c:\\background2.bmp");
m_List.SetTextBkColor(CLR_NONE);