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 sy–repid gv_subdyn.
*
process after input.
module pai_0001 at exit–command.
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
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.
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.
Best regards
Jörg
Complete demo coding:
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
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:
I then call the method from the PBO of the screen controller for 0100. In your case, you should have two calls in PBO:
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:
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
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: