Generate PDF with Telerik Reporting with .NET Core - c#

Currently, we want to implement a PDF generator with telerik reporting.
We would like to create the PDF with our own customized designer template and supply it with a data model.
The following code shows a route that will export a PDF file with the template: "confirmation.trdp" (see attachment).
It's a dummy file that I created for testing purpose.
Here is the source code I have:
public IActionResult Pdf()
{
// mock data
var dataModel = new MockData
{
Name = "Terence",
Amount = 1000
};
var dataSource = new Telerik.Reporting.ObjectDataSource
{
DataSource = dataModel
};
var reportProcessor = new Telerik.Reporting.Processing.ReportProcessor();
var reportSource = new Telerik.Reporting.UriReportSource
{
Uri = "PDF/confirmation.trdp"
};
//reportSource.Parameters
var result = reportProcessor.RenderReport("PDF", reportSource, null);
var output = result.DocumentBytes;
return File(output, "application/pdf");
}
Here is the template I created from Telerik Report Designer
Here is my question:
How can I bind the text boxes with the data model in the pdf file? And how to configure the.trdp template for that?

You need to parse *.trdp file to Telerik Report object.
var reportProcessor = new Telerik.Reporting.Processing.ReportProcessor();
var deviceInfo = new System.Collections.Hashtable();
var reportPackager = new ReportPackager();
Report report;
InstanceReportSource instanceReportSource = new InstanceReportSource();
using (var sourceStream = System.IO.File.OpenRead(path))
{
report = (Report)reportPackager.UnpackageDocument(sourceStream);
}
After that you can change report datasource. For example (with JsonDataSource):
var ds = new JsonDataSource
{
DataSelector = "$",
Source = JsonConvert.SerializeObject(model,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
})
};
report.DataSource = ds;
instanceReportSource.ReportDocument = report;
Telerik.Reporting.Processing.RenderingResult result = reportProcessor.RenderReport("PDF", instanceReportSource, deviceInfo);
If you are using Telerik Reports in .NET Core don't forget to add all NuGet dependencies.
https://docs.telerik.com/reporting/use-reports-in-net-core-apps

Related

Could not load file or assembly 'System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

I am trying to develop a web application that displays some charts based on different data that is stored in a database. I found on different tutorials online that I should transfer the data from the controller to the view using a model and then to use Json.Encode for the data to be read correctly by my chart.
First it didn't recognize Json.Encode method, so I had to use NuGet Packet Manager to install Microsoft.AspNet.WebPages and when I try to run the app, this error pops up.
I am using .NET Core 5.0
This is where the error comes in:
<script type="text/javascript">
var chart = document.getElementById('pie').getContext('2d');
var myChart = new Chart(chart, {
type: 'pie',
data : #Html.Raw(System.Web.Helpers.Json.Encode(Model.PieChartData)),
});
</script>
This is how I build the model that is given to the view:
public PieChartVM GetPieChartData()
{
var model = new PieChartVM();
var labels = new List<string>();
labels.Add("Green");
labels.Add("Blue");
labels.Add("Gray");
labels.Add("Purple");
model.labels = labels;
var dataset = new List<PieChartChildVM>();
var childModel = new PieChartChildVM();
var backgroundColorList = new List<string>();
var dataList = new List<int>();
foreach(var label in labels)
{
if (label == "Green")
{
backgroundColorList.Add("#2ecc71");
dataList.Add(12);
}
if (label == "Blue")
{
backgroundColorList.Add("#3498db");
dataList.Add(20);
}
if (label == "Gray")
{
backgroundColorList.Add("#95a5a6");
dataList.Add(18);
}
if (label == "Purple")
{
backgroundColorList.Add("#9b59b6");
dataList.Add(50);
}
}
childModel.backgroundColor = backgroundColorList;
childModel.data = dataList;
dataset.Add(childModel);
model.datasets = dataset;
return model;
}
Have you tried this:
using System.Text.Json.Serialization;
string jsonString = JsonSerializer.Serialize(yourModel);
OR
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
var jsonModel = serializer.Serialize(YOURMODEL);

Set programatically created ReportBook as HTML5 ReportSource

A user can select multiple orders, and download all the reports as one PDF.
We used PdfSmartCopy to merge the reports:
protected void Print(int[] order_ids)
{
byte[] merged_reports;
using (MemoryStream ms = new MemoryStream())
using (Document doc = new Document())
using (PdfSmartCopy copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (string order_id in order_ids)
{
Telerik.Reporting.InstanceReportSource reportSource = new Telerik.Reporting.InstanceReportSource();
reportSource.ReportDocument = new OrderReport();
reportSource.Parameters.Add(new Telerik.Reporting.Parameter("order_id", order_id));
RenderingResult result = new ReportProcessor().RenderReport("PDF", reportSource, new Hashtable());
using (PdfReader reader = new PdfReader(result.DocumentBytes))
{
copy.AddDocument(reader);
}
}
doc.Close();
merged_reports = ms.ToArray();
}
Response.Clear();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Expires = -1;
Response.Buffer = false;
Response.ContentType = "application/pdf";
Response.OutputStream.Write(merged_reports, 0, merged_reports.Length);
}
But we started using the HTML5 ReportViewer elsewhere and we want to use it there as well to be consistent. I thought of creating a ReportBook programmatically and set it as the ReportSource of the ReportViewer, but the only thing I can set is a string. We have already used ReportBook before, but this was an actual SomeReportBook.cs that we could set through new SomeReportBook().GetType().AssemblyQualifiedName;.
Any clue? Here is what I have at the moment:
protected void Print(int[] order_ids)
{
Telerik.Reporting.ReportBook reportBook = new Telerik.Reporting.ReportBook();
foreach (string order_id in order_ids)
{
Telerik.Reporting.InstanceReportSource reportSource = new Telerik.Reporting.InstanceReportSource();
reportSource.ReportDocument = new OrderReport();
reportSource.Parameters.Add(new Telerik.Reporting.Parameter("order_id", order_id));
reportBook.ReportSources.Add(reportSource);
}
this.ReportViewer.ReportSource = new Telerik.ReportViewer.Html5.WebForms.ReportSource()
{
Identifier = // Can't use reportBook.GetType().AssemblyQualifiedName
};
}
I have also struggled with this challenge for quite some time; I would to share in case
someone else faces such a challenge. Kindly do this.
1.Create a class that inherits from - Telerik.Reporting.ReportBook
2.Create a method that loads all your reports in your reportbook class i.e.
this.ReportSources.Add(new TypeReportSource
{
TypeName = typeof(Report1).AssemblyQualifiedName
});
Call you method in your class constructor
use the following code to set the report viewer source
var reportSource = new Telerik.ReportViewer.Html5.WebForms.ReportSource();
reportSource.IdentifierType = IdentifierType.TypeReportSource;
reportSource.Identifier = typeof(ReportCatalog).AssemblyQualifiedName;//or
namespace.class, assembly e.g. "MyReports.Report1, MyReportsLibrary"
reportSource.Parameters.Add("Parameter1", "Parameter1");
reportSource.Parameters.Add("Parameter2", "Parameter2");
ReportsViewer1.ReportSource = reportSource;
Report1 = Newly created class that inherits from Telerik.Reporting.ReportBook

OpenXmlPowerTools DocumentBuilder merging documents on a seperate page

I am trying to merge 4 word documents and force content of each document starts at new page. But instead of appending text from each document to a separate page, it adds text from all docs to one page.
Like this:
How can i fix it?
This is the code:
public class HomeController : Controller
{
public void DocMerger()
{
var source1 = Server.MapPath(Url.Content("~/App_Data/1.docx")); //source 1
var source2 = Server.MapPath(Url.Content("~/App_Data/2.docx")); //source 2
var source3 = Server.MapPath(Url.Content("~/App_Data/3.docx")); //source 3
var source4 = Server.MapPath(Url.Content("~/App_Data/4.docx")); //source 4
var merged = Server.MapPath(Url.Content("~/App_Data/merged.docx")); //merged
var f1 = new FileInfo(source1);
var f2 = new FileInfo(source2);
var f3 = new FileInfo(source3);
var f4 = new FileInfo(source4);
//Use DocumentBuilder and merge the files
var sources = new List<OpenXmlPowerTools.Source>()
{
new Source(new WmlDocument(f1.FullName),false),
new Source(new WmlDocument(f2.FullName),false),
new Source(new WmlDocument(f3.FullName),false),
new Source(new WmlDocument(f4.FullName),false)
};
var mergedDocument = DocumentBuilder.BuildDocument(sources);
mergedDocument.SaveAs(merged); //save merged data as merged.docx
}
}
This is the constructor that you use for "Source":
public Source(WordprocessingDocument source, bool keepSections)
Then you just have to change false with true on keepSections value:
var sources = new List<OpenXmlPowerTools.Source>()
{
new Source(new WmlDocument(f1.FullName),true),
new Source(new WmlDocument(f2.FullName),true),
new Source(new WmlDocument(f3.FullName),true),
new Source(new WmlDocument(f4.FullName),true)
};
Using OpenXmlPowerTools.NetStandard version 4.6.0, it works so far with this code, which is based on the answers above, but a little simplyfied.
using OpenXmlPowerTools;
public class Example {
public static void MergeWithBreaks() {
string doc_A = "doc_A.docx";
string doc_B = "doc_B.docx";
string destination = "Merged_Result.docx";
var sources = new List<Source> {
new(new WmlDocument(doc_A), true),
new(new WmlDocument(doc_B), true)
};
var outputPath = destination;
DocumentBuilder.BuildDocument(sources, outputPath);
}
}
I am still trying to do a memory based version.

Importing excel file with all the conditional formatting rules to epplus

I have an excel file which contains lots of data along with icon sets and data bars based on the values in the cell. It looks like this:
I want to import this excel sheet along with the conditional formatting. Is there any library for this?? I went through this http://www.sitecorecleveland.com/resources/blogs-posts/easy_excel_interaction_pt6 but it only imports data not format.
If that's not possible is there code in epplus to have these iconsets in excel sheet. I can have arrows, traffic lights, etc but not these.
I dont think EPP supports custom conditional formatting which are stored as "Workbook Extensions" in the xml of the Excel file. You could copy the xml node of the "extLst" which contains the custom formatting from one worksheet to another. Just make sure there is nothing else beside the cond formatting xml in the node that you do not want copied in which case you will have to select only the child nodes you want.
To test, i created the following excel sheet (temp.xlsx), did a copy.paste of values only and saved to a new file (temp2.xlsx):
Then ran the following and it successfully copied the formatting over:
public void Custom_Condition_Copy_Test()
{
//http://stackoverflow.com/questions/28493050/importing-excel-file-with-all-the-conditional-formatting-rules-to-epplus
//File with custom conditional formatting
var existingFile = new FileInfo(#"c:\temp\temp.xlsx");
//Copy of the file with the conditonal formatting removed
var existingFile2 = new FileInfo(#"c:\temp\temp2.xlsx");
using (var package = new ExcelPackage(existingFile))
using (var package2 = new ExcelPackage(existingFile2))
{
//Make sure there are document element for the source
var worksheet = package.Workbook.Worksheets.First();
var xdoc = worksheet.WorksheetXml;
if (xdoc.DocumentElement == null)
return;
//Make sure there are document element for the destination
var worksheet2 = package2.Workbook.Worksheets.First();
var xdoc2 = worksheet2.WorksheetXml;
if (xdoc2.DocumentElement == null)
return;
//get the extension list node 'extLst' from the ws with the formatting
var extensionlistnode = xdoc
.DocumentElement
.GetElementsByTagName("extLst")[0];
//Create the import node and append it to the end of the xml document
var newnode = xdoc2.ImportNode(extensionlistnode, true);
xdoc2.LastChild.AppendChild(newnode);
package2.Save();
}
}
Might want to put some try's in there but this should get you close.
UPDATE: Based on OPs comment.
If you want to be able to add the custom conditional format without the need of the original file that contains it, I see two options.
Option 1, you do it the more "correct" way and use the DocumentFormat.OpenXml namespace. BUT, this would require you to have the Office Open XML library available which may or may not be so easy depending on the environment you are running this in. You can get it from here http://www.microsoft.com/en-us/download/details.aspx?id=30425 and it comes with a Reflection tool that can generate the code you want which gets you this:
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using X14 = DocumentFormat.OpenXml.Office2010.Excel;
using Excel = DocumentFormat.OpenXml.Office.Excel;
......
WorksheetExtensionList worksheetExtensionList1 = new WorksheetExtensionList();
WorksheetExtension worksheetExtension1 = new WorksheetExtension(){ Uri = "{78C0D931-6437-407d-A8EE-F0AAD7539E65}" };
worksheetExtension1.AddNamespaceDeclaration("x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
X14.ConditionalFormattings conditionalFormattings1 = new X14.ConditionalFormattings();
X14.ConditionalFormatting conditionalFormatting1 = new X14.ConditionalFormatting();
conditionalFormatting1.AddNamespaceDeclaration("xm", "http://schemas.microsoft.com/office/excel/2006/main");
X14.ConditionalFormattingRule conditionalFormattingRule1 = new X14.ConditionalFormattingRule(){ Type = ConditionalFormatValues.IconSet, Priority = 2, Id = "{CD6B2710-0474-449D-881A-22CFE15D011D}" };
X14.IconSet iconSet1 = new X14.IconSet(){ IconSetTypes = X14.IconSetTypeValues.FiveArrows, Custom = true };
X14.ConditionalFormattingValueObject conditionalFormattingValueObject1 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula1 = new Excel.Formula();
formula1.Text = "0";
conditionalFormattingValueObject1.Append(formula1);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject2 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula2 = new Excel.Formula();
formula2.Text = "20";
conditionalFormattingValueObject2.Append(formula2);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject3 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula3 = new Excel.Formula();
formula3.Text = "40";
conditionalFormattingValueObject3.Append(formula3);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject4 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula4 = new Excel.Formula();
formula4.Text = "60";
conditionalFormattingValueObject4.Append(formula4);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject5 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula5 = new Excel.Formula();
formula5.Text = "80";
conditionalFormattingValueObject5.Append(formula5);
X14.ConditionalFormattingIcon conditionalFormattingIcon1 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeSymbols, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon2 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTrafficLights1, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon3 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon4 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)1U };
X14.ConditionalFormattingIcon conditionalFormattingIcon5 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)2U };
iconSet1.Append(conditionalFormattingValueObject1);
iconSet1.Append(conditionalFormattingValueObject2);
iconSet1.Append(conditionalFormattingValueObject3);
iconSet1.Append(conditionalFormattingValueObject4);
iconSet1.Append(conditionalFormattingValueObject5);
iconSet1.Append(conditionalFormattingIcon1);
iconSet1.Append(conditionalFormattingIcon2);
iconSet1.Append(conditionalFormattingIcon3);
iconSet1.Append(conditionalFormattingIcon4);
iconSet1.Append(conditionalFormattingIcon5);
conditionalFormattingRule1.Append(iconSet1);
Excel.ReferenceSequence referenceSequence1 = new Excel.ReferenceSequence();
referenceSequence1.Text = "A1:C201";
conditionalFormatting1.Append(conditionalFormattingRule1);
conditionalFormatting1.Append(referenceSequence1);
conditionalFormattings1.Append(conditionalFormatting1);
worksheetExtension1.Append(conditionalFormattings1);
worksheetExtensionList1.Append(worksheetExtension1);
....
worksheet1.Append(worksheetExtensionList1);
Option 2 would be to do as you are asking and perform string manipulation. This is much easier but it is a slightly dirty in that you are messing with strings rather then objects but if the only thing you need to set is the cell range that doesnt seem so bad. I used the test method above to extract the string with = extensionlistnode.OuterXml:
[TestMethod]
public void Custom_Condition_From_String_Test()
{
//http://stackoverflow.com/questions/28493050/importing-excel-file-with-all-the-conditional-formatting-rules-to-epplus
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.Add(new DataColumn("Col1", typeof(int)));
datatable.Columns.Add(new DataColumn("Col2", typeof(int)));
datatable.Columns.Add(new DataColumn("Col3", typeof(int)));
for (var i = 0; i < 20; i++)
{
var row = datatable.NewRow();
row["Col1"] = i;
row["Col2"] = i * 10;
row["Col3"] = i * 100;
datatable.Rows.Add(row);
}
//Copy of the file with the conditonal formatting removed
var existingFile2 = new FileInfo(#"c:\temp\temp2.xlsx");
if (existingFile2.Exists)
existingFile2.Delete();
using (var package2 = new ExcelPackage(existingFile2))
{
//Add the data
var ws = package2.Workbook.Worksheets.Add("Content");
ws.Cells.LoadFromDataTable(datatable, true);
//The XML String extracted from the orginal excel doc using '= extensionlistnode.OuterXml'
var cellrange = "A1:C201";
var rawxml = String.Format(
"<extLst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><ext uri=\"{{78C0D931-6437-407d-A8EE-F0AAD7539E65}}\" xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\"><x14:conditionalFormattings><x14:conditionalFormatting xmlns:xm=\"http://schemas.microsoft.com/office/excel/2006/main\"><x14:cfRule type=\"iconSet\" priority=\"2\" id=\"{{CD6B2710-0474-449D-881A-22CFE15D011D}}\"><x14:iconSet iconSet=\"5Arrows\" custom=\"1\"><x14:cfvo type=\"percent\"><xm:f>0</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>20</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>40</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>60</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>80</xm:f></x14:cfvo><x14:cfIcon iconSet=\"3Symbols\" iconId=\"0\" /><x14:cfIcon iconSet=\"3TrafficLights1\" iconId=\"0\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"0\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"1\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"2\" /></x14:iconSet></x14:cfRule><xm:sqref>{0}</xm:sqref></x14:conditionalFormatting></x14:conditionalFormattings></ext></extLst>"
, cellrange);
var newxdoc = new XmlDocument();
newxdoc.LoadXml(rawxml);
//Create the import node and append it to the end of the xml document
var xdoc2 = ws.WorksheetXml;
var newnode = xdoc2.ImportNode(newxdoc.FirstChild, true);
xdoc2.LastChild.AppendChild(newnode);
package2.Save();
}
}

Why is my report not showing data in devExpress XtraReport?

I'm creating a report in VS2013 C# winform application using devExpress XtraReport. I'm trying to use a table in the entity framework as the datasource. I tried this using a binding source, but the data does not show.
From my dataProvider class:
public static List<vwInventoryRequestReport> GetView(int docNo)
{
var items = new List<vwInventoryRequestReport>();
using (var context = new Sage300Entities())
{
var query = from i in context.vwInventoryRequestReports
where i.InventoryRequestHeaderId == docNo
select i;
items = query.ToList();
}
return items;
}
From my main class:
var docNo = Convert.ToInt32(teDocumentNumber.Text);
var items = DataProvider.GetView(docNo);
this.bindingSource2.DataSource = items;
InventoryRequestReport report = new InventoryRequestReport();
report.DataSource = items;
ReportPrintTool tool = new ReportPrintTool(report);

Categories