tutorials.de Buch-Aktion 05/2012
Seite 1 von 3 123 LetzteLetzte
ERLEDIGT
NEIN
ANTWORTEN
36
ZUGRIFFE
6460
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    Hallo,

    weiß jemand, wie man ein Listenelement sortieren lassen kann, wenn man auf den Titel einer Spalte klickt?
    Ich hab bisher einiges gesucht und gefunden aber hab das nicht so richtig verwenden können bzw. teilweise nicht verstanden...

    Ist es dann auch möglich, das die benachbarten Spalten sich so mitändern, das jede Zeile in sich gleich bleibt?
     

  2. #2
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    Ja, du musst CListCtrl::SortItems() verwenden. Signatur ist folgende:
    BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData );
    pfnCompare ist eine Vergleichsfunktion, die zwei Items vergleicht. Die musst du zu Verfügung stellen und als Pointer übergeben:
    pfnCompare
    Address of the application-defined comparison function. The comparison function is called during the sort operation each time the relative order of two list items needs to be compared. The comparison function must be either a static member of a class or a stand alone function that is not a member of any class.

    The comparison function has the following form:

    int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  3. #3
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    Ähm irgendwie steig ich da noch nicht 100% durch.

    Erstmal wie mache ich es denn, das etwas passiert, wenn man auf den Titel einer Spalte in meinem Listenelement klickt?

    Und muss cih dann zwei Funktionen erstellen, die sortitem und die callback und was schireben cih da für parameter rein bzw was ruft wen auf?

    Sorry aber noch versteh ich das nicht wirklich...
     

  4. #4
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    Das ist alles ganz schön kompliziert. Da ich selber noch nicht mit der SortItemsfunktion gearbeitet habe, habe ich mich drangesetzt, es mal auszuprobieren.

    Zunächst mal: Um eine Reaktion auf den Klick auf den Spaltenheader zu erhalten, musst du dir vom Classwizard folgenden Messagehandler reinmachen lassen: OnColumnclick... mit der Message LVN_COLUMNCLICK. Kriegst du das hin?

    Ich habe das mal gemacht und zum Testen folgenden Code verwendet:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    int CALLBACK sortitems( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
    {
      TRACE( "%d, %d\n", lParam1, lParam2 );
      
      if ( lParam1 < lParam2 ) return +1;
      if ( lParam1 > lParam2 ) return -1;
      return 0;
    }
     
    void CSomeStatisticsDlg::OnColumnclickListResult(NMHDR* pNMHDR, LRESULT* pResult) 
    {
      NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        
      MessageBox( "OnColumnclickListResult" ); 
      m_ctlListResult.SortItems( sortitems, 0 );
     
      *pResult = 0;
    }
    An die Vergleichsfunktion werden die Itemdatas übergeben und zum Vergleich herangezogen. Bei mir hatte ich testweise die Items 0...n mit den Itemdatas 0...n versehen (mit SetItemData). Obiger Code dreht dann die Reihenfolge der Zeilen um.

    Ist das soweit verständlich? Wenn du genauer sagen kannst, wonach du sortieren willst, kann ich vielleicht einen Vorschlag machen, wie du das realisieren kannst.
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  5. #5
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    HI,

    also ich habe den Messagehandler für den Spaltenkopf angelegt, sieht bei mir so aus:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    void CAccess_testDlg::OnColumnclickList(NMHDR* pNMHDR, LRESULT* pResult) 
    {
        NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        // TODO: Code für die Behandlungsroutine der Steuerelement-Benachrichtigung hier einfügen
        
        MessageBox( "OnColumnclickListResult" ); 
        m_ctlList.SortItems( sortitems, 0 );
     
        *pResult = 0;
    }

    Wenn cih nun die CALLBCK FUnktion anlegen will mit Hilfe der Funktion um Mebmber-Funktionen hinzuzufügen erhalte ich allerdings dieses:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    
    int CALLBACK CAccess_testDlg::sortitems(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    {
        TRACE( "%d, %d\n", lParam1, lParam2 );
      
      if ( lParam1 < lParam2 ) return +1;
      if ( lParam1 > lParam2 ) return -1;
      return 0;
    }

    Und dann gibt er mir noch die Fehlermeldung aus, das:

    Code :
    1
    2
    
    error C2664: 'SortItems' : Konvertierung des Parameters 1 von 'int (long,long,long)' in 'int (__stdcall *)(long,long,long)' nicht moeglich
            Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem Zieltyp ueberein
    Also ich habe ein Listenelement mit 5 Spalten und in denen steht Text, nun möchte ich bei klicken auf den Spaltenkopf das die Texte alphabetisch sortiert werde in der angeklickten Spalte aber sich auch gleichzeitig die Zeilen richtig mitsortieren, so das die Logik richtig bleibt zwischen den Spalten...
     

  6. #6
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    int CALLBACK CAccess_testDlg::sortitems(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    Das darf keine Memberfunktion sein. Richtig ist int CALLBACK sortitems(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) .

    Wegen unterschiedlicher Spalten: Der Columnclick gibt dir einen Pointer auf eine Struktur, die weitere Infos beinhaltet.
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    typedef struct tagNMLISTVIEW{
        NMHDR   hdr;
        int     iItem;
        int     iSubItem;
        UINT    uNewState;
        UINT    uOldState;
        UINT    uChanged;
        POINT   ptAction;
        LPARAM  lParam;
    } NMLISTVIEW, FAR *LPNMLISTVIEW;
    Für dich ist wahrscheinlich iSubItem interessant - darin sollte die Spalte stehen.
    Du kommst folgendermassen dran:
    Code :
    1
    
    int iColumn = pNMListView->iSubItem; //ohne Gewähr - keine Zeit zum Testen.
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  7. #7
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    So nun funktioniert das erstmal, dass ich die Spaltennummer herausbekomme.

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    int CALLBACK sortitems( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
    {
      TRACE( "%d, %d\n", lParam1, lParam2 );
      
      if ( lParam1 < lParam2 ) return +1;
      if ( lParam1 > lParam2 ) return -1;
      return 0;
    }
     
     
    void CAccess_testDlg::OnColumnclickList(NMHDR* pNMHDR, LRESULT* pResult) 
    {
        NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        // TODO: Code für die Behandlungsroutine der Steuerelement-Benachrichtigung hier einfügen
        int iColumn = pNMListView->iSubItem; 
        CString spaltennummer;
        mensch.Format("%d", iColumn+1);
        MessageBox( spaltennummer ); 
        m_ctlList.SortItems( sortitems, 0 );
     
        *pResult = 0;
    }

    Abernun gibt er mir in der Messagebox nur die Spaltennumemr aus, aber sortiert nichts sondern gibt im AUsgabefenster 15 mal "0, 0" und das obwohl nur 10 Zeilen vorhanden sind.

    Hast du noch weitere Tipps?
    Hab nämlich nicht ganz verstanden, was ich mit der Struktur nun anfangen soll....
     

  8. #8
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    Ich zitiere mich noch mal selbst:
    An die Vergleichsfunktion werden die Itemdatas übergeben und zum Vergleich herangezogen. Bei mir hatte ich testweise die Items 0...n mit den Itemdatas 0...n versehen (mit SetItemData). Obiger Code dreht dann die Reihenfolge der Zeilen um.
    Meine Sortierfunktion war nur ein Test. Es ist klar, dass da so nix Sinnvolles bei rauskommt, wenn alle ItemDatas 0 sind. Jetzt musst du dir überlegen, wonach du sortieren willst. Vorschläge? Gib mal ein Beispiel für den Inhalt der Tabelle.
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  9. #9
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    Was sind denn ItemDatas, ist das nicht der sichtbare text?

    Also ich habe eine Liste, in der mehrere Spalten sind. 1. Spalte heißt "ID" und beinhaltet Zahlen, zweite Spalte heißt "Titel" und beinhaltet Titel von Büchern, 3. Spalte heißt "Autor" udn beinhaltet die Namen der Autoren etc.
    Es ist also eine Bücherliste.

    War das so verständlich?
     

  10. #10
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    Was sind denn ItemDatas, ist das nicht der sichtbare text?
    Jedes Item hat noch eine Art Userdata. Das sind 4 Byte, die nicht verwendet werden. Der User kann hier zusätzliche infos reinschreiben, wie zum Beispiel eine ID oder auch - was ich häufiger brauche - einen Zeiger auf weitere Daten.
    Lies mal das hier: http://www.tutorials.de/tutorials149542.html
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  11. #11
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    Also ich habe nun

    Code :
    1
    2
    3
    4
    5
    6
    7
    
            m_ctlList.InsertItem(iZeile, sBuch_ID);
            m_ctlList.SetItemText(iZeile, 1, m_strTitel);
            m_ctlList.SetItemData(data, data);
            m_ctlList.SetItemText(iZeile, 2, m_strAutor);
            m_ctlList.SetItemText(iZeile, 3, m_strJahr);
            m_ctlList.SetItemText(iZeile, 4, m_strVerlag);
            m_ctlList.SetItemText(iZeile, 5, m_strIsbn);
    data wird von 0 an hochgezählt so oft wie es einträge gibt.
    nun sortiert es auch, aber egal welche spalte ich anklicke es wird imemr die erste Spalte "ID" sortiert und nicht die anderen, die eigentlich sortiert werden sollen.
    Wie mache cih das, dass er es für die jeweilige Spalte macht?
     

  12. #12
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    nun sortiert es auch, aber egal welche spalte ich anklicke es wird imemr die erste Spalte "ID" sortiert und nicht die anderen, die eigentlich sortiert werden sollen.
    Eigentlich sollten alle Spalten mitsortiert werden, aber nach dem Itemdata, was bei dir der Spalte 0 (ganz links) entspricht, oder?

    Hier eine mögliche Lösung für ein Sortieren nach Spalte 1:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    //- Vergleichsfunktion, nimmt an, dass die Itemdatas Pointer auf CStrings enthalten
    int CALLBACK SortItemsWithString( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
    {
      //- irgendwas 0?
      if ( lParam1 == 0 || lParam2 == 0 ) return 0;
     
      //-die Strings rausholen
      CString* pA = (CString*)lParam1;  
      CString* pB = (CString*)lParam2;
      //- vergleichen und Resultat zurückgeben
      return pA->Compare( LPCTSTR( *pB ) );
    }
     
    void CSomeStatisticsDlg::OnColumnclickListResult(NMHDR* pNMHDR, LRESULT* pResult) 
    {
      NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        
      int subitem = 1; // BAUSTELLE! dies anpassen - ist so nur für Spalte 1
      int i       = 0;
     
      //- "Itemdatas mit Text füllen"
      for ( i = 0; i < m_ctlListResult.GetItemCount(); ++i )
      {
        CString strTemp = m_ctlListResult.GetItemText( i, subitem ); 
        m_ctlListResult.SetItemData( i, (DWORD)( new CString( strTemp ) ) );
      }
      //- in jedem Itemdata steht jetzt ein Pointer auf einen String, 
      //- nach dem sortiert werden kann
      
      //- sortieren
      m_ctlListResult.SortItems( SortItemsWithString, 0 );
     
      //- Itemdatas wieder freigeben
      for ( i = 0; i < m_ctlListResult.GetItemCount(); ++i )
      {
        CString* pA = (CString*) m_ctlListResult.GetItemData( i ); 
        delete pA;
        m_ctlListResult.SetItemData( i, 0 );
      }
     
      *pResult = 0;
    }
    Da gibt es noch Einiges für dich umzubauen, aber das sollte für dich kein Problem mehr sein.
    Geändert von Kachelator (18.03.04 um 14:39 Uhr)
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  13. #13
    ExtremeSnail ExtremeSnail ist offline Mitglied Gold
    Registriert seit
    Nov 2003
    Ort
    Hamburg
    Beiträge
    157
    Das ist genial, es klappt (fast) perfekt.

    Ich hab noch eine kleine Änderung vorgenommen, weil er sonst erst alle Gro0buchstaben sortiert und danach alle kleinen und man so dann sozusagen zweimal das Alphabet stehen hat.

    Änderung:
    Code :
    1
    2
    3
    4
    5
    6
    7
    
    //- "Itemdatas mit Text füllen"
      for ( i = 0; i < m_ctlList.GetItemCount(); ++i )
      {
        CString strTemp = m_ctlList.GetItemText( i, subitem );
        strTemp.MakeLower();
        m_ctlList.SetItemData( i, (DWORD)( new CString( strTemp ) ) );
      }

    Und noch ein Änderung damit es für jede Spalte funktioniert
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    void CAccess_testDlg::OnColumnclickList(NMHDR* pNMHDR, LRESULT* pResult) 
    {
        NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        
        int iSpalte = pNMListView->iSubItem;
      int subitem = iSpalte; // BAUSTELLE! dies anpassen - ist so nur für Spalte 1
      int i       = 0;
     
      //- "Itemdatas mit Text füllen"
      for ( i = 0; i < m_ctlList.GetItemCount(); ++i )

    Allerdings eine Kleinigkeit bleibt, in der "ID" Spalte sind ja nur zahlen vorhanden, Und nun sortiert er so, dass erst alle Zahlen kommen, die mit eins anfangen und dann die mit 2 anfangen etc, also ist eine 2 hinter z.B. einer 13 Also der Sortiert dann so:

    1
    11
    12
    13
    ..
    2
    21
    22
    ..
    3

    Gibt es da noch einen kleinen Trick?

    Man cih frag dir hier Löcher in den Bauch odeR? *gg+

    P.S.: Hätte sogar noch eine zweite Frage, wie stellt man es an, das es wenn es sortiert ist, bei einem weietren Klick sich andersherum sortiert?
     

  14. #14
    Registriert seit
    Jul 2003
    Ort
    Duisburg (NRW)
    Beiträge
    1.788
    Ich hab noch eine kleine Änderung vorgenommen, weil er sonst erst alle Gro0buchstaben sortiert und danach alle kleinen und man so dann sozusagen zweimal das Alphabet stehen hat.
    [...]
    strTemp.MakeLower();
    Verwende einfach CString::CompareNoCase()!

    1
    11
    12
    13
    ..
    2
    21
    22
    ..
    3
    [...]
    Gibt es da noch einen kleinen Trick?
    Ja, schreib die führenden Nullen mit rein (also aus "1" wird "01" oder "0001" usw., je nachdem, wieviele Stellen nötig sind).

    Man cih frag dir hier Löcher in den Bauch odeR?
    Ja.

    P.S.: Hätte sogar noch eine zweite Frage, wie stellt man es an, das es wenn es sortiert ist, bei einem weietren Klick sich andersherum sortiert?
    Merk dir für jede Spalte, in welche Richtung du zuletzt sortiert hast. Ein Bool würde dafür reichen. Du könntest zum Beispiel ein Array dafür verwenden.
    Aber man sagt, dass auch Spalten ein "ItemData" haben können... (Tipp: CListCtrl::GetHeaderCtrl(), CHeaderCtrl, HDITEM, lParam ).
    Geändert von Kachelator (18.03.04 um 16:37 Uhr)
     
    Chor: "Wir sind der Chor, und wir stimmen zu. Wir stimmen zu, wir stimmen zu, wir stimmen zu."

  15. #15
    Avatar von MAN
    MAN MAN ist offline Mitglied Brokat
    Registriert seit
    Mar 2004
    Beiträge
    426
    Ich bins mal wieder

    Habe mir diese "Diskussion" mal durchgelesen, genau so etwas brauchte ich - danke!

    Zu dem wollte ich noch wissen, wie ich den namen (caption, text, oder wie auch immer... ) von der ersten spalte meines liestview rausbekomme

    Das hilft mir beim sortieren mit deinem Tipp:

    Ja, schreib die führenden Nullen mit rein (also aus "1" wird "01" oder "0001" usw., je nachdem, wieviele Stellen nötig sind).
    Bloß, wenn ich einen Text habe, dann kann ich keine Nullen dranhängen, sonst sortiert er logischerweise nich richtig.

    Deshalb wollte ich über die Caption des Columns abfragen, ob die Spalte Zahlen oder Text beinhaltet (Zahlen im Sinne von ID, Text, ja... Text halt )


    mfG

    MAN
     

Ähnliche Themen

  1. IE Listenelement Lücke
    Von dezerver im Forum CSS
    Antworten: 4
    Letzter Beitrag: 19.11.07, 12:53
  2. Listenelement IE und FF
    Von Luna1000 im Forum CSS
    Antworten: 2
    Letzter Beitrag: 11.09.07, 16:55
  3. Text im Listenelement
    Von udo_the_man im Forum HTML & XHTML
    Antworten: 1
    Letzter Beitrag: 22.04.06, 21:32
  4. Hintergrund für Listenelement
    Von ares99kk im Forum CSS
    Antworten: 6
    Letzter Beitrag: 09.12.04, 10:05
  5. Listenelement, Farbe, checkbox
    Von jma im Forum VisualStudio & MFC
    Antworten: 1
    Letzter Beitrag: 22.03.04, 12:22