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. 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 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.

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. 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. La valeur dans wParam est très importante donc je met tous les flags ci-dessous : 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.
  1. 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.
  2. 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.
    1. 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.
  3. 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.
    1. 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.
  4. 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).
  5. 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.
  6. 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. Remarquez les styles. TVS_xxxx représentent des styles spéciaux de Tree View. 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. 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. 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. 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). 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