At the window level, PowerBuilder provides functions and properties for dragging controls onto other controls. Within the TreeView, you can also let the user drag items onto other items. Users might drag items to sort them, move them to another branch, or put child items under a parent.
When you implement drag and drop as a way to move items, you decide whether the dragged item becomes a sibling or child of the target, whether the dragged item is moved or copied, and whether its children get moved with it.
There are several properties and events that you need to coordinate to implement drag and drop for items, as shown in Table 8-4.
Property or event |
Setting or purpose |
---|---|
DragAuto property |
TRUE or FALSE If FALSE, you must call the Drag function in the BeginDrag event. |
DisableDragDrop property |
FALSE |
DragIcon property |
An appropriate icon or None!, which means the user drags an image of the item |
BeginDrag event |
Script for saving the handle of the dragged item and optionally preventing particular items from being dragged |
DragWithin event |
Script for highlighting drop targets |
DragDrop event |
Script for implementing the result of the drag operation |
The key to a successful drag-and-drop implementation is in the details. This section illustrates one way of moving items. In the example, the dragged item becomes a sibling of the drop target, inserted after it. All children of the item are moved with it and the original item is deleted.
A function called recursively moves the children, regardless of the number of levels. To prevent an endless loop, an item cannot become a child of itself. This means a drop target that is a child of the dragged item is not allowed.
BeginDrag event The script saves the handle of the dragged item in an instance variable:
ll_dragged_tvi_handle = handle
If you want to prevent some items from being dragged—such as items at a particular level—that code goes here too:
TreeViewItem tvi This.GetItem(handle, tvi) IF tvi.Level = 3 THEN This.Drag(Cancel!)
DragWithin event The script highlights the item under the cursor so the user can see each potential drop target. If only some items are drop targets, your script should check an item’s characteristics before highlighting it. In this example, you could check whether an item is a parent of the dragged item and highlight it only if it is not:
TreeViewItem tvi This.GetItem(handle, tvi) tvi.DropHighlighted = TRUE This.SetItem(handle, tvi)
DragDrop event This script does all the work. It checks whether the item can be inserted at the selected location and inserts the dragged item in its new position—a sibling after the drop target. Then it calls a function that moves the children of the dragged item too:
TreeViewItem tvi_src, tvi_child long h_parent, h_gparent, h_moved, h_child integer rtn // Get TreeViewItem for dragged item This.GetItem(ll_dragged_tvi_handle, tvi_src) // Don't allow moving an item into its own branch, // that is, a child of itself h_gparent = This.FindItem(ParentTreeItem!, handle) DO WHILE h_gparent <> -1 IF h_gparent = ll_dragged_tvi_handle THEN MessageBox("No Drag", & "Can't make an item a child of itself.") RETURN 0 END IF h_gparent=This.FindItem(ParentTreeItem!, h_gparent) LOOP // Get item parent for inserting h_parent = This.FindItem(ParentTreeItem!, handle) // Use 0 if no parent because target is at level 1 IF h_parent = -1 THEN h_parent = 0 // Insert item after drop target h_moved = This.InsertItem(h_parent, handle, tvi_src) IF h_moved = -1 THEN MessageBox("No Dragging", "Could not move item.") RETURN 0 ELSE // Args: old parent, new parent rtn = uf_movechildren(ll_dragged_tvi_handle, & h_moved) / If all children are successfully moved, // delete original item IF rtn = 0 THEN This.DeleteItem(ll_dragged_tvi_handle) END IF END IF
The DragDrop event script shown above calls the function uf_movechildren. The function calls itself recursively so that all the levels of children below the dragged item are moved:
// Function: uf_movechildren
// Arguments:
// oldparent - Handle of item whose children are
// being moved. Initially, the dragged item in its
// original position
//
// newparent - Handle of item to whom children are
// being moved. Initially, the dragged item in its
// new position.
long h_child, h_movedchild
TreeViewItem tvi
// Return -1 if any Insert action fails
// Are there any children?
h_child = tv_2.FindItem(ChildTreeItem!, oldparent)
IF h_child <> -1 THEN
tv_2.GetItem(h_child, tvi)
h_movedchild = tv_2.InsertItemLast(newparent, tvi)
IF h_movedchild = -1 THEN RETURN -1
// Move the children of the child that was found
uf_movechildren(h_child, h_movedchild)
// Check for more children at the original level
h_child = tv_2.FindItem(NextTreeItem!, h_child)
DO WHILE h_child <> -1
tv_2.GetItem(h_child, tvi)
h_movedchild= tv_2.InsertItemLast(newparent,tvi)
IF h_movedchild = -1 THEN RETURN -1
uf_movechildren(h_child, h_movedchild)
// Any more children at original level?
h_child = tv_2.FindItem(NextTreeItem!, h_child)
LOOP
END IF
RETURN 0 // Success, all children moved