Mfc CComboBoxEx-如何更改背景颜色

时间:2019-11-07 18:35:29

标签: c++ mfc

我有一个从CComboBoxEx派生的类,并且我正在尝试更改背景色。我以为它可以像ComboBox一样工作(使用SetBkColor函数),但是它不会更改背景色。

这就是我尝试过的:

    BEGIN_MESSAGE_MAP(CMyComboBoxEx, CComboBoxEx)   
       ON_WM_CTLCOLOR()
    END_MESSAGE_MAP()

     void CMyComboBoxEx::SetBkColor(COLORREF backgroundColor)
         {
            m_backgroundColor = backgroundColor;
            m_brBkgnd.DeleteObject();
            m_brBkgnd.CreateSolidBrush(backgroundColor);
         }    
     HBRUSH CMyComboBoxEx::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
         {
            HBRUSH brush = __super::OnCtlColor(pDC, pWnd, nCtlColor);
            pDC->SetBkColor(RGB(255,0,0));

            return brush;
         }

我也尝试过OnEraseBkgnd(),但也没有用。

我是否需要继承派生的CComboBox类并在该类中设置背景颜色?

谢谢。

3 个答案:

答案 0 :(得分:1)

这里的问题是WM_CTLCOLOR消息被发送到组合控件的 parent 窗口(可能是对话框),而不是控件本身。同样,对于组合的下拉“列表框”部分,也不会发送此消息(因为除非激活控件,否则对话框无需绘制该消息)。

我要实现所需的方法是使控件所有者绘制,然后(手动)绘制列表中的每个项目。

首先,您需要在CBS_OWNERDRAWFIXED / .rc脚本中向控件添加.rc2样式;像这样,对于典型的组合:

COMBOBOX  IDC_IGONG, 224, 68, 52,120,
    CBS_DROPDOWNLIST | CBS_HASSTRINGS | CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP

然后,您需要将ON_WM_DRAWITEM()添加到对话框类的消息映射中,并覆盖其OnDrawItem()成员。请注意,当通过用户操作使列表可见时,该消息将针对下拉列表中的每个项目发送一次:

void MyDialog::OnDrawItem(int nIDCtl, DRAWITEMSTRUCT *pDIS)
{
    switch (pDIS->CtlType) { // You can switch on the ID if it's only one combo!
    case ODT_COMBOBOX:
        DrawDropDownBox(this, nIDCtl, pDIS);
        break;
    default:
        CDialogEx::OnDrawItem(nIDCtl, pDIS);
        break;
    }
}

DrawDropDownBox()完成了所有艰苦的工作:


void MyDialog::DrawDropDownBox(CWnd *box, int nID, DRAWITEMSTRUCT *pDIS)
{
    CComboBox *pCBC = dynamic_cast<CMyComboBoxEx *>(box->GetDlgItem(nID));
    if (pCBC == nullptr) return; // Skip if we can't get handle to the control
    CDC *pDC = CDC::FromHandle(pDIS->hDC);
    wchar_t buffer[4096]; // Or just char if you ain't using Unicode
    if (pCBC->GetLBText(int(pDIS->itemID), buffer) == CB_ERR) return; // Maybe called during WM_DELETEITEM
    int dcSave = pDC->SaveDC(); // Save DC state for later restoration
    CPen pen(PS_SOLID, 0, ListColor); // ListColor is COLORREF for your desired b/g
    if (pDIS->itemState & ODS_DISABLED) {
        pDC->SelectStockObject(NULL_PEN);
        pDC->SelectObject(BackBrush); // A CBrush for disabled: defined/created elsewhere
        pDC->SetBkMode(TRANSPARENT);
    }
    else {
        pDC->SelectObject(&pen);
        pDC->SelectObject(ListBrush); // A CBrush that draws your desired b/g
        pDC->SetBkMode(OPAQUE);
    }
    CRect rc(pDIS->rcItem); pDC->Rectangle(&rc); // This draws the b/g
    if (pDIS->itemState & ODS_DISABLED) {
        pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
    }
    else if (pDIS->itemState & ODS_SELECTED) { // Use Windows defaults if selected...
        pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
        pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
    }
    else {
        pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
        pDC->SetBkColor(ListColor); // Custom b/g color
    }
    unsigned format = DT_SINGLELINE | DT_VCENTER; // You desired text alignment
    pDC->DrawText(CString(buffer), rc, format);
    pDC->RestoreDC(dcSave); // Restore DC's saved state...
    pDC->Detach();          // ...then 'release it'
    return;
}

显示的代码可处理禁用的组合和列表中的选定项目;如果您想简化操作,则可以跳过其中一些。

随时要求进一步的解释和/或澄清。

答案 1 :(得分:1)

如果只需要更改控件的Bk颜色,那么您就必须在控件的父窗口中处理WM_CTLCOLOR消息:

HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd->GetDlgCtrlID() == IDC_MY_CONTROL)
    {
        pDC->SetBkColor(RGB(0, 0, 0)); //Black color
        hbr = m_hbrBlack;              //Black brush.
    }

    return hbr;
}

否则,您必须在自己的派生类中完全使用CBS_OWNERDRAWFIXEDCBS_OWNERDRAWVARIABLE样式绘制控件,这虽然更复杂,但也有可能。

答案 2 :(得分:1)

我很惊讶您到目前为止所获得的所有答案都建议您在父窗口中处理WM_CTLCOLOR或使用OWNERDRAW样式之一。

在父窗口中处理WM_CTLCOLOR意味着您需要在每个使用此组合框的父窗口的类中复制该代码。如果您想多次使用组合框,显然这是一个糟糕的解决方案。

添加OWNERDRAW样式可能会影响您想继承的其他现有控件,并且可能需要处理其他问题。这也不是完美的解决方案。

幸运的是,还有另一种解决方法-使用Message Reflection。您要做的就是将ON_WM_CTLCOLOR_REFLECT()条目添加到消息映射和CtlColor处理程序中。

在组合框控件的情况下,我会这样做:

MyComboBoxEx.h

class CMyComboBoxEx : public CComboBoxEx
{
public:
    CMyComboBoxEx();
    virtual ~CMyComboBoxEx();

protected:

    CBrush m_BkBrush;

    DECLARE_MESSAGE_MAP()
public:
    afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};

MyComboBoxEx.cpp

CMyComboBoxEx::CMyComboBoxEx()
{
    m_BkBrush.CreateSolidBrush(RGB(0, 255, 0)); 
}

CMyComboBoxEx::~CMyComboBoxEx()
{
}

BEGIN_MESSAGE_MAP(CMyComboBoxEx, CComboBoxEx)
    ON_WM_CTLCOLOR_REFLECT()
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()

HBRUSH CMyComboBoxEx::CtlColor(CDC* pDC, UINT nCtlColor)
{
    pDC->SetTextColor(RGB(255, 0, 0));
    pDC->SetBkColor(RGB(0, 255, 0));
    return m_BkBrush;
}

HBRUSH CMyComboBoxEx::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    return CtlColor(pDC, nCtlColor);
}

这是组合框的外观:

enter image description here

如果要为边框和字形使用自定义颜色,则需要自己处理WM_PAINT。

相关问题