I am using the .NET WebBrowser control to as a WYSIWYG html editor. I've been using the ExecCommand to do the formatting function fine so far, however I now want to add a table inserter. The problem is I can only seem to Append the table to the document, not insert it half way through. Below is some basic test code, if anyone can help, I'd appreciate it.
HtmlElement tableRow = null;
HtmlElement headerElem = null;
HtmlDocument doc = wbDesign.Document;
HtmlElement tableElem = doc.CreateElement("TABLE");
doc.Body.AppendChild(tableElem);
HtmlElement tableHeader = doc.CreateElement("THEAD");
tableElem.AppendChild(tableHeader);
tableRow = doc.CreateElement("TR");
tableHeader.AppendChild(tableRow);
headerElem = doc.CreateElement("TH");
headerElem.InnerText = "Col1";
tableRow.AppendChild(headerElem);
headerElem = doc.CreateElement("TH");
headerElem.InnerText = "Col2";
tableRow.AppendChild(headerElem);
HtmlElement tableBody = doc.CreateElement("TBODY");
tableElem.AppendChild(tableBody);
tableRow = doc.CreateElement("TR");
tableBody.AppendChild(tableRow);
HtmlElement tableCell = doc.CreateElement("TD");
tableCell.InnerText = "Test";
tableRow.AppendChild(tableCell);
tableCell = doc.CreateElement("TD");
tableCell.InnerText = "Test";
tableRow.AppendChild(tableCell);
You need to navigate the HtmlDocument structure, finding the node at where you want to insert it, and then append there. If you Append to the Body, you'll just add to the end of the last element, i.e. the end.
Its a bit late - but I recently had this requirment and came up with this. I've tried to make this a minimal as possible, to show the methodology, and allow you to customise this a required.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.HtmlControls;
using System.Windows.Forms;
using System.IO;
using System.Web.UI;
public class HtmlToWebBrowserControlDoc
{
// The loaded document MUST have a script similar to this
// <script type="text/javascript" >
// function appendHtml(o) {
// var div = document.createElement("div");
// div.innerHTML = o;
// document.body.appendChild( div);
// }
// </script>
public static void InsertHtmlControl(HtmlControl c, WebBrowser wb)
{
// create a HtmlTextWriter;
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlw = new HtmlTextWriter(sw);
// render the control as html
c.RenderControl(htmlw);
//invoke the script passing in the html
object[] p = new object[1];
p[0] = (object)sb.ToString();
wb.Document.InvokeScript("appendHtml", p);
htmlw.Close();
htmlw.Dispose();
sw.Dispose();
}
}
[DllImport("user32.dll")]
public static extern bool GetCaretPos(ref Point pt);
.....
HtmlElement newElement = webBrowser.Document.CreateElement("<div></div>");
Point p = new Point();
GetCaretPos(ref p);
HtmlElement currentElement = webBrowser.Document.GetElementFromPoint(p);
currentElement.InsertAdjacentElement(HtmlElementInsertionOrientation.AfterBegin, newElement);
Related
I have a project where HTML code is converted to a PDF using HTML Renderer. The HTML code contains a single table. The PDF is displayed but the issue is that the contents of the table are cut off at the end. So is there any solution to the problem?
PdfDocument pdf=new PdfDocument();
var config = new PdfGenerateConfig()
{
MarginBottom = 20,
MarginLeft = 20,
MarginRight = 20,
MarginTop = 20,
};
//config.PageOrientation = PageOrientation.Landscape;
config.ManualPageSize = new PdfSharp.Drawing.XSize(1080, 828);
pdf = PdfGenerator.GeneratePdf(html, config);
byte[] fileContents = null;
using (MemoryStream stream = new MemoryStream())
{
pdf.Save(stream, true);
fileContents = stream.ToArray();
return new FileStreamResult(new MemoryStream(fileContents.ToArray()), "application/pdf");
}
HTMLRenderer should be able break the table to the next page.
See also:
https://github.com/ArthurHub/HTML-Renderer/pull/41
Make sure you are using the latest version. You may have to add those CSS properties.
Also see this answer:
https://stackoverflow.com/a/37833107/162529
As far as I know page breaks are not supported, but I've done a bit of a work-around (which may not work for all cases) by splitting the HTML into separate pages using a page break class, then adding each page to the pdf.
See example code below:
//This will only work on page break elements that are direct children of the body element.
//Each page's content must be inside the pagebreak element
private static PdfDocument SplitHtmlIntoPagedPdf(string html, string pageBreakBeforeClass, PdfGenerateConfig config, PdfDocument pdf)
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
var htmlBodyNode = htmlDoc.DocumentNode.SelectSingleNode("//body");
var tempHtml = string.Empty;
foreach (var bodyNode in htmlBodyNode.ChildNodes)
{
if (bodyNode.Attributes["class"]?.Value == pageBreakBeforeClass)
{
if (!string.IsNullOrWhiteSpace(tempHtml))
{
//add any content found before the page break
AddPageToPdf(htmlDoc,tempHtml,config,ref pdf);
tempHtml = string.Empty;
}
AddPageToPdf(htmlDoc,bodyNode.OuterHtml,config,ref pdf);
}
else
{
tempHtml += bodyNode.OuterHtml;
}
}
if (!string.IsNullOrWhiteSpace(tempHtml))
{
//add any content found after the last page break
AddPageToPdf(htmlDoc, tempHtml, config, ref pdf);
}
return pdf;
}
private static void AddPageToPdf(HtmlDocument htmlDoc, string html, PdfGenerateConfig config, ref PdfDocument pdf)
{
var tempDoc = new HtmlDocument();
tempDoc.LoadHtml(htmlDoc.DocumentNode.OuterHtml);
var docNode = tempDoc.DocumentNode;
docNode.SelectSingleNode("//body").InnerHtml = html;
var nodeDoc = PdfGenerator.GeneratePdf(docNode.OuterHtml, config);
using (var tempMemoryStream = new MemoryStream())
{
nodeDoc.Save(tempMemoryStream, false);
var openedDoc = PdfReader.Open(tempMemoryStream, PdfDocumentOpenMode.Import);
foreach (PdfPage page in openedDoc.Pages)
{
pdf.AddPage(page);
}
}
}
Then call the code as follows:
var pdf = new PdfDocument();
var config = new PdfGenerateConfig()
{
MarginLeft = 5,
MarginRight = 5,
PageOrientation = PageOrientation.Portrait,
PageSize = PageSize.A4
};
if (!string.IsNullOrWhiteSpace(pageBreakBeforeClass))
{
pdf = SplitHtmlIntoPagedPdf(html, pageBreakBeforeClass, config, pdf);
}
else
{
pdf = PdfGenerator.GeneratePdf(html, config);
}
For any html that you want to have in its own page, just put the html inside a div with a class of "pagebreak" (or whatever you want to call it). If you want to, you could add that class to your css and give it "page-break-before: always;", so that the html will be print-friendly.
I've just figured out how to make it work, rather than page-break-inside on a TD, do that on the TABLE. Here's the code:
table { page-break-inside: avoid; }
I'm currently on the following versions (not working on stable versions at the moment):
HtmlRenderer on v1.5.1-beta1
PDFsharp on v1.51.5185-beta
I created a dynamic link button, which needs to be inserted in a StringBuilder to not ruin the design of my aspx page.
So here's a "part" of my code where I need to insert my LinkButton:
design.Append("<h3>");
NewAddToCart(); //This is where my linkbutton should be inserted
design.Append("</h3></div>");
My NewAddToCart() is constructed on the following code:
private void NewAddToCart()
{
LinkButton lbtnAddtoCart = new LinkButton();
lbtnAddtoCart.ID = "lbtnCart" + i;
lbtnAddtoCart.CommandArgument = i.ToString();
lbtnAddtoCart.CssClass = "glyphicon glyphicon-shopping-cart pull-right";
lbtnAddtoCart.Click+=lbtnAddtoCart_Click;
using (StringWriter sw = new StringWriter())
{
using (HtmlTextWriter html = new HtmlTextWriter(sw))
{
lbtnAddtoCart.RenderControl(html);
}
}
}
I am using the HtmlTextWriter to create some HTML for me. I want to test if my page actually works but it doesnt render the div.
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
public partial class web_content_notes_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
/* Configuration Start */
string thumb_directory = "img/thumbs";
string orig_directory = "img/original";
int stage_width = 600;
int stage_height = 480;
Random random = new Random();
// array of allowed file type extensions
string[] allowed_types = { "bmp", "gif", "png", "jpg", "jpeg", "doc", "xls" };
/* Opening the thumbnail directory and looping through all the thumbs: */
foreach (string file in Directory.GetFiles(thumb_directory))
{
string title = Path.GetFileNameWithoutExtension(file);
if (allowed_types.Equals(Path.GetExtension(file)) == true)
{
int left = random.Next(0, stage_width);
int top = random.Next(0, 400);
int rotation = random.Next(-40, -40);
if ((top > stage_height - 130) && (left > stage_width - 230))
{
top -= 120 + 130;
left -= 230;
}
}
//display the files in the directory in a label for testing
Label1.Text = (file);
StringWriter stringWriter = new StringWriter();
// Put HtmlTextWriter in using block because it needs to call Dispose.
using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
// The important part:
writer.Write("<div>testing123</div>");
}
}
}
I want to add the various variables into the div.
How do i do this? I remember in classic asp/vbscript you had to wrap the code in <% code %> not sure if this is the case in ASP.NET / C#
I want to test if my page actually works but it doesnt render the div.
Well no, it wouldn't. Look at what you're doing:
StringWriter stringWriter = new StringWriter();
using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
writer.Write("<div>testing123</div>");
So, you're writing to a StringWriter. You're then doing nothing with that string writer: the text is in memory, and you're letting it get garbage collected, basically.
If you want to write to the Page's response, you'd have to write the result to Page.Response. But you should decide whether you want control the whole of the response for the request - in which case Page probably isn't terribly appropriate - or just a single control, in which case you should probably be putting your code in a custom control.
I'm just trying to get the equivalent HTML code that represent a specific control in asp.
for example i have the following label in ASP
Label x=new Label();
x.ID="a123";
x.Text="b123";
i just want to find a way to get
"<span id='a123'>b123</span>"
You can use this method to render controls to html.
public string RenderControl(Control ctrl)
{
StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
HtmlTextWriter hw = new HtmlTextWriter(tw);
ctrl.RenderControl(hw);
return sb.ToString();
}
And use
Label x = new Label();
x.ID = "a123";
x.Text = "b123";
var html = RenderControl(x);
will give you <span id="a123">b123</span>
I'm writing a program to add some code to html files
I was going to use a series of indexof and loops to find what is essentially ""X
(where X is the spot im looking for)
It occurred to me that there might be a more eloquent way of doing this
does anyone have any suggestions.
what it looks like currently
<body onLoad="JavaScript:top.document.title='Abraham L Barbrow'; if (self == parent) document.getElementById('divFrameset').style.display='block';">
what it should look like when im done
<body onLoad="JavaScript:top.document.title='Abraham L Barbrow'; if (self == parent) document.getElementById('divFrameset').style.display='block';">
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-9xxxxxx-1");
pageTracker._trackPageview();
} catch(err) {}</script>
I'm not sure I'm understanding you, but do you mean this?
// Given an HTML document in "htmlDocument", and new content in "newContent"
string newHtmlDocument = htmlDocument.Replace("</body>", newContent+"</body>");
And it's probably obvious I don't know c#... You'd probably want to make the "body" tag case insensitive via regexps.
I would recommend to use HtmlAgilityPack to parse the html into DOM and work with it.
public string AddImageLink(string emailBody,string imagePath)
{
try
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(emailBody);
HtmlNode node = doc.DocumentNode.SelectSingleNode("//body");
// get body using xpath query ("//body")
// create the new node ..
HtmlNodeCollection LinkNode = new HtmlNodeCollection(node);
//
HtmlNode linkNode = new HtmlNode(HtmlNodeType.Element,doc,0);
linkNode.Name = "A";
linkNode.Attributes.Add("href","www.splash-solutions.co.uk");
HtmlNode imgNode = new HtmlNode(HtmlNodeType.Element,doc,1);
imgNode.Name = "img";
imgNode.Attributes.Add("src",imagePath);
//appending the linknode with image node
linkNode.AppendChild(imgNode);
LinkNode.Append(linkNode);
//appending LinkNode to the body of the html
node.AppendChildren(LinkNode);
StringWriter writer = new StringWriter();
doc.Save(writer);
emailBody = writer.ToString();
return emailBody;
}
If the HTML files are valid XHTML you could always use the XmlDocument class to interpret it. You could then easily look for the body element and append a child element to it. This would place the element right before the closing </body> tag.
You might want to look at using the Html Agility Pack
http://www.codeplex.com/htmlagilitypack
I'm not sure whether the example content you want to add after the tag is the correct one or not, but if it is, I'm seeing two problems:
The Google Analytics code should be added just before the end tag, not the opening tag. That ensures that you don't have to wait for it to load before loading your own code.
If you're adding some other javascript, why not add that in an external file, and execute that one onload instead?
Hope that's of some help :)
This is what i got
feel free to make suggestions
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog OFD = new OpenFileDialog();
OFD.Multiselect = true;
OFD.Filter = "HTML Files (*.htm*)|*.HTM*|" +
"All files (*.*)|*.*";
if (OFD.ShowDialog() == DialogResult.OK)
{
foreach (string s in OFD.FileNames)
{
Console.WriteLine(s);
AddAnalytics(s);
}
MessageBox.Show("done!");
}
}
private void AddAnalytics(string filename)
{
string Htmlcode = "";
using (StreamReader sr = new StreamReader(filename))
{
Htmlcode = sr.ReadToEnd();
}
if (!Htmlcode.Contains(textBox1.Text))
{
Htmlcode = Htmlcode.Replace("</body>", CreateCode(textBox1.Text) + "</body>");
using (StreamWriter sw = new StreamWriter(filename))
{
sw.Write(Htmlcode);
}
}
}
private string CreateCode(string Number)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("<script type=\"text/javascript\">");
sb.AppendLine("var gaJsHost = ((\"https:\" == document.location.protocol) ? \"https://ssl.\" : \"http://www.\");");
sb.AppendLine("document.write(unescape(\"%3Cscript src='\" + gaJsHost + \"google-analytics.com/ga.js' ");
sb.AppendLine("<//script>");
sb.AppendLine("<script type=/\"text//javascript/\">");
sb.AppendLine("try {");
sb.AppendLine(string.Format("var pageTracker = _gat._getTracker(/\"{0}/\");", Number));///"UA-9909000-1"
sb.AppendLine("pageTracker._trackPageview();");
sb.AppendLine("} catch(err) {}<//script>");
sb.AppendLine();
return sb.ToString();
}
}