Wednesday 24 October 2018

How to deal with SAPGUI controls in a generic way

A few days ago I wrote a blog post about one of my favorite learning projects that taught me a lot about GUI programming: guidrasil

Today I would like to explain some of the techniques that I used in this project.

What Are Controls?


Controls are ActiveX-components (also called OCX-components), which will be used in the SAPGUI. These Windows-components are controlled by the appropriate classes in SAP. The creation of a control mostly follows the following schema:

1. CREATE OBJECT <object reference>
2. <object reference>-SET_….
3. some  Controls need an extra DISPLAY call.

Typical GUI-Controls are:

◈ CL_GUI_ALV_GRID
◈ CL_GUI_TEXTEDIT
◈ CL_GUI_PICTURE
◈ CL_GUI_CALENDAR
◈ CL_GUI_HTML_VIEWER
◈ CL_GUI_SIMPLE_TREE
◈ CL_GUI_COLUMN_TREE
◈ CL_GUI_LIST_TREE

Controls need a Container, where they are placed in. All Container controls inherit from CL_GUI_CONTAINER:

◈ CL_GUI_DOCKING_CONTAINER
◈ CL_GUI_CUSTOM_CONTAINER
◈ CL_GUI_DIALOGBOX_CONTAINER

Splitter controls are a special kind of control because they again offer child containers:

◈ CL_GUI_SPLITTER_CONTAINER
◈ CL_GUI_EASY_SPLITTER_CONTAINER

Programming of Controls


A typical programming looks like this:

◈ Create a Container
◈ Create a Control inside this Container
◈ Set attributes of the Control

Attributes

Each control has different attributes and each control needs a different setup. A textedit control needs to be told that it is read-only and a picture control needs to know the picture to display. That’s the problem: If you know a control very well than you know all necessary attributes. If not, you will have to read documentation, search examples or browse the controls methods.

Sometimes the setting of flags differs. Sometimes you need a boolean flag (X or space) and sometimes the methods needs to have an integer (1 or 0).

The idea of a GUI-Designers


Because all controls are inherited from CL_GUI_CONTROL, it is possible to pass any control to a method or get it back. Or a control can be created and stored in a reference table.

The following report does exactly this: You can set the attributes on the selection screen and create a control by pressing <enter>. All created control will be stored in an internal table.

SAP ABAP Certification, SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Study Materials

Demo report


REPORT zguidrsail_demo_generic_ctrl.

SELECTION-SCREEN BEGIN OF BLOCK ctrl WITH FRAME TITLE TEXT-ctl.
PARAMETERS p_text RADIOBUTTON GROUP ctrl DEFAULT 'X'.
PARAMETERS p_icon RADIOBUTTON GROUP ctrl.
SELECTION-SCREEN END OF BLOCK ctrl.

SELECTION-SCREEN BEGIN OF BLOCK side WITH FRAME TITLE TEXT-sid.
PARAMETERS p_left RADIOBUTTON GROUP side DEFAULT 'X'.
PARAMETERS p_rigt RADIOBUTTON GROUP side.
PARAMETERS p_botm RADIOBUTTON GROUP side.
SELECTION-SCREEN END OF BLOCK side.

CLASS ctrl_demo DEFINITION.
  PUBLIC SECTION.
    METHODS add_text
      IMPORTING
        side TYPE i.
    METHODS add_icon
      IMPORTING
        side TYPE i.
  PROTECTED SECTION.
    TYPES: BEGIN OF ts_object,
             container TYPE REF TO cl_gui_container,
             control   TYPE REF TO cl_gui_control,
           END OF ts_object.

    DATA objects TYPE STANDARD TABLE OF ts_object.
    METHODS append_control
      IMPORTING
        container TYPE REF TO cl_gui_container
        control   TYPE REF TO cl_gui_control.

ENDCLASS.

CLASS ctrl_demo IMPLEMENTATION.
  METHOD add_text.
    DATA(parent) = NEW cl_gui_docking_container( side = side ratio = 20 ).
    DATA(textedit) = NEW cl_gui_textedit( parent = parent ).
    textedit->set_text_as_stream( VALUE texttab( ( tdline = `This is a demonstration` ) ) ).
    append_control( container = parent control = textedit ).
  ENDMETHOD.
  METHOD add_icon.
    DATA(parent) = NEW cl_gui_docking_container( side = side ratio = 20 ).
    DATA(icon) = NEW cl_gui_picture( parent = parent ).
    icon->load_picture_from_sap_icons( icon_message_question ).
    icon->set_display_mode( cl_gui_picture=>display_mode_fit_center ).
    append_control( container = parent control = icon ).
  ENDMETHOD.
  METHOD append_control.
    APPEND VALUE #( container = container control = control ) TO objects.
  ENDMETHOD.
ENDCLASS.

INITIALIZATION.
  DATA(demo) = NEW ctrl_demo( ).

AT SELECTION-SCREEN.

  CASE 'X'.
    WHEN p_left.
      DATA(side) = cl_gui_docking_container=>dock_at_left.
    WHEN p_rigt.
      side = cl_gui_docking_container=>dock_at_right.
    WHEN p_botm.
      side = cl_gui_docking_container=>dock_at_bottom.
  ENDCASE.

  CASE 'X'.
    WHEN p_text.
      demo->add_text( side = side ).
    WHEN p_icon.
      demo->add_icon( side = side ).
  ENDCASE.

Dynamic administration


Once I have all created containers and controls in a table, I can read the type and attributes. For example I could loop all created controls and ask if the container is of type CL_GUI_DOCKING_CONTAINER. If yes, I cast to a local object reference and read the attributes side and ratio:

IF itab-container IS INSTANCE OF cl_gui_docking_container.
  DATA dock TYPE REF TO cl_gui_docking_container.
  dock ?= itab-container.
  DATA(side) = dock->get_docking_side( ).
  dock->get_ratio( ratio = DATA(ratio) ).
ENDIF.

In this way I could gather all important attributes of any control.

Dynamic creation

With the help of RTTI (Run Time Type Information) and class CL_ABAP_TYPEDESCR I am able to get the classname of the object:

DATA(clsnam) = cl_abap_typedescr=>describe_by_object_ref( itab-container )->get_relative_name( ).

Once I have the name I can create the object dynamically:

DATA: container TYPE REF TO cl_gui_container, 
      exc_ref TYPE REF TO cx_root.

DATA: ptab TYPE abap_parmbind_tab.

ptab = VALUE #( 
                ( name  = 'SIDE' 
                  kind  = cl_abap_objectdescr=>exporting 
                  value = REF #( side ) ) 
                ( name  = 'RATIO' 
                  kind  = cl_abap_objectdescr=>exporting 
                  value = REF #( ratio ) ) ).

TRY. 
    CREATE OBJECT container TYPE (clsnam) 
      PARAMETER-TABLE ptab. 
  CATCH cx_sy_create_object_error INTO exc_ref. 
    MESSAGE exc_ref->get_text( ) TYPE 'I'. 
ENDTRY.

A dynamic creation is not necessary because it is easier to roll this part out to a creator class.

guidrasil


The GUI-Designer guidrasil works somehow like described above. One important thing is that guidrasil offers a much more convenient way for creating containers and controls: Once you chose the side of the container, the GUI-Designer will split the container and place a toolbar at top and show the empty container at the bottom. In the toolbar you are able to select on of the provided containers and controls.

SAP ABAP Certification, SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Study Materials

The Gui-Designer know which controls have been created and where. The creator class does the creation of the control and saves the attributes.

Additionally the creator class offers a dialog or visual settings of the attributes:

SAP ABAP Certification, SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Study Materials

I’d rather write code that writes code than write code


One further quality of guidrasil is, that each creator class exactly knows how the control must be created. that means that it can offer the source code for creating this control.

The GUI-Designer exactly knows about the controls and simply can ask each control creator for the creation code. The GUI-Designer than “simply” will have to put the parts together to have a generated report for re-creating all the controls.

No comments:

Post a Comment