CURS 7
Meniurile unei aplicatii
Definire termeni
top-level menu = bara de meniu ce apare in partea superioara a ferestrei;
drop down menu;
articole din meniu identificate printr-un ID;
selectia unui articol din meniu = executie comanda;
meniuri popup = meniuri contextuale (WM_CONTEXTMENU)
Meniu sistem
redimensionare;
mutare;
minimizare;
maximizare;
inchidere fereastra;
restaurare;
MFC - CMenu - Creare
HMENU = tip de data ce gestioneaza un meniu in Windows; m_hMenu definit in clasa;
Cream un obiect CMenu pe stiva;
Apelam functii membru pentru a lucra cu meniul;
Apelam CWnd::SetMenu() pentru a seta meniul la fereastra;
Apelam functia membru Detach().
Exemplu
CMenu m_Menu;
m_Menu.LoadMenu(IDR_MENU);
SetMenu(&m_Menu);
m_Menu.Detach();
IDR_MENU ID-ul resursei ce descrie meniul;
Exemplu de resursa meniu
IDR_MENU MENU PRELOAD DISCARDABLE
BEGIN
POPUP “&File”
BEGIN
MENUITEM “&New\tCtrl+N”, ID_FILE_NEW ...
MENUITEM SEPARATOR END
POPUP “&View”
BEGIN ...
END END
Functii membru CMenu
BOOL Attach( HMENU hMenu );
Valoarea returnata != 0 pentru succes; altfel 0.
Parametrii: hMenu Specifica un handle la un meniu Windows.
Observatii: Ataseaza un meniu Windows existent la un obiect CMenu. Daca meniul este atasat functia nu trebuie apelata. Handle-rul meniului este memorat in data membru m_hMenu.
Daca meniul este deja asociat cu o fereastra, putem folosi functia CWnd::GetMenu pentru a obtine handle la meniu.
Examplu: (pWnd pointer la fereastra, vezi AfxGetMainWnd()) CMenu meniu;
HMENU hMeniu = pWnd->GetMenu( );
meniu.Attach( hMeniu );
Functii membru CMenu
HMENU Detach( );
Valoarea returnata: handle, de tip HMENU, la un meniu Windows sau NULL daca operatia esueaza.
Observatie: Detaseaza un meniu Windows dintr-un obiect CMenu si returneaza handle-rul. Data
membru m_hMenu este setata pe NULL.
CMenu::FromHandle
static CMenu* PASCAL FromHandle( HMENU hMenu );
Valoarea returnata: un pointer la un CMenu; poate fi permanent sau temporar.
Parametrii: hMenu handle la un meniu.
Observatii:
Daca un obiect CMenu nu este deja atasat la obiectul menu
Windows, se creaza un obiect temporar CMenu si se ataseaza. In general obiectele temporare nu pot fi memorate pentru a fi utilizate in viitor in aplicatie. Obiectele temporare sunt sterse cand nu exista mesaje in coada de mesaje a aplicatiei si atunci se executa functia OnIdle().
CMenu::GetSafeHmenu
HMENU GetSafeHmenu( ) const;
Returneaza HMENU continut in obiectul CMenu, sau valoarea NULL.
CMenu::CreateMenu
BOOL CreateMenu( );
Valoarea returnata != 0 in caz de succes; 0 in caz contrar.
Observatii: Se creaza un meniu si se ataseaza la obiectul CMenu.
Initial acest meniu este vid. Articolele de meniu pot fi adaugate folosind functiile membru AppendMenu sau InsertMenu.
Daca meniul este atribuit la o fereastra, este automat distrus cand fereastra este distrusa.
Inainte de terminare, o aplicatie trebuie sa elibereze resursele sistem asociate cu un meniu daca meniul nu este atribuit la o fereastra.
Eliberarea resurselor de meniu se face prin apelul functiei membru DestroyMenu. DestroyMenu este apelata de destructorul obiectului
CMenu::CreatePopupMenu
BOOL CreatePopupMenu( );
Valoarea returnata != 0 in caz de succes; 0 in caz contrar.
Observatii: Creaza un meniu pop-up si il ataseaza la un obiect CMenu.
Initial meniul este vid. Articole de meniu pot fi adaugate folosind functiile membru AppendMenu sau InsertMenu.
Aplicatia poate adauga meniul pop-up la un meniu existent sau un meniu pop-up.
Functia TrackPopupMenu poate fi folosita pentru a afisa un meniu contextual.
Aceleasi observatii privitoare la distrugerea meniului ca si la CreateMenu.
CMenu::LoadMenu
BOOL LoadMenu( LPCTSTR lpszResourceName );
BOOL LoadMenu( UINT nIDResource );
Valoarea returnata != 0 in caz de succes; 0 altfel.
Parametrii:
lpszResourceName Puncteaza la un sir de caractere terminat cu null, sir ce contine numele resursei de meniu ce trebuie incarcata.
nIDResource ID-ul meniului ce trebuie incarcat.
Observatii: Incarca o resursa de meniu din fisierul executabil al aplicatiei si o ataseaza la obiectul CMenu.
Aceleasi observatii privitoare la distrugerea meniului ca si la CreateMenu.
CMenu::LoadMenuIndirect
BOOL LoadMenuIndirect( const void*
lpMenuTemplate );
Parametrii: lpMenuTemplate puncteaza la un sablon de meniu ce contine o singura structura MENUITEMTEMPLATEHEADER si o
colectie de una sau mai multe structuri MENUITEMTEMPLATE.
CMenu::DeleteMenu
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
Valoarea returnata != 0 in caz de succes; 0 altfel.
Parametrii: nPosition Specifica articolul de meniu ce va fi sters, functie de valorile lui nFlags.
nFlags Determina pozitia articolului de meniu.
MF_BYCOMMAND = nPosition specifica ID-ul articolului de meniu.
MF_BYPOSITION = nPosition furnizeaza pozitia articolului de meniu. Prima pozitie este la index zero.
Observatii: Ori de cate ori un meniu al unei ferestre este schimbat, aplicatia trebuie sa apeleze functia CWnd::DrawMenuBar.
CMenu::AppendMenu
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
AppendMenu - parametri
nFlags = starea noului articol de meniu, cand este adaugat la meniu (MF_OWNERDRAW, MF_STRING,
MF_SEPARATOR).
nIDNewItem specifica fie ID-ul comenzii noului articol de meniu sau handle la un meniu pop-up (HMENU) daca
nFlags are valoarea MF_POPUP. Daca nFlags =
MF_SEPARATOR, atunci nIDNewItem este ignorat.
lpszNewItem = specifica continutul noului articol de
meniu. lpszNewItem se interpreteaza functie de valorile lui nFlags.
nFlags si lpszNewItem
MF_OWNERDRAW – vezi mesajele
WM_MEASUREITEM si WM_DRAWITEM. Valoarea asociata meniului se gaseste in itemData din structura ce insoteste mesajul WM_DRAWITEM. In acest caz
lpszNewItem contine o valoare pe 32 biti gestionata de aplicatie.
MF_STRING lpszNewItem contine textul comenzii.
MF_SEPARATOR lpszNewItem este ignorat.
pBmp Puncteaza la un obiect CBitmap ce va fi utilizat ca articol de meniu.
nFlags- starea articolelor de meniu
MF_CHECKED, MF_UNCHECKED MF_ENBLE, MF_DISABLE
MF_GRAYED
nFlags- starea articolelor de meniu
MF_MENUBARBREAK Plaseaza articolul pe o noua linie intr-un meniu static sau o noua coloana intr-un meniu pop-up. Coloana din noul meniu pop- up va fi separata de vechea coloana de o linie verticala.
MF_MENUBREAK Plaseaza articolul pe o noua linie in meniurile statice sau in o noua coloana in meniurile pop-up. Nu exista linie de divizare intre coloane.
MF_OWNERDRAW Articol desenat de proprietar. Optiunea nu este valida pentru articole de meniu top-level.
MF_POPUP Specifica faptul ca articolul de meniu are un meniu pop-up asociat cu el. Parametrul ID specifica un handle la un meniu pop-up ce va fi asociat cu articolul. Aceasta este folosita pentru adaugarea fie a meniurilor pop up top-level sau a unui meniu pop-up ierarhic la un articol de meniu pop-up.
Alte functii
CheckMenuItem Plaseaza sau sterge un check mark pe un articol de meniu intr-un meniu pop up.
CheckMenuRadioItem EnableMenuItem
GetMenuItemCount
CMenu::GetSubMenu
CMenu* GetSubMenu( int nPos ) const;
Valoarea returnata: un pointer la un obiect CMenu a carui data membru m_hMenu contine un handle la un meniul pop-up, daca un meniu pop-up exista la pozitia data; altfel NULL. Daca un obiect Cmenu nu exista, se creaya unul temporar. Pointerul returnat
nu poate fi memorat pentru utiliyari viitoare.
Parametrii: nPos – specifica pozitia meniului pop- up continuta in meniu. Indexul incepe cu zero.
CMenu::InsertMenu
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
Insereaza un nou articol de meniu la pozitia specificata de nPosition. Vezi MSDN pentru informatii suplimentare.
Asemanator cu AppendMenu.
CMenu::ModifyMenu
BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem =
NULL );
BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
Schimba un articol de meniu existent la pozitia specificata de nPosition. Aplicatia specifica noua stare a articolului
de meniu setand valori pentru nFlags.
Meniuri multiple
Create(NULL, _T(“aplicatie”));
m_menuLong.LoadMenu(IDR_LONGMENU);
m_menuShort.LoadMenu(IDR_SHORTMENU);
SetMenu(m_bShortMenu ? &m_menuShort,
&m_menuLong);
Comutarea intre meniuri
Ca raspuns la o comanda a utilizatorului:
m_bShortMenu = TRUE;
SetMenu(&m_menuShort);
DrawMenuBar();
Raspunsuri la comenzi din meniu
Cind utilizatorul selecteaza un art. de meniu
(bara de meniuri) fereastra primeste o serie de mesaje:
WM_INITMENU ce notifica ferestrei ca un articol al meniului superior (top-level menu item) a fost selectat.
Raspunsuri ...
WM_INITMENUPOPUP, locul unde ar putea fi actualizate (enable, disable, checked, etc.) articolele meniului, meniul inca nu este afisat.
Cind parcurgem articolele unui meniu drop down (popup), fereastra primeste mesajul
WM_MENUSELECT, mesaj ce poate fi tratat, in special in SDK, prin afisarea unui text in
bara de stare (scurt help).
Raspunsuri ...
WM_COMMAND trimis cind utilizatorul selecteaza un articol de meniu.
LOWORD(wParam) pastreaza ID-ul comenzii (art. din meniul popup) si in SDK se face
practic switch pe LOWORD(wParam) pentru a identifica care comanda a fost selectata.
Raspunsuri ...
In MFC se adauga in harta de mesaje a clasei respective macroul ON_COMMAND care trateaza mesajul WM_COMMAND.
ON_COMMAND are doi parametri, primul indica ID_ul comenzii, iar al doilea indica
handlerul comenzii (functia ce va fi apelata la selectia acestei comenzi).
Raspunsuri ...
Exemplu:
ON_COMMAND(ID_FILE_SAVE, OnFileSave);
Functiile ce trateaza comenzile de meniu nu nu au au parametriparametri si au tipul void.void
Intervale pentru comenzi
Sunt folosite pentru a trata un grup de articole de meniu cu o singura functie.
Pp. ca avem definit un meniu cu urmatoarele articole:Rosu, Verde, Albastru;
Pp. ca pastram culoarea intr-o variabila din clasa definita astfel:
int m_nCuloare; // 0 = R, 1 = V, 2 = A
Harta de mesaje
ON_COMMAND(IDM_ROSU, OnRosu);
ON_COMMAND(IDM_VERDE, OnVerde);
ON_COMMAND(IDM_ALBASTRU, OnAlbastru);
Observati ID-urile comenzilor din meniu.
Functiile ...
void CXView::OnRosu() { m_nCuloare = 0;}
void CXView::OnVerde() { m_nCuloare = 1;}
void CXView::OnAlbastru() { m_nCuloare = 2;}
Neajunsuri
Daca adaugam o noua culoare va trebui sa facem urmatoarele modificari in cod:
(.h) prototip functie pentru noua culoare;
(.cpp) modificare harta de mesaje si scriere cod pentru noua functie.
In total trei modificari.
Prima imbunatatire - o singura functie
Grupam aceste comenzi (ID-uri secventiale);
Obtinem ID-ul comenzii selectate si scadem din acesta ID-ul primei comenzi din meniu (IDM_ROSU).
Harta de mesaje pentru o singura functie
ON_COMMAND(IDM_ROSU, OnCuloare);
ON_COMMAND(IDM_VERDE, OnCuloare);
ON_COMMAND(IDM_ALBASTRU, OnCuloare);
Codul functiei OnCuloare
void CXView::OnCuloare() UNIT nID = (UINT)
LOWORD(GetCurrentMessage()->wParam);
m_nCuloare = nID – IDM_ROSU;
O singura functie, o singura intrare in harta de mesaje
In harta de mesaje:
ON_COMMAND_RANGE(IDM_ROSU, IDM_ALBASTRU, OnCuloare);
Retineti ca avem ID-uri secventiale de comenzi, iar variabila m_nCuloare se
calculeaza ca diferenta intre ID comanda si primul ID al comenzilor grupate
(IDM_ROSU).
Actualizarea articolelor intr-un meniu
Metoda 1: actualizare in momentul cind articolul este selectat
void CXView::OnCuloare()
{ CMenu* pMenu = GetMenu();
pMenu->CheckMenuItem(m_nCuloare + IDM_ROSU, MF_UNCHECKED);
pMenu->CheckMenuItem(nID, MF_CHECKED);
m_nCuloare = nID – IDM_ROSU;}
Actualizarea articolelor ...
Metoda 2: mutam codul care actualizeaza meniul in
tratarea mesajului WM_INITMENUPOPUP iar functia este OnInitMenuPopup. Aici actualizarea se face
inainte ca meniul sa fie afisat. Aceasta functie are trei parametri: un pointer de tip CMenu ce pointeaza la submeniul care va fi afisat, un UINT – valoare ce da indexul art din submeniu si o var. BOOL care este # 0 daca mesajul este al meniului sistem si 0 altfel.
Actualizarea articolelor ...
In harta de mesaje avem:
ON_WM_INITMENUPOPUP()
iar functia: (COLOR_MENU_INDEX este o
variabila ce specifica pozitia meniului Color in bara de meniu a aplicatiei) este:
Actualizarea articolelor ...
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{if (!bSysmenu && (nIndex == COLOR_MENU_INDEX))
pPopMenu->CheckMenuItem(IDM_ROSU, MF_UNCHECKED);
pPopMenu->CheckMenuItem(IDM_VERDE, MF_UNCHECKED);
pPopMenu->CheckMenuItem(IDM_ALBASTRU, MF_UNCHECKED);
pPopMenu->CheckMenuItem(m_nCuloare + IDM_ROSU, MF_CHECKED);
Actualizarea articolelor ...
Un alt mecanism este tot la mesajul WM_INITMENUPOPUP si consta in a completa harta de mesaje cu handleri pentru
comenzi, handleri ce vor fi apelati inainte ca meniul sa fie
vizibil si inaintea fiecarei selectii din meniu. Fiecarui handler de actualizare ii este pasat un pointer la un obiect CCmdUI ale carui functii membru pot fi folosite pentru a modifica
articolul de meniu. Acesti handleri pot fi utilizati si pentru
actualizarea butoanelor din toolbar si alte obiecte ale interfetei UI.
Actualizarea articolelor ...
ON_COMMAND_UPDATE_UI(IDM_ROSU, OnUpdateRosu)
ON_COMMAND_UPDATE_UI(IDM_VERDE, OnUpdateVerde)
ON_COMMAND_UPDATE_UI(IDM_ALBASTRU, OnUpdateAlbastru)
void CMainFrame::OnUpdateRosu(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_nCuloare);}, etc.
Alte metode din CCmdUI
Metode din CCmdUI: Enable, SetCheck, SetRadio (nu are echivalent in SDK), SetText.
Creare meniu in momentul executiei
Crearea meniului programatic (at run time) CMenu menuMain;
menuMain.CreateMenu();
Cmenu menuPopup;
menuPopup.CreatePopupMenu();
menuPopup.AppendMenu(MF_STRING, IDM_ROSU, “&Rosu”);
menuMain.AppendMenu(MF_POPUP, (UINT) menuPopup.Detach(), “&Culori”);
menuPopup.CreatePopupMenu();
menuPopup.AppendMenu(MF_STRING, IDM_EXIT, “&Exit”);
menuMain.AppendMenu(MF_POPUP, (UINT)menuPopup.Detach(), “&Sesiune”);
SetMenu(&menuMain);
menuMain.Detach();
Modificarea programatica
Functiile necesare pentru modificare sunt:
AppendMenu, InsertMenu, ModifyMenu, DeleteMenu, RemoveMenu
Inainte de a modifica un meniu, trebuie sa obtinem un pointer la acel meniu cu functia CWnd::GetMenu. In top-level menu,
articolele sunt referite prin indici ce incep cu 0 (zero) – cel mai din stinga.
Operatii
CMenu* pMenu = GetMenu();
pMenu-DeleteMenu(1, MF_BYPOSITION); sau
pMenu->DeleteMenu(IDM_CULORI, MF_BYCOMMAND);
sau item-uri din meniuri
CMenu* pMenu = GetMenu()-GetSubMenu(1);
pMenu->DeleteMenu(IDM_VERDE, MF_BYCOMMAND);
sau echivalent
CMenu* pMenu = GetMenu();
pMenu->DeleteMenu(IDM_VERDE, MF_BYCOMMAND);
Meniul sistem
CMenu* pSysMenu = GetSystemMenu(FALSE);
FALSE = inseamna ca dorim un pointer la o copie a
meniului sistem pe care vrem sa-l modificam. TRUE = reseteaza meniul sistem la starea sa implicita.
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysmenu->AppendMenu(MF_STRING, IDM_COMANDA_NOUA, _T(“&Comanda adaugata”));
Meniul sistem
in harta de mesaje avem:
ON_WM_SYSCOMMAND()
(.cpp) void CMainFrame::OnSysCommand(UINT nID, LPARAM lPram)
{ if ((nID & 0xFFF0 ) == IDM_COMANDA_NOUA) { // ceva }
CFrameWnd::OnSysCommand(nID, lParam);}
Meniuri contextuale
CMenu::TrackPopupMenu afiseaza un asemenea meniu.
BOOL TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPRECT lpRect = NULL)
x, y = locatia pe ecran (coordonate ecran) unde va apare meniul;
nFlags = informatii despre alianiamentul relativ la x
(TPM_LEFTALIGN, TPM_CENTERALIGN, TPM_RIGHTALIGN) si care buton este utilizat in continuare pt a face o selectie
(TPM_LEFTBUTTON, TPM_RIGHTBUTTON).
pWnd = identifica fereastra care va primi mesajul dupa selectia unei comenzi din meniu;
lpRect = dimensiunea unui dreptunghi (coord. ecran) in care utilizatorul poate face clic fara a anula meniul afisat.
Meniu contextual
CMenu menu;
menu.LoadMenu(IDR_CONTEXTMENU);
CMenu* pContextMenu = menu.GetSubMenu(0);
pContextMenu->TrackPopupMenu
(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, AfxGetMainWnd());
WM_CONTEXTMENU
in harta de mesaje avem macroul:
ON_WM_CONTEXTMENU si functia
afx_msg OnContextMenu(CWnd* pWnd, CPoint point);
pWnd = identifica fereastra in care s-a facut clic si point coordonatele punctului unde s-a facut clic.
WM_CONTEXTMENU
Procesarea comenzilor. Flagul TPM_RETURNCMD = folosit la obtinerea unui raspuns la apelul unui meniu contextual.
int nCmd = (int) pContextMenu->TrackPopupMenu(TPM_RETURNCMD...
switch(nCmd) {
case IDM_CONTEXT_1:
break;
...
}
Un meniu afisat in acest mod va genera mesaje WM_COMMAND cind un articol este selectat.