几年前,我们做项目的时候要求能从ASP.NET输出PDF文档。因为当时大家都爱用Access,我也承认,Access的报表做的不错。至今如果要我来做单机报表,我还是会选择用Access。当时要做Web版本的输出,当年VS2003好像去掉了Crystal Report功能,我们以为大势已去,纷纷投向Microsoft Report的怀抱。
刚开始的时候,我们就需要使用Object Data Source。RDL对此支持真的很差。当时甚至想直接连Database。但BO的处理太复杂,更本没有办法直接调用DB。最好的办法其实是手工修改RDL文件。标准的XML。例如
< DataSet Name ="Contact" >
< Fields >
< Field Name ="Title" >
< DataField > Title </ DataField >
< rd:TypeName > System.String </ rd:TypeName >
</ Field >
< Field Name ="Firstname" >
< DataField > FirstName </ DataField >
< rd:TypeName > System.String </ rd:TypeName >
</ Field >
< Field Name ="Lastname" >
< DataField > LastName </ DataField >
< rd:TypeName > System.String </ rd:TypeName >
</ Field >
</ Fields >
</ DataSet >
</ DataSets >
有时候需要在Object中增添新的属性在get中可以任意处理数据,在RDL中拿来就可以用,很方便。这点是RDL比较Access的一个最大的优势,也是我为什么一直坚持用Object 数据源。
比较简单的报表处理起来还挺直观的。有Table, Matrix可供选择。稍微复杂点的,用List试试看。他们都有Group功能。有些项目需要3,4层Group,可以用List嵌套的方式。层间显示标题。这比Table要好。因为Table的内部都是格子,没法精确定位。但Table有个好处,就是换页的时候可以要分组标题直接显示在页上方。List也不是说不可以,我找了很多时候,才发现一个英文介绍的方法,而且还不是完美方案。我的方法是建立在此基础上的,也不完美,却很有效。
比如我们有个Group header 叫type,所在tablebox叫tbType。在repeat的地方建立一个tablebox叫tbTypeCopy。值为Fields!Type.Value,把它隐藏起来。这样有些页有tbType,有些只有tbTypeCopy,有些tbType不是第一个tbTypeCopy。那么header就可以来显示跨页的group的header。我们在header上建立First(ReportItems!tbTypeCopy.Value),直接读取第一个隐藏tb的值。它的hidden属性是什么呢?Expression为 =Code. HideTypeHeader(ReportItems) 在Report Properties/Code下输入:
return Items("tbType1").Value = Items("tbType2").Value
end function
为什么要这样呢?RDL很奇怪,不允许我们直接来处理二个ReportItem对象。tbType1 和tbType2是新建的在header中的隐藏TableBox。分别取用First(ReportItems("tbType").Value)和First(ReportItems("tbTypeCopy").Value)的值。其实这些只是在为了做一个比较而已。因为微软的那些不成熟的技术,我们只能吃亏些了。以后版本据说会扩展ReportItems对象。
利用header和footer是很重要的。上面的例子就隐藏了第一页。在body中可以写第一页的title,再duplicate一个“假的”table header看上去就会很连贯。注意header不能太高,否则 第一页会很难看。而且header的top margin和padding基本上应设为0。