Corso di Visual C++

I menu dinamici II

 

Riprendiamo brevemente l’ultimo stralcio di funzione scritto nella scorsa puntata:

void CMainFrame::OnModificaEliminahelp()
{
            CMenu *pMenu= GetMenu();
           
            pMenu->DeleteMenu(4, MF_BYPOSITION);
                       
            DrawMenuBar();
}

Tutte le operazioni effettuate con i menu a runtime, devono essere effettuate tramite oggetti CMenu. Questi offrono molte funzioni membro per le più disparate operazioni.
La prima operazione effettuata in OnModificaEliminahelp , GetMenu, serve per recuperare il puntatore alle voci di menu di livello superiore.
Poi con DeleteMenu abbiamo eliminato esattamente la voce di posizione 4; ricordo che la numerazione è in base 0, cioè la prima voce è 0, la seconda è 1...

Per riferire i menu i metodi sono solo 2: o per posizione con MF_BYPOSITION o per ID con MF_BYCOMMAND. E' evidente che il secondo metodo in questo caso non sia possibile perché le voci di menu di livello superiore non hanno un identificatore numerico.
Usando DeleteMenu abbiamo eliminato tutto il menu popup, cioè la voce di menu di livello superiore e tutte le sottovoci. Se avessimo voluto eliminare solo la voce di menu principale avremmo dovuto usare RemoveMenu.
In questo caso avremmo potuto usare il menu popup rimasto in vita per usarlo ad esempio come menu di contesto.
Nell'esempio precedente abbiamo poi infine chiamato DrawMenuBar per ridisegnare le voci di menu, questo operazione deve essere sempre effettuata quando la finestra è visibile sullo schermo; per curiosità vedete cosa succede se la commentate provando poi a ricompilare.
Altra curiosità: dopo aver eliminato la voce, scambiate il menu con l'altro creato e poi passate nuovamente al primo: vedrete che la voce dell'Help ricompare, vi sapete spiegare perché?
La ragione non è molto complessa.

Infatti quando scambiate i menu quello che fate è ricaricare le risorse linkate staticamente al codice, quindi tutte le successive modifiche effettuate dinamicamente a runtime (a tempo di esecuzione) vengono annullate. Il problema è ovviamente aggirabile creando un membro di dati di tipo CMenu e caricandovi poi il menu da modificare; successivamente dopo lo scambio verrà visualizzato quest’ultimo e non quello preso dalle risorse.
Vediamo ora come cancellare ad esempio la voce ProvaMenu del menu Visualizza; a tale scopo in questo menu aggiungiamo col Wizard una voce EliminaProvaMenu e la linkiamo al codice:

void CMainFrame::OnVisualizzaEliminaprovamenu()
{
            CMenu *pMenu= GetMenu()->GetSubMenu(2);
           
            pMenu->RemoveMenu(ID_VIEW_PROVAMENU, MF_BYCOMMAND);
                       
            DrawMenuBar();
           
}


In questo caso, come nel precedente, ci dovremmo assicurare che la funzione non venga richiamata 2 volte.

Qui abbiamo usato anche GetSubMenu, questa funzione recupera il puntatore al menu popup di indice specificato dal puntatore al menu principale. Grazie a questa funzione è possibile recuperare i puntatori di menu a cascata di qualsiasi profondità: basta mettere una chiamata di funzione per ogni livello.
Ad esempio: GetMenu()->GetSubMenu(9)->GetSubMenu(0)->GetSubMenu(1) restituisce il puntatore ad un oggetto CMenu posto al terzo livello di profondità. Un esempio di menu con molti popup a diverse profondità è il menu Start (Avvio) di Windows: questo contiene ad esempio in cascata i popup Programmi -> Accessori -> Comunicazioni.
Ritornando al codice sopra evidenziato, dopo aver recuperato il puntatore al popup abbiamo rimosso la voce riferendola tramite il suo identificatore e quindi abbiamo ridisegnato il menu.
Quando si opera dinamicamente con i menu è preferibile, ove possibile, riferire le voci tramite il loro identificatore e non per posizione. Infatti aggiungendo e rimuovendo voci, la loro posizione può cambiare e si può rischiare di fare errori evitabili da subito usando funzioni più appropriate.

E' anche possibile modificare una voce di menu: data la sua posizione o il suo identificatore gli si può assegnare un nuovo identificatore ed una nuova Caption.
Ad esempio se miomenu è un oggetto di tipo CMenu che punta ad un menu popup, i seguenti enunciati operano su alcune sue voci:

miomenu.ModifyMenu (ID_VECCHIO, MF_STRING | MF_BYCOMMAND, ID_NUOVO, "Nuovo Nome");

miomenu.ModifyMenu (POSIZIONE, MF_STRING | MF_BYPOSITION, ID_NUOVO, "Nuovo Nome");

Il primo enunciato accede alla voce per ID, sostituisce questo con quello nuovo specificato in ID_NUOVO e cambia il nome della voce in Nuovo Nome; analogamente il secondo enunciato, ma accede alla voce per posizione.

Se al posto di MF_STRING viene specificato MF_SEPARATOR, la vecchia voce viene trasformate in un segno di separatore (la linea orizzontale), ed i successivi parametri vengono ignorati.
Se inoltre invece di MF_STRING viene specificato MF_POPUP la voce viene trasformata in un popup (appare la freccia verso destra ad indicare un sottomenu) ed il parametro successivo invece di essere l'ID della voce deve essere l'handle al nuovo menu popup.

In or al secondo parametro è possibile anche settare lo stato della voce: MF_CHECKED, MF_DISABLED, MF_GRAYED... per vedere più in dettaglio questa funzione, consultare l'MSDN.


Vi rimando nuovamente alla prossima puntata per continuare la trattazione di questo interessantissimo argomento.

 

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