"Edit"-Window Kann man den Inhalt UPDATEN anstatt ihn zu überschreiben ?



  • Ahaa, erst mal danke für die ganzen ANtworten wieder 🙂

    Wie ich sehe ist das mit dem Edit-Control also durchaus möglich, jedoch tendiere ich dann doch eher zur Listbox. Schließlich scheint das die gängige und auch saubere Methode zu sein und wird von euch auch empfohlen.

    So, nun habe ich mein Listbox-Control so erstellt :

    hOutput = CreateWindowEx( WS_EX_CLIENTEDGE  | LBS_OWNERDRAWVARIABLE | LBS_NOTIFY, 
    "listbox", 
    "",
    WS_CHILD | WS_VSCROLL, 
    10, 
    90, 
    510, 
    280,
    hwnd, 
    NULL, 
    hThisInstance, 
    NULL);
    ShowWindow(hOutput, nFunsterStil);
    

    Guut, hwnd ist dabei mein Handle für das Main-Window. In deren WndProc habe ich dann folgendes gemacht, erstmal nur aus Debugging gründen :

    switch (message)                  /* handle the messages */
        {   
            case WM_COMMAND : 
    
            // ... Paar Sachen hier
    
            else if ( HIWORD(lParam) == WM_MEASUREITEM ) {
                 MessageBox(NULL,"Succ","Got the WM_MEASUREITEM message", MB_OK);
                 }
    

    Leider erscheint diese Message-Box zu Beginn des Programmes nicht, was sie ja eigentlich sollte ? Laut MSDN wird sie beim Erzeugen einer Listbox/etc. gesendet.
    Wozu ist es eigentlich nötig, dass ich sie abfrage ? Habe da nichts von wegen Text-formatierung o.ä gelesen ? Auch nicht bei WM_DRAWITEM 😞



  • WM_MEASUREITEM ist ja auch kein lParam-Wert, der einer anderen Nachricht angehängt wird, sondern eine vollwertige Window-Nachricht, d.h. du kannst ihn auf der selben Ebene verarbeiten wie WM_COMMAND:

    switch (message)                  /* handle the messages */
    {   
        case WM_COMMAND : 
            // ... Paar Sachen hier
            break;
        case WM_MEASUREITEM :
            MessageBox(NULL,"Succ","Got the WM_MEASUREITEM message", MB_OK);
            break;
    
        // ... weitere Nachrichten
    }
    


  • Achso, gilt das für alle Nachrichten mit WM_ als präfix ?

    Funktioniert aber leider trotzdem nicht 😕 Irgendwas mache ich falsch



  • WinApi-Noob schrieb:

    Achso, gilt das für alle Nachrichten mit WM_ als präfix ?

    Soweit ich weiß ja - und für deine selbstdefinierten Nachrichten (WM_USER+x)

    Funktioniert aber leider trotzdem nicht 😕 Irgendwas mache ich falsch

    Was sagt denn dein Debugger, wenn du dort einen Breakpoint reinsetzt?


  • Mod

    Warum machst Du es überhaupt Ownerdraw? Dan musst Du auch alles selber Zeichnen.

    Was willst Du nun? Das das Listcontrol für Dich die Anzeige regelt oder machst Du es selber?

    BTW: Die Suche nach Onerdraw Listbox liefert einiges an Samplecode.



  • Hmm, benutze ATM Dev C++ und da scheint debugging gerade nicht zulaufen. Müsste den Code eben umschreiben, damit er unter MVSC++ compiled ( der macht die konstanten Strings als Parameter gar nicht .... ).

    @ Martin Richter
    Nunja, das habe ich auf Vorschlag von einem der Member hier gemacht ( _Falke ) weil die Strings ja unterschiedlich lang sind. Das Hautpproblem ist ja wohl, dass es keine Zeilenumbrüche gibt -> Alles wird in eine Zeile geklatscht und den Rest kann man nicht mehr lesen.


  • Mod

    Dann wirst Du um Ownerdraw-Variable nicht drum herum kommen.



  • du must nicht unbedingt mit ownerdraw arbeiten.

    kuck dir mal GetTextExtentPoint32 an.

    das liefert dir die schrift höhe & weite des textes.
    die weite vergleichst du mit dem listbox fenster. wenn diese größer ist als die weite des listboxfensters, must du nur den text kürzen. alles was nicht mehr reinpasst fügst du in eine weitere zeile der listbox.

    ich denke das ist einfacher als jedes element zu zeichnen.

    [EDIT]
    btw. könntest du jeden buchstaben damit auf seine weite testen und solange die zeichenfolge erweitern bis du an den rand der listbox kommst. diese zeichenfolge fügst du dann als neues element ein. usw...



  • Ahhh okay. Dann müsste ich praktisch jedes mal wenn ein Client was abschicken will
    a) testen ob GetTextExtentPoint32 > WindowBreite
    b ) wenn ja -> Durch den Text loopen und ein nach dem anderen zu einem temp-string adden, dabei aber jedes mal seine breite auf eine int summen-variable addieren und einne abbruch-bedingung setzen falls summe > breite.
    Muss ich nurnoch mal überlegen, wie genau das aussehen muss. Schliesslich kann der text ja auch 3-4 ( oder mehr ) so breit sein. Dann muss der den 4 mal teilen.



  • Muss ich nurnoch mal überlegen, wie genau das aussehen muss. Schliesslich kann der text ja auch 3-4 ( oder mehr ) so breit sein. Dann muss der den 4 mal teilen.

    deshalb auch der vorschlag jeden buchstaben auf seine weite zu testen.
    das fester wird sich ja nicht all zu oft in seiner weite ändern.

    alles was du tun must, wäre
    a) dir die Listboxfenster weite zu besorgen. einmal zu begin und dann nur noch wenn der benutzer am dialogfenster etwas ändert.

    b) die ankommende zeichenfolge durch ne schleife schicken und jeden buchstaben auf seine weite testen.

    c) die weite jedes buchstaben addierst du zusammen und vergleichst jedesmal, ist die summe noch kleiner als die weite der listbox. wenn ja, setzt du den buchstaben in einen puffer. und testest den nächsten buchstaben.

    d) überschreitet der nächste buchstabe die weite der listbox, fügst du den puffer als neues element in die listbox. mit der verbleibenden zeichenfolge machst du genau das selbe nochmal wie unter b) & c).

    e) wenn dir bei der prüfung mal ein \r\n begegnet, schliebst du den puffer gleich als neues element in die liestbox.

    um das ganze nicht abgehackt ausehen zu lassen(worte), würde ich von leerzeichen zu leerzeichen prüfen ob die summe aller zeichen/karaktere in die listbox past. wenns nicht mehr reinpast den puffer als neues element in die listbox schieben und die verbleibene zeichenfolge weiter prüfen.

    wäre der einfachste weg das sauber und elegant zu lösen. 🕶

    [EDIT]

    Dann müsste ich praktisch jedes mal wenn ein Client was abschicken will...

    lass den sender schicken was er will. ich würde das den empfänger machen lassen. der muss sich drüm kümmern. woher will der sender wissen wie groß das fenster auf der empfänger seite ist.



  • Gut gut, dann fange ich mal mit dem Basteln an 😃 So ungefähr hatte ich es mir auch vorgestellt.

    @ Letzter Punkt : Ja, das ist mir dann auch klar geworden ^^ Der empfänger muss sich natürlich darum kümmern, schliesslich will er das ganze ausgegeben haben.

    Frage : GetTextExtentPoint32 erwartet als 2. Parameter char*. Wenn ich jetzt einzelne zeiche verwende : GetTextExtentWindow( hdc , &c_array[i] , 1 , size ); würde das die Ergebnise verfälschen ? Habe einfach den Addressen-operator verwendet um den Compiler-error zu umgehen 😛 Bin mir jetzt aber nicht mehr sicher, ob das die richtigen Ergebnise liefert.



  • Bin mir jetzt aber nicht mehr sicher, ob das die richtigen Ergebnise liefert.

    kann ich dir jetzt auch nicht sagen, einfach ausprobieren (try&error) 😉
    btw. könntest du es auch casten.

    GetTextExtentWindow( hdc , (char*)c_array[i] , 1 , size );
    

    [EDIT]
    oder aber du testest wie vorgeschlagen von leerzeichen zu leerzeichen (::isspace() / ::iswspace() ). um einen umbruch im wort zu vermeiden. 🙄



  • LowFly schrieb:

    wäre der einfachste weg das sauber und elegant zu lösen. 🕶

    Ähm ja?
    Also ich bin ja immer noch der Überzeugung, dass es mit Ownerdraw-Listboxes recht einfach gehen kann.

    Nehmen wir einmal an, wir speichern die Strings selber (es geht natürlich genau so einfach anders):

    std::vector<std::string> Data;
    

    Die Listbox kann nun folgende Stile bekommen

    WS_VISIBLE | WS_CHILD | WS_VSCROLL | LBS_OWNERDRAWVARIABLE | LBS_NOTIFY | LBS_NODATA
    

    LBS_NODATA deshalb, da die Listbox die ohnehin im Speicher stehenden Zeichenketten sich nicht selber merken muss.
    Das Zeichnen kann größtenteils von DrawText übernommen werden:

    case WM_DRAWITEM:
    	if(wParam == ListboxID)
    	{
    		LPDRAWITEMSTRUCT lpd = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
    		switch (lpd->itemAction) 
    		{
    			case ODA_SELECT: 
    			case ODA_DRAWENTIRE: 
    				if(Data.size() < lpd->itemID) return 0;
    				SetBkMode(lpd->hDC, TRANSPARENT);
    				FillRect(lpd->hDC, &lpd->rcItem,
    						(lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    				DrawText(lpd->hDC,
    						Data[lpd->itemID].c_str(),
    						Data[lpd->itemID].length(),
    						&lpd->rcItem,
    						DT_LEFT | DT_WORDBREAK);
    			case ODA_FOCUS:
    				break;
    		}
    		return 1;
    	}
    	break;
    

    Jetzt müssen wir nur noch jedem Eintrag eine Höhe zuweisen: Dies muss dann geschehen, wenn sich die Listbox in der Größe ändert oder ein neues Element hinzugefügt wurde:

    void SizeListItems(HWND list, const std::vector<std::string>& Data)
    {
    	if(!Data.size()) return;
    	HDC hdc = GetDC(list);
    	RECT ListClient;
    	GetClientRect(list, &ListClient);
    	bool erasebk = false;
    	for(size_t i=0;i<Data.size();++i)
    	{
    		int h = DrawText(hdc,
    			Data[i].c_str(),
    			Data[i].length(),
    			&ListClient,
    			DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
    		if(h != SendMessage(list, LB_GETITEMHEIGHT,i,0))
    		{
    			erasebk  = true; // wenn sich die Höhe eines Eintrags geändert hat, löschen wir den Hintergrund der Liste
    			SendMessage(list, LB_SETITEMHEIGHT, i, h);
    		}
    	}
    	ReleaseDC(list,hdc);
    	InvalidateRect(list, 0, erasebk);
    }
    

    Jetzt gilt es natürlich zu beachten, dass wirklich bei jedem Vergrößern der Liste diese Funktion auch aufgerufen wird. Dass heißt, wenn Data vergrößert wird:

    Data.push_back(...);
    SendMessage(GetDlgItem(hwnd, ListboxID), LB_ADDSTRING, 0,0);
    SizeListItems(GetDlgItem(hwnd, ListboxID), Data);
    

    Es müsste natürlich eigentlich nur die Größe des zuletzt eingefügten Elements angepasst werden.
    Wenn die Strings nicht separat gespeichert werden sollen, kann der Stil LBS_NODATA gestrichen und jeweils mit LB_GETITEMDATA ein Zeiger auf die Zeichenkette des jeweiligen Elements erhalten werden.
    Der Vorteil dieses separaten Speicherns ist natürlich, dass der Vektor nicht nur Strings, sondern auch Hintergrund-und Schriftfarbe, Bitmaps etc. speichert und die dann sehr leicht beim eigenen Zeichnen berücksichtigt werden können.



  • Edit:

    void SizeListItems(HWND list, const std::vector<std::string>& Data)
    {
    	if(!Data.size()) return;
    	HDC hdc = GetDC(list);
    	bool erasebk = false;
    	for(size_t i=0;i<Data.size();++i)
    	{
    		RECT itm;
    		SendMessage(list, LB_GETITEMRECT,i,reinterpret_cast<LPARAM>(&itm));
    		int h = DrawText(hdc,
    				Data[i].c_str(),
    				Data[i].length(),
    				&itm,
    				DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
    ...
    }
    

    Andernfalls wird die Höhe nicht richtig berechnet.



  • Wow, sehr schöner Code muss ich sagen 😛

    Leider verstehe ich ihn nicht ganz.
    Allegemein sollte ich villeicht erstmal wissen, wie eine Listbox aufgebaut ist.
    Es scheint ja so zu sein, dass jede Zeile eine id zu haben scheint ? D.h die 1. Zeile wird wohl 0 sein, die 2. 1, etc ?

    Zum code ( was ich nicht verstehe ) :

    case ODA_SELECT:
    

    Braucht manm denn da garkein break; ? Wird es nicht sonst einfach durchlaufen und jedes mal wenn ODA_SELECT gesendet wird auch ODA_DRAWENTIRE ausführen ?

    if(Data.size() < lpd->itemID) return 0;
    

    Wenn die Anzahl der Strings in dem Vektor kleiner der ... itemID ist ? Sind das die einzelnen Zeilen der Listbox ? Bzw Einträge ? Wenn das der Fall ist gibt es nichts zu tun ?

    (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW));
    

    Ich kenne die Notation an sich, soweit ich mich nicht irre ist das eine andere Schribweise für eine if-else Verzweigung ?
    Aber die Bedingung ist mir unklar ...

    So ... dass man die Einträge irgendwie auch in der Höhe einkalulieren muss verstehe ich, aber soweit ich gesehen habe würdest du den Text da ja nochmal schreiben ? o.0

    int h = DrawText(hdc,
                Data[i].c_str(),
                Data[i].length(),
                &ListClient,
                DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
    

    Die Funktion generell ist mir überhaupt nicht klar 😕 Wie die arbeitet und funktioniert.

    Nochmal danke für den Code erstmal 🙂



  • Zuerst einmal: Nicht nur bei Listboxes kann die Ausgabe übernommen werden, WM_DRAWITEM und das DRAWITEMSTRUCT sind daher nicht speziell auf dieses Control zugeschnitten.

    if(Data.size() < lpd->itemID) return 0;
    

    WinApi-Noob schrieb:

    Es scheint ja so zu sein, dass jede Zeile eine id zu haben scheint ? D.h die 1. Zeile wird wohl 0 sein, die 2. 1, etc ?

    Genau so ist es. lpd->itemID ist bei Listen der Index des Eintrags. Da die Zeichenketten in dem vector gespeichert sind, wird zur Vorsicht dieser Test durchgeführt. Wenn z.B. LB_ADDSTRING an die Listbox gesendet würde, ohne den enstsprechenden vector zu vergrößern, würde lpd->itemID außerhalb des Speicherbereich der Elemente des vectors liegen.

    WinApi-Noob schrieb:

    case ODA_SELECT:
    

    Braucht manm denn da garkein break; ? Wird es nicht sonst einfach durchlaufen und jedes mal wenn ODA_SELECT gesendet wird auch ODA_DRAWENTIRE ausführen ?

    Ja, so ist es, lpd->itemAction gibt an, warum der Eintrag neu gezeichnet werden soll. Würde man nur auf ODA_SELECT reagieren, würden nach einem Mausklick in das Control (mit einfacher Selektion) nur maximal 2 Elemente gezeichnet werden: das, das die Selektion verliert und dasjenige, das neu angewählt wurde.

    WinApi-Noob schrieb:

    (lpd->itemState & ODS_SELECTED)? GetSysColorBrush(COLOR_HIGHLIGHT): GetSysColorBrush(COLOR_WINDOW))

    Je nachdem, ob das Element selektiert ist oder nicht, soll der entsprechende Brush verwendet werden. Natürlich könnte man diese mit if / else sschreiben, dann wäre jedoch jeweils der komplette Funktionsaufruf von FillRect nötig. Das Ganze nennt sich bedingte Anweisung.

    , aber soweit ich gesehen habe würdest du den Text da ja nochmal schreiben ? o.0

    Als letzten Parameter von DrawText wird eine Bitmaske übergeben. Eine Konstante der Veroderung ist DT_CALCRECT. Wenn dieses Bit gesetzt ist, zeichnet DrawText nichts in den Kontext, sondern berechnet nur den Platz, den der Text einnehmen würde.

    Eine Sache habe ich auch noch vergessen: Wenn per GetDC der Kontext eines Fensters angefordert wird, ist die Systemschrift, nicht eine evetuell (per WM_SETFONT) übergebene Schrift gültig. Daher muss die Schrift in SizeListItems manuell gewählt werden:

    void SizeListItems(HWND list, const std::vector<std::string>& Data)
    {
    	if(!Data.size()) return;
    	HDC hdc = GetDC(list);
    	SelectObject(hdc, reinterpret_cast<HFONT>(SendMessage(list, WM_GETFONT, 0, 0)));
    ...
    }
    


  • Hey !

    Sorry, dass ich ewig nicht mehr reingeschaut habe, bin momentam im Urlaub ( Türkei). Hatte mehrfach versucht, etwas mit dem iPod zu posten, was aber nicht so wirklich funktioniert hat .

    Auf jeden Fall : Ich habe dein Code mal so in mein Programm implementiert und es funktioniert einwandfrei 🙂 ( Ausser wenn ein User eine Nachricht schickt, die nicht in einem Ganzen in die gesamte Listbox passt, dann spinnts bisl rum. Aber ... das wären eh extrem viele Zeichen, die Wahrscheinlichkeit ist also sehr gering und man kann das eh umgehen indem man einfach ein bestimmtes Char-Limit setzt ( da das Eingabefeld eine Editbox ist geht das ja ganz einfach, gibts glaube ich ein Makro dazu ) )
    Schade, dass ich das niemals alleine hinbekommen hätte 😛 Naja, ich bin schliesslich noch am Lernen. Bis zu Uni werde ich wohl relativ fit sein 😛

    Auf jeden Fall : Danke nochmal an alle, die hier gepostet haben, besonders an _Falke !



  • Nochwas vergessen :

    Für ein autoscroll reicht es wenn ich

    SendMessage(GetDlgItem(hwnd, ListBoxId) , WM_VSCROLL, SB_BOTTOM, 0);
    

    nach jedem Text-Update calle oder ? Soweit ichs getestet habe funktioniert das einwandfrei. Nur in der Callback function lief es nicht richig.


  • Mod

    Ich würde LB_SETCURSEL verwenden, oder wenn schon LB_SETTOPINDEX...



  • Okay, mal eben sehen ob ich das damit auch hinbekomme.
    Aber wieso würdest du das stattdessen verwenden ? Hat das irgendwelche Vorteile ?

    Mfg


Anmelden zum Antworten