Einführende Informationen über die Programmierung von Kontrollelementen werden hier vorausgesetzt.
Stile
Man kann mit Hilfe der Funktionen GetWindowLong und SetWindowLong nach der Erzeugung des Kontrollelementes Stile ändern oder anfragen.
- TVS_HASLINES
- durch Linien zwischen Eltern und Kindern wird die Hierarchie besser sichtbar gemacht.
- TVS_LINESATROOT
- Auch die Wurzelelemente bekommen eine Linie, die zum linken Rand führt.
- TVS_HASBUTTONS
- Durch Doppelklicken auf das Elternelement kann der Anwender zwar auf- und zuklappen. Deutlicher wird es durch Voranstellen einer Schaltfläche, die dann auch nur einmal geklickt werden muß. Es muß neben TVS_HASLINES auch TVS_LINESATROOT und TVS_HASBUTTONS gesetzt werden.
- TVS_EDITLABELS
- Dies ermöglicht dem Anwender das Editieren der Labels
- TVS_SHOWSELALWAYS
- Die Selektion wird aufrecht erhalten, wenn das Element seinen Fokus verliert.
Ansteuerung
Erzeugung eines Kontollelements
Ein Tree View wird mit der Funktion CreateWindowEx erzeugt. Die einfache CreateWindow funktioniert nicht. Als Fensterklasse wird die Konstante WC_TREEVIEW verwendet. Dazu muß die Common Control Header Datei eingebunden werden.#include "commctrl.h" InitCommonControls(); hTree = CreateWindowEx(0, WC_TREEVIEW , "Tree", WS_VISIBLE|WS_CHILD|WS_BORDER| TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS, 1, 1, 300, 100, // Positionen auf dem Elternfenster hWnd, (HMENU)ID_TREE, 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.
Hinzufügen eines Elements
Eine TVITEM-Struktur beschreibt ein Element. Für das Einfügen wird die TVINSERTSTRUCT-Struktur benötigt. Sie beschreibt das Eltern-Element und das Element, hinter dem das neue Element einsortiert werden soll. Die Adresse der Einfügestruktur wird dem Tree View mit der Nachricht TVM_INSERTITEM zugesandt.
HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, HTREEITEM parent, HTREEITEM brother) { TVITEM tvi; TVINSERTSTRUCT tvins; HTREEITEM hItem; tvi.mask = TVIF_TEXT; tvi.pszText = lpszItem; tvi.cchTextMax = lstrlen(lpszItem); // die Insert-Struktur wird vorbereitet tvins.item = tvi; if (brother) { tvins.hInsertAfter = brother; } else { tvins.hInsertAfter = TVI_FIRST; } if (parent==0) { tvins.hParent = TVI_ROOT; } else { tvins.hParent = parent; } // Nachricht zum Einbinden an das Kontrollelement versenden hItem = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)&tvins); return hItem; } . . . HTREEITEM opa,papa,onkel,bruder, ich; opa = AddItemToTree(hTree, "Opa", 0,0); papa = AddItemToTree(hTree, "Papa", opa,0); onkel = AddItemToTree(hTree, "Onkel", opa, papa); bruder = AddItemToTree(hTree, "Bruder", papa, 0); ich = AddItemToTree(hTree, "ich", papa, 0); |
Zwei Parameter bestimmen die Position des neuen Element. Das Elternelement und der direkte Nachbar, hier als brother bezeichnet. Beides wird in der Insert-Struktur festgelegt.
Verschieben eines Elements (Dragging)
Um das Verschieben eines Elementes zu verfolgen, werden die Nachrichten TVN_BEGINDRAG oder TVN_BEGINRDRAG, WM_MOUSEMOVE und WM_LBUTTONUP oder WM_RBUTTONUP beobachtet. Hat das Item ein Image, muß es gezeichnet werden, wenn es verschoben wird.
static HWND hTree=0; static int g_fDragging = FALSE; POINTS Pos; static HTREEITEM hItem; HTREEITEM htiTarget; // handle to target item TVHITTESTINFO tvht; // hit test information . . . case WM_NOTIFY: switch (((LPNMHDR) lParam)->code) { case TVN_BEGINDRAG: // lParam wird auf LPNMTREEVIEW gecastet und weiterverarbeitet LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW) lParam; hTree = lpnmtv->hdr.hwndFrom; HIMAGELIST himl; RECT rcItem; himl = TreeView_CreateDragImage(hTree, lpnmtv->itemNew.hItem); TreeView_GetItemRect(hTree, lpnmtv->itemNew.hItem, &rcItem, TRUE); ImageList_BeginDrag(himl, 0, 0, 0); ShowCursor(FALSE); SetCapture(GetParent(lpnmtv->hdr.hwndFrom)); g_fDragging = TRUE; break; } break; case WM_MOUSEMOVE: if (g_fDragging) { Pos = MAKEPOINTS(lParam); ImageList_DragMove(Pos.x, Pos.y); tvht.pt.x = Pos.x; tvht.pt.y = Pos.y; if ((htiTarget = TreeView_HitTest(hTree, &tvht)) != NULL) { TreeView_SelectDropTarget(hTree, htiTarget); } } break; case WM_LBUTTONUP: if (g_fDragging) { ImageList_EndDrag(); ReleaseCapture(); ShowCursor(TRUE); g_fDragging = FALSE; } break; |
Editieren der Labels
Das Tree View muß mit dem Stil TVS_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 mit dem Code TVN_ENDLABELEDIT 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 TVN_ENDLABELEDIT: pItem = (LPNMTVDISPINFO) lParam; MessageBox(hWnd, pItem->item.pszText, "entry", MB_OK); return TRUE; |
Wurde das Editieren abgebrochen, wird das pszText-Element 0 sein.
Das Element pItem->item ist nicht das Original TV_ITEM des Baumes, aber die Elemente hItem, lParam und pszText stimmen mit dem Original überein. Für das normale Editieren des Titels reicht das normalerweise auch. Wird aber beispielsweise das Image in Abhängigkeit des Textes verändert, müsste man das Element explizit mit TreeView_GetItem holen.
Soll das Editieren komplett verhindert werden, wird die Nachricht TVN_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.
Ereignisse
Das TreeView sendet WM_NOTIFY-Nachrichten an das Elternfenster. Der code kann folgende Werte annehmen:
Nachricht | Beschreibung |
---|---|
TVN_BEGINDRAG | Start einer Drag-And-Drop Aktion |
TVN_BEGINRDRAG | Start einer Drag-And-Drop Aktion über rechte Maustaste |
TVN_BEGINLABELEDIT | Start Editieren eines Labels |
TVN_ENDLABELEDIT | Ende Editieren eines Labels |
TVN_DELETEITEM | Item wird gelöscht |
TVN_GETDISPINFO | Das Kontrollelement erfragt Informationen zur Darstellung vom Elternfenster |
TVN_SETDISPINFO | Die Information des Elternfensters für das Item muß erneuert werden |
TVN_ITEMEXPANDED | Eine Liste von Kind-Items ist auseinandergezogen oder zusammengeklappt worden |
TVN_ITEMEXPANDING | Eine Liste von Kind-Items ist kurz davor, auseinandergezogen oder zusammengeklappt zu werden |
TVN_KEYDOWN | Taste wurde gedrückt |
TVN_SELCHANGED | Selektion zwischen den Items hat gewechselt |
TVN_SELCHANGING | Selektion zwischen den Items vor dem Wechsel |