Utilisation de boîtes de dialogue

Vous utilisez des boîtes de dialogue pour afficher des informations et demander une entrée de l’utilisateur. Votre application charge et initialise la boîte de dialogue, traite l’entrée de l’utilisateur et la détruit lorsque l’utilisateur termine la tâche. Le processus de gestion des boîtes de dialogue varie selon que la boîte de dialogue est modale ou sans mode. Une boîte de dialogue modale nécessite que l’utilisateur ferme la boîte de dialogue avant d’activer une autre fenêtre dans l’application. Toutefois, l’utilisateur peut activer des fenêtres dans différentes applications. Une boîte de dialogue sans mode ne nécessite pas de réponse immédiate de la part de l’utilisateur. Elle est similaire à une fenêtre main contenant des contrôles.

Les sections suivantes expliquent comment utiliser les deux types de boîtes de dialogue.

Affichage d’une boîte de message

La forme la plus simple de la boîte de dialogue modale est la boîte de message. La plupart des applications utilisent des boîtes de message pour avertir l’utilisateur des erreurs et demander des instructions sur la façon de procéder après qu’une erreur s’est produite. Vous créez une boîte de message à l’aide de la fonction MessageBox ou MessageBoxEx , en spécifiant le message ainsi que le nombre et le type de boutons à afficher. Le système crée une boîte de dialogue modale, en fournissant son propre modèle et sa propre procédure de boîte de dialogue. Une fois que l’utilisateur a fermé la boîte de message, MessageBox ou MessageBoxEx retourne une valeur identifiant le bouton choisi par l’utilisateur pour fermer la boîte de message.

Dans l’exemple suivant, l’application affiche une boîte de message qui invite l’utilisateur à entrer une action après qu’une condition d’erreur s’est produite. La boîte de message affiche le message qui décrit la condition d’erreur et comment la résoudre. Le style MB_YESNO indique à MessageBox de fournir deux boutons avec lesquels l’utilisateur peut choisir comment procéder :

int DisplayConfirmSaveAsMessageBox()
{
    int msgboxID = MessageBox(
        NULL,
        L"temp.txt already exists.\nDo you want to replace it?",
        L"Confirm Save As",
        MB_ICONEXCLAMATION | MB_YESNO
    );

    if (msgboxID == IDYES)
    {
        // TODO: add code
    }

    return msgboxID;    
}

L’image suivante montre la sortie de l’exemple de code précédent :

boîte de message

Création d’une boîte de dialogue modale

Vous créez une boîte de dialogue modale à l’aide de la fonction DialogBox . Vous devez spécifier l’identificateur ou le nom d’une ressource de modèle de boîte de dialogue et un pointeur vers la procédure de boîte de dialogue. La fonction DialogBox charge le modèle, affiche la boîte de dialogue et traite toutes les entrées utilisateur jusqu’à ce que l’utilisateur ferme la boîte de dialogue.

Dans l’exemple suivant, l’application affiche une boîte de dialogue modale lorsque l’utilisateur clique sur Supprimer l’élément dans le menu d’une application. La boîte de dialogue contient un contrôle d’édition (dans lequel l’utilisateur entre le nom d’un élément) et les boutons OK et Annuler . Les identificateurs de contrôle pour ces contrôles sont ID_ITEMNAME, IDOK et IDCANCEL, respectivement.

La première partie de l’exemple se compose des instructions qui créent la boîte de dialogue modale. Ces instructions, dans la procédure de fenêtre de la fenêtre main de l’application, créent la boîte de dialogue lorsque le système reçoit un message WM_COMMAND ayant l’identificateur de menu IDM_DELETEITEM. La deuxième partie de l’exemple est la procédure de boîte de dialogue, qui récupère le contenu du contrôle d’édition et ferme la boîte de dialogue lors de la réception d’un message WM_COMMAND .

Les instructions suivantes créent la boîte de dialogue modale. Le modèle de boîte de dialogue est une ressource dans le fichier exécutable de l’application et a l’identificateur de ressource DLG_DELETEITEM.

case WM_COMMAND: 
    switch (LOWORD(wParam)) 
    { 
        case IDM_DELETEITEM: 
            if (DialogBox(hinst, 
                          MAKEINTRESOURCE(DLG_DELETEITEM), 
                          hwnd, 
                          (DLGPROC)DeleteItemProc)==IDOK) 
            {
                // Complete the command; szItemName contains the 
                // name of the item to delete. 
            }

            else 
            {
                // Cancel the command. 
            } 
            break; 
    } 
    return 0L; 

Dans cet exemple, l’application spécifie sa fenêtre main comme fenêtre propriétaire de la boîte de dialogue. Lorsque le système affiche initialement la boîte de dialogue, sa position est relative à l’angle supérieur gauche de la zone cliente de la fenêtre propriétaire. L’application utilise la valeur de retour de DialogBox pour déterminer s’il faut poursuivre l’opération ou l’annuler. Les instructions suivantes définissent la procédure de boîte de dialogue.

char szItemName[80]; // receives name of item to delete. 
 
BOOL CALLBACK DeleteItemProc(HWND hwndDlg, 
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam) 
{ 
    switch (message) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    if (!GetDlgItemText(hwndDlg, ID_ITEMNAME, szItemName, 80)) 
                         *szItemName=0; 
 
                    // Fall through. 
 
                case IDCANCEL: 
                    EndDialog(hwndDlg, wParam); 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 

Dans cet exemple, la procédure utilise GetDlgItemText pour récupérer le texte actuel à partir du contrôle d’édition identifié par ID_ITEMNAME. La procédure appelle ensuite la fonction EndDialog pour définir la valeur de retour de la boîte de dialogue sur IDOK ou IDCANCEL, en fonction du message reçu, et pour commencer le processus de fermeture de la boîte de dialogue. Les identificateurs IDOK et IDCANCEL correspondent aux boutons OK et Annuler . Une fois que la procédure a appelé EndDialog, le système envoie des messages supplémentaires à la procédure pour détruire la boîte de dialogue et retourne la valeur de retour de la boîte de dialogue à la fonction qui a créé la boîte de dialogue.

Création d’une boîte de dialogue sans mode

Vous créez une boîte de dialogue sans mode à l’aide de la fonction CreateDialog , en spécifiant l’identificateur ou le nom d’une ressource de modèle de boîte de dialogue et un pointeur vers la procédure de boîte de dialogue. CreateDialog charge le modèle, crée la boîte de dialogue et l’affiche éventuellement. Votre application est chargée de récupérer et de distribuer les messages d’entrée utilisateur à la procédure de boîte de dialogue.

Dans l’exemple suivant, l’application affiche une boîte de dialogue sans mode (si elle n’est pas déjà affichée) lorsque l’utilisateur clique sur Accéder à dans un menu d’application. La boîte de dialogue contient un contrôle d’édition, une zone case activée et les boutons OK et Annuler. Le modèle de boîte de dialogue est une ressource dans le fichier exécutable de l’application et a l’identificateur de ressource DLG_GOTO. L’utilisateur entre un numéro de ligne dans le contrôle d’édition et coche la case case activée pour spécifier que le numéro de ligne est relatif à la ligne active. Les identificateurs de contrôle sont ID_LINE, ID_ABSREL, IDOK et IDCANCEL.

Les instructions de la première partie de l’exemple créent la boîte de dialogue sans mode. Ces instructions, dans la procédure de fenêtre de la fenêtre main de l’application, créent la boîte de dialogue lorsque la procédure de fenêtre reçoit un message WM_COMMAND ayant l’identificateur de menu IDM_GOTO, mais uniquement si la variable globale ne contient pas déjà de handle valide. La deuxième partie de l’exemple est la boucle de message main de l’application. La boucle inclut la fonction IsDialogMessage pour s’assurer que l’utilisateur peut utiliser l’interface clavier de la boîte de dialogue dans cette boîte de dialogue sans mode. La troisième partie de l’exemple est la procédure de boîte de dialogue. La procédure récupère le contenu du contrôle d’édition et case activée zone lorsque l’utilisateur clique sur le bouton OK. La procédure détruit la boîte de dialogue lorsque l’utilisateur clique sur le bouton Annuler .

HWND hwndGoto = NULL;  // Window handle of dialog box 
                
...

case WM_COMMAND: 
    switch (LOWORD(wParam)) 
    { 
        case IDM_GOTO: 
            if (!IsWindow(hwndGoto)) 
            { 
                hwndGoto = CreateDialog(hinst, 
                                        MAKEINTRESOURCE(DLG_GOTO), 
                                        hwnd, 
                                        (DLGPROC)GoToProc); 
                ShowWindow(hwndGoto, SW_SHOW); 
            } 
            break; 
    } 
    return 0L; 

Dans les instructions précédentes, CreateDialog est appelé uniquement si hwndGoto ne contient pas de handle de fenêtre valide. Cela garantit que l’application n’affiche pas deux boîtes de dialogue en même temps. Pour prendre en charge cette méthode de vérification, la procédure de dialogue doit définir sur NULL lorsqu’elle détruit la boîte de dialogue.

La boucle de message d’une application se compose des instructions suivantes.

BOOL bRet;

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) 
{ 
    if (bRet == -1)
    {
        // Handle the error and possibly exit
    }
    else if (!IsWindow(hwndGoto) || !IsDialogMessage(hwndGoto, &msg)) 
    { 
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    } 
} 

La boucle vérifie la validité du handle de fenêtre dans la boîte de dialogue et appelle uniquement la fonction IsDialogMessage si le handle est valide. IsDialogMessage traite uniquement le message s’il appartient à la boîte de dialogue. Sinon, elle retourne FALSE et la boucle distribue le message à la fenêtre appropriée.

Les instructions suivantes définissent la procédure de boîte de dialogue.

int iLine;             // Receives line number.
BOOL fRelative;        // Receives check box status. 
 
BOOL CALLBACK GoToProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    BOOL fError; 
 
    switch (message) 
    { 
        case WM_INITDIALOG: 
            CheckDlgButton(hwndDlg, ID_ABSREL, fRelative); 
            return TRUE; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    fRelative = IsDlgButtonChecked(hwndDlg, ID_ABSREL); 
                    iLine = GetDlgItemInt(hwndDlg, ID_LINE, &fError, fRelative); 
                    if (fError) 
                    { 
                        MessageBox(hwndDlg, SZINVALIDNUMBER, SZGOTOERR, MB_OK); 
                        SendDlgItemMessage(hwndDlg, ID_LINE, EM_SETSEL, 0, -1L); 
                    } 
                    else 

                    // Notify the owner window to carry out the task. 
 
                    return TRUE; 
 
                case IDCANCEL: 
                    DestroyWindow(hwndDlg); 
                    hwndGoto = NULL; 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 

Dans les instructions précédentes, la procédure traite les messages WM_INITDIALOG et WM_COMMAND . Pendant WM_INITDIALOG traitement, la procédure initialise la zone case activée en passant la valeur actuelle de la variable globale à CheckDlgButton. La procédure retourne ensuite TRUE pour indiquer au système de définir le focus d’entrée par défaut.

Pendant WM_COMMAND traitement, la procédure ferme la boîte de dialogue uniquement si l’utilisateur clique sur le bouton Annuler , c’est-à-dire si le bouton a l’identificateur IDCANCEL. La procédure doit appeler DestroyWindow pour fermer une boîte de dialogue sans mode. Notez que la procédure définit également la variable sur NULL pour s’assurer que les autres instructions qui dépendent de cette variable fonctionnent correctement.

Si l’utilisateur clique sur le bouton OK, la procédure récupère l’état actuel de la zone case activée et l’affecte à la variable fRelative. Il utilise ensuite la variable pour récupérer le numéro de ligne du contrôle d’édition. GetDlgItemInt traduit le texte du contrôle d’édition en entier. La valeur de fRelative détermine si la fonction interprète le nombre comme une valeur signée ou non signée. Si le texte du contrôle d’édition n’est pas un nombre valide, GetDlgItemInt définit la valeur de la variable fError sur une valeur différente de zéro. La procédure vérifie cette valeur pour déterminer s’il faut afficher un message d’erreur ou effectuer la tâche. En cas d’erreur, la procédure de la boîte de dialogue envoie un message au contrôle d’édition, en lui indiquant de sélectionner le texte du contrôle afin que l’utilisateur puisse facilement le remplacer. Si GetDlgItemInt ne retourne pas d’erreur, la procédure peut exécuter la tâche demandée elle-même ou envoyer un message à la fenêtre propriétaire, en lui indiquant d’effectuer l’opération.

Initialisation d’une boîte de dialogue

Vous initialisez la boîte de dialogue et son contenu lors du traitement du message WM_INITDIALOG . La tâche la plus courante consiste à initialiser les contrôles pour refléter les paramètres de boîte de dialogue actuels. Une autre tâche courante consiste à centrer une boîte de dialogue à l’écran ou dans sa fenêtre propriétaire. Une tâche utile pour certaines boîtes de dialogue consiste à définir le focus d’entrée sur un contrôle spécifié plutôt que d’accepter le focus d’entrée par défaut.

Dans l’exemple suivant, la procédure de boîte de dialogue centre la boîte de dialogue et définit le focus d’entrée lors du traitement du message WM_INITDIALOG . Pour centrer la boîte de dialogue, la procédure récupère les rectangles de fenêtre pour la boîte de dialogue et la fenêtre propriétaire et calcule une nouvelle position pour la boîte de dialogue. Pour définir le focus d’entrée, la procédure vérifie le paramètre wParam pour déterminer l’identificateur du focus d’entrée par défaut.

HWND hwndOwner; 
RECT rc, rcDlg, rcOwner; 

....
 
case WM_INITDIALOG: 

    // Get the owner window and dialog box rectangles. 

    if ((hwndOwner = GetParent(hwndDlg)) == NULL) 
    {
        hwndOwner = GetDesktopWindow(); 
    }

    GetWindowRect(hwndOwner, &rcOwner); 
    GetWindowRect(hwndDlg, &rcDlg); 
    CopyRect(&rc, &rcOwner); 

    // Offset the owner and dialog box rectangles so that right and bottom 
    // values represent the width and height, and then offset the owner again 
    // to discard space taken up by the dialog box. 

    OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); 
    OffsetRect(&rc, -rc.left, -rc.top); 
    OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); 

    // The new position is the sum of half the remaining space and the owner's 
    // original position. 

    SetWindowPos(hwndDlg, 
                 HWND_TOP, 
                 rcOwner.left + (rc.right / 2), 
                 rcOwner.top + (rc.bottom / 2), 
                 0, 0,          // Ignores size arguments. 
                 SWP_NOSIZE); 

    if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME) 
    { 
        SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME)); 
        return FALSE; 
    } 
    return TRUE; 

Dans les instructions précédentes, la procédure utilise la fonction GetParent pour récupérer le handle de fenêtre propriétaire dans une boîte de dialogue. La fonction retourne le handle de fenêtre propriétaire aux boîtes de dialogue et le handle de fenêtre parent aux fenêtres enfants. Étant donné qu’une application peut créer une boîte de dialogue qui n’a pas de propriétaire, la procédure vérifie le handle retourné et utilise la fonction GetDesktopWindow pour récupérer le handle de fenêtre de bureau, si nécessaire. Après avoir calculé la nouvelle position, la procédure utilise la fonction SetWindowPos pour déplacer la boîte de dialogue, en spécifiant la valeur HWND_TOP pour garantir que la boîte de dialogue reste au-dessus de la fenêtre propriétaire.

Avant de définir le focus d’entrée, la procédure vérifie l’identificateur de contrôle du focus d’entrée par défaut. Le système transmet le handle de fenêtre du focus d’entrée par défaut dans le paramètre wParam . La fonction GetDlgCtrlID retourne l’identificateur du contrôle identifié par le handle de fenêtre. Si l’identificateur ne correspond pas à l’identificateur correct, la procédure utilise la fonction SetFocus pour définir le focus d’entrée. La fonction GetDlgItem est nécessaire pour récupérer le handle de fenêtre du contrôle souhaité.

Création d’un modèle en mémoire

Les applications adaptent ou modifient parfois le contenu des boîtes de dialogue en fonction de l’état actuel des données traitées. Dans ce cas, il n’est pas pratique de fournir tous les modèles de boîte de dialogue possibles en tant que ressources dans le fichier exécutable de l’application. Toutefois, la création de modèles en mémoire offre à l’application plus de flexibilité pour s’adapter à toutes les circonstances.

Dans l’exemple suivant, l’application crée un modèle en mémoire pour une boîte de dialogue modale qui contient un message et les boutons OK et Aide .

Dans un modèle de boîte de dialogue, toutes les chaînes de caractères, telles que les titres de la boîte de dialogue et des boutons, doivent être des chaînes Unicode. Cet exemple utilise la fonction MultiByteToWideChar pour générer ces chaînes Unicode.

Les structures DLGITEMTEMPLATE dans un modèle de dialogue doivent être alignées sur les limites DWORD . Pour aligner ces structures, cet exemple utilise une routine d’assistance qui prend un pointeur d’entrée et retourne le pointeur le plus proche aligné sur une limite DWORD .

#define ID_HELP   150
#define ID_TEXT   200

LPWORD lpwAlign(LPWORD lpIn)
{
    ULONG ul;

    ul = (ULONG)lpIn;
    ul ++;
    ul >>=1;
    ul <<=1;
    return (LPWORD)ul;
}

LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPSTR lpszMessage)
{
    HGLOBAL hgbl;
    LPDLGTEMPLATE lpdt;
    LPDLGITEMTEMPLATE lpdit;
    LPWORD lpw;
    LPWSTR lpwsz;
    LRESULT ret;
    int nchar;

    hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
    if (!hgbl)
        return -1;
 
    lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
 
    // Define a dialog box.
 
    lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
    lpdt->cdit = 3;         // Number of controls
    lpdt->x  = 10;  lpdt->y  = 10;
    lpdt->cx = 100; lpdt->cy = 100;

    lpw = (LPWORD)(lpdt + 1);
    *lpw++ = 0;             // No menu
    *lpw++ = 0;             // Predefined dialog box class (by default)

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "My Dialog", -1, lpwsz, 50);
    lpw += nchar;

    //-----------------------
    // Define an OK button.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 10; lpdit->y  = 70;
    lpdit->cx = 80; lpdit->cy = 20;
    lpdit->id = IDOK;       // OK button identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;        // Button class

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50);
    lpw += nchar;
    *lpw++ = 0;             // No creation data

    //-----------------------
    // Define a Help button.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 55; lpdit->y  = 10;
    lpdit->cx = 40; lpdit->cy = 20;
    lpdit->id = ID_HELP;    // Help button identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;        // Button class atom

    lpwsz = (LPWSTR)lpw;
    nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Help", -1, lpwsz, 50);
    lpw += nchar;
    *lpw++ = 0;             // No creation data

    //-----------------------
    // Define a static text control.
    //-----------------------
    lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE)lpw;
    lpdit->x  = 10; lpdit->y  = 10;
    lpdit->cx = 40; lpdit->cy = 20;
    lpdit->id = ID_TEXT;    // Text identifier
    lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;

    lpw = (LPWORD)(lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0082;        // Static class

    for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*lpszMessage++;);
    lpw = (LPWORD)lpwsz;
    *lpw++ = 0;             // No creation data

    GlobalUnlock(hgbl); 
    ret = DialogBoxIndirect(hinst, 
                           (LPDLGTEMPLATE)hgbl, 
                           hwndOwner, 
                           (DLGPROC)DialogProc); 
    GlobalFree(hgbl); 
    return ret; 
}