Drag and Drop from DataGrid or AdvancedDataGrid to Tree

16 December 2009

One of the coolest things in Flex is the ability to create Drag and Drop operations very easily. For List base controls like the List and DataGrid / AdvancedDataGrid, it works out of the box, just set the dragEnabled property of the source and the dropEnabled property of the other and it’s already working. Dragging and Dropping to a Tree however, does not work by simply setting the dropEnabled property. The following example shows you how you can drag items from a DataGrid / AdvancedDataGrid to a Tree.

First let’s create two dataproviders, one for the DataGrid/AdvancedDataGrid and one for the Tree:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Bindable]
private var collection1:ArrayCollection;

[Bindable]
private var collection2:XML = <node name="Root">
                        <node name="Node One">
                            <item name="Item One"/>
                            <item name="Item Two"/>
                        </node>
                        <node name="Node Two">
                            <item name="Item Three"/>
                        </node>
                    </node>;

So the XML typed dataprovider for the Tree already contains data, lets fill the dataprovider for the DataGrid/AdvancedDataGrid in a method that’s called upon initialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private function init():void
{
    var obj:Object;
    collection1 = new ArrayCollection();
    obj = new Object();
    obj.name = "Item Four";
    collection1.addItem(obj);
    obj = new Object();
    obj.name = "Item Five";
    collection1.addItem(obj);
    obj = new Object();
    obj.name = "Item Six";
    collection1.addItem(obj);
    obj = new Object();
    obj.name = "Item Seven";
    collection1.addItem(obj);
    obj = new Object();
    obj.name = "Item Eight";
    collection1.addItem(obj);

    // open all items in Tree control
    this.validateNow();
    tree.expandChildrenOf(collection2, true);
}

Now add the Component we are going to work with, the DataGrid (or AdvancedDataGrid) and the Tree:

DataGrid:

1
2
3
4
5
6
7
8
<mx:DataGrid dragComplete="doDragComplete(event)"
        id="grid" width="50%" height="100%"
        dataProvider="{collection1}" dragEnabled="true"
        dragMoveEnabled="true">
    <mx:columns>
        <mx:DataGridColumn headerText="Name" dataField="name"/>
    </mx:columns>
</mx:DataGrid>

Tree:

1
2
3
4
5
6
7
8
<mx:Tree id="tree" width="50%" height="100%"
                 dataProvider="{collection2}"
                 dropEnabled="true"
                 labelField="@name"
                 dragOver="doDragOver(event)"
                 dragDrop="doDragDrop(event)"
                 dragEnter="doDragEnter(event)"
                 dragExit="doDragExit(event)"/>

Notice the following about the DataGrid:

- The DataGrid has dragEnabled set to true, this will allow the user to drag an item out of the DataGrid.

- The DataGrid has dragMoveEnabled set to true. When dragMoveEnabled is set to true, the Flex framework will automatically remove the dragged items from the source when the drag & drop operation is complete.

- The DataGrid has an event handler on the dragComplete event. The dragComplete event is dispatched by the drag source when your drag & drop operation is finished.

Notice the following about the Tree:

- The Tree has dropEnabled set to true.

- It has an event handler for the dragEnter event, dragEnter is dispatched when a drag operation enters the component (the Tree in this case).

- It has an event handler for the dragOver event. This is needed to tell the Flex framework to stop doing default drag & drop behaviour.

- It has an event handler for the dragDrop event. This is needed to handle the drop, dispatched when the user releases the Mouse cursor.

- It has an event handler for the dragExit event. This is dispatched when the Mouse cursor exits the component (Tree) while in a drag & drop procedure.

Ok now the fun part, implementing the functions. Most of them are simple, add the following four functions to your script area:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private function doDragEnter(event:DragEvent):void
{
    // Cancel default behaviour
    event.preventDefault();
    // Tell the DragManager that the Tree will accent the DragDrop
    DragManager.acceptDragDrop(Tree(event.target));
    // hide the "drop line" that is shown in Tree control
    // when dropping in a Tree
    tree.showDropFeedback(event);
}

private function doDragOver(event:DragEvent):void
{
    // Show the default "drop line" in the Tree control
    tree.showDropFeedback(event);
    // Cancel default behavious
    event.preventDefault();
}

private function doDragExit(event:DragEvent):void
{
    // hide the "drop line" that is shown in Tree control
    // when dropping in a Tree
    tree.hideDropFeedback(event);
}

private function doDragComplete(event:DragEvent):void
{
    // hide the "drop line" that is shown in Tree control
    // when dropping in a Tree
    tree.hideDropFeedback(event);
}

Now we can implement the final function that is going handle the drop for us. Add the following function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private function doDragDrop(event:DragEvent):void
{
    var item:XML;
    // cancel default
    event.preventDefault();
   
    // get the drag format (its always items in our case
    // when you are dragging FROM a Tree its treeItems
    var draggedFormat:String = event.dragSource.formats[0];
   
    // Get the dragged items
    var draggedItems:Array = event.dragSource.dataForFormat(draggedFormat) as Array;
   
    // Calculate the index in the Tree where the items were dropped
    var dropIndex:int = tree.calculateDropIndex(event);
   
    // Set the selected index of the Tree to the dropIndex
    tree.selectedIndex = dropIndex;
   
    // Check if we are dropping on a node
   
    // Add each dragged item to the Tree by apppending it
    // as a child of the selected node in the Tree.
    for each( var object:Object in draggedItems )
    {
        // create item
        item = <item></item>;
        // set the name
        item.@name = object.name;
       
        // use appendChild to add the item.
        // (if selected item is an item then append to parent)
        if( (tree.selectedItem as XML).name() == "node")
        {
            (tree.selectedItem as XML).appendChild(item);
        }
        else{
            (tree.selectedItem as XML).parent().appendChild(item);
        }
    }

   
}

That’s it you’re already done. You’re now able to drag items from Lists, DataGrids and such to a Tree. It’s pretty cool. Check out working example below follwed by the complete source code at the bottom of this post.

NOTE: This example is done in Flash Builder Beta and uses Flex 4. If you need this for Flex 3, just leave a comment and I will add the source for Flex 3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark" initialize="init()"
               xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" viewSourceURL="srcview/index.html">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.controls.DataGrid;
            import mx.events.DragEvent;
            import mx.managers.DragManager;
           
            [Bindable]
            private var collection1:ArrayCollection;
           
            [Bindable]
            private var collection2:XML = <node name="Root">
                                            <node name="Node One">
                                                <item name="Item One"/>
                                                <item name="Item Two"/>
                                            </node>
                                            <node name="Node Two">
                                                <item name="Item Three"/>
                                            </node>
                                        </node>;
           
            private function init():void
            {
                var obj:Object;
                collection1 = new ArrayCollection();
                obj = new Object();
                obj.name = "Item Four";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Five";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Six";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Seven";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Eight";
                collection1.addItem(obj);
               
                // open all items in Tree control
                this.validateNow();
                tree.expandChildrenOf(collection2, true);
               
            }

            private function doDragDrop(event:DragEvent):void
            {
                var item:XML;
                // cancel default
                event.preventDefault();
               
                // get the drag format (its always items in our case
                // when you are dragging FROM a Tree its treeItems
                var draggedFormat:String = event.dragSource.formats[0];
               
                // Get the dragged items
                var draggedItems:Array = event.dragSource.dataForFormat(draggedFormat) as Array;
               
                // Calculate the index in the Tree where the items were dropped
                var dropIndex:int = tree.calculateDropIndex(event);
               
                // Set the selected index of the Tree to the dropIndex
                tree.selectedIndex = dropIndex;
               
                // Check if we are dropping on a node
               
                // Add each dragged item to the Tree by apppending it
                // as a child of the selected node in the Tree.
                for each( var object:Object in draggedItems )
                {
                    // create item
                    item = <item></item>;
                    // set the name
                    item.@name = object.name;
                   
                    // use appendChild to add the item.
                    // (if selected item is an item then append to parent)
                    if( (tree.selectedItem as XML).name() == "node")
                    {
                        (tree.selectedItem as XML).appendChild(item);
                    }
                    else{
                        (tree.selectedItem as XML).parent().appendChild(item);
                    }
                }
            }
           
            private function doDragEnter(event:DragEvent):void
            {
                // Cancel default behaviour
                event.preventDefault();
                // Tell the DragManager that the Tree will accent the DragDrop
                DragManager.acceptDragDrop( Tree(event.target) );
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.showDropFeedback(event);
            }
           
            private function doDragOver(event:DragEvent):void
            {
                // Show the default "drop line" in the Tree control
                tree.showDropFeedback(event);
                // Cancel default behavious
                event.preventDefault();
            }
           
            private function doDragExit(event:DragEvent):void
            {
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.hideDropFeedback(event);
            }
           
            private function doDragComplete(event:DragEvent):void
            {
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.hideDropFeedback(event);
            }
           
        ]]>
    </fx:Script>
    <s:HGroup width="400" height="250" paddingLeft="5" paddingTop="5">
        <mx:DataGrid dragComplete="doDragComplete(event)"
                     id="grid" width="50%" height="100%"
                     dataProvider="{collection1}" dragEnabled="true"
                     dragMoveEnabled="true">
            <mx:columns>
                <mx:DataGridColumn headerText="Name" dataField="name"/>
            </mx:columns>
        </mx:DataGrid>
       
        <mx:Tree id="tree" width="50%" height="100%"
                 dataProvider="{collection2}"
                 dropEnabled="true"
                 labelField="@name"
                 dragOver="doDragOver(event)"
                 dragDrop="doDragDrop(event)"
                 dragEnter="doDragEnter(event)"
                 dragExit="doDragExit(event)"/>

    </s:HGroup>
</s:Application>

Reuested in comment: The code for Flex 3, just a few changes to make it work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()">

    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.controls.DataGrid;
            import mx.events.DragEvent;
            import mx.managers.DragManager;
           
            [Bindable]
            private var collection1:ArrayCollection;
           
            [Bindable]
            private var collection2:XML = <node name="Root">
                                            <node name="Node One">
                                                <item name="Item One"/>
                                                <item name="Item Two"/>
                                            </node>
                                            <node name="Node Two">
                                                <item name="Item Three"/>
                                            </node>
                                        </node>;
           
            private function init():void
            {
                var obj:Object;
                collection1 = new ArrayCollection();
                obj = new Object();
                obj.name = "Item Four";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Five";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Six";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Seven";
                collection1.addItem(obj);
                obj = new Object();
                obj.name = "Item Eight";
                collection1.addItem(obj);
               
                // open all items in Tree control
                this.validateNow();
                tree.expandChildrenOf(collection2, true);
               
            }

            private function doDragDrop(event:DragEvent):void
            {
                var item:XML;
                // cancel default
                event.preventDefault();
               
                // get the drag format (its always items in our case
                // when you are dragging FROM a Tree its treeItems
                var draggedFormat:String = event.dragSource.formats[0];
               
                // Get the dragged items
                var draggedItems:Array = event.dragSource.dataForFormat(draggedFormat) as Array;
               
                // Calculate the index in the Tree where the items were dropped
                var dropIndex:int = tree.calculateDropIndex(event);
               
                // Set the selected index of the Tree to the dropIndex
                tree.selectedIndex = dropIndex;
               
                // Check if we are dropping on a node
               
                // Add each dragged item to the Tree by apppending it
                // as a child of the selected node in the Tree.
                for each( var object:Object in draggedItems )
                {
                    // create item
                    item = <item></item>;
                    // set the name
                    item.@name = object.name;
                   
                    // use appendChild to add the item.
                    // (if selected item is an item then append to parent)
                    if( (tree.selectedItem as XML).name() == "node")
                    {
                        (tree.selectedItem as XML).appendChild(item);
                    }
                    else{
                        (tree.selectedItem as XML).parent().appendChild(item);
                    }
                }
            }
           
            private function doDragEnter(event:DragEvent):void
            {
                // Cancel default behaviour
                event.preventDefault();
                // Tell the DragManager that the Tree will accent the DragDrop
                DragManager.acceptDragDrop( Tree(event.target) );
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.showDropFeedback(event);
            }
           
            private function doDragOver(event:DragEvent):void
            {
                // Show the default "drop line" in the Tree control
                tree.showDropFeedback(event);
                // Cancel default behavious
                event.preventDefault();
            }
           
            private function doDragExit(event:DragEvent):void
            {
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.hideDropFeedback(event);
            }
           
            private function doDragComplete(event:DragEvent):void
            {
                // hide the "drop line" that is shown in Tree control
                // when dropping in a Tree
                tree.hideDropFeedback(event);
            }
           
        ]]>
    </mx:Script>
    <mx:HBox width="400" height="250" paddingLeft="5" paddingTop="5">
        <mx:DataGrid dragComplete="doDragComplete(event)"
                     id="grid" width="50%" height="100%"
                     dataProvider="{collection1}" dragEnabled="true"
                     dragMoveEnabled="true">
            <mx:columns>
                <mx:DataGridColumn headerText="Name" dataField="name"/>
            </mx:columns>
        </mx:DataGrid>
       
        <mx:Tree id="tree" width="50%" height="100%"
                 dataProvider="{collection2}"
                 dropEnabled="true"
                 labelField="@name"
                 dragOver="doDragOver(event)"
                 dragDrop="doDragDrop(event)"
                 dragEnter="doDragEnter(event)"
                 dragExit="doDragExit(event)"/>

    </mx:HBox>
</mx:Application>

Related posts:

  1. Tree in Advanced DataGrid Example
  2. Style AdvancedDataGrid depending on data example
  3. Change open and close icons on Flex Tree
  4. Style Flex Tree Label Example
  5. Progressbar in Datagrid Example

Comments

6 Responses to “Drag and Drop from DataGrid or AdvancedDataGrid to Tree”

  1. sunitha shali on December 23rd, 2009 6:49 am

    Hi,

    Drag and Drop from Advance Data Grid control. There is inbuilt tree structure in Advance Data Grid. So, i have to use that. Drag and Drop within the parent1 or child1,2…to restrict to another parent2 or child1,2…. How to do this in Flex 3.0.

    Thanks & Regards,
    –Sunitha Shali.

  2. Arjan Nieuwenhuizen on December 23rd, 2009 11:03 am

    Can you explain to me in more detail what you want to do? Do you want to drag and drop inside the Tree structure in the AdvancedDataGrid or from another component?

    Regards,

    Arjan Nieuwenhuizen

  3. praemdev on February 10th, 2010 11:33 am

    Can u show me the code for Flex3 I really need it for my project

  4. Arjan Nieuwenhuizen on February 10th, 2010 12:58 pm

    Flex 3 code is added at the bottom of the post.

    Regards,

    Arjan

  5. praemdev on February 15th, 2010 9:47 am

    Thank you very Flex 3 code, I have question when I move record form Datagridto tree. if my algorithm not allow some record move to tree but UI record have already gone from Datagrid when complete drag drop, How can I prevent this case happen

  6. Arjan Nieuwenhuizen on February 15th, 2010 10:15 am

    (I think) You have to program your own logic in the dragDrop event handler. There you can check if the item can be added or not. If not, just don’t add the item to the Tree and add it to the drag source (in this case, the datagrid) again.

    Hope this helps.

    Regards,

    Arjan

Got something to say?