Swami Charan's Blog My personal blog…

7Oct/0931

Drag-drop on non-list based controls in Flex/AIR

The List-based controls like List, DataGrid etc will have support for drag and drop. You use drag-drop on list-based contols by enabling properties like dragEnabled and dropEnabled to true. We can implement this drag-drop on non-list based controls as well. To do that we need to handle drag and drop events.

Drag-drop Events for Drag Initiator

  • mouseDown, mouseMove: mouseDown event will be dispatched when a control is selected with Mouse button down. mouseMove is dispatched when mouse moves.
  • dragStart: this event is dispatched by a list-based control when a drag operation starts. Its handled internally, no need to handle it while implementing on non-list based controls.
  • dragComplete: Dispatched when the drag completes.

If you want to implement drag-drop for any component, you must implement either mouseDown or mouseMove and optionally dragComplete event. In case of List-based controls, once dragEnabled property is set to true, Flex automatically adds event handlers for dragStart and dragComplete events.

Drag-Drop events for Drop Target:

  • dragEnter: Dispatched when drag proxy moves onto drop target from outside the drop target. Event handler for this must be defined for the component. DragManager.acceptDragDrop() method must be called by event handler to accept drop.
  • dragOver: this event is dispatched when mouse is moved over drop target after dragEnter event.
  • dragDrop: Dispatched when the mouse is released over drop target.
  • dragExit: This event is dispatched when user moves the drag proxy off the target withour dropping the data on the target.

While implementing darg-drop for non-list based controls, we must implement event handlers for dragEnter and dragDrop events and optionally for the remaining two events. Incase of list-based controls, when dropEnabled is set to true, Flex automatically adds event handlers for all the events.

Steps of Drag-Drop Operation:

  1. First make a component drag initiator. Use mouseDown or mouseMove event handler to start the drag-drop operation. mx.core.DragSource instance is created that contains the data to be dragged and specifies the format of data. mx.managers.DragManager.doDrag() method should be called to initiate the drap-drop operation.
  2. With mouse button down, if the mouse is moved across the application, Flex displays drag proxy image.
  3. When this drag proxy is moved over a flex component (which had dragEnter event handler),  dragEnter event handler examines the DragSource object to determine whether the data being dragged is in accepted format. To accept the drop, the event handler calls DragManager.acceptDragDrop() method. This method must be called in order for the drop target to receive dragOver, dragExit and dragDrop events.
  4. If the user releases the drag proxy on the drop target, Flex dispatched dragDrop event. The drop target must define event handler for dragDrop event to  add the drag data to the drop target.

Simple Drag-Drop Example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="300">
	<mx:Script>
		<![CDATA[
			import mx.core.DragSource;
			import mx.managers.DragManager;
			import mx.events.*;
			import mx.containers.Canvas;

			private function mouseMoveHandler(event:MouseEvent):void{
				var dragInitiator:Canvas=Canvas(event.currentTarget);

				var dragColor:int = dragInitiator.getStyle('backgroundColor');

				var ds:DragSource = new DragSource();

				ds.addData(dragColor, 'color');

				DragManager.doDrag(dragInitiator, ds, event);
			}

			private function dragEnterHandler(event:DragEvent):void{
				if(event.dragSource.hasFormat('color')){
					var dropTarget:Canvas=Canvas(event.currentTarget);
					DragManager.acceptDragDrop(dropTarget);
				}
			}

			private function dragDropHandler(event:DragEvent):void{
				var data:Object = event.dragSource.dataForFormat('color');
				myCanvas.setStyle("backgroundColor", data);
			}
		]]>
	</mx:Script>
	<mx:HBox x="40" y="40">
		<mx:VBox>
			<mx:Canvas width="30" height="30" backgroundColor="red" borderStyle="solid" mouseMove="mouseMoveHandler(event)" />
			<mx:Canvas width="30" height="30" backgroundColor="blue" borderStyle="solid" mouseMove="mouseMoveHandler(event)" />
			<mx:Canvas width="30" height="30" backgroundColor="green" borderStyle="solid" mouseMove="mouseMoveHandler(event)" />
		</mx:VBox>
		<mx:VBox>
			<mx:Label text="Drag into this Canvas" />
			<mx:Canvas id="myCanvas" width="100" height="100" backgroundColor="#FFFFFF" borderStyle="solid" dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)" />
		</mx:VBox>
		</mx:HBox>
</mx:Application>

In this example, we have a canvas to the right which is drop target (which has dragEnter and dragDrop event handlers). Dragging any smaller canvas from the left to the right canvas fills the background of the drop target with the color of the drag initiator.

Result:

This movie requires Flash Player 9

 

Another Example

In the above example, we have the drag Initiator was external to the drop target. We can make the children of a drop target as drag initiators as well.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="400">
	<mx:Script>
		<![CDATA[
			import mx.managers.DragManager;
            import mx.core.DragSource;
            import mx.events.DragEvent;
            import flash.events.MouseEvent;

            private function mouseMoveHandler(event:MouseEvent):void{
            	var dragInitiator:Canvas=Canvas(event.currentTarget);
                var ds:DragSource = new DragSource();
                ds.addData(dragInitiator, "canvas");               

                DragManager.doDrag(dragInitiator, ds, event);

            }

            private function dragEnterHandler(event:DragEvent):void {
                if (event.dragSource.hasFormat("canvas"))
                {
                    DragManager.acceptDragDrop(Canvas(event.currentTarget));
                }
            }

            private function dragDropHandler(event:DragEvent):void {
                Canvas(event.dragInitiator).x =
                    Canvas(event.currentTarget).mouseX;
                Canvas(event.dragInitiator).y =
                    Canvas(event.currentTarget).mouseY;
            }

		]]>
	</mx:Script>
	<mx:Canvas id="myCanvas" x="20" y="20" width="300" height="300" backgroundColor="#FFFFFF" borderStyle="solid" dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)">
		<mx:Canvas id="thumb" width="50" height="50" borderStyle="solid" backgroundColor="red" mouseMove="mouseMoveHandler(event)" />
	</mx:Canvas>
</mx:Application>

 In this example, we can drag-drop the red canvas all across the white canvas.

Result:

This movie requires Flash Player 9

User-Defined Drag Proxy:

In the above example, while dragging a default drag proxy will be displayed. We can add our own drag proxy image.

In mouseDown or mouseMove event handler, you can optionally specify the drap proxy in doDrag() method. Here are the optional properties to be given incase of user-defined drag proxy:

  • dragImage: Image that specifies the drag proxy
  • xOffset: Specifies the x offset in pixels for the dragImage. Usually it will be negative number.
  • yOffset: Specifies the y offset in pixels for dragImage.Usually it will be negative number.
  • imageAlpha: A number between 0 and 1.0, which specifies the alpha for the drag proxy image. The default value is 0.5

 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import mx.managers.DragManager;
            import mx.core.DragSource;
            import mx.events.DragEvent;
            import flash.events.MouseEvent;

            // Embed icon image.
            [Embed(source='assets/proxy.jpg')]
            public var proxyImage:Class;

            private function mouseMoveHandler(event:MouseEvent):void{
            	var dragInitiator:Canvas=Canvas(event.currentTarget);
                var ds:DragSource = new DragSource();
                ds.addData(dragInitiator, "canvas");  

                var myProxy:Image = new Image();
                myProxy.source = proxyImage;
                myProxy.width = 15;
                myProxy.height = 15;             

                DragManager.doDrag(dragInitiator, ds, event, myProxy, -15, -15, 0.8);

            }

            private function dragEnterHandler(event:DragEvent):void {
                if (event.dragSource.hasFormat("canvas"))
                {
                    DragManager.acceptDragDrop(Canvas(event.currentTarget));
                }
            }

            private function dragDropHandler(event:DragEvent):void {
                Canvas(event.dragInitiator).x =
                    Canvas(event.currentTarget).mouseX;
                Canvas(event.dragInitiator).y =
                    Canvas(event.currentTarget).mouseY;
            }

		]]>
	</mx:Script>
	<mx:Canvas id="myCanvas" x="20" y="20" width="300" height="300" backgroundColor="#FFFFFF" borderStyle="solid" dragEnter="dragEnterHandler(event)" dragDrop="dragDropHandler(event)">
		<mx:Canvas id="thumb" width="50" height="50" borderStyle="solid" backgroundColor="red" mouseMove="mouseMoveHandler(event)" />
	</mx:Canvas>
</mx:Application>

You can provide what ever image you want as your drag proxy. Make sure you give the height and width for the drag proxy image, without which it wont work.

Result:

This movie requires Flash Player 9

These are the basics for implementing drap-drop for non-list based controls for Flex/AIR.

You can experiment more on this.

Comments (31) Trackbacks (0)
  1. Hello from Russia!
    Can I quote a post in your blog with the link to you?

  2. Hi Polprav,

    sure… thanks…

  3. Hi

    please could put another example that uses DragSource

    this is very good.

  4. Hi Vasco,

    The examples here cover DragSource as well… Let me know if you are looking for anything specific about DragSource…

    Thanks

  5. Hi,

    How do i control the shifting of objects when dragging it to another position?
    For example I want that the upper left corner of the draggeditem does not position itself with the mouse position but as it was positionned just before the drop.

    Thanks

  6. Hi Alex,

    Could you be more clear….I could not get your point…

    Thanks

  7. Ok,

    Take the “Another example” example in this tutorial with the red square.
    If you drag it you will see that there is a “default proxy” with a green border,
    I would like that when I drop the square, it positions itself exactly like the “default proxy” and not positions its upper left corner to the mouse position.
    It’s difficult to be more clear.

    I hope someone can help me

    Thanks

  8. Hi, Could you let me know how to do the same in WindowedApplication(AIR). Drag image never comes up. What could be the issue?I am stuck with that. I am unable to get the drag image working at all

    Thanks a lot in advance
    Vishal

  9. Is that not clear ?

  10. Hi Vishal,
    We have to follow the same procedure even if we want to drag Image. All we have to do here is to create dragSource as Image.
    Here’s the code:

    (replace ‘&lt’ with ‘< ' and '&gt' with '>‘)
    &lt?xml version=”1.0″ encoding=”utf-8″?&gt
    &ltmx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”&gt
    &ltmx:Script&gt
    &lt![CDATA[
    import mx.controls.Image;
    import mx.managers.DragManager;
    import mx.core.DragSource;
    import mx.events.DragEvent;
    import flash.events.MouseEvent;

    // Embed icon image.
    [Embed(source='assets/proxy.jpg')]
    public var proxyImage:Class;

    private function mouseMoveHandler(event:MouseEvent):void{
    var dragInitiator:Image=Image(event.currentTarget);
    var ds:DragSource = new DragSource();
    ds.addData(dragInitiator, “image”);

    var myProxy:Image = new Image();
    myProxy.source = proxyImage;
    myProxy.width = 15;
    myProxy.height = 15;

    DragManager.doDrag(dragInitiator, ds, event, myProxy, -15, -15, 0.8);

    }

    private function dragEnterHandler(event:DragEvent):void {
    if (event.dragSource.hasFormat(“image”))
    {
    DragManager.acceptDragDrop(Canvas(event.currentTarget));
    }
    }

    private function dragDropHandler(event:DragEvent):void {
    Image(event.dragInitiator).x =
    Canvas(event.currentTarget).mouseX;
    Image(event.dragInitiator).y =
    Canvas(event.currentTarget).mouseY;
    }

    ]]&gt
    &lt/mx:Script&gt
    &ltmx:Canvas id=”myCanvas” x=”20″ y=”20″ width=”300″ height=”300″ backgroundColor=”#FFFFFF” borderStyle=”solid” dragEnter=”dragEnterHandler(event)” dragDrop=”dragDropHandler(event)”&gt
    &ltmx:Image id=”myImage” width=”50″ height=”50″ source=”@Embed(source=’assets/logo.jpg’)” mouseMove=”mouseMoveHandler(event)” /&gt
    &lt/mx:Canvas&gt
    &lt/mx:Application&gt

  11. Hi Swami,
    Thanks for the reply. Its not about dragging images. I am unable to attach a dragImage to a drag operation when done in AIR(WindowedApplication).

    Say ther’s one component , out of which I am creating a bitmap and setting as its dragImage in the doDrag method.But the dragImage doesnot show up when the drag happens. It shows as if ther’s no dragImage attached at all. Do you have any working example of dragImage in AIR(WindowedAPplication) ?

    Thanks In advance,
    Vishal

  12. Swami, could you plz tell me if you know/don’t know how to do what I want or if you don’t understand.

    Thanks

  13. Hi Alex, Sorry for the delay…

    I got your point. Actually the positioning of dragProxy Image will always be relative to dragInitiator. If you see the doDrag():

    DragManager.doDrag(dragInitiator, ds, event, myProxy, -15, -15, 0.8);

    The parameters xOffset and yOffset refers to the positioning of dragProxy. If we completely remove these parameters of doDrag(), the dragProxy will be shown always at the top left corner of DragInitiator.

    To my knowledge we cannot position the dragProxy at the same location as default dragProxy appears. Its always be relative positioning.
    Hope this clears your query… Let me know if its not clear…

    Thanks

  14. Hi Vishal,

    I tried to implement a simple drag-drop on a button component on AIR. Though i faced an issue while dropping the button using ‘Canvas(event.currentTarget).mouseX;‘, i could able to find a work-around for this.

    Here is a sample code for an AIR application wherein applying a dragProxy to a button component while drap-drop:

    (replace ‘< ' with '<' and '>‘ with ‘>’ in this code)
    &lt?xml version=”1.0″ encoding=”utf-8″?&gt
    &ltmx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”&gt
    &ltmx:Script&gt
    &lt![CDATA[
    import mx.controls.Image;
    import mx.managers.DragManager;
    import mx.core.DragSource;
    import mx.events.DragEvent;
    import flash.events.MouseEvent;

    // Embed icon image.
    [Embed(source='assets/proxy.jpg')]
    public var proxyImage:Class;

    private function mouseMoveHandler(event:MouseEvent):void{
    var dragInitiator:Button=Button(event.currentTarget);
    var ds:DragSource = new DragSource();
    ds.addData(dragInitiator, “button”);

    var myProxy:Image = new Image();
    myProxy.source = proxyImage;
    myProxy.width = 15;
    myProxy.height = 15;

    DragManager.doDrag(dragInitiator, ds, event, myProxy, 0, 0,0.8);

    }

    private function dragEnterHandler(event:DragEvent):void {
    if (event.dragSource.hasFormat(“button”))
    {
    DragManager.acceptDragDrop(Canvas(event.currentTarget));
    }
    }

    private function dragDropHandler(event:DragEvent):void {

    var p:Point = new Point(event.stageX,event.stageY);

    Button(event.dragInitiator).x = event.target.globalToLocal(p).x
    Button(event.dragInitiator).y = event.target.globalToLocal(p).y

    }

    ]]&gt
    &lt/mx:Script&gt
    &ltmx:Canvas id=”myCanvas” x=”20″ y=”20″ width=”300″ height=”300″ backgroundColor=”#FFFFFF” borderStyle=”solid” dragEnter=”dragEnterHandler(event)” dragDrop=”dragDropHandler(event)” &gt
    &ltmx:Button id=”thumb” width=”40″ height=”20″ mouseMove=”mouseMoveHandler(event)” x=”10″ y=”25″/&gt
    &lt/mx:Canvas&gt
    &lt/mx:WindowedApplication&gt

    Check if this clears your query…

    Thanks

  15. Hi Swami,

    Thanks for a quick response. Do you see the dragImage in the example you have given above?I used the same example but still the image doesnot show up.What could be the reason?
    I am pasting the file

    I appreciate you help much.
    Vishal

  16. &lt?xml version=”1.0″ encoding=”utf-8″?&gt
    &ltmx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”&gt
    &ltmx:Script&gt
    &lt![CDATA[
    import mx.controls.Image;
    import mx.managers.DragManager;
    import mx.core.DragSource;
    import mx.events.DragEvent;
    import flash.events.MouseEvent;

    // Embed icon image.
    [Embed(source='assets/Winter.jpg')]
    public var proxyImage:Class;

    private function mouseMoveHandler(event:MouseEvent):void{
    var dragInitiator:Button=Button(event.currentTarget);
    var ds:DragSource = new DragSource();
    ds.addData(dragInitiator, “button”);

    var myProxy:Image = new Image();
    myProxy.source = proxyImage;
    myProxy.width = 100;
    myProxy.height = 100;
    myProxy.x = 300;
    DragManager.doDrag(dragInitiator, ds, event, myProxy, 0, 0,0.8);

    }

    private function dragEnterHandler(event:DragEvent):void {
    if (event.dragSource.hasFormat(“button”))
    {
    DragManager.acceptDragDrop(Canvas(event.currentTarget));
    }
    }

    private function dragDropHandler(event:DragEvent):void {

    var p:Point = new Point(event.stageX,event.stageY);

    Button(event.dragInitiator).x = event.target.globalToLocal(p).x
    Button(event.dragInitiator).y = event.target.globalToLocal(p).y

    }

    ]]&gt
    &lt/mx:Script&gt
    &ltmx:Canvas id=”myCanvas” x=”20″ y=”20″ width=”300″ height=”300″ backgroundColor=”#FFFFFF” borderStyle=”solid” dragEnter=”dragEnterHandler(event)” dragDrop=”dragDropHandler(event)”&gt
    &ltmx:Button id=”thumb” width=”40″ height=”20″ mouseMove=”mouseMoveHandler(event)” x=”10″ y=”25″/&gt
    &lt/mx:Canvas&gt
    &lt/mx:WindowedApplication&gt

  17. Vishal,

    Yes, i am able to see the dragImage in the example i gave.
    In the above example, the dragProxy image is at ‘/assets/proxy.jpg’. Make sure you give the correct path of the dragProxy image.

    Thanks

  18. Vishal,

    Its working absolutely fine at my end. Do you see any error?

  19. Swami,
    I guess its the problem with the AIR runtime. Coz i dont see the dragImage at all.I discussed this with one of my colleague, and he was saying the same application when launched in MAC gives the dragImage but not in Windows. Even in windows , in some it doesnot. Any insights into the AIR runtime issue???Whats the version of the runtime that you use??? Coz the same app that you gave doesnot give an image in my machine. I cant guess any other reason, do you?
    Thanks again,
    Vishal

  20. Vishal,

    Yes, there are many issues with AIR related to drag-and-drop. Let me find out more about this issue…
    The Version of AIR on my machine is 1.5.1.8210. Whats ur AIR Runtime Version?

    Thanks

  21. Mine is 1.5.2.8900..I hope we some how find out a solution to this..Thanks much for your efforts on this.

  22. Any updates???

  23. Vishal,

    Did you try with AIR 2.0??? Please try with it and let me know if you still face the same issue…

    Thanks

  24. Hi Vishal,
    We have to follow the same procedure even if we want to drag Image. All we have to do here is to create dragSource as Image.
    Here’s the code:
    (replace ‘&lt’ with ‘‘)
    &lt?xml version=”1.0″ encoding=”utf-8″?&gt
    &ltmx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”&gt
    &ltmx:Script&gt
    &lt![CDATA[
    import mx.controls.Image;
    import mx.managers.DragManager;
    import mx.core.DragSource;
    import mx.events.DragEvent;
    import flash.events.MouseEvent;
    // Embed icon image.
    [Embed(source='assets/proxy.jpg')]
    public var proxyImage:Class;
    private function mouseMoveHandler(event:MouseEvent):void{
    var dragInitiator:Image=Image(event.currentTarget);
    var ds:DragSource = new DragSource();
    ds.addData(dragInitiator, “image”);
    var myProxy:Image = new Image();
    myProxy.source = proxyImage;
    myProxy.width = 15;
    myProxy.height = 15;
    DragManager.doDrag(dragInitiator, ds, event, myProxy, -15, -15, 0.8);
    }
    private function dragEnterHandler(event:DragEvent):void {
    if (event.dragSource.hasFormat(”image”))
    {
    DragManager.acceptDragDrop(Canvas(event.currentTarget));
    }
    }
    private function dragDropHandler(event:DragEvent):void {
    Image(event.dragInitiator).x =
    Canvas(event.currentTarget).mouseX;
    Image(event.dragInitiator).y =
    Canvas(event.currentTarget).mouseY;
    }
    ]]&gt
    &lt/mx:Script&gt
    &ltmx:Canvas id=”myCanvas” x=”20″ y=”20″ width=”300″ height=”300″ backgroundColor=”#FFFFFF” borderStyle=”solid” dragEnter=”dragEnterHandler(event)” dragDrop=”dragDropHandler(event)”&gt
    &ltmx:Image id=”myImage” width=”50″ height=”50″ source=”@Embed(source=’assets/logo.jpg’)” mouseMove=”mouseMoveHandler(event)” /&gt
    &lt/mx:Canvas&gt
    &lt/mx:Application&gt

    BUt it is saying cannot convert Image into canvas…plz help me..i need it urgently..

  25. Hi Sushma,

    I am not seeing any such kind of errors. Whats exactly the error thats coming?
    Did you try the exact sample which i provided here?

    Thanks

  26. The previous one is giving an error saying that cannot convert Image into canvas…plz help me..i need it urgently..

  27. Thks for the quick response swami..
    I am not using that proxy..except that everything is same..

  28. Sushma,

    Whats the Version of Flex you are using? Are you checking this sample on AIR application or Flex Application? Which platform are you checking it on?
    As there are some differences with various Flex SDK versions and there are some issues with AIR when using drag-and-drop.

    Thanks

  29. Thank you Charan for quick response..

    Everything is wrkng 5n now..thk you..

  30. Hi!

    I am seeking answer to a very related issue, and I hope you can provide me a solution for this. I have a button, which is draggable on to a grid/canvas. I do not want the button to move, just create an instance (replicate) of it on-the-fly and make it appear on the canvas/grid, and also retain the original button at the source.

    The problem with creating an instance during mouseMove or dragDrop (event capture functions) is that they get created all the time, even when the button is dragged and dropped elsewhere.

    Note: I tried using List/DG to do this (dragMoveEnabled=false), and was not successful there.

    Thanks,
    Rajesh

  31. I wish I found your article earlier. I investigate into this for a while and I feel lucky to have found the DragManager during reading Flex source code.


Leave a comment

(required)

No trackbacks yet.