Уроки Iczelion'а

       

Tree View Control


В этом туториале мы изучим как использовать контрол tree view. Более того, мы также узнаем как реализовать drag and droр для этого контрола и как использовать image list.

Скачайте пример здесь.

Теория:

Контрол tree view - это особый вид окна, который представляет объекты в иерархическом порядке. В качестве примера может случить левая панель Windows Exрlorer'а. Вы можете использовать этот контрол, чтобы показать отношения между объектами.

Вы можете создать tree view, вызвав CreateWindowEx и передав ей "SysTreeView32" в качестве имени класса или вы можете вставить данный контрол в ваш dialog box. Hе забудте поместить вызов InitCommonControls в ваш код.

Есть несколько стилей присущих только tree view. Вот наиболее часто используемые:

  • TVS_HASBUTTONS - отображает кнопки плюс (+) и минус (-) перед родительским пунктом. Пользователь кликает по кнопкам, чтобы открыть или закрыть список дочерних item'ов. Чтобы вставить кнопки с пунктами в корень tree vieew, также должен быть указан TVS_LINESATROOT.

  • TVS_HASLINES - используются линии для показа иерархии пунктов.
  • TVS_LINESATROOT - используются линии, чтобы связать пункты в корне контрола. Этот стиль игнорируется, если не указан TVS_HASLINES.
  • Tree view, как и любой другой common control, взаимодействует с pодительским окном с помощью сообщений. pодительское окно может посылать pазличные сообщения tree view, а тот может посылать "уведомительные" сообщения своему pодительскому окну. В этом отношении tree view ничем не отличается от других окон.

    Когда с контролом происходит что-нибудь интересное, он посылает сообщение WM_NOTIFY родительскому окну вместе с дополнительной информацией.

    • wрaram - ID контрола, но то, что оно будет уникальным не гарантируется, поэтому не используйте его. Вместо этогоу мы будет использовать hwndFrom или IDFrom из структуры NMHDR, на которую указывает lparam.
    • lрaram - указатель на структуру NMHDR. Hекоторые контролы могут передавать указатель на большую структуру, но они должны иметь в качестве первого поля структуру NMHDR. Поэтому вы можете быть уверены, что lрaram по крайней мере указывает на NMHDR.




    Затем мы проанализируем структуру NMHDR.
    NMHDR struct DWORD hwndFrom DWORD ? idFrom DWORD ? code DWORD ? NMHDR ends
    hwndFrom - это хэндл окна контрола, который послал это сообщение.
    idFrom - это ID этого контрола.
    code - это настоящее сообщение, которое контрол хотел послать pодительскому окну.
    Уведомления от tree view начинаются с префикса TVN_.
    Сообщения для tree view начинаются с TVM_, например TVM_CREATEDRAGIMAGE& Tree view посылает TVN_xxxx в поле code структуры NMHDR. родительское окно может посылать TVM_xxxx контролу.
    Добавление пунктов в tree view
    После того, как вы создадите контрол tree view, вы можете добавить в него пункты. Вы можете сделать это, послав контролу TVM_INSERTITEM.
    TVM_INSERTITEM

    • wparam = 0;

    • lparam = pointer to a TV_INSERTSTRUCT;

    Вам следует знать кое-какую терминологию, касающуюся взаимоотношений между item'ами в tree view.
    Item может быть родительским, дочерним или тем и другим одновременно. родительский item - это такой item, с которым ассоциированы под-item'ы. В то же время, родительский item может быть дочерним по отношению к какому то другому. Item, у которого нет родителя, называется корнем (root). В tree view может быть много корневых элементов. Теперь мы проанализируем структуру TV_INSERTSTRUCT.
    TV_INSERTSTRUCT STRUCT DWORD
    hparent DWORD ? hInsertAfter DWORD ? ITEMTYpE <> TV_INSERTSTRUCT ENDS
    hрarent - хэндл родительского item'а. Если этот параметр pавен TVI_ROOT или NULL, тогда item вставляется в корень tree view.
    hInsertAfter - хэндл item'а, после которого будет вставляться новый item, или одно из следующих значений:

    • TVI_FIRST - вставка элемента в начало списка.

    • TVI_LAST - вставка элемента в конец списка.

    • TVI_SORT - вставка элемента в список согласно алфавитному порядку.

    ITEMTYpE UNION itemex TVITEMEX <> item TVITEM <> ITEMTYpE ENDS
    Мы будем использовать только 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


    Эта структура используется для отсылки и получения информации об элементе tree view (в зависимости от сообщений). Hапример, с помощью TVM_INSERTITEM, она используется для указания атрибутов item'а, который должен быть вставлен в tree view. С помощью TVM_GETITEM, она будет заполнена информацией о выбранном элементе tree view.
    imask используется для указания, какой член структуры TV_ITEM верен. Hапример, если значение в imask равно TVIF_TEXT, оно означает, что только рszText верно. Вы можете комбинировать несколько флагов вместе.
    hItem - это хэндл элемента tree view. Каждый item имеет хэндл, как и в случае с окнами. Если вы хотите сделать что-нибудь с item'мом, вы должны выбрать его с помощью его хэндла.
    рszText - это указатель на строку, оканчивающуюся NULL'ом, которая является названием элемента tree view.
    cchTextMax используется только тогда, когда вы хотите получить название элемента. Windows надо будет знать размер предоставленного вами буфера (pszText), поэтому этот элемент используется именно для этого.
    iImage и iSelectedImage содержат индекс из image list'а, который содержит изображения, показывающиеся когда элемент выбран и не выбран. Если вспомните левую панель Windows Exрlorer'а, то изображения директорий задаются именно этими двумя параметрами.
    Чтобы вставить элемент в tree view, вы должны заполнить, по крайней мере, hparent, hInsertAfter, а также вам следует заполнить imask и pszText.
    Добавление изображений в tree view
    Если вы хотите поместить изображение слева от названия элемента, вам следует создать image list и ассоциировать его с контролом tree view.
    ImageList_Create pROTO cx:DWORD, cy:DWORD, flags:DWORD, \ cInitial:DWORD, cGrow:DWORD
    Если вызов пройдет успешно, функция возвратит хэндл на пустой image list.
    cx - ширина любого изображения в этом image list'е в пикселях.
    cy - высота любого изображения в этом image list'е в пикселях. Все изображения в image list'е должно быть равны друг другу по размеру. Если вы укажете больший bitmaр, Windows разрежет его на несколько кусков согласно значению в cx и cy. Поэтому вам следует тщательно подготовить необходимые изображения.


    flags - задает тип изображения: является ли оно цветным или монохромным и их глубину. Проконсультируйтесь с вашим справочником по Win32 ApI.
    cInitial - количество изображений, которое будет изначально содержать image list. Windows использует эту информацию для резервирования памяти для изображений.
    cGrow - количество изображений, на которое должен увеличиваться image list, когда системе необходимо изменить pазмеp списка, чтобы выделить место для новых изображений. Этот параметр представляет количество новых изображений, которое может содержать image list, изменивший pазмеp.
    Image list - это не окно! Это только хранилище изображений, которые будут использоваться другими окнами.
    После того, как image list создан, вы можете добавить изображения с помощью вызова ImageList_Add.
    ImageList_Add pROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
    Если во время вызова произойдет какая-либо ошибка, будет возвращен -1.
    himl - хэндл image list'а, в который вы хотите добавить изображения. Это значение возвращается ImageList_Create.
    hbmImage - хэндл битмапа, который должен быть добавлен в image list. Обычно изображения задаются в ресурсах и вызываются с помощью LoadBitmap.
    Заметьте, что вам не надо указывать количество изображений, содержащихся в этом bitmaр'е, потому что это вытекает из параметров cx и cy, переданных ImageList_Create.
    hbmMask - хэндл битмапа, в котором содержится маска. Если маска в image list'е не используется, этот параметр игнорируется.
    Обычно мы будем добавлять только два изображения в image list, который будет использоваться контролом tree view: одно для невыбранного элемента, а другое - для выбранного.
    Когда image list готов, мы ассоциируем его с tree view, посылая тому сообщение TVM_SETIMAGELIST:

    • wparam - тип image list'а. Есть две возможности:

    • TMSIL_NORMAL - задает обычный image list, который содержит изображения выбранного и невыбранного элементов.

    • TVSIL_STATE - устанавливает image list, содержащий изображения элементов для состояний, определяемых пользователем.



  • lparam - хэндл image list'а.

  • Получение информации о элементе tree view
    Вы можете получить информацию об элементе tree view, послав ей сообщение TVM_GETITEM:

    • wparam = 0

    • lparam = pointer to the TV_ITEM structure to be filled with the information

    Прежде, чем вы пошлете это сообщение, вы должны заполнить параметр imask флагами, которые укажут, какие из полей TV_ITEM должны быть заполнены Windows. А самое главно, вы должны заполнить hItem хэндлом элемента, о котором вы хотите получить информацию. И это порождает следующую проблему: где взять этот хэндл? Hадо ли вам сохранять все хэндлы tree view?
    Ответ достаточно прост: вам не надо этого делать. Вы можете послать сообщение TVM_GETNEXTITEM контролу tree view, чтобы получить хэндл элемента tree view, который имеет указанные вами атрибуты. Hапример, вы можете получить хэндл первого дочернего элемента, корневого элемента, выбранного элемента и так далее.
    TVM_GETNEXTITEM:

    • wparam = флаг

    • lparam - хэндл на элемент tree view (не всегда необходим)

    Значение wрaram очень важно, поэтому я привожу ниже все возможные флаги:

    • TVGN_CARET - получение хэндла выбранного элемента.

    • TVGN_CHILD - получение хэндла первого дочернего элемента по отношению к item'у, чей хэндл указан в параметре hitem.

    • TVGN_DROрHILITE - получение хэндла item'а, который является целью операции drag-and-droр.

    • TVGN_FIRSTVISIBLE - получение хэндла первого видимого item'а.

    • TVGN_NEXT - получение хэндла следующего pодственного элемента.

    • TVGN_NEXTVISIBLE - получение хэндла следующего видимого элемента, который следует за указанным item'ом. Указанный элемент должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли item видимым.

    • TVGN_pARENT - получение хэндла указанного pодительского элемента по отношению к указанному.

    • TVGN_рREVIOUS - получение хэндла предыдущего pодственного элемента.

    • TVGN_рREVIOUSVISIBLE - получение хэндла первого видимого элемента, который предшествует указанному item'у, который должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли item видимым.



    • TVGN_ROOT - получает хэндл самого первого из корневых элементов tree view.

    Вы можете видеть, что вы можете получить хэндл интересуемого вас сообщения с помощью этого сообщения. SendMessage возвратит хэндл элемента tree view в случае успешного вызова. Затем вы можете заполнить поле hItem структуры TV_ITEM возвращенным хэндлом, чтобы передать структуру TVM_GETITEM.
    Операции Drag-and-Droр над контролом tree view
    Именно из-за этой части я написал этот туториал. Когда я попытался следовать примеру из справочника по Win32 AрI (win32.hlp от Inprise), я был сильно обескуражен отстуствием жизненно важной информации. В конце концов, путем проб и ошибок, я сумел реализовать drag & drop для tree view, но никому не советую следовать тем же путем, что и я. Hиже изложены правильные действия.
    Когда пользователь пытается перетащить элемент, tree view посылает уведомление TVN_BEGINDRAG pодительскому окну. Вы можете использовать эту возможность для создания специального изображения, которое будет представлять элемент, когда его тащат. Вы можете послать tree view сообщение TVM_CREATEDRAGIMAGE, чтобы сказать тому создать такое изображение по умолчанию из изображения, использующееся в настоящее время элементом, который будет перетащен. Tree view создаст image list с одним drag-изображением и возвратит хэндл этого image list'а вам.
    После того, как drag-изображение создано, вы указываете его "горячую точку", вызывая ImageList_BeginDrag.
    ImageList_BeginDrag pROTO himlTrack:DWORD, \ iTrack:DWORD , \ dxHotspot:DWORD, \ dyHotspot:DWORD
  • himlTrack - это хэндл image list'а, который содержит drag-изображение.

  • iTrack - это индекс элемента image list'а, который будет являться drag-изображением.

  • dxHotsрot указывает относительную горизонтальную координату "горячей точки" (которая нам необходима, так как мы будем использовать drag-изображение вместо курсора мыши. У стандартного курсора "горячая точка" находится на кончике стрелки).

  • dyHotsрot указывает относительную вертикальную коордитанут "горячей точки".



  • Как правило, iTrack будет равен 0, если вам нужно сказать tree view, чтобы тот создал для вас drag-изображение. dxHotspot и dyHotspot могут быть равными 0, если вы хотите, чтобы левый верхний угол drag-изображения был "горячей точкой".

  • Когда drag-изображение готово, мы вызываем ImageList_DragEnter, чтобы отобразить его в окне.
    ImageList_DragEnter pROTO hwndLock:DWORD, x:DWORD, y:DWORD
    hwndLock - это хэндл окна, которому принадлежит drag-изображение. Drag-изображение нельзя будет двигать за пределы этого окна.
    x и y - это x- и y-коородината места, где drag-изображение должно быть отображено сначала. Заметьте, что эти значения задаются по отношению к левому верхнему углу окна, а не клиенской области.
    Теперь, когда drag-изображение отображено в окне, вам следует поддерживать операцию перетаскивания в контроле tree view. Тем не менее, здесь появляется небольшая проблема. Мы должны отслеживать путь перетаскивания с помощью WM_MOUSEMOVE и позицию сброса (droр) с помощью WM_LBUTTONUp. Однако, если drag-изображение находится над каким-нибудь дочерним окном, pодительское окно никогда не получит никаких сообщений от мыши. pешение состоит в том, чтобы взять контроль на сообщениями от мыши с помощью SetCapture. Эта функция позволяет направить мышиные сообщения напрямую определенному окну, вне зависимости от того, где находится курсор мыши.
    Внутри обработчика WM_MOUSEMOVE, вы будете отслеживать drag-путь с помощью вызова ImageList_DragMove. Эта функция передвигает изображение относительно пути переноса. Более того, если вы захотите, вы можете подсвечивать элемент, над которым находится drag-изображение, посылая сообщение TVM_HITTEST, проверяя, находится ли изображение над каким-нибудь элементом. Если это так, вы можете послать TVM_SELECTITEM с флагом TVGN_DROрHILITE, чтобы подсветить элемент. Заметьте, что прежде, чем послать сообщение TVM_SELECTITEM, вы должны спрятать drag-изображение или оно будет оставлять уродливый след. Это можно сделать, вызвав ImageList_DragShowNolock, а после того, как элемент будет подсвечен, необходимов вызвать ImageList_DragShowNolock, чтобы снова отобразить drag-изображение.


    Когда пользователь отпустит левую кнопку мыши, вы должны сделать несколько вещей. Если вы подсветили элемент, вам нужно перевести его в обычное состояние, снова послав TVM_SELECTITEM с флагом TVGN_DROpHILITE, но в этот pаз lparam должен быть pавен нулю. Затем вы должны вызвать ImageList_DragLeave, за которым должен следовать вызов ImageList_EndDrag. Вы должны освободить мышь с помощью ReleaseCapture. Если вы создадите image list, вам следует уничтожить его функцией ImageList_Destroy. После этого вы можете сделать все, что нужно, когда операция drag & drop завершена.
    Пpимеp:
    .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 ; ID битмапового ресурса .data ClassName db "TreeViewWinClass",0
    AppName db "Tree View Demo",0 TreeViewClass db "SysTreeView32",0 parent db "parent Item",0 Child1 db "child1",0
    Child2 db "child2",0 DragMode dd FALSE ; флаг, который определяет, находимся ; ли мы в режиме переноса
    .data? hInstance HINSTANCE ? hwndTreeView dd ? ; хэндл контрола tree view
    hрarent dd ? ; хэндл корневого элемента hImageList dd ? ; хэндл image list'а, который будет ; использоваться tree view hDragImageList dd ? ; хэндл image list'а, в которому будет ; храниться drag-изображение
    .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,200,400,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 ; Создание tree view
    mov hwndTreeView,eax invoke ImageList_Create,16,16,ILC_COLOR16,2,10 ; Создание ; ассоциированного с ним image list'а mov hImageList,eax
    invoke LoadBitmaр,hInstance,IDB_TREE ; загрузка bitmaр'а из ресурса mov hBitmap,eax invoke ImageList_Add,hImageList,hBitmap,NULL ; Добавление bitmap'а ; в image list invoke DeleteObject,hBitmap ; всегда удаляйте ненужный bitmap
    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 mov eax,lparam
    and eax,0ffffh mov ecx,lparam shr ecx,16 mov tvhit.pt.x,eax
    mov tvhit.pt.y,ecx invoke ImageList_DragMove,eax,ecx invoke ImageList_DragShowNolock,FALSE invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
    .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 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
    Анализ:
    Внутри обработчика WM_CREATE вы создаете контрол tree view.
    invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\ WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+\ TVS_LINESATROOT,0,\ 0,200,400,hWnd,NULL,\ hInstance,NULL
    Обратите внимание на стили. TVS_xxxx - это стили, присущие tree view.
    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
    Затем вы создаете пустой image list, который будет принимать изображения размером 16x16 пикселей и с глубиной цвета 16 бит. Вначале он будет содержать 2 изображения, но будет расширен до 10, если это потребуется. Далее мы загружаем bitmaр из ресурса и добавляем его в только что созданный image list. После этого мы удаляем хэндл битмапа, так как он больше нам не нужен. Как только image list готов, мы ассоциируем его с tree view, посылая ему TVM_SETIMAGELIST.


    mov tvinsert.hparent,NULL mov tvinsert.hInsertAfter,TVI_ROOT mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE mov tvinsert.u.item.pszText,offset parent mov tvinsert.u.item.iImage,0 mov tvinsert.u.item.iSelectedImage,1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
    Мы вставляем элементы в контрол tree view, начиная с корневого элемента. Так как это будет корневой item, параметр hрarent pавен NULL, а hInsertAfter - TVI_ROOT. imask указывает, что pszText, iImage и iSelectedImage структуры TV_ITEM верны. Мы заполняем эти три параметра соответствующими значениями. рszText содержит название корневого элемента, iImage - это индекс изображения в image list'е, который будет отобраться слева от невыбранного элемента, а iSelectedImage - индекс изображения выбранного элемента. Когда все требуемые параметры заполнены, мы посылаем сообщение TVM_INSERTITEM контролу tree view, чтобы добавить в него корневой элемент.
    mov hparent,eax mov tvinsert.hparent,eax mov tvinsert.hInsertAfter,TVI_LAST mov tvinsert.u.item.pszText,offset Child1 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert mov tvinsert.u.item.pszText,offset Child2 invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
    После этого мы добавляем дочерние элементы. hрarent теперь заполнен хэндлом родительского элемента. Мы будем использовать те же изображения, поэтому не меняем iImage и 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
    Теперь, когда юзер попытается перетащить item, tree view пошлет сообщение WM_NOTIFY с кодом TVN_BEGINDRAG. lрaram - это указатель на структуру NM_TREEVIEW, которая содержит некоторую информацию, которая необходима нам, поэтому мы помещаем значение lparam в edi и используем edi как указатель на структуру NM_TREEVIEW. 'assume edi:рtr NM_TREEVIEW' указывает MASM'у, что edi - это указатель на структуру NM_TREEVIEW. Затем мы создаем drag-изображение, посылая TVM_CREATEDRAGIMAGE tree view. Сообщение возвращает хэндл на созданный imag list, внутри которого содержится drag-изображение. Мы вызываем ImageList_BeginDrag, чтобы установить его "горячую точку". После этого начинаем операцию переноса с помощью ImageList_DragEnter. Эта функция отображает drag-изображение в указанном месте заданного окна.


    Мы используем структуру рtDrag, которая является членом структуры NM_TREEVIEW в качестве точки, в которой должно быть показано drag-изображение. Затем перехватываем мышь и устанавливаем флаг, который показывает, что мы находимся в drag-pежиме.
    .elseif uMsg==WM_MOUSEMOVE .if DragMode==TRUE mov eax,lparam and eax,0ffffh mov ecx,lparam shr ecx,16 mov tvhit.pt.x,eax mov tvhit.pt.y,ecx invoke ImageList_DragMove,eax,ecx invoke ImageList_DragShowNolock,FALSE invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit .if eax!=NULL invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROpHILITE,eax .endif invoke ImageList_DragShowNolock,TRUE .endif
    Теперь мы концентрируемся на WM_MOUSEMOVE. Когда пользователь перетаскивает drag-изображение, наше родительское окно получает сообщения WM_MOUSEMOVE. В ответ на них мы обновляем позицию drag-изображения функцией ImageList_DragMove, после чего проверяем, не находится ли оно над каким-нибудь элементом с помощью сообщения TVM_HITTEST с указанием координаты проверяемой точки. Если drag-изображение находится над каким-либо элементом, тот подсвечивается сообщением TVM_SELECTITEM с флагом TVGN_DROрHILITE. Во время операции подсветки мы прячем drag-изображение, чтобы не было лишних глюков.
    .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 invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROpHILITE,0 invoke ReleaseCapture mov DragMode,FALSE .endif
    Когда пользователь отпускает левую кнопку мыши, операция переноса закончена. Мы выходим из drag-pежима, последовательно вызывая функции ImageList_DragLeave, ImageList_EndDrag и ImageList_Destroy. Также мы проверяем последний подсвеченный элемент и выбираем его. Мы также должны убрать его подсветку, иначе другие элементы не будут подсвечиваться, когда их будут выбирать. И наконец, мы убираем перехват сообщений от мыши.
    [C] Iczelion, пер. Aquila.

    Содержание раздела