Tree Drag and Drop Part 2

In Tree Drag and Drop Part 1 I covered how to drag items within the Tree control and from outside of the Tree control onto the control. In this article I cover how to drag items from the Tree.

Run Example

To drag items from a Tree you have to set the Tree’s dropEnabled property to false and its dragEnabled property to true:

<mx:Tree x="34" y="81" width="181" height="189"
    
dataProvider="{treeData.node}"
    
labelField="@label"
    
dropEnabled="false"
    
dragEnabled="true"

    
dragMoveEnabled="false"
/>

For this example, suppose the data in the tree represents US states, cities in those states, and restaurants within those cities. Further, suppose the target of the drop is a DataGrid which is defined like this:

<mx:DataGrid x="291" y="81" height="189"
    
dataProvider="{dataGridProvider}"
    
dragEnter="onDragEnter(event)"
    
dragOver="onDragOver(event)"
    
dragDrop="onGridDragDrop(event)"
    
dragExit="onDragExit(event)">
    <mx:columns>
        <mx:DataGridColumn headerText="Label" dataField="label"/>
        <mx:DataGridColumn headerText="Type" dataField="type"/>
    </mx:columns>
</mx:DataGrid>

As with any drag-and-drop operation, the event handlers are the key to making it work successfully.

The dragEnter event (on the DataGrid) determines whether or not the drop will be processed. In this example, only tree nodes which are restaurants will be accepted by the DataGrid. A typical tree node would look like this:

<node label="Lobster Pot" type="restaurant" />

where the @type attribute can be used to determine if the items being dropped are acceptable. The dragEnter event can be defined as:

private function onDragEnter( event:DragEvent ) : void
{
    
if( event.dragInitiator is Tree ) {

        
var ds:DragSource = event.dragSource;
        
if( !ds.hasFormat("treeItems") ) return; // no useful data
        
var items:Array = ds.dataForFormat("treeItems") as Array;
        
for(var i:Number=0; i < items.length; i++) {
            
var item:XML = XML(items[i]);
             if( item.@type != "restaurant" ) return; // not what we want
        
}   
    
}

    
// if the tree passes or the dragInitiator is not a tree, accept the drop
    
DragManager.acceptDragDrop(UIComponent(event.currentTarget));
}

If the control initiating the drag is a Tree control, then the dragSource is examined to see if holds any "treeItems" and if they are all of @type "restaurant". Should the test succeed, the DataGrid (the event.currentTarget) accepts the drop.

The dragOver event handles the feedback which the user is moving the dragProxy:

private function onDragOver( event:DragEvent ) : void
{
    
if( event.dragInitiator is Tree ) {
        
DragManager.showFeedback(DragManager.COPY);
     } else {
        
if (event.ctrlKey)
            
DragManager.showFeedback(DragManager.COPY);
        
else if (event.shiftKey)
            
DragManager.showFeedback(DragManager.LINK);
        
else {
            
DragManager.showFeedback(DragManager.MOVE);
        
}
    
}
}

When the dragInitiator is the Tree, the feedback is always set to COPY. You can of couse, set the feedback to anything you like, but for the sake of the example, the feedback is for a copy operation so the information does not have to be deleted from the Tree.

The dragDrop event on the DataGrid handles the release of the mouse over the DataGrid. The data is examined and then added to the DataGrid.

private function onGridDragDrop( event:DragEvent ) : void
{
    
var ds:DragSource = event.dragSource;
    
var dropTarget:DataGrid = DataGrid(event.currentTarget);
    
var arr:Array;

    
if( ds.hasFormat("items") ) {
        
arr = ds.dataForFormat("items") as Array;
    
} else if( ds.hasFormat("treeItems") ) {
        
arr = ds.dataForFormat("treeItems") as Array;
    
}

    
for(var i:Number=0; i < arr.length; i++) {
        
var node:XML = XML(arr[i]);
        
var item:Object = new Object();
         item.label = node.@label;
        
item.type = node.@type;
        
dataGridProvider.addItem(item);
    
}

    
onDragExit(event);
}

Since it is possible that another control has initiated the drag-drop, the dragSource is examined and the treeitems extracted. Then a loop is created to make new items to add to the DataGrid. Finally, the onDragExit method is called:

private function onDragExit( event:DragEvent ) : void
{
    
var dropTarget:ListBase=ListBase(event.currentTarget);
    
dropTarget.hideDropFeedback(event);
}

This function just makes sure the visual feedback is removed.

Dragging from the Tree is pretty easy – just examine the dragSource for "treeItems" and handle them according to what behavior your application should have. In this example, only"restaurant" type tree nodes are allowed to be dropped onto the DataGrid and, by being handled early in the DragEnter method, eliminates the need to test the value again in later steps.