Tutorial 19: Contrôle 'des dossiers organisés sous la forme d'un arbre'
(Tree View Control)
Dans ce Tutorial, nous allons voir ce qu'est un 'tree view control' (C'est exactement de cette façon là que l'Explorateur Windows nous affiche sous forme d'un arbre tous les dossiers de notre disque dur). De plus, nous apprendrons aussi à construire le mécanisme du 'drag and drop' (glisser-déposer) et comment employer une 'image list' (liste d'image).
Downloadez l'exemple ici.
Théorie:
Un 'tree view control' est une sorte de fenêtre spéciale qui affiche des objets en ordre hiérarchique. Le meilleur exemple c'est la fenêtre de gauche de l'Explorateur de Windows. Vous pouvez employer ce contrôle pour montrer les raccordements entre les différents objets (ici des dossiers).
Vous pouvez créer un 'tree view control' en appelant CreateWindowEx, en lui passant la donnée "SysTreeView32" comme nom de classe ou bien vous pouvez l'incorporer dans une boîte de dialogue. N'oubliez pas de placer un Call InitCommonControls dans votre code.
Il y a plusieurs styles différents de 'tree view control'. Ci-dessous voici les trois plus employés.
TVS_HASBUTTONS sert à mettre les petits boutons carrés 'plus (+) et moins (-)' à côté des l'Items parentaux (à côté des dossiers par exemple). L'utilisateur clique sur un de ces bouton associés à un dossier parent pour qu'il se déploie et affiche ses sous dossiers ou se recroqueville sur lui-même. Pour pouvoir inclure des boutons au niveau de la racine des Items de l'arbre, on doit également spécifié TVS_LINESATROOT.
TVS_HASLINES emploie des lignes pour montrer la hiérarchie entre les différents Items.
TVS_LINESATROOT emploie des lignes pour lier des Items à la racine du 'tree-view control'. Cette valeur est ignorée si TVS_HASLINES n'est pas également spécifié.
Le 'tree view control', comme d'autre common controls, communique avec la fenêtre parente via des messages. La fenêtre parente peut lui envoyer divers messages et le 'tree view control' peut envoyer des messages "d'avis" à sa fenêtre parente. À cet égard, le 'tree view control' n'est pas différent des fenêtres habituelles.
Quand quelque chose d'intéressant lui arrive, il envoie un message WM_NOTIFY à la fenêtre parente en l'accompagnant de l'information.
WM_NOTIFY
WParam est le n°d'IDentité du Contrôle (Control ID), on ne garantit pas que cette valeur soit unique donc nous ne
l'emploierons pas. Au lieu de ça, nous employons hwndFrom ou le membre IDFrom de la structure
NMHDR pointé par lParam
LParam est le pointer qui repère la structure NMHDR. Certaines commandes peuvent passer un pointer de plus
grande taille. Mais il doit avoir une structure NMHDR dans son premier membre.
C'est-à-dire qu'au moins pour lParam, vous devez être sûrs qu'il indique une structure NMHDR.
Maintenant examinons la structure NMHDR.
NMHDR struct DWORD
hwndFrom DWORD ?
idFrom DWORD ?
code
DWORD ?
NMHDR ends
HwndFrom est l'handle du contrôle qui envoie ce message WM_NOTIFY.
IdFrom est le contrôle ID du contrôle qui envoie ce message WM_NOTIFY.
Code est le message réel que le contrôle veut envoyer à la fenêtre parente.
Les réponses du 'Tree View' sont celles avec TVN_ au début du nom. Les messages du Tree View sont ceux avec TVM _, tels que TVM_CREATEDRAGIMAGE. Le 'tree view control' envoie TVN_XXXX dans le membre de code du NMHDR. La fenêtre parente peut envoyer TVM_XXXX pour le contrôler.
Ajout d'Item au tree view control
Après que vous l'ayez créé, vous pouvez même rajouter des Item (des articles) au tree view control. Vous pouvez faire ça en employant le message TVM_INSERTITEM.
TVM_INSERTITEM
wParam = 0;
lParam = est le pointeur qui (pointe) sur TV_INSERTSTRUCT;
A ce niveau, on doit connaître quelques petits trucs à propos des Items (articles) du tree view control.
Un Item peut être lui-même parent, enfant (un child) , ou les deux en même temps. Un Item parent est un Item qui a d'autres sous-Items associés qui dépendent de lui. En même temps, cet Item parent peut aussi être l'enfant d'un autre Item. Un Item sans un parent est appelé un Item racine (par exemple : dans l'Explorateur de Windows, l'item 'Windows' est un item parent alors que l'Item 'System' est un de ses items enfant). Il peut y avoir plusieurs d'Items racine dans un 'tree view control'. Maintenant examinons la structure TV_INSERTSTRUCT.
TV_INSERTSTRUCT STRUCT DWORD
hParent DWORD ?
hInsertAfter DWORD ?
ITEMTYPE <>
TV_INSERTSTRUCT ENDS
hParent est l'handle de l'Item parent. Si ce membre est la valeur TVI_ROOT ou NULL, l'Item est inséré à la racine du 'tree view control'.
hInsertAfter est l'handle d'un prochain Item, qu'il soit un 'Child Item' ou bien un nouvel 'Item Parent':
-
TVI_FIRST = > Insère un Item au début de la liste.
-
TVI_LAST = > Insère un Item en fin de liste.
-
TVI_SORT = > Insère un Item dans la liste dans l'ordre alphabétique.
ITEMTYPE UNION
itemex TVITEMEX <>
item TVITEM <>
ITEMTYPE ENDS
Nous emploierons uniquement TV_ITEM ici.
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
Cette structure est employée pour envoyer et recevoir des renseignements (des messages) d'un Item du Tree View. Par exemple, avec TVM_INSERTITEM, il est employé pour définir l'attribut de l'Item qui doit être inséré dans le tree view control. Avec TVM_GETITEM, on envoie l'information pour choisir le type de Tree View Item.
imask est employé pour spécifier quel(s) membre(s) de la structure TV_ITEM est (sont) valide(s). Par exemple, si la valeur dans Imask est TVIF_TEXT, ça signifie qu'uniquement le membre pszText est valide. Vous pouvez combiner plusieurs flags ensembles.
hItem est l'handle de l'Item du Tree View. Chaque item a son propre handle. Lorsque vous souhaitez manipuler ou utiliser un item, vous devez le choisir grâce à son handle.
pszText est un pointeur qui (pointe) sur une chaîne de caractères terminée par un caractère nul. Cette chaîne de caractères est le label de l'Item du Tree View (Le texte associé à cet Item).
cchTextMax est uniquement employé lorsque vous voulez retrouver le label de l'Item du Tree View. Puisque vous pointer sur le buffer de pszText (on pointe sur la zone mémoire où est écrit le texte), Windows doit connaître la taille de ce buffer. Ce membre (cchTextMax) sert donc à donner la taille du buffer.
iImage et iSelectedImage font référence à l'index d'une 'image list' laquelle contient des images qui doivent être affichées lorsque l'Item n'est pas choisi (iImage) et quand il est choisi (iSelectedImage). Si vous vous souvenez de la fenêtre de gauche de l'Explorateur Windows, les images des dossiers sont contrôlées par ces deux membres.
Afin d'insérer un Item dans un tree view control, vous devez au moins mettre des valeurs dans hParent, hInsertAfter et vous devez remplir imask ainsi que le membre pszText.
Addition d'images à un tree view control :
Si vous souhaitez placer une image à gauche du label (du texte) d'un 'Tree View Item', vous devez créer une 'image list' et l'associer au 'tree view control'. Vous pouvez créer une 'image list' en appelant ImageList_Create.
ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, \
cInitial:DWORD, cGrow:DWORD
Cette fonction ,si elle se déroule avec succès, renvoie l'handle d'une 'image list' vide.
cx représente la largeur (en pixels) de chaque image dans cet 'image list'.
cy est la hauteur de chaque image (en pixels) de cet 'image list'. Toutes les images de l' 'image list' doivent être égales en taille. Si vous définissez un grand bitmap (image), Windows emploiera cx et cy pour la *découper* en plusieurs morceaux. Donc vous devez préparer votre propre image comme une bande d'images avec des dimensions identiques.
flags détermine le type d'images contenues dans cet 'image list', si elles sont colorées ou monochromes et leur profondeur de colorations (16millions de couleurs ou moins). Consultez votre référence win32 api pour de plus amples détails.
cInitial est le nombre d'images que cet 'image list' contiendra au commencement. Windows emploiera ces renseignements pour réserver de la mémoire pour ces images.
cGrow est le nombre d'images dont l'image list peut être agrandit lorsque le système a besoin de retaillée la liste de départ pour faire place pour de nouvelles images. Ce paramètre représente le nombre de nouvelles images que l'image list agrandit peut contenir.
L' image list n'est pas une fenêtre! C'est seulement une sorte d'entrepos d'images pour que d'autres fenêtres puisse s'en servir.
Après que l'image list soit créé, vous pouvez y ajouter des images en appelant ImageList_Add
ImageList_Add
PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
Cette fonction renvoie la valeur -1 si elle échoue.
himl est l'handle de l'image list dans laquelle vous souhaitez ajouter d'autres images. Cette la valeur obtenue après le succès d'un appel à ImageList_Create
hbmImage est l'handle de l'image qui doit être rajoutée à l' 'image list'. D'habitude, on stocke les images en tant que ressources et on les charge grâce à l'appel LoadBitmap. Remarquez que vous n'avez pas à spécifier le nombre de morceaux d'images contenues dans cette image (entière) parce que cette information est représentée par les paramètres cx et cy, lesquels sont déduits lors de leur passage dans l'appel ImageList_Create.
hbmMask est l'handle du bitmap (l'image en entier) qui contient le masque. Si aucun masque n'est employé avec l'image list, ce paramètre est ignoré.
Normalement, nous ajoutons seulement deux images à une image list pour les utiliser avec le tree view control : celles-ci sont employées soit quand l'Item du Tree View n'est pas sélectionné soit l'autre quand l'Item est sélectionné.
Quand l' image list est prête, vous l'associez avec le tree view control en envoyant TVM_SETIMAGELIST au tree view control.
TVM_SETIMAGELIST
WParam = type d'image list à afficher. Il y a deux choix :
-
TVSIL_NORMAL est le Jeu normal d' image list, qui contient les images choisies et non choisies pour l'Item du Tree View.
-
TVSIL_STATE est le Jeu 'd'état' d' image list, qui contient les images d'Item de Tree View qui sont dans un état défini par l'utilisateur.
lParam = handle de l' image list
Recherche des renseignements a propos des items d'un tree view.
Vous pouvez retrouver l'information d'un Item du Tree View en envoyant le message TVM_GETITEM.
TVM_GETITEM
wParam = 0
lParam = pointer sur la structure TV_ITEM pour qu'on puisse la remplir avec l'information.
Avant que vous n'envoyiez ce message, vous devez remplir imask avec le flag qui dit quel(s) sorte(s) de TV_ITEM est ce que vous souhaitez que Windows prenne. Et plus important, vous devez remplir hItem avec l'handle de l'Item dont vous voulez avoir l'information. Et ça pose un problème : Comment peut-on connaître l'handle de l'Item dont on souhaite justement retrouver des renseignements? Doit-on stocker tous les handles du Tree View?
La réponse est très simple : faut pas faire ça. On peut utiliser le message TVM_GETNEXTITEM du tree view control pour retrouver l'handle de l'Item dont on a spécifié l'attribut(s). Par exemple, on peut vérifier l'handle du premier Child Item, de l'Item racine, ou de l'Item choisi, et cetera.
TVM_GETNEXTITEM
wParam
= flag
lParam = handle d'un Item du Tree View (seulement nécessaire pour certaines valeurs de flag)
La valeur dans wParam est très importante donc je met tous les flags ci-dessous :
-
TVGN_CARET Retrouve l'Item actuellement choisi.
-
TVGN_CHILD Retrouve le premier Child Item appartenant à l'Item indiqué par le paramètre hitem.
-
TVGN_DROPHILITE Retrouve l'Item qui est la cible d'une opération de glisser-déplacer. (Drad and Drop)
-
TVGN_FIRSTVISIBLE Retrouve le premier Item visible.
-
TVGN_NEXT Retrouve le Child Item suivant (de mêmes parents).
-
TVGN_NEXTVISIBLE Retrouve l'Item visible qui suit l'Item indiqué. L'Item indiqué doit être visible. Utilisez le message TVM_GETITEMRECT pour déterminer si un Item est visible.
-
TVGN_PARENT Retrouve le parent de l'Item indiqué.
-
TVGN_PREVIOUS Retrouve le Child Item précédent (de mêmes parents).
-
TVGN_PREVIOUSVISIBLE Retrouve le premier Item visible qui précède l'Item indiqué. L'Item indiqué doit être visible. Employez le message TVM_GETITEMRECT pour déterminer si un Item est visible.
-
TVGN_ROOT Retrouve le topmost (ex: C:) ou le tout premier Item du tree-view control.
Vous pouvez voir que, vous pouvez retrouver l'handle de n'importe quel Item du Tree View en vous intéressant à ces messages. SendMessage renvoie l'handle de l'Item du Tree View si la tache est couronnée de succès. Vous pouvez alors remplir le membre hItem (appartenant à TV_ITEM) avec la valeur de l'handle renvoyé pour ensuite pouvoir l'utiliser avec le message TVM_GETITEM.
Opération Drag and Drop (glisser-déposer) dans un tree view control
Cette partie est la raison pour laquelle j'ai décidé d'écrire ce Tutorial. Quand j'ai essayé de suivre l'exemple de la référence win32 api (le win32.hlp d'InPrise), j'ai vraiment été frustré parce qu'en fait il manque l'information essentielle. D'essais en essais et d'erreurs en erreurs, j'ai fini par comprendre comment utiliser le système de 'Drag and Drap' d'un tree view control et je ne veux pas que d'autres personnes aient à refaire le même chemin de croix que moi.
Ci-dessous voici les étapes pour accomplir une opération Drag&Drop dans un tree view control.
-
Quand l'utilisateur essaye de déplacer un Item, le tree view control envoie le message TVN_BEGINDRAG à la fenêtre parente. Vous pouvez profiter de cette occasion pour créer une image de traînée qui est l'image qui sera employée pour représenter l'Item quand on le déplace. Vous pouvez envoyer TVM_CREATEDRAGIMAGE au tree view control pour lui dire de créer une 'image de traînée' par défaut de l'image qui est actuellement employée par l'Item que l'on déplace. Le tree view control créera une image list avec uniquement l 'image de traînée' et vous renverra l'handle de cette image list.
-
Après que l'image de traînée aie été créée, vous définissez le point chaud de l'image de traînée en appelant ImageList_BeginDrag.
ImageList_BeginDrag PROTO himlTrack:DWORD, \
iTrack:DWORD , \
dxHotspot:DWORD, \
dyHotspot:DWORD
himlTrack est l'handle de l'image list qui contient l'image de traînée.
iTrack est l'index qui indique l'image de traînée dans l'image list.
dxHotspot indique la distance relative du point chaud (de l'image de traînée)dans le plan horizontal. Puisque cette image sera employée à la place du curseur de la souris, nous avons donc besoin de définir où se situe le point chaud dans l'image.
dyHotspot indique la distance relative du point chaud dans le plan vertical.
Normalement, iTrack sera mis à 0 si vous dites au tree view control de créer une image de traînée à votre place. Et dxHotspot et dyHotspot peuvent être égales à 0 si vous voulez placer le point chaud au niveau du coin supérieur gauche de l'image de traînée.
-
Quand l'image de traînée est prête à être affichée, nous appelons ImageList_DragEnter pour la faire apparaître dans la fenêtre.
ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
hwndLock est l'handle de la fenêtre qui affiche l'image de traînée. Cette image n'aura pas la possibilité de se déplacer à l'extérieur de cette fenêtre.
x et y sont les coordonnées de l'endroit où l'image de traînée doit être affichée au commencement. Remarquez que ces valeurs on pour référence (0;0) le coin supérieur gauche de la fenêtre, et non pas son secteur client.
-
Maintenant que l'image de traînée est affichée dans la fenêtre, vous devrez maintenir l'opération (DRAG) de traînée dans le tree view control. Cependant, il y a un petit problème ici. Nous devons contrôler le chemin de la traînée avec WM_MOUSEMOVE et l'emplacement où on dépose (DROP) avec le message WM_LBUTTONUP. Cependant, si l'image de traînée appartient à une autre 'Child Windows' (donc pas à notre fenêtre principale [ou parente]), la fenêtre parente ne recevra jamais le message de la souris. La solution est de capturer l'entrée souris avec SetCapture. En employant l'appel, les messages souris seront dirigés vers la fenêtre indiquée indépendamment de l'endroit où se trouve le curseur de la souris (et ici le curseur de la souris vient d'être momentanément remplacé par l'image de traînée).
-
A l'intérieur de WM_MOUSEMOVE, vous mettrez à jour le chemin de la traînée avec l'appel de ImageList_DragMove. Cette fonction déplace l'image que l'on traîne pendant une opération de glisser-déplacer. En outre, si c'est votre souhait, vous pouvez contrôler l'Item de l'image de traînée en envoyant TVM_HITTEST pour vérifier si l'image de traînée passe au dessus d'un autre Item. Si c'est le cas, vous pouvez envoyer le message TVM_SELECTITEM avec le flag rendu par TVGN_DROPHILITE. Remarquez qu'avant l'envoi du message TVM_SELECTITEM, vous devez d'abord cacher l'image de traînée sinon votre image laissera de vilaines traces en se déplaçant. Vous pouvez cacher l'image de traînée en appelant ImageList_DragShowNolock et, après que l'opération soit finie, appelez ImageList_DragShowNolock de nouveau pour réafficher l'image de traînée.
-
Quand l'utilisateur relâche le bouton gauche de la souris, vous devez faire plusieurs choses. Si vous perdez le contôle de votre Item, vous deviez le réactiver en envoyant le message TVM_SELECTITEM avec le flag rendu par TVGN_DROPHILITE (pour retrouver l'handle de l'item qui vient de subir le Déplacer-Poser (Drag and Drop)), mais cette fois-ci, lParam DOIT ÊTRE égal à zéro. Si vous n'avez plus le contrôle de votre Item, vous obtiendrez un étrange effet : quand vous choisissez un autre Item, celui-ci sera contenu dans un rectangle mais le contrôle sera toujours sur le dernier Item. Ensuite, vous devez appeler ImageList_DragLeave suivi d' ImageList_EndDrag. Vous devez relâcher la souris en appelant ReleaseCapture. Si vous avez créé une image list, vous devez la détruire en appelant ImageList_Destroy. Après ça, lorsque l'opération de drag and drop est terminée, vous pouvez continuer le déroulement normal de votre programme.
Code sample:
.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
; c'est l'ID de la ressource bitmap (le n° d'Identité de l'image entière)
.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
; un flag pour déterminer si nous sommes en mode Drag (traînée)
.data?
hInstance HINSTANCE ?
hwndTreeViewdd ?
; handle du tree view control
hParent dd ?
; handle de l 'Item racine' du Tree View.
hImageList dd ?
; handle de l 'image list' employée dans le 'tree view control'.
hDragImageList dd ? ; handle de l 'image list' où on a l'habitude de stocker l'image de traînée.
.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
; Crée le 'tree view control'
mov hwndTreeView,eax
invoke ImageList_Create,16,16,ILC_COLOR16,2,10 ; Crée l 'image list' associée.
mov hImageList,eax
invoke LoadBitmap,hInstance,IDB_TREE
; Charge le bitmap ressource (charge l'image).
mov hBitmap,eax
invoke ImageList_Add,hImageList,hBitmap,NULL ; Ajoute le bitmap dans l 'image list'.
invoke DeleteObject,hBitmap ; Supprime ce bitmap pour toujours.
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:
Avec WM_CREATE, vous créez tree view control.
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL
Remarquez les styles. TVS_xxxx représentent des styles spéciaux de Tree View.
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
Ensuite, vous créez une 'image list' vide à son ouverture, laquelle acceptera des images de 16x16 pixels et en couleur 16 bits. Elle contiendra 2 images, mais on peut l'agrandir à 10 images si le besoin s'en fait ressentir. Nous chargeons alors le bitmap et l'ajoutons à l' image list' juste créé. Après ça, nous supprimons l'handle du bitmap puisque il ne sera plus employé désormais. Quand l 'image list' est définitivement finie, nous l'associons au tree view control en envoyant le message TVM_SETIMAGELIST au tree view control.
mov tvinsert.hParent,NULL
mov tvinsert.hInsertAfter,TVI_ROOT
mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
mov tvinsert.u.item.pszText,offset Parent
mov tvinsert.u.item.iImage,0
mov tvinsert.u.item.iSelectedImage,1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
Nous insérons des Items dans le tree view control, en commençant à partir de l'Item racine. Puisque il s'agit de l'Item racine, le membre hParent est NULL et hInsertAfter est TVI_ROOT. Le membre imask indique que les membres pszText, iImage et iSelectedImage de la structure TV_ITEM sont valables. Nous remplissons ces trois membres des valeurs appropriée. pszText contient le label de l'Item racine (son texte), iImage est l'index de l'image dans l 'image list' qui sera montré à gauche de l'Item non choisi et iSelectedImage est l'index de l'image dans l 'image list' qui sera montré quand l'Item est choisi. Quand tous les membres appropriés sont remplis, nous envoyons le message TVM_INSERTITEM au tree view control pour y ajouter l'Item racine.
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
Après que l'Item racine ait été rajouté, nous pouvons y attacher les Childs Items. Le membre hParent est maintenant rempli de l'handle de l'Item parent. Et nous emploierons des images identiques dans l 'image list' donc nous ne touchons plus aux membres iImage et iSelectedImage.
.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
Maintenant quand l'utilisateur essaye de déplacer un Item, le tree view control envoie le message WM_NOTIFY avec TVN_BEGINDRAG en tant que code. lParam est le pointer qui est sur la structure NM_TREEVIEW laquelle contient plusieurs informations dont nous avons besoin, ainsi nous mettons sa valeur dans edi et employons edi comme pointer pour la structure NM_TREEVIEW. 'assume edi:ptr NM_TREEVIEW' est une façon de dire à MASM de traiter edi en tant que pointer sur la structure NM_TREEVIEW. Nous créons alors une image de traînée en envoyant TVM_CREATEDRAGIMAGE au tree view control. Il renvoie l'handle de l 'image list' nouvellement créé avec une image de traînée à l'intérieur. Nous appelons ImageList_BeginDrag pour définir le point chaud de l'image de traînée. Alors nous passons à l'opération de traînée en appelant ImageList_DragEnter. Cette fonction affiche l'image de traînée à l'emplacement indiqué dans la fenêtre indiquée. Nous employons la structure ptDrag qui est un membre de la structure NM_TREEVIEW en tant que point de départ où l'image de traînée doit être placée au commencement. Après ça, nous capturons l'entrée souris et nous (activons) le flag pour indiquer que nous passons désormais en mode Drag (traînée).
.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
Maintenant nous nous concentrons sur WM_MOUSEMOVE. Quand l'utilisateur traîne l'image, notre fenêtre parente reçoit des messages WM_MOUSEMOVE. En réponse à ces messages, nous mettons à jour la position de l' image de traînée avec ImageList_DragMove. Après ça, nous vérifions si l'image de traînée passe au-dessus d'un Item. Nous faisons ça en envoyant le message TVM_HITTEST au tree view control.
ISi l'image de traînée passe par dessus d'un autre Item, nous reprenons le contrôle de notre Item en envoyant le message TVM_SELECTITEM avec le flag TVGN_DROPHILITE (pour dire que c'est l'Item Drag and Drop qui nous intéresse et pas l'autre) au tree view control. Pendant cette opération, nous cachons l'image de traînée pour qu'elle ne laisse pas de taches dégueulasses sur le tree view control.
.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
Quand l'utilisateur relâche le bouton gauche de la souris, l'opération de traînée se termine. Nous sortons du mode traînée en appelant ImageList_DragLeave, suivi par ImageList_EndDrag et ImageList_Destroy.
[Iczelion's
Win32 Assembly Homepage]
Traduit par Morgatte