續第一篇:MFC總結之CListCtrl用法及技巧(一)
http://blog.csdn.net/zwgdft/article/details/7560592
本篇重點介紹:禁止拖動表頭、讓第一列居中顯示、設置行高與字體、虛擬列表技術、點擊表頭時進行歸類、向上與向下移動、動態調整大小問題、避免閃爍問題。
6、禁止拖動表頭
重載OnNotify消息響應函數,屏蔽兩個消息通知碼:HDN_BEGINTRACKW 和HDN_DIVIDERDBLCLICKW。示例如下:BOOL CXXXX::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// TODO: Add your specialized code here and/or call the base class
//屏蔽兩個消息通知碼,使得禁止拖動List表頭
NMHEADER* pNMHeader = (NMHEADER*)lParam;
if(((pNMHeader->hdr.code == HDN_BEGINTRACKW) |
(pNMHeader->hdr.code == HDN_DIVIDERDBLCLICKW)))
{
*pResult = TRUE;
return TRUE;
}
return CDialog::OnNotify(wParam, lParam, pResult);
}
7、讓第一列居中顯示
在插入列時,我們可以通過參數nFormat來設置文本居中顯示,但是這種設置對於第一列是沒有作用的。這時我們可以考慮將我們的內容從第二列開始插入(設置爲居中顯示)。先插入第一列,然後刪除第一列,這樣原先的第二列就充當了第一列。8、設置行高和字體
設置CListCtrl的行高沒有函數接口,可以通過自繪來實現,但是比較麻煩。有一個比較簡單的方法是通過使用一個空白的圖像將行撐起來,使其高度發生變化。示例如下:CImageList m_image;
m_image.Create(1,24,ILC_COLOR32,1,0);
m_listInfo.SetImageList(&m_image, LVSIL_SMALL);
對於字體的設置,我們可以使用SetFont函數來實現。以修改CListView的字體爲例,在OnInitialUpdate函數中插入列之前調用SetFontSelf函數(該函數自定義,如下示例所示)。首先創建一個字體,然後調用SetFont進行設置。需要注意的是,在退出時需要delete 掉創建的字體,避免內存泄露。//設置字體和大小
void CMyListView::SetFontSelf(int nHeight, LPCTSTR lpszFacename)
{
//先刪除原有字體
if(m_font != NULL)
delete m_font;
m_font = new CFont;
//創建字體
m_font->CreateFont(
nHeight, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
lpszFacename); // lpszFacename
//設置字體
CListCtrl &theCtrl = GetListCtrl(); //獲取控制權,引用變量
theCtrl.SetFont(m_font, TRUE);
}
9、虛擬列表技術
給一個鏈接,介紹的比較詳細:http://hi.baidu.com/qi_xian/blog/item/929b04ce27d02c0592457ef8.html當數據量大時,使用InsertItem插入數據的過程是很漫長的。這時我們有兩個方法來解決該問題:一是使用CListCtrl的虛擬列表技術,二是採用分頁顯示的方法。對於虛擬列表技術,上述鏈接中的文章講的很詳細,我用過它的比較簡單的方法,後來改用了分頁方法。
使用虛擬列表技術,有三點需要搞清楚:
① 使用虛擬技術時,需要將CListCtrl控件的Owner Data屬性設置爲ture。
② 給虛擬列表添加元素時,不需要使用InserItem函數,通過調用SetItemCount設置數據總個數,然後由系統產生不同的消息,在相應的消息響應函數中完成插入工作。
③ 虛擬列表向父窗口發送的消息有三種: ⑴ 當它需要數據時,發送LVN_GETDISPINFO消息; ⑵ 當用戶試圖查找某個元素時,發送LVN_ODFINDITEM消息; ⑶當需要緩衝數據時,發送 LVN_ODCACHEHINT消息。
當我們使用LVN_GETDISPINFO 的消息處理函數來插入元素時,必須首先檢查列表請求的是什麼數據(如LVIF_TEXT、LVIF_IMAGE等),然後插入不同的子項。示例如下:
void CDataAnalysis::OnLvnGetdispinfoAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: Add your control notification handler code here
LV_ITEM* pItem= &(pDispInfo)->item;
int iItemIndex= pItem->iItem;
size_t converted = 0;
wchar_t wStr[30]; //Unicode字符串
if (pItem->mask & LVIF_TEXT) //字符串緩衝區有效
{
switch(pItem->iSubItem)
{
case 0: //填充數據項的名字,xxxxx表示要填充的字符
mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);
lstrcpy(pItem->pszText,wStr);
break;
case 1: //填充子項1
mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);
lstrcpy(pItem->pszText,wStr);
break;
case 2: //填充子項2
mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);
lstrcpy(pItem->pszText,wStr);
break;
case 3: //填充子項3
lstrcpy(pItem->pszText,xxxxxx);
break;
}
}
*pResult = 0;
}
10、點擊表頭時進行歸類排序
系統通過發送LVM_SORTITEMS消息來處理歸類問題,在該消息的處理函數中需要調用一個回調函數,這個回調函數需要我們來設計,以完成不同的歸類方法。回調函數原型如下:int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
針對上述回調函數,有以下幾點需要搞清楚:
① 對於參數lparam1和lparam2,分別爲CListCtrl的兩行數據,是用於比較的對象。通過CListCtrl的成員函數SetItemData來設置,該函數原型:
int SetItemData(int nIndex, DWORD_PTR dwItemData )
其第一個參數爲行號,第二個參數指明瞭該行對應的參數。參數dwItemData 通常設爲一行參數的數組,如: pData[2][2] = {{1, 3},{2, 3}}; 每次使用pData[i]作爲dwItemData。
② 對於參數lParamSort,用於指明列項,即第幾列。該參數和回調函數一同通過CListCtrl的成員函數SortItems來設置,其函數原型爲:
BOOL SortItems( PFNLVCOMPARE pfnCompare,DWORD_PTR dwData )
參數 pfnCompare 爲回調函數入口地址, 參數dwData 爲列項。
③ SetItemData在初始插入數據時進行調用來設置,SortItems則在點擊列表頭時響應的消息處理函數中進行設置。
示例如下:
//初始化列表視圖控件
BOOL CDataAnalysis::InitListCtl()
{
//其他處理,包括設置風格,插入列等等
//插入行
for(int i=0; i<LineNum; i++)
{
//要將char*轉換爲wchar_t*
mbstowcs_s(&converted, wStr, 30, m_analysis[i].Date, _TRUNCATE);
m_listAnalysis.InsertItem(i, wStr); //日期
mbstowcs_s(&converted, wStr, 30, m_analysis[i].Time, _TRUNCATE);
m_listAnalysis.SetItemText(i, 1, wStr); //時間
mbstowcs_s(&converted, wStr, 30, m_analysis[i].ID, _TRUNCATE);
m_listAnalysis.SetItemText(i, 2, wStr); //ID
m_listAnalysis.SetItemText(i, 3, m_analysis[i].lpszEvent); //事件
//設置回調函數的參數
m_listAnalysis.SetItemData(i, (LPARAM)(m_analysis+i));
}
return TRUE;
}
void CDataAnalysis::OnHdnItemclickAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
// TODO: Add your control notification handler code here
//設置回調函數的參數和入口地址
m_listAnalysis.SortItems(SortFunc, phdr->iItem);
*pResult = 0;
}
//排序的回調函數
int CALLBACK SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
int result; //返回值
//兩行的參數,用於比較
ANALYSISFORMAT* pAnalysis1 = (ANALYSISFORMAT*)lParam1;
ANALYSISFORMAT* pAnalysis2 = (ANALYSISFORMAT*)lParam2;
//排序
switch(lParamSort)
{
case 0: //日期
result = strcmp(pAnalysis1->Date, pAnalysis2->Date);
break;
case 1: //時間
result = strcmp(pAnalysis1->Time, pAnalysis2->Time);
break;
case 2: //ID
result = strcmp(pAnalysis1->ID, pAnalysis2->ID);
break;
case 3: //事件
result = wcscmp(pAnalysis1->lpszEvent, pAnalysis2->lpszEvent);
break;
default:
break;
}
return result;
}
11、向上與向下移動
有時需要向上或向下移動表項內容,這裏給出向上移動的方法,向下移動的方法類似。
① 利用第2節所述的內容獲取行號nItem,判斷行號是否爲行首,如果不是行首則進入②;
② 獲取第nItem行的所有子項內容;
③ 刪除第nItem行,並在nItem-1的位置重新插入原先的第nItem行的內容;
④ 使nItem-1的位置高亮顯示
示例如下:
/*************************上移子項**************************/
void CStudyTestDlg::OnPageup()
{
if (nItem == 0)
{
MessageBox("該子項已經位於第一行!");
return;
}
// 提取內容
CString temp[4];
int i;
for(i=0;i<4;i++)
temp[i] = m_ListCtrl.GetItemText(nItem, i);
// 刪除
m_ListCtrl.DeleteItem(nItem);
// 在nItem-1位置處插入
for (i=0; i<4; i++)
m_ListCtrl.SetItemText(nItem-1,i,temp[i]);
//高亮顯示
UINT flag = LVIS_SELECTED|LVIS_FOCUSED;
m_ListCtrl.SetItemState(--nItem, flag, flag);
}
/*************************下移子項**************************/
void CStudyTestDlg::OnPagedown()
{
if (nItem == m_ListCtrl.GetItemCount()-1)
{
MessageBox("該子項已經位於最後一行!");
return;
}
// 提取內容
CString temp[4];
int i;
for (i=0; i<4; i++)
temp[i] = m_ListCtrl.GetItemText(nItem, i);
// 刪除
m_ListCtrl.DeleteItem(nItem);
// 在nItem+1位置處插入
for (i=0; i<4; i++)
m_ListCtrl.SetItemText(nItem+1, i,temp[i]);
//高亮顯示
UINT flag = LVIS_SELECTED|LVIS_FOCUSED;
m_ListCtrl.SetItemState(++nItem, flag, flag);
}
12、避免閃爍問題
這個問題在我的前面一篇博文有提到。http://blog.csdn.net/zwgdft/article/details/7394318
13、動態調整大小
有時由於不確定軟件運行時的電腦屏幕大小,需要根據屏幕大小動態設置CListCtrl控件的大小。動態大小的設置時,需要注意不要將高度和寬度設置的超過區域限制,否則就沒有滾動條了,導致部分內容無法查看。以我遇到的一個例子來說,其情況見第12節提到的那篇博文所述:將View劃分爲三個窗格,在左上角View上有個CPropertySheet,其上有幾個CPropertyPage,每個屬性頁上有個CListCtrl,供用戶查看信息。那麼這時需要設置的CListCtrl的大小即爲:
寬度 = 左上角View寬度
高度 = 左上角View高度 - 屬性頁的Tab項高度
調用MoveWindow函數進行設置即可。
------------------全文完--------------------