我正在创建一个简单的通用 OPENXML excel 生成器,可以将其作为 nuget 包安装,以便我们可以在整个项目中使用它生成 excel。
Excel 正确生成,当我打开它时,它显示有一些错误需要修复。一旦我修复它们,数据就会正确显示,但是当我再次保存并打开文档时,它必须再次修复它。
Excel修复记录显示此修复记录:来自/xl/tables/table1.xml部分的表(表)
Open XML SDK Tools 未指示任何验证错误
我尝试过以下方法
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Excel.Conversion.Dtos;
using Excel.Conversion.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Excel.Conversion.OpenXML
{
public class OpenXMLExcelConverter : IExcelConverter
{
private Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
{
//iterate over all cells getting a max char value for each column
Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
var rows = sheetData.Elements<Row>();
UInt32[] numberStyles = new UInt32[] { 5, 6, 7, 8 }; //styles that will add extra chars
UInt32[] boldStyles = new UInt32[] { 1, 2, 3, 4, 6, 7, 8 }; //styles that will bold
foreach (var r in rows)
{
var cells = r.Elements<Cell>().ToArray();
//using cell index as my column
for (int i = 0; i < cells.Length; i++)
{
var cell = cells[i];
var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
var cellTextLength = cellValue.Length;
if (cell.StyleIndex != null && numberStyles.Contains(cell.StyleIndex))
{
int thousandCount = (int)Math.Truncate((double)cellTextLength / 4);
//add 3 for '.00'
cellTextLength += (3 + thousandCount);
}
if (cell.StyleIndex != null && boldStyles.Contains(cell.StyleIndex))
{
cellTextLength += 1;
}
if (maxColWidth.ContainsKey(i))
{
var current = maxColWidth[i];
if (cellTextLength > current)
{
maxColWidth[i] = cellTextLength;
}
}
else
{
maxColWidth.Add(i, cellTextLength);
}
}
}
return maxColWidth;
}
private Columns AutoSize(SheetData sheetData)
{
var maxColWidth = GetMaxCharacterWidth(sheetData);
Columns columns = new Columns();
//this is the width of my font - yours may be different
double maxWidth = 7;
foreach (var item in maxColWidth)
{
//width = Truncate([{Number of Characters} * {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width}*256)/256
double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;
//pixels=Truncate(((256 * {width} + Truncate(128/{Maximum Digit Width}))/256)*{Maximum Digit Width})
double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);
//character width=Truncate(({pixels}-5)/{Maximum Digit Width} * 100+0.5)/100
double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;
Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
columns.Append(col);
}
return columns;
}
public Table DefineTable(WorksheetPart worksheetPart, int rowMin, int rowMax, int colMin, int colMax)
{
TableDefinitionPart tableDefinitionPart = worksheetPart.AddNewPart<TableDefinitionPart>("rId" + (worksheetPart.TableDefinitionParts.Count() + 1));
int tableNo = worksheetPart.TableDefinitionParts.Count();
string reference = ((char)(64 + colMin)).ToString() + rowMin + ":" + ((char)(64 + colMax)).ToString() + rowMax;
Table table = new Table() { Id = (UInt32)tableNo, Name = "Table" + tableNo, DisplayName = "Table" + tableNo, Reference = reference, TotalsRowShown = false };
AutoFilter autoFilter = new AutoFilter() { Reference = reference };
TableColumns tableColumns = new TableColumns() { Count = (UInt32)(colMax - colMin + 1) };
for (int i = 0; i < (colMax - colMin + 1); i++)
{
tableColumns.Append(new TableColumn() { Id = (UInt32)(colMin + i), Name = "Column" + i }); //changed i+1 -> colMin + i
//Add cell values (shared string)
}
TableStyleInfo tableStyleInfo = new TableStyleInfo() { Name = "TableStyleLight1", ShowFirstColumn = false, ShowLastColumn = false, ShowRowStripes = true, ShowColumnStripes = false };
table.Append(autoFilter);
table.Append(tableColumns);
table.Append(tableStyleInfo);
tableDefinitionPart.Table = table;
TableParts tableParts = (TableParts)worksheetPart.Worksheet.ChildElements.Where(ce => ce is TableParts).FirstOrDefault(); // Add table parts only once
if (tableParts is null)
{
tableParts = new TableParts();
tableParts.Count = (UInt32)0;
worksheetPart.Worksheet.Append(tableParts);
}
tableParts.Count += (UInt32)1;
TablePart tablePart = new TablePart() { Id = "rId" + tableNo };
tableParts.Append(tablePart);
return table;
}
public byte[] ConvertToExcel(WorkbookDto workbook)
{
MemoryStream ms = new MemoryStream();
using (var newWorkbook = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
{
var workbookPart = newWorkbook.AddWorkbookPart();
newWorkbook.WorkbookPart.Workbook = new Workbook();
newWorkbook.WorkbookPart.Workbook.Sheets = new Sheets();
foreach (var table in workbook.WorkSheets)
{
var sheetPart = newWorkbook.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
if (!table.AutoFitColumns)
{
sheetPart.Worksheet = new Worksheet(sheetData);
}
Sheets sheets = newWorkbook.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = newWorkbook.WorkbookPart.GetIdOfPart(sheetPart);
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.WorksheetName };
sheets.Append(sheet);
Row headerRow = new Row();
List<String> columns = new List<string>();
foreach (System.Data.DataColumn column in table.WorksheetData.Columns)
{
columns.Add(column.ColumnName.EscapeXmlCharacters());
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(column.ColumnName.EscapeXmlCharacters());
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (System.Data.DataRow dsrow in table.WorksheetData.Rows)
{
Row newRow = new Row();
foreach (String col in columns)
{
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(dsrow[col].ToString().EscapeXmlCharacters());
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
if (table.AutoFitColumns == true)
{
var columnData = AutoSize(sheetData);
sheetPart.Worksheet = new Worksheet();
sheetPart.Worksheet.Append(columnData);
sheetPart.Worksheet.Append(sheetData);
}
if (table.AddColumnTotals)
{
var tableForWorkSheet = DefineTable(sheetPart, 1, table.WorksheetData.Rows.Count, 1, table.WorksheetData.Columns.Count);
}
}
}
return ms.ToArray();
}
}
}
可以使用了
通过更新代码来解决这个问题,如下所示:似乎表列名称需要与数据列名称匹配:
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Excel.Conversion.Dtos;
using Excel.Conversion.Enums;
using Excel.Conversion.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Excel.Conversion.OpenXML
{
public class OpenXMLExcelConverter : IExcelConverter
{
private Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
{
Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
var rows = sheetData.Elements<Row>();
UInt32[] numberStyles = new UInt32[] { 5, 6, 7, 8 };
UInt32[] boldStyles = new UInt32[] { 1, 2, 3, 4, 6, 7, 8 };
foreach (var r in rows)
{
var cells = r.Elements<Cell>().ToArray();
for (int i = 0; i < cells.Length; i++)
{
var cell = cells[i];
var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
var cellTextLength = cellValue.Length;
if (cell.StyleIndex != null && numberStyles.Contains(cell.StyleIndex))
{
int thousandCount = (int)Math.Truncate((double)cellTextLength / 4);
cellTextLength += (3 + thousandCount);
}
if (cell.StyleIndex != null && boldStyles.Contains(cell.StyleIndex))
{
cellTextLength += 1;
}
if (maxColWidth.ContainsKey(i))
{
var current = maxColWidth[i];
if (cellTextLength > current)
{
maxColWidth[i] = cellTextLength;
}
}
else
{
maxColWidth.Add(i, cellTextLength);
}
}
}
return maxColWidth;
}
private Columns AutoSize(SheetData sheetData)
{
var maxColWidth = GetMaxCharacterWidth(sheetData);
Columns columns = new Columns();
double maxWidth = 7;
foreach (var item in maxColWidth)
{
double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;
double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);
double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;
Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
columns.Append(col);
}
return columns;
}
public Table DefineTable(WorksheetPart worksheetPart, int rowMin, int rowMax, int colMin, int colMax, WorkSheetDto worksheetData, int tableNumber)
{
TableDefinitionPart tableDefinitionPart = worksheetPart.AddNewPart<TableDefinitionPart>("rId" + (worksheetPart.TableDefinitionParts.Count() + 1));
int tableNo = worksheetPart.TableDefinitionParts.Count();
string reference = ((char)(64 + colMin)).ToString() + rowMin + ":" + ((char)(64 + colMax)).ToString() + rowMax;
Table table = new Table() { Id = (UInt32)tableNumber, Name = "Table" + tableNumber, DisplayName = "Table" + tableNumber, Reference = reference, TotalsRowShown = false };
AutoFilter autoFilter = new AutoFilter() { Reference = reference };
TableColumns tableColumns = new TableColumns() { Count = (UInt32)(colMax - colMin + 1) };
for (int i = 0; i < (colMax - colMin + 1); i++)
{
tableColumns.Append(new TableColumn() { Id = (UInt32)(colMin + i), Name = worksheetData.WorksheetData.Columns[i].ColumnName.EscapeXmlCharacters() });
}
TableStyleInfo tableStyleInfo = new TableStyleInfo() { Name = worksheetData.TableStyle.ToString(), ShowFirstColumn = false, ShowLastColumn = false, ShowRowStripes = true, ShowColumnStripes = false };
table.Append(autoFilter);
table.Append(tableColumns);
table.Append(tableStyleInfo);
tableDefinitionPart.Table = table;
TableParts tableParts = (TableParts)worksheetPart.Worksheet.ChildElements.Where(ce => ce is TableParts).FirstOrDefault(); // Add table parts only once
if (tableParts is null)
{
tableParts = new TableParts();
tableParts.Count = (UInt32)0;
worksheetPart.Worksheet.Append(tableParts);
}
tableParts.Count += (UInt32)1;
TablePart tablePart = new TablePart() { Id = "rId" + tableNo };
tableParts.Append(tablePart);
return table;
}
public static CellValues GetCellTypeEnum(string checkValue)
{
if (DateTime.TryParse(checkValue, out DateTime TempDate) == true)
{
return CellValues.String;
}
else if (bool.TryParse(checkValue, out bool TempBool) == true)
{
return CellValues.String;
}
else if (int.TryParse(checkValue, out int TempInt) == true)
{
return CellValues.Number;
}
else if (float.TryParse(checkValue, out float TempFloat) == true)
{
return CellValues.Number;
}
else if (decimal.TryParse(checkValue, out decimal TempDecimal) == true)
{
return CellValues.Number;
}
else
{
return CellValues.String;
}
}
public byte[] ConvertToExcel(WorkbookDto workbook)
{
MemoryStream ms = new MemoryStream();
using (var newWorkbook = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
{
var workbookPart = newWorkbook.AddWorkbookPart();
newWorkbook.WorkbookPart.Workbook = new Workbook();
newWorkbook.WorkbookPart.Workbook.Sheets = new Sheets();
int tableNumber = 0;
foreach (var table in workbook.WorkSheets)
{
var sheetPart = newWorkbook.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
if (!table.AutoFitColumns)
{
sheetPart.Worksheet = new Worksheet(sheetData);
}
Sheets sheets = newWorkbook.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = newWorkbook.WorkbookPart.GetIdOfPart(sheetPart);
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.WorksheetName };
sheets.Append(sheet);
Row headerRow = new Row();
List<String> columns = new List<string>();
foreach (System.Data.DataColumn column in table.WorksheetData.Columns)
{
columns.Add(column.ColumnName.EscapeXmlCharacters());
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(column.ColumnName.EscapeXmlCharacters());
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (System.Data.DataRow dsrow in table.WorksheetData.Rows)
{
Row newRow = new Row();
foreach (String col in columns)
{
Cell cell = new Cell();
cell.DataType = GetCellTypeEnum(dsrow[col].ToString().EscapeXmlCharacters());
cell.CellValue = new CellValue(dsrow[col].ToString().EscapeXmlCharacters());
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
if (table.AutoFitColumns == true)
{
var columnData = AutoSize(sheetData);
sheetPart.Worksheet = new Worksheet();
sheetPart.Worksheet.Append(columnData);
sheetPart.Worksheet.Append(sheetData);
}
if (table.TableStyle.HasValue)
{
tableNumber++;
var tableForWorkSheet = DefineTable(sheetPart, 1, table.WorksheetData.Rows.Count + 1, 1, table.WorksheetData.Columns.Count, table, tableNumber);
}
}
}
return ms.ToArray();
}
}
}