####Excel主義者はきれいな表が好き(単に見かけだけ)
Excel主義者はともかくきれいな表が好き。きれいというのは、単に見かけだけで、セル結合したり、罫線で囲った表は、再利用がほとんど不可能だ。
####この1カ月、ともかくExcelの表に苦しんだ。
そもそも罫線なのか図形なのか不明。
罫線で囲んだ文字を移動しようとすると、罫線がついてこない。
罫線で囲んだ文字がフォントを変更すると一部見えなくなるが、罫線を拡大できない。
罫線のある表を拡大縮小できない。
罫線のある表を無理に拡大縮小すると、枠の罫線がでこでこになる。
30ピクセルのExcel方眼紙を突如20ピクセルに変更することになり、図が全部縦長に。シネスコの映画をスタンダードにしたみたい。
結合してあるので、一部だけを移動できない。謎のメッセージ。
結合してある表を選択してコピーしてもコピーできない…。コピーできないっていったいどういうことだ!
####もうへとへとです。
そこにとなりからExcel主義者の軽快なキーボード操作(単調なコピーペーストと移動)の繰り返しがリズミカルに響いてくる。
この単調なリズムは、究極の手作業をしている感じ。
さて、そう歎いていてもしかたない。プログラムでなんとかできることはしようということで、やってみた。
####まずテキストは、こんな感じ。
見出し 1.画面コントロール
ID/2 名称/9 コントロール名/6 I・O/2 必須/2 説明/20 取得DB名/5 取得カラム名/5
1 タイトル ラベル O - タイトルを示すラベル。 - -
2 ユーザID ラベル O - IDの入力位置を示すラベル。 - -
3 ユーザID テキストボックス O - 登録用のユーザIDを表示するテキストボックス。変更不可。 LoginDB ID
4 ユーザ名 ラベル O - ユーザ名の入力位置を示すラベル。 - -
5 ユーザ名 テキストボックス I ○ 登録用のユーザ名を入力するテキストボックス。 LoginDB UserName
6 パスワード ラベル O - PWの入力位置を示すラベル。 - -
7 パスワード Hiddenテキストボックス I - PWを入力するテキストボックス。 - -
8 パスワード(確認) ラベル O - PWの入力位置を示すラベル。 - -
9 パスワード(確認) Hiddenテキストボックス I - 確認用PWを入力するテキストボックス。入力文字は分からないように表示する。 - -
10 キャンセル ボタン I - 処理を中止するボタン。一覧に戻る。 - -
11 変更 ボタン I - 入力内容をチェックして変更するボタン。一覧に戻る。 - -
見出し 2.イベント処理
No./2 種類/10 説明/39
1 変更ボタン押下時 1.入力チェック(3.2チェック参照)☆ OKの場合2へ。☆ NGの場合エラーメッセージを表示。☆2.ユーザ名チェック(3.2チェック参照)☆ NGの場合エラーメッセージを表示。☆3.画面遷移。☆・3.1 ユーザ変更OKの場合管理者:ユーザ管理(一覧)画面に遷移。☆ NGの場合エラーメッセージを表示。☆・3.2 ユーザ変更OKの場合ユーザ:画面に遷移。☆ NGの場合エラーメッセージを表示。
2 キャンセルボタン押下時 2.画面遷移。☆・2.1 管理者:ユーザ管理(一覧)画面に遷移。☆・2.2 ユーザ:ログイン画面に遷移。
3 パスワード(確認)ロストフォーカス時 1.入力チェック(3.2チェック参照)☆ OKの場合なにもしない。☆ NGの場合エラーメッセージを表示。
見出し 3.チェック
No./2 種類/14 説明/23 表示エラーメッセージ/16
1 PW文字チェック 入力したPWを相互にチェック。 PWが異なります。
2 必須チェック 入力項目の必須文字の入力を促す。 ユーザ名が空です。
見出し 4.特記事項
####Markup
ユーザー管理(変更画面)の仕様書と思ってほしい。
ほとんどは平文だが、ごく少量マークアップの文字を使っている。
そもそも「見出し」とテーブルがあり、それぞれは改行ふたつで区切る。
見出しは「見出し」タブと書く。
テーブルは、1行目がヘッダ行となる。
ヘッダ行は、/2で結合するセルの数を示す。/2ならふたつのセルを結合するという意味。
この結合数をマーキングするために、「/」を使っているので、本文では/を使用できない。このあたりはすこし検討の余地がある。
セル内改行(\n)に☆を使っている。
たとえば見出しを####にすれば、Markdownにそっくりになる。
とはいえ、このテキスト、ほとんどテキストといっていいと思う。タブ区切りで素性もいいと思う。
これを下記のコードを通すと、じゃーん! Excelの表のできあがりである。できた表はぜひご自分で確かめられたい。
これを拡張すれば、txt2xlsxとなるなと思っている。
####code
string excelfilepath= "";
using (var book = new XLWorkbook(excelfilepath))
{
var sheet = book.Worksheet(sheetname);
string path = @"text.txt";
var lastusedlinenumber = sheet.LastRowUsed().RangeAddress.LastAddress.RowNumber;
Cell cell = new Cell("C", lastusedlinenumber+2);
string[] tables =File.ReadAllText(path, Encoding.UTF8).Split(new string[] { "\r\n\r\n" }, StringSplitOptions.None);
foreach(var table in tables)
{
List<int> cellwidths = new List<int>();
if (string.IsNullOrEmpty(table.Trim())) continue;
if (table.Contains("見出し"))
{
int targetline = cell.LineNumber + 2;
string[] cells = table.TrimEnd().Split(new string[] { "\r\n", "\r", "\n", "\t" }, StringSplitOptions.None);
string startcell = "B";
ExcelOperation.SetCellValue(sheet, startcell + targetline, cells[1]);
sheet.Range(startcell + targetline + ":" + startcell + targetline).Style.Font.FontSize = 11;
cell.LineNumber = cell.LineNumber + 4;
}
else
{
string[] lines = table.TrimEnd().Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
foreach (var line in lines.Select((Value, Index) => new { Value, Index }))
{
if (line.Value.Contains("/"))
cellwidths.Clear();
int targetline = cell.LineNumber + line.Index;
string[] cells = line.Value.TrimEnd().Split(new string[] { "\t" }, StringSplitOptions.None);
string endcell = cell.HeadCell;
foreach (var c in cells.Select((Value, Index) => new { Value, Index }))
{
string[] cellvalues = c.Value.Split(new string[] { "/" }, StringSplitOptions.None);
//結合セル
int cellwidth = 4;
if (1 < cellvalues.Length) cellwidth = int.Parse(cellvalues[1]);
if (c.Value.Contains("/")) cellwidths.Add(cellwidth);
else if (c.Index < cells.Length) cellwidth = cellwidths[c.Index];
string startcell = endcell;
endcell = cell.ToAlphabet(cell.ToInt(startcell) + cellwidth - 1);
ExcelOperation.SetCellValue(sheet, startcell + targetline, cellvalues[0].Replace("☆", "\n").Replace("I・O", "I/O"));
sheet.Range(startcell + targetline + ":" + endcell + targetline).Merge().Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Alignment.WrapText = true;
sheet.Range(startcell + targetline + ":" + startcell + targetline).Style.Font.FontSize = 9;
//見出し行
if (line.Index == 0 || line.Value.Contains("/"))
{
sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Fill.BackgroundColor = XLColor.LightGray;
sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Font.Bold = true;
sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
}
endcell = cell.ToAlphabet(cell.ToInt(startcell) + cellwidth);
}
}
cell.LineNumber = cell.LineNumber + lines.Length + 3;
}
}
book.SaveAs(savename);
####注記
使用には、ClosedXMLが必要。
別途。Cellクラスというクラスを作っている。セル文字と行を管理しているだけなので、なくても動く。
やっているのは、テキストを行のかたまり=tableにわけ、見出しとテーブルとで処理をわけている。テーブルは、1行目をヘッダ行として特別扱いし、結合するセルの個数をもって、テーブルぜんたいに適応している。
テキストをレイアウトしたあとに、セルを結合し、周囲に罫線を引いている。
という感じ。
これで業務でじっさいに表を作って、かなり省力化できた。ともかく表を作り直すのが楽。検索もできるし、置換もしやすい。
いや作ってよかった。
感覚的にはTeXみたいな感じ。
####ToDo
今後の、ほぼやらないけど課題と感じていることを、いちおう書き出しておこう。
見出しを####に対応して、Markdown的に拡張することを検討。
コードを整理。リファクタリング。
「/」を区切り文字に使うのをやめる。
セルのheightに対応する。
結合セル数を自動指定。これができれば/2のような指定も不要。
セルによってセンタリングと左寄せを使い分け。
仕様書は書き終わったので目的は達成しているので、よほど気が向いて時間があって、ほかにすることがなければ。