• 1

ActiveReport .net下的一个出色的报表开发程序,虽然和水晶报表相比,名气不那么大,甚至有很多人不知道它的存在,但是并不妨碍它在.net报表开发中的出色表现,本文将一步一步地介绍如何使用它和VS2005开发.net报表。

 

1.         安装:

你可以从Data Dynamics的网站上下载最新的ActiveReport for .net 2 ,你可以免费使用,但是在生成的报表最下边有水印,不过不影响报表的整体美观。下载后直接安装就可以了。安装完后会看到开始-程序菜单里的DataDynamics的目录,里面包括了,帮助,例子程序等。

2.         第一个Winform报表程序。

在安装完成后,启动VS2005,会看到启动界面上有ActiveReport的图标,进入后新建一个工程,并且给工程添加新文件,在文件类型中就可以看到ActiveReport文件的图标。给报表文件命名为ActiveReport1,确定后会看到项目中有一个ActiveReport1.rpx文件,这个就是报表文件了。

打开文件,就是报表的设计界面,在这里可以看到三部分,PageHeader(表头),Detail(明细),PageFooter(表尾)。打开工具箱,给Detail部分随便添加一个Label

下面就要显示这个报表了,在Form上添加一个Viewer控件,如果工具箱里没有,可以通过配置工具箱来找到它。Viewer添加完后,在Form中写代码:

ActiveReports1 rpt = new ActiveReports1();

        rpt.Run();

    this.viewer1.Document = rpt.Document;

然后F5运行,就可以看到在Viewer控件中显示报表了

要注意的是,项目名称不要命名为ActiveReport,否则会提示找不到ActiveReport.rpx文件。

3.       第一个web报表程序。

Web方式的报表开发方式和winform的差不多,不同的是,web方式下需要作一些配置,配置过程不再累述,可以在帮助里搜索“Manually Configuring Web Samples”,按照里面的步骤对IIS作配置。

要注意的是,如果使用的是VS2005,那么可以不作配置,只要在创建Web项目的时候文件位置选项设置为“文件系统”就可以直接运行了。还要注意的是,在web方式下,需要在web.config文件中添加下面这段:

<httpHandlers>

<add verb="*" path="*.rpx" type="DataDynamics.ActiveReports.Web.Handlers.RpxHandler, ActiveReports.Web, Version=4.2.1.1238, Culture=neutral, PublicKeyToken=cc4967777c49a3ff"/>

<add verb="*" path="*.ActiveReport" type="DataDynamics.ActiveReports.Web.Handlers.CompiledReportHandler, ActiveReports.Web, Version=4.2.1.1238, Culture=neutral, PublicKeyToken=cc4967777c49a3ff"/>

<add verb="*" path="*.ArCacheItem" type="DataDynamics.ActiveReports.Web.Handlers.WebCacheAccessHandler, ActiveReports.Web, Version=4.2.1.1238, Culture=neutral, PublicKeyToken=cc4967777c49a3ff"/>

</httpHandlers>

如果是VS2005,系统会自动添加。

接下来的就和winform下的差不多了,不同的是,要在解决方案中新增一个“类库”项目,在这个项目中新建报表文件,然后在Web项目中添加对类库项目的引用,然后在页面上添加WebViewer控件,然后编写代码:

this.WebViewer1.Report = new YourNamespace.ActiveReports1();

然后运行界面,就可以看到在WebViewer中加载的报表了

你可以通过WebViewerViewType属性来设置在页面上如何显示,例如,如果选择了AcrobatReader,就会给报表生成一个PDF文档在页面上嵌入显示。

下次我们将演示如何在报表上加载和显示数据

  • 2

上篇随笔中写了一个使用ActiveReport for .net 的最简单的例子,本文将演示在ActiveReport中如何绑定数据源

 

1.       先建一个表chartingTable,有两个字段,OnlineUserCountProject,通过向导生成DataSet,并拖放生成TableAdapter

2.       接下来在报表设计器中设计显示格式,在PageHeader中放置两个label,分别为“项目”和“在线人数”。再在Detail区方式两个对应的Label,分别设置DataFiledProjectOnlineUserCount,在运行的时候将会看到这两个Label显示的是对应字段中的内容。

3.       可以给报表指定DataSetDataView作为数据源,下面演示给报表设置数据源:

使用DataSet

this.chartingTableTableAdapter.Fill(this.dataSet1.ChartingTable);

rpt.DataSource = this.dataSet1;

rpt.DataMember = this.dataSet1.ChartingTable.TableName;

rpt.Run();

this.viewer1.Document = rpt.Document;

使用DataView

this.chartingTableTableAdapter.Fill(this.dataSet1.ChartingTable);

DataView dv = this.dataSet1.ChartingTable.DefaultView; ;

rpt.DataSource = dv;

dv.RowFilter = "project ='1'";

rpt.Run();

this.viewer1.Document = rpt.Document;

4.         我们还可以通过报表设计器来给报表指定数据源。

在报表设计器的Detail部分的头部,有一个数据库一样的小图标,点击它后会出现向导,按照向导一步步连接数据库,并定义查询就可以了。

 

上面演示的是使用数据集作为数据源进行绑定,但是实际项目中经常会用到使用对象集合作为数据源显示,下一次我们将演示如何在报表上显示一个对象集合中的数据。

  • 3

上篇随笔演示了在ActiveReport for .net中如何绑定数据源,例如DataSetDataView。本文将通过一些示例代码继续演示如何绑定对象集合以及如何从对象集合或列表中提取数据。

1.       直接绑定:

我们可以自己定义一个Collection,从IList继承,并使其中的每个对象都是一个实体,例如:

CustomerCustomerCollection。然后给ActiveReport报表的DataSource直接赋值为CustomerCollection的实例就可以了。这种绑定方法在ActiveReport for .net自带的例子里有完整的演示。

2.       手工加载显示对象的数据:

在这里要用到两个事件:DataInitializeFetchData

DataInitialize用来定义字段列表,获取数据等工作,FetchData用来指定每个字段的值。

我们简单定义一个Customer

public class Customer

        {

                public int ID;

                public string Name;

                public string Address;

    }

再给报表定义一个Customer数组,用来保存要显示的数据,比如:

public Customer[] customers;

还需要一个值来保存当前记录的索引:private int index =0;

接下来在DataInitialize中定义报表有那些字段:

this.Fields.Add("Name");

    this.Fields.Add("Address");

然后在FetchData事件中添加以下代码:

if (this.index >= this.customers.Length )

        {

                eArgs.EOF = true;

                return;

        }

        else

        {

                eArgs.EOF = false;

        }

        this.Fields["Name"].Value = customers[this.index].Name;

        this.Fields["Address"].Value = customers[this.index].Address;

    this.index += 1;

这段代码的含义就是将Customer数组中的每个Customer实例的NameAddress属性值赋给报表的NameAddress字段

代码中的第一个if分支是判断是否已经加载到了最后一条数据,如果是,设置eArgs.EOFture,并返回,否则继续加载下一条数据。

然后我们可以将要显示的数据放在Customers数组中,并启动显示报表:

Customer[] customers = new Customer[2];

        Customer c = new Customer();

        c.Name = "James";

        c.Address = "Noljadfallsjf";

        Customer c2 = new Customer();

        c2.Name = "Joe";

        c2.Address = "adfaafadf";

        customers[0] = c;

        customers[1] = c2;

                       

        rpt.customers = customers;

        rpt.Run();

    this.viewer1.Document = rpt.Document;

 

通过上面的示例可以看到,第二种方式虽然比较烦琐,需要程序员自己处理很多东西,但是这样程序员拥有完全的操控能力,特别是需要对取出的数据作很多复杂的处理时,这样的操控能力是非常重要的。

 

在下一篇随笔中,将展示如何使用子报表来作显示主从表。

  • 4

上篇随笔演示了在ActiveReport for .net中如何从对象集合中提取数据。本文简单介绍如何使用子报表来显示主从表中的数据。

 

1.       在需要显示主从表中的数据时,使用子报表能够提供很大的灵活性,一般就是把主表的信息显示在detail中,如果要每页的上方都要显示主表,就可以放在PageHeader中。再把子表的信息放在Detail中,然后把子报表中的控件直接绑定到数据源上或者使用上篇随笔中的提到的方式从对象集合中提取数据。

2.       使用子报表:

新建报表,然后在工具箱上可以看到SubReport控件,拖放一个控件到Detail部分,用此控件来加载子报表。

再新建一个报表,命名为SubReport1 ,接下来在主报表中写代码,在页面设计器上选中Detail部分,在属性窗口切换到事件栏,双击Format,将会产生Detail_Format事件,下面就要在这个事件里指定子报表控件所要加载的报表。

3.       由于Detail部分会进行重复显示的,所以,只能指定一次报表源,在这里,我们按照下面的例子:

ActiveReport sub = null;

private void Detail_Format(object sender, System.EventArgs eArgs)

{

        if ( sub == null )

        {

                sub = new ActiveReports2();

                this.SubReport.Report = sub;

        }

}

先定义一个全局的子报表,然后在Detail_Format事件中对其进行初始化。

4.       还有要注意的一点是,在使用完子报表后要将其释放,销毁掉,具体的代码放在主报表的ReportEnd事件中,例如:

private void ActiveReports1_ReportEnd(object sender, System.EventArgs eArgs)

{

    sub.Document.Dispose();

sub.Dispose();

sub = null;

}

5.       下面剩下的工作就是摆放控件并设置显示的字段了,加载数据等等了,前面的随笔都有介绍。

 

下篇随笔中将介绍如何使用ActiveReport for .net开发交叉报表。

  • 5

交叉报表是一种常见的报表类型,而且开发起来也是比较烦琐的一种报表,在ActiveReport中,对交叉报表提供了足够的灵活性,使你能够应对各种复杂的业务逻辑。在上篇随笔演示了显示主从表后,本篇随笔简单介绍如何制作交叉报表。

 

交叉报表的一个常见应用就是用作显示销售额的报表上,例如,显示多个连锁店一年内每个月的销售额,常把月份作为列来显示,每个店用一行来表示:

店名     1      2          3        4     ……………   

AC       500       200         10000       50000    ……………

BC       511       85245        4545      124578    ……………

 

但是在数据库中的存储常常采用下面的方式

Sales         Month         Shop

12312           1            AB

243423          2            AB

323232          3            AB

1231312         1            BC

1232            2            BC

 

这样就需要在显示前对数据进行处理,把销售额和月份转换到列上,我们可以在取数据时使用sql来进行这些操作,在这里,为了演示在activeReport中的使用,把转换放到报表里来作,为了简化例子,我们只显示第一个季度每月的销售额。

 

1.       取数据:

使用Select Sales,Month,Shop from CrossReport Order by Shop这样的sql直接取数据,不作任何合计或转换处理。

2.       转换:

我们来定义一个简单的对象来表示最终要显示的记录:

public class Sale

{

    public decimal  money1; //一月

    public decimal money2; //二月

    public decimal money3; //三月

    public string shopname;

}

同时在定义一个Sale的集合sales,来保存转换后的数据。

由于在表中每个店会对应多条记录,为了把多条记录合并为一条,要进行下面的转换动作:

//用来保存已经计算过的店铺,保证每个店铺只有一条记录

ArrayList shopname = new ArrayList();

while (dr.Read())

{

        if (!shopname.Contains(dr.GetString(2))) //该店铺的第一条记录

        {

                Sale s = new Sale ();

        s.shopname = dr.GetString(2); //取店名   shopname.Add(s.shopname);

                if (dr.GetInt32(1) == 1) //一月

                {

                        s.money1 = dr.GetDecimal(0);

}

                else if (dr.GetInt32(1) == 2) //二月

                {

                        s.money2 = dr.GetDecimal(0);

                }

                else if (dr.GetInt32(1) == 3) //三月

                {

                        s.money3 = dr.GetDecimal(0);

                }

                sales.Add(s);

        }

        else //不是该店铺的第一条记录

        {

                Sale s = (Sale)sale [sales.Count - 1];

                if (dr.GetInt32(1) == 1)

                {

                        s.money1 = dr.GetDecimal(0);

}

                else if (dr.GetInt32(1) == 2)

                {

                        s.money2 = dr.GetDecimal(0);

                }

                else if (dr.GetInt32(1) == 3)

                {

                        s.money3 = dr.GetDecimal(0);

                }

        }

}

3.       表示:

上面是对从数据库中取出的记录作转换,将其变成在报表上要显示的格式。接下来就要在报表上显示Sales集合中的数据了。我们可以按照前几篇随笔中介绍的方法来作:

n         在界面上摆放控件,并设置其FiledName字段

n         在报表的DataInitialize事件中设置Filed集合,取出数据:

this.Fields.Add("money1");

        this.Fields.Add("money2");

        this.Fields.Add("money3");

        this.Fields.Add("shopname");

this.GetReportData();  //取数据并作转换

n         设置一个标记来表示是否显示到了最后一条记录:

int index = 0;

n         FetchData事件中显示Sales集合中的数据:

if (index == sales.Count ) //如果到了最后一条记录,就跳出

        {

                eArgs.EOF = true;

                return;

        }

        else

        {

                eArgs.EOF = false;

        }

        Sale s = (Sale)sales[index];

        this.Fields["shopname"].Value = s.shopname;

        this.Fields["money1"].Value = s.money1;

        this.Fields["money2"].Value = s.money2;

        this.Fields["money3"].Value = s.money3;

index+=1;

 

按照上面的步骤,主要的代码都完成了,当然要在窗体上显示,还要加一个Viewer,然后指定加载的报表:

ActiveReports1 rpt = new ActiveReports1();

rpt.Run();

this.viewer1.Document = rpt.Document;

 

如果你不满意显示的效果,可以给报表加上线框,让其显示成表格。

 

总结:

例子中的代码有重复,但是为了说明转换的过程,没有作优化,另外,也可以看到,代码中使用了ArrayList,出现了装箱,拆箱的动作,所以性能还有优化的空间。

从表数据到显示用数据的转换可以在Sql中作,但是业务逻辑较复杂的时候,Sql就显得力不从心,例如,显示每月的数据,而且还有收入,支出,如果再加上税收,折扣,损耗,租金,和上年同期的比较等等无法预测的业务逻辑,如果把SQL写在代码中,调试成问题,如果写成存储过程,有破坏了封装。所以相比之下,在代码中进行的转换工作虽然较复杂,但是还是具有灵活的优势的。

  • 6

ActiveReport提供了多种文件格式的导出,包括:Html,Pdf,Text,Rtf,XlsTiff,这几种格式中,Pdf是效果最好,因为有了抗锯齿,线条和字体看起来都光滑的多,而且是完全的所见即所得,加上很多Web项目都采用Pdf作为报表显示的文件格式,导出Pdf应该是首选。由于是把报表直接生成了图片,Tiff格式的导出也是所见即所得的。

 

其他的文件格式的限制都比较多,每种文件格式的限制可以在ActiveReport的帮助中搜索“Limitations in Export Formats,其中有详细的列表。

 

将报表导出的步骤也很简单,先要在工具栏上添加导出要用的控件,这组控件有:HtmlExport,PdfExport,RtfExport,TextExport,TiffExport,XlsExport六种。这里以PdfExport为例:

拖放一个PdfExport控件到窗体上,就可以进行导出操作了,代码很简单,我们假设有一个报表文件和一个Viewer

窗体加载的时候:

ActiveReports1 rpt = new ActiveReports1();

rpt.Run();

this.viewer1.Document = rpt.Document;

在一个ButtonClick中:

this.pdfExport1.Export(this.viewer1.Document,@"z:\123.pdf");

很简单吧,其他格式的导出也一样,例如Excel格式:

this.xlsExport1.Export(this.viewer1.Document, @"z:\123.xls");

 

在生成Pdf的时候要注意,有时页面设置不当,控件的大小可能会超出范围,导致报表有一部分无法打印出来,在使用AcrobatReader预览的时候,要在Print窗口,通过调整Page ScalingNone,看看右边Preview部分,有没有发生超出打印范围的情况。另外在作表格的时候要放大后看,因为经常会出现线条没有对齐或完全连接的情况,在预览的时候看不出来,但是在打印后会看到线条错位等情况。

  • 7

上篇随笔介绍了在ActievReport中导出报表,本篇随笔简单介绍ActiveReport中的重要事件。

 

1.       只触发一次的事件:

ReportStart在报表启动时触发。该事件在DataInitialize 之前,在这里可以整个报表都要用到的对象或变量作初始化,也可以设置子报表。

DataInitialize :这个事件在前面的几篇随笔中经常出现,经常在这里取数据,绑定数据源,设置报表的Fileds属性等。

ReportEnd 在报表加载完成后触发。

2.       多次触发的事件:

FetchData 在处理每一条数据时都会触发,如果是采用绑定数据源的方式,就不需要再作对Fields中每个FieldValue赋值。如果是从自定义的对象集合中提取数据,就要在这里给Fields赋值。而且,使用自定义集合的时候要自己设置事件的eArgs参数的EOF属性,该属性用来判断是否显示到了最后一条记录,如果是,就设置为true,并且直接返回。具体的代码可以参看以前的随笔。

PageStart 在报表的每页生成的时候触发,在这里作针对每个页的处理,最常见的就是显示页码。

PageEnd 在报表的每也生成完成的时候触发。

3.       区域的事件:

每个区域(PageHeaderDetail PageFooter)都有FormatBeforePrintAfterPrint事件。最好不要在这几个事件中操作报表的Fields集合,而应该在DataInitializeFetchData中进行。

Format 在数据加载并绑定后触发,只有在这里才可以设置某个区域的高度。

BeforePrint在区域被生成前触发,在这里如果设置区域的高度是不会有效果的,而且不要在这里操作数据源。

AfterPrint在区域已经被生成并描画出后发生,可以在该事件里直接在画布上进行描画操作。

 

posted on 2009-06-11 12:27    阅读(1298)  评论(0编辑  收藏  举报