OpenXML мне показался очень неудобным, хотя я сделал с его помощью всё что мне требовалось. Для упрощения жизни пишется набор хелперов вроде:
Костыли 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163.
public static class OpenXmlExcelHelper
{
#region Append To Stylesheet
public static uint AppendCellFormat(this SpreadsheetDocument document, CellFormat cellFormat)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (stylesheet.CellFormats == null)
stylesheet.CellFormats = new CellFormats();
stylesheet.CellFormats.Append(cellFormat);
return (uint)stylesheet.CellFormats.FirstIndex(v => cellFormat == v);
}
public static uint AppendFont(this SpreadsheetDocument document, Font font)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (stylesheet.Fonts == null)
stylesheet.Fonts = new Fonts();
stylesheet.Fonts.Append(font);
return (uint)stylesheet.Fonts.FirstIndex(v => font == v);
}
public static uint AppendBorder(this SpreadsheetDocument document, Border border)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (stylesheet.Borders == null)
stylesheet.Borders = new Borders();
stylesheet.Borders.Append(border);
return (uint)stylesheet.Borders.FirstIndex(v => border == v);
}
public static uint AppendNumberingFormat(this SpreadsheetDocument document, NumberingFormat numberingFormat)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (stylesheet.NumberingFormats == null)
stylesheet.NumberingFormats = new NumberingFormats();
stylesheet.NumberingFormats.Append(numberingFormat);
if (numberingFormat.NumberFormatId == null)
{
var id = (uint)(stylesheet.NumberingFormats.FirstIndex(v => numberingFormat == v) + MinCustomNumberFormatId);
numberingFormat.NumberFormatId = id;
}
return numberingFormat.NumberFormatId;
}
#endregion
#region Stylesheet Query
public static Font GetDefaultFont(this SpreadsheetDocument document)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
return (Font)stylesheet.Fonts.First();
}
public static Font GetCellFont(this SpreadsheetDocument document, Cell cell)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
var cellFormat = document.GetCellFormat(cell);
if (cellFormat.ApplyFont != null && cellFormat.ApplyFont && cellFormat.FontId != null)
return (Font)stylesheet.Fonts.ElementAt((int)(uint)cellFormat.FontId);
return document.GetDefaultFont();
}
public static CellFormat GetDefaultCellFormat(this SpreadsheetDocument document)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
return (CellFormat)stylesheet.CellFormats.First();
}
public static CellFormat GetCellFormat(this SpreadsheetDocument document, Cell cell)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (cell.StyleIndex != null)
return (CellFormat)stylesheet.CellFormats.ElementAt((int)(uint)cell.StyleIndex);
return document.GetDefaultCellFormat();
}
public static string GetCellNumberFormatCode(this SpreadsheetDocument document, Cell cell)
{
var stylesheet = document.WorkbookPart.WorkbookStylesPart.Stylesheet;
if (cell.DataType != CellValues.Number)
return null;
var cellStyle = document.GetCellFormat(cell);
if (cellStyle.ApplyNumberFormat == null || cellStyle.ApplyNumberFormat == false || cellStyle.NumberFormatId == null)
return null;
string defaultNumberFormatCode;
if (DefaultNumberFormats.TryGetValue(cellStyle.NumberFormatId, out defaultNumberFormatCode))
return defaultNumberFormatCode;
var numberFormat = stylesheet.NumberingFormats
.Cast<NumberingFormat>()
.FirstOrDefault(v => (uint)v.NumberFormatId == (uint)cellStyle.NumberFormatId);
if (numberFormat != null && numberFormat.FormatCode != null)
return numberFormat.FormatCode;
return null;
}
#endregion
#region Default Number Formats
// http://closedxml.codeplex.com/wikipage?title=NumberFormatId%20Lookup%20Table
const int MinCustomNumberFormatId = 1000;
static readonly Dictionary<uint, string> DefaultNumberFormats =
new[]
{
// System.Tuple.Create(0, General),
System.Tuple.Create(1, "0"),
System.Tuple.Create(2, "0.00"),
System.Tuple.Create(3, "#,##0"),
System.Tuple.Create(4, "#,##0.00"),
System.Tuple.Create(9, "0%"),
System.Tuple.Create(10, "0.00%"),
System.Tuple.Create(11, "0.00E+00"),
System.Tuple.Create(12, "# ?/?"),
System.Tuple.Create(13, "# ??/??"),
System.Tuple.Create(14, "d/m/yyyy"),
System.Tuple.Create(15, "d-mmm-yy"),
System.Tuple.Create(16, "d-mmm"),
System.Tuple.Create(17, "mmm-yy"),
System.Tuple.Create(18, "h:mm tt"),
System.Tuple.Create(19, "h:mm:ss tt"),
System.Tuple.Create(20, "H:mm"),
System.Tuple.Create(21, "H:mm:ss"),
System.Tuple.Create(22, "m/d/yyyy H:mm"),
System.Tuple.Create(37, "#,##0 ;(#,##0)"),
System.Tuple.Create(38, "#,##0 ;[Red](#,##0)"),
System.Tuple.Create(39, "#,##0.00;(#,##0.00)"),
System.Tuple.Create(40, "#,##0.00;[Red](#,##0.00)"),
System.Tuple.Create(45, "mm:ss"),
System.Tuple.Create(46, "[h]:mm:ss"),
System.Tuple.Create(47, "mmss.0"),
System.Tuple.Create(48, "##0.0E+0"),
System.Tuple.Create(49, "@")
}
.ToDictionary(v => (uint)v.Item1, v => v.Item2);
#endregion
}
Дальше, глаза бояться, руки делают:
Что-то вроде 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323.
namespace SspMain
{
public class OpenXmlExcelTableExporter : IExcelTableExporter
{
#region IExcelTableExporter Members
ExcelTableExporterModel Model;
IEnumerable Data;
SpreadsheetDocument Document;
Worksheet Worksheet;
SheetData SheetData;
OpenXmlExcelTextMeasure TextMeasure;
public Stream Execute(ExcelTableExporterModel model, IEnumerable data)
{
Model = model;
Data = data;
ResetColumnWidths();
var result = TempFileHelper.OpenTempFile();
try
{
var template = Resources.EmptyTemplate;
result.Write(template, 0, template.Length);
result.Seek(0, SeekOrigin.Begin);
using (Document = SpreadsheetDocument.Open(result, true))
using (TextMeasure = new OpenXmlExcelTextMeasure(Document))
{
Worksheet = Document.WorkbookPart.WorksheetParts.First().Worksheet;
SheetData = Worksheet.GetFirstChild<SheetData>();
WriteStyles();
WriteDocumentHeader();
WriteTableHeader();
WriteTableData();
WriteTableColumns();
}
result.Seek(0, SeekOrigin.Begin);
return result;
}
catch
{
result.Dispose();
throw;
}
}
#endregion
#region Styles
uint DocumentHeaderCellFormat;
uint TableHeaderCellFormat;
uint TableCommonCellFormat;
uint TableDateCellFormat;
uint TableDateTimeCellFormat;
void WriteStyles()
{
DocumentHeaderCellFormat = Document.AppendCellFormat(new CellFormat
{
ApplyFont = true,
FontId = Document.AppendFont(new Font
{
FontSize = new FontSize { Val = 16 },
Bold = new Bold()
})
});
var tableBorder = Document.AppendBorder(new Border
{
LeftBorder = new LeftBorder { Style = BorderStyleValues.Thin },
RightBorder = new RightBorder { Style = BorderStyleValues.Thin },
TopBorder = new TopBorder { Style = BorderStyleValues.Thin },
BottomBorder = new BottomBorder { Style = BorderStyleValues.Thin }
});
TableHeaderCellFormat = Document.AppendCellFormat(new CellFormat
{
ApplyFont = true,
FontId = Document.AppendFont(new Font { Bold = new Bold() }),
ApplyBorder = true,
BorderId = tableBorder
});
TableCommonCellFormat = Document.AppendCellFormat(new CellFormat
{
ApplyBorder = true,
BorderId = tableBorder
});
TableDateCellFormat = Document.AppendCellFormat(new CellFormat
{
ApplyBorder = true,
BorderId = tableBorder,
ApplyNumberFormat = true,
NumberFormatId = Document.AppendNumberingFormat(new NumberingFormat
{
FormatCode = "dd/mm/yyyy"
})
});
TableDateTimeCellFormat = Document.AppendCellFormat(new CellFormat
{
ApplyBorder = true,
BorderId = tableBorder,
ApplyNumberFormat = true,
NumberFormatId = Document.AppendNumberingFormat(new NumberingFormat
{
FormatCode = "dd/mm/yyyy hh:mm"
})
});
}
#endregion
#region Cells
void WriteDocumentHeader()
{
var row = new Row();
row.Append(new Cell
{
StyleIndex = DocumentHeaderCellFormat,
DataType = CellValues.String,
CellValue = new CellValue(Model.Name)
});
SheetData.Append(row);
SheetData.Append(new Row());
}
void WriteTableHeader()
{
var row = new Row();
var columnIndex = 0;
foreach (var column in Model.Columns)
{
var cell = new Cell
{
StyleIndex = TableHeaderCellFormat,
DataType = CellValues.String,
CellValue = new CellValue(column.Header)
};
row.Append(cell);
if (column.Width.HasValue == false)
{
var width = TextMeasure.GetCellWidth(cell);
SetColumnWidth(columnIndex, width);
}
columnIndex++;
}
SheetData.Append(row);
}
void WriteTableData()
{
PropertyPathInfo[] valueAccessors = null;
foreach (var dataItem in Data)
{
if (valueAccessors == null)
valueAccessors = Model.Columns
.Select(column => PropertyPathFactory.Create(dataItem.GetType(), column.PropertyName))
.ToArray();
var row = new Row();
var columnIndex = 0;
foreach (var column in Model.Columns)
{
var cell = new Cell();
try
{
var value = valueAccessors[columnIndex].GetValue(dataItem);
WriteCellValue(column, value, cell);
}
catch (Exception e)
{
throw new InvalidOperationException(string.Format(
"Excel column export error. Property name: {0}",
column.PropertyName
), e);
}
row.Append(cell);
if (column.Width.HasValue == false)
{
var width = TextMeasure.GetCellWidth(cell);
SetColumnWidth(columnIndex, width);
}
columnIndex++;
}
SheetData.Append(row);
}
}
// http://blogs.msdn.com/b/arvinwu/archive/2008/10/02/some-excel-2007-limitation.aspx
const int MaxCellStringValueLength = 32767;
void WriteCellValue(ExcelTableExporterColumn column, object value, Cell cell)
{
if (value == null)
{
cell.CellValue = new CellValue("");
cell.DataType = CellValues.String;
cell.StyleIndex = TableCommonCellFormat;
return;
}
if (column.ValueConverter != null)
value = column.ValueConverter(value);
var culture = CultureInfo.InvariantCulture;
if (column.ValueType == ExcelTableExporterValueTypes.String)
{
var stringValue = value.ToString();
if (stringValue.Length > MaxCellStringValueLength)
stringValue = stringValue.Substring(0, MaxCellStringValueLength);
cell.CellValue = new CellValue(stringValue);
cell.DataType = CellValues.String;
cell.StyleIndex = TableCommonCellFormat;
return;
}
if (column.ValueType == ExcelTableExporterValueTypes.Number)
{
var decimalValue = Convert.ToDecimal(value);
cell.CellValue = new CellValue(decimalValue.ToString(culture));
cell.DataType = CellValues.Number;
cell.StyleIndex = TableCommonCellFormat;
return;
}
if (column.ValueType == ExcelTableExporterValueTypes.Boolean)
{
var boolValue = Convert.ToBoolean(value);
cell.CellValue = new CellValue(boolValue ? "Да" : "");
cell.DataType = CellValues.String;
cell.StyleIndex = TableCommonCellFormat;
return;
}
if (column.ValueType == ExcelTableExporterValueTypes.Date || column.ValueType == ExcelTableExporterValueTypes.DateTime)
{
var oaDateTimeValue = Convert.ToDateTime(value).ToOADate();
cell.CellValue = new CellValue(oaDateTimeValue.ToString(culture));
cell.DataType = CellValues.Number;
cell.StyleIndex = column.ValueType == ExcelTableExporterValueTypes.Date ? TableDateCellFormat : TableDateTimeCellFormat;
return;
}
throw new ArgumentException("column.ValueType");
}
#endregion
#region Columns
double[] ColumnWidths;
void ResetColumnWidths()
{
ColumnWidths = new double[Model.Columns.Count];
}
void SetColumnWidth(int columnIndex, double width)
{
if (width > ColumnWidths[columnIndex])
ColumnWidths[columnIndex] = width;
}
void WriteTableColumns()
{
var worksheetColumns = new Columns();
var columnIndex = 0;
foreach (var column in Model.Columns)
{
var columnNum = (uint)(columnIndex + 1);
worksheetColumns.Append(new Column
{
Min = columnNum,
Max = columnNum,
CustomWidth = true,
Width = column.Width ?? ColumnWidths[columnIndex]
});
columnIndex++;
}
Worksheet.InsertBefore(worksheetColumns, SheetData);
}
#endregion
}
}
Есть надстройка ClosedXML . Я этим не пользовался, но на первый взгляд оно показалось удобным.
Если кто-то использовал проект ClosedXML, отпишите пожалуйста. Интересно Ваше мнение по поводу данного проекта.
|