ListCtrl sortieren
-
Hi,
ich hab nochmal ne Frage zu ListCtrl. Ich möchte meine Liste gerne sortieren lassen, wenn man in die Kopfzeile klickt.
Aber irgenwie schmiert er dabei immer ab. Habe das Beispiel aus den FAQ bzw der MSDN genommen.
// in der .h static int CALLBACK MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); in der .cpp int CALLBACK CZeitschrift::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { // lParamSort contains a pointer to the list view control. CListCtrl* pListCtrl = (CListCtrl*) lParamSort; CString strItem1 = pListCtrl.GetItemText(lParam1, 0); // hier schmiert er ab. CString strItem2 = pListCtrl.GetItemText(lParam2, 0); return strcmp(strItem2, strItem1); }Ich vermute das es daran liegt, das ich für mein ListCtrl eine Member-Varialbe names m_lcProgramm angelegt habe. Aber wie muß ich das hier einbauen?

Ach ja, so ruf ich SortItem auf.
void CZeitschrift::OnLvnColumnclickLprog(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); SORT_BY sortBy; switch (pNMLV->iSubItem) { default: ASSERT(false); /**/ case 0: sortBy = UHRZEIT; break; case 1: sortBy = EVENT; break; case 2: sortBy = STREAMER; break; case 3: sortBy = ON_AIR; break; } if (m_sortedBy == sortBy) m_sortedBy = static_cast<SORT_BY>(-sortBy); else m_sortedBy = sortBy; m_lcProgramm.SortItems(MyCompareProc, (LPARAM)m_sortedBy); *pResult = 0; }
-
So, ich hab rausgefunden, dass es wohl daran liegt, das in
int CALLBACK CZeitschrift::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)die Parameter lParam1 und lParam2 mit 0 übergeben werden.
lParamSort wird aber richig übergeben. Wie bekomme ich es jetzt hin, das die ersten beiden Parameter auch richtig übergeben werden?
-
Hallo,
so geht das nicht, in der MSDN ist das allerdings nicht ganz leicht zu erkennen, was denn in lparam1 und lparam2 geliefert wird. Es steht z.B. hier:
[quoute="MSDN"]
The lParam1 and lParam2 parameters specify the item data for the two items being compared.
[/quote]es werden also die "item data"-Werte übergeben, anhand derer die Vergleichsfunktion ihre Arbeit erledigen kann. Du mußt also jedem Item ein "Item Data" zuweisen, mit der Memberfunktion SetItemData (je nach dem, was verglichen werden soll, kann ein "item data" auch ein Zeiger sein, der auf eine Struktur mit Werten zeigt, oder ähnliches, usw...)
MfG
-
Seh ich das richtig, das wenn ich z.B.
strukt prog { CString Uhrzeit; CString Event; CString Streamer; CString Event; } ... prog heute; heute[0].Uhrzeit = "20:15"; heute[1].Uhrzeit = "19:25";habe, ich dann
m_lcProgramm.SortItems(MyCompareProc, (LPARAM)heute.Uhrzeit);hier die Struktur angeben muß, die sortiert werden soll?
So richtig habe ich das noch nicht verstanden.
-
ich sehe immer noch keinen Funktionsaufruf von SetItemData in deinem Code:
du mußt jedem Item, das in das ListControl eingetragen wird, beim Eintrag mit SetItemData diese Struktur "zuweisen". Da SetItemData aber nur ein DWORD als zu setzendes "item data" bietet, kannst du die zum Item gehörenden Daten, die alle in der "struct prog" liegen, als Zeiger auf diese struct übergeben:
// Item 0 ist eingefügt, jetzt "item data" zuordnen // Annahme: progarray ist ein Array aus prog-structs, der Index entspricht dem Item-Index SetItem(0, (DWORD) &progarray[0]);und in der MyCompareProc holst du die "item data" in Form dieser structs wieder heraus, und kannst jetzt, wie vorgesehen, die Vergleiche durchführen:
int CALLBACK CZeitschrift::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { int retVal = 0; // lParamSort contains the sort criteria passed via the second parameter of the SortItems call... switch((SORT_BY) lParamSort) { case UHRZEIT: retVal = ((prog*) lparam1)->Uhrzeit.Compare(((prog*) lparam2)->Uhrzeit); break; case EVENT: // compare event-CStrings break; case STREAMER: // compare streamer-CStrings break; case ON_AIR: // compare on-air CStrings break; default: ASSERT(FALSE); } return retVal; }so in etwa sollte das aussehen.
mußt du natürlich anpassen, je nachdem, ob alle Typen/Konstanten in der MyCompareProc "sichtbar"/verfügbar sind. Die Uhrzeit kann man wohl so vergleichen, weil Compare einen lexikografischen Vergleich durchführt, und so wohl z.B. "20:15" vor "20:30" "gesehen" wird, deswegen habe ich das als Beispiel genommen.
also zusammengefaßt: An die Callback-Funktion werden während des Sortier-Prozesses in den ersten beiden Parametern lParam1 und lParam2 die mit SetItemData gesetzten Werte übergeben. Anhand des dritten Arguments lParamSort, das die Callback-Funktion empfängt (das SortItems übergeben wird, es wird hier meist das Sortier-Kriterium, quasi die Spalte, nach der sortiert werden soll, übergeben) kann dann die geeignete Vergleichsoperationen ausgeführt werden, den Rest erledigt der interne Algorithmus.
MfG
-
So ich hab jetzt das jetzt mal ein bissel in die richtige Reihenfolge gebracht.
Ich habe folgende Strukturstruct TagesProg { CStringArray m_strEvent; CStringArray m_strTime; CStringArray m_strSender; CStringArray m_strChannel; CStringArray m_strOnAir; };Auf diese Stuktur habe ich einen Vector gelegt
vector<TagesProg *> myVectorTagesprog;Dieser wird von einer Website gefüllt. Pro Event ein neuer Vector
while(j < strAEvent.GetSize()) { TagesProg *pTProg = new TagesProg; myVectorTagesprog.push_back(pTProg); while( -1 != (pos = strAEvent[j].Find(';',pos))) { switch (n_Trenner) { case 0: pTProg->m_strTime.Add(strAEvent[j].Mid(old, pos - old)); break; case 1: pTProg->m_strEvent.Add(strAEvent[j].Mid(old, pos - old)); break; case 2: pTProg->m_strSender.Add(strAEvent[j].Mid(old, pos - old)); break; case 3: pTProg->m_strChannel.Add(strAEvent[j].Mid(old, pos - old)); break; case 4: pTProg->m_strOnAir.Add(strAEvent[j].Mid(old, pos - old)); break; } old = ++pos; n_Trenner++; } j++; old = 0; pos = 0; n_Trenner = 0; }Und jetzt das Problem!
Ich kann zwar den Vector ordentlich auslesen und das CListCtrl damit füllen, aber mit dem SetItem habe ich immer noch Probleme.
Im Augenblick sieht das so aus.for(int i = 0; i < myVectorTagesprog.size(); i++) { for(int j = 0; j < myVectorTagesprog[i]->m_strTime.GetSize();j++) { nItem = m_lcProgramm.GetItemCount(); nItem = m_lcProgramm.InsertItem(nItem, myVectorTagesprog[i]->m_strTime.ElementAt(j),-1); m_lcProgramm.SetItem(nItem, 0, (DWORD) &myVectorTagesprog[i]->m_strTime,NULL, 0, LVNI_ALL, LVNI_ALL, 0); m_lcProgramm.SetItemText(nItem, 1, myVectorTagesprog[i]->m_strEvent.ElementAt(j)); //m_lcProgramm.SetItem(nItem, 0, (DWORD) &myVectorTagesprog[i]->m_strEvent,NULL, 0, LVNI_ALL, LVNI_ALL, 0); m_lcProgramm.SetItemText(nItem, 2, myVectorTagesprog[i]->m_strSender.ElementAt(j)); //m_lcProgramm.SetItem(nItem, 0, (DWORD) &myVectorTagesprog[i]->m_strSender,NULL, 0, LVNI_ALL, LVNI_ALL, 0); m_lcProgramm.SetItemText(nItem, 3, myVectorTagesprog[i]->m_strOnAir.ElementAt(j)); //m_lcProgramm.SetItem(nItem, 0, (DWORD) &myVectorTagesprog[i]->m_strOnAir,NULL, 0, LVNI_ALL, LVNI_ALL, 0); } j = 0; } }Beim zweiten SetItem schmiert er dann ab. Hab auch
(DWORD) &myVectorTagesprog[i]statt
(DWORD) &myVectorTagesprog[i]->m_strTimeprobiert.
Das Programm läßt sich dann zwar compilieren, lParam1 und lParam2 werden trotzdem nicht übergeben.

-
arrgghhh...sorry, da wurde ein Data in meiner Antwort oben verschluckt, die Funktion, die du aufrufen mußt, heißt SetItemData!! In meinem Text habe ich es allerdings richtig erklärt (siehe oben), aber dann muß das Beispiel so aussehen (auf meinen alten Beitrag bezogen):
// Item 0 ist eingefügt, jetzt "item data" zuordnen // Annahme: progarray ist ein Array aus prog-structs, der Index entspricht dem Item-Index SetItemData(0, (DWORD) &progarray[0]);ich denke, jetzt kommst du klar, und weißt, was du zu tun hast (wenn's dann noch crasht, dann liegt es nicht mehr daran, aber dann einfach nochmal nachfragen)
MfG
-
Versuch mal mit meiner Klasse:
class CListCtrlSort { struct ListItemData { CString strText; DWORD_PTR dwData; }; struct SortData { bool bDescending; bool bNumeric; }; public: CListCtrlSort(CListCtrl& hWndListView, int iColumn) : m_hWndListView(hWndListView) { ASSERT(::IsWindow(m_hWndListView)); ASSERT(iColumn >= 0 && iColumn < m_hWndListView.GetHeaderCtrl()->GetItemCount()); const int iCount = m_hWndListView.GetItemCount(); for (int i = 0; i < iCount; ++i) { ListItemData* pData = new ListItemData; pData->strText = m_hWndListView.GetItemText(i,iColumn); pData->dwData = m_hWndListView.GetItemData(i); m_hWndListView.SetItemData(i,reinterpret_cast<DWORD_PTR>(pData)); } } bool Sort(bool bDescending = false, bool bNumeric = false) { const SortData sd = {bDescending,bNumeric}; return m_hWndListView.SortItems(OnCompare,reinterpret_cast<DWORD_PTR>(&sd)) != 0; } ~CListCtrlSort() { ASSERT(::IsWindow(m_hWndListView)); const int iCount = m_hWndListView.GetItemCount(); for (int i = 0; i < iCount; ++i) { ListItemData* pData = reinterpret_cast<ListItemData*>(m_hWndListView.GetItemData(i)); ASSERT(AfxIsValidAddress(pData,sizeof(ListItemData),TRUE)); m_hWndListView.SetItemData(i,pData->dwData); delete pData; } } private: static int CALLBACK OnCompare(LPARAM l1, LPARAM l2, LPARAM l3) { const SortData& sd = *reinterpret_cast<const SortData*>(l3); const ListItemData& lid1 = *reinterpret_cast<ListItemData*>(l1); const ListItemData& lid2 = *reinterpret_cast<ListItemData*>(l2); int iRet = 0; if (sd.bNumeric) { const int a = ::atoi(lid1.strText); const int b = ::atoi(lid2.strText); iRet = sd.bDescending ? a < b ? -1 : 1 : a > b ? -1 : 1; } else { iRet = lid1.strText.Compare(lid2.strText); if (sd.bDescending) iRet = iRet == -1 ? 1 : -1; } return iRet; } CListCtrl& m_hWndListView; };
-
Shlo schrieb:
Versuch mal mit meiner Klasse
Hi Shlo,
wie sprech ich deine Klasse denn an.
Ich wollt nen HWND auf meiner CListCtrl machen und die mit einer 0 für die erste Spalte am Konstruktor übergeben.
Klappt aber irgendwie nicht.
-
du musst mit Hifle des Klassen-Assistenten ein CListCtrl Object erstellen, das mit der ListView verknüpft wird. Wenn du einen Dialog hast, kannst du folgendes machen:
//z.B. bei LVN_COLUMNCLICK LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); CListCtrl* pListView = static_cast<CListCtrl*>(GetDlgItem(IDC_LIST1)); ASSERT(pListView && ::IsWindow(*pListView)); CListCtrlSort sort(*pListView,pNMLV->iSubItem); sort.Sort(false);
-
