2019-1-17水晶报表技巧总结【二】
2019-1-16水晶报表技巧总结【一】
2019-1-18水晶报表技巧总结【三】
第四条:如果要让列分上下两处显示---要用到子报表。把子报表和主报表做成一样的格式。
第六条:一种根据记录数和前面一行的内容和后面一行的内容不一样而分页
第一条:水晶报表分组分页且每页最多显示N条记录
要求:1、详细节最多5条记录(不能超过5条);
2、无论前一组是否满5条记录,每个新组都要另起一页
3、可以显示组内分页的情况,即:显示一个组被分为几页并且当前是第几页
我们知道每页最多显示N条的控制方法,打开节专家,在详细节后面新建页公式编辑:
RecordNumber mod N = 0
RecordNumber即记录数,无论分组与否,是自动随记录递增的,所以在这里,我们不能用RecordNumber,而应该是在每个新组前要重置这个RecordNumber,用这个重置的RecordNumber来取模控制每页最多显示5条记录,于是我们很自然就想到了运行总计字段,由它来帮我们重置RecordNumber,
这样我们应该就很明朗了,接下来让我们一起去设计模板:
报表按ID分组就不用再提及了吧?(右击报表空白处->插入->组)
首先新建一>运行总计字段汇总字段:XXX(最好选择的字段和分组字段不同)
汇总类型:计数
求值:对于每个记录
重置:组更改时
字段是刚刚建立的【运行总计字段】 {#RTotal0} mod 10=0
针对需求2,与需求1同理,只不过此时我们是在#组尾(组页脚)节在后面页新建页的公式编辑:
Not OnLastRecord
OnLastRecord意指最后一条记录,Not OnLastRecord自然是非最后一条记录,在这里就是指非最后一条记录时就在后面页新建页,最后一条记录时就不用再新建页了。若不用该公式控制的话,报表的最后一页总是空白页(没有详细节数据)
需求3:组内分页及显示页码
新建一个【公式字段】公式设置如下:
记录的组记录总数:count(一个报表参数,分组参数);前者最好是运行总计的字段。如果有汇总,请在count最后+1,因为多一行数据。!!!
组内条数和当前组内序号,这两个最可能出现有误,在序号为9或者11,就是比你设定的行数大 “1”。
方式一:注意组内数据条数和当前组内序号
//每个组的总记录数 NumberVar groupRecordCount := Count ({DataTable3.Type}, {DataTable3.ID}); //每个组的总页数 NumberVar groupPageCount := groupRecordCount / 5; If Int(groupPageCount) <> groupPageCount Then ( groupPageCount := Int(groupPageCount) + 1 ); //重置后的RecordNumber NumberVar groupRecordNumber := {#RTotal0}; //组内当前页 NumberVar groupPageNumber := groupRecordNumber / 5; If Int(groupPageNumber) <> groupPageNumber Then ( groupPageNumber := Int(groupPageNumber) + 1 ); //最终报表界面显示 '第' + ToText(groupPageNumber, 0) + '页 / 共' + ToText(groupPageCount, 0) + '页' //说明:Int函数是取整函数,如Int(1.2)=1; Int(1.9)=1 //写好后将该公式字段拖放到#组头(组页眉)节即可
方式二:注意组内数据条数和当前组内序号 如果有方法“RoundUp”的,可以把if直接换成此方法:p1:=RoundUp(x1/10); p2同理。
numbervar x1; numbervar x2; numbervar p1; numbervar p2; //x1是每个组的数据条数 x1:=count({OUT_1_DATASET.code1},{OUT_1_DATASET.Warehouse_position}); //得到总页码 if(x1%10=0)then p1:=x1/10 else p1:=INT(x1/10)+1; //得到当前行组内序号。其实就是换页第一条的序号 x2:={#RTotal0}; //用序号除以每页的条数5,得到当前页码 if(x2%10=0)then p2:=x2/10 else p2:=INT(x2/10)+1; //组合 '第'+totext(p2,0)+'页/共'+totext(p1,0)+'页'
第二条:一个获取记录的标记和页脚的显示
在IDE左侧的“字段资源管理器”里的“公式字段”点击右键“新建”,新 建一个字段“Group”,编辑赋值: whilereadingrecords; true; 这里是TRUE,获取的就是“真”,否则获取的就是“假”
控制子报表每页显示数目,假设为10行每页,只是做一下说明,不要设置公式 注意设置的行数最好基本上能打印到页面的底部 拆分一个详细资料节出来,把页脚内容写上,抑制显示,设置公式为 recordnumber mod 10<>0 在这个节上进行分页控制,“在后面页先建页面”勾上,设置公式为 recordnumber mod 10=0 这样的话每页最下面会显示该节模拟页脚 最后一页如果不满10行的话,因为你设置了报表页脚,也可以模拟成一个页脚了 为了防止最后有一页刚好显示10行导致出现双重页脚 还需要进一步改进一下抑制显示的公式 If onlastrecord then true else if recordnumber mod 10<>0 then true else false 最后页不足,用空行填充: (Basic语法) 新增一个空白详细资料行,格式化此节,在抑制显示(无深化)打勾 公式中输入: if not onlastrecord then FORMULA = TRUE else if (RecordNumber mod 5 <= 1) then '第一个 FORMULA = TRUE else FORMULA = FALSE end if end if
第三条:页面大小的设置。
要现在本地的打印机上设置相应的纸张大小,然后应用到报表中。
本地打印机添加新的打印纸张规格:
WIN7系统中:设备和打印机-->菜单栏中【打印服务器属性】-->【表单】或者【格式】-->创建新表单-->输入相应的尺寸,点击保存即可。
在报表中,右击报表的空白处:设计-->页面设计,填写相应的尺寸。设计-->打印设置-->在纸张中选择自己新建的纸张大小就可以了。
第四条:如果要让列分上下两处显示---要用到子报表。把子报表和主报表做成一样的格式。如下显示:
合同编号:XM-003 供应商:广西中金公司
合同条款 金额
付20% 2000
付80% 8000
付款日期 付款金额
2011-10-1 10000
2012-10-12 12000
折开显示,每个页面就可以固定显示,这样按分组分页,防止数据过多分页时,子报表被分开。
特别说明!
使用子报表的方法,在数据量较大的时候,效率非常低。所以要慎重选择使用。
如何将同一个表(记录集)中的字段,拆成上下两部分,每页各显示10条。 也就是这个样子。 有人可能一下子想到的就是两个子报表。 如果数据比较少,不用分页,用子报表是没问题的 但是如果要分页,子报表是做不到的(至少目前的水晶报表还不支持这个机制)。 因为显然两个子报表都要分页(或者有人是用一个主报表,一个子报表) 但是这样一个页面上就出现了两个分页需求,然目前水晶报表的解析,是用后面的规则去覆盖掉前面的规则 也就是说,虽然确实都分页了,但是因为常规分页都是在详细资料节处新起一页的。 那么第2个子报表和第1个子报表的内容会重叠起来。 而如果把子报表放在不同的节上,又会出现这样的情况。 就是第一个子报表全部分页结束,才会显示第2个子报表的内容。 这样就造成了数据不连贯(当然,也有的需求直接就这样,那就直接这么用就可以了)。 下面我开始示例操作 1:样例数据 本文以独立版本水晶报表自带的样例数据库xtreme.mdb中的Orders订单表为例。 2:数据的整理 数据必须有一个连续的ID(或者有一个分组的标志位),这是本文实现的重点。 所以特别说明一下。 我们的方案中,将以这个ID(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22...)为准进行处理。 如每10个分一页,那么我们要构造一个分组字段:(ID-1) \ 10 注意这个符号,是取整的。 这样,就把每10个归为一个组了。这个字段会出来一个0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3... 当然,如果有条件,可以之前就准备好,比如说这个ID直接就是1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3... 这里我先不讲为什么要这样做,只是你务必要注意这一点。 不要依赖于数据库本身里的ID,因为你可以有不同的过滤条件及排序条件,会导致你的本身的数据ID是不联系的 所以这个ID字段,是你自己构造的,与是原先数据并无实际的逻辑关系,只是为报表准备的。 比如,以Access语法为例子,为现有记录集主动加一个ID列。 SQL code ? 1 2 3 4 SELECT ( SELECT count(*) +1 FROM [Orders ] as a where a.[order id]<b.[order id]) AS myIndex, b.* FROM Orders AS b; 其显示的结果如下 当然,我们也可以直接进去到前面说的第2种情况 SQL code ? 1 2 3 4 SELECT ( SELECT count(*)]\10 FROM [Orders ] as a where a.[order id]<b.[order id]) AS myIndex, b.* FROM Orders AS b 其显示的结果如下 本文采用第一种方法来实现。 3:数据与模板怎么进行连接 我们已经知道制作模板有PULL和PUSH两种方法。 使用PUSH的话,就不用关心这个操作了。因为这个SQL是在代码中实现并传给记录集的再传给报表。 而使用PULL的时候,就这样,直接把SQL代码拷贝到“命令”就行了。 当然,你也可以使用存储过程,或者一个中间的实体结果表等。 4:制作主模板 我们先用主报表来显示前半部分字段,然后用子报表挂接后半部分字段。 两个报表可以使用同一个记录集 不过如果数据量比较大,建议还是按照实际需要拆开,这样减小数据的传输 如主报表的记录集只放前5个字段,子报表的记录集放后5个字段等。 我这里为了方便操作,用了同一个记录集。 <1>首先构造主表,做一个基础的明细表 <2>构造分组字段myGroup1,进行分组 如果是【2:数据的整理】中的第2种实现方法,则可以直接用ID作为分组字段了 myGroup1公式如下: 并进行分组,如果需要,把页眉放到组页眉上。 最后出来的模板是这样的 出来的数据是这样的 好了,主报表操作完毕。 5:制作子报表模板 在模板上点右键,插入子报表。 把这个子报表,拖到主报表的组页脚上。 子报表的做法跟【4:制作主模板。】一模一样。 (包括分组公式及操作) 唯一的不同就是在模板上显示的字段不同,此处省略了。 为了便于识别,把子报表上的字体全部设置为红色。 把不需要显示的节,全部抑制显示掉 6:设置主报表和子报表的关系 这个时候报表大致就是这个样子的,在子报表上点右键,选“更改子报表连接” 在出来的界面上,把两个分组字段关联起来。 (我这里两个分组字段取的名称不一样,这个不影响操作) 好了,再预览一下数据。这样数据就组合在一起了 7:分页设置 进入主报表的“节专家”,设置组页脚,勾选“在后面页新建页”就可以实现了。 如下图我们切换到第3页。
第五条:推拉说识
.【水晶报表内功心法】--推拉之间
.【水晶报表内功心法】--PULL模式样板招式
特别说明:
因为水晶报表里不认datatime型,对于2009-06-26 9:23:15
会自动截断为2009-06-26
所以datatime型数据在xsd文件中,可设置为string型
而代码中的SQL中可把字段转换为String传入。
这里做一下说明,在【推拉之间】没有说这个xsd,是因为当时没讲到细节操作。
说了很难理解。
这个xsd,是我们自己构造的,做个比喻吧:
就是xsd比作个一个架子,它负责把水晶报表撑起来,但是是空的,没有内容
我们用 PUSH模式,把数据塞给它,把架子塞满,这样水晶报表也就能呈现出效果来
常见问题:
1:报表可以显示出来,但是没数据,
一般有以下几种情况
a:表中确实没数据
b:da.Fill(dt1, "mytable");表名称与xsd中设计的表不一致
c: 如果你用了多个表,可能是因为表默认的关联关系导致无数据
2:出现下面这个图的提示,特别是翻页、打印、导出的时候,因为这些动作都需要重新连接数据库源。
a:如果是多表的话,可能是部分表没有赋值。
水晶报表需要对用到的每个表进行验证,即使没数据。没有的话,传个空的记录集也行。
b:代码没有放到Page_Load或Page_init里,或者是放到了,但是控制了 postback。
因为http是无状态的,如果控制了postback,水晶报表就丢失了前面的设置,会导致出现此情况的
第六条:一种根据记录数和前面一行的内容和后面一行的内容不一样而分页
在报表上右键-->报表-->节专家-->详细资料-->在以下内容前新建页-- >右边的小方块中新建basic公式: 水晶报表常规页码是以下代码的"3",满3行记录的自动换行,碰到TBUserInfo.SerialNo与上一行记录不同也换行。 Shared SerialNoGG as Number Shared RecordTemp as Number if RecordNumber = 1 then SerialNoGG = {TBUserInfo.SerialNo} RecordTemp = 1 end if if RecordTemp mod 3 = 0 or {TBUserInfo.SerialNo}<>SerialNoGG then RecordTemp = 1 SerialNoGG = {TBUserInfo.SerialNo} formula =true else RecordTemp = RecordTemp + 1 formula =false end if
第七条:主报表和子报表共享数据
使用水晶报表变量实现主报表及子报表之间的数据共享
在使用水晶报表子报表的时候,
可能根据需要在主报表之间和子报表之间共享部分数据
如子报表需要依据主报表的某些数据进行运算等
因为主报表与子报表之间的数据是独立的,所以不能直接引用对方的字段等资源
所以要得到对方的数据有些难度,
有些朋友使用在代码中传递参数等方式来实现,
其实在水晶报表中已经有了问题的解决方法,那就是公式中的共享变量。
大家可能注意到了在水晶报表的公式中定义的变量是有作用域的
分别是Dim/本地(Local)/全局(Global)/共享(Shared)
使用水晶报表的共享变量即可实现在主/子报表之间的数据共享
在下面的例子中,我们以在子报表中获取主报表中的一个数据为目标来进行演示
我们使用水晶报表的自带的数据库来做演示,在主报表中获取一定的数据
然后进行汇总
在子报表中获取这个汇总数据,
以下的公式,使用Basic语法
主报表中的公式如下 公式名称:@fMain1 公式内容: Shared myTest1 as string myTest1={产品.颜色} '此处可以替换为任意想获取的数据 formula = myTest1 将该公式应用到主报表的适当位置,如果不需要显示,可以抑制显示 不影响后续数据的获取 子报表中的公式为: 公式名称:@fSub1 公式内容为: Shared myTest1 as string formula=myTest1 将公式应用到子报表的适当位置,即可显示出主报表的内容 注意:公式中的变量名称与类型需要一致!