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, e ivi inserito dal linker, a partire
da un file sorgente dato in pasto al "resource compiler",
RC), da una chiamata all'API DialogBoxParam
.
Questo tipo di finestre sono detti dialoghi modali;
loro caratteristica precipua è 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 (tipicamente, un compito di "dialogo" con l'utente, da cui il nome), e sparisce quando ha portato a termine quel compito (visto che, sinchè non sparisce, le altre finestre dell'applicazione sono disabilitate).
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 nel corso di questo tutorial.
Ma, chiaramente, si può avere anche una esigenza piuttosto diversa: quella di aprire, in addizione ad altre finestre dell'applicazione, anche un dialogo che non monopolizzi l'interazione con l'utente, bensì "se ne stia buono buono nel suo cantone", mostrando all'utente stesso puro e semplice output (come nel caso del nostro "output di debug"), ovvero permettendogli interazioni per modificare parametri che possono essere cambiati in qualsiasi momento. A questo ruolo adempiono egregiamente i dialoghi non modali.
(In generale, per permettere l'interazione completa e
normale con un dialogo non modale, è necessario
inserire nel nostro "ciclo di gestione messaggi" una
chiamata all'API IsDialogMessage
, che
traduce certi tasti (come tab, e frecce), spediti al
dialogo non modale, in operazioni di "navigazione" sul
dialogo stesso (perfettamente analoghe a quelle che,
automaticamente, sono messe a nostra disposizione dai
dialoghi modali).
Purtroppo, il ciclo di gestione dei messaggi non è sotto il nostro controllo quando è aperto un nostro dialogo modale; prendere il controllo di questo ciclo può anzi essere fra le motivazioni principali del passaggio da un'applicazione basata su dialogo modale, ad un'altra forma, anche se, per applicazioni molto semplici, non doversene occupare costituisce in effetti un vero e proprio beneficio.
Comunque, per ora non soffriremo della mancanza delle operazioni di "navigazione da tastiera" sui dialoghi non modali, e quindi potremo rimandare questa problematica.)
La creazione di un dialogo non modale si effettua
con l'API, del tutto analoga a
DialogBoxParam
:
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 -- tenetela presente se, e solo se, vi troverete
a scrivere la GUI del software di controllo di una
centrale nucleare:-).
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, ovverossia finestre-possedute).
Se non affidata a simili automatismi, la distruzione di una finestra di dialogo non modale si ottiene con l'API:
EndDialog
per un dialogo non modale,
bensì escluusivamente per quelli modali.
Possiamo dunque fare all'esempietto del capitolo precedente le modifiche necessarie per ottenere l'output delle stringhe su di un dialogo separato. Il file RC diverrà qualcosa del genere:
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, e a questo scopo passeremo
come lParam
l'indirizzo di una nostra
variabile di tipo HWND
in cui questo valore
verrà depositato; il frammento rilevanta sarà
dunque:
Questa HWND
la passeremo al dialogo
modale come suo lParam
, quindi
nella relativa dialog procedure avremo:
DWL_USER
della
finestra del dialogo (l'indice, appunto, che
è riservato per i dati "nostri"); la
risposta al clic sul bottone di OK diventerà
dunque qualcosa di simile a:
HWND
del list control per spedirgli il messaggio di
aggiunta-elemento.
Così risolto il cruciale aspetto strutturale, passeremo, al prossimo capitolo, a studiare più in dettaglio i controlli list-box, anche per consentirci poi di rimediare ai due difettucci secondari che avevamo già identificato alla fine del capitolo precedente.
Capitolo 40: output per il debug
Capitolo 42: controlli: i LISTBOX
Elenco dei capitoli