Usare il menu di contesto come fatto nella puntata precedente non
è la prassi, solitamente si preferisce posizionare tale oggetto
in dipendenza della posizione del puntatore del mouse.
Per fare ciò si potrebbe pensare di posizionare il menu quando arrivano
messaggi di tipo WM_RBUTTONDOWN, ma è meglio utilizzare lapposito
messaggio WM_CONTEXTMENU. Agganciando questo messaggio si ha infatti
la possibilità oltre che di conoscere le coordinate di schermo
del click anche di sapere la finestra dalla quale questultimo
è partito.
Ecco il prototipo di OnContextMenu:
afx_msg void OnContextMenu
(CWnd *pWnd, CPoint point)
Se si vogliono trasformare le coordinate di schermo in coordinate
client, si può utilizzare la funzione CWnd::ScreenToClient.
Ricordo che le coordinate client valgono (0,0) nellangolo
superiore sinistro della finestra client, mentre quelle schermo
valgono (0,0) nellangolo superiore sinistro dello schermo.
Se richiamiamo ScreenToClient tramite il puntatore pWnd
avremo le coordinate client rispetto alla finestra dalla quale è
partito il click.
Quando elaboriamo un messaggio WM_CONTEXTMENU e vediamo che questo
non ci interessa (ad esempio perché il click è avvenuto allesterno
dellarea client) è bene ripassare il messaggio alla classe
base per le implementazioni di default.
Proviamo a modificare lultimo progetto che abbiamo create
(Prg36) e ad aggiungere lhandler per visualizzare il menu
di contesto quando si fa click con il tasto destro del mouse nellarea
client della classe di vista. Al momento del click vogliamo che
sia visualizzato un menu popup simile al menu a discesa Color.
Per prima cosa dobbiamo aggiungere tramite Class Wizard lhandler
al messaggio WM_CONTEXTMENU; facciamo attenzione di non aggiungerlo
alla classe finestra CMainFrame, ma alla classe vista CPrg36View.
Fatto questo ci ritroveremo davanti il seguente scheletro di funzione
da modificare:
void CPrg36View::OnContextMenu(CWnd*
pWnd, CPoint point)
{
// TODO: Add your message handler code here
}
Ciò che dovremo fare in poche parole è vedere se il click è stato
effettuato allinterno della nostra area client, in caso positivo
visualizziamo il menu di contesto altrimenti passiamo il messaggio
alla classe base.
Ecco il codice della funzione:
void CPrg36View::OnContextMenu(CWnd* pWnd, CPoint point)
{
CRect clientRect;
GetClientRect (&clientRect);
ClientToScreen (&clientRect);
if (clientRect.PtInRect(point))
{
CMenu *pContextMenu = AfxGetMainWnd()->GetMenu()->GetSubMenu(3);
pContextMenu->TrackPopupMenu (TPM_LEFTALIGN
| TPM_LEFTBUTTON, point.x, point.y, AfxGetMainWnd());
return;
}
CView::OnContextMenu(pWnd,point);
}
Qui ci sono varie cosette da spiegare, niente di difficile, ma da
capire.
Le prime tre righe di codice non fanno altro che recuperare larea
client della finestra e trasformare le sue coordinate in coordinate
di schermo.
Fatto questo verifichiamo che il punto del click ricada allinterno
del rettangolo, in caso negativo passiamo il messaggio alla classe
base, altrimenti continuiamo.
Tanto per iniziare dobbiamo recuperare il puntatore al menu Color.
Sembrerebbe naturale usare semplicemente GetMenu, ma in effetti
non possiamo farlo visto che il menu non appartiene alla classe
vista, ma alla classe finestra.
Questa è la prima volta che tentiamo di accedere alloggetto
di un classe di unapplicazione Documento-Vista da unaltra
classe della stessa applicazione.
Per fare ciò ci viene nuovamente in aiuto MFC mettendoci a disposizione
la funzione globale (non appartenente ad alcuna classe) AfxGetMainWnd();
questa funzione ritorna il puntatore alloggetto finestra principale
di tipo CMainFrame.
Grazie poi a questo puntatore riusciremo ad accedere ai menu della
finestra ed a recuperare quindi un puntatore al menu popup Color.
La successiva istruzione visualizza il menu sullo schermo e fa si
che i messaggi del mouse invece di essere inviati alla nostra finestra
(avremmo usato il puntatore this) vengano inviati alla classe
finestra principale utilizzando nuovamente la funzione AfxGetMainWnd().
Questo è quanto per visualizzare un menu di contesto, non bisogna
neanche utilizzare CMenu::Detach visto che quando la funzione
esce dallo scope il menu non è più visibile sullo schermo ed è stato
già distrutto. Questo perché TrackPopupMenu non ritorna fino
a quando non si fa scomparire il menu (o con un click su una voce
o con un click allesterno dellarea definita dal suo
ultimo parametro LPRECT).
Abbiamo finito con questa lezione la lunga trattazione sui menu.
Come avrete potuto notare sarebbero potute bastarne anche una sola
o due, ma abbiamo sviscerato largomento in profondità ed ora
con un po di esercizio ne diventeremo pienamente padroni.
Lunico argomento non trattato è quello dei menu Owner-Drawn,
cioè i menu personalizzati con bitmap e fronzoli vari. Chi volesse
approfondire questo argomento sarà sicuramente bravo da riuscire
a farcela con il solo aiuto della MSDN.
Clicca qui
per scaricare il progetto Prg36.
Clicca qui
per scaricare direttamente leseguibile Prg36.exe
A presto e alla prossima puntata dove affronteremo un argomento
completamente nuovo a sorpresa!
Torna all'indice Generale del corso di Corso di Visual C++ di Software Planet