在一般的企业应用开发中都会涉及到报表的生成,且一般报表的格式都是生成Excel格式的。对于各种报表的生成一直是程序员心中永远的痛,因为对于很多程序员来说每写一个报表都意味着要写一大断代码来实现,而且有的报表可能极其复杂以及不规范,这时就会浪费程序员大量的时间来编写和调试这些代码,那到底有没有一种方法能使代码尽量少写,且又能实现各种Excel报表的生成呢,下面我们就转入正题。
我们要生成Excel报表,其实我们只需要两样东西:
1.报表的样式,也就是哪个字段应该填在哪里,哪一列应该填什么,字体大小,颜色,单元格高度,宽度,单元格是否合并,是否跨行,是否跨列,是横向报表(所谓横向报表,我自定义为:一张Excel报表就是有一条记录或者有一条记录以及其关联记录而生成的报表,如一张订单报表通常就是有一个订单头和其相关的订单明细所生成,这个就是我自定义的横向报表)还是纵向报表(所谓纵向报表,我自定义为:一张报表就是有一个记录集所生成的,如要生成一张记录某个班级学生情况的报表,则要把这个班级的学生记录都要导出到这张报表中,这种报表基本上就是统计报表,一张报表可能会有几万条记录或者更多,这个就是我自定义的纵向报表)等等报表样式。
2.报表的数据,对于我们而言有了报表样式,我们就知道在哪个或者哪几个单元格应该填哪些数据,具体的数据,我们则可以以DataTable的形式来表现,也可以以DataSet的形式来表现,至于用怎么样的数据格式可以随自己喜欢,自己来定义。
现在我们知道了生成Excel报表只要有了样式和数据,就可以生成一张自己想要的报表了,数据的生成并不困难,但是我们的困难就是这个报表的样式用什么来描述,怎么来描述。
对于样式的描述我们可以用两种方法来实现:
1.直接用XML来描述,这个xml描述文件的主要描述的内容可以如下(举个列子说明一下,现在描述的还不是很规范):
<?xml version="1.0" encoding="UTF-8"?>
<Style>
<ExcelStyle Sort="纵向" Space="1">
<Ranges>
<RangeStyle>
<Range CellWidth="2.0" CellHeight="20" CellBord="1" CellBackColor=""></Range>
<TitleCell TitleName="部件编号" TitleStartxPos="1" TitleEndxPos="1" TitleStartyPos="1" TitleEndyPos="1"></TitleCell>
<DataCell DataName="PartNo" DataStartxPos="2" DataEndxPos="14" DataStartyPos="34" DataEndyPos="34"IsMerger="False" Align="XlHAlign.xlHAlignLeft" IsToArray="True" IsDateTime="False" Interval="1">
</DataCell>
<TitleFont TitleFontName="宋体" TitleFontSize="9" TitleFontColor="Black" TitleFontBold="False" TitleFontItatic="False" TitleFontPosition="Center"> </TitleFont>
<DataFont DataFontName="宋体" DataFontSize="11" DataFontColor="Black" DataFontBold="True" DataFontItatic="False" DataFontHyperLink="False" DataFontPosition="Center">
</DataFont>
</RangeStyle>
</Ranges>
</ExcelStyle>
</Style>
这个XML中结点Excelstyle的属性Sort描述了整个Excel的格式是纵向的还是横向的(所谓纵向还是横向,我上面已有所说明),而属性Space则描述了报表每个记录的行与行之间空多少行。Ranges则是下面的所有子节点RangeStyle的父节点,对于一张报表通常会有很多的RangeStyle结点。RangeStyle其实就开始真正来描述哪个或哪些单元格来填写哪些字段,如结点Range 就定义了单元格高度,宽度,边框宽度,颜色这些属性。TitleCell就是定义了报表(因为我这张是纵向报表,所以有Title)Title的属性,如Title的名称,哪个单元格来填写这个名称。节点DataCell则定义了要帮定数据中的哪个字段,从哪个单元开始填写,单元格是否要合并,对齐方式,还有这个字段行与行之间的间隔。结点Title Font定义了Title字体属性,如大小,颜色,是否斜体,是否粗体,对齐方式。结点DataFont则定义了数据字体的属性,如大小,颜色,是否斜体,是否粗体,对齐方式。一个RangeStyle结点其实也就定义了一个字段的填写,如果你的报表要填写很多字段的话,就会有很多个RangeStyle结点来描述。这个xml样式定义文件可以自己写个工具来生成,我有现成的自己写的生成工具叫ExcelStyle,不过还很简陋,用起来还不方便,不过能生成我需要的样式,哪位需要我可以发给你们(我不知道怎么提供下载)。
2. XML描述文件和Excel模板相结合的方法,对于有些报表会很复杂,特别是那些横向的报表,有时候会极其复杂,这时就需要现成的Excel模板,有了Excel模板我们就可以参照着模板来生成xml样式定义文件,在具体生成报表的时候,我们只要知道用到的Excel模版是哪个,XML样式文件是哪个,再绑上我们的数据,那么即使再复杂的报表也同样能简单生成了。
具体使用,可参照如下测试用列代码(声明ExcelOperate,XmlOperate是我自己写的几个通用的组件,如有需要我可发邮件给你们,其中数据我是以DataTable的形式来描述的):
sing System;
using NUnit.Framework;
using ExcelOperate;
using XmlOperate;
using System.Data;
using System.Data.SqlClient;
namespace TestExcel
{
/// <summary>
/// </summary>
///
[TestFixture]
public class ExcelExport
{
[Test]
public void Export()//不用Excel模板的数据导出
{
string Sql ="Select top 100* from customers ";
//取出100条数据
ExcelFactory _factory = new ExcelFactory();
//定义一个没有参数的ExcelFactory
_factory.CreateExcelwithoutTemplate("c:\\bbbbbbb.xml",GetDataTable(Sql ));
//载入定义的样式文件bbbbbbb.xml和刚取出来的数据GetDataTable(Sql )
_factory.SaveExcel("c:\\bbbbbbbbbbbbbbbb.xls");
//保存导出的文件即可
}
[Test]
public void ExportWithTemplate()//用到了Excel模版的数据导出
{
string Sql ="Select top 1* from customers ";
//取出一条数据
ExcelFactory _factory = new ExcelFactory("c:\\QStandardCustomer.xls");
//导入模版QStandardCustomer.xls
_factory.CreateExcel("c:\\QStandardCustomer.xml",GetDataTable(Sql ));
//载入定义的样式文件QStandadrCustomer.xml和刚取出来的数据GetDataTable(Sql )
_factory.SaveExcel("c:\\Qreportstr.xls");
//最后保存导出的Excel文件即可
}
private DataTable GetDataTable(string Sql)
{
string myConnectionString = "packet size=4096;user id=sa;data source=172.28.200.98;initial catalog=Northwind";
SqlConnection myConnection = new SqlConnection(myConnectionString);
myConnection.Open();
SqlDataAdapter myAdapter = new SqlDataAdapter();
SqlCommand myCommand = new SqlCommand(Sql,myConnection);
myCommand.CommandType = CommandType.Text;
myAdapter.SelectCommand = myCommand;
DataSet ds = new DataSet();
myAdapter.Fill(ds);
myConnection.Close();
return ds.Tables[0];
}
}
}
由上面代码可见,一般的报表生成都是三句代码,即使对于报表的某些行,某些格式要调整我们也不需要在去修改程序,只要改xml描述文件即可,这样就可以使我们大大的在报表的生成上来节省时间,而可以把更多的时间放在业务逻辑的处理上了。
我们要生成Excel报表,其实我们只需要两样东西:
1.报表的样式,也就是哪个字段应该填在哪里,哪一列应该填什么,字体大小,颜色,单元格高度,宽度,单元格是否合并,是否跨行,是否跨列,是横向报表(所谓横向报表,我自定义为:一张Excel报表就是有一条记录或者有一条记录以及其关联记录而生成的报表,如一张订单报表通常就是有一个订单头和其相关的订单明细所生成,这个就是我自定义的横向报表)还是纵向报表(所谓纵向报表,我自定义为:一张报表就是有一个记录集所生成的,如要生成一张记录某个班级学生情况的报表,则要把这个班级的学生记录都要导出到这张报表中,这种报表基本上就是统计报表,一张报表可能会有几万条记录或者更多,这个就是我自定义的纵向报表)等等报表样式。
2.报表的数据,对于我们而言有了报表样式,我们就知道在哪个或者哪几个单元格应该填哪些数据,具体的数据,我们则可以以DataTable的形式来表现,也可以以DataSet的形式来表现,至于用怎么样的数据格式可以随自己喜欢,自己来定义。
现在我们知道了生成Excel报表只要有了样式和数据,就可以生成一张自己想要的报表了,数据的生成并不困难,但是我们的困难就是这个报表的样式用什么来描述,怎么来描述。
对于样式的描述我们可以用两种方法来实现:
1.直接用XML来描述,这个xml描述文件的主要描述的内容可以如下(举个列子说明一下,现在描述的还不是很规范):
<?xml version="1.0" encoding="UTF-8"?>
<Style>
<ExcelStyle Sort="纵向" Space="1">
<Ranges>
<RangeStyle>
<Range CellWidth="2.0" CellHeight="20" CellBord="1" CellBackColor=""></Range>
<TitleCell TitleName="部件编号" TitleStartxPos="1" TitleEndxPos="1" TitleStartyPos="1" TitleEndyPos="1"></TitleCell>
<DataCell DataName="PartNo" DataStartxPos="2" DataEndxPos="14" DataStartyPos="34" DataEndyPos="34"IsMerger="False" Align="XlHAlign.xlHAlignLeft" IsToArray="True" IsDateTime="False" Interval="1">
</DataCell>
<TitleFont TitleFontName="宋体" TitleFontSize="9" TitleFontColor="Black" TitleFontBold="False" TitleFontItatic="False" TitleFontPosition="Center"> </TitleFont>
<DataFont DataFontName="宋体" DataFontSize="11" DataFontColor="Black" DataFontBold="True" DataFontItatic="False" DataFontHyperLink="False" DataFontPosition="Center">
</DataFont>
</RangeStyle>
</Ranges>
</ExcelStyle>
</Style>
这个XML中结点Excelstyle的属性Sort描述了整个Excel的格式是纵向的还是横向的(所谓纵向还是横向,我上面已有所说明),而属性Space则描述了报表每个记录的行与行之间空多少行。Ranges则是下面的所有子节点RangeStyle的父节点,对于一张报表通常会有很多的RangeStyle结点。RangeStyle其实就开始真正来描述哪个或哪些单元格来填写哪些字段,如结点Range 就定义了单元格高度,宽度,边框宽度,颜色这些属性。TitleCell就是定义了报表(因为我这张是纵向报表,所以有Title)Title的属性,如Title的名称,哪个单元格来填写这个名称。节点DataCell则定义了要帮定数据中的哪个字段,从哪个单元开始填写,单元格是否要合并,对齐方式,还有这个字段行与行之间的间隔。结点Title Font定义了Title字体属性,如大小,颜色,是否斜体,是否粗体,对齐方式。结点DataFont则定义了数据字体的属性,如大小,颜色,是否斜体,是否粗体,对齐方式。一个RangeStyle结点其实也就定义了一个字段的填写,如果你的报表要填写很多字段的话,就会有很多个RangeStyle结点来描述。这个xml样式定义文件可以自己写个工具来生成,我有现成的自己写的生成工具叫ExcelStyle,不过还很简陋,用起来还不方便,不过能生成我需要的样式,哪位需要我可以发给你们(我不知道怎么提供下载)。
2. XML描述文件和Excel模板相结合的方法,对于有些报表会很复杂,特别是那些横向的报表,有时候会极其复杂,这时就需要现成的Excel模板,有了Excel模板我们就可以参照着模板来生成xml样式定义文件,在具体生成报表的时候,我们只要知道用到的Excel模版是哪个,XML样式文件是哪个,再绑上我们的数据,那么即使再复杂的报表也同样能简单生成了。
具体使用,可参照如下测试用列代码(声明ExcelOperate,XmlOperate是我自己写的几个通用的组件,如有需要我可发邮件给你们,其中数据我是以DataTable的形式来描述的):
sing System;
using NUnit.Framework;
using ExcelOperate;
using XmlOperate;
using System.Data;
using System.Data.SqlClient;
namespace TestExcel
{
/// <summary>
/// </summary>
///
[TestFixture]
public class ExcelExport
{
[Test]
public void Export()//不用Excel模板的数据导出
{
string Sql ="Select top 100* from customers ";
//取出100条数据
ExcelFactory _factory = new ExcelFactory();
//定义一个没有参数的ExcelFactory
_factory.CreateExcelwithoutTemplate("c:\\bbbbbbb.xml",GetDataTable(Sql ));
//载入定义的样式文件bbbbbbb.xml和刚取出来的数据GetDataTable(Sql )
_factory.SaveExcel("c:\\bbbbbbbbbbbbbbbb.xls");
//保存导出的文件即可
}
[Test]
public void ExportWithTemplate()//用到了Excel模版的数据导出
{
string Sql ="Select top 1* from customers ";
//取出一条数据
ExcelFactory _factory = new ExcelFactory("c:\\QStandardCustomer.xls");
//导入模版QStandardCustomer.xls
_factory.CreateExcel("c:\\QStandardCustomer.xml",GetDataTable(Sql ));
//载入定义的样式文件QStandadrCustomer.xml和刚取出来的数据GetDataTable(Sql )
_factory.SaveExcel("c:\\Qreportstr.xls");
//最后保存导出的Excel文件即可
}
private DataTable GetDataTable(string Sql)
{
string myConnectionString = "packet size=4096;user id=sa;data source=172.28.200.98;initial catalog=Northwind";
SqlConnection myConnection = new SqlConnection(myConnectionString);
myConnection.Open();
SqlDataAdapter myAdapter = new SqlDataAdapter();
SqlCommand myCommand = new SqlCommand(Sql,myConnection);
myCommand.CommandType = CommandType.Text;
myAdapter.SelectCommand = myCommand;
DataSet ds = new DataSet();
myAdapter.Fill(ds);
myConnection.Close();
return ds.Tables[0];
}
}
}
由上面代码可见,一般的报表生成都是三句代码,即使对于报表的某些行,某些格式要调整我们也不需要在去修改程序,只要改xml描述文件即可,这样就可以使我们大大的在报表的生成上来节省时间,而可以把更多的时间放在业务逻辑的处理上了。