Adding Drag-and-Drop & Drop Deny to DataGrid

I wanted to add a drag-and-drop UI element to a current project. What I wanted was to create two DataGrid components, side-by-side, one that I could drag  items from to create an ordered list of selected items in another DataGrid.  I also wanted to reorder the items in the second DataGrid to my liking.   The only restriction I had was that the second DataGrid could not contain duplicate items.   The default functionality of the DataGrid would just not cut it.

I read the Live Docs and found a way to manually create drag-and-drop support to the DataGrid component using the doDragEnter, doDragExit, and doDragDrop methods.   This made it possible to add items to the second DataGrid, and also reorder the items with in the second DataGrid.   But I was still able to add duplicate items to the DataGrid.

I needed a method to determine which DataGrid was the source and which was the destination.  If the source and the destination were the same, then I knew that I was moving with in the same DataGrid.  If they were different, then I was dragging an item from the source to the destination.   I finally used a var to set the name of the DataGrid I initially clicked on with a mouseDown event, then compared that with the name of the DataGrid I was dragging over.

I am not certain this is the elegant solution, but its seems to work pretty good.

Sample File and Source code

Update

I updated the code to work with Flex 2.01. The items that you drag/move within the list were references to the original items in the list. This becomes a problem when you select one the duplicate item, but it really selects the original item. Normally, you would do a complete clone of your item if you wanted the list to actually contain more than one copy of the original. If modify this example to keep adding multiple copies, be sure to clone your items first and break the reference back to the original items. Here is updated code that works for moving items within the same list:

private function doDragDrop(event:DragEvent):void
{
var dropTarget:DataGrid = DataGrid(event.currentTarget);</code>

doDragExit(event);

var items:Array = event.dragSource.dataForFormat("items") as Array;
var dropLoc:int = dropTarget.calculateDropIndex(event);

for(var i:int = 0; i DataGrid(dropTarget).selectedIndex)
dropLoc = dropLoc - 1;
if (dropLoc &gt; IList(dropTarget.dataProvider).length-1)
dropLoc = dropLoc - 1;

IList(dropTarget.dataProvider).removeItemAt(dropTarget.selectedIndex);
IList(dropTarget.dataProvider).addItemAt(items[i], dropLoc);

callLater(updateSelection, [dropLoc]);

} else {
IList(dropTarget.dataProvider).addItemAt(items[i], dropLoc );
}
}
}

private function updateSelection(index : int) : void
{
dropList.selectedIndex = index;
}

&nbsp;

<strong>Additional Update</strong>

Thanks to Chris, we have a update to the DD that adds back in the the drop target indicator:

<code>
private function doDragOver(event:DragEvent):void
{
var dropTarget:DataGrid = DataGrid(event.currentTarget);
// add the DataGrid drop feedback
dropTarget.showDropFeedback(event);
}

Be sure to add the following to the DataGrid properties

dragOver="doDragOver(event)"

Thanks again Chris!

29 Comments

  1. Just curious to know if you have encountered any issues using drag & drop functionality when your content has been scrolled (in my case the scrolling occurs at the Application.application level). I am not sure why, but the containers that dispatch their built-in dragEvents, don’t seem to realize that they have been scrolled up or down and therefore they are firing their dragEvents without regard for thier x & y positions in the context of scrolled content.

    J

  2. I am trying to compile your demo using Flex Builder 2.0.1 and at runtime it does not work as expected. If you try to move an item on the right side to a new location it creates a duplicate. Your posted demo works fine so is this a new bug in Flex?

  3. Not sure why it even worked the first time I put this out. When you duplicate within the list you create references (copies) of the original item, the list gets confused about which is the original. The fix is to either clone your objects so you break the reference or be sure you only have one reference to the same object in the list.

    I posted and update to the code that deletes the first reference before adding it again to the list in the new move location.

  4. It’s too bad they haven’t built this drag and drop functionality into the Flash AS3 DataGrid component.

    Has anyone had any luck porting this functionality over?

  5. the downloadable source available in the sample source view don’t deny duplicate data in the Grid 2 , i can always duplicate items in the same datagrid (grid 2)….
    NB: the source work correctly online ( no duplicate data on grid 2), but when i v tried to run it locally i can create duplicate data again, perhaps i have downloaded the wrong source!!..

    can i have a little explanation about that, cuz i m a beginner on Flex..?!
    thx ,

  6. lifesaver. I have a pretty complex drag and drop screen going on a page of mine, and your sorted worked right out of the box.

  7. I cannot get the code to work. Is there something I should do to configure it? I am getting nothing but errors…

  8. Hai,

    I think the problem is in DragManager.showFeedback(DragManager.MOVE); in the doDragEnter(event) method. Once I edited as follows the duplication of rows didn’t occur.

    //DragManager.showFeedback(DragManager.MOVE);
    event.preventDefault();
    DragManager.acceptDragDrop(dropTarget);

    Thanks Mister for posting the sample. you are really a life saver. 🙂

  9. Thanks for posting this. Is it possible to drag the datagrid column to a list component and back to set the visibility of the column false or true?

    Thanks

  10. Hello:

    I have problems with a similar case in AS3 FLASH is that when I change the item in one datagrid change in the other datagrid… I will like stop this. I will like a copy not a reference

    Thanks for all. And sorry my bad english

  11. Thanks for your examples! I have a question – using Cairngorm, Flex and ColdFusion, I need to take all the items in the list on the right, click a “save” button, and update the contents of the list in the database.

    Using your Master/Detail example, I have ascertained how update the main value object with individual values bound to text inputs, etc, but wonder if you or someone has done this already (update the database with all the items from the list)?

    Thanks in advance for any help or advice!

  12. I save with this code my datagrid(is the same that a list but only one column more or less), I stay breaking my Brain the first time that work with datagrids and list, I hope that you stay minus days, sorry my bad english.

    [as]
    function salvar_xml_disco(){

    var l:int

    var t:int;

    var salida:String=””

    var cr:*=File.lineEnding;

    var field:String;

    salida=”” + cr

    salida+=””+cr

    for (l=0;l<_dataGrid.length;l++){

    salida+="<item"

    for (t=0;t” +cr

    }

    for (l=0;l<_dataGrid2.length;l++){

    salida+="<item"

    for (t=0;t” +cr

    }

    salida+=””

    var docsDir:File = File.documentsDirectory;

    var txtFilter:FileFilter = new FileFilter(“XML”, “*.xml”);

    try

    {

    // myFileReference.browse([imagesFilter, docFilter]);

    docsDir.browseForSave(“Save As”);

    docsDir.addEventListener(Event.SELECT, Callback.create(saveData,salida));

    }

    catch (error:Error)

    {

    trace(“Failed:”, error.message);

    }

    //e.stopPropagation()

    } //SALVAR DEL DISCO
    [/as]

  13. I am trying to make this example work for lists instead of datagrids by simply switching all instances of “DataGrid” to “List” but it’s not working out so well (The dupe is not denied and actually drags 2 instances of the item to the list!). Has anyone by chance had the opportunity to get this working with lists?

    @israel – thanks for the code. I’m not sure I understand how I can get this to work with the scenario I described, but I do appreciate the efforts. If there are any examples for updating the all contents of a list to a database using Cairngorm and ColdFusion, any help would be greatly appreciated!

  14. Yes i think that not work because not have columns the List, here is the document. Also I am seeing the List component

    http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/fl/controls/List.html#List()

    [as]

    If you see there see that to take information for example

    function announceLabel(e:ListEvent):void {
    var list:List = e.target as List; //–> HERE IS YOUR LIST ELEMENT
    // put this trace(list.length) to know the number of elements
    // create a for(;;) to take element to element list[number]
    var item:Object = e.item;
    trace(“Label: ” + item.label);
    trace(“Label displayed: ” + list.itemToLabel(item));
    }
    [/as]

    Sorry I have worst english but with the page, the code that I put the last night and remember that the diference between List and Datagrid is that no have columns more or less.

  15. Put data in a list here in a example
    [as]
    import fl.data.DataProvider;
    var dp:DataProvider = new DataProvider();
    dp.addItem( { abbreviatedLabel:”NY”, label:”New York” } );
    dp.addItem( { abbreviatedLabel:”CA”, label:”California” } );
    dp.addItem( { abbreviatedLabel:”WA”, label:”Washington” } );
    dp.addItem( { abbreviatedLabel:”CT”, label:”Connecticut” } );
    dp.addItem( { abbreviatedLabel:”VT”, label:”Vermont” } );
    var list:List = new List();
    list.setSize(40,100);
    list.labelField = “abbreviatedLabel”
    list.dataProvider = dp;
    [/as]
    Take data of a list
    [as]
    //check the two forms I not remember what is the good
    var ListNum:int=list.dataProvider.length
    var ListNum:int=list.length
    for(var i:int=0;i< ListNum;i++)
    {
    trace(List[i],list[i].label,list.itemToLabel(list[i])
    }
    [/as]

  16. Thanks for the great example, yet I am wondering where this ‘dropInitiator’ variable is coming from? And why hasn’t anybody else picked this up? Is it me?! 😮

    1. @Tom, Take a look at the source code, you will see that dropInitiator is used just to store a reference to the list that started the drag/drop process.

  17. I am trying to compile your demo using Flex Builder 3.2.0.
    When in second DataGrid appear scroll we get unexpected scroll when we mouse move about it. How to get rid of this problem. Sorry my bad english.

    <![CDATA[
                import mx.collections.ArrayCollection;
                import mx.collections.ListCollectionView;
                import mx.controls.List;
                import mx.core.DragSource;
                import mx.events.*;
                import mx.managers.DragManager;
                import mx.controls.Alert;
                import mx.collections.IList;
                import flash.events.MouseEvent;
    
                private var dropInitiator:String
    
                private function setDragInitator(event:MouseEvent, id:String):void
                {
                    dropInitiator = id;
                    //Alert.show("id: " + id);
                }
    
                private function doDragEnter(event:DragEvent):void
                {
    
                       var dropTarget:DataGrid = DataGrid(event.currentTarget);
                    var dataInfo:ArrayCollection = dropTarget.dataProvider as ArrayCollection;
                       var items:Array = event.dragSource.dataForFormat("items") as Array;
    
                    var drop:Boolean = true;
    
                    if(dropInitiator == dropTarget.id){
    
                        DragManager.showFeedback(DragManager.MOVE);
                        DragManager.acceptDragDrop(dropTarget);
                    } else {
                        for(var i:int = 0; i < dataInfo.length; i++){
                            if(dataInfo[i].data == items[0].data) drop = false;
                        }
    
                        if(drop){
                            DragManager.showFeedback(DragManager.COPY);
                            DragManager.acceptDragDrop(dropTarget);
                        }
                    }
                }
    
                private function doDragDrop(event:DragEvent):void
                {
                    var dropTarget:DataGrid = DataGrid(event.currentTarget);
                    doDragExit(event);
                    var items:Array = event.dragSource.dataForFormat("items") as Array;
                    var dropLoc:int = dropTarget.calculateDropIndex(event);
    
                    for(var i:int = 0; i
    
    
    
    
    
    
                        Item 0
                        0
    
    
                        Item 1
                        1
    
    
                        Item 2
                        2
    
    
                        Item 3
                        3
    
    
                        Item 4
                        4
    
    
                        Item 5
                        5
    
    
                        Item 6
                        6
    
    
                        Item 7
                        7
    
    
                        Item 8
                        8
    
    
                        Item 9
                        9
    
    
                        Item 10
                        10
    
    
                        Item 11
                        11
    
    
                        Item 12
                        12
    
    
                        Item 13
                        13
    
    
                        Item 14
                        14
    
    
                        Item 15
                        15
    
    
                        Item 16
                        16
    
    
                        Item 17
                        17
    
    
                        Item 0
                        0
    
    
                        Item 1
                        1
    
    
                        Item 2
                        2
    
    
                        Item 3
                        3
    
    
                        Item 4
                        4
    
    
                        Item 5
                        5
    
    
                        Item 6
                        6
    
    
                        Item 7
                        7
    
    
                        Item 8
                        8
    
    
                        Item 9
                        9
    
    
                        Item 10
                        10
    
    
                        Item 11
                        11
    
    
                        Item 12
                        12
    

  18. I am trying to compile your demo using Flex Builder 3.2.0.
    When in second DataGrid appear scroll we get unexpected scroll when we mouse move about it. How to get rid of this problem. Sorry my bad english.

  19. @Mister
    referring to your question of discarding duplicates.
    you can make ur items in collection implement IUID..then while you dropping just check if dropped object with every other object in the list and accept if it is unique in the list.

    if they are equal don’t accept the drop.

    if(notInList)
    DragManager.acceptDragDrop(dropTarget);

  20. dragMoveEnabledis a Booleanproperty that, when set to truealong with
    dragEnabled, causes items dragged from a Listcontrol be removed from the initiat-
    ing control’s data provider. This property also allows users to reorder data in a control’s
    dataProviderif the control’s dropEnabledproperty is set to true.

  21. Hello;
    Is it possible to make it work in Flash (cs3/cs4) I need grid with drag and drop functionality for flash (not flex) and this post is very promising. Is it possible to populate the DataGrid with images and add them drag/drop functionality

  22. Hey Mister,

    I am not able to view its source code after clicking on the link provided, on the provided link there is only demo not the source code. Please help…

Comments are closed.