C#2.0:エクセルの操作クラス
エクセルのファイルを開き、操作するためのクラス。
IDisposableなのでusingでの使用を想定。
Excel.Subtotalメソッドは、複数回呼ぶことで小計、中計を作成することができる。
Excelのバージョンは2007を想定。
リソースリークは完全には無くなっていないかもしれない。
完全を求めるならば計測する必要があるかも。
.Net Framework 2.0用
2011/6/22: COMObjectのスイーパーつけたり、CSV,タブ区切りファイルを読み込めるようにしたり色々強化
using System; using System.Collections.Generic; using System.Text; using System.Configuration; using System.Runtime.InteropServices; using Excel = Microsoft.Office.Interop.Excel; using System.Data; using System.IO; namespace ComboAndTextBoxBinder { /// <summary> /// COMObjectを保管しておき、dispose(usingの終了)時に解放する。 /// </summary> public class COMObjSweeper : IDisposable { protected Queue<object> queue = new Queue<object>(); protected object queueLock = new object(); protected object refLock = new object(); int refCount = 0; /// <summary> /// usingするときに取得するオブジェクト(Reference Counter付き) /// </summary> public COMObjSweeper Ref { get { lock (this.refLock) { refCount++; } return this; } } /// <summary> /// COMオブジェクトをAddする /// </summary> /// <param name="obj">COMObject</param> public void Add(object obj) { lock (this.queueLock) { this.queue.Enqueue(obj); } } /// <summary> /// オブジェクトの開放 /// </summary> /// <param name="obj"></param> public static void ReleaseComObject(object obj) { try { if (obj != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); } } catch { } } /// <summary> /// COMオブジェクトを解放する。 /// </summary> public void Sweep() { lock (this.queueLock) { while (this.queue.Count > 0) { object obj = this.queue.Dequeue(); ReleaseComObject(obj); } } } #region IDisposable メンバ public void Dispose() { lock (this.refLock) { refCount--; if (refCount <= 0) { Sweep(); refCount = 0; } } } #endregion } /// <summary> /// エクセルを開き、データ集計などを行うためのクラス /// </summary> public class ExcelUtil : IDisposable { protected Excel.Application oXls = null; protected Excel.Workbook oWBook = null; // workbookオブジェクト protected Excel.Worksheet oSheet; // Worksheetオブジェクト protected bool quitWhenDispose = true; protected COMObjSweeper localSwp = new COMObjSweeper(); protected COMObjSweeper clsSwp = new COMObjSweeper(); /// <summary> /// エクセルを表示するか否か? /// </summary> public bool Visible { set { this.oXls.Visible = value; } get { return this.oXls.Visible; } } /// <summary> /// Dispose時にエクセルを終了するか? /// </summary> public bool QuitWhenDispose { set { this.quitWhenDispose = value; } get { return this.quitWhenDispose; } } /// <summary> /// エクセルファイルのオープン /// </summary> /// <param name="excelFileName">開くファイル名</param> /// <param name="readOnly">リードオンリーで開くか?</param> /// <param name="displayAlarts">終了時のセーブしますか?などのアラートを表示するかどうか?</param> public void OpenBook(string excelFileName,bool visible,bool readOnly,bool displayAlarts) { this.oXls = new Excel.Application(); this.Visible = visible; this.oXls.DisplayAlerts = displayAlarts; this.oWBook = (Excel.Workbook)(oXls.Workbooks.Open( excelFileName, Type.Missing, readOnly, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing )); // アクティブシートがあることが前提 this.oSheet = (Excel.Worksheet)oWBook.ActiveSheet; this.clsSwp.Add(this.oSheet); this.clsSwp.Add(this.oWBook); this.clsSwp.Add(this.oXls); } /// <summary> /// データテーブルのRowをエクセルの行に設定 /// </summary> /// <param name="startRow">エクセルへの設定開始行</param> /// <param name="startCol">エクセルへの設定開始列</param> /// <param name="endCol">エクセルへの設定終了列</param> /// <param name="dr">設定するデータテーブル</param> public void SetFromDataRow(int startRow, int startCol,int endCol, DataRow dr) { foreach (object obj in dr.ItemArray) { SetCell(startRow, startCol, obj); startCol++; if (startCol >= endCol) { return; } } } /// <summary> /// データテーブルをエクセルに設定 /// </summary> /// <param name="startRow">1から始まる行</param> /// <param name="startCol">1から始まる列</param> /// <param name="dt">データテーブル</param> public int SetFromDataTable(int startRow,int startCol,int endCol, DataTable dt) { foreach (DataRow dr in dt.Rows) { SetFromDataRow(startRow, startCol, endCol, dr); startRow++; } return startRow; } /// <summary> /// データテーブルをエクセルに設定 /// </summary> /// <param name="startRow">1から始まる行</param> /// <param name="startCol">1から始まる列</param> /// <param name="dt">データテーブル</param> public int SetFromDataTable(int startRow, int startCol, DataTable dt) { foreach (DataRow dr in dt.Rows) { SetFromDataRow(startRow, startCol,Int32.MaxValue, dr); startRow++; } return startRow; } /// <summary> /// 1行分のテキストファイルデータをエクセルに設定する。 /// </summary> /// <param name="startRow">開始行</param> /// <param name="startCol">開始列</param> /// <param name="endCol">終了列(Int32.MaxValueでテキストデータの最後の列まで)</param> /// <param name="textRowData">1行分のデータ</param> /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param> /// <param name="trimChars">1列分のデータの最初と最後で取り除く文字(ダブルクオーテーションなど)</param> public void SetFromTextLine(int startRow, int startCol, int endCol, string textRowData, string delimiterChars,string trimChars) { char[] delimiters = delimiterChars.ToCharArray(); string[] split = textRowData.Split(delimiters); char[] trims = (trimChars == null)?null:trimChars.ToCharArray(); foreach (string data in split) { string srcdata = data; if ((trims != null) && (trims.Length > 0)) { srcdata = srcdata.TrimStart(trims); srcdata = srcdata.TrimEnd(trims); } SetCell(startRow, startCol, srcdata); startCol++; if (startCol >= endCol) { return; } } } /// <summary> /// textfileの内容をエクセルに設定 /// </summary> /// <param name="startRow">1から始まる行</param> /// <param name="startCol">1から始まる列</param> /// <param name="endCol">終了の列(Int32.MaxValueでテキストデータの最後の列まで)</param> /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param> /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param> /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param> public int SetFromTextFile(int startRow, int startCol, int endCol, string textFileData, string delimiterChars, string trimChars) { using(StringReader sr = new StringReader(textFileData)) { while (sr.Peek() >= 0) { string line = sr.ReadLine(); SetFromTextLine(startRow, startCol, endCol, line, delimiterChars,trimChars); startRow++; } } return startRow; } /// <summary> /// textfileの内容をエクセルに設定 /// </summary> /// <param name="startRow">1から始まる行</param> /// <param name="startCol">1から始まる列</param> /// <param name="textFileData">タブ、カンマ区切りのテキストファイル</param> /// <param name="delimiterChars">デリミタとなる文字群(タブ、カンマなど)</param> /// <param name="trimChars">1列分の最初と最後で取り除く文字(ダブルクオーテーションなど)</param> public int SetFromTextFile(int startRow, int startCol, string textFileData, string delimiterChars,string trimChars) { return SetFromTextFile(startRow, startCol,Int32.MaxValue, textFileData, delimiterChars,trimChars); } // カラムの値はExcelではアルファベットで表されるので、整数のカラム値をアルファベットのカラム値に変換する。 public static string ToAlphabetCol(int colNum) { string alphaStr = ""; //カラムは1オリジンなので引いておく // アルファベットの数で割る。 for (int i = colNum; i > 0; i /= 26) { // Aが最初の位置なので、それを値で足し込むことでアルファベットの値を作成 char modChar = Convert.ToChar('A' + ((i % 26) - 1)); // 小さい桁から挿入するので、0の位置にインサート alphaStr = alphaStr.Insert(0, new string(modChar, 1)); } return alphaStr; } // 数値の行、桁の値からアルファベット+数値の値に変更 public static string ToExcelRowCol(int rowNum, int colNum) { return ToAlphabetCol(colNum) + rowNum.ToString(); } public Excel.Range GetRange(int row, int col) { string rngStr = ToExcelRowCol(row, col); return GetRange(rngStr, rngStr); } /// <summary> /// Rangeオブジェクトの取得 /// </summary> /// <param name="startRange">取得開始列行("A1"形式) nullでA1を選択</param> /// <param name="endRange">取得終了列行("A1"形式) nullで最終列行を選択</param> /// <returns>取得したRange</returns> public Excel.Range GetRange(string startRange, string endRange) { if (String.IsNullOrEmpty(startRange) == true) { startRange = "A1"; } Excel.Range rng; if (String.IsNullOrEmpty(endRange) == true) { Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing); rng = this.oSheet.get_Range(startRange, endRng); this.localSwp.Add(endRng); this.localSwp.Add(rng); return rng; } rng = this.oSheet.get_Range(startRange, endRange); this.localSwp.Add(rng); return rng; } /// <summary> /// 1行分のRangeオブジェクトを取得する。 /// </summary> /// <param name="startRange">取得開始列行("A1"形式) nullで自動的に"A1"になる</param> /// <param name="endRange">取得終了列行("A1"形式) nullで自動的に取得開始行の最終列</param> /// <returns>取得したRange</returns> public Excel.Range GetOneLineRange(string startRange, string endRange) { if (String.IsNullOrEmpty(startRange) == true) { startRange = "A1"; } Excel.Range startRng = this.GetRange(startRange, startRange); Excel.Range rng; if (String.IsNullOrEmpty(endRange) == true) { Excel.Range endRng = oSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing); rng = this.oSheet.get_Range(startRng, ExcelUtil.ToExcelRowCol(startRng.Row,endRng.Column)); this.localSwp.Add(endRng); this.localSwp.Add(rng); return rng; } rng = this.oSheet.get_Range(startRange, endRange); this.localSwp.Add(rng); return rng; } /// <summary> /// セルの上下左右と間に罫線を設定する。 /// </summary> /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param> /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式)</param> /// <param name="aroundLineStyle">周りの罫線のラインスタイル /// xlLineStyleNone = -4142, /// xlDouble = -4119, /// xlDot = -4118, /// xlDash = -4115, /// xlContinuous = 1, /// xlDashDot = 4, /// xlDashDotDot = 5, /// xlSlantDashDot = 13, /// </param> public void SetAllBorderLine(string startColRow, string endColRow, int aroundLineStyle, int betweenLineStyle) { using (this.localSwp.Ref) { Excel.Range rng = this.GetRange(startColRow, endColRow); rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = aroundLineStyle; rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).LineStyle = aroundLineStyle; rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = betweenLineStyle; rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeLeft).LineStyle = aroundLineStyle; rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).LineStyle = aroundLineStyle; rng.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).LineStyle = betweenLineStyle; } } /// <summary> /// 特定行のセルの下に罫線を設定する。 /// </summary> /// <param name="startColRow">罫線の設定を開始する列行の文字列("A1"形式)</param> /// <param name="endColRow">罫線の設定を終了する列行の文字列("A1"形式) nullで開始行の最後まで罫線を表示</param> /// <param name="lineStyle">罫線のラインスタイル(Excel.Borders.LineStyle)</param> public void SetBottomBorderLine(string startColRow, string endColRow, int lineStyle) { using (this.localSwp.Ref) { Excel.Range rng = this.GetOneLineRange(startColRow, endColRow); // 引数は"A1"形式で指定 rng.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = lineStyle; } } /// <summary> /// ヘッダーのメッセージを変更(中央ヘッダー) /// </summary> /// <param name="message"></param> public void SetPageCenterHeader(string message) { this.oSheet.PageSetup.CenterHeader = message; } /// <summary> /// フッターのメッセージを変更(右フッター) /// </summary> /// <param name="message"></param> public void SetPageRightFooter(string message) { this.oSheet.PageSetup.RightFooter = message; } /// <summary> /// 印刷時に必ず表示する行を設定($1:$2のような形で設定) /// </summary> /// <param name="rowAndColumns"></param> public void SetPageTitleRows(string rows) { this.oSheet.PageSetup.PrintTitleRows = rows; } /// <summary> /// セルを設定(引数は数値) /// </summary> /// <param name="row">設定する行(1オリジン)</param> /// <param name="col">設定する列(1オリジン)</param> /// <param name="message">設定する文字列</param> public void SetCell(int row, int col, object val) { SetCell(ToExcelRowCol(row, col),val); } /// <summary> /// セルを設定(引数はA1のような形式) /// </summary> /// <param name="colRow">設定する列行("A1"形式)</param> /// <param name="message">設定する文字列</param> public void SetCell(string colRow, object val) { using(this.localSwp.Ref) { Excel.Range rng = this.GetRange(colRow, colRow); rng.Value2 = val; } } /// <summary> /// セルに一度に設定(引数はA1のような形式) /// </summary> /// <param name="startColRow">設定する列行の開始位置("A1"形式)</param> /// <param name="startColRow">設定する列行の終了位置("A1"形式)</param> /// <param name="message">設定する文字列</param> public void SetCells(string startColRow,string endColRow, object val) { using (this.localSwp.Ref) { Excel.Range rng = this.GetRange(startColRow, endColRow); rng.Value2 = val; } } /// <summary> /// ソート処理を行う。 /// </summary> /// <param name="startRowCol">ソート範囲の開始("A1"形式)</param> /// <param name="endRowCol">ソート範囲の終了("A1"形式)</param> /// <param name="firstKeyCol">第1キーの列の位置(1オリジン)</param> /// <param name="secondKeyCol">第2キーの列の位置(1オリジン)</param> /// <param name="thirdKeyCol">第3キーの列の位置(1オリジン)</param> /// <param name="ascending">昇順・降順(全てのキーに適用)</param> public void Sort(string startRowCol,string endRowCol,int firstKeyCol, int secondKeyCol, int thirdKeyCol,bool ascending) { using (this.localSwp.Ref) { Excel.Range rng = this.GetRange(startRowCol, endRowCol); object key1 = (firstKeyCol > 0) ? rng.Columns[firstKeyCol, Type.Missing] : Type.Missing; object key2 = (secondKeyCol > 0) ? rng.Columns[secondKeyCol, Type.Missing] : Type.Missing; object key3 = (thirdKeyCol > 0) ? rng.Columns[thirdKeyCol, Type.Missing] : Type.Missing; this.localSwp.Add(key1); this.localSwp.Add(key2); this.localSwp.Add(key3); Excel.XlSortOrder asc = Excel.XlSortOrder.xlAscending; if (ascending == false) { asc = Excel.XlSortOrder.xlDescending; } rng.Sort(key1, asc, key2, Type.Missing, asc, key3, asc, Excel.XlYesNoGuess.xlNo, Type.Missing, Type.Missing, Excel.XlSortOrientation.xlSortColumns, Excel.XlSortMethod.xlPinYin, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal); } } /// <summary> /// エクセルファイルの集計を行う /// </summary> /// <param name="startColRow">開始列行文字列("A1"形式)</param> /// <param name="endColRow">終了列行文字列("A1"形式)</param> /// <param name="totalList">集計を行う列の値</param> /// <param name="groupByField">同じ値のものを集計して表示するための列の値</param> public void AggregateTotal(string startColRow,string endColRow, int[] totalList, int groupByField) { using (this.localSwp.Ref) { Excel.Range rng = this.GetRange(startColRow, endColRow); // 集計を行う。groupByFieldが同じ値のモノについて、totalListの値を集計して表示する。集計と総計を表示 rng.Subtotal(groupByField, Excel.XlConsolidationFunction.xlSum, totalList, false, Type.Missing, Excel.XlSummaryRow.xlSummaryBelow); } } /// <summary> /// セルを取得 /// </summary> /// <param name="row">行の値(1オリジン)</param> /// <param name="col">列の値(1オリジン)</param> /// <returns></returns> public string GetCell(int row, int col) { return GetCell(ToExcelRowCol(row, col)); } /// <summary> /// セルを取得(引数はA1のような形式) /// </summary> /// <param name="colRow">取得するセルの列行("A1"形式)</param> /// <returns>取得した値</returns> public string GetCell(string colRow) { using (this.localSwp.Ref) { Excel.Range rng = this.GetRange(colRow, colRow); string str = rng.Value2 as string; return str; } } #region IDisposable メンバ /// <summary> /// エクセルプログラムの終了を行いリソースを解放する。 /// </summary> public void Dispose() { try { if ((this.quitWhenDispose == true) && (this.oWBook != null)) { this.oWBook.Close(false, Type.Missing, Type.Missing); } } catch { } try { if ((this.quitWhenDispose == true) && (this.oXls != null)) { this.oXls.Quit(); } } catch { } this.localSwp.Sweep(); this.clsSwp.Sweep(); } #endregion } }