Transparente Editbox
-
Hallo,
ich möchte gerne eine Editbox (über den Ressourceneditor erstellt) in einem Dialog transparent auf eine Bitmap zeichnen. Den Rahmen konnte ich schon per Styleflags entfernen, aber wenn ich auf WM_CTLCOLOREDIT reagiere, indem ich SetBkMode(wParam, TRANSPARENT) aufrufe und anschließend noch einen HOLLOW_BRUSH zurückgebe, gibt es Probleme beim Zeichnen: Zwar hat die Editbox keinen Rahmen und keine Hintergrundfarbe mehr, aber wenn ich ein Wort eingebe und dieses wieder löschen will, bewegt sich nur das Caret wieder zurück. Der Text wird aber nicht entfernt.
Anscheinend gibt es ein Problem beim Neuzeichnen der Editbox. Ich vermute mal, dass irgendeine DefWndProc() mit meinem übergebenen HOLLOW_BRUSH die Editbox "übermalen" will, was natürlich erfolglos bleibt.
Hat jemand eine Idee, wie man dieses Problem lösen könnte? Bei google fand ich nur eine Umsetzung per MFC (ich hab keine Ahnung davon) und außerdem war da irgendwie nichts beschrieben, sondern lediglich ein kompletter Sourcecode des ganzen Projekts verfügbar.
ph4nt0m
-
Das ist doch auch klar. Der Hintergrund wird doch nicht neu gezeichnet.
Und durch die Behandlung von WM_CTLCOLOREDIT verhinderst Du, dass der Background neu gezeichnet wird.
So wie Du es machst ist das unmöglich. Die Standardfenster sehen nicht vor Transparent zu sein. Wenn müsstest Du in OnEraseBkGnd dafür sorgen, dass sich das Parent Fenster neu zeichnet...
-
Danke, dann war meine Vermutung ja richtig. Wie genau meinst du denn das mit WM_ERASEBKGND? Diese Nachricht bezieht sich ja normalerweise auf meinen gesamten Dialog. Muss ich da irgendwie mit Subclassing ran und eine eigene WndProc nur für die Editbox schreiben? Wie soll ich dann von dort eine Neuzeichnung des Parentfensters veranlassen?
ph4nt0m
-
Deine Edit Box bekommt genauso den Befehl den Hintergrund zu löschen. Durch das setzen des hollow brush wird das nun unterbunden. Dadurch wird aber der alte Inhalt auch nicht geöscht.
Du musst also veranllasen, dass beim WM_ERASEBKGND im Edit Control der Parent Hintergrund neu gezeichnet wird.
-
Ja, aber dafür muss ich doch eine gesonderte Fensterprozedur für die Editbox erstellen, oder? Wenn meine normale Prozedur WM_ERASEBKGND bekommt, dann bezieht sich das ja meines Wissens nach auf das gesamte Fenster. Und anhand von wParam oder lParam lässt sich der msdn zufolge auch nicht erkennen, an wen diese Nachricht gerichtet ist.
ph4nt0m
-
Erstmal entschuldige ich mich für den Doppelpost

Ich habe jetzt der Editbox eine eigene Fensterprozedur zugeordnet (Subclassing) und dann dort bei WM_ERASEBKGND ein Neuzeichnen des Parentfensters mit
InvalidateRect(GetParent(hWnd), NULL, TRUE);
veranlasst.Nun wird aber das gesamte Fenster anscheinend extrem oft neugezeichnet, obwohl ich noch gar nichts in die Editbox eingegeben habe und damit eigentlich kein Grund für zahlreiche Aufrufe von WM_ERASEBKGND besteht. Dennoch scheint diese Nachricht sehr oft an die Editbox gesendet zu werden...

Hat denn sonst vielleicht noch jemand einen Vorschlag?
ph4nt0m
-
1. Mach keinen Invalidate. Zumindest wenn Du nicht auch sofort wieder ein UpdateWindow auslöst! Das verursacht evtl. in Deinem eigenen Fensterwieder eine Rückkopplung.
2. Es ist tödlich wenn Du das ganze Fenter invalidierst. Warum besorgst Du Dir nicht das Clipping rectangle und invalidierst nur den Bereich des Parents, das kann schon helfen.
3. Was spricht dagegen WM_ERASEBKGND direkt an das Parent weiterzusenden? Den DC hast Du ja schon und geclippt ist der auch schon!
-
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
-
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
-
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
-
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
-
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
-
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
-
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...