はじめに
この記事は長野高専 Advent Calendar 2024の14日目の記事です。
OBですが参加しております。主催ありがとうございます!
本記事では、C#のスプレッドシートライブラリであるSpreadsheetGearで、結合セルがどのように扱われるか簡単に説明します。
業務で扱っていて少しややこしかった(当社比)ので、備忘録的記事となります。
正直なところこのライブラリを使っている人は本当に少ない(エンタープライズ向けなところはある)ので、需要が無さすぎる記事かと思いますが、自分のために書き残させてください。
わざわざ記事を書いていますが、大体のことは公式ドキュメントに書いてあります。
前提
今回使用しているライブラリは以下の通りです。
- SpreadsheetGear2023 Ver9.2.44.102
説明に使用するworkbook
, worksheet
は以下のように定義しているものとします。
SpreadsheetGear.IWorkbook workbook = SpreadsheetGear.Factory.GetWorkbook();
SpreadsheetGear.IWorksheet worksheet = workbook.ActiveWorksheet;
結合セルのあれこれ
主に行う操作として以下の内容を説明します。
- 結合、解除
- 結合セルか、そうでないかのチェック
- アドレス取得
- 値セット、取得
結合、解除
結合したい範囲のIRange
を定義して、IRange.Merge()
メソッドを実行することでセル範囲が結合されます。
// B2からE5を結合する
SpreadsheetGear.IRange range = worksheet.Cells["B2:E5"];
range.Merge();
結合解除したい範囲のIRange
を定義して、IRange.UnMerge()
メソッドを実行することでセル結合が解除されます。UnMerge()
を使用するとIRange
に含まれるすべてのセル結合が解除されます。
注意点として、IRange
がセル結合を含んでおり、その範囲がセル結合を完全に内包していない場合はSystem.InvalidOperationException
がスローされます。
IRange
がセル結合を含まない場合は、UnMerge()
を実行しても何も起きません。
// セル結合を解除する
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
range1.UnMerge();
// A1からF6はセル結合を完全に内包しているのでOK
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
SpreadsheetGear.IRange range2 = worksheet.Cells["A1:F6"];
range2.UnMerge();
// A1からB2はセル結合を一部しか含まないためNG
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
var range2 = worksheet.Cells["A1:B2"];
range2.UnMerge(); // InvalidOperationException
// 結合セルが含まれない場合は何も起きない
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.UnMerge(); // 何も起きない
結合セルか、そうでないかのチェック
チェックしたい範囲のIRange
でIRange.MergeCells
プロパティを参照することで、セル範囲が結合セルであるかどうかをチェックできます。
MergeCells
プロパティは、厳密には
-
MergeCells = true
…IRange.Merge()
と同じ操作 -
MergeCells = false
…IRange.UnMerge()
と同じ操作
という挙動をするプロパティですが、プロパティの操作で結合の状態が変化するのは不自然だと思う(個人の感想)ので、結合・解除はメソッドを使用して、MergeCell
は参照するだけに留める使い方をしています。
MergeCells
は、IRange
の含むセルすべてが結合セルに属する場合true
、一つでも結合セルではないセルを含む場合はfalse
となります。
// range1に含まれるセルは全て結合セルのためtrue
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
Console.WriteLine(range1.MergeCells); // true
// IRangeの含むセルすべてが結合セルの場合trueとなるので
// 2個以上結合セルがあってもtrueになる場合がある
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
SpreadsheetGear.IRange range2 = worksheet.Cells["F2:G5"];
range2.Merge();
// 2個以上の結合セルを含むセル範囲
SpreadsheetGear.IRange range3 = worksheet.Cells["B2:G5"];
Console.WriteLine(range3.MergeCells); // true
// 1つでも結合セルでないセルを含む場合はfalse
SpreadsheetGear.IRange range1 = worksheet.Cells["B2:E5"];
range1.Merge();
SpreadsheetGear.IRange range2 = worksheet.Cells["B2:G5"];
Console.WriteLine(range2.MergeCells); // false
UnMerge()
で意図せずSystem.InvalidOperationException
が起きたら萎えるので、結構大事だったりします。
注意点として、IRange
が結合セルの一部であっても、セルすべてが何らかの結合セルに属する場合、MergeCells
はtrueになります。
// 1つでも結合セルでないセルを含む場合はfalse
SpreadsheetGear.IRange range1 = worksheet.Cells["B1:D5"];
range1.Merge();
Console.WriteLine(range1.MergeCells); // true ← :)
SpreadsheetGear.IRange range2 = worksheet.Cells["B1:C5"];
Console.WriteLine(range2.MergeCells); // true ← ?????????
じゃあ手元にあるセル範囲がビタビタに結合セルだと判別したい場合はどうするんだ!
B1:D3
に対してMergeCells
を見ても、B1:C3
に対してMergeCells
を見ても、どちらもtrue
が返ってきます。
そうじゃなくて、「手元にあるB1:D3
が結合セルそのものを表しているかどうか」をチェックしたい場合があります。
そんな場合は、次のプロパティを活用しましょう。
アドレス取得
定義されたIRange
のIRange.Address
プロパティを参照することで、そのセル範囲のアドレスを取得できます。
しかし、IRange.Address
がそのまま結合セルのセルアドレスを示すとは限りません。
結合セルのセルアドレスは、IRange.MergeArea.Adderss
プロパティを参照することで取得できます。
IRange.MergeArea
とは
IRange.MergeArea
プロパティは、IRange
が1セルを指す場合にのみ使用でき、そのセルが属する結合セルのIRange
を返します。
// セル結合
SpreadsheetGear.IRange range1 = worksheet.Cells["B1:D5"];
range1.Merge();
Console.WriteLine(range1.Address); // $B$1:$D$5
// 1セルを指定
SpreadsheetGear.IRange range2 = worksheet.Cells["C2"];
Console.WriteLine(range2.Address); // $C$2
Console.WriteLine(range2.MergeArea.Address); // $B$1:$D$5
MergeArea.Address
を活用することで、以下のような判定が可能になります。
// チェックしたいアドレス
var address = "$B$1:$D$5";
SpreadsheetGear.IRange range1 = worksheet.Cells[address];
if (range1[0, 0].MergeArea.Address == address) {
Console.WriteLine("結合セルそのもの");
} else {
Console.WriteLine("ビタビタではない");
}
これで上で述べた要望が解決できそうです。
値セット、取得
結合セルに対しての値セットと取得は、通常のセルと同様IRange.Value
で行います。
SpreadsheetGear.IRange range1 = worksheet.Cells["B1:D5"];
range1.Merge();
// 値セット
range1.Value = "hoge";
// 値取得
var hoge = range1.Value;
この「値」の扱いに気を付ける必要があります。(Excelと挙動が若干異なります)
値セット
結合セルに対して値をセットした後、その結合を解除すると、それぞれのセルの値はどのようになっているでしょうか。マージ直後、解除直後でセルの状態を以下に示します。
SpreadsheetGear.IRange range1 = worksheet.Cells["B1:D5"];
range1.Merge();
range1.Value = "hoge"; // マージ直後
range1.UnMerge(); // 解除直後
マージ直後
解除直後
Excelとの相違点は、「セル結合解除時に元のセルに値が入るかどうか」です。
Excelでは解除時に左上セルのみに値が入りますが、SpreadsheetGearでは、結合解除時に元のすべてのセルに値が入ります。
値取得
B1からD5を結合して、C2から値を取得した場合の違いを見てみましょう。
SpreadsheetGear
SpreadsheetGear.IRange range1 = worksheet.Cells["B1:D5"];
range1.Merge();
range1.Value = "hoge";
Console.WriteLine(worksheet.Cells["C2"]); // hoge
Excel
結合セルから値を取得するときも、値セットと同様に「すべてのセルに値が入っているかどうか」の違いが表れてきます。
おわりに
いかがだったでしょうか?
仕事を始めて2年が経とうとしています。少しずつではありますが、C#とライブラリについて理解できてきた気がします。
記事にするほどでもない備忘録でしたが、最後までお読みいただきありがとうございました。
付録
SpreadsheetGear2023 Reference