改变节点在 ALV 树中的位置

Changing a node's site in ALV tree

我有一个 ALV 树,基本上我需要做的是将特定节点滑动(拖动)到顶部或底部或 ALV 树中的特定位置。

这是如何实现的?

例如,在这里我想将 IF 节点滑动到 AFFECTATIONS 节点之前的位置:

预期节点排列:

TL;DR: cl_gui_alv_tree 不能像你希望的那样去做。

由于 ALV Control Framework 中拖放实现的具体细节,树中的节点可以 可拖动(来源) droppable (target), 不是两者。因此,将一个节点拖到另一个节点的位置 在层次结构的同一级别 上是不可能的,因此也不可能在运行时(相互)更改与其兄弟节点的节点。

不过,我想向您推荐一段有用的代码,它展示了如何通过将节点拖动到层次结构的父级来实现接近问题的效果。

在 SAP ALV Framework 中实现免打扰功能的步骤:

  1. 创建事件 class,其中实现 ondragondrop 事件的处理程序。
  2. 创建 DND 存储对象 lcl_dragdropobj,一个空的 class,您可以通过它传递内存中的网格对象(叶、节点)。
  3. 为源对象和目标对象创建 behavior definition
  4. 借助上述行为对象,通过 is_node_layout 参数为每​​个 node/leaf 分配布局。该布局将定义节点行为:可拖动或可放置。

这里是示例代码,建立在 spfli/sflight 数据库上:

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 ''.
    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.

在上面的示例中,月份可以拖动到根 Flights 节点上。当它们被拖动到它上面时,被拖动节点在层次结构中的位置成为第一位。这不完全是您要求的行为,但是!通过一组 N 拖动,您可以将 any 节点放置到该层级中的 any 位置上。

可以通过 BCALV_TREE_* 模式找到更多有用的免打扰示例。

CL_GUI_ALV_TREE 实现了经典的控制框架拖放功能(参见 concept in the SAP Library)。

它允许将任何节点放在任何其他节点上,无论级别如何,除了不能将节点放在其自身上。这是通过为每个节点提供允许拖放的行为来实现的。

以下是最小程序,以便快速了解实现基本拖放操作所需的内容。我不移动节点,因为它不是特定于拖放的(例如使用方法 MOVE_NODE)。 SFLIGHT table 必须由 运行 程序 SAPBC_DATA_GENERATOR 填写。注意:我不建议像我那样编写它,它只是一个演示程序,而是更像@Suncatcher 的程序。

REPORT.
CLASS lcl_app DEFINITION.
  PUBLIC SECTION.
    METHODS at_selection_screen_output.
    METHODS at_selection_screen_exit.
  PRIVATE SECTION.
    METHODS on_drag FOR EVENT on_drag OF cl_gui_alv_tree
      IMPORTING sender node_key drag_drop_object.
    METHODS on_drop FOR EVENT on_drop OF cl_gui_alv_tree
      IMPORTING sender node_key drag_drop_object.
    DATA: g_alv_tree TYPE REF TO cl_gui_alv_tree,
          gt_sflight TYPE TABLE OF sflight,
          dragged    TYPE sflight.
ENDCLASS.

CLASS lcl_app IMPLEMENTATION.

  METHOD at_selection_screen_output.
    DATA lt_fieldcatalog TYPE lvc_t_fcat.

    IF g_alv_tree IS INITIAL.
      g_alv_tree = NEW #( parent              = cl_gui_container=>screen0
                          node_selection_mode = cl_gui_column_tree=>node_sel_mode_single ).

      CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
        EXPORTING
          i_structure_name = 'SFLIGHT'
        CHANGING
          ct_fieldcat      = lt_fieldcatalog.

      g_alv_tree->set_table_for_first_display(
        EXPORTING is_hierarchy_header = VALUE #( width = 35 )
        CHANGING  it_fieldcatalog     = lt_fieldcatalog
                  it_outtab           = gt_sflight ).

      SET HANDLER on_drop FOR g_alv_tree.
      SET HANDLER on_drag FOR g_alv_tree.

      DATA(dnd_move_source_target) = NEW cl_dragdrop( ).
      dnd_move_source_target->add(
          flavor     = 'SINGLE'
          dragsrc    = abap_true
          droptarget = abap_true
          effect     = cl_dragdrop=>move ).
      dnd_move_source_target->get_handle( IMPORTING handle = DATA(dnd_move_source_target_id) ).

      SELECT * FROM sflight INTO TABLE @DATA(lt_sflight).
      LOOP AT lt_sflight ASSIGNING FIELD-SYMBOL(<ls_sflight>).
        g_alv_tree->add_node(
          EXPORTING i_relat_node_key = ''
                    i_relationship   = cl_gui_column_tree=>relat_last_child
                    i_node_text      = |{ <ls_sflight>-carrid }-{ <ls_sflight>-connid }-{ <ls_sflight>-fldate }|
                    is_outtab_line   = <ls_sflight>
                    is_node_layout   = VALUE #( dragdropid = dnd_move_source_target_id ) ).
      ENDLOOP.

      g_alv_tree->frontend_update( ).
    ENDIF.
  ENDMETHOD.

  METHOD at_selection_screen_exit.
    cl_gui_container=>screen0->free( ).
  ENDMETHOD.

  METHOD on_drag.

    sender->get_outtab_line(
      EXPORTING i_node_key    = node_key
      IMPORTING e_outtab_line = dragged ).

    drag_drop_object->object = me. " must be bound to trigger drop event when user drops

  ENDMETHOD.

  METHOD on_drop.
    DATA dropped TYPE sflight.

    sender->get_outtab_line(
      EXPORTING i_node_key    = node_key
      IMPORTING e_outtab_line = dropped ).

    MESSAGE |{ dragged-carrid }-{ dragged-connid }-{ dragged-fldate
            } dropped on { dropped-carrid }-{ dropped-connid }-{ dropped-fldate }| TYPE 'I'.

  ENDMETHOD.

ENDCLASS.

PARAMETERS dummy.
DATA go_app TYPE REF TO lcl_app.

LOAD-OF-PROGRAM.
  CREATE OBJECT go_app.

AT SELECTION-SCREEN OUTPUT.
  go_app->at_selection_screen_output( ).

AT SELECTION-SCREEN ON EXIT-COMMAND.
  go_app->at_selection_screen_exit( ).