简体   繁体   English

Mfc CComboBoxEx - 如何更改背景颜色

[英]Mfc CComboBoxEx - How to change the background color

I have a class that is derived from CComboBoxEx and I'm trying to change the background color.我有一个派生自 CComboBoxEx 的 class,我正在尝试更改背景颜色。 I was thinking that it would work like a ComboBox (using the SetBkColor function), but it doesn't change the background color.我在想它会像 ComboBox 一样工作(使用 SetBkColor 函数),但它不会改变背景颜色。

Here's what I have tried:这是我尝试过的:

    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;
         }

I've tried with OnEraseBkgnd() too and it didn't worked either.我也尝试过OnEraseBkgnd() ,但也没有用。

Do I need to subclass a derived CComboBox class and set the background color in that class?我是否需要对派生的 CComboBox class 进行子类化并在该 class 中设置背景颜色?

Thx.谢谢。

The problem here is that WM_CTLCOLOR messages are sent to the parent window (dialog box, probably) of your combo control, not to the control itself;这里的问题是WM_CTLCOLOR消息被发送到您的组合控件的window (可能是对话框),而不是控件本身; also, in the case of the drop-down 'list-box' part of the combo, this message is not sent (as the dialog doesn't need to draw it unless the control has been activated).此外,在组合的下拉“列表框”部分的情况下,不会发送此消息(因为对话框不需要绘制它,除非控件已被激活)。

The way I have achieved what you want is by making the control owner-draw and then (manually) drawing each item in the list.我实现您想要的方法是使控件所有者绘制,然后(手动)绘制列表中的每个项目。

First, you need to add the CBS_OWNERDRAWFIXED style to your control in the .rc / .rc2 script;首先,您需要在.rc / .rc2脚本中将CBS_OWNERDRAWFIXED样式添加到您的控件中; like this, for a typical combo:像这样,对于一个典型的组合:

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

Then, you need to add ON_WM_DRAWITEM() to the message map for your dialog class, and override its OnDrawItem() member.然后,您需要为您的对话框 class 将ON_WM_DRAWITEM()添加到消息 map 中,并覆盖其OnDrawItem()成员。 Note that the message is sent once for each item in the drop-down list, when the list is made visible by user-action:请注意,当用户操作使列表可见时,将为下拉列表中的每个项目发送一次消息:

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;
    }
}

The DrawDropDownBox() does all the hard work: 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;
}

The code shown handles both disabled combos and selected items in the list;显示的代码处理列表中禁用的组合和选定的项目; you could possibly skip some of these, if you want to simplify the operation.如果您想简化操作,您可以跳过其中的一些。

Feel free to ask for further explanation and/or clarification.随时要求进一步解释和/或澄清。

If it's all about just changing Bk color of the control, then you have to handle WM_CTLCOLOR message in control's parent window:如果只是改变控件的 Bk 颜色,那么您必须在控件的父级 window 中处理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;
}

Otherwise, you have to draw your control totally yourself in your derived class, with CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE style, which is way more complicated, but ofc possible.否则,您必须在派生的 class 中完全自己绘制控件,使用CBS_OWNERDRAWFIXEDCBS_OWNERDRAWVARIABLE样式,这要复杂得多,但也可以。

I'm surprised that all the answers you've got so far suggest either handling WM_CTLCOLOR in parent window or use one of OWNERDRAW styles.我很惊讶到目前为止你得到的所有答案都建议在父 window 中处理WM_CTLCOLOR或使用OWNERDRAW styles 之一。

Handling WM_CTLCOLOR in parent window means you'll need to duplicate that code in each parent window's class where you'll use such combobox.在父窗口 window 中处理WM_CTLCOLOR意味着您需要在每个父窗口的 class 中复制该代码,您将在其中使用此类 combobox。 This is obviously a bad solution if you want to use combobox more than once.如果您想多次使用 combobox,这显然是一个糟糕的解决方案。

Adding OWNERDRAW style may have impact on other existing controls that you'd like to subclass and you may need to handle additional problems.添加OWNERDRAW样式可能会影响您想要子类化的其他现有控件,并且您可能需要处理其他问题。 That's also far from a sinmple solution.这也远不是一个简单的解决方案。

Fortunately, there's another way to solve it - use Message Reflection .幸运的是,还有另一种解决方法——使用Message Reflection And all you need to do is to add ON_WM_CTLCOLOR_REFLECT() entry to the message map and CtlColor handler.您需要做的就是将ON_WM_CTLCOLOR_REFLECT()条目添加到消息 map 和CtlColor处理程序中。

In case of combobox control I'd do it like this:在 combobox 控制的情况下,我会这样做:

MyComboBoxEx.h 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我的组合框Ex.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);
}

Here's how such combobox looks:以下是 combobox 的外观:

在此处输入图像描述

If you want to have custom color for borders and glyph then you need to handle WM_PAINT yourself.如果您想为边框和字形自定义颜色,那么您需要自己处理 WM_PAINT。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM