Corso di Visual C++

I menu dinamici

 

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