Windows 7 忽略 MFC CButton 背景

发布于 2024-12-16 01:54:58 字数 840 浏览 3 评论 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 中的症状:SetBkColor>SetTextColor 被忽略

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

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

To change the appearance (background color and text foreground color) of a MFC checkbox and a radiobutton, I used the following implementation which worked fine in Windows2000, half OK in Windows XP, but not OK in 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;
}

This works fine as long as the Windows Classic theme is used.
However, when using a different theme:

  • Symptoms in Windows XP: SetBkColor works but SetTextColor is ignored
  • Symptoms in Windows 7: both SetBkColor and SetTextColor are ignored

I tried OnEraseBkgnd to fill the background with a custom color (pDC->FillSolidRect) but even this had no effect.

I want to avoid using ownerdrawn OnPaint so the check and radio marks keep following the theme that is active in Windows. As written before, this code is used in W2000, Windows Xp, Vista and Windows 7... I just want to change the background color and text color.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

莫多说 2024-12-23 01:54:58

CButton 除了调用 Windows 默认窗口过程来绘制按钮之外,什么也不做。您可以重写 OnPaint 代码来完成您自己的事情,但我可以理解为什么您想要避免这种情况 - 在每种不同的情况下获得正确的外观需要做很多工作。

MFC 提供了另一个类 CMFCButton,它有一个可重写的方法 OnFillBackground,看看它是否适合您。

CButton doesn't do anything more than call the Windows default window proc for drawing the button. You can override the OnPaint code to do your own thing, but I can understand why you would want to avoid that - it's a lot of work to get the proper look under every different circumstance.

MFC provides another class CMFCButton that has an overrideable method OnFillBackground, see if that works for you.

蓝海似她心 2024-12-23 01:54:58

我编写了一个 CButton,当主题在 Windows 中处于活动状态时(使用 Windows Classic 时情况并非如此),它将使用 Ownerdraw,并且会动态地执行此操作。此示例代码并不完整,但它演示了获得结果所需的一切。

困难的部分是您需要表示突出显示和按下的状态,请参阅 DrawCheckBox 的参数。我忽略它们,因为它们在我的申请中不会完全被忽略。

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

我什至尝试过运行时主题切换,但是当从 Windows 7 主题切换到 Windows Classic 时,这会产生不良效果(复选框看起来像常规按钮)。所以我没有使用这个,但分享也许很有趣:

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

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;
}
空城仅有旧梦在 2024-12-23 01:54:58

根据这篇 Microsoft 文章

更改 Windows 3.0 中按钮控件的背景颜色和
稍后,需要创建一个所有者绘制按钮。

如果您希望特定控件以不同的方式显示,我认为最好对其进行子类化。

本文将有助于理解子类化。

要更改按钮的背景文本颜色,您需要从CButton派生一个新类,例如MyButton 并重写其 DrawItem 函数以添加用于以您的方式绘制特定控件的代码。

注意:您需要为这些控件设置所有者绘制属性 (BS_OWNERDRAW)。

According to this Microsoft article:

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.

眼眸印温柔 2024-12-23 01:54:58

相信我,最好的解决方案是使用一个带有文本的 CStatic 和一个仅包含复选框的 CButton。至少可以说,从 Vista 开始,更改复选框的背景是一个问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文