列表屏幕(List Screen)
列表屏幕(List Screen)
生成列表屏幕
在START-OF-SELECTION事件处理块中,用WRITE语句向列表缓冲区(List Buffer)输出要显示的内容,当该事件结束的时候,所有在列表缓冲区中的内容将被显示到一个基本列表屏幕(Basic List)上。
当用户在基础列表屏幕上双击一行或按功能键“F2”时,将会触发ABAP事件AT LINE-SELECTION,如果还想进一步显示该行数据的详细信息,则可以继续使用WRITE语句输出要显示的内容,这次生成另外一个详细列表屏幕(Details List Screen)。此详细列表屏幕将覆盖其上一层的基础列表屏幕,若在其界面的工具条上点“返回”或按功能键F3,将返回到基础列表屏幕。
在详细列表屏幕上,当用户双击一行数据或按功能键F2,AT LINE-SELECTION事件将会再次触发,因此还可以继续生成下一级的详细列表屏幕。因此,除了基础列表屏幕外,所有的详细列表屏幕的生成都是在AT LINE-SELECTION 事件块中一起处理的,因此应该有一个机制区别开是哪一个详细列表屏幕将被生成。
ABAP提供了全屏变量sy-lsind来区别屏幕的层次,sy-lsind的值主要用于在 AT LINE-SELECTION事件处理块中进行程序流程控。0表示为基础列表屏幕,1表示第一级详细列表屏幕,依次类推,最多可以有20个详细列表。如果是返回上一次,则sy-lsind会自动减一:
如果要在dialog program (module pool)生成屏幕列表,则可以通过LEAVE TO LIST-PROCESSING向屏幕发送列表。
此部分主要是通过WRITE语句来输出简单的列表,具体参考《ABAP BC Programming.docx》的Write语句用法
复杂LIST
标准LIST
report demo_list_standard.
tables spfli.
skip.
uline at /(62).
select * from spfli where connid ge 0017
and connid le 0400.
write: / sy-vline, spfli-connid, sy-vline,
(15) spfli-cityfrom, 26 sy-vline,
31 spfli-cityto, 51 sy-vline, 62 sy-vline,
/ sy-vline, 8 sy-vline,
spfli-deptime under spfli-cityfrom, 26 sy-vline,
spfli-arrtime under spfli-cityto, 51 sy-vline,
spfli-fltime, 62 sy-vline.
uline at /(62).
endselect.
write: /10 'SAP *** SAP *** SAP *** SAP *** SAP *** SAP',
/19(43) 'Flight Information System',
/19(43) 'International Connections'.
standard page header
至少包括二行standard header,第一行standard header包括了listheader和pagenumber,第二行standardheader是一条水平线。当程序是可执行报表程序时,listheader存储在SY-TITLE中。如果有必要,可以给standardheader添加最多四行的column headers与一条水平线。在水平或垂直滚动时standardheader是不会动的。
自定义LIST
自定义Page Header
TOP-OF-PAGE.
WRITE: ....
在TOP-OF-PAGE事件里输出的为自定义Page Header,会在每页正文本输出前输出
另外,如果屏蔽掉standard header,使用下面语句压制:
REPORT <rep> NO STANDARD PAGE HEADING.
对于每个TOP-OF-PAGE事件,对于basic list设置的标准属性标题与列表头中的占位符 &0 - &9 ,会自动使用系统变量SY-TVAR0到SY-TVAR9来替换他们,所以在TOP-OF-PAGE事件里对SY-TVAR0到SY-TVAR9进行设置即可
REPORT demo_list_page_heading NO STANDARD PAGE HEADING.
TOP-OF-PAGE.
WRITE: sy-title, 40 'Page', sy-pagno. ULINE.
WRITE: / 'SAP AG', 29 'Walldorf, ',sy-datum,
/ 'Neurottstr. 16', / '69190 Walldorf/Baden'.
ULINE.
START-OF-SELECTION.
DO 5 TIMES.
WRITE / sy-index.
ENDDO.
LINE-SIZE
输出宽度由REPORT的LINE-SIZE来决定:
REPORT <rep> LINE-SIZE <width>.
如果<width>设置为0,则采用标准LIST的宽度来输出。缺省的列表的行宽为83个字符
一行最多只能显示255个字符,但大多数的打印机只能打印132个字符。当前页面宽度存储在SY-LINSZ系统变量中
REPORT demo_list_line_size LINE-SIZE 40.
WRITE: 'SY-LINSZ:', sy-linsz.
ULINE.
DO 20 TIMES.
WRITE sy-index.
ENDDO.
LINE-COUNT
限制每页多少行
REPORT <rep> LINE-COUNT <length>[(<n>)].
如果指定了<n>参数,系统会保留<n>行给page footer。如果<length>设置为0,系统会使用标准页面长度。缺省每页的行数为6万行。
页面的实质可输出正本行数为<length>减去page header length,再减去<n>。每面的行数包含了列表头(headings)、列表内容与列表脚注行(footer lines)。
系统变量SY-LINCT会存储页面行数<length>,注意:如果REPORT语句没有设置该值,则该系统变量值为0,除非使用 NEW-PAGE语句进行了设置。
SY-PAGNO系统变量存储了当前页码。
"注意:一定要使用(1)预留一行,否则页脚不会打印出来
REPORT demo_list_line_count NO STANDARD PAGE HEADING LINE-SIZE 40 LINE-COUNT 5(1).
WRITE: 'SY-LINCT:', sy-linct.
SKIP.
DO 6 TIMES.
WRITE / sy-index.
ENDDO.
TOP-OF-PAGE.
WRITE: / 'Page title', ` ` &
`Pag NO.:`,sy-pagno NO-GAP LEFT-JUSTIFIED.
ULINE.
END-OF-PAGE.
WRITE: / '*****Page footer*****'.
Page Footer
END-OF-PAGE.
WRITE: ....
触发的条件是数据要满一页时才触发,否则不会被触发:
REPORT demo_list_end_of_page LINE-SIZE 40 LINE-COUNT 6(2)
NO STANDARD PAGE HEADING.
TOP-OF-PAGE.
WRITE: 'Page with Header and Footer'.
ULINE AT /(27).
END-OF-PAGE.
ULINE.
WRITE: /30 'Page', sy-pagno.
START-OF-SELECTION.
DO 5 TIMES.
WRITE / sy-index.
ENDDO.
强制分页
无条件分页使用NEW-PAGE
如果是想保留多少空行后再分页,使用RESERVE
NEW-PAGE
该语句结束当前页,所有的输出会在新的页面里显示。
该语句不会触发END-OF-PAGE事件(是否触发则要看执行该语句前数据是否已刚好满一页):
REPORT demo_list_new_page LINE-SIZE 40 LINE-COUNT 7(1).
TOP-OF-PAGE.
WRITE: 'TOP-OF-PAGE', sy-pagno.
ULINE AT /(17).
END-OF-PAGE.
WRITE: '**page footer**'.
START-OF-SELECTION.
DO 2 TIMES.
WRITE / 'Loop:'.
DO 3 TIMES.
WRITE / sy-index.
ENDDO.
NEW-PAGE.
ENDDO.
RESERVE
RESERVE <n> LINES.
在调用此语句时,如果最后一次输出到Page Footer之间所剩行小于(注意:等于时不会分页)<n>时会进行分页,在开始新页之前会触发END-OF-PAGE事件(如果最后一页数据不足一页,还是不会显示页脚)。注意:该语句仅只影响后面紧挨着的输出语句,如果不满足分页条件,则后面的输出也不会再受该语句的影响。
另外,RESERVE可以定义后继的输出作为一个行块整体输出,请参照这里
REPORT zdemo_list_reserve LINE-SIZE 40 LINE-COUNT 8(2).
END-OF-PAGE.
WRITE: / '---page footer---'.
WRITE: / '**page footer**'.
START-OF-SELECTION.
DO 4 TIMES.
WRITE / sy-index.
ENDDO.
DO 2 TIMES.
WRITE / sy-index.
ENDDO.
RESERVE 3 LINES.
WRITE: / 'LINE 1',
/ 'LINE 2'.
RESERVE 3 LINES.
WRITE:/ 'LINE 3'.
控制分页是否使用标准page header
标准pageheader是由list header 与 column header组成的。在使用NEW-PAGE进行分页时,可以通过下面选择屏蔽或打开standard page header:
NEW-PAGE [NO-TITLE|WITH-TITLE] [NO-HEADING|WITH-HEADING].
WITH-TITLE or NO-TITLE:控制NEW-PAGE新开启的页面以及后面使用NEW-PAGE开启的页面是否使用标准的list header
NO-HEADING or WITH-HEADING:控制NEW-PAGE新开启的页面以及后面使用NEW-PAGE开启的页面是否使用标准的column header
即使在REPORT后面加上了NO STANDARDPAGE HEADING选项,你也可以单独使用WITH-TITLE and WITH-HEADING来打开。
该选项只对标准的pageheader有作用,对TOP-OF-PAGE中输出的不起作用。
REPORT demo_list_new_page_options LINE-SIZE 40.
WRITE: 'Page', sy-pagno.
NEW-PAGE NO-TITLE.
WRITE: 'Page', sy-pagno.
NEW-PAGE NO-HEADING.
WRITE: 'Page', sy-pagno.
NEW-PAGE WITH-TITLE.
WRITE: 'Page', sy-pagno.
NEW-PAGE WITH-HEADING.
WRITE: 'Page', sy-pagno.
控制分页中每页的行数
为了单独控制分页出页面允许的数据行数,可以使用:
NEW-PAGE LINE-COUNT <length>.
该选项决定了随后的所有(除非又重新通过该语句的这个选项重新指定了)使用NEW-PAGE语句分出的页面的允许的最大数据行数。如果为0,则使用标准页面的长度。pageheader与pagefooter都是要算行数的。另外,不能通过NEW-PAGE的选项控制footer的行数,这只能通过REPORT语句选项指定。
系统变量SY-LINCT会存储页面行数(即<length>)。
REPORT demo_list_new_page_line_c_1 LINE-SIZE 40 LINE-COUNT 0(1).
END-OF-PAGE.
ULINE.
START-OF-SELECTION.
NEW-PAGE LINE-COUNT 5.
DO 4 TIMES.
WRITE / sy-index.
ENDDO.
WRITE: / 'Next Loop:'.
NEW-PAGE LINE-COUNT 6.
DO 6 TIMES.
WRITE / sy-index.
ENDDO.
控制分页的页宽
NEW-PAGE LINE-SIZE <width>.
SY-SCOLS:存储了当前窗口在没有滚动条的情况下允许的最大字符数(或叫Column),而LINE-SIZE选项指的是列表本身最大允许的字符数,与窗口大小没有关系(如果LIST列表的宽度LINE-SIZE大于了窗口允许的最大字符数SY-SCOLS,则会出现滚动条,否则窗口不会出现滚动条)
滚动列表
滚动成功后SY-SUBRC为0
SY-SROWS系统变量中存储了当前窗体可以输出的最大数据行数,包括Page Header
垂直滚动:by Window
SCROLL LIST FORWARD|BACKWARD [INDEX <idx>].
FORWARD:向下滚动一屏(窗口)
FORWARD:向上滚动一屏(窗口)
<idx>:移动哪一Level的List
REPORT demo_list_scroll_1 NO STANDARD PAGE HEADING LINE-SIZE 40.
TOP-OF-PAGE.
"SY-SROWS系统变量中存储了当前窗体可以输出的最大数据行数,包括Page Header
WRITE: 'Top of Page', sy-pagno, 'SY-SROWS:', sy-srows.
ULINE.
START-OF-SELECTION.
DO 100 TIMES.
WRITE / sy-index.
ENDDO.
"向下翻一屏
SCROLL LIST FORWARD.
垂直滚动:by Pages
SCROLL LIST TO FIRST PAGE | LAST PAGE | PAGE <pag> [INDEX <idx>] [LINE <lin>].
如果省略了INDEX选项,该语句是针对当前List操作的。
如果指定LINE选项,则会滚动到列表的第<lin>行(不包括pageheader lines),否则滚动至列表的首行。
另一种滚动方式:
SCROLL LIST FORWARD | BACKWARD <n> PAGES [INDEX <idx>].
REPORT demo_list_scroll_2 NO STANDARD PAGE HEADING LINE-SIZE 40 LINE-COUNT 8(2).
DATA: pag TYPE i VALUE 2, lin TYPE i VALUE 3.
TOP-OF-PAGE.
WRITE: 'Top of Page', sy-pagno.
ULINE.
END-OF-PAGE.
ULINE.
WRITE: 'End of Page', sy-pagno.
START-OF-SELECTION.
DO 3 TIMES.
DO 4 TIMES.
WRITE / sy-index.
ENDDO.
ENDDO.
"第2页的第3行会显示在窗体的第一行
SCROLL LIST TO PAGE pag LINE lin.
水平滚动:滚动到页面最左或最右
SCROLL LIST LEFT | RIGHT [INDEX <idx>].
REPORT demo_list_scroll_3 NO STANDARD PAGE HEADING LINE-SIZE 200.
TOP-OF-PAGE.
WRITE: AT 161 'Top of Page', sy-pagno,
'SY-SCOLS:', sy-scols.
ULINE.
START-OF-SELECTION.
DO 200 TIMES.
WRITE sy-index.
ENDDO.
"水平滚动到最右边
SCROLL LIST RIGHT.
水平滚动:by Columns
SCROLL LIST TO COLUMN <col> [INDEX <idx>].
该语句作用是将滚动条滚动到LIST列表中的第<col>列(而不是滚动到当前窗口中的多少列)
SCROLL LIST LEFT | RIGHT BY <n> PLACES [INDEX <idx>].
该语句作用是向左或向右相对滚动多少列(即多少个字符)。
SY-SCOLS:存储了当前窗口在没有滚动条的情况下允许的最大字符数(或叫Column),而LINE-SIZE选项指的是列表本身最大允许的字符数,与窗口大小没有关系(如果LIST列表的宽度LINE-SIZE大于了窗口允许的最大字符数SY-SCOLS,则会出现滚动条,否则窗口不会出现滚动条)
REPORT demo_list_scroll_4 NO STANDARD PAGE HEADING LINE-SIZE 200.
TOP-OF-PAGE.
WRITE: AT 161 'Top of Page', sy-pagno,
'SY-SCOLS:', sy-scols.
ULINE.
START-OF-SELECTION.
DO 200 TIMES.
WRITE sy-index.
ENDDO.
"滚动到LIST列表中的第178的位置
SCROLL LIST TO COLUMN 178.
水平滚动控制
NEW-LINE NO-SCROLLING.
新起一行将不能水平滚动,但可以垂直滚动
NEW-LINE:相当于 Write: / ,表示换行,在Write之前调用了NEW-LINE时再没有 / 选项的 Write: 语句时会先换行再输出。
REPORT sapmztst NO STANDARD PAGE HEADING LINE-COUNT 3 LINE-SIZE 140.
START-OF-SELECTION.
DO 3 TIMES.
WRITE: / 'SY-INDEX:'.
DO 10 TIMES.
WRITE sy-index.
ENDDO.
ENDDO.
"换行新起一行,但新行不能水平滚动
NEW-LINE NO-SCROLLING.
ULINE AT 20(20).
NEW-LINE NO-SCROLLING.
WRITE AT 20 '| **** Fixed! **** |'.
NEW-LINE NO-SCROLLING.
ULINE AT 20(20).
DO 3 TIMES.
WRITE: / 'SY-INDEX:'.
DO 10 TIMES.
WRITE sy-index.
ENDDO.
ENDDO.
左边指定列不可滚动
SET LEFT SCROLL-BOUNDARY [COLUMN <col>].
REPORT sapmztst NO STANDARD PAGE HEADING LINE-COUNT 3 LINE-SIZE 140.
END-OF-PAGE.
"如果每页左边固定列数相同,则可以写在
"START-OF-PAGE 或 END-OF-PAGE 事件中
* SET LEFT SCROLL-BOUNDARY COLUMN 20.
START-OF-SELECTION.
DO 3 TIMES.
WRITE: /11 'SY-INDEX:'.
DO 10 TIMES.
WRITE sy-index.
ENDDO.
ENDDO.
"如果每一页左边固定的列数不一样,则需要
"单独为每页进行设置,并且只能放在输出下
"一页的前面
SET LEFT SCROLL-BOUNDARY COLUMN 20.
DO 3 TIMES.
WRITE: / 'SY-INDEX:'.
DO 10 TIMES.
WRITE sy-index.
ENDDO.
ENDDO.
SET LEFT SCROLL-BOUNDARY COLUMN 10.
LIST布局
输出位置
可以指定WRITE与ULINE语句在当前页面的任何位置输出,并且WRITE, SKIP, or ULINE在指定输出位置时可能覆盖现有已经的输出。获取当前输出位置,可以通过下面两个变量来获得:
? SY-COLNO
? SY-LINNO (注:这个系统变量指的是下次Write输出的行的位置,且是每页都从1开始,而不像SY-LILLI那样指整个列表屏幕中的决对行号)
ABAP提供了几个关键字以绝对或相对来改变输出位置
绝对定位
水平
ABP提供了两种水平绝对定位方式:
第一种是WRITE与ULINE语句的AT 选项
第二种是使用POSITION语句:POSITION <col>.
<col>会存储到SY-COLNO系统变量中
注意:对于每个WRITE语句输出的内容只会在一行中显示(显示不下时会被截断),不会换行显示,至于换行是因为第二次使用WRITE语句输出
垂直
SKIP TO LINE <lin>.
<lin>会存储到SY-LINNO系统变量中,如果<lin>不在1到pagelength之间时,将忽略此语句,从上一次输出位置后面继续接着输出。注意:这里所指的<lin>是指在一个分页里的行号,而不是指定整个当前窗口,另外,<lin>包括每页的Page Header与Page Footer行在内。
定位到Page Header下面的第一行的首列位置
BACK.
如果该语句不是放在RESERVE语句后面的行中使用时,后面的输出将会显示在Page Header下面开始显示输出。使用该语句后,系统变量SY-COLNO将设置为1,SY-LINNO将会设置为页面的Page Header的下一行。如果与RESERVE语句一起使用时,请参考这里的使用规则
REPORT demo_list_position NO STANDARD PAGE HEADING LINE-SIZE 60 LINE-COUNT 4.
DATA: x(3) TYPE c, y(3) TYPE c.
x = sy-colno.
y = sy-linno.
TOP-OF-PAGE.
WRITE: 'Position of Header: ', x, y.
ULINE.
START-OF-SELECTION.
SKIP TO LINE 4.
POSITION 26.
x = sy-colno.
y = sy-linno.
WRITE: '* <- Position', x, y.
"超过了页而大小,所以被忽略,下画线从
"当前面的Page Header下面第一行开始画
SKIP TO LINE 5.
ULINE AT 30(20).
BACK.
x = sy-colno.
y = sy-linno.
"在没有使用上面BACK的情况下,该WRITE语句会换行后输出,
"原因是上一次输出画线之后,线后面没有足够的空间来存储
"该WRITE语句输出的内容,如果线后有足够空间输出,则是
"会换行再输出。但在这里由于上面的BACK语句,下面的输出
"行的位置又回到了Page Header下的第一行开始输出
WRITE: 'Position after BACK:', x, y.
相对定位
当使用WRITE时如果没有使用定位选项,则其输出位置是会相当于前一次输出后继位置,并且以空格分隔(在没有加 NO-GAP选项的情况下),如果没有足够的空间,将会自动换行。
产生一个换行(NEW-LINE)
NEW-LINE.
SY-COLNO会被设置为1,SY-LINNO会在原来基础上加1
定位到其他输出的下面(WRITE …UNDER)
WRITE <f> UNDER <g>.
该语句对跨页的<g>也有效
定位输出到 the First Line of a Line Block
RESERVE.
.....
BACK.
如果BACK用在了RESERVE后面,则BACK后继续的输出会输出到BACK与RESERVE之间的输出行块中的第一行。但要注意的是,RESERVE不能引起分页,否则BACK还是会定位到Page Header的后面第一行位置上。
REPORT demo_list_position_relative_2
NO STANDARD PAGE HEADING LINE-SIZE 40 .
DATA x TYPE i.
WRITE 'Some numbers:' NO-GAP.
x = sy-colno.
ULINE AT /(x).
RESERVE 5 LINES.
DO 6 TIMES.
WRITE / sy-index.
ENDDO.
x = sy-colno.
BACK.
WRITE AT x ' <- Start of Loop'.
格式化输出
使用WRITE、SKIP、ULINE输出语句输出的内容将被系统输出传递到标准列表屏幕中。请参考《ABAP BC Programming.docx》
FORMAT语句
请参考《ABAP BC Programming.docx》
其他格式化输出
上面章节讲了FORMAT常用的选项,这节讲一下比较特殊的选项,这些选项的格式输出是根据特定中的数据来进行的,通常用户会维护这些定制数据
Country-specific(国家) and User-specific(用户)相关格式化
数字与日期字段的输出格式是与用户主数据相关的,用户主数据可以使用SU3事务码来维护(或者点击相应菜单也可):
或者也可能通过事务码SU01来维护:
虽然可以通过上面的设置来格式化数字与日期时间的格式化输出,但你也可以在程序中使用下面语句来修改:
SET COUNTRY <c>.
<c>可以是国家代码,也可以SPACE,这些国家代码可以在T005X表中查找到
如果<c>不为SPACE,则系统会关闭掉用户主数据的设定,而是去到T005X中查找与国家代码<c>相对应的数据,如果在表中存在这样的数据时,该语句执行后SY-SUBRC为0,并且后继的所有WRITE语句都根据这个表中设定的数据来格式化输出;如果该语句执行失败则SY-SUBRC为4,并且后继的WRITE语句输出的数字的千分位以“,”显示,小数点是“.”来显示,日期显示为 MM/DD/YY格式。
如果<c>设置为SPACE,系统不会读T005X表,而是去使用用户设置的主数据,在这种情况下SY-SUBRC总是0
REPORT demo_list_set_country LINE-SIZE 40.
DATA: num TYPE p DECIMALS 3 VALUE '123456.789'.
ULINE.
WRITE: / 'INITIAL:'.
WRITE: / num, sy-datum.
ULINE.
SET COUNTRY 'US'.
WRITE: / 'US, SY-SUBRC:', sy-subrc.
WRITE: / num, sy-datum.
ULINE.
SET COUNTRY 'GB'.
WRITE: / 'GB, SY-SUBRC:', sy-subrc.
WRITE: / num, sy-datum.
ULINE.
SET COUNTRY 'DE'.
WRITE: / 'DE, SY-SUBRC:', sy-subrc.
WRITE: / num, sy-datum.
ULINE.
SET COUNTRY 'XYZ'.
WRITE: / 'XYZ, SY-SUBRC:', sy-subrc.
WRITE: / num, sy-datum.
ULINE.
SET COUNTRY space.
WRITE: / 'SPACE, SY-SUBRC:', sy-subrc.
WRITE: / num, sy-datum.
ULINE.
Currency-specific(货币)格式化
WRITE <f> CURRENCY <c>.
输出金额<f>时,会根据该语句设置的货币代码<C>来决定其小数位置,如果货币代码<c>在表TCURX(CURRKEY)中存在,则系统将根据TCURX-CURRDEC值来设置<f>的小数点的位置,否则将<f>转换成具有2位小数的数字。这就意味着除非<f>本身就是类型为P(.2)(即货币的最大单位与最小单位换算为100时,如CNY人民币、USD美元)的金额字段,否则需要在TCURX表中配置所对应币种的小数位(因为不配置时会采用默认的2位)。
注意:这里的<f>一般是从数据库里读取出来的金额数据才需要这样格式化输出的,如果<f>本身存储的就是真实的金额,则不需要格式再输出,而是直接输出;另外,这里的格式化只是简单机械的根据TCURX表里所配置的小数位置来设置金额的小数点位置(而并不是乘以或除以某个转换率),并与金额变量<f>类型本身的具有多少小数位有关:如果<f>的类型为P(6.5),值为<f> = 1.234时,且TCURX表里配置的小数位为2时,最后输出的是 1234.00 ,而不是12.34(如果是根据转换率来除,则结果会正确),因为在格式化前,会将小数末的0也参与处理,并不理会<f>本身原来的小位数,而是将所有的数字位(抛开小数点,但包括末尾的0)看作是待格式会的数字字符串:
DATA: p(6) TYPE p DECIMALS 5.
p = '1.234'.
WRITE: p CURRENCY 'aa'."1,234.00
TCURX:货币小数位置表
TCURC:货币代码表
TCURR:汇率表
SAP表里存储的并不是货币的最小单位,一般是以货币最大单位(也是常用计量单元)来存储,不过在存储之前会使用经过转换:比如存储的金额是 100,则存储到表之前会除以一个转换因子后再存入数据表中(该转换因子是通过CURRENCY_CONVERTING_FACTOR函数获得的,如比CNY的转换因子为1,JPY为100),所以如果要取出来自己使用,则需要再次乘以每次以这个因子才能得到真正的金额数(不知道SAP为什么这么设计,或者是一个什么国际标准吧)。另外,数据库中存储的虽然不是最小单位,但取出来后都是放在P类型的变量中的,所以取出来在内存中统计是不会有精度丢失的(P类型相当于Java中的BigDecimal类类型)。
TCURX表中存储的小数位实质上是根据同种币种的最大单位与最小的换算率 = 10X 来计算的,式中的X即TCURX表中的小数位,如CNY中的最大单位元与最小单位分相差100倍,所以100 = 10X,X就为2,最后TCURX-CURRDEC存储的就是2(但如果值为2是可以不需要在TCURX表中配置的,所以查不到CNY的配置数据,因为不配置时默认值也是2);另外,JPY日元没有最小单位,所以最大单位与最小单位的换算率就是1,所以X就为0,所以TCURX-CURRDEC存储的就是0。
数据库中用来存储金额的字段的类型都是P(.2),即带两位小数,因为转换因子最大也就是100,有的是零点几(在存入之前会将真实金额除以这个转换因子后再存入),所以存储类型为两位小数的数字类型即可。用来存储表中存储的内部金额的变量类型一定要具有两位类型的,否则在使用诸如CONVERT_TO_LOCAL_CURRENCY、CONVERT_TO_FOREIGN_CURRENCY转换函数或者是格式化输出时,都会有问题,所以在定义这些内部金额变量时,最好参照相应数据词典类型。
从表中读取日元并正确的格式化输出
DATA: netpr LIKE vbap-netpr,"实际的类型为p(6.2)
waers LIKE vbap-waerk,
jpy_netpr TYPE i,
netpr1(6) TYPE p DECIMALS 3.
"通过SQL从数据库查询出来的是真实存储在表里的数据,没有经过其他转换,日元
"存到数据库中时缩小了100倍,所以要还原操作界面上输入的日元金额,
"则需要使用后面的格式化输出
SELECT SINGLE netpr waerk INTO (netpr,waers) FROM vbap WHERE waerk = 'JPY' AND vbeln = '0500001326'.
WRITE: waers,netpr."数据库中的值,被缩小了100倍
"第一种还原方式
WRITE: / 'Format:', netpr CURRENCY waers.
"第二种转换方式:也可以通过以下函数先获取JPY货币代码的转换因子,再直乘以这个因子也可
DATA: isoc_factor TYPE p DECIMALS 3.
CALL FUNCTION 'CURRENCY_CONVERTING_FACTOR'
EXPORTING
currency = waers
IMPORTING
factor = isoc_factor.
jpy_netpr = netpr * isoc_factor."乘以100倍,因为在存入表中时缩小了100倍
WRITE: / 'Calc factor:', jpy_netpr.
"格式化输出实质上是与存储金额的变量本身的类型小数位有关:上面将从表中
"读出的金额(小数两位)赋值给变量netpr1(小数三位),格式化后会扩大
"10倍(因为多了一位小数位)。所以格式化正确输出的前提是要用来接收从
"表中读取的金额变量的类型要与数据表相应金额字段类型相同,否则格式化输出会出错
netpr1 = netpr.
WRITE: / netpr1, netpr1 CURRENCY waers."格式化的结果是错误的
SAP 货币转换因子
一般而言,币种的小数位为2,所以系统默认的位数也是2.但是有一些特殊币种如日元JPY,没有小数位,或者其他对于2位的小数位。只要小数位不等于2,需要在系统中特殊处理(通过转换因子进行转换,具体请参看后面SAP提供的函数 currency_converting_factor 实现过程)。在编程中
1、List中,当输出CURR字段时,记得指定对应的货币:
如: WRITE: vbap-netwr CURRENCY vbap-waerk.
2、Screen中,对于CURR字段,需要设置对应的货币字段:
3、ALV中,需要对FIELD CATALOG进行设置.
如: ls_cfieldname = ‘WAERS’. "这里的WAERS是内表中的另一货币字段,里面存储了相应金额的货币代码。
货币的是:fieldcat-cfieldname、fieldcat-ctabname(内表名,可以不设置)
随便数量也是相似的方法来处理的:
数量的是:fieldcat-qfieldname、fieldcat-qtabname(内表名,可以不设置)
下面是SAP转换因子函数,在金额存储与在ALV展示时都会自动除以与乘以这个转换因子:
FUNCTION currency_converting_factor.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
* IMPORTING
*" VALUE(CURRENCY) LIKE TCURR-TCURR
*" EXPORTING
*" VALUE(FACTOR) TYPE ISOC_FACTOR
*" EXCEPTIONS
*" TOO_MANY_DECIMALS
*"----------------------------------------------------------------------
DATA: cur_factor TYPE isoc_factor.
*- determine Decimal place in currency from TCURX
CLEAR tcurx.
"首先根据币种到db表tcurx中读取相应的小数位数currdec
SELECT SINGLE * FROM tcurx WHERE currkey EQ currency.
"如果没有维护相应币别信息则默认currdec = 2
IF sy-subrc NE 0.
tcurx-currdec = 2.
ENDIF.
"如果currdec 大于了 5就报错
IF tcurx-currdec GT 5.
*- entry in tcurx with more than 5 decimals not allowed
RAISE too_many_decimals.
ENDIF.
*- compute converting factor respecting currency
"然后默认转换比率是100。如果表tcurx中的currdec = 0就默认转换比率为100
cur_factor = 100.
IF tcurx-currdec NE 0.
"在currdec不等于0的情况下循环currdec次,每次将转换比率除以10
DO tcurx-currdec TIMES.
"当表tcurx中没有找到相应数据时则默认currdec = 2,转换比率也就是100 / 10 / 10 = 1。其他
"的比如表tcurx中的currdec = 4,则转换比率应该为 100 / 10 / 10 / 10 / 10 = 0.01
cur_factor = cur_factor / 10.
ENDDO.
ENDIF.
IF cur_factor = 0.
*- factor 0 not allowed; check data definition of factor
*- entry in tcurx with more than 5 decimals not allowed
RAISE too_many_decimals.
ENDIF.
factor = cur_factor.
ENDFUNCTION.
简单的使用Function CURRENCY_CONVERTING_FACTOR,输入币别,就可以得到相应的转换比率了。我们在SE16中看到的货币金额基本上都经过了这个转换,如日元,都是除以100后存入数据库的。所以当我们从数据库中读取日元金额时也应该作相应的转换,乘以100 。
1、如果某货币的小数位不是2位,则需要通过OY04设置其小数位数,即需要在TCURX表中进行维护
2、系统中的数据表存放的日元JPY、俄卢布RUR等货币比前台输入的金额小100倍,因为它们没有小数位,所以转换因子为100,存入表之前SAP会先将金额除以这个因子后再存入
3、系统根据转换因子将原金额转换成含小位小数的金额后存储(据说根据ISO的什么标准),如日元为0位小数,转换因子为100,120日元除以因子100后转换后变成1.20,缩小100倍。如为USDN为5位小数,其转换因子为100/10/10/10/10/10=0.001,12.01230除以0.001后则转换成12012.30,扩大1000倍。SAP在金额数据存储时会自动的转换,其实SAP是有external及internal的数据格式,可以调用以以下函数实现相互转换。BAPI_CURRENCY_CONV_TO_INTERNAL:转换成跟数据库中存储一样的格式数据,BAPI_CURRENCY_CONV_TO_INTERNAL’:转换成外部实际金额
4、每次币别的汇率更改在正式生产系统中新创建一条记录,利用函数CONVERT_TO_LOCAL_CURRENCY自动会把当前最近的时间的汇率作为转化的汇率,而不是直接在原纪录上更改
5、OB07、OB08,维护各币种之间的汇率。
6、 碰到比较变态的货币,例如日元,它们是没有小数点的,系统内存储的和你看到的不同,有个BAPI可以使用:BAPI_CURRENCY_CONV_TO_INTERNAL
7、 还有一个FM:CONVERT_TO_FOREIGN_CURRENCY,和CONVERT_TO_LOCAL_CURRENCY基本没有区别,可以任选一个使用
货币内外格式转换
"所有金额的在数据库里(内部格式)都是带两位的小数数字类型
"用来存储内部金额时,变量的类型一定要与数据库表里的类型一致
",否则使用WRITE输出时会不准确
DATA: usd(7) TYPE p DECIMALS 2,
jpy(7) TYPE p DECIMALS 2,
jpy_e(12) TYPE p DECIMALS 4.
DATA: usd_k TYPE waers, jpy_k TYPE waers.
DATA: ret TYPE bapireturn.
"此处为实际金额,所以不宜直接格式化(只有对内部表中存储格式的金
"额格式化输出才有意义,否则是错误的输出),不过这里为实际的金额
"似乎也有点不对,因为日元真实金额是不会有小数的,所以变量jpy用来
"存储外部实际金额是不妥的,jpy应该为整数类型才恰当
jpy = '10000.01'.
usd_k = 'USD'.
jpy_k = 'JPY'.
"使用CONVERT_TO_LOCAL_CURRENCY、CONVERT_TO_FOREIGN_CURRENCY
"函数时,涉及到的金额输入输出参数都是采用内部计算格式,所以在使用这
"些函数时,如果是外部金额,应先将它们转换为内部金额后再传入
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'"将一种货币兑换成另一种货币
EXPORTING
date = sy-datum
foreign_amount = jpy"该程序中的jpy本身为外部金额,但在这里会将
"它看作是内部金额,所以最后相当于外部金额1000001
foreign_currency = jpy_k
local_currency = usd_k
IMPORTING
local_amount = usd."转换出来的也是内部格式的金额
*CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
* EXPORTING
* date = sy-datum
* foreign_amount = '1.00'"内部金额,美元的外部金额也是1.00美元
* foreign_currency = 'USD'
* local_currency = 'JPY'
* IMPORTING
* local_amount = usd."结果为内部金额:1.15,相当于外部金额为115日元
*CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
* EXPORTING
* date = sy-datum
* "如果内部金额没有小数,也要补上两位0,否则实质金额不准确,这里正是
* "因为末尾未被两位0,所以这里的金额实质上为0.01美元,而不是1美元
* foreign_amount = '1'"内部金额,相当于外部0.01美元
* foreign_currency = 'USD'
* local_currency = 'JPY'
* IMPORTING
* local_amount = usd. "结果为:0.01内部金额,实质相当于外部金额1日元
WRITE: jpy, jpy_k,usd, usd_k.
"由于jpy本身为实际金额,所以不能在这里格式输出;但usd为内部
"格式的金额,所以需要使用格式化输出(但usd本身就是带两位小数
"的内部金额,转换
WRITE:/ jpy CURRENCY jpy_k, jpy_k,
usd CURRENCY usd_k, usd_k.
ULINE.
jpy_e = jpy.
"将外部金额转换为内部存储金额,实质上过程是将外部金额
"除以转换因子即可得到
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_INTERNAL'
EXPORTING
currency = jpy_k
amount_external = jpy_e"外部金额
max_number_of_digits = 23"没什么作用,一般写23即可
IMPORTING
amount_internal = jpy "转换后的内部存储金额
return = ret.
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = sy-datum
foreign_amount = jpy "源货币金额(内部格式)
foreign_currency = jpy_k"源货币类型
local_currency = usd_k"目标货币类型
IMPORTING
local_amount = usd."目标货币金额(内部格式)
WRITE: jpy, jpy_k,usd, usd_k.
WRITE: / jpy CURRENCY jpy_k, jpy_k,
usd CURRENCY usd_k, usd_k.
Unit-specific(单位)格式
该语句根据Unit <u>的来设置<f>的小数位数。<u>必须要在T006中进行过配置,并且<u>的值就是T006-MSEHI字段值,T006-DECAN字段值决定<f>显示的小数位数,如果<u>在表T006中没有找到,将会忽略该UNIT选项。
? <f>必须是P类型的
? 如果<f>本身的小数位比<u>所配置的小数位小时,系统会忽略该选项
? 如果<f>本身的小数位比<u>所配置的要多时,并且多余的小数位全部是零时,会被截断;如果多余的小数部分不是零时,会直接忽略该选项
注:该选项不像金额选项CURRENCY那样,格式化后可能会改金额本身的大小(如果存储金额的变量定义的类型中设定小数位与对应数据库表字段类型中定义的小数位不同时会发生),但这里的格式化只是确定数量输出时带几位小数而已。
DATA: num1 TYPE p DECIMALS 1 VALUE 1,
num2 TYPE p DECIMALS 4 VALUE '2.5'.
SET COUNTRY 'US'.
WRITE: 'N/A',num1 , num2,
/ 'KG', num1 UNIT 'KG', num2 UNIT 'KG',
/ 'PC', num1 UNIT 'PC', num2 UNIT 'PC',
/ '%', num1 UNIT '%', num2 UNIT '%'.
数量格式化输出
问:通过se11 我们可以看到ekpo中menge的数据元素是BSTMG,BSTMG的域是长度13小数位3位。在程序中我参照ekpo-menge定义的变量显示的时候后面都有3位小数,而我希望做好和me23n一样,去掉小数点后面多余的零,请问大侠们有没有比较好的办法。为什么me23n中“PO数量”显示的时候没有多余的零,而他们的数据元素是一样的。
答:MENGE实际上是个存储度量衡值的字段,他的基本数据类型是QUAN,他的小数位数并不是你看到的3,而是由这个字段关联的度量衡单位决定的,以MENGE为例,你可以在SE11的最右边一个Tab页,Currency/Quantity Fields里看到,他关联的单位是EKPO-MEINS
DATA: i_menge LIKE ekpo-menge, i_meins LIKE ekpo-meins.
START-OF-SELECTION.
SELECT menge meins FROM ekpo INTO (i_menge, i_meins) WHERE ebeln = '4500000059'.
WRITE: i_menge , i_meins.
"格式化输出后也ME23N显示的格式是一样的
WRITE: / i_menge UNIT i_meins, i_meins.
ENDSELECT.
不同单位之间的换算
PARAMETERS: p_in(10) TYPE c,
unit_in LIKE t006-msehi DEFAULT 'M',"米
unit_out LIKE t006-msehi DEFAULT 'MM',"毫米
round(1) TYPE c DEFAULT 'X'.
DATA: result TYPE p DECIMALS 3,
input TYPE p DECIMALS 3.
input = p_in.
CALL FUNCTION 'UNIT_CONVERSION_SIMPLE'
EXPORTING
input = input
round_sign = round"舍入方式(+ up, - down, X comm, SPACE.)
unit_in = unit_in
unit_out = unit_out
IMPORTING
output = result.
WRITE: 'Result: ',result.
交互式LIST
与屏幕一样,你可以通过SET PF-STATUS设置STATUS。在normal screen中,用户的动作可以触发PAI事件,并且在screen flow logic中可以调用相应的ABAP dialog modules来处理是这个动作。但在list processing中,事件会被list processor拦截,并在以下三种LIST事件块里进行处理(具体触发哪一个事件块,则与用户的动作相关):
? AT PF<nn>(obsolete已过时的)
? AT LINE-SELECTION
? AT USER-COMMAND
在各事件块中可以使用SY-UCOMM系统变量来捕获用户动作所对应的Function Code。在以上三种事件块中的任何输出,都会在事件块结束后写到新创建的detail lists中。在一个ABAP 程序中,你可以生成一个basic list与20个detail lists。
Detail Lists
如果输出语句不是在列表事件块(上面三种)里输出时,都会输出到basic list中,而在这三种事件块里输出的的内容都会输出到Detail Lists中去。
系统变量SY-LSIND存储了当前创建的list的索引,并且basic list的SY-LSIND为0。
在默认情况下,basic list具有standard list status和standard page header,并且basic list 在被创建时会触发TOP-OF- PAGE与END-OF-PAGE事件。
在可执行报表程序中,basic list会自动地传递给list processor并在END-OF- SELECTION事件后显示,否则,如果是在屏幕的PAI的ABAP dialog modules中显示basic list,则需要使用LEAVE TO LIST-PROCESSING语句来将basic list发送给list processor处理器,并且在PAI事件块执行完后才显示basic list。
创建Detail Lists
SY-LSIND系统变量存储了当前List的层级,Basic List的层级为0,后面创建出来的Detail List的层次会依据创建的先后顺序自动的增加。凡事在AT PF<nn>、AT LINE-SELECTION、AT USER-COMMAND三个事件块里输出的内容,都会创建一个新的detail lists(每一个事件块结束后都会创建一个新的detail list)。在ABAP程序中,你可以维护一个basic list与20个detail lists。在创建新的List时,会将前面的层级的List保存下来,并且用户还可以选择显示哪层List。
默认情况下,list会完全覆盖前层次的list,但时你可以使用dialog box的方式来显示list。如果没有在事件块里为detail list设置dialog status,系统将会使用以前层次的status,但是,detail lists是没有standard page header的。
START-OF-SELECTION.
WRITE: 'Basic List, SY-LSIND =', sy-lsind.
AT LINE-SELECTION.
WRITE: 'Detail List, SY-LSIND =', sy-lsind.
每次在双击行都会新创建一个Detail List:
Detail Lists不会显示standard page header(如上面的Page Title),创建时也不会触发TOP-OF-PAGE事件,但每个Detail Lists都会触发TOP-OF-PAGE DURING LINE-SELECTION事件,而END-OF-PAGE事件Basic Lists与Detail Lists在满页时都会触发。
Details Lists中的系统变量
Systemfield |
Information |
SY-LSIND |
Indexof thelistcreatedduringthecurrentevent(basiclist= 0) 当前事件所创建的列表层级(下一列表),默认值为SY-LISTI加一, 除非重新设置了SY-LISTI |
SY-LISTI |
Indexof thelistlevelfrom whichtheeventwastriggered 事件触发所在列表层级(当前列表) |
SY-LILLI |
Absolutenumberof thelinefrom whichtheeventwastriggered 事件触发所在列表行的绝对行号(指整个输出列表的行号——所有分页算起来,且如果是SKIP等输出语句输出的空行是计算在内的,如果是分页之间的空行——严格的来讲是空隙,是不算行号的 请注意:该变量与SY-LINNO是有区别的) |
SY-LISEL |
Contentsof thelinefrom whichtheeventwastriggered 触发事件的行(即光标所在行)的内容(只要单击某行,SY-LISEL的内容就是当前正被选中行的内容,即光标所在行的内容) |
SY-CUROW |
Positionof thelineinthewindowfrom whichtheeventwastriggered(countingstartswith1) 事件触发所在窗体中的行号(这是相对于窗体来说的,指触发事件的行在当前窗体中的行号,并且空行也算,注:每个窗体的行号只与窗口的大小有关,且窗口大小定以后,其能显示的最大行数也就固定了) |
SY-CUCOL |
Positionof thecolumninthewindowfrom whichtheeventwastriggered(countingstartswith2) 事件触发所在窗体中的行SY-CUROW中的列位置 |
SY-CPAGE |
Pagenumberof thefirstdisplayedpageof thelistfrom whichtheeventwas triggered 触发事件的列表屏幕中所显示的第一页的页号 |
SY-STARO |
Numberof thefirstlineof thefirstpagedisplayedof thelistfrom whichtheevent wastriggered(countingstartswith1).Thislinemaycontainthepageheader. 事件触发所在列表中显示出来的第一页的第一行的行号 |
Numberof thefirstcolumndisplayedinthelistfrom whichtheeventwastriggered (countingstartswith1) 显示在列表行中的第一列(除开已被隐藏的列)的列的列号: 该系统变量会随着水平滚动条的滚动而变化。
|
|
SY-UCOMM |
Functioncodethattriggeredtheevent |
SY-PFKEY |
Statusof thelistcurrentlybeingdisplayed. |
Detail Lists中的消息
? A(=Abend):Termination结束,停止
.消息会显示在弹出对话框中,且当用户回车确认后,整个程序会结束
? E(=Error)orW(=Warning):
消息会显示在状态栏中。一旦用户使用ENTER确认了这个错误消息,则当前的事件块会终止并且显示前一级别的list。如果是在创建basiclist时出现错误消息,则整个程序也会被终止掉
? I(=Information):
.消息会显示在弹出对话框中。一旦用户确认了消息(ENTER),程序会继续处理MESSAGE语句后面的语句
? S(=status)
消息会在当前list状态栏显示
? X(=Exit)Runtimeerror:
该消息类型会触发一个runtimeerror并且会产生一个shortdump
LIST屏幕导航
可以通过回退到上一级别的List。在以通过修改SY-LSIND来决定当前事件块里的输出内容将输出到哪一级别层次的LIST屏幕上
注:返回时,不会根据程序的导航轨迹来回退,如程序导航轨迹为: 0 à 1 à 2 à 1 à 2 à 1 à 2,在最后一个2号LIST屏幕上点击Back时,返回的轨迹为 2 à 1 à 0(只能回退两次,而不是6次)
当使用sy-lsind重新定位输出到哪个LIST屏幕上时,如果只是定位(当前事件块中根本没有任何的输出时),则定位到的一下屏幕不会被重画,而是原样显示历史数据屏幕,但一旦有任何输出,则屏幕则会被重画。
START-OF-SELECTION.
WRITE: 'Basic List, SY-LSIND =', sy-lsind.
AT LINE-SELECTION.
WRITE: 'Detail List, SY-LSIND =', sy-lsind.
IF sy-lsind = 3.
"使用此事件块中所有输出都输出到Basic List上
"此设置放在事件任何位置都不会影响最终输出目的地,
"只是在这之前使用的sy-lsind的值没有被更新而已
sy-lsind = 0.
ENDIF.
WRITE: 'Detail List, SY-LISTI =',sy-listi.
START-OF-SELECTION.
WRITE: 'Basic List, SY-LSIND =', sy-lsind.
AT LINE-SELECTION.
IF sy-lsind = 3.
sy-lsind = 0.
ELSE.
"当sy-lsind = 3时,这里的内容不会输出到 Basic List上,
"此时会Basic List屏幕不会被重画,还是显示成最初的样子
WRITE: 'Detail List, SY-LSIND =', sy-lsind.
WRITE: 'Detail List, SY-LISTI =',sy-listi.
ENDIF.
GUI Status for Lists
在LIST屏幕上对某行进行双击或按F2就会触发相应的列表事件(AT PF<nn>、AT LINE-SELECTION、AT USER-COMMAND)。在Menu Painter中,double-click 动作与F2绑定了(F2就代表了双击,双击也即F2),如果在GUI status中将某个Function code分配给了F2键,当你双击时,就会触发这个Function Code。
标准的 List Status(SET PF-STATUS space)及事件
你可以像普通屏幕那样,给LIST设计GUI status,并通过SET PF-STATUS语句进行绑定(如果想为特定的层次的LIS设置GUI status,则需要使用SY-LSIND进行判断)。
如果在报表程序(可执行程序)中没有设置(Basic List与Detail List都未设置时)特定的GUI status,则系统会将list screen的Status设置为系统内置的default list status;在其他类型的程序中(如当你在Screen中创建一个list时),你可以明确的调用下面语句来设置成系统内置的default list status(该Status与报表程序默认的Status是一样的):
SET PF-STATUS space.
该语句定义了Standard List所拥有的默认functions,
系统所提供的内置default list status如下(具体有以下这些Menu Bar与Standard toolbar,并且这些功能都已经被系统实现了,可以直接使用):
如果在程序中还定义了如下事件,则系统还会自动为LIST分配额外的functions:
键盘上的F<nn>(01 到 24)键不再具有系统预置功能,它们都将与function codes PF<nn>关联,并且function codes PF<nn>与事件AT PF<nn>对应
REPORT demo_list_at_pf .
START-OF-SELECTION.
"PF5, PF6 这些Function code并没有在Stauts中进行
"过任何定义(这里也没有设置Status,使用的是Standard List Status)
"但按 F5, F6 这些键时会触发相应的事件块 AT PF<nn>
WRITE 'Basic List, Press PF5, PF6, PF7, or PF8'.
AT PF5.
PERFORM out.
AT PF6.
PERFORM out.
FORM out.
WRITE: 'Secondary List by PF-Key Selection',
/ 'SY-LSIND =', sy-lsind,
/ 'SY-UCOMM =', sy-ucomm.
ENDFORM.
当用户在LIST屏幕上的某行上按F2或者鼠标双击时,就会触发该事件块,并且此时的function code默认名为PICK。注:Function code PICK触发的前提是光标已经定位在了LIST列表中的某行上了。
REPORT demo_list_at_line_selection.
START-OF-SELECTION.
WRITE 'Basic List'.
AT LINE-SELECTION."选中某行后按F2或在某行上双击就会触发
WRITE: 'Secondary List by Line-Selection',
/ 'SY-UCOMM =', sy-ucomm.
除上面PF<nn>与PICK两个Function code以外,其他的function codes将被runtime environment拦截或者是触发AT USER-COMMAND事件。
在LIST SCREEN中,那些触发AT USER-COMMAND事件的Function codes(除了PF<nn>与PICK)都是自己在GUI statu中进行自定义过的;而能被runtime environment所拦截直接进行处理的,都是系统提供的standard function codes(具体是哪此Function code请参见Dialog Status for Lists中两个表格中所提到的,但也除了PF<nn>与PICK)。
通过程序触发事件
除了通过手动触发LIST屏幕事件之外,还可以直接通过编程的方式来触发:
?SET USER-COMMAND <fc>.
该语句会在当前列表事件块里的所有输出结束后生效(这意味着该语句放在输出语句的前后都没有关系),并在列表显示之前触发与<fc>对应的事件。其作用与用户手动选择了相应的Function Code是一样的,换句话说,预置的列表function code将会被runtime environment捕获与处理,function codes PICK与PF<nn>将会分别触发AT LINE-SELECTION and AT PF<nn>事件,而用户自定义的Function code将会触发AT USER-COMMAND事件块。
如果在创建LIST时使用好几个SET USER-COMMAND这样的语句,则只有最的一个语句会被执行。
示例:事件连锁反应
REPORT DEMO_LIST_SET_USER_COMMAND NO STANDARD PAGE HEADING.
START-OF-SELECTION.
"当程序启动后立即触发 AT USER-COMMAND 事件
SET USER-COMMAND 'MYCO'.
"即使该输入语句放在了上面语句后面,但该语句还是会输出到Basic List屏幕上
"因为SET USER-COMMAND是在当前事件块里输出结束后才生效
WRITE 'Basic List'.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'MYCO'.
WRITE 'Secondary List from USER-COMMAND,'.
WRITE: 'SY-LSIND', sy-lsind.
"当程序触发AT USER-COMMAND 事件后,又后立即触发AT PF05事件
"相当于在下一Detail List屏幕上按了 F5 快捷键了
SET USER-COMMAND 'PF05'.
ENDCASE.
AT PF05.
"将光标定位到下一Detail LIST屏幕上的第一行。注:尽管这里将SET CURSOR LINE与SET USER-COMMAND
"语句写在了后面两个输出语句的前面,最后的效果与将这两个语句放在后面两个输出语句的后面
"效果是一样的:结果都是定位到了下一Detail List屏幕上的第一行并按F2键
SET CURSOR LINE 1.
SET USER-COMMAND 'PICK'.
"当 AT PF05 事件触发后会立即触发AT LINE-SELECTION事件
WRITE 'Secondary List from PF05,'.
WRITE: 'SY-LSIND', sy-lsind.
AT LINE-SELECTION.
"当AT LINE-SELECTION事件触发后,又会立即触发 %SC 查找事件
SET USER-COMMAND '%SC'.
WRITE 'Secondary List from LINE-SELECTION,'.
WRITE: 'SY-LSIND', sy-lsind.
WRITE: / '-',sy-lisel NO-GAP.
最后显示屏幕如下:
但前面的屏幕可以通过 BACK 回退时显示
Status for Lists
可以通过Menu Painter为LIST创建dialog status,先创建普通类型的Status:
结果如下:
再选择菜单ExtrasàAdjusttemplate,并在弹出框中选择系统内置的“List status”Status Template来填充上面的Status:
结果如下:
上面通过内置“List status”Status模板(通过Status内在模板,这样我们就不必一个个输出设置了)填充的Function Code其实就是系统为List Screen提供的默认标准Status(标准的Status的具体样子可以参照上一节内容中的截图Standard List Status)
standard list status所提供的standard function codes分布在Menu、Standard toolbar、Function key三大Interface中,具体分如下:
Func Code |
所在的的 Menu |
所在的 Standard toolbar |
Function key |
Description |
%PC |
List |
|
|
Savelisttofile |
%SL |
List |
|
|
Savelist in SAPoffice |
%ST |
List |
|
|
Savelist inreporttree |
%PRI/ PRI |
List |
CTRL-P |
Printdisplayedlist |
|
%EX |
List |
|
Shift-F3 |
Exitprocessing |
PICK |
Edit |
|
F2 |
EventAT LINE-SELECTION |
RW |
Edit |
|
F12,ESC |
Cancelprocessing |
%SC |
Edit |
|
CTRL-F |
Find |
%SC+ |
Edit |
|
CTRL-G |
Findnext |
BACK |
Goto |
|
F3 |
Back onelevel |
P-- |
|
|
CTRL-PgUp |
Scrolltofirstwindowpage 注:这4个翻页按键是对整个LIST Window起作用的,而不是针对程序软分页,它们的滚动是针对当前LIST所在Window,窗体高度不一样,则滚动的长度不一样 |
P- |
|
|
PgUp |
Scrollto previouswindowpage |
P+ |
|
|
PgDn |
Scrollto nextwindowpage |
P++ |
|
|
Ctrl-PgDn |
Scrollto lastwindowpage |
%CTX |
|
鼠标右击 |
Shift-F10 |
Contextmenuonlist |
上表格Standard toolbar中的图标所对应的按键是已经预置好了的(这些键不能再分配给其他Function Code了),具体如下:
另外,下面这些function codes也是系统内置的,它们并没有在上面的Status中直接给我们进行了设置,但你可以自由的将这些Function Code在status 中定义它们来使用:
Func Code |
Description |
PF<nn> |
EventAT PF<nn> |
PP<n> |
Scrolltotopof listpage<n> |
PP-[<n>] |
Scrollback onelistpageor<n>pages |
PP+[<n>] |
Scrollforwardonelistpageor<n>pages |
PS<n> |
Scroll to column <n> |
PS-- |
Scroll to first column of the list |
PS-[<n>] |
Scroll left by one or <n> columns |
PS+[<n>] |
Scroll right by one or <n> columns |
PS++ |
Scroll to last column of the list |
PZ<n> |
Scroll to line <n> |
PL-[<n>] |
Scroll back to first line of the page or by <n>lines |
PL+[<n>] |
Scroll to last line of the page or by <n>lines |
/.... |
For other system commands |
除了PICK and PF<nn>这两Function code以外,上面两个表格中所列的Function code都将直接被runtime environment拦截下来进行处理,我们不需要捕捉这些Function Code,也不需要实现它们(因为这些Function Code根本不会触发事件的,所以不能在AT USER-COMMAND中使用它们)。
只要光标在List Screen中的某行上,Function code PICK会触发AT LINE-SELECTION事件;而function codes PF<nn>总是会触发AT PF<nn>事件,所以不能在AT USER-COMMAND事件中使用(因为没有触发此事件)。
如果自己定义的Function Code名不是上面两个表格中所预置的Function Code,则才会触发AT USER-COMMAND 事件。
请注意下面功能键的分配:
? FunctionkeyF2:
双击相当于按功能键F2,所以分配给F2键的functionn code都将具有鼠标双击的功能。但要触发AT LINE-SELECTION事件,则分配给F2的Function code只能是PICK,否同双击或按F2时,将会去触发AT USER- COMMAND事件或者被runtime environment拦截处理。
? FunctionkeySHIFT-F10:
SHIFT-F10键相当于鼠标右击,如果要弹出右键菜单,则需要将此键与名为%CTX的function code进行绑定。
你也可以修改上面Status图中的由list-specific template模板所填充的默认Function code,以满足其他需求:
? 使用自己定义的Function code名替换换掉PICK,这样将不会再触发AT LINE-SELECTION事件,你可以在AT USER-COMMAND中统一进行处理也可。
? 你可以删除那些你不想要的功能Function code。例如,阻止打印与保存。
? 修改内置标准的Key(内置Key有哪此可以参看上面截图中快捷键所示)所对应的Function Code。例如,你可以重新给F3分配自己的Function Code,而不是BACK,这样按时就不会被runtime environment拦截处理,也就是说不会走系统提供的默认回退功能,我们可以在AT USER-COMMAND事件中重新来实现自己特殊的回退功能(比如在回退前弹出一个警告提示框等)
Setting Status
SET PF-STATUS <stat> [EXCLUDING <f>|<itab>] [OF PROGRAM <prog>] [IMMEDIATELY].
所有层次级别的后续List都会使用<stat>,除非重新使用语句进行了设置。系统变量SY-PFKEY存储了当前LIST所使用的Status。
使用SET PF- STATUS SPACE语句可以设置成standard list status。
IMMEDIATELY选项是特定为list processing所设计的(即LIST Screen才具有,其他类型的屏幕没有这个选项)。如果在创建detail list的事件块里使用了该选项,则当前(index SY- LISTI)LISTScreen的Status也会被修改,并且当前事件块所创建出的Detail List也会使用这个设置的Status(因为根本没有为Detail List设置Status,所以使用前一LIST屏幕的Status);如果没有使用该选项,将会在事件块处理结束后修改当前后续(SY- LSIND) List屏幕的Status。
START-OF-SELECTION.
WRITE: 'SY-LSIND:', sy-lsind.
AT LINE-SELECTION.
SET PF-STATUS 'TEST' OF PROGRAM 'DEMO_LIST_SET_PF_STATUS_2' IMMEDIATELY.
上面实例程序中AT LINE-SELECTION.事件块中没有输出内容,所以不会产生新的Detail List,所以SET PF-STATUS语句执行后,我们可以立刻看到Basic List的Status以修改了:
其他选项请参考SET PF-STATUS
REPORT demo_list_menu_painter.
START-OF-SELECTION.
SET PF-STATUS 'TEST'.
WRITE: 'Basic list, SY-LSIND =', sy-lsind.
AT LINE-SELECTION."按F2或双击行 或 点击菜单 Edit-->Choose时触发
WRITE: 'LINE-SELECTION, SY-LSIND =', sy-lsind.
AT USER-COMMAND.
CASE sy-ucomm."按F5或点击工具条中的Test for demo按键时触发
WHEN 'TEST'.
WRITE: 'TEST, SY-LSIND =', sy-lsind.
ENDCASE.
Setting a Title for a List
请参考SET TITLEBAR
右键菜单 for Lists
如果想为LIST屏幕设置右键菜单,先需要为Status分配Context Menu(分配后们可以在Menu Painter中看到快捷键Shift+F10分配一个Function Code名为%CTX,注:Function Code名一定要为%CTX,否则不会触发右键菜单),然后再激活它,最后并使用SET PF-STATUS将这个分配并激活了右键的Status分配给LIST Screen,这样才能使用右键。其分配与激活过程如下:
1、 在Dialog Status for Lists章节中创建的Status基础上,分配右键Context Menu:
2、 再激活Function Code:%CTX
3、 在列表屏幕的行上右击,则会触发右键菜单并回调以下形式的Form:
FORM on_ctmenu_request USING l_menu TYPE REF TO cl_ctmenu.
在该Form中可以通过cl_ctmenu静态的加载(加载静态的右键菜单请参考Context Menus for Screen中的代码示例)已设计好的Context Menu或动态的新增或删除菜单项。
REPORT demo_list_context_menu.
DATA: wa_spfli TYPE spfli,
wa_sflight TYPE sflight.
START-OF-SELECTION.
"先为LIST屏幕设置Status,该设置的Status中就已经将
"右键Function code(%CTX)激活了,所以在列表行上右击
"时就会触发右键菜单
SET PF-STATUS 'BASIC'.
SELECT * FROM spfli INTO wa_spfli.
WRITE: / wa_spfli-carrid,
wa_spfli-connid,
wa_spfli-cityfrom,
wa_spfli-cityto.
"HIDE的使用:将List屏幕上的每一行中指定的列的内容存储到当前层次级别的
"Hide area(即列表缓冲区,每行都会对应有一个这样的缓冲区)
"中,使用该语句就是将列表行中指定的列的内容存储到每一层次级别LIST所对应的
"专用的隐藏区域中
HIDE: wa_spfli-carrid, wa_spfli-connid.
ENDSELECT.
CLEAR wa_spfli.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'DETAIL'.
* CHECK NOT wa_spfli IS INITIAL.
"sy-lisel即选中列表行的内容
WRITE sy-lisel COLOR COL_HEADING.
SELECT * FROM sflight INTO wa_sflight
"wa_spfli-carrid与wa_spfli-connid都是从Hide area中读取得到的
WHERE carrid = wa_spfli-carrid
AND connid = wa_spfli-connid.
WRITE / wa_sflight-fldate.
ENDSELECT.
CLEAR wa_spfli.
ENDCASE.
FORM on_ctmenu_request USING l_menu TYPE REF TO cl_ctmenu.
DATA lin TYPE i.
"获取光标所在列表行的行号(列表号从1开计号)
GET CURSOR LINE lin.
"如果在是第3(或第3以后的)列表行右击,并且列表层次是在第0层上时
IF lin > 2 AND sy-lsind = 0.
CALL METHOD l_menu->add_function
EXPORTING
fcode = 'DETAIL'"自定义Function code,需自己在AT USER-COMMAND中实现相关功能
text = text-001.
ENDIF.
CALL METHOD l_menu->add_function
EXPORTING
fcode = 'BACK'"BACK为系统提供的预置Function Code,它不会触发AT USER-COMMAND
"已被runtime environment拦截被进行处理了
text = text-002.
ENDFORM.
以框模窗口显示LIST
你可以WINDOW语句以对话框方式来显示当前(indexSY-LSIND)的LIST,而不是全屏显示(屏幕与选择屏幕的对话框模式请参考Call屏幕、Call选择屏幕):
WINDOW STARTING AT <left><upper> [ENDING AT <right><lower>].
<left><upper>分别指定了窗口最左上角所在的column 与 line。如果<upper>为0,将会全屏显示。
<right><lower>分别指定了窗口最右下角所在的column 与 line。通常默认值为事件所发生的窗体的右小角所在的位置。
如果窗体指定的宽度小于了预置的列表LIST的宽度,则会出一水平滚动条。为了防止出现滚动条,可以使用下的语句来调LIST列表的输出宽度:
NEW-PAGE LINE-SIZE<width>.
注:WINDOW语句只能用于detaillists,也就是说只能出现在上面Standard List Status及事件所提到的三种交互式事件里。
Dialog boxes(包括对话框模式的LIST、对话模式模式的屏幕与选择屏幕)都是没有menu bar与standard toolbar标准工具栏,并且application toolbar会显示在LIST对话框的左下角下面,这是R/3系统的公共特点。
如果对话框模式的LIST屏幕没有设置status,但在程序中定义了AT LINE-SELECTION 或者 AT PF<nn>事件处理块,则系统会为dialog box使用standard list status 。
如果需要为dialog box类型的LIST定义一个Status,则在创建时需要在Menu Painter中选择Dialog box类型的Status:
这种类型的Status系统不会为我们提供menu bar 与 standard toolbar,但在application toolbar中系统提供了预置的Function Code PRI, %SC, %SC+, and RW允许用户打印、搜索、关闭窗口。
REPORT demo_list_window NO STANDARD PAGE HEADING.
START-OF-SELECTION.
"设置Basic List的Status
SET PF-STATUS 'BASIC'.
WRITE 'Select line for a demonstration of windows'.
AT USER-COMMAND.
CASE sy-ucomm.
"F2所默认对应PICK已被SELE替换,当按F2或在某行上双击或者点击
"Select按钮时触发AT USER-COMMAND事件
WHEN 'SELE'.
IF sy-lsind = 1.
"设置第1个Detail List的Status
SET PF-STATUS 'DIALOG'.
SET TITLEBAR 'WI1'.
"以窗口形式显示Detail List
WINDOW STARTING AT 5 3 ENDING AT 40 10.
WRITE 'Select line for a second window'.
ELSEIF sy-lsind = 2.
"设置第2个Detail List的Status
SET PF-STATUS 'DIALOG' EXCLUDING 'SELE'.
SET TITLEBAR 'WI2'.
WINDOW STARTING AT 45 10 ENDING AT 60 12.
WRITE 'Last window'.
ENDIF.
ENDCASE.
列表屏幕上的数据与程序间的传递
通过系统变量传递
列表交互事件触发时会填充系统变量
REPORT demo_list_system_fields NO STANDARD PAGE HEADING
LINE-COUNT 12 LINE-SIZE 40.
DO 100 TIMES.
WRITE: / 'Loop Pass:', sy-index.
ENDDO.
"只有在创建Basic List时才触发
TOP-OF-PAGE.
WRITE: 'Basic List, Page', sy-pagno.
ULINE.
"该事件只在创建Detail List时触发
TOP-OF-PAGE DURING LINE-SELECTION.
WRITE 'Secondary List'.
ULINE.
AT LINE-SELECTION.
WRITE: 'SY-LSIND:', sy-lsind,"当前事件所创建的列表层级(下一列表)
/ 'SY-LISTI:', sy-listi,"事件触发所在列表层级(当前列表)
/ 'SY-LILLI:', sy-lilli,"事件触发所在列表行的绝对行号(指整个输出列表的行号,不管是否有分页,且空行不算)
/ 'SY-CUROW:', sy-curow,"事件触发所在窗体中的行号(这是相对于窗体来说的,指触发事件的行在当前窗体中的
"行号,并且格也算,注:每个窗体的行号只与窗口的大小有关,且窗口大小定以后,其能显示的最大行数也就固定了)
/ 'SY-CUCOL:', sy-cucol,"事件触发所在窗体中的行SY-CUROW中的列位置
/ 'SY-CPAGE:', sy-cpage,"事件触发所在列表中显示的第一页的页号
/ 'SY-STARO:', sy-staro,"事件触发所在列表中显示的第一页的第一行的行号
/ 'SY-LISEL:',sy-lisel. "事件触发所在列表行的内容
使用SY-LISEL
SY-LISEL类型为C(255),SY-LISEL存储的内容最大只有255(好像一行最多也只能输出255,请参考LINE-SIZE),所以每行输出长度不应该超过这个数,并且它是将整行的内容都存储下来了,所以要取得每个Field,则需要通过offset方式来截取。所以SY-LISEL只适合于取 List的Header行内容,以及检测是否选中了行,
REPORT demo_list_sy_lisel NO STANDARD PAGE HEADING.
DATA num TYPE i.
SKIP.
"平方标题,在第4行输出。当双击此行时,会求1到100各数的平方数
WRITE 'List of Quadratic Numbers between One and Hundred'.
SKIP.
"立方标题,在第6行输出。当双击此行时,会求1到100各数的立方数
WRITE 'List of Cubic Numbers between One and Hundred'.
TOP-OF-PAGE.
WRITE 'Choose a line!'.
ULINE.
TOP-OF-PAGE DURING LINE-SELECTION.
"适合于将选中的行内容做为头信息
WRITE sy-lisel.
ULINE.
AT LINE-SELECTION.
"如果选中行前四位字符串是 List 时
IF sy-lisel(4) = 'List'.
CASE sy-lilli.
WHEN 4."如果选中的是第4行时
DO 100 TIMES.
num = sy-index ** 2.
WRITE: / sy-index, num.
ENDDO.
WHEN 6."如果选中的是第6行时
DO 100 TIMES.
num = sy-index ** 3.
WRITE: / sy-index, num.
ENDDO.
ENDCASE.
ENDIF.
通过程序语句
当存储的信息不是整行时,而是某些输出列时,可以使用这些技术。
HIDE (列表隐藏域)
HIDE <f>.
变量<f>可以是整行或部分输出列的变量,甚至是其他与行内容无关的变量。
当单击(即光标所在的行)List屏幕中的行时,如果对应的行设置了隐藏字段,则HIDE隐藏字段变量<f>会自动被存储值填充;当然也可以使用READ LINE来手动控制读取填充。
注:局部变量不能存储到HIDE区域中
示例请参考右键菜单 for Lists中的代码示例
为了在AT LINE-SELECTION事件的处理块中能够访问到用户在上层列表屏幕上选中的行的主键,应该在上一级列表屏幕每一条记录被显示的时候将该记录的主键保存到隐藏域中,这样当用户在上层屏幕上选中一行的时候,该行保存在隐藏域中的主键在AT LINE-SELECTION事件块中将自动有效:
HIDE : wa_spfli-carrid,
wa_spfli-connid.
而且该保存语句要在本行数据被WRITE语句输出到列表缓冲区后在后面紧着编写(为了隐藏的数据与当前输入的数据一致)。这样当被双击时,保存在隐藏域中的字段的值将自动被传回到原始字段中:
隐藏域是一个内表,其行结构包含了三个字段:被选中行的行号、字段名、字段值,当保存数据时,每个被保存的字段在隐藏域中形成一行。
DATA : wa_spfli TYPE spfli, wa_sflight TYPE sflight.
WRITE : AT 0(8) '航空公司',
AT 10(10) '航线编码', AT 20(10) '出发城市' ,
AT 35(10) '到达城市', AT 50(10) '起飞时间'.
ULINE.
START-OF-SELECTION.
SELECT * FROM spfli
INTO CORRESPONDING FIELDS OF wa_spfli.
WRITE : / wa_spfli-carrid UNDER '航空公司', wa_spfli-connid UNDER '航线编码',
wa_spfli-cityfrom UNDER '出发城市', wa_spfli-cityto UNDER '到达城市' ,
wa_spfli-deptime UNDER '起飞时间' .
HIDE : wa_spfli-carrid,
wa_spfli-connid.
ENDSELECT.
AT LINE-SELECTION.
CASE sy-lsind.
WHEN 1.
WRITE : '航空公司','航线编码','航班日期',
AT 30(10) '最大座位', AT 45(10) '已定座位'.
ULINE.
SELECT SINGLE * FROM sflight
INTO CORRESPONDING FIELDS OF wa_sflight
WHERE carrid = wa_spfli-carrid AND connid = wa_spfli-connid.
WRITE : / wa_sflight-carrid UNDER '航空公司', wa_sflight-connid UNDER '航线编码',
wa_sflight-fldate UNDER '航班日期',wa_sflight-seatsmax UNDER '最大座位',
wa_sflight-seatsocc UNDER '已定座位'.
WHEN 2.
WRITE : / 'currrent list leve is : ' , sy-lsind.
ENDCASE.
READ LINE
[FIELD VALUE <f1>[INTO <g1>]...<fn>[INTO <gn>]] [OF CURRENT PAGE|OF PAGE <p>].
该语句会将事件触发所在的List((index SY-LILLI))中的第<lin>行的内容存储到SY-LISEL系统变量中,并且随之将<lin>行所对应的HIDE字段也进行相应填充。另外会将Write显示输出的名为<fn>字段的值存储到<gn>全局变量中(如果加上了<gn>的前提下,否则会默认存储到名为<fn>的变量中去)。如果读取的行存在,则SY-SUBRC to 0, otherwise to 4
? INDEX<idx>
从<idx>级别的LIST屏幕上读取行
? FIELD VALUE<f1>[INTO<g1>]... <fn>[INTO<gn>]
系统会将输出字段<f1>的值以字符串的存储到同名的变量中,或者当使用INTO时,存储到<g1>变量中。在读取与存储的过程中会应用到相应的转换规则。
行中使用HIDE语句输出的字段不能使用该选项来显示读取(但在使用READ LINE语句会,HIDE字段会自动的被读取填充)。如果某个字段多个显示输出,则系统只会读取第一个。
此选项读取出来的值是Write语句的格式化后的值,即会保留格式字符:
DATA: p(8) TYPE p DECIMALS 3 VALUE '123456.78',
c(8) VALUE '123'.
DATA: PC(16),CC(10).
START-OF-SELECTION.
WRITE:/ p , c RIGHT-JUSTIFIED.
END-OF-SELECTION.
CLEAR: p,c.
READ LINE 3 FIELD VALUE p INTO PC c INTO CC.
? OFCURRENTPAGE
此时的<lin>不是整个列表(包括所有分页)中的行号,而是SY-CPAGE页面中的行号
? OFPAGE<p>
此时的<lin>不是整个列表(包括所有分页)中的行号,而是<p>页面中的行号
TABLES: sflight.
DATA: box(1) TYPE c, lines TYPE i, free TYPE i.
DATA: linno TYPE sy-linno.
START-OF-SELECTION.
SET PF-STATUS 'CHECK'.
SELECT * FROM sflight UP TO 10 ROWS .
WRITE: / box AS CHECKBOX, sflight-fldate.
linno = sy-linno.
"linno是为了测试后面的READ LINE加上的输出字段
HIDE: sflight-fldate, sflight-carrid, sflight-connid,
sflight-seatsmax, sflight-seatsocc,linno.
ENDSELECT.
END-OF-SELECTION.
"Basic List 输出行数(包括Page Header在内两行),sy-linno为当
"前输出的行位置,当Write之后,为了下一个Write输出,sy-linno会自
"动加1,所以屏幕上已输出的实际行数需在sy-linno基础上减1
lines = sy-linno - 1.
TOP-OF-PAGE.
WRITE: 'List of flight dates'.
ULINE.
TOP-OF-PAGE DURING LINE-SELECTION.
"sflight-fldate从Hide区域中读取
WRITE: 'Date:', sflight-fldate.
ULINE.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'READ'.
box = space.
SET PF-STATUS 'CHECK' EXCLUDING 'READ'.
DO lines TIMES.
"该语句会将屏幕上的第sy-index行的数据读取出来,并自动将整行的内容
"存储到SY-LISEL中,同时也会自动去填充对应行的Hide字段(包括本行
"无关的字段,如这里的linno字段,即只要是存储到了Hide区域的变量都
"会被自动的填充);并且在该语句中还会单独读取Write语句中显示输出的box字
"段的值并存储到box全局变量中(可以通过调试观察box全局变量的值的变化)
READ LINE sy-index FIELD VALUE box.
IF box = 'X'."如果选中后
"sflight-seatsmax、sflight-seatsocc从HIDE区域中读取,它们的值是在
"前面的READ LINE之后自动填充
free = sflight-seatsmax - sflight-seatsocc.
IF free > 0.
NEW-PAGE."换页输出
"sflight-carrid、sflight-connid从HIDE区域中读取,它们的值是在
"前面的READ LINE之后自动填充
WRITE: 'Company:', sflight-carrid,
'Connection: ',sflight-connid,
/ 'Number of free seats:', free.
ENDIF.
ENDIF.
ENDDO.
ENDCASE.
GET CURSOR FIELD、GET CURSOR LINE
GET CURSOR FIELD <f> [OFFSET <off>] [LINE <lin>] [VALUE <val>] [LENGTH <len>].
将光标所在字段名存储到变量<f>中。如果光标在字段上,则SY-SUBRC to 0, otherwise to 4
输出字段名可能是全局变量、常量、字段符号、或Form的引用参数,<f>存储的名就是相应名称(字段符号为所分配的变量名,而不是字段符号本身名)。对于字面常量、局部变量或Form的传递值方式的参数,系统会设置SY-SUBRC to 0,并且将<f>设置为SPACE
? OFFSET<off>
<off>存储了光标所在字段里的位置。如果光标在字段的最前,则<off>=0
? LINE<lin>
存储光标所在屏幕中位置行号(SY- LILLI)
? VALUE<val>
存储光标所在字段的内容,包括输出格式字符,取出的类型为字符类型,如果与<val>类型不兼容可能会发生类型转换
? LENGTH<len>
存储光标所在位置字段的输出长度
GET CURSOR LINE <lin> [OFFSET <off>] [VALUE <val>] [LENGTH <len>].
将用户选择的行(光标所在行)的行号存储到变量<lin>中。如果光标在列表行上,则SY-SUBRC to 0,否则为4
? OFFSET<off>
存储光标所在行中的偏移量,如果在行首,则<off> = 0
? VALUE<val>
存储光标所在行的内容,包括格式字符
? LENGTH<len>
存储光标所在行的输出长度
REPORT sapmztst NO STANDARD PAGE HEADING LINE-SIZE 40.
DATA: hotspotn(10) VALUE 'Click me!',
f(10), off TYPE i, lin TYPE i, val(40), len TYPE i.
FIELD-SYMBOLS <fs>.
ASSIGN hotspotn TO <fs>.
WRITE 'Demonstration of GET CURSOR statement'.
SKIP TO LINE 4.
POSITION 20.
WRITE <fs> HOTSPOT COLOR 5 INVERSE ON.
AT LINE-SELECTION.
WINDOW STARTING AT 5 6 ENDING AT 45 20.
GET CURSOR FIELD f OFFSET off LINE lin VALUE val LENGTH len.
WRITE: 'Result of GET CURSOR FIELD: '.
ULINE AT /(28).
WRITE: / 'Field: ', f,"字段符号所对应的名为所分配的变量名,而非字段符号本身
/ 'Offset:', off,
/ 'Line: ', lin,
/ 'Value: ', (10) val,
/ 'Length:', len.
SKIP.
GET CURSOR LINE lin OFFSET off VALUE val LENGTH len.
WRITE: 'Result of GET CURSOR LINE: '.
ULINE AT /(27).
WRITE: / 'Line: ', lin,
/ 'Offset:', off,
/ 'Value: ', val,
/ 'Length:', len.
DESCRIBE LIST获取列表属性
DESCRIBE LIST NUMBER OF LINES|PAGES <n> [INDEX <idx>].
计算指定层次列表屏幕上的总行数或总页数,如果index <idx>的列表不存在,SY-SUBRC为非0
DESCRIBE LIST LINE <lin> PAGE <pag> [INDEX <idx>].
根据指定的<idx>与<lin>来确定指定行所在哪一页,并将页号存储到<pag>中。如果index <idx>的列表不存在,SY-SUBRC为8,如果指定的行<lin>不存在,SY-SUBRC为4
DESCRIBE LIST PAGE <pag> [INDEX <idx>] [<options>]
取回<idx>级LIST屏幕中<pag>页的属性,具体取哪些属性由<options>选项决定。如果index <idx>级的列表不存在,SY-SUBRC为8,如果指定的页<pag>不存在,SY-SUBRC为4
<options>如下:
? LINE-SIZE<col>
将指定的页面宽度存储到<col>变量中
? LINE-COUNT<len>
将指定的页面宽度允许的最大行数到<len>变量中,即程序开头或者NEW-PAGE 所带选项LINE-COUNT的值
? LINES<lin>
将指定的页面中当前已输出的行数存储到<lin>变量中,所以<lin>只可能小于或等于上面的LINE-COUNT<len>
? FIRST-LINE<lin1>
将指定页面的第一行(如果有Header,则指Header的第一行)所在绝对行号(SY-LILLI)存储到<lin1>变量中
? TOP-LINES<top>
将指定页面的header行数存储到<top>变量中,包括standard page header与TOP-OF-PAGE里所输出的总行数。
? TITLE-LINES<ttl>
将standard page header的行数存储到<ttl>变量中,standard page header不包括TOP-OF-PAGE里输出的头所占行数,如果在程序开头REPORT语句后加上NO STANDARD PAGE HEADING选项后,则<ttl>为0
? HEAD-LINES<head>
将standardpageheader部分中的columnheader行数存储到<head>变量中(最多为5,其中包括了一条下划线)
? END-LINES<end>
将pagefooterlines行数存储到<end>变量中
REPORT demo_list_describe_list LINE-SIZE 40 LINE-COUNT 11(1) .
DATA: lin TYPE i, pag TYPE i,
col TYPE i, len TYPE i, lin1 TYPE i,
top TYPE i, tit TYPE i, head TYPE i, end TYPE i.
DO 3 TIMES.
WRITE / sy-index.
ENDDO.
TOP-OF-PAGE.
WRITE 'Demonstration of DESCRIBE LIST statement'.
ULINE.
END-OF-PAGE.
ULINE.
AT LINE-SELECTION.
NEW-PAGE LINE-COUNT 0.
WINDOW STARTING AT 1 13 ENDING AT 40 25.
DESCRIBE LIST: NUMBER OF LINES lin INDEX 0,
NUMBER OF PAGES pag INDEX 0.
WRITE: 'Results of DESCRIBE LIST: '.
ULINE AT /(25).
WRITE: / '输出的总行数: ', lin,
/ '输出的总页数: ', pag.
SKIP.
DESCRIBE LIST LINE sy-lilli PAGE pag INDEX 0.
WRITE: / '选择的行绝对行号:',(2) sy-lilli NO-GAP, ',该行所在的页号:',(1) pag NO-GAP.
SKIP.
DESCRIBE LIST PAGE pag INDEX 0 LINE-SIZE col
LINE-COUNT len
LINES lin
FIRST-LINE lin1
TOP-LINES top
TITLE-LINES tit
HEAD-LINES head
END-LINES end.
WRITE: '第' NO-GAP, (1) pag NO-GAP,'的页面属性如下:' NO-GAP,
/ '页面行宽度: ', 25 col,
/ '页面允许最大行数: ', 25 len,
/ '当前页上已输出的总行数: ', 25 lin,
/ '当前页第一行的绝对行号: ', 25 lin1,
/ '页面头所占行数: ', 25 top,
/ '标准头所占行数: ',25 tit,
/ 'column header行数:', 25 head,
/ '页脚所占行数:',25 end.
操控 Detail Lists
Scrolling in Detail Lists
可以使用SCROLL语句来滚动列表,章节滚动列表描述了basic lists中如何滚动。
当你在detail lists使用SCROLL语句,需要注意以下几点:
? 应该在所有输出语句之后,再使用该语句
? 如果没有指定INDEX选项,在创建下一个新secondarylist时,其滚动是针对当前事件触发所在列表屏幕的(indexSY-LISTI),而不是新创建的下一LIST(SY-LSIND)
? 仅只有当创建basiclist时,SCROLL是针对当前正在创建的LIST
? 你可以使用INDEX选项明确的指定SCROLL作用于哪个LIST。在SCROLL之前是不需要先显示的,可以先滚动,在显示时会滚动条会自动定位到相应位置。如果指定的INDEX列表不存在,则SY-SUBRC为8
具体示例请参考滚动列表章节的示例
也可以使用通过程序触发事件
设置光标位置
SETCURSOR语句只会地最近的已创建的列表(most recently-createdlist)起作用:当创建basiclist时,该语句是针对basiclist;对于正创建的detaillist,则针对以前list的(indexSY-LISTI)。
">SET CURSOR <col><lin>
SET CURSOR <col><lin>.
定位到输出窗口中的<lin>行的<col>(窗口中显示的第一列的<col>值为是1,即使水平滚动后也是这样)列。系统只能将光标定准到窗体中可见的位置上,在显示以外的区域,该语句不起作用。如果要定位目前没有显示出来的区域,则需要先SCROLL列表。
不能将光标定准到使用SKIP语句输出的空行上,但输出的下划线可以。如果<lin>超出来最大行,则会将光标定准到最后一行上面
注:<col>是基于当前窗口显示列,而不是整个LIST Page,即已被隐藏的列不会被计算到<col>里,如下图中使用SET CURSOR 11 3语句,使光标所在位置为第3行11列,即在第一个文本输出框中:
当手动水平滚动到如下的位置后,再使用SET CURSOR 11 3.语句,还是会发生水平滚动,所以<col>是对于显示出的列来算的,即窗口显示第一列的<col>值永远为1:
示例:
REPORT demo_list_set_cursor_1 NO STANDARD PAGE HEADING LINE-SIZE 80.
SET PF-STATUS 'SCROLLING'.
NEW-LINE NO-SCROLLING.
WRITE 'Input Fields:'.
NEW-LINE NO-SCROLLING.
WRITE '-------------'.
NEW-LINE.
DO 5 TIMES.
WRITE: ' Input', (1) sy-index, ' ' INPUT ON NO-GAP.
ENDDO.
SET CURSOR 11 3.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'LEFT'.
IF sy-staco > 1.
SCROLL LIST LEFT BY 12 PLACES.
ENDIF.
WHEN 'RGHT'.
IF sy-staco < 40.
SCROLL LIST RIGHT BY 12 PLACES.
ENDIF.
ENDCASE.
"再次向右滚动到11列前
SET CURSOR 11 3.
SET CURSOR FIELD
SET CURSOR FIELD <f> LINE <lin> [OFFSET <off>].
将光标定位到行<lin>中名为<f>的列上,如果有多个这样的列,则定位到第一个字段上面。如果字段不存在或者字段不在显示区,则忽略该语句,但可以先通过SCROLL语句将它滚动到显示区再定位
使用OFFSET将光标定位到字段的<off>位置,<off> = 0表示字段的列(注:隐藏的部分也算)
注:<lin>是包括Header行在内在的
SET CURSOR LINE
SET CURSOR LINE <lin> [OFFSET <off>].
将光标定准到行<lin>。如果指定的行不存在或不在显示区,则会忽略此语句,可以先通过SCROLL语句滚动到显示区再定位
使用OFFSET选项会将光标定位到<lin>行中的<off>位置(<off>指整行的计数,隐藏的部分也算。<off> = 0表示行的第一列)
REPORT yjzj_list_set_cursor_3 LINE-SIZE 30 NO STANDARD PAGE HEADING.
DATA: inp(2) TYPE c, top(2) TYPE c.
SET PF-STATUS 'LINE_NUMBER'.
DO 50 TIMES.
WRITE: / 'Line ', (2) sy-index.
ENDDO.
TOP-OF-PAGE.
WRITE: 'Line number:', inp INPUT ON.
ULINE.
SKIP.
AT USER-COMMAND.
"header 的行数
DESCRIBE LIST PAGE 1 TOP-LINES top.
CASE sy-ucomm.
WHEN 'LINE'.
"文本框中输入的要跳转换的行号
READ LINE 1 FIELD VALUE inp.
"先滚动
SCROLL LIST TO PAGE 1 LINE inp.
inp = inp + top.
"再定位
SET CURSOR LINE inp OFFSET 6.
ENDCASE.
修改列表行(内容及输出格式)
有以下两种方式来修改LIST SCREEN中的行,
第一种是明确指定要修改的行:
MODIFY LINE <n> [INDEX <idx>|OF CURRENT PAGE|OF PAGE <p>] [<modifications>].
如果没有 [INDEX <idx>|OF CURRENT PAGE|OF PAGE <p>] 选项,则修改的行为事件发生的当前LIST SCREEN(index SY-LISTI)中的<n>行,如果附加了此选项,则各选项的意思如下:
–INDEX <idx>选项指定了索引级别为<idx>中的第<n>
–OF CURRENT PAGE选项指定了当前显示page (page number SY-CPAGE)中的第<n>行
–PAGE <p>选项指定了page <p>的line <n>
第二种是针对最近一次读取到的行:
MODIFY CURRENT LINE [<modifications>].
该语句修改的是针对最近一次选择(F2)的行或使用READLINE语句读取到的行
如果没有<modifications>选项,则上面两个语句会直接使用系统变量SY- LISEL的内容来修改指定行的内容,并且行所对应的HIDE area也将被重写。如果指定的行修改成功,则SY-SUBRC to 0,否则为非0
选项<modifications>可以为以下几种:
修改行的格式
... LINE FORMAT <option1><option2>...
该选将修改整个行的输出格式,其具体的格式请参着FORMAT语句
REPORT demo_list_modify_line_format LINE-SIZE 40
NO STANDARD PAGE HEADING.
DATA c TYPE i VALUE 1.
WRITE 'Select line to modify the background'.
AT LINE-SELECTION.
IF c = 8.
c = 0.
ENDIF.
MODIFY CURRENT LINE LINE FORMAT COLOR = c.
ADD 1 TO c.
修改字段的内容
... FIELD VALUE <f1>[FROM <g1>]<f2>[FROM <g2>]...
该语句的作用是先使用<f1>或者是<g1>变量的值来修改SY-LISEL系统变量中<f1>字段所在位置的值,然后再使用SY-LISEL系统变量的内容来进行整行内容的修改(这样就确保了显示的行与SY-LISEL内容一致)。如果有必要会在修改前会将值转换为C类型。如果<f1>在行中多次出现,则仅修改第一个,如果不存在,则系统会忽略该选项
REPORT demo_list_modify_field_value LINE-SIZE 40
NO STANDARD PAGE HEADING.
DATA c TYPE i.
WRITE: ' Number of selections:', (2) c.
AT LINE-SELECTION.
ADD 1 TO c.
sy-lisel(1) = '*'.
"24的位置刚好是 c 变量输出的位置,虽然这里进行了
"修改,但由于下面的MODIFY中的 FIELD VALUE选项给
"重新覆盖了
sy-lisel+24(1) = '*'.
MODIFY CURRENT LINE FIELD VALUE c.
修改字段的格式
... FIELD FORMAT <f1><options1><f2><options2>...
该选将针对行中的某个字段的的输出格式进行修改,其具体的格式请参着FORMAT语句
选项将覆盖LINE FORMAT选项指定的相应字段的输出格式。如果<f1>在行中多次出现,则仅修改第一个,如果不存在,则系统会忽略该选项
REPORT yjzj_list_modify_field_format NO STANDARD PAGE HEADING.
DATA: box(1) TYPE c, lines TYPE i, num(1) TYPE c.
SET PF-STATUS 'CHECK'.
DO 5 TIMES.
num = sy-index.
WRITE: / box AS CHECKBOX, 'Line', num.
HIDE: box, num.
ENDDO.
"屏幕上输出的总行数
lines = sy-linno.
TOP-OF-PAGE.
WRITE 'Select some checkboxes'.
ULINE.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'READ'.
SET PF-STATUS 'CHECK' EXCLUDING 'READ'.
box = space.
DO lines TIMES.
"读取屏幕上指定行中box字段的内容
READ LINE sy-index FIELD VALUE box.
IF box = 'X'.
WRITE: / 'Line', num, 'was selected'.
box = space.
MODIFY LINE sy-index
FIELD VALUE box
FIELD FORMAT box INPUT OFF
num COLOR 6 .
ENDIF.
ENDDO.
ENDCASE.
Lists 与 Screens之间的切换
当你运行一个可执行程序时,list processor(列表处理器)会在最后一个processing block(事件处理块END-OF- SELECTION)结束时自动的运行,与此同时basic list将会被创建并显示
如果你想在screen processing(屏幕处理)过程中显示 lists,你必须对此进行编程控制,相反,你也可以编程从list processing(列表处理)切换到screen processing(屏幕处理)
从Screen Processing 屏幕处理切换到Lists列表输出
CALL SCREEN屏幕调用或Tcode都会开启screen sequence,系统会进行Screen processing。在Screen processing期间,ABAP program将会被dialog processor(对话处理器)接管控制,screen flow logic中调用的PBO 与 PAI modules将会被执行
为了将dialog processor控制权转交给list processor,你可在PBO dialog modules 中调用下面这样的语句:
LEAVE TO LIST-PROCESSING [AND RETURN TO SCREEN<nnnn>].
该语句可以使用在PBO 或者PAI event中。它的作用是在当前屏幕的PAI processing(一般在PBO块里使用SUPPRESSDIALOG.或LEAVESCREEN.语句后不会显示这个屏幕,此时在PBO事件块结束后立即显示Basic List)结束后开始list processor并显示Basic List。调用该语句所在屏幕的PBO and PAI modules中的list output(列表输出)都会被输出到Basic List中缓存起来,待该屏幕处理完后(如果没有使用SUPPRESSDIALOG.或LEAVE SCREEN.语句,则在PAI结束后显示;如果使用了这两个语句,则会在PBO块结束后就会显示)。如果在ABAP Program事件块AT LINE-SELECTION,AT USER-COMMAND中输出了内容,将会产生更多层次的detail lists
可以使用以下两种方式来离开list processing:
? 在basiclist中点击Back,Exit,orCancel
? 在list processing程序中使用:LEAVE LIST-PROCESSING.
以上两种方式都会使控制权从list processor转交到dialog processor。
在默认的情况下,不带AND RETURN TO SCREEN选项的LEAVE TO LIST-PROCESSING语句在当list processor 处理结束后(如关闭list列表输出窗体时),dialog processor将会又会返回到LEAVE TO LIST-PROCESSING语句调用所在屏幕的PBO事件块,并重新执行PBO事件块(所以这样使用会出现死循环:关不掉List列表输出窗口);选项AND RETURN TO SCREEN允许你指定当前屏幕序列(即LEAVE TO LIST-PROCESSING语句调用所在屏幕所在的屏幕序列)中的某个屏幕,当list processor 处理结束后,dialog processor将会回到指定的屏幕并执行其相应的PBO事件块;另外可以通过下面语句:
LEAVE TO LIST-PROCESSING AND RETURN TO SCREEN 0.
来实现当结束List列表输出窗口时,结束当前屏幕序列(即LEAVE TO LIST-PROCESSING语句调用所在屏幕所在的屏幕序列),并返回到该屏幕序列启动(调用)的地方(如果是该屏幕序列是在屏幕的PAI事件块里的某个MODULE里开启的,则返回到PAI事件块里的相应MODULE调用语句处,并继续后面的PAI事件块语句的执行;如果该屏幕序列是在ABAPProgram中开启的,则会回到 ABAP程序中call screen语句处,继续执行后面程序):
如果你想在screen processing屏幕处理中显示 lists 列表输出屏幕时(即从Screen 切换到 List 屏幕输出时),你需要为这个将要输出的Basic List 屏幕设计一个单独的空的Screen,并在这个空的屏幕(不需要PAI事件块)的PBO事件块中某个module里创建(使用上面语句LEAVE TO LIST-PROCESSING来创建)与显示Basic List,这个空屏幕可以使用CALL SCREEN语句在任何想调用的地方进行调用即可。
实际上,如果在PBO MODULE里没有使用 SUPPRESS DIALOG. 或 LEAVE SCREEN. 语句时,调用LEAVE TO LIST-PROCESSING语句的屏幕还是会显示出来,这时我们只需要在该屏幕上触发一下Function code即可(回车、双击都可)切换到由该屏幕PBO MODULE生成的Basic List,并显示Basic List。
只要切换到了List输出屏幕,则可以对List屏幕进行相关事件的编程
从屏幕生成的Basic List的SY-DYNNR还是为当前屏幕的屏幕号,不过所有的Detail List的屏幕号都为 120
REPORT demo_leave_to_list_processing.
TABLES demo_conn.
DATA: wa_spfli TYPE spfli,
flightdate TYPE sflight-fldate.
CALL SCREEN 100.
MODULE status_0100 OUTPUT.
SET PF-STATUS 'SCREEN_100'.
ENDMODULE. "status_0100 OUTPUT
MODULE cancel INPUT.
LEAVE PROGRAM.
ENDMODULE. "cancel INPUT
MODULE user_command_0100.
CALL SCREEN 500.
"如果这里不设置为100,就会结束整个程序,因为当前100屏幕的
"Next屏幕默认为0,所以如果不重新指定下一屏幕,则会结束屏幕
"序列,而该程序没有选择屏幕,所以最后会导致结束整个屏幕
"这里设置的100的意思是:当前屏幕(100)的下一屏幕还是100,
"当PAI事件块结束后,会又去调用100屏幕的PBO事件
"当前也可以注掉此名,而像下面那样将LEAVE TO LIST-PROCESSING
"语句的返回屏幕设置为 100 也可
SET SCREEN 100.
ENDMODULE. "user_command_0100
MODULE call_list_500 OUTPUT.
"意思是:当生成Basic List结束之后,返回到100屏幕并触发其PBO事件
* LEAVE TO LIST-PROCESSING AND RETURN TO SCREEN 100.
LEAVE TO LIST-PROCESSING AND RETURN TO SCREEN 0.
SET PF-STATUS space.
SUPPRESS DIALOG.
SELECT carrid connid cityfrom cityto
FROM spfli
INTO CORRESPONDING FIELDS OF wa_spfli
WHERE carrid = demo_conn-carrid.
WRITE: / wa_spfli-carrid, wa_spfli-connid,
wa_spfli-cityfrom, wa_spfli-cityto.
HIDE: wa_spfli-carrid, wa_spfli-connid.
ENDSELECT.
CLEAR: wa_spfli-carrid.
ENDMODULE. "call_list_500 OUTPUT
TOP-OF-PAGE.
WRITE text-001 COLOR COL_HEADING.
ULINE.
TOP-OF-PAGE DURING LINE-SELECTION.
WRITE sy-lisel COLOR COL_HEADING.
ULINE.
AT LINE-SELECTION.
CHECK NOT wa_spfli-carrid IS INITIAL.
SELECT fldate
FROM sflight
INTO flightdate
WHERE carrid = wa_spfli-carrid AND
connid = wa_spfli-connid.
WRITE / flightdate.
ENDSELECT.
CLEAR: wa_spfli-carrid.
从List Processing 中调用SCREEN
从list processing处理过程中调用Screen:
CALL SCREEN <nnnn>.
该语句会将生成一个screen sequence,并将它穿插到list processing处理过程中,list processor会将控制权交给dialog processor,并且直到dialog processor处理screen sequence中的Screen 0才会结束dialog processor,并再次将控制权从dialog processor转交到list processing,继续执行CALL SCREEN后面的语句
示例请参考:demo_call_screen_from_list
LIST 打印输出
在默认情况下,系统会将list(basic list and secondary lists)输出到screen。该章节将讨论将lists发送到SAP spool system来代替output screen。
一般来讲,我们将list发送到SAP spool system叫 'printing lists'(打印列表),也就是说,不需要真正的将list输出到打印机上,而是使用spool system临时存储。
ABAP提供了两种打印list方式:标准打印与编程控制打印输出。
在使用系统提供的标准打印时,有时不能立即输出打印内容,可以像下面这样来设置立即打印与默认打印设备:
使用系统提供的标准打印
使用标准打印会出现以下几个问题:
? 如果list屏幕输出中,某逻辑页行数超过了实际打印输出的页大小时,会将这一逻辑页分成多(具体多个与打印输出设备有关)个物理页面,系统会将每个物理页面的pageheader设置成该逻辑页的pageheader,这时如果page header含有页号(SY-PAGNO)是时,就会有问题:页号不会自动更新
REPORT XXX NO STANDARD PAGE HEADING LINE-SIZE 60 .
TOP-OF-PAGE.
WRITE: sy-title, 20 'SY-LINSZ:', sy-linsz, 40 'Page', sy-pagno.
ULINE.
START-OF-SELECTION.
DO 64 TIMES.
WRITE / sy-index.
ENDDO.
? 如果REPORT或者NEW-PAGE语句中使用了LINE-COUNT,且指定的页面大小超过了实际输出设置的页面大小时,超出的部分不会打印出来(这与未设置LINE-COUNT时超出部分会打印到下一个物理页面是不一样的):
REPORT XXX NO STANDARD PAGE HEADING LINE-SIZE 60 LINE-COUNT 166(2) .
TOP-OF-PAGE.
WRITE: sy-title, 20 'SY-LINSZ:', sy-linsz, 40 'Page', sy-pagno.
ULINE.
END-OF-PAGE.
ULINE.
WRITE: /30 'Page', sy-pagno.
START-OF-SELECTION.
DO 163 TIMES.
WRITE / sy-index.
ENDDO.
打印时,会弹出警告框:
? 如果REPORT语句中设置的行LINE-SIZE大于打印设备的最大行宽时,超出的部分是不会打印出来的
? 使用标准的打印时,不能写printcontrol语句来控制打印输出
通过编程来控制打印输出
在程序中向打印输出设置输出时,系统会根据打印输出设备来格式化List,系统会根据打印输出设备的格式来确定页宽与页长,物理页满后会自动换页。
在选择屏幕上点击“执行与打印”
SET_PRINT_PARAMETERS函数的各输入参数与弹出的打印输出设置对话框中的参数相对应,打印输出设置对话框中的参数都可以在该函数中进行设置:
REPORT sapmztst NO STANDARD PAGE HEADING LINE-COUNT 0(2).
PARAMETERS p TYPE i.
INITIALIZATION.
通过程序对弹出的打印对话框中的默认值进行设置,在打印时,弹出的打印参数对话框中的参数默认值就是该函数所设置的值。注:该函数与GET_PRINT_PARAMETERS不一样,SET_PRINT_PARAMETERS本身不会弹出打印参数设置对话框,它只是起到设置默认打印参数的一个作用,而通过GET_PRINT_PARAMETERS除可以设置某些打印参数,而且会自动弹出打印参数设置对话框(不过也可以设置不弹出):
CALL FUNCTION 'SET_PRINT_PARAMETERS'
EXPORTING
archive_mode = '3'
copies = '1'
department = 'BASIS'
destination = 'LP01'
expiration = '2'
immediately = 'X'
layout = 'X_65_132'
line_count = 14
line_size = 50
list_name = 'Test'
list_text = 'Test for User''s Guide'
new_list_id = 'X'
receiver = 'CNZHJIA6'
release = ' '
sap_cover_page = ' '.不打印SAP提供的封面页
START-OF-SELECTION.
DO p TIMES.
WRITE / sy-index.
ENDDO.
TOP-OF-PAGE.
WRITE: 'Page', sy-pagno.
ULINE.
END-OF-PAGE.
ULINE.
WRITE: 'End of', sy-pagno.
注:SET_PRINT_PARAMETERS函数只是对弹出的打印参数设置对话框中的默认值有影响,输出的内容是否能得到正确的格式化,还是看你运行程序时,点击的是“执行”按钮还是“执行与打印”按钮(或者在程序中使用NEW-PAGE PRINT ON语句来强制输入到Spool System过程进行格式化,在选择屏幕上点击“执行”时,普通的Write语句会直接输了到List屏幕中,而不管里前面是否调用SET_PRINT_PARAMETERS函数):
1、 点击选择屏幕上工具条中的“执行与打印”按钮,输出的PDF与SAP spool System中的List内容的格式都是已按照程序中设置的打印参数格式化了
2、 如果还是先点击“执行”,然后在输出List屏幕上点击“打印”,虽然此时弹出的打印参数设置对话框中的默认值为程序中SET_PRINT_PARAMETERS函数所设置的值,但此时打印出来的格式是未格式化的内容,这与点击上面执行与打印按钮输出是不一样的,因为此时的输出是直接Write到了屏幕上,然不是输出到了SAP spool System中:
从程序中启动打印
如果想在创建List时启动打印处理,使用带PRINT ON选项的NEW-PAGE语句:
NEW-PAGE PRINT ON[NEW-SECTION] [PARAMETERS<pripar>]
[ARCHIVEPARAMETERS<arcpar>]
[NODIALOG].
该语句后的所有输出不再直接输出到屏幕上,而是输出到spool system中
在已经有过向spool system输出List后,如果不使用NEW-SECTION选项,则NEW-PAGE PRINT ON语句将失效。如果使用了NEW-SECTION选项,将会重置SY-PAGNO系统字段为1。如果系统已经向spool系统中输出过List,则NEW-SECTION选项有如下作用:
? 如果当前List指定的了打印参数(PARAMETERS )中的PRNEW为SPACE,系统不会创建新的spool request
? 如果当前List没有指定打印参数(PARAMETERS )中PRNEW打印参数,或者指定了但不为SPACE时,系统会关掉当前spool reques并重新创建新的spool request
事件结束时会自动的结束打印处理。如果要明确的结束当前List输出到spool system中,则使用以下语句:
NEW-PAGE PRINT OFF.
该语句的作用是结束向spool system中输出,此语句后的输出会直接显示输出到屏幕上显示
REPORT sapmztst NO STANDARD PAGE HEADING.
DATA: val,
pripar LIKE pri_params,
arcpar LIKE arc_params,
lay(16),
lines TYPE i,
rows TYPE i.
"获取用户配置的默认打印设置
CALL FUNCTION 'GET_PRINT_PARAMETERS'
EXPORTING
"弹出打印参数设置对话框,可以修改默认打印参数
no_dialog = ' '
IMPORTING
out_parameters = pripar "接收打印输出参数
out_archive_parameters = arcpar "接收归档参数
valid = val "参数是否有效
EXCEPTIONS
archive_info_not_found = 1
invalid_print_params = 2
invalid_archive_params = 3
OTHERS = 4.
"如果验证通过
IF val <> space AND sy-subrc = 0.
SET PF-STATUS 'PRINT'.
WRITE ' Select a format!'.
ENDIF.
TOP-OF-PAGE DURING LINE-SELECTION.
WRITE: 'Page', sy-pagno.
ULINE.
AT USER-COMMAND.
CASE sy-ucomm.
WHEN 'PORT'.
lay = 'X_65_80'.
lines = 60.
rows = 55.
PERFORM format.
WHEN 'LAND'.
lay = 'X_65_132'.
lines = 60.
rows = 110.
PERFORM format.
ENDCASE.
FORM format.
"以前面获取到的arcpar、pripar为基础,重新构建arcpar、pripar
CALL FUNCTION 'GET_PRINT_PARAMETERS'
EXPORTING
in_archive_parameters = arcpar
in_parameters = pripar
"下面3个参数会重新修改 pripar 中相应的参数
layout = lay
line_count = lines
line_size = rows
no_dialog = 'X'"重新构建打印参数时不用显示对话框
IMPORTING
out_archive_parameters = arcpar "重新构建的arcpar
out_parameters = pripar "重新构建的pripar
valid =
val
EXCEPTIONS
archive_info_not_found = 1
invalid_print_params = 2
invalid_archive_params = 3
OTHERS = 4.
IF val <> space AND sy-subrc = 0.
PERFORM list.
ENDIF.
ENDFORM. "FORMAT
FORM list.
"使用前面获得的 打印输出参数pripar 与 打印归档参数arcpar
"进行打印处理
NEW-PAGE PRINT ON
NEW-SECTION
PARAMETERS pripar
ARCHIVE PARAMETERS arcpar
NO DIALOG."不用显示打印参数设置对应框了,因为前面已经显示
"所以参数都是验证通过的
DO 440 TIMES.
WRITE (3) sy-index.
ENDDO.
ENDFORM.
SUBMIT 程序时打印List
如果将通过SUBMIT语句被调用的reports的输出发送到spool system中,可以使用TO SAP-SPOOL选项:
SUBMIT<rep>TO SAP-SPOOL
[SPOOLPARAMETERS<pripar>]
[ARCHIVEPARAMETERS<arcpar>]
[WITHOUTSPOOLDYNPRO].
如果使用TO SAP-SPOOL选项,List在创建时会使用格式化打印,然后将格式化后的List输出到spool system。SPOOLPARAMETERS与ARCHIVEPARAMETERS参数与NEW-PAGE PRINT ON语句是一样的。如果不想弹出打印参数设置对话框
与Spool对话框:
,可以使用WITHOUT SPOOL DYNPRO选项进行压制。如果使用了WITHOUT SPOOL DYNPRO选项,则不会显示Spool对话框,此时如果想看Spool system中的格式化List,可以选择菜单:
System → Own Spool Requests
REPORT YJZJ_TEST NO STANDARD PAGE HEADING.
DATA: val,
pripar LIKE pri_params,
arcpar LIKE arc_params.
CALL FUNCTION 'GET_PRINT_PARAMETERS'
EXPORTING
layout = 'X_65_132'
line_count = 65
line_size = 132
IMPORTING
out_parameters = pripar
out_archive_parameters = arcpar
valid = val
EXCEPTIONS
archive_info_not_found = 1
invalid_print_params = 2
invalid_archive_params = 3
OTHERS = 4.
IF val <> space AND sy-subrc = 0.
SUBMIT YJZJ_TEST2 TO SAP-SPOOL
SPOOL PARAMETERS pripar
ARCHIVE PARAMETERS arcpar
WITHOUT SPOOL DYNPRO.
ENDIF.
REPORT yjzj_test2.
START-OF-SELECTION.
DO 100 TIMES.
WRITE: sy-index.
ENDDO.
Print Control
SET MARGIN 与 PRINT-CONTROL语句仅对输出到spool系统中的List起作用,对直接显示在屏幕上的List ,并使用List → Print打印是无效的
限定左边距与上边距
SETMARGIN<x>[<y>].
该语句指定了左边与上边的空白,以列(X)和行(Y)为单位。并将X与Y分别存储到SY-MACOL与SY-MAROW系统变量中,在打印输出时,系统会使用这两个系统变量来决定页面的左边距与上边距。该语句对所有后续页面都起作用,除非一下个这样的语句重新设定。
REPORT sapmztst LINE-SIZE 60.
SET MARGIN 10 6.
PARAMETERS: c.
START-OF-SELECTION.
DO 100 TIMES.
WRITE: sy-index.
ENDDO.
PRINT-CONTROL
PRINT-CONTROL { { formats|{FUNCTION code}
[LINE line] [POSITION col] }
| { INDEX-LINE index_line } }.
该语句只对通过NEW-PAGE PRINT ON, SUBMIT TO SAP-SPOOL或者是选择屏幕上的“Execute and Print”按钮创建的print lists起作用,对“Print”打印screen list(已显示在屏幕上的列表)是不起作用。
... formats|{FUNCTION code} [LINE line] [POSITION col]
该选项对从当前Page的line行col列所有后续输出内容的print format进行设置,如果没有设置line与col,则从当前位置(sy-linno, sy-colno)开始格式化,line会存储到sy-linno中,col会存储到sy-colno。
Formats参数选项可以是下表中的列表的选项,在运行时系统会将这些选项转换printer-independent (与打印机无关)的相应code,当List在实际打印时,这些code又会翻译成printer-specific(与打印相关)的control characters:
<formats> |
Code |
Meaning |
CPI<cpi> |
CI<cpi> |
Charactersper inch |
LPI<lpi> |
LI<lpi> |
Linesper inch |
COLORBLACK |
CO001 |
Colorblack |
COLORRED |
CO002 |
Colorred |
COLORBLUE |
CO003 |
Colorblue |
COLORGREEN |
CO004 |
Colorgreen |
COLORYELLOW |
CO005 |
Color yellow |
COLORPINK |
CO006 |
Colorpink |
LEFTMARGIN<lfm> |
LM<lfm> |
Spacefrom theleftmargin |
FONT<fnt> |
FO<fnt> |
Font |
SIZE siz |
SIsiz |
font size |
FUNCTION<code> |
<code> |
Fordirectlyspecifyinga code |
<formats>可以这样使用:PRINT-CONTROL COLOR GREEN.,但好像没起作用?
Code可以使用FUNCTION方式来调用:PRINT-CONTROL FUNCTION t022d-prctl.
[LINE line] [POSITION col]选项表示设置的格式从哪行哪列起作用
将Code转换成device-specific(打印设备相关)的control characters是根据TSP03 与 T022D两个表来确定的,某个设备类型有效的print control codes可以通过事务码SPAD来查看:
REPORT sapmztst.
PARAMETERS: c.
TABLES: t022d.
SELECT * FROM t022d WHERE typ = 'CNSAPWIN'.设备类型,与事务码SPAD中设置相同
PRINT-CONTROL FUNCTION t022d-prctl.
WRITE :/ t022d-prctl.
ENDSELECT.
点击选择屏幕上的“Execute and Print”按钮,运行结果如下:
输出设备与设备类型:
TSP03中存储了输出设备:
T022D中存储了每种设备类型所对应的打印指令(输出设备属于某种设备类型):
将文本文件转换成PDF
REPORT zzdw_text_to_pdf LINE-SIZE 132.
TYPES: BEGIN OF typ_file,
text(132) TYPE c,
END OF typ_file.
DATA: iw_file TYPE typ_file,
it_file TYPE TABLE OF typ_file,
it_pdf TYPE TABLE OF tline,
g_size TYPE i,
g_spid TYPE rspoid,
g_para TYPE pri_params,
g_flag TYPE c.
PARAMETERS: p_from(128) TYPE c OBLIGATORY LOWER CASE
DEFAULT 'C:\data\char.txt',
p_to(128) TYPE c OBLIGATORY LOWER CASE
DEFAULT 'C:\data\char.pdf'.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_from.
PERFORM f_get_filename.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_to.
PERFORM f_set_filename.
START-OF-SELECTION.
PERFORM f_read_file.
CALL FUNCTION 'GET_PRINT_PARAMETERS'
EXPORTING
no_dialog = 'X'
IMPORTING
* out_archive_parameters =
out_parameters = g_para
valid = g_flag
* valid_for_spool_creation =
EXCEPTIONS
archive_info_not_found = 1
invalid_print_params = 2
invalid_archive_params = 3
OTHERS = 4
.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
CHECK g_flag = 'X'.
NEW-PAGE
NO-TITLE
NO-HEADING
PRINT ON PARAMETERS g_para
NO DIALOG.
LOOP AT it_file INTO iw_file.
WRITE: /1 iw_file-text.
CLEAR iw_file.
ENDLOOP.
NEW-PAGE PRINT OFF.
"* SPOOL to PDF
g_spid = sy-spono."Spool Number of List Print
CALL FUNCTION 'CONVERT_ABAPSPOOLJOB_2_PDF'
EXPORTING
src_spoolid = g_spid
no_dialog = ' '
* dst_device =
* pdf_destination =
IMPORTING
pdf_bytecount = g_size
* pdf_spoolid =
* list_pagecount =
* btc_jobname =
* btc_jobcount =
TABLES
pdf = it_pdf
EXCEPTIONS
err_no_abap_spooljob = 1
err_no_spooljob = 2
err_no_permission = 3
err_conv_not_possible = 4
err_bad_destdevice = 5
user_cancelled = 6
err_spoolerror = 7
err_temseerror = 8
err_btcjob_open_failed = 9
err_btcjob_submit_failed = 10
err_btcjob_close_failed = 11
OTHERS = 12
.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
PERFORM f_save_file.
*&---------------------------------------------------------------------*
*& Form F_GET_FILENAME
*&---------------------------------------------------------------------*
FORM f_get_filename .
DATA: l_rc TYPE i,
it_file TYPE filetable.
DATA:l_obj TYPE REF TO cl_gui_frontend_services.
CREATE OBJECT l_obj.
CALL METHOD l_obj->file_open_dialog
EXPORTING
file_filter = '.txt'
initial_directory = 'C:\data'
CHANGING
file_table = it_file
rc = l_rc.
CHECK l_rc = 1.
READ TABLE it_file INTO p_from INDEX 1.
ENDFORM. " F_GET_FILENAME
*&---------------------------------------------------------------------*
*& Form F_READ_FILE
*&---------------------------------------------------------------------*
FORM f_read_file .
DATA: l_fname TYPE string.
l_fname = p_from.
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
filename = l_fname
filetype = 'ASC'
* has_field_separator = ' '
* header_length = 0
* read_by_line = 'x'
* dat_mode = ' '
* codepage = ' '
* ignore_cerr = abap_true
* replacement = '#'
* check_bom = ' '
* virus_scan_profile =
* no_auth_check = ' '
* IMPORTING
* filelength =
* header =
TABLES
data_tab = it_file
EXCEPTIONS
file_open_error = 1
file_read_error = 2
no_batch = 3
gui_refuse_filetransfer = 4
invalid_type = 5
no_authority = 6
unknown_error = 7
bad_data_format = 8
header_not_allowed = 9
separator_not_allowed = 10
header_too_long = 11
unknown_dp_error = 12
access_denied = 13
dp_out_of_memory = 14
disk_full = 15
dp_timeout = 16
OTHERS = 17
.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDFORM. " F_READ_FILE
*&---------------------------------------------------------------------*
*& Form F_SET_FILENAME
*&---------------------------------------------------------------------*
FORM f_set_filename .
DATA: l_rc TYPE i,
it_file TYPE filetable,
l_name TYPE string,
l_path TYPE string,
l_fpath TYPE string.
DATA:l_obj TYPE REF TO cl_gui_frontend_services.
CREATE OBJECT l_obj.
CALL METHOD l_obj->file_save_dialog
EXPORTING
file_filter = '.pdf'
initial_directory = 'C:\data'
CHANGING
filename = l_name
path = l_path
fullpath = l_fpath
user_action = l_rc.
CHECK l_rc <> 9.
p_to = l_fpath.
ENDFORM. " F_SET_FILENAME
*&---------------------------------------------------------------------*
*& Form F_SAVE_FILE
*&---------------------------------------------------------------------*
FORM f_save_file .
DATA:l_file TYPE string.
l_file = p_to.
CALL FUNCTION 'GUI_DOWNLOAD'
EXPORTING
bin_filesize = g_size
filename = l_file
filetype = 'BIN'
IMPORTING
filelength = g_size
TABLES
data_tab = it_pdf.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
WRITE 'SAVED SUCCESSFULLY!'.
ENDFORM. " F_SAVE_FILE
输出到PDF打印
现在大家都在提倡节约能源,珍惜不可再生资源,减少大气二氧化碳排放量。IT界也提出了绿色IT的口号,要使现绿色IT就从减少打印做起吧。企业在应用SAP ERP时,可能需要大量打印通信文档如催款单,对账单,付款通知等。通过生成PDF格式的通信文件然后用电子邮件的方式发给客户或供应商,不仅大在降低打印成本和邮递费用,加快了与业务合作伙伴的信息沟通,而且还能可达到环保的目的,可谓一举三得。在SAP ERP中安装PDF打印机的方法如下:
1、使用T-Code:SPAD启动打印配置管理
2、在 “Configuration” 菜单下,执行"Output Device"
3.按“修改”按钮进入修改模式,用 "Output Device " 菜单上 "Create"
4.输入Output Device 的命字,例如LP02;设置Device Type 为“CNSAPWIN : MS Windows driver via SAPLPD” ,并将Device Class设置为 "Standard".
5. 在 "Access Method" Tab的参数中设置如下:F 前端打印表示在打印时可以手动选择本地计算机上安装的打印机。
6. 需要打印时,系统会提示选择客户端安装的PDF生成器,用户可选择立即打印;也可选择提交到打印池中,稍后用T-Code: SP02手工执行批量打印。
以下是我们公司的使用本地设备进行打印的配置如下:
这是相应后台打印设备配置:
注:当使用函数CONVERT_OTF、或SX_OBJECT_CONVERT_OTF_PDF等函数转换PDF时,使用的是服务器上的PDF插件,不是使用的本地安装的软件,上面这种本地打印是使用的是本机的打印设备或软件,需要安装相应软件才有。
PDF生成器向大推荐PDF XChnageF 3.0,可以设置批量生成文件到指定文件夹,单张纸面上打印多页内容等,比Adobe Acrobat带的PDF生成器更强大。