我有自己的CMFCListCtrl派生类,我实现了
virtual COLORREF OnGetCellTextColor(int nRow, int nColum)
{
CMyClass* pMyClass = (CMyClass*)GetItemData(nRow);
if (pMyClass && pMyClass->m_bDeleted)
return RGB(255, 0, 0);
return __super::OnGetCellTextColor(nRow, nColum);
}
用于标记已删除条目的红色的功能。除了选择项目时,此方法有效。
我转到了void CMFCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
函数并在行上放置了条件为iRow==selected item
的断点
lplvcd->clrText = OnGetCellTextColor(iRow, iColumn);
执行它,然后我为&lplvcd->clrText
创建了一个新的数据断点。
数据断点在功能上被击中
comctl32.dll!SHThemeComputeTextColors()
,因为callstack图像显示:
,这显然会覆盖变量值。
当我在互联网上搜索SHThemeComputeTextColors
并且没有任何内容出现时,有人可以帮助我强制选定的项目文本为红色吗?
答案 0 :(得分:1)
更改突出显示项目的颜色和字体比看起来更复杂,因为突出显示所选项目是系统范围的问题。事实上,这是我拖延了很长时间的事情,就在今天我终于解决了......
至少有两种方法可以更改列表控件的外观:所有者绘制(您必须自己完成所有绘图)和自定义绘制(系统会告诉您何时要执行绘图的某些步骤和让你改变颜色,字体等)。这个答案是关于自定义绘制的。 This article covers the basics of using Custom Draw with CListCtrl.
如何更改CListCtrl中的高亮颜色(不是CMFCListCtrl,我们很快就会这样做)将被解释为in this other article。这些是你必须要做的步骤:
- 在即将绘制突出显示的行(项目)之前截取listview绘制例程。
- 关闭行高亮显示。
- 将行颜色设置为您想要的颜色。
- 让列表视图绘制行。
- 在绘制行(绘制后的项目)之后截取listview绘制例程。
- 重新开启此行的高亮显示。
醇>
因此,当listview即将绘制一个突出显示的行时,你必须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);
}
我还没有对它进行彻底的测试,但似乎有效。
<强>更新强>
有一点问题。当您调用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
}
}