Skip to Content

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

In this Demoprogram I show how to create a Textedit-Control inside a docking-Container.

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.

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.

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:

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.

abapGit

The GUI-Designer guidrasil is available via abapGit on Github:

https://github.com/tricktresor/guidrasil

 

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Ged Hurst

    I really like these blogs Enno – the GUI Controls Framework has always been underappreciated, in my opinion, and these demo programs are fun to play around with.

    Thanks for taking the time to share them with the rest of us.

    Cheers

    Ged

     

    (2) 
  2. Jelena Perfiljeva

    Another great blog, thanks for sharing!

    If these are Windows-based controls, I wonder how would such program look if some users have Macs? It has been a constant challenge for us, unfortunately, to cater to both platforms.

    (1) 

Leave a Reply