[Quote]Creating basic Excel workbook with Open XML

时间:2022-02-01 15:02:41

Creating basic Excel workbook with Open XML

[Quote from]http://www.codeproject.com/Articles/371203/Creating-basic-Excel-workbook-with-Open-XML
 
This article describes how to create a basic Excel workbook using Open XML.
[Quote]Creating basic Excel workbook with Open XMLIs your email address OK? You are signed up for our newsletters but your email address is either unconfirmed, or has not been reconfirmed in a long time. Please click here to have a confirmation email sent so we can confirm your email address and start sending you newsletters again. Alternatively, you can update your subscriptions.

Introduction

The purpose of this article is to describe how to create an Excel workbook using solely DocumentFormat.OpenXml.dll (namespace is DocumentFormat.OpenXml).

In order to test the samples you have to download and install the Open XML SDK 2.0 from Download Center.

The demo is created for both C# and Visual Basic.

When creating an Excel file with Open XML SDK, there's no need to install Microsoft Office, so this library can be used without Excel installation. However, the demo project starts the xlsx document after it hsa been created so in order to view the file created, a program that can open xlsx files should be installed to view the file.

Little bit of explanation about Open XML

Open XML is an open and standardized format for Office files. The standards used are:

These standards define the structure and the elements for the Office files. The Office files (like xlsx for Excel) themselves are zipped files that contain a specific directory and file structure. The files that hold the content of a spreadsheet are xml files like any other xml files.

In case of Excel files a basic xlsx file contains for example following files:

  • /[Content_Types].xml: Defines parts and extensions for the spreadsheet
  • /xl/workbook.xml: For e xample sheets that are included in the workbook
  • /xl/styles.xml: Styles used in the worksheets
  • /xl/sharedStrings.xml: Strings that are shared among cells
  • /xl/worksheets/sheet1.xml...: The actual worksheets

The actual package contains more files but in the scope of this article these are the most interesting ones. The demo projects included show few operations that are done to produce and modify these files.

About the project

The project itself is very simple. It consists of two classes: MainWindow class and a static Excel Class. The Excel class is responsible of all the operations done against the Excel spreadsheet. It's kinda utility class, but note that it's nowhere near ready. It's supposed to be used as a learning tool or a seed to an actual implementation.

When writing this demo I found out that Excel is very picky on the XML files. One surprise was that the order of the elements in XML files is very important. For example elements in style sheet such as  fonts, fills, borders, cellStyleXfs, cellXfs etc must be in specific order. Otherwise the document is interpreted as corrupted.

Another observation was that the indexes of the elements are quite often used (for example the index of a shared string). However there is no support in the library to fetch the indexes so the collections have to be looped in order to calculate the index of a desired element.

So one of the best tools when building this was a utility to extract  data from the xlsx (=zip) file to see what is the actual content.

If you download the project, you'll notice that fully qualified class names are used. In this article I have removed the namespaces before the classes in order to enhance readability. So if you are copying code from the code snippets, don't forget to define using (in C#) or Imports (in VB) for the two namespaces:

  • DocumentFormat.OpenXml.Packaging
  • DocumentFormat.OpenXml.Spreadsheet

To create the spreadsheet

Now to the code. The first thing is to create the spreadsheet. This is actually the xlsx file. The spreadsheet is created in C# as follows

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Creates the workbook
/// </summary>
/// <returns>Spreadsheet created</returns>
public static SpreadsheetDocument CreateWorkbook(string fileName) {
SpreadsheetDocument spreadSheet = null;
SharedStringTablePart sharedStringTablePart;
WorkbookStylesPart workbookStylesPart; try {
// Create the Excel workbook
spreadSheet = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook, false); // Create the parts and the corresponding objects
// Workbook
spreadSheet.AddWorkbookPart();
spreadSheet.WorkbookPart.Workbook = new Workbook();
spreadSheet.WorkbookPart.Workbook.Save(); // Shared string table
sharedStringTablePart = spreadSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
sharedStringTablePart.SharedStringTable = new SharedStringTable();
sharedStringTablePart.SharedStringTable.Save(); // Sheets collection
spreadSheet.WorkbookPart.Workbook.Sheets = new Sheets();
spreadSheet.WorkbookPart.Workbook.Save(); // Stylesheet
workbookStylesPart = spreadSheet.WorkbookPart.AddNewPart<WorkbookStylesPart>();
workbookStylesPart.Stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet.Save();
} catch (System.Exception exception) {
System.Windows.MessageBox.Show(exception.Message, "Excel OpenXML basics", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
} return spreadSheet;
}

And in Visual Basic

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Creates the workbook
''' </summary>
''' <returns>Spreadsheet created</returns>
Public Shared Function CreateWorkbook(fileName As String) As SpreadsheetDocument
Dim spreadSheet As SpreadsheetDocument = Nothing
Dim sharedStringTablePart As SharedStringTablePart
Dim workbookStylesPart As WorkbookStylesPart Try
' Create the Excel workbook
spreadSheet = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook, False) ' Create the parts and the corresponding objects
' Workbook
spreadSheet.AddWorkbookPart()
spreadSheet.WorkbookPart.Workbook = New Workbook()
spreadSheet.WorkbookPart.Workbook.Save() ' Shared string table
sharedStringTablePart = spreadSheet.WorkbookPart.AddNewPart(Of SharedStringTablePart)()
sharedStringTablePart.SharedStringTable = New SharedStringTable()
sharedStringTablePart.SharedStringTable.Save() ' Sheets collection
spreadSheet.WorkbookPart.Workbook.Sheets = New DocumentFormat.OpenXml.Spreadsheet.Sheets()
spreadSheet.WorkbookPart.Workbook.Save() ' Stylesheet
workbookStylesPart = spreadSheet.WorkbookPart.AddNewPart(Of WorkbookStylesPart)()
workbookStylesPart.Stylesheet = New Stylesheet()
workbookStylesPart.Stylesheet.Save()
Catch exception As System.Exception
System.Windows.MessageBox.Show(exception.Message, "Excel OpenXML basics", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand)
End Try Return spreadSheet
End Function

After the spreadsheet document is created, four elemental parts common to all worksheets are added:

  • Workbook: Contains definition of the workbook itself
  • SharedStringTable: A table of strings that are shared among cells
  • Sheets collection: Collection of sheets in the workbook
  • Stylesheet: Style definitions for the cells. Such as borders, fonts etc.

When creating each of these, first a part is created and after that the object itself. Note that parts are found inDocumentFormat.OpenXml.Packaging namespace while Excel objects are inDocumentFormat.OpenXml.Spreadsheet namespace. So the parts describe the 'storage' while the objects from the Spreadsheet  namespace describe the content elements in the xml files.

Creating the styles

Creating using the code

One way to create the style definitions is to create them from the code. In the demo, the following basic styles are created:

  • Numbering format (in x:numFmts): This is used to properly format a currency value
  • Font (in x:fonts): Calibri with font size 11 is used
  • Fill (in x:fills): A fill with no pattern is defined
  • Border (in x:borders): A border definition with no borders is defined
  • Cell style format (in x:CellStyleXfs): A general cell style format definition is used
  • Cell formats (in x:CellXfs)
    • General text
    • Date: NumberFormatId 22
    • Currency: NumberFormatId 164, references the numbering format
    • Percentage: NumberFormatId 10

The funny thing is that many of the numbering formats are predefined. So in order to use a correct formatting one has to discover the id's. One way to do this is to find the information by creating an Excel file containing the desired format and to examine the contents of the xlsx file.

Another thing is that, as mentioned before, the order of the style sheet elements is critical. If the order isn't correct, Excel will very easily interpret the document as corrupted.

An excerpt from the style creation is the creation of CellStyleFormats element and a CellFormat element inside the formats:

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
Stylesheet stylesheet = spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet;

// Cell formats (x:CellXfs)
stylesheet.InsertAt<CellFormats>(new CellFormats(), 5); // General text
stylesheet.GetFirstChild<CellFormats>().InsertAt<CellFormat>(
new CellFormat() {
FormatId = 0,
NumberFormatId = 0
}, 0);
And in Visual Basic
[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
Dim stylesheet As Stylesheet = spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet

' Cell formats (x:CellXfs)
stylesheet.InsertAt(Of CellFormats)(New CellFormats(), 5) ' General text
stylesheet.GetFirstChild(Of CellFormats)().InsertAt(Of CellFormat)(
New CellFormat() With {
.FormatId = 0,
.NumberFormatId = 0
}, 0)

Using a predefined stylesheet

Another way to define the styles is to use an existing XML file that holds the style definition. In the project theres a PredefinedStyles.xml file included in the build output. The contents of this file are read into a string which is then added to the empty stylesheet created previously.

The style definitions look as following (it's actually the same that is also created using the code):

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
<x:numFmts>
<x:numFmt numFmtId="164" formatCode="#,##0.00\ &quot;€&quot;" />
</x:numFmts>
<x:fonts>
<x:font>
<x:sz val="11" />
<x:name val="Calibri" />
</x:font>
</x:fonts>
<x:fills>
<x:fill>
<x:patternFill patternType="none" />
</x:fill>
</x:fills>
<x:borders>
<x:border>
<x:left />
<x:right />
<x:top />
<x:bottom />
<x:diagonal />
</x:border>
</x:borders>
<x:cellStyleXfs>
<x:xf numFmtId="0" fontId="0" fillId="0" borderId="0" />
</x:cellStyleXfs>
<x:cellXfs>
<x:xf numFmtId="0" xfId="0" />
<x:xf numFmtId="22" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
<x:xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
<x:xf numFmtId="10" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
</x:cellXfs>

And it's added using the following method in C#

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
      /// <summary>
/// Adds a predefined style from the given xml
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="xml">Style definition as xml</param>
/// <returns>True if succesful</returns>
public static bool AddPredefinedStyles(SpreadsheetDocument spreadsheet, string xml) {
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.InnerXml = xml;
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.Save(); return true;
}

And the function in Visual Basic

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
    ''' <summary>
''' Adds a predefined style from the given xml
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="xml">Style definition as xml</param>
''' <returns>True if succesful</returns>
Public Shared Function AddPredefinedStyles(spreadsheet As SpreadsheetDocument, xml As String) As Boolean
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.InnerXml = xml
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.Save() Return True
End Function

As a matter of fact basically any element can be filled by just adding an XML into it. For example a worksheet can be filled by adding a proper XML as the inner text of the worksheet.

Adding a worksheet

Next step is to add a worksheet. Adding a worksheet is straight-forward. However, one gotcha is to remember to define the relation to the workbook. Otherwise the sheet isn't included when the workbook is opened.

The creation of the workbook looks as following in C#

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Adds a new worksheet to the workbook
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="name">Name of the worksheet</param>
/// <returns>True if succesful</returns>
public static bool AddWorksheet(SpreadsheetDocument spreadsheet, string name) {
Sheets sheets = spreadsheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
Sheet sheet;
WorksheetPart worksheetPart; // Add the worksheetpart
worksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
worksheetPart.Worksheet.Save(); // Add the sheet and make relation to workbook
sheet = new DocumentFormat.OpenXml.Spreadsheet.Sheet() {
Id = spreadsheet.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = (uint)(spreadsheet.WorkbookPart.Workbook.Sheets.Count() + 1),
Name = name
};
sheets.Append(sheet);
spreadsheet.WorkbookPart.Workbook.Save(); return true;
}

And the Visual Basic equivalent function

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Adds a new worksheet to the workbook
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="name">Name of the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function AddWorksheet(spreadsheet As SpreadsheetDocument, name As String) As Boolean
Dim sheets As Sheets = spreadsheet.WorkbookPart.Workbook.GetFirstChild(Of Sheets)()
Dim sheet As Sheet
Dim worksheetPart As WorksheetPart ' Add the worksheetpart
worksheetPart = spreadsheet.WorkbookPart.AddNewPart(Of WorksheetPart)()
worksheetPart.Worksheet = New Worksheet(New SheetData())
worksheetPart.Worksheet.Save() ' Add the sheet and make relation to workbook
sheet = New Sheet With {
.Id = spreadsheet.WorkbookPart.GetIdOfPart(worksheetPart),
.SheetId = (spreadsheet.WorkbookPart.Workbook.Sheets.Count() + 1),
.Name = name}
sheets.Append(sheet)
spreadsheet.WorkbookPart.Workbook.Save() Return True
End Function

Another thing is to remember to number the sheets correctly. Because of this the amount of sheets is queried when defining the SheetId.

One more common thing, the shared strings

A workbook contains a table of shared strings. The idea is that the worksheets can reference a shared string without actually storing the string value inside the worksheet.

As this sounds a good idea and is the proposed way, the effect on the file size isn't very huge. When I tested this with 10,000 repetitions with a single (short) string the difference between using a shared string compared to repeating the string inside the worksheet was only few hundred bytes. Largely this is because of the compression algorithm compresses the strings quite nicely even if they are repeated. Of course the structure of the workbook and the location of the strings affect this quite much.

In order to add a shared string to the table I use the following method in C#

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Add a single string to shared strings table.
/// Shared string table is created if it doesn't exist.
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="stringItem">string to add</param>
/// <param name="save">Save the shared string table</param>
/// <returns></returns>
public static bool AddSharedString(SpreadsheetDocument spreadsheet, string stringItem, bool save = true) {
SharedStringTable sharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable; if (0 == sharedStringTable.Where(item => item.InnerText == stringItem).Count()) {
sharedStringTable.AppendChild(
new DocumentFormat.OpenXml.Spreadsheet.SharedStringItem(
new DocumentFormat.OpenXml.Spreadsheet.Text(stringItem))); // Save the changes
if (save) {
sharedStringTable.Save();
}
} return true;
}

And in Visual Basic

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Add a single string to shared strings table.
''' Shared string table is created if it doesn't exist.
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="stringItem">string to add</param>
''' <param name="save">Save the shared string table</param>
''' <returns></returns>
Public Shared Function AddSharedString(spreadsheet As SpreadsheetDocument, stringItem As String, Optional save As Boolean = True) As Boolean
Dim sharedStringTable As SharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable Dim stringQuery = (From item In sharedStringTable
Where item.InnerText = stringItem
Select item).Count() If 0 = stringQuery Then
sharedStringTable.AppendChild(
New DocumentFormat.OpenXml.Spreadsheet.SharedStringItem(
New DocumentFormat.OpenXml.Spreadsheet.Text(stringItem))) ' Save the changes
If save Then
sharedStringTable.Save()
End If
End If Return True
End Function

First, the existence of the string is checked and if it doesn't exist, it's added to the table.

In many methods, I have an optional save parameter. This is because saving the changes is quite slow so when the data is added for example in a loop, the saving is deferred.

Now to the data

The demo adds the following data to the worksheet

  • A shared string, 3 times
  • A decimal number
  • An integer number
  • A currency value
  • A date
  • A percentage
  • And a boolean value

All the data is added through specific methods but they all use a common method to actually store the value inside the worksheet.

I wont list adding the cell value since it's a bit bigger method but it contains the following operations:

  1. Check the existence of the defined row. If it isn't found a row is created
  2. Check the existence of the defined cell (based on the column index). If it isn't found a cell is created
  3. Check if the column is defined in the columns collection. Added if necessary. Actually this isn't mandatory but by defining the columns, their width can be set.
  4. And at last the value is added to the cell

Few important things:

  • Again, take care about the order. Rows and cells must be in correct order in the xml
  • The date data type isn't used. This is because Office 2007 doesn't support date type.
  • The references in cells are defined as A1, B3 etc. This is quite cumbersome way from the code point of view. This is why I use indexes for both columns and rows.

The column string is constructed as follows

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Converts a column number to column name (i.e. A, B, C..., AA, AB...)
/// </summary>
/// <param name="columnIndex">Index of the column</param>
/// <returns>Column name</returns>
public static string ColumnNameFromIndex(uint columnIndex) {
uint remainder;
string columnName = ""; while (columnIndex > 0) {
remainder = (columnIndex - 1) % 26;
columnName = System.Convert.ToChar(65 + remainder).ToString() + columnName;
columnIndex = (uint)((columnIndex - remainder) / 26);
} return columnName;
}

And in VB.Net

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Converts a column number to column name (i.e. A, B, C..., AA, AB...)
''' </summary>
''' <param name="columnIndex">Index of the column</param>
''' <returns>Column name</returns>
Public Shared Function ColumnNameFromIndex(columnIndex As UInt32) As String
Dim remainder As UInt32
Dim columnName As String = "" While (columnIndex > 0)
remainder = (columnIndex - 1) Mod 26
columnName = System.Convert.ToChar(65 + remainder).ToString() + columnName
columnIndex = ((columnIndex - remainder) / 26)
End While Return columnName
End Function

Adding a string or a shared string

As explained before a shared string uses just an index in the worksheet to point to a string in the shared strings table. What was quite amazing was that I didn't find a mechanism to get the index of a string from the table directly. Instead I had to build a loop to calculate the index.

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Returns the index of a shared string.
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="stringItem">String to search for</param>
/// <returns>Index of a shared string. -1 if not found</returns>
public static int IndexOfSharedString(SpreadsheetDocument spreadsheet, string stringItem) {
SharedStringTable sharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable;
bool found = false;
int index = 0; foreach (SharedStringItem sharedString in sharedStringTable.Elements<SharedStringItem>()) {
if (sharedString.InnerText == stringItem) {
found = true;
break; ;
}
index++;
} return found ? index : -1;
}

And in Visual Basic

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Returns the index of a shared string.
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="stringItem">String to search for</param>
''' <returns>Index of a shared string. -1 if not found</returns>
Public Shared Function IndexOfSharedString(spreadsheet As SpreadsheetDocument, stringItem As String) As Int32
Dim sharedStringTable As SharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable
Dim found As Boolean = False
Dim index As Int32 = 0 For Each sharedString As SharedStringItem In sharedStringTable.Elements(Of SharedStringItem)()
If sharedString.InnerText = stringItem Then
found = True
Exit For
End If
index = index + 1
Next If found Then
Return index
Else
Return -1
End If
End Function

The method for adding the string is quite simple. It gives the option of adding a shared string or a normal string.

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Sets a string value to a cell
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="stringValue">String value to set</param>
/// <param name="useSharedString">Use shared strings? If true and the string isn't found in shared strings, it will be added</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, string stringValue, bool useSharedString, bool save = true) {
string columnValue = stringValue;
CellValues cellValueType; // Add the shared string if necessary
if (useSharedString) {
if (Excel.IndexOfSharedString(spreadsheet, stringValue) == -1) {
Excel.AddSharedString(spreadsheet, stringValue, true);
}
columnValue = Excel.IndexOfSharedString(spreadsheet, stringValue).ToString();
cellValueType = CellValues.SharedString;
} else {
cellValueType = CellValues.String;
} return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, cellValueType, columnValue, null, save);
}

And in VB.Net

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Sets a string value to a cell
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="stringValue">String value to set</param>
''' <param name="useSharedString">Use shared strings? If true and the string isn't found in shared strings, it will be added</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetStringCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, stringValue As String, useSharedString As Boolean, Optional save As Boolean = True) As Boolean
Dim columnValue As String = stringValue
Dim cellValueType As CellValues ' Add the shared string if necessary
If (useSharedString) Then
If (Excel.IndexOfSharedString(spreadsheet, stringValue) = -1) Then
Excel.AddSharedString(spreadsheet, stringValue, True)
End If
columnValue = Excel.IndexOfSharedString(spreadsheet, stringValue).ToString()
cellValueType = CellValues.SharedString
Else
cellValueType = CellValues.String
End If Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, cellValueType, columnValue, Nothing, save)
End Function

Adding a numeric value

Adding a numeric value is much like adding a non-shared string value. The only concern is to use proper decimal separator (.) inside the xml so the decimal separator may need to be replaced. Another way is to set the current threads culture to en-US. If you compile the project with EN_US_CULTURE compilation symbol (default in the project) the threads UI culture will be set to local  culture, but the CurrentCulture of the thread will be replaced with en-US. This eliminates the need to reformat decimals. However, keep in mind that if you are getting for example regional settings like CurrencySymbol, it must be fetched from CurrentUICulture.

The code looks like

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Sets a cell value with double number
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="doubleValue">Double value</param>
/// <param name="styleIndex">Style to use</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, double doubleValue, uint? styleIndex, bool save = true) {
#if EN_US_CULTURE
string columnValue = doubleValue.ToString();
#else
string columnValue = doubleValue.ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".");
#endif return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Number, columnValue, styleIndex, save);
}

The Visual Basic version is

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Sets a cell value with double number
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="doubleValue">Double value</param>
''' <param name="styleIndex">Style to use</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetDoubleCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, doubleValue As Double, styleIndex As UInt32?, Optional save As Boolean = True) As Boolean
#If EN_US_CULTURE Then
Dim columnValue As String = doubleValue.ToString()
#Else
Dim columnValue As String = doubleValue.ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".")
#End If Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Number, columnValue, styleIndex, save)
End Function<span style="font-size: 12px; white-space: pre;">
</span>
 

Adding a date value

Date value is a bit more tricky. Things to keep in mind with dates:

  • A style needs to be specified. I've used a predefined format id 22 to format the date correctly
  • In the style definition ApplyNumberFormat has to be true.
  • The date is expressed as a double value
  • Excel starts to count the dates from 1.1.1900. This is different from .Net so OLE automation date has to be used
  • The decimal separator may need to be corrected (depending on the culture settings). For example my decimal separator is , so it has to be replaced with . Or as explained earlier with numbers, en-US culture may be used.

The addition of a date value looks as following

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
/// <summary>
/// Sets a cell value with a date
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="datetimeValue">DateTime value</param>
/// <param name="styleIndex">Style to use</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, System.DateTime datetimeValue, uint? styleIndex, bool save = true) {
#if EN_US_CULTURE
string columnValue = datetimeValue.ToOADate().ToString();
#else
string columnValue = datetimeValue.ToOADate().ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".");
#endif return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Date, columnValue, styleIndex, save);
}

And the Visual Basic equivalent

[Quote]Creating basic Excel workbook with Open XML Collapse | Copy Code
''' <summary>
''' Sets a cell value with a date
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="datetimeValue">DateTime value</param>
''' <param name="styleIndex">Style to use</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetDateCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, datetimeValue As System.DateTime, styleIndex As UInt32?, Optional save As Boolean = True) As Boolean
#If EN_US_CULTURE Then
Dim columnValue As String = datetimeValue.ToOADate().ToString()
#Else
Dim columnValue As String = datetimeValue.ToOADate().ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".")
#End If Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Date, columnValue, styleIndex, save)
End Function

Currency, percentage and boolean values

The next methods for adding different value types look very much the same as the previous so I won't  include them in the article. However, few things to keep in mind:

  • Percentage is like a decimal number but with different format. I've used format id 10.
  • Currency is much like a percentage, but this time also a numFmt needs to be defined.
  • Excel uses 0 and 1 for boolean values. If True or False is used they are interpreted as text. In Visual Basic the value of true (-1) needs to be changed to 1.

Last words

Hopefully this article helps to work with Open XML Excel files. The best way to get to know the code is to debug it and make small changes to see what happens [Quote]Creating basic Excel workbook with Open XML Have fun!

History

  • 22th  April, 2012: Created
  • 25th April, 2012: Readability modification, added alternative way to format numbers when writing to xlsx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

[Quote]Creating basic Excel workbook with Open XML的更多相关文章

  1. Python导出Excel为Lua&sol;Json&sol;Xml实例教程(三):终极需求

    相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 Python导出E ...

  2. Python导出Excel为Lua&sol;Json&sol;Xml实例教程(二):xlrd初体验

    Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出E ...

  3. Excel 数据导入SQL XML 自动生成表头

    去出差的时候应客户要求要要将Excel 文件内的数据批量导入到数据库中,而且有各种不同种类的表格,如果每一个表格多对应一个数据表的话, 按照正常的方法应该是创建数据表,创建数据库中映射的数据模型,然后 ...

  4. SpringBoot-文件系统-Excel,PDF,XML,CSV

    SpringBoot-文件系统-Excel,PDF,XML,CSV 1.Excel文件管理 1.1 POI依赖 1.2 文件读取 1.3 文件创建 1.4 文件导出 1.5 文件导出接口 2.PDF文 ...

  5. Python导出Excel为Lua&sol;Json&sol;Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

  6. 如何在open xml excel 中存储自定义xml数据?

    如何在open xml excel 中存储自定义xml数据? 而且不能放在隐藏的cell单元格内,也不能放在隐藏的sheet内,要类似web网站的Application变量,但还不能是VBA和宏之类的 ...

  7. &lbrack;转&rsqb;Blue Prism Opening a password protected Excel workbook&quest;

    本文转自:https://www.rpaforum.net/threads/opening-a-password-protected-excel-workbook.470/ 问: As the tit ...

  8. 导出Excel&sol;Pdf&sol;txt&sol;json&sol;XML&sol;PNG&sol;CSV&sol;SQL&sol;MS-Word&sol; Ms-Powerpoint&sol;等通过tableExport&period;js插件来实现

    首先去我的云盘下载需要的js: 链接:https://pan.baidu.com/s/13vC-u92ulpx3RbljsuadWw 提取码:mo8m 页面代码: <!DOCTYPE html& ...

  9. TestLink在线Excel用例转换xml

    [原文链接]:https://blog.tecchen.tech ,博文同步发布到博客园. 由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容. 欢迎访问我的个人网站:htt ...

随机推荐

  1. 简单C&num;、asp&period;net mvc验证码的实现

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Text;u ...

  2. ondragover 事件规定在何处放置被拖动的数据

    nternet Explorer 9.Firefox.Opera 12.Chrome 以及 Safari 5 支持拖放. 被拖元素,dragElement : 1.添加事件:ondragstart 2 ...

  3. MVC Action&comma;Service中筛选int 和list&lt&semi;int&gt&semi;

    action: public ActionResult DeleteByID(int id) { this.MessageService.DeleteMailTemplate(id); var fro ...

  4. codeforces 610B

    Description Vika has n jars with paints of distinct colors. All the jars are numbered from 1 to n an ...

  5. Oracle 唯一主键引发的行锁

    SQL> create table test(id int PRIMARY KEY, name char(10)); 表已创建. Session 1: SQL> select * from ...

  6. 让你提前认识软件开发&lpar;31&rpar;:数据库脚本中的begin与end

    版权声明:本文为博主原创文章.对文章内容有不论什么意见或建议,欢迎与作者单独交流.作者QQ(微信):245924426. https://blog.csdn.net/zhouzxi/article/d ...

  7. 大数据技术之&lowbar;14&lowbar;Oozie学习&lowbar;Oozie 的简介&plus;Oozie 的功能模块介绍&plus;Oozie 的部署&plus;Oozie 的使用案列

    第1章 Oozie 的简介第2章 Oozie 的功能模块介绍2.1 模块2.2 常用节点第3章 Oozie 的部署3.1 部署 Hadoop(CDH版本的)3.1.1 解压缩 CDH 版本的 hado ...

  8. C&num;将数据集DataSet中的数据导出到EXCEL文件的几种方法

    using System; using System.Collections.Generic; using System.Text; using System.Data; using System.W ...

  9. centos 7 修改ssh登录端口

    在阿里云上面使用的oneinstack镜像,默认是使用的22端口,每次登录总会发现有人在暴力破解我的服务器,所以想想还是修改一下比较安全. 1.在防火墙打开新的端口 iptables -I INPUT ...

  10. hdu 5232 Shaking hands 水题

    Shaking hands Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pi ...