Autosumme in ALV-Grid – Version 2
Im vorherigen Artikel Autosumme in ALV-Grid (Version 1) habe ich euch gezeigt, wie man das Ereignis set_delay_change_selection nutzen kann, um die Summe von markierten Zellen eines ALV-Grids herausfinden kann. Diese Methode hat leider zwei Nachteile:
- Die Reaktionszeit des Ereignisses Delayed_Changed_Sel_Callback ist auf 1,5 Sekunden voreingestellt. Das ist viel zu lange, um schnell mal eben einige Werte zu prüfen
- Es funktioniert nur, wenn die Datentabelle bekannt ist, denn die Datentabelle des ALV-Grid MT_OUTTAB ist geschützt. Auf sie kann also nicht zugegriffen werden. Es ist demnach nicht möglich, eine Methode zu schreiben, die die Grid-Instanz einfach entgegen nimmt und darauf die Funktion Autosumme anwendet.
Wie kann man nun dieser Herausforderung begegnen? Es gibt zwei Varianten. Die erste Variante habe ich hier bereits beschrieben. Die Lösung ist in diesem Fall, dass man eine eigene Klasse von CL_GUI_ALV_GRID ableitet (erben lässt) und dann eine neue Methode erstellt, die diese geschützte Methode aufruft.
Die andere Variante ist hier von Lukasz Pegiel beschrieben: http://abapblog.com/articles/tricks/105-how-to-access-private-or-protected-data-and-methods-of-cl-gui-alv-grid
In dieser Variante wird einfach das Interface if_alv_rm_grid_friend eingebunden. Hierdurch wird die eigene Klasse als “Freund des ALV-Grid” bekannt gemacht. Und als Freund ist es möglich, auf die geschützten Attribute und Methoden zuzugreifen!
[notice type=’info’]Vielen Dank an dieser Stelle an Lukasz, der mir sehr geholfen hat, diesen Trick Wirklichkeit werden zu lassen! Ich kann jedem nur empfehlen einen oder besser: mehrere Blicke auf seinen ABAPBlog zu werfen. Besonders hervorzuheben ist seine Entwicklung Fast ALV-Grid. [/notice]
1. Verbesserung: Änderung der Reaktionszeit
Um das Event zu behandeln und die markierten Zellen auszulesen, bedarf es keiner großen Tricks. Allerdings ist die Reaktionszeit Das ist deutlich zu lange. Mit Hilfe der Methode set_delay_change_selection kann die Reaktionszeit geändert werden. Diese Methode ist allerdings PROTECTED, also geschützt.
Wir müssen also das oben genannte Interface einbinden und haben so Zugriff auf die Methode set_delay_change_selection mit der man die Reaktionszeit in Millisekunden einstellen kann.
2. Verbesserung: Zugriff auf die Datentabelle des ALV-Grid
Zusätzlich wollte ich an das geschützte Attribute MT_OUTTAB heran, um die aktuellen Daten auszulesen. Auf diese Weise ist es möglich, ein universelles Tool zu schreiben, dass mit jedem ALV-Grid zusammen arbeitet und nicht nur lokal.
Wie sieht’s aus?
Beide Verbesserungen sind in dieser Version 2 des Codes enthalten. Das Programm besteht nun aus zwei Klassen:
- Die Klasse, LCL_MAIN, die den Grid verwendet und darstellt.
- Die Klasse LCL_AUTOSUMME, die die eigentliche Funktionalität zur Verfügung stellt.
LCL_AUTOSUMME kann nun also als globale Klasse universell eingesetzt werden. Jedes Grid, dass die Autosummenfunktion verwenden möchte, muss sich in der Klasse mit der Methode REGISTER registrieren. Und dann kann’s los gehen…!
Code
REPORT. PARAMETERS p_total TYPE p DECIMALS 2. CLASS lcl_autosumme DEFINITION. PUBLIC SECTION. INTERFACES if_alv_rm_grid_friend . CLASS-METHODS register IMPORTING ir_grid TYPE REF TO cl_gui_alv_grid. PROTECTED SECTION. CLASS-METHODS handle_delayed_selection FOR EVENT delayed_changed_sel_callback OF cl_gui_alv_grid IMPORTING sender. ENDCLASS. CLASS lcl_autosumme IMPLEMENTATION. METHOD register. "Set handler SET HANDLER handle_delayed_selection FOR ir_grid. "set delayed selection time ir_grid->set_delay_change_selection( time = 100 ). " Time in Milliseconds "register event for delayed selection ir_grid->register_delayed_event( ir_grid->mc_evt_delayed_change_select ). ENDMETHOD. METHOD handle_delayed_selection. "Local data DATA lt_cells TYPE lvc_t_cell. DATA ls_cell LIKE LINE OF lt_cells. DATA lv_total TYPE p DECIMALS 2. DATA lv_val_type TYPE c. DATA lv_index TYPE n LENGTH 2. DATA lv_tablename TYPE string. DATA lt_grouplevels TYPE lvc_t_grpl. DATA ls_grouplevel LIKE LINE OF lt_grouplevels. FIELD-SYMBOLS <ref_data> TYPE REF TO data. FIELD-SYMBOLS <table> TYPE table. FIELD-SYMBOLS <warea> TYPE any. FIELD-SYMBOLS <val> TYPE any. "data references to sub totals tables DATA ld_ct01 TYPE REF TO data. DATA ld_ct02 TYPE REF TO data. DATA ld_ct03 TYPE REF TO data. DATA ld_ct04 TYPE REF TO data. DATA ld_ct05 TYPE REF TO data. DATA ld_ct06 TYPE REF TO data. DATA ld_ct07 TYPE REF TO data. DATA ld_ct08 TYPE REF TO data. DATA ld_ct09 TYPE REF TO data. "get selected cells (selection via CTRL + Y) sender->get_selected_cells( IMPORTING et_cell = lt_cells ). "If there is only one cell selected, we do not need to sum that... CHECK lines( lt_cells ) > 1. "Read all cell values LOOP AT lt_cells INTO ls_cell. "in case of rowtype (normal cell, total or subtotal) assign correct data table CASE ls_cell-row_id-rowtype(1). "Total sum of all WHEN 'T'. ASSIGN sender->mt_ct00 TO <ref_data>. ls_cell-row_id-index = 1. "subtotals WHEN 'S'. sender->get_subtotals( IMPORTING ep_collect01 = ld_ct01 ep_collect02 = ld_ct02 ep_collect03 = ld_ct03 ep_collect04 = ld_ct04 ep_collect05 = ld_ct05 ep_collect06 = ld_ct06 ep_collect07 = ld_ct07 ep_collect08 = ld_ct08 ep_collect09 = ld_ct09 et_grouplevels = lt_grouplevels ). lv_index = ls_cell-row_id-rowtype+4(2). lv_tablename = 'LD_CT' && lv_index. ASSIGN (lv_tablename) TO <ref_data>. READ TABLE lt_grouplevels INTO ls_grouplevel INDEX ls_cell-row_id-index. IF sy-subrc = 0. ls_cell-row_id-index = ls_grouplevel-cindx_from. ENDIF. "Normal cell value WHEN space. ASSIGN sender->mt_outtab TO <ref_data>. ENDCASE. "assign specified data table ASSIGN <ref_data>->* TO <table>. "Only read table line when index changes AT NEW row_id. READ TABLE <table> ASSIGNING <warea> INDEX ls_cell-row_id-index. ENDAT. "Assign selected fieldname of workarea ASSIGN COMPONENT ls_cell-col_id OF STRUCTURE <warea> TO <val>. IF sy-subrc = 0. "check correct type of field: Only numeric fields will be taken DESCRIBE FIELD <val> TYPE lv_val_type. CASE lv_val_type. WHEN 'P' "Packed OR 'N' "Numchar OR 'b' "Integer OR 'a' "decfloat OR 'e' "decfloat OR 'F'. "Float? "add cell value to total ADD <val> TO lv_total. ENDCASE. ENDIF. ENDLOOP. IF lv_total IS NOT INITIAL. "There were numeric fields selected and therefor we have a total to show: MESSAGE s000(oo) WITH 'TOTAL:' space lv_total. p_total = lv_total. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_main DEFINITION. PUBLIC SECTION. * INTERFACES if_alv_rm_grid_friend . METHODS start. PROTECTED SECTION. DATA mr_grid TYPE REF TO cl_gui_alv_grid. DATA mt_data TYPE STANDARD TABLE OF spfli. DATA mv_data_table TYPE tabname VALUE 'SPFLI'. DATA mr_dock TYPE REF TO cl_gui_docking_container. METHODS create_docker. METHODS create_grid. METHODS select_data. METHODS register_autosumme. ENDCLASS. CLASS lcl_main IMPLEMENTATION. METHOD start. select_data( ). create_docker( ). create_grid( ). register_autosumme( ). ENDMETHOD. METHOD create_docker. "Create Docking container at bottom CREATE OBJECT mr_dock EXPORTING side = cl_gui_docking_container=>dock_at_bottom ratio = 90 no_autodef_progid_dynnr = abap_false. ENDMETHOD. METHOD create_grid. "Create ALV-Grid CREATE OBJECT mr_grid EXPORTING i_appl_events = abap_true i_parent = mr_dock. "and display data mr_grid->set_table_for_first_display( EXPORTING i_structure_name = mv_data_table CHANGING it_outtab = mt_data ). "Set focus on grid so user can directly scroll and select cells via CTRL+Y cl_gui_container=>set_focus( mr_grid ). ENDMETHOD. METHOD select_data. "Select data SELECT * FROM (mv_data_table) INTO TABLE mt_data UP TO 100 ROWS. ENDMETHOD. METHOD register_autosumme. lcl_autosumme=>register( mr_grid ). ENDMETHOD. ENDCLASS. INITIALIZATION. DATA(gr_main) = NEW lcl_main( ). gr_main->start( ).
- Interview mit Björn Schulz (Software-Heroes.com) - 3. September 2024
- Daten aus ALV ermitteln - 3. September 2024
- So lange es den SAPGUI noch gibt… - 27. Juni 2024