Biologie | Chimie | Didactica | Fizica | Geografie | Informatica | |
Istorie | Literatura | Matematica | Psihologie |
Casete de dialog modale si nemodale
In marea majoritate a aplicatiilor, actiunea asupra butoanelor implica aparitia unor noi casete de dialog, care la randul lor vor contine o serie de obiecte de interfata. Aceste casete, la randul lor, pot lansa alte casete si asa mai departe.
Pentru fiecare caseta suplimentara inserata in proiect, trebuie inserate o resursa de tip macheta de dialog si o clasa asociata, derivata din clasa CDialog. Macheta de dialog poate fi creata prin intermediul editorului de resurse si apoi clasa derivata poate fi creata cu ajutorul Class Wizard. Noua clasa poate fi dezvoltata pentru a asigura gestiunea controalelor aflate in spatiul noii machete.
Abordarea uzuala a acestor probleme, este implementarea prin intermediul casetelor de dialog modale. La afisarea unei astfel de casete, restul interfetei cu utilizatorul devine inaccesibil, fiind necesara inchiderea casetei inainte de a relua lucrul obisnuit. Casetele de dialog pot fi cascadate, astfel incat o caseta sa o apeleze pe alta, determinand in acest fel transmiterea controlului catre caseta de dialog cea mai recent deschisa, aflata in fata. Dupa inchiderea acesteia, se reda controlul casetei de dialog apelante.
Exista si posibilitatea lucrului cu casete de dialog nemodale, ca alternativa la casetele de dialog modale. Aceste casete permit accesul si la restul interfetei in timpul in care sunt afisate. In fapt, o serie de controale sunt implementate ca si casete de dialog nemodale.
Adaugarea unei noi machete de caseta de dialog
Pentru inserarea unei noi machete de tip caseta de dialog, se vor executa comenzile:
a. se selecteaza Resource View pentru afisarea resurselor proiectului;
b. se apasa click stanga pe Dialog pentru a fi selectat si apoi se apasa click dreapta pentru a afisa meniul contextual;
c. se alege optiunea Insert, prin aceasta deschizandu-se caseta de dialog Insert Resource se selecteaza Dialog pentru inserarea unei casete de dialog uzuale ;
d. se apasa New pentru inserarea noii resurse de dialog. Ea trebuie sa apara acum in Resource View ca subordonata (de tip Dialog) lui nume_proiect resources;
e. se apasa click dreapta pentru afisarea meniului contextual si se alege Properties;
f. se stabileste ID-ul noii casete ca IDD_CASETADERIVATA;
Pasul urmator este derivarea unei clase asociate machetei, din clasa CDialog. Aceasta clasa va incarca si va afisa caseta de dialog si va raspunde la mesajele provenite de la caseta de dialog sau de la controalele sale. Crearea clasei derivate se face cu Class Wizard.
se apasa click pe noua resursa pentru a o selecta (sau dublu click pe IDD_CASETADERIVATA din Resource View;
se lanseaza Class Wizard;
Class Wizard va sesiza ca macheta de dialog este o resursa noua
si va afisa caseta de dialog Adding a
Class (fig. 15.2);
se apsa OK pentru acceptarea optiunii implicite de creare a unei noi clase;
apare caseta de dialog New Class
in caseta Name se specifica numele noii clase derivate, care incapsuleaza caseta de dialog. Prin conventie, clasele derivate din clasele MFC incep cu litera c. Fie CDerivataDlg numele acestei noi clase;
o data cu tastarea numelui pentru clasa derivata, automat, in caseta File Name va apare numele fisierului care contine implementarea clasei. Acest nume este implici similar cu numele clasei, deci, in cazul nostru, DerivataDlg.cpp. Acest nume poate fi schimbat de catre utilizator;
in caseta Base Class este afisata clasa de baza din care este derivata noua clasa. Aceasta este in cazul nostru CDialog;
in caseta Dialog ID este afisat identificatorul casetei inserate. Si acesta poate fi schimbat, in acest caz clasa referindu-se la alta macheta;
se apasa OK pentru crearea noii clase, scheletul acesteia fiind creat automat in fisierele .h si .cpp;
Class Wizard va afisa apoi pagina Message Maps obisnuita, putand fi adaugate functii de tratare a evenimentelor din noua caseta de dialog;
Se apasa OK pentru inchiderea Class Wizard;
Dupa adaugarea noii clase care incapsuleaza caseta de dialog, aceasta va trebui sa apara in Class View. Implicit, pentru aceasta clasa sunt create constructorul si o functie de schimb de date. Constructorul clasei are implementarea:
In constructor, un membru al clasei dialog derivate, CDerivataDlg::IDD, este transmis clasei de baza, care este CDialog. Acest membru este declarat in definitia clasei (fisierul DerivataDlg.h) ca o valoare de tip enumerare si initializat cu identificatorul de resursa al casetei de dialog:
public:
CDerivataDlg(CWnd* pParent = NULL);// standard constructor
// Dialog Data
//;
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
Observatie: In cazul in care proiectul contine mai multe clase de dialog, este necesara acordarea unei atentii sporite in cazul inserarii de noi variabile cu ajutorul Class Wizard. Trebue ca sa fie selectata clasa potrivita in caseta Class Name, in caz contrar variabila fiind asociata unei alte clase.
Uzual, noua caseta de dialog este apelata ca raspuns la o actiune asupra unui buton din caseta parinte, sau selectarea unui meniu. Deci, in functia de raspuns la evenimentul din caseta parinte, va trebui realizata si afisarea casetei de dialog derivate. Aceasta se realizeaza in doi pasi:
se creeaza o instanta a clasei dialog, declarand un obiect local functiei de tratare, ca de exemplu
CDerivataDlg dlgCasetaDerivata(this);
In linia de mai sus, CDerivataDlg este clasa de dialog derivata generata de App Wizard, care este instantiata la obiectul dlgCasetaDerivata. Constructorului ii este transmis pointerul special this din C++. Cum functia de tratare apartine unei clase derivate din CWnd noii casete de dialog ii vor fi transmise informatii corecte cu privire la fereastra parinte.
Pentru ca procesul de compilare sa recunoasca clasa de dialog derivata, trebuie ca fisierul header de definitie a clasei sa fie inclus la inceputul modulului care contine instantierea. De exemplu, in cazul nostru, daca definitia noii clase se afla in DerivataDlg.h, in fisierul wiz13Dlg.cpp care instantiaza aceasta clasa, la inceput va fi inserata linia
// wiz13Dlg.cpp : implementation file
#include 'stdafx.h'
#include 'wiz13.h'
#include 'wiz13Dlg.h'
#include 'DerivataDlg.h'
#ifdef _DEBUG
#define new DEBUG_NEW
.
se afiseaza caseta si se declanseaza operarea modala a casetei de dialog. Acest lucru se face prin apelul functiei DoModal(). Ca efect, va fi afisata caseta de dialog impreuna cu controalele sale componente si va fi iterata o bucsa de mesaje locale, mesajele fiind transmise de la controalele casetei de dialog catre implementarea utilizator, pana la inchiderea casetei de dialog.
Daca apare o eroare la afisarea noii casete de dialog, DoModal() intoarce un cod de eroarea, care poate fi -1 sau IDABORT. Daca afisarea casetei reuseste, din DoModal() se revine la apelul EndDialog(). Functia EndDialog() primeste o valoare intreaga, care este apoi intoarsa de DoModal() ca si cod de retur la terminare.
Functia EndDialog() este apelata in implementarile implicite ale functiilor OnOK() si OnCancel(), lansate in executie de evenimentul BN_CLICKED pentru butoanele OK si Cancel. In acest caz, ea va transmite codurile IDOK si respectiv IDCANCEL funtiei DoModal().
Caseta de dialog poate fi inchisa de utilizator in orice moment si prin program, prin apelarea functiei EndDialog().
Ca exemplu, se propune
crearea casetei de dialog de baza din fig. 15.4, avand identificatorii:
IDC_NUME si respectiv IDC_PRENUME pentru casetele de editare Nume si Prenume si IDC_COPII, respectiv IDC_AFISEAZA pentru butoane.
Apasarea butonului Copii va lansa o
caseta derivata, ca in fig. 15.5., avand identificatorul pentru casetele de
editare Prenume IDC_PRENUMECOPIL, Nume IDC_NUMECOPIL respectiv Virsta IDC_VIRSTA, iar pentru caseta
combinata, IDC_SEX.
Pentru inceput, vom citi casetele de editare din caseta de dialog de baza, ca si in exemplele trecute. Citirea continutului se face la modificarea acestuia, deci va trebui sa asociem casetelor functiile de tratare a mesajului EN_CHANGE. Evident, acest lucru se face cu ajutorul Class Wizard, asigurandu-ne ca in caseta Class Name este selectata CWiz13Dlg, adica clasa de dialog care incapsuleaza fereastra de dialog de baza.
Pentru preluarea continutului casetelor, vor fi definite variabilele asociate clasei CWiz13Dlg (cu Add Member Variable) de tip CString strNume si respectiv strPrenume
De asemenea, se va implementa functia care raspunde apasarii butonului Afiseaza. Implementarile sunt:
void CWiz13Dlg::OnChangeNume()
void CWiz13Dlg::OnChangePrenume()
void CWiz13Dlg::OnAfiseaza()
Acum va trebui sa ne ocupam de caseta derivata. Pentru inceput, vom defini variabile care sa mapeze obiectele continute in caseta. Vom insera cu ajutorul Class Wizard, avand grija ca la Class Name sa fie selectat CDerivataDlg variabilele: CString m_strPrenumecopil CString m_strNumecopil int m_nVirsta CComboBox m_cbSex. Apoi va trebui sa lansam in executie noua caseta, la apasarea butonului Copii. Pentru inceput, vom asocia evenimentului BN_CLICKED functia
void CWiz13Dlg::OnCopii()
Am definit caseta derivata care va fi identificata prin dlgCasetaDerivata, fiind apoi afisata. Se pot face unele initializari a anumitor variabile, presupunand de exemplu ca numele copilului este acelasi cu al parintelui si ca acesta a fost completat. Initializarea se face inainte de afisarea casetei derivate.
Initializarile mai complexe, cum ar fi popularea casetei combinate, se fac altfel. Se poate observa ca la compilare, o linie de forma
dlgCasetaDerivata.m_cbSex.AddString("F");
nu va da eroare de compilare, dar va da eroare de asertiune la executie. Sa ne reamintim ca astfel de initializari se faceau, pentru fereastra parinte, in functia OnInitDialog(). Daca ne uitam in implementarea casetei derivate, constatam ca aceasta functie nu exista. Dar, ea este functia ce raspunde mesajului WM_INITDIALOG al ferestrei. Deci, o vom putea crea cu ajutorul Class Wizard, avand grija ca la Class Name sa fie selectat CDerivataDlg. Implementarile functiilor de initializare vor fi:
BOOL CDerivataDlg::OnInitDialog()
void CDerivataDlg::InitCombo()
In mod evident, urmeaza implementarea functiilor de citire a controalelor si de raspuns la evenimente. Pentru casetele de dialog Prenume si Nume, se propune raspunsul la mesajul EN_CHANGE, evident functiile fiind inserate prin intrmediul Class Wizard. Sa ne reamintim ca este absolut obligatorie selectarea CDerivataDlg la Class Name.
void CDerivataDlg::OnChangeNumecopil()
void CDerivataDlg::OnChangePrenumecopil()
Pentru citirea valorii selectate in caseta combinata, vom adauga clasei CDerivataDlg o variabila CString m_strSex. Mesajul la care va raspunde functia de tratare este CBN_SELCHANGE.
void CDerivataDlg::OnSelchangeSex()
15.2 Schimbul si validarea datelor
Atunci cand se mapeaza variabile membru peste controale prin intermediul Class Wizard, este generat automat cod pentru efectuarea schimbului intre controale si variabilele asociate prin intermediul functiei DoDataExchange() a casetei de dialog. Aceasta functie raspunde de transferul datelor in ambele directii, inspre si dinspre controalele casetei de dialog.
Procesul de transfer se numeste schimb de date si este initiat printr-un apel al functiei UpdateData(). Aceasta functie poate primi un parametru de tip BOOLEAN, numit m_bSaveAndValidate, care, daca este FALSE, controalele sunt actualizate pe baza continutului variabilelor asociate, iar daca este TRUE, variabilele iau valoarea inscrisa in controale. Functia UpdateData() este apelata automat de functia OnInitDialog() a clasei de baza CDialog, cu parametrul FALSE pentru a initializa controalele la afisarea casetei de dialog. Functia OnOK() a clasei de baza apeleaza UpdateData() cu parametrul TRUE pentru actualizarea variabilelor asociate cu continutul controalelor.
Exista doua tipuri de functii pentru efectuarea transferului de date.
15.2.1 Functii pentru schimbul de date DDX
Functia DoDataExchange() pentru caseta derivata din exemplul wiz13 este:
void CDerivataDlg::DoDataExchange(CDataExchange* pDX)
}AFX_DATA_MAP
Functia primeste un pointer la un obiect CDataExchange(pDX). Acest obiect retine toate detaliile asociate cu directia transferului de date si cu fereastra vizata de transfer. Pointerul pDX este apoi transmis impreuna cu identificatorul din caseta de dialog si variabila asociata in apelul functiei DDX_Text sau DDX_Control ( generate de Class Wizard in functie de tipul variabilei care mapeaza controlul) in scopul implementarii transferului de date. Exista mai multe astfel de functii DDX_, care raspund la diferite tipuri de controale si de date.
Nume |
Tipuri de control |
Tipuri de date asociate |
DDX_Text DDX_Check DDX_Radio DDX_LBString DDX_LBStringExact DDX_CBString DDX_CBStringExact DDX_LB_Index DDX_CBIndex DDX_Scroll |
Caseta de editare Caseta de validare Grup de butoane de optiune Caseta cu lista derulanta Caseta cu lista derulanta Caseta combinata Caseta combinata Caseta cu lista derulanta, val. de index Caseta combinata, val. de index Bara de derulare |
BYTE, short, int, UINT, long, DWORD, CString, LPTSTR, float, double, COleCurrency, COleDateTime int int CString CString CString CString int int int |
Implicit, aceste functii sunt adaugate automat de Class Wizard, dar pot fi adaugate si de utilizator, manual, in functia DoDataExchange(), dupa codul inserat de Class Wizard.
Functiile DDX_ inspecteaza pointerul la obiectul CDataExchange transmis pentru a determina directia transferului, aflata in membrul bSaveAndValidate. Se poate testa acest indicator si efectuarea de operatii specifice, prin linii de forma:
if (pDX->m_bSaveAndValidate == TRUE) .
Ca o observatie, daca se utilizeaza transferul DDX, in cazul in care variabilele ce stocheaza continutul controalelor sunt utilizate doar in fereastra parinte, functiile de implementare a actiunilor vor fi asupra controalelor ferestrei derivate vor fi:
void CDerivataDlg::OnChangeNumecopil()
void CDerivataDlg::OnChangePrenumecopil()
void CDerivataDlg::OnChangeVirsta()
Este suficienta aceasta implementare, pentru ca oricum, la apelul functiei OnOK() al ferestrei derivate, variabilele mapate in DoDataExchange() vor fi actualizate cu continutul controalelor. Implementarile anterioare sunt necesare doar daca continutul variabilelor este utilizat in propria fereastra, caz in care nu este apelata implicit functia UpdateData() nicaieri. In acest al doilea caz, implementarea OnChangeVirsta() va fi
void CDerivataDlg::OnChangeVirsta()
15.2.2 Functii pentru validare de date DDX
Exista anumite situatii, in care trebuie realizate validari ale variabilelor ce preiau continutul unei casete de dialog. De exemplu, pentru un tip CString mapat peste o caseta de editare, se poate preciza de exemplu numarul maxim de caractere din sirul introdus de utilizator. In cazul unui int de exemplu, se pot preciza valori superioara si inferioara pentru variabila.
La adaugarea unei reguli de validare, Class Wizard va genera linii DDV_ corespunzatoare in functia DoDataExchange(). Daca de exemplu, IDC_VIRSTA poate lua valori doar intre 1 si 11 (precizate in campurile MinimumValue si Maximum Value din Member Variables al Class Wizard), aceasta functie va fi
void CDerivataDlg::DoDataExchange(CDataExchange* pDX)
}AFX_DATA_MAP
Se observa aparitia liniei DDV_MinMaxInt(pDX, m_nVirsta, 1, 18); care specifica faptul ca variabila m_nVirsta ia valori intre 1 si 18.
Exista mai multe functii DDV standard, ca mai jos:
Functia DDV |
Tipul de date |
Descriere |
DDV_MaxCharsDDV_MinMaxByte DDV_MinMaxDouble DDW_MinMaxDword DDV_MinMaxFloat DDV_MinMaxInt DDV_MinMaxLong DDV_MinMaxUnsigned |
CString BYTE double DWord float int long unsigned |
Lim. numarul de caractere introduse Lim. numarul la un interval specificat Lim. numarul la un interval specificat Lim. numarul la un interval specificat Lim. numarul la un interval specificat Lim. numarul la un interval specificat Lim. numarul la un interval specificat Lim. numarul la un interval specificat |
Se pot crea functii de validare proprii, care sa efectueze verificari utilizator, construind functii DDV_ proprii. In acest scop, trebuie creata o functie care promeste un pointer la un obiect CDataExchange(pDX), o referinta la variabila mapata care va fi testata si orice alti parametri specifici.
Prima conditie pe care trebuie sa o verifice functia este daca obiectul CDataExchange se afla in modul salveaza-si-valideaza. Acest lucru se intampla daca pDX->m_bSaveAndValidate este TRUE. Daca acest indicator nu este TRUE, atunci se efectueaza initializarea controalelor pe baza variabilelor membru, asa ca nu sunt necesare teste de validare si functia ar trebui sa-si incheie executia.
Daca variabila mapata este valida, functia se incheie normal. In caz contrar, se efectueaza o actiune de eroare, care poate afisa o caseta de avertizare, sau se pot intreprinde actiuni de corectare a variabilei. In primul caz, dupa afisarea casetei de validare, trebuie apelata functia Fail() a pointerului pDX la obiectul CDataExchange pentru a informa functia UpdateData() ca validarea a esuat.
Se propune implementarea unei validari asupra prenumelui, in sensul ca prenumele trebuie sa inceapa intotdeauna cu litera mare.Prenumele este automat transformat in majuscule. Daca numele contine alte caractere decat litere, se afiseaza un mesaj de avertizare.
Pentru aceasta, se va adauga clasei CDerivataDlg functia membru void CDerivataDlg::DDV_ValidatePren(CDataExchange *pDX, CString &nCheckPren) cu implementarea:
void CDerivataDlg::DDV_ValidatePren(CDataExchange *pDX,
Ø CString &nCheckPren)
}
if (!isupper(nCheckPren[0])) nCheckPren.MakeUpper();
}
Functia DDV_ValidatePren() pentru a fi apelata in functia DoDataExchange(), se insereaza dupa linia de actualizare a variabilei prin functia DDX_ corespunzatoare, apelul functiei DDV_.
void CDerivataDlg::DoDataExchange(CDataExchange* pDX)
}AFX_DATA_MAP
In final, valorile variabilelor apartinand casetei derivate, trebuie preluate si utilizate in caseta parinte. Pentru aceasta, vom adauga ca membre ale clasei CWiz13Dlg variabile cu acelasi nume si tip ca si variabilele de categorie Value ferestrei derivate. Schimbul de date intre variabile se va face in functia OnCopii(), in aceasta functie fiind declarata instanta ferestrei derivate, variabilele apartinand acestei ferestre neexistand in afara functiei. Se modifica apoi functia OnCopii() ca mai jos:
void CWiz13Dlg::OnCopii()
if (nRetCode == IDCANCEL)
Daca codul de retur este IDOK (deci a fost apasat butonul OK al casetei derivate, se face actualizarea variabilelor). Daca a fost apasat Cancel, nu se mai face actualizarea. Functia de afisare va fi completata:
void CWiz13Dlg::OnAfiseaza()
12.3 Casete nemodale
O caseta de dialog nemodala nu acapareaza interfata aplicatiei asa cum o face caseta de dialog modala. Ea este afisata de obicei pentru a completa functionalitatea tipica a aplicatiei prin transmiterea de mesaje si detalii catre aplicatia apelanta. Casetele de dialog nemodale sunt folosite de obicei ca bare de instrumente neflotante, permitand utilizatorului sa efectueze click pe butoanele casetei de dialog pentru a a indica optiuni privind modul de lucru sau alegeri exprimate.
O caseta de dialog nemodala foloseste o macheta de dialog obisnuita pentru a specifica dispunerea controalelor, dar pentru acest tip de caseta butoanele OK si Cancel nu mai au un rol important. De asemenea, se foloseste o clasa de incapsulare derivata din CDialog, care poate fi creata pe baza machetei prin intermediul Class Wizard. Diferentele esentiale intre casetele de dialog modale si cele nemodale se regasesc in modul de apelare si inchidere.
O caseta de dialog nemodala este apelata prin intermediul functiei Create() a clase CDialog. Aceasta functie poate fi apelata fie in constructorul clasei asociate machetei casetei de baza, astfel incat sa se creeze o instanta o data cu instantierea unui obiect de tipul clasei, fie intr-o alta functie, caseta nemodala fiind creata numai la apelul acestei functii.
Functia Create() primeste doi parametri: primul este identificatorul machetei care se creeaza, iar al doilea este un pointer optional catre un obiect reprezentand fereastra parinte. Create() returneaza valoarea TRUE in cazul reusitei, respectiv FALSE in cazul esecului. In cazul succesului, cand Create() returneaza valoarea TRUE, fereastra exista dar nu este vizibila. Pentru a o face vizibila, este nevoie de un apel al functiei ShowWindow() cu parametrul SW_SHOW.
Casetele de dialog modale pot fi in general incapsulate fara probleme in interiorul unei functii, deoarece ele preiau controlul absolut al programului in momentul in care sunt lansate in executie. Casetele nemodale, datorita faptului ca nu preiau controlul programului, vor trebui sa poata fi accesate in orice punct al acestuia. Din aceasta cauza, este bine ca caseta nemodala sa fie creata dinamic, prin intermediul operatorului new, retinand adresa ei intr-o variabila pointer globala sau membru. Caseta de dialog poate fi inchisa la nevoie, distrugand-o prin intermediul operatorului delete. Destructorul clasei de baza CDialog se va ocupa de inchiderea ferestrei casetei de dialog.
Sa exemplificam aceste lucruri,
completand caseta de dialog de baza ca in fig. 15.6.
A fost adaugata o caseta cu lista (ListBox), cu identificatorul IDC_LISTA si avand setati parametrii de stil Selection=Multiple si optiunea Sort deselectata. Cu Class Wizard i se asociaza listei o variabila de categorie Control, deci de tip CListBox, numita m_DisplayList. De asemenea, au fost adaugate trei butoane, IDC_TRIMITE, IDC_START_NEMODAL si IDC_STOP_NEMODAL.
Se propune crearea machetei casetei
de dialog nemodale, ca in fig. 15.7.
Macheta contine butoanele IDC_SUS, IDC_JOS si caseta de editare IDC_MESAJ. Casetei de editare i se asociaza variabila de tip CString m_Mesaj. Acestei noi machete i se asociaza clasa CNemodala, respectand pasii de la inceputul capitolului. Clasa va fi implementata in fisierele Nemodala.h si Nemodala.cpp.
Urmatorul pas este de a modifica constructorul clasei, astfel incat sa fie stabilita operarea nemodala a casetei de dialog si sa o afiseze in care a putut fi creata. Se va modifica constructorul, ca mai jos:
CNemodala::CNemodala(CWnd* pParent /*=NULL*/)
: CDialog(CNemodala::IDD, pParent)
}AFX_DATA_INIT
if (Create(IDD_NEMODALA,pParent))
ShowWindow(SW_SHOW);
Pentru a efectua deschiderea si inchiderea noii casete, se vor atasa functii de tratare a mesajului MN_CLICKED butoanelor IDC_START_NEMODAL si IDC_STOP_NEMODAL. Pentru ca sa poata fi realizata compilarea, clasa casetei de dialog de baza va trebui informata despre existenta clasei casetei nemodale. Acest lucru se face prin includerea fisierului de definitie a clasei in fisierul wiz13Dlg.cpp.
// wiz13Dlg.cpp : implementation file
#include 'stdafx.h'
#include 'wiz13.h'
#include 'wiz13Dlg.h'
#include 'DerivataDlg.h'
#include 'Nemodala.h'
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CNemodala* g_pNemodala=NULL;
De asemenea, a fost declarat un pointer global g_pNemodala la caseta de dialog nemodala, care a fost initializat cu NULL. Prin aceasta ne asiguram ca nu exista deschisa nici o instanta a casetei de dialog nemodale in acest moment. De asemenea, acest pointer va fi utilizat pentru accesarea obiectului caseta nemodala de acum inainte.
Implementarile functiilor de tratare a apasarii butoanelor sunt:
void CWiz13Dlg::OnStartNemodal()
void CWiz13Dlg::OnStopNemodal()
In functia OnStartNemodal() se testeaza daca pointerul global are valoarea NULL. Daca da, inseamna ca caseta nu este deschisa si este creata o instanta a acesteia. Sa ne reamintim ca in constructor era apelata functia ShowWindow(SW_SHOW), deci caseta va fi automat afisata.
In functia OnStopNemodal() se testeaza daca pointerul global are valoarea nu NULL. Daca nu o are, inseamna ca caseta este deschisa si atunci ea poate fi distrusa, de inchiderea ei ocupandu-se, dupa cum s-a aratat anterior, destructorul clasei CDialog. Apoi pointerului global i se atribuie valoarea NULL, pentru a specifica ca caseta nu e deschisa.
Fixarea valorilor sa apelurile functiilor membru ale unei casete de dialog nemodale se pot efectua pe toata durata de viata a acesteia, prin intermediul pointerului de acces corespunzator. La transferul de date intre controale si variabilele membru mapate, prin UpdateData() si DoDataExchange() se aplica regulile deja cunoscute. Este posibil de asemenea sa fie apelate functii sau sa fie fixate variabile membru ale altor obiecte ale aplicatiei in cadrul casetei de dialog modale, ca raspuns la interactiunea utilizatorului cu controalele. In acest scop, trebuie transmisi casetei de dialog pointeri la obiectele respective ale aplicatiei. Constructorul casetei de dialog nemodale este un loc bun pentru transmiterea acestor pointeri. Pentru aceasta, se modifica constructorul clasei asociate, astfel incat la crearea casetei de dialog sa fie transmisi pointeri la obiectele respective ale aplicatiei. Apoi acesti pointeri pot fi retinuti de catre caseta de dialog sub forma de variabile membru, astfel incat oricare dintre functiile componente va putea accesa obiectele in cauza.
Ca exemplu, vom scrie programul astfel incat la apasarea butoanelor IDC_SUS si IDC_JOS a casetei nemodale, sa fie transmise si afisate mesajele ** SUS** si respectiv ** JOS** in caseta cu lista. De asemenea, in sens invers, la apasarea butonului IDC_TRIMITE a casetei de baza, in caseta de editare IDC_MESAJ a casetei nemodale va fi afisat mesajul "Am primit mesajul **SUS**" sau "Am primit mesajul **JOS**" in functie de ultimul mesaj primit, respectiv "Nu am primit nimic" daca nu a fost transmis nici un mesaj.
Prima data, trebuie modificat constructorul clasei casetei de dialog nemodale, pentru a asigura ca pointerul este transmis catre parinte transmis este CWiz13Dlg si nu pointerul CWnd curent. Pentru aceasta, se ajusteza ca mai jos definitia constructorului din fisierul antet Nemodala.h:
class CNemodala : public CDialog
void CNemodala::OnJos()
Functiile de tratare folosesc noul pointer CWiz13Dlg membru m_pParent, pentru a accesa variabila m_DisplayList din caseta de dialog principala. Acest lucru este evident, daca ne gandim ca functiile de acces si obiectul accesat sunt in clase diferite.
De asemenea, putem implementa functia ce raspunde la evenimentul BN_CLICKED corespunzator butonului IDC_TRIMITE din macheta parinte.
void CWiz13Dlg::OnTrimite()
else g_pNemodala->m_Mesaj =
CString('Nu am primit nimic!');
g_pNemodala->UpdateData(FALSE);
}
Se observa ca accesul la caseta de editare din caseta nemodala este facut tot pe baza pointerului la macheta respectiva.
In acest moment, programul este functional, dar are o mica problema. Cu toate ca din macheta casetei nemodale au fost inlaturate butoanele OK si Cancel, aceasta poate fi inchisa fortat prin click pe pictograma cruciulita din dreapta sus. Prin inchiderea fortata a casetei, daca fereastra principala nu intercepteaza un mesaj de inchidere, pointerul si memoria asociate pentru caseta nemodala raman alocate, cu toate ca obiectul nu mai exista. Aceasta poate duce la pierdere de memorie si in plus, nu va putea fi apelata o noua instanta, pana cand vechea instanta nu va fi inchisa corect.
Rezolvarea acestei probleme se poate face in doua moduri:
interceptarea mesajului WM_CLOSE. In Class View, se face click dreapta pe clasa CNemodala pentru lansarea meniului contextual, se alege Add Windows Message Handler si se selecteaza WM_CLOSE, apoi Add and Edit. Se insereaza implementarea functiei OnClose():
extern CNemodala* g_pNemodala;
void CNemodala::OnClose()
Pointerul global este de tip extern. Acest identificator asigura ca este utilizat acelasi pointer care a fost declarat in modului CWiz13Dlg.cpp. Functia de tratare apeleaza apoi functia OnClose() a clasei de baza pentru a efectua setarile obisnuite, apoi se distruge caseta nemodala.
inlaturarea butonului cruciulita, prin deselectarea optiunii System Menu de la Properties->Styles a machetei casetei nemodale.
Politica de confidentialitate |
Copyright © 2024 - Toate drepturile rezervate