本文章是由機器翻譯。

Microsoft Office

探討 JavaScript API for Office:資料繫結和自訂 XML 部件

Stephen Oliver
Eric Schmidt

下載代碼示例

本文是 JavaScript API for Office 的深入演練系列文章的第 3 部分。本文將繼續介紹該 API 的主要概念,重點介紹資料繫結以及對使用自訂 XML 部件的支援。第 1 部分「探索新的 JavaScript API for Office」(msdn.microsoft.com/magazine/jj891051) 提供了物件模型的寬泛概述。第 2 部分「探討 JavaScript API for Office:資料訪問和事件」(msdn.microsoft.com/magazine/jj991976) 深入探討了與如何獲取檔內容相關的重要概念並對事件模型進行了全面綜述。本文之後的第 4 部分將專門重點講述第三個類型的 Office 應用程式:郵件應用程式。

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

Office 應用程式中的資料繫結

通過資料繫結,文檔中的特定資料區域與應用程式之間可以緊密集成。區域資料繫結到應用程式中的一個命名物件,這樣應用程式可以訪問指定區域中的資料,即使使用者選擇了別的內容也是如此。

綁定一經創建便會一直存在,即使在頁面上移動了區域(在 Word 中)或者將區域複製到了其他工作表(在 Excel 中)。例如,表的綁定一直存在,即使使用者對其進行了重命名。

當區域資料發生更改時,綁定會引發一個應用程式可掛接到的事件。通過此事件,應用程式可以訪問發生更改的資料並做出相應的反應。

綁定和應用程式「視圖」 毫無疑問,Office 應用程式中的資料繫結使應用程式可以直接存取 Office 檔中的資料集,從而使應用程式可以更輕鬆地分析資料,無需依賴使用者的直接操作。不過,通過資料繫結,不僅可以實現目標資料訪問,開發者還可以將 Office 檔本身包含為應用程式的可自訂和必備元件。

許多 Office 應用程式為其使用者提供的介面單獨包含在工作窗格或內容應用程式 UI 範圍內,這並沒有什麼不妥。不過,簡單說來,Office 檔中的資料及其表示形式本身就是應用程式的「視圖」。使用者與 Office 檔內的資料進行交互。他們輸入新資料,更改現有資料,從文檔內容中刪除不需要的資料。Office 應用程式提供了使用者十分瞭解的資料檢視。

通過 JavaScript API for Office 中的資料繫結功能,可以在應用程式中利用 Office 應用程式所提供的資料檢視。開發者可以使用 Office 中的現成功能為應用程式開發「介面」。這樣,可以使用 Office 應用程式中的現成功能為應用程式視圖設置樣式。因此,資料繫結提供了將應用程式的視圖連接到 JavaScript 檔中包含的業務邏輯「模型」的強大功能。

當然,反之依然。可以將 Office 檔用作資料來源存儲資料模型的內容。隨後可以使用應用程式提供資料檢視。憑藉綁定的靈活性,可以根據需要將模型-視圖-控制器 (MVC) 模式應用於應用程式和 Office 檔。

使用綁定的方案對開發者的創造力沒有硬性限制,應用程式可在以下三種一般化方法的任何組合中使用綁定:

  • 應用程式在使用者更改區域中的資料時進行回應。
  • 應用程式選取區域中的資料,對其進行分析,並向使用者提供建模或提交資料的選項。
  • 應用程式將資料從外部資料源推送到綁定區域。

舉例來說,有一個簡單的股票行情應用程式插入到 Excel 活頁簿,活頁簿中有一列包含股票代碼,另一列包含當前股價。通過資料繫結,該應用程式可以綁定到包含股票代碼的列,從而選取該列中的股票代碼。然後,應用程式通過 Web 服務訂閱這些股票的價格,對從該服務發送的結果進行分析。最後,應用程式可以綁定到工作表中的股價列並即時更新這些值。

我將實現上述功能 — 創建一個股票行情活頁簿 — 下一部分將介紹 Binding 物件。

使用 Binding 物件資料繫結的奇妙之處全在 Bindings 集合和 Binding 物件中。

  • Bindings 集合表示在 Office 檔與 Office 應用程式之間創建的所有綁定。應用程式不能訪問其他應用程式創建的任何綁定。
  • Binding 物件表示 Office 檔中的某一區域與應用程式之間的一個命名綁定。它公開幾個成員,用於獲取、讀取和設置資料,以及對綁定區域中的更改進行回應。

我們將在構建股票行情應用程式時更詳細地瞭解這些物件。

在繼續討論之前,讓我們快速看一下資料。圖 1 顯示了此應用程式視圖的外觀。可以看到,使用的股票代碼是虛構的,僅供演示之用。


圖 1 Excel 活頁簿中一個應用了公式和條件格式、名為「Stocks」的表

此外,我們還在此活頁簿中添加了一些「智慧」。The region of data to which we want to bind has been formatted as a table and named “Stocks.” A custom formula has been added to the values in the right-hand column to compare the other values in the table.我們還向表應用了條件格式,讓圖示集顯示在右列中。

值得注意的是,我們已在 Visual Studio 2012 中將此活頁簿添加到解決方案中,這樣不必每次調試應用程式都重新創建表。要向解決方案添加活頁簿,請在解決方案中按右鍵應用程式專案(使用預設範本時列在解決方案資源管理器中的第一個專案),按一下「添加現有項」,然後選擇相關活頁簿。然後,在應用程式專案的屬性中,為活頁簿檔設置「啟動操作」。調試時,需要手動將應用程式插入活頁簿中(「插入」選項卡 |「適合於 Office 的應用程式」按鈕)。

初始化後,應用程式的業務邏輯需要設置綁定,然後向綁定的事件 Office.EventType.BindingDataChanged 添加事件處理常式。圖 2 顯示了相應代碼。請注意,我們已將代碼封裝在一個自解壓縮匿名函數中,該函數存儲在 StockTicker 變數中。The name of the table on the spreadsheet, the binding name and the binding are all stored as class fields within the StockTicker “class.” The StockTicker “class” exposes only a single member: initializeBinding.

圖 2 創建 Excel 活頁簿的綁定並向該綁定中的資料變更事件添加處理常式。

var StockTicker = (function () {
  var tableName = "Sheet1!Stocks",
      bindingName = "Stocks",
      binding;
  // Create the binding to the table on the spreadsheet.
function initializeBinding() {
    Office.context.document.bindings.addFromNamedItemAsync(
      tableName,
      Office.BindingType.Table,
      { id: bindingName },
      function (results) {
        binding = results.value;
        addBindingsHandler(function () { refreshData(); });
    });
  }
  // Event handler to refresh the table when the
  // data in the table changes.
var onBindingDataChanged = function (result) {
    refreshData();
  }
  // Add the handler to the BindingDataChanged event of the binding.
function addBindingsHandler(callback) {
    Office.select("bindings#" + bindingName).addHandlerAsync(
      Office.EventType.BindingDataChanged,
      onBindingDataChanged,
      function () {
        if (callback) { callback(); }
    });
  }
  // Other member methods of this "class" ...
return {
    initializeBinding: initializeBinding
  };
})();

要在應用程式與工作表中的表之間建立綁定,我們可以使用 JavaScript API 中 Document 類的幾種不同方法之一,這些方法包括 addFromNamedItemAsync、addFromPromptAsync 和 addFromSelectionAsync。(請注意,addFromPromptAsync 僅在 Excel 和 Excel Web App 中可用。)

因為我們知道要綁定到的區域的名稱,該名稱就是 Sheet1 上名為「Stocks」的表,所以我們使用了 addFromNamedItemAsync 方法來建立綁定。我們使用 Excel 區域標記法 (Sheet1!Stocks) 傳入了該表的名稱。此方法調用的結果包括對綁定自身的引用,使我們可以將對綁定的引用存儲在綁定變數(類欄位)中。

在代碼中,我們為該方法的 bindingType 參數傳入了 Office.BindingType.Table 值。這將指定我們需要使用自己的資料創建「表」類型的綁定,但我們也可以指定文本或矩陣類型的綁定。以表形式綁定到區域有幾項優點。例如,如果使用者向表新增列或行,綁定區域的範圍也會增加。That works the other way, as well.TableBinding 物件是綁定的基礎,它公開一些屬性用於添加列、添加行甚至刪除表中所有資料。

(有關 JavaScript API for Office 中的文本和矩陣資料類型的詳細資訊,請參閱本系列文章的第二篇文章中的「從 Office 應用程式訪問 Office 檔內容」部分。)

隨後,代碼向綁定的 BindingDataChanged 事件添加處理常式。當綁定區域中的資料發生更改時,即使用者更改區域中的資料時,需要調用本地定義的 refreshData 函數以啟動更新表的過程。此外,因為該表尚未使用資料來源中的資料進行更新,所以我們將在添加事件處理常式後調用 refreshData。

可以看到,addBindingsHandler 函數使用 Office.select 方法獲取綁定,不過我們可以改用 Bindings.getByIdAsync 方法。這兩個方法之間的主要差異在於對結果中返回的資料的存取層級。Office.select 方法返回一個可能調用代碼的 Binding 物件。如果該方法成功,返回的 Binding 物件只有數量有限的成員可供使用。通過使用 Office.select 選擇綁定,可以直接從 Binding 物件調用成員。這樣,無需向獲取綁定的函數添加回檔即可向綁定添加處理常式。

(你可能會想,我們可以只使用本地「綁定」變數即可捕獲對綁定的引用。是的,我們可以這樣做。我們編寫此代碼只是為了演示。)

圖 3 顯示 refreshData 和 getBindingData 函數。refreshData 函數只是啟動一系列非同步調用,通過調用 getBindingData 從工作表獲取表資料。getBindingData 函數包含對 Binding.getDataAsync 方法的調用,以 TableData 物件的形式返回資料。

圖 3 從表綁定獲取資料並調用 Web 服務

var StockTicker = (function () {
  // Other members of this "class"...
// Refresh the data displayed in the bound table of the workbook.
// This function begins a chain of asynchronous calls that
  // updates the bound table.
function refreshData() {
    getBindingData();
  }
  // Get the stock symbol data from the bound table and
  // then call the stock quote information service.
function getBindingData() {
    binding.getDataAsync(
      {
        startRow: 0,
        startColumn: 0,
        columnCount: 1
      },
      function (results) {
        var bindingData = results.value,
            stockSymbols = [];
        for (var i = 0; i < bindingData.rows.length; i++) {
          stockSymbols.push(bindingData.rows[i][0]);
        }
        getStockQuotes(stockSymbols);
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

在調用圖 3 所示的 getDataAsync 時,我們可以通過為 options 參數傳入匿名物件 {coercionType: Office.CoercionType.Table},指定要顯式檢索的資料類型(或更改資料類型)。 因為尚未指定要檢索的資料類型,getDataAsync 調用在其原始資料類型(TableData 物件)中返回綁定資料。

第二篇文章中討論了 TableData 物件,該物件為要使用的資料提供了更多結構,也就是說我們可以使用標題和行屬性從表中選擇資料。 在本示例中,我們只需獲取表中第一列的股票代碼。 你可能還記得,行屬性以陣列的形式存儲表中的資料,其中第一個陣列中的每一項都與表中的一行相對應。

在使用 TableData 物件的綁定時,可以使用 startRow 和 startColumn 參數指定要從綁定獲取的行和列的子集。 這兩個參數為要從表中提取的資料指定從零開始的起點,其中以表左上角為原點。 (請注意,必須同時使用 startRow 和 startColumn 參數,否則會引發異常。)因為只需獲取表中第一列的資料,所以我們還將傳入設置為 1 的 columnCount 參數。

獲得該列資料後,將每個值推送到一維陣列中。 在圖 3 中,可以看到我們調用了 getStockQuotes 函數,該函數以參數形式接受股票代碼陣列。 在圖 4 中,使用 getStockQuotes 函數從股票報價 Web 服務檢索資料。 (為便於演示,省略了有關該 Web 服務的代碼。)在對來自 Web 服務的結果進行分析後,調用本地定義的 removeHandler 方法。

圖 4 調用 Web 服務並刪除 BindingDataChanged 事件處理常式

var StockTicker = (function () {
  // Other members of this "class"...
// Call a Web service to get new stock quotes.
function getStockQuotes(stockSymbols) {
    var stockValues = [];
    // Make a call to the Web service and parse the results.
// The results are stored in the stockValues variable, which
    // contains an array of arrays that include the stock symbol
    // with the current value.
removeHandler(function () {
      updateTable(stockValues);
    });
  }
  // Disables the BindingDataChanged event handler
  // while the table is being updated.
function removeHandler(callback) {
    binding.removeHandlerAsync(
      Office.EventType.BindingDataChanged,
      { handler: onBindingDataChanged },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
           if (callback) { callback(); }
        }
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

removeHandler 函式呼叫 binding.removeHandlerAsync 方法,該方法刪除 BindingDataChanged 事件的事件處理常式。 現在,如果保留了附加到事件的處理常式,則更新表時將引發該事件。 隨後會再次調用事件處理常式並更新表,從而造成無限迴圈。 使用新資料更新表後,將事件處理常式添加回事件中。

(當然,我們也可以使用矩陣強制類型創建不同的綁定以分隔表中的列。 這樣,可只將事件掛接到使用者可編輯的列。)

removeHandlerAsync 方法採用參數 handler,該參數指定要刪除的處理常式的名稱。 最好使用 handler 參數從綁定事件中刪除處理常式。

圖 5 中,通過調用本地定義的 updateTable 函數,使用新股票值更新表。

圖 5 從表綁定獲取資料並調用 Web 服務

var StockTicker = (function () {
  // Other members of this "class"...
// Update the TableData object referenced by the binding
  // and then update the data in the table on the worksheet.
function updateTable(stockValues) {
    var stockData = new Office.TableData(),
        newValues = [];
    for (var i = 0; i < stockValues.length; i++) {
      var stockSymbol = stockValues[i],
          newValue = [stockSymbol[1]];
      newValues.push(newValue);
    }
    stockData.rows = newValues;
    binding.setDataAsync(
      stockData,
      {
        coercionType: Office.CoercionType.Table,
        startColumn: 3,
        startRow: 0
      },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
          addBindingsHandler();
        }
    });  
  }
  return {
    // Exposed members of the "class."
  };
})();

updateTable 函數獲取從 Web 服務傳入的資料,然後將其寫回綁定表中。在本示例中,stockValues 參數包含另一個陣列的陣列,其中第一個陣列中的每一項都是一個包含股票代碼及其當前股價的陣列。為了將這些資料設置回綁定表中,創建一個新的 TableData 物件並將股票值資料插入該物件中。

需要注意的是,TableData.rows 屬性中設置的資料必須與插入綁定中的資料的形狀相匹配。如果盲目地將全新的 TableData 物件設置到綁定表中,可能會丟失表中的部分資料,如公式等。在圖 5 中,將資料作為一個資料列(陣列的陣列,其中每個子陣列包含一個項)添加到 TableData 物件中。在將此資料插入回綁定表時,需要將這一更新的資料列插入適當的列中。

這裡,再次使用了 startRow 和 startColumn 屬性。updateTable 函數包含對 binding.setDataAsync 的調用,該方法通過指定 startColumn 和 startRow 參數,將 TableData 推送回工作表的表中。startColumn 參數設置為 3,這意味著插入的 TableData 物件將從表中的第四列開始插入其資料。在 setDataAsync 方法的回檔過程中,再次調用 addBindings­Handler 函數將事件處理常式重新應用到事件。

當 binding.setDataAsync 方法成功完成時,新表資料推送到綁定區域並立即顯示出來。從使用者角度來說,這種體驗是無縫的。使用者在表的儲存格中鍵入資料並按 Enter 後,表中的「值」列會自動更新。

自訂 XML 部件

JavaScript API for Office 支援一個特別重要的功能,即在 Word 中創建和操作自訂 XML 部件。要充分瞭解 JavaScript API for Office 用於自訂 XML 部件的巨大潛力,掌握一些背景知識十分有説明。具體來說,需要瞭解如何組合使用 Office Open XML(OOXML 或 OpenXML)檔案格式、自訂 XML 部件、內容控制項和 XML 映射來創建十分強大的解決方案,也就是涉及創建動態 Word 文檔的解決方案。

OOXML 格式 Office 2007 為 Office 文檔引入了新的 OOXML 檔案格式,現在這是 Office 2010 和 Office 2013 的預設檔案格式。(可以看出哪些 Office 文檔是 OOXML 檔案格式,因為這些文檔的副檔名現在由四個字母組成,其中許多是以「x」結尾,例如「.docx」表示 Word 文檔,「.xlsx」表示 Excel 試算表,「.pptx」表示 PowerPoint 文檔。)

OOXML 格式的 Office 文檔基本上是 .ZIP 檔案。每個 .ZIP 檔案都包含一系列 XML 檔(稱為「部件」),它們共同構成了 Office 文檔。如果將 Office 文檔(如 Word .docx 文檔)重命名為 .zip,然後檢查其檔裡面的內容,會發現該文檔實際上就是一系列不同的 XML 檔,它們組織到 .zip 包內的資料夾中,如圖 6 所示。


圖 6 Office Open XML 格式文檔的檔結構

自訂 XML 部件基礎知識雖然有現成的標準 XML 部件,Office 應用程式總是會為每個 OOXML 格式的新 Office 文檔創建這些部件(例如,有一種內置 XML 部件用於描述核心文件屬性),你還可能有興趣向 Word 文檔、Excel 活頁簿或 PowerPoint 演示文稿添加自己的「自訂 XML」部件。自訂 XML 部件添加到 .zip 包的 XML 檔集合中,這些檔構成了 Office 文檔。自訂 XML 部件存儲在文檔的檔結構中,但不顯示給最終使用者。這使你可以插入隨隱藏在檔結構內的 Office 文檔的特定實例移動的業務資料。這樣,你可以在自己的應用程式中使用該自訂 XML,而這就是 JavaScript API for Office 所支援的功能。

內容控制項除了允許將自訂 XML 包含在文檔中的 OOXML 格式及其檔結構之外,Word 2007 還新增了內容控制項,該功能對自訂 XML 部件進行了大量補充。

內容控制項是一種在 Word 文檔中定義固定區域的方法,這些區域可存放特定種類的資料,如純文字、格式文本、圖片、日期甚至重復資料。內容控制項對自訂 XML 部件進行補充的重要方面是,使用 XML 映射的資料繫結。

XML 映射一種內容控制項,可綁定或「映射」到包含在文檔中的 XML 部件的 XML 中的元素。例如,企業可以將業務資料以自訂 XML 部件的形式從後端系統注入到其內容控制項映射到該自訂 XML 部件的 Word 文檔。內容控制項綁定到自訂 XML 部件中的特定節點,這樣,當最終使用者打開文檔時,XML 映射內容控制項使用自訂 XML 部件中的資料進行自動填滿。反之亦然,企業可以使用包含映射內容控制項的同一 Word 文檔,但需要最終使用者將資料輸入內容控制項中。在保存文檔後,映射內容控制項中的資料將保存回 XML 檔中。這樣,應用程式可以從已保存文檔中的自訂 XML 部件獲取資料並將其推送到後端系統。綜上所述,JavaScript API for Office 為開發應用程式提供了豐富的支援功能。

通過 JavaScript API for Office 使用自訂 XML 部件要詳細探討 Office 應用程式 JavaScript 物件模型中自訂 XML 部件 API 的一些比較重要的功能,最好是通過舉例說明。在這一部分中,我們使用 Office 應用程式和 SharePoint 開發者門戶的「示例」區域中的「發票管理器」示例 (bit.ly/YRdlwt),以便你可以遵照操作。發票管理器示例是動態文檔方案的一個示例,在該方案中,企業希望生成從後端系統提取資料的文檔以製成發票。在此例中,資料是客戶名稱和送貨位址以及關聯的客戶採購清單。

示例包含一個用於創建新發票的範本文檔。該範本文檔有一個佈局,其中包含客戶名稱、位址和客戶採購表。文檔的客戶名稱、位址和採購部分都是內容控制項。每個內容控制項都映射到架構中的一個節點,該架構是為保存客戶發票資料而創建的,如圖 7 所示。


圖 7 文檔圖面上映射到自訂 XML 部件的內容控制項

發票管理器應用程式範例的 UI 十分簡單,如圖 8 所示。


圖 8 發票管理器應用程式範例的 UI

最終使用者從應用程式 UI 的下拉清單中選擇發票編號,與該發票編號關聯的客戶資料將顯示在應用程式主體中,如圖 9 所示。


圖 9 使用自訂 XML 部件中的資料進行填充的發票管理器 UI

當使用者選擇「填充」按鈕時,應用程式將顯示的資料以自訂 XML 部件的形式推送到文檔中。因為內容控制項映射到自訂 XML 部件中的節點,所以將自訂 XML 部件推送到文檔後,內容控制項立即為它們映射到的每個 XML 節點顯示資料。你可以即時替換自訂 XML 部件(就像我們在這裡執行的操作一樣),只要該部件符合內容控制項映射到的架構,內容控制項就會顯示映射資料。圖 10 顯示了內容控制項映射到文檔中的自訂 XML 部件時的發票管理器裝箱單。


圖 10 映射到自訂 XML 部件中的節點的內容控制項顯示綁定資料

CustomXmlParts 物件

CustomXmlParts.addAsync 使用自訂 XML 部件的第一步是瞭解如何使用 JavaScript API for Office 將其添加到文檔中。只能使用 customXml­Parts.addAsync 方法執行此操作。顧名思義,customXmlParts.addAsync 方法以非同步方式添加自訂 XML 部件,其簽名如下:

Office.context.document.customXmlParts.addAsync(xml [, options], callback);

請注意,該函數的第一個必需參數是 XML 字串。 這是自訂 XML 部件的 XML。 如前所述,發票管理器使用自訂 XML 部件映射到文檔圖面上的內容控制項,但首先必須獲取要以自訂 XML 形式插入的客戶資料。 在保存整個應用程式邏輯的 InvoiceManager.js 檔中,應用程式類比使用使用者定義的函數 setupMyOrders 從後端系統獲取客戶資料的過程。 此函數創建一個由三個物件組成的陣列,這些物件表示三個客戶訂單。 當然,可以設想,企業可採用各種方法來存儲和獲取客戶採購歷史記錄,如 SQL 資料庫,但簡單起見,只在應用程式中創建三個「硬編碼」客戶訂單。

在創建訂單物件後,它們所表示的資料必須在 XML 中呈現,這樣才能在對 custom­XmlParts.addAsync 的調用中使用。 這是在 initializeOrders 函數中發生的過程,這同時會設置應用程式 UI 並將事件處理常式綁定到 UI 上的控制項。 要注意的重要內容是 jQuery 代碼,該代碼綁定 Populate 按鈕按一下事件的事件處理常式,如圖 11 所示。

圖 11 綁定 Populate 按鈕按一下事件的事件處理常式

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
  });
  var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
  _document.customXmlParts.addAsync(xml, function (result) { });
});

實際上,充當 Populate 按鈕按一下事件的事件處理常式的匿名函數將一個訂單物件(是用 setupMyOrders 函數創建的)轉換為一個 XML 字串,然後調用 customXmlParts.addAsync 方法將包含訂單資訊的該 XML 字串傳遞為第一個必需參數。

customXml­Parts.addAsync 的另一個參數是回呼函數。 當然,這可以是對在代碼其他位置定義的方法的引用,也可以是匿名函數。 發票管理器示例使用內聯匿名函數:

_document.customXmlParts.addAsync(xml,
  function (result) { });

對於 JAVA­Script API for Office 中的所有回檔都是如此,AsyncResult 物件將作為回檔的唯一參數傳入。 對於 customXmlParts.addAsync 和所有 customXmlParts 函數,可以使用 AsyncResult 物件實現以下目的:

  • 使用 Async­Result.value 屬性獲取對新創建的自訂 XML 部件的引用
  • 使用 AsyncResult.status 屬性獲取請求的結果
  • 使用 Async­Result.error 屬性獲取有關錯誤(如果出現)的資訊
  • 使用 AsyncResult.asyncCoNtext 屬性獲取你自己的狀態資料(如果在對 custom­XmlParts.addAsync 的調用中包含任意狀態資料)

對於最後一項,請注意,customXmlParts.addAsync 方法中的另一個參數是一個可選的 options 物件:

Office.context.document.customXmlParts.addAsync(
  xml [, options], callback);

通過提供的 options 物件,可以將你自己的使用者定義物件傳入回檔的調用中。

如發票管理器示例所示,對 customXmlParts.addAsync 的調用中的匿名函數不執行任何操作,但在生產環境中,可能希望執行錯誤檢查,以便在自訂 XML 部件因某種原因未成功添加時正常處理實例。

CustomXmlParts.getByNamespaceAsync 如發票管理器示例所示,通過 JavaScript API for Office 使用自訂 XML 部件的另一個重要內容是使用 customXmlParts.getByNamespaceAsync 方法,在 Populate 按鈕的按一下事件處理常式代碼中可以查看該方法。 customXmlParts.getByNamespaceAsync 的簽名如下:

Office.context.document.customXmlParts.getByNamespaceAsync(
  ns [, options], callback);

第一個必需參數 ns 是一個字串,它指定要獲取的自訂 XML 部件的命名空間。 因此,customXmlParts.getByNamespaceAsync 返回指定了命名空間的文檔中的自訂 XML 部件陣列。 因為發票管理器示例中創建的自訂 XML 部件不使用命名空間,所以對 customXmlParts.getByNamespaceAsync 的調用會傳入一個空字串作為命名空間形參的實參,如圖 12 所示。

圖 12 使用方法 CustomXmlParts.getByNamespaceAsync

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
                    }
     });
     var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
     _document.customXmlParts.addAsync(xml, function (result) { });
   });
   var selOrder = $("#orders option:selected");
   popOrder(selOrder.val());

與 API 中的所有非同步函數一樣,customXmlParts.getByNamespaceAsync 也有可選的 options 和 callback 參數。

CustomXmlParts.getByIdAsync 最後一種在文檔中獲取自訂 XML 部件的程式設計方式是使用 customXmlParts.getByIdAsync。 簽名如下:

Office.context.document.customXmlParts.getByIdAsync(id [, options], callback);

此函數使用部件的 GUID 獲取單個自訂 XML 部件。 在文檔包的 itemPropsn.xml 檔中可以找到自訂 XML 部件的 GUID。 也可以使用 customXmlPart 的 id 屬性獲取 GUID。 這裡要注意的一個要點是,GUID 的字串必須用大括弧 (「{}」) 將 GUID 括起。

發票管理器示例不使用 customXml­Parts.getByIdAsync 函數,以下代碼清楚地說明了這一點:

function showXMLPartBuiltId() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    write(xmlPart.id);
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

與 customXmlParts.addAsync 和 customXmlParts.getByNamespaceAsync 一樣,除了 id 參數以外,customXml­Parts.getByIdAsync 方法也有可選參數 options 和必需參數 callback,它們的用法與在其他函數中的一樣。

CustomXmlPart 物件 customXmlPart 物件表示單個自訂 XML 部件。 使用 customXmlParts 物件的方法獲取對 customXmlPart 的引用後,有多個屬性可用,如圖 13 所示。

圖 13 CustomXmlPart 屬性

「名稱」 說明
builtIn 獲取一個值,該值指示 customXmlPart 是否為內置部件。
Id 獲取 customXmlPart 的 GUID。
namespaceManager 獲取對當前 customXmlPart 使用的命名空間首碼映射集 (customXmlPrefixMappings)。

CustomXmlPart 也有關聯的事件,如圖 14 所示。

圖 14 CustomXmlPart 事件

「名稱」 說明
nodeDeleted 在刪除節點時發生。
nodeInserted 在插入節點時發生。
nodeReplaced 在替換節點時發生。

但就本文而言,我們要重點介紹 customXmlPart 物件的幾個開發者常用重要方法。 如图 15 所示。

圖 15 CustomXmlPart 方法

「名稱」 說明
addHandlerAsync 以非同步方式為 customXmlPart 物件事件添加事件處理常式。
deleteAsync 以非同步方式從集合中刪除此自訂 XML 部件。
getNodesAsync 以非同步方式獲取此自訂 XML 部件中與指定 XPath 匹配的任何 customXmlNodes。
getXmlAsync 以非同步方式獲取此自訂 XML 部件中的 XML。

CustomXMLPart.addHandlerAsync customXmlPart.add­HandlerAsync 方法對於綁定用於回應自訂 XML 部件更改的事件處理常式十分重要。 customXmlPart.addHanderAsync 方法的簽名如下:

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

請注意,第一個必需參數是 Office.EventType 枚舉,該枚舉指定要處理 Office 應用程式物件模型中的哪種事件。 下一個必需參數是事件的處理常式。 此處重要的一點在於,在調用處理程式時,JavaScript API for Office 將傳入特定于所處理事件種類的事件參數(NodeDeletedEventArgs、NodeInsertedEventArgs 或 NodeReplacedEventArgs)。 這樣,就像在 API 的所有非同步函數中一樣,也可以選擇包含 options 和 callback 參數。

考慮這樣一種情況,文檔像資料輸入表單一樣使用。 使用者將資料輸入表單,然後表單選取資料。 表單包含一個重複節內容控制項,以便使用者每次輸入重複項時,都會向基礎自訂 XML 部件添加一個新節點。 每次添加或插入節點時,都會觸發 NodeInserted 事件,你可以使用 customXmlPart.addHandlerAsync 回應事件(及所有 customXmlPart 事件)。

圖 16 顯示如何回應 NodeInserted 事件。

圖 16 綁定 CustomXmlPart.NodeInserted 事件的事件處理常式

function addNodeInsertedEvent() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    xmlPart.addHandlerAsync(Office.EventType.NodeInserted,
      function (eventArgs) {
        write("A node has been inserted.");
    });
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

CustomXMLPart.deleteAsync 當然,除了知道如何添加自訂 XML 部件以外,知道如何刪除也很重要。 customXmlPart.deleteAsync 方法提供了這一功能。 CustomXmlPart.deleteAsync 是非同步函數,簽名如下:

customXmlPart.deleteAsync([options ,] callback);

回到發票管理器示例中,可以看到 customXMLPart.deleteAsync 的演示過程:

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
});

在 Populate 按鈕的按一下事件處理常式中,程式邏輯檢查是否存在任何命名空間為空白的自訂 XML 部件。 如果存在,則使用 custom­XmlPart.deleteAsync 方法全部刪除。

有關使用自訂 XML 部件的知識還有很多,不過通過本文介紹的內容,你可以初步認識 JavaScript API for Office 為自訂 XML 部件提供的豐富支援功能。

接下來:郵件應用程式

本文是系列文章的第三篇,回顧了在 Office 應用程式中使用資料的一些高級方法。 介紹了如何通過資料繫結向 Excel 中的表添加其他智慧。 此外,還探討了如何在 Word 應用程式中利用自訂 XML 部件輕鬆實現自動文檔創建。

在本系列的下一篇和最後一篇文章中,將介紹將 JavaScript API for Office 應用於郵件應用程式。 郵件應用程式代表 JavaScript API for Office 中的一組獨特功能,應用程式開發者和 Exchange 管理員可通過這些功能構建功能強大的工具來處理電子郵件。

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

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

衷心感謝以下技術專家對本文的審閱: Mark Brewster (Microsoft), Shilpa Kothari (Microsoft) and Juan Balmori Labra (Microsoft)
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 擔任過公共部門諮詢事務的首席架構師。