Im building out a c# web app here and I have a Listview. I would like to have a button on the page that when clicked it will print the listview easily. Anyone have any idea how this would be done?
Thanks
Try Adding the following code inside the button click event. Hope fully it will help.
this.ListView1.DataBind();
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
ListView1.RenderControl(hw);
string ListViewHTML = sw.ToString().Replace("\"", "'").Replace(System.Environment.NewLine, "");
StringBuilder sb = new StringBuilder();
sb.Append("<script type = 'text/javascript'>");
sb.Append("window.onload = new function(){");
sb.Append("var printList = window.open('', '', 'left=0");
sb.Append(",top=0,width=800,height=700,status=0');");
sb.Append("printList.document.write(\"");
sb.Append(ListViewHTML);
sb.Append("\");");
sb.Append("printList.document.close();");
sb.Append("printList.focus();");
sb.Append("printList.print();");
sb.Append("printList.close();};");
sb.Append("</script>");
ClientScript.RegisterStartupScript(this.GetType(), "ListViewPrint", sb.ToString());
this.ListView1.DataBind();
There are 2 options that I can think of:
Send this to a different page with an ID (perhaps the order number?) in the query string; read the order id from the query string, and output the details in the page. Then call window.print on window.load
Something like:
Order o = DAL.GetOrderByID(int.Parse(Request.QueryString["OrderID"]));
//output the order in this new page...
On this new page, have a line like this:
window.onload=function(){window.print();};
Use this jquery plugin to do it. It creates a frame in the page and calls .print on the frame. Seems very simple to implement.
Related
to print my Gridview i used following event Button_Print_Click. below is my code:
private void Button_Print_Click(object sender, EventArgs e)
{
GridView1.PagerSettings.Visible = false;
GridView1.DataSource = DataLoad();
GridView1.DataBind();
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
GridView1.RenderControl(hw);
string gridHTML = sw.ToString().Replace("\"", "'").Replace(System.Environment.NewLine, "");
StringBuilder sb = new StringBuilder();
sb.Append("<script type = 'text/javascript'>");
sb.Append("window.onload = new function(){");
sb.Append("var printWin = window.open('', '', 'left=0");
sb.Append(",top=0,width=1000,height=600,status=0');");
sb.Append("printWin.document.write(\"");
sb.Append(gridHTML);
sb.Append("\");");
sb.Append("printWin.document.close();");
sb.Append("printWin.focus();");
sb.Append("printWin.print();");
sb.Append("printWin.close();};");
sb.Append("</script>");
ClientScript.RegisterStartupScript(this.GetType(), "GridPrint", sb.ToString());
GridView1.DataSource = DataLoad();
GridView1.DataBind();
}
But by focusing the page, it doesn't show the css properties. For example my GridViewHeader text font looks simple but not bold!! is there anything wrong with the code? Why dont I get the original CSS styled page printed?
Please help thank you.
The problem is that you are calling a pop-up using window.open(), this popup opens a new HTML page, and even if you inject javascript generated HMTL in it, it doesn't have any reference to your CSS file.
Try to add a link to it in the string output
sb.Append("printWin.document.write(\"");
sb.Append("<link rel='stylesheet' type='text/css' href='...your.css'>")
sb.Append(gridHTML);
Note that this is really an odd way to print a html page, I would find it more simple to generate a extra Print CSS for this using the #media rules, and call the javascript print() on the very same page : see eg http://edutechwiki.unige.ch/en/CSS_for_print_tutorial
I want to use string replace to replace the tag {tag:shoppingcart} and in that it would load the user control:
<%# Register TagPrefix="basket" TagName="cart" src="~/controls/basket.ascx" %>
<basket:cart id="test" runat="server"></basket:cart>
I would be defining the controls at the top of the page that needs them.
My question is what is my best way to achieve the following if it is at all possible
string templatehtml;
templatehtml = template[0].Cms;
string newhtml;
newhtml = templatehtml.Replace("{tag_pagecontent}", currentPage[0].Body);
newhtml = templatehtml.Replace("{tag_shoppingcart}", <basket:cart id="test" runat="server"></basket:cart>);
litcontent.Text = newhtml;
It's the <basket:cart code i am having trouble with.
We actually do a very similar thing, except we have placeholder divs on the page.
In the page_init method, we get a reference to the placeholder div, load the user control into the page, and then add the usercontrol to the div's controls.
For example (very rough code):
// Get a reference to the div
Control oUserControlDiv = this.FindControl("divUserControl");
// Load the user control
Control oControl = this.LoadControl("controls/basket.ascx");
oControl.EnableViewState = true;
// Clear the existing control(s) from the div
oUserControlDiv.Controls.Clear();
// And add the new one
oUserControlDiv.Controls.Add(oControl);
Please try this instead:
var basket = new Basket();
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (HtmlTextWriter writer = new HtmlTextWriter(sw))
{
basket.RenderControl(writer);
}
string basketHtml = sb.ToString();
newhtml = templatehtml.Replace("{tag_shoppingcart}", basketHtml);
I'm having an issue updating a RichText box on a SharePoint 2010 list.
_batchElement.InnerXml =
string.Format(
"<Method ID='1' Cmd='New'><Field RichText='True' Name='Other_x0020_Items_x0020_of_x0020'>{0}</Field><Field Name='Overall_x0020_rating_x0020_of_x0'>{1}</Field><Field Name='Do_x0020_you_x0020_wish_x0020_to'>{2}</Field></Method>",
add_Report_Details.Rtf,
arrText,
addreportwish);
And the code to trigger the update:
ListService.UpdateListItems(ListName, _batchElement);
But, given that this xml element cannot have anything starting with a \ it doesn't want to work.
I have tried HTML as well, even passing HTML through agility pack, and it just doesn't work either.
What is the proper method or field name or something to update that richtextbox?
do i need a cdata? or something? I'm very confused, and the doco on MSDN isn't that great for this method.
Pass it through an HTML encode, and it seems to work fine:
private static string SetProperHTML(string sHtml)
{
var sb = new StringBuilder();
var stringWriter = new StringWriter(sb);
string input = sHtml;
var test = new HtmlAgilityPack.HtmlDocument();
test.LoadHtml(input);
test.OptionOutputAsXml = false;
test.OptionCheckSyntax = true;
test.OptionFixNestedTags = true;
test.OptionAutoCloseOnEnd = true;
test.OptionWriteEmptyNodes = true;
test.Save(stringWriter);
Console.WriteLine(sb.ToString());
return WebUtility.HtmlEncode(sb.ToString().Replace(Environment.NewLine, ""));
}
Also want to make sure that your field descriptor is setup properly:
_batchElement.InnerXml =
string.Format(
"<Method ID='1' Cmd='New'><Field Name='Other_x0020_Items_x0020_of_x0020'>{0}</Field><Field Name='Overall_x0020_rating_x0020_of_x0'>{1}</Field><Field Name='Do_x0020_you_x0020_wish_x0020_to'>{2}</Field></Method>",
SetProperHTML(add_Report_Details.Document.Body.InnerHtml),
arrText,
addreportwish);
it's a field like any other, no special child tags needed. As long as the Sp2010 field on the form is setup for 100% full HTML, this should work. There are other HTMLEncoders out there, which may be better than the WebUtility but, for the most part, this should work, given Agility Pack is fixing most of the HTML.
Is there a better way to generate HTML email in C# (for sending via System.Net.Mail), than using a Stringbuilder to do the following:
string userName = "John Doe";
StringBuilder mailBody = new StringBuilder();
mailBody.AppendFormat("<h1>Heading Here</h1>");
mailBody.AppendFormat("Dear {0}," userName);
mailBody.AppendFormat("<br />");
mailBody.AppendFormat("<p>First part of the email body goes here</p>");
and so on, and so forth?
You can use the MailDefinition class.
This is how you use it:
MailDefinition md = new MailDefinition();
md.From = "test#domain.example";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";
ListDictionary replacements = new ListDictionary();
replacements.Add("{name}", "Martin");
replacements.Add("{country}", "Denmark");
string body = "<div>Hello {name} You're from {country}.</div>";
MailMessage msg = md.CreateMailMessage("you#anywhere.example", replacements, body, new System.Web.UI.Control());
Also, I've written a blog post on how to generate HTML e-mail body in C# using templates using the MailDefinition class.
Use the System.Web.UI.HtmlTextWriter class.
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
html.RenderBeginTag(HtmlTextWriterTag.H1);
html.WriteEncodedText("Heading Here");
html.RenderEndTag();
html.WriteEncodedText(String.Format("Dear {0}", userName));
html.WriteBreak();
html.RenderBeginTag(HtmlTextWriterTag.P);
html.WriteEncodedText("First part of the email body goes here");
html.RenderEndTag();
html.Flush();
string htmlString = writer.ToString();
For extensive HTML that includes the creation of style attributes HtmlTextWriter is probably the best way to go. However it can be a bit clunky to use and some developers like the markup itself to be easily read but perversly HtmlTextWriter's choices with regard indentation is a bit wierd.
In this example you can also use XmlTextWriter quite effectively:-
writer = new StringWriter();
XmlTextWriter xml = new XmlTextWriter(writer);
xml.Formatting = Formatting.Indented;
xml.WriteElementString("h1", "Heading Here");
xml.WriteString(String.Format("Dear {0}", userName));
xml.WriteStartElement("br");
xml.WriteEndElement();
xml.WriteElementString("p", "First part of the email body goes here");
xml.Flush();
Updated Answer:
The documentation for SmtpClient, the class used in this answer, now reads, 'Obsolete("SmtpClient and its network of types are poorly designed, we strongly recommend you use https://github.com/jstedfast/MailKit and https://github.com/jstedfast/MimeKit instead")'.
Source: https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official
Original Answer:
Using the MailDefinition class is the wrong approach. Yes, it's handy, but it's also primitive and depends on web UI controls--that doesn't make sense for something that is typically a server-side task.
The approach presented below is based on MSDN documentation and Qureshi's post on CodeProject.com.
NOTE: This example extracts the HTML file, images, and attachments from embedded resources, but using other alternatives to get streams for these elements are fine, e.g. hard-coded strings, local files, and so on.
Stream htmlStream = null;
Stream imageStream = null;
Stream fileStream = null;
try
{
// Create the message.
var from = new MailAddress(FROM_EMAIL, FROM_NAME);
var to = new MailAddress(TO_EMAIL, TO_NAME);
var msg = new MailMessage(from, to);
msg.Subject = SUBJECT;
msg.SubjectEncoding = Encoding.UTF8;
// Get the HTML from an embedded resource.
var assembly = Assembly.GetExecutingAssembly();
htmlStream = assembly.GetManifestResourceStream(HTML_RESOURCE_PATH);
// Perform replacements on the HTML file (if you're using it as a template).
var reader = new StreamReader(htmlStream);
var body = reader
.ReadToEnd()
.Replace("%TEMPLATE_TOKEN1%", TOKEN1_VALUE)
.Replace("%TEMPLATE_TOKEN2%", TOKEN2_VALUE); // and so on...
// Create an alternate view and add it to the email.
var altView = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
msg.AlternateViews.Add(altView);
// Get the image from an embedded resource. The <img> tag in the HTML is:
// <img src="pid:IMAGE.PNG">
imageStream = assembly.GetManifestResourceStream(IMAGE_RESOURCE_PATH);
var linkedImage = new LinkedResource(imageStream, "image/png");
linkedImage.ContentId = "IMAGE.PNG";
altView.LinkedResources.Add(linkedImage);
// Get the attachment from an embedded resource.
fileStream = assembly.GetManifestResourceStream(FILE_RESOURCE_PATH);
var file = new Attachment(fileStream, MediaTypeNames.Application.Pdf);
file.Name = "FILE.PDF";
msg.Attachments.Add(file);
// Send the email
var client = new SmtpClient(...);
client.Credentials = new NetworkCredential(...);
client.Send(msg);
}
finally
{
if (fileStream != null) fileStream.Dispose();
if (imageStream != null) imageStream.Dispose();
if (htmlStream != null) htmlStream.Dispose();
}
I use dotLiquid for exactly this task.
It takes a template, and fills special identifiers with the content of an anonymous object.
//define template
String templateSource = "<h1>{{Heading}}</h1>Dear {{UserName}},<br/><p>First part of the email body goes here");
Template bodyTemplate = Template.Parse(templateSource); // Parses and compiles the template source
//Create DTO for the renderer
var bodyDto = new {
Heading = "Heading Here",
UserName = userName
};
String bodyText = bodyTemplate.Render(Hash.FromAnonymousObject(bodyDto));
It also works with collections, see some online examples.
I would recomend using templates of some sort. There are various different ways to approach this but essentially hold a template of the Email some where (on disk, in a database etc) and simply insert the key data (IE: Recipients name etc) into the template.
This is far more flexible because it means you can alter the template as required without having to alter your code. In my experience your likely to get requests for changes to the templates from end users. If you want to go the whole hog you could include a template editor.
As an alternative to MailDefinition, have a look at RazorEngine https://github.com/Antaris/RazorEngine.
This looks like a better solution.
Attributted to...
how to send email wth email template c#
E.g
using RazorEngine;
using RazorEngine.Templating;
using System;
namespace RazorEngineTest
{
class Program
{
static void Main(string[] args)
{
string template =
#"<h1>Heading Here</h1>
Dear #Model.UserName,
<br />
<p>First part of the email body goes here</p>";
const string templateKey = "tpl";
// Better to compile once
Engine.Razor.AddTemplate(templateKey, template);
Engine.Razor.Compile(templateKey);
// Run is quicker than compile and run
string output = Engine.Razor.Run(
templateKey,
model: new
{
UserName = "Fred"
});
Console.WriteLine(output);
}
}
}
Which outputs...
<h1>Heading Here</h1>
Dear Fred,
<br />
<p>First part of the email body goes here</p>
Heading Here Dear Fred, First part of the email
body goes here
Emitting handbuilt html like this is probably the best way so long as the markup isn't too complicated. The stringbuilder only starts to pay you back in terms of efficiency after about three concatenations, so for really simple stuff string + string will do.
Other than that you can start to use the html controls (System.Web.UI.HtmlControls) and render them, that way you can sometimes inherit them and make your own clasess for complex conditional layout.
If you don't want a dependency on the full .NET Framework, there's also a library that makes your code look like:
string userName = "John Doe";
var mailBody = new HTML {
new H(1) {
"Heading Here"
},
new P {
string.Format("Dear {0},", userName),
new Br()
},
new P {
"First part of the email body goes here"
}
};
string htmlString = mailBody.Render();
It's open source, you can download it from http://sourceforge.net/projects/htmlplusplus/
Disclaimer: I'm the author of this library, it was written to solve the same issue exactly - send an HTML email from an application.
You might want to have a look at some of the template frameworks that are available at the moment. Some of them are spin offs as a result of MVC but that isn't required. Spark is a good one.
A commercial version which I use in production and allows for easy maintenance is LimiLabs Template Engine, been using it for 3+ years and allows me to make changes to the text template without having to update code (disclaimers, links etc..) - it could be as simple as
Contact templateData = ...;
string html = Template
.FromFile("template.txt")
.DataFrom(templateData )
.Render();
Worth taking a look at, like I did; after attempting various answers mentioned here.
Can I set up HTML/Email Templates in C# on ASP.NET?
This question was asked and answered by SkippyFire and others...I have a follow up question. I like to keep things very simple, as a novice developer.
If I am not correct, Skippyfire said you could send the complete aspx page using this code:
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
Then just use net.mail to send on Page.Load event. This is very confusing to me. I can use this to render controls to an email.Body and send but I can not use this to load an entire page in anyway I have discovered.
Using Net.mail...
How would I send the page above? I tried to put nothing on the page but some text and send it using it's own page load event... I can not figure out any other way to send it from another page or button... (how would you do this? Wouldn't you have to somehow load the URL into an object?)... anyway I tried to do it from Page Load itself as Skippyfire describes in an old post and get this error from Visual studio IDE:
A page can have only one server-side Form tag.
Any help would be appreciated.
CS
It would be sometihng like this:
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter htmlTW = new HtmlTextWriter(sw))
{
this.Render(htmlTW);
}
using (var message = new MailMessage
{
From = new MailAddress("from#company.com"),
Subject = "This is an HTML Email",
Body = sw.ToString(),
IsBodyHtml = true
})
{
message.To.Add("toaddress1#company.com,toaddress2#company.com");
SmtpClient client = new SmtpClient();
client.Send(message);
}
}
There is another way to do this... You can host the ASP.Net runtime in your application. It's not major difficult either, this is "sort-of" what you need to do...
First step is to create a remote-able object that will be used to communicate with the domain. It's only needed method is one to return the output of a page:
internal class RemoteAspDomain : MarshalByRefObject
{
public string ProcessRequest(string page, string query)
{
using (StringWriter sw = new StringWriter())
{
SimpleWorkerRequest work = new SimpleWorkerRequest(page, query, sw);
HttpRuntime.ProcessRequest(work);
return sw.ToString();
}
}
}
Then when your ready to create/work with ASP.Net you setup the environment like:
public static string RunAspPage(string rootDirectory, string page, string query)
{
RemoteAspDomain host;
try
{
host = (RemoteAspDomain)ApplicationHost.CreateApplicationHost(typeof(RemoteAspDomain), "/", rootDirectory);
return host.ProcessRequest(page, query);
}
finally
{
ApplicationManager.GetApplicationManager().ShutdownAll();
System.Web.Hosting.HostingEnvironment.InitiateShutdown();
host = null;
}
}
Now you should be able to use it with the following:
string response = RunAspPage("C:\\MyWebAppRoot\\", "/default.aspx", "asdf=123&xyz=123");
Obviously, you don't want to do this for every request as it takes time to perform the startup-shutdown operations. Simply refactor the RunAspPage to be an IDisposable class that destroys the environment on dispose instead of using the finally block.
Update, BTW if Your already running in an ASP.Net session, there are far easier ways to do this. See HttpServerUtility.Execute Method (String, TextWriter)
Please Note: The above code was copy/pasted and simplified from a working copy, I think I got everything you need but my actual implementation is much more complicated. If you have any trouble there are several real-world examples of these API on the internet.