Skip to Content
Author's profile photo Jörg Krause

MVC (model view controller) framework for ABAP: Part 3

See also:  MVC (model view controller) framework for ABAP part 1

MVC (model view controller) framework for ABAP part 2

Controlling multiple screens

Welcome back to my MVC series. To follow this blog, it is necessary that you install the classes provided in the first two parts (see links above). In part 1 you see a demo application that controls a dynpro and an ALV control. In the second part, I wrote about a report-type program that controlled a selection screen and an ALV control, but no standard dynpro fields. This time I would like to focus on standard dynpros, especially the case, when you have more than one of them.

Installing the demo application

Download the attached file and paste the content into a new report type program. Then create the main screen, which contains only one big subdynpro area (see part 1 for further explanations about the dynpro concept of the framework). After that, create the two sub dynpros 0100 and 0200 as described below. Last, I also included a popup screen 0300 which is controlled by the same framework controller type that contols also the sub dynpros.

Main screen 0001

Attributes:

Screen elements:

(create the subscreen area using the screen painter and make it as big as the whole screen)

Flow logic:

process before output.

   module pbo_0001.

   call subscreen subscreen including syrepid gv_subdyn.

*
process after input.
   module pai_0001 at exitcommand.
   call subscreen subscreen.
   module pai_0001.

Sub dynpro 0100

Attributes:

Elements:

Flow logic:

PROCESS BEFORE OUTPUT.
   MODULE pbo_0100.
*
PROCESS AFTER INPUT.
   module pai_0100.


Appearance:

Sub dynpro 0200

Copy dynpro 0100 to 0200. Then set the two input fields to “no input”. Create a frame to for the data fields of the table spfli and put input fields into it. In the end, it should look like this:

Adjust the flow logic:

PROCESS BEFORE OUTPUT.
   MODULE pbo_0200.
*
PROCESS AFTER INPUT.
   module pai_0200.

Popup screen 0300

Create a modal dialog box containing the data fields of table SCARR. It should look like this:

All fields are display only. Important: use GV_OKCODE_0300 instead of GV_OKCODE:

Adjust the flow logic:

PROCESS BEFORE OUTPUT.
   MODULE pbo_0300.
*
PROCESS AFTER INPUT.
   module pai_0300.


GUI status and title


Create status 0100 with function key assignments:


Enter – ENTER

F3 – BACK

Shift+F3: EXIT (exit command)

ESC: CANC (exit command)


Copy the status to 0200 and add the following functions to the toolbar:


POPUP – Text “Carrier”

DELE – Text “Delete”


Create the status 0300 as a dialogbox status and add the function CANC to key ESC.


Last, create a titlebar MAIN with title “Sample for several dynpros”


How it works


As already discussed in part 1, there is only one main screen 0001. To switch between sub screens on the main carrier screen, the method SET_SUBDYNPRO of the framework class for the main controller is used. In the constructor of the main controller, it is set to 0100:


As you see, the method sets also the used GUI STATUS and the titlebar. All parameters are obtional, so you can use this also for setting only some of them. For example, you could call it to change the GUI STATUS only.

Managing sub dynpro flow

Each sub dynpro has its own controller (LCL_CON_DYNPRO_0100, LCL_CON_DYNPRO_0200). In the PBO and PAI modules, the controller is being fetched from the main controller and the control is being passed to the respective methods of the own dynpro controller.

Therefore, you have to redefine method CREATE_CON_DYNPRO:

On ENTER on the first screen 0100, the second screen is being called:

Note that before the new screen is called, the model is used to read the database. The values of the input fields have been passed already to the model in method PAI_FIELD_CHANGE:

Remember that PAI_FIELD_CHANGE will be called automatically from the framework as soon as the user types in something. It will be called for each field separately giving fieldname (IV_FIELDNAME) and value (IV_SOURCE) as a parameter. This method is intended also for input checks on field level.

During PBO of the main screen, then newly set MV_SUBDYNPRO will be passed to the flow logic and the new sub dynpro is being displayed in the SUBSCREEN area.

In PAI for 0200, we jump back to the last screen on user command BACK:

Exit commands must be treated by the main dynpro, so CANC, which also jumps back to sub screen 0100, but without considering inputs made. So the coding is located in the main PAI method:

Managing popups

The popup screen is managed in a similar way. But instead of setting the variable MV_SUBSCREEN, we have to use CALL SCREEN. In the 0200 user command method, the main controller is being called in order to do this:


You can place the call screen statement directly in the PAI_USER_COMMAND, if you want. But I prefer coding everything regarding the user interface flow in the main controller.

One important thing: use a different OKCODE for each popup screen you create. If you don’t the OKCODE set during PAI of the popup will be processed once more in the main screen controller.

As single reaction to the user interface, we leave sthe screen on pressing the cancel button:

Conclusion

In the productive version of the framework in my company’s system, there are some more controllers like one for dynamic documents (CL_DD_DOCUMENT) and one for the class I described in Using ALV to display/edit fields of a structure. Both of them are sub classes of the CFW controller class, just like the ALV controller described in part 1. I shared this basic version to inspire the reader to use it as a starting point for own developments.

All the best!

Jörg

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Hi Jörg,

      I performed your MVC framework introduced in this blog. It's an amazing design pattern! Code looks like perfect! Thanks for your sharing!

      I didn't get your point when implementing the method call_last_screen — how to get the correct last screen number ? All the screen controllers are stored in the private attribute mt_controllers, which is a sorted table according to keys type and dynnr. This means that all the active screen controllers are saved in screen number order. It works fine when current screen 0200 has last screen 0100. But what if on the contrary ? That's to say, the last screen of the current 0100 is 0200. In productive clients, there will probably be more screens and more complicate screen access orders that cannot be described by screen number.

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      I am sorry that you had trouble with that. In the old SCN, the source code was available in attached files which vanished with the migration to the new SCN. Therefore, here is the whole demo report. However, the point of call_last_screen is that you detect the predecessor screen with IF statements.

        method call_last_screen.
          case mv_subscreen.
            when '0200' or '0300'.
              set_subdynpro(
                exporting
                  iv_dynnr      = '0100'    " Current Screen Number
                  iv_gui_status = '0100'    " IV_GUI_STATUS
                  iv_titlebar   = '0100'
              ).
            when '0100'.
              leave program.
          endcase.
      
        endmethod.
      

      Best regards

      Jörg

       

      Complete demo coding:

      *&---------------------------------------------------------------------*
      *& Report  Zmvcfw_DEMO_DYNPROS
      *& Usage of the MVC framework in Zmvcfw for more than one dynpro
      *& refer to Zmvcfw_DEMO_DYNPRO for furhter explanations
      *&---------------------------------------------------------------------*
      * Prerequisites:
      * - create a dynpro 0001 with a subdynpro container that covers the whole
      *   screen
      * - create several sub dynpros and put fields on it
      *
      *---------------------------------------------------------------------*
      
      report  zp_mvcfw_demo_dynpros.
      
      *--------------------------------------------------------------------*
      * Utility class for questioning popup
      *--------------------------------------------------------------------*
      class lcl_dia definition
        final
        create public .
      
        public section.
      
          class-methods ask
            importing
              !iv_question  type clike optional
              !iv_longtext  type dokhl-object optional
              !iv_cancel    type abap_bool optional
                preferred parameter iv_question
            returning
              value(rv_res) type recabool .
        protected section.
      *"* protected components of class lcl_dia
      *"* do not include other source files here!!!
        private section.
      *"* private components of class lcl_dia
      *"* do not include other source files here!!!
      endclass.
      
      
      
      class lcl_dia implementation.
      
      
      * <SIGNATURE>---------------------------------------------------------------------------------------+
      * | Static Public Method lcl_dia=>ASK
      * +-------------------------------------------------------------------------------------------------+
      * | [--->] IV_QUESTION                    TYPE        CLIKE(optional)
      * | [--->] IV_LONGTEXT                    TYPE        DOKHL-OBJECT(optional)
      * | [--->] IV_CANCEL                      TYPE        ABAP_BOOL(optional)
      * | [<-()] RV_RES                         TYPE        RECABOOL
      * +--------------------------------------------------------------------------------------</SIGNATURE>
        method ask.
      *--------------------------------------------------------------------*
      * sends a yes/no question using a popup screeen
      * if iv_question is set, the text will be used directly
      * if no question is given, the text is taken from the last message
      * iv_longtext can pass a longtext object created with SE61
      * if user agrees: rv_res goes to abap_true
      *--------------------------------------------------------------------*
      
          data lv_answ type char1.
          data lv_question type string.
      
          if iv_question is supplied.
            lv_question = iv_question.
          else.
            " take question text from last message
            message id sy-msgid type sy-msgty number sy-msgno
              with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
              into lv_question.
          endif.
      
          call function 'POPUP_TO_CONFIRM'
            exporting
              text_question         = lv_question
              diagnose_object       = iv_longtext
              display_cancel_button = iv_cancel
            importing
              answer                = lv_answ.
      
          if lv_answ = 'A'.
            rv_res = abap_undefined.
          elseif lv_answ = '1'.
            rv_res = abap_true.
          endif.
      
        endmethod.
      
      endclass.
      *----------------------------------------------------------------------*
      *       CLASS lcl_model DEFINITION
      *----------------------------------------------------------------------*
      class lcl_model definition.
      
        public section.
          methods:
            constructor,
            set_carrid
              importing iv_in type spfli-carrid,
            set_connid
              importing iv_in type spfli-connid,
            set_spfli
              importing is_in type spfli,
            get_spfli
              returning value(rs_res) type spfli,
            read_spfli
              raising
                cx_static_check,
            delete_spfli
              raising
                cx_static_check,
            update_spfli
              raising
                cx_static_check,
            get_scarr
              returning value(rs_res) type scarr.
      
        private section.
          data: ms_spfli type spfli,
                ms_scarr type scarr.
      
          methods:
            error
              raising cx_static_check.
      endclass.                    "lcl_model DEFINITION
      " Main controller - always needed
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_main DEFINITION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_main definition inheriting from zcl_mvcfw_con_main.
      
        public section.
          methods:
            " used by PBO of the subdynpro to get the adequate controller
            " this is a singleton, but 'create private' is not possible
            " because of the inheritance mechanism
            " the constructor will be called by zcl_mvcfw_con_main=>get_instance
            " the correct program name is passed automatically
            constructor
              importing
                iv_program type syrepid,
            pai_main
              changing cv_ucomm type syucomm,
            pbo_main redefinition,
            show_popup
              importing iv_dynnr type sydynnr,
            call_last_screen.
      
      
        protected section.
          methods:
            create_con_dynpro redefinition.
      
        private section.
          " the model will be created in constructor
          data: mo_model                  type ref to lcl_model.
      
      
      endclass.                    "lcl_con_main DEFINITION
      
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_dynpro_0100 DEFINITION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_dynpro_0100 definition inheriting from zcl_mvcfw_con_subdyn.
      
        public section.
          methods:
            " add parameer io_model in addition to the strucure name
            constructor
              importing iv_struc_name type c default 'SPFLI'
                        io_model      type ref to lcl_model
                        io_main       type ref to lcl_con_main,
            " used for filling the dynpro fields from model data
            pbo redefinition.
      
        protected section.
          methods:
            " react to single field changes
            pai_field_change redefinition,
            " process the user command
            pai_user_command redefinition.
      
        private section.
          data: mo_model type ref to lcl_model,
                mo_main  type ref to lcl_con_main.
      
      
      endclass.                    "lcl_con_dynpro_0100 DEFINITION
      
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_dynpro_0200 DEFINITION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_dynpro_0200 definition inheriting from zcl_mvcfw_con_subdyn.
      
        public section.
          methods:
            " add parameer io_model in addition to the strucure name
            constructor
              importing iv_struc_name type c default 'SPFLI'
                        io_model      type ref to lcl_model
                        io_main       type ref to lcl_con_main,
            " used for filling the dynpro fields from model data
            pbo redefinition.
      
        protected section.
          methods:
            " no single field change reaction needed, do all in
            " the one-time called pai_before_field_change
            pai_before_field_change redefinition,
            " process the user command
            pai_user_command redefinition.
      
          data: mo_model        type ref to lcl_model,
                mo_main         type ref to lcl_con_main,
                mv_data_changed type abap_bool.
      
        private section.
      
      endclass.                    "lcl_con_dynpro_0100 DEFINITION
      
      class lcl_con_dynpro_0300 definition inheriting from zcl_mvcfw_con_subdyn.
      
        public section.
          methods:
            constructor
              importing iv_struc_name type tabname default 'SCARR'
                        io_model      type ref to lcl_model
                        io_main       type ref to lcl_con_main,
            pbo redefinition.
      
        protected section.
          methods:
            pai_user_command redefinition.
      
        private section.
          data: mo_model type ref to lcl_model,
                mo_main  type ref to lcl_con_main.
      
      endclass.
      
      tables spfli.
      tables scarr.
      
      data: gv_subdyn      type sydynnr,
            gv_okcode      type syucomm,
            gv_okcode_0300 type syucomm,
            go_main        type ref to lcl_con_main,
            go_dyn_sub     type ref to zcl_mvcfw_con_subdyn.
      
      start-of-selection.
        " This report can be run directly.
        call screen 1.
      *&---------------------------------------------------------------------*
      *&      Module  PAI_0001  INPUT
      *&---------------------------------------------------------------------*
      *       text
      *----------------------------------------------------------------------*
      module pai_0001 input.
      
        go_main->pai_main( changing cv_ucomm = gv_okcode ).
      
      endmodule.                 " PAI_0001  INPUT
      
      *----------------------------------------------------------------------*
      *       CLASS lcl_Model IMPLEMENTATION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_model implementation.
      
        method constructor.
        endmethod.                    "constructor
      
        method set_carrid.
          ms_spfli-carrid = iv_in.
        endmethod.                    "set_carrid
      
        method set_connid.
          ms_spfli-connid = iv_in.
        endmethod.                    "set_connid
      
        method set_spfli.
          ms_spfli = is_in.
        endmethod.                    "set_spfli
      
        method get_spfli.
          rs_res = ms_spfli.
        endmethod.                    "get_spfli
      
        method get_scarr.
          rs_res = ms_scarr.
        endmethod.
      
        method read_spfli.
          select single * from scarr
            into ms_scarr
            where carrid = ms_spfli-carrid.
          if sy-subrc <> 0.
            message e008(a&) into sy-msgli.
            error( ).
          endif.
          select single * from spfli
            into ms_spfli
            where carrid = ms_spfli-carrid and
                  connid = ms_spfli-connid.
          if sy-subrc <> 0.
            message e008(a&) into sy-msgli.
            error( ).
          endif.
        endmethod.                    "read_spfli
      
        method delete_spfli.
          delete from spfli
            where carrid = ms_spfli-carrid and
                  connid = ms_spfli-connid.
          if sy-subrc <> 0.
            message e008(a&) into sy-msgli.
            error( ).
          endif.
          commit work.
          clear ms_spfli.
      
        endmethod.                    "delete_spfli
      
        method update_spfli.
          update spfli from ms_spfli.
          if sy-subrc <> 0.
            message e008(a&) into sy-msgli.
            error( ).
          endif.
          commit work.
      
        endmethod.                    "update_spfli
        method error.
          data ls_t100 type scx_t100key.
      
          ls_t100-msgid = sy-msgid.
          ls_t100-msgno = sy-msgno.
          ls_t100-attr1 = 'MV_MESSAGE_V1'.
          ls_t100-attr2 = 'MV_MESSAGE_V2'.
          ls_t100-attr3 = 'MV_MESSAGE_V3'.
          ls_t100-attr4 = 'MV_MESSAGE_V4'.
          raise exception type zcx_mvcfw_error
            exporting
              textid        = ls_t100
              mv_message_v1 = sy-msgv1
              mv_message_v2 = sy-msgv2
              mv_message_v3 = sy-msgv3
              mv_message_v4 = sy-msgv4.
        endmethod.                    "error
      endclass.                    "lcl_Model IMPLEMENTATION
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_main IMPLEMENTATION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_main implementation.
      
        method constructor.
          data lv_repid type sy-repid.
          lv_repid = sy-repid.
      
          super->constructor( lv_repid ).
      
          create object mo_model.
      
          set_subdynpro(
            exporting
               iv_dynnr      = '0100'    " IV_DYNNR
               iv_gui_status = '0100'     " IV_GUI_STATUS
               iv_titlebar   = 'MAIN'
          ).
      
        endmethod.                    "constructor
      
        method call_last_screen.
          case mv_subscreen.
            when '0200' or '0300'.
              set_subdynpro(
                exporting
                  iv_dynnr      = '0100'    " Current Screen Number
                  iv_gui_status = '0100'    " IV_GUI_STATUS
                  iv_titlebar   = '0100'
              ).
            when '0100'.
              leave program.
          endcase.
      
        endmethod.
      
        method show_popup.
          " call the popup
          call screen iv_dynnr starting at 3 3.
        endmethod.
      
        method pai_main.
      
          case cv_ucomm.
            when 'EXIT'.
              leave program.
            when 'CANC'.
              call_last_screen( ).
          endcase.
      
        endmethod.
      
        method pbo_main.
          data:
            lt_exclude_ucomm              type ui_functions.
      
          rv_res = mv_subscreen.
      
          data(ls_spfli) = mo_model->get_spfli( ).
          if ls_spfli-carrid is initial.
            append 'POPUP' to lt_exclude_ucomm.
          endif.
      
          set pf-status mv_gui_status of program mv_program excluding lt_exclude_ucomm.
          set titlebar mv_titlebar of program mv_program with mv_titlebar_text.
      
        endmethod.
      
        method create_con_dynpro.
          " return the singleton object for the subdynpro controller according to
          " the actual subscreen number
          " a new controller is needed
          case sy-dynnr.
            when '0100'.
              create object ro_res
                type lcl_con_dynpro_0100
                exporting
                  io_model = mo_model
                  io_main  = me.
            when '0200'.
              create object ro_res
                type lcl_con_dynpro_0200
                exporting
                  io_model = mo_model
                  io_main  = me.
            when '0300'.
              create object ro_res
                type lcl_con_dynpro_0300
                exporting
                  io_model = mo_model
                  io_main  = me.
          endcase.
      
        endmethod.                    "get_con_dynpro
      
      endclass.                    "lcl_con_main IMPLEMENTATION
      
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_dynpro_0100 IMPLEMENTATION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_dynpro_0100 implementation.
      
        method constructor.
          super->constructor( iv_struc_name ).
          mo_model = io_model.
          mo_main = io_main.
        endmethod.                    "constructor
      
        method pbo.
          " get the dynpro data from the model
          e_data = mo_model->get_spfli( ).
      
        endmethod.                    "pbo
      
        method pai_user_command.
          try.
              case iv_ucomm.
                when 'BACK' or 'CANC' or 'EXIT'.
                  leave program.
                when 'ENTER'.
                  " make the model read the requested data and
                  " pass to the following subdynpro as a standard reaction
                  mo_model->read_spfli( ).
                  lcl_con_main=>get_instance( )->set_subdynpro(
                      iv_dynnr      = '0200'
                      iv_gui_status = '0200'
                      " title bar is not changed
      *                iv_titlebar   =
                  ).
              endcase.
            catch zcx_mvcfw_error into data(lo_err).
              message lo_err type 'I'.
      
          endtry.
      
        endmethod.                    "pai_user_command
      
        method pai_field_change.
          " will be called for every changed field on the dynpro
          " pass the input field by field
          " as an alternative, you could pass everything in one step using
          " pai_before_field_change
      
          " a reaction to a group of fields is not yet implemented. You can
          " still use chain - endchain in the dynpro logic an d implement
          " own methods for that.
          case iv_fieldname.
            when 'CARRID'.
              mo_model->set_carrid( iv_source ).
            when 'CONNID'.
              mo_model->set_connid( iv_source ).
          endcase.
        endmethod.                    "pai_field_change
      endclass.                    "lcl_con_dynpro_0100 IMPLEMENTATION
      *----------------------------------------------------------------------*
      *       CLASS lcl_con_dynpro_0200 IMPLEMENTATION
      *----------------------------------------------------------------------*
      *
      *----------------------------------------------------------------------*
      class lcl_con_dynpro_0200 implementation.
      
        method constructor.
          super->constructor( iv_struc_name ).
          mo_model = io_model.
          mo_main = io_main.
        endmethod.                    "constructor
      
        method pai_before_field_change.
          if mo_model->get_spfli( ) <> c_data.
            " update everything
            mo_model->set_spfli( c_data ).
            " memorize that data has been changed
            mv_data_changed = abap_true.
          else.
            mv_data_changed = abap_false.
          endif.
        endmethod.                    "pai_field_change
      
        method pbo.
          " get the dynpro data from the method
          e_data = mo_model->get_spfli( ).
      
        endmethod.                    "pbo
      
        method pai_user_command.
          try.
              case iv_ucomm.
                when 'POPUP'.
                  mo_main->show_popup( '0300' ).
                when 'BACK'.
                  mo_main->call_last_screen( ).
                when 'DELE'.
                  " perform a deletion
                  if lcl_dia=>ask( 'Delete record?' ) = abap_true.
                    mo_model->delete_spfli( ).
                  endif.
                when others.
                  if mv_data_changed = abap_true.
                    " Update the record, if data has changed.
                    if lcl_dia=>ask( 'Update record?' ) = abap_true.
                      mo_model->update_spfli( ).
                    else.
                      mo_model->read_spfli( ).
                    endif.
                  endif.
              endcase.
            catch zcx_mvcfw_error into data(lo_err).
              message lo_err type 'I'.
      
          endtry.
      
        endmethod.                    "pai_user_command
      endclass.                    "lcl_con_dynpro_0100 IMPLEMENTATION
      
      class lcl_con_dynpro_0300 implementation.
        method constructor.
          super->constructor( iv_struc_name ).
          mo_model = io_model.
          mo_main = io_main.
        endmethod.                    "constructor
      
        method pbo.
          set pf-status '0300'.
          set titlebar '0200'.
          e_data = mo_model->get_scarr( ).
        endmethod.
      
        method pai_user_command.
          case iv_ucomm.
            when 'CANC'.
              set screen 0.
          endcase.
        endmethod.
      endclass.
      *&---------------------------------------------------------------------*
      *&      Module  PBO_0001  OUTPUT
      *&---------------------------------------------------------------------*
      *       text
      *----------------------------------------------------------------------*
      module pbo_0001 output.
      
        go_main ?= lcl_con_main=>get_instance( ).
        gv_subdyn = go_main->pbo_main( ).
      
      endmodule.                 " PBO_0001  OUTPUT
      module pbo_0100 output.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pbo( importing e_data = spfli ).
      
      endmodule.                 " PBO_0100  OUTPUT
      module pai_0100 input.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pai( exporting iv_ucomm = gv_okcode
                         changing c_data = spfli ).
      
      endmodule.                 " PAI_0100  INPUT
      *&---------------------------------------------------------------------*
      *&      Module  PBO_0100  OUTPUT
      *&---------------------------------------------------------------------*
      *       text
      *----------------------------------------------------------------------*
      module pbo_0200 output.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pbo( importing e_data = spfli ).
      
      endmodule.                 " PBO_0100  OUTPUT
      *&---------------------------------------------------------------------*
      *&      Module  PAI_0100  INPUT
      *&---------------------------------------------------------------------*
      *       text
      *----------------------------------------------------------------------*
      module pai_0200 input.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pai( exporting iv_ucomm = gv_okcode
                         changing c_data = spfli ).
      
      endmodule.                 " PAI_0100  INPUT
      
      module pbo_0300 output.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pbo( importing e_data = scarr ).
      
      endmodule.
      
      module pai_0300 input.
      
        go_dyn_sub ?= go_main->get_con_dynpro( ).
        go_dyn_sub->pai( exporting iv_ucomm = gv_okcode_0300
                         changing c_data = scarr ).
      
      endmodule.                 " PAI_0100  INPUT
      Author's profile photo Patrick Sojka
      Patrick Sojka

      Hallo Jörg,

       

      thank you for sharing this framework! Im using it right now and it works great. I have seome questions to make sure, that im using this framwork right.

      I start with a selection screen. In the run_program( ) i create my model and jump to Dynpro 0100 (call screen 1, then set_subdynpro).

      My dynpro has two container. One at the top for a cl_gui_toolbar. Then follow some Dynpro elements and a subscreen(to use select-options) and after this a big ALV.

      So while creating Dynpro 0100 controller (and fetching the two container), i would also build the ALV controller, a controller for the cl_gui_toolbarand one for the subscreen(containing the select-options)?

      For the cl_alv_toolbar i would create a class deriving from the generic CFW controller?

      Could you share your toolbar container controller class (if there is one)?

       

      For the subscreen containing the select-options i would also create a controller?

      Right now, i just call the subscreen in the pai and pbo of Dynpro 0100. This triggers the "AT SELECTION-SCREEN" event, calling  go_sel_screen->pai( ) from my Main 1000 selscreen-controller, which transports all select-options using methode  get_selections  from the Main controller.

      Even if i splitt the two controllers and using two "AT SELECTION-SCREEN ON BLOCK XY" , in the end, both would call get_selections( )  from the Main controller transporting ALL selection-screen fields?

      Thank you again for this fun to user mvc framework!

       

      Beste regards

      Patrick

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Hi Patrick,

      I'm glad this is useful for you. Yes, meanwhile I created also a toolbar controller. It's very simple - you can find the code at the end.

      To create containers for controllers I normally create a designated method in con_main. This could look like this:

      method show_toolbar.
        if mo_cont_toolbar is not bound.
          " create the container for this controller
          mo_cont_toolbar = new cl_gui_custom_container( ... ).
        endif.
        " this will also create the controller, if needed:
        get_con( cv_con_toolbar )->refresh( ).
      endmethod.
            

      I then call the method from the PBO of the screen controller for 0100. In your case, you should have two calls in PBO:

      " in class lcl_con_dynpro_0100
      method pbo.
      (...)
        mo_main->show_toolbar( ).
        mo_main->show_alv( ).
      endmethod.

      get_selections( ) is called from PAI of the selection screen. In the implementation, you could distinguish two selection structures using sy-dynnr like this:

      If you work with more than one sel screen, I would keep all parameters in the ms_selscreen structure and just pass them all from the screen fields. get_selections is being called from PAI of every selection screen controller.

      Here's the coding of the toolbar class:

      class zcl_mvcfw_con_toolbar definition
        public
        inheriting from ZCL_MVCFW_CON_CFW
        create public .
      
      public section.
      
      *"* public components of class zcl_mvcfw_con_toolbar
      *"* do not include other source files here!!!
        methods CONSTRUCTOR
          importing
            !IO_CONTAINER type ref to CL_GUI_CONTAINER .
      
        methods REFRESH
          redefinition .
      protected section.
      
        data MO_TOOLBAR type ref to CL_GUI_TOOLBAR .
      
      *"* protected components of class zcl_mvcfw_con_toolbar
      *"* do not include other source files here!!!
        methods CREATE_TOOLBAR_ELEMENTS .
        methods ON_FUNCTION_SELECTED
          for event FUNCTION_SELECTED of CL_GUI_TOOLBAR
          importing
            !FCODE .
      private section.
      ENDCLASS.
      
      
      
      CLASS ZCL_MVCFW_CON_TOOLBAR IMPLEMENTATION.
      
      
      * <SIGNATURE>---------------------------------------------------------------------------------------+
      * | Instance Public Method ZCL_MVCFW_CON_TOOLBAR->CONSTRUCTOR
      * +-------------------------------------------------------------------------------------------------+
      * | [--->] IO_CONTAINER                   TYPE REF TO CL_GUI_CONTAINER
      * +--------------------------------------------------------------------------------------</SIGNATURE>
      method CONSTRUCTOR.
      
      
          data: lt_events                 type cntl_simple_events,
                ls_event                  type cntl_simple_event.
      
          super->constructor( io_container ).
      
          create object mo_toolbar
            exporting
              parent = mo_container.     " Container name
      *                with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      
          ls_event-eventid = cl_gui_toolbar=>m_id_function_selected.
          ls_event-appl_event = abap_true.
      
          append ls_event to lt_events.
      
          mo_toolbar->set_registered_events(
            exporting
              events                    = lt_events    " Event Table
          ).
          set handler on_function_selected for mo_toolbar.
      
      
      endmethod.
      
      
      * <SIGNATURE>---------------------------------------------------------------------------------------+
      * | Instance Protected Method ZCL_MVCFW_CON_TOOLBAR->CREATE_TOOLBAR_ELEMENTS
      * +-------------------------------------------------------------------------------------------------+
      * +--------------------------------------------------------------------------------------</SIGNATURE>
      method CREATE_TOOLBAR_ELEMENTS.
      * must be redefined!
      
      *    mo_toolbar->add_button(
      *      exporting
      *        fcode            = 'EXEC'    " Function Code Associated with Button
      *        icon             = icon_execute_object    " Icon Name Defined Like "@0a@"
      **        is_disabled      =     " Button Status
      *        butn_type        = 0    " Button Types Defined in CNTB
      **        text             =     " Text Shown to the Right of the Image
      **        quickinfo        =     " Purpose of Button Text
      **        is_checked       =     " Button Selected
      *    ).
      endmethod.
      
      
      * <SIGNATURE>---------------------------------------------------------------------------------------+
      * | Instance Protected Method ZCL_MVCFW_CON_TOOLBAR->ON_FUNCTION_SELECTED
      * +-------------------------------------------------------------------------------------------------+
      * | [--->] FCODE                          LIKE
      * +--------------------------------------------------------------------------------------</SIGNATURE>
      method ON_FUNCTION_SELECTED.
      
      * must be redefined
      *    try.
      *        case fcode.
      **          when 'EXEC'.
      **            mo_model->read_db( ).
      **            mo_main->show_out_main( ).
      *        endcase.
      *      catch cx_static_check.
      **        mo_main->showlog( ).
      *    endtry.
      
      endmethod.
      
      
      * <SIGNATURE>---------------------------------------------------------------------------------------+
      * | Instance Public Method ZCL_MVCFW_CON_TOOLBAR->REFRESH
      * +-------------------------------------------------------------------------------------------------+
      * | [!CX!] ZCX_MVCFW_ERROR
      * +--------------------------------------------------------------------------------------</SIGNATURE>
        method REFRESH.
          " redefine calling:
      *    create_toolbar_elements( ).
        endmethod.
      ENDCLASS.
      Author's profile photo Ingolf Stemmle
      Ingolf Stemmle

      Hello Jörg,

      meanwhile I'm developing my first application based on your Framework. I have already defined a first Version for a Controller for cl_salv_tree.

      Now I need to use a table control at a certain screen. Do you have any example how to do that? In this case we do not only need to return a structure at PBO but a whole table, which should then be populated using

      LOOP AT table INTO ddic-structure WITH CONTROL tctrl_table .....

      Maybe I should define an own ZCL_CON_TCTR class? Any existing approach would be appreciated.

      And by the way: What was the intention of the GROUP in mt_contollers? I only could find it as parameter in the REFRESH method. But here it is not really used if supplied.

           if iv_group is supplied.
      check ls_con-type iv_type.
      endif.

      Kind regards

      Ingolf

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Hello Ingolf,

      I'm sorry - since I never use table controls, but always use ALV grids instead, I have no example for that. In fact,  SAP declares step loop processing as obsolete.

      With the group component, you can create groups of controllers that can be refreshed together.

      However, I abandoned this concept somehow. I prefer to do explicit refreshs from the main controller. If you want to use the group column, you have to modify the mt_controllers table after having created all controllers, for example like this:

      " in your sub class of zcl_mvcfw_con_main:
      mt_controllers[ cv_cont_alv_1 ]-group = 'GR1'.
      mt_controllers[ cv_cont_alv_2 ]-group = 'GR1'.
      mt_controllers[ cv_cont_tree_1 ]-group = 'GR2'.
      mt_controllers[ cv_cont_tree_2 ]-group = 'GR2'.
      
      (...) 
      refresh( iv_group = 'GR1' ). " refreshes con_alv_1 and con_alv_2