![]() |
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: DWORDhimlTrack 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 ENDSPo 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