‎2008 Feb 11 8:27 PM
I am trying to implement ALV_GRID_XT to enhance the ALV Grid toolbar by adding couple of custom funcitons.As per the SAP documentation I have created 2 methods ON_TOOLBAR - one for adding items on toolbar and the other ON_USER_COMMAND - for processing if the user clicks on the custom function.
I am unable to relate the ALV Grid instance onto the method ON_USER_COMMAND . One of the funciton is to calculate the average of column which represents percentage excluding cells with zero values. Once the user selects column(s) and clicks on the Percent button on the ALV grid my piece of code should trigger ON_USER_COMMAND and then display the average percentage on the Totals row.
Method ON_USER_COMMAND has only one parameter E_UCOMM which i am able to get in the method but I am not able to get the instance of the ALV grid so that I can play around with the data in the Grid.
I could have used a local class and then enhanced the ALV toolbar but this requirement is needed for about 20 reports and hence thought of implementing it globally.
Any ideas would be appreciated.
‎2008 Feb 12 10:25 AM
Hi there
You need to set the handlers of course and register the events
I'm using also a Z-class using functionality from the ALV Grid
in the application program
......
data: z_object type ref to zcl_* "my z_class"
* Instantiate your Z class
create object z_object
exporting
z_object = z_object
cfname = 'CCONTAINER1'.
Now in your Constructor for your Z-Class have a variable
say grid1 which is defined as a type ref to cl_gui_alv_grid.
Code the methods you require --. The trick is to instantiate your OWN Z class and within YOUR CLASS instantiate the GRID1 variable as an instance of cl_gui_alv_grid - then you can use methods etc from that class in your own Z class simple via something like this
method CONSTRUCTOR.
create object grid_container1
exporting
container_name = cfname.
create object grid1
exporting
i_parent = grid_container1.
set handler z_object->on_user_command for grid1.
set handler z_object->on_toolbar for grid1.
set handler z_object->handle_data_changed for grid1.
set handler z_object->handle_data_changed_finished for grid1.
set handler z_object->on_dubbelklik for grid1.
set handler z_object->on_hotspot for grid1.
call method grid1->register_edit_event
exporting
i_event_id = cl_gui_alv_grid=>mc_evt_enter.
endmethod.
method display_grid .
get reference of g_outtab into g_outtab1.
get reference of g_fldcat into g_fldcat1.
struct_grid_lset-edit = i_edit. "To enable editing in ALV
struct_grid_lset-zebra = i_zebra.
struct_grid_lset-cwidth_opt = i_opt.
struct_grid_lset-grid_title = i_gridtitle.
* struct_grid_lset-ctab_fname = 'T_CELLCOLORS'.
* struct_grid_lset-stylefname = 'CELLTAB'.
struct_grid_lset-ctab_fname = style_fname.
struct_grid_lset-stylefname = style_ctab.
* export gt_outtab from gt_outtab to memory id 'gt_outtab'.
call method grid1->set_table_for_first_display
exporting
is_layout = struct_grid_lset
changing
it_outtab = gt_outtab
it_fieldcatalog = it_fldcat.
endmethod.
* ...
You need to add the buttons to the toolbar with your ON TOOLBAR method
for example
method on_toolbar .
type-pools icon.
clear ls_toolbar.
move 0 to ls_toolbar-butn_type.
move 'EXCEL' to ls_toolbar-function.
move space to ls_toolbar-disabled.
move icon_xxl to ls_toolbar-icon.
move 'Excel' to ls_toolbar-quickinfo.
move 'EXCEL' to ls_toolbar-text.
append ls_toolbar to e_object->mt_toolbar.
perform toolbar in program (caller) if found
using e_object.
* ...
endmethod.
Now the functionality should work in the ON_USER_COMMAND .
Here's my ON_USER_COMMAND method within the class
method ON_USER_COMMAND.
* FOR EVENT before_user_command OF cl_gui_alv_grid
* IMPORTING
* e_ucomm
* sender
* When defined in SE24 you don't need to code the
* FOR EVENT etc as this is already defined as an event handler
case e_ucomm.
when 'EXIT'.
leave program.
when 'EXCEL'.
call method me->download_to_excel.
when 'SAVE'.
when 'PROC'.
call method me->process.
when 'REFR'.
call method me->refresh.
when 'SWITCH'.
call method me->switch.
when 'TEST'.
call method me->get_cell.
endcase.
endmethod.
Add whatever extra functionality you want to the on_user_command so long as you've defined toolbars..
Cheers
jimbo
‎2008 Feb 12 10:25 AM
Hi there
You need to set the handlers of course and register the events
I'm using also a Z-class using functionality from the ALV Grid
in the application program
......
data: z_object type ref to zcl_* "my z_class"
* Instantiate your Z class
create object z_object
exporting
z_object = z_object
cfname = 'CCONTAINER1'.
Now in your Constructor for your Z-Class have a variable
say grid1 which is defined as a type ref to cl_gui_alv_grid.
Code the methods you require --. The trick is to instantiate your OWN Z class and within YOUR CLASS instantiate the GRID1 variable as an instance of cl_gui_alv_grid - then you can use methods etc from that class in your own Z class simple via something like this
method CONSTRUCTOR.
create object grid_container1
exporting
container_name = cfname.
create object grid1
exporting
i_parent = grid_container1.
set handler z_object->on_user_command for grid1.
set handler z_object->on_toolbar for grid1.
set handler z_object->handle_data_changed for grid1.
set handler z_object->handle_data_changed_finished for grid1.
set handler z_object->on_dubbelklik for grid1.
set handler z_object->on_hotspot for grid1.
call method grid1->register_edit_event
exporting
i_event_id = cl_gui_alv_grid=>mc_evt_enter.
endmethod.
method display_grid .
get reference of g_outtab into g_outtab1.
get reference of g_fldcat into g_fldcat1.
struct_grid_lset-edit = i_edit. "To enable editing in ALV
struct_grid_lset-zebra = i_zebra.
struct_grid_lset-cwidth_opt = i_opt.
struct_grid_lset-grid_title = i_gridtitle.
* struct_grid_lset-ctab_fname = 'T_CELLCOLORS'.
* struct_grid_lset-stylefname = 'CELLTAB'.
struct_grid_lset-ctab_fname = style_fname.
struct_grid_lset-stylefname = style_ctab.
* export gt_outtab from gt_outtab to memory id 'gt_outtab'.
call method grid1->set_table_for_first_display
exporting
is_layout = struct_grid_lset
changing
it_outtab = gt_outtab
it_fieldcatalog = it_fldcat.
endmethod.
* ...
You need to add the buttons to the toolbar with your ON TOOLBAR method
for example
method on_toolbar .
type-pools icon.
clear ls_toolbar.
move 0 to ls_toolbar-butn_type.
move 'EXCEL' to ls_toolbar-function.
move space to ls_toolbar-disabled.
move icon_xxl to ls_toolbar-icon.
move 'Excel' to ls_toolbar-quickinfo.
move 'EXCEL' to ls_toolbar-text.
append ls_toolbar to e_object->mt_toolbar.
perform toolbar in program (caller) if found
using e_object.
* ...
endmethod.
Now the functionality should work in the ON_USER_COMMAND .
Here's my ON_USER_COMMAND method within the class
method ON_USER_COMMAND.
* FOR EVENT before_user_command OF cl_gui_alv_grid
* IMPORTING
* e_ucomm
* sender
* When defined in SE24 you don't need to code the
* FOR EVENT etc as this is already defined as an event handler
case e_ucomm.
when 'EXIT'.
leave program.
when 'EXCEL'.
call method me->download_to_excel.
when 'SAVE'.
when 'PROC'.
call method me->process.
when 'REFR'.
call method me->refresh.
when 'SWITCH'.
call method me->switch.
when 'TEST'.
call method me->get_cell.
endcase.
endmethod.
Add whatever extra functionality you want to the on_user_command so long as you've defined toolbars..
Cheers
jimbo
‎2008 Feb 12 3:08 PM
James,
That was a perfect solution. Thanks man.
However, I do have a second question.
I hope you are aware that OO ALV Grid does have a function for displaying Average on column but the issue is with rows having initial values (0.0).
For example if the ALV grid has displayed 5 rows then the
Average = sum of the values of 5 rows / # of rows.
COl1
-
121.0
122.0
123.0
124.0
125.0
Average = 615.0 / 5 = 123.0
If any one the row has an initial value then calculated Average is incorrect. Standard functionality does not ignore the rows with initial values.
COl1
-
121.0
122.0
0.0
124.0
125.0
Correct Average = 492.0 / 4 = 123 but SAP calculates as 492.0 / 5 = 98.4 which is incorrect.
I wanted to build a custom function which can calculate averages ignoring rows with initial values. Second functionality we need is to display percentage for 2 selected columns.
In my ON_TOOLBAR method I have added a Toolbar item 'AVRG' and I need to handle this on the ON_USER_COMMAND and display the average on the totals row similar to how SAP does.
‎2008 Feb 13 1:33 PM
I can't answer that specific question but just set a break point and see what the SAP code does.
Also in your ON_USER_COMMAND you can intercept the standard SAP Fcodes before sap gets them. Look also at BEFORE_USER_COMMAND and AFTER_USER_COMMAND in cl_gui_alv_grid for overriding the standard SAP toolbar functionality with your own methods.
I'd probably copy most of the standard SAP method -- just do it the way SAP does but ignore initial / blank lines.
The error probably arises in the sense of say you have 5 lines
0, 2,6,4,2
What you need to do is sum the total of course and then divide by 4 rather than 5.
I'm sure a bit of judicious debugging in ON_TOOLBAR and ON_USER_COMMAND methods will give you a decent answer.
Of course it's easier still if you can get the answer after SAP has done it - all you need to know is the number of lines with zero values and subtract that from the total number of lines.
Say sap has calculated the average as 30 and the re are 6 lines in the table.
Say also you know there is one blank line
so get the total again (30 * 6) = 180.
Now divide by the number of NON blank lines (5) and you new average is now 36.
Since YOU have the initial table - it's one of the parameters passed to the GRID before you display it, you can easily get the number of blank / initial value lines.
Note however mathematically that if a line is GENUINELY zero that does need to be taken into account when calculating a true average. After all say you go to the pub 5 nights a week and want your average daily beer consumption per week -- you still need to divide the amount of beer drunk by the number of days in a week (7) rather than the 5 nights you were actually out drinking. I'd really check your application again to make sure that the average YOU want is the correct one.
cheers
jimbo
‎2008 Feb 13 2:55 PM
> After all say you go to the pub 5 nights a week and want your average daily beer consumption per week -- you still need to divide the amount of beer drunk by the number of days in a week (7) rather than the 5 nights you were actually out drinking.
This actually depends on who's inquiring for this figure:
a) doctor
b) better half
c) drinking pals
Other than that, your OO solutions are spot on, I'm learning a lot by reading your posts.
Prost
Thomas
‎2008 Feb 13 2:32 PM
Thanks again. I too am thinking to copy the whole of SAP code and then remove those lines with initial values(Not easy as said).
I will keep this thread open for sometime and check if I get a better solution and then close.
‎2008 Feb 13 2:49 PM
You don't even need to remove the initial lines -- when calculating your average (with your modified copied SAP code) just divide by the number of NON-ZERO lines. As I said previously you have the GRID table so it's easy to check for number of initial / blank / zero values.
The other way you could do it of course is after you've built your table delete the zero / blank lines BEFORE displaying --then you've really got the easy way of doing this as you can then use 100% SAP functionality.
If the user really needs to see the blank lines then after'he clicks the SAP average button intercept the SAP function code, delete the lines from your table and exit out of your method. The standard SAP code is then executed but it will be using your modified table.
The trick of course is to use a dynamic table in a field symbol.
Say in your code you have something like this for your initial grid display
call method grid1->set_table_for_first_display
exporting is_layout = struct_grid_lset
changing
it_outtab = <dyn_table>
it_fieldcatalog = it_fldcat.
in your method (on_user_command) which captures the FCODE for the average facility you can do the following
method on_user_command .
* FOR EVENT before_user_command OF cl_gui_alv_grid
* IMPORTING
* e_ucomm
* sender
case e_ucomm.
when 'EXIT'.
leave program.
when 'EXCEL'.
call method me->download_to_excel.
when 'SAVE'.
when 'PROC'.
call method me->process.
when 'REFR'.
call method me->refresh.
when 'SWITCH'.
call method me->switch.
when 'TEST'.
call method me->get_cell.
endcase.
endmethod.
say your function is PROC
in method PROCESS code something like this
method process .
perform process in program (caller) if found.
endmethod.
in the application program
form process.
loop at <dyn_table> into wa_elements.
* delete lines where necessary
endloop
* refresh grid if necessary
endform.
...
you'll need to pass the calling program to the method in your class somewhere as a parameter.
Note DON'T PASS SY-REPID. Put sy-repid into a variable First otherwise you'll get the wrong main program name passed.
Believe me I spent some time debugging that particular problem.
Cheers
jimbo