Drag-and-Drop in Flex 4
The Flex 4 gods were kind to us developers when they made the great decision to leave the custom drag-and-drop support unchanged. We just do what we’ve always done: detect the user is trying to drag something via mouseDown or mouseMove and then add both dragEnter and dragDrop event handlers to the drop target. So there is nothing in this post that’s not basically identical to Flex 3, except the coolness of FXG (which you can easily mimic with Degrafa in Flex 3).
Simple Drag-and-Drop
I’ll begin with a basic Flex 4 application. We have two draggable graphics (a Rect and an Ellipse) in the left panel, and a target panel on the right:
<?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"> <fx:Script> <![CDATA[ imports... private function mouseDownHandler(e:MouseEvent):void { DragManager.doDrag(e.currentTarget as IUIComponent, null, e); } private function dragEnterHandler(e:DragEvent):void { DragManager.acceptDragDrop(e.currentTarget as IUIComponent); } private function dragDropHandler(e:DragEvent):void { e.currentTarget.addElement(e.dragInitiator); } ]]> </fx:Script> <s:Panel title="src" width="100" minHeight="133" x="10" y="10"> <s:Graphic width="80" height="80" mouseDown="mouseDownHandler(event)"> <s:Rect ... </s:Rect> </s:Graphic> <s:Graphic width="80" height="80" mouseDown="mouseDownHandler(event)"> <s:Ellipse ... </s:Ellipse> </s:Graphic> ... </s:Panel> <s:Panel title="target" width="100" minHeight="133" x="120" y="10" dragEnter="dragEnterHandler(event);" dragDrop="dragDropHandler(event);"> ... </s:Panel> </s:Application>
Reading the code above, each draggable Graphic has a mouseDown handler that calls DragManager.doDrag() to initiate dragging. And the target Panel calls DragManager.acceptDragDrop() on dragEnter and addElement() on dragDrop. Note that since Panel is a Spark container we must use addElement() to re-parent the dropped graphic (the familiar addChild() is still used for Halo containers).
Our simple drag-and-drop app (view source enabled):
Using a FXG Drag Proxy
In the example above, the moment you start dragging a component the drag proxy is displayed. By default, the proxy is just a bounding rectangle with an alpha value of 0.5. This is particularly noticeable and lame when you try to drag the pink circle. Thankfully, we can use any display object for the drag proxy (more specifically any component that implements IFlexDisplayObject which includes UIComponent and any of its descendants). Why not use a FXG graphic? It is, after all, a major piece of the new hotness that is Flex 4.
First, we need to modify the mouseDownHandler to instantiate our FXG graphic. Then we just add it as the forth parameter to the DragManager.doDrag() call.
private function mouseDownHandler(e:MouseEvent):void { var star:Star = new Star(); DragManager.doDrag(e.currentTarget as IUIComponent, null, e, star); }
And here is Star.mxml:
<?xml version="1.0" encoding="utf-8"?> <s:Graphic xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" width="80" height="80"> <s:Path data="M 119,0 L 148,86 238,86 166,140 192,226 119,175 46,226 72,140 0,86 90,86 Z" y="2" scaleX="0.3361345" scaleY="0.3361345"> <s:fill> <mx:SolidColor color="#FFCC00" /> </s:fill> </s:Path> </s:Graphic>
Bang! We have a gold star as our drag proxy. Try it out for yourself (view source enabled):
Dynamic Drag Proxy
Dragging a gold star is pretty cool, but how can we make the drag proxy look exactly like the drag source? For that we need a dynamic drag proxy.
First, for reasons which will become clear in a moment, we need to revise our app to make each of the drag source graphics into a separate component. So the left Panel changes to contain these custom components:
<s:Panel title="src" width="100" minHeight="133" x="10" y="10"> <graphics:Square fillColor="#6666FF" mouseDown="mouseDownHandler(event)" /> <graphics:Circle fillColor="#FF66FF" mouseDown="mouseDownHandler(event)" /> <graphics:Star fillColor="#FFCC00" mouseDown="mouseDownHandler(event)" /> <graphics:Square fillColor="#66FF99" mouseDown="mouseDownHandler(event)" /> <s:layout> <s:VerticalLayout gap="10" horizontalAlign="center" paddingTop="10" paddingBottom="10" /> </s:layout> </s:Panel>
Next, we again modify the mouseDownHandler, this time we instantiate our drag proxy dynamically. We use reflection on the incoming drag source to get its name, instantiate it, adjust a few properties, and pass it to the DragManager.doDrag() call.
private function mouseDownHandler(e:MouseEvent):void { var dragSrc:Graphic = e.currentTarget as Graphic; //create a proxy by creating a new "copy" of the drag src var className:String = getQualifiedClassName(dragSrc); var klass:Class = getDefinitionByName(className) as Class; var proxy:* = new klass(); //set the proxy's properties to match the src + sexy drop shadow proxy.width = dragSrc.width; proxy.height = dragSrc.height; proxy.fillColor = (dragSrc as IDraggableGraphic).fillColor; proxy.filters = [new DropShadowFilter()]; DragManager.doDrag(dragSrc, null, e, proxy); }
The real reason for the reflection gymnastics, plus the need to package each drag source as a custom component, is the lack of a deep copy operation on UIComponent. If we could just clone the drag source, we’d be golden. Alas, for many reasons, the most obvious of which is that it is a huge pain in the ass, there is no such thing as deep copy. In our case, my work around was to package all the visual stuff of each draggable item into a single custom component with a known API (all the custom graphics implement IDraggableGraphic). Then I reflect, instantiate, configure, and pass to doDrag().
Our dynamic drag proxy now matches the drag source, plus the sexy drop shadow. Check it out (view source enabled):
Files
NOTE: All code was built with Flash Builder 4 Beta 1.

Yarin
9.2.2009
Saturnboy, well done. Clearest, most concise treatment of d&d I’ve come across. Looking forward to more articles…
Michel
11.10.2009
Nice. Looking forward to more.
I’m using Beta 2 and the DragDropProxy Square and Circle didn’t showed-up at first. Seem that the 100% for width and height were the cause. Set them to 80 and it worked. I’ll see if there is another way to correct that.
By the way. Custom Drag And Drop support isn’t unchanged. We are missing some methods like dragExit() ans dragOver(). We can declare them in object but they are not fired. I have some Flex 3 components that used dragExit to set their visual state back to normal.
Michel
11.10.2009
And also.
When adding a element to a container it removes it from it’s origin container. So, we can’t copy an element by dragging it to an other container.
justin
11.10.2009
@Michel:
Thanks for reading, but I must be missing something, because when I compare the “Manually adding drag-and-drop support” pages (Flex 4 vs. Flex 3) they look very similar to me, including the
dragExitanddragOverevents.Also, both Flex 3 and Flex 4 do not support copy. The Flex 3 docs say here, “The list-based controls can automate all of the drag-and-drop operation except for when you copy the drag data to the drop target…Flex leaves it to you to implement object copying…”
Lastly, Flex 4 isn’t fully baked yet, so who really knows what works and what does not.
Michel
11.11.2009
My mistake. Reading back on your posts and answer to my comment I realize that my component wasn’t pointing the
dragExitevent to the right function in my test application.Now… Upon validation of the
dragSourcethe drop target state goes from normal todropYes, anddropNoif it doesn’t validate.I’ll check your others posts to see if I can manage that.
Thanks.
justin
11.11.2009
Glad to help, and thanks for the feedback.
Santiago
11.25.2009
Excellent. Now I’m starting to build an application, and I want to use Panels to contain mostly grids. I want those Panels to be “movable” (using drag & drop) as any program window in Windows (or Mac or any graphical OS).
Can you tell me how to achieve this?
justin
11.26.2009
@Santiago:
Well, following my example above, the Panel are the drag source elements, so they would listen for
mouseDown. You would need to have some parent container to act as the drag target that listens fordragEnteranddragDrop. Lastly, I’d recommend a custom layout applied to the parent container to handle the positioning of the dropped Panels.Good luck.
Steve
3.17.2010
Saturnboy have you stumbled upon any other ways of copying fxg graphics besides creating class proxy?
justin
3.19.2010
@Steve: If I’m understanding you, the answer is no. The lack of deep copy really hurts in this case. You have to instantiate something to be the drag proxy.
sandip kothari
6.19.2010
Saturnboy, well done. Clearest, most concise treatment of d&d I’ve come across. Looking forward to more articles…
thank you very very ………………very much