Sunteți pe pagina 1din 14

Programare orientată obiect

Lucrare laborator 5.

Obiective:
În această lucrare de laborator ne propunem să continuăm familiarizarea cu aplicații de
tip machetă de dialog și să cuprindem continuarea familiarizării lucrului cu controalele de
editare:
 Adăugarea mai multor tipuri de controale și popularea acestora:
o Adăugarea controlului caseta combinată
o Ataşarea de variabile casetei combinate
o Adăugarea controlului arbore
o Ataşarea unei variabile controlului arbore
o Adăugarea controlului casetă cu listă cu variabila aferentă
o Adăugarea controlului listă cu variabila aferentă
o Popularea controalelor
 Testarea și construirea aplicației

1. Casete combinate

Un control tip casetă combinată (combo-box) este de fapt o combinaţie de controale: o


casetă de editare, o casetă listă şi un buton. O casetă combinată permite utilizatorului să
introducă date fie tastând text (ca în cazul unui control de editare), fie selectând un articol din
mai multe opţiuni (gen casetă listă). Casetele combinate se folosesc pentru a afişa o listă de
opţiuni, permiţând selectarea uneia singure. Casetele combinate se disting între controalele de
tip listă prin aceea că elementul selectat este întotdeauna vizibil, fiind afişat în partea superioară
a controlului.
Există trei tipuri de casete combinate:
 Simplă - îmbină o casetă de editare şi o casetă cu listă. Lista este întotdeauna
vizibilă şi elementul selectat este afişat în caseta de editare;
 Derulantă - îmbină o casetă de editare cu un buton şi o casetă cu listă. Caseta cu
listă este vizibilă numai la efectuarea unui clic pe buton;
 Listă derulantă - îmbină o etichetă statică cu un buton şi o casetă cu listă. Acest tip
este similar cu caseta derulantă, cu deosebirea că utilizatorul nu poate introduce date
în cadrul controlului.
Casetele combinate se dovedesc foarte utile atunci când utilizatorul nu este limitat numai
la selectarea articolelor prezentate într-o casetă listă. Porţiunea de casetă listă a casetei combinate
poate fi folosită pentru afişarea ultimelor selecţii efectuate, lăsând utilizatorului libertatea de a
introduce o nouă selecţie în controlul de editare.
Casetele combinate se folosesc şi atunci când există o criză de spaţiu într-o casetă de
dialog. Un mare număr de opţiuni din caseta combinată poate fi ascuns până la deschiderea
casetei, permiţând plasarea mai multor controale într-o zonă mai mică decât cea necesară unei
casete listă.

1.1. Adăugarea unei casete combinată la o machetă dialog


Se consideră un exemplu în care se va utiliza o casetă combinată pentru a permite
selectarea unui director principal, care va fi apoi inspectat pentru a înscrie în celelalte controale
din caseta de dialog informaţii privind subdirectoarele şi fişierele conţinute. Se va crea un proiect
Liste, de tip dialog. Adăugarea unei casete combinate în caseta de dialog creată va presupune
următorii paşi:

1
Programare orientată obiect

 Se deschide caseta de dialog IDD_LISTE_DIALOG în cadrul editorului de resurse


şi se înlătură controlul etichetă TODO;
 Se înlătură butonul Cancel, şi se modifică poziţia lui OK, plasându-l în colţul din
dreapta jos al casetei de dialog;
 Se va selecta pictograma etichetei statice de pe bara de instrumente Controls, şi se va
adăuga un control etichetă în partea superioară stângă a casetei de dialog;
 Se introduce Director Principal pe post de etichetă;
 Se selectează pictograma casetei combinate de pe bara cu instrumente Controls, după
care se adaugă un astfel de control în partea dreaptă a etichetei;
 Se extinde lungimea casetei combinate până în
marginea din dreapta a casetei de dialog;
 Se introduce IDC_DIRECTOR în caseta
combinată ID, din pagina proprietăţilor acesteia.
 Se selectează apoi din caseta de proprietăţi caseta
combinată Type şi se alege Drop list.
La adăugarea unei casete combinate într-o casetă de
dialog, opţiunea Sort este selectată implicit (
). Aceasta înseamnă că elementele adăugate în caseta combinată vor fi
afişate automat în ordine alfabetică. Pentru a inhiba acest comportament, se selectează opţiunea
Sort și se alege False. O altă necesitate uzuală în ceea ce priveşte casetele combinate este
modificarea dimensiunii casetei cu listă derulantă. Aceasta se realizează
efectuând un clic pe săgeata din partea dreaptă a controlului. Astfel va fi
afişat un cadru de formatare care indică dimensiunea casetei cu listă, acesta, putând fi
redimensionat prin intermediul butoanelor sale de dimensionare.

1.2. Ataşarea de variabile casetei combinate


Odată adăugat controlul casetă combinată în cadrul casetei de dialog, se apelează
ClassWizard pentru a ataşa o variabilă trebuie să se parcurgă paşii următori (se va ataşa o
variabile de tip CComboBox la un control casetă combinată prin intermediul
ClassWizard):
 Se apelează ClassWizard;
 Se selectează pagina Member Variables;
 Se selectează CListeDlg din caseta combinată ClassName;
 Se selectează IDC_DIRECTOR din caseta cu listă Control Ids;
 Se dă clic pe butonul Add Variable, şi va fi afişată caseta de dialog Add
Member Variable;
 Trebuie verificat, ca în caseta combinată Category să fie selectat Control şi că în
variable Type este selectat CComboBox;
 Se introduce m_cbDir în caseta
Member Variable Name şi
apoi se efectuează clic pe Finish;
 Se închide ClassWizard prin
selectarea lui OK.
Casetă combinată permite și adăugarea
de imagini.

2
Programare orientată obiect

2. Controale arbore

2.1. Adăugarea de controale arbore


Controlul arbore este unic prin aceea că este singurul control orientat anume înspre
afişarea de informaţii ierarhice. Un control arbore are o structură stânga dreapta. Un element din
extremitatea stângă a arborelui se numeşte nod rădăcină. Un element din extremitatea dreaptă
(care nu are elemente subordonate) se numeşte nod frunză, iar un element aflat între rădăcină şi
o frunză se numeşte nod ramificaţie. Afişarea de linii care să conecteze elementele poate fi
stabilită prin intermediul stilurilor. În mod implicit, controalele arbore permit selectarea unui
singur element la un moment dat; dacă se doreşte acordarea utilizatorului a posibilităţii de a
selecta simultan mai multe elemente dintr-un arbore, va trebui scris cod în acest sens.
În cadrul proiectului Liste va fi utilizat un control arbore pentru a afişa fişierele dintr-un
director în ordine alfabetică, se va crea un nod rădăcină pentru fiecare literă din alfabet şi apoi
se va insera elemente corespunzătoare fişierelor sub nodul adecvat.
Pentru adăugarea unui control arbore în proiectul Liste se vor parcurge următorii paşi,
după care se va ataşa la acest control o variabilă:
 În caseta de dialog IDD_LISTE_DIALOG, sub
eticheta casetei combinată atașată anterior, se adaugă
un control etichetă statică, numit Fisiere, pe post
de titlu pentru controlul arbore;
 se selectează pictograma controlului arbore de pe
bara cu instrumente Controls, după care se adaugă
un control arbore în partea stângă a casetei de dialog;
 se introduce IDC_FISIERE în caseta combinată ID;
 se selectează pagina de proprietăți şi se validează
opţiunile Has Buttons, Has Lines şi Lines at Root.

2.2. Ataşarea unei variabile controlului arbore


Pentru a ataşa o variabilă de tip CTreeCtrl la un control arbore prin intermediul
ClassWizard, se vor parcurge următorii paşi:
 se apelează ClassWizard;
 se selectează pagina Member Variables;
 se selectează CListeDlg din caseta combinată ClassName;
 se selectează IDC_FISIERE din caseta cu listă Control Ids;
 se execută clic pe butonul Add Variable, şi va fi afişată caseta de dialog Add
Member Variable;
 trebuie verificat ca în caseta combinată Category să fie selectat Control, şi că în
Variable Type este selectat CTreeCtrl;
 se introduce m_treeFișiere în caseta Member Variable Name şi apoi se
efectuează clic pe OK;
 se închide ClassWizard prin selectarea opţiunii OK.

3
Programare orientată obiect

3. Controale casetă cu listă


În cea mai elementară formă a sa, o casetă cu listă constă într-o simplă listă de elemente,
dar, spre deosebire de caseta combinată şi de controlul arbore, o casetă cu listă poate permite
atât selectarea elementelor individuale cât şi selecţii multiple.
Pentru a adăuga un control casetă cu listă în caseta de dialog Liste, se vor parcurge
următoarele etape:
 se adaugă un control de etichetă statică numit SubDirectoare, cu rolul de titlu
pentru controlul casetă cu listă;
 se va selecta pictograma casetei cu listă şi se va adăuga pe suprafaţa casetei de dialog;
 se introduce IDC_SUBDIRECTOARE în caseta combinată ID;
 se accesează pagina Properties şi se selectează
Extended din caseta combinată Selection;
 apoi se deselectează opţiunea No Integral Height.

Odată adăugată caseta cu listă, macheta de dialog Liste va arăta astfel:

La caseta cu listă se va adăuga o variabilă m_lbSubdir de tip CListBox prin intermediul lui
ClassWizard.

4. Controlul listă

Controlul listă are o denumire uşor derutantă, pentru că celelalte trei controale prezentate
în continuare, sunt de asemenea de tip listă. Totuşi, controlul listă este cel mai complex dintre
cele patru şi este utilizat mai degrabă în cadrul unei reprezentări decât în casetele de dialog. Un
control listă este în stare să afişeze atât pictograme, cât şi etichete asociate, şi poate opera în
oricare din cele patru moduri descrise mai jos:
 Pictograme - afişează pictograme mari (32x32 pixeli) cu etichete plasate sub fiecare
pictogramă. Elementele sunt plasate în ordine de la stânga la dreapta şi apoi de sus în
jos;
 Pictograme mici - afişează pictograme mici (16x16 pixeli) cu etichete plasate în
dreapta fiecărei pictograme. Elementele sunt plasate în ordine de la stânga la dreapta
şi apoi de sus în jos;

4
Programare orientată obiect

 Listă - afişarea este similară cu modul cu pictograme mici, dar elementele sunt
plasate în ordine de sus în jos şi apoi de la stânga la dreapta;
 Raport - afişează informaţii prin intermediul unor coloane cu antet.
În mod implicit , atunci când un control listă pierde focusul, elementul selectat nu mai
este afişat evidenţiat. Dacă se doreşte ca selecţia să rămână vizibilă, se va valida stilul Show
Selection Always.
În cadrul proiectului Liste, controlul listă va fi utilizat pentru a afişa detalii privind
directoarele selectate în caseta cu listă aflată deasupra sa în caseta de dialog. Controlul listă va
avea trei coloane: prima coloană va conţine numele directorului, a doua va afişa numărul de
fişiere conţinute, iar a treia coloană va înfăţişa dimensiunea în megaocteţi a directorului. Pentru
adăugarea controlului listă se vor parcurge următorii paşi:
 se va adăuga un control etichetă statică, numită Detalii Director Selectat, ca
titlu pentru controlul listă;
 se va selecta pictograma controlului listă (List Control) după care se va adăuga în
caseta de dialog; se va introduce IDC _DIR_SELECTAT
în caseta combinată ID;
 se selectează din pagina Properties opțiunea View şi se
alege Report.
În final, aspectul casetei de dialog, va arăta astfel:

Se va ataşa o variabilă de tip CListCtrl, m_lcDetaliiDir.


Class Wizard va arăta variabilele atașate fiecărui tip de control.

Deoarece controalele listă sunt orientate înspre înfăţişarea şi selecţia elementelor de


informaţie, prima cerinţă în programare este popularea acestor controale cu date. Fiecare
informaţie adăugată într-un control de tip listă reprezintă un element.

5. Popularea casetelor

5.1. Popularea casetelor combinate


O casetă combinată are un număr mare de proprietăţi, o mare parte din acestea sunt
identice cu cele ale casetelor listă şi a controalelor de editare, însă are şi proprietăţi specifice, de
exemplu Type, utilizat pentru specificarea tipului de casetă combinată, se poate alege între
Simple, Dropdown (derulant) şi DropList (listă derulantă). Opţiunea prestabilită este
Dropdown.

5
Programare orientată obiect

Caseta combinată este singurul dintre cele patru controale de tip listă care poate fi
populat cu elemente în cadrul editorului de resurse. Aceasta se realizează prin intermediul
paginii Data a casetei de dialog ComboBox Properties. Fiecare element poate fi introdus
în caseta Enter ListBox Items. Dacă se efectuează la un moment dat această operaţie, nu
trebuie uitat să se apese Ctrl+Enter după fiecare element, pentru că simpla apăsare a tastei
Enter va duce la închiderea casetei de dialog.
O astfel de populare a unei casete combinate nu este uzuală; în mod normal, casetele
combinate sunt populate la momentul execuţiei, de multe ori în cadrul funcţiei
OnInitDialog(). Infrastructura MFC apelează această funcţie la deschiderea casetei de dialog,
înainte de afişarea ei.
Clasa MFC care încapsulează casetele combinate este CComboBox. Această clasă
conţine mai multe funcţii care se ocupă de adăugarea şi înlăturarea elementelor, acestea sunt
reprezentate astfel:
 AddString - adaugă un element fie la sfârşitul listei, fie în poziţia corespunzătoare
ordonării;
 DeleteString - înlătură un element;
 InsertString - inserează un element într-o anumită poziţie;
 ResetContent - înlătură toate elementele existente;
 Dir - metodă specială de populare pentru inserarea ca elemente a numelor de fişiere.
Fiecărui element îi este asociat la adăugare un indice numeric pornind de la zero, acest
indice putând fi utilizat apoi pentru accesarea respectivului element.
În cadrul proiectului Liste, caseta combinată este populată în cadrul funcţiei
OnInitDialog() a casetei de dialog, fiind inserate o serie de directoare principale obţinute prin
intermediul unor funcţii globale din Windows. În acest
scop, se creează mai întâi o funcţie nouă având numele
PopulateCombo () şi tipul void. Pentru aceasta
deschidem meniul derulant al clasei ClisteDlg, alegem Add

Function.
Vom introduce în corpul acestei funcții codul de mai jos:
void CListeDlg::PopulateCombo()
{
TCHAR szBuffer[MAX_PATH];

// ** Aflam directorul Windows, de regula c:Windows,


// ** si il adaugam in caseta combinata
GetWindowsDirectory(szBuffer, MAX_PATH);
m_cbDir.AddString(szBuffer);

// ** Scurtam directorul si lasam doar litera de unitate C:


// ** pe care o adaugam in caseta combinata
szBuffer[2] = 0;
m_cbDir.AddString(szBuffer);

// ** Aflam directorul System,


// de regula, c:/Windows/System
// ** si-l adaugam in caseta combinata
GetSystemDirectory(szBuffer, MAX_PATH);
m_cbDir.AddString(szBuffer);

// ** Aflam directorul curent si-l adaugam in caseta combinata


GetCurrentDirectory(MAX_PATH, szBuffer);
m_cbDir.AddString(szBuffer);
}

6
Programare orientată obiect

 se adaugă directorul Windows în caseta combinată;


 se reduce şirul de caractere la litera de unitate şi îl adăugăm sub această formă în
caseta combinată;
 se adaugă directorul System în caseta combinată;
 se adaugă directorul curent în caseta combinată.
Apelurile de funcţii GetWindowsDirectory (), GetSystemDirectory () şi
GetCurrentDirectory() sunt apeluri de funcţii globale care înscriu în bufferul şir de
caractere szBuffer trimis ca parametru căile de directoare respective. Macrodefiniţia
MAX_PATH este folosită pentru a specifica lungimea maximă a unei căi; în majoritatea
cazurilor, această lungime este 260. După obţinerea fiecărei căi, szBuffer este transmis
funcţiei AddString () a variabilei membru m_cbDir, aceasta ocupându-se de inserarea
elementului în cadrul casetei combinate. Deoarece caseta combinată are stilul Sort, elementele
inserate vor fi dispuse automat în ordine alfabetică.
Apoi se va apela funcția Populate prin adăugarea numelui funcției în structura funcției
OnInitDialog()după comentariile //TODO:
// TODO: Add extra initialization here
// ** Initializam caseta combinata cu directoare principale
PopulateCombo();

Scopul unei casete combinate este să permită selectarea unuia dintre elementele pe care le
conţine. Atunci când aceasta se întâmplă natural, programul
trebuie să afle imediat şi să răspundă la modificarea selecţiei.
Acest lucru, este posibil prin interceptarea mesajului
CBN_SELCHANGE, trimis de către control casetei de
dialog oricând se modifică elementul selectat.
Este bine să se verifice valorile întoarse de funcţiile AddString () şi
InsertString(), deoarece în cazul apariţiei unei erori, aceste funcţii întorc una din valorile
CB_ERR sau CB_ERRSPACE.
Se va adăuga o funcţie de tratare a mesajului
CBN_SELCHANGE pentru caseta combinată
IDC_DIRECTOR. Astfel, ar trebui să fie creată funcţia
membru OnSelchangeDirector().
Este nevoie totodată de o nouă variabilă membru de tip
CString, m_strDir, care va păstra calea selectată; m_strDir
poate fi utilizată şi pentru popularea controlului arbore şi a casetei
cu listă. Pentru adăugarea variabilei membru vom deschide
meniul contextual al clasei CListeDlg, si selectăm Add Variable,
unde introducem datele:
Pentru funcţia membru OnSelchangeMainDir(), se va introduce următorul cod:
1. void CListeDlg::OnSelchangeDirector()
2. {
3. // TODO: Add your control notification handler code here

7
Programare orientată obiect

4. // ** Extragem indicele elementului selectat


5. int nIndex = m_cbDir.GetCurSel();
6.
7. // ** Verificam daca indicele este valid
8. if (nIndex != CB_ERR)
9. {
10. // ** Extragem textul din elementul selectat si-l plasam
11. // ** intr-o variabila membru, dupa care apelam functii
12. // ** care populeaza celelalte controale
13. m_cbDir.GetLBText(nIndex, m_strDir);
14. PopulateTree();
}
}
 Linia 1 este apelată la modificarea selecţiei în cadrul casetei combinate;
 5 - extrage indicele directorului selectat;
 13- extrage şi stochează numele directorului selectat;
 14- apelează o funcţie care populează controlul arbore.
La primirea mesajului, că elementul selectat s-a modificat, în linia 5 este apelată funcţia
GetCurSel () care întoarce în nIndex indicele noului element selectat. Dacă nu este selectat
nici un element, este întoarsă valoarea specială CB_ERR, eventualitate testată în linia 8. Dacă
nIndex este valid, este transmis în linia 13 funcţiei GetLBText () împreună cu variabila
membru m_strMainDir, aceasta din urmă fiind modificată. Se poate observa că linia 14,
conţine apelul unei noi funcţii, PopulateTree ().

5.2. Popularea unui arbore


La popularea unui arbore, de multe ori este necesară stocarea de informaţii privind
structura arborelui pentru a putea insera elemente ca părinţi sau ca subordonaţi ai altor elemente
introduse anterior.
Clasa MFC care încapsulează un arbore este CTreeCtrl, şi include funcţiile următoare:
 InsertItem - inserează un element, fie ca element rădăcină, fie ca element
subordonat, în funcţie de parametrii transmişi;
 DeleteItem - înlătură un element;
 DeleteAllItems - înlătură toate elementele existente.
Funcţia InsertItem () are mai multe versiuni supradefinite pentru a putea să se realizeze
asocieri de imagini şi de pointeri la date externe arborelui, dacă este nevoie. Fiecărui element
inserat îi este asociat un indicator de tip HTREEITEM care poate fi transmis funcţiei
InsertItem, făcând astfel posibilă crearea unei ierarhii.
Pentru a popula arborele din proiect, va trebui să se
creeze mai întâi o funcţie membru numită PopulateTree,
care întoarce tipul void.
Arborele va afişa fişierele aflate în directorul selectat în cadrul casetei combinate, iar
acestea vor fi împărţite alfabetic. Funcţia inserează mai întâi câte un element pentru fiecare literă
a alfabetului, după care adaugă un al 27-lea element care va subordona fişierele ale căror nume
nu încep cu o literă. Sunt folosite apoi, o serie de funcţii oferite de Windows pentru a inspecta
conţinutul directorului şi a insera numele fiecărui fişier în subordinea elementului corespunzător
din arbore. După crearea funcţiei PopulateTree () se va adăuga codul corespunzător.
void CListeDlg::PopulateTree()
{
//**Inlaturam toate elementele existente in arbore
m_treeFisiere.DeleteAllItems();
//** Alocam un vector de elemente HTREEITEMS
HTREEITEM hLetter [27];
//** Inseram ca elemente radacina literele de la 'A' la 'Z'
for (int nChar = 'A'; nChar <= 'Z'; nChar++)

8
Programare orientată obiect

hLetter[nChar - 'A'] = m_treeFisiere.InsertItem((TCHAR*)&nChar);

//** Inseram elementul 'Other' ca element radacina


hLetter[26] = m_treeFisiere.InsertItem(L"Other");

HANDLE hFind;
WIN32_FIND_DATA dataFind;
BOOL bMoreFiles = TRUE;
CString strFile;

//** Cautam primul fisier din directorul principal


hFind = FindFirstFile(m_strDir + "//*.*", &dataFind);

//**Repetam pana cand am gasit toate fisierele


while (hFind != INVALID_HANDLE_VALUE && bMoreFiles == TRUE)
{
//** Ne asiguram ca am gasit un fisier, nu un director
if (dataFind.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE)
{
//** Aflam prima litera din numele fisierului
int nChar = dataFind.cFileName[0];
//** Transformam literele minuscule in majuscule
if (islower(nChar))
nChar -= 32;
//**Daca numele fisierului incepe cu o litera atunci
//**scadem 'A' pentru a afla indicele in cadrul
//**vectorului hLetter, altfel folosim indicele 26
if (isalpha(nChar))
nChar -= 'A';
else
nChar = 26;
//** Inseram numele fisierului in arbore
m_treeFisiere.InsertItem(dataFind.cFileName, hLetter[nChar]);
}
//** cautam urmatorul fisier din directorul principal
bMoreFiles = FindNextFile(hFind, &dataFind);
}
//** Inchidem indicatorul de cautare
FindClose(hFind);
}

Apelul funcţiei DeleteAllItems () înlătură toate elementele existente; această


operaţie este necesară deoarece arborele este populat de fiecare dată când se modifică directorul
selectat în cadrul casetei combinate.
Bucla For apelează funcţia InsertItem(), transmiţând pe rând literele de la A la Z;
deoarece nu sunt transmişi alţi parametrii, literele sunt inserate automat în arbore ca elemente
rădăcină. La fiecare apel al funcţiei InsertItem (), indicatorul HTREEITEM întors este
stocat în vectorul hLetter. Astfel, hLetter [0] conţine indicatorul lui A, hLetter [1]
conţine indicatorul lui B etc. Elementul rădăcină “Other” este inserat pentru a subordona
fişierele ale căror nume nu încep cu o literă.
Pentru a obţine o listă a fişierelor este apelată mai întâi funcţia FindFirstFile ().
Parametrul m_strDir + “//*.*” transmis acestei funcţii va duce la căutarea tuturor fişierelor
aflate în directorul selectat. Căutarea următorului fişier se face prin apelul FindNextFile (),
care întoarce valoarea FALSE atunci când sunt epuizate toate fişierele, ceea ce va duce la
încheierea buclei. Ambele funcţii Find înscriu datele dorite în variabila dataFind, care este o
structură Win32_DATA_FIND, care conţine informaţii despre fiecare fişier găsit.
Este testat apoi, atributul de tip al fişierului găsit pentru a se asigura că este vorba despre
un fişier obişnuit, nu despre un director. Prima literă a numelui de fişier se află în linia

9
Programare orientată obiect

int nChar = dataFind.cFileName [0];

şi transformată eventual în majusculă nChar -= 32; . Testul din linia următoare verifică dacă
numele fişierului începe cu o literă, caz în care traduce valoarea întreagă a literei într-un indice în
vectorul hLetter. În apelul InsertItem () sunt transmise numele fişierului care trebuie
inserat şi indicatorul HTREEITEM al părintelui care-l va subordona.
Uneori este mai uşor să se asocieze fiecare element din arbore cu un pointer la un obiect.
Acest lucru se poate face prin intermediul funcţiilor SetItemData() şi GetItemData ().
Dacă se va asambla şi rula aplicaţia, caseta combinată ar trebui să afişeze mai multe
directoare, iar selectarea unuia dintre acestea ar trebui să conducă la popularea arborelui cu
fişierele conţinute.

5.3. Popularea unei casete cu listă


Popularea unei casete cu listă se realizează practic la fel ca popularea unei casete
combinate, deoarece elementele din lista unei casete combinate sunt de fapt elemente ale unei
casete cu listă. Diferenţa esenţială între cele două controale este modul de afişare şi faptul că în
casetele cu listă este posibilă selectarea mai multor elemente simultan.
Este bine să se verifice valorile întoarse de funcţiile AddString () şi InsertString
(), iar în cazul apariţiei unei erori, aceste funcţii întorc una din valorile CB_ERR sau
CB_ERRSPACE.
Clasa MFC care încapsulează casetele cu listă este CListBox, funcţiile care răspund de
inserarea şi înlăturarea elementelor sunt identice cu cele prezentate la clasa CComboBox.
În cadrul proiectului Liste, caseta cu listă este populată cu numele subdirectoarelor
aflate în directorul principal care a fost selectat în caseta combinată. În acest scop se va adăuga
mai întâi o nouă funcţie membru care se numeşte PopulateListBox () şi are tipul void.
Apoi se va modifica funcţia OnSelchangeDir() pentru a apela această nouă funcţie,
prin adăugarea liniei PopulateListBox(); după apelul PopulateTree ().
Pentru funcţia nou introdusă PopulateListBox se va adăuga următoarea secvenţă de cod:
void CListeDlg::PopulateListBox()
{
// ** Inlaturam toate elementele existente in caseta cu lista
m_lbSubdir.ResetContent();
HANDLE hFind;
WIN32_FIND_DATA dataFind;
BOOL bMoreFiles = TRUE;

//**Cautam primul fisier din directorul principal


hFind = FindFirstFile(m_strDir + "\\*.*", &dataFind);

//**Repetam pana cand am gasit toate fisierele


while (hFind != INVALID_HANDLE_VALUE && bMoreFiles == TRUE)
{
//**Verificanm daca am gasit un director

10
Programare orientată obiect

if (dataFind.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
//**Adaugam numele directorului in caseta cu lista,
//**ignorand directoarele "." si ".."
if (strcmp(dataFind.cFileName, "."))
if (strcmp(dataFind.cFileName, ".."))
m_lbSubdir.AddString(dataFind.cFileName);
}

//** Cautam urmatorul fisier din directorul principal


bMoreFiles = FindNextFile(hFind, &dataFind);
}

//**Inchidem indicatorul de cautare


FindClose(hFind);

Apelul ResetContent() înlătură toate elementele existente, această operaţie fiind


necesară deoarece arborele este populat de fiecare dată când se modifică directorul selectat în
cadrul casetei combinate.
Cele două apeluri ale funcţiei strcmp () au ca efect ignorarea directoarelor speciale “.”
şi “..”. În apelul AddString () este transmis numele fişierului (în acest caz numele
subdirectorului) care trebuie inserat în caseta cu listă. Dacă se asamblează şi rulează aplicaţia în
acest moment, ar trebui să se observe cum selectarea unui director în caseta combinată duce la
popularea atât a arborelui cât şi a casetei listă.
La o rulare a proiectului imaginea va arăta astfel:
Acţiunile efectuate de utilizator asupra controalelor
casetă cu listă au ca efect transmiterea unor mesaje
corespunzătoare către caseta de dialog. Mesajul
LBN_SELCHANGE este trimis în cazul în care elementul
sau (în cazul controalelor care permit selecţia multiplă)
elementele selectate se modifică. Modul în care se operează cu
o selecţie diferă după cum controlul permite sau nu selecţia
multiplă, iar clasa CListBox conţine funcţii adaptate fiecărui caz.
CStringList este un exemplu de clasă MFC ajutătoare, fiind folosită pentru
administrarea unei liste variabile de obiecte individuale CString.
Caseta cu listă din proiectul Liste permite selecţia multiplă, funcţia de tratare a
mesajului creează o listă a directoarelor selectate în cadrul unei noi variabile membru,
m_strLista, care este de tip CStringList.
După ataşarea acestei variabile, se va adăuga o funcţie de tratare a mesajului
LBN_SELCHANGE, respectiv OnSelchangeSubDirs.

Pentru noua funcţie se va introduce următoarea secţiune de cod:


void CListeDlg::OnSelchangeSubdirectoare()
{
// TODO: Add your control notification handler code here

11
Programare orientată obiect

//** Determinam numarul elementelor selectate


int nSelCount = m_lbSubdir.GetSelCount();

//**resetam lista de siruri


m_strLista.RemoveAll();
if (nSelCount)
{
CString str;
//** Cream un vector de intregi care va stoca indici
//** si-l initializam cu indicii elementelor selectate
LPINT pItems = new int[nSelCount];
m_lbSubdir.GetSelItems(nSelCount, pItems);

for (int i = 0; i < nSelCount; i++)


{
//** Extragem numele elementului selectat
//** si-l punem intr-o lista de siruri
m_lbSubdir.GetText(pItems[i], str);
m_strLista.AddTail(str);
}
//** Stergem vectorul de intregi
delete[] pItems;
}
//** Populam controlul lista
PopulateListControl();

Funcţia GetSelCount () întoarce numărul elementelor selectate şi este folosită doar


pentru controalele care permit selecţia multiplă. În linia următoare, toate şirurile stocate în
variabila m_strList sunt şterse prin intermediul funcţiei RemoveAll() a clasei
CStringList, lăsând variabila pregătită pentru regenerare.
În linia LPINT pItems = new int [nSelCount]; este alocat spaţiu pentru un
vector de întregi, care conţine indicii elementelor selectate şi, prin urmare, dimensiunea sa
trebuie să fie egală cu nSelCount. Variabila pItems este un pointer la tipul int şi este
iniţializată ca pointer la începutul vectorului. Spaţiul ocupat de vector este eliberat prin
intermediul lui Delete [ ], nemaifiind necesar.
Funcţia GetSelItems () extrage din cadrul controlului lista elementelor selectate.
Elementele acestei liste se consideră indexate de la zero. Cel de-al doilea parametru este adresa
vectorului de întregi în care funcţia va înscrie datele solicitate.
Bucla for parcurge elementele selectate, la fiecare iteraţie, fiind apelată GetText(),
fiindu-i transmise următorul indice din vector (pItems [i]) şi variabila str în m_strList.
Pentru că datele se vor schimba de fiecare dată când se modifică lista subdirectoarelor selectate,
elementele afişate în controlul listă vor fi actualizate prin apelul PopulateListControl() din

finalul funcţiei.

5.4. Popularea unui control listă


Se observă că funcţia OnSelchangeSubDirs() apelează o nouă funcţie,
PopulateListControl (), care va utiliza m_strLista pentru a introduce informaţii în
controlul listă.
Modalitatea de populare a unui control listă diferă puţin de metodele aplicate în cazul
celorlalte controale de tip listă, apărând diferenţe în funcţie de tipul de control listă care este
utilizat. Există patru tipuri (pictograme, pictograme mici, listă şi raport). Cel mai des întâlnit tip
este tipul raport, care se caracterizează prin afişarea informaţiilor pe coloane, de exemplu,

12
Programare orientată obiect

reprezentarea de tip raport din Explorer afişează coloanele titrate Name, Size, Type şi Modified,
informaţiile respective despre fişiere fiind afişate linie după linie.
Clasa MFC care încapsulează controalele listă este CListCtrl, care include funcţiile
care se ocupă de adăugarea şi înlăturarea elementelor:
 InsertColumn - adaugă o nouă coloană într-o anumită poziţie;
 DeleteColumn- înlătură o coloană;
 InsertItem- adaugă un nou element;
 DeleteItem- înlătură un element existent;
 DeleteAllItems- înlătură toate elementele existente;
 SetItemText- stabileşte conţinutul textual al unei părţi dintr-un element.
Proiectul Liste foloseşte un control listă de tip raport care conţine coloane pentru
numele directorului, numărul de fişiere aflate în director şi dimensiunea totală a acestor fişiere.
Primul lucru care trebuie făcut, este inserarea coloanelor, deoarece ele rămân
neschimbate, această operaţie se poate efectua în cadrul funcţiei OnInitDialog () unde se va
adăuga următoarea secvenţă (după iniţializarea casetei combinate care s-a făcut anterior):

// TODO: Add extra initialization here


// ** Initializam caseta combinata cu directoare principale
PopulateCombo();
//** Initializam coloanele controlului lista
m_lcDetaliiDir.InsertColumn(0, "Directory", LVCFMT_LEFT, 70);
m_lcDetaliiDir.InsertColumn(1, "Files", LVCFMT_RIGHT, 50);
m_lcDetaliiDir.InsertColumn(2, "Size KB", LVCFMT_RIGHT, 60);
return TRUE; // return TRUE unless you set the focus to a control

Primul parametru din apelul InsertColumn() reprezintă indicele numeric al coloanei,


începând de la zero. Se poate opta între modurile de aliniere a textului LVCFMT_LEFT,
LVCFMT_RIGHT, LVCFMT_CENTER, iar ultimul parametru specifică lăţimea iniţială a
coloanei, exprimată în pixeli. Lăţimea poate fi ajustată cu ajutorul mouse-ului. Elementele
propriu-zise vor fi adăugate în control de către funcţia PopulateListControl () specificând
void ca tip întors.
Pentru a efectua popularea completă a controlului se va adăuga la această funcţie
următorul cod:
void CListeDlg::PopulateListControl()
{
//** Inlaturam toate elementele existente in controlul lista
m_lcDetaliiDir.DeleteAllItems();

POSITION pos;
//** Parcurgem lista directioarelor selectate in cadrul casaetei cu lista
for (pos = m_strLista.GetHeadPosition(); pos != NULL; )
{
int nItem;
HANDLE hFind;
WIN32_FIND_DATA dataFind;
BOOL bMoreFiles = TRUE;
CString str;
CString strFind;

str = m_strLista.GetAt(pos);
//** Adaugam un rand in controlul lista (coloana 0)
nItem = m_lcDetaliiDir.InsertItem(0, str);

strFind = m_strDir + "\\" + str + "\\*.*";


hFind = FindFirstFile(strFind, &dataFind);

int nFileCount = 0;

13
Programare orientată obiect

double nFileSize = 0;

//**Cautam iterativ toate fisierele din director si


//** insumam numarul de fisiere si dimensiunile acestora
while (hFind != INVALID_HANDLE_VALUE && bMoreFiles == TRUE)
{
if (dataFind.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE)
{
nFileCount++;
nFileSize += (dataFind.nFileSizeHigh * MAXDWORD +
dataFind.nFileSizeLow);
}
bMoreFiles = FindNextFile(hFind, &dataFind);
}
//** Inchidem indicatorul de cautare
FindClose(hFind);

//** Foramatam un sir care contine numarul de


//** fisiere si-l inseram in coloana 1
str.Format("%ld", nFileCount);
m_lcDetaliiDir.SetItemText(nItem, 1, str);

//** Foramatam un sir care contine dimensiunea


//** totala a fisierelor si-l inseram in coloana 2
str.Format("%-1.2f", nFileSize / 1024.0);
m_lcDetaliiDir.SetItemText(nItem, 2, str);

m_strLista.GetNext(pos);
}

Apelul funcţiei DeleteAllItems () înlătură toate elementele existente în cadrul


controlului listă, dar nu elimină coloanele. Bucla for parcurge iterativ variabila m_strList
care a fost generată în interiorul funcţiei OnSelChangeSubdir (). Fiecare obiect CString
din această listă reprezintă câte un director selectat din caseta cu listă. În apelul InsertItem ()
este transmis numele directorului, iar acesta va reprezenta conţinutul coloanei 0. Funcţia
InsertItem() întoarce indicele elementului nou adăugat, considerând zero ca bază de
indexare, acest indice fiind reţinut în nItem. Indicele este transmis apoi, în apelurile
SetItemText (), efectul fiind stabilirea conţinutului pentru celelalte două coloane.
La asamblarea şi rularea programului, se va testa selecţia multiplă în cadrul casetei cu
listă.

14

S-ar putea să vă placă și