本文章是由機器翻譯。

SharePoint 與 Open XML

從 SharePoint 產生文件,與開啟的 XML 內容控制項

Eric White

下載程式碼範例

它通常是的這樣部門經理需要定期傳送一份精美的格式狀態報表給她的一般經理或小組領導人要傳送每週狀態報告給人士數。 若要在組織中共同作業與其他人,管理員和小組領導人可以維護 SharePoint 清單中的狀態資訊。 開發人員的問題是如何在一份狀態報表文件中的清單中加入資訊。

開啟 XML,Office 2007,) 的預設檔案格式是一種 ISO 標準,(exacting IS29500 詳細說明)。 將只,開啟 XML 檔案是 Zip 檔案,包含 XML,而且很容易產生,或以程式設計方式修改開啟的 XML 文件。 您只需要為程式庫以開啟 [Zip 檔案及 XML API。 使用 [開啟 XML] 和 [SharePoint 的程式設計功能,您可以將一起小的文件產生的系統,利用 Open XML 內容控制項,並將管理員和小組領導人,其報告的負責人員。 此的文件中我將提供一些指引和範例程式碼建立文件產生的系統,會使用 SharePoint 清單來填入開啟 XML 文字處理文件中的資料表。

此範例的概觀

本文中的範例就是小型的 SharePoint 網頁組件產生開啟 XML 文字處理文件從居住在 SharePoint 清單的資料。 我已經建立了兩個自訂 SharePoint 清單,圖 1 所示包含我們想要插入資料表的資料。


圖 1 兩個自訂 SharePoint 清單

我也建立了文字範本開啟 XML 處理文件包含定義清單和產生的文件的資料來源的資料行的內容控制項。 控制項以的圖 2 所示。


圖 2 的範本開啟 XML 文件,包含內容控制項

最後,建立網頁組件擷取特定的文件庫的範本文件的清單,並對使用者顯示清單。 使用者選取的項目清單中,並再按一下 [產生報表] 按鈕。 [網頁組件建立開啟 XML 文字處理文件、 將它放在 [報表] 文件庫並,如此她可以開啟報表,將使用者重新導向至該程式庫。 網頁組件會顯示在圖 3,並的 圖 4] 所示就會產生文件。


圖 3 的 SharePoint 網頁組件,讓使用者選取一個範本文件

開啟的 XML 內容控制項

之前描述 SharePoint 解決方案,我會討論 Open XML 內容控制項的基本概念。 開啟的 XML 內容控制項提供一個功能文書處理文件中,可讓您描述內容和關聯的內容中的中繼資料。 若要將內容控制項,您必須啟用在 Microsoft Office Word 2007 中的開發人員] 索引標籤。 (按一下 [Word 選項,] [Office] 功能表中,然後,在 [Word 選項] 對話方塊選取功能區中顯示開發 o 人 h 員 û 工 u 具 ã 索引標籤)。

若要將內容控制項選取一些文字,然後按一下 [開發 o 人 h 員 û 工 u 具 ã] 索引標籤,可以建立純文字內容控制項,的 [圖 5] 所示的 [控制項] 區域中的按鈕]。


圖 4 文的 Open XML Word-Processing 件,以包含產生的報告


圖 5 使用這個按鈕來建立純文字內容控制項

您可以設定內容,讓它的標題,並將它指派標記控制項的屬性。 按一下的內容控制項,然後按一下在 [開發 o 人 h 員 û 工 u 具 ã] 索引標籤上的 [控制項] 區域中的 [屬性] 按鈕。 這會顯示的對話方塊可用來設定標題] 和 [標記]。

內容控制項的圖 6] 所示的 Open XML 標記中,使用 w:sdt 項目。 內容控制項的內容被定義在 w:sdtContent 項目。 在圖中,您也可以查看 w:alias 項目中內容的控制項和內容控制項標記 w:tag 項目中的標題。

開啟 XML 使用.NET Framework 的程式設計

您可以採取各種不同的方法進行程式設計的 Open XML 使用在 .NET Framework:

  • 使用類別中 System.IO.Packaging
  • 使用 XML 程式設計技術在.NET,包括 XmlDocument、 XmlParser 或 LINQ to XML 中的任何開啟的 XML SDK。 我最愛的網頁是 LINQ to XML。
  • 使用開啟的 XML SDK 版本 2.0 的強型別的物件模型。 您可以找到介紹如何使用這個物件模型設計程式的文件的一個數的字。

這裡,我要使用開啟的 XML SDK (任一版本 1.0 或 2.0) 與 LINQ to XML。 您可以從 go.microsoft.com/fwlink/?LinkId=127912 下載開啟的 XML SDK。

很有用來封裝 ContentControlManager 類別中的內容控制項周圍的一些開啟 XML] 功能。 當您處理這種方式問題時,您可以開發簡單的主控台應用程式中您開啟 XML 的功能。 在撰寫程式碼和偵錯您開啟 XML] 功能之後,您可以再將它在 SharePoint 功能中,最少的工作。 這是相當耗時,會造成部署 「 SharePoint 功能偵錯 Open XML 程式碼時的額外負荷。

我們的 SharePoint 文件產生範例我們要撰寫程式碼擷取範本文件從特定文件庫文件的內容控制項,它包含的查詢,使用中繼資料儲存在每個內容控制項填入適當的 SharePoint 清單中的資料文件。

如果您下載範例並檢查範本開啟 XML 文件時,您會看到包含每個資料表的內容控制項,以及內容控制項會插入下方列的每個資料表中每個儲存格。 針對每個表格儲存格中的內容控制項標記指定 SharePoint 清單中的資料行的名稱。 為了方便起見,我已經也設定每個內容控制項的標題與標籤相同的值。 控制項內插入點時,內容控制項便會顯示其標題。

當您在撰寫程式碼會產生開啟 XML 文件的 SharePoint 功能時程式碼應該先查詢這些內容的控制項的文件。 查詢會傳回描述結構的內容控制項的每個標籤一起 XML 樹狀目錄。 如果您在範例文件上執行此程式碼會產生下列 XML:

<ContentControls>

  <Table Name="Team Members">

    <Field Name="TeamMemberName" />

    <Field Name="Role" />

  </Table>

  <Table Name="Item List">

    <Field Name="ItemName" />

    <Field Name="Description" />

    <Field Name="EstimatedHours" />

    <Field Name="AssignedTo" />

  </Table>

</ContentControls>

這個 XML 文件顯示 SharePoint 列出我們的程式碼需要查詢。 清單中每個項目您需要擷取指定的資料行的值。 撰寫程式碼來查詢開啟 XML 文書處理文,圖 7 所示為一個 LINQ to XML 使用功能建構來形成傳回的 XML 的查詢。

若要將功能的建構程式碼產生 XElement 物件使用它的建構函式,傳遞一個 LINQ to XML 查詢做為建構函式引數。 的 LINQ to XML 查詢使用座標軸方法來擷取文件本文中適當的項目,並使用 Enumerable.Select 延伸方法形成新的 XML,從查詢的結果。 函數建構需要一些研究,若要了解,但是如您所見,您包裝您它的周圍的標頭之後,您可以執行的只是一些程式碼的好很多。

圖 6 開啟 XML 標記的內容控制項的

<w:p>

<w:r>

<w:t xml:space="preserve">Not in content control. </w:t>

</w:r>

<w:sdt>

<w:sdtPr>

<w:alias w:val="Test"/>

<w:tag w:val="Test"/>

<w:id w:val="5118254"/>

<w:placeholder>

<w:docPart w:val="DefaultPlaceholder_22675703"/>

</w:placeholder>

</w:sdtPr>

<w:sdtContent>

<w:r>

<w:t>This is text in content control.</w:t>

</w:r>

</w:sdtContent>

</w:sdt>

<w:r>

<w:t xml:space="preserve"> Not in content control.</w:t>

</w:r>

</w:p>

preatomization 的 XName 和 XNamespace 物件

圖 7 中的程式碼使用稱為 「 preatomization 」 的方法,LINQ to XML 名稱中。 這是只是美觀達到您撰寫靜態類別的方法 (請參閱的圖 8]) 包含限定的項目與您正在使用的屬性名稱會初始化的靜態欄位。

圖 7 擷取範本文件內容的控制項的 [結構

public static XElement GetContentControls(

WordprocessingDocument document)

{

XElement contentControls = new XElement("ContentControls",

document

.MainDocumentPart

.GetXDocument()

.Root

.Element(W.body)

.Elements(W.sdt)

.Select(tableContentControl =>

new XElement("Table",

new XAttribute("Name", (string)tableContentControl

.Element(W.sdtPr).Element(W.tag).Attribute(

W.val)),

tableContentControl

.Descendants(W.sdt)

.Select(fieldContentControl =>

new XElement("Field",

new XAttribute("Name",

(string)fieldContentControl

.Element(W.sdtPr)

.Element(W.tag)

.Attribute(W.val)

)

)

)

)

)

);

return contentControls;

}

沒有初始化 XName 和 XNamespace 的物件,以這種方式的好理由。 LINQ to XML 摘要的 XML 名稱和命名空間分成兩個類別:System.Xml.Linq.XName 和 System.Xml.Linq.XNamespace,分別。 這些類別的語意包括概念若 XNames 兩個具有相同限定的名稱 (命名空間 + 區域名稱),它們將會以相同的物件。 這可讓 XName 物件的快速的比較。 而非使用字串比較,若要選取的指定名稱的 XElement 物件,程式碼必須僅要比較的物件。 當您初始化 XName 物件時,LINQ to XML 會先尋找在判斷相同的命名空間和名稱的 XName 物件是否已經存在快取中。 如果有,物件會初始化為現有的 XName 物件從快取。 如果一個沒有 LINQ to XML 就會初始化一個新,並將它加入至快取。 如您可能想,如果一再重複執行此程序,它會導致效能問題。 初始化靜態類別中的這些物件,工作完成一次。 在就另外藉由使用這項技術您降低可能性的項目或屬性名稱拼錯了本文中的程式碼。 其中一個其他的優點是利用這項技術您支援從取得 IntelliSense 可讓您撰寫使用 LINQ to XML 更容易的 Open XML 程式。

圖 8 A 靜態類別包含靜態欄位 Preatomize XName] 和 [XNamespace 物件

public static class W

{

public static XNamespace w =

"http://schemas.openxmlformats.org/wordprocessingml/2006/main";

public static XName body = w + "body";

public static XName sdt = w + "sdt";

public static XName sdtPr = w + "sdtPr";

public static XName tag = w + "tag";

public static XName val = w + "val";

public static XName sdtContent = w + "sdtContent";

public static XName tbl = w + "tbl";

public static XName tr = w + "tr";

public static XName tc = w + "tc";

public static XName p = w + "p";

public static XName r = w + "r";

public static XName t = w + "t";

public static XName rPr = w + "rPr";

public static XName highlight = w + "highlight";

public static XName pPr = w + "pPr";

public static XName color = w + "color";

public static XName sz = w + "sz";

public static XName szCs = w + "szCs";

}

GetXDocument 和 PutXDocument 的擴充方法

這份文件中顯示此範例也會使用另一個小技巧來簡化程式設計並改善效能。 開啟的 XML SDK 能夠放在文件中的組件中的註解。 這表示您可以附加任何 .NET Framework OpenXmlPart 物件的物件,並稍後擷取它,藉由指定您所連接的物件的型別。

我們可以定義兩個延伸方法,GetXDocument 和 PutXDocument,使用最小化開啟 XML 組件從 XML 的還原序列化的註解。 當我們呼叫 GetXDocument 時,它首先會檢查是否 XDocument 存在於 [OpenXmlPart 的型別附註。 如果註解 GetXDocument 傳回。 如果沒有註解,方法填入從組件的 XDocument、 annotates 部分,然後傳回新 XDocument。

PutXDocument 延伸方法也會檢查是否有型別 XDocument 註解。 如果此附註 PutXDocument 寫入 (假設修改程式碼會呼叫 GetXDocument 之後),XDocument 回 [OpenXMLPart。 的圖 9] 所示 GetXDocument 和 PutXDocument 的延伸方法。 您可以看到 GetXDocument 延伸方法在的圖 7 中先前列出的 GetContentControls 方法中使用。

圖 9 註延伸方法使用開啟 XML SDK 釋到最小化還原序列化的 XML

public static class AssembleDocumentLocalExtensions

{

public static XDocument GetXDocument(this OpenXmlPart part)

{

XDocument xdoc = part.Annotation<XDocument>();

if (xdoc != null)

return xdoc;

using (Stream str = part.GetStream())

using (StreamReader streamReader = new StreamReader(str))

using (XmlReader xr = XmlReader.Create(streamReader))

xdoc = XDocument.Load(xr);

part.AddAnnotation(xdoc);

return xdoc;

}

public static void PutXDocument(this OpenXmlPart part)

{

XDocument xdoc = part.GetXDocument();

if (xdoc != null)

{

// Serialize the XDocument object back to the package.

using (XmlWriter xw =

XmlWriter.Create(part.GetStream

(FileMode.Create, FileAccess.Write)))

{

xdoc.Save(xw);

}

}

}

}

內容控制項取代資料

現在我們有方法,傳回表格及儲存格的內容控制項結構,我們需要方法 (SetContentControls) 建立特定的資料 (從 SharePoint 清單擷取) 資料表中插入開啟的 XML 文件。 我們可以定義這個方法,才能做為引數的 XML 樹狀目錄。 圖 10所示 XML 樹狀結構,並的圖 11 顯示 SetContentControls 時它會傳遞 XML 樹狀結構所建立之文件。

圖 10**,Word-Processing 的文件表格** 中包含插入資料的 XML 樹狀結構

<ContentControls>

<Table Name="Team Members">

<Field Name="TeamMemberName" />

<Field Name="Role" />

<Row>

<Field Name="TeamMemberName" Value="Bob" />

<Field Name="Role" Value="Developer" />

</Row>

<Row>

<Field Name="TeamMemberName" Value="Susan" />

<Field Name="Role" Value="Program Manager" />

</Row>

<Row>

<Field Name="TeamMemberName" Value="Jack" />

<Field Name="Role" Value="Test" />

</Row>

</Table>

<Table Name="Item List">

<Field Name="ItemName" />

<Field Name="Description" />

<Field Name="EstimatedHours" />

<Field Name="AssignedTo" />

<Row>

<Field Name="ItemName" Value="Learn SharePoint 2010" />

<Field Name="Description" Value="This should be fun!" />

<Field Name="EstimatedHours" Value="80" />

<Field Name="AssignedTo" Value=”All” />

</Row>

<Row>

<Field Name="ItemName" Value=

"Finalize Import Module Specification" />

<Field Name="Description" Value="Make sure to handle all document

formats." />

<Field Name="EstimatedHours" Value="35" />

<Field Name="AssignedTo" Value=”Susan" />

</Row>

<Row>

<Field Name="ItemName" Value="Write Test Plan" />

<Field Name=”Description" Value=

"Include regression testing items." />

<Field Name="EstimatedHours" Value="20" />

<Field Name="AssignedTo" Value="Jack" />

</Row>

</Table>

</ContentControls>


圖 11 上的 [產生的文件]

您可以看到包含內容控制項的單一資料列已經由取代多個的資料列包含資料的 XML 樹狀目錄中已做為引數傳遞給方法的每個。 資料傳送到程式碼的處理開啟的 XML 標記中使用 XML 樹狀目錄,您就可以得到使用 SharePoint 物件模型程式碼] 和 [開啟 XML 程式碼好區隔。

程式碼組合新文件會接受任何您已套用到表格的格式。 就例如如果您已設定 [] 表格可顯示的替代資料列,不同的色彩,或是如果您已經設定資料行的背景色彩,新產生的文件會反映出您的格式變更。

如果您下載,並檢查 ContentControlManager 範例,您可以看到程式碼取得一份包含內容控制項,並且將它儲存作為原型列之資料列:

// Determine the element for the row that contains the content controls.

// This is the prototype for the rows that the code will generate from data.

XElement prototypeRow = tableContentControl

    .Descendants(W.sdt)

    .Ancestors(W.tr)

    .FirstOrDefault();

程式然後,為每個從 SharePoint 清單擷取的項目,碼複製原型資料列、 改變使用 [SharePoint] 清單中的資料複製資料列並將它加入至集合,會插入到文件。

建立新的資料列的清單之後,程式碼從清單中移除 [原型] 列中,並插入新建立的資料列的集合,如下所示也一樣:

XElement tableElement = prototypeRow.Ancestors(W.tbl).First();

prototypeRow.Remove();

tableElement.Add(newRows);

建立 「 SharePoint 功能

我使用 2 月 2009 CTP 版本的 Windows SharePoint Services 3.0,v1.3 建置本範例在 Visual Studio 2008 的副檔名。 我已經建立,WSS 的 32 位元和 64 位元版本上執行這個範例。 (吳 Evans 有一些好 網路廣播,顯示如何使用這些擴充功能)。

這個範例包含程式碼以建立網頁組件控制項。 程式碼是如果您習慣建立 SharePoint Web 組件很自我闡明的。 當使用者按一下 [產生報表] 按鈕時,程式碼呼叫此 CreateReport] 方法會從範本的文件的新的 Open XML 文字處理文件組合利用在 SharePoint 中的資料的清單在標籤內容 controls.There 的設定是幾個要注意 CreateReport 方法的程式碼。 在 SharePoint 中文件庫中的檔案會以位元組陣列傳回。 您要將此位元組陣列轉換成記憶體資料流,,讓您可以開啟和修改使用開啟 XML SDK 文件。 MemoryStream 建構函式的其中一個所需的位元組陣列,而且您可能想要使用的建構函式。 但是,記憶體資料流建立的建構函式不 nonresizable 記憶體資料流,且開啟的 XML SDK 需要記憶體資料流是可調整大小。 解決方案是以預設建構函式建立一個 MemoryStream,並再寫入位元組陣列從 SharePoint MemoryStream,圖 12 所示。

圖 12 從 SharePoint 的位元組陣列寫入至 MemoryStream

private ModifyDocumentResults CreateReport(SPFile file, Label message)

{

byte[] byteArray = file.OpenBinary();

using (MemoryStream mem = new MemoryStream())

{

mem.Write(byteArray, 0, (int)byteArray.Length);

try

{

using (WordprocessingDocument wordDoc =

WordprocessingDocument.Open(mem, true))

{

// Get the content control structure from the template

// document.

XElement contentControlStructure =

ContentControlManager.GetContentControls(wordDoc);

// Retrive data from SharePoint,

constructing the XML tree to

// pass to the ContentControlManager.SetContentControls

// method.

...

}

}

}

}

程式碼的其餘部份相當簡單。 它會使用 SharePoint 物件模型來擷取文件庫和程式庫的內容、 擷取清單和擷取的資料行清單中的每個資料列的值。 它會傳遞至 ContentControlManager.SetContentControls,XML 樹狀結構的組合,並接著它會呼叫 SetContentControls。

程式碼就會為報表的 yyyy-公釐-dd.組合產生的報告文件的名稱 如果報表已經存在程式碼會將數字附加至明確其他已產生的樞紐分析報表的報表名稱。 對於執行個體報告-2009年-08-01.docx 已經存在,如果報表是寫入報表-2009年-8-2 (1).docx。

簡單的自訂

您可能會要自訂此範例以符合您自己的需求。 一個可能的增強功能是要讓本文中的範本文件,會在 SharePoint 中儲存指定文件中的重複使用內容中提取內容控制項。 您可以撰寫程式碼,以便您可以將所包含內容控制項中的文字未定案文字的文件的名稱。

而且,此範例硬碟的代碼 [TemplateReports 和報告文件庫的名稱。 您也可以藉由指定這項資訊在 SharePoint 清單中移除此條件約束。 然後程式碼會知道只有這個設定清單的名稱。 會由資料從您的設定清單驅動 [TemplateReports 和報告文件庫的名稱。

SharePoint 是一種功能強大的技術,方便在組織中的其他人共同作業。 開啟的 XML 是一種功能強大的新興技術會變更我們就會產生文件的方式。 同時使用兩種技術可讓您建置應用程式的人員可以利用文件共同作業的新方法。

Eric White 在 Microsoft 寫入器 Office Open XML 檔案格式,Office 和 SharePoint 珍貴。 之前加入 Microsoft 在 2005年中,他的年數,身為開發人員工作,並再啟動 PowerVista 軟體,開發並銷售跨平台格線 Widget 公司。 他具有自訂控制項和 GDI+ 開發在撰寫活頁簿。 讀取他的部落格在 blogs.msdn.com/ericwhite