Using an extra protocol outside of ALV-Grid data handling
When editing an ALV-Grid you can send some messages to the user saying that he entered wrong data. Normally and mostly you will get a popup that displays the errors or warnings.
This looks quite ugly.
Therefore I like to use the possibility to define an extra message container for the grids messages. You can do this by passing a container reference with parameter I_APPLOGPARENT when creating the ALV grid.
CREATE OBJECT mo_alv EXPORTING i_parent = io_container_grid i_applogparent = io_container_prot i_appl_events = 'X' EXCEPTIONS OTHERS = 5.
That looks much nicer:
But then I also wanted to display other messages in this message box that come from other functions but they are important for that grid.
Like: “Please mark a row for executing this function”
First I was struggling because the protocol object used within the ALV grid is PRIVATE *sigh*. So you cannot inherit the CL_GUI_ALV_GRID-class and write a method to access the protcol object MR_DATA_CHANGED.
Then I found two solutions. Both are not perfect but they work:
- Use the friends concept for accessing protected and private methods (IF_ALV_RM_GRID_FRIEND) see ABAPblog or Tricktresor (example Autosum)
- Create and destroy a separate instance of class CL_ALV_CHANGED_DATA_PROTOCOL
I will show you the second variation which works pretty well.
Before passing a message you will have to create an instance of CL_ALV_CHANGED_DATA_PROTOCOL and passing the container where the protocol should be shown to the object:
mo_prot = NEW #( i_calling_alv = mo_alv i_container = mo_message_container ).
The ALV-grid protocol container and the extra container cannot be shown simultanously. Before using one you will have to destroy the other.
The report shows a selection screen and two docking container.
You can enter data in both. But when entering data in the parameter P_TEST you will get an error message in the alv-grid message container.
If you enter values in the KEY field that do not contain A, B or C, you will get an error message created the normal way in grid event DATA_CHANGED:
The disadvantage is that you cannot use both message senders at once. If there are any errors in the grid then the extra messages will not be shown. If you put the checks in a separate method where to pass the protocol object then you could call this method and add your extra messages to the standard alv grid.
Here is the complete coding (also at github):
REPORT ztrcktrsr_alv_edit_protocol. CLASS lcl_demo DEFINITION. PUBLIC SECTION. CLASS-METHODS go IMPORTING io_container_grid TYPE REF TO cl_gui_container io_container_prot TYPE REF TO cl_gui_container. CLASS-METHODS set_message IMPORTING i_message TYPE clike. TYPES: BEGIN OF ts_data, key TYPE char10, value TYPE text40, END OF ts_data, tt_data TYPE STANDARD TABLE OF ts_data WITH DEFAULT KEY. PROTECTED SECTION. CLASS-DATA mo_alv TYPE REF TO cl_gui_alv_grid. CLASS-DATA mo_message_container TYPE REF TO cl_gui_container. CLASS-DATA mo_prot TYPE REF TO cl_alv_changed_data_protocol. CLASS-DATA mt_data TYPE tt_data. CLASS-METHODS handle_data_changed FOR EVENT data_changed OF cl_gui_alv_grid IMPORTING er_data_changed sender. ENDCLASS. "lcl_events DEFINITION CLASS lcl_demo IMPLEMENTATION. METHOD go. DATA lt_fcat TYPE lvc_t_fcat. "Make sure alv will be started only once CHECK mo_alv IS INITIAL. "Remember given container for protocol mo_message_container = io_container_prot. "create alv-grid CREATE OBJECT mo_alv EXPORTING i_parent = io_container_grid i_applogparent = io_container_prot i_appl_events = 'X' EXCEPTIONS OTHERS = 5. "Register edit event mo_alv->register_edit_event( cl_gui_alv_grid=>mc_evt_enter ). "enable edit mode mo_alv->set_ready_for_input( 1 ). "set handler for changing data SET HANDLER handle_data_changed FOR mo_alv. "Build simple field catalog APPEND LINES OF VALUE lvc_t_fcat( ( fieldname = 'KEY' tabname = 'XX' reptext = 'Key' datatype = 'CHAR' inttype = 'C' outputlen = 10 edit = abap_true ) ( fieldname = 'VALUE' tabname = 'XX' reptext = 'Value' datatype = 'CHAR' inttype = 'C' outputlen = 40 lowercase = abap_true edit = abap_true ) ) TO lt_fcat. "Make sure user can enter data DO 5 TIMES. APPEND INITIAL LINE TO mt_data. ENDDO. " First Display mo_alv->set_table_for_first_display( CHANGING it_fieldcatalog = lt_fcat it_outtab = mt_data EXCEPTIONS OTHERS = 4 ). "Set focus on grid so user can immediately enter data in grid cl_gui_alv_grid=>set_focus( mo_alv ). ENDMETHOD. METHOD set_message. IF mo_prot IS INITIAL. "create extra protocol mo_prot = NEW #( i_calling_alv = mo_alv i_container = mo_message_container ). ELSE. "clear older protocol entries mo_prot->refresh_protocol( ). ENDIF. "add user message mo_prot->add_protocol_entry( i_msgty = 'E' i_msgno = '000' i_msgid = 'OO' i_msgv1 = i_message i_fieldname = space ). "and display mo_prot->display_protocol( ). ENDMETHOD. METHOD handle_data_changed. DATA: ls_good TYPE lvc_s_modi. IF mo_prot IS BOUND. "clear extra protocol mo_prot->refresh_protocol( ). mo_prot->free( ). CLEAR mo_prot. ENDIF. "Loop at all changed fields LOOP AT er_data_changed->mt_good_cells INTO ls_good. "Read data table READ TABLE mt_data ASSIGNING FIELD-SYMBOL(<data>) INDEX ls_good-row_id. IF sy-subrc = 0. IF ls_good-fieldname = 'KEY' AND ls_good-value CN 'ABC '. "Field does not contain A, B or C - That's bad! er_data_changed->add_protocol_entry( i_msgty = 'E' i_msgno = '000' i_msgid = 'OO' i_msgv1 = 'Must be ABC!' i_fieldname = 'KEY' i_row_id = ls_good-row_id ). ENDIF. "Change field ASSIGN COMPONENT ls_good-fieldname OF STRUCTURE <data> TO FIELD-SYMBOL(<field>). IF sy-subrc = 0. <field> = ls_good-value. ENDIF. ENDIF. ENDLOOP. "Set focus cl_gui_control=>set_focus( mo_alv ). ENDMETHOD. "handle_data_changed ENDCLASS. "lcl_events IMPLEMENTATION PARAMETERS p_test TYPE c LENGTH 10. INITIALIZATION. DATA(docker_prot) = NEW cl_gui_docking_container( ratio = 20 side = cl_gui_docking_container=>dock_at_bottom ). DATA(docker_grid) = NEW cl_gui_docking_container( ratio = 60 side = cl_gui_docking_container=>dock_at_bottom ). lcl_demo=>go( io_container_grid = docker_grid io_container_prot = docker_prot ). AT SELECTION-SCREEN. IF p_test IS NOT INITIAL. "Demo for showing how to use the extra protocol outside alv data handling lcl_demo=>set_message( 'Not allowed to enter something in parameter!!' ). ENDIF.
Very nice!!! I've been adding my messages as a field in the table. Of course the field isn't long enough and contains very little information. This will be a nice addition to my development.
Thanks for your comment, Michelle! The advantage of the implemented protocol technique is that you can link a message to one cell. The affected cell will be framed with a red box and double clicking on the message leads you to the assigned cell.
"Great minds think alike" (or make the same mistakes 🙂 ) - I've been handling it the same way, through a column that shows items that need to be corrected.
Thanks for sharing, Enno Wulff ! Now we just need to rewrite this for 7.3 syntax... 🙂
YOU have to rewrite it to 7.30, Jelena Perfiljeva . I think Michelle Crapo has to rewrite it to 4.6C... 🙂
The main intention was not using the embedded protocol container but to use it as intentionally designed for PLUS using it from other grid functions.
Yes, I do have to rewrite it. My next project that runs into this requirement will be getting a nice message upgrade.
Still LOVE this blog!
That made we wonder:
Instead of using both protocol instances simultaneously,
cannot you use only the protocol of the local/clone of CL_ALV_CHANGED_DATA_PROTOCOL instance?
(You can suppress the display of standard protocol with method CL_GUI_ALV_GRID->ACTIVATE_DISPLAY_PROTOCOL)
This way, you may combine all the messages in the same screen.
Thanks for your comment, Shai!
yes, you might be right! I'll try this ASAP.
But I think that the internal functionality will be lost (double clicking on a message sets the focus on the field where the message belongs to).
Plus: I will have to do all the handling that is now done by cal_gui_alv_grid itself (displaying the protocol if there were errors, clearing the protocol etc).
But I will gibe it a try next time.
Well, it’s worth a try.
A different approach I wanted to share:
(It requires at least 1 pass through the data_changed event from your CL_GUI_ALV_GRID class)
IF gr_protocol IS NOT BOUND.
gr_protocol ?= er_data_changed.