无法更改应用程序中对话框的颜色
Unable to change the color of my dialog boxes in my application
我正在尝试为我的 Windows C++ 应用程序创建一个“暗模式”,部分是为了好玩,部分是为了尝试并完全理解 MFC 中传递的消息,但我 运行一些我在任何地方都找不到解释的非常奇怪的问题。
我今天大部分时间都在尝试解决这个问题,并将尽我最大的努力引用我看过并尝试实施的许多资源。
我相信我已经根据 this answer 中的示例代码成功地为 WM_CTLCOLOR
和 WM_ERASEBKGND
编写了消息处理程序,但它们似乎对我没有任何影响对话框。我已将代码缩减到此处,但希望我提供的内容足以暴露我的问题。如果这还不够,我可以(不情愿地)分享整个 repo。
SoftwareDlg.h
#ifndef _SOFTWAREDLG_H_INCLUDED_
#define _SOFTWAREDLG_H_INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class SoftwareDlg : public CDialog
{
// Construction
public:
SoftwareDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(SoftwareDlg)
enum { IDD = IDD_SOFTWARE_DIALOG };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(SoftwareDlg)
public:
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
BOOL PreTranslateMessage(MSG* pMsg);
CFont m_font;
CRichEditCtrl m_richEditCtrl;
// Generated message map functions
//{{AFX_MSG(SoftwareDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
/////////////////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////////////////
SoftwareDlg.cpp
#include "stdafx.h"
#include <Windows.h>
#include "AboutDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//Windows Dialog inherited function overrides
/////////////////////////////////////////////////////////////////////////////
// SoftwareDlg dialog
/////////////////////////////////////////////////////////////////////////////
SoftwareDlg::SoftwareDlg(CWnd* pParent /*=NULL*/)
: CDialog(SoftwareDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(SoftwareDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void SoftwareDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(SoftwareDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(SoftwareDlg, CDialog)
//{{AFX_MSG_MAP(SoftwareDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_WM_ERASEBKGND()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// SoftwareDlg message handlers
/////////////////////////////////////////////////////////////////////////////
BOOL SoftwareDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
CWnd* pPlaceholder = GetDlgItem(IDC_PLACEHOLDER);
if (pPlaceholder)
{
CRect rect;
pPlaceholder->GetClientRect(&rect);
if (!m_richEditCtrl.Create(WS_VISIBLE | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | WS_VSCROLL, rect, this, 0))
return FALSE;
m_font.CreateFont(-11, 0, 0, 0, FW_REGULAR, 0, 0, 0, BALTIC_CHARSET, 0, 0, 0, 0, "Courier New");
m_richEditCtrl.SetFont(&m_font);
}
m_nTimerID = SetTimer(0x1234, 1000, NULL); //Used by OnTimer function to refresh dialog box & OSD
return TRUE; // return TRUE unless you set the focus to a control
}
void SoftwareDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
/////////////////////////////////////////////////////////////////////////////
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
/////////////////////////////////////////////////////////////////////////////
void SoftwareDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR SoftwareDlg::OnQueryDragIcon()
{
return (HCURSOR)m_hIcon;
}
void SoftwareDlg::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
}
void SoftwareDlg::OnDestroy()
{
if (m_nTimerID)
KillTimer(m_nTimerID);
m_nTimerID = NULL;
MSG msg;
while (PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));
CDialog::OnDestroy();
}
BOOL SoftwareDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
switch (pMsg->wParam)
{
case ' ':
Sleep(1000);
}
}
return CDialog::PreTranslateMessage(pMsg);
}
BOOL SoftwareDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(255, 0, 0)); // dialog background color
CBrush* pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
return bRes; // CDialog::OnEraseBkgnd(pDC);
}
HBRUSH SoftwareDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// Are we painting the IDC_MYSTATIC control? We can use
m_brush.CreateSolidBrush(RGB(136, 217, 242));
//if (pWnd->GetDlgCtrlID() == IDD_SOFTWARE_DIALOG)
// Set the text color to red
pDC->SetTextColor(RGB(255, 0, 0));
// Set the background mode for text to transparent so background will show thru.
pDC->SetBkMode(TRANSPARENT);
// Return handle to our CBrush object
hbr = m_brush;
return hbr;
}
HBRUSH SoftwareDlg::CtlColor(CDC* pDC, UINT nCtlColor)
{
HBRUSH myBrush = CreateSolidBrush(RGB(136, 217, 242));
return myBrush;
}
Resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Software.rc
//
#define IDM_ABOUTBOX 0x0010
#define IDD_ABOUTBOX 100
#define IDS_ABOUTBOX 101
#define IDD_SOFTWARE_DIALOG 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 141
#define _APS_NEXT_COMMAND_VALUE 32792
#define _APS_NEXT_CONTROL_VALUE 1026
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Another question, posted by the same user ~6 months earlier, was answered with somewhat similar code, but using the type of framework that contains a "WinMain" function(sorry, I can't differentiate the 2+ types yet). My program does not contain a WinMain function so I wasn't able to use the sample code directly... but another difference in this answer was that David was told to catch a WM_CTLCOLORDLG
message type instead the WM_CTLCOLOR
message type. I tried to catch this new message type, but IntelliSense told me it was undefined and that particular message property was completely absent from the resource view of the dialog box:
我也曾尝试按照 Microsoft Docs page 中的描述自己定义 "WM_CTLCOLORDLG"
,但是当我尝试通过“ON_MESSAGE”处理它时,继续收到错误消息。
我的代码不是原始项目,而是取自 RTSS 提供的开源示例。因此,它不使用标准(?)“pch.h”,而是“stdafx.h”(我猜哪个更旧?)。我不确定这是否相关,但我觉得可能是。
我认为这个问题也可能给我带来很多其他成长的烦恼,所以非常感谢任何帮助。
只需使用 CDialogEx
class 它支持 CDialogEx::SetBackgroundColor
并且它可以完成静态和按钮控件的所有工作。
OP 代码的主要问题是每次在 OnCtlColor
中重新创建画笔,并且会不断泄漏 GDI 句柄(MFC 的调试版本对此提出 ASSERT
)。完成下面的逐步修复。
将颜色和画笔声明为对话框的成员。
class SoftwareDlg : public CDialog
{
//...
protected:
COLORREF m_color;
CBrush m_brush;
//...
初始化OnInitDialog
中的颜色和笔刷。
BOOL SoftwareDlg::OnInitDialog()
{
m_color = RGB(136, 217, 242);
m_brush.CreateSolidBrush(m_color);
CDialog::OnInitDialog();
//...
Return刷自OnCtlColor
.
HBRUSH SoftwareDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
//...
return m_brush;
}
使用OnEraseBkgnd
中的颜色重新绘制任何可见区域。
afx_msg BOOL SoftwareDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rc;
pDC->GetClipBox(&rc);
pDC->FillSolidRect(rc, m_color);
return TRUE;
}
在 OnInitDialog
中明确设置 rich edit 控件的背景颜色,因为 rich edit 控件不使用 WM_CTLCOLOR***
消息。
BOOL SoftwareDlg::OnInitDialog()
{
//...
if (pPlaceholder)
{
//...
if (!m_richEditCtrl.Create(WS_VISIBLE | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | WS_VSCROLL, rect, this, 0))
return FALSE;
m_richEditCtrl.SetBackgroundColor(FALSE, m_color);
//...
注意:如果使用其他答案中提出的CDialogEx::SetBackgroundColor
方法,OnCtlColor
和OnEraseBkgnd
部分由CDialogEx
执行。最后一步仍然是必要的,因为 WM_CTLCOLOR
机制只涵盖基本控件(静态、编辑、按钮等)和对话框本身。其他控件(丰富的编辑控件、列表视图控件等)需要分别处理。
我正在尝试为我的 Windows C++ 应用程序创建一个“暗模式”,部分是为了好玩,部分是为了尝试并完全理解 MFC 中传递的消息,但我 运行一些我在任何地方都找不到解释的非常奇怪的问题。
我今天大部分时间都在尝试解决这个问题,并将尽我最大的努力引用我看过并尝试实施的许多资源。
我相信我已经根据 this answer 中的示例代码成功地为 WM_CTLCOLOR
和 WM_ERASEBKGND
编写了消息处理程序,但它们似乎对我没有任何影响对话框。我已将代码缩减到此处,但希望我提供的内容足以暴露我的问题。如果这还不够,我可以(不情愿地)分享整个 repo。
SoftwareDlg.h
#ifndef _SOFTWAREDLG_H_INCLUDED_
#define _SOFTWAREDLG_H_INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class SoftwareDlg : public CDialog
{
// Construction
public:
SoftwareDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(SoftwareDlg)
enum { IDD = IDD_SOFTWARE_DIALOG };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(SoftwareDlg)
public:
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
BOOL PreTranslateMessage(MSG* pMsg);
CFont m_font;
CRichEditCtrl m_richEditCtrl;
// Generated message map functions
//{{AFX_MSG(SoftwareDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
public:
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
/////////////////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////////////////
SoftwareDlg.cpp
#include "stdafx.h"
#include <Windows.h>
#include "AboutDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//Windows Dialog inherited function overrides
/////////////////////////////////////////////////////////////////////////////
// SoftwareDlg dialog
/////////////////////////////////////////////////////////////////////////////
SoftwareDlg::SoftwareDlg(CWnd* pParent /*=NULL*/)
: CDialog(SoftwareDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(SoftwareDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void SoftwareDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(SoftwareDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(SoftwareDlg, CDialog)
//{{AFX_MSG_MAP(SoftwareDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_WM_ERASEBKGND()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// SoftwareDlg message handlers
/////////////////////////////////////////////////////////////////////////////
BOOL SoftwareDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
CWnd* pPlaceholder = GetDlgItem(IDC_PLACEHOLDER);
if (pPlaceholder)
{
CRect rect;
pPlaceholder->GetClientRect(&rect);
if (!m_richEditCtrl.Create(WS_VISIBLE | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | WS_VSCROLL, rect, this, 0))
return FALSE;
m_font.CreateFont(-11, 0, 0, 0, FW_REGULAR, 0, 0, 0, BALTIC_CHARSET, 0, 0, 0, 0, "Courier New");
m_richEditCtrl.SetFont(&m_font);
}
m_nTimerID = SetTimer(0x1234, 1000, NULL); //Used by OnTimer function to refresh dialog box & OSD
return TRUE; // return TRUE unless you set the focus to a control
}
void SoftwareDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
/////////////////////////////////////////////////////////////////////////////
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
/////////////////////////////////////////////////////////////////////////////
void SoftwareDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR SoftwareDlg::OnQueryDragIcon()
{
return (HCURSOR)m_hIcon;
}
void SoftwareDlg::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
}
void SoftwareDlg::OnDestroy()
{
if (m_nTimerID)
KillTimer(m_nTimerID);
m_nTimerID = NULL;
MSG msg;
while (PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));
CDialog::OnDestroy();
}
BOOL SoftwareDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
switch (pMsg->wParam)
{
case ' ':
Sleep(1000);
}
}
return CDialog::PreTranslateMessage(pMsg);
}
BOOL SoftwareDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(255, 0, 0)); // dialog background color
CBrush* pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
return bRes; // CDialog::OnEraseBkgnd(pDC);
}
HBRUSH SoftwareDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// Are we painting the IDC_MYSTATIC control? We can use
m_brush.CreateSolidBrush(RGB(136, 217, 242));
//if (pWnd->GetDlgCtrlID() == IDD_SOFTWARE_DIALOG)
// Set the text color to red
pDC->SetTextColor(RGB(255, 0, 0));
// Set the background mode for text to transparent so background will show thru.
pDC->SetBkMode(TRANSPARENT);
// Return handle to our CBrush object
hbr = m_brush;
return hbr;
}
HBRUSH SoftwareDlg::CtlColor(CDC* pDC, UINT nCtlColor)
{
HBRUSH myBrush = CreateSolidBrush(RGB(136, 217, 242));
return myBrush;
}
Resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Software.rc
//
#define IDM_ABOUTBOX 0x0010
#define IDD_ABOUTBOX 100
#define IDS_ABOUTBOX 101
#define IDD_SOFTWARE_DIALOG 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 141
#define _APS_NEXT_COMMAND_VALUE 32792
#define _APS_NEXT_CONTROL_VALUE 1026
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Another question, posted by the same user ~6 months earlier, was answered with somewhat similar code, but using the type of framework that contains a "WinMain" function(sorry, I can't differentiate the 2+ types yet). My program does not contain a WinMain function so I wasn't able to use the sample code directly... but another difference in this answer was that David was told to catch a WM_CTLCOLORDLG
message type instead the WM_CTLCOLOR
message type. I tried to catch this new message type, but IntelliSense told me it was undefined and that particular message property was completely absent from the resource view of the dialog box:
我也曾尝试按照 Microsoft Docs page 中的描述自己定义 "WM_CTLCOLORDLG"
,但是当我尝试通过“ON_MESSAGE”处理它时,继续收到错误消息。
我的代码不是原始项目,而是取自 RTSS 提供的开源示例。因此,它不使用标准(?)“pch.h”,而是“stdafx.h”(我猜哪个更旧?)。我不确定这是否相关,但我觉得可能是。
我认为这个问题也可能给我带来很多其他成长的烦恼,所以非常感谢任何帮助。
只需使用 CDialogEx
class 它支持 CDialogEx::SetBackgroundColor
并且它可以完成静态和按钮控件的所有工作。
OP 代码的主要问题是每次在 OnCtlColor
中重新创建画笔,并且会不断泄漏 GDI 句柄(MFC 的调试版本对此提出 ASSERT
)。完成下面的逐步修复。
将颜色和画笔声明为对话框的成员。
class SoftwareDlg : public CDialog { //... protected: COLORREF m_color; CBrush m_brush; //...
初始化
OnInitDialog
中的颜色和笔刷。BOOL SoftwareDlg::OnInitDialog() { m_color = RGB(136, 217, 242); m_brush.CreateSolidBrush(m_color); CDialog::OnInitDialog(); //...
Return刷自
OnCtlColor
.HBRUSH SoftwareDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //... return m_brush; }
使用
OnEraseBkgnd
中的颜色重新绘制任何可见区域。afx_msg BOOL SoftwareDlg::OnEraseBkgnd(CDC* pDC) { CRect rc; pDC->GetClipBox(&rc); pDC->FillSolidRect(rc, m_color); return TRUE; }
在
OnInitDialog
中明确设置 rich edit 控件的背景颜色,因为 rich edit 控件不使用WM_CTLCOLOR***
消息。BOOL SoftwareDlg::OnInitDialog() { //... if (pPlaceholder) { //... if (!m_richEditCtrl.Create(WS_VISIBLE | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | WS_VSCROLL, rect, this, 0)) return FALSE; m_richEditCtrl.SetBackgroundColor(FALSE, m_color); //...
注意:如果使用其他答案中提出的CDialogEx::SetBackgroundColor
方法,OnCtlColor
和OnEraseBkgnd
部分由CDialogEx
执行。最后一步仍然是必要的,因为 WM_CTLCOLOR
机制只涵盖基本控件(静态、编辑、按钮等)和对话框本身。其他控件(丰富的编辑控件、列表视图控件等)需要分别处理。