Boîte de dialogue de progression avec annulation

Description

Ce code source montre comment réaliser une boîte de dialogue contenant une ProgressBar permettant de suivre la progression d'une tâche exécutée par l'application avec la possibilité de l'annuler en cliquant sur un bouton ou en appuyant sur la touche Echap. Grâce à l'interception du message WM_WINDOWPOSCHANGED, on sait que notre boîte de progression vient d'être affichée juste après sa création. On appelle alors la fonction exécutant la tâche dont on veut voir l'évolution. Elle prend en paramètre le HWND de la ProgressBar et celui du bouton d'annulation. Le premier lui permet de mettre à jour la position de la ProgressBar. Le second sert à retrouver l'état du drapeau d'annulation stocké comme attribut USERDATA du bouton. Il est mis à 1 suite à un clic sur le bouton d'annulation ou après appui sur la touche Echap ou Entrée. Pour ne pas blocker la boîte de dialogue, cette fonction doit appeler, après chaque étape de la tâche exécutée, une autre fonction permettant de traiter les messages qui lui sont destinés et effacer de la pile tous ceux destinés à sa boîte mère. Ainsi, cette dernière ne pourra plus réagir aux actions (clics etc) qui resteraient dans la pile des messages. Notre fonction d'exécution de la tâche vérifiera ensuite l'état du drapeau d'annulation. S'il est à 1, on arrête la tâche et retourne, sinon on continue avec l'étape suivante. Au retour de cette fonction, la boîte se ferme.
La ProgressBar utilisée est personnalisée afin d'afficher le pourcentage dans la barre de progression. Pour cela, le contrôle est sous-classé et sa zone cliente est entièrement dessinée pendant le traitement du message WM_PAINT.
Ce code est réalisé sous Visual C/C++ 2005 en Win32 API. Ce qui le rend utilisable avec les autres compilateurs.
Pour tester l'exécutable, renommez le en ProgressDialig.exe
Vos remarques, questions ou commentaires sont les bienvenus.

Source / Exemple :


#include <windows.h>
#include <commctrl.h>

WNDPROC OldProgressBarProc;

// Structure de récupération des données d'une ProgressBar:
typedef struct 
{
    HWND hwnd;
    DWORD dwStyle;
    INT iMin;
	INT iMax;
    INT iPos;
    INT iStep;
    HFONT hfont;
    COLORREF bkcolor;
    COLORREF barcolor;
} PRODATA;

// Cette fonction vide la pile des messages de la fenêtre parente de notre boîte de dialogue
// afin de l'empêcher de réagir aux actions survenues pendant la progression.
// Elle permet aussi d'intercepter le clic sur le bouton "Annuler" et l'utilisation de la touche Echap et Entrée.
void TraiterMessages(HWND hwnd)
{
	MSG msg;
	// Enlever tous les messages destinés aux fenêtres de l'application:
	while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
	{
		// Utiliser la fonction IsDialogMessage() pour permmetre à notre boîte de réagir aux actions clavier:
		if(!IsDialogMessage(hwnd, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
}

// Fonction exécutant une boucle de simulation d'une tâche en progression:
int ProgressFunction(HWND hdlg, HWND progress, HWND cancel)
{
	// Mettre l'attribut USERDATA du bouton "Annuler" à 0:
	SetWindowLong(cancel,GWL_USERDATA,0);
	int i;
	// Lancer la boucle pour simuler une progression:
	for(i=0;i<3000000;i++) 
	{ 
		// Mettre à jour la position de la ProgressBar:
		SendMessage(progress,PBM_SETPOS,i/30000,0);
		// Autoriser le traitement des messages de notre boîte de progression:
		TraiterMessages(hdlg);
		// Sortir de la boucle si l'attibut USERDATA du bouton Annuler est à 1 suite à un clic ou un appui sur Echap ou Entrée:
		if (GetWindowLong(cancel,GWL_USERDATA))break;
	}
	// Retourner la dernière valeur de i:
	return i;
}

// Fonction de conversion de la position de la ProgressBar en chaîne contenant un pourcentage:
int utoa(UINT nombre, char* buf, char car)
{
	// retour immédiat si buf est null:
	if(!buf) return 0;
	char tampon[12];
	char* ptampon = tampon;
	char* pbuf=buf;
	UINT i;
	// Boucle de conversion en caractères:
	do
	{
		i=nombre%10;
		nombre=nombre/10;

  • ptampon=i+'0';
ptampon++; }while(nombre); // Boucle de transfert des caractères, à l'envers, vers le buffer destination: while (ptampon > tampon) { ptampon--;
  • pbuf=*ptampon;
pbuf++; } // Ajouter le symbole donné en 3è paramètre de la fonction (ici %):
  • pbuf = car;
// Mettre le 0 final de la chaîne: pbuf++;
  • pbuf = 0;
// Retourner la longueur de la chaîne: return pbuf-buf; } // Procédure de sous-classement de notre ProgressBar: LRESULT CALLBACK ProgressBarProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_PAINT) { PAINTSTRUCT ps; RECT clientrect, leftrect, rightrect, textrect; char buffer[12]; // Obtenir le HDC de la ProgressBar: HDC hdc = BeginPaint(hwnd, &ps) ; // Récupérer les données de la ProgressBar: PRODATA* pdata=(PRODATA*)GetWindowLong(hwnd,0); // Obtenir le rectangle de la zone cliente de la ProgressBar: GetClientRect(hwnd, &clientrect); // Obtenir la position courante de la barre: UINT position = pdata->iPos; // Calculer et définir les rectangles gauche et droit correspondant respectivement à la partie pleine et vide de la ProgressBar: leftrect=rightrect=clientrect; leftrect.right =MulDiv(clientrect.right,position,100); rightrect.left =leftrect.right; // Former le texte à partir de la position courante et obtenir sa longueur: int len=utoa(position,buffer,'%'); // Sélectionner la police de la barre: HFONT oldfont=(HFONT)SelectObject(hdc,pdata->hfont); // Calculer les dimensions du texte: SetRect(&textrect,0,0,0,0); DrawText(hdc,buffer,-1,&textrect,DT_CALCRECT); // Calculer la position horizontale et verticale du texte: int xpos=(clientrect.right/2)-(textrect.right/2); int ypos=(clientrect.bottom/2)-(textrect.bottom/2); // Récupérer la couleur de la barre: COLORREF couleurbarre = pdata->barcolor; // Récupérer la couleur du fond de la ProgressBar: COLORREF couleurfond = pdata->bkcolor; // Mettre les couleurs par défaut si elles ne sont pas définies: if(couleurbarre==CLR_DEFAULT )couleurbarre=GetSysColor(COLOR_HIGHLIGHT); if(couleurfond==CLR_DEFAULT )couleurfond=GetSysColor(COLOR_3DFACE); // Définir la couleur de la partie pleine de la barre: SetBkColor(hdc,couleurbarre); // Définir la couleur du texte de la partie pleine de la barre SetTextColor(hdc,couleurfond); // Remplir la partie pleine et afficher texte: ExtTextOut(hdc,xpos,ypos,ETO_OPAQUE | ETO_CLIPPED,&leftrect,buffer,len,0); // Définir la couleur de la partie vide de la barre: SetBkColor(hdc,couleurfond); // Définir la couleur du texte de la partie vide de la barre SetTextColor(hdc,couleurbarre); // Remplir la partie vide et afficher texte: ExtTextOut(hdc,xpos,ypos,ETO_OPAQUE | ETO_CLIPPED,&rightrect,buffer,len,0); // Remettre la police originale: SelectObject(hdc,oldfont); // Libérer le HDC de la ProgressBar: EndPaint (hwnd, &ps) ; return 0; } // Appeler la procédure originale de la ProgressBar: return CallWindowProc((WNDPROC)OldProgressBarProc, hwnd, message, wParam, lParam); } // Procédure de la boite de dialogue de progression: BOOL CALLBACK ProgressDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hGroupbox, hAnnuler, hProgress; static HFONT police; static HBRUSH couleurfond; switch(message) { case WM_INITDIALOG: // Centrer la boite par rapport à la boite mère: : RECT rect,rect2; POINT pt; GetClientRect(GetParent(hwnd),&rect); GetWindowRect(hwnd,&rect2); pt.x=(rect.right/2)-((rect2.right-rect2.left)/2); pt.y=(rect.bottom/2)-((rect2.bottom-rect2.top)/2); ClientToScreen(GetParent(hwnd),&pt); SetWindowPos(hwnd,0,pt.x,pt.y,0,0,SWP_NOZORDER | SWP_NOSIZE); // Créer le HBRUSH de la couleur du fond: couleurfond=CreateSolidBrush(RGB(255,190,120)); // Créer un GroupBox et le bouton "Annuler": hGroupbox=CreateWindowEx(0,"button"," Progression ",WS_CHILD | WS_VISIBLE | BS_GROUPBOX | BS_CENTER ,10,4,340,94,hwnd,0,0,0); hAnnuler=CreateWindowEx(0,"button","Annuler",WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON ,140,66,80,24,hwnd,(HMENU)IDCANCEL,0,0); // Créer et sous-classer notre ProgressBar: hProgress=CreateWindowEx(0,PROGRESS_CLASS,"",WS_VISIBLE | WS_CHILD | PBS_SMOOTH ,20,30,320,28,hwnd,0,0,0); OldProgressBarProc=(WNDPROC)SetWindowLong(hProgress,GWL_WNDPROC,(LONG)ProgressBarProc); // Définir les couleurs de la ProgressBar: SendMessage(hProgress,PBM_SETBARCOLOR,0,(LPARAM)RGB(180,80,0)); SendMessage(hProgress,PBM_SETBKCOLOR,0,(LPARAM)RGB(255,230,200)); // Créer et appliquer une police à notre ProgressBar: police=CreateFont(24,0,0,0,0,0,0,0,0,0,0,0,0,"Arial"); SendMessage(hProgress,WM_SETFONT,(WPARAM)police,0); // Mettre le focus sur le bouton "Annuler": SetFocus(hAnnuler); // Simuler appui sur ALT Gauche pour interaction avec le clavier au lancement. keybd_event(VK_MENU,0,0,0); keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0); return 0; case WM_CTLCOLORSTATIC: // Assurer la transparence de notre GroupBox: LOGBRUSH lb; GetObject(couleurfond,sizeof(LOGBRUSH),&lb); SetBkColor((HDC)wParam,lb.lbColor); return (BOOL) couleurfond; case WM_CTLCOLORDLG: // Appliquer notre couleur de fond à la boîte de dialogue: return (BOOL) couleurfond; case WM_WINDOWPOSCHANGED: WINDOWPOS* wp; wp=(WINDOWPOS*)lParam; // S'assurer que notre boîte de progression vient d'être affichée: if(wp->flags & SWP_SHOWWINDOW) { // Appeler la fonction exécutant la tâche dont on veut voir la progression: int nbr=ProgressFunction(hwnd,hProgress,hAnnuler); // Détruire les objets GDI: DeleteObject(couleurfond); DeleteObject(police); // Fermer la boîte de progression: EndDialog(hwnd,nbr); } break; case WM_COMMAND: // Mettre l'attribut USERDATA du bouton "Annuler" à 1 s'il est cliqué ou si la touche Echap ou Entrée a été appuyée: if(LOWORD(wParam)==IDCANCEL)SetWindowLong(hAnnuler,GWL_USERDATA,1); break; case WM_CLOSE: // Empêcher notre boîte de se fermer suite à un ALT-F4 en retournant 1 au lieu de 0: return 1; default: break; } return 0; } // Procédure de la boîte principale: BOOL CALLBACK MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hIntro, hQuitter, hLancer; switch(message) { case WM_INITDIALOG: // Définir le titre de la boîte: SetWindowText(hwnd,"Progress Dialog"); // Créer le Static et les Boutons: hIntro=CreateWindowEx(0,"static","Cliquez sur le bouton \"Lancer\" pour démarrer la boîte de progression.", WS_CHILD | WS_VISIBLE | SS_CENTER,50,80,412,50,hwnd,0,0,0); hLancer=CreateWindowEx(0,"button","Lancer", WS_CHILD | WS_VISIBLE,312,200,80,24,hwnd,0,0,0); hQuitter=CreateWindowEx(0,"button","Quitter", WS_CHILD | WS_VISIBLE,120,200,80,24,hwnd,0,0,0); return 0; case WM_CTLCOLORSTATIC: // Assurer la transparence des Statics: SetBkMode((HDC)wParam,TRANSPARENT); return (BOOL) GetStockObject(NULL_BRUSH); case WM_ERASEBKGND: // Remplir le fond de la boîte en vert: RECT rc; GetClientRect(hwnd,&rc); SetBkColor((HDC)wParam,RGB(160,230,160)); ExtTextOut((HDC)wParam,0,0,ETO_OPAQUE,&rc,0,0,0); return 1; case WM_COMMAND: if( (HWND)lParam == hLancer ) { // Allouer de la mémoire pour notre dialog template: LPDLGTEMPLATE lpdt = ( LPDLGTEMPLATE) GlobalAlloc(GPTR, 512); // Définir les styles de la boite de dialogue fille: lpdt->style = WS_POPUP | WS_DLGFRAME ; lpdt->dwExtendedStyle=0; // Obtenir les unités d'affichage des boites de dialogues: LONG baseunits=GetDialogBaseUnits(); // Définir les dimensions de la boite fille: lpdt->cx = MulDiv(360, 4,LOWORD(baseunits)); lpdt->cy = MulDiv(110, 8, HIWORD(baseunits));; // Lancer la boite de dialogue fille (de progression): DialogBoxIndirectParam(GetModuleHandle(0),lpdt,hwnd,(DLGPROC)ProgressDlgProc,(LPARAM)0); // Libérer la mémoire allouée: GlobalFree((HGLOBAL)lpdt); return 0; } if((HWND)lParam==hQuitter) { // Envoyer le message de fermeture de notre boîte de dialogue: SendMessage(hwnd,WM_CLOSE,0,0); } return 0; case WM_CLOSE: // Fermer la boîte de dialogue: EndDialog(hwnd,0); return 0; default: break; } return 0; } // Fonction d'entrée du programme: int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int show) { // Charger La DLL pour les 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 = DS_CENTER | WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX |WS_CAPTION ; lpdt->dwExtendedStyle=0; // Obtenir les unités d'affichage des boites de dialogues: LONG baseunits=GetDialogBaseUnits(); // Définir les dimensions de la boite: lpdt->cx = MulDiv(512, 4,LOWORD(baseunits)); lpdt->cy = MulDiv(240, 8, HIWORD(baseunits));; //Lancer la boite de dialogue DialogBoxIndirectParam(GetModuleHandle(0),lpdt,0,(DLGPROC)MainDlgProc,(LPARAM)0); //Libération de la mémoire allouée: GlobalFree((HGLOBAL)lpdt); // Libérer "comctl32.dll"; FreeLibrary(hCmLib); return 0; }

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.