前回の残件である「セルに文字列をセットする」方法について説明します。
手順としては(おさらいになりますが)以下のようになります。
- SharedStringパーツからセットしたい文字列を検索して、そのインデックス番号(n番目のsi要素)を取得する。
(このときセットした文字列をもつsi要素が存在しなければ新たにsi要素を追加し、そのインデックス番号を取得する。) - 取得したインデックスを、文字列をセットしたいc要素の値として指定する
文字列をセットするためには、前回のプログラムの57行目を以下のように書き換えます。
cell.DataType = CellValues.SharedString;
cell.CellValue = new CellValue(InsertSharedStringItem("abc", bookPart.SharedStringTablePart).ToString());
ここでInsertSharedStringItemの定義はhttps://msdn.microsoft.com/ja-jp/library/office/gg278314.aspx#code-snippet-2にあります。(以下引用します)
// Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
// and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
// If the part does not contain a SharedStringTable, create one.
if (shareStringPart.SharedStringTable == null)
{
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
// Iterate through all the items in the SharedStringTable. If the text already exists, return its index.
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem and return its index.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
InsertSharedStringItemは先の手順2を行い、そのインデックスを返します。
プログラムを実行するとセルA6に文字列"abc"が入力されます。
実用的な側面から
...と、ここまでで説明を終わっているのがMSDNの上記ページです。
しかし上記のInsertSharedStringItemの実装はあくまでサンプルコードとしての実装であって、これをそのまま実際のアプリケーションで使おうとするといろいろと問題があります。(問題というには大げさかもしれませんが...)
フリガナの扱い
先ほどは文字"abc"をセットしましたが、代わりに"東京特許許可局"とするとどうなるでしょうか?
...意図したとおりにセルA6に"東京特許許可局"が入っています。しかしProductivity Toolでworksheetパーツの中身を覗いてみると以下のようになっています。
同じ"東京特許許可局"にもかかわらず、別のインデックスが振られています。
別のインデックスが割り振られる理由は「フリガナ(ルビ)の存在」によります。SharedStringパーツを見てみると、以下のようになっています。
インデックスが1(2番目)のsi要素にはフリガナ「トウキョウトッキョキョカキョク」があります。それゆえ、InsertSharedStringItemの定義の16行目のitem.InnerTextの値は「東京特許許可局トウキョウトッキョキョカキョク」となってしまい、「東京特許許可局」と一致しません。その結果、新たなsi要素(3番目のsi要素 / インデックス2)が作成されてしまいます。
フリガナのことだけを考えるならば、InsertSharedStringItemの定義の16行目を以下のように書き換えることでセルA2, A6ともに同じインデックスを割り振らせることができます。
if (item.Text != null && item.Text.InnerText == text) {
文字の一部にセットされた書式の扱い
InsertSharedStringItemの定義の16行目を上記のように書き換えることで、フリガナの問題は解決されますが、また別の問題が生じます。
セルA2の内容を「東京特許許可局」のように、一部の書式を変更します。そしてInsertSharedStringItemの定義の16行目を上記のように書き換えてプログラムを実行すると、やはり新たなsi要素を作ってしまいます。
...原因については述べるまでもないでしょう。
実装するアプリケーションの用途によっては、これはこれで期待どおりの振る舞いかもしれません。
文字の一部にセットされた書式はsi要素の中に定義されるので、こういった書式についてもコントロールしたいならば、InsertSharedStringItemの引数を工夫する必要があります。
書式を引数で渡す...というのは限界がありますので、セルにセットする「文字列」を引き渡すのではなくsi要素(DocumentFormat.OpenXml.Spreadsheet.SharedStringItem)を引き渡すようにするとうまくいきます。(引き渡すべきSharedStringItemインスタンスをInsertSharedStringItemの呼び出し側で準備する手間が増えますが...)