更改突出顯示的項目的顏色和字體比看起來更復雜,因爲突出顯示所選項目是系統範圍的問題。事實上,這是我延遲了很長一段時間,直到今天我終於解決了......
有至少兩種方法來改變列表控件的外觀:所有者繪製(你必須自己做繪圖)和自定義繪製(系統會告訴您何時它將執行繪圖的一些步驟,並允許您更改顏色,字體等)。這個答案是關於自定義繪製。 This article covers the basics of using Custom Draw with CListCtrl.
如何更改CListCtrl中的高亮顏色(不是CMFCListCtrl,我們很快會達到)解釋爲in this other article。這是你必須做的步驟:
- 攔截列表視圖畫例行之前它是要繪製一個突出顯示的行(項目)。
- 關閉高亮顯示的行。
- 將行顏色設置爲任何你想要的。
- 讓列表視圖繪製行。
- 截取列表視圖繪製例程後,它已繪製行(後繪製項目)。
- 打開這一行的高亮顯示。
所以,當列表視圖將要繪製一個突出顯示的行,你必須telll系統中的行不突出,因此不使用系統顏色畫它,告訴使用什麼顏色,並將該項目再次突出顯示,以便選擇正常工作。
以下是這篇文章的代碼:
COLORREF g_MyClrFgHi; // My foreground hilite color
COLORREF g_MyClrBgHi; // My background hilite color
HWND g_hListView; // Window handle of listview control
void EnableHighlighting(HWND hWnd, int row, bool bHighlight)
{
ListView_SetItemState(hWnd, row, bHighlight? 0xff: 0, LVIS_SELECTED);
}
bool IsRowSelected(HWND hWnd, int row)
{
return ListView_GetItemState(hWnd, row, LVIS_SELECTED) != 0;
}
bool IsRowHighlighted(HWND hWnd, int row)
{
// We check if row is selected.
// We also check if window has focus. This was because the original listview
// control I created did not have style LVS_SHOWSELALWAYS. So if the listview
// does not have focus, then there is no highlighting.
return IsRowSelected(hWnd, row) && (::GetFocus(hWnd) == hWnd);
}
BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
static bool bIsHighlighted = false;
*pResult = 0;
NMHDR *p = (NMHDR *)lParam;
switch (p->code)
{
...
case NM_CUSTOMDRAW:
NMLVCUSTOMDRAW *lvcd = (NMLVCUSTOMDRAW *)p;
NMCUSTOMDRAW &nmcd = lvcd->nmcd;
switch (nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
// We want item prepaint notifications, so...
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int iRow = (int)nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(g_hListView, iRow);
if (bHighlighted)
{
lvcd->clrText = g_MyClrFgHi; // Use my foreground hilite color
lvcd->clrTextBk = g_MyClrBgHi; // Use my background hilite color
// Turn off listview highlight otherwise it uses the system colors!
EnableHighlighting(g_hListView, iRow, false);
}
// We want item post-paint notifications, so...
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
if (bHighlighted)
{
int iRow = (int)nmcd.dwItemSpec;
// Turn listview control's highlighting back on now that we have
// drawn the row in the colors we want.
EnableHighlighting(g_hListView, iRow, true);
}
*pResult = CDRF_DODEFAULT;
break;
}
default:
*pResult = CDRF_DODEFAULT;
break;
}
break;
...
}
}
這正常工作與一個CListCtrl,但你問一個CMFCListCtrl。問題是CMFCListCtrl已經要求得到有關NM_CUSTOMDRAW通知的通知(當它調用OnGetCellTextColor
函數時,正如你所見)。如果你在你自己的CMFCListCtrl派生類中爲那個創建了一個處理程序,那些通知將不會到達CMFCListCtrl,並且你失去了這個功能(根據你的需要它可能沒問題)。
所以這裏是我所做的:我創建了一個NM_CUSTOMDRAW處理程序在我的列表控件中,如果我正在處理突出顯示的行,則更改顏色,否則,我調用CMFCListCtrl :: OnNMCustomDraw( )。你會看到,我只是使用普通的高亮顏色;那是因爲我只是想看看所選項目即使控件沒有焦點:
void CMyListCtrl::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
{
bool callParent = true;
static bool bHighlighted = false;
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
NMCUSTOMDRAW nmcd = lpLVCustomDraw->nmcd;
*pResult = CDRF_DODEFAULT;
switch (lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int row = nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(row);
if (bHighlighted)
{
lpLVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
lpLVCustomDraw->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
EnableHighlighting(row, false);
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
callParent = false;
}
}
break;
case CDDS_ITEMPOSTPAINT:
if (bHighlighted)
{
int row = nmcd.dwItemSpec;
EnableHighlighting(row, true);
callParent = false;
}
*pResult = CDRF_DODEFAULT;
break;
default:
break;
}
if (callParent)
{
__super ::OnCustomDraw(pNMHDR, pResult);
}
}
bool CMyListCtrl::IsRowHighlighted(int row)
{
bool selected = GetItemState(row, LVIS_SELECTED) != 0;
return selected;
}
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
}
我還沒有徹底的測試,但它似乎工作。
UPDATE:
有一個小問題。當您調用EnableHigilighting()時,對話框將獲得LVN_ITEMCHANGED通知,這會觸發重繪,從而觸發LVN_ITEMCHANGED通知...因此,如果您正在偵聽LVN_ITEMCHANGED通知,則可能需要執行下列操作:
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
m_internalStateChange = true;
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
m_internalStateChange = false;
}
void CWhateverDialog::OnLvnItemchangedListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
if (!c_List.InternalStateChange() && /* other conditions */)
{
// Respond to state changes
}
}
爲什麼SetIemState上的0xFF?如果我總結所有'LVIS_ *'標誌遜色於'0xFF':'#定義LVIS_FOCUSED 0x0001' '#定義LVIS_SELECTED 0x0002' '#定義LVIS_CUT 0x0004' '#定義LVIS_DROPHILITED 0x0008' '#定義LVIS_GLOW 0x0010' '#define LVIS_ACTIVATING 0x0020' 我得到'0x3F'! – sergiol
雖然你的方法在這裏不起作用,但我贊成,因爲你的文章似乎在'CListCtrl'自定義繪製上帶有很好的信息。 – sergiol
@sergiol這是0xff,以確保所有的位都設置爲1,但不管它是0xff,0x3f還是隻是LVIS_SELECTED,因爲你告訴第三個參數要使用哪些位。 – MikMik