La selezione corrente del listbox (un singolo elemento, ovvero un numero variabile di elementi, secondo lo stile del listbox) viene normalmente stabilita secondo le interazioni dell'utente, ma può anche essere diretta da programma.
Il
messaggio LB_SETCURSEL
(con l'indice
dell'elemento da selezionare, come al solito, in
wParam
), che funziona solo con liste
a selezione singola, seleziona l'elemento indicato
(e, se non era fra quelli nella zona visibile della
lista, fa scorrere la lista stessa in modo che
lo diventi). Alternativamente, si può
usare anche LB_SELECTSTRING
, che
praticamente congiunge le funzioni di
LB_FINDSTRING
e LB_SETCURSEL
(esso ha gli stessi parametri e valore di ritorno di
LB_FINDSTRING
).
Per liste a selezione multipla, invece,
si usa LB_SETSEL
: in wParam
,
0 per togliere la selezione, 1 per metterla; in
lParam
, l'indice dell'elemento cui
metterla o toglierla (ovvero, -1 per toglierla o
metterla a tutti gli elementi simultaneamente).
(LB_SELITEMRANGE
e
LB_SELITEMRANGEEX
sono delle piccole
ottimizzazioni che consentono di mettere o
togliere la selezione a un gruppo contiguo di
elementi in un sol colpo).
Per chiedere alla listbox informazioni sulla
sua selezione, se la listbox è a selezione
singola, si usa il messaggio LB_GETCURSEL
,
che ritorna l'indice dell'unico elemento selezionato
(LB_ERR
, se nessun elemento è
selezionato).
Per liste a selezione multipla,
invece, si usa LB_GETSELCOUNT
per
ottenere il numero totale di elementi selezionati,
e LB_GETSELITEMS
per avere l'elenco
degli elementi stessi (passando, in wParam
,
il numero massimo che si è preparati ad
accettare, e in lParam
l'indice di
un nostro buffer, un array di interi, che verrà
riempito con l'informazione richiesta); il valore
di ritorno è il numero di interi posti nel
buffer. Alternativamente, LB_GETSEL
,
con l'indice dell'elemento di interesse in
wParam
, permette di sapere se un
dato elemento è selezionato o meno (se
non lo è, il valore di ritorno sarà
zero).
Anche lo stato dello scorrimento (scroll) di una
listbox è normalmente modificato dalle
azioni dell'utente (in questo caso, dalla sua
interazione con la scroll-bar del listbox); anche
qui, naturalmente, lo si può stabilire
anche da programma, spedendo al listbox il
messaggio LB_SETTOPINDEX
, avendo
in wParam
l'indice dell'elemento
che si vuole mostrare in cima alla lista. Per
chiedere quale sia l'elemento attualmente in
cima alla lista, si userà il messaggio
LB_GETTOPINDEX
, che naturalmente
restituisce questa informazione come valore di
ritorno.
Per sapere se l'elemento con un certo indice
è attualmente visualizzato (e, se
sì, a quali coordinate entro l'area
del controllo listbox) si usa il messaggio
LB_GETITEMRECT
, passando, in
wParam
, l'indice dell'elemento,
e, in lParam
, l'indirizzo di
una nostra struttura di tipo RECT
che verrà riempita con le informazioni
richieste.
Il valore di ritorno da questo messaggio sarà
1
se l'elemento è
visualizzato, 0
in caso contrario
(cioè se l'elemento è al di fuori
della zona dell'elenco attualmente in vista),
-1
(cioè LB_ERR
)
in caso di errore; nel RECT
passato,
vengono comunque poste le coordinate-cliente che
sarebbero usate se l'elemento
fosse mostrato, anche se esse sono in realtà
fuori dall'area attualmente in vista (ad
esempio, campi top
e bottom
negativi, se l'elemento è "sopra"
al primo attualmente visualizzato). Si noti che
la "larghezza" indicata per la visualizzazione
corrisponde sempre e comunque all'intera
larghezza della listbox (o della sua colonna in
cui si trova l'elemento, per listbox a molteplici
colonne), anche se l'elemento è in
realtà più stretto, ovvero
più largo.
Notiamo che, con questi messaggi, abbiamo quanto ci serve per garantire che l'ultima stringa appena aggiunta in coda alla lista sia sempre visualizzata: basterà fare qualcosa tipo...
WM_SETREDRAW
, che,
come abbiamo accennato, fanno commutare il bit di stile
LBS_NOREDRAW
della lista. Per garantire il
"ridisegno" finale, occorrerà anche una chiamata
all'API InvalidateRect
alla fine di
questa sequenza.
In alternativa, naturalmente, possiamo scegliere di
calcolare quale dovrà essere il primo
elemento della lista, al fine di garantire che il
nuovo elemento appena aggiunto venga visualizzato
all'ultimo posto. Per fare questo senza perdita di
generalità, bisogna calcolare anche quanti
elementi la lista sia in grado di mostrare allo stesso
tempo (questo, naturalmente, è abbastanza
più difficile per liste owner-drawn con
elementi ad altezza variable, caso nel quale vi
suggerisco decisamente l'approccio più semplice
che ho appena delineato!). Si può partire
dal "top-index", verificando quanti elementi a
cominciare da esso siano visibili. Questo calcolo,
naturalmente, occorre farlo solo una volta per ogni
listbox (se non se ne modifica la dimensione), e si
può poi registrare il risultato come
proprietà della finestra del controllo
(con la solita API SetProp
, che già
abbiamo visto in precedenza).
Questa ottimizzazione è un esercizio molto istruttivo, ed essa può sicuramente venire raccomandata al lettore, anche perchè da questo, se ben svolto, con la dovuta attenzione alla generalità, risulterà un altro utile "mattoncino" di codice riusabile da aggiungere alla propria "piccola libreria personale". Noi, qui, però, non tratteremo più oltre di questa possibilità; pensiamo, infatti, che il primo difettuccio che riscontrammo nel programmillo di "output di debug" possa con queste considerazioni ritenersi già rimediato.
Rimuovere il secondo difettuccio, quello relativo allo scrolling orizzontale, non sarà proprio altrettanto banale, e quindi rimandiamo questo compito al prossimo capitolo.
Capitolo 43: messaggi ai LISTBOX (1)
Capitolo 45: scorrimento orizzontale
Elenco dei capitoli