Tutorial 31: Fichiers organisés sous la forme d'une liste
(Listview Control)

Dans ce tutorial, nous allons voir comment créer et employer un listview control.

Downloadez l'exemple.

Théorie:

Un listview control est aussi un 'common controls' (tutorial 18) tout comme le treeview (tutorial 19), richedit etc. En fait vous l'utilisez couramment sans même connaître son nom. Par exemple, la fenêtre de droite de l'Explorateur Windows est un listview control. Un listview control sert à afficher sous forme d'une liste un groupe de fichiers. En fait, c'est un peu comme une liste, mais avec certaines capacités en plus.
Vous pouvez créer un listview control de deux façons. La première méthode est aussi la plus simple : créez-le avec un éditeur de ressource. Simplement, n'oubliez pas ensuite d'appeler InitCommonControls dans votre code asm source. L'autre méthode c'est d'appeler CreateWindowEx dans votre code source. Vous devez spécifier le nom de la 'Window Class' (classe de fenêtre) correcte de ce control, c'est-à-dire SysListView32. La 'Window Class' "WC_LISTVIEW" n'est pas correcte.
Il y a quatre types de vues des données, dans un listview control : une vue sous forme d'icônes, de petites icônes, sous forme de texte inscrit et des vues sous forme de détails. Vous pouvez voir les exemples de ces vues en choisissant Affichage->Grandes icônes (la vue d'icônes), Affichage->Petites icônes (la vue en petites icônes), Affichage-> Liste (la vue de la liste) et Affichage-> Détails (la vue sous forme de détails). Ces vues sont juste une méthode de représentation des données : Elles affectent seulement la présentation des données. Par exemple, vous pouvez avoir beaucoup de données dans un listview control, mais si vous préférez, vous pouvez voir certaines d'entre elles. La vue détail est la plus informative alors que les vues restantes donnent moins de renseignements. Vous pouvez spécifier la vue que vous voulez quand vous créez un listview control. Vous pourrez même changer de vue plus tard en appelant SetWindowLong, en spécifiant flag GWL_STYLE.

Maintenant que nous savons comment créer un listview control, nous continuerons pour voir comment on peut l'employer. Je me concentrerai de la vue sous forme de détails qui fait appelle à pas mal des particularités du listview control. Les étapes dans l'utilisation d'un listview control sont les suivantes :

  1. Créez le listview control avec CreateWindowEx, et indiquez SysListView32 comme class name. Vous pouvez spécifier la vue initiale à ce niveau là.
  2. (Si elles existes) Créez et initialisez des listes d'images (Tutorial 19) pour les associer aux noms des fichiers du listview.
  3. Insérez des colonnes dans listview control. Cette étapes est nécessaire si votre listview control emploie la vue 'détails'. Explication : revenez sous l'Explorateur Windows et mettez vous en vue 'détails'. Vous voyez bien qu'il y a plusieurs colonnes, une pour le nom des fichiers, une autre pour leur date de création, une pour leur emplacement sur votre Ddur.
  4. Insérez des items et des sous-items dans votre listview control. ex: Nom, Taille ,Type ,Modifier...

Colonnes

Dans la vue 'détails', il y a une ou plusieurs colonnes. Ces données qui sont mises dans différentes colonnes (en vue détails), représentes en fait un tableau de données : les données sont arrangées dans des rangées et des colonnes. Vous devez avoir au moins une colonne dans votre listview control (uniquement vrai pour la vue 'détails'). Pour les autres vues, vous n'avez pas besoin d'insérer de colonne parce qu'il nous faut une et seulement une seule colonne pour ces vues là.
Vous pouvez insérer une colonne en envoyant LVM_INSERTCOLUMN au listview control.

LVM_INSERTCOLUMN
wParam = iCol (nombre de colonnes)
lParam = pointe sur la structure LV_COLUMN

iCol est le nombre de colonnes, en partant de 0.
LV_COLUMN contient l'information sur la colonne qui doit être insérée. Voici sa définition :

LV_COLUMN STRUCT
  imask dd ?
  fmt dd ?
  lx dd ?
  pszText dd ?
  cchTextMax dd ?
  iSubItem dd ?
  iImage dd ?
  iOrder dd ?
LV_COLUMN ENDS

Noms Définitions
imask

Représente une collection de flags qui indique quels membres dans cette structure sont valides. Pourquoi utiliser ce membre ?, c'est pour ne pas que tous les membres dans cette structure soient employés en même temps. Seulement quelques membres sont employés dans certaines situations. Et cette structure est utilisée, à la fois pour les Entrées et les Sorties. C'est pourquoi il est important que vous *marquiez* les membres qui seront employés dans cet appel de Windows pour qu'il sache que ces membres sont valides. Voici les flags disponibles :

LVCF_FMT = Le membre fmt est valide.
LVCF_SUBITEM = Le membre iSubItem est valide.
LVCF_TEXT = Le membre pszText est valide.
LVCF_WIDTH = Le membre lx est valide.

Vous pouvez combiner ces flags. Par exemple, si vous voulez déclarer le titre de la colonne (comme 'taille' par exemple, ou bien 'nom' ou 'modifié'), vous devez donner l'indicateur qui pointe sur la chaîne de caractères dans le membre pszText. Et vous devez dire à Windows que le membre pszText contient des données en rajoutant le flag LVCF_TEXT dans ce champ, sinon Windows ne tiendra pas compte de la valeur (du texte) dans pszText.

fmt

Indique à quel endroit doivent être placées les items et sous-items à l'intérieur des colonnes. Les valeurs disponibles sont :

LVCFMT_CENTER = Texte centé.
LVCFMT_LEFT = Texte aligné à gauche.
LVCFMT_RIGHT = Texte aligné à droite.

lx Est la largeur de la colonne, en pixels. Vous pourrez plus tard la changer avec LVM_SETCOLUMNWIDTH.
pszText Dans le cas où cette structure est employée pour afficher les propriétés de la colonne, elle contient le pointer qui est sur le nom de la colonne. Par contre si cette structure est employée pour recupérer les propriétés d'une colonne, ce champ contient la valeur d'un pointer qui 'pointe' sur un assez grand buffer pour qu'il puisse recevoir le nom de la colonne qui sera renvoyée. Dans ce cas, vous devez indiquer la taille de ce buffer dans cchTextMax ci-dessous. Vous pouvez ignorer cchTextMax si vous souhaitez seulement afficher le nom de la colonne, car un nom est forcément une chaîne de caractères ASCIIZ, et Windows peut facilement retrouver sa longueur.
cchTextMax Est la taille, en octets, du buffer indiqué, ci-dessus, dans pszText. Ce membre est seulement utilisé lorsque vous employez cette structure pour récupérer les renseignements d'une colonne. Si vous employez cette structure pour afficher les propriétés d'une colonne, ce champ est simplement ignoré.
iSubItem indique l'index du sous-item associé à cette colonne. Cette valeur est utilisée un peu comme un marqueur pour lequel le sous-item de cette colonne est associée. Si vous le souhaitez, même en mettant un nombre sans aucun sens dans ce paramètre votre listview control fonctionnera toujours les doits dans le nez. La meilleure démonstration de l'utilisation de ce paramètre, c'est quand vous avez un numéro de colonne et que vous avez besoin de savoir avec quel sous-item cette colonne est associée. Vous pouvez l'interroger le listview control en envoyant le message LVM_GETCOLUMN, en spécifiant LVCF_SUBITEM dans le membre imask. Le listview control remplira le membre iSubItem avec n'importe quelle valeur que vous avez spécifié dans ce paramètre quand la colonne a été insérée. Pour que cette méthode fonctionne, vous avez besoin que d'entrer dans ce paramètre le correcte index du sous-item.
iImage et iOrder Est utilisé avec 'Internet Explorateur 3.0' et plus. Je n'ai pas plus de renseignements à leur sujet.

Ainsi après que le listview control est été créé, vous devez y insérer une ou plusieurs colonnes. Les colonnes ne sont pas nécessaires si vous ne projetez pas de faire fonctionner le listview control en vue 'détails'. Pour insérer une colonne, vous avez besoin de créer la structure LV_COLUMN, la remplir de l'information nécessaire, et indiquer le nombre de colonnes puis envoyer ensuite cette structure au listview control avec LVM_INSERTCOLUMN message.

   LOCAL lvc:LV_COLUMN
   mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
   mov lvc.pszText,offset Heading1
   mov lvc.lx,150
   invoke SendMessage,hList, LVM_INSERTCOLUMN,0,addr lvc

Ce petit bout de code vous montre comment faire. Il détermine le texte d'en-tête de la colonne ainsi que sa largeur à envoyer au listview control, grâce au message LVM_INSERTCOLUMN. C'est aussi simple que ça.

Items et sous-items

Les items sont les principales éléments dans un listview control. Dans les vues autres, que la vue 'détails', vous ne verrez que les items. Les sous-items sont les détails des items. Un unique item peut avoir un ou plus sous-items associés. Par exemple, si l'item est le nom d'un fichier, alors vous pouvez regarder (en tant que sous-items) ses attributs de fichier comme, sa taille, ou sa date de création. Dans la vue 'détails', la colonne extrême gauche contient les items et les colonnes restantes contiennent les sous-items. Vous pouvez vous représenter les items et leurs sous-items un peu comme une base de données. L'item est la clef principale du tableau alors que les sous-items sont ses éléments de description dans ce même tableau.
Au minimum, il y aura quelques Items dans votre listview control : les sous-items ne sont pas nécessaires. Cependant, si vous voulez présenter plus d'informations à l'utilisateur à propos de ces Items, vous devrez leurs associer leurs sous-items respectifs. De cette façon, l'utilisateur pourra voir les détails dans la vue 'détails'.
On insère un item dans le listview control en envoyant le message LVM_INSERTITEM. Vous avez aussi besoin de lui passer l'adresse de la structure LV_ITEM dans lParam. LV_ITEM a la définition suivante :

LV_ITEM STRUCT
  imask dd ?
  iItem dd ?
  iSubItem dd ?
  state dd ?
  stateMask dd ?
  pszText dd ?
  cchTextMax dd ?
  iImage dd ?
  lParam dd ?
  iIndent dd ?
LV_ITEM ENDS

Nom Significations
imask Est un groupe de flags indiquant que les membres dans cette structure sont valides pour cet appel. En général, ce paramètre est semblable au membre imask de la structure LV_COLUMN vue plus haut. Référez vous à votre 'référence win32 api' pour plus de détail à propos des différents flags disponibles.
iItem Est l'index de l'item auquel cette structure se réfère. L'index de base est zéro. Vous pouvez vous représenter ce paramètre comme le numéro de la "rangée" d'un tableau.
iSubItem Est l'index du sous-item associé à l'item indiqué par iItem ci-dessus. Vous pouvez vous représenter ce paramètre comme étant le numéro de la "colonne" d'un tableau. Par exemple, si vous souhaitez insérer un item dans un tout nouveau 'listview control' juste créé, la valeur à mettre dans iItem serait 0 (parce que cet item est le premier) et la valeur dans iSubItem serait aussi 0 (car nous voulons insérer cet item dans la première colonne). Si vous souhaitez associer un sous-item à cet item, alors le iItem représenterait l'index de l'item avec lequel vous voulez l'associer (dans cet exemple, c'est 0). Le iSubItem ne pourrait être que 1 ou plus que 1, suivant la colonne où vous voulez insérer ce sous-item. Par exemple, si votre listview control a 4 colonnes, la première colonne contiendra tous les Items. Les 3 colonnes restantes sont utilisées par les sous-items. Si vous voulez insérer un sous-item dans la 4ème colonne, vous devez mettre valeur 3 dans iSubItem.
state

Ce membre contient les flags qui reflètent l'état de l'item. L'état de l'item peut changer à cause des actions de l'utilisateur, ou bien il peut être modifié par notre programme lui-même. The state includes whether the item has the focus/is hilited/is selected for cut operation/is selected. In addition to the state flags, It can also contains one-based index into the overlay image/state image for use by the item.

stateMask Puisque le membre 'state' ci-dessus peut contenir les flags qui définissent l'état de nos items, nous avons besoin de dire à Windows quelle valeur nous souhaitons mettre (ou remplacer si on a déjà définit son état). La valeur de ce paramètre sert à ça.
pszText Est l'adresse de la chaîne de caractère qui sera utilisée en tant que titre pour votre item dans le cas où vous souhaitez mettre ou insérer un item. Si nous nous servons de cette structure pour récupérer la propriété d'un item donné, ce membre doit contenir l'adresse du buffer qui sera rempli avec le titre de cet item.
cchTextMax Ce paramètre est uniquement utilisé lorsque vous employez cette structure pour recevoir des renseignements à propos de l'item. Dans ce cas, ce paramètre contient la taille en octets du buffer indiqué dans pszText, vu ci-dessus.
iImage Est l'index d'une icône contenue dans l'imagelist. Cet index indique quelle icône doit être employé et associée avec votre item.
lParam C'est une valeur définie par l'utilisateur, laquelle sera réutilisée lorsque vous trierez les Items de votre listview control. Bref, lorsque vous direz au listview control de trier vos Items, celui-ci comparera les Items deux par deux. Il vous renverra les valeurs des lParam des deux Items, et vous pourrez ainsi décider lequel des deux doit être inscrit en premier. Si vous êtes encore un peu dans le coaltar avec ça, ne vous inquiéter pas. Vous en apprendrez plus tard, bien plus du triage.

On va récapituler les étapes clef pour l'ajout d'un item ou sous-item dans le listview control.

  1. Créez une variable de type la structure LV_item
  2. Remplissez-la des informations nécessaires
  3. Envoyez le message LVM_INSERTITEM au listview control si vous voulez insérer un item. Ou si vous voulez *insert* un sous-item, envoyez LVM_SETITEM a la place. Ce sera plutôt le bordel si vous ne comprenez pas le rapport entre un item et ses sous-items. Les Sous-items représentent un peu les propriétés de l'item . Ainsi vous pouvez insérer des items, mais pas des sous-items et vous ne pouvez pas avoir un sous-item sans avoir un item qui lui soit associé. C'est pourquoi pour ajouter un sous-item vous avez besoin d'envoyer le message LVM_SETITEM au lieu de LVM_INSERTITEM.

Messages d'ordres et messages de réponses dans le ListView

Maintenant que vous savez comment créer et remplir un ListView control, l'étape suivante c'est de communiquer avec lui. Le ListView control communique avec la fenêtre parente grâce à des messages et des avis. La fenêtre parente peut commander le ListView control en lui envoyant des messages. En retour, le ListView control fait le contraire, il informe sa fenêtre parente des événements importants ou intéressants grâce à WM_NOTIFY, comme les autres 'common controls'.

Classer les items/sous-items

Vous pouvez spécifier le type de triage par défaut dans le listview control en indiquant les types de tris LVS_SORTASCENDING ou bien LVS_SORTDESCENDING dans CreateWindowEx. Ces deux types classent les Items d'après leurs noms uniquement. Si vous voulez les classer selon un autre critère, vous devez mettre le message LVM_SORTITEMS à l'intention du listview control.

LVM_SORTITEMS
wParam = lParamSort
lParam = pCompareFunction

lParamSort est une valeur définie par l'utilisateur laquelle on passera à la fonction de comparaison. Vous pouvez employer cette valeur de n'importe quelle façon, c'est à vous de voir.
pCompareFunction est l'adresse de la fonction(type de tri) que l'utilisateur à défini pour son listview control. Cette fonction a le prototype suivant :

CompareFunc proto lParam1:DWORD, lParam2:DWORD, lParamSort:DWORD

lParam1 et lParam2 sont les valeurs dans le membre de lParam de LV_item Lequel vous avez défini lorsque vous avez inséré vos Items dans le listview control.
lParamSort est la valeur de wParam que vous avez envoyée avec LVM_SORTITEMS

Quand le listview control reçoit le message LVM_SORTITEMS, il fait appel à la fonction de tri (ou de comparaison) indiquée par lParam, ainsi il nous retourne le résultat de la comparaison entre deux items. Bref, la fonction de comparaison servira à savoir lequel de deux items lui ayant été envoyé sera placé en premier par rapport à l'autre. La règle est simple : si la fonction renvoie une valeur négative, premier item (représenté par lParam1) doit être placé après l'autre. Si la fonction renvoie une valeur positive, cet item doit être placé en premier. Si les deux items sont égaux, un NULL sera renvoyé.

Ce qui fait que cette méthode fonctionne, c'est la valeur de lParam qui se trouve dans la structure LV_item. Si vous avez besoin de trier des Items (comme lorsque l'utilisateur clique sur l'en-tête d'une colonne, essayez ça marche !), vous avez besoin d'imaginer une méthode de tri qui se servira des valeurs dans le membre lParam (IParam1 et IParam2). Dans cet exemple, j'ai mis l'index de l'item dans ce paramètre donc je peux obtenir d'autres informations à propos de ces items en envoyant le message LVM_GETITEM. Remarquez que lorsque les Items sont réarrangés, leurs index changent aussi . De cette façon, quand le tri est fini dans mon exemple, j'ai encore besoin de remettre à jour les valeurs dans lParam pour retrouver les nouveaux index. Si vous voulez trier les Items lorsque l'utilisateur clique sur l'en-tête d'une colonne, vous avez besoin de traiter le message d'avis LVN_COLUMNCLICK dans votre procédure de fenêtre. On passe LVN_COLUMNCLICK à votre proc de fenêtre grâce au message WM_NOTIFY.

Exemple:

Cet exemple crée un 'listview control' et le remplit avec les noms et les tailles des fichiers du dossier actuel où vous êtes. La vue par défaut sera la vue 'détails'. Dans la vue 'détails', vous pouvez cliquer sur les en-têtes de colonne et les Items seront classés par ordre croissant/décroissant (soit par nom, soit par taille). Vous pouvez choisir la vue que vous souhaitez grâce au menu. Quand vous double cliquez sur un item, une boîte de message affichera le nom de l'item que vous venez de sélectionner.

.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
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

IDM_MAINMENU equ 10000
IDM_ICON equ LVS_ICON
IDM_SMALLICON equ LVS_SMALLICON
IDM_LIST equ LVS_LIST
IDM_REPORT equ LVS_REPORT

RGB macro red,green,blue
  xor eax,eax
  mov ah,blue
  shl eax,8
  mov ah,green
  mov al,red
endm

.data
ClassName db "ListViewWinClass",0
AppName db "Testing a ListView Control",0
ListViewClassName db "SysListView32",0
Heading1 db "Filename",0
Heading2 db "Size",0
FileNamePattern db "*.*",0
FileNameSortOrder dd 0
SizeSortOrder dd 0
template db "%lu",0


.data?
hInstance HINSTANCE ?
hList dd ?
hMenu dd ?

.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, NULL
  mov wc.lpfnWndProc, OFFSET WndProc
  mov wc.cbClsExtra,NULL
  mov wc.cbWndExtra,NULL
  push hInstance
  pop wc.hInstance
  mov wc.hbrBackground,COLOR_WINDOW+1
  mov wc.lpszMenuName,IDM_MAINMENU
  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,NULL,ADDR ClassName,ADDR AppName, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst,NULL
  mov hwnd,eax
  invoke ShowWindow, hwnd,SW_SHOWNORMAL
  invoke UpdateWindow, hwnd
  .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

InsertColumn proc
  LOCAL lvc:LV_COLUMN

  mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
  mov lvc.pszText,offset Heading1
  mov lvc.lx,150
  invoke SendMessage,hList, LVM_INSERTCOLUMN, 0, addr lvc
 
or lvc.imask,LVCF_FMT
  mov lvc.fmt,LVCFMT_RIGHT
  mov lvc.pszText,offset Heading2
  mov lvc.lx,100
 
invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc
  ret
InsertColumn endp

ShowFileInfo proc uses edi row:DWORD, lpFind:DWORD
  LOCAL lvi:LV_ITEM
  LOCAL buffer[20]:BYTE
  mov edi,lpFind
  assume edi:ptr WIN32_FIND_DATA
  mov lvi.imask,LVIF_TEXT+LVIF_PARAM
  push row
  pop lvi.iItem
  mov lvi.iSubItem,0
  lea eax,[edi].cFileName
  mov lvi.pszText,eax
  push row
  pop lvi.lParam
  invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
  mov lvi.imask,LVIF_TEXT
  inc lvi.iSubItem
  invoke wsprintf,addr buffer, addr template,[edi].nFileSizeLow
  lea eax,buffer
  mov lvi.pszText,eax
  invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
  assume edi:nothing
  ret
ShowFileInfo endp

FillFileInfo proc uses edi
  LOCAL finddata:WIN32_FIND_DATA
  LOCAL FHandle:DWORD

  invoke FindFirstFile,addr FileNamePattern,addr finddata
  .if eax!=INVALID_HANDLE_VALUE
    mov FHandle,eax
    xor edi,edi
    .while eax!=0
      test finddata.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
      .if ZERO?

         invoke ShowFileInfo,edi, addr finddata
         inc edi
      .endif
      invoke FindNextFile,FHandle,addr finddata
    .endw
    invoke FindClose,FHandle
  .endif
  ret
FillFileInfo endp

String2Dword proc uses ecx edi edx esi String:DWORD
  LOCAL Result:DWORD

  mov Result,0
  mov edi,String
  invoke lstrlen,String
  .while eax!=0
    xor edx,edx
    mov dl,byte ptr [edi]
    sub dl,"0"
    mov esi,eax
    dec esi
    push eax
    mov eax,edx
    push ebx
    mov ebx,10
    .while esi > 0
      mul ebx
      dec esi
    .endw
    pop ebx
    add Result,eax
    pop eax
    inc edi
    dec eax
  .endw
  mov eax,Result
  ret
String2Dword endp

CompareFunc proc uses edi lParam1:DWORD, lParam2:DWORD, SortType:DWORD
  LOCAL buffer[256]:BYTE
  LOCAL buffer1[256]:BYTE
  LOCAL lvi:LV_ITEM

  mov lvi.imask,LVIF_TEXT
  lea eax,buffer
  mov lvi.pszText,eax
  mov lvi.cchTextMax,256
  .if SortType==1
    mov lvi.iSubItem,1
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    invoke String2Dword,addr buffer
    mov edi,eax
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke String2Dword,addr buffer
    sub edi,eax
    mov eax,edi
  .elseif SortType==2
    mov lvi.iSubItem,1
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    invoke String2Dword,addr buffer
    mov edi,eax
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke String2Dword,addr buffer
    sub eax,edi
  .elseif SortType==3
    mov lvi.iSubItem,0
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    invoke lstrcpy,addr buffer1,addr buffer
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke lstrcmpi,addr buffer1,addr buffer
  .else
    mov lvi.iSubItem,0
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    invoke lstrcpy,addr buffer1,addr buffer
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke lstrcmpi,addr buffer,addr buffer1
  .endif
  ret
CompareFunc endp

UpdatelParam proc uses edi
   LOCAL lvi:LV_ITEM

   invoke SendMessage,hList, LVM_GETITEMCOUNT,0,0
   mov edi,eax
   mov lvi.imask,LVIF_PARAM
   mov lvi.iSubItem,0
   mov lvi.iItem,0
   .while edi>0
     push lvi.iItem
     pop lvi.lParam
     invoke SendMessage,hList, LVM_SETITEM,0,addr lvi
     inc lvi.iItem
     dec edi
   .endw
   ret
UpdatelParam endp

ShowCurrentFocus proc
   LOCAL lvi:LV_ITEM
   LOCAL buffer[256]:BYTE

   invoke SendMessage,hList,LVM_GETNEXTITEM,-1, LVNI_FOCUSED
   mov lvi.iItem,eax
   mov lvi.iSubItem,0
   mov lvi.imask,LVIF_TEXT
   lea eax,buffer
   mov lvi.pszText,eax
   mov lvi.cchTextMax,256
   invoke SendMessage,hList,LVM_GETITEM,0,addr lvi
   invoke MessageBox,0, addr buffer,addr AppName,MB_OK
   ret
ShowCurrentFocus endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  .if uMsg==WM_CREATE
    invoke CreateWindowEx, NULL, addr ListViewClassName, NULL, LVS_REPORT+WS_CHILD+WS_VISIBLE, 0,0,0,0,hWnd, NULL, hInstance, NULL
    mov hList, eax
    invoke InsertColumn
    invoke FillFileInfo
    RGB 255,255,255
    invoke SendMessage,hList,LVM_SETTEXTCOLOR,0,eax
    RGB 0,0,0
    invoke SendMessage,hList,LVM_SETBKCOLOR,0,eax
    RGB 0,0,0
    invoke SendMessage,hList,LVM_SETTEXTBKCOLOR,0,eax
    invoke GetMenu,hWnd
    mov hMenu,eax
    invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, IDM_REPORT,MF_CHECKED
  .elseif uMsg==WM_COMMAND
    .if lParam==0
      invoke GetWindowLong,hList,GWL_STYLE
      and eax,not LVS_TYPEMASK
      mov edx,wParam
      and edx,0FFFFh
      push edx
      or eax,edx
      invoke SetWindowLong,hList,GWL_STYLE,eax
      pop edx
      invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, edx,MF_CHECKED
    .endif
  .elseif uMsg==WM_NOTIFY
    push edi
    mov edi,lParam
    assume edi:ptr NMHDR
    mov eax,[edi].hwndFrom
    .if eax==hList
      .if [edi].code==LVN_COLUMNCLICK
        assume edi:ptr NM_LISTVIEW
        .if [edi].iSubItem==1
          .if SizeSortOrder==0 || SizeSortOrder==2
            invoke SendMessage,hList,LVM_SORTITEMS,1,addr CompareFunc
            invoke UpdatelParam
            mov SizeSortOrder,1
          .else
            invoke SendMessage,hList,LVM_SORTITEMS,2,addr CompareFunc
            invoke UpdatelParam
            mov SizeSortOrder,2
          .endif
        .else
          .if FileNameSortOrder==0 || FileNameSortOrder==4
            invoke SendMessage,hList,LVM_SORTITEMS,3,addr CompareFunc
            invoke UpdatelParam
            mov FileNameSortOrder,3
          .else
            invoke SendMessage,hList,LVM_SORTITEMS,4,addr CompareFunc
            invoke UpdatelParam
            mov FileNameSortOrder,4
          .endif
        .endif
        assume edi:ptr NMHDR
      .elseif [edi].code==NM_DBLCLK
        invoke ShowCurrentFocus
      .endif
    .endif
    pop edi
  .elseif uMsg==WM_SIZE
   
mov eax,lParam
    mov edx,eax
    and eax,0ffffh
    shr edx,16
    invoke MoveWindow,hList, 0, 0, eax,edx,TRUE
  .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:

La première chose que le programme fait lorsque la fenêtre principale est créée, c'est de créer notre listview control.

  .if uMsg==WM_CREATE
    invoke CreateWindowEx, NULL, addr ListViewClassName, NULL, LVS_REPORT+WS_CHILD+WS_VISIBLE, 0,0,0,0,hWnd, NULL, hInstance, NULL
    mov hList, eax

On appelle CreateWindowEx, en lui passant le nom de sa window class soit : "SysListView32". La vue par défaut est la vue 'détails' définie par le style LVS_REPORT.

    invoke InsertColumn

Après que le listview control est été créé, nous y insérons des colonnes.

  LOCAL lvc:LV_COLUMN

  mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
  mov lvc.pszText,offset Heading1
  mov lvc.lx,150
  invoke SendMessage,hList, LVM_INSERTCOLUMN, 0, addr lvc

Nous initialisons le label (son nom) et la largeur de la première colonne, qui sert au stockage des noms de fichiers, dans la structure LV_COLUMN. Donc nous avons besoin de mettre les flags LVCF_TEXT et LVCF_WIDTH dans imask . On rempli pszText avec l'adresse du label et lx avec la largeur de la colonne, en pixels. Quand tout est prêt, nous envoyons le message LVM_INSERTCOLUMN au listview control, en lui passant cette structure.

  or lvc.imask,LVCF_FMT
  mov lvc.fmt,LVCFMT_RIGHT

Quand on en a fini avec l'insertion de la première colonne, nous en insérons une autre servant cette fois-ci au stockage des tailles de fichiers. Puisque nous avons besoin des tailles pour les aligner à droite dans la colonne, nous avons besoin de mettre le flag LVCFMT_RIGHT dans le membre fmt. Nous devons aussi mettre le flag LVCF_FMT dans imask, en plus de LVCF_TEXT et LVCF_WIDTH.

  mov lvc.pszText,offset Heading2
  mov lvc.lx,100
  invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc

Le code restant est simple. On Met l'adresse du label (du titre de la colonne) dans pszText et sa largeur dans lx. On envoie alors le message LVM_INSERTCOLUMN au listview control, en indiquant le nombre de colonnes et l'adresse de la structure.

Quand les colonnes sont insérées, nous pouvons placer les Items dans notre listview control.

    invoke FillFileInfo

FillFileInfo présente le code suivant.

FillFileInfo proc uses edi
  LOCAL finddata:WIN32_FIND_DATA
  LOCAL FHandle:DWORD

  invoke FindFirstFile,addr FileNamePattern,addr finddata

Nous appelons FindFirstFile pour obtenir l'information sur le premier fichier qui correspond à nos critères de recherche. FindFirstFile possède le prototype de fonction suivant :

FindFirstFile proto pFileName:DWORD, pWin32_Find_Data:DWORD

pFileName est l'adresse du nom du fichier qu'on chercher. Cette chaîne de caractères peut contenir des caractères de substitutions. Dans notre exemple, nous employons *.*, ce qui fait qu'on mène une recherche sur tous les fichiers du dossier actuel où on se trouve.
pWin32_Find_Data est l'adresse de la structure WIN32_FIND_DATA laquelle sera remplie de l'information du fichier (si on l'a trouvé).

Cette fonction renvoie INVALID_handle_VALUE dans eax si aucun fichier correspondant à ce que l'on cherche n'a été trouvé. Sinon l'handle de la recherche est renvoyée, lequel sera utilisé dans les appels suivant FindNextFile.

  .if eax!=INVALID_HANDLE_VALUE
    mov FHandle,eax
    xor edi,edi

Si un fichier est trouvé, on stocke l'handle de la recherche dans une variable et ensuite nous mettons edi à zéro pour pouvoir l'employé en tant qu'index pour les Items (c'est le numéro de rangée).

    .while eax!=0
      test finddata.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
      .if ZERO?

Dans cet exemple, je ne souhaite rien toucher dans les autres dossiers, donc je les filtre en vérifiant dwFileAttributes. Comme ça, si on trouve des fichiers qui ont un flag FILE_ATTRIBUTE_DIRECTORY associés, on saute par dessus grâce à l'appel FindNextFile.

          invoke ShowFileInfo,edi, addr finddata
          inc edi
      .endif

      invoke FindNextFile,FHandle,addr finddata     
    .endw

Nous insérons le nom et la taille du fichier dans le listview control en appelant la fonction ShowFileInfo. Donc on augmente le numéro de la rangée actuelle (dans edi). Finalement on continue à appeler FindNextFile pour chercher le fichier suivant dans le dossier actuel jusqu'à ce que FindNextFile retourne un 0 (Ce qui signifie qu'il n'y a plus de fichiers à chercher).

    invoke FindClose,FHandle
  .endif
  ret
FillFileInfo endp

Quand tous les fichiers dans le dossier actuel ont étés passés à la loupe, on doit refermer l'handle de la recherche.

Maintenant on va regarder la fonction ShowFileInfo. Cette fonction accepte deux paramètres, l'index de l'item (son numéro de rangée) et l'adresse de la structure WIN32_FIND_DATA.

ShowFileInfo proc uses edi row:DWORD, lpFind:DWORD
  LOCAL lvi:LV_ITEM
  LOCAL buffer[20]:BYTE
  mov edi,lpFind
  assume edi:ptr WIN32_FIND_DATA

On stocke l'adresse de la structure WIN32_FIND_DATA dans edi.

  mov lvi.imask,LVIF_TEXT+LVIF_PARAM
  push row
  pop lvi.iItem
  mov lvi.iSubItem,0

Nous donne le nom de l'item et sa valeur dans lParam donc nous mettons les flags LVIF_TEXT et LVIF_PARAM dans imask. Ensuite nous plaçons l'item dans la bonne rangée (dont on avait passé son numéro à la fonction) et puisque c'est l'item principal, on doit mettre un 0 dans iSubItem (car la première colonne, c'est la colonne 0).

  lea eax,[edi].cFileName
  mov lvi.pszText,eax
  push row
  pop lvi.lParam

Ensuite on met l'adresse de son nom, (dans ce cas), le nom du fichier dans pszText de la structure WIN32_FIND_DATA. Puisque nous effectuons un tri dans le listview control, nous devons remplir lParam avec une valeur. Je souhaite mettre le numéro de rangée dans ce membre donc je peux me servir des renseignements de l'item grâce à son index pour le retrouver.

  invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi

Lorsque tous les paramètres nécessaires dans LV_item sont remplis, on envoie le message LVM_INSERTITEM au listview control pour y insérer un item.

  mov lvi.imask,LVIF_TEXT
  inc lvi.iSubItem
  invoke wsprintf,addr buffer, addr template,[edi].nFileSizeLow
  lea eax,buffer
  mov lvi.pszText,eax

On place, dans la deuxième colonne, le sous-item associé à l'item qu'on vient juste d'insérer. Un sous-item peut seulement posséder un label (un titre). Donc on met LVIF_TEXT dans imask. Ensuite dans iSubItem, on indique la colonne dans laquelle le sous-item doit se trouver. Dans notre cas, nous met iSubItem à 1 en l'incrémentant. Le label que nous utiliserons représentera 'la taille du fichier'. Cependant, nous devons d'abord le convertir en une chaîne de caractères en appelant wsprintf. C'est pourquoi nous mettons l'adresse de la chaîne de caractères dans pszText .

  invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
  assume edi:nothing
  ret
ShowFileInfo endp

Quand tous les paramètres nécessaires à LV_item sont remplis, nous envoyons le message LVM_SETITEM à notre listview control, en lui passant l'adresse de la structure LV_ITEM. Remarquez que nous utilisons LVM_SETITEM, et non pas LVM_INSERTITEM parce qu'un sous-item est considéré comme étant une propriété de l'item. Donc rappelez vous de ça, c'est important, on *pose* (set) la propriété de l'item, mais on n'insère pas un nouvel item.

Lorsque tous les Items sont insérés dans le listview control, on défini le type de texte et les couleurs de fond de notre listview control.

    RGB 255,255,255
    invoke SendMessage,hList,LVM_SETTEXTCOLOR,0,eax
    RGB 0,0,0
    invoke SendMessage,hList,LVM_SETBKCOLOR,0,eax
    RGB 0,0,0
    invoke SendMessage,hList,LVM_SETTEXTBKCOLOR,0,eax

J'emploie la macro RGB pour convertir les valeurs rouge, verte, est bleue dans eax et les employer pour définir la couleur réelle que je souhaite obtenir. On défini les couleurs de premier plan et d'arrière plan du texte avec les messages LVM_SETTEXTCOLOR et LVM_SETTEXTBKCOLOR. On défini la couleur de fond de notre listview control en lui envoyant le message LVM_SETBKCOLOR.

    invoke GetMenu,hWnd
    mov hMenu,eax
    invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, IDM_REPORT,MF_CHECKED

On laisse l'utilisateur choisit ses vues grâce au menu. Donc on doit d'abord récupérer l'handle du menu. Pour aider l'utilisateur à retrouver la vue actuelle, on incorpore un système de bouton radio dans notre menu. L'item du menu qui est associé à la vue actuelle sera ainsi précédé d'un bouton tout rond. Pour faire ça, on doit appeler CheckMenuRadioItem. Cette fonction affichera un bouton radio juste devant l'item voulu dans votre menu.

Remarquez qu'on crée un listview control d'une largeur et d'une hauteur égales à 0. Elle sera redimensionnée plus tard à chaque fois que la fenêtre parente sera elle-même redimensionnée. De cette façon, on peut être sûr que la taille de notre listview control correspondra toujours à celle de la fenêtre parente. Dans notre exemple, on souhaite que le listview control remplisse la totalité du secteur de client de la fenêtre parente.

  .elseif uMsg==WM_SIZE
   
mov eax,lParam
    mov edx,eax
    and eax,0ffffh
    shr edx,16
    invoke MoveWindow,hList, 0, 0, eax,edx,TRUE

Quand la fenêtre parente reçoit le message WM_SIZE, le mot de poids faible de lParam contient la nouvelle largeur du secteur client et le mot de poids fort la nouvelle hauteur. Ensuite on appelle MoveWindow pour redimensionner le listview control pour qu'il recouvre le secteur client de la fenêtre parente, en entier.

Lorsque l'utilisateur sélectionne une des vue dans le menu. On doit faire en sorte de changer la vue du listview control en conséquence. On fait ça en mettant un nouveau style dans le listview control avec SetWindowLong.

  .elseif uMsg==WM_COMMAND
    .if lParam==0
      invoke GetWindowLong,hList,GWL_STYLE
      and eax,not LVS_TYPEMASK

La première chose à faire, c'est obtenir les styles actuels du listview control. Donc on purifie le vieux type de vue renvoyé dans les flags. LVS_TYPEMASK est une constante qui représente la valeur combinée des 4 types de vue (LVS_ICON+LVS_SMALLICON+LVS_LIST+LVS_REPORT). Ainsi quand on exécute l'opération and sur les types actuels de flags avec la valeur "not LVS_TYPEMASK", ça équivaut à nettoyer le type de vue actuel.

Dans la conception du menu, je triche un peu. J'emploie le type de vue des constantes en tant qu'IDs du menu.

IDM_ICON equ LVS_ICON
IDM_SMALLICON equ LVS_SMALLICON
IDM_LIST equ LVS_LIST
IDM_REPORT equ LVS_REPORT

Ainsi quand la fenêtre parente reçoit le message WM_COMMAND, le type de vue voulu est dans le mot de poids faible de wParam pour représenter l'ID du menu.

      mov edx,wParam
      and edx,0FFFFh

On retrouvera le type de vue souhaité dans le mot de poids faible de wParam. Tout ce que nous devons faire c'est de mettre le mot de poids fort à 0 (je parle de wParam).

      push edx
      or eax,edx

Et ajoutez le type de vue désiré aux styles déjà existants (moins le style de vue actuel) du listview control.

      invoke SetWindowLong,hList,GWL_STYLE,eax

Et mettez les nouveaux types avec SetWindowLong.

      pop edx
      invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, edx,MF_CHECKED    
   .endif

Nous avons aussi besoin de mettre le bouton radio devant l'item choisi dans notre menu. Ainsi on appelle CheckMenuRadioItem, en lui passant le type de vue actuel (en double, en tant qu'ID du menu).

Lorsque l'utilisateur clique sur l'en-tête de la colonne dans la vue 'détails', on fait en sorte de déclencher le comportement de triage des Items dans notre listview control. Nous devons répondre au message WM_NOTIFY.

  .elseif uMsg==WM_NOTIFY
    push edi
    mov edi,lParam
    assume edi:ptr NMHDR
    mov eax,[edi].hwndFrom
    .if eax==hList

Quand on reçoit le message WM_NOTIFY, lParam contient le pointer qui est sur la structure NMHDR. On peut vérifier si ce message provient de notre listview control en comparant le membre hwndFrom dans NMHDR à l'handle de notre listview control. S'ils correspondent, alors on est sûr que cet avis provient bien de notre listview control.

      .if [edi].code==LVN_COLUMNCLICK
        assume edi:ptr NM_LISTVIEW

S'il provient bien du listview control, on vérifie si le code est LVN_COLUMNCLICK. Et si c'est ça, ça signifie que l'utilisateur a cliqué sur l'en-tête de la colonne. Dans le cas où le code serait LVN_COLUMNCLICK, on est sûr que lParam contient le pointer qui est sur la structure NM_LISTVIEW laquelle est un 'superset' de la structure NMHDR. On aura alors besoin de savoir sur lequel des en-tête de colonne, l'utilisateur à cliqué. L'examen du membre iSubItem nous révèle ces renseignements. La valeur dans iSubItem peut être considérée comme le numéro de colonne, en commençant par 0 pour la première colonne.

        .if [edi].iSubItem==1
          .if SizeSortOrder==0 || SizeSortOrder==2

Dans le cas où iSubItem est égal à 1, ça signifie que l'utilisateur à cliqué sur la deuxième colonne, (la taille). On utilise des variables d'état pour récupérer le statut actuel de l'ordre de triage. 0 signifie "Pas encore trié", 1 signifie qu'"on tri l'item en le faisant monté d'une place dans la hiérarchie", 2 signifie qu'"on tri l'item en le faisant descendre dans la hiérarchie" des items. Si les Items ou sous-items de la colonne n'ont pas été trié auparavant, ou on été trié en ayant perdu des places, on impose un tri pour les remonter.

            invoke SendMessage,hList,LVM_SORTITEMS,1,addr CompareFunc

On envoie le message LVM_SORTITEMS à notre listview control, en lui passant 1 dans wParam et l'adresse de notre fonction de tri dans lParam. Remarquez que la valeur dans wParam est définie par l'utilisateur, vous pouvez l'employer de la façon que vous le souhaitez. Je l'emploie en tant que méthode de triage dans cet exemple. Bon, on va d'abord jeter un coup d'oeil à la fonction de tri.

CompareFunc proc uses edi lParam1:DWORD, lParam2:DWORD, SortType:DWORD
  LOCAL buffer[256]:BYTE
  LOCAL buffer1[256]:BYTE
  LOCAL lvi:LV_ITEM

  mov lvi.imask,LVIF_TEXT
  lea eax,buffer
  mov lvi.pszText,eax
  mov lvi.cchTextMax,256

Dans la fonction de tri, le listview control passera les lParams (provenant de LV_item) de deux Items dont il a besoin de comparer pour les triés l'un par rapport à l'autre, soit lParam1 et lParam2. Vous vous rappellerez qu'on a mis l'index des l'items dans lParam. Ainsi on peut récupérer l'information des Items en interrogeant le listview control en employant ces index. Les renseignements dont nous avons besoin sont les labels (leurs noms) des Items / sous-items étant triés. Ainsi on prépare la structure LV_ITEM dans ce but, en déclarant LVIF_TEXT dans imask et l'adresse du buffer dans pszText et la taille du buffer dans cchTextMax.

  .if SortType==1
    mov lvi.iSubItem,1
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi

Si la valeur dans SortType est 1 ou 2, on sait que la colonne (de taille) a été cliquée. 1 signifie que les Items ont été triés selon leurs tailles dans un ordre montant. 2 signifie le contraire. Ainsi, on impose iSubItem à 1 (pour spécifier la colonne de taille) et on envoie le message LVM_GETITEMTEXT à notre listview control pour obtenir le label (le nom) de la chaîne de caractères (de taille) du sous-item.

    invoke String2Dword,addr buffer
    mov edi,eax

On cache plus ou moins la taille de la chaîne de caractères dans une valeur Dword avec la fonction String2Dword que j'ai écrite. Elle renvoie cette valeur Dword dans eax. Et en plus on la stocke dans edi pour la trier par la suite.

    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke String2Dword,addr buffer
    sub edi,eax
    mov eax,edi

Faites de même avec la valeur dans lParam2. Quand nous avons les tailles des deux fichiers, nous pouvons alors les comparer.
La règle de la fonction de tri est définie ainsi :

Dans ce cas, on souhaite trier les Items selon leurs critères de tailles par ordre croissant. Donc on peut utiliser la ruse d'une simple soustraction des tailles des deux items et renvoyer le résultat dans eax.

  .elseif SortType==3
    mov lvi.iSubItem,0
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    invoke lstrcpy,addr buffer1,addr buffer
    invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    invoke lstrcmpi,addr buffer1,addr buffer

Dans le cas où l'utilisateur clique sur la colonne 'de nom de fichier', nous devons forcément comparer les noms des fichiers. Il nous faut d'abord récupérer les noms de fichier et on les compare ensuite avec la fonction 'lstrcmpi'. On peut renvoyé la valeur de retour de lstrcmpi sans aucune modification puisqu'elle emploie aussi la même règle de tri. C'est à dire qu'on aura une valeur négative dans eax si la première chaîne de caractères est moindre (B

Une fois que les Items ont été triés, nous avons besoin de remettre à jour les valeurs de lParam de chaques Items pour refléter les nouveaux index en appelant la fonction UpdatelParam.

            invoke UpdatelParam
            mov SizeSortOrder,1

Cette fonction énumère simplement tout les Items du listview control et met à jour les valeurs dans lParam avec les nouveaux index. On doit absolument faire ça sinon le tri suivant ne marchera pas comme attendu, parce qu'on suppose que la valeur dans lParam est l'index de l'item.

      .elseif [edi].code==NM_DBLCLK
        invoke ShowCurrentFocus
      .endif

Quand l'utilisateur double clique sur l'item, on souhaite que son nom s'affiche dans une boîte de message. On doit donc vérifier si le code dans NMHDR est NM_DBLCLK. Si c'est ça, on peut continuer pour récupérer son nom et le montrer dans la MessageBox.

ShowCurrentFocus proc
   LOCAL lvi:LV_ITEM
   LOCAL buffer[256]:BYTE

   invoke SendMessage,hList,LVM_GETNEXTITEM,-1, LVNI_FOCUSED

Comment savons-nous quel item vient d'être double cliqué ? Et bien, lorsque un item est cliqué ou double cliqué, son état est remis à "jour". Même si beaucoup d'Items sont (hilited) sélectionnés avec un encadré pointillé, uniquement un d'entre eux a le centre (est réellement sélectionné). Notre travail, c'est de retrouver l'item qui a le centre. Nous faisons ça en envoyant le message LVM_GETNEXTITEM à notre listview control, en spécifiant l'état désiré dans lParam. -1 dans wParam signifie qu'on recherche tous les Items. L'index de l'item est renvoyé dans eax.

   mov lvi.iItem,eax
   mov lvi.iSubItem,0
   mov lvi.imask,LVIF_TEXT
   lea eax,buffer
   mov lvi.pszText,eax
   mov lvi.cchTextMax,256
   invoke SendMessage,hList,LVM_GETITEM,0,addr lvi

On continue alors pour récupérer son titre (son nom) en envoyant le message LVM_GETITEM au listview control.

   invoke MessageBox,0, addr buffer,addr AppName,MB_OK

Finalement, nous affichons le nom du fichier dans une MessageBox.

Si vous souhaitez savoir comment utiliser des icônes dans votre listview control, vous pouvez lire ça dans mon tutorial sur le treeview (Tutorial 19). Les étapes sont à peu près les mêmes.


[Iczelion's Win32 Assembly Homepage]

 



Traduit par Morgatte