Skip to Content
Author's profile photo Nad Cuky

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 (‘PAI‘).

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'.

    " 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'.
            povstepl        = ev_selected_index
            stepl_not_found = 1
            OTHERS          = 2.

        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid
              TYPE sy-msgty
              NUMBER sy-msgno
              WITH sy-msgv1


        " 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.


    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[].


    " 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:
        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   = ' '
        dynpfields                      = lt_dynpfields[]
        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

    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.



      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>.




ENDFORM.                    "tc_get_curr_line



Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Matthew Billingham
      Matthew Billingham

      Regarding oo - can you give more details of the issue? I assume you tried an importing parameter like

      i_tc        type cxtab_control

      Why did that not work?

      Did you try passing a reference to the table control instead?

      Author's profile photo Nad Cuky
      Nad Cuky
      Blog Post Author

      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.