🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

1回答

4011閲覧

Excelブックの特定のセルの文字列の複数個所の色、文字装飾などを変更したい

dcs

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2021/01/18 05:54

VisualStudio 2017、NPOI v2.5.2を使用
IRichTextString(又はXSSFRichTextString)のApplyFont()を使って1色の変更はできたのですが、2色の変更ができません。
例外的に、1色目の変更箇所が文字列の先頭である場合のみ、正しく動作し、2色の変更ができました。
しかし、1色目の変更箇所が2文字目以降から始まる場合は1色目の色が反映されず、2色目の色だけが変更されてしまいます。

C#

1//赤フォントの設定 2XSSFFont fontRed = (XSSFFont)Book.CreateFont(); 3XSSFColor color1 = new XSSFColor(); 4color1.Indexed = IndexedColors.Red.Index; 5fontRed.SetColor(color1); 6//青フォントの設定 7XSSFFont fontBlue = (XSSFFont)Book.CreateFont(); 8XSSFColor color2 = new XSSFColor(); 9color2.Indexed = IndexedColors.Blue.Index; 10fontBlue.SetColor(color2); 11//リッチテキストを取得 12XSSFRichTextString rText = (XSSFRichTextString)row.GetCell(colIndex).RichStringCellValue; 13rtext.ApplyFont(posBlue, posBlue + 1, fontBlue); //青文字の設定 14cell.SetCellValue(rtext); 15rtext.ApplyFont(posRed, posRed + 1, fontRed); //赤文字の設定 16cell.SetCellValue(rtext);

2文字目(posBlue=1)と3文字目(posRed=2)の色を変更(異常動作:3文字目のみ変更される)
異常動作
イメージ説明
期待した表示
イメージ説明
1文字目(posBlue=0)と3文字目(posRed=2)の色を変更(正常動作)
正常動作
イメージ説明

ApplyFont()をコールする前後でリッチテキストの内容を確認すると、パブリックでないメンバーのXMLが
2度目のApplyFont()のコールで1度目の色設定がクリアされていることが確認できました。

XML

12文字目(posBlue=1)と3文字目(posRed=2)の色を変更(異常動作:3文字目のみ変更される) 2rText.st.XmlText//青文字の設定直後 3<r><t>開</t></r><r><rPr><color rgb=\"0000FF\"/><rFont val=\"Calibri\"/><sz val=\"11\"/></rPr><t>催</t></r><r><t>日</t></r> 4//赤文字の設定直後 5<r><t>開</t></r><r><t>催</t></r><r><rPr><color rgb=\"FF0000\"/><rFont val=\"Calibri\"/><sz val=\"11\"/></rPr><t>日</t></r> 6(催のcolorやrFontの指定が消えてしまっている)

XML

11文字目(posBlue=0)と3文字目(posRed=2)の色を変更(正常動作) 2rText.st.XmlText 3//青文字の設定直後 4<r><rPr><color rgb=\"0000FF\"/><rFont val=\"Calibri\"/><sz val=\"11\"/></rPr><t>開</t></r><r><t>催日</t></r> 5//赤文字の設定直後 6<r><rPr><color rgb=\"0000FF\"/><rFont val=\"Calibri\"/><sz val=\"11\"/></rPr><t>開</t></r><r><t>催</t></r><r><rPr><color rgb=\"FF0000\"/><rFont val=\"Calibri\"/><sz val=\"11\"/></rPr><t>日</t></r>

この箇所は、パブリックでないメンバーなので、外部からは手の下しようがありません。

他にこちらで試した方法としては、変更する色毎に文字列を分割して、Append()を使って追加していく方法を試したところ、一応希望通りの色の変更が行われることは確認しました。

しかし、使い方としては、青文字の設定と、赤文字の設定は別々の処理で行ないたいのです。1色目の設定をして一旦セルに書き戻し、2色目の設定では再度読み直して色の設定を追加してセルに書き戻すような処理を前提としています。

ApplyFont()の使い方が悪いのであれば正しい使い方、ApplyFont()以外の方法があればその方法を教えていただけないでしょうか?よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

回答がつかないようなので多少でもフォローできればと...
NPOIは使用したことがなかったので現象だけに焦点をあてて試してみましたが確かに言われるような動作でした。

そこでdcsさんも言われてるように1文字づつAppendすることで正しく動作することは確認できました。
バグのような感じはするのですがたちまち、自分で対策するとして下記のようなユーティリティクラスを作ってみましたがいかがでしょうか?

動きとしては
1.変更前の設定を1文字単位で保持
2.ApplyFontで設定を追加
3.最後にApplyAppendSettingsで1文字単位でフォントを設定

引数の範囲チェック等は行っていないので使うときはそれなりに実装してください
それと気になったのですが掲示していただいてるサンプルソースでリッチテキスト設定後、cell.SetCellValue(rtext);を行ってますが
CellからRichStringCellValueで取得した場合、その操作は不要のような感じでした。

C#

1 private class RichTextStringController { 2 3 private XSSFRichTextString BaseRichTextString; 4 private Dictionary<int, XSSFFont> AppendSettings = new Dictionary<int, XSSFFont>(); 5 6 public RichTextStringController(XSSFRichTextString richTextString) { 7 this.BaseRichTextString = richTextString; 8 9 var text = richTextString.String; 10 11 if (string.IsNullOrEmpty(text)) { 12 return; 13 } 14 15 for (var i = 0; i < text.Length; i++) { 16 this.AppendSettings.Add(i, richTextString.GetFontAtIndex(i)); 17 } 18 19 } 20 21 public void ApplyFont(int startIndex, int endIndex, XSSFFont font) { 22 23 for (var i = startIndex; i < endIndex; i++) { 24 this.AppendSettings[i] = font; 25 } 26 27 } 28 29 public void ApplyAppendSettings() { 30 31 var text = this.BaseRichTextString.String; 32 33 if (string.IsNullOrEmpty(text)) { 34 return; 35 } 36 37 this.BaseRichTextString.String = ""; 38 39 for (var i = 0; i < text.Length; i++) { 40 this.BaseRichTextString.Append(text.Substring(i, 1), this.AppendSettings[i]); 41 } 42 43 } 44 } 45 46 使用方法 47 var rText = new RichTextStringController((XSSFRichTextString)cell.RichStringCellValue); 48 rText.ApplyFont(posBlue, posBlue + 1, fontBlue); //青文字の設定 49 rText.ApplyFont(posRed, posRed + 1, fontRed); //赤文字の設定 50 rText.ApplyAppendSettings(); 51 52

投稿2021/01/19 06:59

dekaaki

総合スコア292

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dcs

2021/01/19 08:05

回答ありがとうございます。 私も、ご提示いただいたようにAppend()で一気に設定する方法しかないのかなと思いつつ、他にいい案をご存じの方のお知恵を拝借したいと思い質問しました。 cell.SetCellValue(rtext);については、そのあとの再度のGetCell(colIndex).RichStringCellValueのコールが抜けていました。 今取り組んでいるコードでは、1色目の設定と2色目の設定の間に他のセルの操作が結構入り込んでまして、セルにいったん書き戻すようにしています。 各セルの色の設定は、再度セルの値を読み込んで追加するという処理でいけると思ったんですが、大量にある対象セルの色設定情報を保持しておいて、一気に書き戻すしかなさそうですね。 ご提示いただいたコードは参考にさせていただきます。あと2,3日、特に進展がなければ、本回答をもって質問を閉じさせていただきます。
dekaaki

2021/01/19 12:22

>大量にある対象セルの色設定情報を保持しておいて、一気に書き戻すしかなさそうですね 一応、勘違いがあってはいけないので補足しますが回答した方法では必要なタイミングで new RichTextStringController を実行し、その他のセル操作等を行った後、再度 new RichTextStringController で正しく処理できることは確認しましたよ。 なのでパフォーマンスはそこまで高くないかもしれませんがあくまで1セルづつ処理するのでセル数が大量でも少量でもあまり関係ないと思います
dcs

2021/01/19 13:02

はい、そこは理解しているつもりです。ただ、現状のコードではセルの情報を保持していないので、ApplyFont()が思った通りに動けば、セルの色設定情報を保持しなくても良かったのになぁという愚痴です。失礼しました。
dekaaki

2021/01/19 13:35 編集

例えば下記のようにしても正しく動きます。 var rText1 = new RichTextStringController((XSSFRichTextString)row.GetCell(colIndex).RichStringCellValue); rText1.ApplyFont(posBlue, posBlue + 1, fontBlue); //青文字の設定 rText1.ApplyAppendSettings(); ~何らかの処理等や別関数で実行~ var rText2 = new RichTextStringController((XSSFRichTextString)row.GetCell(colIndex).RichStringCellValue); rText2.ApplyFont(posRed, posRed + 1, fontRed); //赤文字の設定 rText2.ApplyAppendSettings(); なので別にセルの情報も保持する必要はないと思うのですが... また、下記のような拡張メソッドを用意してApplyAppendSettingsメソッドでthis.BaseRichTextStringを返すようにすれば rText1.ApplyAppendSettings(); の部分をcell.SetCellValue(rtext)という感じで前と同じ書き方で処理できます。 public static void SetCellValue(this ICell cell, RichTextStringController richTextString) { cell.SetCellValue(richTextString.ApplyAppendSettings()); }
dekaaki

2021/01/19 13:29

すれ違いになりましたね。 理解していただけてるようなのでスルーしておいてください。 それでは他の回答も待ちましょうか。
dekaaki

2021/01/19 14:09

やっぱりちょっと気になったので質問ですが >現状のコードではセルの情報を保持していないので、ApplyFont()が思った通りに動けば、セルの色設定情報を保持しなくても良かったのになぁという愚痴です。 私の示したコードではRichTextStringControllerのコンストラクタで現在設定されている情報を設定してるので前述のコメントでも示したように必要なタイミングでセルのリッチテキスト情報を渡してもらえば正しく処理できるので特にセルの情報を保持する必要もなく、再度シート→行→セルと取得してフォントを設定しても正しく動作できると思うのですが具体的にはどういった場面(処理)を想定しているのでしょうか?
dcs

2021/01/20 00:54

これは失礼しました。私の勘違いです。 richTextString.GetFontAtIndex(i)でフォントの情報は取れているので、仰る通り、色設定のタイミングが異なっていても全く問題ありませんね。 色の設定だけでなく太文字の設定も問題なくできそうです。 大変参考になりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問