I am using EPPlus Library to create an Excel report.
So far I have loaded the DataTable on sheet by using this code:
string FileName = #"DataSource\sampleData.xml";
var excelDocName = #"C:\Excel\OutExcel.xlsx";
var aFile = new FileInfo(excelDocName);
if (File.Exists(excelDocName))
File.Delete(excelDocName);
DataSet ds = new DataSet();
ds.ReadXml(FileName);
DataTable xmlTable = ds.Tables[0];
using (ExcelPackage pck = new ExcelPackage(aFile))
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("DataLoad");
ws.Cells["A1"].LoadFromDataTable(xmlTable, true);
ws.Cells.AutoFitColumns();
pck.Save();
}
The result is perfect, but what I need to is
Merge the rows having same values in Column A and based on that I need to merge rows in Column B,C and D.
Sum range of Values in Column E based on Column A and merge rows in column E and show the Sum result.
I am attaching the screenshot of what I am getting and what result is needed and also share the Excel file and XML Data Source to quickly use the code to generate Excel.
XML DataSource
Excel Result Image
As Suggested by 'alhashmiya', I have change the logic and iterate DataTable row by row to insert and during insertion I hold the specific columns in a variable to update the merge range.
Below code is for any one in future who is having same requirement,
string FileName = #"DataSource\sampleData.xml";
var excelDocName = #"c:\temp\OutExcel.xlsx";
var aFile = new FileInfo(excelDocName);
DataSet ds = new DataSet();
ds.ReadXml(FileName);
DataTable table = ds.Tables[0];
using (ExcelPackage pck = new ExcelPackage(aFile))
{
ExcelWorksheet ws = pck.Workbook.Worksheets[1];
const int startRow = 2;
int mergestarttrow = 2;
int rw = startRow;
int RefNo = 0;
int TTLCONS = 0;
string[] mergedColNamed = new string[] { "SNO", "_NAME", "CAUSE_SUBCAUSE", "_AREAS", "CONS", "GRIDNAM" };
Dictionary<string, string> mergeRange = new Dictionary<string, string>() { { "SNO", "A" }, { "_NAME", "B" }, { "CAUSE_SUBCAUSE", "C" }, { "_AREAS", "D" }, { "CONS", "E" }, { "GRIDNAM", "F" } };
if (table.Rows.Count != 0)
{
foreach (DataRow row in table.Rows)
{
bool needMerge = false;
bool checkMerge = false;
//Hold to compare with next iteration and based to condition match Enable Check Merge Flag
int CurrentRefNo = System.Convert.ToInt32(row["SNO"]);
if (RefNo > 0)
{
if (RefNo == CurrentRefNo)
{
checkMerge = true;
mergeRange = mergeRange = mergeRange = new Dictionary<string, string>() { { "SNO", "A" }, { "_NAME", "B" }, { "CAUSE_SUBCAUSE", "C" }, { "_AREAS", "D" }, { "CONS", "E" }, { "GRIDNAM", "F" } };
}
else
{
mergestarttrow = rw;
needMerge = true;
}
}
int col = 1;
if (rw > startRow)
ws.InsertRow(rw, 1);
if (needMerge)
{
for (int i = 0; i < mergedColNamed.Length; i++)
{
string MergeRangeValue = mergeRange.FirstOrDefault(x => x.Key == mergedColNamed[i]).Value;
using (ExcelRange Rng = ws.Cells[MergeRangeValue])
{
Rng.Merge = true;
ws.Cells[MergeRangeValue].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
ws.Cells[MergeRangeValue].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
Rng.Style.Border.Top.Style = ExcelBorderStyle.Thin;
Rng.Style.Border.Left.Style = ExcelBorderStyle.Thin;
Rng.Style.Border.Right.Style = ExcelBorderStyle.Thin;
Rng.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
if (mergedColNamed[i] == "CONS")
{
Rng.Value = TTLCONS;
TTLCONS = 0;
Rng.Style.Numberformat.Format = "0";
}
if (mergedColNamed[i] == "SNO")
{
Rng.Style.Numberformat.Format = "0";
}
}
}
mergeRange = new Dictionary<string, string>() { { "SNO", "A" }, { "_NAME", "B" }, { "CAUSE_SUBCAUSE", "C" }, { "_AREAS", "D" }, { "CONS", "E" }, { "GRIDNAM", "F" } };
}
foreach (DataColumn dc in table.Columns)
{
if (mergedColNamed.Contains(dc.ColumnName.ToUpper()))
{
if (dc.ColumnName.ToUpper() == "CONS")
TTLCONS = TTLCONS + System.Convert.ToInt32(row[dc]);
if (!checkMerge)//First Row
{
if (dc.ColumnName.ToUpper() == "SNO")
RefNo = System.Convert.ToInt32(row[dc]);
ws.Cells[rw, col].Value = row[dc].ToString();
ws.Cells[rw, col].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
ws.Cells[rw, col].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
ws.Cells[rw, col].Style.Border.Top.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Left.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Right.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
if (dc.ColumnName.ToUpper() == "CONS")
{
ws.Cells[rw, col].Style.Numberformat.Format = "0";
}
}
else //No Need to set Cell Values as ultimately we will merge only updating the merge range
{
string MergeRangeKey = mergeRange.FirstOrDefault(x => x.Value == mergeRange[dc.ColumnName.ToUpper()]).Key;
string MergeRangeValue = mergeRange[dc.ColumnName.ToUpper()];
mergeRange[MergeRangeKey] = string.Format(mergeRange[dc.ColumnName.ToUpper()] + "{0}:" + mergeRange[dc.ColumnName.ToUpper()] + "{1}", mergestarttrow, rw);
}
}
else
{
ws.Cells[rw, col].Value = row[dc].ToString();
ws.Cells[rw, col].Style.Border.Top.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Left.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Right.Style = ExcelBorderStyle.Thin;
ws.Cells[rw, col].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
}
col++;
}
rw++;
}
}
ws.Cells.AutoFitColumns();
pck.Save();
System.Diagnostics.Process.Start(excelDocName);
Related
I am working on a program that uses comboboxes that are populated from a csv file, I initially just used streamreader to populate the combobox, but I was instructed to use gembox spreadsheet to populate it instead.
Here is the streamreader code that I used:
private static List<ItemLine> Readfile(string fileName, string defaultValueDescription)
{
string path = $".\\Backend Files\\{fileName}";
var items = new List<ItemLine>();
int row = 0;
//read the file
StreamReader reader = new StreamReader(path);
while (reader.EndOfStream == false)
{
string lines = reader.ReadLine();
row++;
if (row == 1)
{
items.Add(new ItemLine { Description = defaultValueDescription, Quantity = 0 });
continue;
}
string[] itemFields = lines.Split(",");
ItemLine IL = new ItemLine();
if (itemFields.Length == 2)
{
IL.Description = itemFields[0];
IL.Quantity = 0;
if (int.TryParse(itemFields[1], out int qty))
{
IL.Quantity = qty;
}
}
else if (itemFields.Length >= 3)
{
IL.Plan = itemFields[0];
IL.Description = itemFields[1];
IL.Quantity = 0;
if (int.TryParse(itemFields[2], out int qty))
{
IL.Quantity = qty;
}
}
///Add Items to List
items.Add(IL);
}
reader.Close();
return items;
}
Here is an example of what I used gembox spreadsheet for in another part of my program which I use to update the csv files with, I think the code I am trying to figure out would look something like:
public void updateFile(string filename, int range, ItemLine selectedItem, decimal outValue)
{
var fullPath = $".\\Backend Files\\{filename}";
SpreadsheetInfo.SetLicense("FREE -LIMITED-KEY");
var workbook = ExcelFile.Load(fullPath, new CsvLoadOptions(CsvType.CommaDelimited));
var worksheet = workbook.Worksheets[0];
for (int i = 1; i <= range; i++)
{
var plan = worksheet.Rows[i].Cells[0].Value;
var desc = worksheet.Rows[i].Cells[1].Value;
var csvDescription = plan + " - " + desc;
var platedescription = plan + " - ";
if (selectedItem.ComboDescription == csvDescription)
{
worksheet.Rows[i].Cells[2].Value = outValue;
}
if (selectedItem.plateDescription == platedescription)
{
worksheet.Rows[i].Cells[1].Value = outValue;
}
}
workbook.Save(fullPath, new CsvSaveOptions(CsvType.CommaDelimited));
}
Try this:
private static List<ItemLine> ReadFile(string fileName, string defaultValueDescription)
{
string path = $".\\Backend Files\\{filename}";
var workbook = ExcelFile.Load(path, new CsvLoadOptions(CsvType.CommaDelimited));
var worksheet = workbook.Worksheets[0];
var items = new List<ItemLine>();
items.Add(new ItemLine { Description = defaultValueDescription, Quantity = 0 });
foreach (var row in worksheet.Rows)
{
var cells = row.AllocatedCells;
var il = new ItemLine();
items.Add(il);
if (cells.Count == 2)
{
il.Description = cells[0].Value.ToString();
il.Quantity = cells[1].ValueType == CellValueType.Int ? cells[1].IntValue : 0;
}
else if (cells.Count >= 3)
{
il.Plan = cells[0].Value.ToString();
il.Description = cells[1].Value.ToString();
il.Quantity = cells[2].ValueType == CellValueType.Int ? cells[2].IntValue : 0;
}
}
return items;
}
Also, I'm not sure about the logic around the defaultValueDescription, it seems that in your StreamReader approach you just skip the first line in CSV because of it.
If that is the intended behavior, then add the .Skip(1) extension method from System.Linq, like this:
foreach (var row in worksheet.Rows.Skip(1))
{
// ...
}
This question is answered on a basic level on another post: here However for my case I am not able to hard code the validation values into the sheet I am pulling them from a database based on the content of the cell and will need to do a separate validation for 4 columns on every row. Is there a way this can be achieved? Thank you in advance.
// Data Validations //
// Product Validation //
for (int i = 2; i < rowCount; i++)
{
var val = ws.DataValidations.AddListValidation(ws.Cells[i, 5].Address);
val.ShowErrorMessage = true;
val.ErrorTitle = "Entry was invalid.";
val.Error = "Please choose options from the drop down only.";
var ticketEntity = ticketQueryable.Where(o => o.TTSTicketNumber == ws.Cells[i, 3].Value.ToString()).Single<CustCurrentTicketEntity>();
var prodIds = prodExtQueryable.Where(p => p.ZoneId == ticketEntity.ZoneId && p.TicketTypeId == ticketEntity.TicketTypeId);
if (ticketEntity != null)
{
var prodIdsList = new List<int>();
foreach (var prodId in prodIds)
{
prodIdsList.Add(prodId.ProductId);
}
var ProductList = ProductCache.Instance.AllProducts.Where(p => prodIdsList.Contains(p.ProductId)).Select(p => new SelectListItem() { Value = p.ProductId.ToString(), Text = p.Name });
foreach (var Result in ProductList)
{
var product = Result.Text;
val.Formula.Values.Add(product);
}
}
}
So yes as Ernie said What I did was add a second sheet "ProductValidations" and set it to Hidden (unhide it to check that it is working). I then Load my data from the DataTable and then add some basic EPPLUS formatting. I then iterate over the Rows and Insert values into the "ProductValidations" sheet for each cell. Next I convert my column number to the correct Excel Column letter name (A, AC, BCE etc) I then create a string to pass back as an Excel formula targeting the correct range of cells in the "ProductValidations" sheet. Also to anyone having an issue downloading the Excel file from the server this guid method works just fine for me.
public ActionResult DownloadExcel(EntityReportModel erModel, string filename)
{
var dataResponse = iEntityViewService.LoadEntityView(new EntityViewInput
{
SecurityContext = SessionCache.Instance.SecurityContext,
EntityViewName = "Ticket",
Parameters = new Dictionary<string, object> {
{"MinTicketDateTime", "04/26/16"}
}
});
var table = dataResponse.DataSet.Tables[0];
filename = "TICKETS-" + DateTime.Now.ToString("yyyy-MM-dd--hh-mm-ss") + ".xlsx";
using (ExcelPackage pack = new ExcelPackage())
{
ExcelWorksheet ws = pack.Workbook.Worksheets.Add(filename);
//Add second sheet to put Validations into
ExcelWorksheet productVal = pack.Workbook.Worksheets.Add("ProductValidations");
// Hide Validation Sheet
productVal.Hidden = OfficeOpenXml.eWorkSheetHidden.Hidden;
// Load the data from the datatable
ws.Cells["A1"].LoadFromDataTable(table, true);
ws.Cells[ws.Dimension.Address].AutoFitColumns();
int columnCount = table.Columns.Count;
int rowCount = table.Rows.Count;
// Format Worksheet//
ws.Row(1).Style.Font.Bold = true;
List<string> deleteColumns = new List<string>() {
"CurrentTicketId",
};
List<string> dateColumns = new List<string>() {
"TicketDateTime",
"Updated",
"InvoiceDate"
};
ExcelRange r;
// Format Dates
for (int i = 1; i <= columnCount; i++)
{
// if cell header value matches a date column
if (dateColumns.Contains(ws.Cells[1, i].Value.ToString()))
{
r = ws.Cells[2, i, rowCount + 1, i];
r.AutoFitColumns();
r.Style.Numberformat.Format = #"mm/dd/yyyy hh:mm";
}
}
// Delete Columns
for (int i = 1; i <= columnCount; i++)
{
// if cell header value matches a delete column
if ((ws.Cells[1, i].Value != null) && deleteColumns.Contains(ws.Cells[1, i].Value.ToString()))
{
ws.DeleteColumn(i);
}
}
int col = 0;
int Prow = 0;
int valRow = 1;
// Data Validations //
// Iterate over the Rows and insert Validations
for (int i = 2; i-2 < rowCount; i++)
{
Prow = 0;
col++;
valRow++;
// Add Validations At this row in column 7 //
var ProdVal = ws.DataValidations.AddListValidation(ws.Cells[valRow, 7].Address);
ProdVal.ShowErrorMessage = true;
ProdVal.ErrorTitle = "Entry was invalid.";
ProdVal.Error = "Please choose options from the drop down only.";
var ticketEntity = ticketQueryable.Where(o => o.TTSTicketNumber == ws.Cells[i, 3].Value.ToString()).Single<CustCurrentTicketEntity>();
// Product Validation //
var prodIds = prodExtQueryable.Where(p => p.ZoneId == ticketEntity.ZoneId && p.TicketTypeId == ticketEntity.TicketTypeId);
if (ticketEntity != null)
{
var prodIdsList = new List<int>();
foreach (var prodId in prodIds)
{
prodIdsList.Add(prodId.ProductId);
}
var ProductList = ProductCache.Instance.AllProducts.Where(p => prodIdsList.Contains(p.ProductId)).Select(p => new SelectListItem() { Value = p.ProductId.ToString(), Text = p.Name });
//For Each Item in the list move the row forward and add that value to the Validation Sheet
foreach (var Result in ProductList)
{
Prow++;
var product = Result.Text;
productVal.Cells[Prow, col].Value = product;
}
// convert column name from a number to the Excel Letters i.e A, AC, BCE//
int dividend = col;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
// Pass back to sheeet as an Excel Formula to get around the 255 Char limit for Validations//
string productValidationExcelFormula = "ProductValidations!" + columnName + "1:" + columnName + Prow;
ProdVal.Formula.ExcelFormula = productValidationExcelFormula;
}
}
// Save File //
var fileStream = new MemoryStream(pack.GetAsByteArray());
string handle = Guid.NewGuid().ToString();
fileStream.Position = 0;
TempData[handle] = fileStream.ToArray();
// Note we are returning a filename as well as the handle
return new JsonResult()
{
Data = new { FileGuid = handle, FileName = filename }
};
}
}
[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
byte[] data = TempData[fileGuid] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
//Log err
return new EmptyResult();
}
}
Using EPPlus, I want to read an excel table, then store all the contents from each column into its corresponding List. I want it to recognize the table's heading and categorize the contents based on that.
For example, if my excel table is as below:
Id Name Gender
1 John Male
2 Maria Female
3 Daniel Unknown
I want the data to store in List<ExcelData> where
public class ExcelData
{
public string Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
So that I can call out the contents using the heading name. For example, when I do this:
foreach (var data in ThatList)
{
Console.WriteLine(data.Id + data.Name + data.Gender);
}
It will give me this output:
1JohnMale
2MariaFemale
3DanielUnknown
This is really all I got:
var package = new ExcelPackage(new FileInfo(#"C:\ExcelFile.xlsx"));
ExcelWorksheet sheet = package.Workbook.Worksheets[1];
var table = sheet.Tables.First();
table.Columns.Something //I guess I can use this to do what I want
Please help :(
I have spent long hours searching for sample code regarding this so that I can learn from it but to no avail. I also understand ExcelToLinQ is managed to do that but it can't recognize table.
Not sure why but none of the above solution work for me.
So sharing what worked:
public void readXLS(string FilePath)
{
FileInfo existingFile = new FileInfo(FilePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
//get the first worksheet in the workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
int colCount = worksheet.Dimension.End.Column; //get Column Count
int rowCount = worksheet.Dimension.End.Row; //get row count
for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= colCount; col++)
{
Console.WriteLine(" Row:" + row + " column:" + col + " Value:" + worksheet.Cells[row, col].Value?.ToString().Trim());
}
}
}
}
There is no native but what if you use what I put in this post:
How to parse excel rows back to types using EPPlus
If you want to point it at a table only it will need to be modified. Something like this should do it:
public static IEnumerable<T> ConvertTableToObjects<T>(this ExcelTable table) where T : new()
{
//DateTime Conversion
var convertDateTime = new Func<double, DateTime>(excelDate =>
{
if (excelDate < 1)
throw new ArgumentException("Excel dates cannot be smaller than 0.");
var dateOfReference = new DateTime(1900, 1, 1);
if (excelDate > 60d)
excelDate = excelDate - 2;
else
excelDate = excelDate - 1;
return dateOfReference.AddDays(excelDate);
});
//Get the properties of T
var tprops = (new T())
.GetType()
.GetProperties()
.ToList();
//Get the cells based on the table address
var start = table.Address.Start;
var end = table.Address.End;
var cells = new List<ExcelRangeBase>();
//Have to use for loops insteadof worksheet.Cells to protect against empties
for (var r = start.Row; r <= end.Row; r++)
for (var c = start.Column; c <= end.Column; c++)
cells.Add(table.WorkSheet.Cells[r, c]);
var groups = cells
.GroupBy(cell => cell.Start.Row)
.ToList();
//Assume the second row represents column data types (big assumption!)
var types = groups
.Skip(1)
.First()
.Select(rcell => rcell.Value.GetType())
.ToList();
//Assume first row has the column names
var colnames = groups
.First()
.Select((hcell, idx) => new { Name = hcell.Value.ToString(), index = idx })
.Where(o => tprops.Select(p => p.Name).Contains(o.Name))
.ToList();
//Everything after the header is data
var rowvalues = groups
.Skip(1) //Exclude header
.Select(cg => cg.Select(c => c.Value).ToList());
//Create the collection container
var collection = rowvalues
.Select(row =>
{
var tnew = new T();
colnames.ForEach(colname =>
{
//This is the real wrinkle to using reflection - Excel stores all numbers as double including int
var val = row[colname.index];
var type = types[colname.index];
var prop = tprops.First(p => p.Name == colname.Name);
//If it is numeric it is a double since that is how excel stores all numbers
if (type == typeof(double))
{
if (!string.IsNullOrWhiteSpace(val?.ToString()))
{
//Unbox it
var unboxedVal = (double)val;
//FAR FROM A COMPLETE LIST!!!
if (prop.PropertyType == typeof(Int32))
prop.SetValue(tnew, (int)unboxedVal);
else if (prop.PropertyType == typeof(double))
prop.SetValue(tnew, unboxedVal);
else if (prop.PropertyType == typeof(DateTime))
prop.SetValue(tnew, convertDateTime(unboxedVal));
else
throw new NotImplementedException(String.Format("Type '{0}' not implemented yet!", prop.PropertyType.Name));
}
}
else
{
//Its a string
prop.SetValue(tnew, val);
}
});
return tnew;
});
//Send it back
return collection;
}
Here is a test method:
[TestMethod]
public void Table_To_Object_Test()
{
//Create a test file
var fi = new FileInfo(#"c:\temp\Table_To_Object.xlsx");
using (var package = new ExcelPackage(fi))
{
var workbook = package.Workbook;
var worksheet = workbook.Worksheets.First();
var ThatList = worksheet.Tables.First().ConvertTableToObjects<ExcelData>();
foreach (var data in ThatList)
{
Console.WriteLine(data.Id + data.Name + data.Gender);
}
package.Save();
}
}
Gave this in the console:
1JohnMale
2MariaFemale
3DanielUnknown
Just be careful if you Id field is an number or string in excel since the class is expecting a string.
This is my working version. Note that the resolvers code is not shown but are a spin on my implementation which allows columns to be resolved even though they are named slightly differently in each worksheet.
public static IEnumerable<T> ToArray<T>(this ExcelWorksheet worksheet, List<PropertyNameResolver> resolvers) where T : new()
{
// List of all the column names
var header = worksheet.Cells.GroupBy(cell => cell.Start.Row).First();
// Get the properties from the type your are populating
var properties = typeof(T).GetProperties().ToList();
var start = worksheet.Dimension.Start;
var end = worksheet.Dimension.End;
// Resulting list
var list = new List<T>();
// Iterate the rows starting at row 2 (ie start.Row + 1)
for (int row = start.Row + 1; row <= end.Row; row++)
{
var instance = new T();
for (int col = start.Column; col <= end.Column; col++)
{
object value = worksheet.Cells[row, col].Text;
// Get the column name zero based (ie col -1)
var column = (string)header.Skip(col - 1).First().Value;
// Gets the corresponding property to set
var property = properties.Property(resolvers, column);
try
{
var propertyName = property.PropertyType.IsGenericType
? property.PropertyType.GetGenericArguments().First().FullName
: property.PropertyType.FullName;
// Implement setter code as needed.
switch (propertyName)
{
case "System.String":
property.SetValue(instance, Convert.ToString(value));
break;
case "System.Int32":
property.SetValue(instance, Convert.ToInt32(value));
break;
case "System.DateTime":
if (DateTime.TryParse((string) value, out var date))
{
property.SetValue(instance, date);
}
property.SetValue(instance, FromExcelSerialDate(Convert.ToInt32(value)));
break;
case "System.Boolean":
property.SetValue(instance, (int)value == 1);
break;
}
}
catch (Exception e)
{
// instance property is empty because there was a problem.
}
}
list.Add(instance);
}
return list;
}
// Utility function taken from the above post's inline function.
public static DateTime FromExcelSerialDate(int excelDate)
{
if (excelDate < 1)
throw new ArgumentException("Excel dates cannot be smaller than 0.");
var dateOfReference = new DateTime(1900, 1, 1);
if (excelDate > 60d)
excelDate = excelDate - 2;
else
excelDate = excelDate - 1;
return dateOfReference.AddDays(excelDate);
}
Below code will read excel data into a datatable, which is converted to list of datarows.
if (FileUpload1.HasFile)
{
if (Path.GetExtension(FileUpload1.FileName) == ".xlsx")
{
Stream fs = FileUpload1.FileContent;
ExcelPackage package = new ExcelPackage(fs);
DataTable dt = new DataTable();
dt= package.ToDataTable();
List<DataRow> listOfRows = new List<DataRow>();
listOfRows = dt.AsEnumerable().ToList();
}
}
using OfficeOpenXml;
using System.Data;
using System.Linq;
public static class ExcelPackageExtensions
{
public static DataTable ToDataTable(this ExcelPackage package)
{
ExcelWorksheet workSheet = package.Workbook.Worksheets.First();
DataTable table = new DataTable();
foreach (var firstRowCell in workSheet.Cells[1, 1, 1, workSheet.Dimension.End.Column])
{
table.Columns.Add(firstRowCell.Text);
}
for (var rowNumber = 2; rowNumber <= workSheet.Dimension.End.Row; rowNumber++)
{
var row = workSheet.Cells[rowNumber, 1, rowNumber, workSheet.Dimension.End.Column];
var newRow = table.NewRow();
foreach (var cell in row)
{
newRow[cell.Start.Column - 1] = cell.Text;
}
table.Rows.Add(newRow);
}
return table;
}
}
Yet another way to do it.
I used Ernie S solution but it didn't work if I had empty cells in the first data row (it wasn't able to guess the data type from it).
So instead of getting a data type from Excel table I get it from the T parameter class properties using reflection.
/// <summary>
/// Converts table to list of T objects
/// </summary>
/// <typeparam name="T">The type to return</typeparam>
/// <param name="table">Data source</param>
/// <returns>List of T objects</returns>
public static IEnumerable<T> ConvertToObjects<T>(this ExcelTable table) where T : new()
{
ExcelCellAddress start = table.Address.Start;
ExcelCellAddress end = table.Address.End;
List<ExcelRange> cells = new();
for (int r = start.Row; r <= end.Row; r++)
for (int c = start.Column; c <= end.Column; c++)
cells.Add(table.WorkSheet.Cells[r, c]);
List<IGrouping<int, ExcelRange>> allRows = cells
.GroupBy(cell => cell.Start.Row)
.OrderBy(cell => cell.Key)
.ToList();
IEnumerable<PropertyInfo> typeProperties = typeof(T).GetProperties();
IGrouping<int, ExcelRangeBase> header = allRows.First();
Dictionary<PropertyInfo, int> columns = new();
foreach (ExcelRangeBase col in header)
{
string propName = col.GetValue<string>();
PropertyInfo propInfo = typeProperties.FirstOrDefault(x => x.Name.Equals(propName));
if (propInfo != null)
{
columns.Add(propInfo, col.Start.Column);
}
}
IEnumerable<IGrouping<int, ExcelRangeBase>> rows = allRows.Skip(1);
List<T> objects = new();
foreach (IGrouping<int, ExcelRangeBase> row in rows)
{
T obj = new();
foreach (KeyValuePair<PropertyInfo, int> colInfo in columns)
{
ExcelRangeBase col = row.First(x => x.Start.Column == colInfo.Value);
if (col.Value == null)
continue;
object value = Convert.ChangeType(col.Value, Nullable.GetUnderlyingType(colInfo.Key.PropertyType) ?? colInfo.Key.PropertyType);
colInfo.Key.SetValue(obj, value);
}
objects.Add(obj);
}
return objects;
}
I have got an error on the first answer so I have changed some code line.
Please try my new code, it's working for me.
using OfficeOpenXml;
using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class ImportExcelReader
{
public static List<T> ImportExcelToList<T>(this ExcelWorksheet worksheet) where T : new()
{
//DateTime Conversion
Func<double, DateTime> convertDateTime = new Func<double, DateTime>(excelDate =>
{
if (excelDate < 1)
{
throw new ArgumentException("Excel dates cannot be smaller than 0.");
}
DateTime dateOfReference = new DateTime(1900, 1, 1);
if (excelDate > 60d)
{
excelDate = excelDate - 2;
}
else
{
excelDate = excelDate - 1;
}
return dateOfReference.AddDays(excelDate);
});
ExcelTable table = null;
if (worksheet.Tables.Any())
{
table = worksheet.Tables.FirstOrDefault();
}
else
{
table = worksheet.Tables.Add(worksheet.Dimension, "tbl" + ShortGuid.NewGuid().ToString());
ExcelAddressBase newaddy = new ExcelAddressBase(table.Address.Start.Row, table.Address.Start.Column, table.Address.End.Row + 1, table.Address.End.Column);
//Edit the raw XML by searching for all references to the old address
table.TableXml.InnerXml = table.TableXml.InnerXml.Replace(table.Address.ToString(), newaddy.ToString());
}
//Get the cells based on the table address
List<IGrouping<int, ExcelRangeBase>> groups = table.WorkSheet.Cells[table.Address.Start.Row, table.Address.Start.Column, table.Address.End.Row, table.Address.End.Column]
.GroupBy(cell => cell.Start.Row)
.ToList();
//Assume the second row represents column data types (big assumption!)
List<Type> types = groups.Skip(1).FirstOrDefault().Select(rcell => rcell.Value.GetType()).ToList();
//Get the properties of T
List<PropertyInfo> modelProperties = new T().GetType().GetProperties().ToList();
//Assume first row has the column names
var colnames = groups.FirstOrDefault()
.Select((hcell, idx) => new
{
Name = hcell.Value.ToString(),
index = idx
})
.Where(o => modelProperties.Select(p => p.Name).Contains(o.Name))
.ToList();
//Everything after the header is data
List<List<object>> rowvalues = groups
.Skip(1) //Exclude header
.Select(cg => cg.Select(c => c.Value).ToList()).ToList();
//Create the collection container
List<T> collection = new List<T>();
foreach (List<object> row in rowvalues)
{
T tnew = new T();
foreach (var colname in colnames)
{
//This is the real wrinkle to using reflection - Excel stores all numbers as double including int
object val = row[colname.index];
Type type = types[colname.index];
PropertyInfo prop = modelProperties.FirstOrDefault(p => p.Name == colname.Name);
//If it is numeric it is a double since that is how excel stores all numbers
if (type == typeof(double))
{
//Unbox it
double unboxedVal = (double)val;
//FAR FROM A COMPLETE LIST!!!
if (prop.PropertyType == typeof(int))
{
prop.SetValue(tnew, (int)unboxedVal);
}
else if (prop.PropertyType == typeof(double))
{
prop.SetValue(tnew, unboxedVal);
}
else if (prop.PropertyType == typeof(DateTime))
{
prop.SetValue(tnew, convertDateTime(unboxedVal));
}
else if (prop.PropertyType == typeof(string))
{
prop.SetValue(tnew, val.ToString());
}
else
{
throw new NotImplementedException(string.Format("Type '{0}' not implemented yet!", prop.PropertyType.Name));
}
}
else
{
//Its a string
prop.SetValue(tnew, val);
}
}
collection.Add(tnew);
}
return collection;
}
}
How to call this function? please view below code;
private List<FundraiserStudentListModel> GetStudentsFromExcel(HttpPostedFileBase file)
{
List<FundraiserStudentListModel> list = new List<FundraiserStudentListModel>();
if (file != null)
{
try
{
using (ExcelPackage package = new ExcelPackage(file.InputStream))
{
ExcelWorkbook workbook = package.Workbook;
if (workbook != null)
{
ExcelWorksheet worksheet = workbook.Worksheets.FirstOrDefault();
if (worksheet != null)
{
list = worksheet.ImportExcelToList<FundraiserStudentListModel>();
}
}
}
}
catch (Exception err)
{
//save error log
}
}
return list;
}
FundraiserStudentListModel here:
public class FundraiserStudentListModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
Working solution with validate email,mobile number
public class ExcelProcessing
{
public List<ExcelUserData> ReadExcel()
{
string path = Config.folderPath + #"\MemberUploadFormat.xlsx";
using (var excelPack = new ExcelPackage())
{
//Load excel stream
using (var stream = File.OpenRead(path))
{
excelPack.Load(stream);
}
//Lets Deal with first worksheet.(You may iterate here if dealing with multiple sheets)
var ws = excelPack.Workbook.Worksheets[0];
List<ExcelUserData> userList = new List<ExcelUserData>();
int colCount = ws.Dimension.End.Column; //get Column Count
int rowCount = ws.Dimension.End.Row;
for (int row = 2; row <= rowCount; row++) // start from to 2 omit header
{
bool IsValid = true;
ExcelUserData _user = new ExcelUserData();
for (int col = 1; col <= colCount; col++)
{
if (col == 1)
{
_user.FirstName = ws.Cells[row, col].Value?.ToString().Trim();
if (string.IsNullOrEmpty(_user.FirstName))
{
_user.ErrorMessage += "Enter FirstName <br/>";
IsValid = false;
}
}
else if (col == 2)
{
_user.Email = ws.Cells[row, col].Value?.ToString().Trim();
if (string.IsNullOrEmpty(_user.Email))
{
_user.ErrorMessage += "Enter Email <br/>";
IsValid = false;
}
else if (!IsValidEmail(_user.Email))
{
_user.ErrorMessage += "Invalid Email Address <br/>";
IsValid = false;
}
}
else if (col ==3)
{
_user.MobileNo = ws.Cells[row, col].Value?.ToString().Trim();
if (string.IsNullOrEmpty(_user.MobileNo))
{
_user.ErrorMessage += "Enter Mobile No <br/>";
IsValid = false;
}
else if (_user.MobileNo.Length != 10)
{
_user.ErrorMessage += "Invalid Mobile No <br/>";
IsValid = false;
}
}
else if (col == 4)
{
_user.IsAdmin = ws.Cells[row, col].Value?.ToString().Trim();
if (string.IsNullOrEmpty(_user.IsAdmin))
{
_user.IsAdmin = "0";
}
}
_user.IsValid = IsValid;
}
userList.Add(_user);
}
return userList;
}
}
public static bool IsValidEmail(string email)
{
Regex regex = new Regex(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",
RegexOptions.CultureInvariant | RegexOptions.Singleline);
return regex.IsMatch(email);
}
}
With this code you won't get an error because a cell is null. it will also cast the data type according to the properties in your class!
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using OfficeOpenXml;
public static class ReadExcel
{
public static List<T> ReadExcelToList<T>(this ExcelWorksheet worksheet) where T : new()
{
List<T> collection = new List<T>();
try
{
DataTable dt = new DataTable();
foreach (var firstRowCell in new T().GetType().GetProperties().ToList())
{
//Add table colums with properties of T
dt.Columns.Add(firstRowCell.Name);
}
for (int rowNum = 2; rowNum <= worksheet.Dimension.End.Row; rowNum++)
{
var wsRow = worksheet.Cells[rowNum, 1, rowNum, worksheet.Dimension.End.Column];
DataRow row = dt.Rows.Add();
foreach (var cell in wsRow)
{
row[cell.Start.Column - 1] = cell.Text;
}
}
//Get the colums of table
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToList();
//Get the properties of T
List<PropertyInfo> properties = new T().GetType().GetProperties().ToList();
collection = dt.AsEnumerable().Select(row =>
{
T item = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
if (columnNames.Contains(pro.Name) || columnNames.Contains(pro.Name.ToUpper()))
{
PropertyInfo pI = item.GetType().GetProperty(pro.Name);
pro.SetValue(item, (row[pro.Name] == DBNull.Value) ? null : Convert.ChangeType(row[pro.Name], (Nullable.GetUnderlyingType(pI.PropertyType) == null) ? pI.PropertyType : Type.GetType(pI.PropertyType.GenericTypeArguments[0].FullName)));
}
}
return item;
}).ToList();
}
catch (Exception ex)
{
//Save error log
}
return collection;
}
}
How to call this function? please view below code;
public List<Users> GetStudentsFromExcel(HttpPostedFileBase file)
{
List<Users> list = new List<Users>();
if (file != null)
{
try
{
using (ExcelPackage package = new ExcelPackage(file.InputStream))
{
ExcelWorkbook workbook = package.Workbook;
if (workbook != null)
{
ExcelWorksheet worksheet = workbook.Worksheets.FirstOrDefault();
if (worksheet != null)
{
list = worksheet.ReadExcelToList<Users>();
//Your code
}
}
}
}
catch (Exception ex)
{
//Save error log
}
}
return list;
}
public class Users
{
public string Code { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public DateTime CreatedAt { get; set; }
}
Hope to help someone!
I have template.xlsx file which I must modify by adding a few copies of one row in specific index. When I try use clone method for this operation I add a row but each row modify each other. I need to create deep clone of opemxml row object but when I try this I have an error that openxml row object are not serialized. How can I deep clone row in .xlsx file using openXML with serialization or if there is another way to deep clone openxml row object?
You can deep clone using .CloneNode(true) on an OpenXmlElement
So if you want to duplicate a row inside a table it will look like
// suppose table an OpenXml Table and row the row you want to clone
table.Append(row.CloneNode(true));
edit :
to insert it before a specific row
// suppose r is the row you want to insert it before
r.InsertBeforeSelf(row.CloneNode(true));
In my case, I needed to copy several rows at the end of the sheet.
I literally needed to clone a specific range of rows, i.e. values, styles and formulas, merged cells. After spending many hours on the problem of copy and paste several rows, I finally found a solution. I was able to copy rows 18 to 26 and paste them from row 27.
Example below:
Below is the code:
public static void CopyRowRange(SpreadsheetDocument document, string sheetName,
int srcRowFrom, int srcRowTo, int destRowFrom)
{
WorkbookPart workbookPart = document.WorkbookPart;
if (srcRowTo < srcRowFrom || destRowFrom < srcRowFrom) return;
int destRowFromBase = destRowFrom;
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
IList<Cell> cells = sheetData.Descendants<Cell>().Where(c =>
GetRowIndex(c.CellReference) >= srcRowFrom &&
GetRowIndex(c.CellReference) <= srcRowTo).ToList<Cell>();
if (cells.Count() == 0) return;
int copiedRowCount = srcRowTo - srcRowFrom + 1;
MoveRowIndex(document, sheetName, destRowFrom - 1, srcRowTo, srcRowFrom);
IDictionary<int, IList<Cell>> clonedCells = null;
IList<Cell> formulaCells = new List<Cell>();
IList<Row> cloneRelatedRows = new List<Row>();
destRowFrom = destRowFromBase;
int changedRowsCount = destRowFrom - srcRowFrom;
formulaCells.Clear();
clonedCells = new Dictionary<int, IList<Cell>>();
foreach (Cell cell in cells)
{
Cell newCell = (Cell)cell.CloneNode(true);
int index = Convert.ToInt32(GetRowIndex(cell.CellReference));
int rowIndex = index - changedRowsCount;
newCell.CellReference = GetColumnName(cell.CellReference) + rowIndex.ToString();
IList<Cell> rowCells = null;
if (clonedCells.ContainsKey(rowIndex))
rowCells = clonedCells[rowIndex];
else
{
rowCells = new List<Cell>();
clonedCells.Add(rowIndex, rowCells);
}
rowCells.Add(newCell);
if (newCell.CellFormula != null && newCell.CellFormula.Text.Length > 0)
{
formulaCells.Add(newCell);
}
}
foreach (int rowIndex in clonedCells.Keys)
{
Row row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).FirstOrDefault();
if (row == null)
{
row = new Row() { RowIndex = (uint)rowIndex };
Row refRow = sheetData.Elements<Row>().Where(r => r.RowIndex > rowIndex).OrderBy(r => r.RowIndex).FirstOrDefault();
if (refRow == null)
sheetData.AppendChild<Row>(row);
else
sheetData.InsertBefore<Row>(row, refRow);
}
row.Append(clonedCells[rowIndex].ToArray());
cloneRelatedRows.Add(row);
}
ChangeFormulaRowNumber(worksheetPart.Worksheet, formulaCells, changedRowsCount);
foreach (Row row in cloneRelatedRows)
{
IList<Cell> cs = row.Elements<Cell>().OrderBy(c => c.CellReference.Value).ToList<Cell>();
row.RemoveAllChildren();
row.Append(cs.ToArray());
}
MergeCells mcells = worksheetPart.Worksheet.GetFirstChild<MergeCells>();
if (mcells != null)
{
IList<MergeCell> newMergeCells = new List<MergeCell>();
IEnumerable<MergeCell> clonedMergeCells = mcells.Elements<MergeCell>().
Where(m => MergeCellInRange(m, srcRowFrom, srcRowTo)).ToList<MergeCell>();
foreach (MergeCell cmCell in clonedMergeCells)
{
MergeCell newMergeCell = CreateChangedRowMergeCell(worksheetPart.Worksheet, cmCell, changedRowsCount);
newMergeCells.Add(newMergeCell);
}
uint count = mcells.Count.Value;
mcells.Count = new UInt32Value(count + (uint)newMergeCells.Count);
mcells.Append(newMergeCells.ToArray());
}
}
private static WorksheetPart
GetWorksheetPartByName(SpreadsheetDocument document,
string sheetName)
{
IEnumerable<Sheet> sheets =
document.WorkbookPart.Workbook.GetFirstChild<Sheets>().
Elements<Sheet>().Where(s => s.Name == sheetName);
if (sheets.Count() == 0)
{
return null;
}
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)
document.WorkbookPart.GetPartById(relationshipId);
return worksheetPart;
}
private static void MoveRowIndex(SpreadsheetDocument document, string sheetName, int destRowFrom, int srcRowTo, int srcRowFrom)
{
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
uint newRowIndex;
IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= srcRowFrom && r.RowIndex.Value <= srcRowTo);
foreach (Row row in rows)
{
newRowIndex = Convert.ToUInt32(destRowFrom + 1);
foreach (Cell cell in row.Elements<Cell>())
{
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
}
row.RowIndex = new UInt32Value(newRowIndex);
destRowFrom++;
}
}
private static void ChangeFormulaRowNumber(Worksheet worksheet, IList<Cell> formulaCells, int changedRowsCount)
{
foreach (Cell formulaCell in formulaCells)
{
Regex regex = new Regex(#"\d+");
var rowIndex = Convert.ToInt32(regex.Match(formulaCell.CellReference).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex = regex2.Match(formulaCell.CellReference).Value;
int newRowIndex = rowIndex + changedRowsCount;
Cell cell = GetCell(worksheet, columnIndex, newRowIndex);
cell.CellFormula = new CellFormula(cell.CellFormula.Text.Replace($"{rowIndex}",$"{newRowIndex}"));
}
}
private static MergeCell CreateChangedRowMergeCell(Worksheet worksheet, MergeCell cmCell, int changedRows)
{
string[] cells = cmCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(#"\d+");
var rowIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var rowIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex1 = regex2.Match(cells[0]).Value;
var columnIndex2 = regex2.Match(cells[1]).Value;
var cell1Name = $"{columnIndex1}{rowIndex1 + changedRows}";
var cell2Name = $"{columnIndex2}{rowIndex2 + changedRows}";
CreateSpreadsheetCellIfNotExist(worksheet, cell1Name);
CreateSpreadsheetCellIfNotExist(worksheet, cell2Name);
return new MergeCell() { Reference = new StringValue(cell1Name + ":" + cell2Name) };
}
private static bool MergeCellInRange(MergeCell mergeCell, int srcRowFrom, int srcRowTo)
{
string[] cells = mergeCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(#"\d+");
var cellIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var cellIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
if (srcRowFrom <= cellIndex1 && cellIndex1 <= srcRowTo &&
srcRowFrom <= cellIndex2 && cellIndex2 <= srcRowTo)
return true;
else
return false;
}
private static uint GetRowIndex(string cellName)
{
Regex regex = new Regex(#"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}
private static string GetColumnName(string cellName)
{
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
{
string columnName = GetColumnName(cellName);
uint rowIndex = GetRowIndex(cellName);
IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);
if (rows.Count() == 0)
{
Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Descendants<SheetData>().First().Append(row);
worksheet.Save();
}
else
{
Row row = rows.First();
IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);
if (cells.Count() == 0)
{
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Save();
}
}
}
private static Cell GetCell(Worksheet worksheet, string columnIndex, int newRowIndex)
{
string cellName = columnIndex + newRowIndex;
Row row = worksheet.GetFirstChild<SheetData>().Descendants<Row>().FirstOrDefault(r => r.RowIndex.Value == newRowIndex);
return row.Elements<Cell>().FirstOrDefault(c => c.CellReference.Value == cellName);
}
I am using SQLite to store the data for my application.
The application has a UI that loads the data from the SQLite database to display it table by table to the user. Basically the user can click left or right and view the other tables one by one.
The user can also click a button that will show a print preview of the data and let them print it. I have this working, but I am having some issues devising a dynamic way to display ANY table on the print preview screen perfectly. My concerns are if some column titles are too long etc, basically how to calculate the size of each column etc. Should I loop through and find the max character size of any text in the entire column and then set the column width to just wider than that or is there an easier way to do this?
I also write the data-table to a comma separated csv file so it might be a better alternative to use an existing solution to print from a csv file if any of you know such a solution then please suggest it!
Anyway here is the currently existing code:
// ------------------------ code that gets called when the print button is clicked ----------------------------
// holds the row data
List<string[]> myList = new List<string[]>();
if(ReportPage == 1)
{
int rowCount = MyTestTable.RowCount;
for(int i = 0; i <rowCount; i++)
{
MyTestTable.SelectedRowIndex = i;
var row1 = MyTestTable.GetSelectedRow();
var cols1 = row1.ItemArray;
string Col1 = cols1[row1.FindIndexOfColumn("Col1")].ToString();
string Col2 = cols1[row1.FindIndexOfColumn("Col2")].ToString();
string Col3 = cols1[row1.FindIndexOfColumn("Col3")].ToString();
string Col4 = cols1[row1.FindIndexOfColumn("Col4")].ToString();
string Col5 = cols1[row1.FindIndexOfColumn("Col5")].ToString();
string Col6 = cols1[row1.FindIndexOfColumn("Col6")].ToString();
string Col7 = cols1[row1.FindIndexOfColumn("Col7")].ToString();
myList.Add(new string[] { Col1, Col2, Col3, Col4, Col5, Col6, Col7 });
}
string[] cols = new string[7];
cols[0] = "Col1";
cols[1] = "Col2";
cols[2] = "Col3";
cols[3] = "Col4";
cols[4] = "Col5";
cols[5] = "Col6";
cols[6] = "Col7";
PrintUtility.SetUpDocument("TEST", cols, myList);
}
PrintUtility.TestNewReport();
// ---------------------- plugin code that gets called from the application
namespace PrintUtility
{
public class PrintUtility : UserComponentBase
{
public PrintDocument document;
public DataGridView dataGridView;
public PrintUtility()
{
document = new PrintDocument();
dataGridView = new DataGridView();
}
public void SetUpDocument(string title, string[] cols, List<string[]> rows)
{
document = new PrintDocument();
dataGridView = new DataGridView();
document.DocumentName = title;
document.DefaultPageSettings.Landscape = true;
document.PrintPage += PrintPage;
DataGridView dataGrid = new DataGridView();
dataGrid.ColumnCount = cols.Length;
for (int i = 0; i < cols.Length; i++ )
{
dataGrid.Columns[i].Name = cols[i];
}
foreach(string[] row in rows)
{
dataGrid.Rows.Add(row);
}
this.dataGridView = dataGrid;
document.DocumentName = title;
document.PrintPage += PrintPage;
}
public PrintDocument GetDocument()
{
return this.document;
}
private DataTable ConvertListToDataTable(List<string[]> list)
{
// New table.
DataTable table = new DataTable();
// Get max columns.
int columns = 0;
foreach (var array in list)
{
if (array.Length > columns)
{
columns = array.Length;
}
}
// Add columns.
for (int i = 0; i < columns; i++)
{
table.Columns.Add();
}
// Add rows.
foreach (var array in list)
{
table.Rows.Add(array);
}
return table;
}
public void TestNewReport()
{
Report report = new Report(new PdfFormatter());
FontDef fd = new FontDef(report, "Helvetica");
FontProp fp = new FontPropMM(fd, 4);
FontProp fp_Title = new FontPropMM(fd, 6);
FontProp fp_Word = new FontPropMM(fd, 3);
fp_Title.bBold = true;
List<string> columns = new List<string>();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
columns.Add(column.Name.ToString());
}
List<List<string>> rows = new List<List<string>>();
foreach (DataGridViewRow row in dataGridView.Rows)
{
List<string> rowSingle = new List<string>();
foreach (DataGridViewCell cell in row.Cells)
{
try
{
rowSingle.Add(cell.Value.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
rows.Add(rowSingle);
}
// AUDIT TABLE ( This way of doing things is not dynamic )
Page page = new Page(report);
page.SetLandscape();
int x = 10;
int y = 40;
int pastLength = 0;
foreach(string col in columns)
{
x += ((pastLength * 14) + 31);
page.Add(x, y, new RepString(fp_Title, col));
pastLength = col.Length;
}
page.Add(0, 52, new RepString(fp_Title, "_________________________________________________________________"));
/* Dynamic way starting
int rowX = 10;
int rowY = 105;
foreach (List<string> row in rows)
{
int pastLength2 = 0;
foreach (string rowItem in row)
{
page.Add(rowX, rowY, new RepString(fp_Word, rowItem));
rowX += ((pastLength * 14) + 31);
pastLength2 = rowItem.Length;
}
rowY += 30;
}
*/
fp_Title.rSizeMM = 8;
int amountRowsPerPageSoFar = 0;
int rowY = 80;
foreach (List<string> row in rows)
{
try
{
string iDItem = row[0];
page.Add(40, rowY, new RepString(fp_Word, iDItem));
string typeItem = row[1];
page.Add(120, rowY, new RepString(fp_Word, typeItem));
string descriptionItem = row[2];
page.Add(190, rowY, new RepString(fp_Word, descriptionItem));
string timestampItem = row[3];
page.Add(375, rowY, new RepString(fp_Word, timestampItem));
string userItem = row[4];
page.Add(555, rowY, new RepString(fp_Word, userItem));
string stationItem = row[5];
page.Add(655, rowY, new RepString(fp_Word, stationItem));
string activeItem = row[6];
page.Add(775, rowY, new RepString(fp_Word, activeItem));
amountRowsPerPageSoFar++;
rowY += 30;
if (amountRowsPerPageSoFar >= 17)
{
page = new Page(report);
page.SetLandscape();
amountRowsPerPageSoFar = 0;
rowY = 40;
}
}
catch (Exception ex)
{
}
}
RT.ViewPDF(report, "TestReport.pdf");
}
}
}