Adding a list of tables in migradoc - c#

I have DB table that looks like
id | Name | ImagePath
-------------------------
1 | aaa | xxx
2 | bbb | yyy
3 | ccc | zzz
For each row I want to create a table, so when i generate my pdf each row will look like this in the pdf
--------------------|-------------------
1 |
aaa |xxx (but the actual image)
--------------------|-------------------
--------------------|-------------------
2 |
bbb |yyy (but the actual image)
--------------------|-------------------
--------------------|-------------------
3 |
ccc |zzz (but the actual image)
--------------------|-------------------
The values are passed into a function with creates the table
public MigraDoc.DocumentObjectModel.Tables.Table createTable(List<string> listOfValues)
{
MigraDoc.DocumentObjectModel.Tables.Table table = new MigraDoc.DocumentObjectModel.Tables.Table();
//table = page.AddTable();
table.Style = "Table";
table.Borders.Color = Colors.Black;
table.Borders.Width = 0.25;
table.Borders.Left.Width = 0.5;
table.Borders.Right.Width = 0.5;
table.Rows.LeftIndent = 0;
//create column
MigraDoc.DocumentObjectModel.Tables.Column column = new MigraDoc.DocumentObjectModel.Tables.Column();
column = table.AddColumn("10cm");
column.Format.Alignment = ParagraphAlignment.Left;
//MigraDoc.DocumentObjectModel.Tables.Row tableRow = table.AddRow();
MigraDoc.DocumentObjectModel.Tables.Column ImageColumn = new MigraDoc.DocumentObjectModel.Tables.Column();
ImageColumn = table.AddColumn("10cm");
ImageColumn.Format.Alignment = ParagraphAlignment.Left;
//create rows from list of values
foreach (var value in listOfValues)
{
MigraDoc.DocumentObjectModel.Tables.Row tableRow = table.AddRow();
//if it is an image add to column 2
if (value.Contains("Image="))
{
tableRow.Cells[1].AddParagraph(value);
//merge the cells for the image column (zero based)
tableRow.Cells[1].MergeDown = listOfValues.Count - 1;
}
else
{
//add the value
tableRow.Cells[0].AddParagraph(value);
}
}
return table
}
The above created a table and then returns it, which is put into a list (of type Table)
The list of tables is then passed into a method that generates the pdf.
public void generatePDF(List<MigraDoc.DocumentObjectModel.Tables.Table> listOfTables)
{
//who generated teh report
string author = Environment.UserName;
DateTime currentTime = DateTime.Now;
Document document = new Document();
string pageHeaderText = "Report";
string pageFooterText = string.Format("Report Generated by {0} at {1} on {2}", author, currentTime.ToShortTimeString(), currentTime.ToShortDateString());
Style style = document.Styles["Normal"];
style.Font.Name = "Arial Unicode MS";
//table Style
style = document.Styles.AddStyle("Table", "Normal");
style.Font.Name = "Verdana";
style.Font.Size = 9;
Section page = document.AddSection();
//header
Paragraph header = page.Headers.Primary.AddParagraph();
header.AddText(pageHeaderText);
header.Format.Alignment = ParagraphAlignment.Center;
//footer
Paragraph footer = page.Footers.Primary.AddParagraph();
footer.AddText(pageFooterText);
footer.Format.Alignment = ParagraphAlignment.Center;
//Main paragraph
Paragraph mainPara = page.AddParagraph("test\n");
//go through each table in the list and add to teh page
foreach (var table in listOfTables)
{
MigraDoc.DocumentObjectModel.Tables.Table tableToAdd = new MigraDoc.DocumentObjectModel.Tables.Table();
tableToAdd = table;
tableToAdd = page.AddTable();
}
PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(true);//false, pdfRenderer.);
pdfRenderer.Document = document;
pdfRenderer.RenderDocument();
//save and open the file
fileName = "test;
//show file save dialog
SaveFileDialog sfd = new SaveFileDialog();
sfd.DefaultExt = ".pdf";
sfd.AddExtension = true;
sfd.FileName = fileName;
if (sfd.ShowDialog() == DialogResult.OK)
{
savePath = System.IO.Path.GetDirectoryName(sfd.FileName);
pdfRenderer.PdfDocument.Save(savePath + "\\" + fileName + ".pdf" );
Process.Start(savePath + "\\" + fileName + ".pdf");
}
}
The problem that I am having is that the table does not get generated on the pdf.
If i pass in a list of strings rather than the list of tables and loop through, the table does get generated, but I am unsure why when using the list (of type tables) does not get generated when looping through.

Computers often do what you tell 'em to do, not what you want 'em to do.
Look at this piece of code:
tableToAdd = table;
tableToAdd = page.AddTable();
You add a new, empty table to the page and assign the new table to the variable tableToAdd, thus losing the reference to the table with the contents.
The code that does what you expect is even simpler:
page.Add(tableToAdd);
It adds the table with the contents to the section.
BTW: page is not the best name for a Section object.

Related

Closed XML can't change the font name and size of pivot table

I have created a pivot table using Closed XML. I want to change the font name and size of the pivot table (cells). When I try doing so, it changes the font name and size of the outer sheet (not of the cell content in pivot table). I also tried setting the font name of each cell but couldn't work. Below is the part of my code -
public void CreatePivotTable(List<EmployeeModel> employeeModels)
{
var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("SourceData");
var table = sheet.Cell(1, 1).InsertTable(employeeModels, "SourceData", true);
var sheetName = "PivotTable";
var ptSheet = workbook.Worksheets.Add(sheetName);
ptSheet.Style.Font.FontName = "Arial";
ptSheet.Style.Font.FontSize = 8;
var pt = ptSheet.PivotTables.Add(sheetName, ptSheet.Cell(5, 2), table.AsRange());
// pt.SetPreserveCellFormatting(false);
pt.RowLabels.Add("EmpId").Compact = true;
pt.RowLabels.Add("EmpName");
pt.ColumnLabels.Add("Description");
pt.ColumnLabels.Add("Department");
pt.Values.Add("HR", "Sum of HR");
pt.Values.Add("MT", "Sum of MT");
pt.SetShowGrandTotalsRows(false);
ptSheet.Cell(1, 3).Value = DateTime.Now.ToString();
ptSheet.Cell(2, 3).Style.Font.Bold = true;
ptSheet.Columns().Width = 11;
ptSheet.Columns("C").Width = 25;
ptSheet.Columns("A").Width = 1;
ptSheet.Columns("B").Width = 0;
ptSheet.Columns("1,2").Hide();
ptSheet.SheetView.FreezeColumns(4);
ptSheet.SheetView.FreezeRows(7);
ptSheet.Rows(1, 4).Hide();
ptSheet.Rows(4, 4).Unhide();
pt.Theme = XLPivotTableTheme.None;
pt.SetClassicPivotTableLayout(true);
//Print Options.
PrintOptionsForPivotTable(ptSheet);
workbook.SaveAs(#"C:\Users\prags\source\repos\WindowsFormsApp1\ClosedXmlPivotTable\pivot.xlsx");
}

C# MVC Export from DB to excel . Merge cells

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;

MigraDoc table formatting / cell indent

I'm trying to create a table with MigraDoc. I want it to look like this:
This is how the table should look
Instead I'm only getting this:
This is the table I'm getting
The text is too near to the border. The indent is missing. I tried
table.Format.LeftIndent = 0.2;
and
table.Format.LeftIndent = "0,2cm";
to set the indent for all table cells. But it didn't work. How can an indent for all table cells be achieved?
This is my complete test method:
private Document debugMigraDocTable()
{
Document document = setupDocument();
Section sectionProtocolHeader = document.AddSection();
Paragraph measValueParagraph = sectionProtocolHeader.AddParagraph("Measurement");
Table table = sectionProtocolHeader.AddTable();
table.Format.LeftIndent = 0.8;
// Table header
Column column = table.AddColumn("1,2cm");
column = table.AddColumn("1,7cm");
column = table.AddColumn("1,7cm");
column = table.AddColumn("1,2cm");
column = table.AddColumn("1,7cm");
column = table.AddColumn("1,7cm");
Row row = table.AddRow();
row.HeadingFormat = true;
row.Format.Alignment = ParagraphAlignment.Center;
row.Format.Font.Bold = true;
row.Cells[0].AddParagraph("Key");
row.Cells[1].AddParagraph("Value 1");
row.Cells[2].AddParagraph("Value 2");
row.Cells[3].AddParagraph("Key");
row.Cells[4].AddParagraph("Value 1");
row.Cells[5].AddParagraph("Value 2");
// Table data
row = table.AddRow();
row.Cells[0].AddParagraph("1");
row.Cells[1].AddParagraph("1,13");
row.Cells[2].AddParagraph("");
row.Cells[3].AddParagraph("33");
row.Cells[4].AddParagraph("0,84");
row.Cells[5].AddParagraph("");
row = table.AddRow();
row.Cells[0].AddParagraph("2");
row.Cells[1].AddParagraph("1,19");
row.Cells[2].AddParagraph("");
row.Cells[3].AddParagraph("34");
row.Cells[4].AddParagraph("1,35");
row.Cells[5].AddParagraph("");
row = table.AddRow();
row.Cells[0].AddParagraph("3");
row.Cells[1].AddParagraph("1,01");
row.Cells[2].AddParagraph("");
row.Cells[3].AddParagraph("35");
row.Cells[4].AddParagraph("1,28");
row.Cells[5].AddParagraph("");
return document;
}
For preview and printing I render this to RTF:
Document document = debugMigraDocTable();
RtfDocumentRenderer rtfRenderer = new RtfDocumentRenderer();
string rtf = rtfRenderer.RenderToString(document, Path.GetTempPath());

Kendo Grid print all data to excel, not just visible data

Let´s say I have grid with 80 items and pagesize is 10, when printing from the controller I want to print all the data, not just the visible data on the first page.
I have good "Export Grid to Excel" test project from Telerik, and I´ve got the export feature all covered and working like a charm. Basically just including the NPOI file and start using it.
Is there a way for me to iterate all the product-data from the DataSourceRequest?
My code sample:
public FileResult Export([DataSourceRequest]DataSourceRequest request)
{
//Get the data representing the current grid state - page, sort and filter
IEnumerable products = db.Products.ToDataSourceResult(request).Data;
//TODO: Get all data but not just the data from the visible page as above!!!
//Create new Excel workbook
var workbook = new HSSFWorkbook();
//Create new Excel sheet
var sheet = workbook.CreateSheet();
//(Optional) set the width of the columns
sheet.SetColumnWidth(0, 10 * 256);
sheet.SetColumnWidth(1, 50 * 256);
sheet.SetColumnWidth(2, 50 * 256);
sheet.SetColumnWidth(3, 50 * 256);
//Create a header row
var headerRow = sheet.CreateRow(0);
//Set the column names in the header row
headerRow.CreateCell(0).SetCellValue("Product ID");
headerRow.CreateCell(1).SetCellValue("Product Name");
headerRow.CreateCell(2).SetCellValue("Unit Price");
headerRow.CreateCell(3).SetCellValue("Quantity Per Unit");
//(Optional) freeze the header row so it is not scrolled
sheet.CreateFreezePane(0, 1, 0, 1);
int rowNumber = 1;
//Populate the sheet with values from the grid data
foreach (Product product in products)
{
//Create a new row
var row = sheet.CreateRow(rowNumber++);
//Set values for the cells
row.CreateCell(0).SetCellValue(product.ProductID);
row.CreateCell(1).SetCellValue(product.ProductName);
row.CreateCell(2).SetCellValue(product.UnitPrice.ToString());
row.CreateCell(3).SetCellValue(product.QuantityPerUnit.ToString());
}
//Write the workbook to a memory stream
MemoryStream output = new MemoryStream();
workbook.Write(output);
//Return the result to the end user
return File(output.ToArray(), //The binary data of the XLS file
"application/vnd.ms-excel", //MIME type of Excel files
"GridExcelExport.xls"); //Suggested file name in the "Save as" dialog which will be displayed to the end user
}
The source from the DataSourceRequest class can be found here.
Probably if you disable the paging properties, you'll get all filtered + sorted data:
public FileResult Export([DataSourceRequest]DataSourceRequest request)
{
request.Take = 9999999;
request.Skip = 0;
// Get the data representing the current grid state : sort and filter
IEnumerable products = db.Products.ToDataSourceResult(request).Data;
After some time I stumbled upon an answer that works. #Stef answer got me on the right track although I didn´t actually use his answer, I will therefore up his answer for the help. I found a way to count the number of pages, and then simply edited the DataSourceRequest for each page. This way ensures me all the pages from the database. I hope this helps others in the future :)
public FileResult Export([DataSourceRequest]DataSourceRequest request)
{
//Count pages to use as iterator when adding to list
var pages = db.Products.ToDataSourceResult(request).Total/request.PageSize;
//Get the data representing the current grid state - page, sort and filter
//IEnumerable products = db.Products.ToDataSourceResult(request).Data;
//Get the data representing the current grid state - page, sort and filter
var products = new List<Product>();
//To ensure all pages get fetched from db
for (int i = 1; i < pages + 1; i++)
{
request.Page = i;
IEnumerable prod = db.Products.ToDataSourceResult(request).Data;
products.AddRange(prod.Cast<Product>().ToList());
}
//Create new Excel workbook
var workbook = new HSSFWorkbook();
//Create new Excel sheet
var sheet = workbook.CreateSheet();
//(Optional) set the width of the columns
sheet.SetColumnWidth(0, 10 * 256);
sheet.SetColumnWidth(1, 50 * 256);
sheet.SetColumnWidth(2, 50 * 256);
sheet.SetColumnWidth(3, 50 * 256);
//Create a header row
var headerRow = sheet.CreateRow(0);
//Set the column names in the header row
headerRow.CreateCell(0).SetCellValue("Product ID");
headerRow.CreateCell(1).SetCellValue("Product Name");
headerRow.CreateCell(2).SetCellValue("Unit Price");
headerRow.CreateCell(3).SetCellValue("Quantity Per Unit");
//(Optional) freeze the header row so it is not scrolled
sheet.CreateFreezePane(0, 1, 0, 1);
int rowNumber = 1;
//Populate the sheet with values from the grid data
foreach (Product product in products)
{
//Create a new row
var row = sheet.CreateRow(rowNumber++);
//Set values for the cells
row.CreateCell(0).SetCellValue(product.ProductID);
row.CreateCell(1).SetCellValue(product.ProductName);
row.CreateCell(2).SetCellValue(product.UnitPrice.ToString());
row.CreateCell(3).SetCellValue(product.QuantityPerUnit.ToString());
}
//Write the workbook to a memory stream
MemoryStream output = new MemoryStream();
workbook.Write(output);
//Return the result to the end user
return File(output.ToArray(), //The binary data of the XLS file
"application/vnd.ms-excel", //MIME type of Excel files
"GridExcelExport.xls"); //Suggested file name in the "Save as" dialog which will be displayed to the end user
}
you may use javascript to print all data to excel, like below
function ExportToCSV() {
var dataSource = $("#grid").data("kendoGrid").dataSource;
var filteredDataSource = new kendo.data.DataSource({
data: dataSource.data(),
filter: dataSource.filter()
});
filteredDataSource.read();
var data = filteredDataSource.view();
var result = "data:application/vnd.ms-excel,";
result += "<table><tr><th>ProductID</th><th>ProductName</th><th>UnitPrice</th><th>Discontinued</th><th>UnitsInStock</th><th>Category</th></tr>";
for (var i = 0; i < data.length; i++) {
result += "<tr>";
result += "<td>";
result += data[i].ProductID;
result += "</td>";
result += "<td>";
result += data[i].ProductName;
result += "</td>";
..
result += "</tr>";
}
result += "</table>";
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(new Blob([result]), 'export.xls');
} else {
window.open(result);
}
e.preventDefault();
}
hope this may help

C# Add item to ObjectListView

My program extracts Windows Updates, detects the version numbers and logs them to columns (KB, Version) in a list view, but I'm trying to change this to an ObjectListView so I can sort the columns. I can't for the life of me work out how to write the results to an ObjectListView and nothing I try seems to work. Here's my current code:
foreach (string file in msu)
{
string KB = GetKBNumber(file);
Expand.MSU(file, TempDirectory + "\\" + KB);
List<string> versions = GetVersionNumbers(TempDirectory + "\\" + KB);
foreach (string version in versions)
{
ListViewItem itm = new ListViewItem(new[] { KB, version });
olvOutput.Items.Add(itm);
}
PerformStep();
}
But it just writes blank data to the control. What am I doing wrong? Thanks in advance.
Edit: Here's the olvOutput designer code:
//
// olvOutput
//
this.olvOutput.AllColumns.Add(this.olvKBNumber);
this.olvOutput.AllColumns.Add(this.olvVersion);
this.olvOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.olvOutput.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.olvKBNumber,
this.olvVersion});
this.olvOutput.Location = new System.Drawing.Point(18, 12);
this.olvOutput.Name = "olvOutput";
this.olvOutput.ShowGroups = false;
this.olvOutput.Size = new System.Drawing.Size(571, 193);
this.olvOutput.TabIndex = 8;
this.olvOutput.UseAlternatingBackColors = true;
this.olvOutput.UseCompatibleStateImageBehavior = false;
this.olvOutput.View = System.Windows.Forms.View.Details;
//
// olvKBNumber
//
this.olvKBNumber.AspectName = "";
this.olvKBNumber.CellPadding = null;
this.olvKBNumber.MaximumWidth = 100;
this.olvKBNumber.MinimumWidth = 100;
this.olvKBNumber.Text = "KB Number";
this.olvKBNumber.Width = 100;
//
// olvVersion
//
this.olvVersion.AspectName = "";
this.olvVersion.CellPadding = null;
this.olvVersion.Text = "Version";
this.olvVersion.Width = 113;
Modify the first of your snippets as:
foreach (string file in msu)
{
string KB = GetKBNumber(file);
Expand.MSU(file, TempDirectory + "\\" + KB);
List<string> versions = GetVersionNumbers(TempDirectory + "\\" + KB);
foreach (string version in versions)
{
olvOutput.AddObject(new { kbAspectName = KB, versionAspectName = version });
}
PerformStep();
}
... and modify the second code snippet as:
//
// olvKBNumber
//
this.olvKBNumber.AspectName = "kbAspectName";
// ...
//
// olvVersion
//
this.olvVersion.AspectName = "versionAspectName";
Disclaimer:
never worked with ObjectListView before so I am not saying this is the best way to achieve what you want.

Categories

Resources