Reputation: 3720
I'm building a tool to simply the exporting to excel for later re-use. One of the issues I'm running into is an easy, and reliable way of passing around an object that contains my cell data.
The class that contains my cell data looks like:
public interface IExcelCell {
}
public class ExcelCell<T> : IExcelCell
{
public T Value { get; set; }
}
And I'm using it like so:
private void GenerateCell(IExcelCell cell, int currentColumn)
{
// check type of Value, apply optional formatting
}
Essentially what I need to do is check if the incoming data is of a certain data-type, e.g. string, double, DateTime. Is there a convenient way of doing this?
Edit
This is the relevant exportation code to illustrate somewhat of what I'm trying to do:
public class ExcelExporter
{
private ExcelPackage excelPackage;
private OfficeOpenXml.ExcelWorksheet currentWorksheet;
private int currentRow;
public ExcelPackage ExportExcelWorkbook(ExcelWorkbook workbook)
{
excelPackage = new ExcelPackage();
GenerateWorkbook(workbook);
return excelPackage;
}
private void GenerateWorkbook(ExcelWorkbook workbook)
{
foreach (var worksheet in workbook.Worksheets)
{
GenerateWorksheet(worksheet);
}
}
private void GenerateWorksheet(ExcelWorksheet worksheet)
{
currentWorksheet = excelPackage.Workbook.Worksheets.Add(worksheet.Name);
currentRow = 1;
foreach (var section in worksheet.Sections)
{
GenerateSection(section);
}
}
private void GenerateSection(ExcelSection section)
{
if (!section.Name.IsNullOrEmpty())
{
currentWorksheet.Cells[currentRow, 1].Value = section.Name;
currentRow++;
}
if (section.Headers != null && section.Headers.Any())
{
GenerateHeaders(section.Headers);
currentRow++;
}
foreach (var row in section.Rows)
{
GenerateRow(row);
currentRow++;
}
}
private void GenerateHeaders(IEnumerable<string> headers)
{
var enumerable = headers as IList<string> ?? headers.ToList();
for (var i = 0; i < enumerable.Count(); i++)
{
currentWorksheet.Cells[currentRow, i + 1].Value = enumerable[i];
}
}
private void GenerateRow(ExcelRow row)
{
var currentColumn = 0;
foreach (var cell in row.RowData)
{
GenerateCell(cell, currentColumn);
currentColumn++;
}
}
private void GenerateCell(IExcelCell cell, int currentColumn)
{
var excelCell = (ExcelCell<object>) cell;
if (excelCell.Value is DateTime)
{
currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = "mm/dd/yyyy";
}
currentWorksheet.Cells[currentRow, currentColumn].Value = excelCell.Value;
}
}
Edit 2
Taking Gilad's advice from his answer, I came up with a slightly different solution:
// interface
public interface IExcelCell {
object Value { get; set; }
string NumberFormat { get; }
}
// implementation
public class ExcelCell : IExcelCell
{
public object Value { get; set; }
public string NumberFormat => null;
}
public class DateTimeExcelCell : IExcelCell
{
public object Value { get; set; }
public string NumberFormat => "mm/dd/yyyy";
}
// Cell generation
private void GenerateCell(IExcelCell cell, int currentColumn)
{
if (cell.NumberFormat != null)
{
currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = cell.NumberFormat;
}
currentWorksheet.Cells[currentRow, currentColumn].Value = cell.Value;
}
Upvotes: 2
Views: 62
Reputation: 37281
Seeing updated question:
As I suggested bellow use the is
operator on cell
:
if (cell is ExcelCell<DateTime>)
{
currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = "mm/dd/yyyy";
}
But still see answer below about the Factory pattern part
Original answer:
I guess cleanest will be to declare the interface as generic and have the Value
in it. If not then you can use the is
operator. For example:
IExcelCell cell = new ExcelCell<DateTime>();
bool result1 = cell is ExcelCell<DateTime>; // true
bool result2 = cell is ExcelCell<int>; // false
So in your function:
private void GenerateCell(IExcelCell cell, int currentColumn)
{
if(cell is ExcelCell<DateTime>)
{
}
}
Or the as
operator if you want to use it:
private void GenerateCell(IExcelCell cell, int currentColumn)
{
var c = cell as ExcelCell<DateTime>
if(c != null)
{
// TODO - use casted c
}
}
From your comments it seems like the purpose of knowing the type is to perform formatting. I suggest that instead have a Factory pattern that given a specific type formats that specific type. Or if you change the interface then something like:
public interface IExcelCell<T>
{
T Value { get; set; }
}
public class DateTimeExcelCell : IExcelCell<DateTime>
{
public DateTime Value { get; set; }
public override string ToString()
{
// TODO - formatting you want for DateTime
}
}
Upvotes: 3