내 응용 프로그램에서 대화 상자의 색상을 변경할 수 없습니다.
부분적으로는 재미로, 부분적으로는 MFC에서 전달되는 메시지를 완전히 이해하려고 시도하고 완전히 이해하기 위해 Windows C ++ 앱에 대한 "다크 모드"를 만들려고합니다.하지만 어디에서도 설명 할 수없는 정말 이상한 문제가 발생하고 있습니다.
나는 이것을 알아 내기 위해 오늘의 더 나은 부분을 보냈고 내가보고 구현하려고 시도한 많은 출처를 인용하기 위해 최선을 다할 것이다.
나는 성공적으로 모두 메시지 핸들러를 작성했습니다 생각 WM_CTLCOLOR
과 WM_ERASEBKGND
에서 예제 코드를 기반으로 이 대답 , 그러나 그들은 내 대화 상자에 어떤 영향을 미칠 것 같지 않습니다. 여기서 코드를 잘라 냈지만 내 문제를 노출 할 수있을만큼 충분히 제공했으면합니다. 그래도 충분하지 않다면 전체 저장소를 (마지 못해) 공유 할 수 있습니다.
소프트웨어 Dlg.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
/////////////////////////////////////////////////////////////////////////////
소프트웨어 Dlg.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
6 개월 전에 동일한 사용자가 게시 한 또 다른 질문 은 다소 유사한 코드로 답변되었지만 "WinMain"함수를 포함하는 프레임 워크 유형을 사용했습니다 (죄송합니다. 아직 2 개 이상의 유형을 구별 할 수 없습니다). 내 프로그램에는 WinMain 함수가 포함되어 있지 않아 샘플 코드를 직접 사용할 수 없었지만이 답변의 또 다른 차이점은 David가 WM_CTLCOLORDLG
메시지 유형 대신 메시지 유형 을 포착하라는 지시를 받았다는 것 WM_CTLCOLOR
입니다. 이 새 메시지 유형을 잡으려고했지만 IntelliSense에서 정의되지 않았고 특정 메시지 속성이 대화 상자의 리소스보기에 완전히 없다고 말했습니다.

또한 Microsoft Docs 페이지"WM_CTLCOLORDLG"
에 설명 된대로 자신을 정의하려고 시도했지만 "ON_MESSAGE"를 통해 처리하려고하면 오류 메시지가 계속 나타납니다.
내 코드는 원래 프로젝트가 아니지만 RTSS 와 함께 제공되는 오픈 소스 샘플에서 가져 왔습니다 . 따라서 표준 (?) "pch.h"를 사용하지 않고 "stdafx.h"(내가 생각하기에 더 오래된 것입니까?)를 사용합니다. 관련성이 있는지 확실하지 않지만 그럴 수 있다고 생각합니다.
이 문제가 다른 성장통을 많이 유발할 수도 있다고 생각하므로 어떤 도움이라도 대단히 감사합니다.
답변
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(); //...
에서 브러시를 반환합니다
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
리치 편집 컨트롤은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
메커니즘은 기본 컨트롤 (정적, 편집, 버튼 등)과 대화 자체 만 다루기 때문에 마지막 단계는 여전히 필요 합니다. 다른 컨트롤 (리치 편집 컨트롤, 목록보기 컨트롤 등)은 각각 개별적으로 처리해야합니다.
CDialogEx
지원 하는 클래스를 사용하기 만하면 CDialogEx::SetBackgroundColor
정적 및 버튼 컨트롤에 대한 모든 작업을 수행합니다.