2019-1-17水晶报表技巧总结【二】

2019-1-16水晶报表技巧总结【一】

 

2019-1-18水晶报表技巧总结【三】

 

第一条:水晶报表分组分页且每页最多显示N条记录

第二条:一个获取记录的标记和页脚的显示

第三条:页面大小的设置。

第四条:如果要让列分上下两处显示---要用到子报表。把子报表和主报表做成一样的格式。

第五条:推拉说识 

第六条:一种根据记录数和前面一行的内容和后面一行的内容不一样而分页 

第七条:主报表和子报表共享数据 

第一条:水晶报表分组分页且每页最多显示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

将公式应用到子报表的适当位置,即可显示出主报表的内容

注意:公式中的变量名称与类型需要一致!

  

返回顶部

posted @ 2019-01-17 11:41  IT苦行僧-QF  阅读(1699)  评论(0编辑  收藏  举报