2014-11-27 3 views
0

Мне нужно создать файл Excel xlsx. Итак, я решил использовать Open xml (Open Xml 2.0, потому что мне нужно использовать .net 3.5).Использование Open Xml для создания xlsx-файла для Excel 2013

После некоторых исследований о Open XML, я создал этот класс:

public class ExcelFileManager : FileManager 
{ 
    private SpreadsheetDocument _package; 

    private Row _currentRow; 
    private SheetData _sheetData; 

    private int? _cellColumnN2; 
    private int _cellColumnN1; 
    private int _cellRow; 

    public ExcelFileManager(string path) 
     : base(path) 
    { 
     _package = SpreadsheetDocument.Create(path, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook); 

     _cellColumnN1 = 65; 
     _cellRow = 1; 

     CreateSpreadSheet(); 
    } 

    private void CreateSpreadSheet() 
    { 
     var workbookPart1 = _package.AddWorkbookPart(); 

     workbookPart1.Workbook = new Workbook(); 
     var worksheetPart1 = workbookPart1.AddNewPart<WorksheetPart>(); 
     worksheetPart1.Worksheet = new Worksheet(); 

     var sheets = _package.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets()); 
     var sheet = new Sheet() 
     { 
      Id = _package.WorkbookPart. 
       GetIdOfPart(worksheetPart1), 
      SheetId = 1, 
      Name = "Users" 
     }; 
     sheets.Append(sheet); 

     _sheetData = new SheetData(); 
     _currentRow = new Row(); 

     _sheetData.Append(_currentRow); 
     worksheetPart1.Worksheet.Append(_sheetData); 
    } 

    public override void AddHeader(IEnumerable<string> columnsName) 
    { 
     if (columnsName == null || !columnsName.Any()) 
      throw new ArgumentNullException("columnsName"); 

     try 
     { 
      AddRow(columnsName.ToArray()); 
     } 
     catch (Exception e) 
     { 
      Tracing.Application.TraceError(-1, "An error occured when adding header to excel file", e); 

      throw; 
     } 
    } 

    public override void AddRow(params object[] values) 
    { 
     if (values == null || !values.Any()) 
      throw new ArgumentNullException("values"); 

     try 
     { 
      var listCells = new List<OpenXmlElement>(); 

      foreach (var item in values) 
      { 
       listCells.Add(CreateCellRow(item)); 
      } 

      _currentRow.Append(listCells); 

      AddNewRow(); 
     } 
     catch (Exception e) 
     { 
      Tracing.Application.TraceError(-1, "An error occured when adding row to excel file", e); 

      throw; 
     } 
    } 

    public override void AddCell(object value) 
    { 
     try 
     { 
      var cell = CreateCellRow(value); 

      _currentRow.Append(cell); 
     } 
     catch (Exception e) 
     { 
      Tracing.Application.TraceError(-1, "An error occured when adding cell to excel file", e); 

      throw; 
     } 
    } 

    public override void AddNewRow() 
    { 
     _currentRow = new Row(); 

     _sheetData.Append(_currentRow); 

     IncreaseRow(); 
    } 

    #region Utils 
    private Cell CreateCellRow(object value) 
    { 
     Tracing.Application.TraceInformation("Add value \"{0}\" in cell {1}", value, GetCellLocation()); 

     Cell cell = new Cell(); 

     if (value == null) 
     { 
      cell.CellValue = new CellValue(string.Empty); 
      cell.DataType = CellValues.String; 
     } 
     else 
     {     
      cell.CellValue = new CellValue(value.ToString()); 
      cell.DataType = GetCellType(value); 
     } 

     cell.CellReference = GetCellLocation(); 

     IncreaseCellColumn(); 

     return cell; 
    } 

    private CellValues GetCellType(object value) 
    { 
     CellValues type = CellValues.String; 

     if (value.GetType() == typeof(DateTime) || value.GetType() == typeof(DateTime?)) 
      type = CellValues.Date; 

     if (value.GetType() == typeof(bool) || value.GetType() == typeof(bool?)) 
      type = CellValues.Boolean; 

     if (IsNumericType(value.GetType())) 
      type = CellValues.Number; 

     return type; 
    } 

    public bool IsNumericType(Type type) 
    { 
     TypeCode typeCode = Type.GetTypeCode(type); 

     //The TypeCode of numerical types are between SByte (5) and Decimal (15). 
     return (int)typeCode >= 5 && (int)typeCode <= 15; 
    } 

    private void IncreaseCellColumn() 
    { 
     if (_cellColumnN1 < 90) 
      _cellColumnN1++; 
     else 
     { 
      _cellColumnN1 = 65; 

      if (!_cellColumnN2.HasValue) 
       _cellColumnN2 = 65; 
      else 
       _cellColumnN2++; 
     } 
    } 

    private void IncreaseRow() 
    { 
     _cellRow++; 
     _cellColumnN1 = 65; 
     _cellColumnN2 = null; 
    } 

    private string GetCellLocation() 
    { 
     Char? c1 = null; 

     if (_cellColumnN2.HasValue) 
      c1 = (char)_cellColumnN2; 

     Char c2 = (char)_cellColumnN1; 

     return string.Format("{0}{1}{2}", c1, c2, _cellRow); 
    } 
    #endregion 

    #region IDisposable 
    public override void Dispose() 
    { 
     if (_package != null) 
     { 
      _package.WorkbookPart.Workbook.Save(); 
      _package.Close(); 
     } 
    } 
    #endregion 
} 

Когда я создать файл Excel с помощью этого класса, появляется сообщение об ошибке: «Мы нашли ошибку в содержании ... " Excel исправляет файл и становится доступным. Почему у меня эта ошибка в Excel?

я смотрел немного в XML-файл sheet.xml, содержащийся в файле XLSX, есть несколько отличий:

генерируемый файл:

<x:worksheet xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 
    <x:sheetData> 
    <x:row> 
     <x:c r="A1" t="str"> 
     <x:v>a string</x:v> 
     </x:c> 
     <x:c r="B1" t="str"> 
     <x:v>another string</x:v> 
     </x:c> 
    </x:row> 
    ... 
    </x:sheetData> 
</x:worksheet> 

Капремонт файл:

<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"> 
    <dimension ref="A1:S12"/> 
    <sheetViews> 
    <sheetView tabSelected="1" workbookViewId="0"> 
     <selection activeCell="I11" sqref="I11"/> 
    </sheetView> 
    </sheetViews> 
    <sheetFormatPr baseColWidth="10" defaultRowHeight="15" x14ac:dyDescent="0.25"/> 
    <sheetData> 
    <row r="1" spans="1:19" x14ac:dyDescent="0.25"> 
     <c r="A1" t="s"> 
     <v>0</v> 
     </c> 
     <c r="B1" t="s"> 
     <v>1</v> 
     </c> 
     <c r="C1" t="s"> 
     <v>2</v> 
     </c> 
    </row> 
    ... 
</sheetData> 
<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/> 
</worksheet> 

Я попытался изменить пространства имен, но это не сработало. Я знаю, что общие строки используются во втором xml, но я не думаю, что это проблема. ^^ Меня не интересует стиль, как маржа страницы, шрифт, ... (может быть полезно настроить ширину для столбцов ^^)

Я просто хочу хорошо сформированный файл xlsx.

У меня есть ошибка или упущение в моем коде?

Извините за мой английский, кстати «^^

Спасибо.

ответ

1

Даты хранятся в виде цифр с номером типа, но со стилем, применяемым к ним. Булевы также сохраняются как числа, но с булевым типом.

private Cell CreateCellRow(object value) 
{ 
    Cell cell = new Cell(); 
    cell.CellReference = GetCellLocation(); 
    IncreaseCellColumn(); 

    if (value == null) 
    { 
     cell.DataType = CellValues.InlineString; 
     cell.InlineString = new InlineString 
     { 
      Text = new Text("") 
     }; 
     return cell; 
    } 

    switch (Type.GetTypeCode(value.GetType())) 
    { 
     case TypeCode.DateTime: 
      cell.StyleIndex = 1; 
      cell.CellValue = new CellValue(((DateTime)value).ToOADate().ToString(CultureInfo.InvariantCulture)); 
      break; 
     case TypeCode.String: 
      cell.DataType = CellValues.InlineString; 
      cell.InlineString = new InlineString {Text = new Text((string) value)}; 
      break; 
     case TypeCode.Boolean: 
      cell.DataType = CellValues.Boolean; 
      cell.CellValue = new CellValue((bool)value ? "1" : "0"); 
      break; 
     case TypeCode.Byte: 
     case TypeCode.Int16: 
     case TypeCode.Int32: 
     case TypeCode.Int64: 
     case TypeCode.SByte: 
     case TypeCode.UInt16: 
     case TypeCode.UInt32: 
     case TypeCode.UInt64: 
     case TypeCode.Decimal: 
     case TypeCode.Double: 
     case TypeCode.Single: 
      cell.CellValue = new CellValue(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture)); 
      break; 
     default: 
      throw new Exception("Unrecognized type: " + value.GetType().FullName); 
    } 

    return cell; 
} 

Чтобы создать стиль, необходимый (надрезается из another answer):

var stylesPart = workbookPart.AddNewPart<WorkbookStylesPart>(); 
stylesPart.Stylesheet = CreateStylesheet(); 
private Stylesheet CreateStylesheet() 
{ 
    var stylesheet = new Stylesheet(); 

    // Default Font 
    var fonts = new Fonts() { Count = 1, KnownFonts = BooleanValue.FromBoolean(true) }; 
    var font = new Font 
    { 
     FontSize = new FontSize() { Val = 11 }, 
     FontName = new FontName() { Val = "Calibri" }, 
     FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 }, 
     FontScheme = new FontScheme() { Val = new EnumValue<FontSchemeValues>(FontSchemeValues.Minor) } 
    }; 
    fonts.Append(font); 
    stylesheet.Append(fonts); 

    // Default Fill 
    var fills = new Fills() { Count = 1 }; 
    var fill = new Fill(); 
    fill.PatternFill = new PatternFill() { PatternType = new EnumValue<PatternValues>(PatternValues.None) }; 
    fills.Append(fill); 
    stylesheet.Append(fills); 

    // Default Border 
    var borders = new Borders() { Count = 1 }; 
    var border = new Border 
    { 
     LeftBorder = new LeftBorder(), 
     RightBorder = new RightBorder(), 
     TopBorder = new TopBorder(), 
     BottomBorder = new BottomBorder(), 
     DiagonalBorder = new DiagonalBorder() 
    }; 
    borders.Append(border); 
    stylesheet.Append(borders); 

    // Default cell format and a date cell format 
    var cellFormats = new CellFormats() { Count = 2 }; 

    var cellFormatDefault = new CellFormat { NumberFormatId = 0, FormatId = 0, FontId = 0, BorderId = 0, FillId = 0 }; 
    cellFormats.Append(cellFormatDefault); 

    var cellFormatDate = new CellFormat { NumberFormatId = 22, FormatId = 0, FontId = 0, BorderId = 0, FillId = 0, ApplyNumberFormat = BooleanValue.FromBoolean(true) }; 
    cellFormats.Append(cellFormatDate); 

    stylesheet.Append(cellFormats); 

    return stylesheet; 
} 
Смежные вопросы