I know there are many topics on the issue, but my requirements are more specific.. I'm using EF to select records into my project, and then export them to Excel. I've used This snippet code.
Let me explain what I'm trying to do. Given the following table(It's for simplification, as you will see in the code the table is a bit larger):
Name | Content
A "Content1"
A "Content2"
A "Content3"
B "other content"
......
When I export to excel, I don't want A to appear 3 times next to each content, I'd like to have only one "A" (which I was able to do) and merge the 3 cells (align them to the center too if possible) into one(without touching the Content column) .
This is my code:
public IActionResult Index()
{
var knownCampaigns = _repository.getDataForExport();
//return View(result);
string sWebRootFolder = _hostingEnvironment.WebRootPath;
string sFileName = #"demo.xlsx";
string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
if (file.Exists)
{
file.Delete();
file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
}
using (ExcelPackage package = new ExcelPackage(file))
{
// add a new worksheet to the empty workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("CampaignMatches");
//First add the headers
worksheet.Cells[1, 2].Value = "Customer Name";
worksheet.Cells[1, 3].Value = "Guid";
worksheet.Cells[1, 4].Value = "Campaign Title";
worksheet.Cells[1, 5].Value = "Referrer Title";
worksheet.Cells[1, 6].Value = "Activity Date";
worksheet.Cells[1, 7].Value = "Is clicked?";
int counter = 2;
string oldGuid = "";
foreach (var campaign in knownCampaigns)
{
if (oldGuid == campaign.Guid || worksheet.Cells["C" + (counter - 1)].Value.ToString() == campaign.Guid)
{
oldGuid = campaign.Guid;
worksheet.Cells["A" + counter].Value = "";
worksheet.Cells["B" + counter].Value = "";
}
else
{
oldGuid = "";
worksheet.Cells["A" + counter].Value = campaign.customerName;
worksheet.Cells["B" + counter].Value = campaign.Guid;
}
worksheet.Cells["C" + counter].Value = campaign.campaignTitle;
worksheet.Cells["D" + counter].Value = campaign.reffererTitle;
worksheet.Cells["E" + counter].Value = campaign.activityDate;
worksheet.Cells["F" + counter].Value = campaign.is_clicked;
counter++;
}
package.Save(); //Save the workbook.
}
var result = PhysicalFile(Path.Combine(sWebRootFolder, sFileName), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
Response.Headers["Content-Disposition"] = new ContentDispositionHeaderValue("attachment")
{
FileName = file.Name
}.ToString();
return result;
}
Right now, my Customer Name and Guid column only appears once as intended, but I don't know how to merge the cells together into one cell.
Image of current output:
[![enter image description here][2]][2]
Image of wanted output:
[![enter image description here][3]][3]
It looks like it's not that obvious. Looks like there is an Elements property on worksheet that you can add type of MergedCell. https://learn.microsoft.com/en-us/office/open-xml/how-to-merge-two-adjacent-cells-in-a-spreadsheet
If someone will face the same issue, I've managed to solve this using Merge attribute , I had to extract the positions of the start column index, row index and end row index & end column index.
var Wsc_Guid = worksheet.Cells[startRowIndex, guidIndex, startRowIndex + numOfSameRecords, guidIndex];//Select the correct cells for Guids
Wsc_Guid.Merge = true;
Related
Im currently using Interop library in order to export excel templates to user but the client requested to change to openxml because it doesn't required excel to be installed on the server
using interop im able to add drop down list using interop in dedicated cell as following
worksheet.get_Range("A9", "A9").Value = "Region";
var Regions = _iFunctionalRepository.GetRegions(GetCurrentNeutralCultureId(), -1);
var RegionsArray = Regions.Select(x => x.Value + "- " + x.Text).ToArray();
var RegionsList = string.Join(",", RegionsArray);
//sectors ddl
worksheet.get_Range("B9", "B9").Validation.Delete();
worksheet.get_Range("B9", "B9").Validation.Add(
Microsoft.Office.Interop.Excel.XlDVType.xlValidateList,
Microsoft.Office.Interop.Excel.XlDVAlertStyle.xlValidAlertInformation,
Microsoft.Office.Interop.Excel.XlFormatConditionOperator.xlBetween,
RegionsList,
Type.Missing);
worksheet.get_Range("B9", "B9").Validation.IgnoreBlank = true;
worksheet.get_Range("B9", "B9").Validation.InCellDropdown = true;
worksheet.get_Range("B9", "B9").Value = "---";
my code to export using open xml which need to add drop down list in cell B9 as example
using (ClosedXML.Excel.XLWorkbook wb = new ClosedXML.Excel.XLWorkbook())
{
var worksheet = wb.Worksheets.Add("GeneralInformation");
var worksheetHiddenSheet = wb.Worksheets.Add("generalHidden");
worksheet.Range("A1", "A1").Value = "title";
worksheet.Range("A1", "A1").Style.Font.SetFontSize(12);
worksheet.Range("A1", "A1").Style.Font.FontColor = ClosedXML.Excel.XLColor.White;
worksheet.Range("A2", "A1").Style.Fill.BackgroundColor = ClosedXML.Excel.XLColor.DodgerBlue;
worksheet.Range("A1", "A1").Style.Font.SetBold();
worksheet.Range("A1", "B1").Merge();
worksheet.Range("A9", "A9").Value = "regions";
var Regions = _iFunctionalRepository.GetRegions(GetCurrentNeutralCultureId(), -1);
var RegionsArray = Regions.Select(x => x.Value + "- " + x.Text).ToArray();
var RegionsList = string.Join(",", RegionsArray);
//drop down code
string randomFileName = "Project Template " + DateTime.Now.ToString("dd-MM-yyyy HH.mm.ss") + ".xlsx";
//string randomFileName = Guid.NewGuid() + ".xlsx";
string FilePath = Server.MapPath("~/PDFReports/") + randomFileName;
//Here saving the file in xlsx
wb.SaveAs(FilePath);
wb.Save();
byte[] filedata = System.IO.File.ReadAllBytes(FilePath);
string contentType = MimeMapping.GetMimeMapping(FilePath);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = randomFileName,
Inline = true,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(filedata, contentType);
}
please help in adding drop down lists to my excel file
after searching i found this solution to add drop down to excel cells from predefined list
var Regions = _dbContext.GetRegions(GetCurrentNeutralCultureId(), -1);
var RegionsfromDb = Regions.Select(x => x.Value + "- " + x.Text).ToList(); //extract needed data
var RegionsList = $"\"{String.Join(",", RegionsfromDb)}\""; //seperate items by comma
worksheet.Cell("B9").DataValidation.IgnoreBlanks = true;
worksheet.Cell("B9").DataValidation.InCellDropdown = true;
worksheet.Cell("B9").Value = "---";
worksheet.Cell("B9").DataValidation.List(RegionsList, true);
We are developing web api from where excel will be downloaded with data. While searching on the net we found libraries like npoi, epplus, closedxml.
Do we really need to use these libraries to work with excel or go with the standard approach?
We are using asp.net core for web api development.
Edit: Basically our front end is in angular 5 from where we
are exposing the web api. In web api we have written logic to get data and after getting data we need to place in certain format/template provided(Cell, Column wise, sheet wise etc.). There are quite a number of rows which we need to export in excel.
Also our database and apis are azure based.
Any help on this appreciated !
I have used epplus, and i think it works well for this scenario. Let me give you an example.
Exporting Data
private ExcelPackage CreateDoc(string title, string subject, string keyword)
{
var p = new ExcelPackage();
p.Workbook.Properties.Title = title;
p.Workbook.Properties.Author = "Application Name";
p.Workbook.Properties.Subject = subject;
p.Workbook.Properties.Keywords = keyword;
return p;
}
public ExcelPackage getApplicantsStatistics()
{
ExcelPackage p = CreateDoc("Applicant Statistics", "Applicant statistics", "All Applicants");
var worksheet = p.Workbook.Worksheets.Add("Applicant Statistics");
//Add Report Header
worksheet.Cells[1, 1].Value = "Applicant Statistics";
worksheet.Cells[1, 1, 1, 3].Merge = true;
//Get the data you want to send to the excel file
var appProg = _unitOfWork.ApplicantsProgram
.AllIncluding(pr => pr.Program1)
.GroupBy(ap => ap.Program1.Name)
.Select(ap => new { programName = ap.Key, TotalNum = ap.Count() })
.ToList();
//First add the headers
worksheet.Cells[2, 1].Value = "SR No";
worksheet.Cells[2, 2].Value = "Program";
worksheet.Cells[2, 3].Value = "No. of Applicants";
//Add values
var numberformat = "#,##0";
var dataCellStyleName = "TableNumber";
var numStyle = p.Workbook.Styles.CreateNamedStyle(dataCellStyleName);
numStyle.Style.Numberformat.Format = numberformat;
for (int i = 0; i < appProg.Count; i++)
{
worksheet.Cells[i + 3, 1].Value = i + 1;
worksheet.Cells[i + 3, 2].Value = appProg[i].programName;
worksheet.Cells[i + 3, 3].Value = appProg[i].TotalNum;
}
// Add to table / Add summary row
var rowEnd = appProg.Count + 2;
var tbl = worksheet.Tables.Add(new ExcelAddressBase(fromRow: 2, fromCol: 1, toRow: rowEnd, toColumn: 3), "Applicants");
tbl.ShowHeader = true;
tbl.TableStyle = TableStyles.Dark9;
tbl.ShowTotal = true;
tbl.Columns[2].DataCellStyleName = dataCellStyleName;
tbl.Columns[2].TotalsRowFunction = RowFunctions.Sum;
worksheet.Cells[rowEnd, 3].Style.Numberformat.Format = numberformat;
// AutoFitColumns
worksheet.Cells[2, 1, rowEnd, 3].AutoFitColumns();
return p;
}
The returned ExcelPackage object can be sent as a download to the File with MVC
byte[] reportBytes;
using (var package = _excelRep.getApplicantsStatistics())
{
reportBytes = package.GetAsByteArray();
}
return File(reportBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
There are several good libraries for doing so, my favorite ones are
EPPlus and OpenXML by Microsoft
https://github.com/JanKallman/EPPlus
https://learn.microsoft.com/en-us/office/open-xml/open-xml-sdk
There is not much difference what your db and frontend are as everything is organized by the backend.
This is my code.
public static string LoadPackage(DirectoryInfo outputDir, string name)
{
FileInfo newFile = new FileInfo(outputDir.FullName + #"\test.xlsx");
if (newFile.Exists)
{
newFile.Delete();
newFile = new FileInfo(outputDir.FullName + #"\test.xlsx");
}
var format = new ExcelTextFormat();
format.Delimiter = '\t';
format.SkipLinesBeginning = 1;
using (ExcelPackage package = new ExcelPackage())
{
LoadSheet(package, outputDir, name);
package.SaveAs(newFile);
}
return newFile.FullName;
}
And after that i call LoadSheet method in order to fill my excel file from tsv file.
public static void LoadSheet(ExcelPackage package, DirectoryInfo
outputDir, string name)
{
var ws = package.Workbook.Worksheets.Add("Content");
var format = new ExcelTextFormat();
format.Delimiter = '\t';
format.SkipLinesBeginning = 2;
format.SkipLinesEnd = 1;
var range = ws.Cells["A1"].LoadFromText(new
FileInfo(outputDir.FullName + "\\" + name), format,
TableStyles.Medium27, false);
}
And this is my code on button click event
if (BrowseFileUpload.HasFile)
{
var name = BrowseFileUpload.PostedFile.FileName;
InputTextBox.Text = name;
LoadData.LoadPackage(new
System.IO.DirectoryInfo("C:\\Users\\Nemanja\\Downloads"), name);
InfoLabel.Text = "Your data has been imported!!!";
InfoLabel.ForeColor = System.Drawing.Color.Blue;
InfoLabel.Font.Size = 20;
}
Everything is ok i create new excel file, sheet save it but it does not load data that i need it to load inside excel file. It's only empty file or i get a error the file is corrupted recover what you can.
Can someone figure out what can be a problem based on my explanation and this code. Thank you all good people.
I think that the problem may well be with the format of your source data. I've put together the following sample, based on your code, and it works fine.
var outFile = Path.ChangeExtension(filePath, ".xlsx");
using (var p = new ExcelPackage())
{
var fmt = new ExcelTextFormat();
fmt.Delimiter = '\t';
fmt.SkipLinesBeginning = 2;
fmt.SkipLinesEnd = 1;
fmt.EOL = ((char)10).ToString(); // THIS LINE FIXED THE PROBLEM (UNIX NEWLINE)
var ws = p.Workbook.Worksheets.Add("Imported Text");
ws.Cells[1, 1].LoadFromText(new FileInfo(filePath), fmt, TableStyles.Medium27, false);
p.SaveAs(new FileInfo(outFile));
}
Try running your data through this and see if you get the same issue or not.
UPDATED
The problem was a unix-style newline in the file - EPPlus expects a windows-style newline by default
I'm writing a list to an excel sheet generated using EPPlus with .xlsx extension. Then using worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns(); method I tried to fit the columns.
This is how I write data
using (ExcelPackage xlPackage = new ExcelPackage(newFile))
{
System.Data.DataTable dt = new System.Data.DataTable();
var ws = xlPackage.Workbook.Worksheets.FirstOrDefault(x => x.Name == language.Culture);
if (ws == null)
{
int i = 1, j = 0;
worksheet = xlPackage.Workbook.Worksheets.Add(language.Culture);
foreach (ExcelFields fieldValues in UnmatchedFieldList)
{
//code
}
else
{
int i = 0;
worksheet = xlPackage.Workbook.Worksheets[language.Culture];
colCount = worksheet.Dimension.End.Column;
rowCount = worksheet.Dimension.End.Row;
foreach (ExcelFields fieldValues in UnmatchedFieldList)
{
worksheet.Cells[rowCount + 1, count + 1].Value = itemName;
}
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
xlPackage.Save();
}
I read data as
string sheetName = language.Culture;
var excelFile = new ExcelQueryFactory(excelPath);
IQueryable<Row> excelSheetValues = from workingSheet in excelFile.Worksheet(sheetName) select workingSheet;
string[] headerRow = excelFile.GetColumnNames(sheetName).ToArray();
At headerRow it is throwing the below error
When I'm trying to read the data from excel it is throwing an exception
External table is not in the expected format
I found out,this is due to the columns are not formatted(width) correctly. When I manually set the columns width by double clicking the cell and run the code it is working fine
So I want to achieve this using code
External table is not in the expected format exception is occurred because of exception of connection string so there fore check your connection string with following sample
public static string docPath= #"C:\sourcefolder\myfile.xlsx";
public static string ConnectionString= "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + docPath + ";Extended Properties=Excel 12.0;";
or if you use LinqToExcel then check whether you have set setting the DatabaseEngine property like as follows
public string docPath= #"C:\sourcefolder\myfile.xlsx";
var excelFile = new ExcelQueryFactory(docPath);
excelFile.DatabaseEngine = DatabaseEngine.Ace;
the code below generate pdf documents:
using (FileStream fs = new FileStream("st.csv", FileMode.Open))
{
using (StreamReader configFile = new StreamReader(fs, System.Text.Encoding.GetEncoding("windows-1250")))
{
string line = string.Empty;
while ((line = configFile.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(line))
{
line = line.Replace("\"", "");
string[] varible = line.Split(';');
string number = varible[0];
string stName = varible[1];
string ewidenceNumber = varible[2];
string fileName = "barcodes\\" + Encryption.RandomString(10, true) + ".png";
Generate(line, fileName);
PdfPTable Table = new PdfPTable(2);
Table.WidthPercentage = 100;
Table.SetWidths(new[] { 110f, 190f });
iTextSharp.text.Image barcode = iTextSharp.text.Image.GetInstance(fileName);
barcode.Border = 0;
barcode.ScalePercent(180f);
PdfPCell imageCell = new PdfPCell(barcode);
imageCell.VerticalAlignment = Element.ALIGN_MIDDLE;
Table.AddCell(imageCell);
PdfPCell descriptionCell = new PdfPCell(new Paragraph(
"Enterprise 1 \n\n" +
number + "\n\n" +
"Number1: " + stName + "\n\n" +
"Number2: " + ewidenceNumber, _standardFont));
descriptionCell.HorizontalAlignment = Element.ALIGN_CENTER;
descriptionCell.VerticalAlignment = Element.ALIGN_MIDDLE;
Table.AddCell(descriptionCell);
Table.KeepTogether = true;
Table.SpacingAfter = 10f;
doc.Add(Table);
}
}
}
}
and here is the problem: vertical and horizontal view in adobe acrobat displays correctly, but when I need to print labels with this information CITIZEN label printer always prints it in horizontal view. I can't adapt this data to print in correct orientation. Anyone has solution for this problem? Maybe I incorrectly rotate cells in table?
I would suggest you drop PDF and instead write to it's native format: http://www.citizen-europe.com/support/progman.htm
PDF printing support is supplied by the driver. If the driver doesn't know how to interpret the specific PDF commands then it's not going to work. Usually label printers don't provide very good driver support for anything but writing to their native format or emulating ZPL (zebra) and Datamax.