OpenXMLでExcelファイルを操作しよう (5) - セルに書式を設定したい

## はじめに

『[C#コードレシピ集](https://www.amazon.co.jp/dp/4297122650)』では掲載できなかったOpen XMLを使ってExcelを操作するレシピを5回に分けて紹介しています。

1. [OpenXMLでExcelファイルを操作しよう (1) - Excelファイルを新規作成したい](https://qiita.com/gushwell/items/5c689e86014105313017)
1. [OpenXMLでExcelファイルを操作しよう (2) - セルに値を設定したい](https://qiita.com/gushwell/items/66ef40e9fedf8e0b6237)  
1. [OpenXMLでExcelファイルを操作しよう (3) - セルの値を読み込みたい](https://qiita.com/gushwell/items/d719fd287be1b4815f2f)
1. [OpenXMLでExcelファイルを操作しよう (4) - 全てのセルの値を取得したい](https://qiita.com/gushwell/items/456e9da791f1a8d5bb59) 
1. [OpenXMLでExcelファイルを操作しよう (5) - セルに書式を設定したい](https://qiita.com/gushwell/items/51c90012e724088d318c)　 ← 今ここ

コードレシピの環境は、C#9.0 + .NET 5 のコンソールアプリケーションです。

なお、『[C#コードレシピ集](https://www.amazon.co.jp/dp/4297122650)』では、NPOIを使ってエクセルファイルを操作するレシピを掲載しています。

## 定義するメソッド

最終回の今回はセルにスタイルを設定する方法を示します。

前回同様、MyExcelBookクラスに機能を追加していきます。今回は１つのメソッドを追加するとともに、既存のメソッドを変更します。

メソッド | 機能
------------|----------------------------
CreateMyStylesheet | スタイルシートの作成　(新規)
CreateBook |　Bookの作成 (変更)
SetValueAtCell | セルに値を設定 (変更)
SetValue | 行とカラムで指定したセルに値を設定 (変更)


CreateMyStylesheetメソッドでは、Bookで利用するFont, Fill, Borderを定義し、それを組み合わせて、3つのスタイルを作成しています。
これは汎用的なメソッドではなく、このサンプルで利用する固定的なスタイルを定義しています。
3つのスタイルには、それぞれインデックスが付与され、このインデックスを使いセルのスタイルを指定します。

CreateBookメソッドには、上記CreateMyStylesheetを呼び出し、定義したスタイルをbookに登録し保存する機能を追加しています。

SetValueAtCellメソッドとSetValueメソッドには、引数にスタイルのインデックスを追加しています。


## MyExcelBook　クラス

MyExcelBookクラスを示します。コードが長くなるので、このサンプルでは利用しないメソッドは省略しています。


```cs
using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Packaging;

sealed class MyExcelBook : IDisposable 
{
    private SpreadsheetDocument _document;
    private WorkbookPart _workbookpart;
    private WorksheetPart _worksheetPart;
    private Sheets _sheets;

    private MyExcelBook() { }

    public void Dispose() => _document?.Dispose();

    // 変更したメソッド
    public static MyExcelBook CreateBook(string filepath) 
    {
        var book = new MyExcelBook();
        book._document = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
        book._workbookpart = book._document.AddWorkbookPart();
        book._workbookpart.Workbook = new Workbook();
        book._sheets = book._workbookpart.Workbook.AppendChild<Sheets>(new Sheets());
        var stylePart = book._workbookpart.AddNewPart<WorkbookStylesPart>();
        stylePart.Stylesheet = book.CreateMyStylesheet();
        stylePart.Stylesheet.Save();
        return book;
    }

    public void Save() 
    {
        _workbookpart.Workbook.Save();
        _document.Close();
    }

    // 追加したメソッド
    public Stylesheet CreateMyStylesheet() 
    {
        Fonts fonts = new Fonts(
            new Font(new FontSize() { Val = 12 }),
            new Font(
                new FontSize() { Val = 16 },
                new Bold(),
                new Color() { Rgb = "FFFFFF" }
            )
        );
        Fills fills = new Fills(
            new Fill(),
            new Fill(
                new PatternFill() { PatternType = PatternValues.Gray125 }),
            new Fill(
                new PatternFill(
                    new ForegroundColor() {
                        Rgb = new HexBinaryValue() { Value = "662244" }
                    }
                ) { PatternType = PatternValues.Solid })
        );
        Borders borders = new Borders(
            new Border(),
            new Border(
                new LeftBorder(new Color() { Auto = true }) {
                    Style = BorderStyleValues.Thin
                },
                new RightBorder(new Color() { Auto = true }) {
                    Style = BorderStyleValues.Thin
                },
                new TopBorder(new Color() { Auto = true }) {
                    Style = BorderStyleValues.Thin
                },
                new BottomBorder(new Color() { Auto = true }) {
                    Style = BorderStyleValues.Thin
                },
                new DiagonalBorder())
        );
        CellFormats cellFormats = new CellFormats(
            new CellFormat { FontId = 0, FillId = 0, BorderId = 0 },  // Index 0番のスタイル
            new CellFormat { FontId = 0, FillId = 1, BorderId = 1 },  // Index 1番のスタイル
            new CellFormat { FontId = 1, FillId = 2, BorderId = 0 }   // Index 2番のスタイル
        );
        return new Stylesheet(fonts, fills, borders, cellFormats);
    }

    public void CreateSheet(string sheetname) 
    {
        _worksheetPart = _workbookpart.AddNewPart<WorksheetPart>();
        _worksheetPart.Worksheet = new Worksheet(new SheetData());

        uint max = (uint)(_sheets.Count() + 1);
        Sheet sheet = new Sheet {
            Id = _workbookpart.GetIdOfPart(_worksheetPart),
            SheetId = max,
            Name = sheetname
        };
        _sheets.Append(sheet);
    }

    public void SelectSheet(string sheetName) 
    {
        var theSheet = _workbookpart.Workbook.Descendants<Sheet>().
            Where(s => s.Name == sheetName).FirstOrDefault();

        if (theSheet == null)
            throw new ArgumentException("sheetName");

        _worksheetPart = (WorksheetPart)(_workbookpart.GetPartById(theSheet.Id));
    }

    // 変更したメソッド　(引数にstyleIndexを追加)
    public void SetValue(string text, string columnName, uint rowIndex, uint styleIndex) 
    {
        Cell cell = CreateCell(columnName, rowIndex);
        SetValueAtCell(cell, text, styleIndex);
    }

    // 変更したメソッド　(引数にstyleIndexを追加)
    public void SetValueAtCell(Cell cell, string text, uint styleIndex) 
    {
        cell.CellValue = new CellValue(text);
        cell.DataType = new EnumValue<CellValues>(CellValues.String);
        cell.StyleIndex = styleIndex;
    }

    public Cell CreateCell(string columnName, uint rowIndex) 
    {
        Worksheet worksheet = _worksheetPart.Worksheet;
        SheetData sheetData = worksheet.GetFirstChild<SheetData>();

        Row row = sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
        if (row == null) 
        {
            row = new Row { RowIndex = rowIndex };
            sheetData.Append(row);
        }

        string cellReference = columnName + rowIndex;
        Cell cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference.Value == cellReference);
        if (cell != null)
            return cell;
        Cell refCell = row.Elements<Cell>()
            .FirstOrDefault(c => string.Compare(c.CellReference.Value, cellReference, true) > 0);
        Cell newCell = new Cell() 
        {
            CellReference = cellReference
        };
        row.InsertBefore(newCell, refCell);
        return newCell;
    }

    // 以下省略　読み取り関連のメソッド
}
```

## MyExcelBookクラスを利用し、セルに書式を設定する

最初に示した通り、CreateSheetの中で固定的なスタイルを定義しています。そのため呼び出し側だけではどんなスタイルかはわかりません。
下記のコードだけだと、SetValueでスタイルに付番された番号 0, 1, 2を与えて、スタイルを指定してセルに値を設定しているということだけが読み取れます。
サンプルということでご容赦ください。


```cs
using var book = MyExcelBook.CreateBook("example.xlsx");
book.CreateSheet("sheet1");
book.SelectSheet("sheet1");
book.SetValue("Text 1", "A", 1, 0);　　// 0番のスタイル指定
book.SetValue("Text 2", "B", 2, 1);　　// 1番のスタイル指定
book.SetValue("Text 3", "C", 3, 2);　　// 2番のスタイル指定
book.Save();
```

## 作成された Excel ファイル

![スクリーンショット 2021-11-03 9.41.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/6469b8c0-0c14-022f-6be5-936c72dd87e7.png)


## 最後に

Open XML SDKが提供する機能は、Microfost Docsの[ここ](https://docs.microsoft.com/ja-jp/office/open-xml/open-xml-sdk)で参照することができます。気が遠くなるくらい膨大な量です(笑)。Open XMLについてさらに詳しい内容を知りたい場合は是非参考にしてください。