本文章是由機器翻譯。

Microsoft Office

探討 JavaScript API for Office:資料訪問和事件

Stephen Oliver
Eric Schmidt

本文是 JavaScript API for Office 的深入演練系列文章的第二篇。 第 1 部分(可在 msdn.microsoft.com/magazine/jj891051 處獲得)是物件模型的寬泛概述。 本文接著第 1 部分的內容開始介紹,詳細演練了如何訪問檔內容並考察了事件模型。

在整個系列中,我們經常引用 JavaScript API for Office 參考文檔。 您可以在 MSDN 上的「Office 和 SharePoint 應用程式開發人員中心」(dev.office.com) 找到官方文檔、示例代碼和社區資源。

從 Office 應用程式訪問 Office 檔內容

JavaScript API for Office 為訪問 Office 檔中的資料提供了幾個基本方式:可以獲取或設置當前所選資料,也可以獲取整個檔。 這種資料存取層級可能看上去比較簡單,而且這兩種方法的確十分容易使用。 但是,這兩種方法的靈活性和自訂程度非常高,從而為您的應用程式提供了諸多可能。

除了訪問所選資料或整個檔,JavaScript API for Office 還允許綁定到資料或在文檔中操作自訂 XML 部件。 我們會更深入地瞭解這些使用 Office 內容的技術。

獲取和設置所選資料

如前所述,通過 Document 物件,應用程式可以對檔中的資料進行訪問。 對於工作窗格和內容應用程式,可以使用 Document.getSelectedDataAsync 和 Document.setSelectedDataAsync 方法獲取或設置 Office 檔中的所選內容。

通過調用這兩種方法,可對多種類型的資料格式進行操作。 getSelectedDataAsync 和 setSelectedDataAsync 方法都具有一個參數 coercionType,該參數的值是 Office.CoercionType 枚舉常量。 coercionType 參數指定所要獲取或設置的內容的資料格式。 根據 coercionType 參數的值,可以按純文字、表、矩陣、HTML、甚至是「原始」Office Open XML (OOXML) 的形式選擇資料。 (請注意,截止發佈時,僅 Word 2013 中支援以 HTML 或 OOXML 的形式獲取和設置文本。)

使用 getSelectedDataAsync 和 setSelectedDataAsync 時,可以不指定 coercionType。 會盡可能從上下文推斷 coercionType。 For example, if you pass a string literal into a call to setSelectedDataAsync, then the default coercionType is “text.” If you passed the same data in as an array of arrays, then the default coercionType would be “matrix.”

我們會提供一些示例來說明這些簡單方法有多麼強大(主要以 setSelectedDataAsync 方法為例)。 首先演示一段將某些簡單文本插入 Word 文檔的代碼:

// Define some data to set in the document.
var booksToRead = "Anabasis by Xenophon; \n" +
  "Socrates' Apology by Plato; \n" +
  "The Illiad by Homer.";
// Set some data to the document as simple text.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Text },
  function (result) {
    // Access the results, if necessary.
});

结果如图 1 所示。

Results of Inserting Data as Simple Text
圖 1 以簡單文本形式插入資料的結果

現在,我們對該示例做些更改,以「矩陣」強制類型 (coercionType) 的形式插入文本。 矩陣是陣列的陣列,在 Excel 中以一系列儲存格(簡單範圍)的形式插入,在 Word 中以簡單表的形式插入。

插入到 Word 中時,此代碼會插入包含兩個列的表,該表不含標題且未設置格式。 第一級陣列中的每個項分別對應于所生成的表的一行;子陣列中的每個項則分別對應于該行中的一個資料儲存格:

// Define a matrix of data to set in the document.
var booksToRead = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as an unformatted table.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Matrix },
  function (result) {
    // Access the results, if necessary.
});

结果如图 2 所示。

Results of Inserting Data as a Matrix
圖 2 以矩陣形式插入資料的結果

除了矩陣強制類型之外,還可以使用 TableData 物件以表的形式獲取或設置資料。 這使我們可以為結果增加一些格式設置 — 在此特定示例中,我們來增加一個標題列。 我們將分別使用標題和行屬性訪問 TableData 物件的標題列和內容。

對於 TableData 物件,還可以使用 startRow 和 startColumn 參數指定要插入的資料子集。 例如,在一個含有五個列的表中,您可以使用該功能將資料填充到某個列中。 在本系列的下一篇文章中,我們會更深入地探討 startRow 和 startColumn 參數。

Note:如果文檔中的選中內容是一個表,則選中內容形狀必須與待插入資料相匹配(若未指定 startRow 和 startColumn 參數)。 即,如果待插入資料是一個 2 x 2 表,而文檔中的選中內容是表中的 3 x 2 個儲存格,則方法會失敗。 這也適用于以矩陣形式插入資料。

與矩陣強制類型一樣,標題和行屬性會返回陣列的陣列,其中第一級陣列中的各項分別對應一個資料行,子陣列中的各項分別對應表中的一個資料儲存格,如圖 3 所示。

圖 3 以表形式將資料插入文檔

// Define some tabular data to set in the document,
// including a header row.
var booksToRead = new Office.TableData();
booksToRead.headers = [["Author", "Title"]];
booksToRead.rows = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as a table with a header.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Table },
  function (result) {
    // Access the results, if necessary.
});

圖 4 顯示圖 3 中代碼的結果。

Results of Inserting Data as a Table
圖 4 以表形式插入資料的結果

對於下一個示例,會插入相同資料,不過這次格式化為 HTML,強制類型為 Office.CoercionType.HTML。 現在,我們可以為插入的資料添加其他格式設置(如 CSS 樣式),如圖 5 所示。

圖 5 以 HTML 形式將資料插入文檔

// Define some HTML data to set in the document,
// including header row, text formatting and CSS styles.
var booksToRead =
  "<table style='font-family:Segoe UI'>" +
    "<thead style='background-color:#283E75;color:white'>" +
      "<tr><th>Authors</th><th>Books</th></tr>" +
    "</thead>" +
    "<tbody>" +
      "<tr><td>Xenophon</td><td><u>Anabasis</u></td></tr>" +
      "<tr><td>Plato</td><td><u>Socrates' Apology</u></td></tr>" +
      "<tr><td>Homer</td><td><u>The Iliad</u></td></tr>" +
    "</tbody>" +
  "</table>";
// Set some data to the document as a table with styles applied.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Html },
    function (result) {
    // Access the results, if necessary.
});

圖 6 顯示圖 5 中代碼的結果。

Results of Inserting Data as HTML
圖 6 以 HTML 形式插入資料的結果

最後,我們還可以將文本以 OOXML 的形式插入文檔,這使我們可以在很大程度上自訂資料,並在 Word 中使用許多更高級的內容類型(例如 SmartArt 或文字間圖片)。

我們將用到的資料表如圖 7 中的代碼所示,該資料表為 OOXML 的形式並存儲在字面字串中,(注意:為簡潔起見,僅展示了部分表)。

圖 7 表示一個 Word 表的 OOXML 程式碼片段,存儲為 JavaScript 字面字串

var newTable = "<w:tbl>" +
  "<w:tblPr>" +
    "<w:tblStyle w:val=\"TableGrid\"/>" +
    "<w:tblW w:w=\"0\" w:type=\"auto\"/>" +
    "<w:tblBorders>" +
      "<w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "</w:tblBorders>" +
    "<w:tblLook w:val=\"04A0\" w:firstRow=\"1\" w:lastRow=\"0\"" +
      "w:firstColumn=\"1\" w:lastColumn=\"0\""  +
      "w:noHBand=\"0\" w:noVBand=\"1\"/>" +
  "</w:tblPr>" +
  "<w:tblGrid>" +
    "<w:gridCol w:w=\"4675\"/>" +
    "<w:gridCol w:w=\"4675\"/>" +
  "</w:tblGrid>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\""  +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Authors</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\"" +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Books</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Xenophon</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Anabasis</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  // The rest of the code has been omitted for the sake of brevity.
"
</w:tbl>";

此方法還需要非常熟悉 XML,特別是 OOXML 標準 (ECMA-376) 描述的結構。 將 OOXML 插入到文檔中時,資料必須存儲為一個字串(但不能插入 HTML 文檔物件),其中包含全部所需的資訊,包括檔案格式包中的關係及相關快速組件。 因而,在使用 OOXML 將更高級的內容類型插入 Word 時,必須遵循使用 OOXML 的最佳做法和開放資料包約定來操作 OOXML 資料。

圖 8 中,我們分步解決此問題,首先以 OOXML 形式獲取資料,然後將我們的資料與來自文檔的 OOXML 拼接(將獲取的資料和新資料作為字串進行操作),最後將其插回文檔。 (當然,此代碼之所以能夠工作,部分原因在於我們所添加的內容都不要求在檔中添加或更改任何關係或快速組件。)

圖 8 使用 OOXML 以表形式將資料插入文檔

// Get the OOXML for the data at the point of insertion
// and add a table at the beginning of the selection.
Office.context.document.getSelectedDataAsync(
  Office.CoercionType.Ooxml,
  {
    valueFormat: Office.ValueFormat.Formatted,
    filterType: Office.FilterType.All
  },
  function (result) {
    if (result.status == "succeeded") {
      // Get the OOXML returned from the getSelectedDataAsync call.
var selectedData = result.value.toString();
      // Define the new table in OOXML.
var newTable = "<!--Details omitted for brevity.-->";
      // Find the '<w:body>' tag in the returned data—the tag
      // that represents the body content of the selection, contained
      // within the main document package part (/word/document.xml)—
      // and then insert the new table into the OOXML at that point.
var newString = selectedData.replace(
        "<w:body>",
        "<w:body>" + newTable,
        "gi");
        // Insert the data back into the document with the table added.
Office.context.document.setSelectedDataAsync(
          newString,
          { coercionType: Office.CoercionType.Ooxml },
          function () {
        });
    }
});

圖 9 顯示圖 8 中代碼的結果。

Results of Inserting Data as OOXML
圖 9 以 OOXML 形式插入資料的結果

Note:有一個比較好的方法可以瞭解如何從應用程式操作 OOXML:使用 UI 添加要使用的內容(例如,通過按一下「按一下」|「插圖」|「SmartArt」插入 SmartArt),然後使用 getSelectedDataAsync 獲取內容的 OOXML,最後讀取結果。 有關更多詳細資訊,請參見 bit.ly/SeU3MS 上的「使用 Office 應用程式插入圖像」。

獲取檔中的所有內容

獲取或設置選擇點處的資料固然很有用處,但某些情況下,我們需要獲取檔中的所有內容。 例如,應用程式可能需要以文本形式獲取文檔中的所有內容,分析這些內容,然後在氣泡圖中表示。 另一個示例是,應用程式可能需要將檔中的所有內容發送到遠端 Web 服務,以便進行遠端列印或傳真。

JavaScript API for Office 恰好提供了用於這些情況的功能。 通過使用 JavaScript API,應用程式可以為被插入檔創建一個副本,將副本劃分為指定大小的資料區塊或「切片」(最大 4MB),然後讀取切片中的資料。

獲取檔中的所有內容的過程基本上包括三個步驟:

  1. 對於插入 Word 或 PowerPoint 的應用程式,應用程式會調用 Document.getFileAsync 方法,它將返回一個 File 物件,該物件對應于一個檔副本。
  2. 應用程式獲得對該檔的引用之後,便可以調用 File.getSliceAsync 方法訪問檔中的特定切片(通過傳入要獲取的切片的索引)。 如果採用 for 迴圈來實現,則調用代碼必須謹慎對待對閉包的處理。
  3. 最後,應用程式應在使用完 File 物件之後,通過調用 File.closeAsync 方法關閉該物件。 任何時候記憶體中只能駐留兩個檔;嘗試使用 Document.getFileAsync 打開第三個檔會引發錯誤:「發生了內部錯誤」。

圖 10 中,我們獲取一個劃分為 1KB 區塊的 Word 文檔,遍歷檔中的每個區塊,完成後關閉檔。

圖 10 以文本形式獲取檔中的所有內容,然後遍歷切片

// Get all of the content from a Word document in 1KB chunks of text.
function getFileData() {
  Office.context.document.getFileAsync(
  Office.FileType.Text,
  {
    sliceSize: 1000
  },
  function (asyncResult) {
    if (asyncResult.status === 'succeeded') {
      var myFile = asyncResult.value,
        state = {
          file: myFile,
          counter: 0,
          sliceCount: myFile.sliceCount
        };
      getSliceData(state);
    }
  });
}
// Get a slice from the file, as specified by
// the counter contained in the state parameter.
function getSliceData(state) {
  state.file.getSliceAsync(
    state.counter,
    function (result) {
    var slice = result.value,
      data = slice.data;
    state.counter++;
    // Do something with the data.
// Check to see if the final slice in the file has
    // been reached—if not, get the next slice;
    // if so, close the file.
if (state.counter < state.sliceCount) {
      getSliceData(state);
    }
    else {
      closeFile(state);
    }
  });
}
// Close the file when done with it.
function closeFile(state) {
  state.file.closeAsync(
    function (results) {
      // Inform the user that the process is complete.
});
}

有關如何在 Office 應用程式中獲取所有檔內容的更多資訊,請參見「如何:從 PowerPoint 應用程式獲取整個文檔」,位址為 bit.ly/12Asi4x

從專案中獲取任務資料、視圖資料和資源資料

對於插入 Project 的工作窗格應用程式,JavaScript API for Office 提供了其他方法為活動專案和所選任務、資源或視圖讀取資料。 project-15.js 腳本擴展了 office.js,還為任務、資源和視圖添加了選中內容變更事件。 例如,當使用者在「工作組規劃器」視圖中選擇一個任務時,應用程式可以在一個位置將資訊集成起來並進行顯示,這些資訊可以是為該任務安排的剩餘工作、可參與該任務的人員,以及其他 SharePoint 工作清單中或 Project Server 中會對排程產生影響的相關專案。

插入專案的工作窗格應用程式對專案中內容只有讀取存取許可權。 但是,因為工作窗格應用程式處於網頁的核心位置,所以可以使用 JavaScript 和協定(如具象狀態傳輸 (REST))對外部應用程式進行讀取和寫入。 例如,用於 Office 和 SharePoint 文檔的應用程式含有一個針對 Project Professional 應用程式範例,該應用程式將 jQuery 與 Project 中的 OData 報表服務結合使用,將活動專案的總成本和工作資料與 Project Web App 中所有專案的平均值進行比較(請參見圖 11)。

A Task Pane App that Uses jQuery with an OData Reporting Service
圖 11 A 結合使用 jQuery 與 OData 報表服務的工作窗格應用程式

有關更多資訊,請參見文檔頁面「如何:創建結合使用 REST 與內部部署 Project Server OData 服務的 Project 應用程式」,位址為 bit.ly/T80W2H

因為 ProjectDocument 擴展了 Document 物件,所以 Office.coNtext.document 物件捕獲一個對活動專案的引用 — 類似于一個插入其他主機應用程式的應用程式。 Project 中提供的非同步方法的簽名與 JavaScript API for Office 中的其他方法類似。 例如,getProjectFieldAsync 方法具有三個參數:

  • fieldId:指定要在 callback 參數的物件中返回的欄位。 Office.ProjectProjectFields 枚舉包括 12 個欄位,如專案 GUID、開始日期、完成日期和(如果有)Project Server URL 或 SharePoint 工作清單 URL。
  • asyncCoNtext:(可選)是在 asyncResult 物件中返回的任何使用者定義類型。
  • callback:包含一個對調用返回時運行的函數的引用,並包含用於處理成功或失敗的選項。

圖 12 所示,特定于 Project 中的應用程式的方法的使用類似于其他應用程式中承載的應用程式。 在該腳本片段中,一個本地定義的函式呼叫了一個在應用程式中顯示錯誤訊息的常式。 該腳本未使用 asyncCoNtext 參數。

圖 12 從插入專案的工作窗格獲取欄位的 GUID

var _projectUid = "";
// Get the GUID of the active project.
function getProjectGuid() {
  Office.context.document.getProjectFieldAsync(
    Office.ProjectProjectFields.GUID,
    function (asyncResult) {
      if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
        _projectUid = asyncResult.value.fieldValue;
        }
      else {
        // Display error message to user.
}
    }
  );
}

雖然 getProjectFieldAsync 方法對於常規專案只能獲取 12 個欄位,不過 getTaskFieldAsync 方法可以使用 ProjectTaskFields 枚舉為獲取任務的 282 個不同欄位中的任何一個。 並且 getResourceFieldAsync 方法可以使用 ProjectResourceFields 枚舉獲取資源的 200 個欄位中的任何一個。 ProjectDocument 物件中的其他常規方法有:getSelectedDataAsync(返回支援的任何視圖中的所選文本資料)和 getTaskAsync(返回所選任務的常規資料的幾個專案)。 工作窗格應用程式在 Project 中可使用 16 個不同視圖。

此外,Project 中的工作窗格應用程式可以在使用者更改視圖、選擇任務或選擇資源時,添加或移除事件處理常式。

Office 應用程式中的事件

JavaScript API for Office 使您可以通過事件創建回應能力更好的應用程式。 API 的事件模型支援四個關鍵事件方案,這些方案是為 Office 開發應用程式的基礎(後面討論)。 瞭解這四個方案可牢固掌握 API 的事件模型。

也就是說,Office 應用程式的事件模型是始終一致的,因此瞭解事件處理的常見設計便可瞭解這一重要概念。

關於 Office 應用程式 API 中的事件常見設計,以下物件具有與之關聯的事件:

  • 綁定
  • CustomXMLPart
  • Document
  • RoamingSettings(郵件應用程式)
  • Settings(設置)

除了相關聯的事件之外,列出的每個物件都具有兩個用於處理其事件的方法:

  • addHandlerAsync
  • removeHandlerAsync

因為 removeHandlerAsync 方法只是從事件取消訂閱處理常式,並且因為其簽名幾乎與 addHandlerAsync 的簽名相同,所以在下一節中,我們僅專心探討 addHandlerAsync。

Note:remove­HandlerAsync 與 addHandlerAsync 方法有一個非常重要的差異。 handler 參數對於 removeHandlerAsync 是可選的。 如果未指定,則會移除給定事件種類的所有處理常式。

The AddHandlerAsync Method

addHandlerAsync 方法將事件處理常式綁定到指定事件,對於實現它的每個物件具有相同簽名:

objectName.addHandlerAsync(eventType, handler [, options], callback);

現在來討論此方法的參數。

EventType 參數必需的 eventType 參數採用 EventType 枚舉,會向方法告知要綁定的事件種類。

Handler 參數 eventType 參數後跟 handler 參數。 handler 可以是命名函數或匿名內聯函數。 請注意,如同許多程式設計語言的事件模型一樣,Office 應用程式運行時會調用處理程式並傳入事件物件實際參數作為唯一形式參數。 此外,如果將內聯匿名函數用於 handler 參數,則移除處理常式的唯一方式是通過調用 removeHandler­Async 並且不指定 handler 參數,從事件中移除所有處理常式。

選項參數與 Office 應用程式 API 中的所有非同步函數一樣,可以指定包含可選參數的物件,但是對於所有 addHandlerAsync 方法,可以指定的唯一可選參數是 asyncCoNtext。 提供該參數是為了通過非同步方法傳遞可以在回檔中檢索的任何所需資料。

Callback 參數 callback 的行為如同在 Office 應用程式 API 中其他位置的行為一樣,只不過有一個顯著例外:AsyncResult 物件的值屬性。 如本文前面所討論的一樣,當運行時調用回檔時,它會傳入一個 Async­Result 物件,可使用 AsyncResult 物件的值屬性獲取非同步調用的傳回值。 對於 addHandlerAsync 方法中的回檔,Async­Result 物件的值始終未定義。

圖 13 演示如何對 DocumentSelectionChanged 事件的 addHandlerAsync 方法編寫代碼(代碼假設具有一個 <div> 元素,其 id 屬性值為「message」)。

圖 13 使用 Document.addHandlerAsync 方法為 DocumentSelection­Changed 事件綁定事件處理常式

Office.initialize = function (reason) {
  $(document).ready(function () {       
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged, onDocSelectionChanged,
        addHandlerCallback);
      Office.context.document.addHandlerAsync(
        Office.EventType.DocumentSelectionChanged, onDocSelectionChanged2,
        addHandlerCallback2);
  });
};
function onDocSelectionChanged(docSelectionChangedArgs) {
  write("onDocSelectionChanged invoked each event.");
}
function onDocSelectionChanged2(docSelectionChangedArgs) {
  write("onDocSelectionChanged2 invoked each event.");
}
function addHandlerCallback(asyncResult) {
  write("addHandlerCallback only called once on app initialize.");
}
function addHandlerCallback2(asyncResult) {
  write("addHandlerCallback2 only called once on app initialize.");
}
function write(message) {$('#message').append(message + "\n");

初始化應用程式時,圖 13 中的代碼會將 onDocSelectionChanged 和 onDocSelectionChanged2 處理常式函數綁定到 Document­SelectionChanged 事件,演示了同一個事件可以具有多個事件處理常式。 當 DocumentSelectionChanged 事件觸發時,這兩個處理常式只是寫入到 <div>(「message」)。

addHandlerAsync 的調用還分別包括回檔 addHandlerCallback 和 addHandlerCallback2。 回檔也寫入 <div>(「message」),但是僅在 addHandlerAsync 完成時調用一次。

同樣,可以使用 addHandlerAsync 方法為 JavaScript API for Office 中的任何事件綁定事件處理常式。

Office 應用程式事件模型中的關鍵方案

如前所述,在 JavaScript API for Office 中,考慮 Office 應用程式事件模型時,可以圍繞四個關鍵事件方案來整理您的理解。 API 中的所有事件都屬於這四個關鍵事件方案之一:

  • Office.initialize events
  • 文檔級選中內容變更事件
  • 綁定級選中內容和資料變更事件
  • 設置變更事件

Office.initialize 事件迄今為止,JavaScript API for Office 中您會遇到的最常見事件是 Office.initialize 事件。 對於您所創建的每個 Office 應用程式,initialize 事件都會發生。 事實上,這是運行時首先執行的代碼部分。

如果查看 Visual Studio 2012 為針對 Office 專案的任何新應用程式提供的起始代碼,則會發現應用程式的 ProjectName.js 檔中的前幾行起始代碼會為 Office.initialize 事件綁定事件處理常式,如下所示:

// This function is run when the app is ready to
// start interacting with the host application;
// it ensures the DOM is ready before adding click handlers to buttons.
Office.initialize = function (reason) { /* handler code */ };

從上一篇文章中的物件模型層次結構部分可知,Office 物件是 JavaScript API for Office 中的最頂層物件,表示應用程式在運行時的實例。 當 Office 應用程式運行時完全載入並且準備好與應用程式進行交互時,Initialize 事件會觸發。 因此,Initialize 事件的事件處理常式本質上是應用程式與運行時之間的「握手」,必須在代碼其餘部分運行之前發生。

作為 Office.initialize 事件處理常式而提供的函數只有一個參數 — InitializationReason 枚舉。 Initialization­Reason 枚舉只有兩個枚舉值 — Inserted 和 documentOpened:

  • Inserted 表示由於應用程式剛剛插入文檔而正在初始化應用程式。
  • documentOpened 表示由於剛剛打開已插入了應用程式的文檔而正在初始化應用程式。

運行時會傳入 InitializationReason enumeration 枚舉作為處理常式函數的唯一參數。 在其中可以根據原因對代碼的反應方式進行分支處理。

下面說明了這種方法的工作原理:

Office.initialize = function (reason) {
  // Display initialization reason.
if (reason == "inserted")
  write("The app was just inserted.");
  if (reason == "documentOpened")
  write(
    "The app is already part of the document.");
}
// Function that writes to a div with
// id='message' on the page.
function write(message){
  document.getElementById(
  'message').innerText += message;
}

Note:上面的程式碼片段假設具有一個 <div> 元素,其 id 屬性值為「message」。

有趣的是,不必在作為處理常式而提供的函數中包含任何內容,但是該函數必須存在,否則應用程式會在啟動時引發錯誤。

順便說一句,若要初始化可在應用程式中使用的其他框架(如 jQuery),Office.initialize 事件的事件處理常式是個不錯的位置。 同樣,在 Visual Studio 為針對 Office 專案的新應用程式提供的起始代碼中,會發現類似于圖 14 中的代碼的內容。

圖 14 在 Office.initialize 事件處理常式中初始化其他框架

Office.initialize = function (reason) {
  $(document).ready(function () {
    $('#getDataBtn').click(function () { getData('#selectedDataTxt'); });
    // If setSelectedDataAsync method is supported
    // by the host application, setDatabtn is hooked up
    // to call the method, else setDatabtn is removed.
if (Office.context.document.setSelectedDataAsync) {
        $('#setDataBtn').click(function () { setData('#selectedDataTxt'); });
    }
    else {
      $('#setDataBtn').remove();
    }
  });
};

jQuery .ready 事件在 Office.initialize 事件處理常式中處理。 這可確保首先載入並準備好 JavaScript API for Office,然後供 JQuery 代碼調用。

文檔級選中內容變更事件文檔級選中內容變更事件在文檔中的選中內容從一個選中內容移動到另一個選中內容時發生。 例如,當使用者按一下 Word 文檔中當前選中內容之外的一段文本時,或是按一下文檔中其他位置的物件或位置時,會在文檔級針對選中內容的更改觸發事件。

以下代碼闡明瞭如何回應當前選中內容的更改:

function addEventHandlerToDocument() {
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged,
      MyHandler);
  }
  function MyHandler(eventArgs) {
    doSomethingWithDocument(eventArgs.document);

綁定級選中內容和資料變更事件通過 Office 應用程式物件模型中的綁定,可以按一致方式訪問文檔(或試算表)中的特定區域,具體方式是建立與文檔中唯一命名的區域的連結(或綁定)。 要使用綁定,請先使用 API 提供的方法之一創建一個綁定。 隨後可以使用其唯一識別碼引用所創建的特定綁定。

綁定也會觸發事件,應用程式可以根據需要回應這些事件。 具體而言,當綁定區域內發生選中內容更改以及資料更改時,綁定會觸發事件。 以下兩個程式碼片段演示如何處理給定綁定中的選中內容更改和資料更改(都假設具有一個 <div> 元素,其 id 屬性值為「message」)。

回應 Binding.bindingSelectionChanged 事件:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingSelectionChanged,
    onBindingSelectionChanged);
}
function onBindingSelectionChanged(eventArgs) {
  write(eventArgs.binding.id + " has been selected.");
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

Responding to the Binding.bindingDataChanged event:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingDataChanged, onBindingDataChanged);
}
function onBindingDataChanged(eventArgs) {
  write("Data has changed in binding: " + eventArgs.binding.id);
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

設置變更事件 Office 應用程式物件模型為開發者提供了一種方式,來持久保存與其應用程式相關的設置。 Settings 物件充當屬性包,自訂應用程式設定在其中以鍵/值對的形式存儲。 Settings 物件也有與之關聯的事件(即 Settings.settingsChanged),該事件在所存儲的設置發生更改時觸發。

有關 Settings.settingsChanged 事件的更多資訊,請參見 bit.ly/U92Sbe 上關於 JavaScript API for Office 的 MSDN 文檔。

接下來:更高級主題

在本系列中的第二篇文章中,我們回顧了有關在 Office 應用程式中獲取和設置 Office 檔內容的基礎知識。 我們演示了如何獲取和設置選中內容資料以及如何獲取所有檔資料。 瞭解了如何從 Project 應用程式獲取專案、任務、視圖和資源資料。 最後,我們討論了 JavaScript API for Office 中的事件以及如何對它們編寫代碼。

Note:我們要感謝 Office 部門的程式師 Jim Corbin,他對涉及 Project 應用程式方面的內容貢獻良多。

接下來,我們將詳細介紹 JavaScript API for Office 中的一些更高級主題:資料繫結和自訂 XML 部件。

Stephen Oliver 是 Office 部門的一位程式師,也是 Microsoft 認證專業開發人員 (SharePoint 2010)。他針對 Excel 服務和 Word 自動化服務編寫開發人員文檔,還編寫 PowerPoint 自動化服務開發人員文檔。他説明組織和設計過 Excel Mashup 網站,網址是 ExcelMashup.com

Eric Schmidt 是 Office 部門的一位程式師。他為 Office 應用程式創建了多段示例代碼,包括流行的「持久保存自訂設置」示例代碼。此外,他還就 Office 可程式設計性撰寫過有關其他產品和技術的文章並創建過相關視頻。

衷心感謝以下技術專家對本文的審閱: Mark Brewster, Shilpa Kothari and Juan Balmori Labra
Mark Brewster 于 2008 年獲得了 亞利桑那大學的數學與電腦科學學士學位,已在 Microsoft 從事了四年的軟體發展。 他熱衷於騎自行車,喜歡喝啤酒和聽唱片。

Shilpa Kothari (Bhavsar) 是一位在 Microsoft 從事測試工作的軟體工程師。 她參與過多項 Microsoft 產品的研發工作,其中包括 Bing Mobile、Visual Studio 和 Office。 她對軟體 QA 和使用者體驗充滿熱情,連絡方式:shilpak@microsoft.com

Juan Balmori Labra 是一位專案經理,至少從事了三年的 Microsoft Office JavaScript API 工作。 他以前曾參與過 Office 2010 發佈(帶有 Business Connectivity Services 和 Duet)。 在為了追逐夢想而搬到雷蒙德之前,Juan 曾在 Microsoft Mexico 中擔任過公眾部門諮詢事務的首席架構師。