Utilizzo 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 non modale 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
- Creazione di una finestra di dialogo modale
- Creazione di una finestra di dialogo senza modalità
- inizializzazione di una finestra di dialogo
- Creazione di un modello in memoria
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 richiedere indicazioni su come procedere dopo che si è verificato un errore. È possibile creare una finestra di messaggio utilizzando 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 l'applicazione visualizza 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 per 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:
Creazione di una finestra di dialogo modale
Per creare una finestra di dialogo modale, utilizzare la funzioneDialogBox. È necessario specificare l'identificatore o il nome di una risorsa modello di finestra di dialogo e un puntatore alla procedura della finestra di dialogo. La DialogBox funzione carica il modello, visualizza la finestra di dialogo ed elabora tutti gli 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 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 ok e pulsanti 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 del gestore della finestra principale dell'applicazione, creano la finestra di dialogo quando il sistema riceve un messaggio WM_COMMAND con l'identificatore di menu IDM_DELETEITEM. La seconda parte dell'esempio è la routine della finestra di dialogo, che recupera il contenuto del controllo di modifica e chiude la finestra di dialogo alla ricezione di un messaggio WM_COMMAND.
Le istruzioni seguenti creano la finestra di dialogo modale. Il modello di finestra di dialogo è una risorsa nel file eseguibile dell'applicazione e ha l'identificatore della 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 propria finestra principale come finestra proprietaria 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 di proprietario. L'applicazione usa il valore restituito da DialogBox per determinare se procedere con l'operazione o annullarlo. Le istruzioni seguenti definiscono la routine 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 routine usa GetDlgItemText per recuperare il testo corrente dal controllo di modifica identificato da ID_ITEMNAME. La routine chiama quindi la funzioneEndDialog per impostare il valore restituito della finestra di dialogo su IDOK o IDCANCEL, a seconda del messaggio ricevuto e per iniziare il processo di chiusura della finestra di dialogo. Gli identificatori IDOK e IDCANCEL corrispondono ai pulsanti OK e Annulla. Dopo che la routine 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 dell'acquisizione e dell'invio dei messaggi di input utente alla procedura della finestra di dialogo.
Nell'esempio seguente, l'applicazione visualizza una finestra di dialogo modelless, se non è già aperta, 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 di finestra di dialogo è una risorsa nel file eseguibile dell'applicazione e ha l'identificatore della 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 non modale. Queste istruzioni, nella procedura della finestra per la finestra principale dell'applicazione, creano la finestra di dialogo quando riceve un messaggio WM_COMMAND avente 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 assicurarsi che l'utente possa usare l'interfaccia tramite tastiera nella finestra di dialogo non modale. 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 viene chiamato CreateDialog solo se hwndGoto
non contiene un handle di finestra valido. In questo modo, l'applicazione non visualizza due finestre di dialogo contemporaneamente. Per supportare questo metodo di controllo, la procedura di dialogo deve essere impostata su NULL quando distrugge 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 per la finestra di dialogo e chiama solo la funzioneIsDialogMessagese 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 routine 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 l'elaborazione di WM_INITDIALOG, la procedura 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 l'elaborazione WM_COMMAND, la procedura chiude la finestra di dialogo solo se l'utente fa clic sul pulsante Annulla, ovvero sul pulsante con l'identificatore IDCANCEL. La procedura deve chiamare DestroyWindow per chiudere una finestra di dialogo modale. Si noti che la routine imposta anche la variabile su NULL per assicurarsi che altre istruzioni che dipendono da questa variabile funzionino correttamente.
Se l'utente fa clic sul pulsante OK, la routine 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 con segno 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 routine 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 compito utile per alcune finestre di dialogo è impostare il focus di input su un controllo specifico invece di accettare il focus di input predefinito.
Nell'esempio seguente, la procedura della finestra di dialogo centra la finestra di dialogo e imposta lo stato attivo dell'input mentre elabora il messaggio WM_INITDIALOG. Per centrare la finestra di dialogo, la routine recupera i rettangoli della finestra per la finestra di dialogo e la finestra del 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 procedura usa la funzione GetParent per recuperare l'handle della finestra proprietaria di una finestra di dialogo. La funzione restituisce l'handle della finestra proprietaria nelle finestre di dialogo e l'handle della finestra padre nelle finestre figlie. 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 il focus sull'input, la procedura controlla l'identificatore del controllo del focus input predefinito. Il sistema passa l'handle della finestra dello stato attivo di input predefinito nel parametro wParam. La funzione GetDlgCtrlID restituisce l'identificatore del controllo identificato dall'handle della finestra. Se l'identificatore non corrisponde all'identificatore corretto, la routine usa la funzione SetFocus per impostare il focus sull'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 possibili modelli di finestra di dialogo 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 che contiene 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 usata la funzioneMultiByteToWideChar per generare queste stringhe Unicode.
Le strutture DLGITEMTEMPLATE in un modello di finestra di dialogo devono essere allineate su 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 a 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;
}