【Office Add-ins】Excelアドインの各イベントハンドラー設定

# 概要

Office Add-insのExcelアドイン作成時、いつも忘れてしまうイベントの設定方法を書き留める。

サイドロード形式のOffice Add-insで、
例えばUIにシート名一覧が表示され、ドラッグでシートの並び替えができるとする。
この時、UI上での操作をExcelに反映させるのは当然ながら、
逆にExcel側でのシート並び替えや挿入、削除などを検出し、UI側のシート名一覧も連動させる必要が出てくる。

こういった理由から、イベントで発火するタイプの処理は実装頻度が高いものになる。
Office Add-insではVBAと異なり、イベントそのものの定義が必要になっている。

# イベントハンドラーの登録

アドイン起動時に必要なイベントハンドラーを一括で登録してしまうのが良い。

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {

    // Sheet > 選択チェンジイベント
    let sheet = context.workbook.worksheets.getItem('Sheet1');
    sheet.onSelectionChanged.add(onWorksheetSelectionChange);

    // SheetCollection > シート追加イベント
    context.workbook.worksheets.onAdded.add(onWorksheetCollectionAdded);

    // SheetCollection > シート削除イベント
    context.workbook.worksheets.onDeleted.add(onWorksheetCollectionDeleted);

    /*
    *
    * 他、必要なイベント定義を追加
    *
    */

    await context.sync();
  });
}
```

`yo office` コマンドで作成した新規プロジェクトの **taskpane.js** を参考にし、
起動処理部分で上記関数を実行する。

``` Javascript
Office.onReady((info) => {
  if (info.host === Office.HostType.Excel) {
    registerEventHandlers();
  }
});
```

# イベント一覧

## Workbook

ブックに対するイベント。
VBAで言うところの **Open** や **Close** などが無いのは、
アドインは性質上ブックの内側に存在するものだからだと思われる。
**SelectionChange**はセルの選択範囲が変わった時に反応し、
図形を選択した時は反応しなかった。
Worksheet、WorksheetCollectionにもSelectionChangeはあるが、
もしかするとWorkbookのみで賄えるかもしれない。
また、**AutoSaveSetting** はその名の通り、オートセーブの設定変更時のイベントであり、
オートセーブされた時のイベントではない点に注意。

* **onActivated** *[ExcelApi 1.13]*
* **onSelectionChanged** *[ExcelApi 1.2]*
* **onAutoSaveSettingChanged** *[ExcelApi 1.9]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {

    const workbook = context.workbook;

    // Workbook > アクティベートイベントを定義
    workbook.onActivated.add(onWorkbookActivate);
    // Workbook > 選択変更イベントを定義
    workbook.onSelectionChanged.add(onWorkbookSelectionChange);
    // Workbook > オートセーブ設定変更イベントを定義
    workbook.onAutoSaveSettingChanged.add(onWorkbookAutoSaveChange);

    await context.sync();
  });
}

// ブックを選択
const onWorkbookActivate = async (args) => {
  await Excel.run(async (context) => {
    console.log(args.type);
    // >> WorkbookActivated
  });
}

// セルを移動 
const onWorkbookSelectionChange = async (args) => {
  await Excel.run(async (context) => {
    const range = context.workbook.getSelectedRange();
    range.load(['address']);
    await context.sync();

    console.log(`WorkbookSelectionChange : ${range.address}`);
    // >> WorkbookSelectionChange : [選択したアドレス]
  });
}

// オートセーブ設定を変更 (対象ブックをOneDrive上に保存時のみ可能)
const onWorkbookAutoSaveChange = async (args) => {
  await Excel.run(async (context) => {

    const book = context.workbook;
    book.load(['autoSave']);
    await context.sync();

    console.log(`WorkbookAutoSaveChange : ${book.autoSave}`);
    // >> WorkbookAutoSaveChange : [true or false]
  });
}
```

## Worksheet

ワークシートに対するイベント。
戻り値に **worksheetId** を含むため、シートの特定は容易。
また、セル範囲に関わるものは **address** も返すので、そのまま後処理に繋げられる。

* **onActivated** *[ExcelApi 1.7]*
* **onDeactivated** *[ExcelApi 1.7]*
* **onSingleClicked** *[ExcelApi 1.10]*
* **onSelectionChanged** *[ExcelApi 1.7]*
* **onChanged** *[ExcelApi 1.7]*
* **onFormatChanged** *[ExcelApi 1.9]*
* **onFormulaChanged** *[ExcelApi 1.13]*
* **onProtectionChanged** *[BETA]*
* **onRowHiddenChanged** *[ExcelApi 1.11]*
* **onRowSorted** *[ExcelApi 1.10]*
* **onColumnSorted** *[ExcelApi 1.10]*
* **onCalculated** *[ExcelApi 1.8]*
* **onFiltered** *[BETA]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {

    const sheet = context.workbook.worksheets.getItem('Sheet1');

    // Worksheet > アクティベートイベントを定義
    sheet.onActivated.add(onWorkSheetActivate);

    // Worksheet > ディアクティベートイベントを定義
    sheet.onDeactivated.add(onWorkSheetDeactivate);

    // Worksheet > シングルクリックイベントを定義
    sheet.onSingleClicked.add(onWorkSheetSingleClick);

    // Worksheet > 選択変更イベントを定義
    sheet.onSelectionChanged.add(onWorkSheetSelectionChange);

    // Worksheet > 値変更イベントを定義
    sheet.onChanged.add(onWorkSheetChange);

    // Worksheet > 書式変更イベントを定義
    sheet.onFormatChanged.add(onWorkSheetFormatChange);

    // Worksheet > 数式変更イベントを定義
    sheet.onFormulaChanged.add(onWorkSheetFormulaChange);

    // Worksheet > 保護状態変更イベントを定義
    sheet.onProtectionChanged.add(onWorkSheetProtectionChange);

    // Worksheet > 行非表示状態変更イベントを定義
    sheet.onRowHiddenChanged.add(onWorkSheetRowHiddenChanged);

    // Worksheet > 行並び替えイベントを定義
    sheet.onRowSorted.add(onWorkSheetRowSort);

    // Worksheet > 列並び替えイベントを定義
    sheet.onColumnSorted.add(onWorkSheetColumnSort);

    // Worksheet > 再計算イベントを定義
    sheet.onCalculated.add(onWorkSheetCalculate);

    // Worksheet > フィルターイベントを定義
    sheet.onFiltered.add(onWorkSheetFilter);

    await context.sync();
  });
}

// シート1を選択
const onWorkSheetActivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorksheetActivated : ${sheet.name}`);
    // >> WorksheetActivated : Sheet1
  });
} 

// シート1以外を選択
const onWorkSheetDeactivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorksheetDeactivated : ${sheet.name}`);
    // >> WorksheetDeactivated : Sheet1
  });
} 

// シート1の任意のセルをシングルクリック、またはタップ
const onWorkSheetSingleClick = async (args) => {
  await Excel.run(async (context) => {

    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetSingleClicked : ${sheet.name} [${args.address}]`);
    // >> WorkSheetSingleClicked : Sheet1 [アドレス]
  });
} 

// シート1の任意のセルを選択
const onWorkSheetSelectionChange = async (args) => {
  await Excel.run(async (context) => {

    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetSelectionChanged : ${sheet.name} [${args.address}]`);
    // >> WorkSheetSelectionChanged : Sheet1 [アドレス]
  });
} 

// シート1の任意のセルの値を変更
const onWorkSheetChange = async (args) => {
  await Excel.run(async (context) => {

    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetChanged : ${sheet.name} [${args.address}]`);
    // >> WorkSheetChanged : Sheet1 [アドレス]
  });
} 

// シート1の任意のセルの書式を変更
const onWorkSheetFormatChange = async (args) => {
  await Excel.run(async (context) => {

    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetFormatChanged : ${sheet.name} [${args.address}]`);
    // >> WorkSheetFormatChanged : Sheet1 [アドレス]
  });
} 

// シート1の任意のセルの数式を変更
const onWorkSheetFormulaChange = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetFormulaChanged : ${sheet.name} [${args.address}]`);
    // >> WorkSheetFormulaChanged : Sheet1 [アドレス]
  });
} 

// シート1の保護状態を変更
const onWorkSheetProtectionChange = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetProtectionChanged : ${sheet.name} [${args.isProtected}]`);
    // >> WorkSheetProtectionChanged : Sheet1 [true or false]
  });
} 

// シート1の任意の行を非表示化
const onWorkSheetRowHiddenChanged = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetRowHiddenChanged : ${sheet.name} [${args.address}] [${args.changeType}]`);
    // >> WorkSheetRowHiddenChanged : Sheet1 [アドレス] [Unhidden or Hidden]
  });
} 

// シート1の任意の範囲で並び替えを実行
const onWorkSheetRowSort = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetRowSorted : ${sheet.name} [${args.address}]`);
    // >> WorkSheetRowSorted : Sheet1 [アドレス]
  });
} 

// シート1の任意の範囲で並び替えを実行 (オプション > 列単位)
const onWorkSheetColumnSort = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetColumnSorted : ${sheet.name} [${args.address}]`);
    // >> WorkSheetColumnSorted : Sheet1 [アドレス]
  });
} 

// シート1に対して再計算を適用
const onWorkSheetCalculate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetCalculated : ${sheet.name}`);
    // >> WorkSheetCalculated : Sheet1
  });
}

// シート1の任意の範囲にフィルタを適用
const onWorkSheetFilter = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetFiltered : ${sheet.name}`);
    // >> WorkSheetFiltered : Sheet1
  });
}
```

## WorksheetCollection

全シートに対するイベント。
**onDeleted** の場合、シート自体がいなくなってしまうため
**worksheetId** から辿ってもシート名を取ることができない…？

* **onAdded** *[ExcelApi 1.7]*
* **onDeleted** *[ExcelApi 1.7]*
* 他、Worksheetと同一のため割愛

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {

    const sheets = context.workbook.worksheets;

    // WorksheetCollection > シート追加イベントを定義
    sheets.onAdded.add(onWorkSheetCollectionAdd);

    // WorksheetCollection > シート削除イベントを定義
    sheets.onDeleted.add(onWorkSheetCollectionDelete);

    await context.sync();
  });
}

// シートを追加
const onWorkSheetCollectionAdd = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`WorkSheetCollectionAdded : ${sheet.name}`);
    // >> WorkSheetCollectionAdded : SheetXX
  });
} 

// シートを削除
const onWorkSheetCollectionDelete = async (args) => {
  await Excel.run(async (context) => {
    console.log(`WorkSheetCollectionDeleted : ${args.worksheetId}`);
    // >> WorkSheetCollectionDeleted : SheetID
  });
} 
```

## Table

テーブルに対するイベント。

* **onSelectionChanged** *[ExcelApi 1.7]*
* **onChanged** *[Beta]*
* **onFiltered** *[ExcelApi 1.7]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();

    // サンプルテーブルを作成
    const tb = sheet.tables.add("A1:D1", true /*hasHeaders*/);
    tb.name = "SampleTable";
    tb.getHeaderRowRange().values = [["A", "B", "C", "D"]];
    tb.rows.add(null /*add at the end*/, [
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"]
    ]);

    // Table > 選択変更イベントを定義
    tb.onSelectionChanged.add(onTableSelectionChange);

    // Table > 値変更イベントを定義
    tb.onChanged.add(onTableChange);

    // Table > フィルターイベントを定義
    tb.onFiltered.add(onTableFilter);

    await context.sync();
  });
}

// テーブルの任意のセルを選択
const onTableSelectionChange = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const tb = sheet.tables.getItem(args.tableId);
    tb.load(['name']);
    await context.sync();

    console.log(`TableSelectionChanged : ${tb.name} [${args.address}]`);
    // >> TableSelectionChanged : SampleTable [アドレス]
  });
} 

// テーブルの任意の値を変更
const onTableChange = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const tb = sheet.tables.getItem(args.tableId);
    tb.load(['name']);
    await context.sync();

    console.log(`TableChanged : ${tb.name} [${args.address}]`);
    // >> TableChanged : テーブル名 [アドレス]
  });
} 

// テーブルにフィルタを適用
const onTableFilter = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const tb = sheet.tables.getItem(args.tableId);
    tb.load(['name']);
    await context.sync();

    console.log(`TableFiltered : ${tb.name}`);
    // >> TableFiltered : テーブル名
  });
} 
```

## TableCollection

全テーブルに対するイベント。

* **onAdded** *[ExcelApi 1.9]*
* **onDeleted** *[ExcelApi 1.9]*
* 他、Tableと同一のため割愛 (ただし、**onSelectionChanged** は存在しない)

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    const tables = context.workbook.tables;

    // TableCollection > テーブル追加イベントを定義
    tables.onAdded.add(onTableCollectionAdd);

    // TableCollection > テーブル削除イベントを定義
    tables.onDeleted.add(onTableCollectionDelete);

    await context.sync();
  });
}

// テーブルを追加
const onTableCollectionAdd = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const tb = sheet.tables.getItem(args.tableId);
    sheet.load(['name']);
    tb.load(['name']);
    await context.sync();

    console.log(`TableCollectionAdded : ${sheet.name} [${tb.name}]`);
    // >> TableCollectionAdded : シート名 [テーブル名]
  });
} 

// テーブルを削除 (範囲に変換など)
const onTableCollectionDelete = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`TableCollectionDeleted : ${sheet.name} [${args.tableId}]`);
    // >> TableCollectionDeleted : シート名 [テーブルID]
  });
} 
```

## Chart

グラフのイベント。

* **onActivated** *[ExcelApi 1.8]*
* **onDeactivated** *[ExcelApi 1.8]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    
    // サンプルグラフを作成
    const sheet = context.workbook.worksheets.getActiveWorksheet();

    const tb = sheet.tables.add("A1:D1", true /*hasHeaders*/);
    tb.name = "SampleTable";
    tb.getHeaderRowRange().values = [["A", "B", "C", "D"]];
    tb.rows.add(null /*add at the end*/, [
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"],
        ["1", "2", "3", "4"]
    ]);
    const dataRange = tb.getDataBodyRange();
    const chart = sheet.charts.add('ColumnClustered', dataRange, 'Auto');
    chart.name = 'SampleChart';
    chart.setPosition("A15", "F30");
    chart.title.text = "SampleChart";
    chart.legend.position = "Right";
    chart.legend.format.fill.setSolidColor("white");
    chart.dataLabels.format.font.size = 15;
    chart.dataLabels.format.font.color = "black";
    chart.series.getItemAt(0).name = 'Value in \u20AC';

    // Chart > アクティベートイベントを定義
    chart.onActivated.add(onChartActivate);

    // Chart > ディアクティベートイベントを定義
    chart.onDeactivated.add(onChartDeactivate);

    await context.sync();
  });
}

// SampleChartを選択
const onChartActivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const chart = sheet.charts.getItem(args.chartId);
    sheet.load(['name']);
    chart.load(['name']);
    await context.sync();

    console.log(`ChartActivated : ${sheet.name} [${chart.name}]`);
    // >> ChartActivated : シート名 [SampleChart]
  });
} 

// SampleChartを選択解除
const onChartDeactivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const chart = sheet.charts.getItem(args.chartId);
    sheet.load(['name']);
    chart.load(['name']);
    await context.sync();

    console.log(`ChartDeactivated : ${sheet.name} [${chart.name}]`);
    // >> ChartDeactivated : シート名 [SampleChart]
  });
} 
```

## ChartCollection

全グラフに対するイベント。

* **onAdded** *[ExcelApi 1.8]*
* **onDeleted** *[ExcelApi 1.8]*
* 他、Chartと同一のため割愛

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();
    const charts = sheet.charts;

    // ChartCollection > グラフ追加イベントを定義
    charts.onAdded.add(onChartCollectionAdd);

    // ChartCollection > グラフ削除イベントを定義
    charts.onDeleted.add(onChartCollectionDelete);

    await context.sync();
  });
}

// グラフを追加
const onChartCollectionAdd = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const chart = sheet.charts.getItem(args.chartId);
    sheet.load(['name']);
    chart.load(['name']);
    await context.sync();

    console.log(`ChartCollectionAdded : ${sheet.name} [${chart.name}]`);
    // >> ChartCollectionAdded : シート名 [SampleChart]
  });
} 

// グラフを削除
const onChartCollectionDelete = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`ChartCollectionDeleted : ${sheet.name} [${args.chartId}]`);
    // >> ChartCollectionDeleted : シート名 [グラフID]
  });
} 
```

## Shape

図形に対するイベント。
API 1.12時点では **ShapeCollection** に一切のイベントが定義されていないため
現状、図形の追加削除イベントの取得は不可。

* **onActivated** *[ExcelApi 1.9]*
* **onDeactivated** *[ExcelApi 1.9]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();
    const shape = sheet.shapes.addGeometricShape(Excel.GeometricShapeType.hexagon);

    // Shape > アクティベートイベントを定義
    shape.onActivated.add(onShapeActivate);

    // Shape > ディアクティベートイベントを定義
    shape.onDeactivated.add(onShapeDeactivate);

    await context.sync();
  });
}

// 図形を選択
const onShapeActivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const shape = sheet.shapes.getItem(args.shapeId);
    sheet.load(['name']);
    shape.load(['name']);
    await context.sync();

    console.log(`ShapeActivated : ${sheet.name} [${shape.name}]`);
    // >> ShapeActivated : シート名 [シェイプ名]
  });
} 

// 図形を選択解除
const onShapeDeactivate = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const shape = sheet.shapes.getItem(args.shapeId);
    sheet.load(['name']);
    shape.load(['name']);
    await context.sync();

    console.log(`ShapeDeactivated : ${sheet.name} [${shape.name}]`);
    // >> ShapeDeactivated : シート名 [シェイプ名]
  });
} 
```

## CommentCollection

全コメントに対するイベント。
API 1.12時点では単体の **Comment** にはイベントが定義されていない
**onDelete** はセルの右クリックメニューから **コメントの削除** ではなく、
コメント内操作の **スレッドを削除** だけが対象となっている。


* **onAdded** *[ExcelApi 1.12]*
* **onDeleted** *[ExcelApi 1.12]*
* **onChanged** *[ExcelApi 1.12]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    const comments = context.workbook.worksheets.getActiveWorksheet().comments;

    // CommentCollection > コメント追加イベントを定義
    comments.onAdded.add(onCommentCollectionAdd);

    // CommentCollection > コメント削除イベントを定義
    comments.onDeleted.add(onCommentCollectionDelete);

    // CommentCollection > コメント変更イベントを定義
    comments.onChanged.add(onCommentCollectionChange);

    await context.sync();
  });
}

// コメントを追加
const onCommentCollectionAdd = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const comment = context.workbook.comments.getItem(args.commentDetails[0].commentId);
    sheet.load(['name']);
    comment.load(['creationDate']);
    await context.sync();

    console.log(`CommentCollectionAdded : ${sheet.name} [${args.commentDetails[0].commentId}] [${comment.creationDate}]`);
    // >> CommentCollectionAdded : シート名 [コメントID] [作成日時]
  });
} 

// コメントを削除
const onCommentCollectionDelete = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    sheet.load(['name']);
    await context.sync();

    console.log(`CommentCollectionDeleted : ${sheet.name} [${args.commentDetails[0].commentId}]`);
    // >> CommentCollectionDeleted : シート名 [コメントID]
  });
} 

// コメントを変更
const onCommentCollectionChange = async (args) => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(args.worksheetId);
    const comment = context.workbook.comments.getItem(args.commentDetails[0].commentId);
    sheet.load(['name']);
    comment.load(['content']);
    await context.sync();

    console.log(`CommentCollectionChanged : ${sheet.name} [${args.commentDetails[0].commentId}] [${comment.content}]`);
    // >> CommentCollectionChanged : シート名 [コメントID] [コメント内容]
  });
} 
```

## Binding

よくわからないBinding。
おそらく、Rangeやテーブルなどシート上の特定の範囲などを定数化するもの。

* onDataChanged *[ExcelApi 1.2]*
* onSelectionChanged *[ExcelApi 1.2]*

``` Javascript
// あらかじめ、Sheet1のA1:H30の範囲を BindArea として定義する
const setBind = async () => {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem('Sheet1');

    const bindingRange = sheet.getRange('A1:H30');
    bindingRange.format.fill.color = 'black';
    context.workbook.bindings.add(bindingRange, Excel.BindingType.range, 'BindArea');
    await context.sync();
  });
}
```
``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    
    const binding = context.workbook.bindings.getItemAt(0);

    // Binding > データ変更イベントを定義
    binding.onDataChanged.add(onBindingDataChange);
    // Binding > 選択変更イベントを定義
    binding.onSelectionChanged.add(onBindingSelectionChange);

    await context.sync();
  });
}

// BindArea内のセルを編集
const onBindingDataChange = async (args) => {
  await Excel.run(async (context) => {
    console.log(`BindingDataChange : ${args.binding.id}`);
    // >> BindingDataChange : BindArea
  });
} 

// BindArea内のセルを選択
const onBindingSelectionChange = async (args) => {
  await Excel.run(async (context) => {
    console.log(`BindingSelectionChange : ${args.binding.id}`);
    // >> BindingSelectionChange : BindArea
  });
} 
```

## SettingCollection

* **onSettingsChanged** *[ExcelApi 1.4]*

``` Javascript
const registerEventHandlers = async () => {
  await Excel.run(async (context) => {
    // sampleKey の設定を作成
    const settings = context.workbook.settings; 
    settings.add('sampleKey', 'sampleValue');

    // SettingCollection > 設定変更イベントを定義
    settings.onSettingsChanged.add(onSettingCollectionChange);

    await context.sync();
  });
}

// 設定を変更
const onSettingCollectionChange = async (args) => {
  await Excel.run(async (context) => {
    console.log(`SettingCollectionChanged`);
    // >> SettingCollectionChanged
  });
}