Drag and drop examples

Example: Using a container as a drop target

To use a container as a drop target, you must use the backgroundColor property of the container to set a color. Otherwise, the background color of the container is transparent, and the Drag and Drop Manager is unable to detect that the mouse pointer is on a possible drop target.

In the following example, you use the Image control to load a draggable image into a Canvas container. You then add event handlers to let the user drag the Image control within the Canvas container to reposition it:

<?xml version="1.0"?>
<!-- dragdrop\DandDImage.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <fx:Script>
        <![CDATA[
            //Import classes so you don't have to use full names.
            import mx.managers.DragManager;
            import mx.core.DragSource;
            import mx.events.DragEvent;
            import flash.events.MouseEvent;

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

            // The mouseMove event handler for the Image control
            // initiates the drag-and-drop operation.
            private function mouseMoveHandler(event:MouseEvent):void 
            {                
                var dragInitiator:Image=Image(event.currentTarget);
                var ds:DragSource = new DragSource();
                ds.addData(dragInitiator, "img");               

                DragManager.doDrag(dragInitiator, ds, event);
            }
            
            // The dragEnter event handler for the Canvas container
            // enables dropping.
            private function dragEnterHandler(event:DragEvent):void {
                if (event.dragSource.hasFormat("img"))
                {
                    DragManager.acceptDragDrop(Canvas(event.currentTarget));
                }
            }

            // The dragDrop event handler for the Canvas container
            // sets the Image control's position by 
            // "dropping" it in its new location.
            private function dragDropHandler(event:DragEvent):void {
                Image(event.dragInitiator).x = 
                    Canvas(event.currentTarget).mouseX;
                Image(event.dragInitiator).y = 
                    Canvas(event.currentTarget).mouseY;
            }
        ]]>
    </fx:Script>
    
    <!-- The Canvas is the drag target --> 
    <mx:Canvas id="v1" 
        width="500" height="500"  
        borderStyle="solid" 
        backgroundColor="#DDDDDD"
        dragEnter="dragEnterHandler(event);" 
        dragDrop="dragDropHandler(event);">
        
        <!-- The image is the drag initiator. -->
        <s:Image id="myimg" 
            source="@Embed(source='assets/globe.jpg')" 
            mouseMove="mouseMoveHandler(event);"/> 
    </mx:Canvas>
</s:Application>

The executing SWF file for the previous example is shown below:

Example: Setting the drag indicator for Spark controls

The drag indicator defines the appearance of the dragged data during drag-and-drop operations. The appearance of the dragged data is determined by the item renderer for the list-based control.

By default, Spark item renderers use the normal view state to display the dragged data. For more information on view states in Spark item renderers, see Defining item renderer view states for a Spark container.

Spark item renderers support the optional dragging view state that you can use to control the appearance of the drag indicator. The following item renderer add the dragging view state to display the dragged data in a bold, blue italic font:
<?xml version="1.0" encoding="utf-8"?>
<!-- dragdrop\myComponents\MyListItemRenderer.mxml -->
<s:ItemRenderer focusEnabled="false" 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark">
   
    <s:states>
        <s:State name="normal" />            
        <s:State name="hovered" />
        <s:State name="selected" />
        <s:State name="normalAndShowsCaret"/>
        <s:State name="hoveredAndShowsCaret"/>
        <s:State name="selectedAndShowsCaret"/>
        <s:State name="dragging"/>
    </s:states>
    
    <s:Rect left="0" right="0" top="0" bottom="0">
        <s:stroke.normalAndShowsCaret>
            <s:SolidColorStroke 
                color="{getStyle('selectionColor')}" 
                weight="1"/>
        </s:stroke.normalAndShowsCaret>
        <s:stroke.hoveredAndShowsCaret>
            <s:SolidColorStroke 
                color="{getStyle('selectionColor')}" 
                weight="1"/>
        </s:stroke.hoveredAndShowsCaret>
        <s:stroke.selectedAndShowsCaret>
            <s:SolidColorStroke 
                color="{getStyle('selectionColor')}" 
                weight="1"/>
        </s:stroke.selectedAndShowsCaret>
        <s:fill>
            <s:SolidColor 
                color.normal="0xFFFFFF"
                color.normalAndShowsCaret="0xFFFFFF"
                color.hovered="{getStyle('rollOverColor')}"    
                color.hoveredAndShowsCaret="{getStyle('rollOverColor')}"
                color.selected="{getStyle('selectionColor')}"
                color.selectedAndShowsCaret="{getStyle('selectionColor')}"
                color.dragging="0xFFFFFF"/>
        </s:fill>
    </s:Rect>
    <s:Label id="labelDisplay" verticalCenter="0" left="3" right="3" top="6" bottom="4"
        fontStyle.dragging="italic" fontWeight.dragging="bold" color.dragging="blue"/>
</s:ItemRenderer>
The following application uses this item renderer:
<?xml version="1.0"?>
<!-- dragdrop\SimpleListToListMoveSparkDragIndicator.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    creationComplete="initApp();">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayList;

            private function initApp():void {
                srclist.dataProvider = 
                    new ArrayList(['Reading', 'Television', 'Movies']);
                destlist.dataProvider = new ArrayList([]);
            }
        ]]>
    </fx:Script>

    <s:HGroup>
        <s:VGroup>
            <s:Label text="Available Activities"/>
            <s:List id="srclist" 
                allowMultipleSelection="true"
                dragEnabled="true"
                dragMoveEnabled="true"
                itemRenderer="myComponents.MyListItemRenderer"/>
        </s:VGroup>

        <s:VGroup>
            <s:Label text="Activities I Like"/>
            <s:List id="destlist" 
                dropEnabled="true"/>
        </s:VGroup>
    </s:HGroup>

    <s:Button id="b1" 
        label="Reset"
        click="initApp();"/>
</s:Application>

The executing SWF file for the previous example is shown below:

Example: Specifying the drag indicator by using the DragManager

In the event handler for the mouseDown or mouseUp event, you can optionally specify a drag indicator in the doDrag() method of the DragManager class. If you do not specify a drag indicator, Flex uses a default drag indicator. The doDrag() method takes the following optional arguments to specify the drag indicator and its properties.

Argument

Description

dragIndicator

The image that defines the drag indicator.

To specify a symbol, such as a JPEG image of a product that a user wants to order, use a string that specifies the symbol’s name, such as myImage.jpg.

To specify a component, such as a Flex container or control, create an instance of the control or container, configure and size it, and then pass it as an argument to the doDrag() method.

xOffset

Number that specifies the x offset, in pixels, for the dragImage. This argument is optional. If omitted, the drag indicator is shown at the upper-left corner of the drag initiator. The offset is expressed in pixels from the left edge of the drag indicator to the left edge of the drag initiator, and is usually a negative number.

yOffset

Number that specifies the y offset, in pixels, for the dragImage. This argument is optional. If omitted, the drag indicator is shown at the upper-left corner of the drag initiator. The offset is expressed in pixels from the top edge of the drag indicator to the top edge of the drag initiator, and is usually a negative number.

imageAlpha

A Number that specifies the alpha value used for the drag indicator image. If omitted, Flex uses an alpha value of 0.5. A value of 0 corresponds to transparent and a value of 1.0 corresponds to fully opaque.

You must specify a size for the drag indicator, otherwise it does not appear. The following example modifies the example in Example: Using a container as a drop target to use a 15 pixel by 15 pixel Image control as the drag indicator:

<?xml version="1.0"?>
<!-- dragdrop\DandDImageProxy.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <fx:Script>
        <![CDATA[
            //Import classes so you don't have to use full names.
            import mx.managers.DragManager;
            import mx.core.DragSource;
            import mx.events.DragEvent;
            import flash.events.MouseEvent;

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

            // The mouseMove event handler for the Image control
            // initiates the drag-and-drop operation.
            private function mouseOverHandler(event:MouseEvent):void 
            {                
                var dragInitiator:Image=Image(event.currentTarget);
                var ds:DragSource = new DragSource();
                ds.addData(dragInitiator, "img");               

                // The drag manager uses the Image control 
                // as the drag indicator and sets the alpha to 1.0 (opaque),
                // so it appears to be dragged across the Canvas.
                var imageProxy:Image = new Image();
                imageProxy.source = globeImage;
                imageProxy.height=15;
                imageProxy.width=15;                
                DragManager.doDrag(dragInitiator, ds, event, 
                    imageProxy, -15, -15, 1.00);
            }
            
            // The dragEnter event handler for the Canvas container
            // enables dropping.
            private function dragEnterHandler(event:DragEvent):void {
                if (event.dragSource.hasFormat("img"))
                {
                    DragManager.acceptDragDrop(Canvas(event.currentTarget));
                }
            }

            // The dragDrop event handler for the Canvas container
            // sets the Image control's position by 
            // "dropping" it in its new location.
            private function dragDropHandler(event:DragEvent):void {
                Image(event.dragInitiator).x = 
                    Canvas(event.currentTarget).mouseX;
                Image(event.dragInitiator).y = 
                    Canvas(event.currentTarget).mouseY;
            }
        ]]>
    </fx:Script>
    
    <!-- The Canvas is the drag target --> 
    <mx:Canvas id="v1" 
        width="500" height="500"  
        borderStyle="solid" 
        backgroundColor="#DDDDDD"
        dragEnter="dragEnterHandler(event);" 
        dragDrop="dragDropHandler(event);">
        
        <!-- The image is the drag initiator. -->
        <s:Image id="myimg" 
            source="@Embed(source='assets/globe.jpg')" 
            mouseMove="mouseOverHandler(event);"/> 
    </mx:Canvas>
</s:Application>

The executing SWF file for the previous example is shown below:

To use a control with specific contents, such as a VBox control with a picture and label, you must create a custom component that contains the control or controls, and use an instance of the component as the dragIndicator argument.

Example: Setting the drop indicator for Spark controls

The drop indicator shows where the dragged data will be inserted into the drop target. The appearance of the drop indicator is controlled by the skin class of the drop target. By default, the drop indicator for a Spark control is a solid line that spans the width of the control.

You can create a custom drop indicator by creating a custom skin class for the drop target. In your skin class, create a skin part named dropIndicator in the <fx:Declarations> area of the skin class, as the following example shows:

<fx:Declarations> 
    <fx:Component id="dropIndicator"> 
        <s:Group includeInLayout="false" 
            minWidth="4" minHeight="4" 
            maxWidth="4" maxHeight="4"> 
            <s:Line xFrom="0" xTo="10" yFrom="5" yTo="5"> 
                <s:stroke> 
                    <s:SolidColorStroke color="blue" weight="2"/> 
                </s:stroke> 
            </s:Line> 
            <s:Line xFrom="5" xTo="10" yFrom="0" yTo="5"> 
                <s:stroke> 
                    <s:SolidColorStroke color="blue" weight="2"/> 
                </s:stroke> 
            </s:Line> 
            <s:Line xFrom="5" xTo="10" yFrom="10" yTo="5"> 
                <s:stroke> 
                    <s:SolidColorStroke color="blue" weight="2"/> 
                </s:stroke> 
            </s:Line> 
        </s:Group> 
    </fx:Component> 
</fx:Declarations>

This code shows an excerpt from the MyListSkin.mxml file, a custom skin class for the Spark List control. The lines define the drop indicator to be a blue arrow that appears in the drop target to indicate where the dragged data is added.

Note that the bounding Group container specifies the maxWidth and maxHeight properties. For the drop indicator, only the setting along the major axis of the control is applied. For example, for a List class using vertical layout, the x-axis is the major axis. Therefore, the maxHeight property is applied and the maxWidth property is ignored.

The Spark layout classes use the following rules to size and position the drop indicator:
  1. The drop indicator’s size is calculated to be as big as the gap between the neighboring data items in the control.

    Any minimum or maximum setting in the major axis of orientation is honored. Along the minor axis, minimum and maximum settings are ignored. The drop indicator is sized to be as wide as the respective column and as tall as the respective row.

  2. After drop indicator is sized, it is centered within the gap between the data items.

The following example uses the MyListSkin.mxml skin class:
<?xml version="1.0"?>
<!-- dragdrop\SimpleListToListMoveSparkDropIndicator.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    creationComplete="initApp();">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayList;             
            import myComponents.MyListSkin;

            private function initApp():void {
                srclist.dataProvider = 
                    new ArrayList(['Reading', 'Television', 'Movies']);
                destlist.dataProvider = new ArrayList([]);
            }
        ]]>
    </fx:Script>

    <s:HGroup>
        <s:VGroup>
            <s:Label text="Available Activities"/>
            <s:List id="srclist" 
                allowMultipleSelection="true"
                dragEnabled="true"
                dragMoveEnabled="true"/>
        </s:VGroup>

        <s:VGroup>
            <s:Label text="Activities I Like"/>
            <s:List id="destlist" 
                dropEnabled="true"
                skinClass="myComponents.MyListSkin"/>
        </s:VGroup>
    </s:HGroup>

    <s:Button id="b1" 
        label="Reset"
        click="initApp();"/>
</s:Application>

The executing SWF file for the previous example is shown below:

Example: Setting the cursor styles of the DragManager

The DragManager uses styles to control the display of the different cursors used during the drag and drop operation, such as the move and copy cursors. The cursors are defined as symbols in the Assets.swf file in the flexInstallDir\frameworks\projects\framework\assets directory.

By default, Flex defines the cursor styles as if you had used the following type selector in your application:

<fx:Style> 
    @namespace mx "library://ns.adobe.com/flex/mx"; 
 
    mx|DragManager 
    { 
        copyCursor: Embed(source="Assets.swf",symbol="mx.skins.cursor.DragCopy"); 
        defaultDragImageSkin: ClassReference("mx.skins.halo.DefaultDragImage"); 
        linkCursor: Embed(source="Assets.swf",symbol="mx.skins.cursor.DragLink"); 
        moveCursor: Embed(source="Assets.swf",symbol="mx.skins.cursor.DragMove"); 
        rejectCursor: Embed(source="Assets.swf",symbol="mx.skins.cursor.DragReject"); 
    } 
</fx:Style>
Use the <fx:Style> tag to define your own assets to use for the cursors. The following example replaces the copy cursor with a custom cursor:
<?xml version="1.0"?>
<!-- dragdrop\SimpleListToListMoveStyles.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    creationComplete="initApp();">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    
    <fx:Style>
        @namespace mx "library://ns.adobe.com/flex/mx";

        mx|DragManager
        {
            copyCursor: Embed(source="assets/globe.jpg");
        }         
    </fx:Style>
    
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;

            private function initApp():void {
                srclist.dataProvider = 
                    new ArrayCollection(['Reading', 'Television', 'Movies']);
                destlist.dataProvider = new ArrayCollection([]);
            }
        ]]>
    </fx:Script>

    <s:HGroup>
        <s:VGroup>
            <s:Label text="Available Activities"/>
            <s:List id="srclist" 
                allowMultipleSelection="true"
                dragEnabled="true"
                dragMoveEnabled="true"/>
        </s:VGroup>

        <s:VGroup>
            <s:Label text="Activities I Like"/>
            <s:List id="destlist" 
                dropEnabled="true"/>
        </s:VGroup>
    </s:HGroup>

    <s:Button id="b1" 
        label="Reset"
        click="initApp();"/>
</s:Application>

The executing SWF file for the previous example is shown below:

Example: Handling the dragOver and dragExit events for the drop target

The dragOver event occurs when the user moves the mouse over a drag-and-drop target whose dragEnter event handler has called the DragManager.acceptDragDrop() method. This event is dispatched continuously as the user drags the mouse over the target. The dragOver event handler is optional; you do not have to define it to perform a drag‑and-drop operation.

The dragOver event is useful for specifying the visual feedback that the user gets when the mouse is over a drop target. For example, you can use the DragManager.showFeedback() method to specify the drag-feedback indicator that appears along with the drag indicator. This method uses four constant values for the argument, as the following table shows:

Argument value

Icon

DragManager.COPY

A green circle with a white plus sign indicating that you can perform the drop.

DragManager.LINK

A grey circle with a white arrow sign indicating that you can perform the drop.

DragManager.MOVE

A plain arrow indicating that you can perform the drop.

DragManager.NONE

A red circle with a white x appears indicating that a drop is prohibited. This is the same image that appears when the user drags over an object that is not a drag target.

You typically show the feedback indicator based on the keys pressed by the user during the drag-and-drop operation. The DragEvent object for the dragOver event contains Boolean properties that indicate whether the Control or Shift keys are pressed at the time of the event: ctrlKey and shiftKey, respectively. No key pressed indicates a move, the Control key indicates a copy, and the Shift key indicates a link. You then call the showFeedback() method as appropriate for the key pressed.

Another use of the showFeedback() method is that it determines the value of the action property of the DragEvent object for the dragDrop, dragExit, and dragComplete events. If you do not call the showFeedback() method in the dragOver event handler, the action property of the DragEvent is always set to DragManager.MOVE.

The dragExit event is dispatched when the user drags the drag indicator off the drop target, but does not drop the data onto the target. You can use this event to restore any visual changes that you made to the drop target in the dragOver event handler.

In the following example, you set the dropEnabled property of a List control to true to configure it as a drop target and to use the default event handlers. However, you want to provide your own visual feedback, so you also define event handlers for the dragEnter, dragExit, and dragDrop events. The dragOver event handler completely overrides the default event handler, so you call the Event.preventDefault() method to prohibit the default event handler from execution.

The dragOver event handler determines whether the user is pressing a key while dragging the drag indicator over the target, and sets the feedback appearance based on the key that is pressed. The dragOver event handler also sets the border color of the drop target to green to indicate that it is a viable drop target, and uses the dragExit event handler to restore the original border color.

For the dragExit and dragDrop handlers, you only want to remove any visual changes that you made in the dragOver event handlers, but otherwise you want to rely on the default Flex event handlers. Therefore, these event handlers do not call the Event.preventDefault() method:
<?xml version="1.0"?>
<!-- dragdrop\DandDListToListShowFeedbackSpark.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    creationComplete="initApp();">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.events.DragEvent;
            import mx.managers.DragManager;
            import spark.layouts.supportClasses.DropLocation;
    
            private function initApp():void {
                firstList.dataProvider = new ArrayCollection([
                    {label:"First", data:"1"},
                    {label:"Second", data:"2"},
                    {label:"Third", data:"3"},
                    {label:"Fourth", data:"4"}
                ]);
                secondList.dataProvider = new ArrayCollection([]);
            }

            // Variable to store original border color.
            private var tempBorderColor:uint;
            
            // Flag to indicate that tempBorderColor has been set.
            private var borderColorSet:Boolean = false;

            private function dragOverHandler(event:DragEvent):void {
            
                // Explpicitly handle the dragOver event.            
                event.preventDefault();
                
                // Since you are explicitly handling the dragOver event,
                // call showDropIndicator() to have the drop target
                // display the drop indicator.
                // The drop indicator is removed
                // automatically for the list controls by the built-in 
                // event handler for the dragDrop event.
                var dropLocal:DropLocation = 
                    event.currentTarget.layout.calculateDropLocation(event);
                event.currentTarget.layout.showDropIndicator(dropLocal);
            
                if (event.dragSource.hasFormat("itemsByIndex"))
                {
                    // Set the border to green to indicate that 
                    // this is a drop target.
                    // Since the dragOver event is dispatched continuosly 
                    // as you move over the drop target, only set it once.
                    if (borderColorSet == false) {                 
                        tempBorderColor = 
                            event.currentTarget.getStyle('borderColor');
                        borderColorSet = true;
                    }
                
                    // Set the drag-feedback indicator based on the 
                    // type of drag-and-drop operation.
                    event.currentTarget.setStyle('borderColor', 'green');
                    if (event.ctrlKey) {                    
                        DragManager.showFeedback(DragManager.COPY);
                        return;
                    }
                    else if (event.shiftKey) {
                        DragManager.showFeedback(DragManager.LINK);
                        return;
                    }
                    else {
                        DragManager.showFeedback(DragManager.MOVE);
                        return;
                    }
                }

                // Drag not allowed.
                DragManager.showFeedback(DragManager.NONE);                
            }
            
            private function dragDropHandler(event:DragEvent):void {
                dragExitHandler(event);
            }            

            // Restore the border color.
            private function dragExitHandler(event:DragEvent):void {
              event.currentTarget.setStyle('borderColor', tempBorderColor);
              borderColorSet = true;
            }
        ]]>
    </fx:Script>

    <s:HGroup id="myHG">
        <s:List  id="firstList" 
            dragEnabled="true"
            dragMoveEnabled="true"/>

        <s:List  id="secondList" 
            dropEnabled="true"
            dragOver="dragOverHandler(event);"
            dragDrop="dragExitHandler(event);"
            dragExit="dragExitHandler(event);"/>
    </s:HGroup>
    
    <s:Button id="b1" 
        label="Reset"
        click="initApp();"/>
</s:Application>

The executing SWF file for the previous example is shown below: