Since I develop ABAP in SAP I must deal with the old, dusty dynpros. Unfortunately, many customers are still not willing to more forward any use Web Dynpro, UI5 or any of the other modern technologies out there. In our current project, we were lucky enough and could start from scratch so I introduced the BUS Screen Framework. The first time I came in touch with this little framework was read the book
Anwendungsentwicklung mit ABAP Objects from
Thorsten Franz and
Tobias Trapp.
The BUS Screen Framework is a way to develop dynpros using an object orientated approach. But don’t crow too soon: we still must use the dynpro painter and a handful modules and form routines. But it helps you to create an object orientated report and to handle the dynpro logic and events.
Let’s dive a little bit deeper into the framework. It provides you the abstract basic classes for main screens and sub screens. These classes provide some functions like the object orientated access to tab strips, to set the title and status, raising messages or to handle events triggered from the GUI.
The Dynpro Class
Since the BUS screen classes are defined as abstract you must create your own (local) class and inherit from the proper super class. In my example, I have one Dnypro 9000 which acts as my only main screen. This example should give you a first impression of the framework.
Due to the inheritance, we must redefine the abstract methods
CALL_SCREEN and
CALL_SCREEN_STARTING_AT.
CLASS lcl_scr9000 DEFINITION INHERITING FROM cl_bus_abstract_main_screen.
PROTECTED SECTION.
METHODS call_screen REDEFINITION.
METHODS call_screen_starting_at REDEFINITION.
ENDCLASS.
The implementation of the class very light. All we must do is to call the Dynpro.
CLASS lcl_scr9000 IMPLEMENTATION.
METHOD call_screen.
CALL SCREEN iv_dynpro_number.
ENDMETHOD.
METHOD call_screen_starting_at.
CALL SCREEN iv_dynpro_number STARTING AT iv_xstart iv_ystart
ENDING AT iv_xend iv_yend.
ENDMETHOD.
ENDCLASS.
Now we have our Dynpro class. It does not contain any logic but it is enough to create and show our Dynpro. But wait – we have a Dynpro class but no Dynpro yet.
The Dynpro is created like any other Dynpro you have created in your life before. I have created a normal screen that contains a label only.
The Dynpro does not yet communicate with the framework (and therefore with our screen class). First, we must program some code in the Dynpro flow logic. Due to the coding restrictions, we must create two modules (one for PBO and one for PAI).
PROCESS BEFORE OUTPUT.
MODULE dynpro_pbo.
PROCESS BEFORE OUTPUT.
MODULE dynpro_pbo.
The only thing these modules do is to handover the PBO and PAI handling for the actual Dynpro to the framework. The methods
DYNPRO_PBO and
DYNPRO_PAI retrieve an instance from your screen class from the internal buffer and invokes the corresponding methods on this screen object instance.
MODULE dynpro_pbo OUTPUT.
cl_bus_abstract_screen=>dynpro_pbo(
EXPORTING
iv_program_name = sy-repid
iv_dynpro_number = sy-dynnr
).
ENDMODULE.
MODULE dynpro_pai INPUT.
cl_bus_abstract_screen=>dynpro_pai(
EXPORTING
iv_program_name = sy-repid
iv_dynpro_number = sy-dynnr
).
ENDMODULE.
Both methods, DYNPRO_PBO and DYNPRO_PAI, are internally divided into two methods: PBO_BEGIN/PBO_END and PAI_BEGIN/PAI_END. In a later tutorial, you will see what is the reason for that.
Let’s review the current implementation status:
- We have created a screen class for our main screen 9000. It inherits from the class CL_BUS_ABSTRACT_MAIN_SCREEN.
- We have created the normal Dynpro 9000 and added some flow logic to handover the PBO/PAI handling to the framework.
The Handler Class
As we have learned the flow logic hands over the event handling to our screen class. Well to be accurate the method
PAI_END in super class
CL_BUS_ABSTRACT_MAIN_SCREEN raises the event
PROCESS_AFTER_INPUT with the function code as parameter. For the ones who wonder where we have defined the function codes: this example does not have a custom status. If we don't set a status the BUS framework uses the default status
BUS_MAIN_SCREEN in function group
BUS_LOCATOR. A later example will show how to use a customer own status.
Let’s return to the handler class. The screen class raises an event and we must have a handler that listens to it. There is nothing wrong with adding the handler method into the screen class but I prefer a dedicated class. The local class LCL_HANDLER implements a method that listens to the mentioned event. The only action we perform is to leave the screen.
CLASS lcl_handler DEFINITION.
PUBLIC SECTION.
METHODS handle_pai
FOR EVENT
process_after_input OF cl_bus_abstract_main_screen
IMPORTING
iv_function_code.
ENDCLASS.
CLASS lcl_handler IMPLEMENTATION.
METHOD handle_pai.
CASE iv_function_code.
* when using the default status we can use the constants to
* check the PAI events
WHEN cl_bus_abstract_main_screen=>gc_function_code_back OR
cl_bus_abstract_main_screen=>gc_function_code_cancel.
* static variable contains a reference to the actual main screen
cl_bus_abstract_main_screen=>gv_current_main_screen->leave( ).
ENDCASE.
ENDMETHOD.
ENDCLASS.
When we invoke the method
LEAVE on the actual main screen, the screen instance will be removed from the internal stack. If there was another main screen before the program would return to the previous screen, otherwise the program ends.
Let’s view the current implementation status:
- We have created a screen class and Dynpro for our main screen 9000.
- We have created the handler class LCL_HANDLER that listens to the event PROCESS_AFTER_INPUT which is raised by the framework.
- Since we don't set a custom status the BUS screen framework uses its default status.
Call the Screen and register the Handler
Next we must program some logic to call our screen, register the handler and display the screen. Personally, I like to put my Dynpros into function groups. Therefore, I have created a simple function module that calls our screen.
FUNCTION z_ft_start_example_1.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"----------------------------------------------------------------------
* object reference to main screen
DATA lo_scr9000 TYPE REF TO lcl_scr9000.
DATA lo_handler TYPE REF TO lcl_handler.
* create instance of screen 9000
cl_bus_abstract_main_screen=>get_screen(
EXPORTING
iv_program_name = cl_abap_syst=>get_current_program( )
iv_dynpro_number = |9000|
IMPORTING
ev_screen = lo_scr9000
).
* create and register handler
lo_handler = NEW #( ).
SET HANDLER lo_handler->handle_pai FOR lo_scr9000.
* display dynpro
lo_scr9000->show( ).
ENDFUNCTION.
The method
GET_SCREEN from class
CL_BUS_ABSTRACT_MAIN_SCREEN creates an instance of our screen class
LCL_SCR9000 and adds it to the internal call stack. But how does the framework know that it should create an instance of class
LCL_SCR9000 for our Dynpro 9000? For this purpose, we need the only form routine. Method
GET_SCREEN performs the form routine
BUS_SCREEN_CREATE in the actual program. We must implement this form routine and return an instance of the screen class.
FORM bus_screen_create
USING
iv_program_name TYPE bus_screen-program_name
iv_dynpro_number TYPE bus_screen-dynpro_number
CHANGING
ev_screen TYPE any.
CASE iv_dynpro_number.
WHEN '9000'.
ev_screen = NEW lcl_scr9000(
iv_program_name = iv_program_name
iv_dynpro_number = iv_dynpro_number
).
ENDCASE.
ENDFORM.
Let’s review the current implementation status:
- We have created a screen class and Dynpro for our main screen 9000.
- We have created the handler class that listens to the PAI event.
- We have implemented the form routine BUS_SCREEN_CREATE which creates an instance of our local class.
- We have programmed the logic to call the screen and register the handler.