はじめに
この記事は、ライブラリ「SpreadsheetGear」と「SpreadJS」に関する、備忘録的なものである。
ReferenceとStack Overflowを漁り尽くして、果てには公式に問い合わせながら、ようやく何とかなった内容を忘れるわけにはいかないので、書きます。
背景
Windowsアプリ(SpreadsheetGear)とWebアプリ(SpreadJS)を開発中。
現在はスプレッドに表示される数値の区切り文字が、固定で「,」「.」になっている。
これをユーザの設定と同期させたい!
技術詳細
今回の記事で登場する技術を示す。
使用しているが必要ないと判断したものについては省略している。
- 開発言語
- サーバサイド…C# 6.0
- フロントエンド…TypeScript 4.3
- フレームワーク… .NET Framework 4.6
- ライブラリ…SpreadsheetGear2017 Ver 8.6.10.102, SpreadJS 9J SP2 (Ver. 9.20171.0)
実践
実際に区切り文字を変更してみる。
以降は、記述を省略するために以下のように単語を明記する。
- SpreadsheetGear → Gear
- SpreadJS → SJS
手順
区切り文字を適用するための簡単な手順は、以下のとおりである。
- Gearの場合
- ユーザ設定(Windows)の区切り文字を取得する
- (必要に応じて) 設定対象の
Workbook
をByte
にしてサーバ上で保持しておく - ユーザ設定の区切り文字を適用した
CultureInfo
を作成する - 作成した
CultureInfo
をもとにWorkbookSet
を作成する - スプレッドの
ActiveWorkbookSet
を4. で作成したWorkbookSet
にする - (必要に応じて)
WorkbookSet.WorkBooks
に2. で保持したWorkbook
を読み込む
- SJSの場合
- サーバサイドで、ユーザ設定(ブラウザ)の区切り文字を取得する
- 取得した区切り文字を含めたデータを、スプレッド表示メソッドに渡す
- フロントでユーザ設定の区切り文字を適用した
CultureInfo
を作成する - 作成した
CultureInfo
をSJSのカルチャに追加する - SJSの現在のカルチャを追加したカルチャに変更する
GearはSJSのように安直な設定ができなかった。本当にキレそう。
後述するが、プロパティの操作に難解な部分があったためである。
また、今回の記事の手法は、Microsoftから提供されている標準のカルチャ設定において、区切り文字以外のプロパティを変更する場合を考慮していない。
区切り文字以外のプロパティを操作する場合は、適宜設定を保持した上で区切り文字適用の処理を行うことが望ましい。
Gearの場合
ユーザ設定の区切り文字を取得
//
// 1. ユーザ設定(Windows)の区切り文字を取得する
//
var cultureInfo = System.Globalization.CultureInfo.CurrentCulture;
// 後で使うのでカルチャ名(コード)を保持する
this.m_cultureName = cultureInfo.Name;
// 取得したCultureInfoから、各区切り文字を取得
this.m_cgs = cultureInfo.NumberFormat.CurrencyGroupSeparator;
this.m_nds = cultureInfo.NumberFormat.NumberDecimalSeparator;
.
.
.
WorkbookSet
, Workbook
の操作、区切り文字の適用
//
// 2. (必要に応じて) 設定対象のWorkbookをByteにしてサーバ上で保持しておく
//
this.m_byteWorkbook = workbook.SaveToMemory(this.m_fileFormat);
.
.
.
//
// 3. ユーザ設定の区切り文字を適用したCultureInfoを作成する
// 「ユーザ設定の区切り文字」で保持したカルチャ名を使用する
//
var currentCulture = new System.Globalization.CultureInfo(CultureName);
var numberFormat = currentCulture.NumberFormat;
numberFormat.CurrencyGroupSeparator = this.m_cgs;
numberFormat.NumberDecimalSeparator = this.m_nds;
.
.
.
//
// 4. 作成したCultureInfoをもとにWorkbookSetを作成する
// 5. スプレッドのActiveWorkbookSetを4. で作成したWorkbookSetにする
//
this.Spread.ActiveWrkbookSet = SpreadsheetGear.Factory.GetWorkbookSet(currentCulture);
//
// 6. (必要に応じて) WorkbookSet.WorkBooksに2. で保持したWorkbookを読み込む
//
this.Spread.ActiveWrkbookSet.Workbooks.OpenFromMemory(this.m_byteWorkbook);
SJSの場合
ユーザ設定の区切り文字を取得
//
// 1. サーバサイドで、ユーザ設定(ブラウザ)の区切り文字を取得する
// C#で実装した、Viewを返すAPIで書く
//
public ActionResult Index(string hoge) {
.
.
.
// ユーザ設定の言語一覧を取得
// 配列として入っている。優先度が高いものが若いインデックス
var userLangs = Request.UserLanguages;
// 取得した言語一覧からSystem.Globalization.CultureInfoを作成
CultureInfo userCultureInfo = userLangs != null && userLangs.Length > 0
? new CultureInfo(userLangs[0])
: CultureInfo.CurrentCulture;
// 取得したCultureInfoから、各区切り文字を取得
this.m_cgs = userCultureInfo.NumberFormat.CurrencyGroupSeparator;
this.m_nds = userCultureInfo.NumberFormat.NumberDecimalSeparator;
.
.
.
}
カルチャの区切り文字を変更、そのカルチャを適用
public loadSpread(data: any) {
//
// 2. フロントでユーザ設定の区切り文字を適用したCultureInfoを作成する
//
// 現在のCultureInfoを取得
var cultureInfo = GC.Spread.Common.CultureManager.getCultureInfo(GC.Spread.Common.CultureManager.culture());
// CultureInfoのdeepCopyを作成
// 今回の範囲に影響が無いため、undefinedは考慮しないコピー方法を用いる
var usrCulture: GC.Spread.Common.CultureInfo = JSON.parse(JSON.stringify(cultureInfo));
var numberFormat: any = usrCulture.NumberFormat;
numberFormat.currencyGroupSeparator = data.Separator.CurrencyGroup;
numberFormat.numberDecimalSeparator = data.Separator.NumberDecimal;
.
.
.
//
// 3. 作成したCultureInfoをSJSのカルチャに追加する
//
GC.Spread.Common.CultureManager.addCultureInfo("lo-cl", usrCulture);
//
// 4. SJSの現在のカルチャを追加したカルチャに変更する
//
GC.Spread.Common.CultureManager.culture("lo-cl");
}
要素の解説
機能実現に必要な要素を簡単に説明する。
分かっている人にとっては別にいらないセクションかと。
書式設定
今回はコードを掲載していないが、必要な要素のため説明する。
そもそもセルの書式で「桁区切り」を指定しないと、見た目では小数点だけしか変化しない。
一つのセルの書式設定は、以下の方法で取得できる。
- Gearの場合…
string IRange.NumberFormat
- SJSの場合…
GC.Spread.Sheets.CellRange.formatter(value: undefined): any
ここに#,##.####
のような数値書式を指定することで、セルに入力された数値は桁区切りを伴って表示される。
ユーザ設定の区切り文字
区切り文字のプロパティは、WinもWebも言語設定に含まれている。
WindowsアプリではWindowsの言語設定から取得するようにし、Webアプリではブラウザの言語設定から取得する。
それぞれのプラットフォームで読み書きできる区切り文字の種類は、公式のドキュメントを参考にする。
(Gear)WorkbookSet
, Workbook
の操作
Gearに限り、変更されたカルチャを定義して、そのカルチャを適用したWorkbookSet
を作成して、現在のWorkbookSet
にする、というあほ作業がある。
この手法を取る理由は、直接NumberFormatを指定しても区切り文字が変化しないといった仕様が原因である。
この仕様について簡単に説明する。
直接NumberFormatを指定しても区切り文字が変化しない
今回の実装を行っていて、一番頭を悩まされた点である。
上記のReferenceにもある通り、CultureInfo
はget
のみ付いている。
CultureInfo
が取得できればその中にあるNumberFormat
も操作できてしまう。NumberFormat
はget
とset
がついているためである。
そのため、ActiveWorkbookSet.Culture.NumberFormat.○○○
に直接文字を指定すれば、区切り文字が変わるだろう、と思っていた。
実際は、表示される区切り文字が変化していなかった。
加えて、プロパティのウォッチではActiveWorkbookSet.Culture.NumberFormat.○○○
は指定通りに変更されていたのである。本当に意味が分からない。
この仕様に対する考察
CultureInfo
はWorkbookSet
の作成時に指定するものである、という思想を体現するために、CultureInfo
にget
のみ付けていると思われる。(というかそう考えないと腑に落ちない)
実はSJSにおいても、CurrentCulture
のNumberFormat
を直接指定しても区切り文字が変化しない、という挙動が存在する。SJSの適用の番のコードが、暫定的な回避策である。
この辺りからも怪しいと疑うべきだったか...
この挙動については問い合わせていないため、詳しいことは不明であるが、とにかく不便。
まとめ
いかがでしたか!?
多言語対応(狭義)はもう懲り懲りです。あとTS触らせないでください。僕はC#に籠ります。
この記事が、僕と同じくSpreadsheetGearとSpreadJSの仕様に悩む人々に届くことを祈っています。
追記
2024/05/21
ActiveWorkbookSet
にFactory
で作成したWorkbookSet
を直接指定する方法でも区切り文字を変更することができたため、一部コードを修正。
付録
SpreadJSの仕様についてMescius社への問い合わせ
はじめにのセクションで述べた、公式への問い合わせである。
区切り文字を変更するという目的には関係ないが、見逃せない挙動であることと自己解決が厳しいと判断したため、公式に問い合わせた。
挙動を要約すると、
- 区切り文字を変更すると、小数点以下の数値に桁区切り文字が表示されてしまう
というものである。
結論としては、
- SpreadJS(v17.0.5)以前で発生する既知の不具合
であった。散々悩まされた結果が、過去バージョンに存在する既知のバグだとわかったときは、今年一番萎えた。
興味があれば、以下リンクを参考いただきたい。