C# - How to get excel cell value and its format - c#

Is it possible to get Excel cell value with its format(Number, date or general) with C#? I am using EPPLUS library right now but it does not have this option.

What I did is basically try to cast cell value to certain type. EPPLUS handles conversion for you. If conversion succeeded, value was properly formatted for a specified type. I do not know if there is better way, but i used this in my code:
var file= new FileInfo(path);
using (var package = new ExcelPackage(file))
{
var sheet = package.Workbook.Worksheets["Sheet1"];
var cell = sheet.Cells["A1"];
if(cell.Value is DateTime)
{
//logic for datetime
}
else if(cell.Value is double)
{
//you get the point :)
}
}

Related

Infragistics UltraGrid - Not exporting formulas to Excel

I have a very simple test App with an Infragistics UltraGrid which I have populated with some currency values.
UltraDataSource dataSource = new UltraDataSource();
dataSource.Band.Columns.Add("Cost1", typeof(double));
dataSource.Band.Columns.Add("Cost2", typeof(double));
dataSource.Band.Columns.Add("Cost3", typeof(double));
var dataRow = dataSource.Rows.Add();
dataRow["Cost1"] = 10.50;
dataRow["Cost2"] = 12.30;
dataRow["Cost3"] = 14.96;
gridResults.DataSource = dataSource;
gridResults.DataBind();
gridResults.DisplayLayout.Bands[0].Columns[1].Format = "C";
gridResults.DisplayLayout.Bands[0].Columns[2].Format = "C";
gridResults.DisplayLayout.Bands[0].Columns[3].Format = "C";
I actually want o use a custom format string of '$0.00' as my currency may be different in different columns but for simplicity I have stayed with the generic C format string. I then export the Grid to MS Excel using the UltraGridExcelExporter object as follows.
UltraGridExcelExporter exporter = new UltraGridExcelExporter();
exporter.ExportFormulas = true;
exporter.Export(gridResults, filename);
The display on screen is as I expect with the currency symbol and the value formatted correctly however when I look at the Excel file generated, the values are displayed as standard numeric with no formatting carried over. I have checked the docs and the default is to export formatting but at the moment I am at a loss as to what I am doing wrong.
What you can do is handle UltraGridExcelExporter InitializeColumn event. In this event you have access to excel format string of the column as well as to the grid column format string. Check in the event if the column has a format string and set the excel format string like this:
private void UltraGridExcelExporterInitializeColumn(object sender, InitializeColumnEventArgs e)
{
if (!string.IsNullOrEmpty(e.Column.Format))
{
switch (e.FrameworkFormatStr)
{
case "C":
e.ExcelFormatStr = "$#,##0.00";
break;
// handle here other format strings you may have
default:
e.ExcelFormatStr = e.FrameworkFormatStr;
break;
}
}
}
Note that not all the format strings in C# are the same as the format strings in Excel.

Excel time format using Epplus

I have an excel document which I try to import to my system.
(.net core 2.2 and EPPlus v4.5.3.1)
The excel data like 09:57:32 and Custom Cell format [hh]:mm:ss
private TimeSpan? GetRequiredTimeFromRowOrNull(ExcelWorksheet worksheet, int row, int column, string columnName, StringBuilder exceptionMessage)
{
worksheet.Cells[row, column].Style.Numberformat.Format = "hh:mm:ss";
var cellValue = TimeSpan.Parse(worksheet.Cells[row, column].Value.ToString());
if (cellValue.ToString() != null && !string.IsNullOrWhiteSpace(cellValue.ToString()))
{
return cellValue;
}
exceptionMessage.Append(GetLocalizedExceptionMessagePart(columnName));
return null;
}
"worksheet.Cells[row, column].Value"
comes 0.414953703703704
and also
"worksheet.Cells[row, column].Text" comes 09:12:32
How can I get exact value?
Excel stores dates and time in decimal values. The whole-number part is the day and the decimal part is the time.
So, to get into a C# DateTime, use the OLE Automation converter:
try
{
var val = (double)worksheet.Cells[row, column].Value;
var dt = DateTime.FromOADate(val);
var cellValue = dt.TimeOfDay;
}
catch (Exception)
{
//log conversion error
}
Will want to put a Try.Catch around the cast JIC the cell does not have a number in it.

How to set localized short-date format for a cell in Excel with C#?

Using C# and VSTO, the type of a cell in Excel can be set with the following code:
worksheet.Cells[i, j].NumberFormat = magicString;
, where worksheet is an object of the Microsoft.Office.Interop.Excel.Worksheet class, i is a row number of the cell, j is a column number of the cell, magicString is some string defining the type of the cell (note: Excel calls types as format, but below I'm using the word type).
The following magicStrings define the following Excel types:
string magicString = ""; - defines the "General" Excel type;
string magicString = "#"; - defines the "Text" Excel type;
string magicString = "0%"; - defines the "Percent" Excel type.
The situation is more complicated when I would like to set the "Date" Excel type. The complexity is related to the localization of the Excel and to the localization of the Windows system.
So, for example, I have a Russian version of Excel (in particular, all types are written in Excel in Russian) and my Windows system has the following short-date format: "dd.MM.yyyy" (one can find this setting in Control Panel > Region and Language > Formats). I have an English version of Windows but this plays absolutely no role.
As a result, if I will use the following magicString in my code, the type of the cell will be set to the short-date type:
string magicString = "ДД.ММ.ГГГГ"; - defines the "Date" (or, more exactly, the "Short-Date") Excel type;
As you can see, here the magicString is a combination of the Russian letters (Russian - because the Excel is Russian) and of the format set in the Windows settings.
If, instead of this magicString, I use the magicString equal to "DD.MM.YYYY" (i.e. the English letters), errors appear.
Thus, if I would want that my Excel add-in would be able to set correctly the "Short-Date" type for all (English, Russian, German and all other) versions of Excel and for all localization settings of Windows, I have to be able to use some universal magicString, that is independent of two mentioned factors.
As an option, I can read the short-date format from the Windows settings with this code:
string shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
and then, replace the letters in the obtained shortDatePattern string with those that correspond to the language of Excel. However, this way seems to me too complicated.
My question is: is there some universal magicString that is valid for all Excel languages and for all Windows localization settings, like it takes place for other Excel types like "General", "Text", and "Percent"? Or, maybe, someone knows other simple way to reach such an universality?
You should be able to do it like this:
Application xlApp = new Application();
Workbook wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
Worksheet ws = wb.Worksheets[1];
var yearCode = xlApp.International[XlApplicationInternational.xlYearCode];
var monthCode = xlApp.International[XlApplicationInternational.xlMonthCode];
var dayCode = xlApp.International[XlApplicationInternational.xlDayCode];
ws.Cells[1, 1].NumberFormat = string.Format("{0}{1}.{2}{3}.{4}{5}{6}{7}", dayCode, dayCode, monthCode, monthCode, yearCode, yearCode, yearCode, yearCode);
On the Application there is the International property. You can query it using the XlApplicationInternational enumeration. For me xlYearCode returns é for example. That should be Г for you.
After that you can just construct your NumberFormat using the previously queried format codes.
Thanks a lot to Szabolcs Dézsi for the hint. But it solves only a part of my question. Another part is how to extract date-format codes from the Windows-system localization settings? I have not found the answer in internet and provide my own solution, in combination with the solution of Szabolcs Dézsi.
First of all, let's create the following class:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
namespace MyNamespace
{
internal sealed class DateFormatComponentCodes
{
private readonly char year;
private readonly char month;
private readonly char day;
// Constructs the object based on the system localization.
public DateFormatComponentCodes()
{
DateTimeFormatInfo dateTimeFormatInfo = CultureInfo.CurrentCulture.DateTimeFormat;
var yearMonth = new HashSet<char>(new HashSet<char>(dateTimeFormatInfo.YearMonthPattern.ToCharArray()).Where(c => char.IsLetter(c)));
var monthDay = new HashSet<char>(new HashSet<char>(dateTimeFormatInfo.MonthDayPattern.ToCharArray()).Where(c => char.IsLetter(c)));
var monthHashSet = new HashSet<char>(yearMonth);
monthHashSet.IntersectWith(monthDay);
this.month = monthHashSet.First();
yearMonth.ExceptWith(monthHashSet);
this.year = yearMonth.First();
monthDay.ExceptWith(monthHashSet);
this.day = monthDay.First();
}
// Constructs the object based on the Excel localization.
public DateFormatComponentCodes(Excel.Application application)
{
this.year = application.International[Excel.XlApplicationInternational.xlYearCode].ToString()[0];
this.month = application.International[Excel.XlApplicationInternational.xlMonthCode].ToString()[0];
this.day = application.International[Excel.XlApplicationInternational.xlDayCode].ToString()[0];
}
public char Year
{
get
{
return this.year;
}
}
public char Month
{
get
{
return this.month;
}
}
public char Day
{
get
{
return this.day;
}
}
}
}
And now let's create two objects of this class and use them to generate short-date format pattern (called above as "magic string") for Excel:
private string ConstructExcelShortDatePattern()
{
var systemDateComponentCodes = new DateFormatComponentCodes();
var excelDateComponentCodes = new DateFormatComponentCodes(this.application);
string systemShortDatePattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
string excelShortDatePattern = systemShortDatePattern.Replace(systemDateComponentCodes.Year, excelDateComponentCodes.Year).Replace(systemDateComponentCodes.Month, excelDateComponentCodes.Month).Replace(systemDateComponentCodes.Day, excelDateComponentCodes.Day);
return excelShortDatePattern;
}
The returned string can be used to set the short-date format for all Windows localizations and all Excel localizations, like
worksheet.Cells[i, j].NumberFormat = ConstructExcelShortDatePattern();

Loading dates from a text file in EPPLUS

I'm trying to create an Excel spreadsheet in my web application using a tab-delimited text file as the data source. The code that loads my data looks like this:
// Load the data into the cells
Int32 rowIdx = 1;
foreach (String line in tab.Lines)
{
String[] cellTexts = line.Split(TAB);
Int32 colIdx = 1;
foreach (String cellText in cellTexts)
{
sheet.Cells[rowIdx, colIdx].Value = cellText;
colIdx++;
}
rowIdx++;
}
That seems to work fine. Later, however, I add a NumberFormat of "mm/dd/yyyy" to the cells:
range.Style.Numberformat.Format = "mm/dd/yyyy";
However, this doesn't change the display of the data in the cells. (The dates look like 5/1/15 or 12/31/15 in the original text file, and remain that way after the format is applied.
I am pretty sure that this because I've put a text value into the cell. (While it looks like a date, it's still just a string of characters.) But from my reading, I need to put a double into the cell to meet Excel's expectation that dates are stored as a double. Because the cell contains a string and not a double, the format string isn't applied, leaving the original, unformatted text.
I want to add some code to
Check the type of data in each cell in the range to which I apply a
date format.
If it's not a double, attempt to convert it to a date.
If the date conversion is successful, then convert the .NET date to an OADate and put it back into the cell.
My question is: Is this the best (or at least a reasonable) approach, and if so, how do I do that?
This code doesn't work:
foreach (OfficeOpenXml.ExcelRangeBase oneCell in range)
{
if (typeof(oneCell.Value) == "System.String")
{
// date manipulations here
}
}
The red line appears under oneCell in the typeof(oneCell.Value) call with the message "The type or namespace 'oneCell' could not be found. (Are you missing a using directive or an assembly reference?)"
Note that I can't know in advance where the date fields will be because both the data and the cell formats are provided from an external source. (The external cell formats do indicate when the format being applied is for a date format as opposed to a regular number format or a string.)
As #mason suggested, I'm posting the code I used to get around this problem.
(I didn't get an answer to my original question, which is how to iterate cells in a range and check the data type of each cell's content, but with this solution, I no longer need to do that.)
Instead, I modified the loop that loads the data from the tab-delimited text file to use some TryParse() calls to detect dates, numbers, or regular text data, and then load the appropriately typed data into the cell. Note how it checks for a leading single quote character to suppress the data typing if the cell is actually text, but looks like a number or a date:
// Load the data into the cells
Int32 rowIdx = 1;
foreach (String line in tab.Lines)
{
String[] cellTexts = line.Split(TAB);
Int32 colIdx = 1;
foreach (String cellText in cellTexts)
{
DateTime dateValue;
Double doubleValue;
if(cellText.StartsWith("'"))
{
sheet.Cells[rowIdx, colIdx].Value = cellText.Substring(1);
}
else if(DateTime.TryParse(cellText,out dateValue))
{
sheet.Cells[rowIdx, colIdx].Value = dateValue;
}
else if (Double.TryParse(cellText, out doubleValue))
{
sheet.Cells[rowIdx, colIdx].Value = doubleValue;
}
else
{
sheet.Cells[rowIdx, colIdx].Value = cellText;
}
colIdx++;
}
rowIdx++;
}
With the data typed appropriately in the cells, the formats have the desired effect.

Export to Excel using OpenXML and C#, Integers and DateTime

I'm writing an application that's supposed to export data from a map application.
This application is using Silverlight, and to facilitate export to Excel I am using this library.
All of the data is represented in strings by default. When I write to the spreadsheet, I try to parse each string to see which type it is:
string str = kvp.Value;
int i = 0;
long l = 0;
decimal dec = 0;
DateTime dt;
if (int.TryParse(str, out i))
doc.Workbook.Sheets[0].Sheet.Rows[r].Cells[c].SetValue(i);
else if (decimal.TryParse(str, out dec))
doc.Workbook.Sheets[0].Sheet.Rows[r].Cells[c].SetValue(dec);
else if (long.TryParse(str, out l))
doc.Workbook.Sheets[0].Sheet.Rows[r].Cells[c].SetValue(l);
else if (DateTime.TryParse(str, out dt))
doc.Workbook.Sheets[0].Sheet.Rows[r].Cells[c].SetValue(dt);
else
doc.Workbook.Sheets[0].Sheet.Rows[r].Cells[c].SetValue(str);
This works great, except for DateTime and when I try to parse a social security number, which in my case is 12 characters long.
The social security number is parsed as a decimal number, and is displayed in scientific form in Excel. From what I've gathered through reading it seems like a limitation in Excel. If I mark the cell however, I see the correct number in the top bar where you can write formulas. I've solved this problem so far by simply putting this number as a string and letting the end user handle it for now, but I'd really like for it to be a number in the finished document. Is this possible?
What really boggles my mind though, is the DateTime. The format of the date comes like this from the application: 10/15/2013 1:10:00 AM.
It looks like this in the Excel file: 2455075.
I checked the source code for the date formatting but I don't seem to be adept enough to see if there is anything wrong in it. For anyone intresed, you can check it out here.
The SetValue-function is supposed to identify the following types automatically:
bool
DateTime
decimal
Exception
SharedStringDefinition
string
I apologize for the long post. It boils down to these questions:
Can I make Excel handle long numbers without scientific notation programatically?
Why is the DateTime being outputed to such a weird format?
To be set Cell Value in Date format you have to convert DateTime to OLE Automation Date. Also you can create more clear method for writing cell values. Somthing like this:
public bool UpdateValue(WorkbookPart wbPart, string sheetName, string addressName, string value,
UInt32Value styleIndex, CellValues dataType)
{
// Assume failure.
bool updated = false;
Sheet sheet = wbPart.Workbook.Descendants<Sheet>().Where(
(s) => s.Name == sheetName).FirstOrDefault();
if (sheet != null)
{
Worksheet ws = ((WorksheetPart)(wbPart.GetPartById(sheet.Id))).Worksheet;
Cell cell = InsertCellInWorksheet(ws, addressName);
if (dataType == CellValues.SharedString)
{
// Either retrieve the index of an existing string,
// or insert the string into the shared string table
// and get the index of the new item.
int stringIndex = InsertSharedStringItem(wbPart, value);
cell.CellValue = new CellValue(stringIndex.ToString());
}
else
{
cell.CellValue = new CellValue(value);
}
cell.DataType = new EnumValue<CellValues>(dataType);
if (styleIndex > 0)
cell.StyleIndex = styleIndex;
// Save the worksheet.
ws.Save();
updated = true;
}
return updated;
}
Then call this method like this (first call is for String second is for DateTime):
UpdateValue(workbookPart, wsName, "D14", "Some text", 0, CellValues.String);
UpdateValue(workbookPart, wsName, "H13", DateTime.Parse("2013-11-01").ToOADate().ToString(CultureInfo.InvariantCulture), 0, CellValues.Date);

Categories