Esempio: dlg_two

Diamo ora uno sguardo a CreateDialog(), la sorella di DialogBox(). La differenza tra le due consiste nel fatto che mentre DialogBox() implementa implementa il proprio loop di messaggi e non ritorna finchè la dialog non viene chiusa CreateDialog() si comporta più comonemente come una finestra creata con CreateWindowEx() e ritorna immediatamente, dopodichè tutti i messaggi vengono processati dal loop di messaggi principale del vostro programma. Questo viene soprannominato Modeless, ossia non modale, invece DialogBox() crea dialogs Modal, ovvero modali. Anche in questo caso potete creare la finestra di dialogo direttamente dalle risorse come abbiamo fatto nell’esempio precedente di dialog. Potete anche settare nelle proprietà l’opzione per utilizzare gli stili estesi in modo da visualizzare il titolo piccolo, tipico delle toolbar. La risora viene creata come segue:

IDD_TOOLBAR DIALOGEX 0, 0, 98, 52
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
EXSTYLE WS_EX_TOOLWINDOW
CAPTION "La mia Toolbar"
FONT 8, "MS Sans Serif"
BEGIN
    PUSHBUTTON      "&Premi questo bottone",IDC_PRESS,7,7,84,14
    PUSHBUTTON      "&Oppure questo",IDC_OTHER,7,31,84,14
END

Vi accorgete che l’editor delle risorse ha rimpiazzato DIALOG con DIALOGEX indicando che vogliamo settare un EXSTYLE sulla nostra dialog.

Ora vogliamo creare una dialog quando il nostro programma va in esecuzione, vogliamo che essa sia visibile subito, quindi andiamo ad inserire all’interno dell’handler di WM_CREATE. Inoltre dichiariamo una variabile globale per tenere traccia dell’handle della finestra appena creata con CreateDialog() in modo da poterlo usare in seguito. DialogBox() non ritorna nessun handle e quando DialogBox() ritorna la finestra è stata già distrutta.

HWND g_hToolbar = NULL;
    case WM_CREATE:
        g_hToolbar = CreateDialog(GetModuleHandle(NULL),
                                  MAKEINTRESOURCE(IDD_TOOLBAR),
            hwnd, ToolDlgProc);
        if(g_hToolbar != NULL)
        {
            ShowWindow(g_hToolbar, SW_SHOW);
        }
        else
        {
            MessageBox(hwnd, "CreateDialog ha restituito NULL", "Attenzione!", MB_OK | MB_ICONINFORMATION);        }
    break;

Controlliamo il valore ritornato, che è SEMPRE una buona idea , e se è valido (non NULL) facciamo visualizzare la finestra con ShowWindow(), con DialogBox() questo non è necessario dato che il sistema chiama automaticamente ShowWindow() per noi.

Ora abbiamo bisogno di una Window Procedure per la nostra nuova toolbar.

BOOL CALLBACK ToolDlgProc(HWND hwnd, UINT Message, WPARAM wParam,
                    LPARAM lParam)
{
    switch(Message)
    {
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDC_PRESS:
                    MessageBox(hwnd, "Salve!", "Questo è un messaggio",
                        MB_OK | MB_ICONEXCLAMATION);
                break;
                case IDC_OTHER:
                    MessageBox(hwnd, "Arrivederci!",
                        "Questo è un altro messaggio",
                        MB_OK | MB_ICONEXCLAMATION);
                break;
            }
        break;
        default:
            return FALSE;
    }
    return TRUE;
}

La maggior parte delle regole applicate a DialogBox() sono valide anche per le dialog create con CreateDialog(), non chiamate mai DefWindowProc(), ritornate FALSE per i messaggi che non processate e TRUE per quelli che processate.

Un cambiamento è che non andiamo a chiamare EndDialog() nelle dialog modeless dato che possiamo usare DestroyWindow() proprio come nelle normali finestre. In questo caso distruggo la dialog quando la finestra principale è distrutta. Nella Window Procedure WndProc()

    case WM_DESTROY:
        DestroyWindow(g_hToolbar);
        PostQuitMessage(0);
    break;

Ultimo ma non meno importante, vogliamo essere in grado di visualizzare o nascondere la toolbar quando vogliamo, per questo aggiungiamo altri due comandi al nostro menu per fare proprio questo, e li processiamo in questo modo:

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case ID_DIALOG_SHOW:
                ShowWindow(g_hToolbar, SW_SHOW);
            break;
            case ID_DIALOG_HIDE:
                ShowWindow(g_hToolbar, SW_HIDE);
            break;
            //... altri handlers di comandi
        }
    break;

Ora dovreste essere in grado di creare i vostri menu manualmente oppure con l’editor di risorse ma se non vi ricordate come fare, date uno sguardo al progetto di esempio dlg_two allegato a questo tutorial.

Ora quando eseguite il programma, dovreste essere in grado di accedere sia sulla finestra di dialogo che sulla finestra principale nello stesso momento. Se a questo punto avete eseguito il programma ed avete provato ad usare il tab tra i due bottoni, vi sarete probabilmente resi conto che non funziona; e non funziona nemmeno se provate a premere Alt-P o Alt-O per attivare i bottoni. Come mai? Mentre DialogBox() implementa il proprio loop di messaggi e processa questi eventi di default, CreateDialog() non lo fa. Possiamo farlo noi ovviamente, chiamando IsDialogMessage() nel nostro loop di messaggi. Esso lo processerà automaticamente per noi.

    while(GetMessage(&Msg, NULL, 0, 0))
    {
        if(!IsDialogMessage(g_hToolbar, &Msg))
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }

In questo esempio facciamo processare quindi prima i messaggi a IsDialogMessage(), se il messaggio è destinato alla nostra toolbar (indicata dall’handle che passiamo alla funzione), il sistema eseguirà le procedure di default per i dialog su quell’handle e ritornerà TRUE. In questo caso il messaggio è stato già processato quindi non chiamiamo ulteriormente TranslateMessage() oppure DispatchMessage(). Se invece il messaggio è destinato ad un’altra finestra lo processiamo come sempre.

Ricordate comunque che IsDialogMessage() può anche essere usato per le finestre che non sono dialogs per dar loro le stesse funzionalità delle dialog. Ricordate che una dialog è una finestra e la maggior parte (se non tutte) delle API funzionano su ogni finestra.

E questo è grossomodo tutto quello che c’e’ da sapere sulle modeless dialog! Potrebbe capitare di avere più toolbar… cosa si può fare? Beh, una soluzione potrebbe essere quella di inserire gli handle in una lista (un array, una STL std::list, o simili) ed scorrerla all’interno del message loop, passando ogni handle a IsDialogMessage() finché non viene trovato quello giusto, e se non ne viene trovato nessuno, eseguire le procedure regolari. Questo è un problema generale di programmazione, non relativo a Win32 ed è lasciato come esercizio al lettore.