Corso di Visual C++

La gestione degli eventi di base

 

In questa puntata volevo parlarvi dell'ambiente di compilazione e spiegare cosa sono le intestazioni precompilate, come compilare un progetto in modalità di Debug e Release e come gestire le risorse grafiche, ma ho desistito e così cominciamo a parlare di 'cose pratiche'.

Quando se ne presenterà l'occasione, torneremo su questo argomento, ma parlarne ora avrebbe fatto sembrare il corso di sola teoria, mentre so che voi non vedete l'ora di vedere le vostre applicazioni sugli scaffali dei negozi!

Ok, rimbocchiamoci le maniche, c'è lavoro per tutti ...ehmm, l'ho già sentita. ;-)

Nella seconda puntata abbiamo analizzate le prime operazioni con le finestre e abbiamo visto come la funzione OnDraw della vista si occupa della visualizzazione dell'output sia su schermo che su stampante.

Ora vedremo come far sì che l'applicazione risponda ai messaggi che vengono inviati a causa di azioni dell'utente.

Innanzitutto analizziamo in dettaglio la dichiarazione di un gestore di messaggio.

Tutte i nomi di funzione che il Wizard genera sono in corrispondenza con i nomi dei messaggi; il suffisso WM_ del nome viene sostituito con il suffisso On nella funzione, segue quindi il nome del messaggio in minuscolo, ma con le iniziali di ogni parola in maiuscolo.

Ad esempio la funzione OnLButtonDown è in risposta al messaggio WM_LBUTTONDOWN e che è generato dal clic sul tasto sinistro del mouse, allo stesso modo OnTimer è associata a WM_TIMER e OnInitialUpdate a WM_INITIALUPDATE.

Se cercate il messaggio che genera OnDraw, non lo troverete. Ciò è dovuto al fatto che questa funzione non è una Windows Message Handler (funzione di risposta a messaggi), ma è una funzione virtuale pura che snellisce le operazioni di output verso le finestre.

Il fatto che sia una funzione virtuale pura significa che deve essere obbligatoriamente implementata, in caso contrario non c'è una versione della classe base da richiamare e quindi non è possibile l'operazione di disegno in risposta alle richieste dell'applicazione. Ciò non toglie che possiamo effettuare operazioni di disegno da altre funzioni, ma se seguiamo il Framework riusciremo probabilmente a scrivere codice più pulito e soprattutto manutenibile.

Per capire cosa fa realmente OnDraw diciamo che questa 'di nascosto' risponde al messaggio WM_PAINT, inoltra il messaggio WM_ERASEBKGND di cancellazione dello sfondo, poi richiama la funzione BeginPaint della GDI.

A questo punto viene eseguito il codice inserito da voi in OnDraw(...) e quindi viene richiamata la funzione GDI EndPaint.

Si ricorda che i messaggi WM_PAINT sono a bassa priorità e non vengono prelevati dalla coda dei messaggi fintantoché ci sono altri messaggi in attesa.

Quando aggiungiamo un gestore di messaggio alla nostra classe, vediamo che il Wizard genera codice sparso apparentemente ovunque, vediamo dove e a cosa serve.

Prima però aggiungiamo (se non l'avete già fatto) la funzione di risposta al messaggio WM_LBUTTONDOWN alla nostra vecchia applicazione Prg2, per procedere utilizziamo ClassWizard (dal menu Edit o con Ctrl+W).

Selezionare come ClassName CPrg2View, poi l'object ID: CPrg2View e quindi sulla destra il succitato messaggio WM_LBUTTONDOWN, ora cliccare su Add Function e confermare il tutto con Ok.

Innanzitutto nel file di intestazione ( .h) viene creato il prototipo del messaggio che nel nostro esempio è:

afx_msg void OnLButtonDown (UINT nFlags, CPoint point);

tale prototipo viene inserito tra due MACRO:

//{{AFX_MSG(CPrg2View)
...
//}}AFX_MSG

DECLARE_MESSAGE_MAP()

Queste MACRO sono dei 'placeholder' ( segnaposto) per il Wizard ed identificano l'inizio e la fine della dichiarazione della mappa dei messaggi; allo stesso modo nel file di implementazione ( .cpp ) troviamo il seguente blocco:

BEGIN_MESSAGE_MAP(CPrg2View, CView)

//{{AFX_MSG_MAP(CPrg2View)

ON_WM_LBUTTONDOWN()

//}}AFX_MSG_MAP

// Standard printing commands

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

END_MESSAGE_MAP()

Tale blocco, come vediamo, contiene di default anche le specifiche per le implementazioni delle funzioni di stampa.

Il Wizard riesce a mappare per noi la maggioranza dei messaggi standard del Windows, ma per quelli che sono esclusi si può vedere la specifica nell'help in linea ed aggiungere il codice all'esterno dei commenti //}}AFX_MSG, ma prima delle macro DECLARE_MESSAGE_MAP() nel file Header ed END_MESSAGE_MAP() nel file .cpp.

 

Torna all'indice Generale del corso di Corso di Visual C++ di Software Planet