SharePoint向けExcelエクスポートの改善

SharePointのExcelへの標準エクスポートオプションは、かなり珍しい方法で機能します。 エクスポートボタンをクリックすると、SharePointはExcelが開く特別な形式の要求ファイルを提供し、Excel自体がデータをドラッグします。

このアプローチの利点は、接続があるためExcelのデータを更新できることです。 しかし、さらに不利な点があります:


少量のコードで、ユーザーが何にも気付かないように、標準のエクスポート機能を独自のものに置き換えることができます。


エクスポートボタンの置換


リボンの既存の要素を置き換えるには、既存の要素のIDと等しいLocationパラメーターを使用して、新しいCommandUIDefinitionを追加する必要があります。 すべての標準要素は、ファイルC:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\ 14 \TEMPLATE\GLOBAL\XML\CMDUI.XMLにありますC:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\ 14 \TEMPLATE\GLOBAL\XML\CMDUI.XML for SharePoint 2010(または15 for SharePoint 2013)。

Excelのエクスポートボタンを置き換えるには、IdがRibbon.List.Actions.ExportToSpreadsheetおよびRibbon.Library.Actions.ExportToSpreadsheetと等しいボタンを見つけて、プロジェクトに完全にコピーする必要があります。 新しい要素のLocationとして、これらのIDを指定する必要があります。

コピーペーストがたくさん
 <CommandUIDefinitions> <CommandUIDefinition Location="Ribbon.Library.Actions.ExportToSpreadsheet"> <Button Id="Ribbon.Library.Actions.ExportToSpreadsheet-Replacement" Sequence="40" Command="ExportToSpreadsheet-Replacement" Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-152" Image16by16Left="-32" Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-352" Image32by32Left="0" LabelText="$Resources:core,cui_ButExportToSpreadsheet;" ToolTipTitle="$Resources:core,cui_ButExportToSpreadsheet;" ToolTipDescription="$Resources:core,cui_STT_ButExportListToSpreadsheet;" TemplateAlias="o2" /> </CommandUIDefinition> <CommandUIDefinition Location="Ribbon.List.Actions.ExportToSpreadsheet"> <Button Id="Ribbon.List.Actions.ExportToSpreadsheet-Replacement" Sequence="40" Command="ExportToSpreadsheet-Replacement" Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-152" Image16by16Left="-32" Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-352" Image32by32Left="0" LabelText="$Resources:core,cui_ButExportToSpreadsheet;" ToolTipTitle="$Resources:core,cui_ButExportToSpreadsheet;" ToolTipDescription="$Resources:core,cui_STT_ButExportListToSpreadsheet;" TemplateAlias="o1" /> </CommandUIDefinition> </CommandUIDefinitions> 


Id="Ribbon.Calendar.Calendar.Actions.ExportToSpreadsheet"カレンダー用の別のボタンもあり、同じことを行うことができます。

コントロールを作成する


ボタンはサーバーコードを呼び出す必要があります。 動作を標準ボタンに似せるには 、各ページに配置されるDelegateControlを作成するのが最善です。 Id="AdditionalPageHead"コンテナがあります:
 <Control Id="AdditionalPageHead" Sequence="1000" ControlAssembly="$SharePoint.Project.AssemblyFullName$" ControlClass="$SharePoint.Type.7fd7c6f0-4eda-48ce-ac8f-aa9f9d2666ac.FullName$"/> 

カスタムボタンは、コントロールによって処理されるPostBackを呼び出します。 リストビューページでボタンをクリックすると、追加のパラメータを渡す必要はありません。 また、リストビューをWebパーツとして通常のページに追加した後、リストIDとビューをサーバーに転送する必要があります。

残念ながら、ボタンがクリックされたビューのIDを取得するのは簡単なことではありません。 したがって、リストIDを渡すことのみに制限します。

 <CommandUIHandlers> <CommandUIHandler Command="ExportToSpreadsheet-Replacement" CommandAction="javascript:(function(){var x=SP.ListOperation.Selection.getSelectedList(); if (x) {__doPostBack('ExportToSpreadsheet-Replacement', x);}})();" /> </CommandUIHandlers> 


次に、サーバーで、リストビューページまたは通常のページでボタンが押されているかどうかを確認する必要があります。

 protected override void OnLoad(EventArgs e) { if (this.Page.Request["__EVENTTARGET"] == "ExportToSpreadsheet-Replacement") { var spContext = SPContext.Current; SPList list; SPView view; if (spContext.ViewContext.View != null) { list = spContext.List; view = spContext.ViewContext.View; } else { var listId = new Guid(this.Page.Request["__EVENTARGUMENT"]); var web = spContext.Web; list = web.Lists[listId]; view = list.DefaultView; } ExportData(list.Title + " - " + view.Title, GetDataTable(list, view)); } } 


ビューデータテーブルの取得は非常に簡単です。

 private static System.Data.DataTable GetDataTable(SPList list, SPView view) { var query = new SPQuery(view); SPListItemCollectionPosition position; var flags = SPListGetDataTableOptions.UseBooleanDataType | SPListGetDataTableOptions.UseCalculatedDataType; var result = list.GetDataTable(query, flags, out position); while (position != null) { list.AppendDataTable(query, flags, result, out position); } return result; } 


Excelファイルの生成



最後の手順は、Excelファイルを作成してクライアントに提供することです。 Excelを生成する最も簡単な方法の1つは、ClosedXmlライブラリ( http://closedxml.codeplex.com/ )を使用することです。

 private void ExportData(string title, System.Data.DataTable table) { var wb = new XLWorkbook(); var ws = wb.Worksheets.Add(title); ws.Cell(1, 1).InsertTable(table); var response = this.Page.Response; response.Clear(); response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; var filename = title+".xlsx"; response.AddHeader("content-disposition", GetContentDisposition(filename)); // Flush the workbook to the Response.OutputStream using (var memoryStream = new MemoryStream()) { wb.SaveAs(memoryStream); memoryStream.WriteTo(response.OutputStream); } response.End(); } 


メソッドの最初の3行は実際にExcelドキュメントを形成し(ClosedXmlのおかげです)、他のすべてはクライアントにファイルを送信するためのコードです。

content-disposition応答ヘッダーは、ブラウザごとに非常に異なって認識されるため、正しいロシア名のファイルを指定するには、タンバリンで踊る必要があります。

StackOverflowで正しいcontent-dispositionヘッダーを生成するコードを見つけましたstackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http

 private string GetContentDisposition(string filename) { var request = this.Page.Request; string contentDisposition; if (request.Browser.Browser == "IE" && (request.Browser.Version == "7.0" || request.Browser.Version == "8.0")) contentDisposition = "attachment; filename=" + Uri.EscapeDataString(filename); else if (request.UserAgent != null && request.UserAgent.ToLowerInvariant().Contains("android")) // android built-in download manager (all browsers on android) contentDisposition = "attachment; filename=\"" + MakeAndroidSafeFileName(filename) + "\""; else contentDisposition = "attachment; filename=\"" + filename + "\"; filename*=UTF-8''" + Uri.EscapeDataString(filename); return contentDisposition; } private static readonly Dictionary<char, char> AndroidAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~'=()[]{}0123456789".ToDictionary(c => c); private string MakeAndroidSafeFileName(string fileName) { char[] newFileName = fileName.ToCharArray(); for (int i = 0; i < newFileName.Length; i++) { if (!AndroidAllowedChars.ContainsKey(newFileName[i])) newFileName[i] = '_'; } return new string(newFileName); } 


おわりに


すべてが実稼働環境で機能するには、ClosedXml.dllファイルとDocumentFormat.OpenXml.dllファイルをパッケージに追加する必要があります。 リボンとコントロールボタンは、サイトレベルまたはWebレベルの1つの機能に配置する必要があります。

プロジェクト全体はリンクで見ることができます-spsamples.codeplex.com/SourceControl/latest#ExportToExcel

準備ができたWSPファイルはこちら-spsamples.codeplex.com/releases/view/117220

ほとんどのコードはユニバーサルであり、SharePointに依存しません。 ASP.NETプロジェクトでも同様のアプローチを使用できます。

Source: https://habr.com/ru/post/J208948/


All Articles