I have an ALV tree and basically what I need to do is to slide (drag) a specific node to the top or bottom or a specific location in the ALV tree.
How could this be accomplished?
For example, here I would like to slide the IF node in place before the AFFECTATIONS node:
Expected node's arrangement:
TL;DR: cl_gui_alv_tree
cannot do it like you want it to do it.
Due to the specifics of drag-n-drop implementation in ALV Control Framework, the node in a tree can be either draggable (source) or droppable (target), not both. So dragging a node to another node's place on the same level of hierarchy is not possible, hence it is also not possible to (inter)change nodes with its siblings in runtime.
However, I wanna propose you a helpful piece of code, which shows how one can achieve smth close to the question by dragging the node onto the parent level of hierarchy.
Steps for implementing DND functionality in SAP ALV Framework:
ondrag
and ondrop
events are implemented.lcl_dragdropobj
, an empty class through the which you pass grid objects (leaves, nodes) in memory.is_node_layout
parameter. That layout would define node behavior: either it's draggable or droppable.Here is the sample code, built on spfli/sflight database:
REPORT zyyz.
DATA: g_alv_tree TYPE REF TO cl_gui_alv_tree,
g_custom_container TYPE REF TO cl_gui_custom_container,
g_drag_behaviour TYPE REF TO cl_dragdrop,
g_drop_behaviour TYPE REF TO cl_dragdrop.
DATA: gt_sflight TYPE TABLE OF sflight INITIAL SIZE 0, "Output-Table
gt_fieldcatalog TYPE lvc_t_fcat,
g_top_key TYPE lvc_nkey,
ok_code LIKE sy-ucomm,
save_ok LIKE sy-ucomm, "OK-Code
g_max TYPE i VALUE 255.
CLASS lcl_dragdropobj DEFINITION.
PUBLIC SECTION.
DATA: cp_sflight_root TYPE sflight,
cp_sflights TYPE TABLE OF sflight,
cp_node_text_root TYPE lvc_value,
cp_node_texts TYPE salv_t_value,
lt_selected_carrids TYPE lvc_t_nkey,
lt_selected_leafs TYPE lvc_t_nkey.
ENDCLASS.
CLASS lcl_event_receiver DEFINITION.
PUBLIC SECTION. .
METHODS:
on_drag
FOR EVENT on_drag
OF cl_gui_alv_tree
IMPORTING sender node_key drag_drop_object,
on_drop
FOR EVENT on_drop
OF cl_gui_alv_tree
IMPORTING drag_drop_object.
ENDCLASS.
*---------------------------------------------------------------------*
* CLASS lcl_toolbar_event_receiver IMPLEMENTATION
*---------------------------------------------------------------------*
CLASS lcl_event_receiver IMPLEMENTATION.
METHOD on_drag.
DATA: dataobj TYPE REF TO lcl_dragdropobj,
l_sflight TYPE sflight,
l_node_text TYPE lvc_value.
* create and fill dataobject for event ON_DROP.
CREATE OBJECT dataobj.
* Get dragged month
CALL METHOD sender->get_outtab_line
EXPORTING
i_node_key = node_key
IMPORTING
e_outtab_line = dataobj->cp_sflight_root
e_node_text = dataobj->cp_node_text_root.
CALL METHOD g_alv_tree->get_children
EXPORTING
i_node_key = node_key
IMPORTING
et_children = dataobj->lt_selected_carrids.
LOOP AT dataobj->lt_selected_carrids ASSIGNING FIELD-SYMBOL(<carrid>).
CLEAR: l_sflight, l_node_text.
CALL METHOD sender->get_outtab_line
EXPORTING
i_node_key = <carrid>
IMPORTING
e_outtab_line = l_sflight
e_node_text = l_node_text.
APPEND l_node_text TO dataobj->cp_node_texts.
CALL METHOD g_alv_tree->get_children
EXPORTING
i_node_key = <carrid>
IMPORTING
et_children = dataobj->lt_selected_leafs.
LOOP AT dataobj->lt_selected_leafs ASSIGNING FIELD-SYMBOL(<node>).
CLEAR: l_sflight, l_node_text.
CALL METHOD sender->get_outtab_line
EXPORTING
i_node_key = <node>
IMPORTING
e_outtab_line = l_sflight
e_node_text = l_node_text.
APPEND l_sflight TO dataobj->cp_sflights.
ENDLOOP.
ENDLOOP.
drag_drop_object->object = dataobj.
ENDMETHOD.
METHOD on_drop.
DATA: dataobj TYPE REF TO lcl_dragdropobj,
l_root_key TYPE lvc_nkey,
l_carrid_key TYPE lvc_nkey,
l_new_key TYPE lvc_nkey,
lt_sflights TYPE TABLE OF sflight.
CATCH SYSTEM-EXCEPTIONS move_cast_error = 1.
dataobj ?= drag_drop_object->object.
PERFORM add_month USING dataobj->cp_node_text_root
g_top_key
cl_gui_column_tree=>relat_first_child
CHANGING l_root_key.
*
LOOP AT dataobj->lt_selected_carrids ASSIGNING FIELD-SYMBOL(<node>).
READ TABLE dataobj->cp_node_texts ASSIGNING FIELD-SYMBOL(<text>) INDEX sy-tabix.
DATA(sflight) = VALUE sflight( carrid = <text> ).
PERFORM add_carrid_line USING sflight
l_root_key
cl_gui_column_tree=>relat_last_child
CHANGING l_carrid_key.
CLEAR lt_sflights.
INSERT LINES OF dataobj->cp_sflights INTO TABLE lt_sflights.
DELETE lt_sflights WHERE carrid NE <text>.
LOOP AT lt_sflights ASSIGNING FIELD-SYMBOL(<flight>).
PERFORM add_complete_line USING <flight>
l_carrid_key
CHANGING l_new_key.
ENDLOOP.
ENDLOOP.
PERFORM delete_node.
ENDCATCH.
IF sy-subrc <> 0.
CALL METHOD drag_drop_object->abort.
ENDIF.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
END-OF-SELECTION.
CALL SCREEN 100.
*&---------------------------------------------------------------------*
*& Module PBO OUTPUT
*&---------------------------------------------------------------------*
MODULE pbo OUTPUT.
SET PF-STATUS 'MAIN100'.
SET TITLEBAR 'MAINTITLE'.
IF g_alv_tree IS INITIAL.
PERFORM init_tree.
CALL METHOD cl_gui_cfw=>flush
EXCEPTIONS
cntl_system_error = 1
cntl_error = 2.
ENDIF.
ENDMODULE. " PBO OUTPUT
*&---------------------------------------------------------------------*
*& Module PAI INPUT
*&---------------------------------------------------------------------*
* process after input
*----------------------------------------------------------------------*
MODULE pai INPUT.
save_ok = ok_code.
CLEAR ok_code.
CASE save_ok.
WHEN 'EXIT' OR 'BACK' OR 'CANC'.
PERFORM exit_program.
WHEN OTHERS.
CALL METHOD cl_gui_cfw=>dispatch.
ENDCASE.
CALL METHOD cl_gui_cfw=>flush.
ENDMODULE. " PAI INPUT
*&---------------------------------------------------------------------*
*& Form init_tree
*&---------------------------------------------------------------------*
FORM init_tree.
* create container for alv-tree
DATA: l_tree_container_name(30) TYPE c.
l_tree_container_name = 'CCONTAINER1'.
CREATE OBJECT g_custom_container
EXPORTING
container_name = l_tree_container_name.
IF sy-subrc <> 0.
MESSAGE x208(00) WITH 'ERROR'(100).
ENDIF.
* create tree control
CREATE OBJECT g_alv_tree
EXPORTING
parent = g_custom_container
node_selection_mode = cl_gui_column_tree=>node_sel_mode_single
item_selection = ' '
no_html_header = 'X'
no_toolbar = ''.
IF sy-subrc <> 0.
MESSAGE x208(00) WITH 'ERROR'. "#EC NOTEXT
ENDIF.
DATA l_hierarchy_header TYPE treev_hhdr.
l_hierarchy_header-width = 35.
PERFORM build_fieldcatalog.
CALL METHOD g_alv_tree->set_table_for_first_display
EXPORTING
is_hierarchy_header = l_hierarchy_header
CHANGING
it_fieldcatalog = gt_fieldcatalog
it_outtab = gt_sflight.
PERFORM define_dnd_behaviour.
PERFORM create_hierarchy.
PERFORM register_events.
CALL METHOD g_alv_tree->update_calculations.
CALL METHOD g_alv_tree->frontend_update.
ENDFORM. " init_tree
FORM register_events.
DATA: lt_events TYPE cntl_simple_events,
l_event_receiver TYPE REF TO lcl_event_receiver.
CALL METHOD g_alv_tree->get_registered_events
IMPORTING
events = lt_events.
CALL METHOD g_alv_tree->set_registered_events
EXPORTING
events = lt_events.
IF sy-subrc <> 0.
MESSAGE x208(00) WITH 'ERROR'. "#EC NOTEXT
ENDIF.
CREATE OBJECT l_event_receiver.
SET HANDLER l_event_receiver->on_drop FOR g_alv_tree.
SET HANDLER l_event_receiver->on_drag FOR g_alv_tree.
ENDFORM. " register_events
*&---------------------------------------------------------------------*
*& Form exit_program
*&---------------------------------------------------------------------*
* free object and leave program
*----------------------------------------------------------------------*
FORM exit_program.
CALL METHOD g_custom_container->free.
LEAVE PROGRAM.
ENDFORM. " exit_program
*--------------------------------------------------------------------
FORM build_fieldcatalog.
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
ct_fieldcat = gt_fieldcatalog.
ENDFORM. " build_fieldcatalog
*&---------------------------------------------------------------------*
*& Form create_hierarchy
*&---------------------------------------------------------------------*
FORM create_hierarchy.
DATA: ls_sflight TYPE sflight,
lt_sflight TYPE TABLE OF sflight INITIAL SIZE 0,
l_yyyymm TYPE c LENGTH 8, "year and month of sflight-fldate
l_yyyymm_last(6) TYPE c,
l_carrid LIKE sflight-carrid,
l_carrid_last LIKE sflight-carrid.
DATA: l_month_key TYPE lvc_nkey,
l_carrid_key TYPE lvc_nkey,
l_last_key TYPE lvc_nkey,
l_top_key TYPE lvc_nkey,
l_layout_node TYPE lvc_s_layn.
* Select data
SELECT * FROM sflight INTO TABLE lt_sflight UP TO g_max ROWS.
* sort table according to conceived hierarchy
SORT lt_sflight BY fldate+0(6) carrid fldate+6(2).
PERFORM make_drop CHANGING l_layout_node.
CALL METHOD g_alv_tree->add_node
EXPORTING
i_relat_node_key = ''
i_relationship = cl_gui_column_tree=>relat_last_child
i_node_text = text-050
is_node_layout = l_layout_node
IMPORTING
e_new_node_key = l_top_key.
g_top_key = l_top_key.
LOOP AT lt_sflight INTO ls_sflight.
l_yyyymm = ls_sflight-fldate+0(6).
l_carrid = ls_sflight-carrid.
IF l_yyyymm <> l_yyyymm_last. "on change of l_yyyymm
l_yyyymm_last = l_yyyymm.
* month nodes
PERFORM add_month USING l_yyyymm
l_top_key
cl_gui_column_tree=>relat_last_child
CHANGING l_month_key.
* The month changed, thus, there is no predecessor carrier
CLEAR l_carrid_last.
ENDIF.
* Carrier nodes:
IF l_carrid <> l_carrid_last. "on change of l_carrid
l_carrid_last = l_carrid.
PERFORM add_carrid_line USING ls_sflight
l_month_key
cl_gui_column_tree=>relat_last_child
CHANGING l_carrid_key.
ENDIF.
* Leaf:
PERFORM add_complete_line USING ls_sflight
l_carrid_key
CHANGING l_last_key.
ENDLOOP.
CALL METHOD g_alv_tree->expand_node
EXPORTING
i_node_key = l_top_key.
ENDFORM. " create_hierarchy
*&---------------------------------------------------------------------*
*& Form add_month
*&---------------------------------------------------------------------*
FORM add_month USING p_yyyymm TYPE clike
p_relat_key TYPE lvc_nkey
p_relationship TYPE int4
CHANGING p_node_key TYPE lvc_nkey.
DATA: l_node_text TYPE lvc_value,
ls_sflight TYPE sflight,
l_month TYPE c LENGTH 25,
l_layout_node TYPE lvc_s_layn.
IF p_yyyymm CO ' 0123456789'.
p_yyyymm = p_yyyymm && '01'.
CALL FUNCTION 'CONVERSION_EXIT_LDATE_OUTPUT'
EXPORTING
input = p_yyyymm
IMPORTING
output = l_month.
REPLACE REGEX `(\d\d\.\s)([[:alpha:]]*)(\s\d{4})` IN l_month WITH '$2'.
l_node_text = p_yyyymm(4) && `/` && l_month.
ELSE.
l_node_text = p_yyyymm.
ENDIF.
PERFORM make_drag CHANGING l_layout_node.
* add node
CALL METHOD g_alv_tree->add_node
EXPORTING
i_relat_node_key = p_relat_key
i_relationship = p_relationship
i_node_text = l_node_text
is_outtab_line = ls_sflight
is_node_layout = l_layout_node
IMPORTING
e_new_node_key = p_node_key.
ENDFORM. " add_month
*-----------------------------------------------------------------------
FORM add_carrid_line USING ps_sflight TYPE sflight
p_relat_key TYPE lvc_nkey
p_relationship TYPE int4
CHANGING p_node_key TYPE lvc_nkey.
DATA: l_node_text TYPE lvc_value,
ls_sflight TYPE sflight,
l_layout_node TYPE lvc_s_layn.
l_node_text = ps_sflight-carrid.
CALL METHOD g_alv_tree->add_node
EXPORTING
i_relat_node_key = p_relat_key
i_relationship = p_relationship
i_node_text = l_node_text
is_outtab_line = ls_sflight
is_node_layout = l_layout_node
IMPORTING
e_new_node_key = p_node_key.
ENDFORM. " add_carrid_line
*&---------------------------------------------------------------------*
*& Form add_complete_line
*&---------------------------------------------------------------------*
FORM add_complete_line USING ps_sflight TYPE sflight
p_relat_key TYPE lvc_nkey
CHANGING p_node_key TYPE lvc_nkey.
DATA: l_node_text TYPE lvc_value,
l_layout_node TYPE lvc_s_layn.
WRITE ps_sflight-fldate TO l_node_text MM/DD/YYYY.
CALL METHOD g_alv_tree->add_node
EXPORTING
i_relat_node_key = p_relat_key
i_relationship = cl_gui_column_tree=>relat_last_child
is_outtab_line = ps_sflight
i_node_text = l_node_text
is_node_layout = l_layout_node
IMPORTING
e_new_node_key = p_node_key.
ENDFORM. " add_complete_line
FORM define_dnd_behaviour.
DATA: effect TYPE i.
CREATE OBJECT g_drag_behaviour.
effect = cl_dragdrop=>move.
CALL METHOD g_drag_behaviour->add
EXPORTING
flavor = 'default' "#EC NOTEXT
dragsrc = 'X'
droptarget = ' '
effect = effect.
CREATE OBJECT g_drop_behaviour.
effect = cl_dragdrop=>move.
CALL METHOD g_drop_behaviour->add
EXPORTING
flavor = 'default' "#EC NOTEXT
dragsrc = ' '
droptarget = 'X'
effect = effect.
ENDFORM. " DEFINE_DND_BEHAVIOUR
FORM delete_node.
DATA: lt_selected_nodes TYPE lvc_t_nkey,
l_selected_node TYPE lvc_nkey.
CALL METHOD g_alv_tree->get_selected_nodes
CHANGING
ct_selected_nodes = lt_selected_nodes.
CALL METHOD cl_gui_cfw=>flush.
READ TABLE lt_selected_nodes INTO l_selected_node INDEX 1.
IF sy-subrc EQ 0.
CALL METHOD g_alv_tree->delete_subtree
EXPORTING
i_node_key = l_selected_node.
CALL METHOD g_alv_tree->frontend_update.
ELSE. "sy-subrc EQ 0
MESSAGE i000(0k) WITH 'Please select a node.'(900).
ENDIF.
ENDFORM.
FORM make_drag CHANGING p_layout_node TYPE lvc_s_layn.
DATA l_handle_line TYPE i.
CALL METHOD g_drag_behaviour->get_handle
IMPORTING
handle = l_handle_line.
p_layout_node-dragdropid = l_handle_line.
ENDFORM.
FORM make_drop CHANGING p_layout_node TYPE lvc_s_layn.
DATA l_handle_line TYPE i.
CALL METHOD g_drop_behaviour->get_handle
IMPORTING
handle = l_handle_line.
p_layout_node-dragdropid = l_handle_line.
ENDFORM.
In the above example months are made draggable onto the root Flights node. When they are dragged onto it, the place of the dragged node within hierarchy became first. This is not exactly the behavior you asked, but! Through the set of N draggings you can place any node onto any place within that level of hierarchy.
More useful DND examples can be found via BCALV_TREE_* pattern.