Mfc CComboBoxEx - 如何更改背景颜色

Posted

技术标签:

【中文标题】Mfc CComboBoxEx - 如何更改背景颜色【英文标题】:Mfc CComboBoxEx - How to change the background color 【发布时间】:2019-11-07 18:35:29 【问题描述】:

我有一个派生自 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 类并在该类中设置背景颜色?

谢谢。

【问题讨论】:

您的消息映射中有WM_CTL_COLOR() 条目吗? WM_CTLCOLOR消息被发送到父对话框;但我认为它不是针对组合的“列表框”部分发送的。我为实现这一目标所做的就是将组合声明为“所有者绘制”,然后进行处理。 @sergiol 是的,我会编辑我的问题,谢谢 :) @Adrian-ReinstateMonica 好的,你正在处理 DrawItem() 函数? 内部组合框由CComboBoxEx::GetComboBoxCtrl返回 【参考方案1】:

这里的问题是WM_CTLCOLOR 消息被发送到组合控件的 窗口(可能是对话框),而不是控件本身;此外,在组合的下拉“列表框”部分的情况下,不会发送此消息(因为对话框不需要绘制它,除非控件已被激活)。

我实现您想要的方式是让控件由所有者绘制,然后(手动)绘制列表中的每个项目。

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

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;

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

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

【讨论】:

我不能在对话框中编码,我可以在课堂上做同样的事情吗? @Emile 可能!您仍然需要在资源脚本(或某处)中添加CBD_OWNERDRAWFIXEDCBS_OWNERDRAWVARIABLE 样式,并提供您自己的void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT pDIS) 类成员。您应该能够以与我展示的代码相同的方式使用pDIS;然后pCBC 就等于您的类对象的this【参考方案2】:

如果只是改变控件的 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 样式,这要复杂得多,但也可以。

【讨论】:

我无法在父对话框中执行此操作,因为我不想每次使用 ComboBoxEx 时都复制代码:/【参考方案3】:

我很惊讶到目前为止您得到的所有答案都建议在父窗口中处理 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);

这种组合框的外观如下:

如果您想为边框和字形设置自定义颜色,那么您需要自己处理 WM_PAINT。

【讨论】:

嗨,抱歉耽搁了,这似乎是最好的解决方案,但是当我尝试它时,由于某种原因它不起作用。我的控件是 CMyComboBoxEx,我已经完成了与您的代码示例完全相同的操作。我错过了什么吗? :S @Emile 我添加了工作示例(但没有更改 CtlColor 逻辑中的任何内容)

以上是关于Mfc CComboBoxEx - 如何更改背景颜色的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 mfc 应用程序的背景属性

MFC:如何更改整个编辑框的背景颜色?

如何更改MFC按钮的背景色

如何在 mfc 中更改 CImage 对象的背景颜色?

C ++ MFC更改单选框的背景颜色

如何在 MFC 自定义控件类中挂钩控件关闭