Die Farbe meiner Dialogfelder in meiner Anwendung kann nicht geändert werden
Ich versuche, einen "dunklen Modus" für meine Windows C ++ - App zu erstellen, teilweise zum Spaß, teilweise, um zu versuchen, die in MFC übertragene Nachricht vollständig zu verstehen, aber ich stoße auf einige wirklich seltsame Probleme, die ich nirgendwo erklären kann.
Ich habe den größten Teil des heutigen Tages damit verbracht, dies herauszufinden, und werde mein Bestes geben, um die vielen Quellen zu zitieren, die ich mir angesehen und versucht habe, umzusetzen.
Ich glaube, ich habe erfolgreich Nachrichtenhandler für beide geschrieben WM_CTLCOLOR
und WM_ERASEBKGND
basierend auf Beispielcode aus dieser Antwort , aber sie scheinen keine Auswirkungen auf meine Dialoge zu haben. Ich habe den Code hier gekürzt, hoffe aber, dass ich genug bereitgestellt habe, um mein Problem aufzudecken. Wenn das immer noch nicht ausreicht, kann ich (widerwillig) das gesamte Repo teilen.
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
Eine andere Frage , die ~ 6 Monate zuvor vom selben Benutzer gestellt wurde, wurde mit etwas ähnlichem Code beantwortet, jedoch unter Verwendung des Framework-Typs, der eine "WinMain" -Funktion enthält (leider kann ich die 2+ -Typen noch nicht unterscheiden). Mein Programm enthält keine WinMain-Funktion, daher konnte ich den Beispielcode nicht direkt verwenden ... Ein weiterer Unterschied in dieser Antwort bestand darin, dass David angewiesen wurde, einen WM_CTLCOLORDLG
Nachrichtentyp anstelle des WM_CTLCOLOR
Nachrichtentyps abzufangen. Ich habe versucht, diesen neuen Nachrichtentyp abzufangen, aber IntelliSense hat mir mitgeteilt, dass er nicht definiert ist und dass diese bestimmte Nachrichteneigenschaft in der Ressourcenansicht des Dialogfelds vollständig fehlt:

Ich habe auch versucht, "WM_CTLCOLORDLG"
mich wie auf der Microsoft Docs-Seite beschrieben zu definieren, habe aber weiterhin Fehlermeldungen erhalten, als ich versucht habe, dies über "ON_MESSAGE" zu handhaben.
Mein Code war kein Originalprojekt, sondern wurde einem Open Source-Beispiel entnommen, das mit RTSS bereitgestellt wurde . Als solches wird nicht der Standard (?) "Pch.h" verwendet, sondern "stdafx.h" (was ist wohl älter?). Ich bin mir nicht sicher, ob das relevant ist, aber ich denke, es könnte sein.
Ich denke, dieses Problem kann mir auch viele andere wachsende Schmerzen bereiten, daher wird jede Hilfe sehr geschätzt.
Antworten
Das Hauptproblem mit dem OP-Code besteht darin, dass der Pinsel jedes Mal neu erstellt wird OnCtlColor
und weiterhin GDI-Handles leckt (der Debug-Build von MFC wirft ASSERT
dies auf). Führen Sie die unten stehende schrittweise Korrektur durch.
Deklarieren Sie die Farbe und den Pinsel als Mitglieder des Dialogfelds.
class SoftwareDlg : public CDialog { //... protected: COLORREF m_color; CBrush m_brush; //...
Initialisieren Sie die Farbe und bürsten Sie sie ein
OnInitDialog
.BOOL SoftwareDlg::OnInitDialog() { m_color = RGB(136, 217, 242); m_brush.CreateSolidBrush(m_color); CDialog::OnInitDialog(); //...
Bringen Sie den Pinsel von zurück
OnCtlColor
.HBRUSH SoftwareDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //... return m_brush; }
Verwenden Sie die Farbe in
OnEraseBkgnd
, um alle sichtbaren Bereiche neu zu streichen.afx_msg BOOL SoftwareDlg::OnEraseBkgnd(CDC* pDC) { CRect rc; pDC->GetClipBox(&rc); pDC->FillSolidRect(rc, m_color); return TRUE; }
Stellen Sie die Hintergrundfarbe
OnInitDialog
explizit auf das Rich-Edit-Steuerelement ein , da Rich-Edit-Steuerelemente dieWM_CTLCOLOR***
Nachrichten nicht verwenden .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); //...
Hinweis : Wenn Sie den CDialogEx::SetBackgroundColor
in der anderen Antwort vorgeschlagenen Ansatz verwenden, werden die OnCtlColor
und OnEraseBkgnd
Teile von der CDialogEx
Implementierung abgedeckt . Der letzte Schritt ist weiterhin erforderlich, da der WM_CTLCOLOR
Mechanismus nur die grundlegenden Steuerelemente (statisch, Bearbeiten, Schaltfläche usw.) und den Dialog selbst abdeckt. Andere Steuerelemente als diese (Rich-Edit-Steuerelement, Listenansicht-Steuerelement usw.) müssen jeweils separat behandelt werden.
Verwenden CDialogEx
Sie einfach die unterstützte Klasse und erledigen CDialogEx::SetBackgroundColor
Sie alles für statische und Schaltflächensteuerung.