[C#][转]在C#中利用Excel做高级报表

Visual Studio.Net 自2001年2月问世以来,受到越来越多人的喜爱,C#做为主力军,集VB、Delphi的简单和VC的简炼与强大于一体,更是让许多人爱不释手,纷纷倒向它的怀抱。通常的软件都要用到数据库,数据库中必然要用到报表,在Visual Studio.Net中自带了一个水晶报表,虽然功能十分强大,但市面上相关资料非常缺乏,网上全面介绍其使用的文章也屈指可数。Excel是微软公司办公自动化套件中的一个软件,主要是用来处理电子表格。Excel以其功能强大,界面友好等受到了许多用户的欢迎,几乎每一台机器都安装了它,因此,我们可以将数据导入Excel进行排版。 

由于Excel的格式是封闭的,无法直接创建一个Excel文件然后来排版,只有借助Com组件来完成,同样,介绍用C#操作Excel的文章也就那么几篇,基本上都是告诉你如何新建一个Excel文件,然后,将数据写入某单元格,最多再零星告诉你如何合并单元格,真正使用起来根本无法用C#做出漂亮报表。本文巧妙利用Excel的宏来自动排版,大大减少了工作量,而且可以随时修改模板而无须修改程序,非常实用。

本人使用的是Office 2000,操作系统为windows 2000 professinal,为使问题简单化,这里不介绍数据库的知识,我一个二维数组来代表一个数据库中的表,我们的目的是将该数组放到Excel中,并排版成需要的格式,数组如下:

车牌号 
 型 品 牌 型 号 颜 色 附加费证号 车架号 
浙KA3676 危险品 货车 铁风SZG9220YY 白 1110708900 022836 
浙KA4109 危险品 货车 解放CA4110P1K2 白 223132 010898 
浙KA0001A 危险品 货车 南明LSY9190WS 白 1110205458 0474636 
浙KA0493 上普货 货车 解放LSY9190WS 白 1110255971 0094327 
浙KA1045 普货 货车 解放LSY9171WCD 蓝 1110391226 0516003 
浙KA1313 普货 货车 解放9190WCD 蓝 1110315027 0538701 
浙KA1322 普货 货车 解放LSY9190WS 蓝 24323332 0538716 
浙KA1575 普货 货车 解放LSY9181WCD 蓝 1110314149 0113018 
浙KA1925 普货 货车 解放LSY9220WCD 蓝 1110390626 00268729 
浙KA2258 普货 货车 解放LSY9220WSP 蓝 1110481542 00320 

为了在C#中使用Excel,我们要先做一点准备工作,通过查找(前提是你安装Visual Studio.Net和Excel 2000),在你的计算机中找到TlbImp和Excel9.olb,将他们复制到一个文件夹中,在DOS窗口中执行 TlbImp Excel9.olb,这时会产生以下三个文件:Excel.dll、Office.dll和VBIDE.dll。

我们来完成两项任务,一是按网上文章介绍的
方法,增加将数据写入一个Excel文件,也就是做一个简单报表,二是用Excel创建一个文件,然后以此文件为模板生成高级报表。

打开Visual Studio.Net,新建一个C#的windows应用程序,取名为MyExcel。根据个人爱好,对窗口做一些美化工作,然后放两个按钮:btnNormal和btnAdvance,Caption分别为“普通报表”和“高级报表”。

打开Visual Studio.Net,新建一个C#的windows应用程序,取名为MyExcel。根据个人爱好,对窗口做一些美化工作,然后放两个按钮:btnNormal和btnAdvance,Caption分别为“普通报表”和“高级报表”。

点“打开”按钮,再点“确定”按钮。 

切换到代码窗口中,在文件头添加下面两个引用: 

using System.IO; 
using System.Reflection; 

再添加一个二维数组来表示数据表:
private string [,] myData=  

         


              
{"车牌号","类型","品 牌","型 号","颜 色","附加费证号","车架号"}

              
{"浙KA3676","危险品","货车","铁风SZG9220YY","","1110708900","022836"}

              
{"浙KA4109","危险品","货车","解放CA4110P1K2","","223132","010898"}

              
{"浙KA0001A","危险品","货车","南明LSY9190WS","","1110205458","0474636"}

              
{"浙KA0493","上普货","货车","解放LSY9190WS","","1110255971","0094327"}

              
{"浙KA1045","普货","货车","解放LSY9171WCD","","1110391226","0516003"}

              
{"浙KA1313","普货","货车","解放9190WCD","","1110315027","0538701"}

              
{"浙KA1322","普货","货车","解放LSY9190WS","","24323332","0538716"}

              
{"浙KA1575","普货","货车","解放LSY9181WCD","","1110314149","0113018"}

              
{"浙KA1925","普货","货车","解放LSY9220WCD","","1110390626","00268729"}

              
{"浙KA2258","普货","货车","解放LSY9220WSP","","111048152","00320"} 

      }



切换回设计窗口,双击“普通报表”按钮,设计普通报表,代码如下:

private void btnNormal_Click(object sender, System.EventArgs e) 
         

              
//创建一个Excel文件 

              Excel.Application myExcel 
= new Excel.Application ( ) ; 

              myExcel.Application.Workbooks.Add ( 
true ) ; 

              
//让Excel文件可见 

              myExcel.Visible
=true

              
//第一行为报表名称 

              myExcel.Cells[
1,4]="普通报表"

              
//逐行写入数据, 

              
for(int i=0;i<11;i++
              

                   
for(int j=0;j<7;j++
                   

                       
//以单引号开头,表示该单元格为纯文本 
                       myExcel.Cells[2+i,1+j]=""+myData[i,j]; 
                   }
 
              }
 
         }
 

说明一下,Cells[2,1]指第2行第1个单元格,是以1为基准的,而在C#中的数组是以0为基准的,另外,我们还发现,对于编号之的数据,实际是文本,而Excel将它认成了数字,由于太长,自动换成了科学计数,这不是我们要求的,在Excel中,如果某单元格以单引号“’”开头,表示该单元格为纯文本,因此,我们在每个单元格前面加单引号。

运行结果如下:

可以看出,该报表非常简陋,标题行没有合并局,字体大小也不合适,连表格线都没有。当然,我们可以写代码来设置单元格字体、大小等等工作,这技巧网上很多,但如果真要用C#来完成,是一件非常难的事情,还有个办法就是将想要的操作录制成宏,研究一下宏代码,但宏是用VBA写的,要转换成果C#可不是件容易的事情。

第一种办法不是本文的重点,就到此为止

下面进行高级报表设计,该
方法的原理为:首先打开Excel,按照要求排好版,保存为一个文件做为模板,然后在C#中将该文件复制为一个新文件,在指定位置填入数据就可以了,为了添加表格线,我们录制了一个宏,在C#中执行该宏即可。

参考模板如下:

当然,你还可以排得更漂亮,因为是单纯的Excel操作,不需要特殊说明。如果记录很多,往往一页无法打印完成,我们要求在每一页都显示报表标题和小标题,也就是上图中的第1、2行,这里有一个技巧:选择Excel的菜单“文件”→“页面设置”,选择“工作表”,在“顶端标题行”后的框中输入“$1:$2”,也就是1~2行,当然,你也点右边的红箭头,然后用鼠标选择。当你的记录超过一页时,会自动在下一页加入标题,非常方便。

表格中目前还没有表格线,因为我们不知道到底有多少数据,因此,也无法知道为多少单元格设置边框,我们借助宏来完成。 

按下面步骤录制一个宏:

1、随便选择几个单元格;
2、选择菜单“工具”→“宏”→“录制新宏”,输入宏的名称,就用默认的“宏1”吧,点确定;
3、选择菜单“格式”→“单元格”,在对话框中选择“边框”,将内边框和外边框均选中,按确定;
4、此时,刚才选择的单元格就有了边框,再点工具栏中的“停止录制宏”按钮 来结束宏录制。

刚才的操作目的是录制宏而不是加边框,因此,我们按“Ctrl+Z”来撤消刚才的操作,通过按Alt+F8来调出宏,选择“宏1”,选择编辑,看到的代码应该如下: 

Sub 宏1() 
’ 宏1 Macro 
’ xx 记录的宏 
2003-5-1
 

    Selection.Borders(xlDiagonalDown).LineStyle 
=
 xlNone 

    Selection.Borders(xlDiagonalUp).LineStyle 
=
 xlNone 

    
With
 Selection.Borders(xlEdgeLeft) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

    
With
 Selection.Borders(xlEdgeTop) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

    
With
 Selection.Borders(xlEdgeBottom) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

    
With
 Selection.Borders(xlEdgeRight) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

    
With
 Selection.Borders(xlInsideVertical) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

    
With
 Selection.Borders(xlInsideHorizontal) 

        .LineStyle 
=
 xlContinuous 

        .Weight 
=
 xlThin 

        .ColorIndex 
=
 xlAutomatic 

    
End With
 

End Sub
 

图中表的数据都是供排版参考用的,结束前将实际内容删除掉,即只留下排好版的格式,包括标题、列标题等,将实际内容去掉。将文件保存到一个地方,如D:\Normal.xls,当然,实际开发时,可以放到执行文件所在目录下,为了防止用户随便修改,可以将文件名改为normal.rpt之类。 

有了上面的准备,我们就可以在C#中使用了,添加“高级报表”按钮的响应代码。下面是全部代码清单。
using System; 

using
 System.Drawing; 

using
 System.Collections; 

using
 System.ComponentModel; 

using
 System.Windows.Forms; 

using
 System.Data; 

  

using
 System.IO; 

using
 System.Reflection; 

  

namespace
 MyExcel 



     
/// <summary> 

     
///
 Form1 的摘要说明。 

     
/// </summary> 


     
public class Form1 : System.Windows.Forms.Form 

     


         
private
 System.Windows.Forms.Button btnNormal; 

         
private
 System.Windows.Forms.Button btnAdvace; 

         
/// <summary> 

         
///
 必需的设计器变量。 

         
/// </summary> 


         
private System.ComponentModel.Container components = null

  

         
public
 Form1() 

         


              
//
 

              
//
 Windows 窗体设计器支持所必需的 

              
// 


              InitializeComponent(); 

  

              
// 

              
//
 TODO: 在 InitializeComponent 调用后添加任何构造函数代码 

              
// 


         }
 

  

         
/// <summary> 

         
///
 清理所有正在使用的资源。 

         
/// </summary> 


         
protected override void Dispose( bool disposing ) 
         


              
if
( disposing ) 

              


                   
if (components != null
)  

                   


                       components.Dispose(); 

                   }
 

              }
 

              
base
.Dispose( disposing ); 

         }
 

  

         
Windows Form Designer generated code
 

  

         
/// <summary> 

         
///
 应用程序的主入口点。 

         
/// </summary> 


         [STAThread] 

         
static void Main()  

         


              Application.Run(
new
 Form1()); 

         }
 

  

         
private string [,] myData=
  

         


              
{"车牌号","类型","品 牌","型 号","颜 色","附加费证号","车架号"}


              
{"浙KA3676","危险品","货车","铁风SZG9220YY","","1110708900","022836"}


              
{"浙KA4109","危险品","货车","解放CA4110P1K2","","223132","010898"}


              
{"浙KA0001A","危险品","货车","南明LSY9190WS","","1110205458","0474636"}


              
{"浙KA0493","上普货","货车","解放LSY9190WS","","1110255971","0094327"}


              
{"浙KA1045","普货","货车","解放LSY9171WCD","","1110391226","0516003"}


              
{"浙KA1313","普货","货车","解放9190WCD","","1110315027","0538701"}


              
{"浙KA1322","普货","货车","解放LSY9190WS","","24323332","0538716"}


              
{"浙KA1575","普货","货车","解放LSY9181WCD","","1110314149","0113018"}


              
{"浙KA1925","普货","货车","解放LSY9220WCD","","1110390626","00268729"}


              
{"浙KA2258","普货","货车","解放LSY9220WSP","","111048152","00320"}
 

         }


  

          
//普通报表,即单纯的文件导出功能 


         
private void btnNormal_Click(object sender, System.EventArgs e) 

         


              
//创建一个Excel文件 


              Excel.Application myExcel 
= new Excel.Application ( ) ; 

              myExcel.Application.Workbooks.Add ( 
true
 ) ; 

              
//让Excel文件可见 


              myExcel.Visible
=true

              
//第一行为报表名称 


              myExcel.Cells[
1,4]="普通报表"

              
//逐行写入数据, 


              
for(int i=0;i<11;i++
              

                   
for(int j=0;j<7;j++

                   

                       
//以单引号开头,表示该单元格为纯文本 

                       myExcel.Cells[2+i,1+j]=""+myData[i,j]; 
                   }
 
              }
 
         }
 

         
//高级报表,根据模板生成的报表 


         
private void btnAdvace_Click(object sender, System.EventArgs e) 
         


              
string filename=""


              
//将模板文件复制到一个新文件中 


              SaveFileDialog mySave
=new SaveFileDialog(); 

              mySave.Filter
="Excel文件(*.XLS)|*.xls|所有文件(*.*)|*.*"


              
if(mySave.ShowDialog()!=
DialogResult.OK) 

              


                   
return


              }
 

              
else
 

              


                   filename
=
mySave.FileName; 

                   
//
将模板文件copy到新位置,建议实际开发时用相对路径,如 
                   
//Application.StartupPath.Trim()+"\\report\\normal.xls" 


                    

                   
string filenameold=mySave.FileName; 

                   FileInfo mode
=new FileInfo("d:\\normal.xls"
); 

                   
try
 

                   


                       mode.CopyTo(filename,
true
); 

                   }
 

                   
catch
(Exception ee) 

                   


                       MessageBox.Show(ee.Message); 

                       
return


                   }
 

  

              }
 

  

              
//打开复制后的文件 


              
object missing=Missing.Value; 

              Excel.Application myExcel
=new
 Excel.Application ( ); 

              
//打开新文件 


              myExcel.Application.Workbooks.Open(filename,missing,missing,missing,missing, 
                missing,missing,missing,missing,missing,missing, missing,missing);  

              
//将Excel显示出来 

              myExcel.Visible
=true
              
//逐行写入数据,数组中第一行我列标题,忽略 


              
for(int i=1;i<11;i++
              

                   
for(int j=0;j<7;j++

                   

                       
//以单引号开头,表示该单元格为纯文本 


                       myExcel.Cells[
4+i,1+j]=""+myData[i,j]; 

                   }
 

              }
 

              
//将列标题和实际内容选中 


              Excel.Workbook myBook
=myExcel.Workbooks[1]; 

              Excel.Worksheet mySheet
=(Excel.Worksheet)myBook.Worksheets[1
]; 

              Excel.Range r
=mySheet.get_Range(mySheet.Cells[3,1],mySheet.Cells[14,7
]); 

              r.Select(); 

              
//=====通过执行宏来格表格加边框=======// 


              
try 

              


                   myExcel.Run(
"宏1"
,missing,missing,  

  

                       missing,missing,missing,missing,missing,missing,missing,  

  

                       missing,missing,missing,missing,missing,missing,missing,  

  

                       missing,missing,missing,missing,missing,missing,missing,  

  

                       missing,missing,missing,missing,missing,missing,missing);  

              }
 

              
catch
 

              


              }
 

              
//保存修改 


              myBook.Save(); 
         }
 

     }
//end of form 
}
 


在上述代码中,我们指定了选定范围:
Excel.Range r=mySheet.get_Range(mySheet.Cells[3,1],mySheet.Cells[14,7]); 

具体开发时,我们可以根据数据库中的实际数据来计算范围,我们的列标题是从.Cells[3,1]开始的,在程序中定死了,为灵活使用,我们完全可以在模板的Cells[1,1]或者其他单元格填入一些基本信息,如实际数据起始位置等等,操作时,从该单元格读入数据,然后将该单元格内容替换成需要的内容。还有个问题,我们往单元格中写内容时假设某列应该放什么内容,为灵活起见,我们在得到了列标题起始位置后,读入该单元格内容(即该列应该是什么字段),再从数据库中找到相应的字段来填充该列,可以保证所填内容与设计的报表对应起来,还可以忽略数据库中无用的字段,也就是说同一个数据库表可以有许多种报表,只要有相应的模板就可以了,读入某单元格内容的代码如下:

 Excel.Range r; 
     r
=mySheet.get_Range(mySheet.Cells[2,1],mySheet.Cells[2,1]);  //取得值存放的区域 
     string strValue=r.Value.ToString(); 

一次只能读一个单元格,否则得不到相应的数据,即=mySheet.get_Range(mySheet.Cells[2,1],mySheet.Cells[2,1])中两个参数都必须是同一个单元格,本例中为mySheet.Cells[2,1]。

有了上面的知识,再做报表就简单多了,本软件运行结果如下:
 

posted on 2007-07-20 12:58 ffan 阅读(1994) 评论(1)  编辑 收藏 引用 所属分类: C/C++/VC

评论

# re: [C#][转]在C#中利用Excel做高级报表 2009-04-03 11:54 yazhoucom

值到学习  回复  更多评论   

只有注册用户登录后才能发表评论。
<2007年7月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(12)

随笔分类

随笔档案

相册

搜索

最新评论

阅读排行榜

评论排行榜