最近在做一个CheckBox的透明时,遇到了CButton的重绘,网上关于此类问题的帖子比较多,实现方法也比较多。
这里只说一下我在实际操作中遇见的一些问题和解决方法。
1、在窗体中重载WM_CTLCOLOR实现透明时,在某些使用了XP样式风格的系统中,CheckBox出现了黑乎乎的底色,没有真正达到透明效果,具体原因还不清楚,希望高手指点。代码如下:
HBRUSH CTestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: Change any attributes of the DC here
switch(pWnd->GetDlgCtrlID()){
case IDC_CHECK1:
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(0,0,0));
hbr=(HBRUSH)GetStockObject(NULL_BRUSH);
default:
break;
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
2、使用子类化方法,自定义一个类CCheckBoxTrans并继承自CButton,直接重载WM_PAINT消息处理函数,并在Dialog使用处定义MFC成员量CButton m_oCheck并替换为CCheckBoxTrans m_oCheck,具体代码如下:
class CTestDlg : public CDialog
{
public:
CTestDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CTestDlg)
enum { IDD = IDD_TEST_DIALOG };
CCheckBoxTrans m_oCheck;
//}}AFX_DATA
.....
DECLARE_MESSAGE_MAP()
};
void CCheckBoxTrans::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rect;
GetClientRect(&rect);
CRect BoxRect;
BoxRect=rect;
BoxRect.right=BoxRect.left+13;
dc.DrawFrameControl(BoxRect, DFC_BUTTON, DFCS_BUTTONCHECK|(GetCheck()? DFCS_CHECKED: 0));
CFont oFont;
oFont.CreateFontIndirect(&m_sFont);
CBrush* pOldBrush=(CBrush*)dc.SelectObject((HBRUSH)GetStockObject(NULL_BRUSH));
CFont* pOldFont=(CFont*)dc.SelectObject(&oFont);
dc.SetBkMode(TRANSPARENT);
CString StrWndText;
GetWindowText(StrWndText);
rect.OffsetRect(17, 0);
dc.DrawText(StrWndText, &rect, DT_LEFT|DT_VCENTER|DT_SINGLELINE);
//rect.OffsetRect(-3, 0);
//rect.right=3+StrWndText.GetLength()*(GetDeviceCaps(dc.GetSafeHdc(), LOGPIXELSY)*abs(m_sFont.lfHeight)/72);
//if(GetFocus())
//dc.DrawFocusRect(&rect);
dc.SelectObject(pOldFont);
//dc.SelectObject(pOldBrush);
oFont.DeleteObject();
// Do not call CButton::OnPaint() for painting messages
}
这种方法基本达到要求,不过我还是发现有过两个问题:A.当CheckBox被非聚焦时,有时会出现聚焦的虚线框;B.有时候受XP样式风格影响,字体会变大很多,和其他控件的不一样,同时会有文字重叠显示的现象,具体原因还不清楚,希望高手指点。
3、采用OWNERDRAW的Button,同样继承CButton类,不过这次不重载OnPaint函数,而是像网上说的继承WM_DRAWITEM消息的处理函数virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );//注意不是OnDrawItem,千万不要直接用类向导重载WM_DRAWITEM,这两个可不是一个函数。另外需要重载virtual void PreSubclassWindow();具体代码如下:
class CCheckBoxTrans : public CButton
{
// Construction
public:
CCheckBoxTrans();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCheckBoxTrans)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
......
DECLARE_MESSAGE_MAP()
};
void CCheckBoxTrans::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
SetButtonStyle(GetButtonStyle()|BS_OWNERDRAW);
CButton::PreSubclassWindow();
}
void CXPButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
//从lpDrawItemStruct获取控件的相关信息
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC=CDC::FromHandle(lpDrawItemStruct->hDC);
int nSaveDC=pDC->SaveDC();
UINT state = lpDrawItemStruct->itemState;
POINT pt ;
TCHAR strText[MAX_PATH + 1];
::GetWindowText(m_hWnd, strText, MAX_PATH);
//获取按钮的状态
if (state & ODS_FOCUS){
}else{
}
if (state & ODS_SELECTED || state & ODS_DEFAULT){
}
......//绘制控件其他部件(略)
//显示按钮的文本
if (strText!=NULL)
{
CFont* hFont = GetFont();
CFont* hOldFont = pDC->SelectObject(hFont);
CSize szExtent = pDC->GetTextExtent(strText, lstrlen(strText));
CPoint pt( rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2);
if (state & ODS_SELECTED)
pt.Offset(1, 1);
int nMode = pDC->SetBkMode(TRANSPARENT);
if (state & ODS_DISABLED)
pDC->DrawState(pt, szExtent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);
else
pDC->DrawState(pt, szExtent, strText, DSS_NORMAL, TRUE, 0, (HBRUSH)NULL);
pDC->SelectObject(hOldFont);
pDC->SetBkMode(nMode);
}
pDC->RestoreDC(nSaveDC);
}
此外还需要重载鼠标和键盘的一些相关消息,不然控件画出来了,结果不能用:(,千万要注意哦。
所以子类化自绘的方法应该是可以解决这个问题,但是不如直接CMFCButton的设置图片的方法来的省时省力