ClosedXML - Excelファイルの操作

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

Closed XMLは、Microsoft Excelファイルの作成、読み取り、編集を簡単に行うことができるように設計されている。

Closed XMLの主な特徴は、その使いやすさにある。
開発者は、Excelファイルの操作をプログラム的に行う場合に、複雑なExcel Object Modelを直接扱う必要がない。
代わりに、Closed XMLは直感的なAPIを提供しており、これにより開発者は効率的にExcelファイルを操作することができる。

このライブラリを使用すると、新しいワークブックの作成、既存のワークブックの開封、ワークシートの追加や削除、セルの書式設定、数式の追加、グラフや画像の挿入等が可能である。
また、大量のデータを扱う際のパフォーマンスも考慮されており、メモリ効率の良い操作が可能である。

Closed XMLの大きなメリットの1つは、Microsoft Office製品がインストールされていないPCでも動作することである。
これは、サーバ環境でのExcelファイル操作を可能にして、多くの企業システムで重要な機能となっている。

セキュリティの観点からもClosed XMLは優れており、ファイルの操作がプログラム的に行われるため、マクロを含むExcelファイルによるセキュリティリスクを軽減できる。

Closed XMLの使用例としては、財務レポートの自動生成、大量のデータの分析と視覚化、テンプレートベースの文書生成等が挙げられる。
これらのタスクは、ビジネスアプリケーションでよく必要とされるものである。

このライブラリは活発に開発が続けられており、コミュニティからの支援も豊富なため、バグ修正や新機能の追加が定期的に行われており、最新のExcel機能にも対応している。

ただし、Closed XMLにも制限はある。
例えば、一部の高度なExcel機能はサポートされていない場合がある。
また、非常に大きなファイルを扱う場合には、メモリ使用量に注意が必要となる。

Closed XMLはC#開発者にとって、Excelファイル操作を効率的に行うための強力なツールである。
その使いやすさ、豊富な機能、そして堅牢性により、多くのプロジェクトで重要な役割を果たしている。



Closed XMLとは

Open XML形式のExcelファイル(拡張子 : xlsx / xlsm)を読み書きするためのライブラリである。
MicrosoftのOpen XML SDKをラップし、Excelに特化することで使用しやすいライブラリになっている。

Closed XMLのソースコードは、GitHubで公開されている。
また、Closed XMLのライセンスは、MITライセンスである。

Closed XMLで可能なこと

  • Open XML形式のExcelファイルの作成と編集
  • セルデータの読み書き
    • 数値、文字列、日付等の各種データ型
    • 複雑な数式の挿入と評価
  • セルの書式設定
    • フォント
    • 罫線
    • 背景色
  • ワークシートの操作
    • 追加、削除、名前変更、コピー
    • 行や列の挿入、削除、非表示設定
  • Excelの主要関数のサポート
    • SUM, AVERAGE, VLOOKUP, IF等
  • 大量データの高速処理
  • テンプレートベースの文書生成
  • 基本的な条件付き書式の適用
  • ハイパーリンクの挿入と管理
  • 印刷設定の調整(ページ設定、印刷範囲など)


Closed XMLで実行できないこと

  • Excelアプリケーションの直接操作
  • マクロやVBAコードの実行・編集
  • 複雑なグラフや図表の新規作成
  • ピボットテーブルの新規作成や高度な編集
  • 一部の高度なExcel機能
    • 複雑な条件付き書式
    • スパークライン
    • 高度なデータ検証ルール
  • 外部データソースとの動的連携
  • リアルタイムデータ更新
  • Excelアドインの使用
  • 複雑な画像操作や図形の作成
  • コメントの詳細な管理や高度な編集


※注意
上記は、Closed XMLは継続的に更新されているため、一部の機能は将来のバージョンで追加される可能性がある。

サポートされているプラットフォーム

以下に示すプラットフォームサポートにより、Closed XMLは幅広い.NET環境で使用できる柔軟性の高いライブラリとなっている。
デスクトップアプリケーション、Webアプリケーション、モバイルアプリケーション、ゲーム開発まで、様々な種類のプロジェクトでExcelファイルの操作が可能である。

  • .NET Core 2.0以降 / .NET 5以降
  • .NET Framework 4.5以降(4.0ではなく4.5からのサポート)
  • .NET Standard 2.0以降


.NET Standard 2.0のサポートにより、以下のプラットフォームでも利用可能である。

  • ASP.NET Core 2.0以降
  • UWP(Universal Windows Platform)10.0.16299 (Fall Creators Update、1709) 以降
  • Xamarin.iOS 10.14以降
  • Xamarin.Android 8.0以降
  • Xamarin.Mac 3.8以降
  • Unity 2018.1以降 (ゲーム開発プラットフォームとしても利用可能)


※注意
プラットフォームのバージョンは、Closed XMLのバージョンによって変更される可能性がある。
最新の対応状況は、Closed XMLの公式ドキュメントやGithubリポジトリで確認することを推奨する。
また、一部の機能は、特定のプラットフォームでのみ利用可能な場合がある。

Closed XMLのドキュメントは、ClosedXMLのGithubで公開されている。

Closed XMLの詳細を知りたい場合は、公式ドキュメントを参照すること。


Closed XMLのインストール

RiderまたはVisual StudioからNuGetを使用して、Closed XMLをインストールする。

  • Riderの場合
    1. プロジェクトを開く。
    2. [ツール]メインメニュー - [Nuget] - [ソリューション の Nuget パッケージを管理] (または、[<プロジェクト名> の Nuget パッケージを管理])を選択する。
    3. メイン画面下部にある[パッケージ]タブから ClosedXML と入力して検索する。
    4. メイン画面下部の右にある[+]ボタンを押下して、Closed XMLをインストールする。

  • Visual Studioの場合
    1. プロジェクトを開く。
    2. NuGetパッケージマネージャーを開く。
      • [ツール]メインメニュー - [NuGetパッケージマネージャー]を選択して、[ソリューションのNuGetパッケージの管理]を選択する。
      • または、ソリューションエクスプローラーでプロジェクトを右クリックして、コンテキストメニューから[NuGetパッケージの管理]を選択する。
    3. ClosedXMLを検索する。
      NuGetパッケージマネージャーの検索ボックスに ClosedXML と入力して検索する。
    4. Closed XMLのインストール
      検索結果からClosed XMLを選択して、[インストール]ボタンを押下する。
    5. インストールの確認ダイアログが表示されるので、[OK]ボタンを押下してインストールを完了する。
    6. 参照の確認
      インストールが完了した後、プロジェクトの参照にClosed XMLが追加されていることを確認する。

  • パッケージマネージャーコンソールからインストールする場合
    1. プロジェクトを開く。
    2. [表示]メインメニュー - [その他のウィンドウ] - [パッケージマネージャーコンソール]を選択して、パッケージマネージャーコンソールを開く。
    3. パッケージマネージャーコンソールから、Closed XMLとその依存関係をダウンロードしてインストールする。
      Install-Package ClosedXML
    4. ソリューションエクスプローラーのプロジェクトの参照において、ClosedXMLが追加されていることを確認する。

  • dotnetコマンドを使用する場合
    1. ターミナルを開く。
    2. プロジェクトのルートディレクトリに移動する。
    3. Closed XMLをインストールする。
      最新の安定版をインストールする場合
      dotnet add package ClosedXML

      バージョンを指定してインストールする場合
      dotnet add package ClosedXML --version 0.102.3

    ※注意
    プロジェクトがGit等のバージョン管理システムを使用している場合、これらの変更がトラッキングされることを確認すること。
    プロジェクトを再ビルドして、新しく追加されたパッケージが正しく統合されていることを確認することを推奨する。


また、Closed XMLのGithubからソースコードをダウンロードして、ビルドおよびインストールすることもできる。

Closed XMLをインストールする時、Open XML SDK等のいくつかの必要なパッケージが同時にインストールされる。

プロジェクトにおいて、ClosedXMLを使用する場合は、ソースコードファイルの先頭にusingステートメントを追加する。

 using ClosedXML.Excel;



Excelファイルの作成

以下の例では、Excelファイルおよびシートを新規作成している。

  • Open XML形式のExcelファイルの作成と編集
    new XLWorkbook()を使用して新しいワークブックを作成する。
    workbook.Worksheets.Add("サンプルシート")でワークシートを追加する。
    workbook.SaveAs("sample.xlsx")でファイルを保存する。
    既存のファイルを開く場合は、new XLWorkbook("サンプル.xlsx")を使用する。


 using System;
 using ClosedXML.Excel;
 
 const string ExcelFilePath = @"sample.xlsx";
 
 // Excelファイルの作成
 using (var workbook = new XLWorkbook())
 {
    // ワークシートの追加
    using (var worksheet = workbook.Worksheets.Add("サンプルシート"))
    {
       // 何らかの処理
 
       // ワークブックを保存する
       workbook.SaveAs(ExcelFilePath);
    }
 
    Console.WriteLine(@"Excelファイルを保存しました");
 
    // 保存したファイルを開いて編集
    using (var workbook = new XLWorkbook("サンプル.xlsx"))
    {
       var worksheet = workbook.Worksheet(1);
 
       // 何らかの処理
  
       // 変更を保存
       workbook.Save();
    }
 }



セルデータの読み書き

以下の例では、Open XML形式のExcelファイルにおいて、セルの値の設定、読み取り、数式の挿入、ファイルの保存と再編集等を行っている。

  • セルデータの読み書き
    • 書き込み
      worksheet.Cell("A1").Value = "文字列" のように、Cellメソッドを使用してセルを指定して、Valueプロパティに値を代入する。
    • 読み取り
      var cellValue = worksheet.Cell("A2").Value のように、Valueプロパティを使用してセルの値を取得する。


  • 数値、文字列、日付等の各種データ型
    • 文字列
      worksheet.Cell("A1").Value = "文字列"
    • 数値
      worksheet.Cell("A2").Value = 123
    • 日付
      worksheet.Cell("A3").Value = DateTime.Now


  • 複雑な数式の挿入と評価
    • 基本的な数式
      worksheet.Cell("B1").FormulaA1 = "=SUM(A1:A3)"
    • 条件付き数式
      worksheet.Cell("C1").FormulaA1 = "=IF(A2>100, \"高い\", \"低い\")"


また、大量のデータを扱う場合は、パフォーマンスを考慮したアプローチ (例: バッチ処理) を検討する必要がある。

 using System;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       // 新しいExcelワークブックを作成
       using (var workbook = new XLWorkbook())
       {
          var worksheet = workbook.Worksheets.Add("サンプルシート");
 
          // セルデータの書き込み
          worksheet.Cell("A1").Value = "文字列";
          worksheet.Cell("A2").Value = 123;           // 数値
          worksheet.Cell("A3").Value = DateTime.Now;  // 日付
 
          // 数式の挿入
          worksheet.Cell("B1").FormulaA1 = "=SUM(A1:A3)";
 
          // セルのスタイル設定
          var cell = worksheet.Cell("A1");
          cell.Style.Font.Bold = true;
          cell.Style.Fill.BackgroundColor = XLColor.LightBlue;
 
          // 複雑な数式の挿入
          worksheet.Cell("C1").FormulaA1 = "=IF(A2>100, \"高い\", \"低い\")";
 
          // 位置を指定してセルの値を読み取り
          var cellValue = worksheet.Cell("A2").Value;
          var cellA1 = worksheet.Cell("A1");
          var cellA2 = worksheet.Cell(2, "A");  // 行番号と列名でも指定可能
          var cellB1 = worksheet.Cell(3, 1);    // 行番号と列番号でも指定可能
 
          Console.WriteLine($"A2セルの値: {cellValue}");
          Console.WriteLine($"A1セルの値: {cellA1.GetValue<strring>()}");        // 出力 : A1 = "文字列"
          Console.WriteLine($"A2セルの値: {cellA2.GetValue<int>()}");            // 出力 : A2 = 123
          Console.WriteLine($"A3(値)セルの値: {cellA3.GetValue<DateTime>()}");   // 出力 : A3(値) = 30
          Console.WriteLine($"B1(数式)セルの値: {cellB1.FormulaA1}");            // 出力 : B1(数式) = SUM(A1:A3)
 
          // ファイルを保存
          workbook.SaveAs("サンプル.xlsx");
       }
 
       // 保存したファイルを開いて編集
       using (var workbook = new XLWorkbook("サンプル.xlsx"))
       {
          var worksheet = workbook.Worksheet(1);
 
          // 既存のデータを読み取り
          var a1Value = worksheet.Cell("A1").Value;
          Console.WriteLine($"A1セルの値: {a1Value}");
 
          // 新しいデータを追加
          worksheet.Cell("D1").Value = "新しいデータ";
 
          // 変更を保存
          workbook.Save();
       }
 
       Console.WriteLine("処理が完了しました。");
    }
 }



セルの書式設定

以下の例では、セルにフォント、色、罫線、背景色等の様々な書式設定を適用している。
これらの設定を組み合わせたり、より複雑な条件付き書式を適用することができる。

 using System;
 using System.Drawing;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       using (var workbook = new XLWorkbook())
       {
          var worksheet = workbook.Worksheets.Add("書式設定サンプル");
 
          // フォントの設定
          var cell1 = worksheet.Cell("A1");
          cell1.Value                = "フォント設定";
          cell1.Style.Font.FontName  = "Arial";
          cell1.Style.Font.FontSize  = 14;
          cell1.Style.Font.Bold      = true;
          cell1.Style.Font.Italic    = true;
          cell1.Style.Font.Underline = XLFontUnderlineValues.Single;
          cell1.Style.Font.FontColor = XLColor.Red;
 
          // 背景色の設定
          var cell2                        = worksheet.Cell("A2");
          cell2.Value                      = "背景色設定";
          cell2.Style.Fill.BackgroundColor = XLColor.LightBlue;
 
          // 罫線の設定
          var range = worksheet.Range("A3:C5");
          range.Style.Border.OutsideBorder      = XLBorderStyleValues.Thick;
          range.Style.Border.OutsideBorderColor = XLColor.Black;
          range.Style.Border.InsideBorder       = XLBorderStyleValues.Thin;
          range.Style.Border.InsideBorderColor  = XLColor.Gray;
 
          // セルの配置
          var cell3 = worksheet.Cell("A3");
          cell3.Value                      = "セル配置";
          cell3.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
          cell3.Style.Alignment.Vertical   = XLAlignmentVerticalValues.Center;
 
          // 数値書式の設定
          var cell4 = worksheet.Cell("A6");
          cell4.Value                     = 12345.6789;
          cell4.Style.NumberFormat.Format = "#,##0.00";
 
          // 日付書式の設定
          var cell5 = worksheet.Cell("A7");
          cell5.Value                   = DateTime.Now;
          cell5.Style.DateFormat.Format = "yyyy年MM月dd日";
 
          // セルの幅と高さの設定
          worksheet.Column(1).Width = 20;  // セルの幅
          worksheet.Row(1).Height   = 30;  // セルの高さ
 
          // 条件付き書式の設定 (メソッドチェーンを使用しない場合)
          var range             = worksheet.Range("A8:A10");
          var conditionalFormat = range.AddConditionalFormat();
          conditionalFormat.WhenGreaterThan(50);
 
          var fillFormat = conditionalFormat.Fill;
          fillFormat.SetBackgroundColor(XLColor.Green);
 
          var fontFormat = conditionalFormat.Font;
          fontFormat.SetFontColor(XLColor.White);
 
          worksheet.Cell("A8").Value  = 30;
          worksheet.Cell("A9").Value  = 60;
          worksheet.Cell("A10").Value = 90;
 
          // 条件付き書式の設定 (メソッドチェーンを使用する場合)
          var conditionalFormat = worksheet.Range("A8:A10").AddConditionalFormat();
          conditionalFormat.WhenGreaterThan(50)
                           .Fill.SetBackgroundColor(XLColor.Green)
                           .Font.SetFontColor(XLColor.White);
 
          worksheet.Cell("A8").Value  = 30;
          worksheet.Cell("A9").Value  = 60;
          worksheet.Cell("A10").Value = 90;
 
          // ファイルを保存
          workbook.SaveAs("書式設定サンプル.xlsx");
       }
 
       Console.WriteLine("書式設定サンプルファイルを作成");
    }
 }



ワークシートの操作

以下の例では、ワークシートの追加、削除、名前変更、コピー、行や列の挿入、削除、非表示設定をしている。

また、エラーハンドリングのためのtry-catchブロックも含んでいる。
これにより、操作中に発生する可能性のある例外を捕捉して、適切なエラーメッセージを表示する。

 using System;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       using (var workbook = new XLWorkbook())
       {
          try
          {
             // ワークシートの追加
             var sheet1 = workbook.Worksheets.Add("Sheet1");
             var sheet2 = workbook.Worksheets.Add("Sheet2");
             Console.WriteLine("2つのワークシートを追加");
 
             // ワークシートの名前変更
             sheet1.Name = "新しい名前";
             Console.WriteLine("Sheet1の名前を 新しい名前 に変更");
 
             // ワークシートのコピー (既存のワークシートをコピー)
             var copiedSheet = sheet2.CopyTo("Sheet2のコピー");
             Console.WriteLine("Sheet2をコピー");
 
             // 行の挿入 (指定した行の上に新しい行を挿入)
             sheet1.Row(2).InsertRowsAbove(2);
             Console.WriteLine("2行目の上に2行挿入");
 
             // 列の挿入 (指定した列の前に新しい列を挿入)
             sheet1.Column(2).InsertColumnsBefore(2);
             Console.WriteLine("B列の前に2列挿入");
 
             // 行の削除
             sheet1.Row(5).Delete();
             Console.WriteLine("5行目を削除");
 
             // 列の削除
             sheet1.Column("D").Delete();
             Console.WriteLine("D列を削除");
 
             // 行の非表示設定
             sheet1.Row(3).Hide();
             Console.WriteLine("3行目を非表示");
 
             // 列の非表示設定
             sheet1.Column("C").Hide();
             Console.WriteLine("C列を非表示");
 
             // ワークシートの削除 (ワークシート全体を削除)
             workbook.Worksheet("Sheet2のコピー").Delete();
             Console.WriteLine("Sheet2のコピーを削除");
 
             // ファイルを保存
             workbook.SaveAs("ワークシート操作サンプル.xlsx");
             Console.WriteLine("ファイルを保存");
          }
          catch (Exception ex)
          {
             Console.WriteLine($"エラーが発生: {ex.Message}");
          }
       }
    }
 }



Excelの主要な関数

使用例

以下の例では、SUM、AVERAGE、VLOOKUP、IF等の関数を直接挿入して使用している。

関数の適用にはFormulaA1プロパティを使用して、通常のExcelで使用する形式で数式を記述する。
関数の結果を取得するには、セルのValueプロパティを使用する。

また、文字列を含む数式では、ダブルクォートをエスケープする必要がある。(例: \"高い\")

※注意
実際の使用では、これらの関数をより複雑な数式の一部として使用したり、大量のデータに対して適用したりすることができる。
また、Closed XMLは多くのExcel関数をサポートしているが、一部の高度な関数や最新の関数はサポートされていない可能性がある。
そのような場合は、代替の方法を検討する必要がある。

 using System;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       using (var workbook = new XLWorkbook())
       {
          var worksheet = workbook.Worksheets.Add("関数サンプル");
 
          try
          {
             // サンプルデータの入力
             worksheet.Cell("A1").Value = "商品";
             worksheet.Cell("B1").Value = "価格";
             worksheet.Cell("A2").Value = "りんご";
             worksheet.Cell("B2").Value = 100;
             worksheet.Cell("A3").Value = "バナナ";
             worksheet.Cell("B3").Value = 80;
             worksheet.Cell("A4").Value = "オレンジ";
             worksheet.Cell("B4").Value = 120;
 
             // SUM関数
             // A1形式の数式を設定して、B2からB4セルの値を合計する
             worksheet.Cell("B5").FormulaA1 = "=SUM(B2:B4)";
             Console.WriteLine($"合計金額: {worksheet.Cell("B5").Value}");
 
             // AVERAGE関数
             // B2からB4セルの値の平均を計算する
             worksheet.Cell("B6").FormulaA1 = "=AVERAGE(B2:B4)";
             Console.WriteLine($"平均価格: {worksheet.Cell("B6").Value}");
 
             // 検索用データの入力
             worksheet.Cell("D1").Value = "商品コード";
             worksheet.Cell("E1").Value = "商品名";
             worksheet.Cell("D2").Value = "A001";
             worksheet.Cell("E2").Value = "りんご";
             worksheet.Cell("D3").Value = "B001";
             worksheet.Cell("E3").Value = "バナナ";
             worksheet.Cell("D4").Value = "C001";
             worksheet.Cell("E4").Value = "オレンジ";
 
             // VLOOKUP関数
             // A7セルの値をキーとして、D2:E4の範囲から対応する値を検索する
             // 2は返す列のインデックス、FALSEは完全一致を意味する
             worksheet.Cell("A7").Value     = "B001";  // 検索キー
             worksheet.Cell("B7").FormulaA1 = "=VLOOKUP(A7,D2:E4,2,FALSE)";
             Console.WriteLine($"商品コードB001の商品名: {worksheet.Cell("B7").Value}");
 
             // IF関数
             // B2セルの値が100より大きい場合は"高い"、そうでない場合は"安い"を返す
             worksheet.Cell("C2").FormulaA1 = "=IF(B2>100,\"高い\",\"安い\")";
             worksheet.Cell("C3").FormulaA1 = "=IF(B3>100,\"高い\",\"安い\")";
             worksheet.Cell("C4").FormulaA1 = "=IF(B4>100,\"高い\",\"安い\")";
             Console.WriteLine($"りんごの評価: {worksheet.Cell("C2").Value}");
             Console.WriteLine($"バナナの評価: {worksheet.Cell("C3").Value}");
             Console.WriteLine($"オレンジの評価: {worksheet.Cell("C4").Value}");
 
             // ファイルを保存
             workbook.SaveAs("Excel関数サンプル.xlsx");
             Console.WriteLine("ファイルを保存");
          }
          catch (Exception ex)
          {
             Console.WriteLine($"エラーが発生: {ex.Message}");
          }
       }
    }
 }


関数がサポートされていない場合の代替方法

Closed XMLにおいてサポートされていない高度な関数や最新の関数を扱うための代替方法を、以下に示す。

  • C#でロジックを実装する
    Excelの関数と同等の機能をC#のコードで直接実装する。
    これにより、任意の複雑さの計算を行うことができる。
    例: SUMIFS関数の代わりに、LINQを使用して条件に合う値を合計する。

  • 数式を分解する
    複雑な関数を、サポートされているより単純な関数の組み合わせに分解する。
    これにより、同等の機能を実現できる場合がある。
    例: SUMPRODUCT関数を、複数のセルの乗算とSUM関数の組み合わせで実現する。

  • 名前付き範囲を利用する
    複雑な計算を名前付き範囲として定義して、その結果を参照することにより、計算を管理しやすくする。
    これにより、複雑な計算をより管理しやすい形に分割できる。

  • データ処理をExcelの外で行う
    複雑な計算や処理をC#で行い、結果のみをExcelに書き込む。
    これは、特に大量のデータを扱う場合に効果的である。

  • 動的配列数式の代替
    最新のExcelの動的配列数式がサポートされていない場合、結果を複数のセルに手動で展開する。

  • Power Query相当の処理をC#で実装する
    データの変換や結合等の操作をC#で実装して、結果をExcelに書き込む。

  • VBAマクロの代替
    VBAマクロで行っている処理をC#で実装して、Closed XMLを使用してExcelファイルを操作する。

  • 外部ライブラリの使用
    数学や統計の高度な計算を行う外部ライブラリを使用して、Excelの高度な関数を再現する。


代替方法の例 : C#でロジックを実装する

以下の例では、C#とLINQを使用して、ExcelのSUMIFS関数と同等の機能 (LINQを使用して、条件に合う値を合計) を実現している。

 // 実装例 : SUMIFS関数の代替
 // C#とLINQを使用
 
 public static double SumIfs(IXLRange dataRange, params Tuple<IXLRange, string>[] criteria)
 {
    var data = dataRange.CellsUsed().Select(c => c.GetDouble());
    foreach (var criterion in criteria)
    {
       var criteriaRange = criterion.Item1;
       var condition     = criterion.Item2;
       data = data.Where((value, index) => EvaluateCondition(criteriaRange.Cell(index + 1).GetString(), condition));
    }
 
    return data.Sum();
 }
 
 private static bool EvaluateCondition(string cellValue, string condition)
 {
    // 条件の評価ロジックを実装
    // 例: ">10", "=Test", "<5"などの条件をパースして評価
 }



条件付き書式の適用

条件付き書式は、データの傾向や例外を視覚的に把握するのに役立つ。

Closed XMLでは、AddConditionalFormatメソッドを使用して、各条件付き書式のルールを設定することができる。

以下の例では、条件付き書式をExcelシートに適用している。

  • サンプルデータを生成して、シートに挿入する。
  • 3つの異なる条件付き書式を適用する。
    • 値が50より大きい場合、セルを赤色に塗る。
    • 値が25以下の場合、セルを緑色に塗る。
    • データバーを適用して、値の大きさを視覚的に表現する。


 using System;
 using System.Linq;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       Console.WriteLine("条件付き書式の適用を開始...");
 
       try
       {
          using (var workbook = new XLWorkbook())
          {
             var worksheet = workbook.Worksheets.Add("条件付き書式");
 
             // サンプルデータを挿入
             var data = Enumerable.Range(1, 20).Select(i => new { Value = i * 5 }).ToList();
             worksheet.Cell(1, 1).Value = "Value";
             worksheet.Cell(2, 1).InsertData(data);
 
             // 条件付き書式を適用
             var range = worksheet.Range(2, 1, 21, 1);
 
             // 1. 値が50より大きい場合、セルを赤色に
             var rule1 = range.AddConditionalFormat();
             rule1.WhenGreaterThan(50).Fill.SetBackgroundColor(XLColor.Red);
 
             // 2. 値が25以下の場合、セルを緑色に
             var rule2 = range.AddConditionalFormat();
             rule2.WhenLessThanOrEqual(25).Fill.SetBackgroundColor(XLColor.Green);
 
             // 3. データバーを適用
             var rule3 = range.AddConditionalFormat();
             rule3.DataBar(XLColor.Blue);
 
             workbook.SaveAs("条件付き書式.xlsx");
          }
 
          Console.WriteLine("条件付き書式の適用が完了");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 }



テンプレートベースの文書生成

テンプレートベースの文書の生成は、レポートや請求書等、構造化された文書の生成に適している。

※注意
ただし、テンプレートファイルの構造が変更された場合、コードの修正が必要になる可能性があることに注意すること。

以下の例では、既存のテンプレートファイルを基に新しい文書を生成している。

  • 既存のテンプレートファイルを開いて、新しいワークブックにコピーする。
  • ReplaceTextメソッドを使用して、プレースホルダーを実際の値で置換する。
  • 動的なデータを挿入して、必要な計算を行う。


 using System;
 using System.Collections.Generic;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       Console.WriteLine("テンプレートベースの文書生成を開始...");
 
       try
       {
          // テンプレートファイルを開く
          using (var template = new XLWorkbook("テンプレート.xlsx"))
          using (var result = new XLWorkbook())
          {
             var worksheetTemplate = template.Worksheet(1);
             var worksheetResult = result.Worksheets.Add("生成結果");
 
             // テンプレートをコピー
             worksheetTemplate.CopyTo(worksheetResult);
 
             // プレースホルダーを置換
             worksheetResult.Cells().ReplaceText("{{日付}}", DateTime.Now.ToString("yyyy/MM/dd"));
             worksheetResult.Cells().ReplaceText("{{会社名}}", "株式会社サンプル");
 
             // 動的データを挿入
             var data = new List<object[]>
             {
                new object[] { "商品A", 100, 5000 },
                new object[] { "商品B", 200, 3000 },
                new object[] { "商品C", 150, 4000 }
             };
 
             var dataRange = worksheetResult.Cell("A5").InsertData(data);
 
             // 合計を計算
             var lastRow = dataRange.LastRow().RowNumber();
             worksheetResult.Cell(lastRow + 1, 1).Value = "合計";
             worksheetResult.Cell(lastRow + 1, 3).FormulaA1 = $"=SUM(C5:C{lastRow})";
 
             result.SaveAs("生成結果.xlsx");
          }
 
          Console.WriteLine("テンプレートベースの文書生成が完了");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 }



大量データの高速処理

使用例 : 分割処理なし・ストリーミング処理なし

以下の例では、100万行のデータを生成して、Excelファイルに高速で書き込んでいる。

  • Enumerable.RangeとLINQを使用して大量のデータを効率的に生成する。
  • InsertDataメソッドを使用して、データを一括で挿入する。
    これは、個別のセルに値を設定するよりも高速である。


大量のデータを扱う場合は、MemoryStreamのサイズが大きくなる可能性がある。
そのような場合は、データを分割して処理、あるいは、ストリーミング方式でファイルに書き込む方法を検討する必要がある。

※注意
Closed XMLには直接的な非同期メソッドが存在しないため、これを回避するための1つの方法である。
Closed XMLの将来のバージョンで非同期メソッドが提供された場合は、それを使用するように更新することを推奨する。

 using System;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using ClosedXML.Excel;
 
 class Program
 {
    static async Task Main(string[] args)
    {
       Console.WriteLine("大量データの完全非同期高速処理を開始...");
       var stopwatch = Stopwatch.StartNew();
 
       try
       {
          await ProcessLargeDataAsync();
 
          stopwatch.Stop();
          Console.WriteLine($"大量データの処理が完了  処理時間: {stopwatch.ElapsedMilliseconds}ms");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生しました: {ex.Message}");
       }
    }
 
    static async Task ProcessLargeDataAsync()
    {
       var workbook = await Task.Run(() =>
       {
          var wb = new XLWorkbook();
          var worksheet = wb.Worksheets.Add("大量データ");
 
          // 100万行のデータを生成
          const int rowCount = 1_000_000;
          var data = Enumerable.Range(1, rowCount)
                               .Select(i => new { ID = i, Value = i * 2 })
                               .ToList();
 
          // データをワークシートに書き込む
          worksheet.Cell(1, 1).Value = "ID";
          worksheet.Cell(1, 2).Value = "Value";
          worksheet.Cell(2, 1).InsertData(data);
 
          // 合計を計算
          worksheet.Cell(rowCount + 2, 1).Value = "合計";
          worksheet.Cell(rowCount + 2, 2).FormulaA1 = $"=SUM(B2:B{rowCount + 1})";
 
          return wb;
       });
 
       // ファイルを非同期で保存
       await SaveWorkbookAsync(workbook, "大量データ処理.xlsx");
    }
 
    static async Task SaveWorkbookAsync(XLWorkbook workbook, string filePath)
    {
       using (var memoryStream = new MemoryStream())
       {
          workbook.SaveAs(memoryStream);
          memoryStream.Position = 0;
 
          using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
          {
             await memoryStream.CopyToAsync(fileStream);
          }
       }
    }
 }


使用例 : 分割処理

以下の例では、メモリ使用量を抑えつつ大量のデータを効率的に処理している。
また、メモリに制約のあるシステムで動作させる場合にも有効である。

  • メモリ使用量の削減
    1度に全データを生成せずに、小さなバッチで処理するため、メモリ使用量を抑えることができる。
  • 柔軟性
    バッチサイズを調整することにより、様々なシステム環境に対応できる。


※注意

  • バッチサイズの選択
    システムのメモリ容量とパフォーマンスを考慮して適切なバッチサイズを選択する必要がある。
  • 処理時間
    全データを1度に処理する場合と比較して、若干処理時間が増加する可能性がある。


 using System;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using ClosedXML.Excel;
 
 class Program
 {
    // Mainメソッドは変更なし
    static async Task Main(string[] args)
    {
       // ...略
    }
 
    static async Task ProcessLargeDataAsync()
    {
       const int totalRows = 1_000_000;  // 全ての処理する行数
       const int batchSize = 10_000;     // 1度に処理する行数
 
       using (var workbook = new XLWorkbook())
       {
          var worksheet = workbook.Worksheets.Add("大量データ");
 
          // ヘッダを追加
          worksheet.Cell(1, 1).Value = "ID";
          worksheet.Cell(1, 2).Value = "Value";
 
          for (int start = 0; start < totalRows; start += batchSize)
          {
             int end = Math.Min(start + batchSize, totalRows);
             await ProcessBatchAsync(worksheet, start, end);
             Console.WriteLine($"Processed rows {start + 1} to {end}");
          }
 
          // 合計を計算
          worksheet.Cell(totalRows + 2, 1).Value     = "合計";
          worksheet.Cell(totalRows + 2, 2).FormulaA1 = $"=SUM(B2:B{totalRows + 1})";
 
          await SaveWorkbookAsync(workbook, "大量データ処理_分割.xlsx");
       }
    }
 
    static async Task ProcessBatchAsync(IXLWorksheet worksheet, int start, int end)
    {
       await Task.Run(() =>
       {
          var data = Enumerable.Range(start + 1, end - start)
                               .Select(i => new { ID = i, Value = i * 2 });
 
          worksheet.Cell(start + 2, 1).InsertData(data);
       });
    }
 
    // SaveWorkbookAsyncメソッドは変更なし
    static async Task SaveWorkbookAsync(XLWorkbook workbook, string filePath)
    {
       // ...略
    }
 }


使用例 : ストリーミング処理

ストリーミング処理とは、データを連続的に処理する方法であり、全データを1度にメモリに読み込むことなく、データが利用可能になり次第処理を行う。
典型的には、データを小さな塊 (チャンク) として読み取り、即座に処理・結果を出力する。

ストリーミング処理をExcelファイルの生成に適用するのは難しく、特に、Closed XMLライブラリではサポートされていない。

OpenXML SDKを使用する場合は、より低レベルでのストリーミングに近い処理が可能であるが、実装が複雑になる。


ハイパーリンクの挿入と管理

以下の例では、ハイパーリンクの挿入と管理を行っている。
A1セルには外部リンク (Google)、A2セルには内部リンク (B5セル) が設定される。
また、プログラム内でA1セルのリンクを変更して、A2セルのリンクを削除している。

  • 外部ハイパーリンクの挿入
    worksheet.Cell("A1").Valueでセルにリンク名を指定する。
    worksheet.Cell("A1").Hyperlinkオブジェクトに、XLHyperlinkオブジェクトを割り当てて、外部リンクを指定する。

  • 内部ハイパーリンク (ワークシート内のセル参照) の作成
    worksheet.Cell("A2").Hyperlinkオブジェクトに、XLHyperlinkオブジェクトを割り当てて、セル参照を文字列で指定する。

  • ハイパーリンクの取得と変更
    worksheet.Cell("A1").Hyperlinkオブジェクトでリンクオブジェクトを取得する。
    link.ExternalAddressプロパティでリンク先のURLを取得または変更できる。

  • ハイパーリンクの削除
    セルのHyperlinkオブジェクトにnullを代入することにより、リンクを削除することができる。


 using System;
 using System.IO;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       try
       {
          // 新しいワークブックを作成
          using (var workbook = new XLWorkbook())
          {
             // ワークシートを追加
             var worksheet = workbook.Worksheets.Add("ハイパーリンクサンプル");
 
             // 2つ目のワークシートを追加 (絶対参照リンクで使用)
             var secondWorksheet = workbook.Worksheets.Add("第2シート");
             secondWorksheet.Cell("A1").Value = "これは第2シートである";
 
             try
             {
                // セルにハイパーリンクを挿入
                worksheet.Cell("A1").Value = "Googleへのリンク";
                worksheet.Cell("A1").Hyperlink = new XLHyperlink("https://www.google.com");
 
                // 別のセルへの内部リンクを作成
                worksheet.Cell("A2").Value = "B5セルへのリンク";
                worksheet.Cell("A2").Hyperlink = new XLHyperlink("B5");
 
                worksheet.Cell("B5").Value = "ここにリンクされています";
 
                // 別シートへの絶対参照を含むハイパーリンクを作成
                // シート名に特殊文字やスペースが含まれる場合、必ずシート名を引用符で囲む必要がある
                worksheet.Cell("A3").Value = "第2シートのA1セルへのリンク";
                worksheet.Cell("A3").Hyperlink = new XLHyperlink("'第2シート'!A1");
 
                // 別シートへの絶対参照リンクの確認
                var sheetLink = worksheet.Cell("A3").Hyperlink;
                if (sheetLink != null)
                {
                   Console.WriteLine($"A3のリンク先: {sheetLink.InternalAddress}");
 
                   // 第2シートの内容を確認
                   Console.WriteLine($"第2シートのA1セルの値: {secondWorksheet.Cell("A1").Value}");
                }
                else
                {
                   Console.WriteLine("警告: A3セルにハイパーリンクが見つかりません");
                }
 
                // ハイパーリンクの取得と変更
                var link = worksheet.Cell("A1").Hyperlink;
                if (link != null)
                {
                   Console.WriteLine($"A1のリンク先: {link.ExternalAddress}");
 
                   // リンクを変更
                   link.ExternalAddress = new Uri("https://www.example.com");
                }
                else
                {
                   Console.WriteLine("警告: A1セルにハイパーリンクが見つかりません。");
                }
 
                // ハイパーリンクの削除
                worksheet.Cell("A2").Hyperlink = null;
             }
             catch (Exception ex)
             {
                Console.WriteLine($"エラー: ワークシートの操作中に問題が発生  詳細: {ex.Message}");
             }
 
             try
             {
                // ファイルを保存
                string filePath = "ハイパーリンクサンプル.xlsx";
                workbook.SaveAs(filePath);
                Console.WriteLine($"Excelファイルが正常に作成: {filePath}");
             }
             catch (IOException ioEx)
             {
                Console.WriteLine($"エラー: ファイルの保存中にIOエラーが発生  詳細: {ioEx.Message}");
             }
             catch (Exception ex)
             {
                Console.WriteLine($"エラー: ファイルの保存中に予期せぬエラーが発生  詳細: {ex.Message}");
             }
          }
       }
       catch (Exception ex)
       {
          Console.WriteLine($"致命的なエラー: プログラムの実行中に予期せぬエラーが発生しました。詳細: {ex.Message}");
       }
    }
 }



印刷設定の調整

以下の例では、Excelの印刷設定を調整している。
指定した印刷設定が適用されたExcelファイルが作成されるため、実際に印刷またはプレビューすることにより、これらの設定の効果を確認できる。

※注意
印刷設定の一部 (特にページレイアウトに関するもの) は、Excelで直接ファイルを開いて確認する必要がある。
実際の印刷結果は、使用するプリンタやドライバにより若干異なる場合がある。

実際の印刷時には、プリンタのドライバや設定により上書きされる可能性がある。
最終的な印刷結果を確認するには、実際にプリンタで印刷する、または、Excelのプリントプレビュー機能を使用することを推奨する。

 using System;
 using ClosedXML.Excel;
 
 class Program
 {
    static void Main(string[] args)
    {
       try
       {
          using (var workbook = new XLWorkbook())
          {
             var worksheet = workbook.Worksheets.Add("印刷設定サンプル");
 
             // サンプルデータの追加
             for (int i = 1; i <= 20; i++)
             {
                worksheet.Cell(i, 1).Value = $"行 {i}";
                worksheet.Cell(i, 2).Value = $"データ {i}";
             }
 
             // ページ設定の調整
             // ページ設定にアクセスする
             var pageSetup = worksheet.PageSetup;
 
             // 用紙サイズと向きの設定
             /// 用紙サイズをA4に設定
             pageSetup.PaperSize = XLPaperSize.A4Paper;
 
             /// 印刷の向きを横向きに設定
             pageSetup.PageOrientation = XLPageOrientation.Landscape;
 
             // 上下左右の余白を設定 (単位はポイント)
             pageSetup.Margins.Top    = 28.35; // 1cm = 28.35ポイント
             pageSetup.Margins.Bottom = 28.35;
             pageSetup.Margins.Left   = 28.35;
             pageSetup.Margins.Right  = 28.35;
 
             // ヘッダとフッタの設定
             // ヘッダ, フッタ, テキスト, ページ番号, 日付を追加
             pageSetup.Header.Left.AddText("ClosedXML サンプル");
             pageSetup.Header.Right.AddText(XLHFPredefinedText.PageNumber);
             pageSetup.Footer.Center.AddText("印刷日: ");
             pageSetup.Footer.Center.AddText(XLHFPredefinedText.Date);
 
             // 印刷範囲の設定
             // 特定の範囲のみを印刷するように設定
             worksheet.PageSetup.PrintAreas.Add("A1:B15");
 
             // 印刷タイトルの設定
             // 各ページで繰り返し表示される行 (通常はヘッダ行) を設定
             worksheet.PageSetup.SetRowsToRepeatAtTop(1, 1);
 
             // 1ページに収まるように自動調整
             // 初期の拡大率を設定, 指定したページ数に収まるように自動調整
             worksheet.PageSetup.Scale               = 100;   // 100%からスタート
             worksheet.PageSetup.ScaleHFWithDocument = true;
             worksheet.PageSetup.FitToPages(1, 0);            // 幅を1ページに、高さは自動
 
             // ラインの表示 / 非表示
             // セルの境界線が印刷されなくなる
             // データの視認性が向上するが、セルの区切りが不明確になる場合がある
             pageSetup.ShowGridlines            = false;  // 印刷時にグリッドライン (セルの罫線) を非表示にする
 
             // 各ページに行番号と列文字が印刷される
             // 大きなデータセットを扱う場合に便利である
             pageSetup.ShowRowAndColumnHeadings = true;   // 印刷時に行番号と列文字 (見出し) を表示する
 
             // 印刷品質の設定
             // 印刷品質をDPI (1インチあたりのドット数) で指定する
             // 以下の例では、600 DPIに指定しているが、使用するプリンタがサポートする値に調整すること
             pageSetup.PrintQuality = 600;
 
             // 白黒印刷の指定
             // カラーデータでも白黒で印刷する
             // ただし、色の違いで表現していた情報が失われる可能性があるので注意すること
             pageSetup.BlackAndWhite = true;
 
             // ファイルの保存
             workbook.SaveAs("印刷設定サンプル.xlsx");
             Console.WriteLine("Excelファイルの作成に成功");
          }
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラー: プログラムの実行中に問題が発生  詳細: {ex.Message}");
       }
    }
 }



コレクションやDataTableの使用

Closed XMLは、コレクションやLINQなどと相性が良い。
コレクションやDataTableオブジェクト(System.Data名前空間)を簡単にワークシートに挿入できる。

以下のサンプルコードは、コレクションを挿入している例である。
データを挿入するInsertDataメソッドは、挿入したセルの範囲を返すので、続けてその範囲のセルを処理しやすい。
また、その範囲から特定の行を抽出するには、ラムダ式で条件を指定できる。

以下のサンプルコードではコレクションを挿入したが、DataTableにデータを挿入するときも同様にInsertDataメソッドを使用する。

 const string ExcelFilePath = @"sample.xlsx";
 
 // Excelファイルに貼り付けたいデータ
 // 例として、ここでは今月の日付と曜日のコレクションを用意する
 List<(DateTime, string)> days = new List<(DateTime, string)>();
 var firstDayOfThisMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
 int daysInThisMonth = DateTime.DaysInMonth(firstDayOfThisMonth.Year, firstDayOfThisMonth.Month);
 
 for (int i = 0; i < daysInThisMonth; i++)
 {
    var d = firstDayOfThisMonth.AddDays(i);
    days.Add((d, d.DayOfWeek.ToString()));
 }
 
 // Excelファイルを開く
 using (var workbook = new XLWorkbook(ExcelFilePath))
 {
    // ワークシートの追加
    using (var worksheet = workbook.Worksheets.Add("サンプルシート2"))
    {
       // タイトル行
       var cellA1 = worksheet.Cell("A1");
       cellA1.Value = "今月のカレンダー";
       cellA1.Style.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
                             .Font.SetFontName("Meiryo")
                             .Font.SetBold(true)
                             .Font.SetFontColor(XLColor.White)
                             .Fill.SetBackgroundColor(XLColor.FromArgb(0x00A2E8));
 
       // セルの連結
       worksheet.Range("A1:B1").Merge();
 
       // ワークシートに日付と曜日のコレクションを挿入
       var calendarRange = worksheet.Cell("A2").InsertData(days);
 
       // 日曜日の行を抽出して使用しているセルの背景を赤に変更
       var sundayRows = calendarRange.Rows(r => r.Cell(1).GetDateTime().DayOfWeek == 0);
       foreach (var r in sundayRows)
       {
          r.CellsUsed().Style.Fill.BackgroundColor = XLColor.Red;
       }
 
       // ワークブックの保存
       workbook.Save();
    }
 
    Console.WriteLine("ワークシートを追加しました。");
 }


上記で作成したExcelファイルを読み取る。
何らかのデータや数式が入力されているセルの範囲を取得するRangeUsedメソッドがあるので、セルの位置を指定せずに簡単にデータ範囲を取り出せる。
そのセル範囲からコレクションを生成するには、LINQのSelect拡張メソッドを使用する。
また、DataTableに変換するのも簡単である。

 const string ExcelFilePath = ".\\sample.xlsx";
 
 // Excelファイルを開く
 using (var workbook = new XLWorkbook(ExcelFilePath))
 {
    // ワークシートの取得
    using (var worksheet = workbook.Worksheet("サンプルシート2"))
    {
       // データや数式が入力されているセルの範囲
       var rangeUsed = worksheet.RangeUsed();
       Console.WriteLine($"使用中のセル範囲:{rangeUsed.FirstCell().Address}~{rangeUsed.LastCell().Address}");
 
       // 1列目がDateTime型の行のみを取得してコレクションに変換
       IEnumerable<(DateTime,string)> data = rangeUsed.Rows(r => r.Cell(1).DataType == XLDataType.DateTime)
                                             .Select(r => (r.Cell(1).GetDateTime(), r.Cell(2).GetString()));
 
       // 最初の7個を表示
       // 出力 : 10/1(Monday) 10/2(Tuesday) 10/3(Wednesday) 10/4(Thursday) 10/5(Friday) 10/6(Saturday) 10/7(Sunday)
       foreach (var (d, s) in data.Take(7))
       {
          Console.WriteLine($"{d:M/d}({s})");
       }
 
       // DataTableへの変換もサポート
       System.Data.DataTable dataTable = rangeUsed.CreateTable().AsNativeDataTable();
 
       // 8個目から7個を表示
       // 出力 : 10/8(Monday) 10/9(Tuesday) 10/10(Wednesday) 10/11(Thursday) 10/12(Friday) 10/13(Saturday) 10/14(Sunday)
       foreach(System.Data.DataRow r in dataTable.Select().Skip(7).Take(7))
       {
          Console.WriteLine($"{r[0]:M/d}({r[1]})");
       }
    }
 }