The list-based controls have built-in support for drag and drop, but you can use drag and drop with any Flex component. To support drag-and-drop operations with non-list-based components, or to explicitly control drag and drop with list-based controls, you must handle the drag and drop events.
You use the following classes to implement the drag-and-drop operation:
|
Class |
Function |
|---|---|
|
Manages the drag-and-drop operations; for example, its doDrag() method starts the drag operation. |
|
|
Contains the data being dragged. It also provides additional drag management features, such as the ability to add a handler that is called when data is requested. |
|
|
Represents the event object for all drag-and-drop events. |
A component that acts as a drag initiator handles the following events to manage the drag-and-drop operation:
|
Drag initiator |
Description |
Handler |
Implemented |
|---|---|---|---|
|
mouseDown and |
The mouseDown event is dispatched when the user selects a control with the mouse and holds down the mouse button. The mouseMove event is dispatched when the mouse moves. |
Yes, for nonlist controls |
No |
|
dragStart |
Dispatched by a list-based component when a drag operation starts. This event is used internally by the list-based controls; you do not handle it when implementing drag and drop. If you want to control the start of a drag-and-drop operation, use the mouseDown or mouseMove event. |
Yes, for list controls |
Yes |
|
dragComplete |
Dispatched when a drag operation completes, either when the drag data drops onto a drop target, or when the drag-and-drop operation ends without performing a drop operation. You can use this event to perform any final cleanup of the drag-and-drop operation. For example, if a user moves data from one component to another, you can use this event to delete the item from the drag initiator. For an example, see Example: Moving and copying data for a nonlist-based control. |
No |
Yes |
When adding drag-and-drop support to a component, you must implement an event handler for either the mouseDown or mouseMove event, and optionally for the dragComplete event. When you set the dragEnabled property to true for a list-based control, Flex automatically adds event handlers for the dragStart and dragComplete events.
To use a component as a drop target, you handle the following events:
|
Drop target |
Description |
Handler required |
Implemented |
|---|---|---|---|
|
dragEnter |
Dispatched when a drag proxy moves over the drop target from outside the drop target. A component must define an event handler for this event to be a drop target. The event handler determines whether the data being dragged is in an accepted format. To accept the drop, the event handler calls the DragManager.acceptDragDrop() method. You must call the DragManager.acceptDragDrop() method for the drop target to receive the dragOver, dragExit, and dragDrop events. In the handler, you can change the appearance of the drop target to provide visual feedback to the user that the component can accept the drag operation. For example, you can draw a border around the drop target, or give focus to the drop target. For an example, see Example: Simple drag-and-drop operation for a nonlist-based control. |
Yes |
Yes |
|
dragOver |
Dispatched while the user moves the mouse over the target, after the dragEnter event. You can handle this event to perform additional logic before allowing the drop operation, such as dropping data to various locations within the drop target, reading keyboard input to determine if the drag-and-drop operation is a move or copy of the drag data, or providing different types of visual feedback based on the type of drag-and-drop operation. For an example, see Example: Handling the dragOver and dragExit events for the drop target. |
No |
Yes |
|
dragDrop |
Dispatched when the user releases the mouse over the drop target. Use this event handler to add the drag data to the drop target. For an example, see Example: Simple drag-and-drop operation for a nonlist-based control. |
Yes |
Yes |
|
dragExit |
Dispatched when the user moves the drag proxy off of the drop target, but does not drop the data onto the target. You can use this event to restore the drop target to its normal appearance if you modified its appearance in response to a dragEnter event or other event. For an example, see Example: Handling the dragOver and dragExit events for the drop target. |
No |
Yes |
When adding drag-and-drop support to a nonlist-based component, you must implement an event handler for the dragEnter and dragDrop events, and optionally for the other events. When you set the dropEnabled property to true for a list-based control, Flex automatically adds event handlers for all events.
The following steps define the drag-and-drop operation.
Flex automatically makes the component an initiator when the user clicks and moves the mouse on the component.
The component must detect the user's attempt to start a drag operation and explicitly become an initiator. Typically, you use the mouseMove or mouseDown event to start the drag-and-drop operation.
The component then creates an instance of the mx.core.DragSource class that contains the data to be dragged, and specifies the format for the data.
The component then calls the mx.managers.DragManager.doDrag() method, to initiate the drag-and-drop operation.
You can define your own drag proxy image. For more information, see Example: Specifying the drag proxy.
Flex checks to see if the component can be a drop target.
The component must define an event handler for the dragEnter event to be a drop target.
The dragEnter event handler can examine the DragSource object to determine whether the data being dragged is in an accepted format. To accept the drop, the event handler calls the DragManager.acceptDragDrop() method. You must call the DragManager.acceptDragDrop() method for the drop target to receive the dragOver, dragExit, and dragDrop events.
Flex automatically adds the drag data to the drop target. If this is a copy operation, you have to implement the event handler for the dragDrop event for a list-based control. For more information, see Example: Copying data from one List control to another List control.
The drop target must define an event listener for the dragDrop event handler to add the drag data to the drop target.
If this is a move operation, Flex automatically removes the drag data from the drag initiator.
The drag initiator completes any final processing required. If this was a move operation, the event handler must remove the drag data from the drag initiator. For an example of writing the event handler for the dragComplete event, see Example: Moving and copying data for a nonlist-based control.
The following example lets you set the background color of a Canvas container by dropping either of two colors onto it. You are not copying or moving any data; instead, you are using the two drag initiators as a color palette. You then drag the color from one palette onto the drop target to set its background color.
The drag initiators, two Canvas containers, implement an event handler for the mouseDown event to initiate the drag and drop operation. This is the only event required to be handled by the drag initiator. The drop target is required to implement event handlers for the dragEnter and dragDrop events.
<?xml version="1.0"?>
<!-- dragdrop\DandDCanvas.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
backgroundColor="white">
<mx:Script>
<![CDATA[
import mx.core.DragSource;
import mx.managers.DragManager;
import mx.events.*;
import mx.containers.Canvas;
// Initializes the drag and drop operation.
private function mouseMoveHandler(event:MouseEvent):void {
// Get the drag initiator component from the event object.
var dragInitiator:Canvas=Canvas(event.currentTarget);
// Get the color of the drag initiator component.
var dragColor:int = dragInitiator.getStyle('backgroundColor');
// Create a DragSource object.
var ds:DragSource = new DragSource();
// Add the data to the object.
ds.addData(dragColor, 'color');
// Call the DragManager doDrag() method to start the drag.
DragManager.doDrag(dragInitiator, ds, event);
}
// Called when the user moves the drag proxy onto the drop target.
private function dragEnterHandler(event:DragEvent):void {
// Accept the drag only if the user is dragging data
// identified by the 'color' format value.
if (event.dragSource.hasFormat('color')) {
// Get the drop target component from the event object.
var dropTarget:Canvas=Canvas(event.currentTarget);
// Accept the drop.
DragManager.acceptDragDrop(dropTarget);
}
}
// Called if the target accepts the dragged object and the user
// releases the mouse button while over the Canvas container.
private function dragDropHandler(event:DragEvent):void {
// Get the data identified by the color format
// from the drag source.
var data:Object = event.dragSource.dataForFormat('color');
// Set the canvas color.
myCanvas.setStyle("backgroundColor", data);
}
]]>
</mx:Script>
<!-- A horizontal box with red and green canvases that the user can drag. -->
<mx:HBox>
<mx:Canvas
width="30" height="30"
backgroundColor="red"
borderStyle="solid"
mouseMove="mouseMoveHandler(event);"/>
<mx:Canvas
width="30" height="30"
backgroundColor="green"
borderStyle="solid"
mouseMove="mouseMoveHandler(event);"/>
</mx:HBox>
<mx:Label text="Drag a color onto the Canvas container."/>
<!-- Handles dragEnter and dragDrop events to allow dropping. -->
<mx:Canvas id="myCanvas"
width="100" height="100"
backgroundColor="#FFFFFF"
borderStyle="solid"
dragEnter="dragEnterHandler(event);"
dragDrop="dragDropHandler(event);"/>
<mx:Button id="b1"
label="Clear Canvas"
click="myCanvas.setStyle('backgroundColor', '0xFFFFFF');"
/>
</mx:Application>
The executing SWF file for the previous example is shown below:
The following sections describe the event handlers for the mouseDown, dragEnter, and dragDrop events.
Writing the mouseDown event handler
The event handler that initiates a drag-and-drop operation must do two things.
The DragSource object contains the drag data and a description of the drag data, called the data format. The event object for the dragEnter and dragDrop events contains a reference to this object in their dragSource property, which allows the event handlers to access the drag data.
You use the DragSource.addData() method to add the drag data and format to the DragSource object, where the addData() method has the following signature:
addData(data:Object, format:String):void
The format argument is a text string such as "color", "list data", or "employee record". In the event handler for the dragEnter event, the drop target examines this string to determine whether the data format matches the type of data that the drop target accepts. If the format matches, the drop target lets users drop the data on the target; if the format does not match, the target does not enable the drop operation.
One example of using the format string is when you have multiple components in your application that function as drop targets. Each drop target examines the DragSource object during its dragEnter event to determine if the drop target supports that format. For more information, see Handling the dragEnter event.
If you drag large or complex data items, consider creating a handler to copy the data, and specify it by calling the DragSource.addListener() method instead of using the DragSource.addData() method. If you do this, the data does not get copied until the user drops it, which avoids the processing overhead of copying the data if a user starts dragging data but never drops it. The implementation of the list-based classes use this technique.
The doDrag() method has the following signature:
doDrag(dragInitiator:IUIComponent, dragSource:DragSource, mouseEvent:MouseEvent,
dragImage:IFlexDisplayObject = null, xOffset:Number = 0, yOffset:Number = 0,
imageAlpha:Number = 0.5, allowMove:Boolean = true):void
The doDrag() method requires three arguments: a reference to the component that initiates the drag operation (identified by the event.currentTarget object); the DragSource object that you created in step 1, and the event object passed to the event handler.
Optional arguments specify the drag proxy image and the characteristics of the image. For an example that specifies a drag proxy, see Example: Specifying the drag proxy.
Flex generates a dragEnter event when the user moves the drag proxy over any control. A control must define a handler for a dragEnter event to be a drop target. The event handler typically performs the following actions:
The value of the action property of the event object for the dragEnter event is DragManager.MOVE, even if you are doing a copy. This is because the dragEnter event occurs before the drop target recognizes that the Control key is pressed to signal a copy.
The Flex default event handler for the dragOver event for a list-based control automatically sets the action property. For nonlist-based controls, or if you explicitly handle the dragOver event for a list-based control, use the DragManager.showFeedback() method to set the action property to a value that signifies the type of drag operation: DragManager.COPY, DragManager.LINK, DragManager.MOVE, or DragManager.NONE. For more information on the dragOver event, see Example: Handling the dragOver and dragExit events for the drop target.
The dragDrop event occurs when the user releases the mouse to drop data on a target, and the dragEnter event handler has called the DragManager.acceptDragDrop() method to accept the drop. You must define a handler for the event to add the drag data to the drop target.
The event handler uses the DragSource.dataForFormat() method to retrieve the drag data. In the previous example, the drag data contains the new background color of the drop target. The event handler then calls setStyle() to set the background color of the drop target.
Flex automatically defines default event handlers for the drag-and-drop events when you set dragEnabled or dropEnabled property to true for a list-based control. You can either use these default event handlers, which requires you to do no additional work in your application, or define your own event handlers.
There are three common scenarios for using event handlers with the list-based controls:
Use the default event handlers
When you set dragEnabled to true for a drag initiator, or when you set dropEnabled to true for a drop target, Flex handles all drag-and-drop events for you for a move. You only have to define your own dragDrop event handler when you want to copy data as part of the drag-and-drop operation. For more information, see Moving and copying data.
Define your own event handlers
If you want to control the drag-and-drop operation for a list-based control, you can explicitly handle the drag-and-drop events, just as you can for any component. In this scenario, set the dragEnabled property to false for a drag initiator, or set the dropEnabled property to false for a drop target. For more information on handling these events, see Example: Simple drag-and-drop operation for a nonlist-based control.
Define your own event handlers and use the default event handlers
You might want to add your own event handler for a drag-and-drop event, and also use the build in drag-and-drop handlers. In this case, your event handler executes first, then the default event handler provided by Flex executes. If, for any reason, you want to explicitly prohibit the execution of the default event handler, call the Event.preventDefault() method from within your event handler.
Because of the way data to a Tree control is structured, the Tree control handles drag and drop differently from the other list-based controls. For the Tree control, the event handler for the dragDrop event only performs an action when you move or copy data in the same Tree control, or copy data to another Tree control. If you drag data from one Tree control and drop it onto another Tree control to move the data, the event handler for the dragComplete event actually performs the work to add the data to the destination Tree control, rather than the event handler for the dragDrop event, and also removes the data from the source Tree control. This is necessary because to reparent the data being moved, Flex must remove it first from the source Tree control.
Therefore, if you call Event.preventDefault() in the event handler for the dragDrop or dragComplete events, you implement the drop behavior yourself. For more information, see Example: Moving and copying data for a nonlist-based control.
The following example modifies the example shown in the section Example: Moving and copying data for a nonlist-based control to define an event handler for the dragDrop event that accesses the data dragged from one DataGrid control to another. This event handler is executed before the default event handler for the dragDrop event to display in an Alert control the Artist field of each DataGrid row dragged from the drag initiator to the drop target:
<?xml version="1.0"?>
<!-- dragdrop\SimpleDGToDGAlert.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initApp();">
<mx:Script>
<![CDATA[
import mx.events.DragEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
private function initApp():void {
srcgrid.dataProvider = new ArrayCollection([
{Artist:'Carole King', Album:'Tapestry', Price:11.99},
{Artist:'Paul Simon', Album:'Graceland', Price:10.99},
{Artist:'Original Cast', Album:'Camelot', Price:12.99},
{Artist:'The Beatles', Album:'The White Album', Price:11.99}
]);
destgrid.dataProvider = new ArrayCollection([]);
}
// Define the event listener.
public function dragDropHandler(event:DragEvent):void {
// dataForFormat() always returns an Array
// for the list-based controls
// in case multiple items were selected.
var dragObj:Array=
event.dragSource.dataForFormat("items") as Array;
// Get the Artist for all dragged albums.
var artistList:String='';
for (var i:Number = 0; i < dragObj.length; i++) {
artistList+='Artist: ' + dragObj[i].Artist + '\n';
}
Alert.show(artistList);
}
]]>
</mx:Script>
<mx:HBox>
<mx:VBox>
<mx:Label text="Available Albums"/>
<mx:DataGrid id="srcgrid"
allowMultipleSelection="true"
dragEnabled="true"
dropEnabled="true"
dragMoveEnabled="true">
<mx:columns>
<mx:DataGridColumn dataField="Artist"/>
<mx:DataGridColumn dataField="Album"/>
<mx:DataGridColumn dataField="Price"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
<mx:VBox>
<mx:Label text="Buy These Albums"/>
<mx:DataGrid id="destgrid"
allowMultipleSelection="true"
dragEnabled="true"
dropEnabled="true"
dragMoveEnabled="true"
dragDrop="dragDropHandler(event);">
<mx:columns>
<mx:DataGridColumn dataField="Artist"/>
<mx:DataGridColumn dataField="Album"/>
<mx:DataGridColumn dataField="Price"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:HBox>
<mx:Button id="b1"
label="Reset"
click="initApp()"
/>
</mx:Application>
The executing SWF file for the previous example is shown below:
Notice that the dataForFormat() method specifies an argument value of "items". This is because the list-based controls have predefined values for the data format of drag data. For all list controls other than the Tree control, the format String is "items". For the Tree control, the format String is "treeItems".
Notice also that the return value of the dataForFormat() method is an Array. The dataForFormat() method always returns an Array for a list-based control, even if you are only dragging a single item, because list-based controls let you select multiple items.