Sunteți pe pagina 1din 9

Meniuri Se vor descrie mai intai elementele aplicatiei (bara de titlu, meniul, etc.).

W furnizeaza suport aplicatiilor care utilizeaza meniuri: afisarea barei de meniu, derularea unui meniu popup cind acesta este selectat, notifica aplicatia cind o comanda de meniu a fost selectata. Definire termeni: Bara de meniu care apare in partea cea mai de sus a ferestrei se numeste top-level menu, iar comenzile se numesc top-level menu items. Meniul care apare cind un articol de meniu este selectat se numeste drop down menu, iar articolele din acest meniu se numesc articole meniu (menu items). Articolele de meniu sunt identificate prin valori intregi, numite ID-ul art. de meniu sau ID-ul comenzii. W suporta meniurile popup care sunt asemanatoare cu cele drop down cu deosebirea ca pot fi afisate oriunde pe ecran. Meniurile de context (right click) sunt meniuri popup. Meniul sistem cuprinde czi pt. redimensionare, mutare, minimizare, maximizare, inchidere fereastra. Act. legate de meniuri le gasim in clasa CMenu din MFC. CMenu contine o data membru publica HMENU m_hMenu ce pastreaza un handle la meniu, si mai multe functii ce constituie in fapt apeluri ale fct din API (CMenu::TrackPopupMenu, CMenu::EnableMenu, etc. CMenu contine doua fct virt DrawItem si MeasureItem care pot fi rescrise daca dorim sa cream articole de meniu stilizate ce contin bitmap-uri si alte elemente grafice. Crearea unui meniu in MFC. Modalitati: 1. in mod programabil, CreateMenu, InsertMenu, etc; 2. initializind o serie de structuri de date ce descriu continutul unui meniu si apoi apelam CMenu::LoadMenuIndirect; 3. cream o resursa de meniu si incarcam meniul rezultat in aplicatie in timpul executiei. Crearea unui meniu Metoda cea mai usoara este de a adauga un template de meniu la fis. de resurse al aplicatiei. Un fisier de resurse este un fis. text care are ext rc, si care instructiuni ce definesc meniul. Acest fis. de res este compilat si legat la aplicatie cu un utilitar numit rc.exe (in MS VC++). O resursa este un obiect binar ca de ex. un meniu, o icoana, stringuri, bitmap-uri. Fiecare resursa este identificata printr-un ID intreg sau string (MyMenu sau IDR_MYMENU). ID-urile sunt definite cu #define. O data ce o resursa este compilata si legata la o aplicatie ea poate fi incarcata printr-un simplu apel de functie. Un fis de resurse contine: ID resusrsa, numele art de meniu, ID-urile art de meniu, atribute ale meniului. IDR_MAINFRAME MENU PRELOAD DISCARDABLE BEGIN POPUP &File BEGIN MENUITEM &New\tCtrl+N, ID_FILE_NEW ... MENUITEM SEPARATOR END POPUP &View BEGIN ... END END
1

Indicatii: O elipsa in descrierea meniului inseamna ca sunt necesare inf suplimentare dupa ce art este selectat, ! inseamna ca se executa imediat o comanda (Exit!). Afxres.h defineste valorile ID-urile pt. comenzile uzuale. Valori valide pt. ID-uri in intervalul 10xEFFF sau mai exact 0x8000 0xF000 cf. Nota Teh. 20 din MFC. Textul ce urmeaza car. TAB \t identifica un accelerator. Un accelerator este o tasta sau o combinatie de taste (Ctrl+C, Ctrl+V, etc.) care cind sunt apasate au acelasi efect ca selectarea art din meniu. Cind definim art de meniu, putem sa-i indicam starea initiala, de ex GRAYED, ceea ce-l face disable, CHECKED. Incarcarea si afisarea unui meniu In timpul exec. o res. de meniu tr. incarcata si atasata la fereastra. Cind fer este afisata, meniul va fi afisat de asemenea. Metoda 1: apel la fct CFrameWnd::Create Create(NULL, _T(Nume aplicatie), WS_OVERLAPPEDWINDOW, rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME)); Param. 6 indica resursa de meniu. MAKEINTRESOURCE transforma un intreg intr-o data de tip LPSTR. Metoda 2: apel la fct CFrameWnd::LoadFrame, care creaza o fereastra cadru si ataseaza un meniu la ac. LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW, NULL, NULL); LoadFrame este asemenatoare cu Create, dar poate incarca si icoane si alte resurse ceea ce nu face Create (in spate este CreateWindow , CreateWindowEx). Metoda 3: construim un ob de tip meniu, clasa CMenu, si-l incarcam CMenu::LoadMenu si apoi il atasam la fer. CWnd::SetMenu: CMenu menu; menu.LoadMenu(IDR_MAINFRAME); SetMenu(&menu); menu.Detach(); CMenu::Detach() va detasa meniul din ob CMenu a.i. meniul nu va fi distrus prematur cind on menu va fi distrus. Regula gen. Un meniu incarcat cu LoadMenu ar trebui sa fie distrus cu DestroyMenu inainte ca aplicatia sa se termine. Metoda 3 este folosita in programe care au mai mult de un meniu. Ex. de aplicatie cu doua meniuri: Create(NULL, _T(aplicatie)); m_menuLong.LoadMenu(IDR_LONGMENU); m_menuShort.LoadMenu(IDR_SHORTMENU); SetMenu(m_bShortMenu ? &m_menuShort, &m_menuLong); Comutarea intre cele doua meniuri se face astfel (ca raspuns la o comanda a utilizatorului) de la meniul lung la cel scurt:
2

m_bShortMenu = TRUE; SetMenu(&m_menuShort); DrawMenuBar(); de la m.s la m.l m_bShortMenu = FALSE; SetMenu(&m_menuLong); DrawMenuBar(); Fct. CWnd::DrawMenuBar() redeseneaza bara meniu pt. a reflecta schimbarile facute in meniu. Daca m_menuLong si m_menuShort sunt var. membru ale clasei fereastra cadru (derivata din CWnd), destructorii pt. meniuri se vor apela cind fer. cadru va fi distrusa, deci in acest caz nu mai e nevoie de DestroyMenu. Raspunsul la comenzile meniului Cind utilizatorul selecteaza un art. de meniu (bara de meniuri) fer primeste o serie de mesaje: WM_INITMENU ce notifica fer ca un articol al meniului superior (top-level menu item) a fost selectat. Inainte ca meniul sa fie afisat fer primeste mesajul WM_INITMENUPOPUP, locul unde ar putea fi actualizate (enable, disable, checked, etc.) articolele meniului, meniul inca nu este afisat. Cind parcurgem art. unui meniu drop down (popup), fer primeste mesajul WM_MENUSELECT, mesaj ce poate fi tratat, in special in SDK, prin afisarea unui text in bara de stare (scurt help). Dar cel mai important mesaj este WM_COMMAND trimis cind util. selecteaza un art de meniu. Cuv inferior al param. 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. 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). Ex. ON_COMMAND(ID_FILE_SAVE, OnFileSave); Functiile ce trateaza comenzile de meniu nu au parametri si nu intorc nimic. Intervale pentru comenzi Sunt folosite pentru a trata un grup de art. meniu cu o singura functie. Functia va avea codul necesar pentru a identifica corect care comanda a fost selectata. De exemplu tratam culorile penitei si construim un meniu cu comenzile: Rosu, Verde, Albastru. Pentru a manevra mai usor interfata, comenzile acestui meniu vor fi inserate in harta de mesaje a clasei derivate din CView. Pp. ID-urile sunt: IDM_ROSU, IDM_VERDE, IDM_ALBASTRU iar fct. corespunzatoare OnRosu, OnVerde, OnAlbastru, iar culoarea o pastram intr-un int, m_nCuloare, cu valorile respective 0,1,2. In harta de mesaje vom avea: ON_COMMAND(IDM_ROSU, OnRosu); ON_COMMAND(IDM_VERDE, OnVerde); ON_COMMAND(IDM_ALBASTRU, OnAlbastru); iar fctiile vor fi: void CXView::OnRosu() { m_nCuloare = 0;}
3

void CXView::OnVerde() { m_nCuloare = 1;} void CXView::OnAlbastru() { m_nCuloare = 2;} Daca am avea mai multe culori? Cum procedam? Grupam aceste comenzi si vom extrage ID-ul comenzii cu CWnd:;GetCurrentMessage, iar harta de mesaje va fi: ON_COMMAND(IDM_ROSU, OnCuloare); ON_COMMAND(IDM_VERDE, OnCuloare); ON_COMMAND(IDM_ALBASTRU, OnCuloare); void CXView::OnCuloare() { UNIT nID = (UINT) LOWORD(GetCurrentMessage()->wParam); m_nCuloare = nID IDM_ROSU; } Indecsii meniului incep cu 0 (zero). Dar harta de mesaje este inca prea mare. Vom folosi macroul ON_COMMAND_RANGE care trateaza o serie de comenzi care au ID-uri contigue (secventiale): ON_COMMAND_RANGE(IDM_ROSU, IDM_ALBASTRU, OnCuloare); Actualizarea art intr-un meniu Metoda 1: actualizare in momentul cind art 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; } Metoda 2: mutam codul care actualizeaza meniul in tratarea mesajului WM_INITMENUPOPUP iar fct 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. In harta de mesaje avem: ON_WM_INITMENUPOPUP() iar fct: (COLOR_MENU_INDEX este o variabila ce specifica pozitia meniului Color in bara de meniu a aplicatiei) void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) {
4

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); } Un alt mecanism este tot la mesajul WM_INITMENUPOPUP si consta in a completa harta de mesaje cu fct (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 a carei functii membru pot fi folosite pentru a modifica art de meniu. Si pt. ca aceasta clasa CCmdUI, nu este specifica unui tip particular al unui element al interfetei utilizatorului, acesti handleri pot fi utilizati si pentru actualizarea butoanelor din toolbar si alte obiecte ale interfetei UI. Ex: in harta de mesaje: 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. Metode din CCmdUI: Enable, SetCheck, SetRadio (nu are echivalent in SDK), SetText Keyboard Acceleratori Adaugare la aplicatie Acceleratori: combinatii de taste pentru a selecta o comanda. Un accelerator produce mesajul WM_COMMAND. Se creaza o tabela de acceleratori in fis de resurse, o resursa speciala care coreleaza ID-ul art de meniu cu tastele sau combinatiile de taste, si incarca resursa in program cu un apel de functie. Resursa tabela de acceleratori este definita de un bloc ACCELERATORS in fis de resurse. Format general: ResurseID ACCELERATORS BEGIN ... END In MFC tabela de acceleratori are structura: IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE BEGIN N, ID_FILE_NEW, VIRTKEY, CONTROL ... VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT END
5

VIRTKEY = spune compilatorului de resurse ca prima intrare este un cod de cheie virtuala, iar urmatorul este CONTROL, ALT sau SHIFT Incarcarea acceleratorilor se face cu fct: LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME)); De asemenea se poate face si cu LoadFrame. Daca doua resurse au acelasi nume, LoadFrame le incarca pe amindoua la un singur apel de functie. Pentru ca acceleratorii sa lucreze, bucla de mesaje trebuie sa includa un apel la functia API ::TranslateAccelerator 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, CWnd::GetMenu. In toplevel menu, articolele sunt referite prin indici ce incep cu 0 (zero) cel mai din stinga. 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); // merge bine Cititi clasa CMenu.
6

Meniul system CMenu* pSysMenu = GetSystemMenu(FALSE); FALSE = inseamna ca dorim un pointer la o copie a meniului sistem pe care vrem sa-l modificam. TRUE = reset system menu la starea sa implicita. pSysMenu->AppendMenu(MF_SEPARATOR); pSysmenu->AppendMenu(MF_STRING, IDM_COMANDA_NOUA, _T(&Comanda adaugata)); in harta de mesaje: ON_WM_SYSCOMMAND() void CMainFrame::OnSysCommand(UINT nID, LPARAM lPram) { if ((nID & 0xFFF0 ) == IDM_COMMANDA_NOUA) { // ceva } CFrameWnd::OnSysCommand(nID, lParam); } Owner Draw Menus = Meniuri grafice Metoda 1: se creaza bitmap-uri care se folosesc apoi in AppendMenu. Vezi clasa CBitmap. Dzavantaj: bitmap-urile au marime fixa. Metoda 2: articole meniu desenate de utilizator. Se trateaza mesajul WM_DRAWITEM. Art. de meniu are atributul ca trebuie desenat de proprietar si nu de windows. Inainte ca meniul ce contine un articol owner-draw sa fie afisat pentru prima data, W trimite un mesaj WM_MEASUREITEM pentru a obtine informatii despre dimensiunile art. meniului. Pentru fiecare articol owner-draw al meniului se transmite un asemenea mesaj. Pas 1. Primul pas in implementarea unui asemenea meniu este de a seta atributul (stilul) meniului cu MF_OWNERDRAW. Editorul de resurse nu suporta acest stil. Se poate apela la ModifyMenu sau la modificarea manuala a resurselor. Pas 2. se adauga fct OnMeasureItem la mesajul WM_MEASUREITEM afx_msg OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis); nIDCtl = ID controlului typedef struct tagLP{ UINT CtlType; // ODT_MENU UINT CtlID; UINT itemID; // ID art meniu UINT itemWidth; // lat. orizontala in pixeli UINT itemHeight; // inalt in pixeli UINT itemData; } MEASUREITEMSTRUCT;

lpmis->itemWidth = ::GetSystemMetrics(SM_CYMENU) * 4; lpmis->itemHeight = ::GetSystemMetrics(SM_CYMENU); Pas 3. Se scrie fct OnDrawItem pentru mesajul WM_DRAWITEM afx_msg OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis); Are la baza urmatoarea structura DRAWITEMSTRUCT care contine: UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT itemState; HWND hwndItem; HDC hDC; RECT rcItem; DWORD itemData; Mai multe informatii in Help si J. Prossie Procesare WM_MENUCHAR : OnMenuChar O parte neplacuta a meniurilor owner-draw este aceea ca windows nu furnizeaza shortcut-uri pentru acestea. Chiar daca folosim inainte un meniu text, apoi il modificam in owner-draw, shortcut-ul de la text nu mai functioneaza. rezolvarea problemei consta in tratarea mesajului WM_MENUCHAR, care este primit de fer. cind un meniu este afisat si o tasta care nu corespunde unui articol de meniu este apasata. Prin procesarea acestui mesaj, putem adauga shortcut-uri la meniurile owner-draw. Prototipul functiei CWnd::OnMenuChar este urmatorul: afx_msg LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu) nChar = contine codul ANSI al tastei; nFlags = contine un flag MF_POPUP daca meniul la care mesajul vine este un submeniu; pMenu = pointer la meniu; Valoarea returnata: HIWORD(LRESULT) trebuie sa contina una din urm. valori: 0 = daca W ar trebui sa ignore tasta; 1 = daca W ar trebui sa inchida meniul 2 = daca W ar trebui sa selecteze unul din art. afisate din meniu In acest din urma caz LOWORD(LRESULT) contine ID-ul art. de meniu corespunzator. Exista definit macroul MAKELRESULT care seteaza valoarea returnata: LRESULT lResult = MAKELRESULT(IDM_ROSU, 2); va seta high word pe 2 si low word cu IDM_ROSU, Meniuri contextuale: WM_CONTEXTMENU Se activeaza de obicei la clic dreapta mouse. Un meniu contextual nu este nimic altceva decit un submeniu care nu e atasat la un top-level menu. Functia CMenu::TrackPopupMenu afiseaza un asemenea meniu. Prototipul fct. este: BOOL TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPRECT lpRect = NULL)
8

x, y = locatia pe ecran (coord. ecran) unde va apare meniul; nFlags = inf. despre alianiamentul relativ la x (TPM_LEFTALIGN, TPM_CENTERALIGN, TPM_RIGHTALIGN) si care buton este utilizat in continuare pt a fcae 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. Ex. CMenu menu; menu.LoadMenu(IDR_CONTEXTMENU); CMenu* pContextMenu = menu.GetSubMenu(0); pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, AfxGetMainWnd()); Daca tratam mesajul WM_CONTEXTMENU in harta de mesaje avem macroul: ON_WM_CONTEXTMENU si fct 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. 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.