Abbiamo visto che, benchè fra gli stili del
nostro controllo list-box avessimo specificato anche
il bit WS_HSCROLL
, la desiderata
barra di scorrimento orizzontale non voleva saperne
di apparire, anche quando inserivamo il lista delle
stringhe molto lunghe. Perchè no...? E, come
potremmo rimediare...?
La risposta alla prima delle due domande è
abbastanza semplice. Il controllo listbox mantiene
un suo valore di "ampiezza (massima) corrente", ma
non lo aggiorna automaticamente; dobbiamo essere
invece noi ad aggiornarlo, esplicitamente, inviando
al controllo un messaggio LB_SETHORIZONTALEXTENT
,
con l'ampiezza in pixel in wParam
.
Possiamo anche chiedere al controllo quale sia la
sua ampiezza corrente, spedendogli un messaggio
LB_GETHORIZONTALEXTENT
, che ci
restituirà l'ampiezza stessa come valore
di ritorno. Se facciamo ad un listbox appunto
questa richiesta, senza avere fatto aggiornamenti
alla sua ampiezza con LB_SETHORIZONTALEXTENT
,
vedremo che, per default, la "ampiezza corrente"
del controllo listbox è sempre 0
;
non c'è da stupirsi, dunque, se la barra
di scorrimento orizzontale non viene visualizzata.
In linea di principio, anche la risposta alla seconda domanda non è poi tanto astrusa; in pseudo-codice, potremmo dire:
Questa sequenza potremo, naturalmente, incapsularla in una nostra funzione riusabile, o anche implementarla, come risposta ad un qualche opportuno messaggio, in una nostra versione subclassata (o "superclassata", una variante che esamineremo più avanti) del controllo standard list-box.
Resta un solo problemino sostanziale...: come realizzare la funzioncina che, in termini di pseudo-codice, abbiamo così semplicemente espresso come "scopri la larghezza in pixel della stringa"...?
La funzionalità necessaria per scoprire quanti pixel di larghezza richiederà una stringa è una funzione "grafica", che dipende da un device context (un "contesto di dispositivo", concetto che già abbiamo incontrato in precedenza nello sviluppo del nostro programmino per il grafico di una funzione) e dal font selezionato in quel contesto. Di conseguenza, per potere ottenere questa informazione dobbiamo prima ottenere un device context per il controllo list-box, e selezionarvi il giusto font (e, alla fine, dovremo rimettere a posto il font e liberare il device context).
Ecco, dunque, una funzione che incapsula appunto tutte queste operazioni...:
L'API GetDC
prende come parametro un'HWND
,
e ritorna l'HDC
corrispondente alla finestra (esso va
poi rilasciato con l'API ReleaseDC
, come nella
funzione qui elencata).
Il messaggio WM_GETFONT
chiede a una finestra (qui,
al listbox) quale sia il suo font; il valore di ritorno è
(va sottoposto naturalmente a cast...) un HFONT
, che
NON dovremo distruggere (ci penserà la finestra,
quando a sua volta verrà distrutta). Se la finestra "non
ha nessun font particolare" (cioè, se ricorre al font di
default di sistema), in risposta al messaggio tornerà 0
(in questo caso, evitiamo i passi descritti al prossimo
paragrafo).
L'API SelectObject
accetta come primo parametro un
HDC
, e, come secondo, la handle di un oggetto
grafico (una penna, un pennello, un font...); essa inserisce
l'oggetto stesso come "oggetto corrente" di quel tipo in quel DC, e
torna la handle del precedente "oggetto corrente" dello stesso
tipo nello stesso DC. Qui, chiamiamo questa API (se la
chiamiamo) passandole come secondo
parametro la handle di un font, quindi essa imposta il font
corrente del DC, e ritorna l'HFONT
(anche qui,
naturalmente, ci serve un cast!) del font che era in
precedenza selezionato in quel DC; dobbiamo conservarci
quest'ultima handle (qui, lo facciamo nella variabile locale
hfOld
), al fine di re-inserirla (sempre con
SelectObject
) nel DC, per ripristinarlo allo
stato di riposo, prima di rilasciarlo.
Tutte queste API, molto generali, potranno servirci, in
altri casi, anche a scopi piuttosto diversi. Le altre
due API usate in questa funzione sono invece specializzate
ai problemi di misurazone delle stringhe e dei font.
La GetTextMetrics
prende come primo
parametro un HDC
, e come secondo parametro
l'indirizzo di una nostra struttura di tipo
TEXTMETRIC
, che riempie con abbondanza
di dati relativi alle dimensioni del font attualmente
selezionato nel DC stesso. Qui, ci serve, fra tutti i
suoi campi, il solo campo tmAveCharWidth
,
che ci dà l'ampiezza media, in pixel, di un
carattere; è questo proprio il numero di pixel
che dobbiamo sommare alla misurazione ritornata
dall'API GetTextExtentPoint32
, per avere
un decente "margine" di spazio bianco per la
visualizzazione. Quest'ultima API, dato un DC e
una stringa (indirizzo e lunghezza), riempie una
struttura SIZE
, ponendo nei suoi due
campi cx
e cy
rispettivamente
l'ampiezza e l'altezza in pixel della stringa
così come verrà disegnata nel font
attualmente selezionato in quel DC.
Notiamo, naturalmente, che questa funzione
extent
, che abbiamo qui sviluppato,
è perfettamente generale, e si applica al
calcolo dell'ampiezza in pixel di una stringa in
una qualsiasi finestra; per avere
una generalità ancora un poco maggiore, si
potrebbe al massimo prevedere di accettare anche
un'HFONT
come parametro, per coprire
il caso in cui vorremo poi disegnare la stringa in
un font diverso da quello della finestra (lasciamo
al lettore questa ulteriore generalizzazione,
limitandoci a ricordargli che l'arte del progetto
di buon codice riusabile sta anche nel decidere
quali generalizzazioni non inserire...:-).
Ecco, comunque, la funzione completa di "aggiungi questa stringa in coda a questo listbox", che tiene conto delle problematiche di scorrimento, sia verticale, sia orizzontale:
Notiamo che occorrono anche precauzioni "analoghe ma inverse" per la corretta gestione della barra di scorrimento orizzontale a fronte della rimozione di stringhe dal listbox; se viene rimossa la stringa più lunga, si deve aggiornare l'"extent orizzontale" del listbox. Questa funzionalità non ci serve per il nostro programmino di "output di debug" (poichè in esso non cancelliamo mai stringhe dal listbox), e quindi la lasciamo alla buona volontà del lettore; per realizzarla in modo ragionevolmente ottimizzato, sarà necessario fare uso d'ingegno, e di strutture dati non banali.
Oltre a questi svariati usi per mostrare elenchi di stringhe, i controlli list-box, come già abbiamo accennato, possono tornare utili per mostrare elenchi di grafica del tutto arbitrari, grazie alle funzionalità di "owner-drawn". Esemplificheremo uno di questi usi, e coglieremo l'occasione di esaminare altre API, nel prossimo capitolo.
Capitolo 44: messaggi ai LISTBOX (2)
Capitolo 46: un elenco di bitmap
Elenco dei capitoli