CButton 实现重绘时需要注意(转)

最近在做一个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的设置图片的方法来的省时省力

原文地址:https://www.cnblogs.com/rainbowzc/p/1749867.html