La programmation de l'interface utilisateur
Date de publication : 22 mai 2008. Date de mise à jour : 23 mars 2009.
I-C. Les contrôles communs
I-C-1. Introduction
I-C-2. Initialisation
I-C-3. Les versions
I-C-4. Choix des bibliothèques
I-C-5. Exemple : Le contrôle ListView
I-C. Les contrôles communs
I-C-1. Introduction
Les contrôles communs sont les contrôles spécifiques d'une version donnée de Windows
(barre d'outils, barre d'état, barre de progression, glissière, etc.). Ils sont implémentés dans le
fichier comctl32.dll. Pour les utiliser, il faut se lier avec comctl32.lib et inclure
commctrl.h.
I-C-2. Initialisation
Il ne nous est plus à rappeler que la création d'une fenêtre se fait toujours à partir d'une classe
de fenêtre existante. Lorsqu'un programme se lie avec user32.dll, le système enregistre les
classes de fenêtre standard (les contrôles standard) avant même que la fonction WinMain ne
soit appelée. C'est pour cette raison que nous n'avons jamais eu à enregistrer ces classes
nous-mêmes. Ce n'est pas le cas avec les contrôles communs et l'application doit donc
initialiser ces contrôles elle-même avant de pouvoir les utiliser. Cela se fait tout simplement
en appelant la fonction InitCommonControlsEx (qui est venue remplacer la fonction
InitCommonControls, devenue obsolète).
void InitCommonControls (VOID);
BOOL InitCommonControlsEx (LPINITCOMMONCONTROLSEX lpInitCommCtrls);
|
Le type INITCOMMONCONTROLSEX est tout simplement une structure comportant
deux champs : dwSize et dwICC. Le premier doit indiquer la taille en octets de la structure et
le dernier la ou les classes de fenêtre qu'on veut enregistrer (nous y reviendrons là-dessus un
peu plus bas). La fonction InitCommonControls ne permet pas de spécifier individuellement
les contrôles à initialiser.
I-C-3. Les versions
Le fichier comctl32.dll n'est pas forcément le même sur tous les
ordinateurs car sa version dépend du système d'exploitation et/ou logiciels installés (en
particulier Internet Explorer). Il n'est pas exclu non plus que plusieurs versions soient
simultanément présentes sur un même ordinateur (évidemment séparées dans des répertoires
différents). Par exemple, Windows XP et Vista sont accompagnés des versions 5 (en
l'occurrence 5.82) et 6 alors que Windows 2000 n'était accompagné que de la version 5
(5.81). Si vous avez Windows 95, vous avez la version 4.0 mais si vous installez ensuite
Internet Explorer 3.x, vous bénéficierez de la version 4.70. Chaque nouvelle version est un sur
ensemble des versions antérieures.
La version 6 intègre non seulement les contrôles supplémentaires mais aussi les contrôles
standard, cependant ces derniers ne sont pas les mêmes que ceux de user32 (mais ils
continuent à envoyer leurs notifications via le message WM_COMMAND, et non
WM_NOTIFY comme le reste des contrôles communs). En effet, Windows XP est venu avec
une nouvelle interface utilisateur personnalisable, permettant à l'utilisateur de choisir luimême
le style visuel à appliquer. Les contrôles de la version 6 utilisent ce style alors que ceux
de user32 et des versions antérieures à 6 sont basés sur le style classique. Ainsi, si vous voulez
utiliser les styles visuels dans vos applications, vous devez utiliser les contrôles de la version
6 ou plus récente. Nous en reparlerons plus tard.
En outre, il faut également savoir que la fonction InitCommonControlsEx n'a été introduite
que depuis la version 4.70 (livrée avec IE 3.x). Selon la version de votre fichier d'en-tête, il se
peut donc que cette fonction ne soit déclarée que si vous définissez explicitement la macro
_WIN32_IE à 0x0300 ou supérieur avant d'inclure commctrl.h (sous Visual Studio .NET,
elle vaut par défaut 0x0500).
I-C-4. Choix des bibliothèques
Comme nous venons tout juste de le dire, la version 6.0 de la Common Controls Library
est la première à avoir supporté les styles visuels. Or les applications compilées avec Visual
Studio .NET et 2005 utilisent par défaut les contrôles de user32.dll et de comctl32.dll version
5 pour être compatibles avec les anciennes versions de Windows. Pour utiliser la version 6 (ou une autre ...),
c'est à l'application de le spécifier. En effet, quand on se lie
avec comctl32.lib, il n'est spécifié nulle part quelle version de comctl32.dll veut-on utiliser.
C'est là qu'interviennent les fichiers MANIFEST.
Un fichier manifest est un fichier texte, utilisant une grammaire XML, permettant dans Windows XP et plus récents (en effet il est ignoré par les versions antérieures) de
spécifier entre autres les composants (les « dépendances ») requis par une application pour
fonctionner. Le nom d'un fichier manifest doit être le nom de l'application suivi de
l'extension .manifest (par exemple hello.exe.manifest). Par exemple, pour utiliser
comctl32.dll version 6.0, il faut créer le manifest suivant :
<? xml version='1.0' encoding='UTF-8' standalone='yes'? >
< assembly xmlns = ' urn:schemas-microsoft-com:asm.v1 ' manifestVersion = ' 1.0 ' >
< dependency >
< dependentAssembly >
< assemblyIdentity type = ' Win32 '
name = ' Microsoft.Windows.Common-Controls '
version = ' 6.0.0.0 '
processorArchitecture = ' x86 '
publicKeyToken = ' 6595b64144ccf1df '
/ >
< / dependentAssembly >
< / dependency >
< / assembly >
|
Les paramètres sont plutôt parlants.
De plus, si vous utilisez par exemple Visual Studio 2005, vos applications utiliseront la
version 8.0 du C Run-Time Library (msvcr80.dll), sauf bien sûr si vous utilisez la version
statique. Dans ce cas, il faut également le spécifier dans le manifest. Donc si vous utilisez
msvcr80.dll et comctl32.dll version 6.0, vous devez créez le manifest suivant :
<? xml version='1.0' encoding='UTF-8' standalone='yes'? >
< assembly xmlns = ' urn:schemas-microsoft-com:asm.v1 ' manifestVersion = ' 1.0 ' >
< dependency >
< dependentAssembly >
< assemblyIdentity type = ' win32 '
name = ' Microsoft.VC80.CRT '
version = ' 8.0.50608.0 '
processorArchitecture = ' x86 '
publicKeyToken = ' 1fc8b3b9a1e18e3b '
/ >
< / dependentAssembly >
< / dependency >
< dependency >
< dependentAssembly >
< assemblyIdentity type = ' Win32 '
name = ' Microsoft.Windows.Common-Controls '
version = ' 6.0.0.0 '
processorArchitecture = ' x86 '
publicKeyToken = ' 6595b64144ccf1df '
/ >
< / dependentAssembly >
< / dependency >
< / assembly >
|
En Debug vous mettrez plutôt VC80.DebugCRT à la place de VC80.CRT.
Il existe deux manières différentes d'utiliser un manifest (dans tous les cas, n'oubliez pas que le manifest n'est lu qu'à l'exécution) :
- En le plaçant dans le même répertoire que l'exécutable
- En l'embarquant à l'intérieur même de l'exécutable, autrement dit le mettre en ressource.
Lorsqu'on place un manifest dans le répertoire de l'exécutable, celui qui se trouve en
ressource sera tout simplement ignoré par Windows. Sachez également que lorsqu'un manifest
est utilisé, il n'est plus nécessaire d'appeler la fonction InitCommonControlsEx().
En fait, Visual Studio .NET et plus récents génèrent automatiquement un manifest, ne
serait-ce que pour spécifier la version du CRT utilisée. Le fichier final, créé à partir d'un
fichier "intermédiaire" nommé votreapp.exe.intermediate.manifest et des éventuelles options
de génération que vous avez spécifiées, nommé votreapp.exe.embed.manifest, est ensuite embarqué dans
l'exécutable de sorte que ce dernier soit plus ou moins autonome. Si vous voulez néanmoins
l'utiliser en tant que fichier à part, accompagnant votre exécutable, vous n'avez qu'à
renommer le fichier votreapp.exe.embed.manifest en votreapp.exe.manifest ou, tout
simplement (et plus proprement !), de dire à Visual Studio de ne pas embarquer le manifest.
Visual Studio dispose d'une interface simple et intuitive permettant de
contrôler très simplement et efficacement la génération de manifest. Vous pouvez par exemple spécifier des fichiers à fusionner avec le fichier par défaut utilisé pour générer
le manifest grâce à l'option Additional manifest Files du gestionnaire de fichiers manifest (donc
dans vos fichiers, vous ne devez plus lister la CRT parmi les dépendances requis car elle est déjà listée
dans le fichier par défaut (votreapp.exe.intermediate.manifest)).
Ceci étant, voyons maintenant comment embarquer un manifest à l'intérieur de l'exécutable sans utiliser l'interface de Visual Studio.
Et bien, pour mettre un manifest dans la section ressources de votre fichier exécutable, il
suffit de créer un script de ressource contenant la ligne suivante (inclure windows.h) :
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST " hello.exe.manifest "
|
I-C-5. Exemple : Le contrôle ListView
Le contrôle ListView (WC_LISTVIEW) est un contrôle permettant, comme son nom l'indique, d'afficher une liste.
La zone d'affichage des dossiers et des fichiers dans l'explorateur Windows, la zone d'affichage
des processus en cours d'exécution dans le gestionnaire des tâches, etc. sont par exemple
des contrôles ListView.
Le contrôle ListView peut afficher les éléments de la liste de 4 façons différentes dont voici les plus utilisées :
- Affichage en icônes (style LVS_ICON) : Les éléments sont affichés avec des grandes icônes avec leur en dessous de l'icône.
- Affichage en liste (style LVS_LIST) : Les éléments sont affichés en liste, organisée en colonnes, avec des petites icônes avec leur nom à droite de l'icône.
- Affichage en liste détaillée (style LVS_REPORT) : Chaque élément occupe une ligne et est chaque ligne comporte une ou plusieurs colonnes. La colonne la plus à gauche affiche l'icône et nom de l'élément (placé à droite de l'icône).
Les autres colonnes servent à afficher d'autres informations. Chaque colonne est pourvu d'un en-tête qui sert à afficher son nom sauf si le style LVS_NOCOLUMNHEADER a été spécifié.
Depuis Windows XP (plus précisément comctl32.dll version 6.0), il est également possible d'utiliser d'autres styles d'affichage comme l'affichage en mosaïques (messages en jeu : LVM_SETVIEW, LVM_SETTILEVIEWINFO et LVM_SETTILEINFO)
ou par groupe (messages en jeu : LVM_ENABLEGROUP, LVM_SETGROUPINFO et LVM_SETGROUPMETRICS) par exemple.
Comme la plupart des contrôles communs, le contrôle ListView envoie ses notifications à la fenêtre parent via le message WM_NOTIFY avec dans wParam son identifiant (ID), comme le fait n'importe quel contrôle commun, et dans lParam un pointeur vers une
structure dérivée de la structure de base NMHDR. En fait, cette dérivation (spécialisation) de la structure NMHDR est utilisée par la quasi-totalité des contrôles communs. Il faut lire la documentation du contrôle pour savoir quelle structure est utilisée
dans quelles circonstances.
L'exemple suivant a pour but de vous aider à comprendre le fonctionnement des contrôles communs à travers la création et l'utilisation d'un contrôle ListView.
# include <windows.h>
# include <commctrl.h>
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void OnCreate (HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void lvInitColumns (HWND hwndLV);
void lvInsertItems (HWND hwndLV);
void OnNotify (HWND hwnd, LPNMHDR lpnmhdr);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
INITCOMMONCONTROLSEX icc;
WNDCLASS wc;
HWND hWnd;
MSG msg;
icc.dwSize = sizeof (icc);
icc.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx (& icc);
wc.cbClsExtra = 0 ;
wc.cbWndExtra = 0 ;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 );
wc.hCursor = LoadCursor (NULL , IDC_ARROW);
wc.hIcon = LoadIcon (NULL , IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = " Classe 1 " ;
wc.lpszMenuName = NULL ;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass (& wc);
hWnd = CreateWindow ( " Classe 1 " , " Contrôle ListView " ,
WS_POPUP | WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
300 , 150 , 400 , 200 ,
NULL , NULL , hInstance, NULL
);
ShowWindow (hWnd, nCmdShow);
while (GetMessage (& msg, NULL , 0 , 0 ))
{
TranslateMessage (& msg);
DispatchMessage (& msg);
}
return (int )msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
OnCreate (hwnd, (LPCREATESTRUCT)lParam);
break ;
case WM_NOTIFY:
OnNotify (hwnd, (LPNMHDR)lParam);
break ;
case WM_DESTROY:
PostQuitMessage (0 );
break ;
default :
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0L ;
}
void OnCreate (HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
HWND hwndLV;
RECT r;
GetClientRect (hwnd, & r);
hwndLV = CreateWindow ( WC_LISTVIEW, " " , WS_CHILD | WS_VISIBLE | LVS_REPORT,
r.left, r.top, r.right, r.bottom,
hwnd, (HMENU)1 , lpCreateStruct- > hInstance, NULL
);
lvInitColumns (hwndLV);
lvInsertItems (hwndLV);
}
void lvInitColumns (HWND hwndLV)
{
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT | LVCF_FMT;
lvc.fmt = LVCFMT_LEFT;
lvc.pszText = " Langage " ;
ListView_InsertColumn (hwndLV, 0 , & lvc);
ListView_SetColumnWidth (hwndLV, 0 , 100 );
lvc.pszText = " Créateur " ;
ListView_InsertColumn (hwndLV, 1 , & lvc);
ListView_SetColumnWidth (hwndLV, 1 , 200 );
}
void lvInsertItems (HWND hwndLV)
{
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = 0 ;
lvi.iSubItem = 0 ;
lvi.pszText = " C " ;
ListView_InsertItem (hwndLV, & lvi);
lvi.iSubItem = 1 ;
lvi.pszText = " Brian Kernighan & Denis Ritchie " ;
ListView_SetItem (hwndLV, & lvi);
}
void OnNotify (HWND hwnd, LPNMHDR lpnmhdr)
{
if (lpnmhdr- > idFrom = = 1 )
{
HWND hwndLV = lpnmhdr- > hwndFrom;
if (lpnmhdr- > code = = NM_DBLCLK)
{
LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lpnmhdr;
LVHITTESTINFO lvhti;
int ret;
lvhti.pt = lpnmia- > ptAction;
ret = ListView_SubItemHitTest (hwndLV, & lvhti);
if (ret ! = - 1 )
{
LVITEM lvi;
char lpBuffer[256 ];
lvi.mask = LVIF_TEXT;
lvi.pszText = lpBuffer;
lvi.cchTextMax = sizeof (lpBuffer);
lvi.iItem = lvhti.iItem;
lvi.iSubItem = lvhti.iSubItem;
ListView_GetItem (hwndLV, & lvi);
MessageBox (hwnd, lvi.pszText, " " , MB_OK);
}
}
}
}
|
Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
2008 Melem. Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et
jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.