Programarea aplicatiilor SDI
Introducere
Probabil una dintre cele mai utilizate aplicatii realizate de firma Microsoft este la ora actuala pachetul de programe MsOffice, iar din cadrul acestuia, cu siguranta editorul MsWord. Acest editor este în fapt o aplicatie Windows tipica, oferind utilizatorului o interfata specifica, constând într-o bara de titlu, un meniu derulant, una sau mai multe bare de instrumente, o zona de lucru si în fine, la baza ferestrei, o zona de afisare. O interfata de acest tip poate fi foarte usor programata utilizând biblioteca MFC. AppWizard poate crea foarte usor un schelet de interfata continând elementele enumerate mai sus, care ulterior pot fi dezvoltate sau modificate dupa dorinta programatorului. Clasele utilizate de AppWizard sunt asamblate formând o structura omogena numita arhitectura Document Reprezentare (Document View). Arhitectura Document Reprezentare ofera o independenta a modului de reprezentare a datelor de datele propriuzise, reprezentarea fiind realizata de metodele oferite de o clasa reprezentare (View), iar datele fiind stocate de o clasa document.
Structura de clase creata de AppWizard pentru o aplicatie SDI difera de cea creata pentru o aplicatie de tip Dialog. si în acest caz clasele vor deriva printr-un mecanism de mostenire simpla din clasa CObject, dar vor fi utilizate clase noi, care nu apar într-o aplicatie de tip Dialog.
Daca vom
crea un proiect SDI (pe care sa presupunem ca îl vom numi cap1), AppWizard va crea 4
clase asociate proiectului, aflate la baza ierarhiei din fig. 1.
Clasa CCap1App derivata din CWinApp este dupa cum se stie clasa care încapsuleaza obiectul aplicatie. În cadrul ei se initializeaza aplicatia, se primesc mesajele Windows si se trimit spre fereastra aplicatie corespunzatoare. De asemenea se proceseaza relatiile dintre clasele document, reprezentare si fereastra cadru.
Clasa CCap1Doc este clasa care încapsuleaza obiectul document. Ea este derivata din clasa CDocument. Obiectul document va stoca datele aplicatiei, acestea putând lua orice forma. De exemplu, pentru un editor de text, datele ar putea reprezenta textul propriuzis, precum si informatii de formatare a fonturilor, culoare, etc. Obiectul încapsulat de aceasta clasa nu este responsabil si cu interpretarea si afisarea informatiilor continute. Interfata cu utilizatorul este realizata de clasa CCap1View.
Clasa CCap1View derivata din clasa CView este responsabila cu furnizarea interfetei pentru utilizator. Instanta ei interpreteaza datele din instanta clasei document si le afiseaza în mod corespunzator pe ecran. În acest fel, se realizeaza o flexibilitate ridicata a aplicatiei, aceleasi date putând fi interfatate în mod diferit de diferite instante a claselor reprezentare. În consecinta, orice instanta a clasei reprezentare va corespunde unei singure instante a clasei document, dar o instanta a clasei document va putea avea asociate mai multe instante reprezentare, implicând modalitati diferite de fisare a acelorasi date.
Clasa CMainFrame derivata din CFrameWnd furnizeaza aplicatiei o fereastra cadru în care sa ruleze. Spre deosebire de obiectele CFrameWnd care reprezinta ferestre simple, obiectele CMainFrame contin o serie de componente suplimentare, cum ar fi meniuri, bare cu instrumente, bare de stare, etc., care pot fi inserate si afisate în diferite forme, cu ajutorul unor indicatori de stil.
Pentru crearea proiectului cap1 se parcurg pasii:
se deschide un proiect de tip AppWizard (exe);
în caseta MFC AppWizard - Step1 se alege Single Document, cu optiunea Document View Architecture Support validata. Se apasa Next;
la pasul 2 se
alege None si se
apasa Next;
la pasul 3 se apasa None, lasând activata optiunea implicita ActiveX Controls. Se apasa Next;
la pasul 4 se
lasa validate optiunile implicite si se apasa Next. Semnificatia acestor
optiuni va fi lamurita mai târziu;
la pasul 5 se apasa Next;
la pasul 6 se
apasa Finish. De fapt,
acelasi proiect ar fi putut fi obtinut si prin apasarea
butonului Finish la pasul 1,
parcurgerea tuturor celor 6 pasi permitând personalizarea
proiectului;
la pasul 6 am utilizat ca si clasa parinte pentru clasa reprezentare asociata proiectului clasa CView implicita. AppWizard ofera mai multe clase reprezentare, acestea fiind prezentate în tabelul de mai jos:
Tabelul 1.
Clasa |
Descriere |
CView |
Clasa implicita reprezentare, implementând interfata si interactiunea cu clasa document; |
CScrollView |
Este derivata din CView. Permite derularea imaginii în cazul în care suprafata logica a reprezentarii depaseste suprafata afisabila efectiv; |
CListView |
Utilizeaza pentru afisare un control lista. Permite afisarea de coloane de text sau pictograme; |
CTreeView |
Utilizeaza pentru afisare un control arbore. Informatiile pot fi afisate ierarhic; |
CEditView |
Utilizeaza un control multilinie de reprezentare, adaugând facilitati de derulare si cautare; |
CRichEditView |
Similar clasei precedente, adaugând facilitati de derulare si cautare mai puternice; |
CFormView |
Utilizeaza ca reprezentare o macheta dialog, aplicatia fiind în general un formular pentru interfatarea unei baze de date; |
CRecordView |
Este derivata din CFormView. Face legatura între reprezentare si o multime de înregistrari apartinând unei baze de date. Este accesibila numai daca la pasul 2 este selectat suportul pentru baze de date; |
CHtmlView |
Utilizeaza o reprezentare Internet Explorer pentru dezvoltarea de browsere Web; |
similar proiectului de tip Dialog, va apare caseta New Project Information în care sunt afisate clasele create precum si fisierele în care sunt stocate. Se apasa OK si noul proiect este creat.
Daca vom urmari clasele create (fig. 3) vom constata crearea celor 4 clase amintite anterior, precum si a clasei CAboutDlg cu acelasi rol ca si în cazul proiectului de tip Dialog.
Dupa crearea
proiectului, la executie, programul va afisa o interfata
tipica de tip document, ca în fig. 4.
Crearea machetei programului
În mod similar aplicatiilor de tip dialog, initializarea caracteristicilor interfetei se face în functia InitInstance(), apelata de constructorul clasei aplicatie. În cazul nostru, aceasta are implementarea de mai jos:
BOOL CCap1App::InitInstance()
În functie, dupa mai multe initializari de conectare la MFC, urmeaza crearea unui obiect de clasa CSingleDocTemplate. Aceasta este clasa utilizata de aplicatiile SDI pentru crearea machetei documentului. Obiectul pointat de pDocTemplate este creat pe baza identificatorului resursei (IDR_MAINFRAME), precum si a pointerilor la informatii de clasa dinamice a claselor document, cadru si reprezentare. Acesti pointeri sunt creati cu ajutorul macrodefinitiei RUNTIME_CLASS, care implica crearea dinamica a acestor clase.
Obiectul macheta odata creat, este înregistrat în cadrul clasei CWinApp prin intermediul functiei AddDocTemplate.
Urmeaza interpretarea eventualelor informatii transmise prin linia de comanda la lansarea programului. Tratarea parametrilor transmisi de linia de comanda este realizata cu ajutorul unui obiect al clasei CCommandLineInfo. Functia ParseCommandLine primeste ca parametru o referinta la acest obiect. Prin intermediul acestei functii, se completeaza variabilele membru ale obiectului CCommandLineInfo cu valorile corespunzatoare ale parametrilor din linia de comanda. Pot fi transmisi urmatorii parametri:
Tabelul 2.
Parametru |
Efect |
nume fisier /p nume fisier /pt nume fisier imprimanta port /dde /automation /embedding |
Se creeaza un nou document Se deschide fisierul specificat Se tipareste fisierul specificat la imprimanta implicita Se tipareste fisierul specificat la imprimanta si portul specificat Începe o sesiune DDE Lanseaza aplicatia pentru a edita un obiect OLE încapsulat Lanseaza aplicatia pentru a edita un obiect OLE încapsulat într-o alta aplicatie |
Este apoi apelata functia ProcessShellCommand pentru crearea documentului, a ferestrei cadru si reprezentare. Aceasta functie apeleaza o serie de functii de infrastructura, pentru a implementa actiunile transmise prin linia de comanda. Aceleasi functii sunt apelate si de mesajele transmise de elementele meniului. O parte din aceste functii sunt:
CDocument OnNewDocument() - este apelata la crearea obiectului aplicatie sau la alegerea optiunii New din meniul File. În aplicatiile SDI, este reinitializat documentul curent. Pentru initializare, va fi apelata functia DeleteContents(). Apoi, este fixat la FALSE indicatorul de modificare, aratând ca documentul nu este modificat si se reseteaza sirul de caractere care contine numele fisierului document.
Daca se doreste supraînscrierea acestei functii, este absolut necesara apelarea întâi a functiei din clasa de baza, ca mai jos:
BOOL CCap1Doc::OnNewDocument()
Daca functia returneaza FALSE, crearea noului document esueaza.
CDocument::DeleteContents() - sterge continutul documentului, fara a distruge obiectul document. Functia este apelata la initializarea, deschiderea sau închiderea unui document. Implementarea implicita nu face nimic. Daca în program au fost create variabile sau obiecte dinamice asociate documentului, functia trebuie supraînscrisa, pentru reinitializarea variabilelor si eliberarea memoriei alocate în cadrul documentului. O posibila supradefinitie este:
void CCap1Doc::DeleteContents()
CDocument::OnOpenDocument() - Este apelata la alegerea optiunii Open din meniul File. Implementarea de baza a acestei functii primeste numele (cu cale eventual) pentru fisierul care trebuie deschis, apeleaza DeleteContents() pentru a sterge continutul obiectului document si încarca în acesta noul continut din fisier. O data încarcate datele. Fisierul este închis iar indicatorul de modificare este setat la FALSE. Supraînscrierea acestei functii se poate face astfel:
BOOL CCap1Doc::OnOpenDocument(LPCTSTR lpszPathName)
Daca functia returneaza valoarea FALSE, încarcarea datelor în document esueaza.
CDocument::OnSaveDocument() - Este apelata la alegerea optiunii Save sau Save As din meniul File, sau la închiderea documentului, când se cere confirmarea salvarii continutului acestuia. Functia are ca parametru un pointer la un sir de caractere care reprezinta numele fisierului în care se salveaza continutul documentului. În implementarea implicita, fisierul este deschis si apoi se apeleaza functia Serialize() pentru salvarea datelor. În final, fisierul este închis, iar identificatorul de modificare este fixat la FALSE. Despre functia Serialize() se va vorbi în unul din capitolele urmatoare.
CDocument::OnCloseDocument() - Este apelata la închiderea documentului curent, atât la închiderea propriuzisa a acestuia, cât si înainte de deschiderea unui nou document. Functia închide mai întâi toate reprezentarile atasate la document, apeleaza DeleteContents() si distruge obiectul document.
MFC furnizeaza o serie de cai pentru accesarea obiectului document din cadrul aplicatiei. Cea mai simpla cale de accesare a documentului este prin intermediul functiei CView::GetDocument().
Lucrul în comun Document/Reprezentare. Meniuri si butoane
Dupa cum s-a aratat anterior, AppWizard insereaza automat clasele document si reprezentare în proiect (CCap1Doc si CCap1View în cazul nostru). În mod ideal, toate datele ar trebui sa apartina instantelor claselor document, iar caracteristicile de afisare instantelor claselor reprezentare. În mod real, acest lucru nu este posibil, efectuându-se în general operatii asupra datelor si din functii ale clasei reprezentare, iar modificari ale modului de afisare pot fi facute si din functii ale claselor document.
Pentru exemplificarea modului de lucru în sistemul Document/Reprezentare, vom completa programul cap1 cu afisarea unui numar de discuri, de dimensiune descrescatoare, înfipte într-un bat, în mijlocul zonei reprezentare.
Clasa document este o clasa ca oricare alta, deci variabilele membru ale acesteia pot fi adaugate prin Add Member Variable din meniul contextual asociat clasei în Class View. În general, pentru asigurarea securitatii variabilelor document, acestea trebuie sa fie protejate. Cum totusi anumite date vor trebui accesate si din functii ale clasei reprezentare, clasa document va trebui sa ofere metode de acces la variabilele private. Aceste metode sunt publice, fiind inserate în general cu Add Member Function.
Vom insera în clasa CCap1Doc variabila m_nTower care va mentine numarul de discuri înfipte în bat. Intuitiv, ar fi mult mai simplu ca aceasta variabila sa fie declarata publica, pentru ca probabil ea va fi modificata în functii ale clasei reprezentare. Este însa de dorit ca ea, fiind o variabila a clasei document, sa nu poata fi modificata accidental în secvente de cod apartinând altor clase. O vom declara deci privata, furnizând si o metoda de acces la ea, prin functia int GetTowerNumber(), care va returna valoarea variabilei .
Pentru aceasta, se vor parcurge pasii:
în ClassView se intra în meniul contextual asociat clase CCap1Doc (click dreapta asupra clasei) si se alege Add Member Variable;
în caseta Add Member Variable se introduce int la Variable Type, m_nTower la Variable Name si se selecteaza butonul de optiune Protected. În acest fel, va fi inserata variabila dorita;
în meniul contextual asociat CCap1Doc se alege Add Member Function, introducând int la Function Type si respectiv GetTowerNumber la Function Declaration. Se selecteaza butonul de optiune Public;
se completeaza scheletul functiei ca mai jos:
int CCap1Doc::GetTowerNumber()
cum în clasa document a fost adaugata o variabila, aceasta va trebui initializata la orice reinitializare a documentului. Dupa cum s-a aratat anterior, acest lucru se face prin supradefinirea functiei DeleteContents(). Pentru aceasta:
în ClassWizard, la pagina MessageMaps, se selecteaza la Class Name clasa CCap1Doc, la Object IDs CCap1Doc, iar la Messages, DeleteContents.
Se apasa Add Function
si se accepta denumirea propusa de ClassWizard (fig. 5)
se apasa Edit Code si se insereaza codul de mai jos:
void CCap1Doc::DeleteContents()
Avem acum o variabila protejata care mentine numarul de discuri, initializata de functia supradefinita DeleteContents(), precum si o metoda de acces la variabila privata.
De afisarea efectiva a discurilor este responsabila clasa CCap1View. Codul care deseneaza efectiv discurile este implementat în functia CCap1View::OnDraw(), care este apelata de fiecare data când reprezentarea trebuie generata pe ecran. AppWizard creaza automat un schelet pentru aceasta functie. Vom completa scheletul, cu codul de mai jos. Pentru o mai buna întelegere, vom comenta actiunile în cadrul implementarii functiei.
void CCap1View::OnDraw(CDC* pDC)
Se obtine rezultatul din fig. 6.
Pentru a
realiza anumite actiuni în cadrul programului, este necesar sa utilizam
optiunile din meniu. Acestea pot fi modificate, actualizate sau
sterse si de asemenea, le pot fi asociate diferite functii. Se
propun urmatoarele: Se va modifica meniul în asa fel încât la File sa ramâna
doar optiunea Iesire,
iar otiunea View sa
fie redenumita în Discuri,
având optiunile Adauga
si Sterge. Prima
optiune va adauga un disc pe bat, iar a doua va sterge
un disc de pe bat. Pentru aceasta se parcurg pasii:
se deschide Resource View, alegând Menu-IDR_MAINFRAME. Se propun urmatoarele modificari:
se face click pe File. Se selecteaza prima optiune si se apasa tasta Delete pâna ramâne doar optiunea Exit. Pentru a se schimba numele în Iesire se face click dreapta pe Exit, iar în meniul contextual asociat se alege Properties; În caseta de proprietati, la Caption se introduge &Iesire;
Se sterge
optiunea Edit prin
selectare si apasarea tastei Delete;
Se modifica numele optiunii View în Discuri (click dreapta si Properties în meniul contextual) si se sterg toate componentele. Apoi se introduc optiunile Adauga si Sterge; Introducerea noilor optiuni se face prin dublu click pe locatia goala a meniului. Se completeaza pentru fiecare optiune astfel:
La ID: ID_ADAUGA si respectiv ID_STERGE
La Caption: &Adauga si respectiv &Sterge;
La Prompt: Adauga un disc si respectiv Sterge un disc;
Se asociaza functiile corespunzatoare optiunilor din meniu. Pentru aceasta se lanseaza Class Wizard si în Message Maps la mesajul COMMAND se asociaza urmatoarele functii:
Pentru ID_ADAUGA, OnAdauga();
Pentru ID_STERGE, OnSterge();
Trebuie avut grija ca la Class name sa fie selectata clasa CCap1Doc, în caz contrar nu vom putea apela variabila privata
Se implementeaza codul pentru functiile generate:
void CCap1Doc::OnAdauga()
void CCap1Doc::OnSterge()
Prima functie incrementeaza numarul discurilor, iar a doua îl decrementeaza, daca este pozitiv. Se apeleaza apoi functia CDocument::UpdateAllViews() cu parametrul NULL, ceea ce are ca efect actualizarea tuturor reprezentarilor. La actualizare, zona client a ferestrei reprezentarii este invalidata si este apelata automat functia OnDraw() pentru redesenarea continutului acesteia.
Pentru manipularea mai usoara a paginilor, optiunilor de la Discuri li se pot asocia butoane în bara de unelte. Pentru aceasta, în Resource View se deschide optiunea Toolbar-IDR_MAINFRAME si se executa urmatoarele:
Se
elimina toate butoanele, cu exceptia semnului de întrebare si a
butonului gol. Eliminarea se face prin simpla tragere cu mouse-le în afara
barei de unelte;
Se pozitioneaza butonul gol înaintea semnului de întrebare si se editeaza cu sageata spre stânga, folosind uneltele grafice si setul de culori pus la dispozitie de wizard;
Se face dublu click pe noul buton creat în bara de unelte, iar în caseta Properties aparuta se da la ID acelasi nume ca si al elementului din meniu, adica ID_ADAUGA. Se face apoi click în caseta Prompt, acceptându-se mesajul afisat;
Se deseneaza imaginea ce va apare pe buton, folosind bara de instrumente pusa la dispozitie. Se propune o imagine ca în fig. 9.
Se repeta
operatiunea pentru celalalt buton;
Se obtine
astfel interfata program din fig. 10.
Modelarea interfetei meniu
La fel ca si în cazul obiectelor asociate casetelor de tip Dialog si în cazul meniurilor, MFC furnizeaza o serie de functii pentru modelarea aspectului, stilului si comportamentului elementelor meniului. Functiile care modifica atributele elementelor de meniu actioneaza în general asupra unui obiect CCmdUI, primind ca argument un pointer spre un astfel de obiect.
Obiectul CCmdUI pointat în orice moment se va referi la componente de meniu sau submeniu activa în acel moment. Functiile de tratare UI pentru un element de meniu se adauga în ClassWizard, prin selectarea la Messages a optiunii UPDATE_COMMAND_UI. O functie UPDATE_COMMAND_UI este lansata în executie de fiecare data când elementul de meniul devine activ, iar modul lui de afisare va fi facut în concordanta cu setarile continute în corpul functiei.
4.1 Activarea si dezactivarea elementelor de meniu
Câteva metode utile ale clase CCmdUI sunt prezentate în tabelul de mai jos:
Tabelul 3.
Metoda |
Efect |
Virtual void Enable(BOOL bOn=TRUE) Virtual void SetCheck(int nCheck= Virtual void SetText(LPCTSTR lpszText) |
Activeaza sau dezactiveaza elementul curent de meniu. Daca parametrul transmis este TRUE elementul este activat, iar daca este FALSE este dezactivat. Încarca variabila nCheck cu valoarea corespunzatoare starii elementului de meniu. Afiseaza textul pointat de pointerul lpszText în elementul de meniu activ. |
Ca exemplu, sa inseram functiile de tratare UI pentru meniurile Adauga si Sterge, astfel încât daca sunt mai mult de 6 discuri meniul Adauga sa se dezactiveze, iar daca sunt mai putin de 1 disc, meniul Sterge sa se dezactiveze. În ambele cazuri, pe elementul de meniu dezactivat va scrie Gata!.
Se parcurg pasii:
se lanseaza ClassWizard, pagina Message Maps;
se selecteaza la Class name: CCap1Doc, la Object IDs: ID_ADAUGA, iar la Messages: UPDATE_COMMAND_UI
se apasa Add Function si se accepta numele propus;
se apasa Edit Code si se insereaza codul de mai jos:
void CCap1Doc::OnUpdateAdauga(CCmdUI* pCmdUI)
else
se repeta operatiile pentru ID_STERGE, cu implementarea de mai jos:
void CCap1Doc::OnUpdateSterge(CCmdUI* pCmdUI)
else
Marcaje de validare
Este util de multe ori ca starea unui anumit element de meniu sa poata fi comutata. O implementare simpla a acestei actiuni este realizata prin utilizarea unui comutator de stare, care sa basculeze într-o functie COMMAND, iar starea elementului de meniu sa fie modificata în functia UPDATE_COMMAND_UI
Pentru exemplificare, sa inseram în meniul Discuri al treilea element, cu textul Marcat. Fie ID_MARCAT_NEMARCAT identificatorul acestuia. La alegerea elementului de meniu, textul afisat va deveni Nemarcat, apoi iar Marcat, s.a.m.d.
Pentru aceasta, vom adauga clasei CCap1Doc variabila protejata membru (meniu contextual, Add Member Variable) int m_nComutator, care va bascula starea elementului de meniu. Apoi, în ClassWizard, pentru ID_MARCAT_NEMARCAT, având grija ca la Class name sa fie CCap1Doc, se insereaza functiile COMMAND si UPDATE_COMMAND_UI, cu implementarile de mai jos:
void CCap1Doc::OnMarcatNemarcat()
void CCap1Doc::OnUpdateMarcatNemarcat(CCmdUI* pCmdUI)
Meniuri contextuale
Observatie: Daca încercati implementarea programelor prezentate mai sus în Visual C în aceasta faza creati o dublura a proiectului. Paragraful 5.2 va relua proiectul din acelasi punct.
5.1 Crearea meniurilor contextuale asistata de AppWizard
Implementarea meniurilor contextuale se face prin interceptarea mesajului WM_CONTEXTMENU si asocierea unei functii corespunzatoare. Acest mesaj este trimis spre tratare procedurii fereastra în momentul apasarii butonului drept al mouse-lui, oriunde în zona client a reprezentarii.
Pentru implementarea functiei, este absolut necesar ca în prealabil sa fie inserata în aplicatie o noua resursa meniu, care sa fie afisata la apasarea butonului drept al mouse-lui. Pentru aceasta, în Resource View fac urmatoarele operatii:
se apasa click dreapta pe cap1 resources , pentru obtinerea meniului contextual. Se alege optiunea Insert.;
în caseta Insert Resource se alege Menu si New;
pe noua resursa IDR_MENU1 se face click dreapta, iar în meniul contextual aparut se alege Properties;
în caseta Menu Properties se redfineste ID-ul în IDR_MENIU_CONTEXTUAL
se insereaza o noua resursa meniu, cu textul Culoare. Pentru aceasta se face click dreapta pe dreptunghiul gol, se alege Properties, iar la Caption în caseta Menu Item Properties se tasteaza &Culoare
se
insereaza submeniurle cu ID : IDR_ROSU IDR_VERDE IDR_ALBASTRU si IDR_GALBEN, având respectiv la
Caption &Rosu,
&Verde, &Albastru si &Galben. Inserarea se face
ca si la paragraful 3; În figura 11 este afisata caseta Menu Properties asociata noii
resurse meniu.
Urmeaza adaugarea efectiva a functiei de tratare a meniului contextual, deci a functiei asociate mesajului WM_CONTEXTMENU Pentru aceasta se parcurg urmatorii pasi:
se intra în Class View si se face click dreapta pe clasa reprezentare în care se doreste inserarea meniului contextual (în cazul nostru CCap1View);
în meniul contextual se alege Add Windows Message Handler;
în macheta New Windows Message and Event Handlers for class CCap1View, în caseta New Windows Messages Events se alege WM_CONTEXTMENU si se apasa butonul Add and Edit;
se insereaza pentru functie codul de mai jos:
void CCap1View::OnContextMenu(CWnd* pWnd, CPoint point)
Pentru afisarea meniului contextual se creeaza întâi un obiect CMenu. Clasa CMenu este derivata direct din clasa CObject si încapsuleaza toate metodele de creare, urmarire, actualizare si distrugere a unui obiect meniu.
Initializarea noului obiect meniu cu resursa meniu corespunzatoare se face prin intermediul functiei BOOL LoadMenu(UINT nIDResource ), care primeste ca parametru identificatorul resursei meniu asociate (IDR_MENIU_CONTEXTUAL în cazul nostru) si ataseaza resursa de obiectul nou creat. Aceasta functie returneaza o valoare diferita de 0 daca încarcarea resursei a reusit si respectiv 0 daca a esuat. Ca efect, obiectul meniuContext va fi initializat cu resursa corespunzatoare. Afisarea elementelor de submeniu se face cu functia CMenu* GetSubMenu( int nPos ) const, transmitând valoarea nPos 0 pentru a solicita afisarea începând cu primul element din submeniu. De fapt, în implementarea actuala, o alta valoare da eroare de asertie.
Pentru noul meniu obtinu, se va apela functia BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd) care afiseaza si controleaza meniul contextual. Daca afisarea este reusita, functia va întoarce o valoare diferita de 0, iar în caz de esec, va întoarce valoarea 0. Primeste ca argumente un indicator de aliniere, care specifica în ce parte se gaseste prompterul mouse-lui referitor la meniul contextual afisat. Indicatorul de aliniere poate lua urmatoarele valori:
Tabelul 4.
Valoare |
Descriere |
TPM_LEFTALIGN TPM_RIGHTALIGN TPM_TOPALIGN TPM_BOTTOMALIGN TPM_CENTERALIGN TPM_VCENTERALIGN TPM_HORIZONTAL TPM_VERTICAL |
Promterul mouse-lui este pozitionat în partea stânga a meniului Promterul mouse-lui este pozitionat în partea dreapta a meniului Promterul mouse-lui este pozitionat în partea superioara a meniului Promterul mouse-lui este pozitionat în partea inferioara a meniului Promterul mouse-lui este pozitionat în partea centrala a meniului Promterul mouse-lui este pozitionat în partea centrala a meniului, pe verticala Daca nu exista spatiu suficient pe ecran, alinierea orizontala este implicita Daca nu exista spatiu suficient pe ecran, alinierea verticala este implicita |
Coordonatele x si y primite ca argumente de functia TrackPopupMenu() reprezinta coordonatele pozitiei de afisare a meniului în cadrul zonei client a reprezentarii. Uzual, ele sunt transmise ca si coordonatele point.x si point.y a obiectului CPoint primit ca argument de functia OnContextMenu(), care reprezinta coordonatele pozitiei în obiectul fereastra pointat de pWnd (fereastra reprezentarii) unde a fost apasat butonul drept al mouse-lui (vezi capitolul "Gestiunea Mouse-lui" din "Programarea aplicatiilor Windows în Visual C . Ultimul parametru este un pointer spre fereastra în care este afisat meniul. Reamintim ca si în acest caz, pointerul this pointeaza spre fereastra curenta.
Urmeaza implementarea functiilor asociate elementelor de submeniu. Acestea se asociaza ca si la paragraful 3, în Class Wizard, corespunzator mesajului COMMAND, având grija ca la Class Name sa fie CCap1View. Se propune implementarea unor functii care sa schimbe culoarea discurile afisate în culoarea specificata de elementele de meniu. Vom face urmatoarea conventie: culoarea rosie va fi codificata cu 0, culoarea verde cu 1, culoarea albastra cu 2, iar culoarea galbena cu 3. Pentru mentinerea culorii curente, va trebui sa asociem clasei CCap1View o variabila privata int Color. Aceasta se face prin Add Member Variable în meniul contextual asociat clasei în Class View.
Cum se doreste ca initial culoarea sa fie verde (ca în exemplul anterior) variabila va trebui sa ia initial valoarea 1. Sa ne reamintim ca initializarea variabilelor membru se face uzual în constructorul clasei. Vom completa constructorul ca mai jos:
CCap1View::CCap1View()
Va trebui de asemenea sa modificam functia OnDraw(), astfel încât sa se creeze pensule de culoarea specificata de variabila. Pentru aceasta, functia se completeaza ca mai jos:
void CCap1View::OnDraw(CDC* pDC)
case 1:
case 2:
case 3:
}
pDC->SelectObject(&Brush);
CWnd* DocWindow=pDC->GetWindow();
CRect WindowSize=0;
DocWindow->GetClientRect(WindowSize);
CPoint Center=WindowSize.CenterPoint();
for (int contor=1;contor<=pDoc->GetTowerNumber();contor++)
pDC->Rectangle(Center.x-200+20*contor,
Center.y-20*contor,Center.x+200-20*contor,
Center.y-20*contor+20);
pDC->MoveTo(Center.x,Center.y);
pDC->LineTo(Center.x,Center.y-150);
pDC->SelectObject(pOldB);
Pentru functiile care raspund alegerii optiunilor din submeniu, se propun implementarile:
void CCap1View::OnRosu()
void CCap1View::OnVerde()
void CCap1View::OnAlbastru()
void CCap1View::OnGalben()
Dupa aceste modificari, orice click dreapta în zona client a reprezentarii va avea ca efect afisarea meniului contextual, iar dupa alegerea unei culori, la orice apasare a optiunilor Adauga sau Sterge (sau a butoanelor corespunzatoare) culoarea discurilor va fi schimbata corespunzator.
Daca doriti ca schimbarea culorii discurilor sa se faca în momentul în care ati ales culoarea, implementati functiile ca mai jos:
void CCap1View::OnRosu()
void CCap1View::OnVerde()
void CCap1View::OnAlbastru()
void CCap1View::OnGalben()
Crearea si accesarea prin program a obiectelor meniu
În multe cazuri nu este practica crearea prin wizard a meniului, acesta trebuind sa fie creat, actualizat sau sters dinamic, în cursul executiei programului. În acest sens, va trebui sa reconstituim prin cod actiunile efectuate de AppWizard pentru interceptarea mesajului WM_CONTEXTMENU. Sa ne reamintim care sunt acestea:
va trebui sa inseram macroul corespunzator în harta de mesaje asociata clasei CCap1View. Vom completa aceasta harta ca mai jos:
BEGIN_MESSAGE_MAP(CCap1View, CView)
//}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
va trebui sa declaram prototipul functiei afx_msg care trateaza mesajul, în fisierul cap1View.h. Functia va fi similara ca nume si parametri cu cazul precedent. În zona de generare a mesajelor din fisier, vom adauga codul de mai jos:
// Generated message map functions
protected:
//}AFX_MSG
DECLARE_MESSAGE_MAP()
cum identificatorii sunt în general variabile UINT, dar care uzual sunt recunoscute prin denumiri, urmeaza sa definim identificatorii si sa le asociem valori UINT. La începutul fisierului cap1View.h se adauga codul:
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define IDR_CULOAREROSIE 1
#define IDR_CULOAREVERDE 2
#define IDR_CULOAREALBASTRA 3
#define IDR_CULOAREGALBENA 4
class CCap1View : public CView
else
AfxMessageBox(" Creare meniu esuata!");
Crearea unui meniu se face cu functia BOOL CreateMenu( ). Aceasta functie creeaza meniul si îl ataseaza obiectului CMenu corespunzator. Resursa meniu creata este goala, urmând ca în ea sa fie adaugate elemente. O alternativa este functia BOOL CreatePopupMenu( ), având acelasi rol, dar care creeaza un meniu derulant. Aceasta este functia de creare a meniului utilizata în functia de mai sus. Ambele functii returneaza o valoare diferita de 0 în caz de succes, respectiv 0 în caz de esec.
Adaugarea elementelor de meniu se poate face cu functiile de ma jos:
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ). Functia insereaza un element în meniu pe pozitia specificata de nPosition. nFlags reprezinta un indicator care specifica tipul elementului introdus, precum si o combinatie de indicatori de stil. Indicatorii de stil sunt adaugati printr-o operatie SAU logic la tipul elementului. Tipul elementului este specificat în tabelul de bai jos:
Tabelul 5.
Tip |
Semnificatie |
MF_BYCOMMAND MF_BYPOSITION |
elementul se insereaza înaintea unui element de meniu existent, specificat prin identificatorul ID; elementul se insereaza în meniu la pozitia specificata. Primul element va avea pozitia nPosition 0. Daca valoarea nPosition elementul va fi inserat la coada meniului. |
Indicatorii de stil pot fi:
Tabelul 6.
Indicator |
Semnificatie |
MF_STRING MF_SEPARATOR |
noul element contine un pointer spre un sir de caractere obisnuit, terminat cu terminatorul NULL (0); noul element este un separator (o linie orizontala). În acest caz, urmatorul parametru nu se mai transmite; |
Parametrul nIDNewItem reprezinta identificatorul ce va fi atribuit elementului care urmeaza sa fie inserat, iar ultimul parametru reprezinta continutul afisat pentru noul element de meniu.
În cazul nostru, pe prima pozitie a meniului contextual se insereaza elementul IDR_CULOAREROSIE, iar textul afisat va fi Rosu;
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ). Functia adauga la sfârsitul meniului un nou element. Parametrul nFlags poate lua una din valorile:
Tabelul 7.
Indicator |
Semnificatie |
MF_STRING MF_SEPARATOR |
noul element contine un pointer spre un sir de caractere obisnuit, terminat cu terminatorul NULL (0); noul element este un separator (o linie orizontala). În acest caz, urmatorul parametru nu se mai transmite; |
Acesti indicatori pot fi completati (prin operatia de SAU logic) cu altii, specificând modul de lucru al meniului derulant. Acesti indicatori suplimentari sunt tratati în tabelul de mai jos:
Tabelul 8.
Indicator |
Semnificatie |
MF_CHECKED MF_UNCHECKED MF_ENABLED MF_DISABLED MF_GRAYED MF_POPUP MF_MENUBARBREAK MF_MENUBREAK |
afiseaza marcajul de validare alaturi de elementul nou inserat anuleaza indicatorul de validare activeaza elementul de meniu dezactiveaza elementul de meniu similar MF_DISABLED elementul de meniu are asociat un submeniu specificat de catre identificatorul transmis ca parametru elementul de meniu este plasat în cazul meniurilor derulante pe o linie sau coloana noua, separat printr-o linie similar MF_MENUBARBREAK, dar fara linie verticala |
urmeaza declararea variabilei private Color si initializarea ei în constructor. De asemenea, se implementeaza functia OnDraw() ca la paragraful 5.1;
în final, se vor implementa functiile care trateaza comenzile asupra elementelor de meniu. si în acest caz, va trebui sa refacem manual actiunile AppWizard.
se declara functiile afx_msg în fisierul cap1View.h ca mai jos:
// Generated message map functions
protected:
int Color;
//}AFX_MSG
DECLARE_MESSAGE_MAP()
se declara macrourile corespunzatoare în MESSAGE_MAP, în fisierul cap1View.cpp:
BEGIN_MESSAGE_MAP(CCap1View, CView)
//}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
se implementeaza functiile corespunzatoare, la sfârsitul aceluiasi fisier:
void CCap1View::OnCuloarerosie()
void CCap1View::OnCuloareverde()
void CCap1View::OnCuloarealbastra()
void CCap1View::OnCuloaregalbena()
Elementele de meniu pot fi modificate dinamic, prin program, prin intermediul functiei BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL ), parametrii având aceleasi semnificatii ca si la AppendMenu().
De exemplu, adaugând linia îngrosata în cod, meniul nu va mai fi pe doua coloane:
void CCap1View::OnContextMenu(CWnd* pWnd, CPoint point)
else
AfxMessageBox(" Creare meniu esuata!");
Bare cu instrumente
Barele cu instrumente reprezinta o modalitate rapida de furnizare a comenzilor pentru program. AppWizard genereaza o bara cu instrumente standard pentru aplicatiile SDI. Aceasta bara este un obiect de clasa Ctoolbar m_wndToolBar, definit în cadrul clasei CMainFrame, clasa care raspunde de gestionarea ferestrei cadru.
Pentru afisarea barei cu instrumente standard, AppWizard implementeaza un mecanism destul de complex, descris mai jos.
BOOL CreateEx(CWnd* pParentWnd,
DWORD dwCtrlStyle = TBSTYLE_FLAT, DWORD dwStyle = WS_CHILD |
WS_VISIBLE | CBRS_ALIGN_TOP, CRect rcBorders = CRect(0, 0, 0, 0),
UINT nID = AFX_IDW_TOOLBAR);
Primul parametru al functiei este un pointer spre fereastra parinte. Al doilea parametru reprezinta un stil aditional pentru crearea barelor de instrumente înglobate. Uzual el are valoarea TBSTYLE_FLAT. Parametrul 3 furnizeaza stilul de afisare a barei de instrumente, ca o combinatie de indicatori, obtinuta prin intermediul operatiei SAU logic. Parametrul 4 specifica coordonatele obiectului CRect care margineste bara de instrumente. Al cincilea parametru este identificatorul barei cu instrumente afisate. Functia returneaza o valoare diferita de 0 în caz de succes, respectiv 0 la esec.
Crearea barei este facuta de catre AppWizard în functia CMainFrame::OnCreate(), pe baza codului de mai jos:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
Se observa ca bara de instrumente este creata în fereastra cadru, aceasta fiind pointata de pointerul ascuns this (încarcat cu adresa obiectului fereastra cadru, de clasa CMainFrame). Al doilea parametru precizeaza faptul ca bara de instrumente va avea butoane plate. Daca se doreste ca butoanele sa fie în relief, acest parametru trebuie sa ia valoarea 0.
Indicatorii de stil (prezentati în tabelul de mai jos) pot fi transmisi în orice combinatie. Este absolut obligatoriu însa transmiterea indicatorului de fereastra copil si de fereastra vizibila.
Tabelul 9.
Indicator |
Semnificatie |
|
WS_CHILD WS_VISIBLE CBRS_TOP CBRS_BOTTOM CBRS_LEFT CBRS_RIGHT CBRS_ALIGN_ANY CBRS_FLOAT_MULTI CBRS_TOOLTIPS CBRS_FLYBY CBRS_GRIPPER CBRS_SIZE_DYNAMIC CBRS_SIZE_FIXED |
bara apartine unei ferestre copil bara este vizibila ancoreaza bara în partea superioara a ferestrei cadru si traseaza o bordura sub ea ancoreaza bara în partea inferioara a ferestrei cadru si traseaza o bordura peste ea ancoreaza bara în partea stânga a ferestrei cadru si traseaza o bordura în dreapta ei ancoreaza bara în partea dreapta a ferestrei cadru si traseaza o bordura în stânga ei ancoreaza bara oriunde în fereastra cadru exista mai multe bare care se pot misca într-o fereastra cadru bara de controale are atasate etichete flotante pentru descrierea actiunilor actualizeaza continutul mesajelor la actualizarea etichetelor flotante adauga o bara de agatare pentru deplasarea barei de instrumente permite modificarea dimensiunilor barei prin actiuni ale mouse-lui dimensiune barei nu poate fi modificata |
|
Bara cu instrumente este apoi încarcata cu functia BOOL LoadToolBar( UINT IDResource ), transmitându-i ca argument numele resursei în care se încarca bara, în cazul nostru IDR_MAINFRAME
Daca operatiile de creare sau încarcare esueaza, functia afiseaza un mesaj de eroare si se termina returnând un cod de eroare.
O bara de instrumente o data creata, poate fi lasata libera, la fel ca si orice fereastra standard, putând fi miscata cu ajutorul mouse-lui, sau, mai frecvent, poate fi ancorata de fereastra parinte. Prin ancorare, bara cu instrumente este legata de fereastra parinte, fara a mai fi afisata fereastra cadru copil în care este creata bara. Specificarea pozitiei de ancorare a barei se face prin intermediul functiei void EnableDocking( DWORD dwStyle ). Indicatorul de ancorare dwStyle poate lua una din valorile:
Tabelul 10.
Indicator |
Semnficatie |
CBRS_ALIGN_ANY CBRS_ALIGN_TOP CBRS_ALIGN_BOTTOM CBRS_ALIGN_LEFT CBRS_ALIGN_RIGHT |
Permite ancorarea de orice parte a ferestrei cadru permite ancorarea de latura superioara a ferestrei cadru permite ancorarea de latura inferioara a ferestrei cadru permite ancorarea de latura stânga a ferestrei cadru permite ancorarea de latura dreapta a ferestrei cadru |
Bara de instrumente este ancorata prin linia m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); care are ca efect ancorarea barei de instrumente în orice pozitie a ferestrei cadru. Pentru a putea face acest lucru, trebuie ca si fereastra cadru, la rândul ei, sa permita ancorarea unei bare pe orice latura a sa. Acest fapt este declarat de linia EnableDocking(CBRS_ALIGN_ANY);
Ancorarea efectiva a barei de instrumente se face cu functia void DockControlBar( CControlBar* pBar, UINT nDockBarID = 0, LPCRECT lpRect = NULL ) în care doar primul parametru este obligatoriu si reprezinta un pointer spre bara de instrumente care trebuie afisata.
Daca se transmite al doilea parametru, acesta specifica pozitia de ancorare a barei de controale. El poate lua una din valorile de mai jos:
Tabelul 11.
Indicator |
Semnificatie |
AFX_IDW_DOCKBAR_TOP AFX_IDW_DOCKBAR_ BOTTOM AFX_IDW_DOCKBAR_LEFT AFX_IDW_DOCKBAR_RIGHT |
ancoreaza bara de instrumente de latura superioara a ferestrei cadru ancoreaza bara de instrumente de latura inferioara a ferestrei cadru ancoreaza bara de instrumente de latura stânga ferestrei cadru ancoreaza bara de instrumente de latura dreapta a ferestrei cadru |
Al treilea parametru permite specificarea exacta a pozitiei de ancorare, prin intermediul unui pointer la o structura CRect, continând coordonatele de ancorare.
O alterntiva la ancorarea barei este lasarea ei flotanta. Acest lucru se face prin intermediul functiei FloatControlBar().
Modul de adaugare a butoanelor în bara de instrumente standard a fost exemplificat la paragraful 3. În acelasi paragraf, s-a vazut cum sunt adaugate functiile care trateaza mesajul COMMAND primit de la butoane. Ca o observatie, si în cazul butoanelor barei de stare se pot face initializari, interceptând mesajul UPDATE_COMMAND_UI similar elementelor de meniu.
6.1 Adaugarea unei bare cu instrumente utilizator în fereastra cadru
În general, pentru aplicatiile mai complexe, nu este suficient sa se personalizeze bara de stare standard oferita de scheletul AppWizard. Marea majoritate a aplicatiilor utilizeaza si alte bare de unelte. Deci, este util sa se poata insera în cadrul programului noi bare de instrumente.
Pentru a exemplifica inserarea si utilizarea unor noi bare de instrumente, vom construi o bara noua, ancorata în partea stânga a ferestrei cadru, având patru butoane, de culoare rosie, verde, albastra si galbena. Apasarea butoanelor va avea acelasi efect ca si selectarea componentelor meniului contextual.
Pentru a utiliza o noua bara de instrumente în program, va fi necesar întâi sa inseram o noua resursa de tip Toolbar. Pentru aceasta se parcurg pasii:
în Resource View, se face clik dreapta pe cap1 resources si în meniul contextual se alege Insert.;
în Insert Resorce se alege Toolbar si se apasa New;
noii resurse bara de instrumente inserate i se schimba identificatorul în IDR_COLORBAR (meniu contextual, Properties, etc);
în noua bara de instrumente se adauga cele 4 butoane colorate, avînd respectiv identificatorii ID_ROSU ID_VERDE ID_ALBASTRU si ID_GALBEN si respectiv completat în caseta Prompt Coloreaza in rosu, Coloreaza in verde, Coloreaza in albastru si Coloreaza în galben;
Cu aceasta, resursa bara cu instrumente este inserata în proiect. Urmeaza ca ea sa fie atasata ferestrei cadru si sa raspunda la mesaje.
va trebui întâi, ca în fisierul MainFrm.h sa declaram o variabila de clasa CToolBar care sa implementeze functionalitatea barei cu instrumente. Fie m_wndColorBar aceasta variabila. Vom insera în acest fisier linia:
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
CToolBar m_wndColorBar;
sau, sa fel de bine o putem insera ca Add Member Variable. Nu este absolut necesar ca variabila sa fie privata, poate fi la fel de bine publica, mai ales daca vom dori sa o apelam din alta clasa, cum ar fi CCap1View.
urmeaza modificarea functiei OnCreate() pentru crearea si ancorarea barei de instrumente. Vom insera în aceasta functie codul:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
m_wndColorBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndColorBar);
return 0;
în acest moment, bara va apare în partea stânga a ferestrei cadru, dar butoanele nu vor fi activate. Pentru a deveni active, trebuie sa implementam functiile care raspund la mesajul COMMAND asociat. Functiile se adauga cu Class Wizard, având grija ca la Class name: sa fie selectat CCap1View. Se propune urmatoarea implementare pentru functiile ce raspund la apasarea butoanelor:
void CCap1View::OnRosu()
void CCap1View::OnVerde()
void CCap1View::OnAlbastru()
void CCap1View::OnGalben()
În acest moment, bara de instrumente este complet functionala.
Bare de dialog
Barele de dialog sunt bare de instrumente care au în componenta obiecte caracteristice casetelor de dialog. Ele sunt încapsulate de clasa CDialogBar, care, fiind derivata direct din clasa CControlBar va mosteni complet functionalitatea acesteia.
Pentru exemplificarea modului de lucru cu bare de dialog, vom insera o bara de dialog care contine un obiect ComboBox, în care sunt inserate culorile discurilor. La selectia culorii, aceasta va fi transmisa functiei OnDraw(). Trebuie urmati pasii:
se insereaza în Resource View o noua resursa de tip dialog, alegând o resursa IDD_DIALOGBAR
se schimba identificatorul resursei în IDD_BARA_DIALOG_CULORI
se insereaza în resursa o eticheta statica cu
textul Culoare discuri,
si o caseta combinata (fig. 12);
se schimba identificatorul casetei combinate în IDD_COMBO_CULOARE, având grija ca optiunea Sort de la Styles sa nu fie validata;
Urmeaza implementarea codului care afiseaza si trateaza mesajele provenite de la bara de dialog. Pentru aceasta :
se adauga clasei CMainFrame (Add Member Variable) variabila publica CDialogBar m_wndColorDialogBar. Variabila trebuie sa fie publica, pentru ca va fi folosita în alta clasa;
se insereaza în functia OnCreate() codul de mai jos:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
// TODO: Delete these three lines if you don't want the toolbar to
/ be dockable
m_wndColorDialogBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndColorDialogBar);
return 0;
Bara de dialog este creata prin apelul functiei BOOL Create( CWnd* pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID ) care primeste urmatorii parametri:
un pointer la fereastra parinte;
identificatorul machetei barei de dialog care urmeaza sa fie creata;
starea de ancorare a barei de dialog, prin intermediul unuia din indicatorii de aliniere de mai jos:
Tabelul 12.
Indicator |
Semnificatie |
CBRS_TOP CBRS_BOTTOM CBRS_LEFT CBRS_RIGHT CBRS_NOALIGN |
ancoreaza bara de dialog în partea superioara a ferestrei cadru ancoreaza bara de dialog în partea inferioara a ferestrei cadru ancoreaza bara de dialog în partea stânga a ferestrei cadru ancoreaza bara de dialog în partea dreapta a ferestrei cadru bara nu este ancorata de fereastra cadru |
identificatorul barei de dialog propriuzise. Aceasta este o marime UINT care poate lua valori în intervalul cuprins între AFX_IDW_CONTROLBAR_FIRST si AFX_IDW_CONTROLBAR_LAST. Este bine sa se utilizeze identificatorii în sens descrescator, pentru a nu se suprapune peste identificatorii deja existenti. De aceea, pentru prima bara inserata se va folosi identificatorul AFX_IDW_CONTROLBAR_LAST-1, apoi AFX_IDW_CONTROLBAR_LAST-2, etc.
Bara de dialog este apoi ancorata la fel ca orice bara de instrumente, prin intermediul functiilor EnableDocking() si DockControlBar();
în acest moment, bara este afisata, urmând sa completam continutul casetei combinate. Functia care se apeleaza de fiecare data când se creeaza un obiect reprezentare este OnInitialUpdate(). Va trebui deci sa supraînscriem aceasta functie, iar în corpul ei sa apelam functia de initializare a casetei combinate. Supraînscrierea functiei OnInitialUpdate() se face astfel:
se intra în ClassWizard;
la eticheta Message Maps, având grija ca în caseta Class name: sa fie CCap1View, se selecteaza în caseta Messages: mesajul OnInitialUpdate;
Se apasa Add Function si Edit Code, implementându-se codul functiei ca mai jos:
void CCap1View::OnInitialUpdate()
Se implementeaza functia PopulateCombo() (Class View, meniu contextual pentru clasa CCap1View, Add Member Function, etc.) ca mai jos:
void CCap1View::PopulateCombo()
Deoarece variabila CDialogBar m_wndColorDialogBar este o variabila declarata în clasa CMainFrame, dar initializarea si tratarea mesajelor casetei combinate se face în metode ale clasei CCap1View, este necesar prima data sa construim un pointer spre fereastra cadru, care este fereastra parinte a ferestrei care încadreaza bara. Acest pointer este returnat de functia CFrameWnd* GetParentFrame() const. Pe baza acestui pointer, se construieste un pointer la caseta combinata, prin intermediul caruia vor putea fi apelate metodele ce încapsuleaza functionalitatea acesteia. Apoi, se populeaza caseta si se declara elementul Verde ca fiind selectat implicit.
Pentru a putea apela variabile din clasa CMainFrame, la începutul fisierului cap1View.cpp se insereaza linia
#include "cap1Doc.h"
#include "cap1View.h"
#include "MainFrm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
urmeaza implementarea functiei care raspunde la mesajul CBN_SELCHANGE generat de caseta combinata. Schimbarea selectiei elementului din caseta, va avea ca efect actualizarea cu indexul sau a variabilei Color. Pentru aceasta:
în Resource View se selecteaza Dialog-IDD_BARA_DIALOG_CULORI
se selecteaza caseta combinata si în meniul contextual se alege ClassWizard;
nu e necesar sa se creeze o clasa pentru bara de dialog. Având grija ca în caseta Class name: sa fie selectat CClas1View, iar în caseta Object IDs: sa fie IDC_COMBO_CULOARE, se selecteaza mesajul CBN_SELCHANGE în caseta Messages:
se apasa Add Function si Edit Code;
se implementeaza pentru functia OnSelchangeComboCuloare() codul:
void CCap1View::OnSelchangeComboCuloare()
În acest moment, aplicatia este complet functionala.
Bare de stare
Bara de stare este afisata în general în partea inferioara a ferestrei cadru, dând diferite informatii despre starea programului. Implementarea standard a barei de stare furnizeaza indicatori pentru starea tastelor CAPS LOCK, NUM LOCK si SCRL LOCK.
Bara de stare este un obiect de clasa CStatusBar, clasa derivata direct din CControlBar. Crearea si afisarea barei de stare este facuta de catre AppWizard tot în functia OnCreate() a clasei CMainFrame.
Clasa CMainFrame declara o variabila
CStatusBar m_wndStatusBar;
care reprezinta obiectul care mapeaza bara de satre. Crearea efectiva a barei se face prin secventa de cod
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD |
WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR )
Aceasta functie primeste trei argumente: un pointer la fereastra parinte, indicatorii de pozitie, aceeasi ca si cei prezentati în tabelul 12 si apoi identificatorul machetei barei de stare.
Daca apelul functiei Create() reuseste, se apeleaza functia
BOOL SetIndicators( const UINT* lpIDArray, int nIDCount );
care realizeaza initializarea casetei de stare. Functia primeste ca argumente un pointer spre un vectori de indicatori, declara la începutul fisierului MainFrm.cpp si un contor care specifica numarul de indicatori.
Vectorul de indicatori are forma:
static UINT indicators[] =
unde ID_INDICATOR_CAPS ID_INDICATOR_NUM si ID_INDICATOR_SCRL sunt identificatorii celor trei indicatori impliciti din bara de stare.
Numarul de indicatori se calculeaza împartind dimensiunea vectorului la dimensiunea unui indicator (sizeof(indicators)/sizeof(UINT)
ID_SEPARATOR reprezinta un spatiu normal, neadâncit, în bara de stare, folosit ca separator între grupuri de indicatori.
Observatie: Indicatorii apar în bara de instrumente în aceeasi ordine în care identificatorii lor apar în vector!
8.1 Personalizarea barei de stare
În bara de stare
se pot adauga noi indicatori, cu functii specificate de utilizator.
Pentru adaugarea unui nou indicator sunt de efectuat mai multe
operatii, care vor fi descrise mai jos. Afisarea efectiva a
indicatorului o data creat, se va face prin interceptarea mesajului UPDATE_COMMAND_UI asociat si prin
transmiterea valorii TRUE
pentru functia Enable().
Pentru exemplificare, vom insera în bara de instrumente 2 indicatori, cu identificatorii ID_INDICATOR_CULOARE si respectiv ID_INDICATOR_ALES. Primul indicator va afisa tot timpul textul "Ati ales culoarea", iar cel de-al doilea va afisa culoarea aleasa la apasarea unui buton din bara de instrumente utilizator.
Observatie: Toate cele ce urmeaza se refera la clasa CMainFrame.
Se parcurg pasii:
se insereaza identificatorii indicatorilor în tabela de siruri. Pentru aceasta se parcurg etapele:
se intra în Resource View si se face dublu click pe String Table;
se deruleaza tabela de siruri pâna se gaseste sectiunea cu identificatorii indicatorilor din bara de stare (ID_INDICATOR_NUM, etc.);
se selecteaza ultimul identificator din sectiune si se apasa tasta Insert pentru a insera un nou element în tabela de siruri. În caseta String Proprieties aparuta, se completeaza la ID: ID_INDICATOR_CULOARE si la Caption: Ati ales culoarea (fig. 13);
se repeta operatia pentru ID_INDICATOR_ALES. Se vor tasta unul sau mai multe blancuri la Caption, în caz contrar identificatorul nefiind luat în considerare;
urmeaza completarea vectorului de identificatori. Se completeaza acest vector ca mai jos:
static UINT indicators[] =
la pasul urmator va trebui sa interceptam mesajul UPDATE_COMMAND_UI pentru cei doi identificatori. Din pacate, acesti identificatori nu sunt receptionati de ClassWizard, deci va trebui sa implementam functiile manual. Se parcurg pasii:
se insereaza macrourile corespunzatoare în MESSAGE MAP. Macrourile se plaseaza în general dupa cele generate de ClassWizard, adica dupa linia "//}}AFX_MSG_MAP
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//}AFX_MSG_MAP
ON_UPDATE_COMMAND_UI(ID_INDICATOR_CULOARE,
OnUpdateIndicatorCuloare)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_ALES, OnUpdateIndicatorAles)
END_MESSAGE_MAP()
se declara prototipurile functiilor corespunzatoare în fisierul MainFrm.h:
// Generated message map functions
protected:
//}AFX_MSG
afx_msg void OnUpdateIndicatorCuloare(CCmdUI* pCmdUI);
afx_msg void OnUpdateIndicatorAles(CCmdUI* pCmdUI);
DECLARE_MESSAGE_MAP()
se implementeaza codul functiilor la sfârsitul sectiunii de constructie-distrugere a fisierului MainFrm.cpp:
.
void CMainFrame::OnUpdateIndicatorCuloare(CCmdUI* pCmdUI)
void CMainFrame::OnUpdateIndicatorAles(CCmdUI* pCmdUI)
// CMainFrame diagnostics
.
La compilare si executie, indicatorii vor fi afisati în bara de stare.
Urmeaza sa implementam în clasa CMainFrame o functie care sa afiseze un text dorit în bara de stare, în spatiul indicatorului ID_INDICATOR_ALES; Pentru aceasta:
în Class View, pentru CMainFrame, se adauga functia publica void ScrieInBara(CString strText) (meniu contextual, Add Member Function, etc.);
se implementeaza pentru aceasta functie codul:
void CMainFrame::ScrieInBara(CString strText)
Functia int CommandToIndex( UINT nIDFind ) const returneaza pozitia în bara de stare a indicatorului al carui identificator este transmis ca parametru. Pe baza acestui indice, în indicatorul dat se înscrie textul dorit, cu ajutorul functiei BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE ). Apoi, este determinat contextul dispozitiv asociat barei de stare, iar prin intermediul lui se apeleaza functia CSize GetTextExtent( const CString& str ) const, care scaleaza caseta de pe bara de stare la dimensiunea textului.
În final, se apeleaza functia void SetPaneInfo( int nIndex, UINT nID, UINT nStyle, int cxWidth ), care seteaza stilul de afisare. Primeste ca argumente pozitia indicatorului pentru care se specifica sirul de afisare, identificatorul indicatorului, sau un nou identificator daca dorim sa-l schimbam pe cel curent, un indicator de stil, conform tabelului 13, precum si latimea casetei indicatorului. În cazul nostru, latimea este setata la dimensiunea textului înscris.
Tabelul 13.
Indicator de stil |
Semnificatie |
SBPS_NORMAL SBPS_POPOUT SBPS_DISABLED SBPS_STRETCH SBPS_NOBORDERS |
caseta de indicare normala, cu aspect adâncit caseta de indicare iesita în relief nu se mai afiseaza continutul textului în indicator caseta indicatorului ocupa tot spatiul ramas pe bara de stare (nu pot fi doi indicatori cu acest stil) caseta de indicare nu are aspect 3D |
se modifica functiile asociate butoanelor din bara de unelte utilizator, astfel încât sa înscrie culorile alese în indicatorul barei de stare. Cum aceste functii sunt în clasa CCap1View, va trebui ca în fiecare sa determinam un pointer spre obiectul CMainFrame care contine fereastra cadru si prin intermediul lui sa apelam functia ScrieInBara(). Pentru functiile asociate apasarii butoanelor, se propun modificarile:
void CCap1View::OnRosu()
void CCap1View::OnVerde()
void CCap1View::OnAlbastru()
void CCap1View::OnGalben()
|