平常開發時,或多或少都需要和Word打交道,特變是編輯、導出Word。
利用DocX,開源的讀寫Word組件,可以快速幫助我們進行對Word的操作。
DocX官方網站:http://docx.codeplex.com/
DocX主要功能
- 在文檔中(Word)插入,刪除或者替換文本,支持所有的標准文本格式,如字體{Family,Size,Color},出體,斜體、下划線、高亮等。
- 提供段落屬性,你可以設置其對其方向,如從左到右,居中對齊等。
- DocX同樣支持對圖片的操作、超鏈接、表格、頁首、頁眉等。
- 最重要的一點DocX支持自定義文檔屬性(Custom Properties)
最近要對一個SharePoint項目進行修改,客戶希望對上報的文檔審批結束后(如下圖),可以導出Word,方便打印。對於OA系統而言,這是很重要的功能,客戶催着急,所以需要快速開發。
創建文檔屬性
DocX支持Load一個事先預定好的模版,通過對模版的修改即可創建出新的DocX類型對象,直接調用DocX.SaveAs方法即可對其進行保存到指定路徑,當然你也可以保存到一個內存流中。具體的強大功能,可以參考DocX提供Example,寫的非常詳細。
廢話少說,我們開始吧。Word2010(07不確定,沒用過)以上版本支持文檔屬性(Document Propery),文檔屬性對開發者來講是一個非常重要的功能,你可以擴展Word文檔的屬性,我以Word 2013為例,打開Word 2013,單擊左上角的文件,在新彈出的頁面,選擇屬性à高級屬性,如下所示:
接着,新建文檔屬性,注意取一個合適的名稱,別與已存在的屬性重名,對於上圖介紹的投訴審批,你可以新建以下文檔屬性:
當創建完畢后,插入文檔屬性:切換到插入Tab,找到文檔部件,單擊文檔部件選擇域,再彈出的新窗體中,在左邊域名選擇DocProperty,在右邊找到需要插入的屬性插入到相應位置即可:
這樣可以快速創建一個標准模版,如下圖。接下來的工作就是很簡單了,依次在這幾個“坑”中填充內容即可:
DocX邏輯實現
- ComplaintModel屬性的創建
首先我們約定,屬性值不能包含換行(\r\n),否則插入含有換行符的屬性值文本會和預想的有問題,那我們怎樣去避免這個情況呢?很簡單,假設某個屬性的屬性值包含換行符,我們不將其作為屬性加入文檔屬性,而是直接Replace掉[]。有了這個約定后,我們接着創建我們對象(ComplainModel)的屬性(注意,ComplainModel類型對象的屬性必須要合之前創建的文檔屬性名稱相同,原因稍后解釋)
public class ComplainModel { //標題 public string CTitle { get; set; } //投訴人 public string Complainer { get; set; } //聯系電話 public string Mobile { get; set; } //分類 public string Sort { get; set; } //投訴內容 public string Complain { get; set; } //街道 public string Stretch { get; set; } //備注 public string Remark { get; set; } //一級審批者 public string FirstApprover { get; set; } //一級審批內容 public string FirstApproveText { get; set; } //二級審批者 public string SecondApprover { get; set; } //二級審批內容 public string SecondApproveText { get; set; } //三級審批者 public string ThirdApprover { get; set; } //三級審批內容 public string ThirdApproveText { get; set; } }
- 接下來就是核心步驟了,我們Load預先定義好的Template(如果拋出異常,請加上EveryOne權限,然后去掉只讀)。
DocX gDocument = DocX.Load(@"C:\Users\Administrator\Desktop\投訴審批表.docx");
- 初始化ComplainModel
//創建CustomProperty對象 ComplainModel complainModel = new ComplainModel(); complainModel.CTitle = newItem["Title"].ToString(); complainModel.Complainer = newItem["Complainter"].ToString(); complainModel.Mobile = newItem["Tel"].ToString(); complainModel.Sort = newItem["ComplaintType"].ToString(); complainModel.Complain = newItem["ComplaintContent"].ToString(); complainModel.Stretch = newItem["RoadSelect"].ToString(); complainModel.Remark = newItem["Remark"].ToString(); //審批意見 complainModel.FirstApprover = newItem["FirstApprover"].ToString(); complainModel.FirstApproveText = newItem["FirstApproverText"].ToString(); complainModel.SecondApprover = newItem["SecondApprover"].ToString(); complainModel.SecondApproveText = newItem["SecondApproverText"].ToString(); complainModel.ThirdApprover = newItem["ThirdApprover"].ToString(); //還沒持久化到數據庫,所以直接 txtLevelThreeSuggestion.Text complainModel.ThirdApproveText = txtLevelThreeSuggestion.Text;
- 定義DocXHelper,他提供反射機制,給Load Template創建的新DocX對象添加文檔屬性
public static class DocXHelper { public static void AddCustomProperty<T>(this DocX docx, T source) { Type type=typeof(T); //反射得到指定類型所有的非靜態公開屬性 PropertyInfo[] props = type.GetProperties(BindingFlags.Instance|BindingFlags.Public); foreach (var prop in props) { //得到指定對象屬性值 var value = string.Format("{0}",prop.GetValue(source,null)); //如果包含\r\n,直接無視,不加入文檔屬性中,而是直接Replace if (!value.Contains(Environment.NewLine)) { CustomProperty customProperty = new CustomProperty(prop.Name, value); docx.AddCustomProperty(customProperty); continue; } //把[]替換成指定屬性值 docx.ReplaceText(string.Format("[{0}]",prop.Name),value); } }
正如前面所說的那樣,必須文檔屬性和ComplainModel對象屬性名稱一樣,原因在於docx.AddCustomProperty方法內部(DocX組件是開源的,可以查看AddCustomPropery的實現),幫我們做了如下步驟:首先判斷文檔屬性是否存在,如果是,刪除它(Remove),之后創建一個新的文檔屬性(注意名稱是相同的哦,否則會出現錯誤!未知的文檔屬性名稱,具體可以拿個Word手動刪除文檔屬性后,更新域),最后Update更新域,這樣屬性值就同步到了文檔屬性插入的相應位置了。
- 全部代碼如下
//創建投訴審批docx文檔,以附件形式附加到Attachment欄 DocX gDocument; try { gDocument = DocX.Load(@"C:\Users\Administrator\Desktop\武林管委會\投訴審批表.docx"); //創建CustomProperty對象 ComplainModel complainModel = new ComplainModel(); complainModel.CTitle = newItem["Title"].ToString(); complainModel.Complainer = newItem["Complainter"].ToString(); complainModel.Mobile = newItem["Tel"].ToString(); complainModel.Sort = newItem["ComplaintType"].ToString(); complainModel.Complain = newItem["ComplaintContent"].ToString(); complainModel.Stretch = newItem["RoadSelect"].ToString(); complainModel.Remark = newItem["Remark"].ToString(); //審批意見 complainModel.FirstApprover = newItem["FirstApprover"].ToString(); complainModel.FirstApproveText = newItem["FirstApproverText"].ToString(); complainModel.SecondApprover = newItem["SecondApprover"].ToString(); complainModel.SecondApproveText = newItem["SecondApproverText"].ToString(); complainModel.ThirdApprover = newItem["ThirdApprover"].ToString(); //還沒持久化到數據庫,所以直接 txtLevelThreeSuggestion.Text complainModel.ThirdApproveText = txtLevelThreeSuggestion.Text; //給docx文檔添加CustomProperty對象 gDocument.AddCustomProperty<ComplainModel>(complainModel); //從模版里取出的Docx文件另存為以流的形式 var stream = new System.IO.MemoryStream(); gDocument.SaveAs(stream); //設置stream的位置為0 stream.Position = 0; //將得到的流附加到SharePoint List Attachment欄上 newItem.Attachments.Add("投訴審批表_導出打印.docx",stream.ReadFully()); //最后Update,執行工作流,執行審批歸檔 newItem.Update(); } catch (Exception ex) { //如果模版文檔不存在,或者List Attachment中忘記設置了,那么什么也不發生,也就是沒有生成可以導出的文檔,不影響整個審批過程。 }
查看生成的審批附件
在審批結束歸檔后,即可在附件欄查看到他,相關領導即可下載打印。
- 導出查看Word
總結
DocX是一個非常方便的輕量級開源組件,可以方便操作Word,更強大的功能可以查看DocX Codeplex官網Example,更強大的功能等着你去探索。