SAP BUS-Screen 框架介绍及消息处理

213 阅读6分钟

在 Web Dynpro 和酷炫的 UI5 库的世界里,我仍然发现自己在使用 ABAP Reports 和经典的 Dynpros。作为一名技术人员,很容易想象为什么我不认为这类任务很有趣。然而,我希望通过探索现代(和更好)的方法来改善开发,让它变得有趣。

所以。。。这是有关如何使用 BUS-Screen 框架实施 ABAP 报告的简短指南。尽管这最初仅供内部 SAP 使用,但并未公开提供。

误解: 如果你在网上搜索如何使用 BUS-Screen Framework,除了没有得到很多结果外,你还会得到一些错误的信息。BUS-Screen Framework 无法动态创建 Dynpro。它只是一种以面向对象的方式构建报表的方法。

仍然感兴趣?...可以!

首先,我必须推荐 Thorsten Franz 和 Tobias Trapp 合著的《Anwendungsentwicklung mit ABAP Objects》一书,该书包含一整章专门介绍 BUS-Screen 框架。

与此框架相关的所有 SAP 对象都可以在包 BUS_TOOLS 中找到。这个包还包含一个小的 demo 事务码,但我觉得有点不清楚。

image.png

以下步骤描述了使用 BUS-Screen Framework 实现屏幕的基础:

  • 创建具有普通屏幕的新报表。(我已将我的屏幕命名为“2000”)。

在 Screen 2000 的 Flow Logic 中,分别调用 modules dynpro_pbo 进行输出,dynpro_pai 进行输入。

PROCESS BEFORE OUTPUT.
     MODULE dynpro_pbo.
PROCESS AFTER INPUT.
     MODULE dynpro_pai.
  • 直接在报表中或在 include 中声明这两个模块。
MODULE dynpro_pbo OUTPUT.
     cl_bus_abstract_screen=>dynpro_pbo(
          iv_dynpro_number    = sy-dynnr
          iv_program_name     = sy-repid ).
ENDMODULE.
MODULE dynpro_pai INPUT.
     cl_bus_abstract_screen=>dynpro_pai(
          iv_dynpro_number    = sy-dynnr
          iv_program_name     = sy-repid ).
ENDMODULE.

注意:这些模块只能声明一次,然后为每个屏幕(普通屏幕和子屏幕)重复使用。

  • 使用此框架实例化 screens 的条件之一是在程序中声明一个 form 例程。
FORM bus_screen_create USING value(im_program_name) TYPE bus_screen-program_name
                                                       value(im_dynpro_number) TYPE bus_screen-dynpro_number
                                           CHANGING ch_screen ##called.
     CASE im_dynpro_number.
          WHEN '2000'.
" This is the name of the wrapper class for Screen 2000
" If the called iv_dynpro_number is not found and the ch_screen is not instantiated,
" a dummy screen of type CL_BUS_ABSTRACT_SCREEN will be return.
               CREATE OBJECT ch_screen TYPE lcl_demo_main_screen
                    EXPORTING
                         iv_program_name  = im_program_name
                         iv_dynpro_number = im_dynpro_number.
     ENDCASE.
ENDFORM.
  • 现在,是时候使用本地 ABAP 类以面向对象的方式定义界面逻辑了。

注意:尽管类 CL_BUS_ABSTRACT_SCREEN 是基础,但实现正确的继承很重要。在这种情况下,必须使用 CL_BUS_ABSTRACT_MAIN_SCREEN,因为 2000 是普通屏幕。(对于子屏幕,应使用相应的CL_BUS_ABSTRACT_SUB_SCREEN):

CLASS lcl_demo_main_screen DEFINITION
             INHERITING FROM cl_bus_abstract_main_screen
             FINAL.
     PUBLIC SECTION.
" The constructor method can be used for initializations.
          METHODS constructor
               IMPORTING
                    value(iv_program_name) TYPE bus_screen-program_name
                    value(iv_dynpro_number) TYPE bus_screen-dynpro_number.
     PROTECTED SECTION.
" The following two methods must be redefined as ABAP Objects
" does not support the CALL SCREEN statement.
          METHODS call_screen REDEFINITION.
          METHODS call_screen_starting_at REDEFINITION.
     PRIVATE SECTION.
" This method is the central handling for all PAI events in this screen.
          METHODS handle_pai FOR EVENT process_after_input OF cl_bus_abstract_main_screen
                                                IMPORTING iv_function_code.
ENDCLASS.
CLASS lcl_demo_main_screen IMPLEMENTATION.
     METHOD constructor.
          super->constructor(
               EXPORTING
                    iv_program_name = iv_program_name
                    iv_dynpro_number = iv_dynpro_number ).
" The GUI Title of the application is set using the following method
          set_title( 'DEMO BUS-Screen Framework' ).
          SET HANDLER handle_pai FOR me.
     ENDMETHOD.
     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.
     METHOD handle_pai.
          CASE iv_function_code.
" For the default GUI Status, global constants can be used to evaluate the function code.
" However, if you set your own GUI Status using method set_status( ),
" then you will have to evaluate your specific function codes.
               WHEN gc_function_code_back
                    OR gc_function_code_exit.
                         leave( ).
               WHEN gc_function_code_cancel.
                         LEAVE PROGRAM.
          ENDCASE.
     ENDMETHOD.
ENDCLASS.
  • 现在我们的屏幕已经定义好了,我们可以调用它。为此,应在 START-OF-SELECTION 事件中使用以下代码:
DATA lo_main_screen TYPE REF TO lcl_demo_main_screen.
" This statement will try to call the BUS_SCREEN_CREATE form routine that we previously defined.
cl_bus_abstract_screen=>get_screen(
     EXPORTING iv_program_name = sy-repid
                           iv_dynpro_number = '2000'
     IMPORTING ev_screen = lo_main_screen ).
IF lo_main_screen IS BOUND.
     lo_main_screen->show( ).
ENDIF.

这几乎总结了基本功能。在下一篇博客中,我将向您展示如何使用 tabstrip 包装器(CL_BUS_TABSTRIPCL_BUS_TABSTRIP_TAB 类)以及如何处理消息(CL_BUS_MESSAGE)。

BUS-Screen 框架消息处理

继续介绍 BUS-Screen 框架,我将介绍消息处理的基础。

为了演示如何使用 BUS-Screen Framework 处理消息,我们将在之前定义的主屏幕 (2000) 中创建一个虚拟输入字段。我们将检查此字段的值并抛出一条消息。

image.png

请注意,输入字段名称对应于 demo 类中的 public static 属性。因此,我们还必须在其中定义它:

CLASS lcl_demo_main_screen DEFINITION
           INHERITING FROM cl_bus_abstract_main_screen
             FINAL.
     PUBLIC SECTION.
" Screen fields are static as all screens are instantiated as Singletons
          CLASS-DATA cv_field TYPE char10.
* ...

注意:除了控件声明(在 OO 上下文中不受支持)之外,所有字段绑定都可以而且应该在其相应的包装类中定义为公共静态属性。

为了检查属性值并抛出消息,我们将在之前定义的 HANDLE_PAI 方法中添加一个额外的 WHEN 部分。

METHOD handle_pai.
* ...
     DATA lo_message TYPE REF TO cl_bus_message.
     DATA lv_message TYPE string ##needed.
* ...
     CASE iv_function_code.
          WHEN gc_function_code_enter.
               IF cv_field NE 'ABC123'.
                    MESSAGE e100(sap_guidelines) INTO lv_message.
" Using a dummy statement MESSAGE ... INTO ... can be helpful
" when accessing the 'Where-Used' functionality in a message class.
*
                    CREATE OBJECT lo_message
                         EXPORTING
*                              is_message = ...
" Optional: MOVE-CORRESPONDING sy TO ls_message, where ls_message is of type symsg.
                              iv_cursor_screen    = me
                              iv_highlighted_field = 'CL_DEMO_MAIN_SCREEN=>CV_FIELD'.
" Passing the SY-MSG structure to the BUS message instance is optional.
"If empty, syst fields will be automatically used. Alternatively, the ls_message structure can be filled manually.
* ...


这个想法是让 PAI 处理继续进行,即使有故障,并且只在 PBO 部分显示消息。

对于实际的消息显示,我们将在本地演示类的 public 部分中重新定义方法 PAI_END

image.png

METHODS pbo_end REDEFINITION.

注意:尽管 CL_BUS_ABSTRACT_SCREEN 基类中提供了与消息相关的函数,但消息的实际显示只应在当前 CL_BUS_ABSTRACT_MAIN_SCREEN 实例中执行 (一次)。

METHOD pbo_end.
     super->pbo_end( ).
*
" Attribute gt_messages is filled automatically by passing reference 'me'
" as parameter iv_cursor_screen in the CONSTRUCTOR of CL_BUS_MESSAGE.
     cl_bus_message=>get_most_severe_message(
          EXPORTING it_message = gt_messages
          IMPORTING ev_message = gv_message ).
*
     IF gv_message IS BOUND.
          MESSAGE ID gv_message->gs_message-msgid TYPE 'S'
                             NUMBER gv_message->gs_message-msgno
                             TYPE gv_message->gs_message-msgv1
                                        gv_message->gs_message-msgv2
                                        gv_message->gs_message-msgv3
                                        gv_message->gs_message-msgv4
                             DISPLAY LIKE gv_message->gs_message-msgty.
"
*
" After displaying the message, the buffer should be refreshed.
          clear_messages( ).
     ENDIF.
ENDMETHOD.


注意:了解 PBO 和 PAI 方法的调用顺序以及如何在类中重新定义它们非常重要。

在下一篇博文中,我将向您展示如何使用 BUS-Screen 框架实现 tabstrip 和 sub screens 的逻辑。