Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek |
|
Na tej lekcji nauczymy się używać kontrolki widoku drzewa (tree view control). Co więcej poznamy sposoby przeciągania i upuszczania elementów w kontrolce widoku drzewa oraz zastosowanie z niż listy obrazków.
Pobierz plik z przykładem {z tego archiwum}.
Kontrolka widoku drzewa jest specjalnym rodzajem okna, które pokazuje obiekty w porządku hierarchicznym. Przykładem może być lewy panel Explorera systemu Windows. Możesz użyć tej kontrolki do pokazania stosunków pomiędzy obiektami.
Kontrolkę widoku drzewa możesz utworzyć za pomocą wywołania funkcji CreateWindowEx przekazując jej nazwę klasy "SysTreeView32" albo możesz zawrzeć ją w oknie dialogowym. Nie zapomnij o umieszczeniu wywołania InitCommonControls w swoim kodzie.
Istnieje kilka specyficznych styli dla kontrolki widoku drzewa. Poniżej zebrałem te najczęściej stosowane:
Kontrolka widoku drzewa, podobnie jak inne kontrolki typowe, porozumiewa się z oknem nadrzędnym za pośrednictwem wiadomości. Okno nadrzędne może wysyłać do niej różne wiadomości, a kontrolka widoku drzewa może przesyłać wiadomości "powiadomień" do swojego okna nadrzędnego. W tym względzie kontrolka widoku drzewa nie różni się od innych okien.
Gdy dzieje się z nią coś interesującego, wysyła ona wiadomość WM_NOTIFY do okna nadrzędnego z towarzyszącą informacją.
WM_NOTIFY | |
---|---|
wParam | Numer identyfikacyjny ID kontrolki. Nie jest zagwarantowane, iż wartość ta będzie unikalna, zatem nie korzystamy z niej. Zamiast tego wykorzystujemy pola hwndFrom lub IDForm struktury NMHDR wskazywanej przez lParam. |
lParam | Wskazanie do struktury NMHDR. Niektóre z kontrolek mogą przekazywać wskazanie do większej struktury, jednakże w każdym przypadku pola struktury NMHDR znajdują się na jej początku w tej samej kolejności. Zatem mając lParam możesz być pewny, iż wskazuje on co najmniej strukturę NMHDR. |
Teraz zbadajmy strukturę NMHDR.
NMHDR STRUCT DWORD hwndFrom DWORD ? idFrom DWORD ? code DWORD ? NMHDR ENDS
Powiadomienia z kontrolki widoku drzewa są stałymi z przedrostkami TVN_. Wiadomości kontrolki widoku drzewa mają przedrostki TVM_, jak TVM_CREATEDRAGIMAGE. Kontrolka widoku drzewa wysyła powiadomienia TVN_xxxx w polu code struktury NMHDR. Okno nadrzędne może wysyłać wiadomości TVM_xxxx do sterowania nią.
Po utworzeniu kontrolki widoku drzewa możesz dodawać do niej elementy. Można to wykonać przez wysyłanie do niej wiadomości TVM_INSERTITEM
TVM_INSERTITEM | |
---|---|
wParam | równe 0 |
lParam | wskazanie struktury TV_INSERTSTRUCT |
W tym momencie powinieneś poznać nieco terminologii odnoszącej się do zależności elementów w kontrolce widoku drzewa.
Element może być nadrzędny (parent), potomny (child) lub jednocześnie nadrzędny i potomny.
Element nadrzędny jest elementem, który posiada powiązane z sobą jakieś inne elementy. Jednocześnie ten element nadrzędny może być potomnym jakiegoś innego elementu. Element nie posiadający elementu nadrzędnego jest nazywany elementem głównym (root element). W kontrolce widoku drzewa może być wiele elementów głównych. Teraz zbadajmy strukturę TV_INSERTSTRUCT:
TV_INSERTSTRUCT STRUCT DWORD hParent DWORD ? hInsertAfter DWORD ? ITEMTYPE <> TV_INSERTSTRUCT ENDS
ITEMTYPE UNION itemex TVITEMEX <> item TVITEM <> ITEMTYPE ENDS
Tutaj użyjemy tylko struktury TVITEM.
TV_ITEM STRUCT DWORD imask DWORD ? hItem DWORD ? state DWORD ? stateMask DWORD ? pszText DWORD ? cchTextMax DWORD ? iImage DWORD ? iSelectedImage DWORD ? cChildren DWORD ? lParam DWORD ? TV_ITEM ENDS
W zależności od typu wiadomości struktura ta jest używana do wysyłania lub odbierania informacji na temat elementu widoku drzewa. Na przykład z wiadomością TVM_INSERTITEM stosuje się ją do określania atrybutu elementu, który ma zostać wstawiony do kontrolki widoku drzewa. Po wysłaniu wiadomości TVM_GETITEM zostanie ona wypełniona informacjami na temat wybranego elementu kontrolki widoku drzewa.
Aby wprowadzić element do kontrolki widoku drzewa, musisz przynajmniej wypełnić pola hParent, hInsertAfter oraz również imask i pszText.
Jeśli chcesz umieścić obrazek po lewej stronie etykiety elementu widoku drzewa, musisz utworzyć listę obrazków (image list) i skojarzyć ją z kontrolką widoku drzewa. Listę obrazków możesz utworzyć przez wywołanie ImageList_Create.
ImageList_Create PROTO cx: DWORD,\ cy: DWORD,\ flags: DWORD,\ cInitial: DWORD,\ cGrow: DWORD
Jeśli wywołanie się powiedzie, to funkcja zwróci uchwyt do pustej listy obrazków.
Lista obrazków nie jest oknem. Stanowi ona jedynie przechowalnię dla obrazków, które mogą być wykorzystywane przez inne okna.
Po utworzeniu listy obrazków możesz dodawać do niej obrazki wywołując ImageList_Add.
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
Jeśli operacja się nie powiedzie, to funkcja ta zwraca wartość -1.
Zwykle będziemy dodawać jedynie dwa obrazki na tej liście przy zastosowaniu jej w kontrolce widoku drzewa: jeden używany w sytuacji, gdy element widoku drzewa nie jest wybrany oraz drugi, gdy ten element jest wybrany.
Gdy lista obrazków jest gotowa, wiążesz ją z kontrolką widoku drzewa wysyłając do niej wiadomość TVM_SETIMAGELIST
TVM_SETIMAGELIST | |
---|---|
wParam | rodzaj listy obrazków do ustawienia. Istnieją dwie
możliwości:
|
lParam | uchwyt do listy obrazków |
Możesz otrzymać informację o elemencie widoku drzewa wysyłając do kontrolki wiadomość TVM_GETITEM
TVM_GETITEM | |
---|---|
wParam | równe 0 |
lParam | wskazanie struktury TV_ITEM, która zostanie wypełniona pożądaną informacją |
Zanim wyślesz tę wiadomość, powinieneś wypełnić pole imask znacznikami, które określają pola struktury TV_ITEM do wypełnienia przez system Windows. A najważniejszym jest umieszczenie w polu hItem uchwytu elementu, na temat którego chcesz otrzymać informację. I to stwarza problem. Skąd masz znać uchwyt elementu, o którym chcesz uzyskać informację? Czy musisz zachować wszystkie uchwyty widoku drzewa?
Odpowiedź jest całkiem prosta: nie musisz tego robić. Możesz wysłać wiadomość TVM_GETNEXTITEM do kontrolki widoku drzewa, aby otrzymać uchwyt do elementu posiadającego określone przez ciebie atrybuty. Na przykład możesz zażądać uchwytu pierwszego elementu potomnego, elementu głównego, elementu wybranego itd.
TVM_GETNEXTITEM | |
---|---|
wParam | znacznik |
lParam | uchwyt do elementu widoku drzewa (potrzebny jedynie przy niektórych wartościach znacznika) |
Wartość wParam jest bardzo ważna, zatem poniżej przedstawiam wszystkie wartości znaczników:
Jak widać, z tej wiadomości można otrzymać uchwyt do interesującego nas elementu widoku drzewa. Jeśli wywołanie SendMessage powiedzie się, to zwróci ono uchwyt elementu widoku drzewa. Możesz następnie wpisać zwrócony uchwyt w pole hItem struktury TV_ITEM, aby wykorzystać ją z wiadomością TVM_GETITEM.
Ta część jest powodem napisania tego rozdziału. Gdy starałem się śledzić przykład w pomocy dla funkcji bibliotecznych API Win32 (plik win32.hlp z firmy Borland), byłem bardzo sfrustrowany, ponieważ brakowało tam najistotniejszych informacji. W końcu metodą prób i błędów odkryłem, w jaki sposób należy implementować operację przeciągania i upuszczania (drag-and-drop) w kontrolce widoku drzewa i nie chcę, aby inni musieli podążać tą samą ciernistą drogą, co ja.
Poniżej wymieniam kolejne kroki implementacji operacji przeciągania i upuszczania w kontrolce widoku drzewa:
ImageList_BeginDrag PROTO himlTrack: DWORD,\ iTrack: DWORD,\ dxHotspot: DWORD,\ dyHotspot: DWORD
himlTrack jest uchwytem listy obrazków, która zawiera obrazek przeciągania.
iTrack jest indeksem przeciąganego obrazka na liście obrazków.
dxHotspot określa względną pozycję w poziomie gorącego punktu na obszarze obrazka przeciągania. Skoro obraz ten ma być stosowany w miejsce kursora myszki, zatem musimy określić, który fragment obrazu jest gorącym punktem.
dyHotspot określa względną pozycję w pionie gorącego punktu na obszarze obrazka przeciągania.
Zwykle parametr iTrack będzie wynosił 0, jeśli nakażesz kontrolce widoku drzewa utworzyć obrazek przeciągania, a dxHotspot i dyHotspot mogą mieć wartość 0, jeśli chcesz, aby gorący punkt znajdował się w lewym górnym narożniku obrazka przeciągania.
ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
Plik TREVIEW.ASM
.386 .MODEL FLAT, STDCALL OPTION CASEMAP:NONE INCLUDE \masm32\include\windows.inc INCLUDE \masm32\include\user32.inc INCLUDE \masm32\include\kernel32.inc INCLUDE \masm32\include\comctl32.inc INCLUDE \masm32\include\gdi32.inc INCLUDELIB \masm32\lib\gdi32.lib INCLUDELIB \masm32\lib\comctl32.lib INCLUDELIB \masm32\lib\user32.lib INCLUDELIB \masm32\lib\kernel32.lib WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD .CONST IDB_TREE EQU 4006 .DATA ClassName DB "TreeViewWinClass", 0 AppName DB "Widok Drzewa", 0 TreeViewClass DB "SysTreeView32", 0 Parent DB "Element nadrzędny", 0 Child1 DB "Potomek1", 0 Child2 DB "Potomek2", 0 DragMode DD FALSE .DATA? hInstance HINSTANCE ? hwndTreeView DD ? hParent DD ? hImageList DD ? hDragImageList DD ? .CODE start: INVOKE GetModuleHandle, NULL mov hInstance, eax INVOKE WinMain, hInstance, NULL, NULL, SW_SHOWDEFAULT INVOKE ExitProcess, eax INVOKE InitCommonControls WinMain PROC hInst: HINSTANCE,\ hPrevInst: HINSTANCE,\ CmdLine: LPSTR,\ CmdShow: DWORD LOCAL wc : WNDCLASSEX LOCAL msg : MSG LOCAL hwnd : HWND mov wc.cbSize, SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW OR CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra, NULL mov wc.cbWndExtra, NULL push hInst pop wc.hInstance mov wc.hbrBackground, COLOR_APPWORKSPACE mov wc.lpszMenuName, NULL mov wc.lpszClassName, OFFSET ClassName INVOKE LoadIcon, NULL, IDI_APPLICATION mov wc.hIcon, eax mov wc.hIconSm, eax INVOKE LoadCursor, NULL, IDC_ARROW mov wc.hCursor, eax INVOKE RegisterClassEx, ADDR wc INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\ ADDR ClassName, ADDR AppName,\ WS_OVERLAPPED + WS_CAPTION +\ WS_SYSMENU + WS_MINIMIZEBOX+\ WS_MAXIMIZEBOX + WS_VISIBLE, CW_USEDEFAULT,\ CW_USEDEFAULT, 200, 200, NULL, NULL,\ hInst, NULL mov hwnd, eax .WHILE TRUE INVOKE GetMessage, ADDR msg, NULL, 0, 0 .BREAK .IF (!eax) INVOKE TranslateMessage, ADDR msg INVOKE DispatchMessage, ADDR msg .ENDW mov eax, msg.wParam ret WinMain ENDP WndProc PROC USES edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL tvinsert: TV_INSERTSTRUCT LOCAL hBitmap: DWORD LOCAL tvhit: TV_HITTESTINFO .IF uMsg==WM_CREATE INVOKE CreateWindowEx, NULL, ADDR TreeViewClass, NULL,\ WS_CHILD + WS_VISIBLE +\ TVS_HASLINES + TVS_HASBUTTONS +\ TVS_LINESATROOT, 0,\ 0, 200, 400, hWnd, NULL,\ hInstance, NULL mov hwndTreeView, eax INVOKE ImageList_Create, 16, 16, ILC_COLOR16, 2, 10 mov hImageList, eax INVOKE LoadBitmap, hInstance, IDB_TREE mov hBitmap, eax INVOKE ImageList_Add, hImageList, hBitmap, NULL INVOKE DeleteObject, hBitmap INVOKE SendMessage, hwndTreeView,TVM_SETIMAGELIST, 0, hImageList mov tvinsert.hParent, NULL mov tvinsert.hInsertAfter, TVI_ROOT mov tvinsert.item.imask, TVIF_TEXT + TVIF_IMAGE + TVIF_SELECTEDIMAGE mov tvinsert.item.pszText, OFFSET Parent mov tvinsert.item.iImage, 0 mov tvinsert.item.iSelectedImage, 1 INVOKE SendMessage, hwndTreeView,TVM_INSERTITEM, 0, ADDR tvinsert mov hParent, eax mov tvinsert.hParent, eax mov tvinsert.hInsertAfter, TVI_LAST mov tvinsert.item.pszText, OFFSET Child1 INVOKE SendMessage, hwndTreeView,TVM_INSERTITEM, 0, ADDR tvinsert mov tvinsert.item.pszText, OFFSET Child2 INVOKE SendMessage, hwndTreeView,TVM_INSERTITEM, 0, ADDR tvinsert .ELSEIF uMsg==WM_MOUSEMOVE .IF DragMode==TRUE movzx eax, WORD PTR lParam movzx ecx, WORD PTR lParam + 2 mov tvhit.pt.x, eax ;eax pozioma pozycja przeciąganego obrazka mov tvhit.pt.y, ecx ;ecx pozycja pionowa INVOKE ImageList_DragMove, eax, ecx INVOKE ImageList_DragShowNolock, FALSE INVOKE SendMessage, hwndTreeView,TVM_HITTEST, NULL, ADDR tvhit ; sprawdź, czy element jest kliknięty .IF eax!=NULL INVOKE SendMessage, hwndTreeView,TVM_SELECTITEM, TVGN_DROPHILITE, eax .ENDIF INVOKE ImageList_DragShowNolock, TRUE .ENDIF .ELSEIF uMsg==WM_LBUTTONUP .IF DragMode==TRUE INVOKE ImageList_DragLeave, hwndTreeView INVOKE ImageList_EndDrag INVOKE ImageList_Destroy, hDragImageList INVOKE SendMessage, hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE, 0 ;Pobierz obecnie wybrany element INVOKE SendMessage, hwndTreeView,TVM_SELECTITEM, TVGN_CARET, eax INVOKE SendMessage, hwndTreeView,TVM_SELECTITEM, TVGN_DROPHILITE, 0 INVOKE ReleaseCapture mov DragMode, FALSE .ENDIF .ELSEIF uMsg==WM_NOTIFY mov edi, lParam ASSUME edi:PTR NM_TREEVIEW .IF [edi].hdr.code==TVN_BEGINDRAG INVOKE SendMessage, hwndTreeView,TVM_CREATEDRAGIMAGE, 0,[edi].itemNew.hItem mov hDragImageList, eax INVOKE ImageList_BeginDrag, hDragImageList, 0, 0, 0 INVOKE ImageList_DragEnter, hwndTreeView,[edi].ptDrag.x, [edi].ptDrag.y INVOKE SetCapture, hWnd mov DragMode, TRUE .ENDIF ASSUME edi:NOTHING .ELSEIF uMsg==WM_DESTROY INVOKE PostQuitMessage, NULL .ELSE INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam ret .ENDIF xor eax, eax ret WndProc ENDP END start
Plik TREEVIEW.RC
#define IDB_TREE 4006 IDB_TREE BITMAP list.bmp
W sekcji obsługi wiadomości WM_CREATE tworzysz kontrolkę widoku drzewa
INVOKE CreateWindowEx, NULL, ADDR TreeViewClass, NULL,\ WS_CHILD + WS_VISIBLE +\ TVS_HASLINES + TVS_HASBUTTONS +\ TVS_LINESATROOT, 0,\ 0, 200, 400, hWnd, NULL,\ hInstance, NULL
Zwróć uwagę na style: TVS_xxxx są stylami specyficznymi dla widoku drzewa.
INVOKE ImageList_Create, 16, 16, ILC_COLOR16, 2, 10 mov hImageList, eax INVOKE LoadBitmap, hInstance, IDB_TREE mov hBitmap, eax INVOKE ImageList_Add, hImageList, hBitmap, NULL INVOKE DeleteObject, hBitmap INVOKE SendMessage, hwndTreeView, TVM_SETIMAGELIST, 0, hImageList
Następnie tworzysz pustą listę obrazków, która będzie przechowywała obrazki o rozmiarze 16 x 16 pikseli w 16 kolorach i początkowo będzie zawierała 2 obrazki, lecz może być rozszerzona do 10, jeśli potrzeby wzrosną. Dalej ładujemy grafikę rastrową z zasobu i dodajemy ją do właśnie utworzonej listy obrazków. Po tej operacji usuwamy uchwyt do grafiki rastrowej, ponieważ nie będzie nam już potrzebny. Gdy lista obrazków jest już w całości gotowa, kojarzymy ją z kontrolką widoku drzewa wysyłając do niej wiadomość TVM_SETIMAGELIST.
mov tvinsert.hParent, NULL mov tvinsert.hInsertAfter, TVI_ROOT mov tvinsert.item.imask, TVIF_TEXT + TVIF_IMAGE + TVIF_SELECTEDIMAGE mov tvinsert.item.pszText, OFFSET Parent mov tvinsert.item.iImage, 0 mov tvinsert.item.iSelectedImage, 1 INVOKE SendMessage, hwndTreeView, TVM_INSERTITEM, 0, ADDR tvinsert
Wstawiamy elementy do kontrolki widoku drzewa rozpoczynając od elementu głównego. Ponieważ będzie to element główny, pole hParent ma wartość NULL a hInsertAfter ma wartość TVI_ROOT. Pole imask określa, iż dane zawarte są w polach pszText, iImage oraz iSelectedImage w strukturze TV_ITEM. Pola te wypełniamy odpowiednimi wartościami:
Gdy zostaną wypełnione wszystkie odpowiednie pola, wysyłamy wiadomość TVM_INSERTITEM do kontrolki widoku drzewa, aby dodać do niej element główny.
mov hParent, eax mov tvinsert.hParent, eax mov tvinsert.hInsertAfter, TVI_LAST mov tvinsert.item.pszText, OFFSET Child1 INVOKE SendMessage, hwndTreeView, TVM_INSERTITEM, 0, ADDR tvinsert mov tvinsert.item.pszText, OFFSET Child2 INVOKE SendMessage, hwndTreeView, TVM_INSERTITEM, 0, ADDR tvinsert
Po dodaniu elementu głównego możemy dołączyć do niego elementy potomne. Pole hParent zostaje wypełnione uchwytem elementu głównego. Ponieważ będziemy używać identycznych obrazków z listy obrazków, zatem nie zmieniamy pól iImage i iSelectedImage
.ELSEIF uMsg==WM_NOTIFY mov edi, lParam ASSUME edi:PTR NM_TREEVIEW .IF [edi].hdr.code==TVN_BEGINDRAG INVOKE SendMessage, hwndTreeView, TVM_CREATEDRAGIMAGE, 0, [edi].itemNew.hItem mov hDragImageList, eax INVOKE ImageList_BeginDrag, hDragImageList, 0, 0, 0 INVOKE ImageList_DragEnter, hwndTreeView, [edi].ptDrag.x, [edi].ptDrag.y INVOKE SetCapture, hWnd mov DragMode, TRUE .ENDIF ASSUME edi:NOTHING
Teraz gdy użytkownik próbuje przeciągać jakiś element, kontrolka widoku drzewa wysyła wiadomość WM_NOTIFY z kodem TVN_BEGINDRAG. lParam jest wskazaniem struktury NM_TREEVIEW, która zawiera kilka informacji nam potrzebnych, zatem w rejestrze edi umieszczamy tę wartość i używamy go jako wskazania struktury NM_TREEVIEW. ASSUME edi:PTR NM_TREEVIEW jest sposobem nakazania asemblerowi MASM potraktowania rejestru edi jako wskazania do struktury NM_TREEVIEW. Następnie tworzymy obrazek przeciągania wysyłając wiadomość TVM_CREATEDRAGIMAGE do kontrolki widoku drzewa. Zwraca ona uchwyt do nowo utworzonej listy obrazków z umieszczonym wewnątrz niej obrazkiem przeciągania. Wywołujemy ImageList_BeginDrag do ustawienie gorącego punktu na obrazku przeciągania. Następnie wchodzimy do operacji przeciągania wywołując ImageList_DragEnter. Funkcja ta wyświetla obrazek przeciągania na podanej pozycji w podanym oknie. Wykorzystujemy strukturę ptDrag będącą polem struktury NM_TREEVIEW jako punkt, gdzie początkowo powinien zostać wyświetlony obrazek przeciągania. Struktura NM_TREEVIEW jest następująca:
NM_TREEVIEW STRUCT hdr NMHDR <> action DWORD ? itemOld TVITEM <> itemNew TVITEM <> ptDrag POINT <> NM_TREEVIEW ENDS
Po tych działaniach przechwytujemy dane z myszki i ustawiamy znacznik, który wskazuje, iż weszliśmy do trybu obsługi przeciągania.
.ELSEIF uMsg==WM_MOUSEMOVE .IF DragMode==TRUE movzx eax, WORD PTR lParam movzx ecx, WORD PTR lParam + 2 mov tvhit.pt.x, eax ;eax pozioma pozycja mov tvhit.pt.y, ecx ;ecx pozycja pionowa INVOKE ImageList_DragMove, eax, ecx INVOKE ImageList_DragShowNolock, FALSE INVOKE SendMessage, hwndTreeView, TVM_HITTEST, NULL, ADDR tvhit ; sprawdź, czy element jest kliknięty .IF eax!=NULL INVOKE SendMessage, hwndTreeView, TVM_SELECTITEM, TVGN_DROPHILITE, eax .ENDIF INVOKE ImageList_DragShowNolock, TRUE .ENDIF
Teraz skoncentrujemy się na WM_MOUSEMOVE. Gdy użytkownik przeciąga obrazek, nasze okno nadrzędne otrzymuje wiadomości WM_MOUSEMOVE. W odpowiedzi na te wiadomości uaktualniamy pozycję obrazka przeciągania przy pomocy wywołania funkcji ImageList_DragMove. Po tej operacji sprawdzamy, czy obrazek przeciągania znajduje się ponad jakimś elementem. Informację tę uzyskamy wysyłając wiadomość TVM_HITTEST do kontrolki widoku drzewa z punktem do sprawdzenia. Jeśli obrazek przeciągania znajduje się ponad jakimś elementem, to podświetlamy ten element wysyłając do kontrolki widoku drzewa wiadomość TVM_SELECTITEM ze znacznikiem TVGN_DROPHILITE. Podczas trwania operacji podświetlania ukrywamy obrazek przeciągania, aby nie pozostawił na ekranie niepożądanych śladów.
.ELSEIF uMsg==WM_LBUTTONUP .IF DragMode==TRUE INVOKE ImageList_DragLeave, hwndTreeView INVOKE ImageList_EndDrag INVOKE ImageList_Destroy, hDragImageList INVOKE SendMessage, hwndTreeView, TVM_GETNEXTITEM, TVGN_DROPHILITE, 0 ;Pobierz obecnie wybrany element INVOKE SendMessage, hwndTreeView, TVM_SELECTITEM, TVGN_CARET, eax INVOKE SendMessage, hwndTreeView, TVM_SELECTITEM, TVGN_DROPHILITE, 0 INVOKE ReleaseCapture mov DragMode, FALSE .ENDIF
Gdy użytkownik zwolni lewy przycisk myszki, operacja przeciągania jest kończona. Opuszczamy tryb przeciągania wywołując kolejno ImageList_DragLeave i ImageList_EndDrag oraz ImageList_Destroy. Aby elementy widoku drzewa wyglądały poprawnie, musimy również zaznaczyć ostatnio podświetlony element i wybrać go. Należy także usunąć podświetlenie, w przeciwnym wypadku inne elementy nie byłyby podświetlane w momencie wybierania ich. A na koniec zwalniamy przechwytywanie myszki.
Ta sama aplikacja w Pascalu:
{******************************** ** I Liceum Ogólnokształcące ** ** w Tarnowie ** ** mgr Jerzy Wałaszek ** ********************************} program TreeView; uses Windows; const IDB_TREE = 4006; ClassName = 'TreeViewWinClass'; AppName = 'Widok Drzewa'; TreeViewClass = 'SysTreeView32'; Parent = 'Element nadrzędny'; Child1 = 'Potomek1'; Child2 = 'Potomek2'; var DragMode : boolean; hInstance : HINST; hwndTreeView : longword; hParent : longword; hImageList : longword; hDragImageList : longword; function WndProc(hWnd:HANDLE;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint; var tvinsert : TV_INSERTSTRUCT; tvhit : TV_HITTESTINFO; hBitmap : longword; x : longint; tvp : ^NM_TREEVIEW; begin Result := 0; case uMsg of WM_CREATE: begin hwndTreeView := CreateWindowEx(0,TreeViewClass,0, WS_CHILD + WS_VISIBLE + TVS_HASLINES + TVS_HASBUTTONS + TVS_LINESATROOT,0,0,200,400, hWnd,0,hInstance,0); hImageList := ImageList_Create(16,16,ILC_COLOR16,2,10); hBitmap := LoadBitmap(hInstance,IDB_TREE); ImageList_Add(hImageList,hBitmap,0); DeleteObject(hBitmap); SendMessage(hwndTreeView,TVM_SETIMAGELIST,0,hImageList); tvinsert.hParent := 0; tvinsert.hInsertAfter := TVI_ROOT; tvinsert.item.mask := TVIF_TEXT + TVIF_IMAGE + TVIF_SELECTEDIMAGE; tvinsert.item.pszText := Parent; tvinsert.item.iImage := 0; tvinsert.item.iSelectedImage := 1; hParent := SendMessage(hwndTreeView,TVM_INSERTITEM,0,longint(@tvinsert)); tvinsert.hParent := HTREEITEM(hParent); tvinsert.hInsertAfter := TVI_LAST; tvinsert.item.pszText := Child1; SendMessage(hwndTreeView,TVM_INSERTITEM,0,longint(@tvinsert)); tvinsert.item.pszText := Child2; SendMessage(hwndTreeView,TVM_INSERTITEM,0,longint(@tvinsert)); end; WM_MOUSEMOVE: if DragMode then begin tvhit.pt.x := lParam and $ffff; tvhit.pt.y := lParam shr 16; ImageList_DragMove(tvhit.pt.x,tvhit.pt.y); ImageList_DragShowNolock(false); x := SendMessage(hwndTreeView,TVM_HITTEST,0,longint(@tvhit)); if x <> 0 then SendMessage(hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,x); ImageList_DragShowNolock(true); end; WM_LBUTTONUP: if DragMode then begin ImageList_DragLeave(hwndTreeView); ImageList_EndDrag; ImageList_Destroy(hDragImageList); SendMessage(hwndTreeView,TVM_SELECTITEM,TVGN_CARET, SendMessage(hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0)); SendMessage(hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0); ReleaseCapture; DragMode := false; end; WM_NOTIFY: begin tvp := POINTER(lParam); if tvp^.hdr.code = TVN_BEGINDRAG then begin hDragImageList := SendMessage(hwndTreeView,TVM_CREATEDRAGIMAGE,0, longint(tvp^.itemNew.hItem)); ImageList_BeginDrag(hDragImageList,0,0,0); ImageList_DragEnter(hwndTreeView,tvp^.ptDrag.x,tvp^.ptDrag.y); SetCapture(hWnd); DragMode := true; end; end; WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(hWnd,uMsg,wParam,lParam); end; end; function WinMain(hInst,hPrevInst:HINST;CmdLine:LPSTR;CmdShow:DWORD) : longint; var wc : WNDCLASSEX; msg : MSG; hwnd : HANDLE; begin with wc do begin cbSize := sizeof(WNDCLASSEX); style := CS_HREDRAW or CS_VREDRAW; lpfnWndProc := @WndProc; cbClsExtra := 0; cbWndExtra := 0; hInstance := hInst; hbrBackground := COLOR_APPWORKSPACE; lpszMenuName := 0; lpszClassName := ClassName; hIcon := LoadIcon(0,IDI_APPLICATION); hIconSm := hIcon; hCursor := LoadCursor(0,IDC_ARROW); end; RegisterClassEx(wc); hwnd := CreateWindowEx(WS_EX_CLIENTEDGE,ClassName,AppName, WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_MINIMIZEBOX + WS_MAXIMIZEBOX + WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT, 200,200,0,0,hInst,0); while GetMessage(msg,0,0,0) do begin TranslateMessage(msg); DispatchMessage(msg); end; Result := msg.wParam; end; begin InitCommonControls; DragMode := false; hInstance := GetModuleHandle(0); ExitProcess(WinMain(hInstance,0,0,SW_SHOWDEFAULT)); end.
Autorem
kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną
zgodą autora. |
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe