Transparente Editbox



  • Es sollte sich vllt. einer irgendwann mal die Mühe machen, und für die F.A.Q. beschreiben, wie man ein Bild als Edit-Hintergrund bzw. ein transparentes Edit-Feld erzeugen kann. Weil die Frage wurde schon zum sonst wievielten mal gestellt. Ich hatte damit schonmal angefangen, aber habe keine Zeit mehr...

    Mfg Ominion



  • Erstmal danke für die Antwort!

    Leider funktioniert es bisher immer noch nicht. Ich habe jetzt einmal ein RECT entsprechend gefüllt und dann
    InvalidateRect(GetParent(hWnd), &rc, TRUE);
    UpdateWindow(GetParent(hWnd));

    als Reaktion auf WM_ERASEBKGND des Editfensters aufgerufen. Im Bereich des RECTs berfindet sich nun aber ein dauerhafter Streifen in dem standard "Dialoggrau".

    Auch ein Aufruf von
    SendMessage(GetParent(hWnd), WM_ERASEBKGND, wParam, 0L);
    Bewirkt dieselbe graue Farbe, nur auf dem ganzen Dialog und nicht nur innerhalb des Clippingrechteckes.

    Ich habe nach wie vor irgendwie den Verdacht, dass WM_ERASEBKGND extrem oft an die Editbox gesendet wird und daher durch das viele Löschen ein dauerhaftes Grau entsteht. Ist WM_ERASEBKGND vielleicht die falsche Nachricht, auf die ich beim Editcontrol reagieren sollte?

    ph4nt0m


  • Mod

    Wie zeichnest Du denn den Hintergrund in dem Dialog? In einem WM_ERASEBKGND Handler oder in WM_PAINT?



  • Ich zeichne den Hintergrund (ein Bitmap) in WM_PAINT.



  • Ich muss mich leider nochmal melden. Mein Problem ist immer noch nicht richtig gelöst, aber der Thread rutscht irgendwie immer wieder ab.

    @Martin Richter, deine sehr spezifische Frage, wo ich denn den Hintergrund zeichne, klang für mich so, als hättest du eine Lösung 🙂 Wäre sehr nett, wenn du dich da nochmal melden würdest.

    ph4nt0m


  • Mod

    Der Hintergrund gehört in WM_ERASEBKGND gezeichnet. Dnan kannst Du nämlich auch aus dem Handler des Edit Controls WM_ERASEBKGND an das Parent weiterleiten.



  • Ok, ich habe jetzt alles aus WM_PAINT (hauptsächlich das Zeichnen des Hintergrundbitmaps) in WM_ERASEBKGND ausgelagert und halt den DC nicht mehr mit BeginPaint() sondern mit GetDC() ermittelt. Beim Editcontrol wird WM_ERASEBKGND jetzt durch SendMessage(GetParent(hWnd), WM_ERASEBKGND, wParam, 0L); an den Dialog weitergeleitet.

    Aber was soll ich sagen: Es geht einfach immer noch nicht: Die eingegebenen Zeichen werden zwar angezeigt, aber beim anschließenden Löschen befinden sie sich immernoch auf dem Hintergrund. Ich hab echt keine Ahnung mehr, was da noch falsch sein könnte 😞

    ph4nt0m


  • Mod

    Also ich habe hier ein kleines Beispiel gebaut (mit der MFC aber das Prinzip ist gleich).

    Es geht mit zwei simplen Handlern. WM_CTLCOLOREDIT wird abgefangen und WM_ERASEBKGND.

    IMPLEMENT_DYNAMIC(CTransparentEdit, CEdit)
    
    CTransparentEdit::CTransparentEdit()
    {
    
    }
    
    CTransparentEdit::~CTransparentEdit()
    {
    }
    
    BEGIN_MESSAGE_MAP(CTransparentEdit, CEdit)
    	ON_WM_ERASEBKGND()
    	ON_WM_CTLCOLOR_REFLECT()
    END_MESSAGE_MAP()
    
    // CTransparentEdit message handlers
    
    BOOL CTransparentEdit::OnEraseBkgnd(CDC* pDC)
    {
    	return static_cast<BOOL>(::SendMessage(GetParent()->GetSafeHwnd(),WM_ERASEBKGND,reinterpret_cast<WPARAM>(pDC->GetSafeHdc()),0));
    }
    
    HBRUSH CTransparentEdit::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
    {
    	Default();
    	pDC->SetBkMode(TRANSPARENT);
    	return reinterpret_cast<HBRUSH>(GetStockObject(HOLLOW_BRUSH));
    }
    


  • Danke für deine Mühe! Ich poste hier einfach mal die relevanten stellen aus meinem Code:

    static LONG_PTR prevEditProc;
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
    	return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
    }
    
    BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {		
    	HDC hDC;
    	static IPicture * back;
    
    	switch(message)
    	{
    	case WM_INITDIALOG: 
    		prevEditProc = SetWindowLongPtr(GetDlgItem(hDlg, IDC_EDIT_NAME), GWLP_WNDPROC, (LONG_PTR) EditProc);
    		back = LoadPicture(MAKEINTRESOURCE(IDB_BACKGROUND));
    		return TRUE;
    
    	case WM_ERASEBKGND:
    		hDC = GetDC(hDlg);		
    		DrawPicture(hDC, back, 0, 0);		
    		ReleaseDC(hDlg, hDC);		
    		return TRUE;
    
    	case WM_CTLCOLOREDIT:		
    		SetBkMode((HDC) wParam, TRANSPARENT);
    		SetTextColor((HDC) wParam, RGB(255,255,255));
    		return (BOOL) GetStockObject(HOLLOW_BRUSH);
    
    	case WM_DESTROY:
    	case WM_CLOSE:   
    		SetWindowLongPtr(GetDlgItem(hDlg, IDC_EDIT_NAME), GWLP_WNDPROC, (LONG_PTR) prevEditProc);		
    		EndDialog(hDlg,0);
    		return TRUE;
    	}
    	return FALSE;
    }
    
    LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message)
    	{
    	case WM_ERASEBKGND:		
    		return SendMessage(GetParent(hWnd), WM_ERASEBKGND, wParam, 0L);
    
    	default:
    		break;
    	}
    	return CallWindowProc((WNDPROC) prevEditProc, hWnd, message, wParam, lParam);
    }
    

    Eigentlich müsste ich meines Wissens nach alles so gemacht haben wie du (mal von MFC->WinAPI abgesehen). Mir ist nur irgendwie aufgefallen, dass du WM_CTLCOLOREDIT beim Editcontrol selbst zu verarbeiten scheinst!? Ich mache das in der Dialogprozedur, aber daran kanns eigentlich nicht liegen, weil der Hintergrund ja schließlich transparent ist. Das Problem ist halt, dass das Hintergrundbitmap nicht neu gezeichnet wird, wenn sich der Inhalt der Editbox ändert...

    ph4nt0m



  • Mir ist noch was anderes aufgefallen: In deinem Code steht was von "ON_WM_CTLCOLOR_REFLECT". Ich kann zwar überhaupt kein MFC, aber daraus würde ich ableiten, dass die entsprechende Nachricht WM_CTLCOLOR_REFLECT heißt? Ist das vielleicht die Lösung meines Problems? Andererseits finde ich in der MSDN dazu nichts und mein Compiler kennt diese Nachricht ebensowenig...

    Was könnte ich denn noch ausprobieren? Ich bin für jeden Vorschlag dankbar. Mittlerweile hänge ich schon seit fast einer Woche an dem Problem und würde dafür so gerne endlich mal eine Lösung haben 😞

    ph4nt0m


  • Mod

    Das Reflect bedeutet nur, dass ich die Nachricht, die normalerweise an das Parent gesendet wird von der Fensterklasse selbst behandelt wird.

    Was ist denn jetzt noch Dein Problem. Mein Code funktioniert relativ gut. Besser wäre er noch wenn ich das Clipping auf das rect des Edit Controls beschränke.
    Ich habe aktuell aber keine Lust 🤡 das ganze runterzuschreiben...



  • Was mein Problem ist? :p Es funktioniert immer noch nicht! Den Code, den ich oben gespostet habe, hatte ich vorher genau so wie nach deinem Post. Ich habe einfach nichts gefunden, was noch falsch an meiner Umsetzung sein könnte.

    Allerdings habe ich folgendes entdeckt: Spy++ auf das Editcontrol angewandt zeigt bei Löschen des Inhalts nie WM_ERASEBKGND an. Und jetzt kommts noch besser: Ein direkter Breakpoint auf die Anweisung unter WM_ERASEBKGND des Editcontrols stoppt die Programmausführung nicht, wenn ich einen eingegebenen Text lösche. Ist das nicht ein Beweis dafür, dass WM_ERASEBKGND gar nicht an meine Editbox gesendet wird, wenn ich Text darin lösche, und somit auch keine Weiterleitung an das Parent geschieht -> Hintergrund wird NICHT neu gezeichnet?

    ph4nt0m


  • Mod

    Jupp! Sieht so aus. Vermutlich zeichnet das Edit Control die ganze Sache sogar direkt bei der Eingabe.
    Ich habe nur einen groben Test mit meinem Code gemacht. Ich habe aktuell etwas wenig Zeit. Ich kann also nicht auschliessen, dass das ganze doch nicht 100% funktioniert.
    Aber das würde bedeuten, dass Du gar keine Chance zum eingreifen hast. Denn Du bekommst nur WM_CTLCOLOREDIT gesendet. Das Edit Control verwendet immer den Brush, den Du lieferst zum löschen des Backgrounds...

    Such doch nochmal bei www.codeproject.com


  • Mod

    Versuch doch noch mal genau das bisherige Verfahren und zusätzlich nach jedem EN_CHANGE machst Du ein Invalidate().
    Dadurch habe ich auch das Löschen ins den Griff gekriegt. Ich habe es gerade noch mal probiert. Aber nun ist Schluß für heute...



  • Hmm, das wäre ziemlich blöd. Mit der WinAPI hat man doch sonst schon so viele Möglichkeiten... Ich habe über google schon vor Erstellen dieses Threads das gefunden. Nach Beschreibung genau das, was ich suche. Allerdings ist es in MFC (wie gesagt hab ich da keine Ahnung von) und die Kommentare unten drunter auf der Seite meinen teilweise, dass es sowieso nicht klappen würde. Testen kann ich es aber aus dem genannten Grund leider nicht 😞

    ph4nt0m



  • Also hab mir das Beispiel (von Deinem Link) mal angeguckt und finde das Resultat einfach nur lächerlich 😃 . Ähm, das ist doch gar nicht 'echt' transparent... .

    Hab Dir bei Codeproject mal das rausgesucht, das sieht ganz gut aus:
    - http://www.codeproject.com/dialog/pellucid.asp
    - http://www.codeproject.com/staticctrl/transparentstatic.asp?df=100&forumid=13858&exp=0&select=431984

    PS: Das ist auch alles (leider 😉 ) MFC, aber ich bin der Meinung, wer die WinAPI und C++ beherrscht (naja, mit dem Begriff sollte man wohl vorsichtig umgehen), kann auch MFC-Code verstehen.

    Leider geht es mit SetLayeredWindowAttributes *nicht*...das wäre ne schöne Sache :p gewesen.



  • So, also durch das Reagieren auf EN_CHANGE funktioniert das Löschen durch Drücken der Backspacetaste schonmal super 🙂

    Probleme gibt es aber leider noch, wenn ich den Text in der Editbox mit der Maus durch Ziehen erst markiere und anschließend den blau markierten Bereich wieder durch Ziehen verkleinere. Dann bleibt die blaue Markierung leider bestehen. Gibt es da eventuell auch eine weitere Nachricht, bei der man den Hintergrund löschen könnte?

    Eine andere Frage: Ich habe neben dem Hintergrundbitmap auch zwei Bitmapbuttons in meinem Dialog. Das sind normale Buttons, allerdings ist das Flag BS_OWNERDRAW gesetzt. Als Reaktion auf WM_DRAWITEM werden diese dann gezeichnet.

    Da aber zwischendurch ja jetzt durch das Editcontrol ein WM_ERASEBKGND an das Parent weitergeleitet wird, wird mein Bitmaphintergrund neugezeichnet, OHNE dass die Bitmapbuttons danach neugezeichnet werden. Sie sind also kurz gesagt nicht mehr zu sehen. Es wird nach dem WM_ERASEBKGND weder WM_DRAWITEM, noch WM_PAINT aufgerufen. Wie könnte ich diese Problem lösen? 🙂

    ph4nt0m


  • Mod

    Du musst das Löschen des Hintergrundes evtl. natürlich clippen.

    Oder WS_CLIPCHILDREN verwenden für den Dialog.



  • Danke für die schnelle Antwort 🙂 Ich habe das Flag jetzt für den Dialog gesetzt. Nun gibt es beim Verschieben des Fensters bei den beiden Schaltflächen aber ein unschönes Flackern (keine Ahnung woran das wieder liegen soll 🙄) und im Bereich der Editbox wird der Hintergrund überhaupt nicht gezeichnet. Der Dialog hat hier ein Loch 🙂 Der Grund ist klar: Durch WS_CLIPCHILDREN wird bei WM_ERASEBKGND ja auch genau dieser Bereich ausgespart, dabei soll ja gerade NUR hier der Hintergrund neugezeichnet werden.

    Es tut mir leid, wenn ich da langsam mit nerve. Ich hänge jetzt hier schon ewig mit diesem "eigentlich" trivialen Problem rum und halte dich damit irgendwie auf. Ich hätte am Anfang wirklich gedacht, dass das alles einfacher geht und nicht bei jedem Schritt wieder neue Probleme auftauchen 😞

    ph4nt0m


  • Mod

    Dann verwende die Links auf Codeproject.


Anmelden zum Antworten