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



  • 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


  • Mod

    Wenn Du nur scrollst bleibt das letzte Item aktiv. Bewegt nun der Benutzer die Cursor-Taste springt die Anzeige auf das zuletzt markierte Item zurück.

    Durch LB_SETCURSEL wird die Selektion versetzt.



  • Achso okay, das wusste ich nicht . Vielen Dank 🙂

    Habe noch eine weitere Frage : Wie machen Programme wie ICQ/MSN das, dass sie 2 Fenster auf einmal haben ? Ich denke mal, dass das Hauptfenster das mit den Kontakten ist. Wenn man dann ein Kontak doppel-klickt bekommt wird ja ein neues Fenster ( eben zum chatten mit diesem Kontakt ) geöffnet. Und dieses Fenster ist wohl definitiv kein Child-Window sondern ein eigenes Fenster. Da aber doch jedes Fenster eine eigene, unendliche Message-Schleife hat, müsste man das ganze wohl Mutlithreaden ?



  • Oh moment, ich bin wohl doch fündig geworden :

    http://stackoverflow.com schrieb:

    Your message loop fetches, and dispatches, messages for all windows created on the current thread.

    A simple message loop like this will suffice.

    MSG msg;
    while(GetMessage(&msg,0,0,0,0)>0)
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }

    Otherwise, to create windows on the thread, you simply call CreateWindow(...) as often as you want/need to build your UI.

    Warning: A common pattern seen in many simple programs is to handle WM_DESTROY in a WindowProc to call PostQuitMessage(). PostQuitMessage() posts a message to the thread, telling the message loop to exit. This will terminate the app, destroying all the windows on the thread. If you have a main application window that, if closed, should exit the app, only that window should call PostQuitMessage. If you have two (or more) windows that can be closed in any sequence, then you need to maintain a count of open windows and call PostQuitMessage only when the last window is closed by the user.

    Really, any sample program that demonstrates a dialog box is ALSO demonstrating how to create many windows on a thread, as every control on a dialog box IS also a window with a windowproc.

    Many new Windows developers fall into a trap of trying to filter messages for a specific window:

    while(GetMessage(&msg,hwnd,0,0,0)...

    This is wrong as it prevents any child windows getting their messages. Lots of utilitie libraries will create hidden windows on a thread and use them to receive messages from other processes / threads - filtering only messages for the app window like that will cause the message queue to grow, and other things to fail in strange an unusual ways. Pass NULL for the hwnd until you understand exactly why you might want to filter messages for a specific window for a while.

    Auf English googlen hilft wohl 😛



  • WinApi-Noob schrieb:

    Wenn man dann ein Kontak doppel-klickt bekommt wird ja ein neues Fenster ( eben zum chatten mit diesem Kontakt ) geöffnet. Und dieses Fenster ist wohl definitiv kein Child-Window sondern ein eigenes Fenster.

    Ich hoffe ich habe dich nicht falsch verstanden, aber das Erstellen eines Child-Window und das Erstellen eines neuen Fensters schließen sich ja nicht gegenseitig aus. Das zweite ist einfach ein modeless child window (kA wie's auch Deutsch richtig heißt).



  • Hmm, ich dachte ein Child-Window liegt bedingt innerhalb des Parent-Windows ? Bei den Chat-Programmen ist das ja eher nicht der Fall.



  • Sorry, dass ich schon wieder nerve aber ich bekomme hier was nicht hin :

    Ich habe eine Funktion, die in einer Endlosschleife auf Daten warten soll ( recv() von den WinSockets ) .
    Mein Problem : Wo soll die laufen ? Denn das Programm hat ja eig. schon eine Endlosschleife, die ausgeführt werden muss : Die Message-Schleife.

    Hatte mir überlegt, die funktion ( receive() ) in ein Thread zu packen und vor der Message-Schleife aufzuführen, in der Hoffnung, dass die sich wechselseitig CPU-Zeit geben 😛

    LPDWORD dwThreadId;
    HANDLE hTread = CreateThread( NULL , 0 , reinterpret_cast<LPTHREAD_START_ROUTINE>( receiveData ) , NULL , 0 , dwThreadId);            
    
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (GetMessage (&messages, NULL, 0, 0))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    

    Naja, scheint nicht so zu klappen. Mein Programm hängt sich auf.

    Villeicht denke ich auch zu kompliziert, kann ich die Funktion evtl auch einfach in der Message-Schleife ausführen lassen ? Etwa hier oder so :

    while (GetMessage (&messages, NULL, 0, 0))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
            // --> Hier
        }
    

    Und dann eben die Endlosschleife aus der Funktion an sich raushausen ?

    Mal meine receiveData() an sich :

    [cpp]
    void receiveData() {
    int rec = 0;
    char buffer[1024];
    while(1) {
    rec = recv(s,buffer,1024,0);
    //If recv returns 0 or SOCKET_ERROR the server closed the connection.
    if(rec == 0 || rec == SOCKET_ERROR ) {
    Data.push_back( "Server closed the Connection ..." );
    SendMessage(GetDlgItem(hwnd, ListBoxId), LB_ADDSTRING, 0,0);
    SizeListItems(GetDlgItem(hwnd, ListBoxId), Data);
    closesocket(s);
    WSACleanup();
    exit(1);
    }
    else {
    buffer[rec] = '\0';
    SendMessage(GetDlgItem(hwnd, ListBoxId), LB_ADDSTRING, 0,0);
    SizeListItems(GetDlgItem(hwnd, ListBoxId), Data);
    Data.push_back(buffer);
    }

    }

    }[cpp]



  • Umpf ... nochmal die Funktion :

    void receiveData() {
         int rec = 0;
         char buffer[1024];
         while(1) {
              rec = recv(s,buffer,1024,0);
              //If recv returns 0 or SOCKET_ERROR the server closed the connection.
              if(rec == 0 || rec == SOCKET_ERROR ) {
                 Data.push_back( "Server closed the Connection ..." );
                 SendMessage(GetDlgItem(hwnd, ListBoxId), LB_ADDSTRING, 0,0);
                 SizeListItems(GetDlgItem(hwnd, ListBoxId), Data);
                 closesocket(s);
                 WSACleanup();
                 exit(1);
              }
    
              else {
                buffer[rec] = '\0';
                SendMessage(GetDlgItem(hwnd, ListBoxId), LB_ADDSTRING, 0,0);
                SizeListItems(GetDlgItem(hwnd, ListBoxId), Data);
                Data.push_back(buffer);
              }
    
         }
    
    }
    

  • Mod

    Bau einen eigenen Thread dafür.
    Der neue Thread benötigt keine Messageloop!
    Du kannst enfach dort auf die Daten oder das Programmende warten.



  • Hmm, du meinst so wie im 1. Code den ich geposted habe :

    LPDWORD dwThreadId;
        HANDLE hTread = CreateThread( NULL , 0 , reinterpret_cast<LPTHREAD_START_ROUTINE>( receiveData ) , NULL , 0 , dwThreadId);            
    
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (GetMessage (&messages, NULL, 0, 0))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    

    Mir wurde gesagt, dass hier 1. der reinterpret_cast keinen Sinn macht ( wollte eig. auch static cast verwenden ) und 2. garkeiner gebraucht ist wenn man die Funktion richtig deklariert, und zwar sollte sie so aussehen :

    DWORD WINAPI receiveData(LPVOID)
    

    Warum man diese calling convention ( WINAPI alias __stdcall ) angeben muss is mir aber irgendwie schleiferhaft 😛

    Aber auch so funktioniert das nicht, ich weiss halt nicht wo ich den Thread starten soll.



  • Soooo, ich bins wieder mit neuen Probleme 😛

    Und zwar habe ich in meinem Main-Fenster ein Edit-Control zur Eingabe. Wenn der User Enter drückt, soll der Content davon abgeschickt werden. Das Problem : Das Edit-Control gibt mir keinerlei Infos darüber, was gerade eingegeben wurde. Da gibt es leider nur ES_UPDATE, dass mir zwar sagt, dass der Text gleich erweitert wird, aber nicht um welches Zeichen -.-

    Also musste meiner Meinung nach GetAsyncKeyState her :

    while (GetMessage (&messages, NULL, 0, 0))
        {
            if( GetAsyncKeyState( VK_RETURN ) & 0x8000 ) { 
               char temp_buf[1024];
               Edit_GetText( hInput, temp_buf , sizeof(temp_buf) );
               if ( strcmp( temp_buf, "" ) != 0 ) {
                   Data.push_back(temp_buf);
                   SendMessage(GetDlgItem(hwnd, ListBoxId), LB_ADDSTRING, 0,0);
                   SizeListItems(GetDlgItem(hwnd, ListBoxId ), Data);
                   SendMessage(GetDlgItem(hwnd, ListBoxId) , WM_VSCROLL, SB_BOTTOM, 0);
                   Edit_SetText ( hInput , "" );
               }
            } 
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    

    Problem : Da wird lauter Schmarrn ausgegeben ;P Das was ich eingebe, dann irgendwie noch 3 Leerzeilen. Verstehe ich also nicht, denn eigentlich dürfte GetAsyncKeyState ja nur anschlagen, wenn Enter down ist.

    Ein weiter Lösungvorschlag, der mir gemacht wurde : Im Hauptwindow Key-Events abfangen. Ja, das ist natürlich gut möglich, aber wie soll ich prüfen, ob der Key in der Edit-Box eingegeben wurde ? Da wurde dann Subclassing erwähnt, aber konkret habe ich keine Ahnung wie mir jetzt ne Klasse weiterhelfen soll ;P



  • EINE Zeile wird ausgegeben ( hab einfach mal der leere Zeugs angeklickt, und scheint ein Eintrag in der Listbox zu sein )


Anmelden zum Antworten