Text "effektiv" in eine Winapi-Textbox einfügen



  • Hallo, ich schreibe gerade in kleines p2p-Chatprogramm, um den Gesprächsverlauf anzuzeigen benutze ich eine einfache mit winapi erstellte textbox:

    HWND textbox=CreateWindow(TEXT("Edit"), TEXT("Gesprächsverlauf"), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|WS_BORDER|WS_THICKFRAME|ES_MULTILINE|ES_AUTOHSCROLL|ES_AUTOVSCROLL, 20, 50, 700, 400, chatfenster, (HMENU) 1, hi2, 0);
    

    Mein Problem ist jetzt: Gibt es eine effiziente fertige Methode um eine neue Nachricht "unten", also unter die letzte Zeile, einzufügen?

    Natürlich könnte man jetzt ein riesiges Array allokieren, jedes mal mit GetWindowText() den kompletten Chatboxinhalt in das Array schreiben, den neuen Text einfügen und über SetWindowText() das komplette Array wieder in die Textbox zurückschreiben...

    Das Problem dieser Methode liegt da, dass ich ein wirklich riesiges Array brauche weil hier ziemlich schnell viel text zusammenkommt, außerdem würde der Text wahrscheinlich ziemlich flackern...

    Bei den WinForms-Textboxen gibt es ja z.B. eine schöne fertige Methode:

    this.TextBox1.Text += "neue Nachricht: ...";
    

    sowas für WinAPI-Textboxen wäre perfekt 🙂

    gibt es so eine Methode auch für eine WinAPI-Textbox? Es kann ja nicht sein dass man bei jeder Nachricht so einen Aufwand betreiben und so viel Speicher allokieren muss...

    danke schonmal,
    andi01.


  • Mod

    EM_SETSEL, EM_REPLACESEL.

    Windows Formsmacht nichts anderes.



  • momentan versuche ich es so:

    void text_einfügen(string nachricht)
    {
    	int länge=GetWindowTextLength(chatbox);
    
    	SendMessage(chatbox, EM_SETSEL, länge, länge);
    
    	SendMessage(chatbox, EM_REPLACESEL, (WPARAM) nachricht.c_str(), (LPARAM) nachricht.c_str());
    }
    

    (weil ich nicht sicher wusste ob der neue text ins wparam oder ins lparam muss habe ich ihn einfach mal in beide gesetzt^^)

    allerdings passiert dabei einfach gar nichts -.- (das hwnd der chatbox ist natürlich korrekt.)

    was mache ich denn falsch?

    lg,
    andi01.



  • wParam
    Specifies whether the replacement operation can be undone. If this is TRUE, the operation can be undone. If this is FALSE , the operation cannot be undone.

    lParam
    A pointer to a null-terminated string containing the replacement text.

    http://msdn.microsoft.com/en-us/library/bb761633(VS.85).aspx

    Aber schau dr WM_SETTEXT an:

    http://msdn.microsoft.com/en-us/library/ms632644(v=VS.85).aspx





  • EDIT: ok, ich habe jetzt ein paar fehler ausgebessert ...

    jetzt geht es soweit dass wenigstens hinten etwas in die textbox eingefügt wird 🙂

    allerdings ausschließlich unleserliche Zeichen 😞

    hier mal der code meine einfüge-funktion:

    void add(string text)
    {
    	int länge=GetWindowTextLength(textbox);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) text.c_str());
    }
    

    was ist denn falsch?

    in der msdn steht doch eindeutig:

    msdn schrieb:

    lParam
    A pointer to a null-terminated string containing the replacement text.

    lparam enthält doch diesen pointer 😕

    hier mal ein screen:
    chatbox ist der text mit dem es initialisiert wurde, no sieht es nach add("TEST") aus:

    http://img545.imageshack.us/img545/3149/zwischenablage01t.png

    lg,
    andi01.



  • Ist Dein Projekt evtl. ein UNICODE-Projekt? Dann müsstest Du einen Zeiger auf einen WCHAR mitgeben ...



  • danke genau daran lag es... hätte ich irgendwie auch selbst drauf kommen können 🙄

    naja, so gehts jedenfalls:

    void add(string text)
    {
    	int länge=GetWindowTextLength(textbox);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	char buf[1000];
    	strcpy(buf, text.c_str());
    
    	WCHAR buf2[1000];
    
    	_USE(cout.getloc(), ctype<wchar_t>).widen(buf, buf+strlen(text.c_str()), buf2);//konvertieren...
    
    	buf2[text.length()]=TEXT('\0');//null-terminieren...
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) buf2);//in chatbox schreiben...
    }
    

    gleih noch eine kleine Frage zur Textbox - auch wenn das jetzt nicht grade zum thema passt, abe ich wollte keinen neuen thread deswegen eröffnen:

    jetzt würde ich ganz gerne einzelne Zeilen farbig ausgeben... Ich bin schon so weit dass ich die hintergrund- und textfarbe und die farbe de´s ganzen Textes ändern kann:

    COLORREF textfarbe=RGB(1, 1, 1);
    COLORREF hintergrundfarbe=RGB(255, 255, 255);
    
    LRESULT CALLBACK WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
    {
    	switch ( msg )
    	{
    	case WM_CTLCOLOREDIT:
    		cout<<"message\n";//zur kontrolle
    		SetBkColor((HDC) wParam, hintergrundfarbe);
    		SetTextColor((HDC) wParam, textfarbe);
    		break;
    	default:
    		return DefWindowProc(hwnd, msg, wParam, lParam);
    		break;
    	}
    
    }
    void add(string text, COLORREF neue_textfarbe, COLORREF neue_hintergrundfarbe)
    {
    	int länge=GetWindowTextLength(textbox);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	char buf[1000];
    	strcpy(buf, text.c_str());
    
    	WCHAR buf2[1000];
    
    	_USE(cout.getloc(), ctype<wchar_t>).widen(buf, buf+strlen(text.c_str()), buf2);//konvertieren...
    
    	buf2[text.length()]=TEXT('\0');//null-terminieren...
    
    	//neue farbe setzen:
    	textfarbe=neue_textfarbe;
    	hintergrundfarbe=neue_hintergrundfarbe;
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) buf2);//in chatbox schreiben...
    }
    

    Beim Debugging sieht man klar, dass die einzelnen Farben immer richtig gesetzt werden 🙂 . Mein Problem ist aber, dass immer die Farbe vom kompletten Text bzw. Hintergrund geändert wird. Geht das auch zeilenweise?

    lg,
    andi01.


  • Mod

    Diese Konvertierung ist ja grässlich. Mit den ATL/TCHAR Makros bekommst Du das viel einfacher hin.
    Das ist ein Einzeiler!

    SendMessage(textbox, EM_REPLACESEL, true, reinterpret_cast<LPARAM>(CATW(text.c_str()));
    

    Unterschiedliche Farbe je Zeile bekommst Du mit einem Edit Control nicht hin. Dazu musst Du ein RTF Control einsetzen.



  • gut dann nehme ich eben ein RTF Control:

    //library laden:
    LoadLibraryA("riched32.dll");
    //...
    //textbox:
    textbox=CreateWindow(TEXT("RichEdit"), TEXT("chatbox"), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|WS_BORDER|WS_THICKFRAME|ES_MULTILINE|ES_AUTOHSCROLL|ES_AUTOVSCROLL, 20, 20, 300, 300, parent, (HMENU) 1, hi, 0);
    

    allerdings geht da die bisherige Methode nicht mehr in der WndProc bei der Nachricht WM_CTLCOLOREDIT SetBkColor() und SetTextColor() zu verwenden... welche Nachricht muss ich denn bei RichTextBoxen benutzen?

    wegen der Konvertierung: ich habe bis jetzt noch nie mit der ATL gearbeitet... ich habe schonmal in den projekteigenschaften die atl statisch gelinkt, allerdings ist der Bezeichner CATW() nicht definiert:

    Microsoft Visual C++ 2010 Express schrieb:

    test.cpp(392): error C3861: "CATW": Bezeichner wurde nicht gefunden.

    muss ich noch irgendwelche header einbinden oder so?

    btw: kann man in der expressversion überhaupt ATL benutzen?

    googlen bringt wiedermal nur Beispiele für WinForms, da geht das alles recht einfach -.-

    lg,
    andi01.



  • so ich habe unterdessen selbst eine Lösung gefunden:

    void AddText(HWND hwnd, string text, COLORREF farbe)
    {
    	int länge=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	char buf[1000];
    	strcpy(buf, text.c_str());
    
    	WCHAR buf2[1000];
    
    	_USE(cout.getloc(), ctype<wchar_t>).widen(buf, buf+strlen(text.c_str()), buf2);//konvertieren...
    
    	buf2[text.length()]=TEXT('\0');//null-terminieren...
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) buf2);//in chatbox schreiben...
    
    	//NEU:
    	CHARFORMAT cf;
    	cf.cbSize=sizeof(CHARFORMAT);
    	cf.dwMask      = CFM_COLOR | CFM_UNDERLINE | CFM_BOLD; 
    	cf.dwEffects   = (unsigned long)~(CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD); 
    	cf.crTextColor = farbe; 
    
    	int l=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, l, länge);
    
    	SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_SELECTION, (LPARAM)&cf); 
    }
    

    edit: ich habe doch noch eine Frage zu dieser Zeile:

    cf.dwEffects   = (unsigned long)~(CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD);
    

    wenn man die benutzt wird es farbig, aber nicht fett und nicht unterstrichen...

    wenn ich stattdessen

    cf.dwEffects   =   CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD
    

    benutze wird es unterstrichen, aber nicht farbig und nicht fett.

    kriegt man die Zeile irgendwie farbig, unterstrichen und fett?

    geblieben ist nur diese "grässliche Konvertierung" weil soweit ich weiß in der Express-Version von VisualStudio kein ATL verfügbar ist - korrigiert mich bitte wenn ich falsch liege!

    btw: wenn jemand eine bessere Konvertierungsmöglichkeit hat übernehme ich die gerne, nur ohne ATL kenne ich keine andere 🙄

    lg,
    andi01.


  • Mod

    Korrekt ATL/MFC sind in der EE nicht drin!



  • andi01 schrieb:

    wegen der Konvertierung: ...

    #include <atlconv.h> // für A2W
    ..
    // ATL
    USES_CONVERSION; // ich persönlich mag dieses Makro nicht
    SendMessage(textbox, EM_REPLACESEL, true, reinterpret_cast<LPARAM>(A2W(text.c_str()));
    
    // oder, da du C++ verwendest
    #include <vector>
    std::vector<wchar_t> wstr(str.length()+1,0);
    MultiByteToWideChar(CP_ACP,0,str.c_str(),str.length(),&wstr[0],str.length());
    SendMessage(textbox, EM_REPLACESEL, true, reinterpret_cast<LPARAM>(&wstr[0]);
    

    Edit: Das mit der ATL-Version hat sich damit sowieso erledigt



  • ok, die zweite Konvertierungsmöglichkeit werde ich dann mal übernehmen...

    bleibt nur noch die Frage von oben wie ich gleichzeitig farbig, fett und unterstrichen hinkriege. wäre schön wenn das auch noch funktioniert 🙂

    lg,
    andi01.



  • Ich habe es nicht ausprobiert, aber diese Zeile kommt mir verdächtig vor

    cf.dwEffects = (unsigned long)~(CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD);
    // vielleicht so?
    // Ich habe gerade erst gesehen, dass cf.dwEffects nicht initialisiert wurde, also
    // vor Bitoperationen
    cd.dwEffedcts = 0;
    // die nächste Zeile ist damit überflüssig
    cf.dwEffects &= ~CFE_AUTOCOLOR;
    cf.dwEffects |= (CFE_UNDERLINE | CFE_BOLD);
    

    Wie geschrieben, nicht getestet...

    Edit: Tag missraten



  • ich habe es gerade getestet. jetzt ist es farbig und unterstrichen.

    von wenn man BOLD weglässt merkt man aber keinen Unterschied - allerdings weiß ich nicht ob es einfach daran liegt dass eine RichTextbox das nicht besser darstellen kann oder daran dass es nicht funktioniert.

    btw: sowas habe ich ja noch nie gesehen:

    cf.dwEffects &= ~CFE_AUTOCOLOR;
    cf.dwEffects |= (CFE_UNDERLINE | CFE_BOLD);
    

    wo liegt denn da der Unterschied zu

    cf.dwEffects = (unsigned long)~(CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD);
    

    edit: hier mal 2 screens:

    mit boult: http://img842.imageshack.us/img842/1466/boult.png
    ohne boult: http://img600.imageshack.us/img600/8324/nichtboult.png

    ich sehe da keinen unterschied...

    lg,
    andi01.



  • Ich habe noch einen Edit reingeschrieben.

    dw = 2;
    dw &= ~(1<<1); // löscht das Bit
    dw |= ((1<<2)|(1<<3));
    // bei dw sind nun die Bits 2 und 3 gesetzt-> dw hat den Wert 12.
    


  • also hier mal der komplette code beider versionen:

    Version 1:

    void AddText(HWND hwnd, string text, COLORREF farbe)
    {
    	int länge=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	char buf[1000];
    	strcpy(buf, text.c_str());
    
    	WCHAR buf2[1000];
    
    	_USE(cout.getloc(), ctype<wchar_t>).widen(buf, buf+strlen(text.c_str()), buf2);//konvertieren...
    
    	buf2[text.length()]=TEXT('\0');//null-terminieren...
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) buf2);//in chatbox schreiben...
    
    	//NEU:
    	CHARFORMAT cf;
    	cf.cbSize=sizeof(CHARFORMAT);
    	cf.dwMask      = CFM_COLOR | CFM_UNDERLINE | CFM_BOLD; 
    	cf.dwEffects =0;
    	cf.dwEffects &= ~CFE_AUTOCOLOR;
    	cf.dwEffects|= (CFE_UNDERLINE | CFE_BOLD); 
    	cf.crTextColor = farbe; 
    
    	int l=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, l, länge);
    
    	SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_SELECTION, (LPARAM)&cf); 
    }
    

    version 2:

    void AddText(HWND hwnd, string text, COLORREF farbe)
    {
    	int länge=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, länge, länge);
    
    	char buf[1000];
    	strcpy(buf, text.c_str());
    
    	WCHAR buf2[1000];
    
    	_USE(cout.getloc(), ctype<wchar_t>).widen(buf, buf+strlen(text.c_str()), buf2);//konvertieren...
    
    	buf2[text.length()]=TEXT('\0');//null-terminieren...
    
    	SendMessage(textbox, EM_REPLACESEL, true, (LPARAM) buf2);//in chatbox schreiben...
    
    	//NEU:
    	CHARFORMAT cf;
    	cf.cbSize=sizeof(CHARFORMAT);
    	cf.dwMask      = CFM_COLOR | CFM_UNDERLINE | CFM_BOLD; 
    	cf.dwEffects =2;
    	cf.dwEffects &= ~(1<<1); // löscht das Bit
    	cf.dwEffects|= ((1<<2)|(1<<3));// bei dw sind nun die Bits 2 und 3 gesetzt-> dw hat den Wert 12.
    	cf.crTextColor = farbe; 
    
    	int l=GetWindowTextLength(hwnd);
    	SendMessage(textbox, EM_SETSEL, l, länge);
    
    	SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_SELECTION, (LPARAM)&cf); 
    }
    

    bei beiden Versionen sieht man keinen Unterschied:

    das Wort chatbox war der Text mit dem die chatbox initialisiert wurde, vermutlich nicht bold.
    hallo ist das mit AddText() reingeschriebene Wort.

    Version 1: http://img23.imageshack.us/img23/299/version1q.jpg
    Version 2: http://img204.imageshack.us/img204/4088/version2h.jpg

    mit und ohne boult sehen beider versionen gleich aus, deshalb reicht 1 screen pro version.

    ich sehe noch immer keinen Unterschied... könte das daran liegen dass immer boult print verwendet wird?
    **
    bleiben noch ein paar Fragen:
    1.liegt das mit dem Bould am RichEdit oder dem Code?
    2.Wieso kriegt mein RichEdit (v1.0) eigentlich kein VSCROLL hin?? selbst ein normales Edit schafft das doch! im Windowstyle habe ich auch WS_HSCROLL|WS_VSROLL|WS_AUTOHSCROLL|WS_AUTOVSCROLL angegeben...
    3. könnte man auch die Schriftgröße noch ändern?
    4.trotz dem Windowstyle WS_THICKFRAME sieht man keine dicke Abgrenzung zum restliche Fenster, erst nachdem man die Größe des RichEditfelds geändert hat. Wie kriegt man es hin dass man die sofort sieht?
    **
    edit: auch zu Frage 4 mal screens:

    vor Größenänderung: http://img88.imageshack.us/img88/1285/vorherm.jpg
    nach Größenänderung: http://img148.imageshack.us/img148/963/nachher.jpg

    wie schaffe ich es dass es gleich wie am 2. screen (nachher) aussieht?

    lg,
    andi01.



  • Es war nur ein Beispiel auf deine Frage nach den Bitmanipulationen.
    Was ein Wert von 12 bei cfEffects bewirkt, dass weiß ich nicht.
    Die doku schreibt

    CFM_COLOR
    The crTextColor member and the CFE_AUTOCOLOR value of the dwEffects member are valid.

    Zu dwEffects:

    CFE_AUTOCOLOR
    The text color is the return value of GetSysColor(COLOR_WINDOWTEXT).

    Hmm..

    CHARFORMAT cf;
    memset(&cf,0,sizeof(CHARFORMAT);
    cf.cbSize=sizeof(CHARFORMAT);
    cf.dwMask      = CFM_COLOR | CFM_UNDERLINE | CFM_BOLD; // probeweise mal CFM_COLOR rausnehmen
    cf.dwEffects = (CFE_UNDERLINE | CFE_BOLD);
    

    Am Besten, du spielst einmal ein wenig an dwMask und dwEffects herum.

    Zu Bitmanipulationen findest du hier eine kurze Erläuterung.



  • thx für den Link mit den Bitoperationen, hat wirklich geholfen obigen code zu verstehen 👍

    ok das mit dem bould hat sich wohl erledigt: wenn man den Ausschnitt ganz klein setzt und ungefähr um Faktor 20 im Bildbearbeitungsprogramm vergrößert erkennt man doch tatsächlich mit Spezialmarkierung dass der grüne Text 1 Pixel breiter ist 👍 damit wäre das "Problem" gelöst, das soll fett sein 😉

    edit: hier ein screen vom 20-fachen zoom xD: http://img176.imageshack.us/img176/6926/ultrazoomq.jpg

    das eine Pixel bringts jetzt 😃

    edit: bleibt noch ein problem: wenn ich ES_MULTILINE als WindowSTYLE nehme geht hscreoll und kein vscroll, wenn ihch es nicht nehme geht vscroll und kein hscroll -.- kann man es irgendwie schaffen dass beides geht?

    lg,
    andi01.



  • edit: ok, Problem gelöst: wenn man die RichTextBox (v1.0) mit Buchstaben vollschreibet gehen alle scrolls perfekt. wenn man stattdessen nur leerzeichen verwendet verhält sich die editbox reichlich komisch: es erscheinen keine scrollbalken, man kann einfach über die grenze der textbox hinausschreiben und autoscroll geht schon gleich gar nicht... sobald ich einen buchstaben einfüge normalisiert sich alles.

    auch wenn ich keine Ahnung habe warum das so ist, aber egal... ich will die editbox sowieso nicht nur mit leerzeichen füllen 🙂

    danke an alle,
    andi01.


Anmelden zum Antworten