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

Soyez le premier à donner votre avis sur cette source.

Vue 6 623 fois - Téléchargée 498 fois

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

Ajouter un commentaire

Commentaires

racpp
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
11
ROBSOUILLE >> Je n'ai rien caché car il n'y a pas d'erreur. La cellule n'est pas rendue invisible mais si tu te documentes un peu sur les listviews tu découvriras que les index des colonnes commencent par 1 et non 0. L'index 0 correspond à la ligne entière et non à une colonne cachée. L'index 0 est inutile dans une listview multicolonnes.
robsouille
Messages postés
3
Date d'inscription
mardi 29 novembre 2005
Statut
Membre
Dernière intervention
3 septembre 2009

Franchement bien,
Juste une tite erreur que tu caches, mis a part la premiere ligne, toutes tes villes sont repetees 2 fois. Tu repetes toujours la ville dans la premiere cellule de la ligne d'apres, mais comme tu n'affiches paa cette cellule, ca ne ce voit pas.
Mais qd meme, ^^, un ptit : lvi.pszText = ""; resoud ce pb qui n'en ai pas un, ;).
yassirguitare
Messages postés
29
Date d'inscription
mardi 20 décembre 2005
Statut
Membre
Dernière intervention
3 juin 2009

Bravo 10
uaip
Messages postés
1466
Date d'inscription
mardi 20 février 2007
Statut
Membre
Dernière intervention
7 février 2011

Très bonne idée, je trouve.
Rien à dire au niveau du code, si ce n'est que je n'aurais peut-être pas utilisé une DialogBox, mais pour ton exemple, c'est peut-être ce qu'il y a de plus 'simple'.
Il y a quelques jours je cherchais désespérément une source de ce style, sans succès.

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.