グラフィックをMicrosoft Excelにエクスポート

こんにちは、ハブロフチャン。



私はSharePoint開発の初心者であり、今日は興味深い問題の解決についてお話ししたいと思います。 SharePoint用のWebパーツを作成する必要がありました。これは、データを含むテーブルを処理し、画面にグラフを表示し、テーブルとグラフをMicrosoft Excelにエクスポートできる必要があります。 catの下で、私のソリューション、Webパーツコードのスニペット、およびプロジェクトへのリンク。



理論的解決策


1.ページ上のグラフ。


これは、 MS Chart Controlを使用して行うのが最も便利です。 プロセス自体は非常に単純です(もちろん、必要なスケジュールの種類によって異なります)。 唯一の要件:テーブルの最初の列は、グラフのX軸の値です。



2.テーブルをExcelにエクスポートする


かなりの数のエクスポート方法がありますが、私は最も単純な方法を使用することにしました(私の意見では)。 ご存知のように、Office 2007/2010は.docx、.xlsx、pptxファイルにOpen XML形式を使用します。つまり、これらのファイルはXMLファイルを含む単純なZIPアーカイブです。 ZIPアーカイブの操作は簡単で、この方法でデータをXMLファイルに直接書き込むことができます。

しかし、列名でラベルを書くのはそれほど簡単ではないことが判明しました。 Excelはデジタルフィールドを直接sheet1.xmlに書き込み、テキストフィールドをsharedStrings.xmlに書き込み、それらへのリンクのみがsheet1.xmlに移動します。 幸いなことに、テキスト値をxlsxファイルに書き込むことを実装する素晴らしいプロジェクトを見つけました。



3.グラフをExcelにエクスポートする


当然、グラフを含む画像を挿入するだけでは機能せず、実際のExcelグラフである必要があります。 グーグルは価値のあることを何も教えてくれませんでした-それが収まらないか、難しすぎます。 Excelのグラフを少し操作して、興味深い機能に気付きました。グラフが値を取得するデータ領域を設定してからセル値を変更すると、グラフが自動的に変更されます。 アイデアはそれ自身で生まれました-グラフで空のファイルを作成し、それにテーブルを書き込み、XML編集を使用してグラフのデータ領域も指定します(このプロジェクトでは動的テーブルがあり、サイズはコードで計算されました)。



実用的なソリューション


たとえば、タブレット、2つのボタン、およびグラフ(最初は非表示)を含む単純なWebパーツを作成することにしました。 テーブルの列名をエクスポートする必要があるため、最初の行にそれらを入力し、ShowHeaderを無効にする必要があります。

DataTable dt = new DataTable(); protected void Page_Load(object sender, EventArgs e) { for (int i = 0; i < 4;i++ ) dt.Columns.Add(); dt.Rows.Add(new string[] { " ", "", "", "" }); dt.Rows.Add(new string[] { "1", "17", "5", "8" }); dt.Rows.Add(new string[] { "2", "18", "4", "10" }); dt.Rows.Add(new string[] { "3", "15", "6", "9" }); dt.Rows.Add(new string[] { "4", "19", "7", "10" }); dt.Rows.Add(new string[] { "5", "13", "4", "7" }); GridView1.DataSource = dt; GridView1.DataBind(); GridView1.Width = 300; GridView1.ShowHeader = false; }
      
      





次に、グラフを描画します。 データは2行目から始まり、1列目はX軸の値であることを考慮します。

  Series[] series = new Series[dt.Columns.Count - 1]; for (int i = 0; i < series.Length; i++) { series[i] = new Series(dt.Columns[i + 1].ColumnName); series[i].ChartType = SeriesChartType.Column; for (int k = 1; k < dt.Rows.Count; k++) series[i].Points.AddXY((double.Parse((string)dt.Rows[k][0])), double.Parse((string)dt.Rows[k][i + 1])); Chart1.Series.Add(series[i]); } ChartArea chartArea = new ChartArea(); chartArea.AxisX.Minimum = double.Parse(dt.Rows[1][0].ToString()); chartArea.AxisX.Maximum = double.Parse(dt.Rows[dt.Rows.Count - 1][0].ToString()); Chart1.ChartAreas.Add(chartArea); Chart1.Width = 300; Chart1.Visible = true;
      
      





エクスポートを始めましょう。 最初に必要なのは、テンプレート(グラフを含む空のファイル)です。 Excelで新しいブックを作成し、グラフを挿入して、データ領域を示します。 保存して閉じます。 テーブルを固定サイズでない場合は、xmlを使用して直接編集できます(キャッシュされた値を削除します)。次に、数値をデータ領域として置き換えることができます(ごまかしました。次に、これらの数値を必要な数値に置き換えます)。 テンプレートは、Sharepointノードのルートフォルダーにアップロードする必要があります。

上記のプロジェクトを使用して、目的に合わせてファイナライズしています-最初に、テーブルの最初の行(列名)のコピーを作成します。 HashTableを使用して、このテキストデータへのリンクを作成し、データ自体をsharedStrings.xmlに書き込みます。

 public static ArrayList CreateStringTables(DataTable data, out Hashtable lookupTable) { ArrayList stringTable = new ArrayList(); lookupTable = new Hashtable(); foreach (DataRow row in data.Rows) foreach (DataColumn column in data.Columns) if (column.DataType == typeof(string)) { string val = (string)row[column]; if (!lookupTable.Contains(val)) { lookupTable.Add(val, stringTable.Count); stringTable.Add(val); } } return stringTable; } public static void WriteStringTable(Stream output, ArrayList stringTable) { using (XmlTextWriter writer = new XmlTextWriter(output, Encoding.UTF8)) { writer.WriteStartDocument(true); writer.WriteRaw("<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"" + stringTable.Count.ToString() + "\" uniqueCount=\"" + stringTable.Count.ToString() + "\">"); foreach (string str in stringTable) { writer.WriteRaw("<si><t>" + str + "</t></si>"); } writer.WriteRaw("</sst>"); } }
      
      





データテーブルをxml-codeに変換し、同時にテキストデータへのリンクを挿入します。 すべてをsheet1.xmlファイルに書き込みます。

 public static void WriteWorksheetData(XmlTextWriter writer, DataTable dt, Hashtable lookupTable) { int rowsCount = dt.Rows.Count; int columnsCount = dt.Columns.Count; string relPos = RowIndexToName(0); writer.WriteRaw("<row r=\"" + relPos + "\" spans=\"1:" + columnsCount.ToString()+"\">"); for (int column = 0; column < columnsCount; column++) { relPos = RowColumnToPosition(0, column); writer.WriteRaw("<cr=\"" + relPos + "\" t=\"s\">"); string val = lookupTable[dt.Rows[0][column]].ToString(); writer.WriteRaw("<v>" + val + "</v>"); writer.WriteRaw("</c>"); } writer.WriteRaw("</row>"); for (int row = 1; row < rowsCount; row++) { relPos = RowIndexToName(row); writer.WriteRaw("<row r=\"" + relPos + "\" spans=\"1:" + columnsCount.ToString() + "\">"); for (int column = 0; column < columnsCount; column++) { relPos = RowColumnToPosition(row, column); writer.WriteRaw("<cr=\"" + relPos + "\">"); string val = dt.Rows[row][column].ToString(); writer.WriteRaw("<v>" + val + "</v>"); writer.WriteRaw("</c>"); } writer.WriteRaw("</row>"); } } public static void WriteWorksheet(Stream output, DataTable dt, Hashtable lookupTable) { using (XmlTextWriter writer = new XmlTextWriter(output, Encoding.UTF8)) { writer.WriteStartDocument(true); writer.WriteRaw("<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"); string lastCell = RowColumnToPosition(dt.Rows.Count - 1, dt.Columns.Count - 1); writer.WriteRaw("<dimension ref=\"A1:" + lastCell + "\" />"); writer.WriteRaw("<sheetViews>"); writer.WriteRaw("<sheetView tabSelected=\"1\" workbookViewId=\"0\" />"); writer.WriteRaw("</sheetViews>"); writer.WriteRaw("<sheetFormatPr defaultRowHeight=\"15\" />"); writer.WriteRaw("<sheetData>"); WriteWorksheetData(writer, dt, lookupTable); writer.WriteRaw("</sheetData>"); writer.WriteRaw("<pageMargins left=\"0.7\" right=\"0.7\" top=\"0.75\" bottom=\"0.75\" header=\"0.3\" footer=\"0.3\" />"); writer.WriteRaw("<drawing r:id=\"rId1\" />"); writer.WriteRaw("</worksheet>"); } }
      
      





データ領域を変更することを忘れないでください。 最初のプロジェクトでは5列しか持てなかったため、5列のテンプレートを作成し、余分な列を削除しました。 行数は、単にテーブルから計算されます。

 public static void FieldEdit(Stream xmlFile, int rowsCount, int columnsCount) { XmlDocument document = new XmlDocument(); document.Load(xmlFile); XmlNodeList xmlColumns = document.GetElementsByTagName("c:ser"); XmlNode xmlChart = xmlColumns[0].ParentNode; for (int i = xmlColumns.Count - 1; i > columnsCount - 2; i--) xmlColumns[i].ParentNode.RemoveChild(xmlColumns[i]); XmlNodeList xmlRows = document.GetElementsByTagName("c:f"); for (int i = 0; i < xmlRows.Count; i++) xmlRows[i].InnerText = xmlRows[i].InnerText.Replace("15", rowsCount.ToString()); MemoryStream ms = new MemoryStream(); document.Save(ms); xmlFile.SetLength(ms.Length); xmlFile.Position = 0; document.Save(xmlFile); ms.Close(); }
      
      





これで、ファイルの準備ができました! HttpContext.Current.Responseを使用して保存/ロードするためにユーザーに提供します。

 public static void SendContent(byte[] fileContent, string outFileName) { HttpContext.Current.Response.ClearContent(); HttpContext.Current.Response.AddHeader("Content-Type", "application/force-download"); HttpContext.Current.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", outFileName.Replace(" ", "_"))); HttpContext.Current.Response.AddHeader("Content-Length", fileContent.Length.ToString()); HttpContext.Current.Response.OutputStream.Write(fileContent, 0, fileContent.Length); HttpContext.Current.Response.OutputStream.Flush(); HttpContext.Current.Response.OutputStream.Close(); HttpContext.Current.Response.Flush(); HttpContext.Current.ApplicationInstance.CompleteRequest(); }
      
      





ここではコード全体を公開しませんでした。大きすぎるため、プロジェクト全体(詳細なコメントと空のテンプレートを含む)はここからダウンロードできます



注:




ご清聴ありがとうございました。



All Articles