実用的アプリケーションの開発(Outlook編)

何を作るか?

連絡先にWebページアドレスが指定されていた場合は、アイテム表示ウインドウ内でWebページを表示できるようにします。

プロジェクトの作成

[ファイル]⇒[新規作成]⇒[プロジェクト]から、[Outlook 2007 アドイン]を作成します。


[プロジェクト]⇒[新しい項目の追加]を開き、[Outlookフォーム領域]を選択します。


その後、[新しいフォーム領域のデザイン]、[分離]を選択し、WebSitePreviewという名前を付けておきます。


最後に、フォーム領域を表示するメッセージクラスを[連絡先]のみに変更し、完了を押します。


フォーム領域のデザイン画面が開くので、フォームの大きさを適当に広げてから、WebBrowserコントロールを配置します。

連絡先の取得

フォーム領域のコードを表示し、連絡先からURLを取得してブラウザコントロールに渡す部分を実装します。


追加したコードは以下の通りです。

Outlook.ContactItem myItem = (Outlook.ContactItem)this.OutlookItem;
if ((myItem != null) && (myItem.BusinessHomePage != null))
{
    webBrowser1.Navigate(myItem.BusinessHomePage);
}

[デバッグ]⇒[デバッグの開始]を行うと、Outlookが起動するので、左下のボタンを使って、連絡先表示に切り替えます。


ダブルクリックして連絡先アイテムを表示させます。リボンの[表示]の部分に、[WebSitePreview]というボタンが追加されています。


[WebSitePreview]ボタンをクリックすると、ウインドウ内にWebサイトが開きます。


この例においても、使用しない場合は、アドインを削除しておく必要があります。

実用的アプリケーションの開発(Excel編)

何を作るか?

カスタム作業ウインドウでボタンを押すと、Excelブック内のシートに帳票フォーマットを生成する、ドキュメントレベルのVSTOアプリケーションを作成します。

プロジェクトの作成

[ファイル]⇒[新規作成]⇒[プロジェクト]から、[Excel 2007 ブック]を作成します。


ドキュメントは新規に作成します。


[プロジェクト]⇒[ユーザーコントロールの追加]⇒[新しい項目の追加]⇒[ユーザーコントロール]を選択し、追加を押します。


ユーザーコントロールのデザイナが開きますが、ここでは何もしません。
ThisWorkBook.csのコードを表示し、Startupイベントハンドラ(ThisWorkbook_Startup)内に、カスタム作業ウインドウを表示するためのコードを記述します。
実際のコードは以下となります。

UserControl1 uc = new UserControl1();
this.ActionsPane.Controls.Add(uc);
this.Application.CommandBars["Task Pane"].Position
    = Microsoft.Office.Core.MsoBarPosition.msoBarRight;

カスタム作業ウインドウの編集

ユーザーコントロールのデザイナを開き、「帳票作成」、「クリア」、「全てクリア」のボタンを作成します。


「全てクリア」ボタンをダブルクリックし、イベントハンドラを生成させます。
ここでは、全てのシートをクリアする必要があります。追加するコードは以下となります。

private void button3_Click(object sender, EventArgs e)
{
    Globals.ThisWorkbook.ClearAllSeets(sender, e);
}

実際にクリア作業を行うのは、ThisWorkbookのClearAllSeetsです。以下のように実装します。

public void ClearAllSeets(object sender, System.EventArgs e)
{
    foreach (Excel.Worksheet workSheet in this.Worksheets)
    {
        workSheet.Cells.Clear();
    }
}

ユーザーコントロールのデザイナを開き、「クリア」ボタンをダブルクリックして、イベントハンドラを生成させます。
ここでは、アクティブなシートのみクリアする必要があります。追加するコードは以下となります。

private void button2_Click(object sender, EventArgs e)
{
    Globals.ThisWorkbook.ClearActiveSeets(sender, e);
}

実際にクリア作業を行うのは、ThisWorkbookのClearActiveSeetsです。以下のように実装します。

public void ClearActiveSeets(object sender, System.EventArgs e)
{
    ((Excel.Worksheet)this.ActiveSheet).Cells.Clear();
}

帳票の作成

ユーザーコントロールのデザイナを開き、「帳票作成」ボタンをダブルクリックして、イベントハンドラを生成させます。追加するコードは以下となります。

private void button1_Click(object sender, EventArgs e)
{
    Globals.ThisWorkbook.MakeFormatedSeet(sender, e);
}

帳票の作成そのものは、ThisWorkbookのMakeFormatedSeetが行います。
まずはひな形を示します。

public void MakeFormatedSheet(object sender, EventArgs e)
{
}

作成する帳票は、以下のようなものにします。


まずは、外枠部分を描画します。
記述したコードは以下となります。

#region 外枠の描画

// 1ページ目外枠
Excel.Worksheet activeSheet = (Excel.Worksheet)this.ActiveSheet;
Excel.Range temp = ((Excel.Range)activeSheet.get_Range(
    activeSheet.Cells[1, 1], activeSheet.Cells[59, 9]));
temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
    , Excel.XlColorIndex.xlColorIndexAutomatic, missing);

// 2ページ目外枠
temp = ((Excel.Range)activeSheet.get_Range(
    activeSheet.Cells[60, 1], activeSheet.Cells[118, 9]));
temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
    , Excel.XlColorIndex.xlColorIndexAutomatic, missing);

#endregion

次に、タイトル周りの表部分を描画します。
記述したコードは以下となります。

#region タイトル周りの描画

// 配布先
temp = ((Excel.Range)activeSheet.get_Range(
    activeSheet.Cells[1, 1], activeSheet.Cells[4, 2]));
temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
    , Excel.XlColorIndex.xlColorIndexAutomatic, missing);
temp.Merge(true);
temp.Merge(false);
temp.VerticalAlignment = Excel.Constants.xlTop;

// タイトル
temp = ((Excel.Range)activeSheet.get_Range(
    activeSheet.Cells[1, 3], activeSheet.Cells[4, 7]));
temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
    , Excel.XlColorIndex.xlColorIndexAutomatic, missing);
temp.Merge(true);
temp.Merge(false);
temp.HorizontalAlignment = Excel.Constants.xlCenter;
temp.VerticalAlignment = Excel.Constants.xlCenter;

// 文書番号、日付、所属、氏名
for (int i = 0; i < 4; ++i)
{
    temp = ((Excel.Range)activeSheet.get_Range(
        activeSheet.Cells[1 + i, 8], activeSheet.Cells[1 + i, 9]));
    temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
        , Excel.XlColorIndex.xlColorIndexAutomatic, missing);
    temp.Merge(true);
    temp.HorizontalAlignment = Excel.Constants.xlCenter;
}

#endregion

更新履歴表を描画します。
記述したコードは以下となります。

#region 更新履歴表の描画

for (int i = 0; i < 3; ++i)
{
    for (int j = 0; j < 3; ++j)
    {
        temp = (Excel.Range)activeSheet.Cells[7 + i, 2 + j];
        temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
            , Excel.XlColorIndex.xlColorIndexAutomatic, missing);
        temp.HorizontalAlignment = Excel.Constants.xlCenter;
    }
    temp = ((Excel.Range)activeSheet.get_Range(
        activeSheet.Cells[7 + i, 5], activeSheet.Cells[7 + i, 8]));
    temp.BorderAround(missing, Excel.XlBorderWeight.xlThin
        , Excel.XlColorIndex.xlColorIndexAutomatic, missing);
    temp.Merge(true);
}

#endregion

各部分の文字列を描画します。
記述したコードは以下となります。

#region 文字列を書く

// Excel.Range.Valueは未サポート、Excel.Range.set_Valueは引数が不明で使えず。

((Excel.Range)activeSheet.Cells[1, 1]).Value2 = "配布先";
((Excel.Range)activeSheet.Cells[1, 3]).Value2 = "Excel仕様書テンプレート";
((Excel.Range)activeSheet.Cells[1, 8]).Value2 = "IJ-XXXXXX";
((Excel.Range)activeSheet.Cells[2, 8]).Value2 = "2008/5/21";
((Excel.Range)activeSheet.Cells[3, 8]).Value2 = "情報技術開発部";

((Excel.Range)activeSheet.Cells[6, 2]).Value2 = "改版履歴";
((Excel.Range)activeSheet.Cells[7, 2]).Value2 = "バージョン";
((Excel.Range)activeSheet.Cells[7, 3]).Value2 = "日付";
((Excel.Range)activeSheet.Cells[7, 4]).Value2 = "作成者";
((Excel.Range)activeSheet.Cells[7, 5]).Value2 = "内容";

#endregion

[デバッグ]⇒[デバッグの開始]を行うと、カスタム作業ウインドウが表示された状態で起動します。カスタム作業ウインドウにある[帳票作成]ボタンを押すと、以下のように枠線などが描画されます。


Sheet2、Sheet3に切り替えても、同様のことが行えます。
最後に、[全てクリア]ボタンを押すと、全てのシートが白紙に戻ります。

実用的アプリケーションの開発(Word編)

何を作るか?

ニュース記事のRSSフィードを読み込んで、Wordドキュメントを生成する、ドキュメントレベルのVSTOアプリケーションを作成します。

プロジェクトの作成

[ファイル]⇒[新規作成]⇒[プロジェクト]から、[Word 2007 ドキュメント]を作成します。


ドキュメントは新規に作成します。


[プロジェクト]⇒[ユーザーコントロールの追加]⇒[新しい項目の追加]⇒[ユーザーコントロール]を選択し、追加を押します。


デザイン画面が開きますが、ここでは何もしません。
次に、ソリューションエクスプローラでWebNewsReaderプロジェクトを右クリックし、[追加]⇒[新しい項目]を開きます。[リボン(ビジュアルデザイナ)]を選択し、追加を押します。
リボンのデザイン画面が開くので、タブとグループのLabelプロパティを設定します。


フィードの選択、登録を行うためのインタフェースを追加します。


フィードの取得を行うインタフェースを追加します。


フィードの初期値を設定しておくために、コードを編集します。


現在選択しているURLを保持するためのプロパティを追加します。


イベントハンドラを実装します。


フィードの取得を行うイベントハンドラ(button2_Click)については、後で実装します。

XMLの取得

XMLを取得して、Wordドキュメントに書き出す部分を実装します。


XMLファイルを読む部分が出来たので、フィードの取得を行うイベントハンドラ(button2_Click)から呼び出せるようにします。

XMLの解析

現時点で、以下のようなドキュメントが生成されます。


しかし、これは生のXMLを直接表示しているだけなので、非常に読みづらいです。
そこで、XMLのタグを元に、必要な情報のみを抽出します。
プロジェクト名で右クリックして、[追加]⇒[新しい項目]⇒[クラス]を選択します。


この新しいクラスの実装イメージは次ページの図のようになります。これから順に説明していきます。


まず、XMLファイルの構造を見てみましょう。


サイトの名前が、<rss><channel><title>の位置に記述されています。サイトのURLは、<rss><channel><link>の位置にあります。以下、同様にして、必要な情報をXPathで記述してみると、以下のようになります。

情報の種別 XPath
サイトの名前 /rss/channel/title
サイトのURL /rss/channel/link
エントリのタイトル /rss/channel/item/title
エントリのURL /rss/channel/item/link
エントリの更新時刻 /rss/channel/item/pubDate
エントリの内容 /rss/channel/item/description

まず、サイトの名前とURLを抽出します。ここでは、サイトの名前とURLは1つしか存在しないことを前提としています。


次に、各エントリの情報を抽出します。エントリは複数ある可能性が高いので、List<>を使って受けています。


ここまでで、XMLから必要な情報が取り出せました。

ドキュメントの生成

前節で抽出した文字列情報を、Wordの書式付きでドキュメントに書き込みます。書き出す情報と書式は以下の通りです。

情報の種別 書式
サイトの名前 見出し1
サイトのURL ハイパーリンク(書式のみ)
エントリのタイトル 見出し2
エントリの更新時刻 強調斜体
エントリの内容 標準

ソースコードは以下となります。実際の処理はWriteWordHelperというヘルパー関数が行っています。


以下はヘルパー関数のソースです。


引数に渡された文字列と書式情報を元に、Wordの段落を作成します。リンク要素にハイパーリンクを設定したかったのですが、うまくいかずに断念しました。

ドキュメントの消去

リボンインターフェースにボタンを追加し、生成したドキュメントを削除できるようにします。


作成したボタンをダブルクリックして、イベントハンドラを生成します。
クラスライブラリClass1に、Wordドキュメントをクリアして、初期状態に戻すコードを追加します。


このコードをイベントハンドラから呼び出します。

ドキュメントを単独で開けないようにする

VSTOを使用したOfficeアプリケーションを、より大きなアプリケーションの一部で使うようになった場合、生成されたOfficeドキュメントに直接触られると困ることがあります。
ここでは、Excelにアドインでパスワード認証をつけておき、上位アプリケーションからパスワードを指定して開く、ということを行います*1

名前を付けて保存と印刷を無効化する

Excelアドインプロジェクトを新規作成し、ThisAddIn.csのコードを開きます。
名前を付けて保存、及び、印刷機能を無効化するため、WorkbookのBeforeSaveとBeforePrintのイベントハンドラを実装します。

private void ThisWorkBook_BeforeSave(bool b, ref bool Cancel)
{
    if (b == true)
    {
        Cancel = true;
        MessageBox.Show("保存はキャンセルされました。");
    }
}
private void ThisWorkBook_BeforePrint(ref bool Cancel)
{
    Cancel = true;
    MessageBox.Show("印刷はキャンセルされました。");
}

BeforeSaveは、ファイル選択ダイアログが出るケース(引数bがtrueの場合)のみキャンセルし、上書き保存は通るようになっています。
これらのイベントハンドラは、ファイルオープン時に設定出来るように、Openイベントハンドラ内に実装します*2

private void ThisAddIn_Open(Excel.Workbook wb)
{
    this.Application.ActiveWorkbook.BeforePrint +=
        new Excel.WorkbookEvents_BeforePrintEventHandler(
            ThisWorkBook_BeforePrint);
    this.Application.ActiveWorkbook.BeforeSave +=
        new Excel.WorkbookEvents_BeforeSaveEventHandler(
            ThisWorkBook_BeforeSave);
    MessageBox.Show("名前を付けて保存、印刷、の機能を無効化しました。");
}

最後に、Openイベントハンドラを登録します。

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    this.Application.WorkbookOpen
        += new Excel.AppEvents_WorkbookOpenEventHandler(ThisAddIn_Open);
}

これは、Startupイベントハンドラ中で行います。

パスワードを設定する

Excelブックにパスワードを設定するため、上書き保存時に処理を追加します。上書き保存は、BeforeSaveイベントハンドラの第一引数がfalseの場合に相当するので、以下のように実装します。

private void ThisWorkBook_BeforeSave(bool b, ref bool Cancel)
{
    if (b == true)
    {
        Cancel = true;
        MessageBox.Show("保存はキャンセルされました。");
    }
    else
    {
        this.Application.ActiveWorkbook.Password = "hogehoge";
        MessageBox.Show("ドキュメントにパスワードを設定しました。");
    }
}

これで、既存のExcelファイルを開いて上書き保存すると、常にパスワードが付加されるようになります。
パスワード付きドキュメントを開くと、次のようなダイアログが表示され、パスワードを入力する旨を知らせます。

アプリケーションからExcelファイルを開く

新規にWindowsフォームプロジェクトを作成し、フォームにボタンを配置します。
ソリューションエクスプローラの[参照設定]を右クリックし、[参照の追加]を選択します。

この画面から、以下を全て選択し、OKを押します。

さらに、Form1.csのusing宣言に以下を追加します。

using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;

フォームデザイナ上のボタンをダブルクリックして、イベントハンドラを生成します。イベントハンドラ内では、ファイル選択ダイアログを表示し、選択されたファイルをExcelで開く処理を実装します。この時、パスワード認証を回避するためにExcelワークブックのOpenメソッドでパスワードを指定します。
コードを以下に示します。

private void button1_Click(object sender, EventArgs e)
{
    OpenFileDialog dialog = new OpenFileDialog();
    if (dialog.ShowDialog() == DialogResult.OK)
    {
        Excel.Application app = new Excel.Application();
        try
        {
            this.Visible = false;
            app.Visible = true;
            Excel.Workbook wb = app.Workbooks.Open(
                dialog.FileName, Type.Missing, Type.Missing,
                Type.Missing, "hogehoge", Type.Missing,
                Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing);
            while (app.Visible) ;
            this.Visible = true;
        }
        catch
        {
            app.Workbooks.Close();
        }
        finally
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
            GC.Collect();
        }
    }
}

これにより、このアプリケーションを用いれば、同じパスワードのついた文書を開くことができるようになります。
なお、この例においても、登録されたCOMアドインを削除しておく必要があります。

*1:アプリケーションレベルで行った場合、全てのExcelブックに適用されてしまうため、本来なら、ドキュメントレベルで行うべき処理だと思います。

*2:Startupイベントハンドラ内ではうまく設定できなかったため、ファイルオープン時まで遅らせて対処しました。

文字列に対する文字単位での書式変更

本文の文字列(Word編)

Wordドキュメントプロジェクトを新規に作成し、ドキュメント上にボタンを1つ置きます。その後、ボタンをダブルクリックし、以下のようなコードを記述します。


起動直後は以下のようになっています。


その後、ボタンを押すたびに、文字が追加されていきます。

セル内の文字列(Excel編)

Excelブックプロジェクトを作成し、シート上にボタンを置きます。その後、ボタンをダブルクリックし、以下のようなコードを記述します。


起動直後は、以下のようになっています。


ボタンを押すと、セル上に文字が表示されます。


カスタム作業ウインドウのリサイズに応じてスクロールバーを出す

カスタム作業ウインドウはリサイズ可能ですが、内部のコントロールを考慮してはくれないので、そのままでは問題が出る場合があります。これを回避するための方法を示します。
Excelアドインプロジェクトを新規に作成し、カスタム作業ウインドウを実装します。
カスタム作業ウインドウには、Panelコントロールとボタンを配置します。


ユーザーコントロールを選択し、プロパティから、Resizeイベントのイベントハンドラ名を入力し、イベントハンドラのひな型を生成します。
生成されたイベントハンドラは、以下のように実装します。

private void uc1_Resize(object sender, EventArgs e)
{
    panel1.Width = Globals.ThisAddIn.CustomTaskPanes[0].Width;
    panel1.Height = Globals.ThisAddIn.CustomTaskPanes[0].Height
        - 20; // これを忘れるとスクロールバーが不可視になるので注意!
}

これにより、UserControl1のResizeイベントが生じた際には、カスタム作業ウインドウの全体をPanelが占有している状態になります。また、初期値については、
ThisAddIn_Startup内で、以下のように指定します。

customTaskPane.Width = 210;

ユーザーコントロールのデザイナで、Panelコントロールを選択し、以下を指定します。

プロパティ
AutoScroll true
AutoScrollMinSize 200, 500

この状態で、デバッグして実行すると、カスタム作業ウインドウが表示された状態で起動します。カスタム作業ウインドウの幅を狭めていくと、途中からスクロールバーが出ることが確認できます。また、ウインドウを縦方向に狭めていったときも、同様に、スクロールバーが出ることが確認できます。
なお、この例においても、登録されたCOMアドインを削除しておく必要があります。

印刷不可にする

印刷不可にする(Excel編)

Excelアドインプロジェクトを新規に作成します。
ThisAddIn.csのコードを開き、スタートアップイベントハンドラ(ThisAddIn_Startup)に、印刷をキャンセルするコードを追加します。


変更を加えたコードは以下となります。

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    this.Application.ActiveWorkbook.BeforePrint +=
        new Excel.WorkbookEvents_BeforePrintEventHandler(
            ThisWorkBook_BeforePrint);
}
  ...
private void ThisWorkBook_BeforePrint(ref bool Cancel)
{
    Cancel = true;
    MessageBox.Show("印刷はキャンセルされました。");
}

実行して、印刷を行うと、以下のダイアログが表示され、印刷がキャンセルされます。印刷プレビューについても、同様にキャンセルされます。


なお、この例においても、登録されたCOMアドインを削除しておく必要があります。

印刷不可にする(Word編)

Wordアドインプロジェクトを新規に作成します。
ThisAddIn.csのコードを開き、スタートアップイベントハンドラ(ThisAddIn_Startup)に、印刷をキャンセルするコードを追加します。


変更を加えたコードは以下となります。

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    this.Application.Application.DocumentBeforePrint +=
        new Word. ApplicationEvents4_DocumentBeforePrintEventHandler(
            ThisDocument_BeforePrint);
}
  ...
private void ThisDocument_BeforePrint(Word.Document document, ref bool Cancel)
{
    Cancel = true;
    MessageBox.Show("印刷はキャンセルされました。");
}

実行して、印刷を行うと、以下のダイアログが表示され、印刷がキャンセルされます。


なお、この例においても、登録されたCOMアドインを削除しておく必要があります。