日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)C#.NET → .NET編程實現(xiàn)采用COM組件導(dǎo)出Excel文件實例

      .NET編程實現(xiàn)采用COM組件導(dǎo)出Excel文件實例

      相關(guān)軟件相關(guān)文章發(fā)表評論 來源:西西整理時間:2012/10/19 16:36:48字體大小:A-A+

      作者:西西點擊:144次評論:1次標(biāo)簽: Excel

       將.NET數(shù)據(jù)導(dǎo)出為Excel文件,有許多種方法,我這里介紹采用COM組件來操作Excel文件,并且還會涉及異步、同步、進程管理、文件定位等內(nèi)容,使用WPF做到一個盡量可用的導(dǎo)出界面。

      一、WPF前臺

        這個就不用多說了,堆上幾個按鈕,做一個數(shù)據(jù)錄入的東西,一個狀態(tài)條:

        我這里的數(shù)據(jù)錄入,就是用了幾個Textbox,實際上大家可以用任何東西(DataGrid、ListView等),因為在最后都會轉(zhuǎn)成List<MyData>的形式進行導(dǎo)出的,MyData是表示數(shù)據(jù)記錄的對象:

      1 // 自定義數(shù)據(jù)類
      2 public struct MyData
      3 {
      4      public string Col1, Col2, Col3;
      5      public MyData(string col1, string col2, string col3)
      6      {
      7           Col1 = col1; Col2 = col2; Col3 = col3;
      8      }
      9 }

      二、后臺

        1、錄入組織數(shù)據(jù)就不說了,先來說下選擇默認導(dǎo)出路徑:

      1 using Forms = System.Windows.Forms;
      2 // 選擇導(dǎo)出目錄
      3 private void SelectPath()
      4 {
      5         var dialog = new Forms.FolderBrowserDialog();
      6         dialog.ShowDialog();
      7         string path = dialog.SelectedPath;
      8         if (path != "")
      9         {
      10              _path = path;           
      11              if (path[path.Length - 1] != '\\')
      12             _path += '\\';
      13         }
      14 } 

        代碼使用System.Windows.Forms命名空間下的FloderBrowserDialog來選擇目錄,并把選擇的path保存到全局變量中,另外還有一個判斷,如果路徑結(jié)尾不是'\\'的話,就加上這個字符,以便于后面合成文件全路徑。效果圖:

        

        2、如果是導(dǎo)出到非默認的路徑,并命名文件,則:

      1 // 保存文件到指定目錄
      2 private void SaveFile()
      3 {
      4     // ....
      5     var dialog = new Forms.SaveFileDialog();
      6     dialog.FileOk += new CancelEventHandler((o, e) =>
      7     {
      8         BTN_Export.Content = "取消";
      9         var fullName = dialog.FileName;
      10         int i = fullName.LastIndexOf('\\') + 1;
      11         int j = fullName.LastIndexOf('.');
      12         _bgWorker.RunWorkerAsync(new ExportInput<MyData>(_sources,
      13             fullName.Substring(0, i),
      14             fullName.Substring(i, j - i),
      15             fullName.Substring(j, fullName.Length - j), _heads));
      16     });
      17     dialog.InitialDirectory = ServerPath;
      18     dialog.DefaultExt = ".xlsx";
      19     dialog.FileName = "MyData";
      20     dialog.Filter = "Excel 2010文檔|*.xlsx|Excel 2003文檔|*.xls";
      21     dialog.ShowDialog();
      22     // ...                   
      23 }

        這里使用了System.Windows.Froms的SaveFileDialog方法,彈出一個文件保存對話框,我們輸入、選擇路徑、文件名、后綴后,點擊“保存”,就能通過dialog.FileName得到全路徑,然后分別截取目錄、文件名、后綴,構(gòu)成參數(shù)類ExportInput<MyData>,以啟動后臺線程進行導(dǎo)出。

      ExportInput參數(shù)類

      1     /// <summary>
      2     /// 導(dǎo)出成Excel文件時需要傳入的參數(shù)類
      3     /// </summary>
      4     public class ExportInput<T>
      5     {
      6         /// <summary>
      7         /// 數(shù)據(jù)源
      8         /// </summary>
      9         public IEnumerable<T> Sources { get; set; }
      10         /// <summary>
      11         /// 列的表頭
      12         /// </summary>
      13         public IEnumerable<string> Headers { get; set; }
      14         /// <summary>
      15         /// 文件的名稱
      16         /// </summary>
      17         public string FileName { get; set; }
      18         /// <summary>
      19         /// 文件的絕對路徑
      20         /// </summary>
      21         public string Path { get; set; }
      22         /// <summary>
      23         /// 文件后綴
      24         /// </summary>
      25         public string Ext { get; set; }
      26
      27         /// <summary>
      28         /// 構(gòu)造傳入?yún)?shù)
      29         /// </summary>
      30         /// <param name="sources">數(shù)據(jù)源</param>
      31         /// <param name="filename">文件名</param>
      32         /// <param name="path">文件的絕對路徑</param>
      33         /// <param name="headers">列的表頭</param>
      34         public ExportInput(IEnumerable<T> sources, string path, string filename, string ext, IEnumerable<string> headers = null)
      35         {
      36             Sources = sources;
      37             FileName = filename;
      38             Path = path;
      39             Ext = ext;
      40             Headers = headers;
      41         }
      42     }

      3、導(dǎo)出時使用的后臺線程來自System.ComponentModel.BackgroundWorker,使用它可以非常方便地完成線程運行、取消、通知的功能:

      1 private BackgroundWorker _bgWorker = new BackgroundWorker();
      2 // 初始化
      3 private void Window_Loaded(object sender, RoutedEventArgs e)
      4 {
      5     // ...
      6     _bgWorker.WorkerReportsProgress = true;
      7     _bgWorker.WorkerSupportsCancellation = true;
      8     _bgWorker.DoWork += new DoWorkEventHandler(ExcelHelper.ExportMyData);
      9     _bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnWorkCompleted);
      10     _bgWorker.ProgressChanged += new ProgressChangedEventHandler(OnProgressChanged);
      11 }
      12 // 報告進度
      13 private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
      14 {
      15     PB_State.Value = e.ProgressPercentage;
      16 }
      17 // 導(dǎo)出完成
      18 private void OnWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      19 {
      20     if (e.Error != null)
      21       MessageBox.Show("導(dǎo)出失。" + e.Error.Message);
      22     else if (e.Cancelled)
      23         MessageBox.Show("已取消導(dǎo)出!");
      24     else
      25         MessageBox.Show("導(dǎo)出成功!");
      26 }

        WorkerReportsProgress、WorkerSupportsCancellation這兩個布爾值分別是是否支持報告后臺線程進度、是否支持取消后臺線程的功能,DoWork是后臺工作線程的委托,在上面代碼中,用的是ExcelHelper.ExportMyData這個靜態(tài)事件處理函數(shù)來完成導(dǎo)出功能。RunWorkerCompleted、ProgressChanged 分別是工作完成、進度改變時回調(diào)給前臺的委托。

        4、終于到了導(dǎo)出的部分了,代碼如下:

      導(dǎo)出Excel

      1     using Excel = Microsoft.Office.Interop.Excel;
      2    
      3     /// <summary>
      4     /// 導(dǎo)出成Excel文件
      5     /// </summary>
      6     public class ExcelHelper
      7     {
      8         /// <summary>
      9         /// 導(dǎo)出Excel時使用的同步
      10         /// </summary>
      11         private static object syncRoot = new object();
      12
      13         /// <summary>
      14         /// 將數(shù)據(jù)集導(dǎo)出為Excel文件
      15         /// </summary>
      16         public static void ExportMyData(object sender, DoWorkEventArgs e)
      17         {
      18             // 創(chuàng)建Excel
      19             Monitor.Enter(syncRoot);
      20             var proListStart = Process.GetProcessesByName("EXCEL");
      21             Excel.Application excelApp = new Excel.Application();
      22             var proList = Process.GetProcessesByName("EXCEL").Except(proListStart, new ProcessComparer());           
      23             Monitor.Exit(syncRoot);
      24             try
      25             {
      26                 // 檢查參數(shù)
      27                 var input = (ExportInput<MyData>)e.Argument;
      28                 var bgWorker = (BackgroundWorker)sender;
      29                 // 創(chuàng)建工作簿
      30                 Excel.Workbook excelDoc = excelApp.Workbooks.Add();
      31                 // 創(chuàng)建工作表
      32                 Excel.Worksheet excelSheet = (Excel.Worksheet)excelDoc.Worksheets[1];
      33                 // 數(shù)字類型以文本格式顯示
      34                 excelSheet.Cells.NumberFormat = "@";
      35                 // 單元格索引從1開始
      36                 int i = 1, j = 1, count = input.Sources.Count();
      37                 // 導(dǎo)入標(biāo)題
      38                 if (input.Headers != null)
      39                 {
      40                     foreach (string head in input.Headers)
      41                         excelSheet.Cells[1, j++] = head;
      42                     ++i;
      43                 }
      44                 //將數(shù)據(jù)導(dǎo)入到工作表的單元格
      45                 foreach (MyData data in input.Sources)
      46                 {
      47                     if (bgWorker.CancellationPending)
      48                     {
      49                         e.Cancel = true;
      50                         return;
      51                     }
      52                     j = 1;
      53                     excelSheet.Cells[i, j++] = data.Col1;
      54                     excelSheet.Cells[i, j++] = data.Col2;
      55                     excelSheet.Cells[i, j++] = data.Col3;
      56                     ++i;
      57                     bgWorker.ReportProgress((95 * i - 190) / count);
      58                 }
      59                 //將其進行保存到指定的路徑
      60                 excelDoc.SaveAs(input.Path + input.FileName + input.Ext,
      61                     input.Ext == ".xls" ? Excel.XlFileFormat.xlExcel7 : Excel.XlFileFormat.xlOpenXMLWorkbook);
      62                 excelDoc.Close();
      63                 // 返回路徑
      64                 e.Result = input.Path;
      65                 bgWorker.ReportProgress(100);
      66             }
      67             catch (System.Exception ex)
      68             {
      69                 throw ex;
      70             }
      71             finally
      72             {
      73                 excelApp.Quit();
      74                 // 釋放COM組件,其實就是將其引用計數(shù)減1
      75                 System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
      76                 excelApp = null;
      77                 //釋放可能還沒釋放的進程
      78                 KillProcess(proList);
      79             }
      80         }
      81     }

        首先,引用Microsoft.Office.Interop.Excel命名空間,如果機器上安裝了office,那么它的位置是在
        C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\14.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll
        的位置,office版本不同“14.0.0.0__71e9bce111e9429c目錄”可能名稱會有一點差別。

        接下來,啟動Excel進程:
        Excel.Application excelApp = new Excel.Application();

        創(chuàng)建工作簿:
        Excel.Workbook excelDoc = excelApp.Workbooks.Add();

        創(chuàng)建工作表:
        Excel.Worksheet excelSheet = (Excel.Worksheet)excelDoc.Worksheets[1];

        填入數(shù)據(jù)(注意到行和列都是從1開始的):
        excelSheet.Cells[行, 列] = 數(shù)據(jù);
        在填入數(shù)據(jù)時,每趕往記錄前,都判斷一次是否取消導(dǎo)出,每填入一條記錄后,就使用bgWorker.ReportProgress()匯報工作進度。

      1 if (bgWorker.CancellationPending)
      2 {
      3   e.Cancel = true;
      4   return;
      5 }  

        將工作簿保存到指定的路徑,關(guān)閉:  

      1   excelDoc.SaveAs(input.Path + input.FileName + input.Ext,
      2     input.Ext == ".xls" ? Excel.XlFileFormat.xlExcel7 : Excel.XlFileFormat.xlOpenXMLWorkbook);  
      3   excelDoc.Close();

        網(wǎng)上很多地方說保存成office2003用的枚舉是Excel.XlFileFormat.xlExcel8,經(jīng)過我實際測試,這個枚舉是從office 2007才開始出現(xiàn)的,如果機器上安裝了2007及更高版本的office的話是可以正常使用的,如果機器上只安裝了office 2003,則只有用xlExcel7這個枚舉才能正常保存為excel2003文檔。

      5、優(yōu)化

        上面雖然功能完成了,但是還不夠,打開任務(wù)管理器,每導(dǎo)出一次會發(fā)現(xiàn)Excel.exe進程多一個,也就是說Excel.exe進程沒有被關(guān)閉,需要手動釋放資源。首先,釋放Com資源非常簡單:  

      1    excelApp.Quit();
      2   // 釋放COM組件,其實就是將其引用計數(shù)減1
      3   System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
      4   excelApp = null;

        但是從系統(tǒng)中刪除線程就比較麻煩,有一種方式是把所有Excel.exe進程關(guān)閉,但是這會影響事先打開的Excel文件。所以我這里創(chuàng)建了一個列表保存用來導(dǎo)出Excel的進程,并在導(dǎo)出結(jié)束后關(guān)閉這些進程:  

      1    // 獲取已打開的Excel程序 Interaction.GetObject(null, "Excel.Application") as Excel.Application;
      2   Monitor.Enter(syncRoot);
      3   var proListStart = Process.GetProcessesByName("EXCEL");
      4   Excel.Application excelApp = new Excel.Application();
      5   var proList = Process.GetProcessesByName("EXCEL").Except(proListStart, new ProcessComparer()); 
      6   Monitor.Exit(syncRoot);

        在創(chuàng)建Excel應(yīng)用前進入鎖定,并記錄當(dāng)前Excel.exe進程列表,然后創(chuàng)建,對比判斷新增的進程,結(jié)束鎖定。對比判斷ProcessComparer類,實現(xiàn)了IEqualityComparer<Process>接口,通過進程的Id來標(biāo)識唯一性。

        在導(dǎo)出結(jié)束之后,我再調(diào)用KillProcess函數(shù),把proList列表中的進程全部關(guān)閉,以釋放資源:

      1 foreach (Process theProc in list)
      2   if (theProc.CloseMainWindow() == false)
      3     theProc.Kill();

      三、總結(jié)

        這個東西本來就做好很久了,一直沒時間寫博文,現(xiàn)在感覺寫博文有種很想偷懶的感覺,唉,不行了,對文字工作不感冒。這個東西實際上難度不大,關(guān)鍵是各種配合起來,達到諧調(diào)的目的,然后資源釋放那塊也琢磨了不少方法才采用的死辦法的,看有沒有園友能找到更好的釋放進程的方法。

        有一個問題,現(xiàn)在我是使用List<實體對象>這樣的數(shù)據(jù)源的,這就是說每一個實體對象都是會要一個導(dǎo)出處理函數(shù)的,希望大家注意,如果是想使用通用性的處理函數(shù),數(shù)據(jù)源可以更改為一個本身就有行、列概念的對象,然后可以修改一下傳入?yún)?shù)應(yīng)該能完成想要的功能了。

        相關(guān)評論

        閱讀本文后您有什么感想? 已有人給出評價!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評論

        最新評論

        發(fā)表評論 查看所有評論(1)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字數(shù): 0/500 (您的評論需要經(jīng)過審核才能顯示)