Corso di Visual C++

Il menu di contesto II

 

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 l’apposito 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 quest’ultimo è 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) nell’angolo superiore sinistro della finestra client, mentre quelle schermo valgono (0,0) nell’angolo 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 all’esterno dell’area client) è bene ripassare il messaggio alla classe base per le implementazioni di default.

Proviamo a modificare l’ultimo progetto che abbiamo create (Prg36) e ad aggiungere l’handler per visualizzare il menu di contesto quando si fa click con il tasto destro del mouse nell’area 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 l’handler 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 all’interno 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 l’area client della finestra e trasformare le sue coordinate in coordinate di schermo.

Fatto questo verifichiamo che il punto del click ricada all’interno 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 all’oggetto di un classe di un’applicazione Documento-Vista da un’altra 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 all’oggetto 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 all’esterno dell’area 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 l’argomento in profondità ed ora con un po’ di esercizio ne diventeremo pienamente padroni. L’unico 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 l’eseguibile 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