基于Button的控件怎么改变他的背景色?

时间:2020-12-08 19:44:08
我重写了OnDraw函数根本不调用。
重写了OnPaint函数,虽然调用但不改变背景色,为啥?
代码如下:
LRESULT CButtonRainny::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
HWND hWnd = m_hWnd;
HDC hDC= GetDC();
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255));
SelectObject(hDC,hBrush);
Rectangle(hDC,0,0,100,100);
return 0;
}
hDC和m_hWnd都非空。

21 个解决方案

#1


重写OnEraseBkGnd

#2


细节请参考window消息:WM_ERASEBKGND

#3


处理 WM_CTLCOLOR 


//成员变量 HBRUSH m_brushRed; 
CXXXDlg::OnInitDialog()
{
    m_brushRed = ::CreateSolidBrush(RGB(255,0,0)); 
}

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

// TODO:  Change any attributes of the DC here
if ( GetDlgItem(IDC_BUTTION1) == pWnd )
        {
             return m_brushRed;
        }

// TODO:  Return a different brush if the default is not desired
return hbr;
}

CXXXDlg:OnDestroy
{
    if (NULL != m_brushRed)
        DeleteObject(m_brushRed);
}

#4


直接在OnCtlColor函数中处理,或者可以重新绘制按钮.

#5


重载CButton类修改颜色的方法可参考:

http://www.codeguru.com/Cpp/controls/buttonctrl/article.php/c2087

#6


拜托各位,我的Button是基于Button的ATL控件,没有WM_CTLCOLOR事件。
WM_ERASEBKGND我试过了,依然没有用。
还有人知道么?

#7


学习了

#8


其实我一向觉得button的话加图片不就好了
比单纯的button控件美观很多
也不用考虑背景色问题

#9


把你的代码放在OnDraw函数里边肯定是可以的。

重载:
HRESULT CButtonRainny::OnDraw(ATL_DRAWINFO& di)

HDC hdc = di.hdcDraw;
RECT& rc = *(RECT*)di.prcBounds;

#10


HWND hWnd = m_hWnd; 
HDC hDC= GetDC();   //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd)
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100);

OnDraw函数是需要在OnPaint中调用才执行的

你要看到效果就应该刷新控件,这样OnPaint才会被执行
void  CXXButton::SetBkColor(COLORREF clrBkgndColor)
{
       ....
       Invalidate();
}

#11


我一开始就写了
引用楼主 gfz 的帖子:
我重写了OnDraw函数根本不调用。 

#12


改变背景色有这么麻烦??我以为只要.setBackgroundcolor之类的就可以了。

#13


引用 10 楼 Ryanwen 的回复:
HWND hWnd = m_hWnd; 
HDC hDC= GetDC();  //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd) 
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100); 

OnDraw函数是需要在OnPaint中调用才执行的 

你要看到效果就应该刷新控件,这样On…

正解

#14




看看我的这段代码吧,祝好运。

// 自定义的按钮函数
void CCGStoreBmpDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{

CString strText;

// 窗体坐标
RECT WRect;
this->GetWindowRect(&WRect);

// 按钮坐标
RECT BRect;

switch( lpDrawItemStruct->CtlID )
{
case IDC_Start: m_bStart.GetWindowTextA(strText); m_bStart.GetWindowRect(&BRect); break;
case IDC_Stop: m_bStop.GetWindowTextA(strText); m_bStop.GetWindowRect(&BRect); break;

default: m_bLoad_For_motion.GetWindowTextA(strText); m_bLoad_For_motion.GetWindowRect(&BRect); break;
}



// 定义变量
CDC * pDC   = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;



// 画控制效果
if (state & ODS_SELECTED)
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED );
else
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_BUTTONPUSH );

// 将绘图展开为按钮大小
rect.DeflateRect( CSize(  GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE) ) );
    
// 填充颜色到按钮
int butter_color = BRect.top - WRect.top ;
pDC->FillSolidRect(rect, RGB( 0x08+butter_color/20, 
                          0x4C+butter_color/8, 
  0xAF+butter_color/20 )); 



// 绘制文本
if (!strText.IsEmpty())
{
CSize Extent = pDC->GetTextExtent(strText);
CPoint pt( rect.CenterPoint().x - Extent.cx/2, 
           rect.CenterPoint().y - Extent.cy/2 );

if (state & ODS_SELECTED) 
pt.Offset(2,2);

int nMode = pDC->SetTextColor( RGB(0xFF,0xFF,0xFF) );

if (state & ODS_DISABLED)
pDC->DrawState( pt, Extent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL );
else
pDC->TextOut( pt.x, pt.y, strText );

pDC->SetBkMode( nMode );
}


}

#15


引用 6 楼 gfz 的回复:
拜托各位,我的Button是基于Button的ATL控件,没有WM_CTLCOLOR事件。 
WM_ERASEBKGND我试过了,依然没有用。 
还有人知道么?


可以在父窗口中重载WM_CTLCOLORBTN消息

你在WTL中对于BUTTON的绘制大致可以采用以下几种方法。

1、就是所谓的超类化了,重载WM_ERASEBKGND和WM_PAINT,不过这种方法比较极端,很少采用
class CMyButton : public CWindowImpl<CMyButton, CButton>
{
public:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkGround)
END_MSG_MAP()
public:
LRESULT OnEraseBkGround(UINT,WPARAM,LPARAM,BOOL&)
{
return TRUE;
}
LRESULT OnPaint(UING,WPARAM,LPARAM,BOOL&)
{
WTL::CPaintDC dc(m_hWnd);
RECT rt ={0}
GetClientRect(&rt);
dc.FillSolidRect(&rt,RGB(255,0,0)); // 绘制红底背景
        //..... 绘制文本,图片等,还需要考虑鼠标的各种状态
}
};
然后将该类添加到你的对话框中,用SubClassWindow()方法钩入。

2、 就是在对话框中的实现WM_CTLCOLORBTN, 或者将消息反射到你的按钮类中处理。
class CXXXDlg : public CDialogImpl<CXXXDlg>
{
public: 
       WTL::CBrush m_brButton;
public:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
END_MSG_MAP()
public:
LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (m_brButton.IsNull())
m_brButton.CreateSolidBrush(RGB(255,0,0)); // 创建画刷,画刷不能是函数局部变量,不然会造成GDI资源泄漏
        return (LRESULT)m_brButton.m_hBrush;
}

};

你也可以将WM_CTLCOLORBTN消息反射到你自己定义的类中, 那你需要在MESSAGE_MAP加入REFLECT_NOTIFICATIONS()反射
那么你类中的消息号就不能用WM_CTLCOLORBTN要改成OCM_CTLCOLORBTN 

3、可以在父窗口中采用OWNERDRAW, 重载WM_DRAWITEM消息绘制

4、可以在父窗口中采用CustomDraw绘制, 重载NM_CUSTOMDRAW消息,自己按阶段绘制,MSDN中有例子。

#16


谁有这个问题的ATL代码。我看看

#17


还有就是基于Button的ATL控件怎么设置它的风格,使其成为PushButton?

#18


引用 10 楼 Ryanwen 的回复:
HWND hWnd = m_hWnd; 
HDC hDC= GetDC();  //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd) 
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100); 

OnDraw函数是需要在OnPaint中调用才执行的 

你要看到效果就应该刷新控件,这样On…

我确定OnPaint被执行了,但没有改变颜色

#19


OnDraw里那个有个 rcRect,用它做范围去画

#20


弱弱的问一下:

按钮的客户去已经占去整个Window Rect了, 你整背景色有啥用呢?


#21


OnDraw不被执行,我还要说多少次!
我是用ATL做的Button控件当作可点击的颜色方块,使其还有PushButton的效果。可没法改变颜色

#1


重写OnEraseBkGnd

#2


细节请参考window消息:WM_ERASEBKGND

#3


处理 WM_CTLCOLOR 


//成员变量 HBRUSH m_brushRed; 
CXXXDlg::OnInitDialog()
{
    m_brushRed = ::CreateSolidBrush(RGB(255,0,0)); 
}

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

// TODO:  Change any attributes of the DC here
if ( GetDlgItem(IDC_BUTTION1) == pWnd )
        {
             return m_brushRed;
        }

// TODO:  Return a different brush if the default is not desired
return hbr;
}

CXXXDlg:OnDestroy
{
    if (NULL != m_brushRed)
        DeleteObject(m_brushRed);
}

#4


直接在OnCtlColor函数中处理,或者可以重新绘制按钮.

#5


重载CButton类修改颜色的方法可参考:

http://www.codeguru.com/Cpp/controls/buttonctrl/article.php/c2087

#6


拜托各位,我的Button是基于Button的ATL控件,没有WM_CTLCOLOR事件。
WM_ERASEBKGND我试过了,依然没有用。
还有人知道么?

#7


学习了

#8


其实我一向觉得button的话加图片不就好了
比单纯的button控件美观很多
也不用考虑背景色问题

#9


把你的代码放在OnDraw函数里边肯定是可以的。

重载:
HRESULT CButtonRainny::OnDraw(ATL_DRAWINFO& di)

HDC hdc = di.hdcDraw;
RECT& rc = *(RECT*)di.prcBounds;

#10


HWND hWnd = m_hWnd; 
HDC hDC= GetDC();   //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd)
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100);

OnDraw函数是需要在OnPaint中调用才执行的

你要看到效果就应该刷新控件,这样OnPaint才会被执行
void  CXXButton::SetBkColor(COLORREF clrBkgndColor)
{
       ....
       Invalidate();
}

#11


我一开始就写了
引用楼主 gfz 的帖子:
我重写了OnDraw函数根本不调用。 

#12


改变背景色有这么麻烦??我以为只要.setBackgroundcolor之类的就可以了。

#13


引用 10 楼 Ryanwen 的回复:
HWND hWnd = m_hWnd; 
HDC hDC= GetDC();  //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd) 
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100); 

OnDraw函数是需要在OnPaint中调用才执行的 

你要看到效果就应该刷新控件,这样On…

正解

#14




看看我的这段代码吧,祝好运。

// 自定义的按钮函数
void CCGStoreBmpDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{

CString strText;

// 窗体坐标
RECT WRect;
this->GetWindowRect(&WRect);

// 按钮坐标
RECT BRect;

switch( lpDrawItemStruct->CtlID )
{
case IDC_Start: m_bStart.GetWindowTextA(strText); m_bStart.GetWindowRect(&BRect); break;
case IDC_Stop: m_bStop.GetWindowTextA(strText); m_bStop.GetWindowRect(&BRect); break;

default: m_bLoad_For_motion.GetWindowTextA(strText); m_bLoad_For_motion.GetWindowRect(&BRect); break;
}



// 定义变量
CDC * pDC   = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;



// 画控制效果
if (state & ODS_SELECTED)
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED );
else
pDC->DrawFrameControl( rect, DFC_BUTTON, DFCS_BUTTONPUSH );

// 将绘图展开为按钮大小
rect.DeflateRect( CSize(  GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE) ) );
    
// 填充颜色到按钮
int butter_color = BRect.top - WRect.top ;
pDC->FillSolidRect(rect, RGB( 0x08+butter_color/20, 
                          0x4C+butter_color/8, 
  0xAF+butter_color/20 )); 



// 绘制文本
if (!strText.IsEmpty())
{
CSize Extent = pDC->GetTextExtent(strText);
CPoint pt( rect.CenterPoint().x - Extent.cx/2, 
           rect.CenterPoint().y - Extent.cy/2 );

if (state & ODS_SELECTED) 
pt.Offset(2,2);

int nMode = pDC->SetTextColor( RGB(0xFF,0xFF,0xFF) );

if (state & ODS_DISABLED)
pDC->DrawState( pt, Extent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL );
else
pDC->TextOut( pt.x, pt.y, strText );

pDC->SetBkMode( nMode );
}


}

#15


引用 6 楼 gfz 的回复:
拜托各位,我的Button是基于Button的ATL控件,没有WM_CTLCOLOR事件。 
WM_ERASEBKGND我试过了,依然没有用。 
还有人知道么?


可以在父窗口中重载WM_CTLCOLORBTN消息

你在WTL中对于BUTTON的绘制大致可以采用以下几种方法。

1、就是所谓的超类化了,重载WM_ERASEBKGND和WM_PAINT,不过这种方法比较极端,很少采用
class CMyButton : public CWindowImpl<CMyButton, CButton>
{
public:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkGround)
END_MSG_MAP()
public:
LRESULT OnEraseBkGround(UINT,WPARAM,LPARAM,BOOL&)
{
return TRUE;
}
LRESULT OnPaint(UING,WPARAM,LPARAM,BOOL&)
{
WTL::CPaintDC dc(m_hWnd);
RECT rt ={0}
GetClientRect(&rt);
dc.FillSolidRect(&rt,RGB(255,0,0)); // 绘制红底背景
        //..... 绘制文本,图片等,还需要考虑鼠标的各种状态
}
};
然后将该类添加到你的对话框中,用SubClassWindow()方法钩入。

2、 就是在对话框中的实现WM_CTLCOLORBTN, 或者将消息反射到你的按钮类中处理。
class CXXXDlg : public CDialogImpl<CXXXDlg>
{
public: 
       WTL::CBrush m_brButton;
public:
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
END_MSG_MAP()
public:
LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (m_brButton.IsNull())
m_brButton.CreateSolidBrush(RGB(255,0,0)); // 创建画刷,画刷不能是函数局部变量,不然会造成GDI资源泄漏
        return (LRESULT)m_brButton.m_hBrush;
}

};

你也可以将WM_CTLCOLORBTN消息反射到你自己定义的类中, 那你需要在MESSAGE_MAP加入REFLECT_NOTIFICATIONS()反射
那么你类中的消息号就不能用WM_CTLCOLORBTN要改成OCM_CTLCOLORBTN 

3、可以在父窗口中采用OWNERDRAW, 重载WM_DRAWITEM消息绘制

4、可以在父窗口中采用CustomDraw绘制, 重载NM_CUSTOMDRAW消息,自己按阶段绘制,MSDN中有例子。

#16


谁有这个问题的ATL代码。我看看

#17


还有就是基于Button的ATL控件怎么设置它的风格,使其成为PushButton?

#18


引用 10 楼 Ryanwen 的回复:
HWND hWnd = m_hWnd; 
HDC hDC= GetDC();  //在这里GetDC返回的是CDC*,不是HDC句柄,返回句柄应该用::GetDC(m_hWnd) 
HBRUSH hBrush = CreateSolidBrush(RGB(0,128,255)); 
SelectObject(hDC,hBrush);    //调用全局函数还是加双引号,好区分 ::SelectObject(hDC,hBrush); 
Rectangle(hDC,0,0,100,100);  //::Rectangle(hDC,0,0,100,100); 

OnDraw函数是需要在OnPaint中调用才执行的 

你要看到效果就应该刷新控件,这样On…

我确定OnPaint被执行了,但没有改变颜色

#19


OnDraw里那个有个 rcRect,用它做范围去画

#20


弱弱的问一下:

按钮的客户去已经占去整个Window Rect了, 你整背景色有啥用呢?


#21


OnDraw不被执行,我还要说多少次!
我是用ATL做的Button控件当作可点击的颜色方块,使其还有PushButton的效果。可没法改变颜色