Hintergrundfarbe einer ComboBox ändern



  • Hallo Zusammen,

    ich möchte in einem Dialog das Steuerelement (Edit control und Comboboxen), welches aktuell den Focus hat, Gelb hinterlegen. Ich habe dazu eine Variable angelegt, welche bei die ID des Steuerelements, welches aktuell den Focus hat speichert, mit folgendem Code Färbe ich dann die ganzen Elemente:

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd->GetDlgCtrlID() == Fokus && Fokus>0)
    {
    	pDC->SetBkColor(RGB(255, 230, 153));
    	pDC->SetBkMode(OPAQUE);
    	hbr = m_Rot;
    	return hbr;
    }
    else
    {
    	pDC->SetBkColor(RGB(255, 255, 255));
    	pDC->SetBkMode(OPAQUE);
    	hbr = m_Weiss;
    	return hbr;
    }
    

    }

    Das ganze funktioniert für die Edit control Elemente problemlos, nur die ComboBoxen werden nicht gefärbt, obwohl bei Anwählen dieser die Variable "Fokus" die entsprechende ID übernommen hat.

    Hat jemand eine Idee hier?



  • Kleines Update:

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    CWnd* AktFokus;
    AktFokus = pWnd->GetFocus();
    int Element = AktFokus->GetDlgCtrlID();
    
    if (pWnd->GetDlgCtrlID() == Element && nCtlColor != CTLCOLOR_STATIC)
    {
    	pDC->SetBkColor(RGB(255, 230, 153));
    	pDC->SetBkMode(OPAQUE);
    	hbr = m_Gelb;
    	return hbr;
    }
    else
    {
    	pDC->SetBkColor(RGB(255, 255, 255));
    	pDC->SetBkMode(OPAQUE);
    	hbr = m_Weiss;
    	return hbr;
    }
    

    }

    Mit diesem COde werden die Comboboxen eingefärbt, allerdings werden nach dem Click auf die Combobox (also während der Fokus auf ihr liegt) alle anderen Comboboxen ebenfalls eingefärbt, sobald man mit der Maus über diese drüber fährt.



  • Hmm, ich habe mit MFC längere Zeit nichts gemacht, aber du suchst doch eigenlich das Control im Elternfenster, das momentan den Fokus innehat?

    const CWnd* focusWnd = GetParent()?GetParent()->GetFocus():nullptr;
    bool hasFocus = focusWnd && focusWnd->GetSafeHwnd() == pWnd->GetSafeHwnd();
    

    Um welchen Typ von Comboboxen handelt es sich denn (Dropdown vs Droplist)? Ich habe das mal (allerdings ohne MFC) ausprobiert:
    https://i.imgur.com/6N2hpsi.png
    CTLCOLOR_LISTBOX wird, wenn die Listbox angezeigt wird, immer berücksichtigt.
    Bei Dropdown klappt alles wie gewünscht (bis auf die Farbe der Auswahl, die sich nicht so leicht ändern lässt).

    Bei einer Droplist ist es nicht so einfach, die Farbe zu wechseln, es wird zwar CTLCOLOR_EDIT für das Control gesendet, allerdings wird dies ignoriert (wie CTLCOLOR_BTN bei Buttons).

    Die einzige Lösung, die bei Droplists zuverlässig funktioniert, ist mMn Ownerdraw. Ich habe auch versucht, das Editcontrol zu deaktivieren, damit sich Droplist-CBs wie Dropdown-CBs verhalten, dies scheiterte allerdings ausnahmslos. Entweder wurde ES_READONLY ignoriert bzw. immer wieder entfernt oder das komplette Control wurde deaktiviert, wenn das Editfeld der Combobox per EnableWindow deaktiviert wurde.

    Edit: Ich bin mir relativ sicher, dass AktFokus bei dir auch nullptr sein kann. Hast du mal versucht, die Anwendung zu wechseln, während der Dialog angezeigt wird?

    Edit2: Bild noch einmal verändert



  • @yahendrik sagte in Hintergrundfarbe einer ComboBox ändern:

    Edit: Ich bin mir relativ sicher, dass AktFokus bei dir auch nullptr sein kann. Hast du mal versucht, die Anwendung zu wechseln, während der Dialog angezeigt wird?

    Hallo Hendrik,

    danke schonmal für deine Mühe, du hast Recht, sobald ich die Anwendung wechsle, gibt es sofort eine Fehlermeldung. Ich habe jetzt folgenden Code versucht:

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    	const CWnd* focusWnd = GetParent() ? GetParent()->GetFocus() : nullptr;
    	bool hasFocus = focusWnd && focusWnd->GetSafeHwnd() == pWnd->GetSafeHwnd();
    
    	if (hasFocus == TRUE)
    	{
    		int Element = pWnd->GetFocus()->GetDlgCtrlID();
    
    		if (pWnd->GetDlgCtrlID() == Element && nCtlColor != CTLCOLOR_STATIC)
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 230, 153));
    			hbr = m_Gelb;
    			return hbr;
    		}
    		else
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 255, 255));
    			hbr = m_Weiss;
    			return hbr;
    		}
    	}
    }
    

    Hiermit funktioniert es, allerdings sind die Listen der Comboboxen (bei allen CB's ist Dropdown eingestellt) komplett schwarz.
    Da ich in C++ noch nicht soo fundiert bin, könntest du mir kurz erklären, was die beiden Codezeilen von dir bewirken?



  • @yahendrik

    Mit folgendem Code funktioniert nun alles wie gewünscht, auch die Liste wird entsprechend eingefärbt:)

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    	const CWnd* focusWnd = GetParent() ? GetParent()->GetFocus() : nullptr;
    	bool hasFocus = focusWnd && focusWnd->GetSafeHwnd() == pWnd->GetSafeHwnd();
    
    	if (nCtlColor == CTLCOLOR_STATIC || nCtlColor == CTLCOLOR_DLG)
    	{
    		pDC->SetBkMode(TRANSPARENT);
    		pDC->SetBkColor(RGB(255, 255, 255));
    		hbr = m_Weiss;
    		return hbr;
    	}
    	if (nCtlColor == CTLCOLOR_LISTBOX)
    	{
    		pDC->SetBkMode(TRANSPARENT);
    		pDC->SetBkColor(RGB(255, 230, 153));
    		hbr = m_Gelb;
    		return hbr;
    	}
    	if (hasFocus == TRUE)
    	{
    		int Element = pWnd->GetFocus()->GetDlgCtrlID();
    
    		if (pWnd->GetDlgCtrlID() == Element && nCtlColor != CTLCOLOR_STATIC)
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 230, 153));
    			hbr = m_Gelb;
    			return hbr;
    		}
    		else
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 255, 255));
    			hbr = m_Weiss;
    			return hbr;
    		}
    	}
    }
    


  • @medic89 Es fehlt noch ein return hbr; am Ende, damit der Returnwert von CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor) zurückgegeben wird. Auch wenn das vielleicht nur ein Nullptr ist.
    Die Auswertung könntest du auch an die entsprechende Stelle verlegen, bei CTLCOLOR_STATIC, CTLCOLOR_DLG und CTLCOLOR_LISTBOX sind diese Überprüfungen nicht nötig.
    Und if (hasFocus == TRUE) -> if (hasFocus)

    Früher gabs mal einen Weihnachtselch (oder wars ein Rentier?) 🎅🏼



  • @yahendrik

    Was genau bewirken denn die beiden Zeilen, die du in deinem ersten Post geschrieben hast?

    Warum den "Pinsel" am Schluss nochmal ausgeben, wenn die If-Bedingungen nicht erfüllt sind, ist der ja leer, oder?



  • @medic89 Es schadet nicht, wenn hasFocus immer bestimmt wird, in vielen Fällen ist dies nur nicht nötig:

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    	if (nCtlColor == CTLCOLOR_STATIC || nCtlColor == CTLCOLOR_DLG)
    	{
    		...
    		return hbr;
    	}
    	if (nCtlColor == CTLCOLOR_LISTBOX)
    	{
    		...
    		return hbr;
    	}
    
    	// hier erst überprüfen:
    	const CWnd* focusWnd = GetParent() ? GetParent()->GetFocus() : nullptr;
    	bool hasFocus = focusWnd && focusWnd->GetSafeHwnd() == pWnd->GetSafeHwnd();
    	if (hasFocus)
    	{
    		...
    	}
    	// nCtrClr !=CTLCOLOR_STATIC, !=CTLCOLOR_DLG, !=CTLCOLOR_LISTBOX und !hasFocus
    	// Den Returnwert von CDialogEx::OnCtlColor zurückgeben
    	return hbr;
    }
    


  • @yahendrik

    ok, ich glaube ich habe einen Denkfehler. Ich dachte die Funktion OnCtlColor() ist dauernd aktiv, d. h. es werden ständig alle Elemente des Dialogs durchlaufen. Darum habe ich zuerst die Static und Dialoge eingefügt, denn diese sind immer weiß. Stimmt denn das?

    Bezüglich dem return am Schluss:

    wenn innerhalb der ganzen Abfragen schon der Pinsel zurückgegeben wird, kommt die Funktion doch nie an das Ende, wo nochmal ein return steht, denn mit den bereits innerhalb der Abfragen enthaltenen returns ist die Funktion doch vorzeitig am Ende, oder?



  • @yahendrik

    Habe ich mit meinen Gedanken im vorherigen Beitrag (nr. 9) die Funktion richtig verstanden?



  • Noch eine Frage:

    Ich würde gerne die entsprechende Box, welche grade den Fokus inne hat mit einem roten Rahmen versehen, mit folgendem Code funktioniert das schonmal, allerdings sieht man den roten Rahmen nur an der oberen und linken Kante der Box:

    CRect rect;
    GetWindowRect(&rect);
    rect.OffsetRect(-rect.left, -rect.top);
    CBrush brush(RGB(255, 0, 0));
    pDC->FrameRect(&rect, &brush);
    ReleaseDC(pDC);
    

    EDIT: Hab es gelöst, vor dem GetWindowRect hat ein "pWnd->" gefehlt. Allerdings kann ich die Comboboxen (formatiert als Dropdown) so nicht färben, hat da jemand eine Idee?



  • @yahendrik

    HBRUSH CDialog23::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    	if (nCtlColor == CTLCOLOR_STATIC || nCtlColor == CTLCOLOR_DLG)
    	{
    		pDC->SetBkMode(TRANSPARENT);
    		pDC->SetBkColor(RGB(255, 255, 255));
    		hbr = m_Weiss;
    		return hbr;
    	}
    	if (nCtlColor == CTLCOLOR_LISTBOX)
    	{
    		pDC->SetBkMode(TRANSPARENT);
    		pDC->SetBkColor(RGB(255, 230, 153));
    		hbr = m_Gelb;
    		return hbr;
    	}
    	const CWnd* focusWnd = GetParent() ? GetParent()->GetFocus() : nullptr;
    	bool hasFocus = focusWnd && focusWnd->GetSafeHwnd() == pWnd->GetSafeHwnd();
    	if (hasFocus)
    	{
    		if (pWnd->GetDlgCtrlID() == pWnd->GetFocus()->GetDlgCtrlID() && nCtlColor != CTLCOLOR_STATIC)
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 230, 153));
    
    			CRect rect;
    			pWnd->GetWindowRect(&rect);
    			rect.OffsetRect(-rect.left, -rect.top);
    			CBrush brush(RGB(255, 0, 0));
    			pDC->FrameRect(&rect, &brush);
    			ReleaseDC(pDC);
    
    			hbr = m_Gelb;
    			return hbr;
    		}
    		else
    		{
    			pDC->SetBkMode(TRANSPARENT);
    			pDC->SetBkColor(RGB(255, 255, 255));
    			hbr = m_Weiss;
    			return hbr;
    		}
    	}
    	return hbr;
    }
    

    Hier noch einmal mein aktueller Stand mit deinen Anmerkungen und den rot gefärbten TextBoxen (bei den Comboboxen bin ich noch nicht weiter gekommen).



  • Zeichnen würde ich da nichts. Meiner Meinung nach hast du mindestens 2 Möglichkeiten, das wie von Windows vorgesehen zu bewerkstelligen:

    1. Subclassing der Controls. Also WS_BORDER hinzufügen und WM_NCPAINT bearbeiten und den Rahmen selber zeichnen, allerdings könnte die Strichstärke nicht ausreichend sein, sodass auch noch WM_NCCALCSIZE und WM_NCHITTEST selber bearbeitet werden müssen.
    2. Die mMn wesentlich einfachere Methode: Beim Zeichnen des Dialogs das Control mit dem Fokus bestimmen und einen Rahmen um dieses Control zeichnen. Wenn der Fokus wechselt, ein Neuzeichnen veranlassen. Wenn du das zuletzt gewählte Element bzw. die Position speicherst, reichen auch bis zu zwei Rahmen: Einmal in der Hintergrundfarbe des Dialogs um das zuletzt gezeichnete Element und dann in der gewünschten Farbe um das aktuelle.


  • @yahendrik

    Also Zeichnen mithilfe von DrawRect()? Das habe ich grade versucht, allerdings kennt er die Funktion nicht, obwohl ich den Header afxdrawmanager.h includiert habe.



  • FrameRect (bzw. hier). Für einen Rahmen mit einer Strichstärke von mehr als einer logischen Einheit kannst du auch eine Funktion schreiben, die vier Rechtecke zeichnet.



  • @yahendrik

    Aber das habe ich ja schon in meiner Funktion stehen, aber die ComboBoxen werden da nicht eingefärbt.



  • Ja, aber OnCtlColor wird aufgerufen, bevor das Control gezeichnet wird. Alles was du zeichnest, wird wohl übermalt. Die zweite Möglichkeit, die ich kurz beschrieben habe, bezieht sich auf die Paint-Methode des Dialogs. Edit: Und hier muss natürlich das Rechteck entsprechend vergrößert werden, sodass genau außen herum gezeichnet wird.



  • @yahendrik

    Woran liegt es denn, dass der Bereich überschrieben wird? Die OnCtlColor Message wird ja gesendet, sobald ich zb die Edit Box 1 anklicke, und dann wird der Rahmen gezeichnet, der das Febster überdeckt, und erst wenn ich die Maus bewege, wird onCtlColor nochmal aktiviert, und der Text wird wieder sichtbar?



  • Mal eine ganz "blöde" Frage:

    Wenn ich den Focus auf ein Edit Control setze, färbt sich dessen Rahmen per Default blau. Kann man da nicht irgendwo eingreifen und das Ändern? Irgendwo muss ja festgelegt sein, dass es die Farbe blau sein soll.



  • Ich vermute mal dass das Blau die System-Farbe COLOR_HIGHLIGHT ist. Die einzige Möglichkeit diese Farbe zu ändern die ich gefunden habe ist SetSysColors:

    static const int count = 1;
    INT elements[count] = {COLOR_HIGHLIGHT};
    COLORREF colors[count] = {RGB(255, 0, 0)};
    
    SetSysColors(count, elements, colors);
    

    Dummerweise gilt das dann global für alle Fenster/Anwendungen der aktuellen Session.


Anmelden zum Antworten