Uso delle finestre di dialogo

Le finestre di dialogo consentono di visualizzare informazioni e richiedere l'input dell'utente. L'applicazione carica e inizializza la finestra di dialogo, elabora l'input dell'utente e elimina definitivamente la finestra di dialogo al termine dell'attività. Il processo di gestione delle finestre di dialogo varia a seconda che la finestra di dialogo sia modale o non modale. Una finestra di dialogo modale richiede all'utente di chiudere la finestra di dialogo prima di attivare un'altra finestra nell'applicazione. Tuttavia, l'utente può attivare finestre in applicazioni diverse. Una finestra di dialogo senza modalità non richiede una risposta immediata da parte dell'utente. È simile a una finestra principale contenente i controlli.

Le sezioni seguenti illustrano come usare entrambi i tipi di finestre di dialogo.

Visualizzazione di una finestra di messaggio

La forma più semplice della finestra di dialogo modale è la finestra di messaggio. La maggior parte delle applicazioni usa le finestre di messaggio per avvisare l'utente degli errori e per richiedere istruzioni su come procedere dopo che si è verificato un errore. Per creare una finestra di messaggio, utilizzare la funzione MessageBox o MessageBoxEx, specificando il messaggio e il numero e il tipo di pulsanti da visualizzare. Il sistema crea una finestra di dialogo modale, specificando il modello e la routine della finestra di dialogo. Dopo che l'utente chiude la finestra di messaggio, MessageBox o MessageBoxEx restituisce un valore che identifica il pulsante scelto dall'utente per chiudere la finestra di messaggio.

Nell'esempio seguente viene visualizzata una finestra di messaggio che richiede all'utente un'azione dopo che si è verificata una condizione di errore. Nella finestra di messaggio viene visualizzato il messaggio che descrive la condizione di errore e come risolverlo. Lo stile MB_YESNO indirizza MessageBox a fornire due pulsanti con cui l'utente può scegliere come procedere:

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'immagine seguente mostra l'output dell'esempio di codice precedente:

finestra di messaggio

Creazione di una finestra di dialogo modale

Per creare una finestra di dialogo modale, utilizzare la funzione DialogBox . È necessario specificare l'identificatore o il nome di una risorsa modello di finestra di dialogo e un puntatore alla routine della finestra di dialogo. La funzione DialogBox carica il modello, visualizza la finestra di dialogo ed elabora tutto l'input dell'utente fino a quando l'utente non chiude la finestra di dialogo.

Nell'esempio seguente l'applicazione visualizza una finestra di dialogo modale quando l'utente fa clic su Elimina elemento da un menu dell'applicazione. La finestra di dialogo contiene un controllo di modifica (in cui l'utente immette il nome di un elemento) e i pulsanti OK e Annulla . Gli identificatori di controllo per questi controlli sono rispettivamente ID_ITEMNAME, IDOK e IDCANCEL.

La prima parte dell'esempio è costituita dalle istruzioni che creano la finestra di dialogo modale. Queste istruzioni, nella procedura della finestra per la finestra principale dell'applicazione, creare la finestra di dialogo quando il sistema riceve un messaggio di WM_COMMAND con l'identificatore del menu IDM_DELETEITEM. La seconda parte dell'esempio è la procedura della finestra di dialogo, che recupera il contenuto del controllo di modifica e chiude la finestra di dialogo quando riceve un messaggio di WM_COMMAND .

Le istruzioni seguenti creano la finestra di dialogo modale. Il modello della finestra di dialogo è una risorsa nel file eseguibile dell'applicazione e ha l'identificatore di risorsa 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; 

In questo esempio, l'applicazione specifica la relativa finestra principale come finestra di proprietario per la finestra di dialogo. Quando il sistema visualizza inizialmente la finestra di dialogo, la relativa posizione è relativa all'angolo superiore sinistro dell'area client della finestra proprietario. L'applicazione usa il valore restituito da DialogBox per determinare se procedere con l'operazione o annullarlo. Le istruzioni seguenti definiscono la procedura della finestra di dialogo.

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; 
} 

In questo esempio la procedura usa GetDlgItemText per recuperare il testo corrente dal controllo di modifica identificato da ID_ITEMNAME. La routine chiama quindi la funzione EndDialog per impostare il valore restituito della finestra di dialogo su IDOK o IDCANCEL, a seconda del messaggio ricevuto e per avviare il processo di chiusura della finestra di dialogo. Gli identificatori IDOK e IDCANCEL corrispondono ai pulsanti OK e Annulla . Dopo che la procedura chiama EndDialog, il sistema invia messaggi aggiuntivi alla routine per eliminare definitivamente la finestra di dialogo e restituisce il valore restituito della finestra di dialogo alla funzione che ha creato la finestra di dialogo.

Creazione di una finestra di dialogo senza modalità

Per creare una finestra di dialogo senza modalità, utilizzare la funzione CreateDialog , specificando l'identificatore o il nome di una risorsa modello di finestra di dialogo e un puntatore alla routine della finestra di dialogo. CreateDialog carica il modello, crea la finestra di dialogo e, facoltativamente, la visualizza. L'applicazione è responsabile del recupero e dell'invio dei messaggi di input dell'utente alla procedura della finestra di dialogo.

Nell'esempio seguente l'applicazione visualizza una finestra di dialogo senza modalità, se non è già visualizzata, quando l'utente fa clic su Vai a da un menu dell'applicazione. La finestra di dialogo contiene un controllo di modifica, una casella di controllo e i pulsanti OK e Annulla . Il modello della finestra di dialogo è una risorsa nel file eseguibile dell'applicazione e ha l'identificatore di risorsa DLG_GOTO. L'utente immette un numero di riga nel controllo di modifica e controlla la casella di controllo per specificare che il numero di riga è relativo alla riga corrente. Gli identificatori di controllo sono ID_LINE, ID_ABSREL, IDOK e IDCANCEL.

Le istruzioni nella prima parte dell'esempio creano la finestra di dialogo senza modalità. Queste istruzioni, nella procedura della finestra per la finestra principale dell'applicazione, creare la finestra di dialogo quando la routine della finestra riceve un messaggio di WM_COMMAND con l'identificatore di menu IDM_GOTO, ma solo se la variabile globale non contiene già un handle valido. La seconda parte dell'esempio è il ciclo di messaggi principale dell'applicazione. Il ciclo include la funzione IsDialogMessage per garantire che l'utente possa usare l'interfaccia della tastiera della finestra di dialogo in questa finestra di dialogo senza modalità. La terza parte dell'esempio è la procedura della finestra di dialogo. La procedura recupera il contenuto del controllo di modifica e la casella di controllo quando l'utente fa clic sul pulsante OK . La procedura elimina definitivamente la finestra di dialogo quando l'utente fa clic sul pulsante Annulla .

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; 

Nelle istruzioni precedenti , CreateDialog viene chiamato solo se hwndGoto non contiene un handle di finestra valido. Ciò garantisce che l'applicazione non visualizzi due finestre di dialogo contemporaneamente. Per supportare questo metodo di controllo, la routine della finestra di dialogo deve essere impostata su NULL quando elimina definitivamente la finestra di dialogo.

Il ciclo di messaggi per un'applicazione è costituito dalle istruzioni seguenti.

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); 
    } 
} 

Il ciclo controlla la validità dell'handle di finestra nella finestra di dialogo e chiama solo la funzione IsDialogMessage se l'handle è valido. IsDialogMessage elabora il messaggio solo se appartiene alla finestra di dialogo. In caso contrario, restituisce FALSE e il ciclo invia il messaggio alla finestra appropriata.

Le istruzioni seguenti definiscono la procedura della finestra di dialogo.

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; 
} 

Nelle istruzioni precedenti la routine elabora i messaggi WM_INITDIALOG e WM_COMMAND . Durante WM_INITDIALOG'elaborazione , la routine inizializza la casella di controllo passando il valore corrente della variabile globale a CheckDlgButton. La procedura restituisce quindi TRUE per indirizzare il sistema a impostare lo stato attivo di input predefinito.

Durante WM_COMMAND'elaborazione , la procedura chiude la finestra di dialogo solo se l'utente fa clic sul pulsante Annulla , ovvero il pulsante con l'identificatore IDCANCEL. La procedura deve chiamare DestroyWindow per chiudere una finestra di dialogo senza modalità. Si noti che la procedura imposta anche la variabile su NULL per garantire che altre istruzioni che dipendono da questa variabile funzionino correttamente.

Se l'utente fa clic sul pulsante OK , la procedura recupera lo stato corrente della casella di controllo e la assegna alla variabile fRelative . Usa quindi la variabile per recuperare il numero di riga dal controllo di modifica. GetDlgItemInt converte il testo nel controllo di modifica in un numero intero. Il valore di fRelative determina se la funzione interpreta il numero come valore firmato o senza segno. Se il testo del controllo di modifica non è un numero valido, GetDlgItemInt imposta il valore della variabile fError su diverso da zero. La procedura controlla questo valore per determinare se visualizzare un messaggio di errore o eseguire l'attività. In caso di errore, la procedura della finestra di dialogo invia un messaggio al controllo di modifica, indirizzandolo a selezionare il testo nel controllo in modo che l'utente possa sostituirlo facilmente. Se GetDlgItemInt non restituisce un errore, la procedura può eseguire l'attività richiesta stessa o inviare un messaggio alla finestra del proprietario, indirizzandolo per eseguire l'operazione.

Inizializzazione di una finestra di dialogo

Inizializzare la finestra di dialogo e il relativo contenuto durante l'elaborazione del messaggio WM_INITDIALOG . L'attività più comune consiste nell'inizializzare i controlli in modo da riflettere le impostazioni correnti della finestra di dialogo. Un'altra attività comune consiste nel centrare una finestra di dialogo sullo schermo o all'interno della finestra del proprietario. Un'attività utile per alcune finestre di dialogo consiste nell'impostare lo stato attivo di input su un controllo specificato anziché accettare lo stato attivo di input predefinito.

Nell'esempio seguente la procedura della finestra di dialogo centra la finestra di dialogo e imposta lo stato attivo di input durante l'elaborazione del messaggio WM_INITDIALOG . Per centrare la finestra di dialogo, la procedura recupera i rettangoli di finestra per la finestra di dialogo e la finestra proprietario e calcola una nuova posizione per la finestra di dialogo. Per impostare lo stato attivo dell'input, la procedura controlla il parametro wParam per determinare l'identificatore dello stato attivo di input predefinito.

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; 

Nelle istruzioni precedenti la routine usa la funzione GetParent per recuperare l'handle della finestra di proprietario in una finestra di dialogo. La funzione restituisce l'handle della finestra del proprietario nelle finestre di dialogo e l'handle della finestra padre nelle finestre figlio. Poiché un'applicazione può creare una finestra di dialogo senza proprietario, la procedura controlla l'handle restituito e usa la funzione GetDesktopWindow per recuperare l'handle della finestra desktop, se necessario. Dopo aver calcolato la nuova posizione, la routine usa la funzione SetWindowPos per spostare la finestra di dialogo, specificando il valore HWND_TOP per assicurarsi che la finestra di dialogo rimanga nella parte superiore della finestra di proprietario.

Prima di impostare lo stato attivo per l'input, la procedura controlla l'identificatore del controllo dello stato attivo di input predefinito. Il sistema passa l'handle della finestra dello stato attivo di input predefinito nel parametro wParam . La funzione GetDlgCtrlID restituisce l'identificatore per il controllo identificato dall'handle della finestra. Se l'identificatore non corrisponde all'identificatore corretto, la routine usa la funzione SetFocus per impostare lo stato attivo di input. La funzione GetDlgItem è necessaria per recuperare l'handle della finestra del controllo desiderato.

Creazione di un modello in memoria

Le applicazioni talvolta adattano o modificano il contenuto delle finestre di dialogo a seconda dello stato corrente dei dati elaborati. In questi casi, non è pratico fornire tutti i modelli di finestra di dialogo possibili come risorse nel file eseguibile dell'applicazione. Tuttavia, la creazione di modelli in memoria offre all'applicazione maggiore flessibilità per adattarsi a qualsiasi circostanza.

Nell'esempio seguente l'applicazione crea un modello in memoria per una finestra di dialogo modale contenente un messaggio e i pulsanti OK e Guida .

In un modello di finestra di dialogo tutte le stringhe di caratteri, ad esempio la finestra di dialogo e i titoli dei pulsanti, devono essere stringhe Unicode. In questo esempio viene utilizzata la funzione MultiByteToWideChar per generare queste stringhe Unicode.

Le strutture DLGITEMTEMPLATE in un modello di dialogo devono essere allineate ai limiti DWORD . Per allineare queste strutture, in questo esempio viene utilizzata una routine helper che accetta un puntatore di input e restituisce il puntatore più vicino allineato su un 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; 
}