Virtual list

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.
Post Reply
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Virtual list

Post by mesajflaviu » 13 Mar 2012, 22:18

Am luat un control derivat din CListView si am incercat sa il folosesc in mod virtual, ca in exemplul atasat.
Am declarat o structura si apoi un tablou de structuri :

Code: Select all

	typedef struct tagListItem
	{
		CString sItemData;
		CString sInt;
		CString sString;
		CString sList;
		CString sDateTime;
		CString sRandom;
	}ListItem;

	CArray<ListItem,ListItem> m_arrListItem;
si apoi

Code: Select all

void CTestList6View::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	LV_ITEM* pItem= &(pDispInfo)->item;

	CTestList6Doc* pDoc = (CTestList6Doc*)GetDocument();

	if(pItem->mask & LVIF_TEXT)
	{
		switch(pItem->iSubItem)
		{
		case 0:
			lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sInt);
			break;
		case 1:
			lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sString);
			break;
		case 2:
			lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sList);
			break;
		case 3:
			lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sDateTime);
			break;
		case 4:
			lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sRandom);
			break;
		}
	}

	if(pItem->mask & LVIF_IMAGE)
	{
		if(0 == pItem->iItem % 2)pItem->iImage = 2;
		else if(0 == pItem->iItem % 3)pItem->iImage = 1;
		else pItem->iImage = 0;
	}

//	if(10 == pItem->iItem)SetRowColors(pItem->iItem,RGB(230,255,230),-1);

	*pResult = 0;
}
... si merge OK, se poate incerca asta ...

Insa raman unele caracteristici pe care in modul de lucru virtual nu stiu cum se rezolva :
  • Cum se poate folosi SetItemData/GetItemData ? Pentru asta am pastrat o variabila speciala in structura listei (sItemData), dar apoi nu stiu cum se acceseaza !?
  • Sortarea listei nu mai merge clasic ... o solutie ar fi ordonarea structurii si apoi reincarcarea listei ?
  • Nu stiu cum as putea seta iconitza la o coloana diferita de coloana 0 ... acest lucru l-am facut in mod normal cu

    Code: Select all

    ListCtrl.SetItem(3,4,LVIF_TEXT | LVIF_IMAGE,_T("ListItemText"),2,0,0,0);
    dar aceasta solutie nu mai merge in modul virtual ...
  • Acest control listview are diferite imbunatatiri (de exemplu capacitatea de a colora linii, coloane sau celule, in mod normal acest lucru facandu-se cu

    Code: Select all

    SetCellColors(7,3,RGB(80,255,55),-1);
    ) ... toate acestea nu se mai pot folosi acum ...
Modul de lucru virtual este util cand numarul de inregistrari din lista este mare, dar acest mod de lucru este un compromis ?
Attachments
TestList6.4.zip
(168.56 KiB) Downloaded 256 times



User avatar
bu7ch3r
Membru++
Membru++
Posts: 326
Joined: 17 May 2011, 15:17
Judet: Iaşi
Location: Sofia
Contact:

Re: Virtual list

Post by bu7ch3r » 13 Mar 2012, 22:42

Nu, nu e nimic compromis.
Lista nevirtuala are model-ul intern.
Lista virtuala are model-ul custom, in cazul tau pe m_arrListItem.
De ce ai vrea sa faci tu GetItemData si SetItemData in lista cand tu ai datele in m_arrListItem:)
Adik daca lista e virtuala, structura de date nu e in lista, e la tine,... in buzunar :) Poti sa faci o manevra si sa bagi array-ul ala in clasa derivata din CListView si sa suprascrii(override) GetItemData si SetItemData si ai rezolvat balciul :)
Si sortarea normal ca nu merge, trebuie s-o faci tu in array-ul ala. Defapt iti merge sortarea dar numai pe alea 5-10-30 de elemente care se vad pe ecran. N-o intereseaza pe lista restul elementelor care nu se vad:)

Toate chestiile pe care le faceai inainte cu set-erii originali trebuie sa le faci manual sau sa-i modifici sa-ti populeze structura.
De exemplu poti sa mai adaugi "int iImage" si " "UINT mask" si in OnDispInfo sa decizi care si cum se deseneaza:)

Codul pus de tine pune o imagine la randurile pare si alta la cele impare, dar poti sa pui ce vrei tu din ImageList-ul atasat listei....

pItem ala tre sa-l populezi in onDisplayInfo nu inves.
Structura ta ar trebui sa aibe campurile care-ti trebuie tie din asta:(nu toate, doar alea care-ti trebuie si care nu sunt deja populate(iItem si iSubItem etc :) plus ce mai ai tu nevoie ca sa faci giubuslucuri :) )

Code: Select all

typedef struct {
  UINT   mask;
  int    iItem;
  int    iSubItem;
  UINT   state;
  UINT   stateMask;
  LPTSTR pszText;
  int    cchTextMax;
  int    iImage;
  LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
  int    iIndent;
#endif 
#if (_WIN32_WINNT >= 0x0501)
  int    iGroupId;
  UINT   cColumns;
  UINT   puColumns;
#endif 
#if (_WIN32_WINNT >= 0x0600)
  int    piColFmt;
  int    iGroup;
#endif 
} LVITEM, *LPLVITEM;

L.E.
Mai poti s-o pacalesti si cu Set/GetItemState :D
Cu stima,
Lupu Claudiu

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Virtual list

Post by Ovidiu Cucu » 14 Mar 2012, 10:01

O lista virtuala e ca un nene cu alzheimer care stie ca are vreo suta de stranepoti dar poti sa-l tai ca nu-si mai aminteste cum il cheama pe vreunul. :)

Cu scuzele de rigoare daca repet ceva deja spus, reiau punctele din OP.
  1. SetItemData/GetItemData: uita-le! (la fel si pe suratele ei din seria SetItem/GetItem); toate datele despre items le tii in afara controlului.
  2. SortItems: idem, n-ai nimic in control deci n-ai ce sorta in el; ai in schimb la dispozitie std::sort sau qsort si poti sorta datele din afara.
  3. Iconite pe subitems: no problem, pui stilul LVS_EX_SUBITEMIMAGES si o sa te intrebe el pe LVN_GETDISPINFO.
  4. Celule colorate: iarasi no problem; virtuala sau nu, o faci si custom-draw.
[ Later edit ]
Completare la #2
Daca lista ta contine informatii dintr-o baza de date atunci poti face sortarea direct din query, cu ORDER BY. Este in general mai eficient, te scuteste de "carcalete CRT/STL+MFC" si nu in cele din urma, iti permite sa optimizezi la maximum performanta listei, cu cache si tot dichisul (vezi postul urmator).

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Virtual list

Post by Ovidiu Cucu » 14 Mar 2012, 11:35

Ar mai fi cate ceva de spus la liste virtuale.
Cum cred ca se stie deja, facem o lista virtuala daca avem de-a face cu un numar foarte mare de date.
Sa zicem ca am facut un query bengos din care rezulta zeci si sute de mii de inregistrari.
Daca ne apucam sa dam InsertItem + SetItem pentru fiecare record fetch-uit, pana-i gata putem pleca (dupa caz) la o tigara, cafea, bere, in weekend, concediu, somaj tehnic... :).
De aceea s-a inventat lista virtuala. In principiu, asta nu mai contine intern nimic despre item-uri (sau ma rog, apropape nimic... vezi "jongleriile" de care spunea Claudiu mai devreme ;)). In schimb intreaba de fiecare data cand vrea/trebuie sa afiseze unul, trimitand acel LVN_GETDISPINFO.
Buuun, am imbunatatit considerabil performanta si in marea majoritate a cazurilor e suficient (scuze, am folosit un pleonasm folosit de marea majoritate pe la TV :)).

Sa zicem insa ca avem de-a faca cu un numar foarte-foarte mare de inregistrari, fetch-rile merg ca darku iar in final tot dureaza mult. Sau s-ar putea sa nu vrem sa bagam dintr-o data amar de mizerii in memorie din care folosesc la un momentdat numai o mica parte.
No problem, lista virtuala mai pune o intrebare, de genul: "Mah, acusi iti trimit LVN_GETDISPINFO! Ai tot ce-mi trebuie?". Mai precis, inainte de LVN_GETDISPINFO trimite un LVN_ODCACHEHINT cu care ne poate scuti sa incarcam de la inceput si cap-coada lista externa de date.

Mai bine de-atat, nici ca se poate! :)

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Virtual list

Post by mesajflaviu » 19 Mar 2012, 22:15

Am facut lista virtuala si merge foarte bine, cu tot cu sortare, custom draw si icon-uri pe diferite coloane. Multumesc tuturor pentru indrumari. Am intampinat totusi o problema : desi demo-ul merge fara probleme la 10 000 de randuri, in aplicatia reala, cand incerc sa sortez o coloana a listei ce contine text mai lung, aplicatia se opreste cu urmatorul mesaj :

Code: Select all

STATUS_STACK_BUFFER_OVERRUN encountered
mentionez ca sortarea datelor o fac cu qsort (am mai lucrat cu aceasta functie) dar nu cred ca asta sa fie problema ... am cautat si pe net, dar n-am gasit o situatie asemanatoare ...

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Virtual list

Post by mesajflaviu » 19 Mar 2012, 22:25

Se pare ca nu fac ceva bine ... comportarea asta o are lista dupa mai multe sortari pe alte coloane si apoi scroll ... mai sap ...

User avatar
bu7ch3r
Membru++
Membru++
Posts: 326
Joined: 17 May 2011, 15:17
Judet: Iaşi
Location: Sofia
Contact:

Re: Virtual list

Post by bu7ch3r » 20 Mar 2012, 01:03

Pai asta e exceptie de la vista in sus :) Probabil copiezi un string in alt string si folosesti functii safe si nu dai size-ul stringurilor corect...
de exemplu _stprintf_s arunca asemenea exceptie...

Am mai vazut castatori care faceau strlen pe wchar in loc de wcslen :-??
Cu stima,
Lupu Claudiu

User avatar
bu7ch3r
Membru++
Membru++
Posts: 326
Joined: 17 May 2011, 15:17
Judet: Iaşi
Location: Sofia
Contact:

Re: Virtual list

Post by bu7ch3r » 20 Mar 2012, 10:35

Ce structura de date folosesti pentru stringuri/elemente/date? La qsort trebuie sa-i dai size-ul fix al elementului, functia nu iti permite sa sortezi elemente de size diferit...si de aici poate sa fie o problema.
Incearca cu sort/stable_sort; callback-ul trebuie sa-l modifici sa intoarca bool nu int ca la qsort...

Dupa ce sortezi poti sa-i zici la lista ca ai terminat de sortat: Invalidate(), RedrawWindow(). Nu stiu sigur daca astea doua fac ca lista sa ceara din nou incarcarea din model: UpdateData(TRUE)?
Cu stima,
Lupu Claudiu

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Virtual list

Post by mesajflaviu » 20 Mar 2012, 12:17

Folosesc un array de structuri de date in care am CString-uri, iar qsort-ul il folosesc asa :

Code: Select all

	typedef struct tagListItem
	{
		CString sItemData;
		CString sInteger;
		CString sString;
		CString sList;
		CString sDateTime;
		CString sRandom;
	}ListItem;

	CArray<ListItem,ListItem> m_arrListItem;

qsort((void*)m_arrListItem.GetData(),m_arrListItem.GetSize(),sizeof(ListItem),CompareStringAscending);
chestia e ca demo-ul merge fara probleme .... in cazul real mai am un element in structura de tip int (sa inlocuiesc setitemdata/getitemdata) ....

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Virtual list

Post by Ovidiu Cucu » 20 Mar 2012, 15:42

Sa zicem ca ai facut ceva de genul:

Code: Select all

void CRecordsListView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
   LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
   LV_ITEM* pItem = &(pDispInfo)->item;
   // ...
   lstrcpy(pItem->pszText, strText);
}
Daca strText e "de bun simt", no problem. Daca insa e lung ca o zi de post, putem zice mersi (lui Vista sau unui debugger mai destept) daca programul zice ceva cand crapa.
De ce oare?
Daca ne uitam cu un pic mai multa atentie in docomentatia lui LVITEM, putem observa scris negru pe alb:
pszText
...Note that although the list-view control allows any length string to be stored as item text, only the first 260 TCHARs are displayed.

Noi nu am alocat nimic, deci inseamna ca pe LVN_GETDISPINFO a venit un buffer gata alocat, cel mai probabil cat sa incapa max. 260 de caractere.
Ca sa nu umblam cu constante magice batute-n cod, tot in documentatie putem observa ca LVITEM are un membru numit cchTextMax
cchTextMax

Number of TCHARs in the buffer pointed to by pszText, including the terminating NULL.

This member is only used when the structure receives item attributes. It is ignored when the structure specifies item attributes. For example, cchTextMax is ignored during LVM_SETITEM and LVM_INSERTITEM. It is read-only during LVN_GETDISPINFO and other LVN_ notifications.

Note: Never copy more than cchTextMax TCHARs where cchTextMax includes the terminating NULL into pszText during an LVN_ notification, otherwise your program can fail.
(sublinierile imi apartin)

Bingo!
Folosim deci o functie care sa limiteze numarul de caractere copiate ca sa nu dea bufferul pe dinafara...

Code: Select all

   lstrcpyn(pItem->pszText, strText, pItem->cchTextMax);
...si-am scapat de crapaciuni si dureri de cap.

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Virtual list

Post by mesajflaviu » 20 Mar 2012, 16:46

Asta era baiu ... acum merge brici ... mii de multumiri !!!

Eu incercasem cu

Code: Select all

lstrcpy(pItem->pszText, pDoc->m_arrListItem.GetAt(pItem->iItem).sCompany.Left(100));
dar fara succes ...

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Virtual list

Post by Ovidiu Cucu » 21 Mar 2012, 11:17

E destul de ciudat ca nu ti-a mers fenta cu Left(100)...
Oricum, ca sa nu stam acum si sa dam cu presupusul e bine ca, atunci cand faci sortarea sa stergi mai intai lista.
In plus, ca sa nu enervezi userul si/sau sa-l impiedici sa faca prostii in timpul sortarii pui wait cursor si faci lock la desenarea listei.

Exemplu

Code: Select all

void CVirtualListView::OnSort() 
{
   CListCtrl& list = GetListCtrl();
   // a lengthy operation is expected; let's show a wait cursor!
   CWaitCursor waitCursor;
   // prevent drawing list window during the lengthy operation
   list.LockWindowUpdate(); 
   // clear list
   list.DeleteAllItems();   

   // sort external data
   // qsort(<...blah-blah...>);

   // set list items count
   list.SetItemCountEx(m_arrItems.GetSize());
   // unlock list window update
   list.UnlockWindowUpdate();	
}
In rest, pe LVN_GETDISPINFO ramane ca in postul precedent adica:

Code: Select all

void CVirtualListView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
   LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
   LV_ITEM* pItem = &(pDispInfo)->item;
   // ...
   // copy maximum pItem->cchTextMax characters into pItem->pszText buffer
   lstrcpyn(pItem->pszText, strText, pItem->cchTextMax);
}

Post Reply