List-View to jedna z przydatniejszych kontrolek, pozwalająca wyświetlać informacje w postaci tabeli. Lubi też sprawiać problemy (jak niemal wszystko w WinAPI), więc warto przyjrzeć się jej bliżej.
ListView
Zaczynamy oczywiście od utworzenia kontrolki. Na początek wołamy InitCommonControlsEx, jako że List-View należy do grupy ''Common Controls''. Potem tradycyjnie, bez większych niespodzianek:
RECT rcl;
GetClientRect( hwnd, & rcl );
hListView = CreateWindowEx( 0, WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT |
LVS_EDITLABELS, 0, 0, rcl.right - rcl.left, rcl.bottom - rcl.top,
hwnd,( HMENU ) 1000, hInstance, NULL );
Kontrolka List-View oferuje cztery możliwe widoki: "duże ikony", "małe ikony", "lista" i "szczegóły" (ang. ''report''). Flaga
LVS_REPORT powoduje wyświetlenie kontrolki w widoku "szczegóły", notabene chyba najczęściej używanym (pewnie właśnie dlatego nie jest domyślny, jak to w WinAPI).
LVS_EDITLABELS powoduje, że mamy możliwość edycji etykietek w czasie wykonania programu – może się przydać np. do zmiany nazwy plików, które wyświetlamy w kontrolce.
Dodajemy zawartość
Pusta kontrolka nie wygląda z pewnością zbyt interesująco, więc dodajmy do niej parę kolumn. Są one widoczne tylko w widoku "szczegóły", jako nagłówki tabeli:
LVCOLUMN lvc;
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.iSubItem = 0;
lvc.cx = 200;
lvc.pszText = "Nazwa pliku";
ListView_InsertColumn( hListView, 0, & lvc );
lvc.iSubItem = 1;
lvc.cx = 50;
lvc.pszText = "Rozmiar";
ListView_InsertColumn( hListView, 1, & lvc );
lvc.iSubItem = 2;
lvc.cx = 100;
lvc.pszText = "Data";
ListView_InsertColumn( hListView, 2, & lvc );
Podobnie jak w przypadku wielu innych rzeczy w WinAPI, dodawanie kolumn polega na wypełnieniu odpowiedniej struktury (
LVCOLUMN). Pole
mask określa, które z pozostałych pól zawierają prawidłowe wartości. Nas na razie interesuje przede wszystkim ustawienie tekstu (
LVCF_TEXT), a także szerokości kolumny (
LVCF_WIDTH) oraz jej numeru (
LVCF_SUBITEM). Następnie pozostaje nam już tylko wykorzystanie makra
ListView_InsertColumn, albo bezpośrednie wysłanie komunikatu
LVM_INSERTCOLUMN, podając wskaźnik do struktury.
Skoro mamy już kolumny, można dodać jakieś elementy do listy, co wygląda bardzo podobnie do dodawania kolumn. Tym razem pole
iSubItem oznacza, że struktura, z której korzystamy (
LVITEM) odnosi się do elementu (''item''), a nie kolumny (''subitem''), dlatego też ustawiamy to pole na 0:
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.pszText = "file.dat";
lvi.iItem = 0;
lvi.iSubItem = 0;
ListView_InsertItem( hListView, & lvi );
lvi.pszText = "program.exe";
lvi.iItem = 1;
lvi.iSubItem = 0;
ListView_InsertItem( hListView, & lvi );
lvi.pszText = "archive.zip";
lvi.iItem = 2;
lvi.iSubItem = 0;
ListView_InsertItem( hListView, & lvi );
Efekt naszych starań będzie następujący:
Jak można dodać tekst do pozostałych kolumn? Można posłużyć się makrem
ListView_SetItemText lub wysłać
LVM_SETITEMTEXT. Pierwszy sposób jest o tyle wygodniejszy, że nie musimy wypełniać struktury LVITEM, tylko po prostu podajemy tekst:
ListView_SetItemText( hListView, 0, 1, "15" );
ListView_SetItemText( hListView, 0, 2, "14 kwietnia 2003" );
ListView_SetItemText( hListView, 1, 1, "3701" );
ListView_SetItemText( hListView, 1, 2, "31 lutego 1999" );
ListView_SetItemText( hListView, 2, 1, "100" );
ListView_SetItemText( hListView, 2, 2, "7 sierpnia 2006" );
Pierwszym parametrem makra (nie licząc uchwytu kontrolki) jest numer elementu (liczony oczywiście od 0), zaś drugim – numer kolumny (liczony też od 0). Ponieważ kolumnę zerową mamy już wypełnioną, pozostaje nam tylko ustawienie tekstu w kolumnach 1 ("Rozmiar") i 2 ("Data"). Jeśli jednak chcemy, możemy oczywiście wykorzystać
ListView_SetItemText do zmiany tekstu w zerowej kolumnie. A oto co uzyskaliśmy:
Ikonki
Rzeczą przydatną jest wyświetlanie ikonek w kontrolce List-View. W przypadku widoku "szczegóły" nie ma to aż takiego znaczenia, jak na przykład w widoku "duże ikony", ale i tak kontrolka z obrazkami wygląda wtedy dużo lepiej, a my możemy użytkownikowi przekazać dodatkowe informacje, np. o typie danego pliku.
Aby dorzucić ikony, musimy sobie najpierw stworzyć kontrolkę o nazwie ImageList, wypełnić ją kolejnymi obrazkami i przypisać do naszej List-View. Dobra wiadomość jest taka, że można narysować wszystkie ikonki na jednej bitmapie i wczytać ją do ImageList-y, a o dzielenie tego na kawałki martwić się już nie musimy. Tak więc do dzieła! Najpierw stworzymy kilka stałych, które będą oznaczały nasze ikonki:
#define IDB_EXE 3000
#define IDB_FOLDER 3001
#define IDB_FILE 3002
W naszym przykładzie będziemy mieć 3 ikonki: dla plików, folderów i plików wykonywalnych (exe). Teraz jeszcze trzeba przygotować odpowiedni plik
*.rc:
IDB_EXE BITMAP "exe.bmp"
IDB_FOLDER BITMAP "folder.bmp"
IDB_FILE BITMAP "file.bmp"
Zakładam, że już masz odpowiednie pliki
*.bmp do ikonek. Teraz możemy przejść do utworzenia kontrolki
ImageList.
HIMAGELIST himl;
HBITMAP hbmp;
himl = ImageList_Create( 16, 16, ILC_COLOR32, 3, 0 );
hbmp = LoadBitmap( hInstance, MAKEINTRESOURCE( IDB_EXE ) );
ImageList_Add( himl, hbmp,( HBITMAP ) NULL );
DeleteObject( hbmp );
hbmp = LoadBitmap( hInstance, MAKEINTRESOURCE( IDB_FOLDER ) );
ImageList_Add( himl, hbmp,( HBITMAP ) NULL );
DeleteObject( hbmp );
hbmp = LoadBitmap( hInstance, MAKEINTRESOURCE( IDB_FILE ) );
ImageList_Add( himl, hbmp,( HBITMAP ) NULL );
DeleteObject( hbmp );
Teraz mamy już gotową listę ikonek. Zwróć uwagę na flagę
ILC_COLOR32, której użyliśmy w funkcji
ImageList_Create. Dzięki niej będziemy mieć wszystkie kolory w naszych ikonkach. Domyślnie wybrane by zostało tylko znane z konsoli 16 kolorów, co jest efektem raczej niepożądanym ;-) Kontrolka
ImageList została dokładniej opisana w artykule o
Drzewo (TreeView) więc nie będę tutaj wyjaśniał poszczególnych funkcji.
Aby dołączyć do ListView te ikonki możemy się posłużyć makrem
ListView_SetImageList. Jako pierwszy argument podajemy uchwyt do kontrolki, jako drugi podajemy uchwyt do ImageListy a jako trzeci flagi. Ponieważ dodajemy ikonki do widoku szczegóły (czyli te małe) użyjemy flagi
LVSIL_SMALL. Jeżeli mamy duże ikony do widoku "Duże ikony" to użyjemy flagi
LVSIL_NORMAL.
ListView_SetImageList( hListView, himl, LVSIL_SMALL );
Mamy już ikonki, teraz możemy je przypisać konkretnym elementom listy. W tym celu musimy nieco zmodyfikować kod odpowiedzialny za dodawanie elementów. Przede wszystkim do pola
mask struktury
LVITEM dorzucamy flagę
LVIF_IMAGE. Teraz w tej strukturze będzie aktywne pole odpowiedzialne za ikonkę czyli
iImage. Do niego należy wstawić index ikonki z kontrolki
ImageList. Tak więc kod dodający elementy powinien wyglądać mniej więcej tak:
VITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_IMAGE; lvi.pszText = "file.dat";
lvi.iItem = 0;
lvi.iSubItem = 0;
lvi.iImage = 2; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "program.exe";
lvi.iItem = 1;
lvi.iSubItem = 0;
lvi.iImage = 0; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "archive.zip";
lvi.iItem = 2;
lvi.iSubItem = 0;
lvi.iImage = 2; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "Nowy Folder";
lvi.iItem = 3;
lvi.iSubItem = 0;
lvi.iImage = 1;
ListView_InsertItem( hListView, & lvi );
Do naszej listy dodaliśmy jeszcze element reprezentujący folder - żeby wykorzystać wszystkie ikonki ;-) A oto efekt naszych starań:
Style rozszerzone ListView
Nasza kontrolka ma pewną denerwującą cechę. Jeśli klikniemy jakiś element, zostanie on zaznaczony, ale zaznaczenie będzie widoczne tylko w zerowej kolumnie. Można to na szczęście zmienić, ustawiając styl
LVS_EX_FULLROWSELECT. "EX" w nazwie sugeruje, że ustawia się go w jakiś inny sposób, niż "zwykłe" style, no i faktycznie. Używamy tutaj makra
ListView_SetExtendedListViewStyle lub
ListView_SetExtendedListViewStyleEx. Pierwsze jest prostsze, ale używając tego drugiego można np. łatwo dodać nowy styl, nie usuwając poprzedniego i nie używając żadnych arytmetyki bitowej. Możemy też po prostu wysłać
LVM_SETEXTENDEDLISTVIEWSTYLE do kontrolki. Poniżej korzystamy z pierwszego sposobu:
ListView_SetExtendedListViewStyle( hListView, LVS_EX_FULLROWSELECT );
Rozszerzonych stylów jest więcej. Niektóre z nich są bardziej przydatne, inne mniej. Warto wymienić
LVS_EX_CHECKBOXES, który dodaje checkbox-y do elementów listy,
LVS_EX_DOUBLEBUFFER (Common Controls 6.0, od XP w górę), który włącza podwójne buforowanie w procedurze rysowania kontrolki, co redukuje irytujące migotanie,
LVS_EX_FLATSB, "spłaszczający" paski przewijania oraz
LVS_EX_GRIDLINES, który powoduje rysowanie siatki na kontrolce, co ułatwia użytkownikowi czytanie informacji. Pozostałe style pozostawiam ci do własnych eksperymentów.
Grupy
Inną możliwością kontrolki ListView jest podział elementów na grupy. Jeżeli otworzymy w Exploratorze Windows folder "Mój Komputer" to nad ikonkami najprawdopodobniej zobaczymy nagłówek np. ''"Dyski Twarde"'' itp. To są właśnie grupy. Żeby móc ich używać w naszej kontrolce musimy się zapoznać z kolejną strukturą (o radości! ;P) o nazwie
LVGROUP.
Użytkownicy kompilatora Dev-C++ muszą teraz przeżyć rozczarowanie - mianowicie Dev-C++ nie ma zdefiniowanej tej struktury, a także i wielu innych powiadomień, makr, stałych itd. związanych z grupami w kontrolce ListView. W związku z tym nie jest możliwe tworzenie ListView z grupami na Dev-C++.
Oto jak mniej więcej jest ona zadeklarowana:
typedef struct tagLVGROUP {
UINT cbSize;
UINT mask;
LPWSTR pszHeader;
int cchHeader;
LPWSTR pszFooter;
int cchFooter;
int iGroupId;
UINT stateMask;
UINT state;
UINT uAlign;
#if _WIN32_WINNT >= 0x0600
LPWSTR pszSubtitle;
UINT cchSubtitle;
LPWSTR pszTask;
UINT cchTask;
LPWSTR pszDescriptionTop;
UINT cchDescriptionTop;
LPWSTR pszDescriptionBottom;
UINT cchDescriptionBottom;
int iTitleImage;
int iExtendedImage;
int iFirstItem;
UINT cItems;
LPWSTR pszSubsetTitle;
UINT cchSubsetTitle;
#endif
} LVGROUP, * PLVGROUP;
LPWSTR czyli ciąg znaków Unicode znalazł się w powyższym kodzie nieprzypadkowo. Mianowice struktura LVGROUP ma zdefiniowaną tylko i wyłącznie wersję unikodową i trzeba o tym pamiętać!
Jak widać, znaczna część tej struktury jest dostępna dopiero na systemie Windows Vista i nowszych. Omówimy sobie niektóre z pól tej struktury.
Pozostałych pól nie będę omawiał, gdyż znajomość powyższych w zupełności wystarczy do prostej pracy z grupami i zrozumieniem przykładu omówionego w tym artykule. Jeżeli bardzo cię to ciekawi to możesz poczytać o tej strukturze w
MSDN (LVGROUP Structure).
Przyjrzyjmy się teraz bliżej polu
mask struktury
LVGROUP. Jeżeli chcemy, by tytuł grupy był poprawny, wstawiamy tam flagę
LVGF_HEADER. Natomiast do uaktywnienia pola
iGroupId posłuży flaga
LVGF_GROUPID. Zanim jednak zabierzemy się za dodawanie grup, musimy włączyć ich obsługę. Służy do tego komunikat
LVM_ENABLEGROUPVIEW. W parametrze
wParam podajemy
TRUE czyli ''włącz'' ;-):
SendMessage( hListView, LVM_ENABLEGROUPVIEW, TRUE, 0 );
Teraz możemy już utworzyć kilka grup. Posłuży nam do tego makro
ListView_InsertGroup:
LVGROUP lvg;
lvg.cbSize = LVGROUP_V5_SIZE; lvg.mask = LVGF_HEADER | LVGF_GROUPID;
lvg.pszHeader = L"Pliki";
lvg.iGroupId = 1;
ListView_InsertGroup( hListView, - 1, & lvg );
lvg.pszHeader = L"Foldery";
lvg.iGroupId = 2;
ListView_InsertGroup( hListView, - 1, & lvg );
Powyższy kod powinien się znaleść przed kodem odpowiedzialnym za dodawanie elementów (tym z
LVITEM). Teraz przyjrzyjmy się bliżej temu kodowi. Do rozmiaru wstawiliśmy stałą
LVGROUP_V5_SIZE bo używamy tylko XP'kowej części struktury. Literka
L przed stringami oznacza, że są one Unikodowe. Powinienieś to już dawno wiedzieć, ale tak dla przypomnienia napisałem ;-). Jako drugi parametr makra
ListView_InsertGroup wpisaliśmy
-1. Dlaczego? A to dlatego, że ten argument oznacza po którym elemencie listy ma się pojawić grupa. Jak damy
-1, to pojawi się na początku. Ponieważ nie mamy jeszcze żadnych elementów to wartość
-1 wydaje się najrozsądniejsza.
No właśnie - elementy! Jak sprawić, żeby znalazły się w grupach? W tym celu trzeba nieco zmodyfikować kod, który dodaje nam elementy do kontrolki. W strukturze
LVITEM mamy pole
iGroupId do którego wpisujemy ID grupy, do której ma należeć element. Żeby uaktywnić to pole, trzeba do pola
mask dorzucić flagę
LVIF_GROUPID. Czyli teraz nasz kod dodający elementy powinien wyglądać tak:
LVITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_GROUPID; lvi.pszText = "file.dat";
lvi.iItem = 0;
lvi.iSubItem = 0;
lvi.iImage = 2;
lvi.iGroupId = 1; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "program.exe";
lvi.iItem = 1;
lvi.iSubItem = 0;
lvi.iImage = 0;
lvi.iGroupId = 1; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "archive.zip";
lvi.iItem = 2;
lvi.iSubItem = 0;
lvi.iImage = 2;
lvi.iGroupId = 1; ListView_InsertItem( hListView, & lvi );
lvi.pszText = "Nowy Folder";
lvi.iItem = 3;
lvi.iSubItem = 0;
lvi.iImage = 1;
lvi.iGroupId = 2; ListView_InsertItem( hListView, & lvi );
Teraz możemy już podziwiać rezultaty - nasze ListView z grupami:
Zaznaczenie
W kontrolce ListView często interesuje nas, która pozycja jest aktualnie zaznaczona. Aby się tego dowiedzieć, można posłużyć się makrem
ListView_GetSelectionMark. Przyjmuje ono tylko jeden argument - uchwyt do kontrolki:
int index = ListView_GetSelectionMark( hListView );
Makro to zwraca nam index zaznaczonej pozycji. Jeżeli żadna pozycja nie jest zaznaczona, zwracana jest wartość
-1.
Wielokrotne zaznaczenie
Gdy użytkownik zaznaczy kilka elementów na raz, będziemy mieć znacznie bardziej skomplikowaną sytuację. Wtedy makro
ListView_GetSelectionMark zwraca nam index pierwszej zaznaczonej pozycji, jednak w niektórych układach zaznaczeń makro to "nawala" i podaje niepoprawną pozycję. Dostępny jest za to komunikat
LVM_GETSELECTEDCOUNT, który mówi nam ile pozycji jest zaznaczonych. Nie ma żadnego komunikatu, który pozwoliłby na pobranie indexów wszystkich zaznaczonych elementów.
W związku z tym musimy taką funkcję napisać sami. Zastanówmy się najpierw jak. Otóż ListView posiada pewien komunikat o nazwie
LVM_GETNEXTITEM, który zwraca nam index następnego elementu. Ważną rzeczą jest to, że może on filtrować rezultaty i znajdować taki następny element, jaki spełnia nasze kryteria. A nasze kryteria to - ''jest zaznaczony''. To kryterium reprezentowane jest przez flagę
LVNI_SELECTED umieszczaną w parametrze
lParam komunikatu. Parametr
wParam powinien zawierać index elementu od którego zaczynamy szukanie (
-1 oznacza początek). Jeżeli nie będzie już więcej elementów, to komunikat zwróci
-1.
Możemy więc wykorzystać ten komunikat, wywołując go w pętli dopóki nie zwróci
-1, a wcześniej zwrócone indexy zapamiętywać w tablicy. Oto przykładowa implementacja takiej funkcji napisane przeze mnie ;-)
int * ListView_GetSelectedItemsIndexes( HWND hListView ) {
int items = SendMessage( hListView, LVM_GETSELECTEDCOUNT, 0, 0 ); if( items == 0 ) return NULL;
int * index = new int[ items ]; int poprzedni = - 1; int i = 0; while( true ) { int zaznaczony = SendMessage( hListView, LVM_GETNEXTITEM, poprzedni, LVNI_SELECTED ); if( zaznaczony == - 1 ) break; index[ i ] = zaznaczony; poprzedni = zaznaczony; i++; }
return index; }
Funkcja ta zwraca tablicę indexów elementów, które są zaznaczone, lub
NULL jeśli żaden nie jest zaznaczony. Omówmy ją po kolei. Najpierw sprawdzamy ile elementów jest zaznaczonych (1) i zwracamy
NULL jeśli żaden nie jest. Następnie alokujemy tablicę na indexy (2) o rozmiarze poznanym w kroku (1). Później deklarujemy zmienną pomocniczą
poprzedni (3), która będzie przechowywać index poprzednio wyszukanego elementu.
-1 oznacza początek, dlatego z taką wartością jest ona inicjalizowana. Tworzymy też zmienną
i (4), która będzie nam mówić, do którego elementu tablicy zapisać index. Następnie przetwarzamy w pętli (5) kolejne wartości zwrócone przez
LVM_GETNEXTITEM (6). Jeżeli zwróci
-1 (7) to znaczy, że odczytaliśmy już wszystkie zaznaczone elementy i przerywamy pętlę. Jeśli nie, zapamiętujemy index w tablicy (9) i zwiększamy zmienną i (10). Na koniec zwracamy tablicę z indexami zaznaczonych elementów (11).
Powyższą funkcję można do woli stosować w swoich programach ;-)
Edycja etykiet
O edycji etykiet wspomnieliśmy już na początku artykułu, przy omawianiu stylu
LVS_EDITLABELS. Warto przyjrzeć się bliżej temu zagadnieniu.
Gdy użytkownik kliknie na zaznaczonym elemencie kontrolki, edycja etykiet powinna rozpocząć się automatycznie. Można też wymusić edycję etykiet za pomocą komunikatu
LVM_EDITLABEL. W parametrze
wParam przyjmuje on index elementu, który ma być kliknięty. I tak też zrobimy - wymusimy edycję etykiet, gdy użytkownik podwójnie kliknie na element. W tym celu musimy obsłużyć powiadomienie
NM_DBLCLK, które oznacza podwójne kliknięcie. Powiadomienie to przychodzi do nas ze strukturą
NMITEMACTIVATE, która zawiera różne informacje o elemencie. Pole
iItem to index elementu, który przekażemy do
LVM_EDITLABEL. Zanim jednak to zrobimy, musimy się upewnić, że kontrolka ma focus'a. No to do dzieła:
case WM_NOTIFY:
switch((( LPNMHDR ) lParam )->code ) {
case NM_DBLCLK:
if(((( LPNMHDR ) lParam )->hwndFrom ) == hListView ) {
LPNMITEMACTIVATE item =( LPNMITEMACTIVATE ) lParam;
if( item->iItem != - 1 ) {
SetFocus( hListView );
SendMessage( hListView, LVM_EDITLABEL, item->iItem, 0 );
}
}
break;
}
break;
Teraz gdy podwójnie klikniemy w którymkolwiek miejscu wiersza, rozpocznie się edycja etykiet - czyli w zerowej kolumnie zniknie text, a zamiast niego pojawi się pole textowe do wprowadzenia nowego textu. Gdy rozpocznie się edycja etykiet, otrzymamy powiadomienie
LVN_BEGINLABELEDIT. Przychodzi ono do nas ze strukturą
NMLVDISPINFO, której pole
item to struktura
LVITEM zawierająca dane elementu, który edytujemy (to ta sama struktura, której użyliśmy przy dodawaniu elementów). Jeżeli chcemy, by dla niektórych elementów nie była możliwa edycja etykiet, to zwracamy wartość niezerową (np.
1):
case WM_NOTIFY:
switch((( LPNMHDR ) lParam )->code ) {
case NM_DBLCLK:
break;
case LVN_BEGINLABELEDIT:
if(((( LPNMHDR ) lParam )->hwndFrom ) == hListView ) {
if(((( LPNMLVDISPINFO ) lParam )->item.iItem ) == 0 ) {
return TRUE;
}
}
break;
}
break;
W naszym przykładzie poszliśmy na łatwiznę i zablokowaliśmy edycję pierwszego elementu (index
0) tylko po to aby pokazać, jak to się robi ;-). Inną ciekawą rzeczą jest to, że możemy się dobrać do kontrolki
EDIT, która pojawia się w ListView. Służy do tego komunikat
LVM_GETEDITCONTROL, który zwraca uchwyt do
EDITa lub
NULL jeśli
EDITa nie ma. Możemy w ten sposób np. ograniczyć ilość znaków wpisywanych w kontrolce używając komunikatu
EM_LIMITTEXT dotyczącego ogólnie pól textowych, a nie tylko tych z ListView.
Zmodyfikujemy więc nieco nasz przykład. Ustawimy maxymalną ilość znaków na
20. Oto jak to zrobić:
case WM_NOTIFY:
switch((( LPNMHDR ) lParam )->code ) {
case NM_DBLCLK:
break;
case LVN_BEGINLABELEDIT:
if(((( LPNMHDR ) lParam )->hwndFrom ) == hListView ) {
if((((( NMLVDISPINFO * ) lParam )->item ).iItem ) == 0 ) {
return TRUE;
} else {
HWND hEdit =( HWND ) SendMessage( hListView, LVM_GETEDITCONTROL, 0, 0 );
SendMessage( hEdit, EM_LIMITTEXT, 20, 0 );
}
}
break;
}
break;
Gdy użytkownik skończy już wpisywać nowy text i zatwierdzi enterem bądź anuluje escapem, to wysyłane jest powiadomienie
LVN_ENDLABELEDIT. Przychodzi ono do nas ze strukturą
NMLVDISPINFO tak samo jak
LVN_BEGINLABELEDIT. Jak zapewne pamiętasz w polu
item tej struktury siedzi inna struktura -
LVITEM. Jeżeli pole
pszText tej struktury jest równe
NULL, to znaczy, że user anulował zmianę etykiety. Jeśli nie, jest tam nowy text wpisany przez usera. Warto też sprawdzić, czy nie jest on przypadkiem pusty, użytkownicy lubią czasem robić takie kawały. Tak więc do dzieła:
case WM_NOTIFY:
switch((( LPNMHDR ) lParam )->code ) {
case NM_DBLCLK:
break;
case LVN_BEGINLABELEDIT:
break;
case LVN_ENDLABELEDIT:
if(((( LPNMHDR ) lParam )->hwndFrom ) == hListView ) {
LVITEM lvitem =(( NMLVDISPINFO * ) lParam )->item;
if( lvitem.pszText == NULL ) break;
if( lstrlen( lvitem.pszText ) < 1 ) break;
ListView_SetItemText( hListView, lvitem.iItem, 0, lvitem.pszText );
}
break;
}
break;
Zauważ, że użyliśmy makra
ListView_SetItemText by ustawić nowy text. Zrobiliśmy to dlatego, że kontrolka automatycznie nie ustawia nowego textu - musimy to zrobić sami. Poza tym żadnych rewelacji nie ma, nie licząc rezultatu: