Lezione 8 – Finestre di dialogo non-Modali



By admin on settembre 12, 2010


I programmi che abbiamo sviluppato sino a questo punto del corso presentano sempre una sola "finestra principale", e più precisamente un dialogo, generato (a partire da un "template di dialogo" contenuto nelle risorse del nostro eseguibile), da una chiamata all'API DialogBoxParam.
Questo tipo di finestre sono detti dialoghi modali; la loro caratteristica peculiare è quella di prendere pieno controllo della gestione degli eventi nel corso della loro esistenza (cioè nel tempo che intercorre fra la creazione attraverso l'API DialogBoxParam o simili, e la terminazione attraverso l'API EndDialog).
Questa "modalità" non è naturalmente un particolare problema per un dialogo che costituisce l'unica finestra della nostra applicazione, come sin qui è stato. In casi più generali, un dialogo modale costituisce invece una finestra necessariamente "transitoria", che viene attivata per un compito specifico, e sparisce quando ha portato a termine quel compito.
In quest'ottica, ad esempio, il sistema mette a nostra disposizione un certo numero di "dialoghi comuni", già pronti e "impacchettati", che svolgono comuni compiti di dialogo con l'utente, come quello di fargli scegliere un file da aprire, e così via. Sull'argomento "dialoghi comuni" ritorneremo più avanti.
Ma, chiaramente, si può avere anche una esigenza piuttosto diversa: quella di aprire, in aggiunta ad altre finestre dell'applicazione, anche un dialogo che non monopolizzi l'interazione con l'utente, mostrando all'utente stesso puro e semplice output ovvero permettendogli interazioni per modificare parametri che possono essere cambiati in qualsiasi momento. A questo ruolo adempiono egregiamente i dialoghi non modali.
La creazione di un dialogo non modale si effettua con l'API, del tutto analoga a DialogBoxParam:

HWND CreateDialogParam(
  HINSTANCE hInstance,     // istanza dell'applicazione
  LPCTSTR lpTemplateName,  // il template del dialogo
  HWND hWndParent,         // finestra proprietaria
  DLGPROC lpDialogFunc,    // la "dialog procedure"
  LPARAM dwInitParam       // usato in WM_INITDIALOG
);

 

I parametri sono appunto identici alla API DialogBoxParam, che è usata per la creazione di dialoghi modali.
A differenza di un dialogo modale, uno non-modale non viene automaticamente mostrato alla creazione, a meno che non abbia fra i suoi bit di stile WS_VISIBLE. Più in generale, il programma lo mostrerà (e, se occorre, lo nasconderà di nuovo) con l'API ShowWindow.
Alla creazione, il dialogo non-modale viene reso "finestra attiva" di questo processo; il programma, naturalmente, può ignorare questo fatto, usando l'API SetActiveWindow per porre attiva quella che preferisce fra le proprie finestre (l'analoga API SetForegroundWindow, più "drammatica" nei propri effetti, va invece chiamata solo in situazioni di emergenza, tali da esigere immediata attenzione da parte dell'utente; tipicamente, essa si giustifica solamente per errori critici che vanno rimediati con la massima urgenza).
La cosa fondamentale, naturalmente, è che il dialogo non-modale non "monopolizza" l'interazione; l'unico rapporto con la finestra "genitrice", o "proprietaria" che dir si voglia, è che il dialogo modale si mantiene sempre al di sopra di essa (quindi, può in parte coprirla) anche se non è attivo inoltre, naturalmente, se viene distrutta una finestra-proprietaria, ovvero genitrice, il sistema distrugge anche, e in effetti subito prima, le finestre-figlie.
Se non affidata a simili automatismi, la distruzione di una finestra di dialogo non modale si ottiene con l'API:

BOOL DestroyWindow(
  HWND hWnd   // handle della finestra da distruggere
);

 

L'importante è ricordarsi di non chiamare mai EndDialog per un dialogo non-modale, bensì esclusivamente per quelli modali.
Possiamo dunque fare un esempio. Il file RC dovrà contenere qualcosa del genere:

IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 117, 47
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Modale"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,60,5,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,5,5,50,14
    EDITTEXT        IDC_EDIT1,5,25,105,20,ES_AUTOHSCROLL
END

IDD_DIALOG2 DIALOG DISCARDABLE  230, 0, 122, 68
STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Non-modale"
FONT 8, "MS Sans Serif"
BEGIN
    LISTBOX         IDC_LIST1,10,5,105,60,NOT LBS_NOTIFY | LBS_NOSEL | 
                    WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
END

 

Negli stili del dialog template da cui costruiremo la finestra non modale, abbiamo omesso la "modal frame" e lo stile "popup", abbiamo invece inserito WS_VISIBLE affinché la finestra venga visualizzata alla creazione.
La dialog procedure per il dialogo non-modale ha qui l'unico scopo di restituire, all'inizializzazione, la handle del listbox control (che vedremo nel prossimo capitolo), e a questo scopo passeremo come lParam l'indirizzo di una nostra variabile di tipo HWND in cui questo valore verrà depositato; il frammento di codice sarà dunque:

case WM_INITDIALOG: HWND* pHw = (HWND*)lParam;
                     *pHw = GetDlgItem(hwndDlg, IDC_LIST1);
                     return 1;
        

 

Questa HWND la passeremo al dialogo modale come suo lParam, quindi nella relativa dialog procedure avremo:

 
case WM_INITDIALOG: SetWindowLong(hwndDlg, DWL_USER, lParam);
                     return 1;

scegliendo di depositare questo valore come long-word di indice DWL_USER della finestra del dialogo (l'indice, appunto, che è riservato per i "nostri" dati); la risposta al click sul pulsante OK diventerà dunque qualcosa di simile a:

case IDOK: char buf[256];
            GetDlgItemText(hwndDlg, IDC_EDIT1, buf, 256);
            LONG hlist = GetWindowLong(hwndDlg, DWL_USER);
            SendMessage((HWND)hlist, LB_ADDSTRING, 0, (LPARAM)buf);
            return 1;




Lascia un Commento