凯锐

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  96 随笔 :: 39 文章 :: 208 评论 :: 22 Trackbacks
接我上一篇C#導出Excel源碼.
网上反应比较强烈。本人也因为工作需要的原因,将其封装了成了ExcelManager。企业当中,做报表的数据来源肯定就是数据库了。该ExcelManager目前只提供Ms Sql Server的支持,因为我们公司使用的就是ms sql server 2000 了。封装后的ExcelManager,你只需传入你的报表表头(一级表头、二级表头。大部分有两级也就够了。如果你有多个,可自行修改该类.),并将对应的数据库表字段传入类库中的方法DeclareExcelApp即可。
同前一篇一样,你可将下面代码复制另存一个新类就可以了(不知为什么,我在家里上网老是传附件不上来!faint...)。随后,我会给出一个调用的方法的:
namespace ExportToExcel
{
    
using System;
    
using System.Data;
    
using System.Data.SqlClient;
    
using System.Windows.Forms;
    
using System.Runtime.InteropServices;

    
/***********************************************************************************
     ****Class Name :   ExcelManger
     ****Author:            KingNa
     ****Create Date :   2006-9-1
     ****CopyRight:     Reserve this info if you want to User this Class
    **********************************************************************************
*/
    
public class ExcelManager:IDisposable
    {
        Excel.Range m_objRange 
= null;
        Excel.Application m_objExcel 
= null;
        Excel.Workbooks m_objBooks 
= null;
        Excel._Workbook m_objBook 
= null;
        Excel.Sheets m_objSheets 
= null;
        Excel._Worksheet m_objSheet 
= null;
        Excel.QueryTable m_objQryTable 
= null;
        
object m_objOpt = System.Reflection.Missing.Value;
        
//DataBase-used variable
        private System.Data.SqlClient.SqlConnection sqlConn = null;
        
private string strConnect = string.Empty;
        
private System.Data.SqlClient.SqlCommand sqlCmd = null;

        
//Sheets variable
        private double dbSheetSize = 65535;//the hight limit number in one sheet
        private int intSheetTotalSize = 0;//total record can divied sheet number
        private double dbTotalSize = 0;//record total number


        
/// <summary>
        
/// 建构函数
        
/// </summary>
        public ExcelManager(){}

        
/// <summary>
        
/// 建构函数
        
/// </summary>
        
/// <param name="dbHL">一个Excel表格的最大记录数</param>
        
/// <param name="dbTotal">该数据库表共查询出多少条记录</param>
        
/// <param name="intDivide">查询出的记录可分成几个Excel</param>
        
/// <param name="conn">sqlConnection</param>
        public ExcelManager(Double dbHL,Double dbTotal,int intDivide,SqlConnection conn )
        {
            dbSheetSize 
= dbHL;
            intSheetTotalSize 
= intDivide;
            dbTotalSize 
= dbTotal;
            sqlConn 
= conn;
        }
        
/// <summary>
        
/// 建构函数
        
/// </summary>
        
/// <param name="dbHL">一个Excel表格的最大记录数</param>
        
/// <param name="strTableName">需查询的数据库的表名</param>
        
/// <param name="conn">sqlConnection</param>
        public ExcelManager(Double dbHL,string strTableName,SqlConnection conn)
        {
            dbSheetSize 
= dbHL;
            sqlConn 
= conn;
            intSheetTotalSize 
= GetTotalSize(strTableName,sqlConn);
        }

        
public void Dispose()
        {
            Dispose(
true);
            GC.SuppressFinalize(
this);
        }
        
private void Dispose(bool disposing)
        {
            
if(disposing)
            {
                
// Dispose managed resources.
                Marshal.FinalReleaseComObject(m_objExcel);
                m_objRange 
= null;
                m_objSheet 
= null;
                m_objSheets 
= null;
                m_objBooks 
= null;
                m_objBook 
= null;
                m_objExcel 
= null;
            }
        }
        
/// <summary>
        
/// 取得总记录数跟可分成几个Excel sheet.
        
/// </summary>
        
/// <param name="strTableName">被查询的数据库的表名</param>
        
/// <param name="sqlConn">sqlConnection</param>
        
/// <returns>可分成Excel Sheet的个数</returns>
        private int GetTotalSize(string strTableName,SqlConnection sqlConn)
        {
            
//sqlConn = new System.Data.SqlClient.SqlConnection(strConnect);
            sqlCmd = new System.Data.SqlClient.SqlCommand("Select Count(*) From "+strTableName, sqlConn);
            
if(this.sqlConn.State == ConnectionState.Closed) sqlConn.Open();
            dbTotalSize 
= (int)sqlCmd.ExecuteScalar();
            sqlConn.Close();
            
return (int)Math.Ceiling(dbTotalSize / this.dbSheetSize);
        }

        
/// <summary>
        
/// 新建一个Excel实例
        
/// </summary>
        
/// <param name="strTitle">Excel表头上的文字</param>
        public void DeclareExcelApp(string[] strTitle,string strSql,string strTableName,string strMastTitle)
        {
            m_objExcel 
= new Excel.ApplicationClass();
            m_objExcel.Visible 
= true;
            m_objBooks 
= (Excel.Workbooks)m_objExcel.Workbooks;
            m_objBook 
= (Excel._Workbook)(m_objBooks.Add(m_objOpt));
            m_objSheets 
= (Excel.Sheets)m_objBook.Worksheets;
            
if (intSheetTotalSize <= 3)
            {
                
if (this.dbTotalSize <= this.dbSheetSize)
                {
                    
this.ExportDataByQueryTable(1false,strTitle,strSql,strTableName,strMastTitle );
                    
return;
                }
                
else if (this.dbTotalSize <= this.dbSheetSize * 2)
                {
                    
this.ExportDataByQueryTable(1false,strTitle,strSql,strTableName,strMastTitle );
                    
this.ExportDataByQueryTable(2true,strTitle,strSql,strTableName,strMastTitle );
                    
return;
                }
                
else
                {
                    
this.ExportDataByQueryTable(1false,strTitle,strSql,strTableName,strMastTitle );
                    
this.ExportDataByQueryTable(2true,strTitle,strSql,strTableName,strMastTitle );
                    
this.ExportDataByQueryTable(3true,strTitle,strSql,strTableName,strMastTitle );
                    
return;
                }
            }
            
for (int i = 3; i < intSheetTotalSize; i++)
            {
                m_objSheets.Add(m_objOpt, m_objSheets.get_Item(i), m_objOpt, m_objOpt);
            }
            ExportDataByQueryTable(
1false,strTitle,strSql,strTableName,strMastTitle );
            
for (int i = 2; i <= m_objSheets.Count; i++)
            {
                ExportDataByQueryTable(i, 
true,strTitle,strSql,strTableName,strMastTitle );
            }
        }
        
/// <summary>
        
/// 以用户输入的文件名保存文件
        
/// </summary>
        public void SaveExcelApp()
        {
            
string excelFileName = string.Empty;
            SaveFileDialog sf 
= new SaveFileDialog();
            sf.Filter 
= "*.xls|*.*";
            
if (sf.ShowDialog() == DialogResult.OK)
            {
                excelFileName 
= sf.FileName;
            }
            
else
            {
                
return;
            }
            m_objBook.SaveAs(excelFileName, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, 
                Excel.XlSaveAsAccessMode.xlNoChange, m_objOpt, m_objOpt, m_objOpt, m_objOpt,m_objOpt);
            
if (m_objExcel != null)
                m_objExcel 
= null;
        }
        
/// <summary>
        
/// 利用Excel的QueryTable导出数据
        
/// </summary>
        
/// <param name="intSheetNumber">导出第几个sheet</param>
        
/// <param name="blIsMoreThan">余下的数据是否大于指定的每个Sheet的最大记录数</param>
        
/// <param name="strTitle">表头,需与查询sql语句对齐一致。</param>
        
/// <param name="strSql">查询的sql语句,表头的文字需与该sql语句对齐一致。</param>
        
/// <param name="strTablName">查询的表名</param>    
        
/// <param name="strMastTitle">主标题</param>
        
/// </summary>
        public void ExportDataByQueryTable(int intSheetNumber, bool blIsMoreThan,string[] strTitle,string strSql,string strTablName,string strMastTitle)
        {
            
string strQuery = string.Empty;
            
if (blIsMoreThan)
            {
                strQuery 
= "Select Top " +
                    
this.dbSheetSize + strSql + "  From " + strTablName + " Where Not  OrderID In (Select Top " +
                    dbSheetSize 
* (intSheetNumber - 1+ "  OrderID From " + strTablName + ")";
            }
            
else
            {
                strQuery 
= "Select Top " + this.dbSheetSize + strSql+ "  From "+strTablName;

            }
            m_objSheet 
= (Excel._Worksheet)(m_objSheets.get_Item(intSheetNumber));

            m_objSheet.Cells[
1,1= strMastTitle;
            m_objSheet.Cells[
2,1= "打印日期"+DateTime.Now.ToShortDateString();
            
for(int i = 1;i<=strTitle.Length;i++)
            {
                m_objSheet.Cells[
4,i] = strTitle[i-1].ToString();
            }
            m_objRange 
= m_objSheet.get_Range("A5", m_objOpt);
            m_objQryTable 
= m_objSheet.QueryTables.Add("OLEDB;Provider=SQLOLEDB.1;" + sqlConn.ConnectionString, m_objRange, strQuery);
            m_objQryTable.RefreshStyle 
= Excel.XlCellInsertionMode.xlInsertEntireRows;
            m_objQryTable.FieldNames 
= false;
            m_objQryTable.Refresh(
false);
        }
    }
}

全盘复制另存新类后,调用方法示例如下:
        private void button2_Click(object sender, EventArgs e)
        {
            
#region ExcelManager封装类导出Excel
            String strConnet 
="Data Source='localhost';Password = ;User ID=sa;Initial Catalog=Northwind";
            System.Data.SqlClient.SqlConnection sqlConn 
=
                
new System.Data.SqlClient.SqlConnection(strConnet);
            ExcelManager exc 
= new ExcelManager(65530"Orders", sqlConn);
            
try
            {
                exc.DeclareExcelApp(
new string[] { "编号","供应商编号" }, " OrderID,CustomerID ""Orders""报表标题");
                
//exc.SaveExcelApp();
            }
            
catch(Exception E)
            {
                MessageBox.Show(E.ToString());
            }
            
finally
            {
                exc.Dispose();
            }
            
#endregion
        } 

以上使用的是Excel 2002 英文版。2003有些方法稍有出入。可参照前篇的C#导出Excel源码。另外,如果可能的话,我将封装其它数据库类型的Excel导出。有兴趣的朋友,请继续关继!

posted on 2006-09-01 22:28 凯锐 阅读(6912) 评论(15)  编辑 收藏 所属分类: C# Programing

评论

#1楼  2006-09-02 00:26 RicCC      
版本问题。
Office的版本太多了,这样写不仅要求安装环境需要指定Excel版本,多人开发时开发环境也必须一致,很麻烦。
可以使用后绑定方式,最简单的是用VB.Net来封装,用CreateObject方法创建Excel对象。这样项目不需要引用Excel,也不需要额外代码很容易做到同时支持Office 2000、XP、2003。
  回复  引用  查看    

#2楼  2006-09-07 14:23 LEOXIONG [未注册用户]
我也是觉得存在OFFICE,多版本的问题,楼上的说的后绑定方法,能不能写一个示例上来啊,小弟学习,UP~` UP~~`
  回复  引用    

#3楼  2006-09-07 14:28 LEOXIONG [未注册用户]
有个问题,我用的是OFFICE2003,上面的代码要用, 要修改什么地方?能不能简要说明一下, 是不是少了引用? 谢过~~谢过~~
  回复  引用    

#4楼 [楼主] 2006-09-07 19:04 精浪      
@LEOXIONG
没错,你如果需要引用上面的代码,首先需要加如下COM引用:
Microsoft Excel 11.0 Object Libary 和
Microsoft Office 11.0 Object Libary

至于后期绑定,一个示例:
private void btnExcel_Click(object sender, System.EventArgs e)
{
try
{
Type excel = Type.GetTypeFromProgID("Excel.Application");
object[] para = new object[1];
para[0] = true;
object excelobject = Activator.CreateInstance(excel);
excel.InvokeMember("Visible",BindingFlags.SetProperty,null,excelobject,para);
}
catch(Exception E)
{
MessageBox.Show(E.Message);
}
finally
{
Thread.Sleep(500);
}
}


  回复  引用  查看    

#5楼  2006-11-27 18:46 求知 [未注册用户]
有没有从excel读取数据的方法呢,最好不用类似于数据库联接字符串的那种方法
  回复  引用    

#6楼 [楼主] 2006-11-27 20:59 精浪      
@求知
参看以下这篇,是导出Excel的完整方法。
http://www.cnblogs.com/jinliangliu/archive/2006/10/25/480391.html#539368
至于你说的不用数据库联接字符串,而从Excel读至DataTable,我还真没有更好的方法。呵呵...



  回复  引用  查看    

#7楼  2006-12-12 22:59 lulu [未注册用户]
是否也可以用于asp.net呢?
我在网页中这么用了,可是运行到
m_objExcel = new Excel.ApplicationClass();
的时候,报错:拒绝访问。

拒绝访问。
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.UnauthorizedAccessException: 拒绝访问。

ASP.NET 未被授权访问所请求的资源。请考虑授予 ASP.NET 请求标识访问此资源的权限。ASP.NET 有一个在应用程序没有模拟时使用的基进程标识(通常,在 IIS 5 上为 {MACHINE}\ASPNET,在 IIS 6 上为网络服务)。如果应用程序正在通过 <identity impersonate="true"/> 模拟,则标识将为匿名用户(通常为 IUSR_MACHINENAME)或经过身份验证的请求用户。

若要授予 ASP.NET 对文件的写访问权,请在资源管理器中右击该文件,选择“属性”,然后选择“安全”选项卡。单击“添加”添加适当的用户或组。突出显示 ASP.NET 帐户,选中所需访问权限对应的框。

源错误:


行 104: public void DeclareExcelApp(string[] strTitle,string strSql,string strTableName,string strMastTitle)
行 105: {
行 106: m_objExcel = new Excel.ApplicationClass();
行 107: m_objExcel.Visible = true;
行 108: m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;

可以如何解决呢?
都不知道他说的哪个文件!
  回复  引用    

#8楼 [楼主] 2007-01-15 15:46 精浪      
后期綁定的另一示例﹐最近發現:
private void btnExcel_Click(object sender, EventArgs e)
{
try
{
Type tExcel = Type.GetTypeFromProgID("Excel.Application");
object m_objExcel = Activator.CreateInstance(tExcel);
object m_objBooks = m_objExcel.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, m_objExcel,null);
object m_objBook = m_objBooks.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, m_objBooks, null);
object m_objSheets = m_objBook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, m_objBook, null);
tExcel.InvokeMember("Visible", BindingFlags.SetProperty, null, m_objExcel, new object[] { true });
}
catch (Exception E)
{
MessageBox.Show(E.Message);
}
finally
{
Thread.Sleep(500);
}
}
以上代碼直接調用Excel﹐并不需要引用office DLL﹐順帶貼出直接調用Word的示范代碼:
Type wordType = System.Type.GetTypeFromProgID("Word.Application");
Object word = System.Activator.CreateInstance(wordType);
wordType.InvokeMember("Visible", BindingFlags.SetProperty, null, word, new Object[] {true});
Object documents = wordType.InvokeMember("Documents", BindingFlags.GetProperty, null, word, null);
Object document = documents.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, documents, null);
  回复  引用  查看    

#9楼 [楼主] 2007-01-15 17:13 精浪      
上面后期綁定Word代碼,取自于
http://blog.joycode.com/kaneboy/archive/2005/08/03/61489.aspx?Pending=true
  回复  引用  查看    

#10楼  2007-01-17 19:52 itking [未注册用户]
我用vs.net2003编译不过去
\ExcelManager.cs(73): “System.Runtime.InteropServices.Marshal”并不包含对“FinalReleaseComObject”的定义

  回复  引用    

#11楼 [楼主] 2007-01-18 08:42 精浪      
@itking
確實System.Runtime.InteropServices.Marshal是2005才有的。

  回复  引用  查看    

#12楼  2007-01-19 16:14 静谧 [未注册用户]
m_objExcel.Visible = true;
我用ASP.NET执行到上面这一句时 Visible的值还是false
为什么?????
  回复  引用    

#13楼  2007-02-02 09:05 308697935 [未注册用户]
strQuery = "Select Top " +
this.dbSheetSize + strSql + " From " + strTablName + " Where Not OrderID In (Select Top " +
dbSheetSize * (intSheetNumber - 1) + " OrderID From " + strTablName + ")";
这个语句在数据超过65530的时候,速度会突然降下了,按照条数的增加成指数增加!有办法优化吗?
  回复  引用    

#14楼  2008-07-16 00:42 xinber [未注册用户]
我想写个自动化的程序,使每遇到有一级标题、二级标题的位置就新起一页?
mail: hebihong@163.com
  回复  引用    

#15楼  2008-07-16 00:43 xinber [未注册用户]
c#如何获取到word的一级标题、二级标题?
我想写个自动化的程序,使每遇到有一级标题、二级标题的位置就新起一页?
mail: hebihong@163.com
  回复  引用    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
 
另存  打印