面具 许廷铿 歌词解释:利用XSL将DataSet数据转换为Excel文档

来源:百度文库 编辑:九乡新闻网 时间:2024/05/02 09:06:40

最近在一个ASP.NET项目的开发中有一个模块需要采用OWC控件呈现Excel样式的表格内容,表格内容比较复杂,包括锁定表头、公式、小计、合计、排序等。

以前采用客户端操作OWC实例一个单元格一个单元格的去写,那麻烦程度可想而知,于是便尝试采用新方法,利用XSL转换实现需要的功能。

经过几天一穷二白为基础的学习和摸索,总结出一点心得,还有一点疑问和未处理的问题,放在园子里,如果总结的有什么不正确的地方,还望大家指出。

我采用这种方法前,首先确认了一些限制条件的满足:

1、 在项目中可以统一使用MS Office 2003版本的OWC控件

2、 我们的客户都在使用MS Office 2003,这样这种做法也可以作为直接生成客户端Excel文档的方法

我的目标格式为以下Excel文档(部分):

 



这个表的特点是:

1、 列数固定

2、 表头锁定

3、 每个部门的费用明细数量不定

4、 小计统计各个部门所属的费用金额之和

5、 合计统计所有部门所有费用的总金额

6、 小计和合计应该设为公式,这样在修改费用明细的时候,小计和合计可以即时反映变化

首先,需要在Excel里面做好一个模板,然后另存为“XML电子表格”,模板内容如下:

 



这个模板包含了几乎所有需要的元素,包括单元格锁定、各种外观样式、行级别的公式设定(比如J4=I4-H4)、一行明细、一行小计、一行合计等一切可以在“设计时”确定的内容。

通过分析这个模板,我们观察到,完成这个模板的数据填充需要进行2层循环:

l 外层循环:需要针对每个部门进行循环,在循环之内填充每个部门的费用明细,并且为每个部门的小计进行公式设置

l 内层循环:在每个部门内,循环填充每个部门的费用明细

为了使用XSL简单起见,我们的DataSet就包含2个DataTable,第一个DataTable(命名为DepNames)包含需要填充的数据中的部门信息;第二个DataTable(命名为FeeDetail)包含所有需要填充的数据(包括部门标识和费用信息)。当然,从原则上来说,只需要第二个DataTable的数据就可以了,第一个DataTable中的数据完全可以通过XSL的某些操作得到,但是我没有查到如何通过XSL的某些操作可以达到Sql语句中类似SELECT DISTINCT SomeField FROM SomeTable的效果(那位大虾知道?),因此只能退而求其次。

DepNames表包含的信息如下:

DepCode         DepName

10101                A部门

10102                B部门

……                  ……

 

FeeDetail表包含的信息如下:

DepCode         FeeCode          ……

10101                550101     ……

10102                640101     ……

……                  ……                  ……

通过DataSet的GetXml()方法得到的XML文档格式如下:

 


 
    101010005
    一个部门
 

 
    101010006
    又一个部门
 

 
    101010005
    66010102
    一项费用
0.0000
 

    
    101010006
    66010103
    另一项费用
  0.0000
 



可见,得到的DataSet数据中,节点是DataSet的名称;节点和节点分别代表DepNames结果集的行数据和FeeDetail结果集的行数据,它们的子节点则代表每行的各列数据值。

XSL各种语法很丰富,不过通常我们只需要利用其中的一小部分就能达到我们的目的。下面我们来分析一下模板背后的XML文档格式。

Excel文件另存的XML文档,似乎很长,内容很复杂,但是,借助VS2005以及IE对XML文档中节点良好的“折叠”特性,我们可以很容易的分析出文档中的一些主要元素,另外MS也提供了MS Office 2003的XML Schema参考,对我们也有很大帮助。

我们主要需要关注XML文档中的节点及其子节点,很明显,它代表Excel文档中的各个Worksheet,对于我们的文档,节点中包含了一个

节点,样例如下:

 x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">

其中,我们主要需要关注2个属性:

l ExpandedColumnCount:代表Excel文档中的列数

l ExpandedRowCount:代表Excel文档中的行数

以上2个属性如果比文档中实际数据的行数和列数小,通常就会导致文档错误而无法用Excel打开,因此我们生成的文档,需要根据实际数据行数来更新ExpandedRowCount属性的值,或者把他们设置为一个非常大的数。

节点的子节点包括若干节点和节点,这很清楚代表表格中的行信息和列信息,对于我们这次的任务,只需要关心节点,一个简单的节点类似以下格式:


         
                   这是单元格的内容
         

其中,节点的ss:StyleID属性代表单元格的样式ID,引用Styles节点中的样式节点ID,节点的ss:Type属性代表单元格值的类型,可以是String(字符串)、Number(数值)、DateTime(日期)等类型。

我们找到样例中示例数据所在的节点,结合DataSet的结构并利用XSL循环语法完成2层循环操作:


         
         
                    
                   
         
         

以下对上面的语句逐一解释:

外层循环,选取DataSet中的所有DepNames节点进行循环,也就是说对每一个部门进行循环

声明一个变量,这个变量存放第一层循环中的当前行的部门编码,用以控制第二层循环只获取当前部门所属的费用

内层循环,选取DataSet中部门编码为第一层循环当前部门编码的费用明细项进行循环,注意表达这种意思的XPath语法,其中$DepCode即为我们上面所定义的那个变量

对选取的费用明细按照费用编码排序

这是费用明细行,下面会说明的子节点(单元格)的填充。

内层循环结束

费用小计行,后面会说明费用小计的单元格如何填充

外层循环结束

最后的费用合计

 

通过上面的2层循环框架,我们可以得到需要的结果格式。下面,我们简单说明一下费用明细和费用小计的节点的填充。

节点的填充比较简单,利用XSL的value-of语法取代模板中的示例值将DataSet中的内容直接填充上去就可以了:


 
  
 

其中select属性的内容是相对于循环所在的当前节点的相对路径。

这里主要需要说明的是对Excel中公式的处理。由于我们的Excel行数不定,因此小计和合计不能简单地写一个固定的公式就能搞定,有一定动态性、相对性。

好在Excel的公式描述形式就是动态的(当然,利用绝对单元格引用的公式除外)。我们来看一个简单的公式:

公式中,单元格是以R[X]C[X]的形式标识的,其中,R和C代表行(Row)和列(Column),而中括号中的数字则代表相对于当前单元格的目标单元格的位置,

比如RC[-2]代表这样一个单元格:与当前单元格在同一行,列在当前单元格所在列的前2列的那个单元格。

我们首先应该考虑如何描述费用小计的公式,然后把我们的描述转化为具体的公式。

我们可以这样描述我们的费用小计共识:费用小计=对 当前单元格的上一单元格 与 当前单元格的上费用明细个数个单元格 的求和。

其中费用明细个数,是指费用小计所属的部门下的费用明细个数。

然后我们可以将它转化为以下XSL描述:


 
 =SUM(R[-1]C:R[- 
  
 ]C) 
 
 
  
 
 

注意XSL中为一个节点追加属性的做法,通过来实现

 对于合计,公式比较难设,本来没想到什么很好的办法,不过我一个同事提到了,可以将所有数据行(包括费用明细和费用小计行)求和然后除以2,就差不多能达到效果,这是个不错的注意,真是数学高手,头脑够灵活,佩服,佩服……具体我就不写了。

 点击这里下载本文所说的完整的xsl文件。

由于时间有限,文章写得有一种虎头蛇尾的感觉,望大家见谅 :) 

 

 

 

 

 



 
 
    xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:html="http://www.w3.org/TR/REC-html40">
   
    林立
    林立
    2007-01-16T07:17:25Z
    2007-01-16T09:20:39Z
    Microsoft
    12.00
   

   
    
    
    1024x768
   

   
    4590
    15420
    -15
    4545
    False
    False
   

   
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
   

   
    

     x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">
     
      
     

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
      
       年度
      

      
       月份
      

      
       费用编码
      

      
       费用明细
      

      
       费用类别
      

      
       属性
      

      
       责任部门
      

      
       前期费用预算分析
      

      
       年度预算
      

      
       上月止累计年度
      

      
       上月止累计
      

      
       上月止累计
      

      
       本月费用
      

      
       审核确认
      

      
       DepartmentLineID
      

      
       DepCode
      

     

     
      
       累计预算(T-1)
      

      
       累计实际(T-1)
      

      
       
      

      
       分解到月
      

      
       预算分解到月金额
      

      
       实际发生金额
      

      
       结余金额
      

      
       预算金额
      

      
       预算金额
      

     

     
      
      
      
      
      
      
      
      
      
      
      
       1
      

      
       2
      

      
       3
      

      
       4=3-2
      

      
       5=1+4
      

      
     

     
      
      
       
       
        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

        
         
          
         

        

       

      

      
       
        小计
       

       
       
       
       
       
       
       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

       
        =SUM(R[-1]C:R[-]C)
        
         
        

       

      

     

     
      
       合计
      

      
      
      
      
      
      
      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

      
       
        
       

      

     

    

    
     
      

      

      
     
     
     
     
     
     3
     3
     7
     7
     0
     
      
       3
      

      
       1
      

      
       2
      

      
       0
      

     

     False
     False
    
   
  
 

分享到: