CMFCListCtrl强制选中的项目为红色

时间:2016-12-19 12:02:26

标签: mfc selecteditem visual-c++-2010 textcolor clistctrl

我有自己的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图像显示:

callstack

,这显然会覆盖变量值。

当我在互联网上搜索SHThemeComputeTextColors并且没有任何内容出现时,有人可以帮助我强制选定的项目文本为红色吗?

1 个答案:

答案 0 :(得分:1)

更改突出显示项目的颜色和字体比看起来更复杂,因为突出显示所选项目是系统范围的问题。事实上,这是我拖延了很长时间的事情,就在今天我终于解决了......

至少有两种方法可以更改列表控件的外观:所有者绘制(您必须自己完成所有绘图)和自定义绘制(系统会告诉您何时要执行绘图的某些步骤和让你改变颜色,字体等)。这个答案是关于自定义绘制的。 This article covers the basics of using Custom Draw with CListCtrl.

如何更改CListCtrl中的高亮颜色(不是CMFCListCtrl,我们很快就会这样做)将被解释为in this other article。这些是你必须要做的步骤:

  
      
  1. 在即将绘制突出显示的行(项目)之前截取listview绘制例程。
  2.   
  3. 关闭行高亮显示。
  4.   
  5. 将行颜色设置为您想要的颜色。
  6.   
  7. 让列表视图绘制行。
  8.   
  9. 在绘制行(绘制后的项目)之后截取listview绘制例程。
  10.   
  11. 重新开启此行的高亮显示。
  12.   

因此,当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
    }
}