Iczelion - 19 - Tree View Steuerelement

Tutorial 19: Tree View Steuerelement


In diesem Tutorial werden wir lernen, wie man ein Tree View Steuerelement benutzt. Desweiteren werden wir Drag and Drop unter dem Tree View Steuerelement lernen und wie eine Image-List damit benutzt wird.
Laden Sie das Beispiel hier herunter.

Theorie:

Ein Tree View Steuerelement ist eine besondere Art von Fenster, das Objekte in hierarchischer Anordnung darstellt. Eine Beispiel dafür ist die linke Seite des Windows Explorer. Sie können dieses Steuerelement benutzen, um Beziehungen zwischen Objekten aufzuzeigen.
Sie können ein Tree View Steuerelement erzeugen, indem Sie CreateWindowEx mit "SysTreeView32" als Klassennamen aufrufen oder Sie können es in eine Dialog Box integrieren. Vergessen Sie nicht einen InitCommonControls Aufruf in Ihrem Code einzufügen.
Es gibt verschiedene Stile für das Tree View Steuerelement. Diese drei sind die am meist genutztesten:
    TVS_HASBUTTONS == Zeigt Plus (+) und Minus (-) Buttons neben dem Parent Items an. Der Benutzer kann durch klicken der Buttons die Child-Items einer Parent Items List anzeigen lassen oder auch wieder verstecken. Um Buttons bei den Root-Items des Tree View einzufügen, muss TVS_LINESATROOT auch angegeben werden.
    TVS_HASLINES == Benutzt Linien um die Hierarchie der Items zu zeigen.
    TVS_LINESATROOT == Benutzt Linien um Items vom Root des Tree View Steuerelement zu verbindne. Dieser Wert wird ignoriert, wenn nicht auch TVS_HASLINES angegeben wird.
Das Tree View Steuerelement kommuniziert mit dem Parent-Fenster, so wie andere Standard-Steuerelemente, via Nachrichten. Das Parent-Fenster kann verschiedene Nachrichten senden und das Tree View Steuerelement kann "Benachrichtigungs" Nachrichten an sein Parent-Fenster schicken. In dieser Hinsicht ist das Tree View Steuerelement nicht anders als jedes andere Fenster.
Wenn etwas interessantes auftaucht, sendet es eine WM_NOTIFY Nachricht an das Parent-Fenster mit den dazugehörigen Informationen.
    WM_NOTIFY
    wParam == Control ID, dieser Wert ist nicht unbedingt eindeutig, deshalb benutzen wir ihn nicht. Statt dessen benutzen wir das hwndFrom oder IDFrom Element der NMHDR Struktur auf die lParam zeigt
    lParam == Zeiger auf eine NMHDR Struktur. Einige Steuerelemente übergeben vielleicht einen Pointer auf einen größere Struktur, aber die muss dann eine NMHDR Struktur als erstes Element haben. Deshalb, wenn Sie lParam haben, können Sie sich sicher sein, dass auf mindestens eine NMHDR Struktur gezeigt wird.
Als nächstes schauen wir uns die NMHDR Struktur an.
NMHDR struct DWORD hwndFrom DWORD ? idFrom DWORD ? code DWORD ? NMHDR ends


hwndFrom ist das Fenster Handle des Steuerelementes, das diese WM_NOTIFY Nachricht sendet.
idFrom ist die Steuerelement ID des Steuerelementes, das die WM_NOTIFY Nachricht sendet.
code ist die aktuelle Nachricht des Steuerelementes, dass das Steuerelement an das Parent-Fenster senden möchte.
Tree View Benachrichtigungen sind diese, die mit TVN_ beginnen. Tree View Nachrichten beginnen mit TVM_, so wie TVM_CREATEDRAGIMAGE. Das Tree View Steuerelement sendet TVN_xxxx im code Element von NMHDR. Das Parent-Fenster kann TVM_xxxx an das Steuerelement senden.

Items dem Tree View Steuerelement hinzufügen

Nachdem Sie das Tree View Steuerelement erzeugt haben, können Sie Items hinzufügen. Sie können das machen, indem Sie TVM_INSERTITEM senden.
TVM_INSERTITEM wParam = 0; lParam = Zeiger auf TV_INSERTSTRUCT;


Sie sollten an diesem Punkt einige Terminologien über die Beziehungen zwischen Items im Tree View Steuerelement wisse.
Ein Item kann ein Paren, Child oder beides zur selben Zeit sein. Ein Parent-Item ist das Item, das einige Sub-Item(s) besitzt. Zur selben Zeit kann das Parent-Item ein Child eines anderen Items sein. Ein Item ohne Parent wir Root Item genannt. Es können mehrere Root Items in einem Tree View Steuerelement vorhanden sein. Nun schauen wir uns die TV_INSERTSTRUCT Struktur an:
TV_INSERTSTRUCT STRUCT DWORD hParent DWORD ? hInsertAfter DWORD ? ITEMTYPE TV_INSERTSTRUCT ENDS


hParent = Handle des Parent Item. Wenn dieses Element der TVI_ROOT Wert oder NULL ist, wird das Item als Root in das Tree View Steuerelement eingefügt.
hInsertAfter = Handle des Items nach welchem das neue Item eingefügt werden soll oder einer der folgende Werte:
  • TVI_FIRST ==> Fügt das Item am Anfang der Liste ein.
  • TVI_LAST ==> Fügt das Item am Ende der Liste ein.
  • TVI_SORT ==> Fügt das Item in die Liste in alphabetischer Reihenfolge ein.
ITEMTYPE UNION itemex TVITEMEX item TVITEM ITEMTYPE ENDS


Wir werden hier nur TVITEM benutzen.
TV_ITEM STRUCT DWORD imask DWORD ? hItem DWORD ? state DWORD ? stateMask DWORD ? pszText DWORD ? cchTextMax DWORD ? iImage DWORD ? iSelectedImage DWORD ? cChildren DWORD ? lParam DWORD ? TV_ITEM ENDS


Diese Struktur wird benutzt um Informationen über das Tree View Item zu senden und zu empfangen, abhängig von der Nachricht. Mit TVM_INSERTITEM zum Beispiel, werden die Attribute des Items spezifiziert, wie es in das Tree View Steuerelement eingefügt werden soll. Mit TVM_GETITEM, wird sie mit Informationen über das selektierte Tree View Item gefüllt.
imask wird benutzt um zu spezifizieren, welche(s) Element(e) der TV_ITEM Struktur gültig ist/sind. Wenn der Wert in imask zum Beispiel TVIF_TEXT ist, bedeutet das, dass nur das pszText Element gültig ist. Sie können verschiedene Flags miteinander kombinieren
hItem ist das Handle des Tree View Item. Jedes Item hat sein eigenes Handle, wie ein Fenster-Handle. Wenn Sie etwas mit dem Item machen möchten, müssen Sie es mittels seines Handles selektieren.
pszText ist der Zeiger auf einen Null-Terminierten String, der das Label des Tree View Items ist.
cchTextMax wird nur benutzt, wenn Sie das Label des Tree View Items ermitteln wollen. Da Sie den Zeiger auf den Buffer angeben, muss Windows wissen, wie groß der Buffer ist. Sie müssen die Größe des Buffers in diesem Element angeben.
iImage und iSelectedImage referrieren zu einem Index einer Image List, die die zu zeigenden Images enthält, wenn das Item nicht selektiert ist und wenn es selektiert ist. Wenn Sie sich an die linke Hälfte des Windows Explorer erinnen, werden die Verzeichnis-Images bei diesen beiden Elementen spezifiziert.
Um ein Item in das Tree View Steuerelement einzufügen, müssen Sie mindestens hParent, hInsertAfter füllen und Sie sollten auch die Elemente imask und pszText füllen.

Images zum Tree View Steuerelement hinzufügen

Wenn Sie ein Image links vom Tree View Item's Label plazieren wollen, müssen Sie eine Image List erzeugen und Sie mit dem Tree View Steuerelement verbinden. Sie können eine Image List erzeugen, indem Sie ImageList_Create aufrufen.
ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, \ cInitial:DWORD, cGrow:DWORD


Diese Funktion liefert bei Erfolg das Handle einer leere Image List zurück.
cx == Breite von jedem Image in dieser Image List in Pixeln.
cy == Höhe von jedem Image in dieser Image List in Pixeln. Jedes Image in einer Image List muss die gleichen Dimensionen wie die anderen haben. Wenn Sie ein größeres Bitmap angeben, wird Windows cx und cy benutzen, um es in verschiedene Teile zu *schneiden*. Deshalb sollten Sie ihre eigenen Images mit den selben Dimensionen erzeugen.
flags == spezifiziert die Art der Images in der Image List, ob sie farbig oder monochrom sind und ihre Farb-Tiefe. Konsultieren Sie Ihre Win32 API Referenz für mehr Details.
cInitial == Die Anzahl der Images die die Image List zur Initialisierung enthält. Windows benutzt diese Information um Speicher für die Images zu alloziieren.
cGrow == Menge der Images, zu der die Image List anwachsen kann, wenn das System die Größe der Liste verändern muss um Platz für neue Images zu machen. Dieser Parameter repräsentiert die Anzahl der neuen Bilder, die eine in der Göße geänderte Liste enthalten kann.
Eine Image List ist kein Fenster! Sie ist nur eine Zwischenlager von Images für die Benutzung von anderen Fenstern.
Nachdem eine Image List erzeugt wurde, können Sie Images hinzufügen, indem Sie ImageList_Add aufrufen.
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD


Diese Funktion liefert beim Fehlschlagen -This function zurück.
himl == das Handle der Image List, der Sie die Images hinzufügen wollen. Das ist der Wert, der von einem erfolgreichen ImageList_Create Aufruf zurückgeliefert wird.
hbmImage == Das Handle des Bitmaps, das der Image List hinzugefügt werden soll. Normalerweise speichern Sie Bitmaps in Ressourcen und laden Sie dann mit einem LoadBitmap Aufruf. Beachten Sie, dass Sie die Anzahl der Images, die im Bitmap enthalten sind, nicht angeben müssen, da diese Informationen aus den Parametern cx und cy, die ImageList_Create übergeben werden, gewonnen werden.
hbmMask == Handle des Bitmaps, dass die Maske enthält. Wenn keine Maske bei der Image List benutzt wird, wird dieser Parameter ignoriert.
Normalerweise fügen wir nur zwei Images in die Image List ein, wenn wir ein Tree View Steuerelement benutzen: eins, das benutzt wird, wenn das Tree View Item nicht selektiert ist und das andere, wenn das Item selektiert ist.
Wenn die Image List bereit ist, verknüpfen Sie sie mit dem Tree View Steuerelement indem Sie TVM_SETIMAGELIST an das Tree View Steuerelement senden.
    TVM_SETIMAGELIST
    wParam = Art der zu setzenden Image List. Es gibt zwei Möglichkeiten:
    • TVSIL_NORMAL Setzt die normale Image List, die die selektierten und unselektierten Images für das Tree-View Item enthält.
    • TVSIL_STATE Setzt die Status Image List, die die Images für das Tree-View Item enthält, welche in einem benutzerdefinierten Status sind.
    lParam = Handle der Image List

Informationen über Tree View Items erhalten

Sie können Informationen über ein Tree View Item erhalten, indem Sie eine TVM_GETITEM Nachricht senden.
    TVM_GETITEM
    wParam = 0
    lParam = Zeiger auf die TV_ITEM Struktur die mit den Informationen gefüllt wird
Bevor Sie diese Nachricht senden, müssen Sie das imask Element mit den Flag(s) füllen, dass angibt, welche(s) Element(e) von TV_ITEM Sie von Windows füllen lassen wollen. Und am wichtigsten, Sie müssen hItem mit dem Handle des Item, über das Sie Infos haben wollen, belegen. Und das bringt ein Problem mit sich: Wie können Sie das Handle des Items kennen, über das Sie Informationen erhalten wollen? Müssen Sie alle Tree View Handles speichrn?
Die Antwort ist ziemlich einfach: sie müssen nicht. Sie können eine TVM_GETNEXTITEM Nachricht an das Tree View Steuerelement senden, um das Handle des Tree View Items, das diese(s) angegebenen Attribut(e) hat, zu ermitteln. Sie können zum Beispiel das Handle des ersten Child Item erfragen, des Root Items, das selektierten Items und so weiter.
    TVM_GETNEXTITEM
    wParam = Flag
    lParam = Handle eines Tree View Items (nur für einige Flag-Werte nötig)
Der Wert in wParam ist sehr wichtig, weshalb ich alle Flags präsentiere:
    • TVGN_CARET Ermittelt das aktuell selektierte Item.
    • TVGN_CHILD Ermittelt das erste Child Item, des Items, das im hItem Parameter angegeben ist.
    • TVGN_DROPHILITE Ermittelt das Item, das das Ziel einer Drag-and-Drop Operation ist.
    • TVGN_FIRSTVISIBLE Ermittelt das erste sichtbare Item.
    • TVGN_NEXT Ermittelt das nächste Sibling-Item.
    • TVGN_NEXTVISIBLE Ermittelt das nächste sichtbare Element, das dem angegebenen Element folgt. Das angegeben Item muss sichtbar sein. Benutzen Sie die TVM_GETITEMRECT Nachricht, um zu bestimmen, ob das Item sichtbar ist.
    • TVGN_PARENT Ermittelt das Parent des angegebenen Items.
    • TVGN_PREVIOUS Ermittelt das vorherige Sibling-Item.
    • TVGN_PREVIOUSVISIBLE Ermittelt das erste sichtbare Item, das vor dem angegebenen Item ist. Das angegebene Item muss sichtbar sein. Benutzen Sie die TVM_GETITEMRECT Nachricht, um zu bestimmen, ob das Item sichtbar ist.
    • TVGN_ROOT Ermittelt das oberste oder allererste Item des Tree View Steuerelementes.
Sie sehen, dass Sie das Handle des Tree View Items, an dem Sie Interesse haben, mit dieser Nachricht ermitteln können. SendMessage liefert bei Erfolg das Handle des Tree View Items zurück. Sie können das zurückgegebene Handle im hItem Element von TV_ITEM speichern, um es mit der TVM_GETITEM Nachricht zu benutzen.

Drag and Drop Operationen im Tree View Steuerelement

Dieser Teil ist der Grund, warum ich micht entschieden habe, dieses Tutorial zu schreiben. Als ich versucht habe, dem Beispiel in der Win32 Api Referenz (die win32.hlp von InPrise) zu folgen, war ich recht frustriert, da die wichtigsten Informationen fehlten. Mit Trial-and-Error habe ich herausgefunden, wie man Drag & Drop in einem Tree View Steuerelement implementiert und ich möchte nicht, dass jemand nochmal den selben Weg wie ich geht.
Folgend die Schritte um Drag & Drop Operationen in einem Tree View Steuerelement zu implementieren.
  1. Wenn der Benutzer versucht, ein Item zu draggen, sendet das Tree View Steuerelement eine TVN_BEGINDRAG Benachrichtigung an das Parent-Fenster. Sie können diese Möglichkeit wahrnehmen, um ein Drag-Image zu erzeugen, was das Item repräsentiert, während es gedragged wird. Sie können TVM_CREATEDRAGIMAGE an das Tree View Steuerelement senden, um ihm mitzuteilen, das es ein Standard Drag-Image aus dem Image, das gerade vom Item benutzt wird, das gedragged wird, erzeugen soll. Das Tree View Steuerelement erzeugt eine Image List mit nur einem Drag-Image und liefert das Handle der Image List an Sie zurück.
  2. Nachdem das Drag-Image erzeugt wurde, spezifizieren Sie den Hotspot des Drag Image, indem Sie ImageList_BeginDrag aufrufen.
  3. ImageList_BeginDrag PROTO himlTrack:DWORD, \ iTrack:DWORD , \ dxHotspot:DWORD, \ dyHotspot:DWORD



    himlTrack ist das Handle der Image List, dass das Drag-Image enthält.
    iTrack ist der Index in die Image List, der das Drag-Image spezifiziert
    dxHotspot spezifiziert die relative Distanz des Hotspots in horizontaler Ebene im Drag Image, da dieses Image anstatt des Maus-Cursors benutzt wird, weshalb wir den Teil des Images, der den Hotspot darstellt, spezifizieren müssen.
    dyHotspot spezifiziert die Relative Distanz des Hotspots in der vertikalen Ebene.
    Normalerweise würde iTrack 0 sein, wenn Sie dem Tree View Steuerelement mitteilen, dass es für Sie das Drag-Image erzeugen soll. Und dxHotspot und dyHotspot können 0 sein, wenn Sie die linke obere Ecke des Drag-Image als Hotspot haben wollen.
  • Wenn das Drag Image bereit ist, angezeigt zu werden, rufen wir ImageList_DragEnter auf, um das Drag Image im Fenster anzuzeigen.
    1. ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
      hwndLock ist das Handle des Fensters, das das Drag Image besitzt. Das Drag Image kann nicht aus dem Fenster heraus bewegt werden.
      x und y sind die X/Y-Koordinaten des Ortes, wo das Drag-Image bei Initialisierung angezeigt werden soll. Beachten Sie, dass diese Werte relativ zur oberen linken Ecke des Fensters sind, nicht zur Client Area.
  • Nun, da das Drag Image im Fenster angezeigt wird, müssen Sie die Drag Operation im Tree View Steuerelement unterstützen. Hier gibt es allerdings ein kleines Problem. Wir müssen den Drag-Weg mit WM_MOUSEMOVE überwachen und den Drop-Ort mit WM_LBUTTONUP. Wenn das Drag Image über einem anderen Child-Fenster ist, wird das Parent Fenster niemals je eine Maus-Nachricht erhalten. Die Lösung ist, die Maus-Eingaben mit SetCapture abzufangen. Mit diesem Aufruf werden die Maus-Nachrichten an das angegebene Fenster geleitet, egal wo sich der Maus-Cursor befindet.
  • Innerhalb des WM_MOUSEMOVE Handler, werden Sie den Drag-Weg mit einem ImageList_DragMove Aufruf updaten. Diese Funktion bewegt das Image, das gedragged wird, während einer drag-and-drop Operation. Des weiteren können Sie, wenn Sie es wünschen, das Item hiliten, über dem das Drag-Image ist, indem Sie TVM_HITTEST senden, um zu überprüfen, ob das Drag Image über anderen Items ist. Wenn es ist, können Sie TVM_SELECTITEM mit dem TVGN_DROPHILITE Flag senden, um das Item zu hiliten. Beachten Sie, dass Sie, bevor Sie die TVM_SELECTITEM Nachricht senden, erste das Drag Image verstecken müssen, ansonsten hinterlässt ihr Drag Image häßliche Spuren. Sie können das Drag Image verstecken, indem Sie ImageList_DragShowNolock aufrufen und, nach Beendigung der hilite Operation ImageList_DragShowNolock aufrufen, um das Drag Image erneut zu zeigen.
  • Wenn der Benutzer die linke Maus-Taste loslässt, müssen Sie verschiedene Dinge tun. Wenn Sie ein Item gehilited haben, müssen Sie es wieder un-hiliten, indem Sie erneut TVM_SELECTITEM mit dem TVGN_DROPHILITE Flag senden, aber diesmal MUSS lParam 0 sein. Wenn Sie das Item nicht un-hiliten, werden Sie einen seltsamen Effekt bekommen: wenn Sie ein anderes Item selektieren, wird dieses Item von einem Rechteck umschlossen, aber das Hilite wird beim letzten Item bleiben. Als nächstes müssen Sie ImageList_DragLeave gefolgt von ImageList_EndDrag aufrufen. Sie müssen die Maus wieder freigeben, indem Sie ReleaseCapture aufrufen. Wenn Sie eine Image List erzeugt haben, müssen Sie sie mit ImageList_Destroy zerstören. Danach, können Sie mit Ihrem Programm fortfahren, wenn die Drag & Drop Operation beendet ist.
  • Code Beispiel:

    .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\comctl32.inc include \masm32\include\gdi32.inc includelib \masm32\lib\gdi32.lib includelib \masm32\lib\comctl32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD .const IDB_TREE equ 4006 ; ID der Bitmap Ressource .data ClassName db "TreeViewWinClass",0 AppName db "Tree View Demo",0 TreeViewClass db "SysTreeView32",0 Parent db "Parent Item",0 Child1 db "child1",0 Child2 db "child2",0 DragMode dd FALSE ; ein Flag um zu bestimmen ob wir im Drag-Mode sind .data? hInstance HINSTANCE ? hwndTreeView dd ? ; handle des tree view Steuerelementes hParent dd ? ; Handle des root tree view item hImageList dd ? ; Handle der Image Liste die im tree view steuerelement benutzt wird hDragImageList dd ? ; handle der image liste das benutzt wird um das Drag-Image zu speichern .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT invoke ExitProcess,eax invoke InitCommonControls WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_APPWORKSPACE mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\ CW_USEDEFAULT,200,400,NULL,NULL,\ hInst,NULL mov hwnd,eax .while TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endw mov eax,msg.wParam ret WinMain endp WndProc proc uses edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL tvinsert:TV_INSERTSTRUCT LOCAL hBitmap:DWORD LOCAL tvhit:TV_HITTESTINFO .if uMsg==WM_CREATE invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\ WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\ 0,200,400,hWnd,NULL,\ hInstance,NULL ; Erzeuge das Tree view Steuerlement mov hwndTreeView,eax invoke ImageList_Create,16,16,ILC_COLOR16,2,10 ; erzeuge die damit verbundene image list mov hImageList,eax invoke LoadBitmap,hInstance,IDB_TREE ; lade das Bitmap aus der Ressource mov hBitmap,eax invoke ImageList_Add,hImageList,hBitmap,NULL ; Füge das Bitmap der Image List hinzu invoke DeleteObject,hBitmap ; lösche immer die bitmap ressource invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList mov tvinsert.hParent,NULL mov tvinsert.hInsertAfter,TVI_ROOT mov tvinsert.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE mov tvinsert.item.pszText,offset Parent mov tvinsert.item.iImage,0 mov tvinsert.item.iSelectedImage,1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert mov hParent,eax mov tvinsert.hParent,eax mov tvinsert.hInsertAfter,TVI_LAST mov tvinsert.item.pszText,offset Child1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert mov tvinsert.item.pszText,offset Child2 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert .elseif uMsg==WM_MOUSEMOVE .if DragMode==TRUE mov eax,lParam and eax,0ffffh mov ecx,lParam shr ecx,16 mov tvhit.pt.x,eax mov tvhit.pt.y,ecx invoke ImageList_DragMove,eax,ecx invoke ImageList_DragShowNolock,FALSE invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit .if eax!=NULL invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax .endif invoke ImageList_DragShowNolock,TRUE .endif .elseif uMsg==WM_LBUTTONUP .if DragMode==TRUE invoke ImageList_DragLeave,hwndTreeView invoke ImageList_EndDrag invoke ImageList_Destroy,hDragImageList invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0 invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0 invoke ReleaseCapture mov DragMode,FALSE .endif .elseif uMsg==WM_NOTIFY mov edi,lParam assume edi:ptr NM_TREEVIEW .if [edi].hdr.code==TVN_BEGINDRAG invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem mov hDragImageList,eax invoke ImageList_BeginDrag,hDragImageList,0,0,0 invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y invoke SetCapture,hWnd mov DragMode,TRUE .endif assume edi:nothing .elseif uMsg==WM_DESTROY invoke PostQuitMessage,NULL .else invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .endif xor eax,eax ret WndProc endp end start


    Analyse:

    Innerhalb des WM_CREATE Handler erzeugen Sie das Tree View Steuerelement
    invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\ WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\ 0,200,400,hWnd,NULL,\ hInstance,NULL


    Beachten Sie die Stile. TVS_xxxx sind die Tree View spezifischen Stile.
    invoke ImageList_Create,16,16,ILC_COLOR16,2,10 mov hImageList,eax invoke LoadBitmap,hInstance,IDB_TREE mov hBitmap,eax invoke ImageList_Add,hImageList,hBitmap,NULL invoke DeleteObject,hBitmap invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList


    Als nächstes erzeugen Sie eine leere Image Liste, welche Images der Größe 16x16 Pixel akzeptiert, 16-Bit Farbe und anfangs 2 Images enthält, was bei Bedarf aber auf 10 wachsen kann. Wir laden dann das Bitmap aus der Ressource und fürgen es der eben erzeugten Image-List hinzu. Danach löschen wir das Handle des Bitmaps, da wir es nicht mehr benötigen. Wenn die Image List gesetzt ist, verbinden wir sie mit dem Tree View Steuerelement, indem wir TVM_SETIMAGELIST an das Tree View Steuerelement senden.
    mov hParent,eax mov tvinsert.hParent,eax mov tvinsert.hInsertAfter,TVI_LAST mov tvinsert.u.item.pszText,offset Child1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert mov tvinsert.u.item.pszText,offset Child2 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert


    Wir fügen Items in das Tree View Steuerelement ein, beginnend beim Root Item. Da es das Root Item sein wird, ist das hParent Element NULL und hInsertAfter ist TVI_ROOT. Das imask Element spezifiziert, dass die Elemente pszText, iImage und iSelectedImage der TV_ITEM Struktur gültig sind. Wir füllen diese drei Elemente mit den entsprechenden Werten. pszText enthält das Label des Root Item, iImage ist der Index des Images in der Image List, das links vom unselektierten Item angezeigt wird und iSelectedImage ist der Index des Images in der Image List, dass angezeigt wird, wenn das Item selektiert wird. Wenn alle entsprechenden Elemente gefüllt sind, senden wir eine TVM_INSERTITEM Nachricht an das Tree View Steuerelement, um das Root Item hinzuzufügen.
    mov hParent,eax mov tvinsert.hParent,eax mov tvinsert.hInsertAfter,TVI_LAST mov tvinsert.u.item.pszText,offset Child1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert mov tvinsert.u.item.pszText,offset Child2 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert


    Nachdem das Root Item hinzugefügt wurde, können wir die Child-Items anhängen. Das hParent Element ist nun mit dem Handle des Parent-Item belegt. Und wir werden die selben Images in der Image-List benutzen, so dass wir die Elemente iImage und iSelectedImage nicht ändern müssen.
    .elseif uMsg==WM_NOTIFY mov edi,lParam assume edi:ptr NM_TREEVIEW .if [edi].hdr.code==TVN_BEGINDRAG invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem mov hDragImageList,eax invoke ImageList_BeginDrag,hDragImageList,0,0,0 invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y invoke SetCapture,hWnd mov DragMode,TRUE .endif assume edi:nothing


    Nun, wenn der Benutzer versucht ein Item zu 'draggen', sendet das Tree View Steuerelement eine WM_NOTIFY Nachricht mit TVN_BEGINDRAG als code. lParam ist der Zeiger auf eine NM_TREEVIEW Struktur, welche verschieden Informationen enthält, die wir benötigen, weshalb wir den Wert in EDI abspeichern und EDI als Zeiger auf die NM_TREEVIEW Struktur benutzen. assume edi:ptr NM_TREEVIEW ist ein Weg, um MASM mitzuteilen, dass EDI als Zeiger auf die NM_TREEVIEW Struktur benutzt werden soll. Wir erzeugen dann ein Drag-Image, indem wir TVM_CREATEDRAGIMAGE an das Tree View Steuerelement senden. Es liefert das Handle der neu erzeugten Image List zurück, mit einem Drag-Image innerhalb. Wir rufen ImageList_BeginDrag auf, um den Hotspot im Drag-Image zu setzen. Dann beginnen wir mit der Drag-Operation, indem wir calling ImageList_DragEnter aufrufen. Diese Funktion zeigt das Drag-Image an der angegebenen Position in dem angegebenen Fenster an. Wir benutzen eine ptDrag Struktur, die Element der NM_TREEVIEW Struktur ist, als den Punkt, wo das Drag Image anfangs angezeigt werden soll. Danach, fangen wir die Maus-Eingaben ab und setzen das Flag um zu indizieren, dass wir nun im Drag-Mode sind.
    .elseif uMsg==WM_MOUSEMOVE .if DragMode==TRUE mov eax,lParam and eax,0ffffh mov ecx,lParam shr ecx,16 mov tvhit.pt.x,eax mov tvhit.pt.y,ecx invoke ImageList_DragMove,eax,ecx invoke ImageList_DragShowNolock,FALSE invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit .if eax!=NULL invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax .endif invoke ImageList_DragShowNolock,TRUE .endif


    Nun konzentrieren wir uns auf WM_MOUSEMOVE. Wenn der Benutzer das Drag-Image dragged, erhält unser Parent-Fenster WM_MOUSEMOVE Nachrichten. Als Antwort auf diese Nachrichten, aktuallisieren wir die Drag-Image Position mit ImageList_DragMove. Danach überprüfen wir, ob das Drag-Image über einem anderen Item ist. Das machen wir, indem wir eine TVM_HITTEST Nachricht an das Tree View Steuerelement sende, mit einem Punkt zum überprüfen. Wenn das Drag Image über einem Item ist, hilighten wir das Item, indem wir eine TVM_SELECTITEM Nachricht mit TVGN_DROPHILITE Flag an das Tree View Steuerelement senden. Während der Hilight Operation verstecken wir das Drag Image, so dass keine unschönen Effekte im Tree View Steuerelement entstehen.
    .elseif uMsg==WM_LBUTTONUP .if DragMode==TRUE invoke ImageList_DragLeave,hwndTreeView invoke ImageList_EndDrag invoke ImageList_Destroy,hDragImageList invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0 invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0 invoke ReleaseCapture mov DragMode,FALSE .endif


    Wenn der Benutzer die linke Maus-Taste loslässt, ist die Drag-Operation beendet. Wir beenden den Drag-Mode, indem wir calling ImageList_DragLeave aufrufen, gefolgt von ImageList_EndDrag und ImageList_Destroy. Um die Tree View Items gut aussehen zu lassen, überprüfen wir auch das zu letzt gehilightede Item und selektieren es. Wir müssen es auch un-hilighten, ansonsten werden die anderen Items nicht gehilighted, wenn sie selektiert werden. Und zu guter letzt, fangen wir die Maus nicht mehr ab.

    Deutsche Übersetzung: Joachim Rohde
    Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage