Excel-DNAで遊ぼう(2)

誰かが新たにデファクトスタンダードになるような帳票ソフトを開発してくれればそれで済むんですが、仕方ないのでそう言ったものが出てくるまでなんとかExcelでやり過ごしていくしかないのです。

と言うわけで、Excel-DNAなる面白いおもちゃがあるのでExcel方眼紙で割と汎用的に使える機能をどんどん作っていきましょう。

セル結合/解除のショートカット

これ、なんでデフォルトでないんでしょうね?やっぱMS社からしても「あんなクソ機能普通使わないだろ…。」って思ってるんでしょうか?私もそう思います。みんなにもそう思って欲しいんですが、ないので実装します。

[ExcelFunction(IsHidden = true)][ExcelCommand(ShortCut = "^M")]
public static void MergeCells()
{
    dynamic app = ExcelDnaUtil.Application;
    app.DisplayAlerts = false;
    app.Selection.MergeCells = app.Selection.MergeCells ? false : true;
    app.DisplayAlerts = true;
}

セル結合の有無はRange.MergeCellsがboolで所持しています。Application.Selectionで現在選択しているセルのRangeを取得し、既に設定されているMergeCellsと逆の値を設定する。それだけです。
選択されているセルに値が入っていると警告が出るのでApplication.DisplayAlertsで制御しています。

あ、大事なことを言い忘れていましたが、Excel-DNAで変更したものはCtrl+Zで元に戻らないので注意してくださいVBAでマクロをバリバリ書いてる人は知っていると思いますが、マクロ実行で変更されたものはUndo/Redoの対象になりません。流石にファイルが自動で保存されたりはしないけれども、うっかり変なことをすると後で泣きを見ます。

背景色のスポイル/ペースト

たまに「こんな色どうやって作ったんだよ…。」みたいな微妙な色をあらゆるところに使う人がいます。別にそれはいいんですけど、例えば設計書の手直しとかになると死ぬほど面倒です。仕方ねぇ書式コピーするかと思うも、どーでもいい罫線も貼り付けられて二度手間三度手間。更にExcel方眼紙になると罫線の扱いがより面倒になり、どんどんと余計なストレスがたまっていきます。
「俺は背景色だけ欲しいんだよ!」とか「デフォルトにある色でいいだろ!このドチャクソオシャレ野郎!」と思うんですが、「背景色のみをコピー」なんて機能はセル結合のショートカットキー以上に実装されなさそうです。作りましょう。

private static int _cacheColor = -4142;

//背景色スポイル
[ExcelFunction(IsHidden = true)][ExcelCommand(ShortCut = "^C")]
public static void SpoilColor()
{
    _cacheColor = (ExcelDnaUtil.Application as dynamic).Selection.Interior.ColorIndex;
}

//背景色ペースト
[ExcelFunction(IsHidden = true)][ExcelCommand(ShortCut = "^P")]
public static void PasteColor()
{
    (ExcelDnaUtil.Application as dynamic).Selection.Interior.ColorIndex = _cacheColor;
}

背景色はRange.Interior.ColerIndexにいます。staticなメンバ変数を宣言しておけば、とりあえずBookを開いている間は保持してくれます。Excelで色にこだわることが多いオシャレ野郎の場合はこのあたりを参考にenumを作っておくと後々楽かもしれません。
ちなみに、-4142は未設定の状態です。戻したい時に使いましょう。
文字色の場合はInteriorをFontにすればOKです。

罫線をいじる

Excelの罫線はRange.Borders(XlBordersIndex).LineStyle = XlLineStyleで設定可能です。
XlBordersIndexにはツールバーにある「格子」や「外枠」にあたるものがありませんが、格子に関してはRange.Borders().LineStyleとすることで、外枠に関してはRange.BorderAroundと言うメソッドで設定可能です。

Excel-DNAにはこの辺のenumがないので(あったら教えてください)、まずこいつらを実装してあげましょう。
勿論各値を直接入れてもいいんですが、かなりわかりにくくなります。

/// <summary>Excel内で使用している罫線の方向</summary>
private enum XlBordersIndex
{
    /// <summary>範囲内の各セルの左上隅から右下への罫線 </summary>
    xlDiagonalDown = 5,
    /// <summary>範囲内の各セルの左下隅から右上への罫線</summary>
    xlDiagonalUp = 6,
    /// <summary>範囲内の下側の罫線</summary>
    xlEdgeBottom = 9,
    /// <summary>範囲内の左端の罫線</summary>
    xlEdgeLeft = 7,
    /// <summary>範囲内の右端の罫線</summary>
    xlEdgeRight = 10,
    /// <summary>範囲内の上側の罫線</summary>
    xlEdgeTop = 8,
    /// <summary>範囲外の罫線を除く、範囲内のすべてのセルの水平罫線</summary>
    xlInsideHorizontal = 12,
    /// <summary>範囲外の罫線を除く、範囲内のすべてのセルの垂直罫線</summary>
    xlInsideVertical = 11,
    /// <summary>範囲内のセルを囲う罫線</summary>
    xlAround = 0,
    /// <summary>範囲内のすべてのセルに上下左右の罫線</summary>
    xlAll = 1,
}

/// <summary>罫線の種類</summary>
private enum XlLineStyle
{
    /// <summary>直線</summary>
    xlContinuous = 1,
    /// <summary>破線</summary>
    xlDash = -4115,
    /// <summary>一点鎖線</summary>
    xlDashDot = 4,
    /// <summary>ニ点鎖線</summary>
    xlDashDotDot = 5,
    /// <summary>点線</summary>
    xlDot = -4118,
    /// <summary>2本線</summary>
    xlDouble = -4119,
    /// <summary>線なし</summary>
    xlLineStyleNone = -4142,
    /// <summary>斜破線</summary>
    xlSlantDashDot = 13,
}

実際に罫線を変更するメソッド

/// <summary>罫線を変更する</summary>
/// <param name="direction">変更する方向</param>
/// <param name="isAdd">罫線の種類(xlLineStyleNoneの場合は削除)</param>
private static void Change(XlBordersIndex direction, XlLineStyle ls)
{
    dynamic selection = (ExcelDnaUtil.Application as dynamic).Selection;

    switch (direction)
    {
        case XlBordersIndex.xlAll:
            selection.Borders().LineStyle = ls;
            break;
        case XlBordersIndex.xlAround:
            if (ls != XlLineStyle.xlLineStyleNone)
            {
                selection.BorderAround(LineStyle: ls);
            }
            else
            {
                selection.Borders(XlBordersIndex.xlEdgeTop).LineStyle = ls;
                selection.Borders(XlBordersIndex.xlEdgeLeft).LineStyle = ls;
                selection.Borders(XlBordersIndex.xlEdgeRight).LineStyle = ls;
                selection.Borders(XlBordersIndex.xlEdgeBottom).LineStyle = ls;
            }
            break;
        default:
            selection.Borders(direction).LineStyle = ls;
            break;
    }
}

厄介なのはRange.BorderAroundでxlLineStyleNoneを指定できないこと。上下左右に対しxlLineStyleNoneを指定することでそれっぽい動きになります。