[ABAP] XLSX-Datei mit Klasse cl_ehfnd_xlsx einlesen und in SALV-Grid anzeigen

TRY.
    DATA: lv_rc TYPE i.
    DATA: it_files TYPE filetable.
    DATA: lv_action TYPE i.

* FileOpen-Dialog aufrufen
    cl_gui_frontend_services=>file_open_dialog( EXPORTING file_filter = |xlsx (*.xlsx)\|*.xlsx\|{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING  file_table  = it_files
                                                          rc          = lv_rc
                                                          user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
      IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
        DATA: lv_filesize TYPE w3param-cont_len.
        DATA: lv_filetype TYPE w3param-cont_type.
        DATA: it_bin_data TYPE w3mimetabtype.

* Excel-Datei auf Appl. Server hochladen (binary)
        cl_gui_frontend_services=>gui_upload( EXPORTING filename   = |{ it_files[ 1 ]-filename }|
                                                        filetype   = 'BIN'
                                              IMPORTING filelength = lv_filesize
                                              CHANGING  data_tab   = it_bin_data ).

* solix -> xstring
        DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

**********************************************************************
* XLSX Handling: Kapselt cl_xlsx_document
**********************************************************************
        DATA(o_excel) = cl_ehfnd_xlsx=>get_instance( ).

* XLSX Workbook (XString --> XML)
        DATA(o_doc) = o_excel->load_doc( iv_file_data = lv_bin_data ).

* XLSX Sheets des Workbooks holen
        DATA(it_sheets) = o_doc->get_sheets( ).

        IF lines( it_sheets ) > 0.
* XLSX Sheet: erste Sheet holen
          DATA(o_sheet) = o_doc->get_sheet_by_id( iv_sheet_id = 1 ).
          DATA(lv_sheet_name) = it_sheets[ 1 ]-name.

* max. Zeilenzahl der Sheet
          DATA(lv_max_rows) = o_sheet->get_last_row_number( ).
          DATA(lv_max_cols) = o_sheet->get_last_column_number_in_row( 1 ).

          IF lv_max_rows > 0 AND lv_max_cols > 0.
* Komponenten (Spalten) der Tabelle --> generische Stringtable bauen
            DATA(it_components) = VALUE cl_abap_structdescr=>component_table( ).

* Überschriften (Header) für ALV-Grid
            DATA(it_colnames) = VALUE stringtab( ).

            DO lv_max_cols TIMES.
* Spaltenbezeichner aus 1. Zeile (Header) der Excel-Tabelle holen
              DATA(lv_col_header) = o_sheet->get_cell_content( iv_row    = 1
                                                               iv_column = sy-index ).

* alle Vorkommen, die nicht [a-zA-Z0-9_] entsprechen, durch '_' ersetzen
              REPLACE ALL OCCURRENCES OF REGEX '([^\w]|[äöüÄÖÜß])+' IN lv_col_header WITH '_'.

* Tabelle mit Überschriften für ALV-Grid füllen
              APPEND lv_col_header TO it_colnames.

* Spalte vom Typ String mit generischem Namen zur Komponententabelle hinzufügen
              APPEND VALUE #( name   = |COL{ sy-index }|
                              type   = cl_abap_elemdescr=>get_string( )
                            ) TO it_components.
            ENDDO.

**********************************************************************
* generische interne Tabelle mit hilfe dynamischer Objekte erzeugen
**********************************************************************
* Strukturdeskriptor für Komponententabelle erzeugen
            DATA(o_struct_desc) = cl_abap_structdescr=>create( it_components ).

* Tabellendeskriptor erzeugen
            DATA(o_table_desc) = cl_abap_tabledescr=>create( p_line_type = o_struct_desc ).

* dynamisches Tabellenobjekt anhand des Tabellendeskriptors erstellen
            DATA: o_table TYPE REF TO data.
            CREATE DATA o_table TYPE HANDLE o_table_desc.

* Feldsymbol auf das Tabellenobjekt vom Typ STANDARD TABLE anlegen
            FIELD-SYMBOLS <table> TYPE STANDARD TABLE.
            ASSIGN o_table->* TO <table>.

* Inhalt (ohne Header) aus XLSX in interne Tabelle schreiben
            DATA(lv_row) = 2.

            DO lv_max_rows - 1 TIMES.
              DATA(lv_col) = 1.

* neue Ausgabezeile anfügen
              APPEND INITIAL LINE TO <table>.

* neue Ausgabezeile holen und Feldsymbol zuweisen
              DATA(lv_lc) = lines( <table> ).
              ASSIGN <table>[ lv_lc ] TO FIELD-SYMBOL(<row>).

              IF <row> IS ASSIGNED.
* für alle Spalten der Tabelle
                DO lv_max_cols TIMES.
* n-te Spalte <col> der akt. Tabellenzeile <row> ermitteln
                  ASSIGN COMPONENT lv_col OF STRUCTURE <row> TO FIELD-SYMBOL(<cell>).
                  IF <cell> IS ASSIGNED.
* Inhalt der akt. Zelle in die Zelle der internen Tabelle schreiben
                    <cell> = o_sheet->get_cell_content( iv_row    = lv_row
                                                        iv_column = lv_col ).


                  ENDIF.

                  lv_col = lv_col + 1.
                ENDDO.
              ENDIF.

              lv_row = lv_row + 1.
            ENDDO.

**********************************************************************
* Anzeige der gefüllten generischen Tabelle in einem SALV-Grid
**********************************************************************
            TRY.
*             SALV-Table
                DATA: o_salv TYPE REF TO cl_salv_table.

                cl_salv_table=>factory( IMPORTING r_salv_table = o_salv
                                        CHANGING  t_table      = <table> ).

*             Grundeinstellungen
                o_salv->get_functions( )->set_all( abap_true ).
                o_salv->get_columns( )->set_optimize( abap_true ).
                o_salv->get_display_settings( )->set_list_header( CONV #( lv_sheet_name ) ).
                o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
                o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

*             Spaltenüberschriften: technischer Name und Beschreibungstexte, Short Text und Medium Text leer lassen für Autosize
                LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
                  DATA(lv_idx) = sy-tabix.

                  DATA(o_col) = <c>-r_column.
                  o_col->set_short_text( || ).
                  o_col->set_medium_text( || ).
                  o_col->set_long_text( CONV #( it_colnames[ lv_idx ] ) ).
                ENDLOOP.

                o_salv->display( ).
              CATCH cx_root INTO DATA(e_txt).
                WRITE: / e_txt->get_text( ).
            ENDTRY.
          ENDIF.

        ENDIF.

      ENDIF.

    ENDIF.

  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
ENDTRY.

[ABAP] Editierbares SALV-Grid (IF_SALV_GUI_OM_EXTEND_GRID_API, IF_SALV_GUI_OM_EDIT_RESTRICTED)

* Quelle: https://blogs.sap.com/2022/08/01/editable-cl_salv_table-after-release-756/
* ab SAP Release 756
* Achtung: das Ganze funktioniert nur für kleine Tabellen mit max. 5000 Zellen, siehe:
*
* Methode: CL_SALV_GUI_OM_ADAPTER_TABLE->CAN_RUN_RESTRICTED_EDIT_MODE( )
* Konstante: CV_MAX_CELLS_FOR_EDITABLE (Wert: 5000).

* Eventhandler
CLASS lcl_events DEFINITION.
  PUBLIC SECTION.
* Bezeichner der Buttons
    CONSTANTS: co_btn_edit TYPE string VALUE 'BTN_EDIT'.
    CONSTANTS: co_btn_save TYPE string VALUE 'BTN_SAVE'.

* Platzhalter für Referenz auf SALV-Grid
    CLASS-DATA: o_salv TYPE REF TO cl_salv_table.

* Eventhandler-Methode für Button-Klicks in der Toolbar des SALV-Grids
    CLASS-METHODS : on_toolbar_click FOR EVENT added_function OF cl_salv_events_table
      IMPORTING
        e_salv_function
        sender.
  PRIVATE SECTION.
* Edit-Status des SALV-Grids
    CLASS-DATA: gv_edit TYPE abap_bool VALUE abap_false.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.
  METHOD on_toolbar_click.
    IF o_salv IS BOUND.

      DATA(o_api) = o_salv->extended_grid_api( ).
      DATA(o_edit) = o_api->editable_restricted( ).

      CASE e_salv_function.

        WHEN co_btn_edit.
* Edit-Modus umschalten
          IF gv_edit = abap_false.
            gv_edit = abap_true.
          ELSE.
            gv_edit = abap_false.
          ENDIF.

          TRY.
* Spalte(n) (nicht) editierbar setzen
              o_edit->set_attributes_for_columnname( EXPORTING columnname              = 'EKGRP'
                                                               all_cells_input_enabled = gv_edit ).

              o_edit->set_attributes_for_columnname( EXPORTING columnname              = 'SMTP_ADDR'
                                                               all_cells_input_enabled = gv_edit ).
            CATCH cx_salv_not_found.
          ENDTRY.

          o_edit->validate_changed_data( ).
          o_salv->refresh( ).

        WHEN co_btn_save.
* Daten auf Validität prüfen
          DATA(lv_data_is_valid) = abap_false.

          TRY.
              o_edit->validate_changed_data( IMPORTING is_input_data_valid = lv_data_is_valid ).
              o_salv->refresh( ).
            CATCH cx_salv_not_found.
          ENDTRY.

          IF lv_data_is_valid = abap_true.
* Daten hier speichern / weiterverarbeiten
            MESSAGE co_btn_save TYPE 'I'.
          ENDIF.
      ENDCASE.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  TRY.
* Beispieldaten (Einkäufergruppen) holen
      SELECT FROM t024
        FIELDS *
        INTO TABLE @DATA(it_t024).

      IF sy-subrc = 0.
        cl_salv_table=>factory( EXPORTING r_container  = cl_gui_container=>default_screen
                                IMPORTING r_salv_table = lcl_events=>o_salv
                                CHANGING  t_table      = it_t024 ).

* Standardbuttons der SALV-Table ausblenden
        lcl_events=>o_salv->get_functions( )->set_all( abap_false ).

* Eigenen SALV-Button hinzufügen
* das Hinzufügen des Buttons funktioniert nur, wenn die SALV-Table innerhalb eines Containers (z.B. cl_gui_container=>default_screen) eingebettet ist
        lcl_events=>o_salv->get_functions( )->add_function( name = |{ lcl_events=>co_btn_edit }|
                                                            icon = |{ icon_edit_file }|
                                                            text = 'Edit'
                                                            tooltip = 'Daten editieren'
                                                            position = if_salv_c_function_position=>right_of_salv_functions ).

        lcl_events=>o_salv->get_functions( )->add_function( name = |{ lcl_events=>co_btn_save }|
                                                            icon = |{ icon_save_as_template }|
                                                            text = 'Save'
                                                            tooltip = 'Daten speichern'
                                                            position = if_salv_c_function_position=>right_of_salv_functions ).

* Eventhandler für Klicks in die Toolbar des SALV-Grids setzen
        SET HANDLER lcl_events=>on_toolbar_click FOR lcl_events=>o_salv->get_event( ).

* SALV anzeigen
        lcl_events=>o_salv->display( ).

* Toolbar der Listausgabe unterdrücken
        cl_abap_list_layout=>suppress_toolbar( ).

* Listausgabe erzwingen für Erzeugung von cl_gui_container=>default_screen
        WRITE: space.

      ENDIF.
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] Zwei SALV-Grids in einem Splittercontainer anzeigen

* Daten für SALV-Grid oben
SELECT *
  INTO TABLE @DATA(it_scarr)
  FROM scarr.

* Daten für SALV-Grid unten
SELECT *
  INTO TABLE @DATA(it_sflight)
  FROM sflight.

* Referenzen auf GUI-Objekte
* Splitter
DATA: o_splitter_main TYPE REF TO cl_gui_splitter_container.
* Splitter-Container oben
DATA: o_container_o   TYPE REF TO cl_gui_container.
* Splitter-Container unten
DATA: o_container_u   TYPE REF TO cl_gui_container.

* Splitter auf default_screen erzeugen
o_splitter_main = NEW #( parent                  = cl_gui_container=>default_screen
                         no_autodef_progid_dynnr = abap_true       " wichtig
                         rows                    = 2
                         columns                 = 1 ).

* Höhe oberer Splitter in %
o_splitter_main->set_row_height( id = 1 height = 40 ).

* REF auf oberen und unteren Splitcontainer holen
o_container_o = o_splitter_main->get_container( row = 1 column = 1 ).
o_container_u = o_splitter_main->get_container( row = 2 column = 1 ).

* SALV-Table oben mit Fluggesellschaften
DATA: o_salv_o TYPE REF TO cl_salv_table.

cl_salv_table=>factory( EXPORTING
                          r_container  = o_container_o
                        IMPORTING
                          r_salv_table = o_salv_o
                        CHANGING
                          t_table      = it_scarr ).

* Grundeinstellungen
o_salv_o->get_functions( )->set_all( abap_true ).
o_salv_o->get_columns( )->set_optimize( abap_true ).
o_salv_o->get_display_settings( )->set_list_header( 'Fluggesellschaften' ).
o_salv_o->get_display_settings( )->set_striped_pattern( abap_true ).
o_salv_o->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

* Spaltenüberschriften: technischer Name und Beschreibungstexte
LOOP AT o_salv_o->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<so>).
  DATA(o_col_o) = <so>-r_column.
  o_col_o->set_short_text( || ).
  o_col_o->set_medium_text( || ).
  o_col_o->set_long_text( |{ o_col_o->get_columnname( ) }| ).
ENDLOOP.

* SALV-Grid anzeigen
o_salv_o->display( ).

* SALV-Table unten mit Flügen
DATA: o_salv_u TYPE REF TO cl_salv_table.

cl_salv_table=>factory( EXPORTING
                          r_container  = o_container_u
                        IMPORTING
                          r_salv_table = o_salv_u
                        CHANGING
                          t_table      = it_sflight ).

* Grundeinstellungen
o_salv_u->get_functions( )->set_all( abap_true ).
o_salv_u->get_columns( )->set_optimize( abap_true ).
o_salv_u->get_display_settings( )->set_list_header( 'Flüge' ).
o_salv_u->get_display_settings( )->set_striped_pattern( abap_true ).
o_salv_u->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

* Spaltenüberschriften: technischer Name und Beschreibungstexte
LOOP AT o_salv_u->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<su>).
  DATA(o_col_u) = <su>-r_column.
  o_col_u->set_short_text( || ).
  o_col_u->set_medium_text( || ).
  o_col_u->set_long_text( |{ o_col_u->get_columnname( ) }| ).
ENDLOOP.

* SALV-Grid anzeigen
o_salv_u->display( ).

* leere Toolbar ausblenden
cl_abap_list_layout=>suppress_toolbar( ).

* Erzwingen von cl_gui_container=>default_screen
WRITE: space.

[ABAP] Klasse zur Darstellung von Nachrichtentexten in einem SALV-Grid in einem Popup-Dialog

CLASS lcl_popup_msg_box DEFINITION.

  PUBLIC SECTION.
* Typ Textzeile
    TYPES: BEGIN OF ty_textline,
             idx  TYPE rspos,
             text TYPE bapi_msg,
           END OF ty_textline.

    TYPES: ty_it_messagetab TYPE STANDARD TABLE OF ty_textline WITH DEFAULT KEY.

* Typ Button-Event
    TYPES: ty_it_events TYPE STANDARD TABLE OF cntl_simple_event WITH DEFAULT KEY.

* Anzeige des Popups
    CLASS-METHODS show
      IMPORTING
                i_window_title   TYPE string
                i_window_top     TYPE i
                i_window_left    TYPE i
                i_window_width   TYPE i
                i_window_height  TYPE i
                i_show_functions TYPE boolean
                i_it_messages    TYPE ty_it_messagetab
      RETURNING VALUE(rv_ok)     TYPE boolean.

  PRIVATE SECTION.

* Button Funktionskonstanten
    CONSTANTS: co_btn_ok TYPE ui_func VALUE 'BTN_OK'.
    CONSTANTS: co_btn_cancel TYPE ui_func VALUE 'BTN_CANCEL'.

* Message-Tabelle
    CLASS-DATA: it_messages TYPE ty_it_messagetab.

* GUI-Objekte
    CLASS-DATA: o_cnt TYPE REF TO cl_gui_dialogbox_container.
    CLASS-DATA: o_salv TYPE REF TO cl_salv_table.
    CLASS-DATA: o_split TYPE REF TO cl_gui_splitter_container.
    CLASS-DATA: o_tool TYPE REF TO cl_gui_toolbar.
    CLASS-DATA: gv_retval TYPE boolean VALUE abap_false.

* on_close-Handler
    CLASS-METHODS:
      on_close FOR EVENT close OF cl_gui_dialogbox_container
        IMPORTING
            sender.
* on_function_selected Handler
    CLASS-METHODS:
      on_function_selected FOR EVENT function_selected OF cl_gui_toolbar
        IMPORTING
            fcode
            sender.

ENDCLASS.

CLASS lcl_popup_msg_box IMPLEMENTATION.

  METHOD show.

    it_messages = i_it_messages.

* Container für GUI-Elemente
    o_cnt = NEW #( parent = cl_gui_container=>default_screen
                   caption = |{ i_window_title }|
                   top = i_window_top
                   left = i_window_left
                   width = i_window_width
                   height = i_window_height
                   no_autodef_progid_dynnr = abap_true
                 ).

* on_close-Handler setzen
    SET HANDLER on_close FOR o_cnt.

* Splitter für Grid und Toolbar
    o_split = NEW #( parent = o_cnt
                     no_autodef_progid_dynnr = abap_true
                     rows = 2
                     columns = 1 ).

* Unteren Splitterbereich setzen
    o_split->set_row_sash( id    = 2
                           type  = cl_gui_splitter_container=>type_movable
                           value = cl_gui_splitter_container=>false ).

    o_split->set_row_sash( id    = 2
                           type  = cl_gui_splitter_container=>type_sashvisible
                           value = cl_gui_splitter_container=>false ).

    o_split->set_row_height( id = 2 height = 8 ).

* Oberen und unteren Splittercontainer holen
    DATA(o_container_top)    = o_split->get_container( row = 1 column = 1 ).
    DATA(o_container_bottom) = o_split->get_container( row = 2 column = 1 ).
* Toolbar für Buttons erzeugen
    DATA(o_tool) = NEW cl_gui_toolbar( parent       = o_container_bottom
                                       display_mode = cl_gui_toolbar=>m_mode_horizontal
                                       align_right  = 1 ).

* Toolbarevents
    DATA(it_events) = VALUE ty_it_events( ( eventid    = cl_gui_toolbar=>m_id_function_selected
                                            appl_event = abap_true ) ).

    o_tool->set_registered_events( events = it_events ).

* Buttons + Separator einfügen
    o_tool->add_button( fcode       = co_btn_ok
                        icon        = icon_okay
                        butn_type   = cntb_btype_button
                        text        = 'Ok'
                        quickinfo   = 'Ok'
                        is_checked  = abap_false
                        is_disabled = abap_false ).

    o_tool->add_button( fcode       = ''
                        icon        = ''
                        butn_type   = cntb_btype_sep
                        text        = ''
                        quickinfo   = ''
                        is_checked  = abap_false
                        is_disabled = abap_false ).

    o_tool->add_button( fcode       = co_btn_cancel
                        icon        = icon_cancel
                        butn_type   = cntb_btype_button
                        text        = 'Abbruch'
                        quickinfo   = 'Abbruch'
                        is_checked  = abap_false
                        is_disabled = abap_false ).

* on_function_selected-Handler
    SET HANDLER on_function_selected FOR o_tool.

* SALV-Grid im oberen Splitter einfügen
    cl_salv_table=>factory( EXPORTING
                              r_container  = o_container_top
                            IMPORTING
                              r_salv_table = o_salv
                            CHANGING
                              t_table      = it_messages ).

* Eigenschaften SALV-Grid
    o_salv->get_columns( )->set_optimize( abap_true ).
    o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
    IF i_show_functions = abap_true.
      o_salv->get_functions( )->set_all( ).
    ENDIF.
    o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).
    o_salv->display( ).

* Dummy-Screen aufrufen und somit cl_gui_container=>default_screen erzeugen -> Trägerdynpro für cl_gui_dialogbox_container
    CALL SCREEN 100.

* nach Buttonclick noch die Ergebnisrückgabe
    rv_ok = gv_retval.
  ENDMETHOD.

  METHOD on_close.

* cl_gui_dialogbox_container bei Klick auf Schließen-Kreuz schließen
    IF sender IS NOT INITIAL.
      sender->free( ).
    ENDIF.

* Zum aufrufenden Dynpro zurück
    LEAVE TO SCREEN 0.

  ENDMETHOD.

* Button ermitteln -> Reaktion -> Setzen des Rückgabewertes beim Schließen des Popups
  METHOD on_function_selected.
    CASE fcode.
      WHEN co_btn_ok.
        gv_retval = abap_true.
      WHEN co_btn_cancel.
        gv_retval = abap_false.
    ENDCASE.

* Popup-Fenster schließen
    on_close( o_cnt ).
  ENDMETHOD.
ENDCLASS.

* Dummy-Screen für cl_gui_container=>default_screen deklarieren
SELECTION-SCREEN BEGIN OF SCREEN 100.
SELECTION-SCREEN END OF SCREEN 100.

START-OF-SELECTION.

* Nachrichtentabelle erzeugen
  DATA(it_msg) = VALUE lcl_popup_msg_box=>ty_it_messagetab( ( idx = 1 text = 'Nachricht 1' )
                                                            ( idx = 2 text = 'Nachricht 2' )
                                                          ).

* Popup mit Nachrichten anzeigen
  IF abap_true = lcl_popup_msg_box=>show( EXPORTING
                                            i_window_title   = 'Meldungen'
                                            i_window_top     = 100
                                            i_window_left    = 100
                                            i_window_width   = 240
                                            i_window_height  = 240
                                            i_show_functions = abap_false
                                            i_it_messages    = it_msg ).
    WRITE: / 'OK.'.
  ELSE.
    WRITE: / 'Abbruch.'.
  ENDIF.

[ABAP] SALV-Table in der Listausgabe in Custom-Container anzeigen

DATA: o_alv TYPE REF TO cl_salv_table.

START-OF-SELECTION.
  DATA: it_spfli TYPE STANDARD TABLE OF spfli WITH DEFAULT KEY.

  SELECT * FROM spfli INTO CORRESPONDING FIELDS OF TABLE it_spfli.

  WRITE: / 'GUI-Objekt in der Listanzeige'.

  ULINE.

* freier Custom-Container in der Listenansicht
  DATA(o_cnt) = NEW cl_gui_custom_container( container_name = ''
                                             repid          = 'SAPMSSY0'
                                             dynnr          = '0120' ).
* Position des Containers
  o_cnt->set_top( 50 ).
  o_cnt->set_left( 50 ).
  o_cnt->set_width( 1000 ).
  o_cnt->set_height( 200 ).


* SALV-Grid für Anzeige im Container
  TRY.
      cl_salv_table=>factory( EXPORTING
                                r_container  = o_cnt
                              IMPORTING
                                r_salv_table = o_alv
                              CHANGING
                                t_table      = it_spfli ).

      o_alv->display( ).

    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

  DO 100 TIMES.
    WRITE: / |{ sy-index } Lorem ipsum.|.
  ENDDO.

[ABAP] SALV-Table: Darstellung (Ausgabeformat) einer Spalte über einen Konvertierungs-Exit anpassen

Beispiel 1: Zeitstempel formatieren (==TSTMP)

DATA: o_alv TYPE REF TO cl_salv_table.

DATA: it_tab TYPE ...

cl_salv_table=>factory( IMPORTING
                          r_salv_table = o_alv
                        CHANGING
                          t_table      = it_tab ).

* Der Inhalt der Spalte 'TIMESTAMP' soll anstelle des zusammengesetzten Standartformates für den p-Datentyp
* eine andere Darstellung, getrennt nach Datum und Uhrzeit, bekommen
* '20180101120101,1234567' -> '01.01.2018 12:01:01'
*
* Dazu wird der vorhandene Konvertierungs-Exit CONVERSION_EXIT_TSTMP_INPUT und CONVERSION_EXIT_TSTMP_OUTPUT
* aufgerufen, indem man das Kürzel '==TSTMP' (mittlere Buchstaben im Bezeichner CONVERSION_EXIT_... ) angibt.
*
* alle vorhandenen Konvertierungs-Exits: Transaktion SE37 -> Funktionsbaustein "CONVERSION_EXIT_*" -> Lupe
* Tabelle: TFDIR
o_alv->get_columns( )->get_column( 'TIMESTAMP' )->set_edit_mask( '==TSTMP' ).

o_alv->display( ).

Beispiel 2: Nachkommastellen abschneiden (==DEC0)

SELECT FROM marc
  FIELDS matnr, werks, eisbe
  INTO TABLE @DATA(it_marc)
  UP TO 100 ROWS.

IF sy-subrc = 0.
  TRY.
*   SALV-Table
      DATA: o_salv TYPE REF TO cl_salv_table.

      cl_salv_table=>factory( IMPORTING r_salv_table = o_salv
                              CHANGING  t_table      = it_marc ).

*   Grundeinstellungen
      o_salv->get_functions( )->set_all( abap_true ).
      o_salv->get_columns( )->set_optimize( abap_true ).
      o_salv->get_display_settings( )->set_list_header( 'MARC' ).
      o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
      o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

*   Spaltenüberschriften: technischer Name und Beschreibungstexte, Short Text und Medium Text leer lassen für Autosize
      LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
        DATA(o_col) = <c>-r_column.
        o_col->set_short_text( || ).
        o_col->set_medium_text( || ).
        o_col->set_long_text( |{ o_col->get_long_text( ) }| ).
      ENDLOOP.

* Sicherheitsbestand --> Nachkommastellen in der Darstellung abschneiden
      o_salv->get_columns( )->get_column( 'EISBE' )->set_edit_mask( value = '==DEC0' ).

      o_salv->display( ).
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.
ENDIF.

Links

[ABAP] GUI-Toolbar mit statischem Context-Menü, Eventhandling

TYPES: ty_it_events TYPE STANDARD TABLE OF cntl_simple_event WITH DEFAULT KEY.

CLASS lcl_events DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      on_function_selected FOR EVENT function_selected OF cl_gui_toolbar
        IMPORTING
            fcode
            sender.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.
* Toolbar: Button oder Menüpunkt geklickt
  METHOD on_function_selected.

* Funktionscode auswerten
    CASE fcode.
      WHEN 'BTN_CLOSE'.
        LEAVE PROGRAM.
      WHEN 'M1'.
      WHEN 'M2'.
    ENDCASE.

* fcode ausgeben
    MESSAGE fcode TYPE 'S'.

  ENDMETHOD.
ENDCLASS.

INITIALIZATION.

* Splitter erzeugen
  DATA(o_splitter) = NEW cl_gui_splitter_container( parent = cl_gui_container=>default_screen
                                                    no_autodef_progid_dynnr = abap_true
                                                    rows = 2
                                                    columns = 1 ).

* Absoluter Modus für Zeilenhöhe
  o_splitter->set_row_mode( mode = cl_gui_splitter_container=>mode_absolute ).

* Höhe absolut 24 Pixel für Splitter oben
  o_splitter->set_row_height( id = 1 height = 24 ).

* Splitter für oberen Container fest und verborgen
  o_splitter->set_row_sash( id    = 1
                            type  = cl_gui_splitter_container=>type_movable
                            value = cl_gui_splitter_container=>false ).

  o_splitter->set_row_sash( id    = 1
                            type  = cl_gui_splitter_container=>type_sashvisible
                            value = cl_gui_splitter_container=>false ).

  DATA(o_container_top) = o_splitter->get_container( row = 1 column = 1 ).
  DATA(o_container_bottom) = o_splitter->get_container( row = 2 column = 1 ).

* Toolbar hoizontal
  DATA(o_tool) = NEW cl_gui_toolbar( parent       = o_container_top
                                     display_mode = cl_gui_toolbar=>m_mode_horizontal ).

* Eventtypten müssen gesondert registriert werden
  DATA(it_events) = VALUE ty_it_events( ( eventid    = cl_gui_toolbar=>m_id_function_selected
                                          appl_event = abap_true ) ).

  o_tool->set_registered_events( events = it_events ).

* Toolbar-Buttons und Menüs hinzufügen
* Buttontypen sind in Typgruppe CNTB definiert
  o_tool->add_button( fcode       = 'BTN_MENU'
                      icon        = icon_activate
                      butn_type   = cntb_btype_menu
                      text        = 'Menü'
                      quickinfo   = 'Menü anzeigen'
                      is_checked  = abap_false
                      is_disabled = abap_false ).

* statisches Kontextmenü für Button "BTN_MENU" erstellen
  DATA(o_menu) = NEW cl_ctmenu( ).
  o_menu->add_function( fcode   = 'M1'
                        checked = abap_false
                        text    = 'Punkt1' ).

  o_menu->add_function( fcode   = 'M2'
                        checked = abap_false
                        text    = 'Punkt2' ).

* statisches Kontextmenü mit Button verknüpfen, Zuordnung erfolgt über den Namen
* daher muss "function" gleich "fcode" des Menü-Buttons sein, sonst Exception
  DATA(it_ctxmenu) = VALUE ttb_btnmnu( ( function = 'BTN_MENU'
                                         ctmenu   = o_menu ) ).

* siehe auch Methode "set_static_ctxmenu"
  o_tool->assign_static_ctxmenu_table( it_ctxmenu ).

* Separator
  o_tool->add_button( fcode       = ''
                      icon        = ''
                      butn_type   = cntb_btype_sep
                      text        = ''
                      quickinfo   = ''
                      is_checked  = abap_false
                      is_disabled = abap_false ).

* Schließen-Button
  o_tool->add_button( fcode       = 'BTN_CLOSE'
                      icon        = icon_close
                      butn_type   = cntb_btype_button
                      text        = 'Schließen'
                      quickinfo   = 'Schließen'
                      is_checked  = abap_false
                      is_disabled = abap_false ).

* Eventhandler registrieren
  SET HANDLER lcl_events=>on_function_selected FOR o_tool.

* im unteren Splitter Beispieldaten anzeigen
  DATA: it_sflight TYPE STANDARD TABLE OF sflight.

  SELECT * FROM sflight INTO TABLE @it_sflight.

  DATA: o_salv TYPE REF TO cl_salv_table.

  cl_salv_table=>factory( EXPORTING
                            r_container  = o_container_bottom
                          IMPORTING
                            r_salv_table = o_salv
                          CHANGING
                            t_table      = it_sflight ).

  o_salv->get_functions( )->set_all( ).
  o_salv->display( ).

* leere Standard-Toolbar ausblenden
  cl_abap_list_layout=>suppress_toolbar( ).

* Ausgabe von cl_gui_container=>default_screen erzwingen
  WRITE: space.

[ABAP] GUI-Toolbar mit dynamischen Context-Menü, Eventhandling, Umschaltung checked-state

TYPES: ty_it_events TYPE STANDARD TABLE OF cntl_simple_event WITH DEFAULT KEY.

TYPES: BEGIN OF ty_menuitem,
         fcode   TYPE ui_func,
         checked TYPE cua_active,
         text    TYPE gui_text,
       END OF ty_menuitem.

TYPES: ty_it_menuitems TYPE STANDARD TABLE OF ty_menuitem WITH DEFAULT KEY.

DATA: it_menuitems TYPE ty_it_menuitems.

CLASS lcl_events DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      on_function_selected FOR EVENT function_selected OF cl_gui_toolbar
        IMPORTING
            fcode
            sender.
    CLASS-METHODS:
      on_dropdown_clicked FOR EVENT dropdown_clicked OF cl_gui_toolbar
        IMPORTING
            fcode
            posx
            posy
            sender.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.
* Toolbar: Button oder Menüpunktgeklickt
  METHOD on_function_selected.

* Checked-Status eines Dropdown-Menüpunktes umschalten
    IF line_exists( it_menuitems[ fcode = fcode ] ).
      ASSIGN it_menuitems[ fcode = fcode ] TO FIELD-SYMBOL(<mc>).
      <mc>-checked = COND #( WHEN <mc>-checked = abap_true THEN abap_false ELSE abap_true ).
    ENDIF.

* Funktionscode auswerten
    CASE fcode.
      WHEN 'BTN_CLOSE'.
        LEAVE PROGRAM.
      WHEN 'BTN_DROP'.
      WHEN 'M1'.
      WHEN 'M2'.
    ENDCASE.

* fcode ausgeben
    MESSAGE fcode TYPE 'S'.

  ENDMETHOD.
* Toolbar: Dropdown aktiviert
  METHOD on_dropdown_clicked.
* Menü erzeugen
    DATA(o_ctx_menu) = NEW cl_ctmenu( ).

    LOOP AT it_menuitems ASSIGNING FIELD-SYMBOL(<m>).
      o_ctx_menu->add_function( fcode   = <m>-fcode
                                checked = <m>-checked
                                text    = <m>-text ).
    ENDLOOP.

* Kontextmenü an der akt. Position anzeigen
    sender->track_context_menu( context_menu = o_ctx_menu
                                posx         = posx
                                posy         = posy ).
  ENDMETHOD.
ENDCLASS.

INITIALIZATION.

  it_menuitems = VALUE #( ( fcode   = 'M1'
                            checked = abap_false
                            text    = 'Punkt1' )
                          ( fcode   = 'M2'
                            checked = abap_true
                            text    = 'Punkt2' ) ).

* Splitter erzeugen
  DATA(o_splitter) = NEW cl_gui_splitter_container( parent = cl_gui_container=>default_screen
                                                    no_autodef_progid_dynnr = abap_true
                                                    rows = 2
                                                    columns = 1 ).

* Absoluter Modus für Zeilenhöhe
  o_splitter->set_row_mode( mode = cl_gui_splitter_container=>mode_absolute ).

* Höhe absolut 24 Pixel für Splitter oben
  o_splitter->set_row_height( id = 1 height = 24 ).

* Splitter für oberen Container fest und verborgen
  o_splitter->set_row_sash( id = 1
                            type = cl_gui_splitter_container=>type_movable
                            value = cl_gui_splitter_container=>false ).

  o_splitter->set_row_sash( id = 1
                            type = cl_gui_splitter_container=>type_sashvisible
                            value = cl_gui_splitter_container=>false ).

  DATA(o_container_top) = o_splitter->get_container( row = 1 column = 1 ).
  DATA(o_container_bottom) = o_splitter->get_container( row = 2 column = 1 ).

* Toolbar hoizontal
  DATA(o_tool) = NEW cl_gui_toolbar( parent = o_container_top
                                     display_mode = cl_gui_toolbar=>m_mode_horizontal ).

* Eventtypten müssen gesondert registriert werden
  DATA(it_events) = VALUE ty_it_events( ( eventid = cl_gui_toolbar=>m_id_function_selected
                                          appl_event = abap_true )
                                        ( eventid = cl_gui_toolbar=>m_id_dropdown_clicked
                                          appl_event = abap_true ) ).

  o_tool->set_registered_events( events = it_events ).

* Toolbar-Buttons hinzufügen
* Buttontypen sind in Typgruppe CNTB definiert
  o_tool->add_button( fcode       = 'BTN_DROP'
                      icon        = icon_activate
                      butn_type   = cntb_btype_dropdown
                      text        = 'Menü'
                      quickinfo   = 'Menü anzeigen'
                      is_checked  = abap_false
                      is_disabled = abap_false ).

  o_tool->add_button( fcode       = ''
                      icon        = ''
                      butn_type   = cntb_btype_sep
                      text        = ''
                      quickinfo   = ''
                      is_checked  = abap_false
                      is_disabled = abap_false ).

  o_tool->add_button( fcode       = 'BTN_CLOSE'
                      icon        = icon_close
                      butn_type   = cntb_btype_button
                      text        = 'Schließen'
                      quickinfo   = 'Schließen'
                      is_checked  = abap_false
                      is_disabled = abap_false ).

* Eventhandler registrieren
  SET HANDLER lcl_events=>on_function_selected FOR o_tool.
  SET HANDLER lcl_events=>on_dropdown_clicked FOR o_tool.

* im unteren Splitter Beispieldaten anzeigen
  DATA: it_sflight TYPE STANDARD TABLE OF sflight.

  SELECT * FROM sflight INTO TABLE @it_sflight.

  DATA: o_salv TYPE REF TO cl_salv_table.

  cl_salv_table=>factory( EXPORTING
                            r_container    = o_container_bottom
                          IMPORTING
                            r_salv_table   = o_salv
                          CHANGING
                            t_table        = it_sflight ).

  o_salv->get_functions( )->set_all( ).
  o_salv->display( ).

* leere Standard-Toolbar ausblenden
  cl_abap_list_layout=>suppress_toolbar( ).

* Ausgabe von cl_gui_container=>default_screen erzwingen
  WRITE: space.

Weiterführender Link zur Ideen-Quelle: Link

[ABAP] ALV-Grid: Dropdown-Liste verwenden

**********************************************************************
*
* Variablen
*
**********************************************************************
DATA: gv_screen_status TYPE string VALUE 'INIT'.
DATA: gv_carrid TYPE spfli-carrid.
DATA: gv_connid TYPE spfli-connid.
DATA: o_alv TYPE REF TO cl_gui_alv_grid.
DATA: it_spfli TYPE STANDARD TABLE OF spfli WITH DEFAULT KEY.
**********************************************************************
*
* leeres Dynpro als Dummy für ALV-Grid
*
**********************************************************************
SELECTION-SCREEN BEGIN OF SCREEN 2000.
SELECTION-SCREEN END OF SCREEN 2000.
**********************************************************************
*
* SELECTION-SCREEN
*
**********************************************************************
SELECT-OPTIONS: so_carr FOR gv_carrid.
SELECT-OPTIONS: so_conn FOR gv_connid.
**********************************************************************
*
* Eventhandler
*
**********************************************************************
CLASS lcl_events DEFINITION.

  PUBLIC SECTION.

    CLASS-METHODS:
      on_toolbar FOR EVENT toolbar OF cl_gui_alv_grid
        IMPORTING
            e_object
            e_interactive
            sender.

    CLASS-METHODS:
      on_data_changed FOR EVENT data_changed OF cl_gui_alv_grid
        IMPORTING
            er_data_changed
            sender.
ENDCLASS.

CLASS lcl_events IMPLEMENTATION.

  METHOD on_data_changed.
* geänderte Zellen durchgehen
    LOOP AT er_data_changed->mt_good_cells ASSIGNING FIELD-SYMBOL(<fs_changed>).
      IF <fs_changed> IS ASSIGNED.
* Zeile x aus der iTab it_mara rausholen und daraus die Zelle anhand des Spaltennamens (Feldnamens) holen
        ASSIGN COMPONENT <fs_changed>-fieldname OF STRUCTURE it_spfli[ <fs_changed>-row_id ] TO FIELD-SYMBOL(<fs_mara_field>).

* Änderungswert in die Zelle der iTab (it_mara) rückschreiben
        <fs_mara_field> = <fs_changed>-value.
      ENDIF.

    ENDLOOP.

* DB Update
    FIELD-SYMBOLS: <fs_tab> TYPE table.
    FIELD-SYMBOLS: <fs_row> TYPE spfli.

    ASSIGN er_data_changed->mp_mod_rows->* TO <fs_tab>.

    LOOP AT <fs_tab> ASSIGNING <fs_row>.
* DB Update hier
    ENDLOOP.

  ENDMETHOD.

  METHOD on_toolbar.
* alle Buttons entfernen, bis auf folgende:
    DELETE e_object->mt_toolbar WHERE
        function NE cl_gui_alv_grid=>mc_fc_refresh          " Refresh
    AND function NE cl_gui_alv_grid=>mc_mb_export           " Excel
    AND function NE cl_gui_alv_grid=>mc_fc_current_variant. " Layout

  ENDMETHOD.
ENDCLASS.
**********************************************************************
*
* INITIALIZATION
*
**********************************************************************
INITIALIZATION.

* Vorbelegungen für Selektionsbild
  so_carr[] = VALUE #( ( sign = 'I' option = 'EQ' low = 'LH' ) ).

**********************************************************************
*
* AT SELECTION-SCREEN OUTPUT
*
**********************************************************************
AT SELECTION-SCREEN OUTPUT.

* Wenn vorher das Selektionsbild 1000 angezeigt wurde
  IF gv_screen_status = 'IN_SELECTION'.
* Daten holen
    SELECT * FROM spfli INTO TABLE @it_spfli
       WHERE carrid IN @so_carr
         AND connid IN @so_conn.
* ALV-Gitter anzeigen
    o_alv = NEW #( i_parent      = cl_gui_container=>default_screen
                   i_appl_events = abap_true ).

* Eventhandler registrieren
    SET HANDLER lcl_events=>on_toolbar FOR o_alv.
    SET HANDLER lcl_events=>on_data_changed FOR o_alv.

* Ereignisse registrieren
    o_alv->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_enter ).
    o_alv->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_modified ).

* ALV-Grid selektionsbereit setzen
    o_alv->set_ready_for_input( i_ready_for_input = 1 ).

* Layout des ALV setzen
    DATA(lv_layout) = VALUE lvc_s_layo( zebra      = abap_true
                                        cwidth_opt = 'A'
                                        grid_title = 'Flugverbindungen' ).

* Feldkatalog automatisch durch SALV erstellen lassen
    DATA: o_salv TYPE REF TO cl_salv_table.

    cl_salv_table=>factory( IMPORTING
                              r_salv_table = o_salv
                            CHANGING
                              t_table      = it_spfli ).

    DATA(it_fcat) = cl_salv_controller_metadata=>get_lvc_fieldcatalog( r_columns      = o_salv->get_columns( )
                                                                       r_aggregations = o_salv->get_aggregations( ) ).

* Drop-Down-Liste definieren und an das ALV-Gitter übergeben
    DATA(it_dropdown) = VALUE lvc_t_drop( ( handle = 1
                                            value  = 'BERLIN' )
                                          ( handle = 1
                                            value  = 'FRANKFURT' )
                                          ( handle = 1
                                            value  = 'NEW YORK' ) ).

    o_alv->set_drop_down_table( it_drop_down = it_dropdown ).

* im Feldkatalog alle Zellen der Spalte "CITYFROM" des ALV-Grids auf
* editierbar stellen, die restlichen Zellen sind nicht editierbar
    LOOP AT it_fcat ASSIGNING FIELD-SYMBOL(<fcat>).
      CASE <fcat>-fieldname.
        WHEN 'CITYFROM'.
          <fcat>-edit = abap_true.
          <fcat>-drdn_hndl = 1.      " Drop-Down-Liste mit Handle = 1 für die Zelle setzen
          <fcat>-outputlen = 10.
        WHEN OTHERS.
          <fcat>-edit = abap_false.
      ENDCASE.
    ENDLOOP.

* ALV anzeigen
    o_alv->set_table_for_first_display( EXPORTING
                                          i_bypassing_buffer = abap_false
                                          i_save             = 'A'
                                          is_layout          = lv_layout
                                        CHANGING
                                          it_fieldcatalog    = it_fcat
                                          it_outtab          = it_spfli ).

* Focus auf ALV setzen
    cl_gui_alv_grid=>set_focus( control = o_alv ).

* leere SAP-Toolbar ausblenden
    cl_abap_list_layout=>suppress_toolbar( ).

* Flag für Screen-Status auf ALV-Anzeige setzen
    gv_screen_status = 'IN_ALV'.
  ENDIF.
**********************************************************************
*
* START-OF-SELECTION
*
**********************************************************************
START-OF-SELECTION.

* Wir befinden uns im Anzeigebereich des Selektionsbildes
  gv_screen_status = 'IN_SELECTION'.

* Trick: leeren Dummy-Screen 2000 anzeigen und intern für das ALV-Grid in
* AT SELECTION-SCREEN OUTPUT als cl_gui_container=>default_screen nutzen
  CALL SELECTION-SCREEN 2000.