Vediamo adesso l'ultima aspetto per quanto riguarda la programmazione
dei menu: la gestione dinamica.
Gestire un sistema dinamico di menu consiste essenzialmente nel
creare o modificare dei menu in base allo stato interno dell'applicazione
o in base a particolari comandi impartiti dall'utente.
Per prima cosa vediamo come scambiare il menu che compare all'avvio
del nostro programma con un'altro creato staticamente.
In questo caso il nuovo menu è stato creato con l'editor delle risorse
e pertanto ha un suo identificatore di risorsa.
Come esempio creiamo un menu e come ID diamogli IDR_MIOMENU2, in
questo creiamo una voce 'Torna al primo menu' e come ID lasciamo
quello che propone in automatico il Wizard. (basta settare Caption
senza scegliere alcun ID)
Nel menu principale dell'applicazione, sotto la voce Visualizza,
mettiamo un voce 'Vai al secondo menu' e lasciamo l'ID di
default.
Ora create l'handler di questo ultimi ID e inseriteli nella classe
di default CMainFrame.
Quando state creando l'handler per la voce di menu del secondo
menu, il Wizard vi avverte che è stata trovata una nuova risorsa
e vi permette di associarla ad una nuova classe o ad una già esistente.
Voi scegliete la seconda possibilità e associate la risorsa a CMainFrame.
Ecco ora come appariranno le due funzioni appena create:
void CMainFrame::OnVisualizzaVaialsecondomenu()
{
CMenu menu;
menu.LoadMenu(IDR_MIOMENU2);
SetMenu(&menu);
menu.Detach();
DrawMenuBar();
}
void CMainFrame::OnMiomenuTornaalprimomenu()
{
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();
DrawMenuBar();
}
Ogni funzione crea un oggetto di tipo CMenu, lo inizializza
con le voci che voi avete creato con l'Editor, lo imposta come menu
corrente dell'applicazione e poi fa il Detach.
Il Detach è essenziale perché permette al nuovo menu di restare
in vita anche quando l'oggetto menu di tipo CMenu esce dallo
scope e viene distrutto. Infatti normalmente la classe CMenu
chiama DestroyMenu nel suo distruttore e quindi l'oggetto
non esiste più quando esce dallo scope. Con Detach invece
il menu viene associato alla finestra e quando questa verrà distrutta,
il suo distruttore penserà automaticamente anche a chiamare DestroyMenu.
In tal modo non si avrà alcuna perdita di memoria, i famigerati
memory leaks.
Come regola generale è consigliabile chiamare DestroyMenu
per i menu caricati con LoadMenu prima che il menu esca dallo
scope, sempre e solo nel caso in cui non si utilizzi il Detach.
DrawMenuBar() provvede a ridisegnare il menu sullo schermo
ed è molto importante richiamarla quando la finestra con il menu
da ridisegnare è visibile sullo schermo, in caso contrario (se non
è visibile) avviene in automatico al ricevimento del primo messaggio
di ridisegno quando questa ridiventerà visibile.
Con questa procedura è possibile scambiare, in fase di esecuzione,
i menu dell'applicazione. Questo può risultare utile per adattare
i menu al contesto, oppure ad esempio per utilizzare dei menu lunghi
o corti in base alle preferenze dell'utente.
Se insieme al nuovo menu, volete caricare anche una diversa tabella
degli acceleratori da tastiera, potete usare il seguente enunciato:
LoadAccelTable(MAKEINTRESOURCE( IDR_MAINFRAME ));
con al posto di IDR_MAINFRAME l'identificatore che avete scelto
per la nuova tabella.
Vediamo ora invece come manipolare dinamicamente le voci di menu.
Dell'esempio precedente proviamo ad esempio ad eliminare la voce
di menu di livello superiore ? dopo aver selezionato la voce
di menu EliminaHelp dal menu Modifica.
Innanzitutto creiamo questa voce di menu e agganciamo l'handler
al messaggio WM_COMMAND.
In questa funzione quello che dovremo fare è recuperare un puntatore
di tipo CMenu al menu dell'help e quindi eliminare quest'ultimo
e ridisegnare le voci:
void CMainFrame::OnModificaEliminahelp()
{
CMenu *pMenu= GetMenu();
pMenu->DeleteMenu(4, MF_BYPOSITION);
DrawMenuBar();
}
Dovremmo anche assicurarci che questa funzione non venga richiamata
una seconda volta, infatti tenterebbe di cancellare una voce inesistente.
Per tale scopo dovremmo sincronizzare lo stato della voce con quello
dell'applicazione: esercizio da fare per casa :) e a cui non darò
soluzione; voi provateci!
Alla prossima puntata per continuare la trattazione di questo argomento.
Torna all'indice Generale del corso di Corso di Visual C++ di Software Planet