Enabling Drag and Drop Functionality

Introduction

You can add drag and drop functionality to your components. Drag and drop is a user interface action that involves dragging components or objects using a pointing device and dropping them over other objects on the frame, which results in some action. For example, a text entry control that contains the name of a SAS table could be dragged onto a data table component and then dropped to display the contents of the SAS table in the data table. Drag and drop also works between objects on different frames.
Components that can be dragged are called drag sites, and components that can receive a dropped object are called drop sites. When an object is dropped, data is passed to the drop site and an action occurs. The action is determined by:
  • the form in which the data is passed, called the data representation
  • the type of drop operation
The data representation is the form of data that a drag site is capable of transferring and a drop site is capable receiving. The data can be as simple as a string of text or as complex as an SCL list. SAS/AF components that are based on the SAS Component Object Model (SCOM) use
characterData
a generic representation to indicate when a character string is passed as the data
numericData
a generic representation to indicate when numeric values are passed as the data
Legacy objects support three default data representations, all of which provide data in a character string:
  • _DND_TEXT for text
  • _DND_FILE for an external file
  • _DND_DATASET for a SAS table
Additional data representations enable you to drag items from the SAS Explorer and to drop them onto a frame. These representations include
  • EXPLORER_DATASET_NAME for SAS tables. The data is provided in a character string that contains the two-level SAS table name, such as sasuser.fitness.
  • EXPLORER_CATALOG_NAME for SAS catalogs. The data is provided in a character string that contains the two-level SAS catalog name, such as sashelp.classes.
  • EXPLORER_ENTRY_NAME for a specific catalog entry. When the selection includes a single entry, the data is provided in a character string that contains the four-level entry name, such as work.a.a.scl. When the selection includes two or more entries, the data is provided in a list in which each item is a character string that contains the four-level entry name.
  • EXPLORER_MEMBER_NAME for multiple selections from a SAS library. When the selection includes a single entry, the data is provided in a character string that contains a two-level member name for catalogs or a three-level member name for SAS tables (including those of type VIEW). When the selection includes two or more members, the data is provided in a list in which each item is a character string. Note that multiple selections of SAS tables or SAS catalogs are sent in an EXPLORER_MEMBER_NAME data representation.
If you create a component based on SCOM architecture, you can define your own data representations if characterData and numericData do not meet your needs.
A data representation is essentially a “verbal contract” between two components. As a component developer, you must name the data representation. For example, the characterData representation states that whatever component uses this as its drag representation, the drag site will send a valid character value as the data. The drop site then expects to receive a character value and can react accordingly.
It is recommended that component developers fully document their data representations so that other developers can know how to use them. See How the Drag and Drop Component Works for more information about the data representation.
Drop operations define actions that are performed on the data representation:
Copy
Data is provided with no post-processing.
Link
Some mechanism synchronizes the source and destination.
Move
Data is provided and the source is removed.
Copy is the default.
Drag and drop operations in SAS/AF software have the following limitations:
  • You cannot drag objects outside the SAS environment.
  • Slider and scroll bar components do not support drag and drop operations.
  • The behavior of drag and drop operations may vary according to the host environment.

What Happens during a Drag and Drop Action

SAS/AF software performs several operations to enable drag and drop functionality between two components. When the object is instantiated, SAS/AF checks to see whether the object's dragEnabled or dropEnabled attributes are set to “Yes.” If either of these attribute values is “Yes”, then:
  1. the object is queried for its respective dragInfo or dropInfo attribute values.
    • For drag sites, SAS/AF reads the value or values of the dragInfo attribute's dataRepresentation and invokes the _addDragRep method to register each drag data representation that is supported by the object.
    • For drop sites, SAS/AF reads the value or values of the dropInfo attribute's dataRepresentation and invokes the _addDropRep method to register each drop data representation that is supported by the object.
  2. the object is queried for its dragOperations and/or dropOperations settings.
    • For drag sites, the _addDragOp method is invoked to register the supported drag operations for the object.
    • For drop sites, the _addDropOp method is invoked to register the supported drop operations for the object.
SAS/AF permits a drop action to occur between two objects only when the two objects have a shared data representation and a shared operation. For example, consider an object named listbox1 that is enabled as a drag site using the characterData representation. A second object named listbox2 is enabled as a drop site using the characterData representation. A user can successfully complete a drag and drop action between the two objects if they share a common operation such as “Copy.” Objects that do not have a matching data representation and a matching operation will not permit a drop action to occur.
When a valid drag and drop action occurs between two objects, SAS/AF takes the value of an attribute on the drag site and sets it as the value of another attribute on the drop site. Several methods are invoked automatically, passing an instance of sashelp.classes.DragAndDrop.class:
  1. The _startDrag method is invoked on the drag site. An instance of the Drag and Drop component is created.
  2. The _respondToDragOnto method is invoked on a drop site as the cursor passes over a valid drop site.
  3. The _respondToDragOff method is invoked on a drop site as the cursor moves off of a valid drop site.
  4. The _getDragData method is invoked on the drag site after the user has released the drag item on a valid drop site. The implementation of this method prepares the data that is passed between the drag site and the drop site. It also sets the values of the appropriate attributes (including attributeName and attributeValue) on an instance of the Drag and Drop component before passing the object. The default implementation of _getDragData
    • determines whether an attributeName is defined for the associated dragInfo data representation that was selected for the drop
    • retrieves the current value of the named attribute and sets its name on the drag and drop object's attributeName attribute
    • retrieves the current value for that attribute and sets the value of the drag and drop object's attributeValue attribute
    If no attributeName is specified as part of the dragInfo data representation, then you can override the _getDragData method to set the value of the drag and drop object's attributeValue attribute.
  5. The _validateDropData method is invoked on the drop site to perform any validation that may be required.
  6. The _completeDrag method is invoked on the drag site to complete any processing.
  7. The _drop method is invoked on the drop site. The default implementation of the _drop method
    • retrieves the attributeName from the drop site's dropInfo attribute based on the selected data representation
    • sets the value of the attribute identified in attributeName using the attributeValue that was passed in the drag and drop object
    If no attributeName exists on the drop site, you can override the _drop method and implement the desired drop behavior.

Adding Drag and Drop Functionality to Your Components

Defining Drag and Drop Properties

For most of your applications, you will likely find it sufficient to implement drag and drop functionality simply by setting attribute values.
You can define drag and drop properties for any component that is a subclass of the Widget class or the Frame class. To implement drag and drop, you define the drag properties of the component that you designate as a drag site, as well as the drop properties of another component that you want to behave as a drop site. A component can act as both a drag site and a drop site.
The following attributes must be defined for drag sites. You might want to review these attribute settings if you encounter problems with a drag site, or if you want more information on a drag site's behavior.
dragEnabled
sets the state that determines whether the object can be dragged when selected. In many cases, setting this attribute to “Yes” is all that is needed to implement a drag site that uses default values for dragInfo and dragOperations.
dragInfo
sets the information that defines what information is transferred from the object. The dragInfo attribute is a list that includes a named item for each drag representation:
  • the dataRepresentation, which is a name that describes the type of data that a drag site is capable of transferring. The data can be as simple as a string of text or as complex as an SCL list.
  • the attributeName, which is the name of the attribute whose value is passed to the drop site. (The drop site value is specified in the dropInfo attribute.)
A component becomes a drag site when its data representation is defined. More than one data representation can be defined for the drag site. When a component is dropped onto another component, the system checks through the list of representations for each component and chooses the first matching representation. This matching representation enables the drag site to format the data in the required representation. The drop site is then given its data, which it processes accordingly.
dragOperations
enables the object to indicate the action that occurs following the drag: Copy, Move, and/or Link. By default, an object allows the COPY action.
The following attributes must be defined for drop sites. You might want to review these attribute settings if you encounter problems with a drop site, or want more information on a drop site's behavior.
dropEnabled
sets the state that determines whether the control can serve as a drop site. In many cases, setting this attribute to Yes is all that is needed to implement a drop site using default values for dropInfo and dropOperations.
dropInfo
sets the information that defines what information is transferred to the object. The dropInfo attribute is a list that includes a named item for each drop representation:
  • the attributeName, which is the name of the attribute whose value is set by the value of attributeName in the dragInfo attribute. (The _setAttributeValue method is called on the drop site's attribute, using the value of the attribute specified in the dragInfo attribute.)
  • the dataRepresentation, which is a name that describes the type of data that a drop site is capable of receiving. The data can be as simple as a string of text or as complex as an SCL list. It is compared to the drag site's dataRepresentation. If a match exists, the drop action occurs.
    More than one data representation can be defined. The last representation defined is given the highest priority.
dropOperations
defines the drag actions that the drop site can handle: Copy, Move, and/or Link. By default, an object allows the Copy action. In addition to the drag actions, you can specify the associated method that runs when the drop occurs, as well as the pop-up menu text that is displayed for non-default drag and drop processing.

Overriding Drag and Drop Methods

SAS/AF software implements the drag and drop process by automatically executing several methods. Although you can override these methods to add functionality, you cannot directly call them from an SCL program.
Drag and Drop Methods to Overrride
Override this method...
To...
_startDrag
change the appearance or other state of the drag site when the drag begins. The code that you add runs when the user begins dragging from the drag site.
_respondToDragOnto
change the appearance or other state of the drop site as the dragged item passes over it. For example, you could change the drop site's color to indicate that a drop action is valid.
_respondToDragOff
change the appearance or other state of the drop site as the dragged item moves off of the drop site. For example, you could change the drop site back to its normal appearance if it was changed by _respondToDragOnto.
_getDragData
prepare the data that you want to pass to the drop site by setting attributes of the drag and drop object that is passed between all drag and drop methods.
_validateDropData
perform any validation that you require before the drop action occurs. For example, you can verify the range of data that is passed and then set the completeDrag attribute of the drag and drop object to either “Yes” or “No” to indicate whether the drop can occur. If completeDrag is set to “No,” then the drop is cancelled, the _drop method does not run, and no attribute values are set on the drop site.
_completeDrag
complete processing on the drag site. For example, consider two list boxes on a frame that have the same data representation and a “Move” operation. When the item is dragged from the first list box and dropped onto the second, the “Move” operation indicates that the item should be removed from the first list box. You can override _completeDrag to implement the “Move” operation and delete the item from the drag site.
Note: The _completeDrag method runs even if the drag and drop object's completeDrag attribute is set to “No.” If necessary, check the value of this attribute before executing the code.
_drop
process the drop action. This method executes only if the completeDrag attribute of the drag and drop object is set to “Yes.” Typically, you override this method to perform some action other than setting the appropriate attribute value on the drop site. For example, you may want to display a message box that confirms the success of the drag and drop action.

How the Drag and Drop Component Works

All drag and drop methods contain signatures that accept an object as their only argument. This object is an instance of the Drag and Drop Component (that is, sashelp.classes.DragAndDrop.class). It provides a container for the standard drag and drop information that is passed between a drag site and a drop site. The information is stored in the following attributes of the Drag and Drop component:
attributeName
is the name of the attribute that is passed from the drag site to the drop site.
attributeValue
is the value of the attribute that is passed from the drag site. This attribute is a complex attribute whose type is sashelp.classes.type.class, which contains the following attributes:
type represents the type of the attribute value (Character, Numeric, List, Object). Based on the value of type, you can query the appropriate “value” attribute.
characterValue stores the character value if type is “Character.”
listValue stores the SCL list if type is “List.”
numericValue stores the numeric value if type is “Numeric.”
objectValue stores the object identifier if type is “Object.”
For example, your method override could contain code as follows:
drop: public method    
  dndObj:input:object;
    if dndObj.attributeValue.type = 'Character' then
       do;
          /* Retrieve dndObj.attributeValue.characterValue, */
          /* then add any other code.                       */
       end;
    else if dndObj.attributeValue.type = 'Numeric' then
        do;
          /* Retrieve dndObj.attributeValue.numericValue,   */
          /* then add any other code.                       */
        end;
          /* and so forth... */
 endmethod; 
completeDrag
is a character attribute that you can optionally set to indicate whether a drop can successfully occur. For example, you can perform validation in an override of the _validateDropData method, and you can cancel the drop action by setting this attribute to “No”. You can also query this value in an override of the _completeDrag method to verify whether the drop will actually occur before performing some action on the drag site.
dataOperation
is the selected drag operation; corresponds to one of the operations specified in the dragOperations attribute that is set on the drag site.
dataRepresentation
is the form of the data that is passed between sites; corresponds to the particular list item in the dragInfo attribute that is set on the drag site
dragSiteID
is the object identifier of the drag site
dragSiteLocation
indicates whether the drag started inside or outside of the current frame
XLocation
specifies the x location of the drag site or drop site
YLocation
specifies the y location of the drag site or drop site
For components that are based on the SCOM architecture, the data specified in the data representation (such as characterData or EXPLORER_MEMBER_NAME) is stored in the attributeValue complex attribute of the Drag and Drop component. If one item is dragged, then the data is stored in the characterValue attribute of the attributeValue object. If two or more items are dragged, then the data is stored in the listValue attribute as separate character items.
For complete details on the Drag and Drop component, see SAS/AF Component Reference” in the SAS/AF online Help.

Drag and Drop Example

To demonstrate how drag and drop functionality works, consider an example that defines two subclasses of the list box control. The first subclass defines an object that is a drag site. The second defines an object that is a drop site. (Of course, it is entirely possible to have a single object that is both a drag site and a drop site.)
To create the first list box subclass:
  1. Using the Class Editor, create a new class whose parent is sashelp.classes.ListBox_c.class and whose description is Source List Box.
  2. Select the Attributes node, and then set the following values for attributes:
    • Select the dragEnabled attribute, and then right-click and select Override from the pop-up menu. Set the Initial Value to Yes.
    • Select the dragOperations attribute, and then right-click and select Override from the pop-up menu. Click the ellipsis (...) button in the Initial Value field. In the dragOperations dialog box, deselect the Enabled check box for Default Copy Operation and then select the Enabled check box for Default Move Operation. Click OK to return to the Class Editor.
  3. Save the class as sasuser.test.SourceListBox.class.
  4. Select the Methods node, and then select the second _completeDrag method in the list (with the O:SASHELP.CLASSES.DRAGANDDROP.CLASS signature). Right-click and select Override from the pop-up menu, and then select Source from the pop-up menu. Add the following SCL code to sasuser.test.SourceListBox.scl:
    USECLASS sasuser.test.SourceListBox.class;
    
    /* Override of the _completeDrag method */
    completeDrag: method dndobj:sashelp.classes.draganddrop.class;
       dcl num rc;
       /* If the rep is one that is not understood, call super. */
       if ( upcase(dndobj.dataRepresentation) = 'CHARACTERDATA' ) then
       do;
          /* Check the status of the completeDrag attribute. */
          if ( ( upcase(dndobj.completeDrag) = 'YES' ) AND
               ( upcase(dndobj.dataOperation) = 'MOVE' ) ) then
          do;
              /* Remove the selected item from the items list. */
              if (selectedIndex ^= 0 ) then
              do;
                 /* Delete the item from the items attribute. */
                 rc = delitem(items, selectedIndex );
    
                 /* Set the items attribute equal to itself to */
                 /* update this list box.                      */
                 items = items;
              end;
          end;
       end;
       else
          _super( dndobj );
    endmethod;
    enduseclass;
  5. Compile and save the code, and then close the Source window.
  6. Close the Class Editor.
To create the second list box subclass:
  1. Using the Class Editor, create a new class whose parent is sashelp.classes.ListBox_c.class and whose description is Target List Box.
  2. Select the Attributes node, and then set the following values for attributes:
    • Select the dropEnabled attribute, and then right-click and select Override from the pop-up menu. Set the Initial Value to Yes.
    • Select the dropInfo attribute, and then right-click and select Override from the pop-up menu. Click the ellipsis (...) button in the Value field. In the Drop Info dialog box, click Add, and then set the Drop Attribute to a blank value and the Drop Representation to CHARACTERDATA.
    • Select the dropOperations attribute, and then right-click and select Override from the pop-up menu. Click the ellipsis (...) button in the Value field. In the dropOperations dialog box, deselect the Enabled check box for Default Copy Operation and then select the Enabled check box for Default Move Operation.
  3. Save the class as sasuser.test.TargetListBox.class.
  4. Select the Methods node, and then select the second _drop method in the list (with the O:SASHELP.CLASSES.DRAGANDDROP.CLASS signature). Right-click and select Override from the pop-up menu. Select the second _validateDropData method in the list (with the O:SASHELP.CLASSES.DRAGANDDROP.CLASS signature). Right-click and select Override from the pop-up menu, and then select Source from the pop-up menu. Add the following SCL code to implement both methods in sasuser.test.TargetListBox.scl:
    USECLASS sasuser.test.TargetListBox.class;
    
    /* Override of the _validateDropData method */
    validateDropData: method dndobj:sashelp.classes.draganddrop.class;
       /* If the rep is one that is not understood, call super. */
       if ( upcase(dndobj.dataRepresentation) = 'CHARACTERDATA' ) then
       do;
          /* Ensure that the type is 'Character' and that the */
          /* value is not blank.  If either of these checks   */
          /* fail, then do not let the drop happen.           */
          if ( ( upcase(dndobj.attributeValue.type) ^= 'CHARACTER' ) OR
               ^length( dndobj.attributeValue.characterValue ) ) then
             dndobj.completeDrag = 'No';
       end;
       else _super( dndobj );
    endmethod;
    
    /* Override of the _drop method */
    drop: method dndobj:sashelp.classes.draganddrop.class;
       dcl num rc;
       /* If the rep is one that is not understood, call super. */
       if ( upcase(dndobj.dataRepresentation) = 'CHARACTERDATA' ) then
       do;
          /* Ensure that the attributeValue is the correct type. */
          /* If so, then insert it at the end of the items list. */
          if ( upcase(dndobj.attributeValue.type) = 'CHARACTER' ) then
          do;
             dcl num rc;
             rc = insertc( items,dndobj.attributeValue.characterValue, -1 );
    
             /* Set the items attribute equal to itself in order to */ 
             /* update this list box.                               */
             items = items;
          end;
       end;
       else _super( dndobj );
    endmethod;
    enduseclass;
  5. Compile and save the code, and then close the Source window.
  6. Close the Class Editor.
You could then use two classes in a frame:
  1. In the SAS Explorer, select Filethen selectNew then select a FRAME entry.
  2. In the Components window, right-click and select Add Classes to add sasuser.test.SourceListBox.class and sasuser.test.TargetListBox.class. If the Components window is not displayed when the new frame appears in the Build window, then select Viewthen selectComponents Window to display it.
  3. Drag an instance of the Source List Box object and drop it onto the frame, and then drag an instance of the Target List Box object and drop it onto the frame.
  4. Select Buildthen selectTest to test the frame. Drag an item from the source list box and drop it onto the target list box.
In the Target List Box class, the _validateDropData method verifies that the data representation is CHARACTERDATA. Its _drop method queries the attributeValue attribute that is passed in the drag and drop object and adds the value to its items attribute. Finally, the _completeDrag method on the Source List Box verifies that a drop has successfully occurred by querying the completeDrag attribute on the drag and drop object. It then removes the item from the list of items displayed in the list box to complete the MOVE action.