Search code examples
delphivirtualtreeviewtvirtualstringtree

How to drag and drop nodes from TVirtualStringTree into a VCL control with DragType dtOLE?


Is it possible to drag and drop nodes from TVirtualStringTree into a VCL control when the DragType is set to dtOLE (not dtVCL)

I have a situation where I need to be able to drag nodes from one VT to another VT (dtOLE works fine), but at the same time, while dragging, I need an option to be able to drop the nodes into a TListBox (or other VCL control).

I tried setting DragAcceptFiles() API for the TListBox handle, but it had no effect.

How can it be done?


Solution

  • There is no DragAcceptFiles property on TListBox. If you mean that you are calling the Win32 API DragAcceptFiles() function on the TListBox.Handle window, that would only work if:

    1. you manually subclass the TListBox.WindowProc property to handle the WM_DROPFILES window message.

    2. TVirtualStringTree provides the CF_HDROP format during OLE dragging.

    2 is not true, though. CF_HDROP is meant only for dragging filesystem paths, which is not what TVirtualStringTree drags. So using DragAcceptFiles() is out.

    TVirtualStringTree (and other TBaseVirtualTree descendants) uses custom data formats during OLE dragging (the interface section of the VirtualTrees.pas unit declares these format IDs, so you do not need to register them manually in your own code):

    • CF_VIRTUALTREE contains an IStream or HGLOBAL holding a serialized form of the selected tree nodes that are being dragged. The serialization is comprised of a series of data chunks describing each node. You will have to refer to the implementation of the VirtualTree.pas unit to decipher this format (I'm not going to do it here).

    • CF_VTREFERENCE contains an HGLOBAL holding a TVTReference record (which is also declared in the interface section of the VirtualTrees.pas unit) containing a pointer to the actual TBaseVirtualTree object that is being dragged from and the ID of the process that the tree belongs to.

    So, in order for you to be able to drop tree nodes onto the TListBox, you will have to do the following:

    1. write a class that implements the IDropTarget interface (or use a pre-existing implementation, such as from Anders Melander's Drag&Drop suite).

    2. register that class with the TListBox.Handle window using RegisterDragDrop().

    3. in your IDropTarget implementation, you can query the provided IDataObject for the CF_VIRTUALTREE and CF_VTREFERENCE formats. If successful, your Drop() method will have access to the tree node data that is being dragged and can copy it into the TListBox as needed.

    Assuming your TListBox exists in the same process as the TVirtualStringTree being dragged from, I would suggest focusing on just CF_VTREFERENCE, since it is a very small and simple format (see the implementation of the TBaseVirtualTree.GetTreeFromDataObject() method), and parsing the CF_VIRTUALTREE data would be overkill (see the implementation of the TBaseVirtualTree.ProcessOLEData() method) when you can just enumerate through the source TVirtualStringTree directly instead.