Getting the currently selected line in a TableCtrl (before PAI) dynamically
I have two table controls on my dynpro screen and in one of them there is a field which, upon F4, should check the value of another field and then execute one of two possible search helps, depending on the value of the other field.
In addition, another field in that table control contains a pushbutton, that displays information regarding the row in which the button was clicked.
In both cases I need to have to selected line as a local structure that I can work with. The first case happens in event Process on Value-Request (‘
POV‘) and the second one happens in event Process After Input (‘
I decided to create a fully generic code which returns the selected line in any given TableCtrl object, at any given screen event. I checked class
CL_TABLECONTROL but couldn’t find any similar method to do so.
Unfortunately, it seems that it cannot be implemented as an external Method (in my Utilities class) because the table control object (of type
CXTAB_CONTROL) does not exist in this OO context. So the logic must be implemented as a Form in the dynpro program.
I would be glad to hear your thoughts on this!
Edit: it occurred to me that this logic actually needs to return different results according to the event during which it is called. In POV it should return the currently selected visible line, but in PAI it should return the absolute index of the row in the local table, and thus should always be executed only AFTER the end of the wizard-generated table-control loop.
I will try to find the time to update this logic to match all possible table-control events and cases. In the meantime please use it either in POV event or at the end of PAI event.
Here is the logic:
*&---------------------------------------------------------------------* *& Form tc_get_curr_line *&---------------------------------------------------------------------* * This FORM returns the currently selected line in a desired TableCtrl. * It uses system field SY_STEPL and CXTAB_CONTROL, and so cannot be used * in Object Oriented programming (e.g. inside a class method). *----------------------------------------------------------------------* * -->IS_STRUCTURE Structure of identical type as a TableCtrl's line * -->IV_STRUCTURE_NAME Name of the given structure, as defined on the Dynpro Screen / in DEF include / etc. * -->IV_TC_NAME Name of the desired TableCtrl, as defined on the Dynpro Screen / in DEF include / etc. * -->IV_SCREEN_EVENT Used to determine if it's Process on Value-Request ('POV') or Process After Input ('PAI') (two different ways to get selected line's index) * <--ES_CURR_TC_LINE The selected line, returned in the matching type for the given structure * <--EV_SELECTED_INDEX Absolute index of the selected line in the global table it is linked to *----------------------------------------------------------------------* FORM tc_get_curr_line USING is_structure TYPE any iv_structure_name TYPE dynfnam iv_tc_name TYPE dynfnam iv_screen_event TYPE char10 CHANGING es_curr_tc_line TYPE any ev_selected_index TYPE i. DATA: lv_selected_index TYPE sy-stepl, lo_struct_descr TYPE REF TO cl_abap_structdescr, lv_struct_dynp_name TYPE dynfnam, ls_tc_column TYPE LINE OF cxtab_control-cols, lt_dynpfields TYPE dynpread_tabtype, ls_dynpfield LIKE LINE OF lt_dynpfields, ls_selected_line TYPE REF TO data. FIELD-SYMBOLS: <ls_tc_attr> TYPE cxtab_control, <ls_selected_line> TYPE ANY, <lv_line_field_value> TYPE ANY, <ls_tc_line_to_return> TYPE ANY. " Getting the relevant TableControl structure with all attributes of the TableCtrl itself: ASSIGN (iv_tc_name) TO <ls_tc_attr>. IF sy-subrc <> 0. " Replace this statement with an appropriate statement using a message from a message-class: MESSAGE 'Error getting the desired TableControl screen component' TYPE 'E'. ELSE. " Getting the index of the currently selected line in the TableCtrl: CASE iv_screen_event. " When pressing F4, using this function to get the selected line's index: WHEN 'POV'. CALL FUNCTION 'DYNP_GET_STEPL' IMPORTING povstepl = ev_selected_index EXCEPTIONS stepl_not_found = 1 OTHERS = 2. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF. " When any process after input, using this keyword to get the selected line's index: WHEN OTHERS. " 'PAI' GET CURSOR LINE ev_selected_index. " This calculation returns the absolute index of the selected line even after scrolling the TableCtrl: ev_selected_index = <ls_tc_attr>-top_line + ev_selected_index - 1. ENDCASE. ls_dynpfield-stepl = ev_selected_index. " Building a DynproFields table with the names of all the displayed fields in " the given TableCtrl (only the ones that are OUTPUT - which means without any pushbuttons if exist) LOOP AT <ls_tc_attr>-cols INTO ls_tc_column WHERE screen-output EQ 1. ls_dynpfield-fieldname = ls_tc_column-screen-name. APPEND ls_dynpfield TO lt_dynpfields. ENDLOOP. " Getting the DDIC description (fields' names and attributes) of the " given structure that I need to return eventually: lo_struct_descr ?= cl_abap_typedescr=>describe_by_data( is_structure ). " Reading the values from the currently selected line in the given TableCtrl: CALL FUNCTION 'DYNP_VALUES_READ' EXPORTING dyname = sy-cprog dynumb = sy-dynnr * translate_to_upper = ' ' * request = ' ' * perform_conversion_exits = ' ' * perform_input_conversion = ' ' * determine_loop_index = ' ' start_search_in_current_screen = abap_true * start_search_in_main_screen = ' ' * start_search_in_stacked_screen = ' ' * start_search_on_scr_stackpos = ' ' * search_own_subscreens_first = ' ' * searchpath_of_subscreen_areas = ' ' TABLES dynpfields = lt_dynpfields EXCEPTIONS invalid_abapworkarea = 1 invalid_dynprofield = 2 invalid_dynproname = 3 invalid_dynpronummer = 4 invalid_request = 5 no_fielddescription = 6 invalid_parameter = 7 undefind_error = 8 double_conversion = 9 stepl_not_found = 10 OTHERS = 11 . IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ELSEIF lt_dynpfields IS NOT INITIAL. " Creating the structure to return: CREATE DATA: ls_selected_line TYPE HANDLE lo_struct_descr. ASSIGN ls_selected_line->* TO <ls_selected_line>. " Going over the Dynpro screen fields to copy their values LOOP AT lt_dynpfields INTO ls_dynpfield. " The Dynpro Fields table contains the names of the Screen fields: The DynproStructure's name and " a hyphen '-' (e.g. 'GS_TC_DATA-MY_LOVELY_FIELD'). I need to compare between the names of the Dynpro FIELDS themselves " and the names of the DDIC fields in the DDIC structure. So I need only the fields' names without the structure's name. " I'm building a string with the Dynpro Structure's name and a hyphen: CONCATENATE iv_structure_name '-' INTO lv_struct_dynp_name. " Now I'm removing the name of the Dynpro Structure (and the '-') from FIELDNAME in the DynpFields table: REPLACE lv_struct_dynp_name IN ls_dynpfield-fieldname WITH space IGNORING CASE. " Now I have the name of the actual FIELD in the dynpro structure, which should correspond to " the name of the matching field in the DDIC structure that I need to return. " So finally I can dynamically assign that field in the returned DDIC structure and copy " the value from the Dynpro field to it: ASSIGN COMPONENT ls_dynpfield-fieldname OF STRUCTURE <ls_selected_line> TO <lv_line_field_value>. IF sy-subrc EQ 0 AND <lv_line_field_value> IS ASSIGNED. " Copying the value from the Dynpro Screen field to the returned DDIC structure's field: <lv_line_field_value> = ls_dynpfield-fieldvalue. ENDIF. ENDLOOP. ASSIGN es_curr_tc_line TO <ls_tc_line_to_return>. " Copying the complete local structure to the returned Dynpro Screen structure: IF sy-subrc EQ 0 AND <ls_tc_line_to_return> IS ASSIGNED. <ls_tc_line_to_return> = <ls_selected_line>. ENDIF. ENDIF. ENDIF. ENDFORM. "tc_get_curr_line
Regarding oo - can you give more details of the issue? I assume you tried an importing parameter like
Why did that not work?
Did you try passing a reference to the table control instead?
Well what do you know, you were right! I've just tried to send the actual TableCtrl structure to my method and it worked.
But I found another problem: I'm starting to think that I should split my code into two separate processes - one for PAI event and one for POV (Process on Value-Request) event. It occurred to me that the index of the currently selected line is handled differently in each event - in fact, it is expected to contain completely different values for completely different uses.
In POV, the main reason to get the row's contents is to use one field's value in another one's search help. During POV, the local table behind the TC doesn't yet reflect the recent changes in the screen, and can even be empty. Thus, I need to simulate the PAI event using all those 'DYNP_*' functions. Function 'DYNP_GET_STEPL' returns the VISIBLE selected line in the table-control (If I scrolled 4 lines down and selected the second line that is visible, which is actually line #6 in the whole table, the function will return '2'). Function 'DYNP_VALUES_READ' reads the selected row's values from the screen and not from the local table itself. This is why it needs the relative index and not the absolute one. Otherwise it won't work correctly.
In PAI, function 'DYNP_GET_STEPL' simply doesn't work (it returns 0), so I need to use statement GET CURSOR LINE. Like the function, this statement returns the VISIBLE selected line in the table-control. In PAI I can place my code either before, after, or during the automatically-generated TC code. However, only after the table-control loop is finished can I depend on the local table. This form that I posted just cannot know the exact time or event during which it is being executed, and thus can't safely depend on the local table for its operation. So, any custom code that uses the TC's data (including this FORM of course) should always be placed AFTER the TC code in PAI.
I hope I can find the time to re-write my code in order to try and handle ALL possible cases. Meanwhile I'll update the post with a warning until I can fully complete it.