Réarranger les lignes d'une listview à la souris (win32 api)

Description

Ce code source montre comment réarranger les lignes d'une ListView à la souris. Il utilise la notification LVN_BEGINDRAG, le message LVM_CREATEDRAGIMAGE et les fonctions ImageList_BeginDrag(), ImageList_DragEnter(), ImageList_DragMove(), ImageList_DragLeave() et ImageList_EndDrag(). Ces fonctions sont utilisées pour l'aspect visuel des opérations de glissement des lignes.
Projet réalisé avec Visual C/C++ 2005 mais très facilement adaptable aux autres outils car il est en WIN32 API.

Source / Exemple :


#include <windows.h>
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")

BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hquit,hlistview;
	static LVITEM lvi;
	static BOOL bDragging;
	static HIMAGELIST hDragImage;
	static char buffer[MAX_PATH];
	static HCURSOR hDragCursor,hNoDragCursor,hNormalCursor;
	static LVHITTESTINFO   lvhti;

	switch(message)
	{
	case WM_INITDIALOG:
		{
			// Définir le titre de la boite de dialogue:
			SetWindowText(hwnd,"Réarranger les lignes d'une ListView à la souris");
			//Créer les controles:
			hquit=CreateWindowEx(0,"button","Quitter", WS_CHILD | WS_VISIBLE  ,330, 100, 60, 20, hwnd, 0, 0, 0);
			hlistview=CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,0, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL ,10, 10, 304, 200, hwnd, 0, 0, 0);
			SendMessage(hlistview,LVM_SETEXTENDEDLISTVIEWSTYLE,0,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES );
			// Créer les colonnes de la ListView:
			LVCOLUMN lvc; 
			lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT |LVCF_SUBITEM; 
			lvc.fmt = LVCFMT_CENTER; 
			lvc.iSubItem =0; lvc.cx = 0; lvc.pszText = 0;
			SendMessage(hlistview,LVM_INSERTCOLUMN,0,(LPARAM)&lvc); 
			lvc.iSubItem =1; lvc.cx = 100; lvc.pszText = "Rang";
			SendMessage(hlistview,LVM_INSERTCOLUMN,1,(LPARAM)&lvc); 
			lvc.iSubItem =2; lvc.cx = 100; lvc.pszText = "Pays";
			SendMessage(hlistview,LVM_INSERTCOLUMN,2,(LPARAM)&lvc); 
			lvc.iSubItem =3; lvc.cx = 100; lvc.pszText = "Capitale";
			SendMessage(hlistview,LVM_INSERTCOLUMN,3,(LPARAM)&lvc); 
			// Ajout des éléments de la ListView:
			lvi.mask=LVIF_TEXT | LVIF_STATE ;
			char* contenu[]={"1","France","Paris","2","Allemagne","Berlin","3","Angleterre","London","4","Italie","Rome","5","Espagne","Madrid"};
			int i;
			for(i=0;i<5;i++)
			{
				lvi.iItem=i;
				lvi.iSubItem=0;
				SendMessage(hlistview,LVM_INSERTITEM ,0,(LPARAM)&lvi);
				lvi.iSubItem=1;
				lvi.pszText=contenu[i*3];
				SendMessage(hlistview,LVM_SETITEMTEXT ,i,(LPARAM)&lvi);
				lvi.iSubItem=2;
				lvi.pszText=contenu[i*3+1];
				SendMessage(hlistview,LVM_SETITEMTEXT ,i,(LPARAM)&lvi);
				lvi.iSubItem=3;
				lvi.pszText=contenu[i*3+2];
				SendMessage(hlistview,LVM_SETITEMTEXT ,i,(LPARAM)&lvi);
			}
			// Charger les différents curseurs;
			hDragCursor=LoadCursor((HMODULE)lParam,"IDC_DRAG");
			hNoDragCursor=LoadCursor((HMODULE)lParam,"IDC_NODRAG");
			hNormalCursor=LoadCursor(0,IDC_ARROW);
			return 0;
		}

	case WM_NOTIFY:
		LPNMHDR pnmhdr;
		pnmhdr=(LPNMHDR)lParam;
		if(pnmhdr->code==LVN_BEGINDRAG)
		{
			POINT pt;
			// Obtenir l'index de l'élément sélectionné:
			int iSelected = SendMessage(hlistview,LVM_GETNEXTITEM,-1,LVIS_SELECTED);
			// Créer une DragImage de l'élément sélectionné:
			hDragImage = (HIMAGELIST)SendMessage(hlistview,LVM_CREATEDRAGIMAGE ,iSelected,(LPARAM)&pt);
			// Commencer le dragging de notre DragImage:
			ImageList_BeginDrag(hDragImage, 0, 0, 0);
			// Déterminer la position de notre DragImage sur l'écran:
			LPNM_LISTVIEW pnml;
			pnml=(LPNM_LISTVIEW)pnmhdr;
			pt = pnml->ptAction;
			ClientToScreen(hlistview, &pt);
			// Afficher notre DragImage:
			ImageList_DragEnter(0, pt.x, pt.y);
			// Mettre le flag du drag en cours à TRUE:
			bDragging = TRUE;
			// Empêcher le curseur de la souris de sortir de notre fenêtre:
			RECT rect;
			GetWindowRect(hwnd,&rect);
			ClipCursor(&rect);
			// Capturer les événements de la souris:
			SetCapture(hwnd);
			return 0;
		}
		break;

    case WM_MOUSEMOVE:
		{
			// Ne rien faire si le flag du drag en cours est FALSE:
			if (!bDragging) break;
			// Obtenir l'index de l'élément pointé par la souris:
			lvhti.pt.x = LOWORD(lParam);
			lvhti.pt.y = HIWORD(lParam);
			ClientToScreen(hwnd, &lvhti.pt);
			ScreenToClient(hlistview, &lvhti.pt);
			SendMessage(hlistview,LVM_HITTEST,0,(LPARAM)&lvhti);
			// Si l'index est valide alors afficher le curseur de drag:
 			if(lvhti.iItem!=-1)SetCursor(hDragCursor);
			// Sinon afficher le curseur d'interdiction de drag:
			else SetCursor(hNoDragCursor);
			// Mettre notre DragImage à la position courante de la souris:
 			lvhti.pt.x=0;
			ClientToScreen(hlistview, &lvhti.pt);
			ImageList_DragMove(lvhti.pt.x, lvhti.pt.y);
		}
        break;

    case WM_LBUTTONUP:
		{
			// Mettre le flag de drag en cours à FALSE:
			bDragging = FALSE;
			// Cacher notre DragImage:
			ImageList_DragLeave(hlistview);
			// Terminer le dragging:
			ImageList_EndDrag();
			// Détruire notre DragImage:
			ImageList_Destroy(hDragImage);
			// Arrêter la capture des événements souris:
			ReleaseCapture();
			// Libérer le curseur souris:
			ClipCursor(0);
			// Afficher le curseur nomal (flèche):
			SetCursor(hNormalCursor);
			// Déterminer l'élément pointé par la souris:
			lvhti.pt.x = LOWORD(lParam);
			lvhti.pt.y = HIWORD(lParam);
			ClientToScreen(hwnd, &lvhti.pt);
			ScreenToClient(hlistview, &lvhti.pt);
			SendMessage(hlistview,LVM_HITTEST,0,(LPARAM)&lvhti);
			// Ne rien faire si en dehors des éléments de la ListView:
			if (lvhti.iItem == -1)  break;
			// Trouver l'élément sélectionné:
			int iSelected = SendMessage(hlistview,LVM_GETNEXTITEM,-1,LVIS_SELECTED);
			// Ne rien faire si aucun élément sélectionné ou s'il s'agit du même élément que celui pointé par la souris:
			if(iSelected==-1 || iSelected==lvhti.iItem) break;
			// Initialiser la structure LVITEM pour l'élément à ajouter:
			lvi.iSubItem = 0;
			if(lvhti.iItem<iSelected)lvi.iItem = lvhti.iItem;
			else lvi.iItem = lvhti.iItem + 1;
			// Ajouter le nouvel élément:
			int iNewItem=SendMessage(hlistview,LVM_INSERTITEM,0,(LPARAM)&lvi);
			// Incrémenter l'index de l'élément sélectionné si celui-ci se trouve après l'élément pointé par la souris:
			if (iNewItem < iSelected) iSelected++;
			// Obtenir le nombre de colonnes de la ListView:
			HWND hheader= (HWND)SendMessage(hlistview, LVM_GETHEADER, 0, 0);
			int iColumnCount=SendMessage(hheader,HDM_GETITEMCOUNT ,0,0);
			// Transférer le contenu des colonnes de l'élément sélectionné vers le nouvel élément:
			lvi.cchTextMax = MAX_PATH;
			lvi.pszText = buffer;
			int i;
			for (i = 1; i < iColumnCount; i++)
			{
				lvi.iSubItem=i;
				SendMessage(hlistview,LVM_GETITEMTEXT,iSelected,(LPARAM)&lvi);
				SendMessage(hlistview,LVM_SETITEMTEXT,iNewItem,(LPARAM)&lvi);
			}
			// Sélectionner l'élément nouvellement inséré:
			lvi.state=LVIS_SELECTED;
			lvi.stateMask=LVIS_SELECTED;
			SendMessage(hlistview,LVM_SETITEMSTATE,iNewItem,(LPARAM)&lvi);
			// Supprimer l'élément de la position originale:
			SendMessage(hlistview,LVM_DELETEITEM,iSelected,0);
		}
        break;

	case WM_COMMAND:
		// Envoyer le message de fermeture de la boite de dialogue si clic sur le bouton "Quitter":
		if((HWND)lParam==hquit) SendMessage(hwnd,WM_CLOSE,0,0);
		break;

	case WM_CLOSE:
		//Fermer la boite de dialogue:
		EndDialog(hwnd,0);
		break;
	default:
		break;
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int show)
{
	// Charger La DLL des Common Controls:
	HINSTANCE hCmLib= LoadLibrary("comctl32.dll");
	//Allouer de la mémoire pour notre dialog template:
	LPDLGTEMPLATE lpdt = ( LPDLGTEMPLATE) GlobalAlloc(GPTR, 512); 
	// Définir les styles de la boite de dialogue:
	lpdt->style =   WS_DLGFRAME| DS_CENTER  |WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
	// Obtenir les unités d'affichage des boite de dialogues:
	long baseunits=GetDialogBaseUnits();
	// Définir les dimensions de la boite de dialogue:
	lpdt->cx = MulDiv(406, 4,LOWORD(baseunits)); 
	lpdt->cy = MulDiv(220, 8, HIWORD(baseunits));       
	//Lancer la boite de dialogue
	DialogBoxIndirectParam(hinst,lpdt,0,(DLGPROC)DlgProc,(LPARAM)hinst);
	//Libération de la mémoire allouée:
	GlobalFree((HGLOBAL)lpdt);
	// Libérer la DLL des Common Controls:
	FreeLibrary(hCmLib);
	return 0;
}

Conclusion :


Le zip contient le projet complet.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.