01-25-2018 4:54 PM
Hi there!
today I doubt that I do programming for so many years...
The task: Provide an easy sorting mechanism in ALV grid.
The user should be able to mark any lines (one, a block of lines or arbitrary lines) and move them via drag and drop to another place.
I want to have a solution that can be used instead of the cl_gui_alv_grid. Therefore I inherited the class and implemented drag and drop feature.
The task seems to be quite easy: renumber the marked rows, find the new position in the data table and renumber the old entries.
May be I do think to complex or it really is not that trivial...
I would like to invite you to a challenge and try to find a working solution.
I myself had three approaches which either do not work for every selection or which use too much memory and performance.
The following code provides a demo report that
Just copy and paste the following code and try to find a simple solution that just sets the new order-id in the given field.
I am excited to see your solutions! 🙂
Enno
EXAMPLE
this is one of the most complex example: marking several lines that are not in one block and dragging them to another line:
Lines 4,6,7 and 9 are marked and are moved to line 2.
After dropping the lines the renumbering will be done and shown in field NATIO:
HINTS
It matters if you move the lines up or down.
Calculating the new order number should be preferred. I have a solution where I copy the selected lines to a temporary created identical table, deleting the marked lines, do the numbering and insertion at new position. This might be a problem in huge tables...
CODE
REPORT .
CLASS lcl_dragdrop_sort DEFINITION.
PUBLIC SECTION.
DATA at_rows TYPE lvc_t_row.
DATA ao_data TYPE REF TO data.
ENDCLASS.
CLASS lcl_gui_alv_grid_sort DEFINITION
INHERITING FROM cl_gui_alv_grid.
PUBLIC SECTION.
METHODS set_sort_field
IMPORTING
fieldname TYPE clike.
PROTECTED SECTION.
DATA mv_sort_field TYPE char30 .
CONSTANTS mc_sort_flavor TYPE c LENGTH 10 VALUE 'SortMe'.
METHODS on_drag FOR EVENT ondrag OF cl_gui_alv_grid
IMPORTING e_column e_row e_dragdropobj es_row_no sender.
METHODS on_drop FOR EVENT ondrop OF cl_gui_alv_grid
IMPORTING e_column e_row e_dragdropobj es_row_no sender.
ENDCLASS.
CLASS lcl_gui_alv_grid_sort IMPLEMENTATION.
METHOD on_drag.
DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
lo_dragdrop_sort = NEW #( ).
sender->get_selected_rows( IMPORTING et_index_rows = lo_dragdrop_sort->at_rows ).
IF lo_dragdrop_sort->at_rows IS INITIAL.
"No lines selected: get current cell
sender->get_current_cell(
IMPORTING
e_row = DATA(lv_row) ).
IF NOT lv_row IS INITIAL.
APPEND VALUE #( index = lv_row ) TO lo_dragdrop_sort->at_rows.
ENDIF.
ENDIF.
lo_dragdrop_sort->ao_data = mt_outtab.
e_dragdropobj->object ?= lo_dragdrop_sort.
ENDMETHOD.
METHOD on_drop.
DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
FIELD-SYMBOLS <lft_data> TYPE table.
CHECK e_dragdropobj->flavor = mc_sort_flavor.
TRY.
lo_dragdrop_sort ?= e_dragdropobj->object.
CHECK lo_dragdrop_sort->at_rows IS NOT INITIAL.
CATCH cx_sy_move_cast_error.
RETURN.
ENDTRY.
ASSIGN lo_dragdrop_sort->ao_data->* TO <lft_data>.
LOOP AT <lft_data> ASSIGNING FIELD-SYMBOL(<lfs_data>).
ASSIGN COMPONENT mv_sort_field OF STRUCTURE <lfs_data>
TO FIELD-SYMBOL(<lfv_sortn>).
CHECK sy-subrc = 0.
<lfv_sortn> = CONV numc10( sy-tabix ).
"------->
"PLACE YOUR SOLUTION HERE
"AND HERE
"...
"....AND
"------> HERE
"---------------------------------------------> SOLUTION!! <------"
ENDLOOP.
"Set sort field
me->get_sort_criteria( IMPORTING et_sort = DATA(lt_sort) ).
IF NOT line_exists( lt_sort[ fieldname = mv_sort_field ] ).
INSERT VALUE #( fieldname = mv_sort_field spos = 1 up = abap_true )
INTO lt_sort INDEX 1.
ENDIF.
"Set sorting
me->set_sort_criteria( lt_sort ).
"Show changes
refresh_table_display( ).
ENDMETHOD.
METHOD set_sort_field.
mv_sort_field = fieldname.
SET HANDLER on_drop FOR me.
SET HANDLER on_drag FOR me.
DATA(lo_dd) = NEW cl_dragdrop( ).
lo_dd->add( dragsrc = abap_true
droptarget = abap_true
flavor = mc_sort_flavor
effect = cl_dragdrop=>copy
effect_in_ctrl = cl_dragdrop=>move ).
DATA(lv_dd_handle) = 0.
lo_dd->get_handle( IMPORTING handle = lv_dd_handle ).
"Set drag-drop-handle
get_frontend_layout( IMPORTING es_layout = DATA(ls_layout) ).
ls_layout-s_dragdrop-row_ddid = lv_dd_handle.
set_frontend_layout( ls_layout ).
refresh_table_display( ).
ENDMETHOD.
ENDCLASS.
CLASS main DEFINITION.
PUBLIC SECTION.
TYPES ty_data TYPE t005t.
TYPES ty_data_t TYPE STANDARD TABLE OF ty_data
WITH DEFAULT KEY.
DATA ms_data TYPE ty_data.
DATA mt_data TYPE ty_data_t.
DATA mr_grid TYPE REF TO lcl_gui_alv_grid_sort.
METHODS start.
PROTECTED SECTION.
METHODS selection.
METHODS display.
ENDCLASS.
CLASS main IMPLEMENTATION.
METHOD selection.
SELECT * FROM t005t INTO TABLE mt_data UP TO 10 ROWS
WHERE spras = sy-langu.
ENDMETHOD.
METHOD start.
selection( ).
display( ).
ENDMETHOD.
METHOD display.
WRITE 'DUMMY'.
CREATE OBJECT mr_grid
EXPORTING
i_parent = cl_gui_container=>screen0
i_appl_events = space.
DATA lv_structure_name TYPE dd02l-tabname VALUE 'T005T'.
DATA ls_variant TYPE disvariant.
DATA lv_save TYPE char01 VALUE 'U'.
DATA lv_default TYPE char01 VALUE abap_true.
DATA ls_layout TYPE lvc_s_layo.
ls_layout-sel_mode = 'A'.
ls_layout-grid_title = 'Countries'.
mr_grid->set_table_for_first_display(
EXPORTING
i_structure_name = lv_structure_name
is_variant = ls_variant
i_save = lv_save
i_default = lv_default
is_layout = ls_layout
CHANGING
it_outtab = mt_data ).
mr_grid->set_sort_field( 'NATIO' ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
NEW main( )->start( ).
01-25-2018 5:08 PM
Just a hunch without delving into your problem, can you employ virtual sorting somehow?
(I know that this was invented by some demand of the ALV framework)
01-25-2018 5:12 PM
Thanks for the hint! The sorting itself is not the problem. Knowing or calculating the new sort order is the challenge.
01-25-2018 5:36 PM
Just found out the following:
If ALV grid is in edit-mode then you can move the lines by default even between two lines:
but:
1. the sorting is wrong when selecting multiple "blocks" of lines
2. How can I access the event "dragging complete"? Event DATA_CHANGED does not work for dragging and dropping.
01-25-2018 7:57 PM
If the number of lines can be limit to 100/1000/10000, which are Drap&Drop at once:
I put line comment > " new < on changed/new lines.
REPORT .
CLASS lcl_dragdrop_sort DEFINITION.
PUBLIC SECTION.
DATA at_rows TYPE lvc_t_row.
DATA ao_data TYPE REF TO data.
ENDCLASS.
CLASS lcl_gui_alv_grid_sort DEFINITION
INHERITING FROM cl_gui_alv_grid.
PUBLIC SECTION.
METHODS set_sort_field
IMPORTING
fieldname TYPE clike.
PROTECTED SECTION.
DATA mv_sort_field TYPE char30 .
CONSTANTS mc_sort_flavor TYPE c LENGTH 10 VALUE 'SortMe'.
METHODS on_drag FOR EVENT ondrag OF cl_gui_alv_grid
IMPORTING e_column e_row e_dragdropobj es_row_no sender.
METHODS on_drop FOR EVENT ondrop OF cl_gui_alv_grid
IMPORTING e_column e_row e_dragdropobj es_row_no sender.
METHODS on_dropcomplete FOR EVENT ondropcomplete OF cl_gui_alv_grid " new
IMPORTING e_row e_column es_row_no e_dragdropobj. " new
ENDCLASS.
CLASS lcl_gui_alv_grid_sort IMPLEMENTATION.
METHOD on_drag.
DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
lo_dragdrop_sort = NEW #( ).
sender->get_selected_rows( IMPORTING et_index_rows = lo_dragdrop_sort->at_rows ).
IF lo_dragdrop_sort->at_rows IS INITIAL.
"No lines selected: get current cell
sender->get_current_cell(
IMPORTING
e_row = DATA(lv_row) ).
IF NOT lv_row IS INITIAL.
APPEND VALUE #( index = lv_row ) TO lo_dragdrop_sort->at_rows.
ENDIF.
ENDIF.
lo_dragdrop_sort->ao_data = mt_outtab.
e_dragdropobj->object ?= lo_dragdrop_sort.
ENDMETHOD.
METHOD on_drop.
DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
FIELD-SYMBOLS <lft_data> TYPE table.
DATA newnatio TYPE i.
CHECK e_dragdropobj->flavor = mc_sort_flavor.
TRY.
lo_dragdrop_sort ?= e_dragdropobj->object.
CHECK lo_dragdrop_sort->at_rows IS NOT INITIAL.
ASSIGN lo_dragdrop_sort->ao_data->* TO <lft_data>.
READ TABLE <lft_data> ASSIGNING FIELD-SYMBOL(<lfs_data>) INDEX e_row-index.
CHECK sy-subrc EQ 0.
ASSIGN COMPONENT 'NATIO' OF STRUCTURE <lfs_data> TO FIELD-SYMBOL(<natio>).
newnatio = <natio>.
LOOP AT lo_dragdrop_sort->at_rows ASSIGNING FIELD-SYMBOL(<at_rows>).
READ TABLE <lft_data> ASSIGNING <lfs_data> INDEX <at_rows>-index.
CHECK sy-subrc EQ 0.
ADD 1 TO newnatio.
ASSIGN COMPONENT 'NATIO' OF STRUCTURE <lfs_data> TO <natio>.
<natio> = CONV numc10( newnatio ).
ENDLOOP.
CATCH cx_sy_move_cast_error.
RETURN.
ENDTRY.
* ASSIGN lo_dragdrop_sort->ao_data->* TO <lft_data>. " new >>>
*
* LOOP AT <lft_data> ASSIGNING FIELD-SYMBOL(<lfs_data>).
* ASSIGN COMPONENT mv_sort_field OF STRUCTURE <lfs_data>
* TO FIELD-SYMBOL(<lfv_sortn>).
* CHECK sy-subrc = 0.
* <lfv_sortn> = CONV numc10( sy-tabix ).
* "------->
* "PLACE YOUR SOLUTION HERE
* "AND HERE
* "...
* "....AND
* "------> HERE
* "---------------------------------------------> SOLUTION!! <------"
* ENDLOOP.
*
* "Set sort field
* me->get_sort_criteria( IMPORTING et_sort = DATA(lt_sort) ).
* IF NOT line_exists( lt_sort[ fieldname = mv_sort_field ] ).
* INSERT VALUE #( fieldname = mv_sort_field spos = 1 up = abap_true )
* INTO lt_sort INDEX 1.
* ENDIF.
* "Set sorting
* me->set_sort_criteria( lt_sort ).
*
* "Show changes
* refresh_table_display( ). " new <<<
ENDMETHOD.
METHOD on_dropcomplete. " new >>>
DATA lo_dragdrop_sort TYPE REF TO lcl_dragdrop_sort.
FIELD-SYMBOLS <lft_data> TYPE table.
CHECK e_dragdropobj->flavor = mc_sort_flavor.
TRY.
lo_dragdrop_sort ?= e_dragdropobj->object.
CHECK lo_dragdrop_sort->at_rows IS NOT INITIAL.
CATCH cx_sy_move_cast_error.
RETURN.
ENDTRY.
ASSIGN lo_dragdrop_sort->ao_data->* TO <lft_data>.
SORT <lft_data> BY ('NATIO').
LOOP AT <lft_data> ASSIGNING FIELD-SYMBOL(<lfs_data>).
ASSIGN COMPONENT mv_sort_field OF STRUCTURE <lfs_data>
TO FIELD-SYMBOL(<lfv_sortn>).
CHECK sy-subrc = 0.
<lfv_sortn> = CONV numc10( 1000 + ( sy-tabix * 100 ) ).
"------->
"PLACE YOUR SOLUTION HERE
"AND HERE
"...
"....AND
"------> HERE
"---------------------------------------------> SOLUTION!! <------"
ENDLOOP.
"Set sort field
me->get_sort_criteria( IMPORTING et_sort = DATA(lt_sort) ).
IF NOT line_exists( lt_sort[ fieldname = mv_sort_field ] ).
INSERT VALUE #( fieldname = mv_sort_field spos = 1 up = abap_true )
INTO lt_sort INDEX 1.
ENDIF.
"Set sorting
me->set_sort_criteria( lt_sort ).
"Show changes
refresh_table_display( ).
ENDMETHOD. " new <<<
METHOD set_sort_field.
mv_sort_field = fieldname.
SET HANDLER on_drop FOR me.
SET HANDLER on_drag FOR me.
SET HANDLER on_dropcomplete FOR me.
DATA(lo_dd) = NEW cl_dragdrop( ).
lo_dd->add( dragsrc = abap_true
droptarget = abap_true
flavor = mc_sort_flavor
effect = cl_dragdrop=>copy
effect_in_ctrl = cl_dragdrop=>move ).
DATA(lv_dd_handle) = 0.
lo_dd->get_handle( IMPORTING handle = lv_dd_handle ).
"Set drag-drop-handle
get_frontend_layout( IMPORTING es_layout = DATA(ls_layout) ).
ls_layout-s_dragdrop-row_ddid = lv_dd_handle.
set_frontend_layout( ls_layout ).
refresh_table_display( ).
ENDMETHOD.
ENDCLASS.
CLASS main DEFINITION.
PUBLIC SECTION.
TYPES ty_data TYPE t005t.
TYPES ty_data_t TYPE STANDARD TABLE OF ty_data
WITH DEFAULT KEY.
DATA ms_data TYPE ty_data.
DATA mt_data TYPE ty_data_t.
DATA mr_grid TYPE REF TO lcl_gui_alv_grid_sort.
METHODS start.
PROTECTED SECTION.
METHODS selection.
METHODS display.
ENDCLASS.
CLASS main IMPLEMENTATION.
METHOD selection.
SELECT * FROM t005t INTO TABLE mt_data UP TO 10 ROWS
WHERE spras = sy-langu.
LOOP AT mt_data ASSIGNING FIELD-SYMBOL(<mt_data>). " new
<mt_data>-natio = CONV numc10( 1000 + ( sy-tabix * 100 ) ). " new
ENDLOOP. " new
ENDMETHOD.
METHOD start.
selection( ).
display( ).
ENDMETHOD.
METHOD display.
WRITE 'DUMMY'.
CREATE OBJECT mr_grid
EXPORTING
i_parent = cl_gui_container=>screen0
i_appl_events = space.
DATA lv_structure_name TYPE dd02l-tabname VALUE 'T005T'.
DATA ls_variant TYPE disvariant.
DATA lv_save TYPE char01 VALUE 'U'.
DATA lv_default TYPE char01 VALUE abap_true.
DATA ls_layout TYPE lvc_s_layo.
ls_layout-sel_mode = 'A'.
ls_layout-grid_title = 'Countries'.
mr_grid->set_table_for_first_display(
EXPORTING
i_structure_name = lv_structure_name
is_variant = ls_variant
i_save = lv_save
i_default = lv_default
is_layout = ls_layout
CHANGING
it_outtab = mt_data ).
mr_grid->set_sort_field( 'NATIO' ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
NEW main( )->start( ).
01-26-2018 9:35 AM
Thanks for this solution Tibor!
This is a simple solution. I remember I also thought a second about this but rejected the solution because in my special case the sort field was just 4 digits long...
And as I began to calculate the new positions I thought that it must be possible to exactly calculate it. Maybe it's possible but not that simple...
Why did you put your last code in OnDropComplete? In this case it should also work w/o OnDropComplete...?
Regards
Enno
01-26-2018 4:06 PM
Sorry, could not post it as reply to comment above. Tried it several times.
Enno wrote:
Why did you put your last code in OnDropComplete? In this case it should also work w/o OnDropComplete...?
I used OnDropComplete, because you mentioned it in your additional comment ("2. How can I access the event "dragging complete"?)
And it is inspired from paper algorithm.
Starting with initialized numbering:
On_Drap move selected lines and number them:
On_Drop move lines to new position and insert them:
On_DropComplete initialize numbering for next roundtrip:
01-26-2018 4:37 PM
haha, great! Love your paper work! 😉
The additional question especially referred to the drag-and-drop IN EDIT MODE. In this case the "normal" drag-and-drop events are not triggered.