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.
-
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.
-
Nachdem das Drag-Image erzeugt wurde, spezifizieren Sie den Hotspot des Drag Image, indem Sie ImageList_BeginDrag aufrufen.
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.
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