Die Farbe meiner Dialogfelder in meiner Anwendung kann nicht geändert werden

Dec 07 2020

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_CTLCOLORund WM_ERASEBKGNDbasierend 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_CTLCOLORDLGNachrichtentyp anstelle des WM_CTLCOLORNachrichtentyps 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

4 dxiv Dec 08 2020 at 00:12

Das Hauptproblem mit dem OP-Code besteht darin, dass der Pinsel jedes Mal neu erstellt wird OnCtlColorund weiterhin GDI-Handles leckt (der Debug-Build von MFC wirft ASSERTdies 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 OnInitDialogexplizit auf das Rich-Edit-Steuerelement ein , da Rich-Edit-Steuerelemente die WM_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::SetBackgroundColorin der anderen Antwort vorgeschlagenen Ansatz verwenden, werden die OnCtlColorund OnEraseBkgndTeile von der CDialogExImplementierung abgedeckt . Der letzte Schritt ist weiterhin erforderlich, da der WM_CTLCOLORMechanismus 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.

4 xMRi Dec 07 2020 at 13:47

Verwenden CDialogExSie einfach die unterstützte Klasse und erledigen CDialogEx::SetBackgroundColorSie alles für statische und Schaltflächensteuerung.