2016年10月3日月曜日

Office Excel 用アドイン(今日の日付を入力)の作成

Excelで編集しているときに、割とよく入力中のセルに今日の日付を入れたい、と思う時があります。
例えば10月3日なら、10/3 と入力するだけで今日の日付になる、というのは知ってます。
でもね、それをワンタッチで実行したいわけですよ。

VBAとやらでマクロを作ることでもできそうですが、そこは C#使いのあちきのことですから、アドインで実行したい。
ということで、Officeアドインの作り方をまとめてみたいと思います。(前にも書いたような気がするけど気のせいだなw)

まずは Visual Studio (今回は2015)で、新規プロジェクトを作成します。
「新しいプロジェクト」で「Office Addin」-「Excel 2010 VSTO アドイン」を選択します。





アドインプロジェクトができたら、まずリボンを実装してみましょう。
プロジェクトに新しい項目としてリボン(ビジュアルなデザイナー)を追加して、グループボックスとボタンを追加します。

今回、リボン名は TodayRibbon にしました。
ThisAddIn.cs ファイルに次のコードを追加します。
protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
{ 

    return Globals.Factory.GetRibbonFactory().CreateRibbonManager(
        new Microsoft.Office.Tools.Ribbon.IRibbonExtension[] { new TodayRibbon() }

    );
}


ここまでの手順だけでプロジェクトをビルドして実行すると、Excelのリボンに「アドイン」と書かれたタブが追加されます。リボンの追加方法は、あちこちのサイトで説明されていますので他も参考にしてくださいませ。
タブが追加されてボタンが表示されるようになっても、セル上に今日の日付を入力するのに、タブを選択してボタンを押してまたホームタブに戻らなきゃいけない、ってのはとってもめんどくさいですよね?
エクセル標準のリボンタブ「ホーム」にボタンが配置されるようにしたいですよね?
ところが、その方法はググってもあまり説明されていないみたいです。
XAMLを書かなきゃいけないと書いてあるところもありますが、リボンデザイナーで記述することができます。
ここではその辺を詳しく説明したいと思います。
まず必要になるのが、Office 2010 Help Files: Office Fluent User Interface Control Identifiers
です。
こちらからダウンロードできます。
https://www.microsoft.com/en-us/download/details.aspx?id=6627
今回使用するのは、このファイルの中に含まれる、ExcelControls.xlsx ファイルになります。

開いてみましょう。


何やらいろいろ書かれていますが、←部分を見てください。
TabHome           tab
と書かれています。ここから下の部分がExcelを開いたときに表示される、「ホーム」のリボンメニュータブになっています。
次の行には「GroupClipboard」と書かれてますね。エクセルを最初に開くとリボンメニューのもっとも左側に「クリップボード」のグループがあります。
つまり、このリストの順番でコントロールが並べられているわけです。
今回のアドインで作成するボタンは、

このあたりに追加してみたい
ということで、エクセルファイルをスクロールしていくと、
数値タブを示す、[GroupNumber]の group とスタイルタブを示す「GroupStyles」のgroupを見つけることができます。
さて、準備完了です。リボンデザイナーで次のように処理していきます。

1.タブをExcelのホームタブに設定


リボンデザイナの上部をクリックして、プロパティの RibbonType を Microsoft.Excel.Workbookに設定します。
この設定は、リボンがExcelのワークブック用であることを示しています。

2.タブの設定
次にタブメニューを設定します。

リボンデザイナのタブの位置をクリックして、以下のようにプロパティを設定します。
ControlId-ControlIdType をOfficeに設定し、OfficeIdを TabHome に設定します。

TabHome ...どこかで出てきましたね。
そう先ほどの ExcelControls.xlsx ファイルで tab として記載されていた語句です。
この指定で、このタブが、Excelの「ホーム」タブに所属するタブだということが設定されます。

3.グループの設定
最後にグループの設定を行います。もう要領がわかってきたかな?

リボンデザイナでグループボックスをクリックして、プロパティを設定します。
GroupBoxのPositionプロパティのPositionType をBeforeOfficeId として、OfficeIdの部分に GroupStyles を入力します。
これが、ExcelControls.xlsxに記載されていたグループの名前で、その Before、つまり「スタイルタブ」の前に配置する、という意味ですね。
もちろん、AfterOfficeIdを選んで GroupNumberと入力しても同じ位置に表示されます。
(実際には、他のアドインがその場所を横取りする場合があるかもしれません)

ということで、プロジェクトを実行すると、目的の場所に[Today]ボタンが表示されるようになりました。

最後は、[Today]ボタンが押された時の処理を設定します。
リボンデザイナで[Today]ボタンをダブルクリックすると、リボンのコードに次のコードが作成されます。


private void btnToday_Click(object sender, RibbonControlEventArgs e)
{
}


リボンのコードは class TodayRibbon に属していますが、アドインで処理したいエクセルの情報はThisAddIn のインスタンスから参照するほうが楽ですので、次のように ThisAddIn クラスのインスタンスに対して処理を依頼するようにします。

private void btnToday_Click(object sender, RibbonControlEventArgs e)
{
     ThisAddIn logic = ThisAddIn.Instance();
     if(logic != null)
     {
         logic.onButtonToday();
     }
}


ThisAddInのインスタンスを取得できるように、ThisAddIn.cs に次のようなコードを追加します。

public static ThisAddIn instance = null;
public static ThisAddIn Instance()
{
     return instance;
}


instance = this; を ThisAddIn_Startup 内に記述すればOKですね。
※ブレイクポイントを付けてデバッグしてみるとわかるのですが、ThisAddIn_Startup よりも先に CreateRibbonExtensibilityObject が呼び出されてきますので、この位置に instance = this; を付けることもできます。

さて、最後に現在のセル内に今日の日付を設定する処理です。
エクセル用のデータを操作するには、通常、Excel.Workbook オブジェクトを取得し、Excel.WorkSheetオブジェクトを取得し、んでもって...という手順を踏む必要があるのですが、選択中のセルに対する処理では、ThisAddIn.Application.Selection を使用すると現在のセルが取得できました。
ThisAddInには次のコードを記述ました。

internal void onButtonToday()
{
    Excel.Range range = null;
    try
    {
        range = Application.Selection;
        if (range != null)
        {
            range.Value = DateTime.Today;
        }
    }
    finally
    {
        if (range != null)
        {
            Marshal.ReleaseComObject(range);
        }
    }

}


ビルドして実行してみましょう。

うまく動いてくれました。

このアドインのインストーラ欲しい人っているかなー?
求められたら、フリーウェアで公開しますね。

【追記】
上記の実装では、次のような問題があります。
1.IMEがONの場合、全角の日付が入力されるので半角変換しないと日付フォーマットが効かない。
2.アンドゥができない(致命傷w

ということで、これらの問題をいろいろ解決しようと努力しているところで、こんなコマンドを知ることになりました。
[CTRL]+[;](CTRLキーを押しながらセミコロンを押す)

なんと、今日の日付がワンタッチで入力できてしまいました。
ちゃんちゃんw