C# - Creating HTML reports - c#

I have in my winform application 4 datasets that gets values from my database. Values like like how many products I have in my product table and information about categories. I was wondering how I can save the dataset informaiton in a html page. I want to create a html template so that I can present the information in nice way. How can I do that? Eny good guide that explains how to do that?

This seems like a great job for something like RazorEngine. You can define a template using Razor syntax, and then render out the content using the RazorEngine template service, e.g.:
#helper RenderItem(Item item) {
<tr>
<td>item.Name</td>
<td>item.Price</td>
</tr>
}
<html>
<head></head>
<body>
<table>
#foreach (Item item in Model.Items) {
#RenderItem(item)
}
</table>
</body>
</html>

You can save DataSet as XML and then transform it by using XSLT.
You can take a look at this samples:
XSLT - Transformation
Creating and Populating an HTML Template
Using XSLT and .NET to Manipulate XML Files

I personally recommend generating HTML with Linq to Xml (using System.Xml.Linq)
You don't even have to use the strict XHTML schema's, but you'll get heap s of mileage out of Xml.Linq. Here is a snippet from my own code bases:
#region Table Dump Implementation
private static XNode Dump<T>(IEnumerable<T> items, IEnumerable<string> header, params Func<T, string>[] columns)
{
if (!items.Any())
return null;
var html = items.Aggregate(new XElement("table", new XAttribute("border", 1)),
(table, item) => {
table.Add(columns.Aggregate(new XElement("tr"),
(row, cell) => {
row.Add(new XElement("td", EvalColumn(cell, item)));
return row;
} ));
return table;
});
html.AddFirst(header.Aggregate(new XElement("tr"),
(row, caption) => { row.Add(new XElement("th", caption)); return row; }));
return html;
}
private static XNode EvalColumn<T>(Func<T, string> cell, T item)
{
var raw = cell(item);
try
{
var xml = XElement.Parse(raw);
return xml;
}
catch (XmlException)
{
return new XText(raw);
}
}
#endregion
#region Dot Diagrams
public void LinkDiagram(Digraph graph, string id)
{
if (!graph.AllNodes.Any())
return;
var img = Path.GetFileName(GenDiagramFile(graph, _directory, id));
_body.Add(
new XElement("a",
new XAttribute("href", img),
new XElement("h4", "Link naar: " + graph.name),
new XElement("img",
new XAttribute("border", 1),
new XAttribute("src", img),
new XAttribute("width", "40%"))));
}
Note that is it exceedingly easy to use inline HTML text as well (as long as it is valid XML), using a helper like so:
public void GenericAppend(string content)
{
if (!string.IsNullOrEmpty(content))
_body.Add(XElement.Parse(content));
}

What you want is pretty easy so you may just want to generate html directly and use some pre-created CSS for the styling. However if you want something more sophisticated, please take a look at Windward Reports.

Related

Inserting Custom Element with AngleSharp

I'm trying to update a site that uses an sanitizer based on AngleSharp to process user-generated HTML content. The site users need to be able to embed iframes, and I am trying to use a whitelist to control what domains the frame can load. I'd like to rewrite the 'blocked' iframes to a new custom element "blocked-iframe" that will then be stripped out by the sanitizer, so we can review if other domains need to be added to the whitelist.
I'm trying to use a solution based on this answer: https://stackoverflow.com/a/55276825/794
It looks like so:
string BlockIFrames(string content)
{
var parser = new HtmlParser(new HtmlParserOptions { });
var doc = parser.Parse(content);
foreach (var element in doc.QuerySelectorAll("iframe"))
{
var src = element.GetAttribute("src");
if (string.IsNullOrEmpty(src) || !Settings.Sanitization.IFrameWhitelist.Any(wls => src.StartsWith(wls)))
{
var newElement = doc.CreateElement("blocked-iframe");
foreach (var attr in element.Attributes)
{
newElement.SetAttribute(attr.Name, attr.Value);
}
element.Insert(AdjacentPosition.BeforeBegin, newElement.OuterHtml);
element.Remove();
}
}
return doc.FirstElementChild.OuterHtml;
}
It ostensibly works but I notice that the angle brackets in the new element's tag are being escaped on insertion, so the result just gets written into the page as text. I think I could build a map of replacements and just execute them against the string before sending back but I'm wondering if theres a way to do it using AngleSharp's API. The site is using 0.9.9 currently and I'm not sure how far ahead we'll be able to update considering some of the other dependencies in play.
Digging around in the source I found the ReplaceChild method in INode, which works if called from the parent of element
string BlockIFrames(string content)
{
var parser = new HtmlParser(new HtmlParserOptions { });
var doc = parser.Parse(content);
foreach (var element in doc.QuerySelectorAll("iframe"))
{
var src = element.GetAttribute("src");
if (string.IsNullOrEmpty(src) ||
!Settings.Sanitization.IFrameWhitelist.Any(wls => src.StartsWith(wls)))
{
var newElement = doc.CreateElement("blocked-iframe");
foreach (var attr in element.Attributes)
{
newElement.SetAttribute(attr.Name, attr.Value);
}
element.Parent.ReplaceChild(newElement, element);
}
}
return doc.FirstElementChild.OuterHtml;
}
I will keep testing but this seems decent enough to me, if there is a better way I'd love to hear it.

itextsharp c# need a perfect method for page break

I am using itextsharp library.I design an HTML page and convert to PDF .in that case some table are not split perfectly and row also not split correctly
table.keepTogether;
table.splitRows;
table.splitLate
I try this extension but it does not work correctly it mess my CSS and data in PDF. if you have method..answer me:)
finally i got it
public class TableProcessor : Table
{
const string NO_ROW_SPLIT = "no-row-split";
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent)
{
IList<IElement> result = base.End(ctx, tag, currentContent);
var table = (PdfPTable)result[0];
if (tag.Attributes.ContainsKey(NO_ROW_SPLIT))
{
// if not set, table **may** be forwarded to next page
table.KeepTogether = false;
// next two properties keep <tr> together if possible
table.SplitRows = true;
table.SplitLate = true;
}
return new List<IElement>() { table };
}
}
use this class and
var tagfac = Tags.GetHtmlTagProcessorFactory();
tagfac.AddProcessor(new TableProcessor(), new string[] { HTML.Tag.TABLE });
htmlContext.SetTagFactory(tagfac);
integrate this method with htmlpipeline context
this method every time run when the tag hasno-row-split.if it contain key it will make kept by keep together keyword to make page breaking
html
<table no-row-split style="width:100%">
<tr>
<td>
</td>
</tr>
</table>

MVC How to bundle html templates of type "text/html"?

I have a series of template files (*.html) that are used by some JS components. Instead of having those JS components write the templates to the DOM when loaded, I wanted to bundle them together like scripts and have them downloaded separately by the client. This way should be faster (clientside), allow for caching (less round trips), and be more readable (template doesn't have to be stored in a JS string which breaks highlighting/intellisense).
How can this be accomplished?
I.
Use BundleTransformer [http://bundletransformer.codeplex.com/] and Mustache templates [https://mustache.github.io/] or Handlebars [http://handlebarsjs.com/]
II.
[Angular example but you can inspire a lot]
I'm not saying this is the best approach for your case, but i cannot left it like a comment.
Here is an example where the OP stores his bundle in $templateCache. Angular has a templateCache object, which stores all the templates it has loaded so far. It also lets you pre-load templates into the template cache.
Create a BundleTransform class, as he did:
public class PartialsTransform : IBundleTransform
{
private readonly string _moduleName;
public PartialsTransform(string moduleName)
{
_moduleName = moduleName;
}
public void Process(BundleContext context, BundleResponse response)
{
var strBundleResponse = new StringBuilder();
// Javascript module for Angular that uses templateCache
strBundleResponse.AppendFormat(
#"angular.module('{0}').run(['$templateCache',function(t){{",
_moduleName);
foreach (var file in response.Files)
{
// Get the partial page, remove line feeds and escape quotes
var content = File.ReadAllText(file.FullName)
.Replace("\r\n", "").Replace("'", "\\'");
// Create insert statement with template
strBundleResponse.AppendFormat(
#"t.put('partials/{0}','{1}');", file.Name, content);
}
strBundleResponse.Append(#"}]);");
response.Files = new FileInfo[] {};
response.Content = strBundleResponse.ToString();
response.ContentType = "text/javascript";
}
}
But you can store the templates where you want [i don't know where you want to store them].
Then create a Bundle.
public class PartialsBundle : Bundle
{
public PartialsBundle(string moduleName, string virtualPath)
: base(virtualPath, new[] { new PartialsTransform(moduleName) })
{
}
}
And you can use it like a ScriptBundle or StyleBundle.
bundles.Add(new PartialsBundle("testSPA", "~/bundles/partials").Include(
"~/Partials/nav-bar.html",
"~/Partials/home-page.html",
"~/Partials/investment-filter.html",
"~/Partials/investments-component.html",
"~/Partials/sector-component.html",
"~/Partials/transactions-component.html"));
And render like this: #Scripts.Render("~/bundles/partials")
In production transforming in this:
<script src="/bundles/partials?v=dq0i_tF8ogDVZ0X69xyBCdV2O2Qr3nCu0iVsatAzhq41"></script>
This guy is using the $templateCache object forcing Angular not to dynamically download template when are needed.
Further reading here: http://blog.scottlogic.com/2014/08/18/asp-angular-optimisation.html

HTML is being rendered as literal string using RazorEngine. How can I prevent this?

I'm trying to generate a HTML document with RazorEngine (http://razorengine.codeplex.com/). Everything is mostly working, but the problem I have now is some of the HTML is being rendered correctly, and HTML that I have nested in that is being rendered as literal HTML, so rather than the browser displaying the tables & divs as expected, it's displaying e.g.
"<table></table><div></div>"
I kick off this process by calling the following:
string completeHTML = RazorEngine.Razor.Parse("InstallationTemplate.cshtml", new { Data = viewModels });
The completeHTML is then written to a file.
"InstallationTemplate.cshtml" is defined as:
#{
var installationReport = new InstallationReport(Model.Data);
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>
<!-- I would expect this to write the rendered HTML
in place of "#installationReport.DigiChannels()" -->
#installationReport.DigiChannels()
</div>
</body>
</html>
Where InstallationReport and DigiChannels are defined as follows:
public static class InstallationReportExtensions
{
public static string DigiChannels(this InstallationReport installationReport)
{
return installationReport.GetDigiChannelsHtml();
}
}
public class InstallationReport
{
public string GetDigiChannelsHtml()
{
// the following renders the template correctly
string renderedHtml = RazorReport.GetHtml("DigiChannels.cshtml", GetDigiChannelData());
return renderedHtml;
}
}
public static string GetHtml(string templateName, object data)
{
var templateString = GetTemplateString(templateName);
return RazorEngine.Razor.Parse(templateString, data);
}
After GetDigiChannelsHtml() runs and returns renderedHtml, the line of execution returns to TemplateBase.cs into the method ITemplate.Run(ExecuteContext context), which is defined as:
string ITemplate.Run(ExecuteContext context)
{
_context = context;
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
_context.CurrentWriter = writer;
Execute(); // this is where my stuff gets called
_context.CurrentWriter = null;
}
if (Layout != null)
{
// Get the layout template.
var layout = ResolveLayout(Layout);
// Push the current body instance onto the stack for later execution.
var body = new TemplateWriter(tw => tw.Write(builder.ToString()));
context.PushBody(body);
return layout.Run(context);
}
return builder.ToString();
}
When I inspect builder.ToString(), I can see it contains proper HTML for the InstallationTemplate.cshtml stuff, and escaped HTML for the DigiChannels.cshtml stuff. For example:
How can I get #installationReport.DigiChannels() to include the correct HTML instead of the escaped HTML that it's currently doing?
Have you tried:
#Raw(installationReport.DigiChannels())
Edit : I could use it in following way (MVC3)
#Html.Raw(installationReport.DigiChannels())
The alternative to #Raw is to change your API to return HtmlStrings in appropriate places
Represents an HTML-encoded string that should not be encoded again.
The default razor behaviour is to encode strings.

How can I parse this HTML to get the content I want?

I am currently trying to parse an HTML document to retrieve all of the footnotes inside of it; the document contains dozens and dozens of them. I can't really figure out the expressions to use to extract all of content I want. The thing is, the classes (ex. "calibre34") are all randomized in every document. The only way to see where the footnotes are located is to search for "hide" and it's always text afterwards and is closed with a < /td> tag. Below is an example of one of the footnotes in the HTML document, all I want is the text. Any ideas? Thanks guys!
<td class="calibre33">1.<span><a class="x-xref" href="javascript:void(0);">
[hide]</a></span></td>
<td class="calibre34">
Among the other factors on which the premium would be based are the
average size of the losses experienced, a margin for contingencies,
a loading to cover the insurer's expenses, a margin for profit or
addition to the insurer's surplus, and perhaps the investment
earnings the insurer could realize from the time the premiums are
collected until the losses must be paid.</td>
Use HTMLAgilityPack to load the HTML document and then extract the footnotes with this XPath:
//td[text()='[hide]']/following-sibling::td
Basically,what it does is first selecting all td nodes that contain [hide] and then finally go to and select their next sibling. So the next td. Once you have this collection of nodes you can extract their inner text (in C#, with the support provided in HtmlAgilityPack).
How about use MSHTML to parse HTML source?
Here is the demo code.enjoy.
public class CHtmlPraseDemo
{
private string strHtmlSource;
public mshtml.IHTMLDocument2 oHtmlDoc;
public CHtmlPraseDemo(string url)
{
GetWebContent(url);
oHtmlDoc = (IHTMLDocument2)new HTMLDocument();
oHtmlDoc.write(strHtmlSource);
}
public List<String> GetTdNodes(string TdClassName)
{
List<String> listOut = new List<string>();
IHTMLElement2 ie = (IHTMLElement2)oHtmlDoc.body;
IHTMLElementCollection iec = (IHTMLElementCollection)ie.getElementsByTagName("td");
foreach (IHTMLElement item in iec)
{
if (item.className == TdClassName)
{
listOut.Add(item.innerHTML);
}
}
return listOut;
}
void GetWebContent(string strUrl)
{
WebClient wc = new WebClient();
strHtmlSource = wc.DownloadString(strUrl);
}
}
class Program
{
static void Main(string[] args)
{
CHtmlPraseDemo oH = new CHtmlPraseDemo("http://stackoverflow.com/faq");
Console.Write(oH.oHtmlDoc.title);
List<string> l = oH.GetTdNodes("x");
foreach (string n in l)
{
Console.WriteLine("new td");
Console.WriteLine(n.ToString());
}
Console.Read();
}
}

Categories