Einführende Informationen über die Programmierung von Kontrollelementen werden hier vorausgesetzt.
Allgemein
Das ListView gehört zu den Common Controls. In C-Programmen ist das Einbinden der Datei COMMCTRL.H notwendig und die comctl32.lib muss zum Programm gelinkt werden.Ein ListView unterscheidet sich von der bisher bekannten Listbox in diversen Details. Während die Listbox typischerweise eine Liste von Textzeilen repräsentiert, ist das ListView eher die Darstellung von Objekten. Beide Elemente können mehrere Spalten darstellen. Das ListView ermöglicht eine Überschrift und die Verwendung dieser als Schaltleiste.
Stile
Typen
Die grundlegenden Stile eines ListView sind die unterschiedlichen Erscheinungsformen des Elements, wie man es aus dem Explorer kennt.
- LVS_ICON
- Jedes Element erscheint als ein großes Icon. Es kann vom Anwender frei verschoben werden.
- LVS_SMALLICON
- Jedes Element wird mit einem kleinen Icon dargestellt und einem Textlabel an seiner rechten Seite. Auch diese können frei verschoben werden.
- LVS_LIST
- Die Listendarstellung unterscheidet sich im Aussehen nicht von SMALLICON, die Elemente sind allerdings nicht verschiebbar und werden spaltenweise angeordnet.
- LVS_REPORT
- Jedes Element erscheint in einer eigenen Zeile mit mehreren Spalten. Die linke Spalte stellt dabei den Ankerpunkt dar. Hier kann auch ein kleines Icon stehen. Die Spalte ist immer linksbündig. Die weiteren Spalten werden als Subitem angesprochen. Jede Spalte besitzt einen Header, sofern LVS_NOCOLUMNHEADER nicht als Stil angegeben wurde.
Darstellung
LVS_ALIGNLEFT | Linksbündig (ICON und SMALLICON) |
LVS_ALIGNTOP | Obenbündig (ICON und SMALLICON) |
LVS_AUTOARRANGE | Elemente richten sich automatisch selbst aus (ICON und SMALLICON) |
LVS_EDITLABELS | Die Labels sind direkt editierbar. |
LVS_NOCOLUMNHEADER | Spaltenüberschriften sind nicht sichtbar (REPORT) |
LVS_NOLABELWRAP | Das Label bricht bei langen Zeilen nicht um. (ICON) |
LVS_NOSCROLL | Das Scrollen ist abgeschaltet. Alle Elemente müssen innerhalb der Clientfläche dargestellt werden. (ICON und SMALLICON) |
LVS_NOSORTHEADER | Die Spaltenüberschriften arbeiten nicht als Schaltflächen. Sinnvoll, wenn Sortierung o. ä. nicht unterstützt wird. |
LVS_OWNERDATA | Version 4.70. Specifiziert ein virtuelles ListView. |
LVS_OWNERDRAWFIXED | Die Anwendung zeichnet die Elemente. Sie erhält dazu die WM_DRAWITEM-Nachricht. (REPORT) |
LVS_SHAREIMAGELISTS | Die Imageliste wird nicht gelöscht, wenn das ListView zerstört wird. |
LVS_SHOWSELALWAYS | Die Selektion bleibt auch erhalten, wenn das Element den Fokus verliert. |
LVS_SINGLESEL | Es kann nur ein Element gleichzeitig selektiert werden. Standard ist die Multiselektierbarkeit. |
LVS_SORTASCENDING | Die Item Indizes werden in aufsteigender Folge anhand ihrer Texte sortiert. |
LVS_SORTDESCENDING | Die Item Indizes werden in absteigender Folge anhand ihrer Texte sortiert. |
LVS_SORTASCENDING und LVS_SORTDESCENDING werden bei LIST und REPORT sofort sichtbar, da sie nach der Reihenfolge ihrer Indizes angezeigt werden. Dagegen ist die Position der Icons bei ICON und SMALLICON vom Index unabhängig.
Nachträgliches Verändern der Stile
Der Stil eines ListView kann nach der Erzeugung geändert werden. Dazu werden die Funktionen GetWindowLong and SetWindowLong verwendet. Über Masken können verschiedene Stile abgegrenzt werden.
Maske | Maskierte Stile |
---|---|
LVS_TYPEMASK | LVS_ICON, LVS_LIST, LVS_REPORT und LVS_SMALLICON |
LVS_ALIGNMASK | LVS_ALIGNLEFT und LVS_ALIGNTOP |
LVS_TYPESTYLEMASK | LVS_ALIGNLEFT und LVS_ALIGNTOP aber auch LVS_NOCOLUMNHEADER und LVS_NOSORTHEADER |
Bei der folgenden Sequenz enthält dwView den Stil, in den gewechselt werden soll, beispielsweise LVS_REPORT oder LVS_ICON.
DWORD dwStyle = GetWindowLong(hwndLV, GWL_STYLE); // hole aktuellen Stil if ((dwStyle & LVS_TYPEMASK) != dwView) // nur bei Aenderung SetWindowLong(hwndLV, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView); } |
Ansteuerung des ListView-Controls
Erzeugung einer Liste
Ein List View wird mit der Funktion CreateWindow erzeugt. Als Fensterklasse wird die Konstante WC_LISTVIEW verwendet. Dazu muß die Common Control Header Datei eingebunden werden.#include "commctrl.h" InitCommonControls(); hwndList = CreateWindow(WC_LISTVIEW, "", WS_VISIBLE|WS_BORDER|WS_CHILD | LVS_REPORT | LVS_EDITLABELS, 10, 10, 300, 100, hWnd, (HMENU)ID_LIST, hInst, 0); |
Im Dialog wird sie einfach in der Ressource definiert.
Kommt es zu nicht aufgelösten Externals, sollte man prüfen, ob die Library für die Common Controls (comctl32.lib) eingebunden wird.
Spalten des ListViews
Bevor in einem REPORT etwas eingefügt werden kann, müssen die Spalten definiert werden. Beschrieben wird eine Spalte durch die Struktur LVCOLUMN. Die folgende Routine erzeugt eine Spalte.
int CreateColumn(HWND hwndLV, int iCol, char *Text, int iBreite) { LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; lvc.cx = iBreite; lvc.pszText = Text; lvc.iSubItem = iCol; return ListView_InsertColumn(hwndLV, iCol, &lvc); } |
Die Spalten können per Nachricht an das ListView geädert werden oder durch Aufruf von Makros, die letztlich ein SendMessage ausführen.
Nachricht | Makro-Aufruf | Funktion |
---|---|---|
LVM_INSERTCOLUMN | ListView_InsertColumn(HWND, int, LVCOLUMN *) | Spalte einfügen |
LVM_DELETECOLUMN | ListView_DeleteColumn(HWND, int) | Spalte löschen |
LVM_GETCOLUMN | ListView_GetColumn(HWND, int, LVCOLUMN *) | Eigenschaften der Spalte holen |
LVM_SETCOLUMN | ListView_SetColumn(HWND, int, LVCOLUMN *) | Eigenschaften der Spalte ändern |
LVM_GETCOLUMNWIDTH | ListView_GetColumnWidth(HWND, int) | Ermittle Spaltenbreite |
LVM_SETCOLUMNWIDTH | ListView_SetColumnWidth(HWND, int, int) | Setze Spaltenbreite |
Einfügen einer Zeile
Ein Element des ListView wird durch die Struktur LVITEM beschrieben. Jedes Element kann als ICON, SMALLICON, LIST-Element oder als linke Spalte einer REPORT-Zeile dargestellt werden.int CreateItem(HWND hwndList, char *Text) { LVITEM lvi = {0}; lvi.mask = LVIF_TEXT; lvi.pszText = Text; return ListView_InsertItem(hwndList, &lvi); } |
Das Feld mask bestimmt, welche Elemente der LVITEM-Struktur wirklich verwendet werden. Da es oft sinnvoll ist, einen Zeiger auf das Speicherobjekt mitzuführen, das die Daten hält, die hinter dem Objekt stehen, bietet sich das lParam-Feld an. Damit dies aber auch verwendet wird, muss LVIF_TEXT|LVIF_PARAM als Maske gesetzt werden.
Die Konstanten von mask und die Felder, die sie aktivieren:
LVIF_IMAGE | iImage |
LVIF_INDENT | iIndent |
LVIF_PARAM | lParam |
LVIF_STATE | state |
LVIF_TEXT | pszText |
Die weiteren Spalten eines Reports
Das Element selbst steht in der Reportansicht immer links und ist allein selektierbar. Um weitere Spalten zu füllen, wird ein Text dem Element hinzugefügt.int Create2ColItem(HWND hwndList, char *Text1, char *Text2) { LVITEM lvi = {0}; int Ret; // Initialize LVITEM members that are common to all items. lvi.mask = LVIF_TEXT; lvi.pszText = Text1; Ret = ListView_InsertItem(hwndList, &lvi); if (Ret>=0) { ListView_SetItemText(hwndList, Ret, 1, Text2); } return Ret; } |
Die Struktur LVITEM
Die Struktur LVITEM beschreibt ein Element der ListView. Die wichtigsten Elemente werden hier kurz beschrieben. Zunächst die Definition:typedef struct _LVITEM { UINT mask; int iItem; int iSubItem; UINT state; UINT stateMask; LPTSTR pszText; int cchTextMax; int iImage; LPARAM lParam; #if (_WIN32_IE >= 0x0300) int iIndent; #endif } LVITEM, FAR *LPLVITEM; |
Mit den Nachrichten LVM_GETITEM und LVM_SETITEM werden die Attribute eines Elements geändert. Sie erhalten als Parameter neben dem HWND des ListViews einen Zeiger auf eine LVITEM-Struktur, die vorher zu besetzen ist.
Die Strukturelemente im Einzelnen:
- mask
-
Spezifiziert, welche Elemente verwendet werden. Eine Oderung der folgenden Flags ist möglich:
LVIF_IMAGE iImage LVIF_INDENT iIndent LVIF_PARAM lParam LVIF_STATE state LVIF_TEXT pszText - iItem
- Index (0-basierend) des Items, auf das sich die Struktur bezieht.
- iSubItem
- Index (1-basierend) des Subitems, auf das sich die Struktur bezieht. 0, wenn sich die Struktur auf ein Item statt auf ein Subitem bezieht.
- pszText
-
zeigt auf einen nullterminierten String.
Ist der Wert LPSTR_TEXTCALLBACK, ist es ein Callback-Item.
Wenn sich dieser ändert, muß pszText auf LPSTR_TEXTCALLBACK
gesetzt werden und das ListView durch
LVM_SETITEM oder LVM_SETITEMTEXT informiert werden.
pszText darf nicht auf LPSTR_TEXTCALLBACK gesetzt werden, wenn das ListView den Stil LVS_SORTASCENDING oder LVS_SORTDESCENDING hat.
- cchTextMax
- Größe des Puffers, wenn der Text ausgelesen wird.
- iImage
- Index des Icons dieses Elements aus der Imageliste.
- lParam
- 32-bit Wert, der spezifisch für dieses Element ist.
Aktionen mit Elementen
LVM_INSERTITEM | Einfügung eines Elementes |
LVM_DELETEITEM | Löschen eines Elementes |
LVM_DELETEALLITEMS | Löschen aller Elemente |
LVM_GETITEM | Eigenschaften des Elementes lesen |
LVM_GETITEMTEXT | Text des Elementes lesen |
LVM_SETITEM | Änderung |
LVM_SETITEMTEXT | Änderung am Text |
Vor dem Einfügen mehrerer Items wird dem ListView eine LVM_SETITEMCOUNT Nachricht gesendet werden, die angibt, wieviele Elemente letztlich enthalten wird. Dies ermöglicht es dem ListView seine Speicherreservierung und -freigabe zu optimieren. Wieviele Elemente ein ListView enthält, ist mit LVM_GETITEMCOUNT zu ermitteln.
Bearbeiten selektierter Elemente
int Pos = -1; LVITEM Item; Pos = ListView_GetNextItem(hwndList, Pos, LVNI_SELECTED); while (Pos>=0) { Item.iItem = Pos; Item.iSubItem = 0; ListView_GetItem(hwndList, &Item); TuWasMitElement((ElementType*)Item.lParam); Pos = ListView_GetNextItem(hwndList, Pos, LVNI_SELECTED); } |
Ereignisse
Das ListView sendet WM_NOTIFY-Nachrichten an das Elternfenster. Der code kann folgende Werte annehmen:
Nachricht | Beschreibung |
---|---|
LVN_BEGINDRAG | Start einer Drag-And-Drop Aktion |
LVN_BEGINRDRAG | Start einer Drag-And-Drop Aktion über rechte Maustaste |
LVN_BEGINLABELEDIT | Start Editieren eines Labels |
LVN_ENDLABELEDIT | Ende Editieren eines Labels |
LVN_DELETEITEM | Meldet, daß das Item gelöscht wird |
LVN_DELETEALLITEMS | Meldet, daß alle Items gelöscht werden |
LVN_COLUMNCLICK | Zeigt an, daß der Anwendet in den Header einer Reportdarstellung klickte |
LVN_GETDISPINFO | Das Kontrollelement erfragt Informationen zur Darstellung vom Elternfenster |
LVN_SETDISPINFO | Die Information des Elternfensters für das Item muß erneuert werden |
LVN_INSERTITEM | Zeigt das Einfügen eines Items an |
LVN_ITEMCHANGED | Zeigt an, daß ein Item geändert wurde |
LVN_ITEMCHANGING | Zeigt die beabsichtigte Änderung eines Items an |
LVN_KEYDOWN | Taste wurde gedrückt |
Editieren der Labels
Das List View muß mit dem Stil LVS_EDITLABELS erzeugt worden sein. Dann kann bereits ein Label angeklickt werden und Eingaben werden entgegengenommen. Allerdings wird die Eingabe direkt anschließend wieder verworfen. Um Änderungen im Label zu gestatten, braucht man nur die WM_NOTIFY zu fangen und return TRUE zu geben. Um dazwischen auf den eingegebenen Text zuzugreifen, wird ein Zugriff auf den Text des Items durchgeführt. Das Beispiel zeigt die Eingabe in einer Messagebox.case WM_NOTIFY: switch (((LPNMHDR) lParam)->code) { case LVN_ENDLABELEDIT: pItem = (NMLVDISPINFO) lParam; MessageBox(hWnd, pItem->item.pszText, "entry", MB_OK); return TRUE; |
Wurde das Editieren abgebrochen, wird das pszText-Element 0 sein.
Soll das Editieren verhindert werden, wird die Nachricht LVN_BEGINLABELEDIT gefangen und TRUE zurückgegeben. Auch hier kann über lParam in gleicher Weise auf das Item zugegriffen werden und so beispielsweise eine bestimmte Itemgruppe ausgegrenzt werden.
Anklicken des Spaltenkopfs im ListView
case WM_NOTIFY: switch (((LPNMHDR) lParam)->code) { case LVN_COLUMNCLICK: SpalteNr = ((LPNMLISTVIEW) lParam)->iSubItem; |
Selektionsereignis
Das Ereignis LVN_ITEMACTIVATE wird gesendet, wenn der Anwender ein Item aktiviert. Wie bei den anderen ListView Ereignissen erreicht es die Fensterfunktion im Rahmen einer WM_NOTIFY Nachricht.case WM_NOTIFY: switch (((LPNMHDR) lParam)->code) { case LVN_ITEMACTIVATE: HWND hwndFrom = (HWND)((LPNMHDR) lParam)->hwndFrom; MarkedItemIndex = ListView_GetNextItem(hwndFrom,-1,LVNI_SELECTED); |
Mit Hilfe der LVM_GETSELECTEDCOUNT-Nachricht kann ermittelt werden, wieviele Items aktiviert wurden. Die LVM_GETNEXTITEM Nachricht wird mit dem Attribut LVNI_SELECTED gesendet bist alle Items bearbeitet wurden.