Introduzione alle API Win32

Capitolo 10: controlli: i BUTTON

Gli STATIC, benchè utili e flessibili, non sono certo gli unici tipi di controlli predefiniti che Windows ci mette a disposizione. I "controlli elementari", infatti, comprendono anche BUTTON, COMBOBOX, EDIT, LISTBOX, e SCROLLBAR, mentre i più avanzati "controlli comuni" (la terminologia non è proprio limpida...!) "iniziano" con il "Rich Edit" (normalmente nelle due versioni, rispettivamente ANSI e UNICODE, RichEdit20A e RichEdit20W; la macro RICHEDIT_CLASS, definita, naturalmente, negli header file di sistema, permette di usare il nome di classe migliore in ciascun caso).

I controlli comuni proseguono, poi, con la ricca panoplia che comprende Animate, HotKey, Date-Time Picker, ProgressBar, ReBar, StatusBar, Tooltip, Trackbar, UpDown, Combo Box estesa, Header, IP-Address, Listview, Pager, TabControl, e TreeView (e altri ancora, in versioni più recenti di Win32!). I nomi delle classi di tutta questa varietà di controlli "comuni" tendono a cambiare spesso, per cui per essi si usano normalmente delle macro definite, assieme a tante altre cose, nell'header file di sistema <commctrl.h>.

L'enorme varietà e ricchezza di questi controlli ci impedirà, naturalmente, di darne una trattazione soddisfacente nell'ambito di quello che vuol essere un "tutorial" (cioè una trattazione puramente introduttiva sul tema "uso delle API Win32"), soprattutto considerando quale vasta, enorme ricchezza funzionale di dettaglio c'è dietro ciascuno di questi (tanti!) controlli predefiniti!

Presenteremo, invece, i controlli più importanti ed elementari, ed eventualmente qualche cenno ed esempio di argomenti più avanzati; quanto basta, possiamo sperare, per indirizzare il lettore verso un uso proficuo dell'help in linea del Platform SDK, di MSDN, e di altri testi disponibili su questi temi.

I controlli BUTTON sono abbastanza semplici e versatili. Un BUTTON è quasi sempre un modo per l'utente di comunicare al nostro programma, "cliccando" (col tasto sinistro del mouse, o con la tastiera) sul bottone stesso; a seconda del suo "stile", può essere un "pushbutton", una "checkbox", o un "radiobutton", con aspetti e significati diversi.

Esistono inoltre i bottoni "owner-drawn" (cioè "disegnati dal proprietario"), nel qual caso il nostro programma si rende pienamente responsabile per l'aspetto del button e per il suo comportamento; e i bottoni "group box", che, pur appartenendo alla stessa classe di finestre, non ricordano per nulla gli altri button, bensì, se mai, gli static, poichè si tratta di pure "decorazioni" della finestra di dialogo, senza alcuna interazione con l'utente (l'aspetto è quello di un rettangolo, normalmente posto attorno a un gruppo di altri controlli, appunto allo scopo di "raggrupparli" visivamente, con un'etichetta di testo in alto a sinistra).

Un "push button" è il più tipico e riconoscibile dei "bottoni". Il suo stile-base è BS_PUSHBUTTON; uno, e uno solo, dei bottoni dei dialogo può invece essere designato come BS_DEFPUSHBUTTON, cioè "bottone di default" -- esso verrà disegnato in modo leggermente diverso ed "enfatizzato", e, se l'utente aziona il tasto Enter (Return) dovunque nel dialogo, sarà "proprio come se" avesse cliccato il bottone di default (un dialogo può correttamente avere uno o nessun bottone di default; se ne ha più di uno, invece, è un errore, e il comportamento non è definito -- a seconda della versione di Windows, si possono avere in questo caso effetti differenti).

Un push button mostra il proprio testo (primo argomento dell'istruzione CONTROL nel file RC) centrato nel "bottone" vero e proprio. Quando viene cliccato, come tutti i bottoni, avverte il dialogo che lo contiene con il messaggio WM_COMMAND, codice di notifica BN_CLICKED (proprio come uno STATIC con il bit di stile SS_NOTIFY, se cliccato, avverte il dialogo con lo stesso WM_COMMAND, ma codice di notifica SN_CLICKED).

Una "check box" è una casella che può essere "spuntata" ("checked") o meno, con un testo esplicativo a fianco. Con lo stile BS_CHECKBOX, si ottiene il comportamento-base: ogni volta che l'utente clicca, il dialogo riceve il WM_COMMAND, ed è responsabile di gestire lo stato on/off della casella "check box". Spesso utile è invece lo stile BS_AUTOCHECKBOX, in cui la casella "commuta" automaticamente il proprio stato on/off (oltre a spedire il solito WM_COMMAND). Esistono anche caselle "a tre stati" (BS_3STATE, e BS_AUTO3STATE) che, oltre agli stati on e off, prevedono un "terzo stato indeterminato" (che è rappresentato "ingrigendo" la casella).

Un "radio button" è simile a una checkbox (gli stili sono BS_RADIOBUTTON e -- nulla a che vedere con le autoradio! -- BS_AUTORADIOBUTTON), con un aspetto un poco diverso (rotondo, con o senza "pallino", invece che quadrato con o senza "spuntatura"), e una cruciale differenza di comportamento: un radiobutton non si "spegne" quando l'utente vi clicca, bensì quando egli clicca su di un altro radiobutton, che si accende. I radiobutton indicano, insomma, una situazione in cui l'utente può scegliere una di N possibilità, reciprocamente esclusive.

La determinazione "automatica" di quali radio button di un dialogo siano nello stesso "gruppo" si appoggia sul loro flag di stile WS_GROUP e sull'ordine in cui sono stati creati nel dialogo. Un "gruppo" comincia dal primo controllo creato, e continua, nell'ordine di creazione, sino a che non si incontra un controllo con WS_GROUP -- quest'ultimo è escluso dal gruppo; ciascun gruppo successivo al primo inizia da un controllo con WS_GROUP, incluso, e prosegue, sempre nell'ordine di creazione, sino al prossimo controllo che ha WS_GROUP, escluso.

Questo concetto di "gruppo", tra l'altro, oltre alla sua funzione cruciale per gruppi di radio button, è anche assai rilevante relativamente alle possibilità di "navigazione" all'interno del dialogo, da parte dell'utente che voglia usare la tastiera invece del mouse: un "gruppo" di controlli (come qui definito) è anche l'insieme di controlli fra cui l'utente può muoversi coi tasti-freccia della tastiera. L'ordine di creazione dei controlli (che, usando un file .RC, è esattamente l'ordine con cui i controlli sono elencati fra il BEGIN e l'END del dialogo) è poi anche lo stesso ordine in cui l'utente si può muovere da un controllo all'altro usando TAB, gruppi a parte (usando il tasto TAB, l'utente potrà raggiungere direttamente solo i controlli che hanno il bit di stile WS_TABSTOP).

Se non usiamo gli stili AUTO, lo stato on/off di check box e radio button dipende solo dai messaggi che il nostro codice invia ai controlli stessi. Il messaggio BM_SETCHECK, con lParam obbligatoriamente 0, contiene in wParam il valore che vogliamo impostare per lo stato di check del check box o radio button cui lo spediamo: BST_CHECKED fà sì che il bottone appaia "impostato" (col segno di spuntatura, o il pallino, secondo il suo stile), mentre BST_UNCHECKED fà sì che il bottone appaia "non impostato" ("vuoto").

Si può anche chiedere ad un bottone il suo stato di check, spedendogli il messaggio BM_GETCHECK; lo stato sarà allora comunicato come valore di ritorno della API SendMessage usata per spedire questo messaggio (i possibili valori di ritorno sono, nuovamente, BST_CHECKED e BST_UNCHECKED). Si tenga ben presente questo tipo di possibilità: tenere un "doppione" della informazione "stato del bottone", visto che il bottone stesso "conosce" il proprio stato (ed è possibile chiederglelo ogni volta che esso interessi), è una di quelle "micro ottimizzazioni" che, 99 volte su 100, è meglio evitare accuratamente (a tenere simili "doppioni" dello stato, si corrono vari rischi di "errori di aggiornamento" fra quel che il nostro programma "crede" che sia lo stato attuale, e l'opinione che in merito hanno invece altre "copie ridondanti" dello stato stesso...).

Lo stile BS_BITMAP e quello BS_ICON sono simili a quelli SS_BITMAP e SS_ICON: il bottone mostra un'immagine (bitmap, o icona, rispettivamente) invece di un testo. L'immagine può essere impostata col messaggio BM_SETIMAGE (così come, per uno static, può esserlo col messaggio STM_SETIMAGE).

Ma torneremo su questi temi più avanti, quando inizieremo ad affrontare i problemi della grafica in Windows; similmente, rimandiamo la gestione degli "owner-drawn" (sia per i button, sia per gli static), visto che, sino ad allora, non sapremmo ancora come e cosa disegnare. Lo stesso vale per i messaggi di tipo WM_CTLCOLOR..., che permettono ad un dialogo di stabilire i colori dei controlli che contiene (questo richiede l'uso di meccanismi tipici della "grafica" di Windows, da cui la decisione di rimandarne l'esame).

Così come uno STATIC può essere abilitato a spedire alcuni messaggi (tipo SN_CLICKED) al dialogo col bit di stile SS_NOTIFY, così un BUTTON può essere abilitato a spedire altri messaggi (oltre a BN_CLICKED, che esso spedisce comunque!) al suo dialogo, con il bit di stile BS_NOTIFY. I messaggi che così si ottengono sono:

(Sarebbe abbastanza strano che un dialogo volesse veramente fare qualcosa di peculiare a fronte di queste situazioni, ma, non si sà mai...!)

Viceversa, se un'applicazione, per qualche strana ragione, vuole simulare il clic su di un bottone da parte dell'utente, può farlo spedendo al bottone stesso il messaggio BM_CLICK (con entrambi i parametri obbligatoriamente pari a zero); questo messaggio deve essere spedito solo ai bottoni di un dialogo che è attualmente attivo.

Naturalmente, anche altri stili (come WS_BORDER, cui già abbiamo accennato), applicabili a generiche finestre, sono, in particolare, applicabili anche ai bottoni; così pure, altri messaggi (come WM_GETTEXT e WM_SETTEXT, cui pure accennammo), sono, come a tante altre finestre, applicabili anche ai bottoni.

L'idea che la comunicazione fra finestre, in entrambi i versi, proceda sostanzialmente solo tramite "messaggi", è una potente "unificazione", e corrisponde abbastanza bene all'effettivo funzionamento "interno" di Windows. Tuttavia, esistono delle "vesti alternative" che possiamo dare, sia alla spedizione, sia alla ricezione, analisi, ed elaborazione, dei messaggi stessi, e a volte queste alternative possono offrire una certa comodità. È dunque giunto il momento di prendere nuovamente in considerazione il tema "messaggi", cosa che faremo nei prossimi due capitoli.


Capitolo 9: problemi di struttura
Capitolo 11: macro e API "di comodo"
Elenco dei capitoli