Tutorial 19: Control Tree View

En este tutorial aprenderemos a usar el control Tree View (vista de árbol). Es más, también aprenderemos cómo arrastrar objetos y ponerlos en el control Tree View; además veremos cómo usar una lista de imágenes en este control. Puedes bajar el ejemplo aquí.

Teoría:

Un control Tree View es un tipo especial de ventana que representa objetos en orden jerárquico. Un ejemplo es el panel izquierdo del Explorador de Windows. Se puede personalizar este control para mostrar relaciones entre los objetos.

Puedes crear un control Tree View llamando CreateWindowEx y pasando la cadena "SysTreeView32" como el nombre de la clase o puedes incorporarlo en una caja de diálogo. No hay que olvidar poner una llamada a InitCommonControls en el código fuente. Hay varios estilos específicos al control Tree View. Estos tres son los más usados.

TVS_HASBUTTONS == Despliega botones más (+) y menos (-) al lado de los elementos [items] padres. El usuario hace click con los botones del ratón sobre los elementos para expandir o contraer una lista de elementos hijos subordinada a un elemento padre. Para incluir botones con elementos en la raiz del tree view, debe especificarse el estilo TVS_LINESATROOT.
TVS_HASLINES == Usa líneas para mostrar la jerarquía de elementos.
TVS_LINESATROOT == Usa líneas para enlazar elementos en la raiz del control tree-view. Este valor es ignorado ignored si TVS_HASLINES tampoco es especificado.

El control Tree View, como otros controles comunes, se comunica con su ventana padre a través de mensajes. La ventana padre le puede enviar varios mensajes y el control Tree View puede enviar mensajes de "notificación" a su ventana padre. En este proceso, el control Tree View no difiere de las otras ventanas: cuando algo interesante ocurre en él, envía un mensaje WM_NOTIFY con información a la ventana padre.

WM_NOTIFY
wParam == ID del control, nada garantiza que este valor sea el único, así que nosotros
                    no lo usamos.
Es mejor usar hwndFrom o el miembro IDFrom de la estructura
                    NMHDR a la que apunta lParam

lParam ==   Puntero a la estructura NMHDR. Algunos controles pueden pasar un puntero
                    más largo pero debe tener una estructura NMHDR como primer miembro.

                    Es decir, cuando tenemos lParam, podemos estar seguros que apunta a una
                    estructura NMHDR.

Ahora examinaremos la estructura NMHDR.

NMHDR struct DWORD
    hwndFrom    DWORD ?
    idFrom          DWORD ?
    code              DWORD ?
NMHDR ends

hwndFrom es el manejador de la ventana que envía este mensaje WM_NOTIFY.

idFrom es el ID del control que envía este mensaje.

code es el mensaje actual que el control quiere enviar a su ventana padre.

Las notificaciones del control Tree view tienen TVN_ al comienzo, como TVM_CREATEDRAGIMAGE. El control tree view envía TVN_xxxx en el miembro código de NMHDR. La ventana padre puede enviar TVM_xxxx para controlarlo.

 

Agregando elementos a un control list view

Después de crear un control Tree View, podemos agregarle elementos. Puedes hacer esto enviándole TVM_INSERTITEM.

TVM_INSERTITEM
wParam = 0;
lParam = pointer to a TV_INSERTSTRUCT;

A estas alturas, bebes conocer cierta terminología sobre la relación entre los elementos en el control Tree View. Un elemento puede ser al mismo tiempo padre, hijo, o ambos. Un elemento padre es el que tiene algún(os) otro(s) subelemento(s) asociado(s) con él. Al mismo tiempo, el el elemento padre puede ser un hijo de algún otro elemento. Un elemento sin un padre se llama elemento raíz. Puede haber muchos elementos raíz en un control tree view. Ahora examinemos la estructura TV_INSERTSTRUCT

TV_INSERTSTRUCT STRUCT DWORD
  hParent       DWORD      ?
  hInsertAfter  DWORD ?
                      ITEMTYPE < >
TV_INSERTSTRUCT ENDS

hParent = Manejador del elemento padre. Si este miembro es el valor TVI_ROOT o NULL, el elemento es insertado en la raiz del control tree-view.
hInsertAfter = Manejador del elemento después del cual el nuevo elemento va a ser insertado o uno de los siguientes valores:

ITEMTYPE UNION
        itemex TVITEMEX < >
        item TVITEM < >
ITEMTYPE ENDS

Aquí usaremos solamente 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

Esta estructura es empleada para enviar y recibir información sobre un elemento del tree view, dependiendo de los mensajes. Por ejemplo, con TVM_INSERTITEM, es usada para especificar el atributo del elemento a ser insertado dentro del control tree view. Con TVM_GETITEM, será llenado con información sobre el elemento seleccionado.

imask es usado para especificar cual(es) miembro(s) de la estructura TV_ITEM es (son) valido(s). Por ejemplo, si el valor en imask es TVIF_TEXT, significa sólo que el miembro pszText es válido. Puede cpmbinarse con algunas banderas.
hItem es el manejador del elemento en el control tree view. Cada elemento tiene su propio manejador, lo mismo que una ventana tiene su manejador. Si se quiere hacer algo con un elemento, debe seleccionarse ese elemento por su manejador.
pszText es el puntero a una cadena terminada en cero que indica el nivel de un elemento dentro de un control tree view.
cchTextMax es usado sólo cuando se quiere regresar el nivel de un elemento dentro de un control tree view. Como el prgramador debe suministrar el puntero al bufer en pszText, Windows debe saber el tamaño del buffer proveído. Hay que suministrar el tamaño del buffer en este miembro.
iImage y iSelectedImage refieren al índice dentro de una lista de imágenes que contienen las imágenes a ser mostradas cuando el elemento no es seleccionado (iImage) y cuando es seleccionado (iSelectedImage). Si corres el Explorador de Windows, verás que en el panel izquierdo unas imágenes que representan las carpetas; esas imágenes están especificadas por estos dos miembros.

Con el fin de insertar un elemento dentro del control tree view, al menos deben llenarse los miembros hParent, hInsertAfter, así como imask and pszText.

 

Agregando imágenes a un control tree view

Si quieres poner una imagen a la izquierda de la etiqueta de un elemento del control tree view, se debe crear una lista de imágenes y asociarla con el control tree view. Se puede crear una lista de imágenes llamando a ImageList_Create.

ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, \
                                            cInitial:DWORD,  cGrow:DWORD

Si esta función tiene éxito, regresa el manejador a una lista de imágenes vacía.

cx == ancho de cada imagen en esta lista, en pixeles.
cy == altura de cada imagen en esta lista, en pixeles. Todas las imágenes en una lista deben ser iguales en tamaño. Si se especifica un gran bitmap, Windows usará cx y cy para *cortarla* en varios pedazos. Así que las imágenes propias deben prepararse como una secuencia de "pinturas" del mismo tamaño.
flags == especifica el tipo de imágnes en esta lista: si son de color o monócromos y su profundidad de color. Consulta la refencia de la API de Win32 para mayor información.
cInitial == El número de imágenes que esta lista contendrá inicialmente. Windows usará esta información para localizar memoria para las imágenes.
cGrow == Cantidad de imágenes a las que la lista de imágenes puede expandirse cuando el sistema necesita cambiar el tamaño de la lista para hacer disponibles más imágnes. Este parámetro representa el número de imágenes nuevas que puede tener una lista de imágenes que ha cambiado de tamaño.

¡Una lista de imágenes no es una ventana! Es sólo un depósito de imágenes que ha de ser usado por otras ventanas. Después de crear una lista de imágens, se deben agregar imágenes llamando a ImageList_Add

ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD

Esta función regresa -1 si no tiene éxito.
himl == manejador de la lista de imágenes a la cual quieres agregar imágenes. Es el valor regresado por una llamada satisfactoria a ImageList_Create
hbmImage == manejador del bitmap a ser agregado a la lista de imágenes. Usualmente almacenas el bitmap en el recurso y lo cargas en la llamada a LoadBitmap. Nota que no tienes que especificar los parámetros pasados con el número de imágenes contenidas en este bitmap porque esta información es inferida a partir de los parámetros cx y cy pasados con la llamada a ImageList_Create.
hbmMask == Manejador [handle] al bitmap que contiene la máscara. Si no se usa ninguna máscara con la lista de imágenes, se ignora este parámetro.

Normalmente, agregaremos sólo dos imágenes a la lista de imágenes para usar con el control treeview: una usada cuando el elemento del control tree view no está seleccionado, y otra para cuando el elemento es seleccionado.

Cuando la lista de imágenes ya está lista, la asociamos con el control treeview enviando TVM_SETIMAGELIST al control.

TVM_SETIMAGELIST
wParam = tipo de lista de imagen a establecer. Hay dos posibilidades:

lParam = Manejador de la lista de imágenes

 

Recuperar información sobre el elemento del treeview

Puedes recuperar información sobre un elemento de un control tree view enviando el mensaje TVM_GETITEM.

TVM_GETITEM
wParam = 0
lParam = puntero a la estructura TV_ITEM structure a ser llenada con la información

antes de enviar este mensaje, debe llenarse el miembro imask con la(s) bandera(s) que especifica(n) cual(es) miembro(s) de TV_ITEM queires que llene Windows. Y lo más importante, debes llenar hItem con el manejador al elemento del cual deseas obtener información. Pero esto tiene un problema: ¿Cómo puedes conocer el manejador del elemento del cual deseas recuperar información? ¿Habrá que almacenar todos los manejadores del control Tree View? La respuesta es simple: no tienes necesidad de hacerlo. Puedes enviar el mensaje TVM_GETNEXTITEM al control tree view para recuperar el manejador al elemento del tree view elemento que tiene el (o los) que tú especificaste. Por ejemplo, puedes solicitar el manejador del primer elemento hijo, del elemento raiz, del elemento seleccionado, etc.

TVM_GETNEXTITEM
wParam = bandera
lParam = manejador a un elemento tree view (sólo necesario para algunos valores de la bandera)

El valor en wParam es tan importante que prefiero presentar abajo todas las banderas:

Como puede verse, si se quiere recuperar el manejador a un elemento del control tree view este mensaje resulta de gran interés. SendMessage regresa el manejador al elemento del tree view si tiene éxito. Se puede llenar el valor regresado dentro del miembro hItem de TV_ITEM a ser usado con el mensaje TVM_GETITEM.

 

Operación Drag and Drop [arrastrar y soltar] en un control tree view

Esta parte es la razón por la cual decidí escribir este tutorial. Cuando intenté seguir el ejemplo en la referencia de la api win32 (el win32.hlp desde InPrise), quedé muy frustrado porque carecía de la información vital sobre este punto. Por error y prueba, finalmente esbozé como implementar drag & drop [arrastrar y soltar] en un control tree view y no quiero que nadie pase por lo mismo que yo.

Abajo están los pasos para implementar operaciones drag & drop en un control tree view.

  1. Cuando el usuario trata de arrastrar [drag] un elemento, el control tree view control envía la notificación TVN_BEGINDRAG a la ventana padre. Puedes aprovechar esta oportunidad para crear una imagen de arrastre [drag image] que sea la imagen a ser usada para representar el elemento mientras que está siendo arrastrado. Puedes enviar TVM_CREATEDRAGIMAGE al control tree view para decirle que cree una imagen de arrastre por defecto a partir de la imagen que está siendo usada por el elemento que será arrastrado. El control tree view creará una lista de imágenes con sólo una imagen de arratre y te regresará el manejador de la lista de imágenes.
  2. Después de que la imagen de arratre es creada, especificas el "punto caliente" [hotspot] de la imagen de arratre llamando a ImageList_BeginDrag.
  3. ImageList_BeginDrag PROTO himlTrack:DWORD,  \
                                                        iTrack:DWORD , \
                                                        dxHotspot:DWORD, \
                                                        dyHotspot:DWORD
    himlTrack es el manejador a la lista de imágenes que contiene la imagen de arratre.
    iTrack es el índice a la lista de imágenes que especifica la imagen de arrastre
    dxHotspot especifica la distancia relativa al "punto caliente" [hotspot] en el plano horizontal en la imagen de arrastre ya que esta imagen será usada en el lugar del cursor del ratón, así que especificamos usar cual parte de la imagen es el "punto caliente" .
    dyHotspot especifica la distancia relativa del "punto caliente" en el plano vertical.
    Normalmente, iTrack debería ser 0 si le dices al control tree view que cree la imagen de arrastre para tí, y dxHotspot y dyHotspot pueden ser 0 si quieres que la esquina izquierda superior de la imagen de arrastre sea el hotspot.

  4. Cuando la imagen de arrastre esté lista para ser desplazada, llamamos a ImageList_DragEnter para desplegar la imagen de arrastre en la ventana.
  5. ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
    hwndLock es el manejador [handle] de la imagen propietaria de la imagen de arrastre. La imagen de arrastre no será capaz de moverse fuera de esa ventana.
    x e y son las coordenadas x- e y- del lugar donde la imagen de arrastre debería estar desplegada inicialmente. Nota que estos valores son relativos a la esquina izquierda superior de la ventana, no del área cliente.

  6. Ahora que la imagen de arrastre está desplegada en la ventana, tendrás que soportar la operación de arrastre en el control tree view. Sin embargo, no hay mucho problema con esto. Tenemos que monitorear el paso del arrastre con WM_MOUSEMOVE y la localización para la operación de soltar [drop] con mensajes WM_LBUTTONUP. Sin embargo, si la imagen de arrastre está sobre una de las ventanas hijas, la ventana padre recibirá cualquier mensaje del ratón. La solución es capturar la entrada del ratón con SetCapture. Usando esta llamada, los mensajes del ratón serán enviados a la ventana especificada independientemente de donde esté el cursor del ratón.
  7. Dentro del manejador de WM_MOUSEMOVE, actualizarás el paso del arrastre con una llamada a ImageList_DragMove. Esta función mueve la imagen que está siendo arrastrada durante una operación de arrastar-y-soltar [drag-and-drop]. Además, si lo deseas, puedes "iluminar" [hilite] el elemento sobre el cual está la imagen de arrastre enviando TVM_HITTEST para chequear si la imagen de arrastre está sobre algún elemento. Si lo está, puedes enviar TVM_SELECTITEM con la bandera TVGN_DROPHILITE para "iluminar" ese elemento. Nota que antes de enviar el mensaje TVM_SELECTITEM, debes esconder primero la imagen de arrastre sino tu imagen de arrastre dejará trazas horribles. Puedes esconder la imagen de arrastre enviando ImageList_DragShowNolock y, después que la operación de iluminación [hilite] haya finalizado, llamar de nuevo a ImageList_DragShowNolock para mostrar la imagen de arrastre.
  8. Cuando el ususario suelta el botón izquierdo del ratón, debes hacer varias cosas. Si tú iluminas [hilite] un elemento, debes quitarle la iluminación [un-hilite it] enviando TVM_SELECTITEM con la bandera TVGN_DROPHILITE de nuevo, pero esta vez, lParam DEBE ser cero. Si tú no le quitas la iluiminación el elemento, obtendrás un efecto extraño: cuando selecciones algún otro elemento, ese elemento será encerrado por un rectángulo pero la iluminación estará sobre el último elemento iluminado. Luego, debes llamar a ImageList_DragLeave seguido por ImageList_EndDrag. Debes liberar al ratón llamando a ReleaseCapture. Si creas una lista de imágenes, debes destruirla llamando a ImageList_Destroy. Después de eso, puedes ir a lo que que quieres que haga tu programa cuando la operación arrastrar y soltar [drag & drop] se haya completado.

Código de demostración:

.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 del recurso bitmap
.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                ; una bandera para determinar si estamos en modo de arrastre

.data?
hInstance  HINSTANCE ?
hwndTreeView dd ?            ; manjeador del control tree view
hParent  dd ?                      ; manejador del elemento raíz del control tree view
hImageList dd ?                  ; manejador de la lista de imágenes usadas en el control tree view
hDragImageList  dd ?          ; manejador de la lista de imágenes usada para almacenar la imagen de arrastre

.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,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            ; Create the tree view control
        mov hwndTreeView,eax
        invoke ImageList_Create,16,16,ILC_COLOR16,2,10    ; crear la lista de imágenes asociada
        mov hImageList,eax
        invoke LoadBitmap,hInstance,IDB_TREE        ; cargar el bitmap desde el recurso
        mov hBitmap,eax
        invoke ImageList_Add,hImageList,hBitmap,NULL; Agregar el bitmap a la lista de imágenes
        invoke DeleteObject,hBitmap    ; borrar siempre el recurso 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

Análisis:

Dentro del manejador WM_CREATE, se crea el control 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

Notar los estilos. TVS_xxxx son los estilos específicos del control 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

Después, se crea una lista de imágenes vacía para que acepte imágenes de 16x16 pixeles en tamaño, 16-bit de color e inicialmente, contendrá 2 imágenes pero puede ser expandido hasta 10 si se necesita. Luego cargamos el bitmap desde el recurso y lo agregamos a la lista de imágenes. Después de eso, borramos el manejador del bitmap ya que no será usado más. Cuando la lista de imágenes esté toda establecida, la asociamos con el control tree view control enviando TVM_SETIMAGELIST al control tree view.

        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

Insertamos elementos en el control tree view y empezamos del elemento de la raíz. Puesto que será el elemento raíz, el miembro del hParent es NULL y hInsertAfter es TVI_ROOT. El miembro imask especifica a ese pszText, iImage y miembros del iSelectedImage de la estructura de TV_ITEM es válido. Llenamos a esos tres miembros de valores apropiados. pszText contiene la etiqueta del elemento raíz, iImage es el índice a la imagen en la lista de la imagen que se desplegará a la izquierda del elemento del no seleccionado, y iSelectedImage es el índice a la imagen en la lista de la imagen que se desplegará cuando el elemento se selecciona. Cuando todos los miembros apropiados estén llenos, enviamos el mensaje TVM_INSERTITEM al control tree view para agregar el elemento raíz a él.

        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

Después de agregar el elemento [item] raiz, podemos enganchar los elementos a la ventana hija. El miembro hParent es llenado ahora con el manejador del elemento padre. Y usaremos imágenes idénticas en la lista de imágenes, así que no cambiemos los miembros iImage e 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

Ahora cuando el usuario trata de arrastrar [drag] un elemento, el control tree view control envía un mensaje WM_NOTIFY con el código TVN_BEGINDRAG. lParam es el puntero a una estructura NM_TREEVIEW que contiene algunas piezas de información que necesitamos para poner su valor dentro de edi y usar edi como el puntero a la estructura NM_TREEVIEW. assume edi:ptr NM_TREEVIEW es una manera de decirle a MASM que trate a edi como el puntero a la estructura NM_TREEVIEW. Luego creamos una imagen de arrastre enviando TVM_CREATEDRAGIMAGE al control tree view. Regresa el manejador de la lista de imágenes nuevamente creada con una imagen drag image dentro. Llamamos a ImageList_BeginDrag para establecer el "punto caliente" en la imagen de arrastre. Luego introducimos la operación de arrastre llamando a ImageList_DragEnter. Esta función emplea la imagen de arrastre en el lugar especificado de la ventana. Usamos la estructura ptDrag que es un miembro de la estructura NM_TREEVIEW como el punto donde la imagen de arratre debería ser inicializada. Después de eso capturamos la entrada del ratón y ponemos la bandera para indicar que ahora introducimos el modo de arrastre.

   .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

Ahora nos concentramos en WM_MOUSEMOVE. Cuando el usuario arrastra [drags] la imagen de arrastre [the drag image along], nuestra ventana padre recibe el mesaje WM_MOUSEMOVE. En respuesta a estos mensajes, actualizamos la posición de la imagen de arrastre con ImageList_DragMove. Después de eso, chequeamos si la drag image está sobre algún elemento. Hacemos eso enviando el mensaje TVM_HITTEST al control tree view con un punto para que él lo chequee. Si la imagen de arrastre está sobre el elemento, iluminamos [hilite] ese elemento enviando el mensaje TVM_SELECTITEM con la bandera TVGN_DROPHILITE al control tree view. Durante la operación de iluminación, escondemos la imagen de arrastre de manera que no deje desagradables manchas sobre el control tree view.

    .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

Cuando el usuario suelta el botón izquierdo del ratón, llega a su final la operación de arrastre. Abandonamos el modo de arrastre llamando a ImageList_DragLeave, seguido por ImageList_EndDrag y ImageList_Destroy. Para hacer que los elementos del cotrol tree view se vean bien, también chequeamos el último elemento iluminado [hilited], y lo seleccionamos. También debemos quitar la iluminación [un-hilite], sino los otros elementos no serán iluminaods cuando ellos sean seleccionados. Y finalmente, liberamos la captura del ratón.


Index

Next

[Iczelion's Win32 Assembly HomePage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por   n u M I T_o r

www.000webhost.com