Wednesday, 9 September 2020

ALV Grid With Multiple Header Lines

Sometimes the column description of an ALV grid is not sufficient and one would like to have multiple lines as column header. With this trick it is possible to realize such a behaviour.

Demonstration


This is how it looks like deomnstrated with the countries table T005T:

SAP ABAP Development, SAP ABAP Learning, SAP ABAP Certification, SAP ABAP Exam Prep

Solution Description


Two grids will be created: one for the header lines (column description) and one for the data. The field catalog of the data table will be copied to the header structure. All fields are set to “TEXT40” rollname to display longer column titles. Disturbing attributes like conversion exit are deleted.

The two grids are displayed in a splitter container. The upper container is set to “not resizeable” to have fixed header lines.

The data table will be adapted so that no column changes are possible. All changes like resizing and moving of columns must be done in the header grid. Unfortunately there is no event that reacts on these changes. So the changes made to the header grid can only be copied to the data grid after pressing a key.

Enhancements


This is a proof-of-concept. The data table is defined within the class. To reuse the functionality, the data table should be passed from outside. Maybe a direct inheritance of CL_GUI_ALV_GRID would also be helpful.

If a toolbar is needed, then all function codes from the header grid must be passed to the data grid without effecting the header grid.

Github


Code is also on github

Code


REPORT ztrcktrsr_multi_header_grid.

* two grids
* upper: multi rows title columns
* lower: data

PARAMETERS test.

CLASS my_grid DEFINITION INHERITING FROM cl_gui_alv_grid.
  PUBLIC SECTION.
    METHODS set_resize_cols_public
      IMPORTING
        res TYPE i.
ENDCLASS.

CLASS my_grid IMPLEMENTATION.
  METHOD set_resize_cols_public.
    set_resize_cols( res ).
  ENDMETHOD.

ENDCLASS.

CLASS multi_title_grid DEFINITION.
  PUBLIC SECTION.
    METHODS constructor
      IMPORTING
        container TYPE REF TO cl_gui_container.
    METHODS set_data.
    METHODS copy.

    METHODS set_title_for_column
      IMPORTING
        column TYPE lvc_fname
        text1  TYPE string
        text2  TYPE string OPTIONAL
        text3  TYPE string OPTIONAL.

    METHODS set_title_for_column_n
      IMPORTING
        column TYPE lvc_fname
        idx    TYPE i
        text   TYPE string.

    DATA grid_head TYPE REF TO my_grid.
    DATA grid_data TYPE REF TO my_grid.

  PROTECTED SECTION.
    DATA data TYPE STANDARD TABLE OF t005t.
    DATA head TYPE REF TO data.

    DATA splitter TYPE REF TO cl_gui_splitter_container.
    DATA container_head TYPE REF TO cl_gui_container.
    DATA container_data TYPE REF TO cl_gui_container.

    DATA fcat_head TYPE lvc_t_fcat.
    DATA fcat_data TYPE lvc_t_fcat.

    METHODS convert_table_to_header.

ENDCLASS.

CLASS multi_title_grid IMPLEMENTATION.
  METHOD constructor.

    convert_table_to_header( ).

    splitter = NEW #( parent = container rows = 2 columns = 1 ).
    splitter->set_row_sash(
          EXPORTING
          id                = 1
          type              = cl_gui_splitter_container=>type_sashvisible
          value             = cl_gui_splitter_container=>false ).

    splitter->set_row_mode( mode = cl_gui_splitter_container=>mode_absolute ).

    splitter->set_row_height( id = 1 height = 80 ).


    container_head = splitter->get_container(
                       row    = 1
                       column = 1 ).

    container_data = splitter->get_container(
                       row    = 2
                       column = 1 ).

    grid_head = NEW #( i_parent = container_head ).

    FIELD-SYMBOLS <head> TYPE ANY TABLE.

    ASSIGN head->* TO <head>.

    grid_head->set_table_for_first_display(
      EXPORTING
         i_default                     = space
         is_layout                     = VALUE lvc_s_layo( no_toolbar = abap_true
                                                           no_headers = abap_false
                                                           no_hgridln = abap_true )
      CHANGING
        it_outtab                      = <head>
        it_fieldcatalog                = fcat_head
      EXCEPTIONS
        invalid_parameter_combination  = 1
       program_error                   = 2
        too_many_lines                 = 3
        OTHERS                         = 4  ).

    grid_data = NEW #( i_parent = container_data ).
    grid_data->set_table_for_first_display(
      EXPORTING
         i_default                     = space
         is_layout                     = VALUE lvc_s_layo( no_toolbar = abap_true
                                                           no_headers = abap_true )
      CHANGING
        it_outtab                      = data
        it_fieldcatalog                = fcat_data
      EXCEPTIONS
        invalid_parameter_combination  = 1
       program_error                   = 2
        too_many_lines                 = 3
        OTHERS                         = 4  ).


    grid_data->set_resize_cols_public( 0 ).

  ENDMETHOD.

  METHOD convert_table_to_header.

    DATA strdef_header TYPE REF TO cl_abap_structdescr.
    DATA tabdef_header TYPE REF TO cl_abap_tabledescr.

    DATA components_head TYPE cl_abap_structdescr=>component_table.

    CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
      EXPORTING
        i_buffer_active        = 'X'
        i_structure_name       = 'T005T'
      CHANGING
        ct_fieldcat            = fcat_data        " Field Catalog with Field Descriptions
      EXCEPTIONS
        inconsistent_interface = 1                  " Call parameter combination error
        program_error          = 2                  " Program Errors
        OTHERS                 = 3.
    IF sy-subrc = 0.

      LOOP AT fcat_data ASSIGNING FIELD-SYMBOL(<field_data>).
        APPEND INITIAL LINE TO fcat_head ASSIGNING FIELD-SYMBOL(<field_head>).
        <field_head> = <field_data>.
        <field_head>-rollname  = 'TEXT40'.
        <field_head>-domname   = 'TEXT40'.
        <field_head>-inttype   = 'C'.
        <field_head>-datatype  = 'CHAR'.
        <field_head>-ref_field = space.
        <field_head>-ref_table = space.
        <field_head>-edit_mask = space.
        <field_head>-convexit  = space.
        IF <field_head>-key = abap_true.
          <field_head>-key = abap_false.
          <field_head>-emphasize = 'C310'.
        ELSE.
          <field_head>-emphasize = 'C300'.
        ENDIF.
        APPEND VALUE #(
          name = <field_data>-fieldname
          type = CAST cl_abap_datadescr( cl_abap_elemdescr=>describe_by_name( 'TEXT40' ) )
          ) TO components_head.
      ENDLOOP.
    ENDIF.

    TRY.
        strdef_header = cl_abap_structdescr=>create(
                          p_components = components_head ).
        tabdef_header = cl_abap_tabledescr=>create(
                          p_line_type  = strdef_header
                          p_table_kind = cl_abap_tabledescr=>tablekind_std  ).
        CREATE DATA head TYPE HANDLE tabdef_header.

        LOOP AT fcat_head
        ASSIGNING <field_head>.
          set_title_for_column(
            column = <field_head>-fieldname
            text1  = CONV #( <field_head>-scrtext_s )
            text2  = CONV #( <field_head>-scrtext_m )
            text3  = CONV #( <field_head>-reptext ) ).


        ENDLOOP.

      CATCH cx_sy_table_creation.
      CATCH cx_sy_struct_creation.
    ENDTRY.

  ENDMETHOD.

  METHOD set_title_for_column.

    set_title_for_column_n(
      column = column
      idx    = 1
      text   = text1 ).

    set_title_for_column_n(
      column = column
      idx    = 2
      text   = text2 ).

    set_title_for_column_n(
      column = column
      idx    = 3
      text   = text3 ).

  ENDMETHOD.

  METHOD set_title_for_column_n.

    FIELD-SYMBOLS <head> TYPE STANDARD TABLE.
    ASSIGN head->* TO <head>.

    READ TABLE <head> INDEX idx ASSIGNING FIELD-SYMBOL(<head_line>).
    IF sy-subrc > 0.
      APPEND INITIAL LINE TO <head> ASSIGNING <head_line>.
    ENDIF.

    ASSIGN COMPONENT column OF STRUCTURE <head_line> TO FIELD-SYMBOL(<text>).
    CHECK sy-subrc = 0.

    <text> = text.

  ENDMETHOD.

  METHOD set_data.

    SELECT * FROM t005t
      INTO TABLE @data
     WHERE spras = @sy-langu.

    grid_data->refresh_table_display( ).

  ENDMETHOD.

  METHOD copy.

    grid_head->get_frontend_fieldcatalog(
      IMPORTING
        et_fieldcatalog = DATA(fcat_head) ).

    grid_data->get_frontend_fieldcatalog(
      IMPORTING
        et_fieldcatalog = DATA(fcat_data) ).

    LOOP AT fcat_head INTO DATA(field_head).
      fcat_data[ fieldname = field_head-fieldname ]-outputlen = field_head-outputlen.
    ENDLOOP.

    grid_data->set_frontend_fieldcatalog( fcat_data ).
    grid_data->refresh_table_display(
      i_soft_refresh = abap_true
      is_stable = VALUE #(
        col = abap_true
        row = abap_true ) ).

    "eliminate marks
    grid_head->refresh_table_display(
      i_soft_refresh = abap_true
      is_stable = VALUE #(
        col = abap_true
        row = abap_true ) ).

  ENDMETHOD.

ENDCLASS.

INITIALIZATION.

  DATA(docker) = NEW cl_gui_docking_container( side = cl_gui_docking_container=>dock_at_bottom ratio = 90 ).
  DATA(demo) = NEW multi_title_grid( container = docker ).
  demo->set_data( ).

AT SELECTION-SCREEN.
  demo->copy( ).

No comments:

Post a Comment