Dzisiaj omówimy sobie jedną z ważniejszych kontrolek WinApi, jaką jest kontrolka ListView. Służy ona do wyświetlania informacji z możliwością przyporządkowania im ikon. Żeby móc używać niektórych opcji tej kontrolki, musimy posiadać bibliotekę ComCtl32.dll przynajmniej w wersji 6 (WinXP i następne). Wszystkie niżej wymienione funkcje znajdują się w pliku nagłówkowym commctrl.h. Najpierw najbardziej podstawowa rzecz, czyli utworzenie kontrolki:
1 2 3 |
InitCommonControls(); listview=CreateWindow(WC_LISTVIEW,"",WS_VISIBLE|WS_CHILD,10,10,300,200,hwnd,0,GetModuleHandle(NULL),0) |
Żeby zapewnić załadowanie odpowiedniej biblioteki do obsługi tej kontrolki, musimy na samym początku wywołać funkcję InitCommonControls.
Kontrolka ListView posiada 4 różne typy widoków wyświetlanych informacji i odpowiadające im flagi:
small icon view – LVS_SMALLICON, przy każdej pozycji, po lewej, jest wyświetlana mała ikona
icon view – LVS_ICON, przy każdej pozycji, wyświetlana jest ikona normalnych rozmiarów
list view – LVS_LIST, wyświetlana jest lista informacji, ikona jest po lewej
reported view – LVS_REPORT, widok kolumnowy, każda informacja jest umieszczona w oddzielnej linii, informacje w pierwszej kolumnie mogą posiadać ikonę, która wyświetlana jest po lewej, każda kolumna posiada nagłówek, ale można to zmienić, uwzględniając styl LVS_NOCOLUMNHEADER przy tworzeniu okna.
Jeżeli chcemy stworzyć kontrolkę z widokiem np. reported view, to musimy dołączyć odpowiednią flagę:
1 |
listview=CreateWindow(WC_LISTVIEW,"",WS_VISIBLE|WS_CHILD|LVS_REPORT,10,10,300,200,hwnd,0,GetModuleHandle(NULL),0); |
Możemy również używać rozszerzonych stylów, takich jak:
LVS_EX_CHECKBOXES – przy każdej informacji zostaje umieszczone pole checkbox
LVS_EX_FULLROWSELECT – cały rząd zostaje podświetlony przy zaznaczaniu pola
LVS_EX_FLATSB – zmienia scrollbary w kontrolce na płaskie
Żeby ustawić rozszerzony style kontrolki, używamy funkcji ListView_SetExtendedListViewStyle(uchwyt,styl):
1 |
ListView_SetExtendedListViewStyle(listview,LVS_EX_CHECKBOXES) ; |
My weźmiemy na warsztat widok LVS_REPORT, ponieważ jest najbardziej przydatny. A więc, przydało by się dodać parę kolumn. Służy do tego funkcja ListView_InsertColumn:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void DodajKolumny(HWND h){ LVCOLUMN c; c.mask=LVCF_TEXT|LVCF_WIDTH; // flagi c.cx=100; // szerokość kolumny w pikselach c.pszText="Kolumna 1"; // nagłówek ListView_InsertColumn(h,0,&c); // dodajemy kolumnę c.pszText="Kolumna 2"; // nagłówek ListView_InsertColumn(h,1,&c); // dodajemy kolumnę c.pszText="Kolumna 3"; // nagłówek ListView_InsertColumn(h,2,&c); // dodajemy kolumnę } |
Teraz wystarczy tylko wywołać tą funkcję i mamy już gotowe kolumny:
1 |
DodajKolumny(listview) |
Kolumny mamy, więc dodamy teraz parę pozycji. Jak zwykle jest do tego gotowa funkcja ListView_InsertItem. Jako jeden z parametrów podajemy strukturę LVITEM. Przyjrzyjmy się jej trochę bliżej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
typedef struct _LVITEM { 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_IE >= 0x560) int iGroupId; UINT cColumns; // tile view columns PUINT puColumns; #endif } LVITEM, *LPLVITEM; |
Ta struktura służy również do pobierania informacji o danym polu, dlatego każde pole w zależności od kontekstu użycia musi być wypełnione (kiedy dodajemy pole) lub zostanie zwrócone (kiedy pobieramy informacje). A teraz po kolei parametry:
mask – flagi decydujące o tym, które pole muszą być wypełnione, lub które pola funkcja zwróci, może przyjmować, jedną lub więcej wartości:
LVIF_COLUMNS – pole cColums musi być wypełnione lub zostanie zwrócone
LVIF_GROUPID – pole iGroupId musi być wypełnione lub zostanie zwrócone
LVIF_IMAGE – pole iImage musi być wypełnione lub zostanie zwrócone
LVIF_INDENT – pole iIndent musi być wypełnione lub zostanie zwrócone
LVIF_PARAM – pole lParam musi być wypełnione lub zostanie zwrócone
LVIF_STATE – pole state musi być wypełnione lub zostanie zwrócone
LVIF_TEXT – pole pszText musi być wypełnione lub zostanie zwrócone
iItem – numer pola na którym operujemy
iSubItem – numer subitema na którym operujemy, jeżeli ustawimy wartość 0, to pole odnosi się do pozycji (informacji w pierwszej kolumnie)
state – wskazuje na stan pola
stateMask – informuje, o tym, które bity pola state mają być brane pod uwagę przy ustawianiu stanu pola
pszText – bufor z tekstem pola
cchTextMax rozmiar bufora
iImage – numer rysunku przypisanego do danego pola
lParam – wartość specyficzna dla każdego pola, używana przy sortowaniu pól
iIndent – pole to określa na ile szerokości obrazka przypisanego do danego pola wcięte będzie pole, standardowo 1
Dodajmy więc kilka pól do kontrolki. Stworzymy jedną funkcję, która będzie dodawała nowe pozycje, a potem wywołamy ją 5 razy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
void DodajPozycje(HWND h,char*itemtext,char*subitem1,char*subitem2,int i) { LVITEM p; ZeroMemory(&p,sizeof(p)); // czyścimy zmienną p.mask=LVIF_TEXT|LVIF_PARAM; // tekst i informacje potrzebne do sortowania p.lParam=(LPARAM)itemtext; p.iSubItem=0; p.iItem=i; // numer pola p.pszText=itemtext; // tekst pola ListView_InsertItem(h,&p); p.mask=LVIF_TEXT; // tekst p.iItem=i; p.iSubItem=1; // numer subitema p.pszText=subitem1; ListView_SetItem(h,&p); // ustawiamy pole z jednym subitemem p.iItem=i; p.iSubItem=2; p.pszText=subitem2; ListView_SetItem(h,&p); // ustawiamy pole z drugim subitemem } DodajPozycje(listview,"pozycja1","pozycja1 subitem1","pozycja1 subitem2",0); DodajPozycje(listview,"pozycja2","pozycja2 subitem1","pozycja2 subitem2",1); DodajPozycje(listview,"pozycja3","pozycja3 subitem1","pozycja3 subitem2",2); DodajPozycje(listview,"pozycja4","pozycja4 subitem1","pozycja4 subitem2",3); DodajPozycje(listview,"pozycja5","pozycja5 subitem1","pozycja5 subitem2",4); |
Poznamy teraz możliwości jakie stwarza dla nas WinApi w zakresie modyfikowania wyglądu kontrolki ListView. Aby zmienić kolor tekstu użyjemy funkcji SendMessage, podając jako komunikat LVM_SETTEXTCOLOR:
1 |
SendMessage(listview,LVM_SETTEXTCOLOR,0,(LPARAM)(COLORREF)0xFFFFFF); // biały kolor |
Do zmiany koloru tła tekstu i tła kontrolki służą odpowiednio komunikaty LVM_SETTEXTBKCOLOR i LVM_SETBKCOLOR:
1 2 3 |
SendMessage(listview,LVM_SETBKCOLOR,0,(LPARAM)(COLORREF)0x000000); SendMessage(listview,LVM_SETTEXTBKCOLOR,0,(LPARAM)(COLORREF)CLR_NONE); |
Jeżeli chcemy, aby kolor był całkowicie przeźroczysty, podstawiamy pod kolor wartość CLR_NONE. Przydatną rzeczą może również okazać się możliwość podstawienia obrazka jako tło kontrolki:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
CoInitialize(NULL); LVBKIMAGE h; h.ulFlags=LVBKIF_SOURCE_URL|LVBKIF_STYLE_TILE; h.pszImage="d:\\rys.bmp"; h.xOffsetPercent=0; h.yOffsetPercent=0; SendMessage(listview,LVM_SETBKIMAGE,0,(LPARAM)&h); case WM_CLOSE: CoUninitialize(); SendMessage(hwnd,WM_DESTROY,0,0); break; |
Powyższy kod ustawia jako tło kontrolki plik d:\rys.bmp i rysuje go kafelkowo. Jeżeli chcemy, aby obrazek został tylko raz narysowany zamiast flagi LVBKIF_STYLE_TILE podstawiamy flagę LVBKIF_STYLE_NORMAL. Ponieważ kontrolka ListView używa techniki OLE przy obsłudze tła, nasza aplikacja musi wywołać na początku funkcję CoInitialize, a na końcu funkcję CoUninitialize.
Omówimy na koniec jeszcze jedną ważną rzecz, a mianowicie sortowanie pól. Aby posortować pola musimy najpierw zdefiniować własną funkcję porównującą dwa elementy:
1 2 3 4 5 6 7 8 9 |
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { return -strcmp((char*)lParam1,(char*)lParam2); } |
Funkcja strcmp porównuje dwa napisy z uwzględnieniem wielkości znaków i zwraca 0 gdy są napisy równe, 1 gdy napis pierwszy jest większy od drugiego lub -1 w przypadku gdy drugi napis jest większy od drugiego. My chcemy posortować pola w kierunku malejącym, więc musimy zmienić znak wyniku funkcji strcmp. Parametry lParam1 i lParam2 są to zdefiniowane przez nas informacje, które ustawiliśmy w polu lParam podczas dodawania nowych pozycji. Parametr lParamSort zawiera dodatkowe informacje przekazywane podczas wywoływania funkcji SendMessage:
1 |
SendMessage(listview,LVM_SORTITEMS,(WPARAM)0/*dodatkowe informacje*/,(LPARAM)CompareFunc); |
Jeżeli jest coś, co jeszcze chcecie wiedzieć, a ja o tym nie wspomniałem, piszcie na maila.
Autor: krajew4