现在的位置: 首页 > 综合 > 正文

Crystal Report内功心法

2016年10月26日 ⁄ 综合 ⁄ 共 7553字 ⁄ 字号 评论关闭
1.对象的操作
开始之前

先说一下,操作对象,是依靠ReportDocument,
基础代码如下:

C# code
ReportDocument myReport = new ReportDocument();string reportPath = Server.MapPath("crystalreport1.rpt");myReport.Load(reportPath);//PULL或PUSH方式的数据处理//... CrystalReportViewer1.ReportSource = myReport;//然后,才是下文中的代码

关于水晶报表里的对象,我们直观的来看一下。
在模板空白处点邮件,选“插入”,鼠标放到“特殊字段”上,不要点,会出现如下菜单。

上面这些,都是报表对象或说是报表部件。恩,这些我们都可以进行控制。

操作的基本语法是:

C# code
myReport.ReportDefinition.ReportObjects[对象序号].方法或属性;//myReport.ReportDefinition.ReportObjects[对象名称].方法或属性;

对象序号在 .Net的水晶报表版本里很难直观的看出来,一般他是按加入报表的顺序来排的
这个很难去记住。有时候为了一些需要,在遍历的时候会用的到。
一般我们会用名称
在对象上点右键,设置对象格式,

在出来的界面上就可以看到对象的名称

ReportObjects就是我们前面所指的报表对象集合,单一对象就是一个ReportObject
对于ReportObject来说,通用的属性有这样一些

一般我们可以控制对象的位置,通过控制左上角的坐标,也就是top和left属性。
这里说一下水晶报表的坐标系和计量单位。

水晶报表的坐标系,以每个节的左上角为原点,往下往右两个方向递增延展。
计量单位是缇,与厘米的换算关系是:1厘米=567缇
所以在后面的代码中,你会看到我有这样的写法: 567*N 其实也就是N厘米。

另外还有ObjectFormat下面的属性,可以控制对象的隐藏与显示,这个很实用吧,呵呵。

如抑制显示可以这样操作:

C# code
myReport.ReportDefinition.ReportObjects("text4").ObjectFormat.EnableSuppress = true;

ReportObject是个通用对象,而其实际上又是一个具体的对象。他有各自的特殊属性。
以文本框为例,他就会多出一些特性,比如Text属性,这样我们可以修改到文本框的内容。

具体操作如下:

C# code
//更改一个文本框的内容TextObject x;x = (TextObject)myReport.ReportDefinition.ReportObjects["text4"];x.Text = "我们的世界";

另外说一下,前面没提到的数据字段,也可以这样操作。

C# code
//调整字段的位置FieldObject fo;fo = (FieldObject)myReport.ReportDefinition.ReportObjects["NAME1"];fo.Height = 567 * 3;fo.Width = 567 * 4;fo.Left = 567 * 1;

每个对象都有各自不同的属性,至于具体有多少对象,你可以按一下F2,
CrystalDecisions.CrystalReports.Engine这个命名空间下,以Object结尾的,基本都是。

2:子报表里对象的操作
子报表里的对象操作跟主报表是一样的,主要是要先找到这个子报表对象。

下面这个方法是不对的

C# code
SubreportObject subObj;subObj = (SubreportObject)myReport.ReportDefinition.ReportObjects["subreport1"];
这个只会定义子报表在主报表内的特性,不能设置到子报表内的部件

要这样

C# code
myReport.Subreports[子报表序号].ReportDefinition.ReportObjects["xx"];//序号从0开始计数,单纯子报表的一个序号//myReport.Subreports[子报表名称].ReportDefinition.ReportObjects["xx"];
注意这个名称不是我们在子报表上点右键看到的名称,而是我们在设计子报表时指定的子报表名称

C# code
TextObject subtext;subtext = (TextObject)myReport.Subreports["xyz"].ReportDefinition.ReportObjects["text3"];subtext.Text = "我是子报表里的内容哦";

3:参数的基本操作
参数是程序与报表交互的一个重要媒介,本节只做一个基础说明,后续的章节中会进一步介绍。
新建一个参数p1


拖到模板上,会自动显示为 ?pa1

代码中传递参数给报表的代码为:
SetParameterValue 后面的几个参数依次为:参数名称,参数值,子报表名称(如果是子报表参数的话)。

C# code
myReport.SetParameterValue("pa1", "我是一个参数");

在子报表中建立一个新的参数spa1,注意xyz是子报表名称

C# code
myReport.SetParameterValue("spa1", "我是一个参数","xyz");

当然,参数也可以传递给CrystalreportViewer,这里就不展开说了,用到的时候再说。

单纯从这个代码上来看,textbox和参数似乎可以等价
但是textbox一般只能用于显示一个字符串,但是参数则可以参与公式,过滤数据,控制对象的一些特性等等,用途更广泛一些。

好了,就到这里。这个基本上没有什么需要特别注意的。

修正记录:
20090707:
坐标系原点应该是每个节的左上角,而不是整个模板的左上角

push 模式 
新建一个 Asp.Net CrystalReports的网站

弹出下面这个界面后,选第2项-作为空白报表,然后点确认,进入一个空白界面。

新建一个数据集合

出现此提示时,点“是”

然后还会出来连接数据库的提示,一律点否,直至界面上什么提示也没有
在这个界面上,点右键,添加一个datatable,命名为myTable。

然后在myTable里增加相应的列,我这里有3个列,注意要设置好类型。

特别说明:
因为水晶报表里不认datatime型,对于2009-06-26 9:23:15
会自动截断为2009-06-26
所以datatime型数据在xsd文件中,可设置为string型
而代码中的SQL中可把字段转换为String传入。

xsd设计完毕后,开始进入报表模板设计阶段。

在“数据库字段”上点右键,选“数据库专家”

再弹出的界面上,点“项目数据”,依次点开,找到我们刚才自己做的mytable

确定后,在“数据库专家”下面会出现表名mytable,点开后会看到字段,
把字段拖到模板上的详细资料节即完成基础模板的制作了

点一下预览,会看到虽然我们还没有任何实际的数据操作,但是界面上已经有数据了
这是水晶报表的一个虚拟数据,可以看到一个最终显示的效果。

好了,上代码

C# code
using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using CrystalDecisions.Shared;using CrystalDecisions.CrystalReports.Engine;using CrystalDecisions.Web;using System.Data.OleDb;public partial class _Default : System.Web.UI.Page {    protected void Page_Load(object sender, EventArgs e)    {        ConfigureCrystalReports();    }    private void ConfigureCrystalReports()    {               //连接字串        String connstr = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:\CrZen\testall.mdb;";        OleDbDataAdapter da = new OleDbDataAdapter();        OleDbConnection cn = new OleDbConnection(connstr);        //        da = new OleDbDataAdapter("SELECT * From RPT_CR_TEST1", cn);        //创建我们的DataSet1实例        DataSet1 dt1 = new DataSet1();        //填充dt1        //注意:表名mytable必须与我们在xsd设计的表名称一致。        //本例中数据库的表实际名称为RPT_CR_TEST1,而最终是以mytable为准的        //使用 PUSH模式的优点就在此,可以自由组合SQL        //前提是表名称和字段名(需要在SQL中使用as别名的方式跟xsd中设计的字段名一致)都要一致        da.Fill(dt1, "mytable");                  ReportDocument myReport = new ReportDocument();        string reportPath = Server.MapPath("crystalreport1.rpt");        myReport.Load(reportPath);        //绑定数据集,注意,一个报表用一个数据集。        myReport.SetDataSource(dt1);        CrystalReportViewer1.ReportSource = myReport;    }}

然后运行就可以了。

这里做一下说明,在【推拉之间】没有说这个xsd,是因为当时没讲到细节操作。
说了很难理解。
这个xsd,是我们自己构造的,做个比喻吧:
就是xsd比作个一个架子,它负责把水晶报表撑起来,但是是空的,没有内容
我们用 PUSH模式,把数据塞给它,把架子塞满,这样水晶报表也就能呈现出效果来

常见问题:
1:报表可以显示出来,但是没数据,
一般有以下几种情况
a:表中确实没数据
b:da.Fill(dt1, "mytable");表名称与xsd中设计的表不一致
c: 如果你用了多个表,可能是因为表默认的关联关系导致无数据

2:出现下面这个图的提示,特别是翻页、打印、导出的时候,因为这些动作都需要重新连接数据库源。

a:如果是多表的话,可能是部分表没有赋值。
水晶报表需要对用到的每个表进行验证,即使没数据。没有的话,传个空的记录集也行。
b:代码没有放到Page_Load或Page_init里,或者是放到了,但是控制了 postback。
因为http是无状态的,如果控制了postback,水晶报表就丢失了前面的设置,会导致出现此情况的

本文的主题是:函数与公式、运行时总计
本文还是以基础讲解为主,后面的一些实际场景中会有具体的使用

1:公式
公式可以说是水晶报表里精髓,其重要性有点像Excel的宏一样。
公式基本上可以分为两类,一是格式化类。一是运算类。当然,格式化的公式也是需要运算的。
水晶报表的一些内置的功能,本身也是依赖于公式的,只是做了封装。
公式有两种语法:crystal语法和Basic语法,没有根本性差异。
如果你之前用过Basic类的语言,那么这个就非常容易了。
可以从此处下载一个独立版本的水晶报表帮助文档,方便查阅。
CR XI水晶报表开发官方中文帮助文档

格式化类主要出现在如下位置:
比如,我们要把某个列中值等于12的值设置为蓝色,等于15的设置为红色,其余的设置为黑色。
在字段上点右键,格式化该字段,

这个图上的(x+2)后面就是一个公式,如果里面没有内容,这个图标就是这种默认的颜色。
如果里面有内容,则图标会变成红色。

本文的示例数据延续前文中的例子

实例1,如果用户分数等于12,则显示为蓝色,如果等于15分则显示为蓝色,其他显示为黑色。
点击上图中的(x+2),进入公式编辑界面。

我抓了个大图,里面做了一些标记。
公式本身就比较简单了,不必再进行说明。
注意前面的图上左上角的两个指向,x+2是指检查该公式是否正确,检查完毕后停留在当前界面
保存并关闭,则是先检查后保存公式并且关闭本界面,回到前一界面上。

好了,我们再运行一下程序,效果就出来了。

实例2,控制每3条记录换页

进入节专家


点中“详细资料节”,勾选后面的“在后面新建页”,然后编辑后面的公式为

如果公式有错误,则会有类似下面的提示,当然,根据错误的不同,提示也不一样

再预览一下,之前我们显示1页的数据,已经分页了。注意上面的 1+,说明页数多于一页。

但是并没有显示实际页数,这是水晶报表的一个优化机制。如果是多页的数据,它只显示第一页
有点类似于我们的一些数据库分页操作一样。但是这个也带来很多批评。
而实际上,解决方法也是有的。
就是这样

C# code
CrystalReportViewer1.ReportSource = myReport;CrystalReportViewer1.ShowLastPage();CrystalReportViewer1.ShowFirstPage();

实际上也是模拟了我们的一个手工动作,就是先点击跳到最后一页,然后再切到第一页。
这个动作用户是感觉不到的,尚可以接受。

前面的是两个格式化类的公式,下面说一下运算类公式。

实例3,如果分数<15则显示不合格,等于15则为合格,大于15则为优秀
这个就是对数据的再加工了。

新建一个公式字段x1

然后把x1拖到界面上,放到原来数据的后面,预览后就显示

实例4,代码中修改公式
新建一个公式字段x2,里面就放一个现有字段即可

显示效果如下

恩,没什么感觉吧。
另外做一个公式字段x2title,作为x2这一列的题头


然后代码里控制一下:

C# code
myReport.DataDefinition.FormulaFields["x2"].Text = "{RPT_CR_TEST1.Scores}";myReport.DataDefinition.FormulaFields["x2title"].Text = "'得分情况'";

特别注意的是,原公式中返回的是字符串的话,要把引号带进去。
myReport.DataDefinition.FormulaFields["x2title"].Text = "'得分情况'";

再看效果

这个可以实时变化的字段,有很多用处的。
用过的朋友应该记得,在分组、图表、交叉表的时候,都可以使用公式字段作为运算字段的
如果我们在代码中控制公式里的字段变化,那么自然也达到了动态分组、动态图表等效果了
类似的应用可以参考:
在水晶报表中实现任意选择指定字段显示-模板及C#升级版
用公式实现动态设置图表的轴数据项

这个是单一公式,公式之间可以嵌套使用,有时候也可以结合全局变量进行多公式协作
这些在后面的一些实际应用场景中会讲到。

2:运行时总计

我们知道报表里内置了求和和计数等汇总功能,但是如何只求部分数据的和呢?

实例5:
求大于15分的所有分数的和,求大于15分的人的个数,并求得平均值

新建一个运行时总计字段Rtotal0,这个用于获取分数和

注意在“求值”的位置,编辑后面的公式:

新建一个运行时总计字段Rtotal1,这个用于获取人数

注意这个求值也是用了公式的,所以(x+2)是红色的,公式的内容跟Rtotal0一样。
因为都是同样的规则。

新建一个公式字段x3,

C# code
//定义一个字符串变量stringvar s1;//定在一个数值型变量numbervar n1;s1 := '得分大于15分的所有人的平均得分为';//对于If Else逻辑来说,每个分支逻辑返回的数据类型,都应该是一样的//进行一个保护,防止分母为0if {#RTotal1}=0 thens1+'-'else//一个分支逻辑有多条语句,可以用括号括起来,这样更加清晰(//计算平均值n1 := {#RTotal0}/{#RTotal1};s1+cstr(n1,2);)

这个公式是个小典型,里面用到了变量。
实质上跟我们在程序里用的变量差别不大。
因为考虑到求平均值时可能出现分母为0的情况,所以进行了一个保护。
注意公式的默认语法是Crystal语法,其赋值方式为 x:=5,x=5 则是一个逻辑判断
(有时候又可以混用,不过最好按照规则走)

看一下实际的运行效果

运行时总计,就是数据从上往下显示时的一种数据处理,
他的数据只能从上往下走,按照一定的规则处理其中的一些数据。
它一定要出现在数据的下面(或与数据平行),而不能与其他Max,Sum等聚合函数一样可以出现在数据之前。

实质上,运行时总计,本身就是公式的一种特殊应用。

3:函数

函数其实也是基于公式的,跟我们普通程序一样。如果有很多重复的公式,那么可以提炼成函数进行复用。
我们就以前面的公式x1进行改造。
模板上不能直接进入函数编辑界面,先要进入公式编辑界面。
新建一个函数,setTitle


先使用编辑器,
直接把现在的公式拷贝进去,注释一份做参考,实际一份做改造
点击保存的时候,有报错。函数中是不能直接使用字段的。

我们做一个变量x来接后面可能的输入值,可能是字段,也可能是其他

然后我们把公式x1改掉

还是可以实现同样的效果。

前面的界面上我们看到另外一个“使用提取器”的按钮。
我们也来用一下
新建一个函数setTitle2,点击使用提取器按钮


这个就是从现有的公式中提炼成函数的,很方便吧,呵呵。
我们选中x1(为了方便演示,我把函数删掉,恢复到原来的公式内容),这时候工具会自动把数据库字段设置为参数。
点击确定后,函数就建好了,可以再进行适当编辑

其他:

公式可以通过与参数的结合,实现更复杂的效果。
函数是基于公式的,公式是可以直接调用函数。
函数间可以互相调用。
函数中不能使用报表参数字段(parameterField),不能使用数据库字段
公式和运行时总计中则都可以使用。

本文中的公式相对比较基础一些,后面开始进入更复杂的阶段。
主要是分组、图表、子报表及交叉表,这些部分的处理方法与之前稍有不同
基础应用之外,还会结合一些实际场景,并且会综合之前讲解的一些内容,做一些深入。

抱歉!评论已关闭.