0

为了改变 MFC 复选框和单选按钮的外观(背景颜色和文本前景色),我使用了以下实现,它在 Windows2000 中运行良好,在 Windows XP 中正常,但在 Windows 7 中不行:

BEGIN_MESSAGE_MAP(mycheckbox, CButton)
  ...
  ON_WM_CTLCOLOR_REFLECT()
  ...
END_MESSAGE_MAP()

HBRUSH mycheckbox::CtlColor(CDC* pDC, UINT nCtlColor)
{
  pDC->SetBkColor( RGB( 255, 0, 0 ) );
  pDC->SetTextColor( RGB( 0, 255, 0 ) );
  return m_brush;
}

只要使用 Windows 经典主题,它就可以正常工作。但是,当使用不同的主题时:

  • Windows XP 中的症状:SetBkColor有效但SetTextColor被忽略
  • Windows 7 中的症状:两者SetBkColorSetTextColor被忽略

我尝试 OnEraseBkgnd 用自定义颜色 ( pDC->FillSolidRect) 填充背景,但即使这样也没有效果。

我想避免使用 ownerdrawn OnPaint,以便复选标记和单选标记继续遵循 Windows 中活动的主题。如前所述,此代码用于 W2000、Windows Xp、Vista 和 Windows 7... 我只是想更改背景颜色和文本颜色。

4

4 回答 4

1

CButton 除了调用 Windows 默认窗口 proc 来绘制按钮外,什么都不做。你可以重写 OnPaint 代码来做你自己的事情,但我可以理解你为什么要避免这种情况——在各种不同的情况下获得正确的外观需要做很多工作。

MFC 提供了另一个类 CMFCButton ,它有一个可覆盖的方法OnFillBackground,看看它是否适合你。

于 2011-11-14T16:15:46.123 回答
1

I have written a CButton that will use ownerdraw when theming is active in Windows (that is not the case when Windows Classic is used), and will do so dynamically. This sample code is not complete but it demonstrates everything needed to get the results.

The difficult part is that you need to represent highlighted and pressed states, see the parameters for DrawCheckBox. I am ignoring them as they will not be entirely missed in my application.

IMPLEMENT_DYNAMIC(mycheckbox, CButton)

mycheckbox::mycheckbox()
  : mv_bIsChecked( false )
{
  m_brush.CreateSolidBrush( RGB( 0,0,255) );
}

mycheckbox::~mycheckbox()
{
}

BEGIN_MESSAGE_MAP(mycheckbox, CButton)
  ON_WM_CTLCOLOR_REFLECT()
  ON_WM_PAINT()
  ON_CONTROL_REFLECT(BN_CLICKED, &mycheckbox::OnBnClicked)
END_MESSAGE_MAP()

HBRUSH mycheckbox::CtlColor(CDC* pDC, UINT nCtlColor)
{
  pDC->SetBkColor( RGB( 255, 0, 0 ) );   // text background color
  pDC->SetTextColor( RGB( 0, 255, 0 ) ); // text foreground color
  return m_brush;                        // control background
}

void mycheckbox::DrawItem(LPDRAWITEMSTRUCT)
{
}

void mycheckbox::OnPaint()
{
  if( ( GetStyle() & BS_OWNERDRAW ) == BS_OWNERDRAW )
  {
    CPaintDC dc( this );

    RECT rect;
    GetClientRect( & rect );
    rect.right = rect.left + 20;
    CMFCVisualManager::GetInstance()->DrawCheckBox(
                & dc
              , rect
              , false                               // highlighted
              , mv_bIsChecked ? 1 : 0 // state
              , true                                // enabled
              , false                               // pressed
              );

    // draw text next to the checkbox if you like
  }
  else
    __super::OnPaint();
}

  // when BS_OWNERDAW is active,
  // GetCheck() is reporting a wrong value
  // so we have to do a little bookkeeping ourselves
void mycheckbox::OnBnClicked()
{
  mv_bIsChecked = ! mv_bIsChecked;
}

BOOL mycheckbox::PreCreateWindow( CREATESTRUCT & cs )
{
  CString lv_szValue;
  CSettingsStore lv_Registry( FALSE, TRUE );
  lv_Registry.Open( _T("Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager") );
  lv_Registry.Read( _T("ThemeActive"), lv_szValue );
  lv_Registry.Close();
  if( lv_szValue == _T("1") )
    cs.style |= BS_OWNERDRAW;

  return CButton::PreCreateWindow(cs);
}

I even tried runtime theme switching, however that gives undesired effect when switching from Windows 7 theme to Windows Classic (checkbox then looks like a regular button). So I am not using this but maybe it is interesting to share:

void mycheckbox::OnNMThemeChanged( NMHDR * pNMHDR, LRESULT * pResult )
{
  CString lv_szValue;
  CSettingsStore lv_Registry( FALSE, TRUE );
  lv_Registry.Open( _T("Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager") );
  lv_Registry.Read( _T("ThemeActive"), lv_szValue );
  lv_Registry.Close();

  ModifyStyle( BS_OWNERDRAW, 0 ); // turn off
  if( lv_szValue == _T("1") )
    ModifyStyle( 0, BS_OWNERDRAW ); // turn on

  // This feature requires Windows XP or greater.
  // The symbol _WIN32_WINNT must be >= 0x0501.
  // TODO: Add your control notification handler code here
  *pResult = 0;
}
于 2011-11-16T16:24:46.257 回答
0

根据这篇微软文章

To change the background color of a button control in Windows 3.0 and later, it is necessary to create an owner draw button.

If you want specific controls to appear differently, I think it is better to subclass it.

This article will be helpful to understand subclassing.

Here to change the background and text color of a button you will need to derive a new class from CButton say MyButton and override its DrawItem function to add the code for drawing that particular control in your way.

Note: you will need to set owner draw property (BS_OWNERDRAW) to those controls.

于 2011-11-15T12:08:05.497 回答
0

Trust me, the best solution here is to have a CStatic with the text and a CButton containing only the check box. As of Vista, changing the background of checkboxes is a problem, to say the least.

于 2011-11-16T13:03:41.123 回答